.. _qt-programming-guidelines: Qt programming guidelines ========================= The following Qt guidelines are intended to ensure compatibility between the supported Qt bindings and Qt major versions. 1. Avoid importing a specific Qt binding directly in Taurus and Taurus plugins. For example, imports such as:: from PyQt5 import QtCore from PyQt5 import QtGui from PyQt5 import QtWidgets Should be replaced by:: from taurus.external.qt import Qt from taurus.external.qt import QtCore from taurus.external.qt import QtGui from taurus.external.qt import QtWidgets .. note:: this guideline applies to code which is part of the taurus module or its plugins. For end-user applications that use taurus, this rule may not apply, as mentioned in `TEP18`_: *For an end-user application based on taurus* it is probably better to import directly from a specific binding (a specific supported binding can be selected) and let taurus to adapt to that choice. In this way, one can write idiomatic code that better matches the chosen binding. Using the ``taurus.external.qt`` shim is also possible if one wants to make the code binding-agnostic, but in that case one must keep in mind that the resulting code will be less idiomatic and that the shim's API may be eventually altered to better fit with taurus own requirements (and that those changes may not be aligned with the application needs). 2. Taurus 5 supports both Qt5 and Qt6 through PyQt and PySide bindings. When porting legacy Qt4 code, remember that Taurus no longer supports Qt4. In particular, old code relying on the PyQt4 API v1 types must be updated to use normal Python objects (the behavior formerly known as `PyQt API v2`_). - Use standard python strings (e.g., use :class:`str` for Qt strings instead of :class:`QString`). Code like:: my_string = Qt.QString(" hello ") my_string2 = my_string.trimmed() Should be replaced by:: my_string = " hello " my_string2 = my_string.strip() - Do not use :class:`QVariant`. Code like:: def setData(self, index, qvalue, role=Qt.Qt.EditRole): value = qvalue.toString() # this assumes qvalue to be a :class:`QVariant` self.buffer[index.column()] = value def data(self, index, role=Qt.Qt.DisplayRole): value = self.buffer[index.column()] if role == Qt.Qt.DisplayRole: return Qt.QVariant(value) else: return Qt.QVariant() Should be replaced by:: def setData(self, index, value, role=Qt.Qt.EditRole): self.buffer[index.column()] = value # value is already a python object def data(self, index, role=Qt.Qt.DisplayRole): value = self.buffer[index.column()] if role == Qt.Qt.DisplayRole: return value else: return None For backwards-compatibility reasons, `taurus.external.qt.QtCore` defines `QVariant`, `from_qvariant()` and `to_qvariant()`, but they are deprecated and should not be used anymore. 3. Use "new-style" signals. Code that still uses "old style" signals like the following:: class MyWidget(Qt.QWidget): def foo(self): self.connect(self, Qt.SIGNAL('mySignal(int)', self.bar)) self.emit(Qt.SIGNAL('mySignal(int)', 123)) Must be replaced by:: class MyWidget(Qt.QWidget): mySignal = Qt.pyqtSignal(int) def foo(self): self.mySignal.connect(self.bar) self.mySignal.emit(123) 4. The `taurus.external.qt.compat` module defines some convenience utilities that help in writing Qt-binding agnostic code 5. Use of :class:`taurus.qt.qtgui.application.TaurusApplication` instead of :class:`QApplication` is recommended (it takes care of various initialization and exit tasks that are convenient). .. _`PyQt API v2`: http://pyqt.sourceforge.net/Docs/PyQt4/incompatible_apis.html .. _TEP18: https://gitlab.com/taurus-org/taurus/-/blob/develop/doc/source/tep/TEP18.md