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()