Автоматический отступ — очень удобная функция, предлагаемая 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. Проблема
Описанный в первом абзаце подход без лексера работает. Подход, описанный во втором абзаце с лексером, не работает. Независимо от того, какую настройку я выберу в качестве параметра для 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
- Питон 3.6
- QScintilla 2.10 или выше