2

I am working on a project involving PyQt5, and I am struggling with managing inheritance between widgets.

I have one QWidget screen that inherits off QtWidgets.QWidget and another class which is generated by QtDesigner. It reads something like this:

class a(QtWidgets.QWidget, Ui_a):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        self.setupUi(self)

        <some attributes>

    <some functions

Here, I inherit off Ui_a, a separate class stored in a generated file, and I can call setupUi (a method of Ui_a) fine.

I now want to create another class b, which also is a QWidget that needs to be displayed. This class b requires the use of some of the functions and attributes from class a. I can easily just copy paste the required stuff in but that is bad practice so I am looking for a more neat solution. If I do the code:

class b(QtWidgets.QWidget, Ui_b, a):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init(self, parent)
        self.setupUi(self)

This then crashes with an error message saying that it cannot create a consistent method resolution order.

My first question is - I know I need to call the init method of class a since a's attributes are created there, but I don't know how.

My second question is - How do I fix this MRO error and succeed in creating the new class b which can use a's attributes and functions?

user2314737
  • 27,088
  • 20
  • 102
  • 114
samlu1999
  • 53
  • 2
  • Have a look at http://stackoverflow.com/questions/1848474/method-resolution-order-mro-in-new-style-python-classes – dahrens Dec 30 '16 at 13:34
  • Possible duplicate of [TypeError: Cannot create a consistent method resolution order (MRO)](http://stackoverflow.com/questions/29214888/typeerror-cannot-create-a-consistent-method-resolution-order-mro) – whereswalden Dec 30 '16 at 13:50

2 Answers2

5

You are trying to mix in the parent classes before the derived class. There is no need to, just inherit directly from a and the new Ui_b and nothing else:

class b(a, Ui_b):
    # *no* __init__ needed either

a already pulls in QtWidgets.QWidget.

Now, Ui_a and Ui_b may not play well together. You may have to invoke both Ui_a.setupUi() and Ui_b.setupUi(). You can do so explicitly:

class b(a, Ui_b):
    def __init__(self, parent=None):
        super().__init__(parent)
        # Ui_b.setupUi would have shadowed Ui_a.setupUi, so 
        # call the latter explicitly
        Ui_a.setupUi(self, self)  # unbound

It may be that Ui_a and Ui_b can't be mixed at all. In that case you should just pull out all the methods you want to re-use into a separate base class and have both a and b inherit from that:

class SharedStuff:
    # ...

class a(QtWidgets.QWidget, SharedStuff, Ui_a):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        self.setupUi(self)

class b(QtWidgets.QWidget, SharedStuff, Ui_b):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        self.setupUi(self)
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Saying that "`Ui_a` and `Ui_b` may not play well together" is quite an understatement if they were both generated from qt designer files (which is where the `setupUi` method comes from). It is generally unsafe to call `setupUi` more than once with the same arguments (although it might still "work" through sheer dumb luck). – ekhumoro Dec 30 '16 at 21:00
  • @ekhumoro: I have 0 experience with qt, let alone with the auto-generated code. Added an alternative option. – Martijn Pieters Dec 30 '16 at 21:13
0

Your question is slightly unclear, but I am going to assume that Ui_a and Ui_b aren't both classes generated from Qt Designer files - because there is no sane way of inheriting from two such classes.

I'm guessing your current classes are equivalent to this:

class A(QtWidgets.QWidget, Ui_FromQtDesigner):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        self.setupUi(self)

    def methodA(self):
        print('A:', self.windowTitle())

class B(QtWidgets.QWidget):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)

    def methodB(self):
        print('B:', self.windowTitle())

and you want a class C which will be a QWidget subclass that gets both the Qt Designer UI elements and the custom methods from classes A and B.

Now, Qt uses single inheritance almost exclusively. Inheriting from two QWidget classes is usually an error, unless the classes share a common set of base-classes (and even then, there are a number of limitations that still make it a questionable design choice - for more details, see this blog post).

In your case, it would be better to use a simple mixin class. It is only really necessary for class A to inherit from QWidget, so the set of classes can be defined like this:

class WidgetA(QtWidgets.QWidget, Ui_FromQtDesigner):
    def __init__(self, parent=None):
        super(WidgetA, self).__init__(parent)
        self.setupUi(self)

    def methodA(self):
        print('A:', self.windowTitle())

class WidgetMixinB(object):
    def methodB(self):
        print('B:', self.windowTitle())

class WidgetC(WidgetA, WidgetMixinB):
    pass

And if you also want to use WidgetMixinB as a widget in its own right, you can create another class for it like this:

class WidgetB(QtWidgets.QWidget, WidgetMixinB):
    pass
ekhumoro
  • 115,249
  • 20
  • 229
  • 336