viewportEvent 应该如何在 QAbstractScrollArea 中实现?

How should viewportEvent be implemented in a QAbstractScrollArea?

我在获取适合 QAbstractScrollArea 的详细信息时遇到了很多问题。这是我当前实现的 viewportEvent:

def viewportEvent(self, event):
    if event.type() in [QEvent.MouseButtonPress,
                        QEvent.MouseMove,
                        QEvent.MouseButtonRelease,
                        QEvent.ContextMenu,
                        QEvent.KeyPress,
                        QEvent.KeyRelease]:
        return self.my_viewport.event(event)

    if event.type() == QEvent.Resize:
        self.my_viewport.resizeEvent(event)
        return super().viewportEvent(event)

    if event.type() in [QEvent.UpdateLater,
                        QEvent.UpdateRequest]:
        self.my_viewport.event(event)
    if event.type() == QEvent.Paint:
        self.my_viewport.paintEvent(event)
    return super().viewportEvent(event)

这个想法是通过(到视口小部件)按键和鼠标按下之类的东西。调整大小事件需要传递并发送到抽象滚动区域本身?滚动条的大小如何?不应更改调整大小事件的大小。如果我不传递绘制事件,则视口小部件不会绘制。


带有 QAbstractScrollArea 的损坏的 QOpenGLWidget 的最小工作示例:

import sys

from PyQt5.QtCore import QEvent
from PyQt5.QtWidgets import (QAbstractScrollArea, QApplication, QMainWindow,
                             QOpenGLWidget)


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.scope_view_widget = ScrollingScopeView()
        self.setCentralWidget(self.scope_view_widget)

class ScopeView(QOpenGLWidget):

    def paintGL(self):
        super().paintGL()
        print("Painting")


class ScrollingScopeView(QAbstractScrollArea):

    def __init__(self):
        super().__init__()
        self.set_my_viewport(ScopeView())

    def set_my_viewport(self, new_viewport):
        self.my_viewport = new_viewport
        self.setViewport(self.my_viewport)

    def viewportEvent(self, event):
        # Uncommenting this breaks painting.
        if event.type() == QEvent.Paint:
            self.my_viewport.paintEvent(event)
        return super().viewportEvent(event)


application = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
sys.exit(application.exec_())

归档为 Qt 错误:https://bugreports.qt.io/browse/QTBUG-53269

我之前的回答完全错了。感谢 OP 的调查。

根据文档:

When inheriting QAbstractScrollArea, you need to do the following:

  • Control the scroll bars by setting their range, value, page step, and tracking their movements.

  • Draw the contents of the area in the viewport according to the values of the scroll bars.

  • Handle events received by the viewport in viewportEvent() - notably resize events.

  • Use viewport->update() to update the contents of the viewport instead of update() as all painting operations take place on the viewport.

除非您需要进行其他事件管理,否则您的 MCVE 中非常短的 viewportEvent() 正确的 Take a look at the code(比我做的更好看)你会看到大多数事件(包括绘画事件)没有传递到视口。奇怪的是,代码确实异常调整大小 QOpenGLWidget.

我现在意识到 默认绘制背后的逻辑是允许您 仅更新当前可见的视口区域

总之,下面的就可以了。我建议检查以确保绘画事件仅包括当前可见的矩形(检查绘画事件中 rect() 的值),否则您将绘制当前在视口中不可见的区域。

def viewportEvent(self, event):
    # Uncommenting this breaks painting.
    if event.type() == QEvent.Paint:
        self.my_viewport.paintEvent(event)
    return super().viewportEvent(event)

为我的失误道歉。希望对您有所帮助。