Главная » 2016 » Май » 6 » Изучаем Киви- События и свойства
01:44
Изучаем Киви- События и свойства

События и свойства

События являются важной частью программирования Kivy. Это не может быть удивительным для тех, у кого есть опыт разработки в  GUI, но это важная концепция для новичков. После того, как вы поймете, как события работают и как связываться с ними, вы увидите их повсюду в Kivy. Они позволяют легко построить любое поведение, которое вы хотите в Kivy.

Введение в Event Dispatcher

Одним из наиболее важных базовых классов каркаса является EventDispatcher класса. Этот класс позволяет регистрировать типы событий, и направить их заинтересованным сторонам (как правило, других диспетчеров событий). Widget , Animation и Clock классы являются примерами диспетчеров событий.

EventDispatcher объекты зависят от основного цикла для создания и обработки событий.

Основной цикл

Этот цикл выполняется в течение всего времени жизни приложения и  завершает работу при выходе из приложения.

Внутри цикла, на каждой итерации, события генерируются из пользовательского ввода, аппаратных датчиков или нескольких других источников, а также кадры визуализируются на дисплее.

Ваша заявка будет указать функции обратного вызова (подробнее об этом позже), которые называются в основном цикле. Если обратный вызов занимает слишком много времени или не выходит вообще, основной цикл нарушается, и ваше приложение не работает должным образом больше.

В приложениях Kivy, вы должны избегать длинных / бесконечных циклов или спать. Например, следующий код делает так:

 

while True:
 animate_something()
 time.sleep(.10)

При запуске этого кода, программа никогда не будет выходить из цикла, запрещая Kivy делать все другие вещи, которые необходимо сделать. В результате, все, что вы увидите, это черное окно, с которым вы не будете иметь возможность взаимодействовать с. Вместо этого вам нужно создать "график" ваш animate_something() функцию, которая вызывается повторно.

Планирование повторяющихся событий

Вы можете вызвать функцию или метод каждые Х раз в секунду , используя schedule_interval() . Ниже приведен пример вызова функции с именем my_callback 30 раз в секунду:



def my_callback(dt):
 print 'My callback is called', dt
Clock.schedule_interval(my_callback, 1 / 30.)

У вас есть два способа unscheduling ранее запланированное событие. Первым будет использовать unschedule() :

Clock.unschedule(my_callback)

Или, вы можете вернуть значение False в вашей функции обратного вызова:

count = 0
def my_callback(dt):
 global count
 count += 1
 if count == 10:
 print 'Last call of my callback, bye bye !'
 return False
 print 'My callback is called'
Clock.schedule_interval(my_callback, 1 / 30.)

 

Планирование одноразового события 

Используя schedule_once() , вы можете вызвать функцию "позже", как и в следующем кадре, или через несколько секунд:

def my_callback(dt):
 print 'My callback is called !'
Clock.schedule_once(my_callback, 1)

Это будет вызывать функцию my_callback в одну секунду. Вторым аргументом является количество времени ожидания перед вызовом функции, в секундах. Тем не менее, вы можете достичь некоторых других результатов со специальными значениями второго аргумента:

  • Если X больше 0, то функция обратного вызова будет вызвана через несколько секунд
  • Если X = 0, то функция обратного вызова будет вызвана после следующего кадра
  • Если X равен -1, обратный вызов будет вызываться до следующего кадра

-1 В основном используется, когда вы уже в запланированном событии, и если вы хотите запланировать вызов до следующего кадра.

Второй способ повторить вызов функции - сначала назначить функцию обратного вызова один раз с schedule_once() , а второй вызов этой функции внутри самой функции обратного вызова:

def my_callback(dt):
 print 'My callback is called !'
 Clock.schedule_once(my_callback, 1)
Clock.schedule_once(my_callback, 1)

В то время как основной цикл будет пытаться сохранить графику в соответствии с просьбой, существует некоторая неопределенность относительно того, когда именно функция запланированного обратного вызова будет вызвана. Иногда другой обратный вызов или какой-либо другой задачи в заявке займет больше времени, чем ожидалось, и, таким образом, сроки могут быть немного не соответствовать .

В последнем решении повторяющейся  задачи обратного вызова, следующая итерация будет называться по меньшей мере, через одну секунду после последней закончившейся итерации . С schedule_interval() однако, обратный вызов вызывается каждую секунду.

Trigger события

Если вы хотите запланировать функцию, которая будет вызываться только один раз для следующего кадра, как спусковой крючок, вас может возникнуть соблазн добиться того, чтобы выглядеть примерно так:

Clock.unschedule(my_callback)
Clock.schedule_once(my_callback, 0)

Этот способ программирования триггера является не корректным, так как вы всегда будете называть запланированную функцию, когда вы хотите этого или нет. Кроме того, запланированную функцию должен просматривать список weakref (Часов), чтобы найти свой обратный вызов и удалить его. Используйте триггер вместо того, чтобы:

