gpt4 book ai didi

python - 如何在 QScintilla 中实现适用于多选的评论功能?

转载 作者:太空狗 更新时间:2023-10-30 00:01:12 26 4
gpt4 key购买 nike

我正在尝试在 QScintilla 中实现一个适用于多项选择的切换评论功能。不幸的是我不太清楚该怎么做,到目前为止我已经想出了这段代码:

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 Commenter():

def __init__(self, sci, comment_str):
self.sci = sci
self.comment_str = comment_str

def is_commented_line(self, line):
return line.strip().startswith(self.comment_str)

def toggle_comment_block(self):
sci = self.sci

line, index = sci.getCursorPosition()

if sci.hasSelectedText() and self.is_commented_line(sci.text(sci.getSelection()[0])):
self.uncomment_line_or_selection()
elif not self.is_commented_line(sci.text(line)):
self.comment_line_or_selection()
else:
start_line = line
while start_line > 0 and self.is_commented_line(sci.text(start_line - 1)):
start_line -= 1

end_line = line
lines = sci.lines()
while end_line < lines and self.is_commented_line(sci.text(end_line + 1)):
end_line += 1

sci.setSelection(start_line, 0, end_line, sci.lineLength(end_line))
self.uncomment_line_or_selection()
sci.setCursorPosition(line, index - len(self.comment_str))

def comment_line_or_selection(self):
sci = self.sci

if sci.hasSelectedText():
self.comment_selection()
else:
self.comment_line()

def uncomment_line_or_selection(self):
sci = self.sci

if sci.hasSelectedText():
self.uncomment_selection()
else:
self.uncomment_line()

def comment_line(self):
sci = self.sci

line, index = sci.getCursorPosition()
sci.beginUndoAction()
sci.insertAt(self.comment_str, line, sci.indentation(line))
sci.endUndoAction()

def uncomment_line(self):
sci = self.sci

line, index = sci.getCursorPosition()

if not self.is_commented_line(sci.text(line)):
return

sci.beginUndoAction()
sci.setSelection(
line, sci.indentation(line),
line, sci.indentation(line) + len(self.comment_str)
)
sci.removeSelectedText()
sci.endUndoAction()

def comment_selection(self):
sci = self.sci

if not sci.hasSelectedText():
return

line_from, index_from, line_to, index_to = sci.getSelection()
if index_to == 0:
end_line = line_to - 1
else:
end_line = line_to

sci.beginUndoAction()
for line in range(line_from, end_line + 1):
sci.insertAt(self.comment_str, line, sci.indentation(line))

sci.setSelection(line_from, 0, end_line + 1, 0)
sci.endUndoAction()

def uncomment_selection(self):
sci = self.sci

if not sci.hasSelectedText():
return

line_from, index_from, line_to, index_to = sci.getSelection()
if index_to == 0:
end_line = line_to - 1
else:
end_line = line_to

sci.beginUndoAction()
for line in range(line_from, end_line + 1):
if not self.is_commented_line(sci.text(line)):
continue

sci.setSelection(
line, sci.indentation(line),
line,
sci.indentation(line) + len(self.comment_str)
)
sci.removeSelectedText()

if line == line_from:
index_from -= len(self.comment_str)
if index_from < 0:
index_from = 0

if line == line_to:
index_to -= len(self.comment_str)
if index_to < 0:
index_to = 0

sci.setSelection(line_from, index_from, line_to, index_to)
sci.endUndoAction()


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)

# Use raw messages to Scintilla here
# (all messages are documented here: http://www.scintilla.org/ScintillaDoc.html)
# Ensure the width of the currently visible lines can be scrolled
self.SendScintilla(QsciScintilla.SCI_SETSCROLLWIDTHTRACKING, 1)
# Multiple cursor support
self.SendScintilla(QsciScintilla.SCI_SETMULTIPLESELECTION, True)
self.SendScintilla(QsciScintilla.SCI_SETMULTIPASTE, 1)
self.SendScintilla(
QsciScintilla.SCI_SETADDITIONALSELECTIONTYPING, True)

# Comment feature goes here
self.commenter = Commenter(self, "//")
QShortcut(QKeySequence("Ctrl+7"), self,
self.commenter.toggle_comment_block)


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()

相关的 Qscintilla 文档位于此处:

目前该功能仅支持单选/光标,而且评论方式非常丑陋。正如您在代码中看到的,如果您在按住鼠标的同时按下 ctrl,您将能够创建多个光标/选择。

虽然现在我不知道如何实现一些事情:

1) 我希望注释对齐良好,也就是说,它们应该以相同的缩进级别开始。现有功能现在会产生丑陋的未对齐评论,我称之为“良好对齐”评论的示例:

enter image description here

2) 现在只考虑一个光标/选择。如何循环游标/选择以应用 toggle_selection 函数?

enter image description here

enter image description here

3) 我想如果你循环选择结果会比在特定行中有偶数个光标不会注释该行(注释,取消注释),例如,像这样:

enter image description here

4) 特定行中的奇数个游标会影响该行,因为(注释、取消注释、注释),例如,如下所示:

enter image description here

5) 如果您遍历游标/选择,您最终会产生如下所示的输出。

enter image description here

编辑:第一稿

class Commenter():

def __init__(self, sci, comment_str):
self.sci = sci
self.comment_str = comment_str

