You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取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  # 跳过,不重复插入
    # 执行插入方块的操作

完整实现思路调整

  1. 放弃在高亮器里改文本:把颜色方块的插入/更新逻辑移到textChanged信号槽中
  2. 处理方块的插入与样式:插入■后,给它设置对应颜色的背景色(用QTextCharFormat)
  3. 点击方块修改颜色:重写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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 07:53:35