我有一个应用程序通过某个服务器充当另一个应用程序的UI。UI 应用程序将有多个实例。UI 应用程序有一个属性 n_property
,表示远程应用程序的参数。当通过 UI 更改n_property
时,它会被发送到服务器 - 这里通过 send_value
模拟。服务器将其传递给要控制的应用程序,在那里进行验证,然后传递回服务器。服务器将新值发送回 UI(以及 UI 的其他连接实例),此处使用 receive_value
进行模拟。
我想在不触发新n_property
事件的情况下将n_property
(以及表示它的Slider
)设置为新值,因为我不想进入不断变化的值的无限循环,就像这里发生的那样,当滑块拖动得足够快时。
在其他框架中,我会在 receive_value
中静音 on change 事件,但我还没有找到一种优雅的方式来在 kivy[1] 中做到这一点。
下面是一个示例程序:
from kivy.lang import Builder
from kivy.app import App
from kivy.properties import BoundedNumericProperty
from kivy.clock import Clock
class PropApp(App):
n_property = BoundedNumericProperty(5, min=0, max=10)
def build(self):
rw = Builder.load_string("""
GridLayout:
cols:2
Label:
text: "Property Value"
Label:
id: prop_label
text: str(app.n_property)
Label:
text: "Control"
Slider:
id: prop_slider
min: 0
max: 10
value: app.n_property
""")
self.bind(n_property=rw.ids.prop_slider.setter('value'))
rw.ids.prop_slider.bind(value=self.setter('n_property'))
self.bind(n_property=self.send_value)
return rw
def send_value(self, inst, val):
print self.n_property
Clock.schedule_once(lambda dt: self.receive_value(val), .02)
def receive_value(self, val):
self.n_property = val
if __name__ == '__main__':
PropApp().run()
编辑:
根据文档,一旦处理程序返回True
,事件调度就会停止,并且处理程序以与附件相反的顺序调用。所以我想把receive_value
改成
def receive_value(self, val):
print "Old value: {} new value: {}".format(self.n_property, val)
def swallow(inst, val):
print "Got swallowed {}".format(val)
inst.funbind('n_property', swallow)
return True
self.fbind('n_property', swallow)
self.n_property = val
这将是实现这一目标的聪明方法,虽然是的,但我似乎无法最终陷入无限循环,但仍有一些"反弹"。
似乎确实, 存储回调的EventObservers
在Property
的定义中用dispatch_reverse=0
初始化,但对于注册register_event_type
的事件,它是dispatch_reverse=1
.
[1] 我想我可以有一个属性
_n_property
,并n_property
成为一个AliasProperty
,其setter
和getter
访问_n_property
。但对于Property
的不同子类来说,这不是一个通用的解决方案(即BoundedNumericPropery
或OptionProperty
的边界检查必须单独处理)。
我会使用一个装饰器来阻止滑块on_value
方法执行得太快:
测试.kv:
#:kivy 1.9.1
GridLayout:
cols: 1
ResponseButton:
text: 'send response from server'
on_press: self.send_response(int(my_input.text), my_slider)
TextInput:
id: my_input
size_hint_y: 0.1
text: '50'
MySlider:
id: my_slider
main.py:
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from kivy.app import App
from kivy.uix.slider import Slider
from time import time, sleep
from threading import Thread
from kivy.uix.button import Button
class ResponseButton(Button):
def send_response(self, value, slider):
slider.receive_response(value)
class delayed:
def __init__(self, seconds):
self.seconds = seconds
self.start = time()
self.refused = False
self.function = None
self.args = None
self.run_thread()
def run_thread(self):
def job():
while True:
sleep(self.seconds)
if self.refused and self._time_ok():
self.function(*self.args)
self.refused = False
thread = Thread(target=job)
thread.daemon = True
thread.start()
def _time_ok(self):
return time() - self.start > self.seconds
def __call__(self, function):
self.function = function
def decorated(*args):
self.args = args
if self._time_ok():
self.start = time()
function(*self.args)
else:
self.refused = True
return decorated
class MySlider(Slider):
_call_server = True
def receive_response(self, value):
print '@@@ received from server:', value
self._call_server = False
self.value = value
@delayed(seconds=2)
def on_value(self, obj, value):
if self._call_server:
self.send_value(value)
else:
self._call_server = True
def send_value(self, value):
print '>>> sent value to server:', value
class Test(App):
pass
Test().run()