我正在尝试在QScintilla组件上实现fold_by_level SublimeText3功能,但我不知道如何做到这一点,到目前为止我已经提出了这个代码:
import sys
import re
import math
from PyQt5.Qt import * # noqa
from PyQt5.Qsci import QsciScintilla
from PyQt5 import Qsci
from PyQt5.Qsci import QsciLexerCPP
class Foo(QsciScintilla):
def __init__(self,parent=None):
super().__init__(parent)
# http://www.scintilla.org/ScintillaDoc.html#Folding
self.setFolding(QsciScintilla.BoxedTreeFoldStyle)
# Indentation
self.setIndentationsUseTabs(False)
self.setIndentationWidth(4)
self.setBackspaceUnindents(True)
self.setIndentationGuides(True)
# Set the default font
self.font = QFont()
self.font.setFamily('Consolas')
self.font.setFixedPitch(True)
self.font.setPointSize(10)
self.setFont(self.font)
self.setMarginsFont(self.font)
# Margin 0 is used for line numbers
fontmetrics = QFontMetrics(self.font)
self.setMarginsFont(self.font)
self.setMarginWidth(0,fontmetrics.width("000") + 6)
self.setMarginLineNumbers(0,True)
self.setMarginsBackgroundColor(QColor("#cccccc"))
# Indentation
self.setIndentationsUseTabs(False)
self.setIndentationWidth(4)
self.setBackspaceUnindents(True)
lexer = QsciLexerCPP()
lexer.setFoldAtElse(True)
lexer.setFoldComments(True)
lexer.setFoldCompact(False)
lexer.setFoldPreprocessor(True)
self.setLexer(lexer)
QShortcut(QKeySequence("Ctrl+K,Ctrl+J"),self,lambda level=-1: self.fold_by_level(level))
QShortcut(QKeySequence("Ctrl+K,Ctrl+1"),lambda level=1: self.fold_by_level(level))
QShortcut(QKeySequence("Ctrl+K,Ctrl+2"),lambda level=2: self.fold_by_level(level))
QShortcut(QKeySequence("Ctrl+K,Ctrl+3"),lambda level=3: self.fold_by_level(level))
QShortcut(QKeySequence("Ctrl+K,Ctrl+4"),lambda level=4: self.fold_by_level(level))
QShortcut(QKeySequence("Ctrl+K,Ctrl+5"),lambda level=5: self.fold_by_level(level))
def fold_by_level(self,lvl):
if lvl < 0:
self.foldAll(True)
else:
for i in range(self.lines()):
level = self.SendScintilla(
QsciScintilla.SCI_GETFOLDLEVEL,i) & QsciScintilla.SC_FOLDLEVELNUMBERMASK
level -= 0x400
print(f"line={i+1},level={level}")
if lvl == level:
self.foldLine(i)
def main():
app = QApplication(sys.argv)
ex = Foo()
ex.setText("""\
#include
我所遵循的文档是https://www.scintilla.org/ScintillaDoc.html#Folding和http://pyqt.sourceforge.net/Docs/QScintilla2/classQsciScintilla.html.
正如我所说,fold_by_level功能的目的与SublimeText完全相同,但我不确定ST的功能实现细节.在任何情况下,让我在SublimeText上测试一些基本序列之后发布一些截图,这些序列可以澄清我在这里想要实现的内容:
Sequence1:{ctrl k,ctrl 5},{ctrl k,ctrl j} {ctrl k,ctrl 4},ctrl 3},ctrl 2},ctrl 1},ctrl j}
Sequence2:{ctrl k,ctrl 1}
我确信SublimeText行为有更多的内部细节,但是如果我的示例在测试序列之后表现得与在这些镜头上发布完全相同,那么您可以说该功能已经变得非常方便使用.
最佳答案
您的示例的问题主要是由QsciScintilla API中的一些不良命名引起的.应该真正调用foldLine和foldAll方法toggleFoldLine和toggleFoldAll,因为它们实际上撤消了先前的状态.这意味着,例如,如果两个连续的行具有相同的折叠级别,则调用foldLine两次将不会导致净更改.
在下面的实现中,我使用了更明确的Scintilla消息,因此只有真正需要折叠的行才会受到影响.我还更改了键盘快捷键以匹配SublimeText中的默认值:
class Foo(QsciScintilla):
def __init__(self,parent=None):
...
QShortcut(QKeySequence("Ctrl+K,self.fold_by_level)
QShortcut(QKeySequence("Ctrl+K,Ctrl+0"),self.fold_by_level)
...
def fold_by_level(self,level=0):
SCI = self.SendScintilla
if level:
level += 0x400
MASK = QsciScintilla.SC_FOLDLEVELNUMBERMASK
for line in range(self.lines()):
foldlevel = SCI(QsciScintilla.SCI_GETFOLDLEVEL,line) & MASK
print('line=%i,level=%i' % (line + 1,foldlevel),end='')
if foldlevel == level:
line = SCI(QsciScintilla.SCI_GETFOLDPARENT,line)
if SCI(QsciScintilla.SCI_GETFOLDEXPANDED,line):
print(',foldline:',line + 1,end='')
SCI(QsciScintilla.SCI_FOLDLINE,line,QsciScintilla.SC_FOLDACTION_CONTRACT)
print()
else:
SCI(QsciScintilla.SCI_FOLDALL,QsciScintilla.SC_FOLDACTION_EXPAND)