python - 如何在 QScintilla 中实现 SublimeText 的逐级折叠功能

标签 python pyqt folding scintilla qscintilla

我正在尝试在 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"), self,
                  lambda level=1: self.fold_by_level(level))
        QShortcut(QKeySequence("Ctrl+K, Ctrl+2"), self,
                  lambda level=2: self.fold_by_level(level))
        QShortcut(QKeySequence("Ctrl+K, Ctrl+3"), self,
                  lambda level=3: self.fold_by_level(level))
        QShortcut(QKeySequence("Ctrl+K, Ctrl+4"), self,
                  lambda level=4: self.fold_by_level(level))
        QShortcut(QKeySequence("Ctrl+K, Ctrl+5"), self,
                  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 <iostream>
using namespace std;

void Function0() {
    cout << "Function0";
}

void Function1() {
    cout << "Function1";
}

void Function2() {
    cout << "Function2";
}

void Function3() {
    cout << "Function3";
}


int main(void) {
    if (1) {
        if (1) {
            if (1) {
                if (1) {
                    int yay;
                }
            }
        }
    }

    if (1) {
        if (1) {
            if (1) {
                if (1) {
                    int yay2;
                }
            }
        }
    }

    return 0;
}\
    """)
    ex.resize(800, 600)
    ex.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

我遵循的文档是 https://www.scintilla.org/ScintillaDoc.html#Foldinghttp://pyqt.sourceforge.net/Docs/QScintilla2/classQsciScintilla.html .

正如我所说,fold_by_level 功能旨在表现得与 SublimeText 完全一样,但我不确定 ST 的功能实现细节。无论如何,让我在 SublimeText 上测试一些基本序列后发布一些屏幕截图,这些屏幕截图可以澄清我在这里想要实现的目标:

序列 1: {ctrl+k,ctrl+5},{ctrl+k,ctrl+j} {ctrl+k,ctrl+4},{ctrl+k,ctrl +j} {ctrl+k, ctrl+3}, {ctrl+k, ctrl+j} {ctrl+k, ctrl+2}, {ctrl+k, ctrl+j} {ctrl+k, ctrl+1} , {ctrl+k, ctrl+j}

enter image description here

序列 2: {ctrl+k,ctrl+5},{ctrl+k,ctrl+4},{ctrl+k,ctrl+3},{ctrl+k, ctrl+2}, {ctrl+k, ctrl+1}

enter image description here

我确信有更多关于 SublimeText 行为的内部细节,但如果我的示例在测试序列后表现得与发布在那些镜头上的完全一样,你可以说该功能已经变得非常易于使用。

最佳答案

您的示例中的问题主要是由于 QsciScintilla API 中的命名不当造成的。 foldLinefoldAll方法实际上应该称为 toggleFoldLinetoggleFoldAll,因为它们实际上撤消 以前的状态。这意味着,例如,如果两个连续的行具有相同的折叠级别,则调用 foldLine 两次将不会产生任何净变化。

在下面的实现中,我使用了更明确的 Scintilla 消息,这样只有真正需要折叠的行才会受到影响。我还更改了键盘快捷键以匹配 SublimeText 中的默认设置:

class Foo(QsciScintilla):
    def __init__(self, parent=None):
        ...
        QShortcut(QKeySequence("Ctrl+K, Ctrl+J"), self, self.fold_by_level)
        QShortcut(QKeySequence("Ctrl+K, Ctrl+0"), self, 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)

关于python - 如何在 QScintilla 中实现 SublimeText 的逐级折叠功能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50177976/

相关文章:

python - 趋势的 ARIMA 顺序

python - 提高sqlite查询速度

python - 清除 QWebEngineView 中的 cookie

Vim 缩进折叠依赖

python - 管理 Django 中的错误有困难 - NoReverseMatch at

python - 如何从具有更改日志的表和 Django 中的事件表中获取可以推迟给定日期的事件?

python - 在 Windows 上使用 python 获取 QProcess 的 PID

r - Vim 折叠 R 的语法

python - 对 Python 代码使用 Vim 折叠的推荐方法是什么

python - 如果密码无效,使 AES 解密失败