如何获取QTextEdit中文本的XY位置?实现VSCode风格颜色预览
QTextEdit实现VSCode式颜色方块功能的问题修复
解决光标定位错误问题
核心问题是你在QSyntaxHighlighter的highlightBlock方法里直接修改文本——这个方法本来是用来做语法高亮渲染的,不是用来修改内容的,这么做会触发文本变更的循环,还会打乱光标位置。正确的做法是:
- 改用
QTextEdit的textChanged信号来监听文本变化,在信号槽里处理颜色方块的插入/更新 - 操作文本前先记录当前光标位置,插入方块后根据插入位置调整光标:
# 记录原光标位置 original_cursor = self.textEdit.textCursor() original_pos = original_cursor.position() # 执行插入操作后,若插入位置在光标之前,给光标位置加1(因为■是1个字符) if insert_pos < original_pos: original_cursor.setPosition(original_pos + 1) self.textEdit.setTextCursor(original_cursor) - 操作文本时记得临时阻塞信号(
blockSignals(True)),避免循环触发textChanged
解决无法检测已存在■的问题
要避免重复插入方块,得在匹配十六进制颜色时排除后面已经跟着■的情况,或者检查匹配位置的下一个字符:
方法1:用正则负向断言匹配不带■的颜色
直接在正则里加(?!■),确保匹配的颜色后面没有■:
pattern = re.compile(r'#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})(?!■)')
方法2:手动检查匹配后的字符
遍历匹配结果时,获取匹配的结束位置,检查下一个字符是否是■:
text = self.textEdit.toPlainText() for match in pattern.finditer(text): end_pos = match.end() # 检查是否已经有■ if end_pos < len(text) and text[end_pos] == '■': continue # 跳过,不重复插入 # 执行插入方块的操作
完整实现思路调整
- 放弃在高亮器里改文本:把颜色方块的插入/更新逻辑移到
textChanged信号槽中 - 处理方块的插入与样式:插入■后,给它设置对应颜色的背景色(用
QTextCharFormat) - 点击方块修改颜色:重写
mousePressEvent,检测点击的是■时,找到前面的十六进制颜色,弹出颜色选择器,替换原颜色并更新方块样式
关键代码片段
文本变更处理槽函数
def on_text_changed(self): self.textEdit.blockSignals(True) doc = self.textEdit.document() cursor = QTextCursor(doc) cursor.beginEditBlock() # 1. 插入新的颜色方块(不带■的颜色) color_pattern = re.compile(r'#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})(?!■)') text = self.textEdit.toPlainText() # 反向遍历,避免插入后位置偏移影响前面的匹配 for match in reversed(list(color_pattern.finditer(text))): color_hex = match.group() end_pos = match.end() # 插入■ cursor.setPosition(end_pos) cursor.insertText('■') # 设置■的背景色 fmt = QTextCharFormat() fmt.setBackground(QColor(color_hex)) cursor.setPosition(end_pos) cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor) cursor.setCharFormat(fmt) # 2. 更新已存在方块的颜色(当原十六进制字符串修改时) square_pattern = re.compile(r'#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})■') for match in square_pattern.finditer(text): color_hex = match.group(1) square_pos = match.end() - 1 cursor.setPosition(square_pos) cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor) fmt = QTextCharFormat() fmt.setBackground(QColor(color_hex)) cursor.setCharFormat(fmt) cursor.endEditBlock() # 恢复原光标位置 original_cursor = self.textEdit.textCursor() self.textEdit.setTextCursor(original_cursor) self.textEdit.blockSignals(False)
点击方块修改颜色的事件处理
def mousePressEvent(self, event): pos = event.pos() cursor = self.textEdit.cursorForPosition(pos) cursor.select(QTextCursor.WordUnderCursor) if cursor.selectedText() == '■': # 定位到前面的十六进制颜色 cursor.movePosition(QTextCursor.Left, QTextCursor.MoveAnchor) # 选中#开头的6位或3位颜色(这里按最长7位处理:#FFFFFF) cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 7) color_hex = cursor.selectedText().strip() # 验证颜色格式 if re.match(r'#[0-9a-fA-F]{6}$|#[0-9a-fA-F]{3}$', color_hex): # 弹出颜色选择器 selected_color = QColorDialog.getColor(QColor(color_hex), self) if selected_color.isValid(): new_hex = selected_color.name() # 替换原颜色字符串 cursor.insertText(new_hex) # 更新■的背景色 cursor.movePosition(QTextCursor.Right) cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor) fmt = QTextCharFormat() fmt.setBackground(selected_color) cursor.setCharFormat(fmt) super().mousePressEvent(event)
内容的提问来源于stack exchange,提问作者Cesar
相关产品推荐
相关产品推荐

