Menu

How to override a virtual C++ function with PythonQt

2015-09-26
2015-09-30
  • Allen C Kempe

    Allen C Kempe - 2015-09-26

    I am trying to add Python scripting capability to my Qt application. So far, I have been able to write wrappers for many classes. However, one class has a virtual 'init()' function which can be overridden by a derived class if necessary.

    // Qt C++ class:

    class MyClass : public QObject
    {
     Q_OBJECT
     public:
      MyClass() 
      {
       init(); // perform user initialization
      }
      virtual void init() {}
    };
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    The MyClass constructor calls the virtual function 'init()'. It is intended that when MyClass is sub-classed that the init function can be overridden if necessary to provide user initialization.
    
    I wish to use 'MyClass' in PythonQt so I have written a wrapper class:
    
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    class MyClass_Wrapper : public QObject
    {
     Q_OBJECT
    public Q_SLOTS:
     // add a constructor
     MyClass* new_MyClass() {return new MyClass(); }
     void init(MyClass* mc) { mc->init(); }
    };
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    The class 'MyClass' is now registered with PythonQt:
    
        PythonQt::self()->registerCPPClass("MyClass",NULL,"myModule", PythonQtCreateObject<MyClass_Wrapper>);
    
    Now, I create a Python script to use 'MyClass':
    
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    import myModule
    Class TestClass(MyClass) :
      def init(self):
      # init() is called exactly once at the beginning to do
      # any necessary configuration.
      print "Inside init(self)"
      .
      .
      .
      return
    

    The problem is that the print statement (and the following code) is never executing. The init() function is not being overridden by the PythonQt code.

     

    Last edit: Allen C Kempe 2015-09-26
  • Florian Link

    Florian Link - 2015-09-27

    PythonQt can unfortunately not do 'magic'... If you want to override a virtual method from Python, then you need a shell class which derives the base C++ class and forwards the virtual method call to Python.
    the pythonqt_generator can do this, but it is not as easy to use as writing your own wrappers. You need to create an xml file that defines your typesystem and there is no good documentation about this. There are typesystems for the whole qt library, so you can copy from that.

     
  • Allen C Kempe

    Allen C Kempe - 2015-09-27

    Thanks! I'm not sure what you mean by 'shell class'. I have found some posts related to
    'pythonqt_generator' so I'll continue my investigation there.

     
  • Florian Link

    Florian Link - 2015-09-27

    A shell class is an extra class which derives from a C++ class and overrides all virtual methods and redirects them to Python. This is the only way to allow inheritance of Python classes from C++ classes, at least I don't know any other way this could be done. The pythonqt generator will generate such shell classes for any wrapped class that is in the typesystem and has virtual methods.

     
  • Allen C Kempe

    Allen C Kempe - 2015-09-28

    OK: I'm sort of hung up on creating the typesystem file. I found this QtJambi doc: http://qtjambi.org/doc/generator_typesystem which explains the use in the QtJambi package. I'm assuming that you robbed the code from tje QtJambi product to create Python code instead of Java code from Qt classes.

    So far, I've gotten this output but there don't seem to be any files generated:
    allen@ubuntu:~/Projects/DecoderPro/fossil/DecoderPro_app/LayoutEditor$ ../../../../PythonQt3.0/generator/pythonqt_generator --include-paths=/home/allen/Projects/DecoderPro/fossil/DecoderPro_app/libPr3/ abstractautomaton.h typesystem_abstractautomat.xml
    Please wait while source files are being generated...
    Parsing typesystem file [typesystem_abstractautomat.xml]
    PreProcessing - Generate [.preprocessed.tmp] using [abstractautomaton.h] and include-paths [/home/allen/Projects/DecoderPro/fossil/DecoderPro_app/libPr3/]


    Using QT at: /usr/local/Trolltech/Qt-4.8.3

    Building model using [.preprocessed.tmp]
    WARNING(MetaJavaBuilder) :: enum 'QMetaType::Type' does not have a type entry or is not an enum
    WARNING(MetaJavaBuilder) :: unknown baseclass for 'Runnable': 'QThread'
    QByteArray.contains(const char * a) const mostly equal to QByteArray.contains(char c) const
    QByteArray.contains(char c) const mostly equal to QByteArray.contains(const char * a) const
    WARNING(MetaJavaBuilder) :: signature 'hasRegisteredConverterFunction()' for function modification in 'QMetaType' not found. Possible candidates:
    WARNING(MetaJavaBuilder) :: signature 'hasRegisteredDebugStreamOperator()' for function modification in 'QMetaType' not found. Possible candidates:
    WARNING(MetaJavaBuilder) :: signature 'hasRegisteredComparators()' for function modification in 'QMetaType' not found. Possible candidates:
    WARNING(MetaJavaBuilder) :: signature 'registerComparators()' for function modification in 'QMetaType' not found. Possible candidates:
    WARNING(MetaJavaBuilder) :: signature 'registerConverter()' for function modification in 'QMetaType' not found. Possible candidates:
    WARNING(MetaJavaBuilder) :: signature 'registerDebugStreamOperator()' for function modification in 'QMetaType' not found. Possible candidates:
    Classes in typesystem: 86
    Generated:
    - header....: 2 (2)
    - impl......: 2 (2)
    - modules...: 0 (0)
    - pri.......: 1 (0)

    Done, 8 warnings (1011 known issues)

     
  • Allen C Kempe

    Allen C Kempe - 2015-09-28

    Apparently one must specify the --output-directory option for it to generate any output! So now I have to figure out what to do with the output.

     
  • Allen C Kempe

    Allen C Kempe - 2015-09-30

    OK, I've finally got the generator figured out and now I can override a virtual function. However there's another problem. The class cannot run in the GUI thread so the 'start' method moves it to another thread. This works fine for straighte C++ classes but now when the class is wrapped:
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    include "myclass.h"

    include <QDebug>

    include <QThread>

    MyClass::MyClass(QObject *parent) :
    QObject(parent)
    {
    qDebug() << "MyClass constructor";
    }

    void MyClass::init()
    {
    qDebug() << "Init was not overridden";
    }
    void MyClass::start()
    {
    QThread* thread = new QThread;
    connect(thread, SIGNAL(started()), this, SLOT(run()));
    moveToThread(thread);
    thread->start();
    }

    void MyClass::run()
    {
    init();
    }
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    I'm thinking that I may have to run the python script interpretor in another thread.

     
  • Allen C Kempe

    Allen C Kempe - 2015-09-30

    What does "Post awaiting moderation." mean?

     
  • Allen C Kempe

    Allen C Kempe - 2015-09-30

    The solution to my problem is to use the PythonQt generator application to create the wrapper functions. I've created a small Qt project "TestPython" that should serve as an example on how to do it.

     

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.