<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>