Menu

subclassing Qt classes in Python

Help
johnw
2013-08-21
2013-08-22
  • johnw

    johnw - 2013-08-21

    Hi. I'm wondering if it's possible to write Python classes that inherit from Qt (QObject) classes. In particular, I want to specialise QAbstractItemModel in python to provide a custom model to QTreeView to display a nested Python structure. It seems Python is letting me write the subclass, as in:

    from PythonQt.Qt import QAbstractItemModel
    
    class MyModel(QAbstractItemModel):
    
      def index(self, row, column, parent):
         ...
    
      ...
    

    and even lets me instantiate MyModel without complaint, as in:

      m = MyModel()
    

    and lets me call python methods, but fails if I call methods inherited from QAbstractModel, as in:

    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "c:/projects/aplab/trunk/APLab\directory/directory_pane.py", line 31, in __init__
        self.setObjectName("foo")
    ValueError: Trying to call 'setObjectName' on a destroyed QAbstractItemModel object
    

    and printing m shows:

    MyModel (QAbstractItemModel at: 0x00000000)
    

    a null pointer, suggesting the object has not been properly instantiated.

    Is this at all possible, and if so, how do I make it work?

    Thanks very much,
    John.

     
  • Florian Link

    Florian Link - 2013-08-21

    It is possible to derive Qt classes from Python using PythonQt.
    Your example code misses a call to the init method of QAbstractItemModel:

    def __init__(self):
        QAbstractItemModel.__init__(self)
    

    But apart from that, you will run into a different problem: PythonQt currently does not support QModelIndex, so it is not possible to create your own models in PythonQt. It would not be very difficult to add correct QModelIndex wrapping to PythonQt, but nobody has done it yet.

    There are other alternatives:

    • you can use QTreeWidget and QTreeWidgetItem to do the same thing
    • you can use QStandardItemModel and fill it with standard items

    regards,
    Florian

     

    Last edit: Florian Link 2013-08-21
  • johnw

    johnw - 2013-08-21

    Thanks for the quick response, Florian. I pushed on this a little further, since QModelIndex does seem (at first blush) to be scriptable, but ran into another issue. The call to QAbstractItemModel.init() fixed the incomplete instantiation, however, since QAbstractItemModel::createIndex(...) and other support methods are protected, they don't seem to be callable from within my Python subclass, so I tried inserting an intermediate C++ Q_OBJECT class deriving from QAbstractItemModel that surfaces a version of createIndex() as a slot and subclassed that in Python. Now I get the following:

    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "c:/projects/aplab/trunk/APLab\directory/directory_pane.py", line 31, in __init__
        AbstractDirectoryItemModel.__init__(self)
    ValueError: No constructors available for AbstractDirectoryItemModel
    

    The intermediate class looks like this:

    class AbstractDirectoryItemModel : public QAbstractItemModel
    {
        Q_OBJECT
    
    public:
        explicit AbstractDirectoryItemModel(QObject* parent = 0);
    
    public slots:
        QModelIndex createIndex(int row, int column, quint32 id) { return QAbstractItemModel::createIndex(row, column, id); }
    };
    

    Any suggestions? Meanwhile, I'll investigate ways to perhaps make QTreeWidgetItem or QStandardItemModel approaches less wasteful.

    Cheers,
    John.

     
  • Florian Link

    Florian Link - 2013-08-21

    You are right, it seems that I do not generate wrappers for protected methods if they are not virtual. This could probably be changed in the wrapper generator, but might have some problems due to protected enums/types that may be used in the methods.

    Regarding your example, you need to register a constructor decorator for your derived class to allow instanciation of the type from Python. Have a look at the generated wrappers to see how that is done or use an extra QObject as decorator class (see the examples).

    Still not sure if you will get that running...

     
  • johnw

    johnw - 2013-08-22

    Florian, I've got the Python AbstractItemModel subclass apparently going, test calls in Python code to it's index() & parent() and other methods correctly build proper-looking QModelIndexes, etc. However, perhaps as I should have expected, calls from a QTreeView on an instance of that python class when it is set as the QTreeView's model are not invoking the Python methods, but rather the methods on it's C++ superclass. Perhaps the only way to rig this is to make the Python class act as a delegate and have the C++ AbstractItemModel subclass explicitly bounce calls through to the Python instance via PythonQtObjectPtr::call(), does that sounds right?

    John.

     
  • Florian Link

    Florian Link - 2013-08-22

    Hm, the problem is that you do not derive from

    PythonQtShell_QAbstractItemModel

    but from QAbstractItemModel. If you create QAbstractItemModel in PythonQt, you get a PythonQtShell_QAbstractItemModel instance, which does the virtual method call magic.

    I think instead of using your own derived AbstractItemModel, you could use QAbstractItemModel and add the missing methods via decorators (you can decorate any class with additional methods in PythonQt).

     
  • johnw

    johnw - 2013-08-22

    Understood, thanks. Unfortunately, the 'missing' method can't be added via a decorator, far as I can see, since it needs to bounce to a protected method in QAbstractItemModel which is not accessible in slot methods in the decorating class. That's the main reason I needed an intermediate C++ class.

    I was trying to avoid using explicit mirroring structures like QTreeWidgetItems as the tree under display has potentially large, lazy subtrees, and QTreeWidgetItem is not sufficiently virtual to permit lazy subclasses, so I'll continue to explore the python-based model delegate idea.

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.