setAutoIndentStyle() на лексере QScintilla не работает
Автоматическая установка отступов - очень хорошая функция, предлагаемая QScintilla. При вставке новой строки автоматическое отступление перемещает курсор к тому же уровню отступа, что и предыдущий:
1. Без лексера
Если вы НЕ установили лексер, вы можете легко включить эту функцию следующим образом:
self.__editor.setAutoIndent(True)
self.__editor
в этой кодовой строке является примером QsciScintilla()
, так что он представляет собой настоящий редактор в моем коде.
2. С лексером
Даже если вы активируете лексер, предыдущий подход будет работать. Но это не очень хорошая практика, потому что лексер может переопределить эту настройку. Таким образом, лучшим подходом было бы удалить предыдущую кодовую строку и включить автоматический отступ в лексере:
class MyLexer(QsciLexerCustom):
def __init__(self, parent):
super(MyLexer, self).__init__(parent)
[...]
self.setAutoIndentStyle(QsciScintilla.AiMaintain)
''''''
def language(self):
[...]
''''''
def description(self, style):
[...]
''''''
def styleText(self, start, end):
[...]
''''''
'''--- end class ---'''
Еще одним преимуществом этого подхода является большая гибкость. Включение автоматического отступа в лексере позволяет выбирать между (или даже комбинировать):
QsciScintilla.AiOpening
QsciScintilla.AiClosing
QsciScintilla.AiMaintain
3. Проблема
Подход, описанный в первом абзаце без лексера, работает. Подход, описанный во втором абзаце с помощью lexer, отсутствует. Независимо от того, какую настройку я выберу в качестве параметра setAutoIndentStyle(..)
ничего не меняется.
Зачем?
4. Полный пример эксперимента
Вот пример для эксперимента. Просто скопируйте и вставьте его в .py
файл и запустить его. Вы должны получить работающий редактор с простой подсветкой синтаксиса. Вы можете использовать его, чтобы поэкспериментировать с автоматическим отступом:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.Qsci import *
import re
class MyLexer(QsciLexerCustom):
def __init__(self, parent):
super(MyLexer, self).__init__(parent)
# Default text settings
# ----------------------
self.setDefaultColor(QColor("#ff000000"))
self.setDefaultPaper(QColor("#ffffffff"))
self.setDefaultFont(QFont("Consolas", 14))
# Initialize colors per style
# ----------------------------
self.setColor(QColor("#ff000000"), 0) # Style 0: black
self.setColor(QColor("#ff7f0000"), 1) # Style 1: red
self.setColor(QColor("#ff0000bf"), 2) # Style 2: blue
self.setColor(QColor("#ff007f00"), 3) # Style 3: green
# Initialize paper colors per style
# ----------------------------------
self.setPaper(QColor("#ffffffff"), 0) # Style 0: white
self.setPaper(QColor("#ffffffff"), 1) # Style 1: white
self.setPaper(QColor("#ffffffff"), 2) # Style 2: white
self.setPaper(QColor("#ffffffff"), 3) # Style 3: white
# Initialize fonts per style
# ---------------------------
self.setFont(QFont("Consolas", 14, weight=QFont.Bold), 0) # Style 0: Consolas 14pt
self.setFont(QFont("Consolas", 14, weight=QFont.Bold), 1) # Style 1: Consolas 14pt
self.setFont(QFont("Consolas", 14, weight=QFont.Bold), 2) # Style 2: Consolas 14pt
self.setFont(QFont("Consolas", 14, weight=QFont.Bold), 3) # Style 3: Consolas 14pt
# Auto indent
# ------------
#self.setAutoIndentStyle(QsciScintilla.AiOpening | QsciScintilla.AiClosing)
self.setAutoIndentStyle(QsciScintilla.AiMaintain)
print(self.autoIndentStyle())
''''''
def language(self):
return "SimpleLanguage"
''''''
def description(self, style):
if style == 0:
return "myStyle_0"
elif style == 1:
return "myStyle_1"
elif style == 2:
return "myStyle_2"
elif style == 3:
return "myStyle_3"
###
return ""
''''''
def styleText(self, start, end):
# 1. Initialize the styling procedure
# ------------------------------------
self.startStyling(start)
# 2. Slice out a part from the text
# ----------------------------------
text = self.parent().text()[start:end]
# 3. Tokenize the text
# ---------------------
p = re.compile(r"[*]\/|\/[*]|\s+|\w+|\W")
# 'token_list' is a list of tuples: (token_name, token_len)
token_list = [ (token, len(bytearray(token, "utf-8"))) for token in p.findall(text)]
# 4. Style the text
# ------------------
# 4.1 Check if multiline comment
multiline_comm_flag = False
editor = self.parent()
if start > 0:
previous_style_nr = editor.SendScintilla(editor.SCI_GETSTYLEAT, start - 1)
if previous_style_nr == 3:
multiline_comm_flag = True
###
###
# 4.2 Style the text in a loop
for i, token in enumerate(token_list):
if multiline_comm_flag:
self.setStyling(token[1], 3)
if token[0] == "*/":
multiline_comm_flag = False
###
###
else:
if token[0] in ["for", "while", "return", "int", "include"]:
# Red style
self.setStyling(token[1], 1)
elif token[0] in ["(", ")", "{", "}", "[", "]", "#"]:
# Blue style
self.setStyling(token[1], 2)
elif token[0] == "/*":
multiline_comm_flag = True
self.setStyling(token[1], 3)
else:
# Default style
self.setStyling(token[1], 0)
###
###
###
''''''
''' end Class '''
myCodeSample = r"""#include <stdio.h>
/*
* This is a
* multiline
* comment */
int main()
{
char arr[5] = {'h', 'e', 'l', 'l', 'o'};
int i;
for(i = 0; i < 5; i++) {
printf(arr[i]);
}
return 0;
}""".replace("\n","\r\n")
class CustomMainWindow(QMainWindow):
def __init__(self):
super(CustomMainWindow, self).__init__()
# -------------------------------- #
# Window setup #
# -------------------------------- #
# 1. Define the geometry of the main window
# ------------------------------------------
self.setGeometry(300, 300, 800, 400)
self.setWindowTitle("QScintilla Test")
# 2. Create frame and layout
# ---------------------------
self.__frm = QFrame(self)
self.__frm.setStyleSheet("QWidget { background-color: #ffeaeaea }")
self.__lyt = QVBoxLayout()
self.__frm.setLayout(self.__lyt)
self.setCentralWidget(self.__frm)
self.__myFont = QFont()
self.__myFont.setPointSize(14)
# 3. Place a button
# ------------------
self.__btn = QPushButton("Qsci")
self.__btn.setFixedWidth(50)
self.__btn.setFixedHeight(50)
self.__btn.clicked.connect(self.__btn_action)
self.__btn.setFont(self.__myFont)
self.__lyt.addWidget(self.__btn)
# -------------------------------- #
# QScintilla editor setup #
# -------------------------------- #
# ! Make instance of QSciScintilla class!
# ----------------------------------------
self.__editor = QsciScintilla()
self.__editor.setText(myCodeSample) # 'myCodeSample' is a string containing some C-code
self.__editor.setLexer(None) # We install lexer later
self.__editor.setUtf8(True) # Set encoding to UTF-8
self.__editor.setFont(self.__myFont) # Gets overridden by lexer later on
# 1. Text wrapping
# -----------------
self.__editor.setWrapMode(QsciScintilla.WrapWord)
self.__editor.setWrapVisualFlags(QsciScintilla.WrapFlagByText)
self.__editor.setWrapIndentMode(QsciScintilla.WrapIndentIndented)
# 2. End-of-line mode
# --------------------
self.__editor.setEolMode(QsciScintilla.EolWindows)
self.__editor.setEolVisibility(False)
# 3. Indentation
# ---------------
self.__editor.setIndentationsUseTabs(False)
self.__editor.setTabWidth(4)
self.__editor.setIndentationGuides(True)
self.__editor.setTabIndents(True)
#self.__editor.setAutoIndent(True) <- This is set in the lexer!!!
# 4. Caret
# ---------
self.__editor.setCaretForegroundColor(QColor("#ff0000ff"))
self.__editor.setCaretLineVisible(True)
self.__editor.setCaretLineBackgroundColor(QColor("#1f0000ff"))
self.__editor.setCaretWidth(2)
# 5. Margins
# -----------
# Margin 0 = Line nr margin
self.__editor.setMarginType(0, QsciScintilla.NumberMargin)
self.__editor.setMarginWidth(0, "0000")
self.__editor.setMarginsForegroundColor(QColor("#ff888888"))
# -------------------------------- #
# Install lexer #
# -------------------------------- #
self.__lexer = MyLexer(self.__editor)
self.__editor.setLexer(self.__lexer)
# ! Add editor to layout !
# -------------------------
self.__lyt.addWidget(self.__editor)
self.show()
''''''
def __btn_action(self):
print("Hello World!")
''''''
''' End Class '''
if __name__ == '__main__':
app = QApplication(sys.argv)
QApplication.setStyle(QStyleFactory.create('Fusion'))
myGUI = CustomMainWindow()
sys.exit(app.exec_())
''''''
Я взял этот пример (и немного адаптировал его к потребностям этого вопроса) с моего сайта на QScintilla: https://qscintilla.com/custom-lexer-example/
5. Примечания
Ради полноты, это моя система:
- Windows 10
- Python 3.6
- QScintilla 2.10 или выше
1 ответ
Лексер сцинтиллы состоит из 2 частей, первая из которых предназначена для окраски синтаксиса, а вторая - для отступа.
Пример, который вы используете, предназначен только для окрашивания синтаксиса.
Я не использовал реализацию QScintilla на python, поэтому не уверен, что функции доступны в python, однако в C++ вы можете использовать indentLine в своем лексере, чтобы установить его, или задать ключевые слова для начала и конца блока, чтобы позволить qscintilla сделай это для тебя.