[PyQt] Fwd: Mixin classes and PyQt4

Martin Teichmann martin.teichmann at gmail.com
Mon Feb 10 09:55:11 GMT 2014


Hi List,

I tried to write a mixin class for PyQt4 and, well, failed.
Here is the short version of what I did:

========================
from PyQt4.QtGui import QWidget, QApplication, QLabel

class A(QWidget):
    def sizeHint(self):
        print('mixed in!', self.parent())
        return super().sizeHint()

class B(A, QLabel):
    pass

class C(QLabel, A):
    pass
========================

as you see, my mixin class A inherits from QWidget, as it is supposed
to re-write sizeHint. Now B and C inherit from it and QLabel. You
will say stop! now, you're inheriting from two Qt classes, that's
illegal! Indeed, it is. But actually, from a Qt standpoint, I am only
inheriting from QLabel, which in turn inherits from QWidget anyways.
Let's see what happens using these classes as follows:

========================
app = QApplication([])
b = B()
try:
    b.setText('b')
    b.sizeHint()
except Exception as e:
    print("b", e)
print('-------------------')
c = C()
try:
    c.setText('c')
    c.sizeHint()
except Exception as e:
    print("c", e)
========================

this scipt now outputs:

b 'B' object has no attribute 'setText'
-------------------

so, B is a QWidget, not a QLabel, while C is a QLabel,
but the mixed in method is not called (which is expected).
The latter is correct (albeit not desired) behavior, the former
is actually, odd. The reason is, that cpython calculates
B.__base__ incorrectly. I filed an issue on the cpython
issue tracker (http://bugs.python.org/issue20518)
which discusses the details, but it is actually a better
idea to simply get around using __base__ at all,
and use the method resolution order (MRO) instead. I did
that, see the attached patch. The idea of the patch is
as follows:  instead of taking the first base class to find the PyQt
class to inherit from, go through the MRO in order to
find the most specialized PyQt class.

With this patch, the same script gives the following
output:

mixed in! None
-------------------

so, everything works as expected.

Greetings

Martin

The promised patch follows

--- siplib.c.in.orig    2014-02-08 17:02:29.000000000 +0100
+++ siplib.c.in    2014-02-09 13:15:38.000000000 +0100
@@ -9361,11 +9361,13 @@

     /*
      * If we don't yet have any extra type specific information (because
we are
-     * a programmer defined sub-class) then get it from the (first)
super-type.
+     * a programmer defined sub-class) then get it from the super-types.
      */
     if (self->type == NULL)
     {
-        PyTypeObject *base = ((PyTypeObject *)self)->tp_base;
+        PyObject *mro = ((PyTypeObject *)self)->tp_mro;
+        PyObject *base;
+        int i, n = PyTuple_GET_SIZE(mro);

         /*
          * We allow the class to use this as a meta-type without being
derived
@@ -9374,8 +9376,15 @@
          * from this meta-type.  This condition is indicated by the
pointer to
          * the generated type structure being NULL.
          */
-        if (base != NULL && PyObject_TypeCheck((PyObject *)base,
(PyTypeObject *)&sipWrapperType_Type))
-            self->type = ((sipWrapperType *)base)->type;
+
+        for (i = 0; i < n; i++) {
+            base = PyTuple_GET_ITEM(mro, i);
+            if (PyObject_TypeCheck(base, (PyTypeObject
*)&sipWrapperType_Type)) {
+                self->type = ((sipWrapperType *)base)->type;
+                if (self->type && self->type->u.td_py_type == base)
+                    break;
+            }
+        }
     }
     else
     {
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.riverbankcomputing.com/pipermail/pyqt/attachments/20140210/4fbcbd73/attachment.html>


More information about the PyQt mailing list