在 PyQt5 和 PySide2 中覆盖paintEvent

Posted

技术标签:

【中文标题】在 PyQt5 和 PySide2 中覆盖paintEvent【英文标题】:Overriding paintEvent in PyQt5 and PySide2 【发布时间】:2018-10-30 20:01:45 【问题描述】:

我使用 PyQt 和 PySide 已经有一段时间了。今天我偶然发现了一个奇怪的行为:重新实现paintEvent似乎不适用于Qt5的Python版本。我在 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 就不再有效。

太奇怪了。帮助表示赞赏。

【问题讨论】:

【参考方案1】:

paintEvent() 在需要重新绘制时被调用,如果小部件有size(0, 0),或者尺寸无效或被隐藏,则不会调用该方法,这就是你的情况,当使用布局时它会默认取sizeHint()的大小,默认一个QWidgetsizeHint()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,而在于具体的例子。

【讨论】:

以上是关于在 PyQt5 和 PySide2 中覆盖paintEvent的主要内容,如果未能解决你的问题,请参考以下文章

QTableView 在 PyQt5/PySide2 中的每个选择上加载数据时锁定主窗体 [重复]

了解不一致的 cythonized 代码行为 - PyQt5 与 PySide2

具有内置多处理功能的 PySide2

PyQt5 & PySide2 / 无法在“”中加载 Qt 平台插件“windows”,即使找到了

PySide2 与 Pyqt5 的区别

如何在 PyQt5 / PySide2 中从一个类访问属性到另一个类