Need help with custom enum properties in Qt6 Designer
Ivan Sinkarenko
ivan.sinkarenko at cern.ch
Thu Mar 7 16:02:34 GMT 2024
Completely blind guessing here, since I'm not familiar with PyQt C++
side, but what if:
- Chimera::_py_enum_types contained mapping not only to C++ name, but
also type id
- Chimera::registerPyEnum the would insert both items into the hash,
generating type id using qRegisterMetaType() (assuming that it's
possible to get hold of corresponding QMetaType instance here)
- The lookup inside Chimera::parse_py_type would extract both name and
value, and use assign "metatype = QMetaType(metaTypeId)" instead of
"QMetaType(QMetaType::Int)"
Alternatively, it's probably possible to not track int IDs, just do:
- QMetaType::registerType()
<https://doc.qt.io/qt-6/qmetatype.html#registerType> in
Chimera::registerPyEnum
- Use QMetaType::fromName(cpp_qualname) in Chimera::parse_py_type
Cheers,
Ivan
On 05/03/2024 17:49, Phil Thompson wrote:
> The problem is 4. For Qt6 PyQt has to use qMetaTypeId() to register
> the Qt enums. This is done statically with inline calls. As it's
> template based I don't know how to do the equivalent for dynamically
> created Python enums. I suspect that this is the missing step.
>
> Phil
>
> On 04/03/2024 15:58, Ivan Sinkarenko wrote:
>> Hi Phil,
>>
>> Thanks for the info. Do you think this could be addressed in future
>> releases? My project has a frequent use of designer plugins that have
>> custom enum properties, so I would be interested to solve it.
>> Potentially I could give you some assistance?
>>
>> I've tried to investigate the flow myself a bit, here's what I found:
>>
>> 1. When setting a new value, QMetaProperty::write() is called
>> 2. It recognizes that it receives a new int value, and tries to
>> convert it to the target type
>> 3. Target type is derived from a meta type, referenced by the meta
>> property, through a call "QMetaType
>> t(mobj->d.metaTypes[data.index(mobj)]);"
>> 4. This target type for some reason has ID = 0 (i.e. UnknownType, as
>> defined by QMetaType::Type)
>> 5. Conversion fails early, rejecting UnknownType, chained through
>> QMetaProperty::write() -> QVariant::convert() ->
>> QMetaType::canConvert()
>> 6. QMetaProperty::write() returns early.
>>
>> Thanks,
>> Ivan
>>
>> On 01/03/2024 18:50, Phil Thompson wrote:
>>> On 01/03/2024 17:24, Ivan Sinkarenko wrote:
>>>> Hi everybody,
>>>>
>>>> I've seen there's been a lot happening with enums in PyQt6,
>>>> and I've managed to adapt to most of the problems, except one.
>>>> I cannot figure out how to make custom enums work in custom widgets
>>>> that are exposed to Qt Designer.
>>>>
>>>> This is my simplified code:
>>>>
>>>> ----------------------------------------------------------------------
>>>> import enum
>>>> from PyQt6 import QtDesigner, QtGui, QtWidgets, QtCore
>>>>
>>>> class MyWidget(QtWidgets.QWidget):
>>>>
>>>> @QtCore.pyqtEnum
>>>> class MyEnum(enum.IntEnum):
>>>> ONE = enum.auto()
>>>> TWO = enum.auto()
>>>>
>>>> def __init__(self, *args, **kwargs) -> None:
>>>> super().__init__(*args, **kwargs)
>>>> self._prop = MyWidget.MyEnum.TWO
>>>>
>>>> @QtCore.pyqtProperty(MyEnum)
>>>> def prop(self):
>>>> print(f'Getting property val {self._prop}')
>>>> return self._prop
>>>>
>>>> @prop.setter
>>>> def prop(self, new_val):
>>>> print(f'Setting new property val {new_val}')
>>>> self._prop = new_val
>>>>
>>>> class Plugin(QtDesigner.QPyDesignerCustomWidgetPlugin):
>>>>
>>>> def name(self):
>>>> return "MyWidget"
>>>>
>>>> def group(self):
>>>> return "Buttons"
>>>>
>>>> def isContainer(self):
>>>> return False
>>>>
>>>> def createWidget(self, parent):
>>>> return MyWidget(parent)
>>>>
>>>> def icon(self):
>>>> return QtGui.QIcon()
>>>>
>>>> def toolTip(self):
>>>> return ""
>>>>
>>>> def whatsThis(self):
>>>> return ""
>>>>
>>>> def includeFile(self):
>>>> return "pyqt6_enum_designer_poc_plugin"
>>>> ----------------------------------------------------------------------
>>>>
>>>> I want an enum property to be displayed in the PropertySheet.
>>>> It's correctly represented by a combobox showing ONE and TWO as
>>>> available options.
>>>>
>>>> TWO is correctly selected by default. However, when in Property sheet
>>>> I try to set it to ONE,
>>>> as soon as I click away from there, it's reset back to TWO. In fact,
>>>> the setter does not get called,
>>>> since the message inside is never printed. (Getter message is being
>>>> printed).
>>>>
>>>> Properties do work without issues, if they have built-in types,
>>>> such as QColor,
>>>> or even native enums, such as Qt.Orientation.
>>>>
>>>> To try this code, you can save this code to
>>>> "pyqt6_enum_designer_poc_plugin.py" and run like so:
>>>> PYQTDESIGNERPATH=$(pwd) designer
>>>>
>>>> I use Qt Designer 6.6.2 and:
>>>> - PyQt6 6.6.1
>>>> - PyQt6-Qt6 6.6.2
>>>> - PyQt6-sip 13.6.0
>>>>
>>>> (Also tried with PyQt6-6.5.3 PyQt6-Qt6-6.5.3, same result)
>>>>
>>>> There used to be a way to make this work in PyQt5, but in PyQt6 I
>>>> tried multiple approaches without luck.
>>>> If anybody knows the correct path, that would be very appreciated!
>>>>
>>>> Thanks,
>>>> Ivan
>>>
>>> This is ringing a faint bell. I looked at it a long time ago and
>>> found that Designer was just not making the normal call to write the
>>> changed property value, maybe due to some sort of "optimisation".
>>> There may be something wrong in the way that PyQt creates the
>>> QMetaObject for the Python class but I never managed to get to the
>>> bottom of it.
>>>
>>> Sorry for not being more helpful.
>>>
>>> Phil
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.riverbankcomputing.com/pipermail/pyqt/attachments/20240307/f10e2590/attachment.htm>
More information about the PyQt
mailing list