简体   繁体   English

如何防止猕猴桃的属性变化事件循环?

[英]How to prevent property change event loop in kivy?

I have an application acting as a UI for another application through some server. 我有一个应用程序通过某个服务器充当另一个应用程序的UI。 There will be several instances of the UI application. UI应用程序将有多个实例。 The UI application has a property, n_property , representing a parameter of the remote app. UI应用程序具有属性n_property ,该属性表示远程应用程序的参数。 When n_property is changed through the UI, it is sent to the server – here simulated through send_value . 通过UI更改n_property ,会将其发送到服务器-在这里通过send_value模拟。 The server passes it on to the application to be controlled, it gets validated there, and passed back to the server. 服务器将其传递给要控制的应用程序,并在那里进行验证,然后传递回服务器。 The server sends the new value back to the UI (and other connected instances of the UI), simulated here with receive_value . 服务器将新值发送回UI(以及UI的其他连接实例),在此处使用receive_value模拟。

I would like to set n_property (and the Slider representing it) to the new value without triggering a new n_property event , because I do not want to enter an infinite loop of changing values, as happens here when the slider is dragged fast enough. 我想将n_property (以及代表它的Slider )设置为新值而不触发新的n_property事件 ,因为我不想进入一个不断变化的值循环,就像当滑块被足够快地拖动时那样。

In other frameworks, I'd silence the on change event in receive_value , but I haven't found an elegant way to do this in kivy [1] . 在其他框架中,我将使receive_value的on change事件静音,但是在kivy [1]中还没有找到一种完美的方法来做到这一点。

Here is an example program: 这是一个示例程序:

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

Edit: 编辑:

According to the docs, event dispatching is stopped once a handler returns True , and handlers are called in reverse order of attachment. 根据文档,一旦处理程序返回True ,事件分发就会停止,并且以相反的顺序调用处理程序。 So I thought changing receive_value to 所以我想将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

would be a clever way to achieve this, and while yes, I don't seem to be able to end up in an infinite loop, there are still a few 'bounces'. 这将是实现此目标的聪明方法,虽然是的,但我似乎无法陷入无限循环,但仍有一些“反弹”。

And it seems that indeed, EventObservers , which stores the callbacks, is initialized with dispatch_reverse=0 in the definition of Property , but for events registered with register_event_type it's dispatch_reverse=1 . 似乎确实,存储回调的EventObserversProperty的定义中使用dispatch_reverse=0进行了初始化,但是对于使用register_event_type的事件,则使用dispatch_reverse=1进行了初始化。


[1] I suppose I could have an attribute _n_property , and make n_property an AliasProperty whose setter and getter access _n_property . [1]我想我可以拥有一个_n_property属性,并使n_property成为一个AliasPropertysettergetter访问_n_property But that would not be a general solution for the different subclasses of Property (ie bounds checking for BoundedNumericPropery or OptionProperty would have to be separately dealt with). 但这不是Property的不同子类的通用解决方案(即,必须单独处理BoundedNumericProperyOptionProperty边界检查)。

I would use a decorator that stops on_value method of a slider from being executed too quickly: 我会使用一个装饰器,以on_value滑块的on_value方法执行得太快:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM