Создание поведения kivy, которое отправляет пузырящееся событие так же, как on_touch_down

#python #kivy

#python #kivy

Вопрос:

Давайте начнем с базового примера:

 class OuterWidget(Widget):
    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            print('on_touch_down', self)
            return True  # doesn't dispatch to InnerWidget
        return super().on_touch_down(touch)


class InnerWidget(Widget):
    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            print('on_touch_down', self)
        return super().on_touch_down(touch)
  

Событие отправляется OuterWidget первому, если оно вызывает super().on_touch_down(touch) отправку событий дочерним виджетам, иначе событие не отправляется следующим. Это понятно.

Я хочу создать некоторое поведение, которое происходит с on_touch_down и действует таким же образом. Давайте попробуем:

 class TestBehavior(Widget):
    def __init__(self, **kwargs):
        self.register_event_type('on_test_event')
        super().__init__(**kwargs)

    def on_test_event(self, touch):
        pass

    def on_touch_down(self, touch):
        self.dispatch('on_test_event', touch)  # Some event that happens with on_touch_down
        return super().on_touch_down(touch)


class OuterWidget(TestBehavior, Widget):
    def on_test_event(self, touch):
        if self.collide_point(*touch.pos):
            print('on_test_event', self)
            return True  # doesn't affect anything, both widgets would recieve on_test_event
        return super().on_test_event(touch)


class InnerWidget(TestBehavior, Widget):
    def on_test_event(self, touch):
        if self.collide_point(*touch.pos):
            print('on_test_event', self)
            return True
        return super().on_test_event(touch)
  

on_test_event пузырящийся не будет работать так же, как on_touch_down . Независимо от того OuterWidget , куда будет отправлено возвращаемое событие InnerWidget . Это происходит потому, что оба виджета получат on_touch_down событие, которое срабатывает on_test_event .

Похоже, что если я использую on_touch_down для отправки какого-либо события в поведении, это событие всегда будет отправляться всем дочерним элементам, независимо от того, что любой из них вернул. Но я не хочу отправлять on_test_event next, если какой-то виджет не вызывался super().on_test_event(touch) .

Как я могу это сделать? Есть идеи?

Ответ №1:

Класс Widget обрабатывает свои события для всех своих дочерних элементов следующим образом:

(скопировано из widget.py )

 def on_touch_down(self, touch):
    '''Receive a touch down event.
    :Parameters:
        `touch`: :class:`~kivy.input.motionevent.MotionEvent` class
            Touch received. The touch is in parent coordinates. See
            :mod:`~kivy.uix.relativelayout` for a discussion on
            coordinate systems.
    :Returns: bool
        If True, the dispatching of the touch event will stop.
        If False, the event will continue to be dispatched to the rest
        of the widget tree.
    '''
    if self.disabled and self.collide_point(*touch.pos):
        return True
    for child in self.children[:]:
        if child.dispatch('on_touch_down', touch):
            return True
  

Как вы можете видеть, он выполняет итерацию по своему дочернему элементу и отправляет событие до тех пор, пока не будет возвращено значение True.

Чтобы создать подобное поведение, вы можете создать свой собственный класс mix-in и сделать весь свой класс неотъемлемым от него или в ** monkey patch Widget **

ИМХО, это будет немного запутанно, я бы сделал, чтобы создать функцию:

 def fire_my_event(widget):
    if hasattr(widget, 'on_test_event'):
       if widget.on_test_event():
          return True
    for c in widget.children[:]:
        if fire_my_event(c):
            return True

#in your code somewhere
class SomeWidget(Image): #or anything else ...
    def on_touch_down(self, touch):
       fire_my_event(self)
  

Получайте удовольствие!