def selections(self):
regions = []
for i in range(self.sci.SendScintilla(QsciScintilla.SCI_GETSELECTIONS)):
regions.append({
'begin': self.selection_start(i),
'end': self.selection_end(i)
})

return regions

def selection_start(self, selection):
return self.sci.SendScintilla(QsciScintilla.SCI_GETSELECTIONNSTART, selection)

def selection_end(self, selection):
return self.sci.SendScintilla(QsciScintilla.SCI_GETSELECTIONNEND, selection)

def text(self, *args):
return self.sci.text(*args)

def run(self):
send_scintilla = self.sci.SendScintilla

for region in self.selections():
print(region)
print(repr(self.text(region['begin'],region['end'])))

EDIT2:我发现我尝试实现的这个功能的源代码在 SublimeText Default.sublime-package(zip 文件)上可用,comments.py .该代码不仅支持普通注释//,还支持 block 注释/* ... */。主要问题是将该代码移植到 QScintilla 似乎非常棘手:/

最佳答案

QsciScintilla 似乎没有公开 Scintilla 的所有功能,但是通过 SendScintilla 我们可以访问它的其余部分,正如您似乎已经发现的那样。所以 Commenter 类可能看起来像这样(棘手的部分是恢复选择,因为 Scintilla 在插入时取消选择):

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 Commenter():

def __init__(self, sci, comment_str):
self.sci = sci
self.comment_str = comment_str
self.sel_regions = []

def toggle_comments(self):
lines = self.selected_lines()
if len(lines) <= 0:
return
all_commented = True
for line in lines:
if not self.sci.text(line).strip().startswith(self.comment_str):
all_commented = False
if not all_commented:
self.comment_lines(lines)
else:
self.uncomment_lines(lines)

def selections(self):
regions = []
for i in range(self.sci.SendScintilla(QsciScintilla.SCI_GETSELECTIONS)):
regions.append({
'begin': self.sci.SendScintilla(QsciScintilla.SCI_GETSELECTIONNSTART, i),
'end': self.sci.SendScintilla(QsciScintilla.SCI_GETSELECTIONNEND, i)
})

return regions

def selected_lines(self):
self.sel_regions = []
all_lines = []
regions = self.selections()
for r in regions:
start_line = self.sci.SendScintilla(QsciScintilla.SCI_LINEFROMPOSITION, r['begin'])
end_line = self.sci.SendScintilla(QsciScintilla.SCI_LINEFROMPOSITION, r['end'])
for cur_line in range(start_line, end_line + 1):
if not cur_line in all_lines:
all_lines.append(cur_line)
if r['begin'] <= r['end']:
self.sel_regions.append(r)
return all_lines

def comment_lines(self, lines):
indent = self.sci.indentation(lines[0])
for line in lines:
indent = min(indent, self.sci.indentation(line))
self.sci.beginUndoAction()
for line in lines:
self.adjust_selections(line, indent)
self.sci.insertAt(self.comment_str, line, indent)
self.sci.endUndoAction()
self.restore_selections()

def uncomment_lines(self, lines):
self.sci.beginUndoAction()
for line in lines:
line_start = self.sci.SendScintilla(QsciScintilla.SCI_POSITIONFROMLINE, line)
line_end = self.sci.SendScintilla(QsciScintilla.SCI_GETLINEENDPOSITION, line)
if line_start == line_end:
continue
if line_end - line_start < len(self.comment_str):
continue
done = False
for c in range(line_start, line_end - len(self.comment_str) + 1):
source_str = self.sci.text(c, c + len(self.comment_str))
if(source_str == self.comment_str):
self.sci.SendScintilla(QsciScintilla.SCI_DELETERANGE, c, len(self.comment_str))
break
self.sci.endUndoAction()

def restore_selections(self):
if(len(self.sel_regions) > 0):
first = True
for r in self.sel_regions:
if first:
self.sci.SendScintilla(QsciScintilla.SCI_SETSELECTION, r['begin'], r['end'])
first = False
else:
self.sci.SendScintilla(QsciScintilla.SCI_ADDSELECTION, r['begin'], r['end'])

def adjust_selections(self, line, indent):
for r in self.sel_regions:
if self.sci.positionFromLineIndex(line, indent) <= r['begin']:
r['begin'] += len(self.comment_str)
r['end'] += len(self.comment_str)
elif self.sci.positionFromLineIndex(line, indent) < r['end']:
r['end'] += len(self.comment_str)



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)

# Use raw messages to Scintilla here
# (all messages are documented here: http://www.scintilla.org/ScintillaDoc.html)
# Ensure the width of the currently visible lines can be scrolled
self.SendScintilla(QsciScintilla.SCI_SETSCROLLWIDTHTRACKING, 1)
# Multiple cursor support
self.SendScintilla(QsciScintilla.SCI_SETMULTIPLESELECTION, True)
self.SendScintilla(QsciScintilla.SCI_SETMULTIPASTE, 1)
self.SendScintilla(
QsciScintilla.SCI_SETADDITIONALSELECTIONTYPING, True)

# Comment feature goes here
self.commenter = Commenter(self, "//")
QShortcut(QKeySequence("Ctrl+7"), self,
self.commenter.toggle_comments)


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()

关于python - 如何在 QScintilla 中实现适用于多选的评论功能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50355919/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com