11. Frequently Asked Question

11.1. Python Bindings

11.1.1. How do I use the widget?

The typesettings file (the XML in the Input section), includes the name of the Python package. Next, you need to know the name of the class, which is the same as in C++. A very simple application could be:

import sys
from PySide2.QtWidgets import QApplication
from QeWidgetExample import QeDartBoard

app = QApplication()
dartboard = QeDartBoard()
dartboard.show()
sys.exit(app.exec_())

11.1.2. If I connect a signal to one of the widget’s slots, it does nothing.

Most likely, the bindings.h file does not include the sentence to make visible the classifiers (signal, slots keywords in the C++ header).

#define QT_ANNOTATE_ACCESS_SPECIFIER(a) __attribute__((annotate(#a)))

Remember Shiboken2 needs to pass this instruction to the C++ preprocessor in order to generate the code.

Source: https://blog.basyskom.com/2019/using-shiboken2-to-create-python-bindings-for-a-qt-library/

11.1.3. PySide::Signal namespace not found

Compiler outputs an error like this one:

error: ‘PySide::Signal’ has not been declared

Most likely, you have not activated in Shiboken2 executable, the option that enables support for signal and slots. Shiboken2 is a fully capable binding generator, and Qt’s is one of the things it does. PySide2 support is not enabled by default. Check if your generation script includes this option for the Shiboken2 executable:

--enable-pyside-extensions

Source: https://github.com/ros-visualization/qt_gui_core/issues/142

11.1.4. If I connect a signal to one of the widget’s slots and trigger it, the application crashes with a Segmentation Fault.

Most likely, you have not activated in Shiboken2 executable, the option that enables support for signal and slots. Shiboken2 is a fully capable binding generator, and Qt’s is one of the things it does. PySide2 support is not enabled by default. Check if your generation script includes this option for the Shiboken2 executable:

--enable-pyside-extensions

11.1.5. A symbol is missing (a method), and it is from my widgets:

This happens when you generate the code for all the widgets you want to, but forget to include them in the final linking of the Python shared library that acts as a Python Package. Check in the wscript that does the compilation, if all <widget_class_name>_wrapper.cpp are listed as target for the generation task, and listed as sources in the compilation task.

11.1.6. Application crash on startup, indicates Qt_5_6 version is used, but it was compiled against Qt_5_14

DevEnv comes with PyQt installed (provided by anaconda) and it is used by multiple applications. But the compilation procedure will resolve against Qt5.14, if every dependency is declared.

In your project, you have to manually configure the dependencies, and then use them in widgets, and in bindings.

For example, in the project (first) wscript:

def configure(cnf):
    for pkg in 'Qt5Core Qt5Gui Qt5Widgets'.split():
        cnf.check_cfg(package=pkg, uselib_store=pkg)

And then in your wscript of the widget:

declare_qt5cshlib( target='widgetexample-widgets',
                   use='QT5CORE QT5GUI QT5WIDGETS'
)

These are just snipper of code. Take a look into the example in https://gitlab.eso.org/ahoffsta/cut/-/tree/master/examples/widget_example for complete wscripts.

11.1.7. My application crashes, in a section that is C++, how can I get a back trace

You need to start the python interpeter with GDB:

gdb
file python
run <path_to_python_script>

When the application crashes, type in the GDB console:

bt

11.2. Widget Library

11.2.1. Why the separation between the widget and plugin library

The Plugin library is only used by Qt Designer. When compiling an application, only the header file and the widgets library themselves are only needed. The Qt Designer Plugins only provides presentation in the Qt Designer for the plugin, and a piece of XML to be included in the UI File. This piece of XML include the header and Class for the include statement.

Also, these library are expected to be found in different places. Qt Designer needs them to be in a designer directory to find them, while the widgets needs to be in LD_LIBRARY_PATH for the application to find them. Certainly one can point LD_LIBRARY_PATH to $INTROOT/lib64/designer, but this is not encouraged.

This way you can also debug and developed modularly the widgets. When you have the widget code ready, then you focus on the plugin aspect of it. Since plugin code is very similar to all plugin, then this is very straightforward. As a recommendation, I would suggest to add a fourth module: a showcase application, that allows you to see and interact with the widgets. Think of it as a “demo”. This showcase application should only link to the widgets module, but its UI file should be constructed using Qt Designer, using the provided plugins. If the plugin is not ready, you can use `Widget promotion`_ to transform one widget into another that does not have a plugin.

