【问题标题】:How to prevent property change event loop in kivy?如何防止kivy中的属性更改事件循环?
【发布时间】:2016-03-16 00:13:55
【问题描述】:

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

我想将n_property(以及代表它的Slider)设置为新值而不触发新的n_property 事件,因为我不想进入无限循环改变值,当滑块拖动得足够快时就会发生这种情况。

在其他框架中,我会在 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 一个AliasPropertysettergetter 访问_n_property。但这不是Property 的不同子类的通用解决方案(即必须单独处理BoundedNumericProperyOptionProperty 的边界检查)。

【问题讨论】:

    标签: python events event-handling kivy


    【解决方案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()
    

    【讨论】:

    • 谢谢,这适用于on_value,但我不明白这会如何影响绑定到value 的回调。
    • @zeeMonkeez 我已经更新了代码。使用这种方法,您可以保持多个客户端 GUI 同步而不会溢出服务器。
    • decorated 中的self.args = args 更改为self.args = [a.proxy_ref if isinstance(a, Widget) else a for a in args] 以创建弱引用以允许垃圾回收。
    • 我换了行,虽然现在看起来有点丑。
    • 虽然比内存泄漏要好:)
    猜你喜欢
    • 2016-11-05
    • 1970-01-01
    • 2016-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-12
    • 1970-01-01
    • 2014-11-08
    相关资源
    最近更新 更多