<div dir="ltr"><div class="gmail_quote"><div dir="ltr">Hi List,<div><br></div><div><div>I tried to write a mixin class for PyQt4 and, well, failed.</div></div><div>Here is the short version of what I did:</div><div><br></div>
<div>========================</div>
<div><div>from PyQt4.QtGui import QWidget, QApplication, QLabel</div><div><br></div><div>class A(QWidget):</div><div>    def sizeHint(self):</div><div>        print('mixed in!', self.parent())</div><div>        return super().sizeHint()</div>

<div><br></div><div>class B(A, QLabel):</div><div>    pass</div><div><br></div><div>class C(QLabel, A):</div><div>    pass</div><div><div>========================</div><div><br></div><div>as you see, my mixin class A inherits from QWidget, as it is supposed</div>

<div>to re-write sizeHint. Now B and C inherit from it and QLabel. You</div><div>will say stop! now, you're inheriting from two Qt classes, that's </div><div>illegal! Indeed, it is. But actually, from a Qt standpoint, I am only </div>

<div>inheriting from QLabel, which in turn inherits from QWidget anyways.</div><div>Let's see what happens using these classes as follows:</div><div><br></div><div>========================<br></div>
<div><div>app = QApplication([])</div><div>b = B()<br></div><div>try:</div><div>    b.setText('b')</div><div>    b.sizeHint()</div><div>except Exception as e:</div><div>    print("b", e)</div><div>
print('-------------------')</div><div>c = C()</div><div>try:</div><div>    c.setText('c')</div><div>    c.sizeHint()</div><div>except Exception as e:</div><div>    print("c", e)</div><div><div>
========================<br>
</div><div><br></div><div>this scipt now outputs:</div><div><br></div><div><div>b 'B' object has no attribute 'setText'</div><div>-------------------</div><div><br></div><div>
so, B is a QWidget, not a QLabel, while C is a QLabel,</div><div>but the mixed in method is not called (which is expected).</div><div>The latter is correct (albeit not desired) behavior, the former</div><div>
is actually, odd. The reason is, that cpython calculates</div><div>B.__base__ incorrectly. I filed an issue on the cpython</div><div>issue tracker (<a href="http://bugs.python.org/issue20518" target="_blank">http://bugs.python.org/issue20518</a>) </div>

<div>which discusses the details, but it is actually a better<br>idea to simply get around using __base__ at all,<br>and use the method resolution order (MRO) instead. I did<br>that, see the attached patch. The idea of the patch is <br>
as follows:  instead of taking the first base class to find the PyQt</div><div>class to inherit from, go through the MRO in order to<br>find the most specialized PyQt class. <br></div><div><br></div><div>With this patch, the same script gives the following</div>
<div>
output:</div><div><br></div><div><div>mixed in! None</div><div>-------------------</div><div></div><div><br></div><div>so, everything works as expected.<br></div></div><div><br></div><div>Greetings</div><div><br></div><div>
Martin</div><div><br>
</div><div>The promised patch follows</div><div><br></div>--- siplib.c.in.orig    2014-02-08 17:02:29.000000000 +0100<br>+++ <a href="http://siplib.c.in">siplib.c.in</a>    2014-02-09 13:15:38.000000000 +0100<br>@@ -9361,11 +9361,13 @@<br>
 <br>     /*<br>      * If we don't yet have any extra type specific information (because we are<br>-     * a programmer defined sub-class) then get it from the (first) super-type.<br>+     * a programmer defined sub-class) then get it from the super-types.<br>
      */<br>     if (self->type == NULL)<br>     {<br>-        PyTypeObject *base = ((PyTypeObject *)self)->tp_base;<br>+        PyObject *mro = ((PyTypeObject *)self)->tp_mro;<br>+        PyObject *base;<br>+        int i, n = PyTuple_GET_SIZE(mro);<br>
 <br>         /*<br>          * We allow the class to use this as a meta-type without being derived<br>@@ -9374,8 +9376,15 @@<br>          * from this meta-type.  This condition is indicated by the pointer to<br>          * the generated type structure being NULL.<br>
          */<br>-        if (base != NULL && PyObject_TypeCheck((PyObject *)base, (PyTypeObject *)&sipWrapperType_Type))<br>-            self->type = ((sipWrapperType *)base)->type;<br>+<br>+        for (i = 0; i < n; i++) {<br>
+            base = PyTuple_GET_ITEM(mro, i);<br>+            if (PyObject_TypeCheck(base, (PyTypeObject *)&sipWrapperType_Type)) {<br>+                self->type = ((sipWrapperType *)base)->type;<br>+                if (self->type && self->type->u.td_py_type == base)<br>
+                    break;<br>+            }<br>+        }<br>     }<br>     else<br>     {<br>
</div></div></div></div></div></div></div><br></div>