如何防止 kivy 中的属性更改事件循环



我有一个应用程序通过某个服务器充当另一个应用程序的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

这将是实现这一目标的聪明方法,虽然是的,但我似乎无法最终陷入无限循环,但仍有一些"反弹"。

似乎确实, 存储回调的EventObserversProperty的定义中用dispatch_reverse=0初始化,但对于注册register_event_type的事件,它是dispatch_reverse=1 .


[1] 我想我可以有一个属性_n_property,并n_property成为一个AliasProperty,其settergetter访问_n_property。但对于Property的不同子类来说,这不是一个通用的解决方案(即BoundedNumericProperyOptionProperty的边界检查必须单独处理)。

我会使用一个装饰器来阻止滑块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()

相关内容

  • 没有找到相关文章

最新更新