在 PyQt5 和 PySide2 中覆盖 paintEvent

Overriding paintEvent in PyQt5 and PySide2

我使用 PyQt 和 PySide 有一段时间了。今天我偶然发现了一个奇怪的行为:重新实现 paintEvent 似乎在 Python 版本的 Qt5 中不起作用。我在 Qt4 中从来没有遇到过这个问题。

from PySide2 import QtWidgets, QtCore, QtGui # use pyside
# from PyQt5 import QtWidgets, QtCore, QtGui # use pyqt
import sys


class TagWidget(QtWidgets.QWidget):

    def __init__(self, parent):
        super().__init__(parent)
        print("__init__")


    def paintEvent(self, e): 
        # this is called or not
        # depends (see below)
        print("paintEvent")
        raise(AssertionError)


class MyGui(QtWidgets.QMainWindow):


    def __init__(self,parent=None):
        super(MyGui, self).__init__()
        self.setupUi()

    def setupUi(self):
        self.setGeometry(QtCore.QRect(100,100,500,500))

        self.w=QtWidgets.QWidget(self)    
        self.setCentralWidget(self.w)

        self.lay = QtWidgets.QHBoxLayout(self.w)
        self.image = TagWidget(self.w)
        self.lay.addWidget(self.image)

        # return 
        # exit here, and TagWidget.paintEvent 
        # is still being called

        self.file_list = QtWidgets.QListWidget(self.w)

        # return 
        # exit here, and TagWidget.paintEvent 
        # is still being called

        self.lay.addWidget(self.file_list)

        # .. but if we reach all the way here, 
        # TagWidget.paintEvent is never called !


def main():
    app=QtWidgets.QApplication(["test_app"])
    mg=MyGui()
    mg.show()
    app.exec_()


if (__name__=="__main__"):
    main()

因此,我们只是测试是否正在调用 paintEvent(通过在调用时引发 AssertionError)。

一旦我们将另一个小部件添加到 TagWidget 所在的同一布局,paintEvent 将不再有效。

太奇怪了。感谢帮助。

paintEvent() 在需要重新绘制时被调用,如果小部件具有 size(0, 0),或者大小无效或被隐藏,则不会调用该方法,这就是您的情况,使用布局时,默认情况下它将采用 sizeHint() 的大小,默认情况下 QWidget sizeHint()QSize(-1, -1) 因此不需要绘制。

所以解决办法就是设置一个合适的sizeHint():

class TagWidget(QtWidgets.QWidget):
    def paintEvent(self, e): 
        print("paintEvent")
        raise(AssertionError)

    def sizeHint(self):
        print("default sizeHint: ", super(TagWidget, self).sizeHint())
        return QtCore.QSize(640, 480)

我已经用 PyQt4PySide 试过了,同样的问题发生了,所以问题不是 Qt,而是这个例子。