战前准备
github
以及它的访问权限- 强大的心理支撑
- 一点点英文水平(
至少会念ABC) - 会写测试样例
- 面向对象要学好(
没学真的会感到是在地狱)
使用环境
python 3.8.0
pyqt 5.15.9
pycharm 2021.3
基础类
基本上在这两个类反复横跳
QsciScintilla
QTextEdit
手册或者参考
QTextEdit
的应用
这是我的小小爹
QsciScintilla
的应用
这是大爹但又没完全是
QTextEdit
跟着github
上的代码走就没问题…
import sys
from PyQt5.QtCore import QRegExp
from PyQt5.QtGui import QColor, QTextCharFormat, QFont, QSyntaxHighlighter
## 不用QS的...
def format(color, style=''):
"""
Return a QTextCharFormat with the given attributes.
"""
_color = QColor()
if type(color) is not str:
_color.setRgb(color[0], color[1], color[2])
else:
_color.setNamedColor(color)
_format = QTextCharFormat()
_format.setForeground(_color)
if 'bold' in style:
_format.setFontWeight(QFont.Bold)
if 'italic' in style:
_format.setFontItalic(True)
return _format
STYLES = {
'header': format([0, 128, 0]), # 头文件
'd_header': format([60,179,113]), # 头文件
'keyword': format('#2E8B57', 'bold'),
'operator': format([255, 140, 0]), # 运算符
'brace': format([255, 140, 0]), # 符号
'defclass': format([0, 80, 50], 'bold'), # 类名
'string': format([132, 26, 138]), # 字符串
'string2': format([132, 26, 138]), # 字符串
'comment': format([107, 147, 186]), # 注释
'self': format([150, 85, 140], 'italic'), # 自身
'numbers': format([42, 0, 255]), # 数字
'constant': format([202, 0, 202], 'bold'), # 常量
'deprecated': format([123, 23, 43], 'bold underline'), # 弃用的成员
'enums': format([128, 0, 255]), # 枚举
'fields': format([128, 0, 128]), # 变量
'return': format([255, 0, 85], 'bold'), # return关键字
'method_decl': format([255, 128, 64], 'bold'), # 方法定义
'method': format([0, 48, 96]), # 方法
'others': format([78, 123, 0]), # 其他
'static_fields': format([33, 0, 189], 'bold'), # 静态变量
}
class CppHighlighter(QSyntaxHighlighter):
"""Syntax highlighter for the C/C++ language.
"""
# header
header = [
'stdio.h', 'stdlib.h', 'math.h', 'string.h', 'time.h', 'ctype.h',
'stdbool.h', 'assert.h', 'limits.h', 'float.h', 'stddef.h', 'errno.h',
'signal.h', 'setjmp.h', 'stdarg.h', 'locale.h', 'wchar.h', 'time.h',
'unistd.h', 'fcntl.h', 'sys/types.h', 'sys/stat.h', 'dirent.h',
'pthread.h', 'semaphore.h', 'sys/socket.h', 'netinet/in.h',
'arpa/inet.h', 'netdb.h', 'sys/time.h', 'sys/wait.h'
]
# C/C++ keywords
keywords = [
'auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do',
'double', 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', 'int',
'long', 'register', 'return', 'short', 'signed', 'sizeof', 'static',
'struct', 'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile',
'while', 'bool', 'catch', 'class', 'const_cast', 'delete', 'dynamic_cast',
'explicit', 'false', 'friend', 'inline', 'mutable', 'namespace', 'new',
'operator', 'private', 'protected', 'public', 'reinterpret_cast',
'static_cast', 'template', 'this', 'throw', 'true', 'try', 'typeid',
'typename', 'using', 'virtual', 'wchar_t',
]
# C/C++ operators
operators = [
'=',
# Comparison
'==', '!=', '<', '<=', '>', '>=',
# Arithmetic
'\+', '-', '\*', '/', '//', '\%', '\*\*',
# In-place
'\+=', '-=', '\*=', '/=', '\%=',
# Bitwise
'\^', '\|', '\&', '\~', '>>', '<<',
]
# C/C++ braces
braces = [
'\{', '\}', '\(', '\)', '\[', '\]',
]
def __init__(self, document):
QSyntaxHighlighter.__init__(self, document)
rules = []
# Header rules: #include<XX.h>
header_rules1 = [('#include\s*<{}>'.format(a), 0, STYLES['header'])
for a in CppHighlighter.header]
# Header rules: #include "XXX.h"
header_rules2 = [(r'#include\s*"([^"]+)"', 0, STYLES['d_header']),]
rules += [(r'\b%s\b' % w, 0, STYLES['keyword'])
for w in CppHighlighter.keywords]
# print(rules)
rules += [(r'%s' % o, 0, STYLES['operator'])
for o in CppHighlighter.operators]
rules += [(r'%s' % b, 0, STYLES['brace'])
for b in CppHighlighter.braces]
# All other rules
rules += [
# 'self'
(r'\bself\b', 0, STYLES['self']),
# Double-quoted string, possibly containing escape sequences
(r'"[^"\\]*(\\.[^"\\]*)*"', 0, STYLES['string']),
# Single-quoted string, possibly containing escape sequences
(r"'[^'\\]*(\\.[^'\\]*)*'", 0, STYLES['string2']),
# 'def' followed by an identifier
(r'\bdef\b\s*(\w+)', 1, STYLES['defclass']),
# 'class' followed by an identifier
(r'\bclass\b\s*(\w+)', 1, STYLES['defclass']),
# From '#' until a newline
(r'#[^\n]*', 0, STYLES['comment']),
# Numeric literals
(r'\b[+-]?[0-9]+[lL]?\b', 0, STYLES['numbers']),
(r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b', 0, STYLES['numbers']),
(r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0,
STYLES['numbers']),
]
rules += header_rules1
rules += header_rules2
self.rules = [(QRegExp(pat), index, fmt)
for (pat, index, fmt) in rules]
def highlightBlock(self, text):
for expression, nth, format in self.rules:
index = expression.indexIn(text, 0)
while index >= 0:
# We actually want the index of the nth match
index = expression.pos(nth) + expression.cap(nth).index(expression.cap(nth))
length = len(expression.cap(nth))
self.setFormat(index, length, format)
index = expression.indexIn(text, index + length)
self.setCurrentBlockState(0)
if __name__ == "__main__":
from PyQt5 import QtWidgets
app = QtWidgets.QApplication([])
editor = QtWidgets.QPlainTextEdit()
editor.setStyleSheet("""QPlainTextEdit{
font-family:'Consolas';
background-color: rgb(204,232,207);}""")
highlight = CppHighlighter(editor.document())
editor.show()
# Load sample.cpp into the editor for demo purposes
infile = open('your_file_path', 'r', encoding='utf-8')
editor.setPlainText(infile.read())
app.exec_()
QsciScintilla
全部重写
你没看错全部重写…
QsciScintilla
类重写动作
class MeQsciScintilla(QsciScintilla):
# 继承编辑器类 重写键盘按键方法
def __init__(self, parent=None):
super(MeQsciScintilla, self).__init__(parent)
def keyPressEvent(self, e):
''' 测试按下按键 '''
if e.key() == Qt.Key_Escape:
pass
super().keyPressEvent(e)
def wheelEvent(self, e):
''' Ctrl + 滚轮 控制字体缩放 '''
if e.modifiers() == Qt.ControlModifier:
da = e.angleDelta()
if da.y() > 0:
self.zoomIn(1) # QsciScintilla 自带缩放
elif da.y() < 0:
self.zoomOut(1)
else:
super().wheelEvent(e)
LexerCustom
类重写高亮和自动补全
class MeLexer(QsciLexerCustom):
def __init__(self, parent):
super(MeLexer, self).__init__(parent)
# 父类是编辑器
# 设置默认颜色
# 设置默认背景
# 设置默认字号
self.setDefaultColor(QColor("#ff000000"))
self.setDefaultPaper(QColor("#CCE8CF")) # 背景 豆沙绿
self.setDefaultFont(QFont("Consolas", 13))
# 样式表 0-1-2-3-4-5
# 0: 关键字 1: 运算符 2: 格式符 3: 数字 4: 默认 5: 注释
# 颜色
self.setColor(QColor("#3CB371"), 0)
self.setColor(QColor("#6A5ACD"), 1)
self.setColor(QColor("#20B2AA"), 2)
self.setColor(QColor("#4169E1"), 3)
self.setColor(QColor("#2D7C7F"), 4)
self.setColor(QColor("#C0C0C0"), 5)
# 字体 consolas DevC++的默认字体 字号自定
self.setFont(QFont("Consolas", 13, weight=QFont.Bold), 0)
self.setFont(QFont("Consolas", 13, weight=QFont.Bold), 1)
self.setFont(QFont("Consolas", 13, weight=QFont.Bold), 2)
self.setFont(QFont("Consolas", 13, weight=QFont.Bold), 3)
self.setFont(QFont("Consolas", 13, weight=QFont.Bold), 4)
self.setFont(QFont("Consolas", 13), 5)
self.font(5).setItalic(True)
# 定义关键词列表
self.keywords_list = [
'auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do',
'double', 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', 'int',
'long', 'register', 'return', 'short', 'signed', 'sizeof', 'static',
'struct', 'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile',
'while', 'bool', 'catch', 'class', 'const_cast', 'delete', 'dynamic_cast',
'explicit', 'false', 'friend', 'inline', 'mutable', 'namespace', 'new',
'operator', 'private', 'protected', 'public', 'reinterpret_cast',
'static_cast', 'template', 'this', 'throw', 'true', 'try', 'typeid',
'typename', 'using', 'virtual', 'wchar_t', 'include', 'std',
"byte", "word", "dword",
"int8_t", "uint8_t", "int16_t", "uint16_t",
"int32_t", "uint32_t", "int64_t", "uint64_t",
"int8", "uint8", "int16", "uint16",
"int32", "uint32", "int64", "uint64"
]
# 定义运算符列表
self.operator_list = [
'=',
# Comparison
'==', '!=', '<', '<=', '>', '>=',
# Arithmetic
'+', '-', '*', '/', '%',
# In-place
'+=', '-=', '*=', '/=', '%=',
# Bitwise
'^', '|', '&', '~', '>>', '<<', '"', '%s', '%f', '%d', '%ld'
]
# 定义格式符列表
self.format_list = [
'{', '}', '(', ')', '[', ']', '#', ';', ','
]
def description(self, style):
if style == 0:
return "keyword_style"
elif style == 1:
return "operate_style"
elif style == 2:
return "format_style"
elif style == 3:
return "number_style"
elif style == 4:
return "default_style"
elif style == 5:
return "tips_style"
### 无需返回值 但需要定义内容
return ""
def styleText(self, start, end):
# 1. 初始化风格类
self.startStyling(start)
# 2. 切片数据
text = self.parent().text()[start:end]
# 3. 词法分析
p = re.compile(r"\*\/|\/\*|//.*?(?=\r?\n|$)|\s+|\w+|\W") # // and /**/
# 关键词列表里是这样的元组 (token_name, token_len) :(关键词内容,关键词长度)
token_list = [(token, len(bytearray(token, "utf-8"))) for token in p.findall(text)]
# 4. 风格化
# 4.1 分支
multiline_comm_flag = False
editor = self.parent()
if start > 0:
previous_style_nr = editor.SendScintilla(editor.SCI_GETSTYLEAT, start - 1)
if previous_style_nr == 3:
multiline_comm_flag = True
# 4.2 循环风格化
for i, token in enumerate(token_list):
if multiline_comm_flag:
# 处于块注释状态,使用样式5进行风格化
self.setStyling(token[1], 5)
if token[0] == "*/":
multiline_comm_flag = False
elif token[0].startswith("//"):
line_number = self.parent().SendScintilla(self.parent().SCI_LINEFROMPOSITION, start)
line_start = self.parent().SendScintilla(self.parent().SCI_POSITIONFROMLINE, line_number)
line_end = self.parent().SendScintilla(self.parent().SCI_GETLINEENDPOSITION, line_number)
self.startStyling(line_start)
self.setStyling(line_end - line_start + 1, 5)
break # 结束循环,不再继续处理该行后面的内容
else:
# 其他情况根据关键词、运算符、格式符、数字进行风格化
if token[0] in self.keywords_list:
self.setStyling(token[1], 0)
elif token[0] in self.operator_list:
self.setStyling(token[1], 1)
elif token[0] in self.format_list:
self.setStyling(token[1], 2)
elif token[0].isdigit():
self.setStyling(token[1], 3)
elif token[0] == "/*":
multiline_comm_flag = True
self.setStyling(token[1], 5)
else:
self.setStyling(token[1], 4)
TextEditorWidget
类,重写QWidget
类,隐藏上面两个重写类,并写交互接口
class TextEditorWidget(QWidget):
gotoDeclarationSign = QtCore.pyqtSignal()
gotoDefinitionSign = QtCore.pyqtSignal()
gotoCallExpressSign = QtCore.pyqtSignal()
def __init__(self, filename, filepath):
super(TextEditorWidget, self).__init__(parent=None)
# 配置
config_obj = Config()
self.config_ini = config_obj.read_config()
# 可访问成员变量
self.filename = filename
self.filepath = filepath
self.status = False
# 创建布局
self.__layout = QVBoxLayout(self)
self.__frame = QFrame(self)
self.__frameLayout = QVBoxLayout(self.__frame)
self.__layout.addWidget(self.__frame)
# 创建编辑器
self.__editor = MeQsciScintilla(self.__frame)
# 这里设置自定义词法解析器CPP
self.__lexer = MeLexer(self.__editor)
# 配置MeLexer 里面有自定义的高亮风格
self.__editor.setLexer(self.__lexer)
# 设置自动补全敏感字数
self.__editor.setAutoCompletionThreshold(1)
self.__editor.setAutoCompletionSource(QsciScintilla.AcsAll)
# 设置自动补全对象
self.__api = QsciAPIs(self.__lexer)
# 设置自动补全敏感
self.__editor.setAutoCompletionCaseSensitivity(True)
# 设置自动补全替换
self.__editor.setAutoCompletionReplaceWord(True)
# 设置自动填充
self.__editor.setAutoCompletionFillupsEnabled(True)
# 显示全部调用 不受上下文限制
self.__editor.setCallTipsStyle(QsciScintilla.CallTipsNoContext)
# 自动补全选项在下面
self.__editor.setCallTipsPosition(QsciScintilla.CallTipsBelowText)
self.__editor.setCallTipsVisible(0)
# 菜单
# 设置默认菜单为自定义菜单
self.__editor.setContextMenuPolicy(Qt.CustomContextMenu)
# 设置触发
self.__editor.customContextMenuRequested.connect(self.show_context_menu)
autocompletions = [
'include', 'using', 'namespace', 'std',
'scanf', 'printf', 'return', 'char', '{}',
'[]', '()', 'int', 'double', 'long', 'float',
'string', 'endl', 'stdio.h', 'stdlib.h', 'iostream', '<>',
'free', 'malloc', 'new', 'delete', 'public', 'private', 'protected',
'cin', 'cout', 'for', 'while', 'do', 'const', 'continue', 'break', 'if', 'else',
'auto', 'signed', 'short', 'case', 'try', 'catch', 'switch', 'default',
'true', 'false', 'struct', 'typedef', 'goto', 'sizeof', 'void', 'static', 'union',
'enum', 'inline', 'extern', 'throw', 'bool', 'class', 'template', 'this', 'vector',
'math.h', 'abs', 'strcat', 'strcmp', 'strlen', 'strcpy', 'strchr', 'strstr', 'rand',
'exit', 'time.h', 'string.h', 'ctype.h', 'isdigit', 'isalpha', 'isblank', 'isalnum',
'getchar', 'fopen', 'fflush', 'fclose', 'remove', 'fprintf', 'puts', 'abort', 'ctime'
]
for ac in autocompletions:
self.__api.add(ac)
self.__api.prepare()
self.__editor.setCallTipsBackgroundColor(QColor('#D8BFD8'))
# 设置自动补全字体颜色
self.__editor.setCallTipsForegroundColor(QColor('#F08080'))
# utf-8
self.__editor.setUtf8(True)
# 将编辑器添加到布局中
self.__frameLayout.addWidget(self.__editor)
# 细节
# 设置背景色
self.__editor.setPaper(QColor("#CCE8CF"))
# 显示自动换行
self.__editor.setWrapMode(QsciScintilla.WrapWord)
self.__editor.setWrapVisualFlags(QsciScintilla.WrapFlagByText)
self.__editor.setWrapIndentMode(QsciScintilla.WrapIndentIndented)
# 使用tab
self.__editor.setIndentationsUseTabs(True)
# 设置换行符长度4
self.__editor.setTabWidth(4)
# 设置Tab自动对齐
self.__editor.setAutoIndent(True)
# 设置鼠标光标颜色 前景色...
self.__editor.setCaretForegroundColor(QColor("#0000CD"))
# 设置选中行颜色
self.__editor.setCaretLineVisible(True)
self.__editor.setCaretLineBackgroundColor(QColor("#AAEDCB"))
# 行号/页边距颜色
# 显示行号 行号范围
self.__editor.setMarginLineNumbers(1, True)
self.__editor.setMarginWidth(1, '0000')
self.__editor.setMarginsForegroundColor(QColor("#006400"))
# 默认未修改
self.__editor.setModified(False)
def show_context_menu(self, point):
self.context_menu = self.__editor.createStandardContextMenu()
# 添加默认选项
self.context_menu.insertSeparator(self.context_menu.actions()[0])
ui_icon = self.config_ini['main_project']['project_name'] + self.config_ini['ui_img']['ui_turn_to']
action_goto_declaration = QAction("转到声明", self)
action_goto_declaration.setIcon(QIcon(ui_icon))
action_goto_declaration.triggered.connect(self.gotoDeclaration)
action_goto_definition = QAction("转到定义", self)
action_goto_definition.setIcon(QIcon(ui_icon))
action_goto_definition.triggered.connect(self.gotoDefinition)
action_goto_call_express = QAction("转到调用", self)
action_goto_call_express.setIcon(QIcon(ui_icon))
action_goto_call_express.triggered.connect(self.gotoCallExpress)
# 分隔符
self.context_menu.insertSeparator(self.context_menu.actions()[0])
self.context_menu.insertAction(self.context_menu.actions()[0], action_goto_declaration)
self.context_menu.insertAction(self.context_menu.actions()[1], action_goto_definition)
self.context_menu.insertAction(self.context_menu.actions()[2], action_goto_call_express)
# 应用
self.context_menu.exec_(self.__editor.mapToGlobal(point))
def gotoDeclaration(self):
self.gotoDeclarationSign.emit()
def gotoDefinition(self):
self.gotoDefinitionSign.emit()
def gotoCallExpress(self):
self.gotoCallExpressSign.emit()
def highlight_function_declaration(self, positions):
# 传入的是整个位置数据....
indicator_number = 1 # 指示器的编号
lines = self.__editor.lines() - 1
indexs = self.__editor.lineLength(lines)
indicator_color = QColor('#f05b72') # 蔷薇色
if positions:
self.highlight_handle(positions, lines, indexs, indicator_number, indicator_color)
def highlight_function_definition(self, positions):
# 传入的是整个位置数据....
indicator_number = 2 # 指示器的编号
lines = self.__editor.lines() - 1
indexs = self.__editor.lineLength(lines)
indicator_color = QColor('#ed1941') # 赤色
if positions:
self.highlight_handle(positions, lines, indexs, indicator_number, indicator_color)
def highlight_function_call_express(self, positions):
# 传入的是整个位置数据....
indicator_number = 3 # 指示器的编号
lines = self.__editor.lines() - 1
indexs = self.__editor.lineLength(lines)
indicator_color = QColor('#f47920') # 橙色
if positions:
self.highlight_handle(positions, lines, indexs, indicator_number, indicator_color)
def highlight_handle(self, positions, lines, indexs, indicator_number, indicator_color):
self.__editor.SendScintilla(QsciScintilla.SCI_SETINDICATORCURRENT, indicator_number)
# 清除所有指示器的色块填充
for i in range(1, 4):
self.__editor.clearIndicatorRange(0, 0, lines, indexs, i)
self.__editor.SendScintilla(QsciScintilla.SCI_INDICATORCLEARRANGE, 0,
self.__editor.SendScintilla(QsciScintilla.SCI_GETLINECOUNT))
for start_line, start_index, end_line, end_index in positions:
self.__editor.SendScintilla(QsciScintilla.SCI_INDICSETSTYLE, indicator_number,
QsciScintilla.INDIC_CONTAINER)
self.__editor.SendScintilla(QsciScintilla.SCI_INDICSETFORE, indicator_number,
indicator_color)
self.__editor.fillIndicatorRange(start_line, start_index, end_line, end_index, indicator_number)
self.__editor.setCursorPosition(end_line, end_index)
# 获取选中位置文本 返回位置和一模一样文本
def getSelected_Position_Content(self):
if self.__editor.getSelection() != (-1, -1, -1, -1):
selected_text = self.__editor.selectedText()
start_line = self.__editor.SendScintilla(Qsci.QsciScintilla.SCI_LINEFROMPOSITION,
self.__editor.SendScintilla(
Qsci.QsciScintilla.SCI_GETSELECTIONSTART)) # 设置起始行号为当前选中文本所在行
start_index = self.__editor.SendScintilla(Qsci.QsciScintilla.SCI_GETCOLUMN, self.__editor.SendScintilla(
Qsci.QsciScintilla.SCI_GETSELECTIONSTART)) # 设置起始索引为当前选中文本的起始位置
end_line = self.__editor.SendScintilla(Qsci.QsciScintilla.SCI_LINEFROMPOSITION, self.__editor.SendScintilla(
Qsci.QsciScintilla.SCI_GETSELECTIONEND)) # 设置结束行号为当前选中文本的结束行
end_index = self.__editor.SendScintilla(Qsci.QsciScintilla.SCI_GETCOLUMN, self.__editor.SendScintilla(
Qsci.QsciScintilla.SCI_GETSELECTIONEND)) # 设置结束索引为当前选中文本的结束位置
return [(start_line, start_index, end_line, end_index)], selected_text
def getSelectdFunctionName(self, input_string):
import re
pattern = r'\b(\w+)\s*\('
match = re.search(pattern, input_string)
if match:
return match.group(1)
words = re.findall(r'\b\w+\b', input_string) # 提取字符串中的单词列表
for word in words:
if re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', word): # 判断单词是否符合函数名的命名规则
return word # 返回第一个符合要求的单词作为函数名
return None
# 这里是添加内容
def addText(self, content):
# 防止有憨憨放列表进来...
input_content = ''
if isinstance(content, list):
if '\r' or '\n' in content:
input_content = ''.join(content)
else:
input_content = '\n'.join(content)
elif isinstance(content, str):
input_content = content
self.__editor.setText(input_content)
# 得到当前文本
def getText(self):
content = self.__editor.text()
content = content.replace('\r', '')
return content
# 得到编辑器当前状态
def getStatus(self):
# bool
status = self.__editor.isModified()
return status
# 修改当前编辑器状态
def changeStatus(self, flag):
self.__editor.setModified(flag)
# 得到搜索结果....
def search_interface(self, keyword, *state):
"""
obj = QsciScintilla()
flag = obj.findFirst(self,expr,re,cs,wo,wrap,forward,line,index,show,posix,cxx11)->bool
:param keyword: 你的关键词
:param state: 元组(***)
tips -> [虽然是 Any 但默认写 bool]
expr: Any, --> 匹配词 keyword
re: Any, --> 是否使用正则表达式匹配 -> 默认向后搜索 regexp
cs: Any, --> 是否区分大小写匹配
wo: Any, --> 是否匹配整个关键词 --> 不需要完整匹配 --> False
wrap: Any, --> 是否在匹配结束之后回到搜索起点 --> 默认是 True
forward: bool, --> 向前搜索: False, 向后搜索: True; 一般是True即向后搜索... 这里注意不要搞反
line: -1, --> 搜索的起始行号 表示从当前行开始... 0~N
index: -1, --> 搜索的起始索引 表示从当前行的当前光标开始 0~N
show: True, --> 是否显示搜索结果 默认高亮...显示
posix: False, --> 是否使用POSIX正则表达式匹配 默认False
username: False, --> 是否用用户名模式匹配 我的用户名: cxx11
:return: bool --> 返回一个布尔值 表明是否找到...
"""
return self.__editor.findFirst(keyword, *state) # bool
def search_interface_(self):
# 直接找下一个....
return self.__editor.findNext()
def send_signal(self, parameter1, parameter2=None):
v1 = getattr(self.__editor, parameter1)
if parameter2 is None:
return self.__editor.SendScintilla(v1)
else:
return self.__editor.SendScintilla(v1, parameter2)
def send_signal_(self, item1, item2):
item3 = self.__editor.SendScintilla(item2)
return self.__editor.SendScintilla(item1, item3)
# 必须搭配findFirst()/findNext()食用 在目标被选中的前提下替换
def replace_interface(self, keywords):
self.__editor.replace(keywords)
def moveCursor(self, line, index):
self.__editor.setCursorPosition(line, index)
def highlight_text(self, positions):
start_line, start_index, end_line, end_index = positions
self.__editor.setSelectionBackgroundColor(QColor('#4169E1')) # 蓝
self.__editor.setSelectionForegroundColor(QColor('#FF8C00')) # 橘
self.__editor.setSelection(start_line, start_index, end_line, end_index)
def multi_highlight_text(self, positions):
indicator_number = 1 # 指示器的编号
lines = self.__editor.lines() - 1
indexs = self.__editor.lineLength(lines)
self.__editor.SendScintilla(QsciScintilla.SCI_SETINDICATORCURRENT, indicator_number)
self.__editor.clearIndicatorRange(0, 0, lines, indexs, indicator_number)
for start_line, start_index, end_line, end_index in positions:
self.__editor.SendScintilla(QsciScintilla.SCI_INDICSETSTYLE, indicator_number,
QsciScintilla.INDIC_CONTAINER)
self.__editor.SendScintilla(QsciScintilla.SCI_INDICSETFORE, indicator_number,
QColor('#4169E1'))
self.__editor.fillIndicatorRange(start_line, start_index, end_line, end_index, indicator_number)
def clear_all_indicator_sign(self):
indicator_number = 1 # 指示器的编号
lines = self.__editor.lines() - 1
indexs = self.__editor.lineLength(lines)
self.__editor.clearIndicatorRange(0, 0, lines, indexs, indicator_number)
def getSelectionState(self):
return self.__editor.getSelection()
def getCursorLocation(self):
return self.__editor.getCursorPosition()
- 在
QtabWidget
里使用TextEditorWidget
类
def openfile(self):
test_path = self.config_ini["main_project"]["project_name"] + self.config_ini["test"]["folder_path"]
fileName, isOk = QFileDialog.getOpenFileName(self, "选取文件", test_path, "C/C++源文件 (*.c *.cpp)")
path = ''
name = ''
if fileName:
flag = self.main_detector(fileName)
else:
message_ = CustomMessageBox(icon=QIcon(self.ui_icon),title='提示',text='您没有选择文件!')
message_.exec_()
return
if flag:
if isOk:
path, name = split(fileName)
if path:
text_editor_obj = TextEditorWidget(filename=name, filepath=path)
text_editor_obj.addText(content=content)
self.ui.text_editor.addTab(text_editor_obj, text_editor_obj.filename)
self.ui.text_editor.setCurrentWidget(text_editor_obj)
查找替换
因为那个重写的QsciScintilla
类里有自带的查找findFirst
、替换函数replace
,但是它并没有说明书,所以又遇到对手了。接下来就需要我们学会自己克服困难,比如说把参数爬下来,丢给大爹让它分析,然后我再自己写一些特殊逻辑。
提前说明:
findFirst
这个函数有多个参数,传入的方式除了关键词之外,都是bool
值,我分析得到的结果如下:
def search_interface(self, keyword, *state):
"""
obj = QsciScintilla()
flag = obj.findFirst(self,expr,re,cs,wo,wrap,forward,line,index,show,posix,cxx11)->bool
:param keyword: 你的关键词
:param state: 元组(***)
tips -> [虽然是 Any 但默认写 bool]
expr: Any, --> 匹配词 keyword
re: Any, --> 是否使用正则表达式匹配 -> 默认向后搜索 regexp
cs: Any, --> 是否区分大小写匹配
wo: Any, --> 是否匹配整个关键词 --> 不需要完整匹配 --> False
wrap: Any, --> 是否在匹配结束之后回到搜索起点 --> 默认是 True
forward: bool, --> 向前搜索: False, 向后搜索: True; 一般是True即向后搜索... 这里注意不要搞反
line: -1, --> 搜索的起始行号 表示从当前行开始... 0~N
index: -1, --> 搜索的起始索引 表示从当前行的当前光标开始 0~N
show: True, --> 是否显示搜索结果 默认高亮...显示
posix: False, --> 是否使用POSIX正则表达式匹配 默认False
username: False, --> 是否用用户名模式匹配 我的用户名: cxx11
:return: bool --> 返回一个布尔值 表明是否找到...
"""
return self.__editor.findFirst(keyword, *state) # bool
replace
这个是必须在选中的情况下使用,首先用findFirst
查找选中关键词,让这些关键词处于选中状态,然后直接用self.__editor.replace(replace_word)
就自动替换完成,注意是选中状态有几个替换几个。
参数说明
区分大小写:就是根据大小写来匹配,如果输入一个全部大写的是不会匹配到小写内容的。
全部匹配:就是必须严格是那个关键词,res
只能匹配res
不能匹配res1
正则表达式匹配: 根据你输入的关键词得到那个关键词的正则表达式,根据它再查找匹配其他字符。
是否回到搜索起点:默认搜索起点是光标停留处,若不回到就不会循环查找/替换光标范围之外的。
向前/先后搜索:相对于光标停留位置向前或者向后,True
是向后
搜索行号:默认-1 默认从第一行,可以自定义,0~N-1
搜索下标:默认-1 默认第一行第一个开始,可以自定义,0~N-1
默认返回值是bool
,代表在特定位置上是否找到
范围书写
这里由于替换很简单,只写搜索
替换就是在搜索基础上替换
全部搜索:
def search_all_string(self):
self.select_keywords_pos.clear()
self.keywords_pos.clear()
# 获取输入
input_string = self.ui.input_s.currentText()
# 判断重复和大小写
current_tab = self.father.currentWidget()
if input_string and current_tab:
# 太棒了! 我逐渐理解一切。
# 清空矛盾
self.select_keywords_pos.clear()
self.keywords_pos.clear()
# 关键词
positions = set() # 存储匹配的位置
line = 0 # 设置起始行号为0
index = 0 # 设置起始索引为0
count = 0 # 匹配次数计数器
forward = self.isfoward()
while True:
state = (
self.ui.re_s.isChecked(), # regexp
self.ui.cs_s.isChecked(), # cs
self.ui.wo_s.isChecked(), # wo
False, # 回到开始?
forward, # 向前向后?
line, # 行
index, # 下标
True, False, False)
flag = current_tab.search_interface(input_string, *state)
if not flag:
break
found_pos = current_tab.send_signal(parameter1='SCI_GETCURRENTPOS', parameter2=None)
found_line = current_tab.send_signal(parameter1='SCI_LINEFROMPOSITION', parameter2=found_pos)
found_index = found_pos - current_tab.send_signal(parameter1='SCI_POSITIONFROMLINE',
parameter2=found_line) - 1
if len(input_string) > 1:
positions.add(
(
found_line, found_index - len(input_string) + 1, found_line,
found_index + 1)) # 记录匹配的位置(行号和索引)
else:
positions.add(
(found_line, found_index, found_line, found_index + len(input_string))) # 记录匹配的位置(行号和索引)
count += 1
line = found_line
index = found_index + len(input_string)
self.keywords_pos = list(positions)
current_tab.multi_highlight_text(self.keywords_pos)
# self.ui.msg1 && self.ui.msg2
self.ui.msg1.setText(f"共搜索到关键词: '{
input_string}' {
count}次!")
选中搜索
def search_select_string(self):
# 点击之前先清空一切阻碍
self.select_keywords_pos.clear()
self.keywords_pos.clear()
# 获取输入
input_string = self.ui.input_s.currentText()
current_tab = self.father.currentWidget()
if current_tab.getSelectionState() == (-1, -1, -1, -1):
message_box = CustomMessageBox(icon=QIcon(self.ui_icon), title='提示', text='请先选中一个区域!')
message_box.exec_()
elif input_string and current_tab.getSelectionState() != (-1, -1, -1, -1):
start_line = current_tab.send_signal_(Qsci.QsciScintilla.SCI_LINEFROMPOSITION,
Qsci.QsciScintilla.SCI_GETSELECTIONSTART)
start_index = current_tab.send_signal_(Qsci.QsciScintilla.SCI_GETCOLUMN,
Qsci.QsciScintilla.SCI_GETSELECTIONSTART)
end_line = current_tab.send_signal_(Qsci.QsciScintilla.SCI_LINEFROMPOSITION,
Qsci.QsciScintilla.SCI_GETSELECTIONEND)
end_index = current_tab.send_signal_(Qsci.QsciScintilla.SCI_GETCOLUMN,
Qsci.QsciScintilla.SCI_GETSELECTIONEND)
# 存储匹配的位置
positions = set()
count = 0
current_line = start_line
current_index = start_index
while True:
if current_line > end_line:
break
state = (
self.ui.re_s.isChecked(), # regexp
self.ui.cs_s.isChecked(), # cs
self.ui.wo_s.isChecked(), # wo
False, # 回到开始?
self.isfoward(), # 向前向后?
current_line, # 行
current_index, # 下标
True, False, False)
if (current_index >= end_index and current_line == end_line) or (current_line > end_line):
break
flag = current_tab.search_interface(input_string, *state)
if flag:
found_pos = current_tab.send_signal(parameter1='SCI_GETCURRENTPOS', parameter2=None)
found_line = current_tab.send_signal(parameter1='SCI_LINEFROMPOSITION', parameter2=found_pos)
found_index = found_pos - current_tab.send_signal(parameter1='SCI_POSITIONFROMLINE',
parameter2=found_line) - 1
current_line = found_line
current_index = found_index + len(input_string)
# 再判断 因为这个先加
if current_line > end_line:
break
if len(input_string) > 1:
positions.add(
(found_line, found_index - len(input_string) + 1, found_line,
found_index + 1)) # 记录匹配的位置(行号和索引)
else:
positions.add(
(found_line, found_index, found_line, found_index + len(input_string))) # 记录匹配的位置(行号和索引)
count += 1
else:
# 搜不到就需要换行....
if current_index <= end_index and current_line <= end_line:
current_index += len(input_string)
elif current_line <= end_line and current_index > end_index:
current_index = 0
current_line += 1
self.select_keywords_pos = list(positions)
current_tab.multi_highlight_text(self.select_keywords_pos)
self.ui.msg1.setText(f"共搜索到关键词: '{
input_string}' {
count}次!")
干货干得要缺水了!
语法树到时候更是重量级…
任重而道远…
放在另一篇里面吧…
这里是链接
不知不觉竟然写了这么多代码(嗐)
如果付出和收获对等,我也能成为最强吗?