[PyQt] Use pyqtProperty on QObject instance
Kyle Altendorf
sda at fstab.net
Fri May 31 15:34:52 BST 2019
Definitely look at attrs.org. It will give a commonly expected
declarative form for classes and you can add your own metadata to the
fields as needed. But, I'm not really clear what variable you are
creating a closure on. And, have you looked at three-arg type()
calls? And one more, the @ decorator syntax is just a shortcut.
@d
def f():
pass
Is basically the same as:
def f():
pass
f = d(f)
I'll note that I did something at least vaguely similar to this, though
I bet it ought to be rewritten.
https://github.com/altendky/stlib/blob/b532ae346b88e2465a54fb96d31639de0c73bff1/epyqlib/utils/qt.py#L914-L996
used like
https://github.com/altendky/stlib/blob/master/epyqlib/tests/utils/test_qt.py#L13-L34
Though admittedly I did not make anything an actual slot. It just turns
the attrs attributes into properties (or optionally pyqtProperty's) and
creates associated on-change signals.
Cheers,
-kyle
On 2019-05-31 03:37, Patrick Stinson wrote:
> Well, I have no idea what kind of trouble I am asking for, but after
> spending a bunch fo time in the sip and PyQt5 code I came up with the
> idea to hack a slot in a closure, which seems to work:
>
> @staticmethod
> def _setup_pyqtProperties(classAttrs, fields):
> def closure(attr, kind):
> signalName = '%sChanged' % attr
> getterName = attr
> setterName = 'set' + attr[0].upper() + attr[1:]
> #
> qsignal = pyqtSignal()
> getter = lambda self: self.get(attr)
> def setter(self, x):
> self.set(attr, x)
> getattr(self, signalName).emit()
> #
> qproperty = pyqtProperty(kind,
> fget=getter,
> fset=setter,
> notify=qsignal)
> @pyqtSlot(kind, name=setterName)
> def qslot(self, x):
> setter(self, x)
> #
> ret = {}
> ret[signalName] = qsignal
> ret[getterName] = qproperty
> ret[setterName] = qslot
> return ret
> for k, (attr, kind, label) in enumerate(fields):
> propAttrs = closure(attr, kind)
> classAttrs.update(propAttrs)
>
> On May 30, 2019, at 9:34 AM, Patrick Stinson <patrickkidd at gmail.com>
> wrote:
>
> Being able to call pyqtSlot as a function and not a decorator as you
> can with pyqtProperty would allow me to avoid having to add an explicit
> setter for every property and simply generate them from the FIELDS
> class attribute.
>
> @pyqtSlot(str)
> def setFirstName(self, x): self.set('firstName', x)
>
> On May 30, 2019, at 9:18 AM, Patrick Stinson <patrickkidd at gmail.com>
> wrote:
>
> I am playing with dynamically adding signals, slots, and properties at
> class-declaration time using locals(). The following code works with
> pyqtSignal and pyqtProperty, but not pyqtSlot? It looks like
> pyqtProperty can read the attributes from fset and fget, but pyqtSlot
> doesn't read the attribute from name?
>
> class PropertySheet(QObject):
>
> @staticmethod
> def _setup_pyqtProperties(classAttrs, fields):
> def closure(attr, kind):
> qsignal = pyqtSignal()
> getterName = attr
> setterName = 'set' + attr[0].upper() + attr[1:]
> getter = lambda self: self.get(attr)
> setter = lambda self, x: self.set(attr, x)
> qproperty = pyqtProperty(kind,
> fget=getter,
> fset=setter,
> notify=qsignal)
> qslot = pyqtSlot(kind, name=setterName)
> ret = {}
> ret['%sChanged' % attr] = qsignal
> ret[getterName] = qproperty
> ret[setterName] = qslot
> return ret
> for k, (attr, kind, label) in enumerate(fields):
> propAttrs = closure(attr, kind)
> classAttrs.update(propAttrs)
>
> class QmlPersonProperties(PropertySheet):
>
> FIELDS = [('firstName', str, 'First Name'),
> ('middleName', str, 'Middle Name'),
> ]
> PropertySheet._setup_pyqtProperties(locals(), FIELDS)
>
> On May 27, 2019, at 11:29 AM, Kyle Altendorf <sda at fstab.net> wrote:
>
> When do you figure out what the properties should be and when do you
> need to create the class? You can dynamically create classes with
> three-arg type() calls.
>
> I went another approach for my needs with signals. A simplified
> explanation of the idea is that instead of adding signals dynamical to
> a class you can instead add other QObject instances with signals on
> them as attributes of your primary class. I wrapped this up with a
> descriptor which I can use pretty much as a drop in replacement for
> pyqtSignal but without having to inherit from QObject. Not sure if
> that approach ends up helpful here though. Of course there is overhead
> associated with extra objects etc but... sometimes that doesn't matter.
>
> https://github.com/altendky/stlib/blob/f779e9c4d5ca4015eafe946b3281a9dcdd7a9fd9/epyqlib/utils/qt.py#L1172-L1210
>
> Cheers,
> -kyle
>
> On May 27, 2019 2:38:04 PM EDT, Patrick Stinson <patrickkidd at gmail.com>
> wrote:
> So they are. I wonder if this can be done with attached properties.
>
> On May 27, 2019, at 9:59 AM, Phil Thompson
> <phil at riverbankcomputing.com> wrote:
>
> On 27/05/2019 17:41, Patrick Stinson wrote:
> Ok, that answers my question. I will figure out a way to add the
> properties to the class the first time they are added to one of the
> instances. They are always the same, after all.
> Properties (like signals) are part of the class *definition* (as far
as Qt is concerned). You can't add them dynamically.
> Phil
>
> On May 27, 2019, at 9:11 AM, Phil Thompson
<phil at riverbankcomputing.com> wrote:
> On 27/05/2019 16:33, Patrick Stinson wrote:
> I have a custom object property system that adds properties to
QObject
> instances and am trying to expose those [dynamic] properties to
qml.
> Is it possible to add a qt property to a QObject instance, as
opposed
> to adding it using pyqtProperty as a decorator in the class
> declaration?
> The PyQt5 docs say that you can use pyqtProperty in the same way
as
> the python property() function apart from the decorator, but I
haven't
> had much success with this:
> def test_property():
> class A(QObject):
> def __init__(self):
> self._mine = 12
> self.mine = pyqtProperty(int, self.get_mine,
self.set_mine)
> def get_mine(self):
> return self._mine
> def set_mine(self, x):
> self._mine = x
> a = A()
> print(a.mine)
> print(a.mine())
> turin:pkdiagram patrick$ python test.py
> <PyQt5.QtCore.pyqtProperty object at 0x114ea1840>
> Traceback (most recent call last):
> File "test.py", line 743, in <module>
> test_property()
> File "test.py", line 740, in test_property
> print(a.mine())
> TypeError: Required argument 'fget' (pos 1) not found
> turin:pkdiagram patrick$ Properties are class objects not instance
> objects.
> Phil
_______________________________________________
PyQt mailing list PyQt at riverbankcomputing.com
https://www.riverbankcomputing.com/mailman/listinfo/pyqt
More information about the PyQt
mailing list