[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