- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
考虑这个 mcve:
import math
import sys
import textwrap
import time
from pathlib import Path
from collections import defaultdict
from PyQt5.Qsci import QsciLexerCustom, QsciScintilla
from PyQt5.Qt import *
from pygments import lexers, styles, highlight, formatters
from pygments.lexer import Error, RegexLexer, Text, _TokenType
from pygments.style import Style
EXTRA_STYLES = {
"monokai": {
"background": "#272822",
"caret": "#F8F8F0",
"foreground": "#F8F8F2",
"invisibles": "#F8F8F259",
"lineHighlight": "#3E3D32",
"selection": "#49483E",
"findHighlight": "#FFE792",
"findHighlightForeground": "#000000",
"selectionBorder": "#222218",
"activeGuide": "#9D550FB0",
"misspelling": "#F92672",
"bracketsForeground": "#F8F8F2A5",
"bracketsOptions": "underline",
"bracketContentsForeground": "#F8F8F2A5",
"bracketContentsOptions": "underline",
"tagsOptions": "stippled_underline",
}
}
def convert_size(size_bytes):
if size_bytes == 0:
return "0B"
size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
i = int(math.floor(math.log(size_bytes, 1024)))
p = math.pow(1024, i)
s = round(size_bytes / p, 2)
return f"{s} {size_name[i]}"
class ViewLexer(QsciLexerCustom):
def __init__(self, lexer_name, style_name):
super().__init__()
# Lexer + Style
self.pyg_style = styles.get_style_by_name(style_name)
self.pyg_lexer = lexers.get_lexer_by_name(lexer_name, stripnl=False)
self.cache = {
0: ('root',)
}
self.extra_style = EXTRA_STYLES[style_name]
# Generate QScintilla styles
self.font = QFont("Consolas", 8, weight=QFont.Bold)
self.token_styles = {}
index = 0
for k, v in self.pyg_style:
self.token_styles[k] = index
if v.get("color", None):
self.setColor(QColor(f"#{v['color']}"), index)
if v.get("bgcolor", None):
self.setPaper(QColor(f"#{v['bgcolor']}"), index)
self.setFont(self.font, index)
index += 1
def defaultPaper(self, style):
return QColor(self.extra_style["background"])
def language(self):
return self.pyg_lexer.name
def get_tokens_unprocessed(self, text, stack=('root',)):
"""
Split ``text`` into (tokentype, text) pairs.
``stack`` is the inital stack (default: ``['root']``)
"""
lexer = self.pyg_lexer
pos = 0
tokendefs = lexer._tokens
statestack = list(stack)
statetokens = tokendefs[statestack[-1]]
while 1:
for rexmatch, action, new_state in statetokens:
m = rexmatch(text, pos)
if m:
if action is not None:
if type(action) is _TokenType:
yield pos, action, m.group()
else:
for item in action(lexer, m):
yield item
pos = m.end()
if new_state is not None:
# state transition
if isinstance(new_state, tuple):
for state in new_state:
if state == '#pop':
statestack.pop()
elif state == '#push':
statestack.append(statestack[-1])
else:
statestack.append(state)
elif isinstance(new_state, int):
# pop
del statestack[new_state:]
elif new_state == '#push':
statestack.append(statestack[-1])
else:
assert False, "wrong state def: %r" % new_state
statetokens = tokendefs[statestack[-1]]
break
else:
# We are here only if all state tokens have been considered
# and there was not a match on any of them.
try:
if text[pos] == '\n':
# at EOL, reset state to "root"
statestack = ['root']
statetokens = tokendefs['root']
yield pos, Text, u'\n'
pos += 1
continue
yield pos, Error, text[pos]
pos += 1
except IndexError:
break
def highlight_slow(self, start, end):
style = self.pyg_style
view = self.editor()
code = view.text()[start:]
tokensource = self.get_tokens_unprocessed(code)
self.startStyling(start)
for _, ttype, value in tokensource:
self.setStyling(len(value), self.token_styles[ttype])
def styleText(self, start, end):
view = self.editor()
t_start = time.time()
self.highlight_slow(start, end)
t_elapsed = time.time() - t_start
len_text = len(view.text())
text_size = convert_size(len_text)
view.setWindowTitle(f"Text size: {len_text} - {text_size} Elapsed: {t_elapsed}s")
def description(self, style_nr):
return str(style_nr)
class View(QsciScintilla):
def __init__(self, lexer_name, style_name):
super().__init__()
view = self
# -------- Lexer --------
self.setEolMode(QsciScintilla.EolUnix)
self.lexer = ViewLexer(lexer_name, style_name)
self.setLexer(self.lexer)
# -------- Shortcuts --------
self.text_size = 1
self.s1 = QShortcut(f"ctrl+1", view, self.reduce_text_size)
self.s2 = QShortcut(f"ctrl+2", view, self.increase_text_size)
# self.gen_text()
# # -------- Multiselection --------
self.SendScintilla(view.SCI_SETMULTIPLESELECTION, True)
self.SendScintilla(view.SCI_SETMULTIPASTE, 1)
self.SendScintilla(view.SCI_SETADDITIONALSELECTIONTYPING, True)
# -------- Extra settings --------
self.set_extra_settings(EXTRA_STYLES[style_name])
def get_line_separator(self):
m = self.eolMode()
if m == QsciScintilla.EolWindows:
eol = '\r\n'
elif m == QsciScintilla.EolUnix:
eol = '\n'
elif m == QsciScintilla.EolMac:
eol = '\r'
else:
eol = ''
return eol
def set_extra_settings(self, dct):
self.setIndentationGuidesBackgroundColor(QColor(0, 0, 255, 0))
self.setIndentationGuidesForegroundColor(QColor(0, 255, 0, 0))
if "caret" in dct:
self.setCaretForegroundColor(QColor(dct["caret"]))
if "line_highlight" in dct:
self.setCaretLineBackgroundColor(QColor(dct["line_highlight"]))
if "brackets_background" in dct:
self.setMatchedBraceBackgroundColor(QColor(dct["brackets_background"]))
if "brackets_foreground" in dct:
self.setMatchedBraceForegroundColor(QColor(dct["brackets_foreground"]))
if "selection" in dct:
self.setSelectionBackgroundColor(QColor(dct["selection"]))
if "background" in dct:
c = QColor(dct["background"])
self.resetFoldMarginColors()
self.setFoldMarginColors(c, c)
def increase_text_size(self):
self.text_size *= 2
self.gen_text()
def reduce_text_size(self):
if self.text_size == 1:
return
self.text_size //= 2
self.gen_text()
def gen_text(self):
content = Path(__file__).read_text()
while len(content) < self.text_size:
content *= 2
self.setText(content[:self.text_size])
if __name__ == '__main__':
app = QApplication(sys.argv)
view = View("python", "monokai")
view.setText(textwrap.dedent("""\
'''
Ctrl+1 = You'll decrease the size of existing text
Ctrl+2 = You'll increase the size of existing text
Warning: Check the window title to see how long it takes rehighlighting
'''
"""))
view.resize(800, 600)
view.show()
app.exec_()
要运行它,您需要安装:
QScintilla==2.10.8
Pygments==2.3.1
PyQt5==5.12
我正在尝试弄清楚如何在 QScintilla 小部件上使用 pygments,现在我需要解决的主要问题是处理非微型文档时的性能。
我希望编辑器在处理大型文档 (>=100kb) 时变得灵敏且可用,但我不太清楚我应该在这里采用什么方法。为了测试性能,您可以使用 Ctrl+1 或 Ctrl+2 并且小部件文本将减少/分别增加。
当我说“响应式”时,我的意思是visible screen 的高亮计算应该不再需要 [1-2]frame/highglight <=> [17-34]ms/highlight (假设为 60fps)因此在打字时您不会感到任何减速。
注意:正如您在上面的 mcve 中看到的,我已经包含了 pygments 分词器,因此您可以使用它......感觉为了实现“实时突出显示”我需要使用以某种聪明的方式进行内存/缓存,但我正在努力弄清楚我需要缓存的数据是什么以及缓存它的最佳方式是什么......:/
演示:
在上面的演示中,您可以看到使用这种天真的突出显示编辑器很快就会变得无法使用,在我的笔记本电脑中重新突出显示 32kb 的文本 block 仍然可以提供交互式帧率,但如果超过该值,编辑器将变得完全无法使用。
注意事项:
Alt+F3
时,您选择了光标下的所有事件引用资料:
以下文档并不特定于此特定问题,但它们讨论了缓存和语法突出显示的可能策略:
最佳答案
在 highlight_slow
中,您接收到 start
和 end
值,但您忽略了结束值。因此,无论何时键入单个字符,代码都会重新突出显示整个缓冲区的其余部分。这就是为什么如果您在长缓冲区的末尾键入,时间会非常快 - 大约 .1 - .2 毫秒 - 但如果您在开头键入,它会非常慢。
仅从正确突出显示的角度考虑,在大多数情况下(至少使用 Python),当您引入新字符时,只需重新设置当前行的样式。有时,例如如果您开始一个函数定义或打开一个括号,可能需要设置多行样式。只有当您打开或关闭多行 """
或 '''
字符串时,缓冲区的其余部分才需要重新设置样式。
如果您在日志记录中包含 start
和 end
,您会发现大多数情况下,当您键入它们时,它们跨越的范围非常小。如果您将 highlight_code
方法的一行从
code = view.text()[start:]
到
code = view.text()[start:end]
您会发现该方法现在几乎总是需要亚毫秒的时间,而且它几乎总是能正确突出显示。
据我所知,这只会在涉及多行引号时导致样式错误。但是,您当前的代码存在同样的问题:尝试打开多行字符串,键入回车,然后在下一行继续该字符串。第二行将突出显示为代码。 Qscintilla 通过提供不包括多行引号开头的 start
在这里让您有点误入歧途。不过,它并没有试图做到完美 - 文档说
In fact, QScintilla says: “Hey, I think you should restyle the text between the character at position start up to the character at position end“. You are completely free to ignore this suggestion.
正确处理多行引用会有点棘手!如果是我,并且我想让某些东西快速运行,我可能会实现击键来刷新整个缓冲区的突出显示,并在出现问题时使用它。
关于python - QScintilla 中的颜料,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55775837/
我想试用 QScintilla。所以我下载并安装了它,没问题。当我运行示例项目时,它说我缺少 QtCored4.dll,所以我将它复制到目录中,然后它说它还需要其他 dll,所以也复制了它们。 最后它
当我将 QsciScintilla 对象添加到主窗口时,水平滚动条处于事件状态并且超宽(大量明显的空白)。容易修复吗? 最佳答案 简单修复: sc.SendScintilla(sc.SCI_SETHS
我正在尝试为 QScintilla 创建一个基于 JavaScript 的自定义词法分析器。我已经弄清楚如何在词法分析器中添加关键字。但是,我不知道如何改变它们在输入时的外观,例如,当您输入单词 fu
我正在使用 C++ 开发源代码编辑器,使用 Qt5 和 QScintilla 作为框架。在这个项目中,我想连续显示文本光标的行和列(光标位置),所以我需要一个在移动文本光标时发出的信号。根据 QSci
考虑这个 mcve: import math import sys import textwrap import time from pathlib import Path from collecti
如果自动完成阈值设置为 1,我想在 html 文件的自动完成列表中显示所有属性和标签。我已经尝试使用此代码来使用 API,我在文件加载到新的 mdi child 后设置了此代码(子窗口)但它不工作:
我得到了这段简单的 mcve 代码: import sys import re from PyQt5 import QtGui, QtWidgets, QtCore from PyQt5.Qsci i
我得到了这个小 mcve 代码: import sys import re from PyQt5 import QtGui, QtWidgets, QtCore from PyQt5.QtCore i
我找到了这个演示 autocompletion-using-pyqt4-and-qscintilla 但是,有时会导致段错误。 这个演示是否正确? 最佳答案 要回答标题“如何使用 QScintilla
我正在尝试实现编辑器的快捷方式,但到目前为止还没有取得任何成功。 我想覆盖一些默认的 QScintilla 快捷键。我读过这篇answer ,但我不确定这是否有助于解决我的问题。 我还阅读了 Scin
我喜欢 PySide,并且已经使用了一段时间,但是在我目前正在编写的程序中,我需要一个高级代码编辑器。 我找到了 QScintilla ,但那是针对 PyQt 的。它与 PySide 兼容吗?如果兼容
我正在尝试在 QScintilla 中实现一个适用于多项选择的切换评论功能。不幸的是我不太清楚该怎么做,到目前为止我已经想出了这段代码: import sys import re import mat
考虑这个片段: import sys from PyQt5.Qsci import QsciScintilla from PyQt5.Qt import * if __name__ == '__mai
这里的最终目标是在 QScintilla 中实现基于缩进的代码折叠,类似于 SublimeText3 的方式。 首先,这里有一个小例子,说明如何使用 QScintilla 机制手动提供折叠: impo
我想防止在我的 QScintilla 小部件中编辑代码时触发应用程序键盘快捷键,就像普通的 QLineEdit 字段不会一样。 在下面的可执行示例代码中,不可能在 QScintilla 小部件中键入空
我正在努力告诉 QScitilla textEdit(我的 MainWindow 应用程序的主要小部件)接受在右键单击鼠标时显示个性化上下文菜单。 如果与 QScintilla 替代品一起使用,如果我
我最近一直在通过在我的 python 应用程序中实现 QsciLexerCustom 来创建我自己的 Progress 4GL 编辑器。但是在浏览 Qscintilla Source 时我注意到他们已
我正在尝试编写一个搜索框,其中包含查找/查找上一个/查找下一个以与 QScintilla textEditor 小部件进行交互。因此,我写了一些方法,一个用于突出显示所有匹配的单词然后选择第一次出现,
类似于这个问题:Creating and colorizing new constructs on a existing Scintilla lexer但我不想添加,而是想修改 pyqt4 中词法分析
我想让我的边距看起来像这样:- 到目前为止我已经这样做了:- 使用以下代码:- self.setMarginType(1,Qsci.QsciScintilla.NumberMargin) self.s
我是一名优秀的程序员,十分优秀!