trigger = Clock.create_trigger(my_callback)
# later
trigger()

Каждый раз, когда вы называете триггер (), он будет планировать один вызов вашего обратного вызова. Если он уже был запланирован, он не будет перенесен.

Виджет событий

Виджет имеет 2 типа событий по умолчанию:

  • Событие свойства: если ваш виджет изменяет свое положение или размер, событие срабатывает.
  • Widget определенные события: например, событие будет срабатывать для кнопки, когда она нажата или отпущена.

Для обсуждения о том , как виджет события прикосновения управляются и распространяются, пожалуйста , обратитесь к сенсорным событие пузырьковой Widget раздела.

Создание пользовательских событий

Чтобы создать диспетчер событий с пользовательскими событиями, вам необходимо зарегистрировать имя события в классе, а затем создать метод с тем же именем.

Смотрите следующий пример:

class MyEventDispatcher(EventDispatcher):
 def __init__(self, **kwargs):
 self.register_event_type('on_test')
 super(MyEventDispatcher, self).__init__(**kwargs)

 def do_something(self, value):
 # when do_something is called, the 'on_test' event will be
 # dispatched with the value
 self.dispatch('on_test', value)

 def on_test(self, *args):
 print "I am dispatched", args

Прикрепление обратных вызовов

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

Обратный вызов может быть любой питон вызов, но вы должны убедиться, что он принимает аргументы, испускаемые событием. Для этого, как правило , используют * ARGS аргумент, который будет ловить все аргументы в списке аргументов.

Пример:

def my_callback(value, *args):
 print "Hello, I got an event!", args


ev = MyEventDispatcher()
ev.bind(on_test=my_callback)
ev.do_something('test')

Радует что к kivy.event.EventDispatcher.bind() документации метода есть большое количество примеров о том , как присоединить обратные вызовы.

Введение в Свойства

Свойства являются удивительным способом определения событий и связываются с ними. По существу, они производят события таким образом, что, когда изменяется атрибут в ваших объектах, все свойства, которые ссылаются на этот атрибут автоматически обновляются.

Существуют различные виды свойств для описания типа данных, которые вы хотите обработать.

Декларация о собственности

Чтобы объявить свойства, вы должны объявить их на уровне класса. Класс будет делать свою работу, чтобы создать экземпляр реальных атрибутов при создании объекта. Эти свойства не являются атрибутами: они являются механизмами создания событий на основе ваших атрибутов:

class MyWidget(Widget):

 text = StringProperty('')

Когда переопределение __init__, всегда принимают ** kwargs и использовать супер () используется для вызова метода __init__ родителя, проходя в вашем экземпляре класса:

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

Диспетчер свойства события

Kivy свойства по умолчанию обеспечивают on_ <property_name> событие. Это событие вызывается, когда значение свойства изменяется.

Заметка

Если новое значение свойства равно текущему значению, то <property_name> событие on_ не будет называться.

Например, рассмотрим следующий код:

  
 class CustomBtn(Widget):

 pressed = ListProperty([0, 0])

 def on_touch_down(self, touch):
 if self.collide_point(*touch.pos):
 self.pressed = touch.pos
 return True
 return super(CustomBtn, self).on_touch_down(touch)

 def on_pressed(self, instance, pos):
 print ('pressed at {pos}'.format(pos=pos))

В приведенном выше в строке 3 кода:

pressed = ListProperty([0, 0])

Определим отжатый свойство типа ListProperty , придавая ему значение по умолчанию [0, 0]. Начиная с этого момента, то on_pressed событие будет вызываться всякий раз , когда значение этого свойства меняется.

В строке 5:

def on_touch_down(self, touch):
 if self.collide_point(*touch.pos):
  self.pressed = touch.pos
 return True
 return super(CustomBtn, self).on_touch_down(touch)

Мы переопределили on_touch_down() метод класса Widget. Здесь мы проверяем на столкновение контакт с нашим виджетом.

Если сенсорн попадает внутрь нашего виджета, мы меняем значение прижаты к touch.pos и возвращает истину, показывая , что мы коснулись и не хотим, чтобы в дальнейшем использовать любой другой виджет.

И, наконец, если сенсор выходит за пределы нашего виджета, мы вызываем исходное событие , используя супер (...) и возвращается результат. Это позволяет зафиксировать сенсорное события, чтобы продолжить, с изменениями которые произошли.

Наконец в строке 11:

def on_pressed(self, instance, pos):
 print ('pressed at {pos}'.format(pos=pos))

Определим on_pressed функцию , которая будет вызвана свойством всякий раз , когда значение свойства изменяется.

Заметка

Это on_ <prop_name> событие вызывается в классе , где определяется собственностью. Для контроля / наблюдать какие-либо изменения в собственность за пределами класса, в котором она определена, вы должны привязать к свойству, как показано ниже.

