def test_the_touch_that_might_already_ended(actually_ended): import time from kivy.clock import Clock from kivy.uix.widget import Widget from kivy.tests.common import UnitTestTouch import asynckivy as ak from asynckivy.exceptions import MotionEventAlreadyEndedError Clock.tick() async def job(w, t): if actually_ended: with pytest.raises(MotionEventAlreadyEndedError): async for __ in ak.rest_of_touch_moves(w, t): pass else: async for __ in ak.rest_of_touch_moves(w, t): pass w = Widget() t = UnitTestTouch(0, 0) t.time_end = 1 # something other than -1 task = ak.start(job(w, t)) if actually_ended: time.sleep(2.) Clock.tick() else: t.grab_current = None w.dispatch('on_touch_up', t) t.grab_current = w w.dispatch('on_touch_up', t) assert task.done
def test_a_number_of_on_touch_moves_fired(touch_cls, n_touch_moves): from time import perf_counter from kivy.uix.widget import Widget import asynckivy as ak async def _test(w, t): n = 0 async for __ in ak.rest_of_touch_moves(w, t): n += 1 assert n == n_touch_moves nonlocal done done = True done = False w = Widget() t = touch_cls() ak.start(_test(w, t)) for __ in range(n_touch_moves): t.grab_current = None w.dispatch('on_touch_move', t) t.grab_current = w w.dispatch('on_touch_move', t) t.grab_current = None w.dispatch('on_touch_up', t) t.grab_current = w w.dispatch('on_touch_up', t) assert done
def test_break_during_a_for_loop(touch_cls, holds_a_ref): from kivy.uix.widget import Widget import asynckivy as ak async def _test(w, t): import weakref nonlocal n_touch_moves weak_w = weakref.ref(w) assert weak_w not in t.grab_list if holds_a_ref: agen = ak.rest_of_touch_moves(w, t) async for __ in agen: assert weak_w in t.grab_list n_touch_moves += 1 if n_touch_moves == 2: break else: async for __ in ak.rest_of_touch_moves(w, t): assert weak_w in t.grab_list n_touch_moves += 1 if n_touch_moves == 2: break assert weak_w not in t.grab_list # fails if holds_a_ref is True await ak.event(w, 'on_touch_up') nonlocal done done = True n_touch_moves = 0 done = False w = Widget() t = touch_cls() ak.start(_test(w, t)) for expected in ( 1, 2, 2, ): t.grab_current = None w.dispatch('on_touch_move', t) t.grab_current = w w.dispatch('on_touch_move', t) assert n_touch_moves == expected assert not done t.grab_current = None w.dispatch('on_touch_up', t) t.grab_current = w w.dispatch('on_touch_up', t) assert n_touch_moves == 2 assert done
def test_eat_touch_events(touch_cls, eat_touch, expectation): from kivy.uix.widget import Widget import asynckivy as ak async def _test(parent, t): async for __ in ak.rest_of_touch_moves(parent, t, eat_touch=eat_touch): pass nonlocal done done = True done = False n_touches = { 'move': 0, 'up': 0, } def on_touch_move(*args): n_touches['move'] += 1 def on_touch_up(*args): n_touches['up'] += 1 parent = Widget() child = Widget( on_touch_move=on_touch_move, on_touch_up=on_touch_up, ) parent.add_widget(child) t = touch_cls() ak.start(_test(parent, t)) for i in range(2): t.grab_current = None parent.dispatch('on_touch_move', t) t.grab_current = parent parent.dispatch('on_touch_move', t) assert n_touches['move'] == expectation[i] t.grab_current = None parent.dispatch('on_touch_up', t) t.grab_current = parent parent.dispatch('on_touch_up', t) assert n_touches['up'] == expectation[2] assert done
def test_stop_dispatching(stop_dispatching, expectation): from kivy.uix.widget import Widget from kivy.tests.common import UnitTestTouch import asynckivy as ak async def _test(parent, t): async for __ in ak.rest_of_touch_moves( parent, t, stop_dispatching=stop_dispatching): pass n_touches = { 'move': 0, 'up': 0, } def on_touch_move(*args): n_touches['move'] += 1 def on_touch_up(*args): n_touches['up'] += 1 parent = Widget() child = Widget( on_touch_move=on_touch_move, on_touch_up=on_touch_up, ) parent.add_widget(child) t = UnitTestTouch(0, 0) task = ak.start(_test(parent, t)) for i in range(2): t.grab_current = None parent.dispatch('on_touch_move', t) t.grab_current = parent parent.dispatch('on_touch_move', t) assert n_touches['move'] == expectation[i] t.grab_current = None parent.dispatch('on_touch_up', t) t.grab_current = parent parent.dispatch('on_touch_up', t) assert n_touches['up'] == expectation[2] assert task.done
def test_break_during_a_for_loop(): from kivy.uix.widget import Widget from kivy.tests.common import UnitTestTouch import asynckivy as ak async def _test(w, t): import weakref nonlocal n_touch_moves weak_w = weakref.ref(w) assert weak_w not in t.grab_list async for __ in ak.rest_of_touch_moves(w, t): assert weak_w in t.grab_list n_touch_moves += 1 if n_touch_moves == 2: break assert weak_w not in t.grab_list await ak.event(w, 'on_touch_up') n_touch_moves = 0 w = Widget() t = UnitTestTouch(0, 0) task = ak.start(_test(w, t)) for expected in ( 1, 2, 2, ): t.grab_current = None w.dispatch('on_touch_move', t) t.grab_current = w w.dispatch('on_touch_move', t) assert n_touch_moves == expected assert not task.done t.grab_current = None w.dispatch('on_touch_up', t) t.grab_current = w w.dispatch('on_touch_up', t) assert n_touch_moves == 2 assert task.done
def test_a_number_of_on_touch_moves_fired(n_touch_moves): from kivy.uix.widget import Widget from kivy.tests.common import UnitTestTouch import asynckivy as ak async def _test(w, t): n = 0 async for __ in ak.rest_of_touch_moves(w, t): n += 1 assert n == n_touch_moves w = Widget() t = UnitTestTouch(0, 0) task = ak.start(_test(w, t)) for __ in range(n_touch_moves): t.grab_current = None w.dispatch('on_touch_move', t) t.grab_current = w w.dispatch('on_touch_move', t) t.grab_current = None w.dispatch('on_touch_up', t) t.grab_current = w w.dispatch('on_touch_up', t) assert task.done
class Replay(App): """A widget that replays a game from a log file captured by Logger Replay monkeypatches kivy.clock.time to fire events read from the log file. Touching the replay will change the replay speed based on the y (up/down) -coordinate of the touch, from 50% to 200%. (NB. every clock tick is still processed, so speedups might not actually be that big). This is more of a dirty hack than other parts of the code. """ def __init__(self, log_filename): super(Replay, self).__init__() self.log_filename = log_filename kivy.clock.time = self.time self.stream = gzip.GzipFile(fileobj=open(self.log_filename, 'rb')) self.first_tick = time() self.last_tick = time() self.time_elapsed = 0 self.stream_time = 0 self.next_id = 0 self.clock_speed = 1 self.running = True def build(self): self.top_widget = Widget() self.content_widget = Widget() self.top_widget.add_widget(self.content_widget) self.top_widget.add_widget(SpeedAdjuster(self)) return self.top_widget def time(self): """Like time.time(), but handles events from the log file """ current = time() self.time_elapsed += (current - self.last_tick) * self.clock_speed self.last_tick = current if not self.running: return self.last_tick while self.stream and self.time_elapsed > self.stream_time: try: rec = pickle.load(self.stream) except EOFError: self.handle__end() self.running = False break else: getattr(self, 'handle_' + rec[0])(*rec[1:]) if rec[0] == 'dt': # Ensure every tick gets handled break return self.first_tick + self.stream_time def handle__end(self): parent = self.top_widget.get_parent_window() with self.top_widget.canvas: Color(0, 0, 0.2, 0.5) Rectangle(size=(parent.width, 40), pos=(0, parent.height / 2 - 20)) self.top_widget.add_widget(Label(text='Replay finished', width=self.top_widget.get_parent_window().width, pos=(0, parent.height / 2 - 5), height=10, color=(1, 1, 1, 1))) # Handlers for events from the log file: def handle_random(self, state): """Set the random state""" random.setstate(state) def handle_add_widget(self, cls): """Add a child widget""" self.content_widget.add_widget(cls()) def handle_dt(self, dt): """Tick the clock""" self.stream_time += dt def handle_down(self, attrs): """Generate a touch-down event""" self.handle__touch_event(attrs, 'on_touch_down') def handle_move(self, attrs): """Generate a touch-move event""" self.handle__touch_event(attrs, 'on_touch_move') def handle_up(self, attrs): """Generate a touch-up event""" self.handle__touch_event(attrs, 'on_touch_up') def handle__touch_event(self, attrs, event): """Generate any touch-down event""" touch = ReplayMotionEvent(None, attrs['id'], attrs) for name, value in attrs.items(): setattr(touch, name, value) self.content_widget.dispatch(event, touch) def handle_window_size(self, width, height): print width, height children_to_do = set([self.content_widget]) children_done = set() while children_to_do: child = children_to_do.pop() child.window_width = width child.window_height = height children_done.add(child) children_to_do.update(child.children) children_to_do.difference_update(children_done) self.content_widget.canvas.before.clear() with self.content_widget.canvas.before: PushMatrix() win = self.content_widget.get_parent_window() window_width = win.width window_height = win.height the_min = min(window_width / width, window_height / height) Scale(the_min) self.content_widget.canvas.after.clear() with self.content_widget.canvas.after: PopMatrix()
class Replay(App): """A widget that replays a game from a log file captured by Logger Replay monkeypatches kivy.clock.time to fire events read from the log file. Touching the replay will change the replay speed based on the y (up/down) -coordinate of the touch, from 50% to 200%. (NB. every clock tick is still processed, so speedups might not actually be that big). This is more of a dirty hack than other parts of the code. """ def __init__(self, log_filename): super(Replay, self).__init__() self.log_filename = log_filename kivy.clock.time = self.time self.stream = gzip.GzipFile(fileobj=open(self.log_filename, 'rb')) self.first_tick = time() self.last_tick = time() self.time_elapsed = 0 self.stream_time = 0 self.next_id = 0 self.clock_speed = 1 self.running = True def build(self): self.top_widget = Widget() self.content_widget = Widget() self.top_widget.add_widget(self.content_widget) self.top_widget.add_widget(SpeedAdjuster(self)) return self.top_widget def time(self): """Like time.time(), but handles events from the log file """ current = time() self.time_elapsed += (current - self.last_tick) * self.clock_speed self.last_tick = current if not self.running: return self.last_tick while self.stream and self.time_elapsed > self.stream_time: try: rec = pickle.load(self.stream) except EOFError: self.handle__end() self.running = False break else: getattr(self, 'handle_' + rec[0])(*rec[1:]) if rec[0] == 'dt': # Ensure every tick gets handled break return self.first_tick + self.stream_time def handle__end(self): parent = self.top_widget.get_parent_window() with self.top_widget.canvas: Color(0, 0, 0.2, 0.5) Rectangle(size=(parent.width, 40), pos=(0, parent.height / 2 - 20)) self.top_widget.add_widget( Label(text='Replay finished', width=self.top_widget.get_parent_window().width, pos=(0, parent.height / 2 - 5), height=10, color=(1, 1, 1, 1))) # Handlers for events from the log file: def handle_random(self, state): """Set the random state""" random.setstate(state) def handle_add_widget(self, cls): """Add a child widget""" self.content_widget.add_widget(cls()) def handle_dt(self, dt): """Tick the clock""" self.stream_time += dt def handle_down(self, attrs): """Generate a touch-down event""" self.handle__touch_event(attrs, 'on_touch_down') def handle_move(self, attrs): """Generate a touch-move event""" self.handle__touch_event(attrs, 'on_touch_move') def handle_up(self, attrs): """Generate a touch-up event""" self.handle__touch_event(attrs, 'on_touch_up') def handle__touch_event(self, attrs, event): """Generate any touch-down event""" touch = ReplayMotionEvent(None, attrs['id'], attrs) for name, value in attrs.items(): setattr(touch, name, value) self.content_widget.dispatch(event, touch) def handle_window_size(self, width, height): print width, height children_to_do = set([self.content_widget]) children_done = set() while children_to_do: child = children_to_do.pop() child.window_width = width child.window_height = height children_done.add(child) children_to_do.update(child.children) children_to_do.difference_update(children_done) self.content_widget.canvas.before.clear() with self.content_widget.canvas.before: PushMatrix() win = self.content_widget.get_parent_window() window_width = win.width window_height = win.height the_min = min(window_width / width, window_height / height) Scale(the_min) self.content_widget.canvas.after.clear() with self.content_widget.canvas.after: PopMatrix()