How to create PyQt Properties dynamically

后端 未结 2 2067
梦毁少年i
梦毁少年i 2020-12-20 04:29

I am currently looking into a way to create GUI desktop applications with Python and HTML/CSS/JS using PyQt5\'s QWebEngineView.

In my little demo ap

相关标签:
2条回答
  • 2020-12-20 04:35

    Thanks for your metaclass idea, I modified it slightly to work properly with classes that contain more than one instance. The problem I faced was that the value was stored into the Property itself, not in the class instance attributes. Also I split up the Property class into two classes for clarity.

    
    class PropertyMeta(type(QtCore.QObject)):
        def __new__(cls, name, bases, attrs):
            for key in list(attrs.keys()):
                attr = attrs[key]
                if not isinstance(attr, Property):
                    continue
                initial_value = attr.initial_value
                type_ = type(initial_value)
                notifier = QtCore.pyqtSignal(type_)
                attrs[key] = PropertyImpl(
                    initial_value, name=key, type_=type_, notify=notifier)
                attrs[signal_attribute_name(key)] = notifier
            return super().__new__(cls, name, bases, attrs)
    
    
    class Property:
        """ Property definition.
    
        This property will be patched by the PropertyMeta metaclass into a PropertyImpl type.
        """
        def __init__(self, initial_value, name=''):
            self.initial_value = initial_value
            self.name = name
    
    
    class PropertyImpl(QtCore.pyqtProperty):
        """ Actual property implementation using a signal to notify any change. """
        def __init__(self, initial_value, name='', type_=None, notify=None):
            super().__init__(type_, self.getter, self.setter, notify=notify)
            self.initial_value = initial_value
            self.name = name
    
        def getter(self, inst):
            return getattr(inst, value_attribute_name(self.name), self.initial_value)
    
        def setter(self, inst, value):
            setattr(inst, value_attribute_name(self.name), value)
            notifier_signal = getattr(inst, signal_attribute_name(self.name))
            notifier_signal.emit(value)
    
    def signal_attribute_name(property_name):
        """ Return a magic key for the attribute storing the signal name. """
        return f'_{property_name}_prop_signal_'
    
    
    def value_attribute_name(property_name):
        """ Return a magic key for the attribute storing the property value. """
        return f'_{property_name}_prop_value_'
    

    Demo usage:

    
    class Demo(QtCore.QObject, metaclass=PropertyMeta):
        my_prop = Property(3.14)
    
    demo1 = Demo()
    demo2 = Demo()
    demo1.my_prop = 2.7
    
    
    0 讨论(0)
  • 2020-12-20 04:53

    One way to do this is by using a meta-class:

    class Property(pyqtProperty):
        def __init__(self, value, name='', type_=None, notify=None):
            if type_ and notify:
                super().__init__(type_, self.getter, self.setter, notify=notify)
            self.value = value
            self.name = name
    
        def getter(self, inst=None):
            return self.value
    
        def setter(self, inst=None, value=None):
            self.value = value
            getattr(inst, '_%s_prop_signal_' % self.name).emit(value)
    
    class PropertyMeta(type(QObject)):
        def __new__(mcs, name, bases, attrs):
            for key in list(attrs.keys()):
                attr = attrs[key]
                if not isinstance(attr, Property):
                    continue
                value = attr.value
                notifier = pyqtSignal(type(value))
                attrs[key] = Property(
                    value, key, type(value), notify=notifier)
                attrs['_%s_prop_signal_' % key] = notifier
            return super().__new__(mcs, name, bases, attrs)
    
    class HelloWorldHtmlApp(QWebEngineView):
        ...
        class Backend(QObject, metaclass=PropertyMeta):
            foo = Property('Hello World')
    
            @pyqtSlot()
            def debug(self):
                self.foo = 'I modified foo!'
    
    0 讨论(0)
提交回复
热议问题