Search code examples
pythonpython-3.xpyqt5metaclassabc

Abstract class inheriting from ABC and QMainWindow


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.


Solution

  • 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 collaboratively with other metaclasses - this will be a matter of luck. The metaclass for ABC, abc.ABCMeta (which I referred to below as type(ABC) for illustration purposes) is a bit better suited for collaborative inheritance, so, 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.