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