29

I need a double inheritance for a class. I tried several syntaxes but I don't understand the concept of metaclass.

from PyQt5.QtGui import QStandardItem
from configparser import ConfigParser

class FinalClass(ConfigParser, QStandardItem):
    def __init__(self, param):
        ConfigParser.__init__(self)
        QStandardItem.__init__(self)
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Mauricio
  • 670
  • 1
  • 8
  • 23
  • You have no meta classes here. What problems are you facing? – Martijn Pieters Feb 25 '15 at 13:39
  • 1
    @MartijnPieters - he's probably refering to the error which will this class definition will produce: `TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases` – mata Feb 25 '15 at 13:42
  • 2
    @mata: ah, so `QStandardItem` presumably uses a metaclass. The real question here then is why the OP is trying to mix a `QStandardItem` and `ConfigParser` in one class. – Martijn Pieters Feb 25 '15 at 13:45
  • I need this because I use QStandardItemModel with QTableView. Each row is an instance of FinalClass and each instance is a file. – Mauricio Feb 25 '15 at 14:37
  • @Mauricio. That isn't a good reason to use multiple inheritance. Just make `FinalClass` a subclass of `QStandardItem`, and then delegate to an internal instance of `ConfigParser`. Or, even better, forget about using standard items and use Qt's ModeL/View Architecture to create a [custom model](http://doc.qt.io/qt-5/model-view-programming.html#model-subclassing-reference) instead. – ekhumoro Feb 25 '15 at 18:37

2 Answers2

50

The problem in your case is that the classes you try to inherit from have different metaclasses:

>>> type(QStandardItem)
<class 'sip.wrappertype'> 
>>> type(ConfigParser)
<class 'abc.ABCMeta'>

Therefore python can't decide which should be the metaclass for the newly created class. In this case, it would have to be a class inheriting from both sip.wrappertype (or PyQt5.QtCore.pyqtWrapperType for older PyQt5 versions) and ABCMeta.

Therefore the metaclass conflict could be resolved by explicitly introducing such a class as metaclass like this:

from PyQt5.QtGui import QStandardItem
from configparser import ConfigParser

class FinalMeta(type(QStandardItem), type(ConfigParser)):
    pass

class FinalClass(ConfigParser, QStandardItem, metaclass=FinalMeta):
    def __init__(self, param):
        ConfigParser.__init__(self)
        QStandardItem.__init__(self)

If you want a more detailed description, this article is a good start.

However I'm not really convinced that using multiple inheritance for this situaction is such a good idea, specially using multiple inheritance together with QObjects can be tricky. Maybe it would be better to just store the ConfigParser object as an instance variable and use that when needed.

mata
  • 67,110
  • 10
  • 163
  • 162
  • Thank you very much for this explanation and workable answer. Use only with personal objects so. You're right, I'll pick an instance of ConfigParser otherwise it raises other problems. – Mauricio Feb 25 '15 at 20:11
  • Thanks for this concise description. I was so lost before I read this post. – jooks Dec 05 '17 at 20:18
  • In my environment (Python 3.7.2 + Anaconda + PyQt5 5.12 + PyQt5_sip 4.19.14) the `from PyQt5.QtCore import pyqtWrapperType` does not exist. – LoneWanderer Mar 15 '19 at 17:49
  • Seems that at some point the metaclass was changed from `PyQt5.QtCore.pyqtWrapperType` to `sip.wrappertype`. I'll update the answer. – mata Mar 15 '19 at 21:03
5

You can make your base class's metaclass extends from Qt metaclass system

import abc

from PySide2 import QtWidgets, QtCore


class MixinMeta(type(QtCore.QObject), abc.ABCMeta):
    pass


class MyMixin(object, metaclass=MixinMeta):
    @abc.abstractmethod
    def howToShow(self):
        pass

    def doShow(self):
        self.howToShow()


class MyWidget(QtWidgets.QWidget, MyMixin):
    def howToShow(self):
        self.show()


app = QtWidgets.QApplication()
widget = MyWidget()
widget.doShow()
app.exec_()
BaiJiFeiLong
  • 3,716
  • 1
  • 30
  • 28