1
from abc import ABC, abstractmethod
from PyQt5.QtWidgets import QMainWindow


class _ControlGUI(QMainWindow, ABC):
    pass

The very simple code above is raising an error that is not very clear to me.

TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

My goal is to define a base class _ControlGUI with the mandatory structure/properties; and then define multiple concrete classes inheriting from it, with additional functionalities/properties. All those concrete classes are small GUIs, and thus inherit from QMainWindow, thus I thought it would be best to have the abstract class inherit from QMainWindow directly. However, it doesn't seem to be possible.

What is the best design, solution to this problem? My current idea is to define the abstract class without QMainWindow and to have all the concrete class inherit from _ControlGUI and from QMainWindow.

Mathieu
  • 5,410
  • 6
  • 28
  • 55
  • This is the problem with metaclasses; they don't compose well. Subclasses of `_ControlGUI` won't be able to inherit from `QMainWindow` either; it's the metaclass used by `QMainWindow` that conflicts with `ABCMeta`. – chepner Jul 08 '21 at 15:32
  • As far as I know, the only solution is to not use `ABC`, and rely on subclassers to implement methods documented as needing to be overridden. – chepner Jul 08 '21 at 15:34
  • And there's also the fact that PyQt classes don't always mix up very well with multiple inheritance, so an extra level of care must be taken, especially when dealing with arguments in the constructor. See [this](https://stackoverflow.com/q/42822154) and [this](https://stackoverflow.com/q/52932238). If you can provide a basic [mre] of what you're trying to achieve we might be able to better understand where the problem is and see if there are other possibilities. – musicamante Jul 08 '21 at 15:36
  • 1
    You can define `class YourMetaClass(type(QMainWindow), type(ABC)):` `pass` and then specify this in `class _ControlGUI(QMainWindow, ABC, metaclass=YourMetaClass):`. [Maybe this will help](https://stackoverflow.com/questions/28720217/multiple-inheritance-metaclass-conflict) – Julian Fock Jul 08 '21 at 15:39
  • @JulianFock I gave a shot at your answer. It looks like it's working well! Feel free to enter it as a complete answer to this question. – Mathieu Jul 08 '21 at 18:18

1 Answers1

3

As the error says, you have to create a metaclass that is a subclass of each metaclass of the involved classes.

It is possible that the metaclass for PyQT Widgets is not designed to play colaboratively with other metaclasses - this will be a matter of luck. The metaclass for ABC, abc.ABCMeta (whcih I referred bellow as type(ABC) for ilustration purposes) if a bit better suited for colaborative inheritance, som, putting it first when deriving your metaclass will get you better chances of getting things actually working. (i.e., among other things, it will use super() call in its methods and not hardcode a call to type.<method>).

Once you get the derived metaclass, just use that on your inheriting base class. As all examples are one line of code, and it is interesting to check the results, I just wrote things on the interactive environment:

In [1]: from PyQt5.QtWidgets import QMainWindow

In [2]: from abc import ABC, ABCMeta

In [3]: class A(QMainWindow, ABC): pass
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-86552c93e708> in <module>
----> 1 class A(QMainWindow, ABC): pass

TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

In [4]: class Meta(type(ABC), type(QMainWindow)): pass

In [5]: class B(QMainWindow, ABC, metaclass=Meta): pass

As a side note, regardless of what Qt documentation says (and other graphical toolkit as well), working with subclasses of the widgets to create your app may be a major pain. If you just instantiate the widgets and refer to them as attributes in a plain class inheriting only things that concern to your project, you might be in for a much nicer journey, in contrast with inheriting from QMainWindow to start.

jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • (sorrym, I wrote the answer, and just later I stopped to check the comments. This is also a clear duplicate, and seaching on [metaclass] will yield the same problem at least 3 other times. But as I like writting a few extra advices each time it pops up, I just entered the answer) – jsbueno Jul 08 '21 at 18:59
  • 1
    Thank you, indeed thanks to the comment I have a similar working solution, but where the order in Meta is the other way around. This idea of metaclass and what it represents is not yet very clear to me, I'll keep looking. And for the Qt part, yes it's probably much better, but I am not at all familiar with Qt, and completely changing the structure of the project (which I am not the original author) would be a pain. – Mathieu Jul 08 '21 at 20:55