如何在不子类化 QsciLexerCustom 的情况下创建新的 QsciLexer 实例?

问题描述 投票:0回答:1

我发现了这些匿名枚举者

SCLEX_CONTAINER
SCLEX_NULL
SCLEX_PYTHON
SCLEX_CPP
QsciScintillaBase

我想将上面的内容设置为 scintilla 编辑器实例的当前词法分析器。

对于实现的词法分析器,我知道我可以通过

self.editor.setLexer(QsciLexerPython(self.editor))
等从 asm、avs、bash 到 vhdl、xml 和 yaml 来做到这一点。

但是枚举中也有未实现的东西,例如

SCLEX_FORTH
SCLEX_ERLANG
SCLEX_GUI4CLI
等。

我试过这个

self.editor.SendScintilla(self.editor.SCI_SETLEXER, self.editor.SCLEX_ERLANG)

但它不起作用。 python 解释器确实没有错误地解析它,但只是没有突出显示和折叠。

所以基本上我的问题是如何使用这些枚举器设置词法分析器而不重新实现和子类化

QsciLexerCustom
,正如你所知,不要重复自己并重建轮子两次。

任何帮助将不胜感激!

顺便说一句,我在 Windows 11 PC 上使用 python 3.12 和 PyQt5 Qscintilla 2.14.1

python python-3.x pyqt pyqt5 qscintilla
1个回答
0
投票

TL;DR:是的,可以。


不需要子类化

QsciLexerCustom
并从头开始定义整个词汇规则。相反,您正在寻找的是裸继承
QsciLexer
子类化。

没有其他废话介绍,让我先向您展示整个生殖示例代码(抱歉不是一个最小的代码):

(根据您提供的代码片段,我相信您正在使用Python,因此我用Python编写了代码,但格式与C++相同):

# This defines the interface to the abstract QsciLexerAsm class.

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.Qsci import *

import platform
import sys

# For testing purposes only.
if "QsciLexerAsm" in dir():
    del QsciLexerAsm


