pyqt: 动态更新动态创建的控件属性
我正在寻找一种方法,来更新那些动态创建的控件中的标签,这些控件可以被删除,更新的内容是根据前面的控件动态设置的属性。
有没有办法可以自动地把一个pyqt对象和其他控件的属性关联起来,这样当我在任何地方改变一个值时,这个对象也能自动更新呢?
举个例子:对象'a'有属性start
、bars
和end
;bars
是给定的,start
是由前一个对象获取的(如果没有就取1),end
是计算得出的。对象'b'的start
来自于
class myclass(object):
def __init__(self, referrer=None, value=1):
self.referrer=referrer
self.value=value
def _get_start(self):
if not self.referrer:
return 1
else:
return self.referrer.end+1
def _get_end(self):
return self.start+self.value-1
start=property(_get_start)
end=property(_get_end)
def create(value=1):
if not vList:
ref=None
else:
ref=vList[-1]
val=myclass(ref, value)
vList.append(val)
def showList():
for i in vList:
print 'item: %d\tstart: %d\tend: %d' % (vList.index(i),i.start, i.end)
vList=[]
如果我调用create()三次,showList()会显示:
item: 0 start: 1 end: 1
item: 1 start: 2 end: 2
item: 2 start: 3 end: 3
如果我把vList[0].value改成3:
item: 0 start: 1 end: 3
item: 1 start: 4 end: 4
item: 2 start: 5 end: 5
当我需要在界面上保持这些值更新时,就出现了问题(想象成这样的界面):每个水平控件都有一个标签显示start
的属性,一个用于bars
的旋转框,以及一个显示end
的标签,一旦任何旋转框的值改变,后续的每个控件都应该根据前一个控件更新它们的start
和end
属性,并在相应的标签中显示出来。此外,当任何控件被删除时,所有后续控件都应该重新计算每个属性。
使用getter/setter来设置下一个控件标签中的值显然不太有效,因为当我改变任何x.value
时,后续控件的start
和end
实际上只有在重新调用时才会更新,按照我的理解。
我可以把每个新控件连接到它的前一个控件(使用valueChanged()
),或者创建一个函数来找到后续控件并更新它们的属性,但这并不是一个好的解决方案。
因为我对python几乎是新手(最重要的是:我不是程序员),我觉得我可能忽略了某些关于“连接”变量的更好、更清晰的方法(可能与信号或线程有关?)。
请考虑这些控件实际上将是另一个“主”控件的子控件,主控件也会有类似的属性:start
来自于它的前一个主控件(如果有的话),bars
是所有子控件中bars
的总和,end
将再次是start+bars-a
。
谢谢!
(希望你能理解我的意思,我的英语不是很好,而且因为我不是程序员,我的术语也不总是正确的)
1 个回答
我找不到你问题中的具体应用场景,不过这里有一个可能的解决方案,使用的是Qt的信号和槽机制:
# -*- coding: utf-8 -*-
import functools
from PyQt4 import QtGui, QtCore
class ObservableVariable(QtCore.QObject):
""" Represents variable with value, when value changes it emits
signal: changed(new_value)
"""
changed = QtCore.pyqtSignal(object)
def __init__(self, initial_value=0):
super(ObservableVariable, self).__init__()
self._value = initial_value
def get_value(self):
return self._value
def set_value(self, new_val):
self._value = new_val
self.changed.emit(new_val)
value = property(get_value, set_value)
def __str__(self):
return str(self.value)
# it can support more operators if needed
def __iadd__(self, other):
self.value += other
return self
def __isub__(self, other):
self.value -= other
return self
class MyClass(object):
def __init__(self, referrer=None, value=1):
self.referrer = referrer
self.value = ObservableVariable(value)
self._initial_value = value
if referrer:
# propagate referrer changes to subscribers
referrer.value.changed.connect(
lambda x: self.value.changed.emit(self.value.value)
)
@property
def start(self):
if not self.referrer:
return self.value.value
return self.referrer.end + 1
@property
def end(self):
return self.start + self.value.value - 1
class GuiExample(QtGui.QWidget):
def __init__(self):
super(GuiExample, self).__init__()
self.values = []
layout = QtGui.QVBoxLayout(self)
obj = None
for i in range(5):
obj = MyClass(obj, i)
self.values.append(obj)
# create gui elements
hlayout = QtGui.QHBoxLayout()
spinbox = QtGui.QSpinBox()
spinbox.setValue(obj.value.value)
start_label = QtGui.QLabel()
end_label = QtGui.QLabel()
hlayout.addWidget(start_label)
hlayout.addWidget(spinbox)
hlayout.addWidget(end_label)
layout.addLayout(hlayout)
# function called on value change
def update_start_end(instance, start_label, end_label):
start_label.setText(str(instance.start))
end_label.setText(str(instance.end))
action = functools.partial(update_start_end, obj, start_label,
end_label)
action() # set initial start end text to labels
# connect signals to gui elements
obj.value.changed.connect(action)
spinbox.valueChanged.connect(obj.value.set_value)
obj.value.changed.connect(spinbox.setValue)
layout.addWidget(QtGui.QPushButton('test',
clicked=self.test_modification))
def test_modification(self):
self.values[1].value += 1
app = QtGui.QApplication([])
gui = GuiExample()
gui.show()
app.exec_()