如何在Kivy的过度滚动中触发一次动作?

时间:2016-10-29 15:19:30

标签: python python-3.x kivy

我有一个ScrollView,当您过度滚动到顶部时(例如在许多应用中),它应该具有更新功能。我已经找到了一种在过度滚动超过某个阈值时触发它的方法,但它会多次触发它,因为每次移动都会触发on_overscroll事件。那么有限制它的方法吗? 我的代码如下所示:

from kivy.app import App
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button

from kivy.effects.dampedscroll import DampedScrollEffect

class Effect(DampedScrollEffect):
    def on_overscroll(self, *args):
        super().on_overscroll(*args)
        if self.overscroll < -50:
            print('hey')

class TestApp(App):
    def build(self):
        sv = ScrollView(effect_cls = Effect,
                        size_hint_y = 0.2)

        gl = GridLayout(cols = 1,
                        size_hint_y = None)
        gl.bind(minimum_height = gl.setter('height'))

        for i in range(5):
            gl.add_widget(Button(text = str(i),
                                 size_hint = (None, None)))

        sv.add_widget(gl)

        return sv

TestApp().run()

因此,正如您所看到的,当过度滚动超过50时,它会打印一条简单的消息。但是当你真正尝试它时,你会发现它打印了很多次。我想要的是触发一个事件,在一段时间内(例如一秒钟)保持不可触发并更新内容。我已经尝试过使用布尔标志和Clock,但它没有用。在这可以做些什么?

2 个答案:

答案 0 :(得分:2)

我会在这里使用有状态的装饰器:

class call_control:

    def __init__(self, max_call_interval):
        self._max_call_interval = max_call_interval
        self._last_call = time()

    def __call__(self, function):

        def wrapped(*args, **kwargs):
            now = time()

            if now - self._last_call > self._max_call_interval:
                self._last_call = now

                function(*args, **kwargs)

        return wrapped


class Effect(DampedScrollEffect):

    def on_overscroll(self, *args):
        super().on_overscroll(*args)

        if self.overscroll < -50:
            self.do_something()

    @call_control(max_call_interval=1)
    def do_something(self):
        print('hey')

答案 1 :(得分:0)

我知道这是一个老问题,但有人可能会觉得有用 这是来自tshirtman的github gist的示例

from threading import Thread
from time import sleep
from kivy.app import App
from kivy.lang import Builder
from kivy.factory import Factory
from kivy.clock import mainthread
from kivy.properties import ListProperty, BooleanProperty



KV = '''
FloatLayout:
    Label:
        opacity: 1 if app.refreshing or rv.scroll_y > 1 else 0
        size_hint_y: None
        pos_hint: {'top': 1}
        text: 'Refreshing…' if app.refreshing else 'Pull down to refresh'
    RecycleView:
        id: rv
        data: app.data
        viewclass: 'Row'
        do_scroll_y: True
        do_scroll_x: False
        on_scroll_y: app.check_pull_refresh(self, grid)
        RecycleGridLayout:
            id: grid
            cols: 1
            size_hint_y: None
            height: self.minimum_height
            default_size: 0, 36
            default_size_hint: 1, None
<Row@Label>:
    _id: 0
    text: ''
    canvas:
        Line:
            rectangle: self.pos + self.size
            width: 0.6
'''

class Application(App):
    data = ListProperty([])
    refreshing = BooleanProperty()

    def build(self):
        self.refresh_data()
        return Builder.load_string(KV)

    def check_pull_refresh(self, view, grid):
        max_pixel = 200
        to_relative = max_pixel / (grid.height - view.height)
        if view.scroll_y < 1.0 + to_relative or self.refreshing:
            return

        self.refresh_data()

    def refresh_data(self):
        Thread(target=self._refresh_data).start()

    def _refresh_data(self):
        self.refreshing = True
        sleep(2)

        self.append_data([
            {'_id': i, 'text': 'hello {}'.format(i)}
            for i in range(len(self.data), len(self.data) + 50)
        ])
        self.refreshing = False

    @mainthread
    def append_data(self, data):
        self.data = self.data + data


if __name__ == "__main__":
    Application().run()