11.2.2. The widget plugin does not load

Try using designer command. This will start the Qt Designer in standalone mode. In the main window Menu > Help > Plugins, the application will show you a dialog with information of the plugins found, and why they failed to load. If the plugin is not listed, then the QT_PLUGIN_PATH is not set.:

QT_PLUGIN_PATH=$INTROOT/lib64:$QT_PLUGIN_PATH designer

11.2.3. What method of library is used for bindings

Shiboken2. At the moment this is not supported by wtools, but support will come soon.

11.2.4. Why is there a header called “libraryname.h”

While creating python bindings, a module name must be given to the resulting python module (bindings.xml file has this information).

The widgets plugin return through the name() method the name of the class used to create the widget Object. The includeFile() method returns the C++ header file to put into the generated code #include statement.

uic-qt5 –the tool that generates code from UI file– uses the same plugin methods to get the name of the include and import statements. If the names differs (between the Python bindings module and the C++ include), either the C++ or Python version of the resulting generated code for the UI file would fail.

Example 1: We do not have the libraryname.h file:

  • C++ Class name: widgetnameone

  • C++ header file: widgetnameone.hpp

  • Python Class name: widgetnameone

  • Python module: libraryname

Then, the resulting include and import statements would be:

#include <widgetnameone.hpp>
From widgetnameone.hpp import widgetnameone

pyside2-uic cuts the last two characters from the C++ header file, and uses the name of the file as the module it should include. In this case, the python import fails.

Example 2: We create the libraryname.h file, and include other widgets here:

  • C++ Class name: widgetnameone

  • C++ header file: libraryname.h

  • Python Class name: widgetnameone

  • Python module: libraryname

Then, the resulting include and import statements would be:

#include <libraryname.h>
From libraryname import widgetnameone

11.2.5. libraryname.h? why not libraryname.hpp?

Code generation in Qt PySide2, only uses names that ends in .h or without any extension. If the file has any other suffix, then the whole string is used. This would cause that under Python, the UIC generated code would look like:

from libraryname.hpp import WidgetNameOne

11.2.6. I have created a templated QObject, but it does not compile. What am I doing wrong?

Error

An important limitation of QObject is that it cannot include templates, either in the class or in a method.

Developers may mitigate the need for templates by using QVariant, which is a class that acts as a union, but is aware of its datatype and can be queried about it.

Also, a series of signals or slots required, each with the needed datatype can be created.

Source: https://stackoverflow.com/questions/4397478/qt-templated-q-object-class

11.2.7. paintEvent() is not called in python

This can happen to any xyzEvent() method. The cpp widget works, but the python bound version does not respond to events.

When overriding a xyzEvent(QEvent *) method in cpp, the python bindings needs to be told about this. Adapt this example to you typesystem file:

<object-type name="AWidget">
<modify-function signature="resizeEvent(QResizeEvent*)">
    <modify-argument index="1" invalidate-after-use="yes">
        <rename to="event"/>
    </modify-argument>
</modify-function>
</object-type>

11.3. Qt in General

11.3.1. How to start a process and monitor if it is still running?

QProcess is the class you are looking for. It allows to start a second process as a child to your QApplication.

It can start an executable. It has a signal that changes when the state of the process changes. You can use it to changes the state of a QLed or QLabel to indicate was is the current state of the monitored process. This is particularly useful when a console process is launched, and it has no GUI.

It is recommended to use QProcess.start() instead of QProcess.startDetached(). The second one does not monitor not update the signal.

If your use case needs to keep the process running after your exit the QApplication , add this to the class that holds the QProcess object:

def __del__(self):
    # This allows a process started by QProcess.start()
    # to be detached when the QApplication exits.
    self._qprocess_object.setProcessState(QProcess.NotRunning)

This will detach the second process upon exiting the QApplication.

NOTE: details of implementation at https://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/io/qprocess.cpp#n1311. Here the reader can see that only when the second process state is different from NotRunning, the kill() command is executed.