PyQt: сбой при двойном наследовании от QWidget - PullRequest
2 голосов
/ 03 июля 2019

Следующая проблема возникает с PyQt5, Qt 5.9.6.

Я хочу создать модальный класс диалога (Dialog) на основе другого класса (UserForm). Но если UserForm уже наследуется от QWidget, тогда мой скрипт вызывает исключение

Объект 'Dialog' не имеет атрибута 'exec _'

или просто молча падает или вылетает с сообщением в консоли:

QDialog :: exec: обнаружен рекурсивный вызов

MRO выглядит точно так же, если UserForm наследуется от QWidget или нет:

(<class '__main__.Dialog'>, <class '__main__.UserForm'>, <class
'PyQt5.QtWidgets.QDialog'>, <class 'PyQt5.QtWidgets.QWidget'>, <class
'PyQt5.QtCore.QObject'>, <class 'sip.wrapper'>, <class
'PyQt5.QtGui.QPaintDevice'>, <class 'sip.simplewrapper'>, <class 'object'>)
* * 1 022 Пример: * 1 023 *
from PyQt5 import QtWidgets
app = QtWidgets.QApplication([])

class UserForm(QtWidgets.QWidget):
    pass

# class Dialog(UserForm, QtWidgets.QDialog):
#     pass
Dialog = type("Dialog", (UserForm, QtWidgets.QDialog), {})

print(Dialog.__mro__)
Dialog().exec_()

Я также пытался запустить этот код с PySide2, Qt 5.12, и он работает без проблем. Значит ли это, что в PyQt есть какая-то ошибка?

1 Ответ

2 голосов
/ 04 июля 2019

Напротив, это ошибка PySide2, поскольку она противоречит документам :

Множественное наследование Требует, чтобы QObject был первым

Если вы используете множественное наследование, moc предполагает, что первый унаследованный класс является подклассом QObject.Кроме того, убедитесь, что только первый унаследованный класс является объектом QObject.

Если рассматривается поведение, описанное в предыдущей части, то класс Dialog должен учитывать только UserForm, а не QDialog, поэтому Dialog не долженесть метод exec_ (), поскольку его могут иметь только классы, наследуемые от QDialog.

  • В Qt вам не следует наследовать от 2 QObject, поддерживаются только миксины (QObject + not QObject) (1) (2) .

  • Если это миксин, то первым должен быть объект QObject.

Ивот образец, который нужно унаследовать:

from PyQt5 import QtCore, QtGui, QtWidgets
# or
# from PySide2 import QtCore, QtGui, QtWidgets

class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(400, 300)
        self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
        self.buttonBox.setGeometry(QtCore.QRect(30, 240, 341, 32))
        self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
        self.buttonBox.setStandardButtons(
            QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok
        )
        self.buttonBox.setObjectName("buttonBox")

        self.retranslateUi(Dialog)
        self.buttonBox.accepted.connect(Dialog.accept)
        self.buttonBox.rejected.connect(Dialog.reject)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "Dialog"))


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

# or
# class Dialog(QtWidgets.QDialog, Ui_Dialog):
#    def __init__(self, parent=None):
#        super(Dialog, self).__init__(parent)
#        self.setupUi(self)

Dialog = type("Dialog", (QtWidgets.QDialog, Ui_Dialog), {"__init__": init})

if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    Dialog().exec_()

(1) Множественное наследование
(2) КооперативMulti-наследование

...