[PyQt] Multiple inheritance from custom widgets and uic
Kyle Altendorf
sda at fstab.net
Thu Aug 30 12:27:31 BST 2018
On 2018-08-30 01:34, Alexander Bruy wrote:
> usually I use following code to create a form from the Qt Designer's
> .ui
> file dynamically
>
> from PyQt5 import uic
> FORM_CLASS, BASE_CLASS = uic.loadUiType('/path/to/ui/file.ui')
>
> class MyDialog(BASE_CLASS, FORM_CLASS):
> def __init__(self, parent=None):
> super(MyDialog, self).__init__(parent)
> self.setupUi(self)
Maybe don't inherit from the form class?
https://github.com/altendky/basicpyqt5example/blob/08457cf4fa303e7f3cd6be341725ac9cf8ee3a62/src/basicpyqt5example/mainwindow.py#L37-L47
Ui, UiBase = PyQt5.uic.loadUiType(
pathlib.Path(__file__).with_name('mainwindow.ui'),
)
class MainWindow(UiBase):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui()
self.ui.setupUi(self)
This reduces the inheritance load and also gets you an isolated `.ui`
attribute for holding all your UI objects. No more writing a method
whose name conflicts with and overwrites a button you had in your UI.
> But seems this approach seems does not work with multiple inheritance.
> There is a custom dialog class (QDialog subclass with custom UI and
> some
> logic) available in the API library. I want to subclass this custom
> dialog
> and also apply my own UI to it. I tried following code
>
> from PyQt5 import uic
> from api.ui import CustomDialogBase
>
> FORM_CLASS, BASE_CLASS = uic.loadUiType('/path/to/ui/file.ui')
>
> class MyDialog(BASE_CLASS, CustomDialogBase, FORM_CLASS):
> def __init__(self, parent=None):
> super(MyDialog, self).__init__(parent)
> self.setupUi(self)
Why are you inheriting from a CustomDialogBase that already inherits
from BASE_CLASS? I see below that you tried without. If you want to
make sure that it really is the case you could check if BASE_CLASS in
CustomDialogBase.__mro__ and raise an exception if not. Though I'm not
really sure how inheriting a UI and also calling `setupUi()` into the
same widget would make sense.
> TypeError: Cannot create a consistent method resolution order (MRO)
> for bases QDialog, QgsOptionsDialogBase, Ui_OptionsDialog
>
> As I understand, this is because all classes have the same base class
> QDialog.
Ui_OptionsDialog does not have QDialog as a base class. Here's
diagnostic output from my linked code (I added the diagnostic prints
locally).
Ui
<class 'Ui_MainWindow'>
(<class 'Ui_MainWindow'>, <class 'object'>)
UiBase
<class 'PyQt5.QtWidgets.QMainWindow'>
(<class 'PyQt5.QtWidgets.QMainWindow'>, <class
'PyQt5.QtWidgets.QWidget'>, <class 'PyQt5.QtCore.QObject'>, <class
'sip.wrapper'>, <class 'PyQt5.QtGui.QPaintDevice'>, <class
'sip.simplewrapper'>, <class 'object'>)
> Also I tried to specify only one parent class — custom dialog, as it is
> already subclassed from QDialog
>
> from PyQt5 import uic
> from api.ui import CustomDialogBase
>
> FORM_CLASS, BASE_CLASS = uic.loadUiType('/path/to/ui/file.ui')
>
> class MyDialog(CustomDialogBase, FORM_CLASS):
> def __init__(self, parent=None):
> super(MyDialog, self).__init__(parent)
> self.setupUi(self)
>
> But this also does not work, with the error
>
> AttributeError: 'MyDialog' object has no attribute 'groupBox'
>
> As I understand, in this case issue is that FORM_CLASS is a QDialog
> subclass,
> not CustomDialogBase's subclass. After some searching I have found this
> thread
> https://riverbankcomputing.com/pipermail/pyqt/2017-October/039647.html.
> Am I right that currently the only way to solve my issue is to
> construct
> form from the compiled .ui file?
Double check but I don't expect FORM_CLASS has anything other than
object in it's more. Try print(FORM_CLASS.__mro__)
But, since you are trying to merge two UI's in this case you likely need
to figure out where in the CustomDialogBase's existing UI that you want
to insert your custom UI from file.ui. If there's a layout you then
need to make a QWidget, add it to that layout, pass it to the
self.setupUi() (hopefully self.ui.setupUi() by now) and edit your .ui
file to be based on a QWidget.
Mostly, don't use inheritance to mix things together. Sure, it can
work, but you really have to know what's going on and they have to be
compatible things. It's often just not worth it. Also, is this Python
2? If not and it's Python 3, just super().__init__() with no args to
super. It'll do the same thing but be less error prone.
Cheers,
-kyle
More information about the PyQt
mailing list