将PyQT/PySide小部件绑定到Python中的局部变量

Tem*_*ayo 12 python dependency-properties pyqt pyside

我是PySide/PyQt的新手,我来自C#/ WPF.我已经在这个主题上搜索了很多但似乎并没有出现好的答案.

我想问有没有办法可以将QWidget绑定/连接到局部变量,从而每个对象在更改时自行更新.

示例:如果我有一个QLineEdit并且我self.Name在给定的类中有一个局部变量,我该如何绑定这两个,从而在textChanged()触发a时或简单地说QLineEdit变量上的文本更改被更新,同时,当变量是更新了QLineEditget更新,无需调用任何方法.

在C#中,有依赖项属性,转换器和Observable集合用于处理此函数的列表.

如果有人能给出好的榜样,我会很高兴的

Dan*_*ank 20

你在这里要求两件不同的东西.

  1. 你想拥有一个普通的python对象,self.name订阅一个更改QLineEdit.
  2. 您希望QLineEdit订阅普通python对象上的更改self.name.

订阅更改QLineEdit很容易,因为这就是Qt信号/插槽系统的用途.你就是这样做的

def __init__(self):
    ...
    myQLineEdit.textChanged.connect(self.setName)
    ...

def setName(self, name):
    self.name = name
Run Code Online (Sandbox Code Playgroud)

更棘手的部分是让QLineEdit中的文本在更改时self.name更改.这很棘手,因为self.name它只是一个普通的python对象.它对信号/插槽一无所知,而且python没有内置系统来以C#的方式观察对象的变化.你仍然可以做你想做的事.

使用具有Python属性功能的getter/setter

最简单的方法是制作self.name一个房产.以下是链接文档的简要示例(为了清晰起见而进行了修改)

class Foo(object):

    @property
    def x(self):
        """This method runs whenever you try to access self.x"""
        print("Getting self.x")
        return self._x

    @x.setter
    def x(self, value):
        """This method runs whenever you try to set self.x"""
        print("Setting self.x to %s"%(value,))
        self._x = value
Run Code Online (Sandbox Code Playgroud)

您可以添加一行来更新QLineEditsetter方法.这样,每当修改任何值时x,QLineEdit都会更新.例如

@name.setter
def name(self, value):
    self.myQLineEdit.setText(value)
    self._name = value
Run Code Online (Sandbox Code Playgroud)

请注意,名称数据实际上保存在一个被调用的属性中,_name因为它必须与getter/setter的名称不同.

使用真正的回调系统

所有这些的弱点在于您无法在运行时轻松更改此观察者模式.要做到这一点,你需要一些非常像C#提供的东西.python中的两个C#样式观察器系统是obsub,我观察了自己的项目.我在自己的pyqt项目中使用了观察并取得了很大的成功.请注意,PyPI上观察到的版本落后于github上的版本.我推荐github版本.

制作自己的简单回调系统

如果你想以最简单的方式自己做,你会做这样的事情

import functools
def event(func):
    """Makes a method notify registered observers"""
    def modified(obj, *arg, **kw):
        func(obj, *arg, **kw)
        obj._Observed__fireCallbacks(func.__name__, *arg, **kw)
    functools.update_wrapper(modified, func)
    return modified


class Observed(object):
    """Subclass me to respond to event decorated methods"""

    def __init__(self):
        self.__observers = {} #Method name -> observers

    def addObserver(self, methodName, observer):
        s = self.__observers.setdefault(methodName, set())
        s.add(observer)

    def __fireCallbacks(self, methodName, *arg, **kw):
        if methodName in self.__observers:
            for o in self.__observers[methodName]:
                o(*arg, **kw)
Run Code Online (Sandbox Code Playgroud)

现在,如果您只是子类Observed,则可以在运行时向所需的任何方法添加回调.这是一个简单的例子:

class Foo(Observed):
    def __init__(self):
        Observed.__init__(self)
    @event
    def somethingHappened(self, data):
        print("Something happened with %s"%(data,))

def myCallback(data):
    print("callback fired with %s"%(data,))

f = Foo()
f.addObserver('somethingHappened', myCallback)
f.somethingHappened('Hello, World')
>>> Something happened with Hello, World
>>> callback fired with Hello, World
Run Code Online (Sandbox Code Playgroud)

现在,如果您.name按上述方法实现该属性,则可以使用@event它来装饰setter 并订阅它.

  • 好吧,实现绑定的所有这些麻烦,我想这些是要付出的代价,我已经完成了所有这些,我只需要一些自动的东西,因为,我正在研究一些可以动态生成对象并设置一些对象的东西小部件元素的属性,谢谢,至少我从你的例子中得到了一些想法。再次感谢 (2认同)
  • @Temitayo:我还要指出"实现绑定的所有这些努力"归结为18行代码,如我的事件装饰器示例所示.那不是那么糟糕:) (2认同)