Привязка к свойству

Как контролировать изменения в собственности, когда все у вас есть доступ к виджет экземпляра? Вы связываете к собственности:

  your_widget_instance. Bind (property_name = function_name)

Например, рассмотрим следующий код:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
 class RootWidget(BoxLayout):

 def __init__(self, **kwargs):
 super(RootWidget, self).__init__(**kwargs)
 self.add_widget(Button(text='btn 1'))
 cb = CustomBtn()
 cb.bind(pressed=self.btn_pressed)
 self.add_widget(cb)
 self.add_widget(Button(text='btn 2'))

 def btn_pressed(self, instance, pos):
 print ('pos: printed from root widget: {pos}'.format(pos=.pos))

Если вы запустите код, как это, вы заметите два заявления для печати в консоли. Один из on_pressed события , который вызывается внутри класса CustomBtn , а другой из btn_pressed функции , которую мы связываем с изменением свойств.

Причина, по которой называются обе функции просто. Связывание не означает подмену. Наличие обоих этих функций является излишним, и вы должны вообще использовать только один из методов прослушивания / реагирующих на изменения свойств.

Вы должны также принять во внимание параметры, которые передаются в <property_name> события on_ или функции , связанной с собственностью.

def btn_pressed(self, instance, pos):

Первым параметром является сам, который является экземпляром класса , где определена эта функция. Вы можете использовать функцию в интерактивном режиме, следующим образом:

1
2
3
4
5
6
7
 cb = CustomBtn()

 def _local_func(instance, pos):
 print ('pos: printed from root widget: {pos}'.format(pos=.pos))

 cb.bind(pressed=_local_func)
 self.add_widget(cb)

Первый параметр будет экземпляр класса свойство которого определено.

Второй параметр будет значение, которое имеет новое значение свойства.

Вот полный пример, полученный из приведенных выше отрывков, которые вы можете использовать, чтобы скопировать и вставить в редактор, чтобы экспериментировать.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
 from kivy.app import App
 from kivy.uix.widget import Widget
 from kivy.uix.button import Button
 from kivy.uix.boxlayout import BoxLayout
 from kivy.properties import ListProperty

 class RootWidget(BoxLayout):

 def __init__(self, **kwargs):
 super(RootWidget, self).__init__(**kwargs)
 self.add_widget(Button(text='btn 1'))
 cb = CustomBtn()
 cb.bind(pressed=self.btn_pressed)
 self.add_widget(cb)
 self.add_widget(Button(text='btn 2'))

 def btn_pressed(self, instance, pos):
 print ('pos: printed from root widget: {pos}'.format(pos=pos))

 class CustomBtn(Widget):

 pressed = ListProperty([0, 0])

 def on_touch_down(self, touch):
 if self.collide_point(*touch.pos):
 self.pressed = touch.pos
 # we consumed the touch. return False here to propagate
 # the touch further to the children.
 return True
 return super(CustomBtn, self).on_touch_down(touch)

 def on_pressed(self, instance, pos):
 print ('pressed at {pos}'.format(pos=pos))

 class TestApp(App):

 def build(self):
 return RootWidget()


 if __name__ == '__main__':
 TestApp().run()

Выполнение кода выше даст вам следующий вывод:

../_images/property_events_binding.png

Наш CustomBtn не имеет визуального представления и, таким образом, кажется черным. Вы можете прикоснуться к / нажать на черную область, чтобы увидеть вывод на консоли.

Составные свойства

При определении AliasProperty , обычно определяют геттер и сеттер функционировать самостоятельно. Здесь, он падает на вас , чтобы определить , когда геттер и сеттер функции вызываются с помощью привязки аргумента.

Рассмотрим следующий код.

1
2
3
4
5
6
7
 cursor_pos = AliasProperty(_get_cursor_pos, None, bind=(
 'cursor', 'padding', 'pos', 'size', 'focus',
 'scroll_x', 'scroll_y'))
 '''Current position of the cursor, in (x, y).

 :attr:`cursor_pos` is a :class:`~kivy.properties.AliasProperty`, read-only.
 '''

Здесь cursor_pos является AliasProperty , который использует геттерных _get_cursor_pos с инкубационном части установлено значение Нет, подразумевая , что это только для чтения свойства.

Связывают аргумент в конце определяет , что on_cursor_pos событие отправляется , если какой - либо из свойств используется в связывания = изменения аргумента.

Категория: Kivy | Просмотров: 3013 | Добавил: kuzma | Рейтинг: 1.0/1
Всего комментариев: 1
avatar
1 Bosss • 07:05, 21.05.2022
Можно было машинный перевод хоть причесать малость...
avatar

Программирование игр на Python

Django - создание сайтов с нуля

Javascript - просто используем готовые решения