[prev in list] [next in list] [prev in thread] [next in thread]
List: pykde
Subject: Re: Need help with custom enum properties in Qt6 Designer
From: Ivan Sinkarenko <ivan.sinkarenko () cern ! ch>
Date: 2024-03-07 16:02:34
Message-ID: b46205d3-0eff-43a8-bdfb-9b5b5a989043 () cern ! ch
[Download RAW message or body]
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
[Attachment #3 (text/html)]
<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
Completely blind guessing here, since I'm not familiar with PyQt C++
side, but what if:<br>
<br>
- <span style=""><a class="moz-txt-link-freetext" \
href="Chimera::_py_enum_types">Chimera::_py_enum_types</a></span> contained mapping \
not only to C++ name, but also type id<br>
- <span style=""><a class="moz-txt-link-freetext" \
href="Chimera::registerPyEnum">Chimera::registerPyEnum</a></span> 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)<br>
- The lookup inside <span style=""><a class="moz-txt-link-freetext" \
href="Chimera::parse_py_type">Chimera::parse_py_type</a></span> would extract both \
name and value, and use assign "metatype = QMetaType(metaTypeId)" instead \
of "QMetaType(<span style=""><a class="moz-txt-link-freetext" \
href="QMetaType::Int">QMetaType::Int</a></span>)"<br> <br>
Alternatively, it's probably possible to not track int IDs, just do:
<br>
- <a href="https://doc.qt.io/qt-6/qmetatype.html#registerType" \
moz-do-not-send="true">QMetaType::registerType()</a> in <span style=""><a \
class="moz-txt-link-freetext" \
href="Chimera::registerPyEnum">Chimera::registerPyEnum</a></span><br>
- Use <span style=""><a class="moz-txt-link-freetext" \
href="QMetaType::fromName(cpp_qualname)">QMetaType::fromName(cpp_qualname)</a></span> \
in <span style=""><a class="moz-txt-link-freetext" \
href="Chimera::parse_py_type">Chimera::parse_py_type</a></span><br> <br>
Cheers,<br>
Ivan<br>
<br>
<div class="moz-cite-prefix">On 05/03/2024 17:49, Phil Thompson
wrote:<br>
</div>
<blockquote type="cite" \
cite="mid:4dd7c10738dcf84e5e30a406d29424d5@riverbankcomputing.com">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. <br>
<br>
Phil <br>
<br>
On 04/03/2024 15:58, Ivan Sinkarenko wrote: <br>
<blockquote type="cite">Hi Phil, <br>
<br>
Thanks for the info. Do you think this could be addressed in
future <br>
releases? My project has a frequent use of designer plugins that
have <br>
custom enum properties, so I would be interested to solve it. <br>
Potentially I could give you some assistance? <br>
<br>
I've tried to investigate the flow myself a bit, here's what I
found: <br>
<br>
1. When setting a new value, <a class="moz-txt-link-freetext" \
href="QMetaProperty::write()" moz-do-not-send="true">QMetaProperty::write()</a> is \
called <br> 2. It recognizes that it receives a new int value, and tries to
<br>
convert it to the target type <br>
3. Target type is derived from a meta type, referenced by the
meta <br>
property, through a call "QMetaType <br>
t(mobj->d.metaTypes[data.index(mobj)]);" <br>
4. This target type for some reason has ID = 0 (i.e.
UnknownType, as <br>
defined by <a class="moz-txt-link-freetext" href="QMetaType::Type" \
moz-do-not-send="true">QMetaType::Type</a>) <br>
5. Conversion fails early, rejecting UnknownType, chained
through <br>
<a class="moz-txt-link-freetext" href="QMetaProperty::write()" \
moz-do-not-send="true">QMetaProperty::write()</a> -> <a \
class="moz-txt-link-freetext" href="QVariant::convert()" \
moz-do-not-send="true">QVariant::convert()</a> -> <br>
<a class="moz-txt-link-freetext" href="QMetaType::canConvert()" \
moz-do-not-send="true">QMetaType::canConvert()</a> <br>
6. <a class="moz-txt-link-freetext" href="QMetaProperty::write()" \
moz-do-not-send="true">QMetaProperty::write()</a> returns early. <br>
<br>
Thanks, <br>
Ivan <br>
<br>
On 01/03/2024 18:50, Phil Thompson wrote: <br>
<blockquote type="cite">On 01/03/2024 17:24, Ivan Sinkarenko
wrote: <br>
<blockquote type="cite">Hi everybody, <br>
<br>
I've seen there's been a lot happening with enums in PyQt6,
<br>
and I've managed to adapt to most of the problems, except
one. <br>
I cannot figure out how to make custom enums work in custom
widgets <br>
that are exposed to Qt Designer. <br>
<br>
This is my simplified code: <br>
<br>
---------------------------------------------------------------------- <br>
import enum <br>
from PyQt6 import QtDesigner, QtGui, QtWidgets, QtCore <br>
<br>
class MyWidget(QtWidgets.QWidget): <br>
<br>
@QtCore.pyqtEnum <br>
class MyEnum(enum.IntEnum): <br>
ONE = enum.auto() <br>
TWO = enum.auto() <br>
<br>
def __init__(self, *args, **kwargs) -> None: <br>
super().__init__(*args, \
**kwargs) <br>
self._prop = \
MyWidget.MyEnum.TWO <br> <br>
@QtCore.pyqtProperty(MyEnum) <br>
def prop(self): <br>
print(f'Getting property val \
{self._prop}') <br>
return self._prop <br>
<br>
@prop.setter <br>
def prop(self, new_val): <br>
print(f'Setting new property \
val {new_val}') <br>
self._prop = new_val <br>
<br>
class Plugin(QtDesigner.QPyDesignerCustomWidgetPlugin): <br>
<br>
def name(self): <br>
return "MyWidget" \
<br> <br>
def group(self): <br>
return "Buttons" \
<br> <br>
def isContainer(self): <br>
return False <br>
<br>
def createWidget(self, parent): <br>
return MyWidget(parent) <br>
<br>
def icon(self): <br>
return QtGui.QIcon() <br>
<br>
def toolTip(self): <br>
return "" <br>
<br>
def whatsThis(self): <br>
return "" <br>
<br>
def includeFile(self): <br>
return \
"pyqt6_enum_designer_poc_plugin" <br>
---------------------------------------------------------------------- <br>
<br>
I want an enum property to be displayed in the
PropertySheet. <br>
It's correctly represented by a combobox showing ONE and TWO
as <br>
available options. <br>
<br>
TWO is correctly selected by default. However, when in
Property sheet <br>
I try to set it to ONE, <br>
as soon as I click away from there, it's reset back to TWO.
In fact, <br>
the setter does not get called, <br>
since the message inside is never printed. (Getter message
is being printed). <br>
<br>
Properties do work without issues, if they have built-in
types, such as QColor, <br>
or even native enums, such as Qt.Orientation. <br>
<br>
To try this code, you can save this code to <br>
"pyqt6_enum_designer_poc_plugin.py" and run like so: <br>
PYQTDESIGNERPATH=$(pwd) designer <br>
<br>
I use Qt Designer 6.6.2 and: <br>
- PyQt6 6.6.1 <br>
- PyQt6-Qt6 6.6.2 <br>
- PyQt6-sip 13.6.0 <br>
<br>
(Also tried with PyQt6-6.5.3 PyQt6-Qt6-6.5.3, same result) <br>
<br>
There used to be a way to make this work in PyQt5, but in
PyQt6 I <br>
tried multiple approaches without luck. <br>
If anybody knows the correct path, that would be very
appreciated! <br>
<br>
Thanks, <br>
Ivan <br>
</blockquote>
<br>
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. <br>
<br>
Sorry for not being more helpful. <br>
<br>
Phil <br>
</blockquote>
</blockquote>
</blockquote>
<br>
<br>
<br>
</body>
</html>
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic