随机绑定工作 [英] Bind working at random times

查看:68
本文介绍了随机绑定工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为kivy编写自定义窗口小部件(请参阅这个问题),但我发现由于某些未知原因,有时bind方法实际上并未绑定回调.

I'm writing a custom widget for kivy(see this question), but I just found out that, for some unknown reason, sometimes the bind method does not actually bind the callbacks.

在我的课堂上,我有这段代码:

In my class I have this code:

def __init__(self, **kwargs):
    super(SpinBox, self).__init__(**kwargs)

    self._value_label = Label(text=str(self.value))
    self.inc_button = TimedButton(text='+')
    self.dec_button = TimedButton(text='-')


    def inc(inst):
        if float(self.value) + float(self.step) <= self.max_value:
            self.value += self.step

    def dec(inst):
        if float(self.value) - float(self.step) >= self.min_value:
            self.value -= self.step

    self.inc_button.bind(on_press=inc)
    self.inc_button.bind(on_time_slice=inc)
    self.dec_button.bind(on_press=dec)
    self.dec_button.bind(on_time_slice=dec)
    # ...

TimedButton是另一个自定义类.这是一个Button子类,它在on_touch_down上启动计时器,如果经过一定时间后仍未收到on_touch_up,它将认为按下是长按,并使用以下命令开始调度on_time_slice事件: Clock.schedule_interval每几毫秒.

Where TimedButton is an other custom class. It is a Button subclass that on on_touch_down starts a timer, if after a certain amount of time it didn't receive a on_touch_up it consider the press a long-press and starts to dispatch the on_time_slice event using Clock.schedule_interval every some milliseconds.

因此,尝试使用这样的自定义类:

So, trying to use my custom class like this:

class MyApp(App):
    def build(self):
        return SpinBox()

MyApp().run()

value根本不增加.

如果我这样做:

class MyApp(App):
    def build(self):
        s = SpinBox()
        def inc(inst):
            s.value += 1
        s.inc_button.bind(on_time_slice=inc)
        return s
MyApp().run()

在每个on_time_slice事件中,值 都会增加.我不明白为什么MyApp类内部的绑定有效,而SpinBox.__init__方法中的绑定却无效. 我在做什么错了?

The value is incremented at every on_time_slice event. I don't understand why the bind inside the MyApp class works, while binding in the SpinBox.__init__ method does not. What am I doing wrong?

推荐答案

我找到了解决方法.我没有绑定on_touch_downon_touch_up,而是尝试绑定state属性,在TimedButton中实现了on_state方法,它可以工作.我仍然不明白为什么以前的实现单独使用而不在SpinBox中使用时会起作用,除非直接在App类内部绑定函数.

I found a work around. Instead of binding the on_touch_down and on_touch_up I tried to bind the state property implementing an on_state method in the TimedButton and it works. I still don't understand why the previous implementation worked when used alone but not in the SpinBox, except when binding a function inside the App class directly.

无论如何,由于看起来TimedButton类与它有关,所以这是旧实现的代码:

Anyway, since it looks like the TimedButton class has something to do with it, here's the code of the old implementation:

class TimedButton(Button):
    """A simple ``Button`` subclass that produces an event at regular intervals
    when pressed.

    This class, when long-pressed, emits an ``on_time_slice`` event every
    ``time_slice`` milliseconds.

    :param long_press_interval: Defines the minimum time required to consider
                                the press a long-press.
    :type long_press_interval: int
    :param time_slice: The number of milliseconds of each slice.
    :type time_slice: int
    """

    def __init__(self, long_press_interval=550, time_slice=225, **kwargs):
        super(TimedButton, self).__init__(**kwargs)

        self.long_press_interval = long_press_interval
        self.time_slice = time_slice

        self._touch_start = None
        self._touch_uid = None
        self._long_press_callback = None
        self._slice_callback = None

        self.register_event_type('on_time_slice')
        self.register_event_type('on_long_press')


    def on_touch_down(self, touch):
        start_time = time.time()
        self._touch_start = start_time
        self._touch_uid = touch.uid

        def callback(dt):
            self._check_long_press(dt)

        Clock.schedule_once(callback, self.long_press_interval / 1000.0)
        self._long_press_callback = callback
        super(TimedButton, self).on_touch_down(touch)

    def _check_long_press(self, dt):
        delta = dt * 1000
        if delta > self.long_press_interval and self._touch_uid is not None:
            self.dispatch('on_long_press')
            self._long_press_callback = None
            def slice_callback(dt):
                self.dispatch('on_time_slice')

            Clock.schedule_interval(slice_callback, self.time_slice / 1000.0)

            self._slice_callback = slice_callback

    def on_touch_up(self, touch):
        end_time = time.time()
        delta = (end_time - (self._touch_start or 0)) * 1000
        Clock.unschedule(self._slice_callback)
        if (self._long_press_callback is not None and
                delta > self.long_press_interval):
            self.dispatch('on_long_press')
        self._touch_start = self._touch_uid = None
        self._long_press_callback = self._slice_callback = None
        super(TimedButton, self).on_touch_up(touch)


    def on_long_press(self):
        pass

    def on_time_slice(self):
        pass

这是使用state的新代码,效果很好:

And here's the new code using state which works perfectly well:

class TimedButton(Button):
    """A simple ``Button`` subclass that produces an event at regular intervals
    when pressed.

    This class, when long-pressed, emits an ``on_time_slice`` event every
    ``time_slice`` milliseconds.

    :param long_press_interval: Defines the minimum time required to consider
                                the press a long-press.
    :type long_press_interval: int
    :param time_slice: The number of milliseconds of each slice.
    :type time_slice: int
    """

    def __init__(self, long_press_interval=550, time_slice=225, **kwargs):
        super(TimedButton, self).__init__(**kwargs)

        self.long_press_interval = long_press_interval
        self.time_slice = time_slice

        self._touch_start = None
        self._long_press_callback = None
        self._slice_callback = None

        self.register_event_type('on_time_slice')
        self.register_event_type('on_long_press')


    def on_state(self, instance, value):
        if value == 'down':
            start_time = time.time()
            self._touch_start = start_time

            def callback(dt):
                self._check_long_press(dt)

            Clock.schedule_once(callback, self.long_press_interval / 1000.0)
            self._long_press_callback = callback
        else:
            end_time = time.time()
            delta = (end_time - (self._touch_start or 0)) * 1000
            Clock.unschedule(self._slice_callback)
            if (self._long_press_callback is not None and
                delta > self.long_press_interval):
                self.dispatch('on_long_press')
            self._touch_start = None
            self._long_press_callback = self._slice_callback = None

    def _check_long_press(self, dt):
        delta = dt * 1000
        if delta > self.long_press_interval and self.state == 'down':
            self.dispatch('on_long_press')
            self._long_press_callback = None

            def slice_callback(dt):
                self.dispatch('on_time_slice')
                return self.state == 'down'

            Clock.schedule_interval(slice_callback, self.time_slice / 1000.0)

            self._slice_callback = slice_callback


    def on_long_press(self):
        pass

    def on_time_slice(self):
        pass

这篇关于随机绑定工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