class QsciLexerAsm(QsciLexer):

    propertyChanged = pyqtSignal(str, str)
    
    Default: int = 0
    Comment: int = 1
    Number: int = 2
    DoubleQuotedString: int = 3
    Operator: int = 4
    Identifier: int = 5
    CPUInstruction: int = 6
    FPUInstruction: int = 7
    Register: int = 8
    Directive: int = 9
    DirectiveOperand: int = 11
    BlockComment: int = 12
    SingleQuotedString: int = 13
    UnclosedString: int = 14
    ExtendedInstruction: int = 16
    CommentDirective: int = 17

    __fold_comments: bool
    __fold_compact: bool
    __comment_delimiter: str
    __fold_syntax_based: bool

    def __init__(self, parent: QObject=None) -> None:
        super(QsciLexerAsm, self).__init__(parent)
        self.__fold_comments = True
        self.__fold_compact = True
        self.__comment_delimiter = '~'
        self.__fold_syntax_based = True
    def __del__(self) -> None:
        del self
    def language(self) -> str:
        return "ASM"
    def lexer(self) -> str:
        return "asm"
    def defaultColor(self, style: int) -> QColor:
        match style:
            case QsciLexerAsm.Comment | QsciLexerAsm.BlockComment:
                return QColor(0x00, 0x7f, 0x00)
            case QsciLexerAsm.Number:
                return QColor(0x00, 0x7f, 0x7f)
            case QsciLexerAsm.DoubleQuotedString | QsciLexerAsm.SingleQuotedString:
                return QColor(0x7f, 0x00, 0x7f)
            case QsciLexerAsm.Operator | QsciLexerAsm.UnclosedString:
                return QColor(0x00, 0x00, 0x00)
            case QsciLexerAsm.CPUInstruction:
                return QColor(0x00, 0x00, 0x7f)
            case QsciLexerAsm.FPUInstruction | QsciLexerAsm.Directive | QsciLexerAsm.DirectiveOperand:
                return QColor(0x00, 0x00, 0xff)
            case QsciLexerAsm.Register:
                return QColor(0x46, 0xaa, 0x03)
            case QsciLexerAsm.ExtendedInstruction:
                return QColor(0xb0, 0x00, 0x40)
            case QsciLexerAsm.CommentDirective:
                return QColor(0x66, 0xaa, 0x00)
        return super(QsciLexerAsm, self).defaultColor(style)
    def defaultEolFill(self, style: int) -> bool:
        if style == QsciLexerAsm.UnclosedString:
            return True
        return super(QsciLexerAsm, self).defaultEolFill(style)
    def defaultFont(self, style: int) -> QFont:
        f: QFont = QFont()

        match style:
            case QsciLexerAsm.Operator | QsciLexerAsm.CPUInstruction | QsciLexerAsm.Register:
                f = super(QsciLexerAsm, self).defaultFont(style)
                f.setBold(True)

            case QsciLexerAsm.Comment | QsciLexerAsm.BlockComment:
                if platform.system() == "Windows":
                    f = QFont("Comic Sans MS", 9)
                elif platform.system() == "Darwin":
                    f = QFont("Comic Sans MS", 12)
                else:
                    f = QFont("Bitstream Vera Serif", 9)

            case _:
                f = super(QsciLexerAsm, self).defaultFont(style)

        return f
    def defaultPaper(self, style: int) -> QColor:
        if style == QsciLexerAsm.UnclosedString:
            return QColor(0xe0, 0xc0, 0xe0)
        return super(QsciLexerAsm, self).defaultPaper(style)
    def keywords(self, set_: int) -> str:
        if set_ == 1:
            return (
                "" # <- CPU instructions, replace this by copying official docs
        )

        if set_ == 2:
            return (
                "" # <- FPU instructions, replace this by copying official docs
        )

        if set_ == 3:
            return (
                "" # <- Register names, replace this by copying official docs
        )

        if set_ == 4:
            return (
                "" # <- Directives, replace this by copying official docs
        )

        if set_ == 5:
            return (
                "" # <- Directive Operands, replace this by copying official docs
        )

        if set_ == 6:
            return (
                "" # <- Extended Instructions, replace this by copying official docs
        )

        return ""
    def description(self, style: int) -> str:
        match style:
            case QsciLexerAsm.Default:
                return self.tr("Default")
            case QsciLexerAsm.Comment:
                return self.tr("Comment")
            case QsciLexerAsm.Number:
                return self.tr("Number")
            case QsciLexerAsm.DoubleQuotedString:
                return self.tr("Double-quoted string")
            case QsciLexerAsm.Operator:
                return self.tr("Operator")
            case QsciLexerAsm.Identifier:
                return self.tr("Identifier")
            case QsciLexerAsm.CPUInstruction:
                return self.tr("CPU instruction")
            case QsciLexerAsm.FPUInstruction:
                return self.tr("FPU instruction")
            case QsciLexerAsm.Register:
                return self.tr("Register")
            case QsciLexerAsm.Directive:
                return self.tr("Directive")
            case QsciLexerAsm.DirectiveOperand:
                return self.tr("Directive operand")
            case QsciLexerAsm.BlockComment:
                return self.tr("Block comment")
            case QsciLexerAsm.SingleQuotedString:
                return self.tr("Single-quoted string")
            case QsciLexerAsm.UnclosedString:
                return self.tr("Unclosed string")
            case QsciLexerAsm.ExtendedInstruction:
                return self.tr("Extended instruction")
            case QsciLexerAsm.CommentDirective:
                return self.tr("Comment directive")

        return ""
    def refreshProperties(self) -> None:
        self.__setCommentProp()
        self.__setCompactProp()
        self.__setCommentDelimiterProp()
        self.__setSyntaxBasedProp()
    def foldComments(self) -> bool:
        return self.__fold_comments
    def foldCompact(self) -> bool:
        return self.__fold_compact
    def commentDelimiter(self) -> str:
        return self.__comment_delimiter
    def foldSyntaxBased(self) -> bool:
        return self.__fold_syntax_based
    def setFoldComments(self, fold: bool) -> None:
        self.__fold_comments = fold
        self.__setCommentProp()
    def setFoldCompact(self, fold: bool) -> None:
        self.__fold_compact = fold
        self.__setCompactProp()
    @pyqtSlot(str)
    def setCommentDelimiter(self, delimiter: str) -> None:
        self.__comment_delimiter = delimiter
        self.__setDelimiterProp()
    @pyqtSlot(bool)
    def setFoldSyntaxBased(self, syntax_based: bool) -> None:
        self.__fold_syntax_based = syntax_based
        self.__setSyntaxBasedProp()
    def readProperties(self, qs: QSettings, prefix: str) -> bool:
        self.__fold_comments = bool(qs.value(prefix + "foldcomments", True))
        self.__fold_compact = bool(qs.value(prefix + "foldcompact", True))
        self.__comment_delimiter = str(qs.value(prefix + "commentdelimiter",
                str('~')))
        self.__fold_syntax_based = bool(qs.value(prefix + "foldsyntaxbased", True))

        return True
    def writeProperties(self, qs: QSettings, prefix: str) -> bool:
        qs.setValue(prefix + "foldcomments", self.__fold_comments)
        qs.setValue(prefix + "foldcompact", self.__fold_compact)
        qs.setValue(prefix + "commentdelimiter", self.__comment_delimiter)
        qs.setValue(prefix + "foldsyntaxbased", self.__fold_syntax_based)

        return True

    def __setCommentProp(self):
        self.propertyChanged.emit("fold.asm.comment.multiline",
                ("1" if self.__fold_comments else 0))

    def __setCompactProp(self):
        self.propertyChanged.emit("fold.compact", ("1" if self.__fold_compact else 0))

    def __setCommentDelimiterProp(self):
        self.propertyChanged.emit("lexer.asm.comment.delimiter",
                self.__comment_delimiter)

    def __setSyntaxBasedProp(self):
        self.propertyChanged.emit("fold.asm.syntax.based",
                ("1" if self.__fold_syntax_based else 0))


