[PyQt] Bug in QTableView.setModel()

Brian Kelley kelley at eyesopen.com
Tue Jan 8 14:25:35 GMT 2008


On Jan 8, 2008, at 7:12 AM, Aaron Digulla wrote:

> Quoting Phil Thompson <phil at riverbankcomputing.co.uk>:
>
>> I'm happy to consider ways to make this more obvious (even if it's  
>> just a
>> better FAQ), but I'm not happy about departing from the Qt API and
>> introducing inconsistencies in fundamental behaviour.
>
> Then let me put it this way: If I wanted to care about who calls the  
> dtor, I'd be using C++ and not Python. ;-)
>
> As a Python developer, I expect PyQt to behave like python code: As  
> long as anyone (my code, PyQr or Qt) still has references to some  
> object, the dtor must not be called, no matter what and PyQt as a  
> *python* wrapper for Qt should follow the Python rules, not the Qt  
> rules, especially not in a case where there is no conflict.

So you have never written to a closed pipe?  This is an equivalent  
situtation, you have a python reference to a destructed (or closed in  
this case) object.

Trust me, I know what you are saying here, but I agree with Phil that  
in this case it is difficult to have your cake and eat it too.  When  
wrapping C++ objects in python in any complicated enough system, be it  
using sip or swig you really have no choice when the C++ destructor is  
called.  Consider a simple callback:

def callback():
       f = QtGui.QFrame()
       f.show()

button = QtGui.QPushButton("clickme")
button.connect(QtCore.SIGNAL("clicked()"), callback)

In this case "f" is created and immediately goes out of scope in the  
function, and yet the QFrame is shown and not deleted.  This is  
because "f" now belongs to the Qt event loop.  This, in itself, should  
show why you can't control when constructors and destructors are called.

or suppose you hang onto a widget ( say b = QtGui.QPushButton ) that  
lives in a widget that the user closes and Qt deletes.  b is now a  
dangling reference to a deleted C++ object!  Trust me, you cannot  
prevent this and you probably really don't want to, anyway.

The reason that the model is different when using QModel is that you  
*MUST* control when the object is destructed because it is shared.

Let's say that you make this change in setModel but you use a model  
created in C++ by some other mechanism (yes, PyQt doesn't care where  
the model comes from, python or C++).  By making this change you may  
destruct the model accidentally but because it came from C++   
somewhere, other C++ objects know nothing about Python's garbage  
collection and this may lead to nasty, nasty problems.

So what is Phil to do?  The best solution is to be consistent as  
possible with the Qt API since that is where these behaviors are  
documented.

The other option is to write a GUI completely with python, or at least  
with python in mind and you are most welcome to do that or you can  
just make a subclass yourself (you essentially did this already)

class MyTable(QtGui.QTableView):
    def setModel(self, m):
         self.model = m
         QtGui.QTableView.setModel(m)

There, now you have the behavior you want, and it is even explicitly  
and not implicitly set which is quite pythonic.

Brian


More information about the PyQt mailing list