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

时间:2016-03-16 00:13:56

标签: python events event-handling kivy

我有一个应用程序通过某个服务器充当另一个应用程序的UI。将有几个UI应用程序实例。 UI应用程序具有属性n_property,表示远程应用程序的参数。通过用户界面更改n_property时,会将其发送到服务器 - 此处通过send_value进行模拟。服务器将其传递给要控制的应用程序,它在那里得到验证,然后传回服务器。服务器将新值发送回UI(以及UI的其他连接实例),此处使用receive_value进行模拟。

我想将n_property(以及代表它的Slider设置为新值,而不会触发新的n_property事件,因为我不知道想要进入一个无限循环的变化值,就像滑动拖动得足够快时一样。

在其他框架中,我会在receive_value中对变更事件进行沉默,但我还没有找到一种优雅的方法来实现这一点 [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

将是一种聪明的方法来实现这一点,虽然是的,我似乎无法以无限循环结束,仍然有一些“反弹”。

似乎确实存储回调的EventObserversdispatch_reverse=0的定义中使用Property初始化,但对于使用register_event_type注册的事件,它是{ {1}}。

<小时/> [1] 我想我可以拥有一个属性dispatch_reverse=1,并使_n_property成为n_property AliasPropertysetter访问{ {1}}。但这不是getter的不同子类的一般解决方案(即必须单独处理_n_propertyProperty的边界检查。)

1 个答案:

答案 0 :(得分:1)

我会使用一个装饰器来阻止滑块的on_value方法执行得太快:

test.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()