I am creating a custom widget that inherits from QLabel
, and I would like to have a property on my widget to represent how the data must be formatted when presenting to the user.
For that I am trying to use Q_ENUMS
, but I'm not having much success. I can get the property to show in Designer, but the UI file saved shows the enum as PyDMLabel::STRING
and not as I would expect DisplayFormat::STRING
.
Here is my code for the widget:
class PyDMLabel(QLabel, PyDMWidget):
class DisplayFormat:
DEFAULT = 0
STRING = 1
DECIMAL = 2
EXPONENTIAL = 3
HEX = 4
BINARY = 5
Q_ENUMS(DisplayFormat)
"""
A QLabel with support for Channels and more from PyDM
Parameters
----------
parent : QWidget
The parent widget for the Label
init_channel : str, optional
The channel to be used by the widget.
"""
def __init__(self, parent=None, init_channel=None):
QLabel.__init__(self, parent)
PyDMWidget.__init__(self, init_channel=init_channel)
self.setTextFormat(Qt.PlainText)
self.setTextInteractionFlags(Qt.NoTextInteraction)
self.setText("PyDMLabel")
self._display_format_type = PyDMLabel.DisplayFormat.DEFAULT
@pyqtProperty(DisplayFormat)
def displayFormat(self):
return self._display_format_type
@displayFormat.setter
def displayFormat(self, new_type):
if self._display_format_type != new_type:
self._display_format_type = new_type
What is the correct way to deal with Q_ENUMS
and PyQt?
UPDATE:
As of PyQt-5.11, Q_ENUMS/Q_FLAGS
have been deprecated, so that Q_ENUM/Q_FLAG
should be used instead. This does not affect the original solution given below, which will still work with either Q_ENUM
or Q_ENUMS
. However, in PyQt6, these functions are no longer available and have been replaced by pyqtEnum. This means user-defined enums must now be subclasses of enum.Enum and be decorated accordingly. For consistency with the built-in APIs, they should probably also be fully scoped (i.e. so that PyDMLabel.STRING
is no longer allowed), but this is not strictly necessary:
from enum import Enum
from PyQt6.QtCore import pyqtEnum
@pyqtEnum
class DisplayFormat(Enum):
DEFAULT = 0
STRING = 1
DECIMAL = 2
EXPONENTIAL = 3
HEX = 4
BINARY = 5
class PyDMLabel(QLabel, PyDMWidget):
DisplayFormat = DisplayFormat
Original Solution (PyQt5):
In order for Qt (Designer) to see an enum, PyQt has to add it to the meta-object of the custom class. So it could never be referred to by Qt as DisplayFormat::STRING
.
In Qt, enums declared in the class scope expose their constants as members of the class. So for example, the QComboBox class defines an InsertPolicy enum, and the constants can be referred to like this: QComboBox::InsertAtTop
. So in that respect, the behaviour of PyQt enums in Qt Designer plugins is exactly as expected, since the ui file shows PyDMLabel::STRING
.
However, getting fully equivalent behaviour in Python code requires some extra work. The nearest I could come up with is this:
class DisplayFormat:
DEFAULT = 0
STRING = 1
DECIMAL = 2
EXPONENTIAL = 3
HEX = 4
BINARY = 5
class PyDMLabel(QLabel, PyDMWidget, DisplayFormat):
DisplayFormat = DisplayFormat
Q_ENUM(DisplayFormat)
This will still result in Qt Designer using PyDMLabel::STRING
(as expected). But Python code can now access the constants in any of these ways:
PyDMLabel.STRING
PyDMLabel.DisplayFormat.STRING
DisplayFormat.STRING
And in fact, if you don't mind losing the second of these options, you could simplify things even further to this:
class DisplayFormat:
DEFAULT = 0
...
class PyDMLabel(QLabel, PyDMWidget, DisplayFormat):
Q_ENUM(DisplayFormat)
PS:
In PyQt5, it does not matter what the type of the enum class is - so it could be a subclass of int
, a plain object
, or even a python enum
.