【问题标题】:drawRect() in QTextEdit not functioning properly when cursor is moved移动光标时 QTextEdit 中的 drawRect() 无法正常工作
【发布时间】:2021-05-12 17:31:02
【问题描述】:

我的 QTextEdit 的 paintEvent 中有这个简单的代码,它在当前选定的 QTextBlock 下绘制了一个灰色框:

def paintEvent(self, ev):
    painter = QPainter()
    painter.begin(self.viewport())
    currentPos = self.textCursor().position()
    block = self.document().findBlock(currentPos)
    rect = self.document().documentLayout().blockBoundingRect(block)
    margin = self.document().documentMargin()
    rect.setTopLeft(QPoint(int(rect.topLeft().x()-margin), int(rect.topLeft().y()-margin)))
    rect.setBottomRight(QPoint(int(rect.bottomRight().x()+margin), int(rect.bottomRight().y())))
    painter.fillRect(rect, QBrush(QColor(10, 10, 10,20)))
    if self._last_selected_block and (self._last_selected_block != block):
        lastrect = self.document().documentLayout().blockBoundingRect(self._last_selected_block) #clean up artifacts
        painter.eraseRect(lastrect)
    painter.fillRect(self.contentsRect(), QBrush(QColor(123, 111, 145, 80))) #background color
    painter.end()
    self._last_selected_block = block
    super().paintEvent(ev)

(注意,“清理工件”行会擦除在先前选择的 QTextBlock 区域中绘制的任何内容,因为如果在其中绘制文本,最后一个块下方会保留一条细灰线。可能是相关的。)

这样的效果是:

但是,当通过单击另一行移动光标时,会发生这种情况:

下一个矩形仅在光标移动到的字符周围部分绘制,前一个矩形没有被擦除。 eraseRect() 似乎无法删除此工件。当继续输入或换行时,一切都会恢复正常(通过换行换行时不会出现此问题)。我已经确认当光标移动时会调用paintEvent(),并且要绘制的矩形的宽度永远不会改变。这里发生了什么?

【问题讨论】:

标签: python pyqt pyqt5 qtextedit


【解决方案1】:

出于优化原因,Qt 仅尝试仅重绘小部件中实际需要更新的部分。

在 QTextEdit 的情况下,这意味着只有在移动它(通过编辑、使用箭头键或鼠标)之前存在“插入符号”的部分以及现在的部分将被更新,而忽略其他所有内容。

这显然是您的问题,因为它只会更新小部件的一小部分,结果是不会重绘先前突出显示的块以显示您的自定义背景。

解决方案是跟踪当前光标位置,并在块发生更改时立即正确更新both 前一个块 新块。这是通过使用QRegion 调用update() 来实现的,该QRegion 是通过合并当前块边界矩形和前一个(如果有的话)创建的;这将安排仅重绘该区域内的内容的更新(这是 QTextEdit 通常所做的,但我们将其扩展到整个块区域之前的区域)。

请注意,由于以下原因,我已经完全改变了您对 paintEvent 的实现,因为它大部分是不必要的:

  • 文档的边距不应用于块;
  • 背景绘制不考虑滚动区域背景(稍后会详细介绍);
  • 没有必要“擦除”前一个块矩形:我们的update() 调用包括该区域,并且由于它不是当前块,所以背景将只是(重新)绘制在那里;
  • 不应在绘制事件中更新当前块;

滚动区域的渲染总是涉及基于backgroundRole 绘制其背景,该backgroundRole 自动设置为QTextEdit 的Base 调色板角色。结果是您的背景颜色将不是您所相信的,而是基础颜色(通常是接近白色的颜色)您的背景的组合。
为了确保背景正是您想要的颜色,您必须使用 Base 角色的颜色更新小部件上的调色板,它也应该是不透明的颜色(否则它将使用 @987654328 @颜色角色)。

class TextEdit(QtWidgets.QTextEdit):
    _last_selected_block = None
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        palette = self.palette()
        palette.setColor(palette.Base, QtGui.QColor(203, 200, 210))
        self.setPalette(palette)
        self.cursorPositionChanged.connect(self.trackCursorPosition)

    def trackCursorPosition(self):
        block = self.textCursor().block()
        currentRect = self.document().documentLayout().blockBoundingRect(block)
        updateRegion = QtGui.QRegion(currentRect.toRect())
        if self._last_selected_block and self._last_selected_block != block:
            oldRect = self.document().documentLayout().blockBoundingRect(
                self._last_selected_block)
            updateRegion |= QtGui.QRegion(oldRect.toRect())
        updateRegion.translate(0, -self.verticalScrollBar().value())
        self._last_selected_block = block
        self.viewport().update(updateRegion)

    def paintEvent(self, ev):
        painter = QtGui.QPainter(self.viewport())
        block = self.textCursor().block()
        rect = self.document().documentLayout().blockBoundingRect(block)
        rect.translate(0, -self.verticalScrollBar().value())
        painter.fillRect(rect, QtGui.QColor(10, 10, 10,20))
        super().paintEvent(ev)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-02
    • 2022-10-19
    • 2019-10-02
    • 1970-01-01
    • 2011-06-13
    • 2018-05-02
    相关资源
    最近更新 更多