# Example
if __name__ == "__main__":
    app: QApplication = QApplication(sys.argv)
    w: QsciScintilla = QsciScintilla()
    lexer: QsciLexerAsm = QsciLexerAsm(w)
    w.setLexer(lexer)
    w.show()
    sys.exit(app.exec())

我只有 Asm 自定义解决方法词法分析器实现,但听说您正在使用 QScintilla 2.14,我很抱歉它已实现,所以我无法为您提供一个很好的示例,因为它已经实现了。但逻辑是一样的。

结果应如下所示:

如您所提到的,在 Windows 11 上运行,使用 Python 3.12。看起来与官方词法分析器相同,不是吗?

说明

让我们仔细看看。启用并增强此解决方法的魔力在于:

    def lexer(self) -> str:
        return "asm"

编辑器读取词法标识符相应地设置内部词法分析器,这是在编辑器上采用词法分析器的唯一方法。我想这就是为什么您尝试过的代码不起作用,因为这些枚举器仅供内部使用。这也是为什么继承了

QsciLexer
抽象类后必须重新实现这个方法的原因。

您可能会问,这些词法分析器的标识符是什么,例如

Baan
Gui4Cli
等?

它们在

/scintilla/include/SciLexer.h
标头中定义。由于它是一个纯C++文件,你可能无法在Python包中找到它。因此,您需要导航到此处仔细查看。

正如你所看到的,有枚举器,就像你提到的那样,从第17行,

SCLEX_CONTAINER
SCLEX_NULL
SCLEX_PYTHON
SCLEX_CPP
SCLEX_HTML
等,直到第142行
SCLEX_AUTOMATIC
. 枚举器的名称无一例外都是词法分析器的标识符,并且必须由
QsciLexer.lexer() -> str
const char *QsciLexer::lexer()
重写方法返回。

代币类型

例如

Default
中的
Comment
UnclosedString
QsciLexerCPP
CPUInstruction
中的
QsciLexerAsm
等。您还可以在
SciLexer.h
中找到它,从第143行开始,宏(或
#define
后的标识符)以
SCE_
开头。下面的名称几乎与
lexer()
返回的词法分析器标识符相同,但有一些特殊约定:

  • 图例:
    Convention
    =
    "lexer identifier"
  • P
    =
    "python"
  • C
    =
    "cpp"
    "cppnocase"
  • H
    =
    "html"
  • HJ
    =
    "html"
    的 JavaScript 部分
  • HJA
    =
    "html"
    的 ASP JavaScript 部分
  • HB
    =
    "html"
    的 VB 脚本部分
  • HBA
    =
    "html"
    的 ASP VB 脚本部分
  • HP
    =
    "html"
    的Python部分
  • HPA
    =
    "html"
    的 ASP Python 部分
  • HPHP
    =
    "html"
    的 PHP 部分
  • PL
    =
    "perl"
  • RB
    =
    "ruby"
  • B
    =
    "basic"
  • PROPS
    =
    "properties"
  • L
    =
    "latex"
  • Err
    =
    "errorlist"

属性读/写

例如,

setSmartHighlighting(bool)
中的
QsciLexerPascal

您可以通过一次创建三个方法来完成此操作:

set...()
...()
和私有
__set...Prop()
;以及重写
readProperties()
writeProperties()
方法,如上面的代码所示。

set...Prop()
的属性字符串(例如
__setSyntaxBasedProp()
中的“fold.asm.syntax.based”)可以在每个词法分析器文件中找到。例如,在
QsciLexerBasic
(想象力)中,您可以拥有(想象力):

    @pyqtSlot(bool)
    def setBasicExplicitComment(self, explicit: bool) -> None:
        self.__basic_explicit_comment = explicit
        self.__setExplicitCommentProp()
    def basicExplicitComment(self) -> bool:
        return self.__basic_explicit_comment
    def __setExplicitCommentProp(self) -> None:
        self.propertyChanged.emit("fold.basic.comment.explicit", ("1" if self.__basic_explicit_comment else "0")

来源:这里

记住,您需要将

propertyChanged = pyqtSignal(str, str)
信号定义为类属性(不要在
__init__
中定义它),原生 Scintilla 中
bool
的值是
"1"
的 str-value
True
"0"
代表
False
,而不是
bool
或隐式
1
0

如果有什么不明白的地方,请不要犹豫,在下面评论我。

© www.soinside.com 2019 - 2024. All rights reserved.