class Projectile(Image): '''Lauch this piece of ammunition towards a target object (`target`), located at coordinates (`tx`, `ty`). When the projectile reaches its target, it disappears. Collision handling is done elsewhere. The `target` object is used to determine which collide_projectile method to check for collision in the on_progress method of the projectile. ''' def shoot(self, tx, ty, target): self.target = target self.animation = Animation(x=tx, y=ty) self.animation.bind(on_start=self.on_start) self.animation.bind(on_progress=self.on_progress) self.animation.bind(on_complete=self.on_stop) self.animation.start(self) def on_start(self, instance, value): pass def on_progress(self, instance, value, progression): if self.target.collide_projectile(self): self.animation.stop(self) def on_stop(self, instance, value): self.parent.remove_widget(self)
class AnimationTestCase(unittest.TestCase): def sleep(self, t): start = time() while time() < start + t: sleep(.01) Clock.tick() def setUp(self): self.a = Animation(x=100, d=1, t='out_bounce') self.w = Widget() def test_start_animation(self): self.a.start(self.w) self.sleep(1.5) self.assertAlmostEqual(self.w.x, 100) def test_animation_duration_0(self): a = Animation(x=100, d=0) a.start(self.w) self.sleep(.5) def test_stop_animation(self): self.a.start(self.w) self.sleep(.5) self.a.stop(self.w) self.assertNotAlmostEqual(self.w.x, 100) self.assertNotAlmostEqual(self.w.x, 0) def test_stop_all(self): self.a.start(self.w) self.sleep(.5) Animation.stop_all(self.w) def test_stop_all_2(self): self.a.start(self.w) self.sleep(.5) Animation.stop_all(self.w, 'x') def test_duration(self): self.assertEqual(self.a.duration, 1) def test_transition(self): self.assertEqual(self.a.transition, AnimationTransition.out_bounce) def test_animated_properties(self): self.assertEqual(self.a.animated_properties['x'], 100) def test_animated_instruction(self): instruction = Scale(3) self.a.start(instruction) self.assertEqual(self.a.animated_properties['x'], 100) self.assertAlmostEqual(instruction.x, 3) self.sleep(1.5) self.assertAlmostEqual(instruction.x, 100)
class PlanillaScreen(Screen): def update_timepos(self, *args): timepos = self.pw.horario.get_timepos() Logger.debug("%s: update_timepos timepos=%s" % (APP, timepos)) self.pw.timepos = timepos def on_enter(self, *args): self.update_timepos() Clock.schedule_interval(self.update_timepos, 60) self.anim = Animation(alpha=0.7, d=1) + Animation(alpha=0.2, d=1) self.anim.repeat = True self.anim.start(self.pw) def on_pre_leave(self, *args): Clock.unschedule(self.update_timepos) self.anim.stop(self.pw)
def on_touch_up(self, touch): if touch.grab_current is self: x, y = self.tpos diff_x = touch.x - x diff_y = touch.y - y space = 20 if self.backgroundscreen: # if (diff_x > space): # self.backgroundscreen.center_x = touch.x # elif (diff_x < -space): # self.backgroundscreen.center_x = touch.x if (diff_y > space): self.backgroundscreen.y = touch.y # 맵 변경이 일어남 app.game.door.door_down() # up animation self.backgroundscreen.opacity = 0 self.backgroundscreen.y = self.y anim = Animation(center_y=self.center_y, duration=.2) anim &= Animation(opacity=1, duration=.2) if anim: anim.stop(self) anim.start(self.backgroundscreen) self.backgroundcolor = random.random( ), random.random(), random.random(), .5 elif (diff_y < -space): self.backgroundscreen.y = touch.y app.game.door.door_up() # down animation self.backgroundscreen.opacity = 0 self.backgroundscreen.top = self.top anim = Animation(center_y=self.center_y, duration=.2) anim &= Animation(opacity=1, duration=.2) if anim: anim.stop(self) anim.start(self.backgroundscreen) self.backgroundcolor = random.random( ), random.random(), random.random(), .5 else: self.backgroundscreen.center_x = self.center_x self.backgroundscreen.center_y = self.center_y touch.ungrab(self)
class Welcome(FloatLayout): blinking_text = ObjectProperty() def __init__(self): super(Welcome, self).__init__() self.anim = Animation(opacity=0, d=1) Clock.schedule_interval(self.blinking, 1) def blinking(self, dt): self.blinking_text.opacity = 1 self.anim.start(self.blinking_text) def on_touch_down(self, touch): Clock.unschedule(self.blinking) self.anim.stop(self.blinking_text) parent = self.parent parent.clear_widgets() parent.add_widget(Principal())
class AccordionListItem(Selectable, GridLayout): title = ObjectProperty(None) content = ObjectProperty(None) drag_opacity = NumericProperty(0.75) listview = ObjectProperty(None) shadow_color = ListProperty([]) ix = NumericProperty(None) text = StringProperty('') why = BooleanProperty(False) collapse_alpha = NumericProperty(1.0) title_height_hint = NumericProperty(0.0) content_height_hint = NumericProperty(0.0) def __init__(self, **kwargs): self._anim_collapse = None self.register_event_type('on_release') super(AccordionListItem, self).__init__(**kwargs) def select(self, *args): if self._anim_collapse: self._anim_collapse.stop() self._anim_collapse = None self._anim_collapse = Animation(collapse_alpha=0.0, t='out_expo', d=0.25).start(self) def deselect(self, *args): if self._anim_collapse: self._anim_collapse.stop() self._anim_collapse = None self._anim_collapse = Animation(collapse_alpha=1.0, t='out_expo', d=0.25).start(self) def on_collapse_alpha(self, instance, value): instance.listview._do_layout() def on_touch_down(self, touch): if not self.collide_point(*touch.pos): return False else: return super(AccordionListItem, self).on_touch_down(touch)
class AnimationTestCase(unittest.TestCase): def setUp(self): self.a = Animation(x=100, d=1, t='out_bounce') self.w = Widget() def test_start_animation(self): self.a.start(self.w) sleep(1) def test_stop_animation(self): self.a.start(self.w) sleep(.5) self.a.stop(self.w) def test_stop_all(self): self.a.start(self.w) sleep(.5) Animation.stop_all(self.w)
class AnimationTestCase(unittest.TestCase): def sleep(self, t): start = time() while time() < start + t: sleep(0.01) Clock.tick() def setUp(self): self.a = Animation(x=100, d=1, t="out_bounce") self.w = Widget() def test_start_animation(self): self.a.start(self.w) self.sleep(1.5) self.assertAlmostEqual(self.w.x, 100) def test_stop_animation(self): self.a.start(self.w) self.sleep(0.5) self.a.stop(self.w) self.assertNotAlmostEqual(self.w.x, 100) self.assertNotAlmostEqual(self.w.x, 0) def test_stop_all(self): self.a.start(self.w) self.sleep(0.5) Animation.stop_all(self.w) def test_stop_all_2(self): self.a.start(self.w) self.sleep(0.5) Animation.stop_all(self.w, "x") def test_duration(self): self.assertEqual(self.a.duration, 1) def test_transition(self): self.assertEqual(self.a.transition, AnimationTransition.out_bounce) def test_animated_properties(self): self.assertEqual(self.a.animated_properties["x"], 100)
class Ammo(Image): def shoot(self, tx, ty, target): self.target = target self.animation = Animation(x=tx, top=ty) self.animation.bind(on_start=self.on_start) self.animation.bind(on_progress=self.on_progress) self.animation.bind(on_complete=self.on_stop) self.animation.start(self) def on_start(self, instance, value): self.boom = Boom() self.boom.center = self.center self.parent.add_widget(self.boom) def on_progress(self, instance, value, progression): if progression >= .1: self.parent.remove_widget(self.boom) if self.target.collide_ammo(self): self.animation.stop(self) def on_stop(self, instance, value): self.parent.remove_widget(self)
class ImageScreen(Screen): timepos = NumericProperty(0.5) alpha = NumericProperty(.8) def on_enter(self, *args): self.canvas.after.clear() with self.canvas.after: w = 10 tp = self.app.horario.get_timepos() pos = (self.width - tp*self.width - w/2, 0) size = (w, self.height) self.tpc = Color(1, .1, .1, self.alpha) self.tp = Rectangle(pos=pos, size=size) Clock.schedule_interval(self.update_timepos, 60) self.anim = Animation(alpha=0.7, d=1) + Animation(alpha=0.2, d=1) self.anim.repeat = True self.anim.start(self) def load(self, path=''): assert path self.clear_widgets() img = AsyncImage(source=path, keep_ratio=False, allow_stretch=True) self.add_widget(img) def update_timepos(self, *args): tp = self.app.horario.get_timepos() Logger.debug("%s: update_img_timepos timepos=%s" % (APP, tp)) self.tp.pos = (self.width - tp*self.width - 5, 0) def on_alpha(self, *args): self.tpc.a = self.alpha def on_pre_leave(self, *args): Clock.unschedule(self.update_timepos) self.anim.stop(self)
class InterpreterGui(BoxLayout): output_window = ObjectProperty() code_input = ObjectProperty() scrollview = ObjectProperty() input_fail_alpha = NumericProperty(0.) lock_input = BooleanProperty(False) _lock_input = BooleanProperty(False) halting = BooleanProperty(False) '''True when the interpreter has been asked to stop but has not yet done so.''' interpreter_state = OptionProperty( 'waiting', options=['waiting', 'interpreting', 'not_responding', 'restarting']) status_label_colour = StringProperty('0000ff') _output_label_queue = ListProperty([]) dequeue_scheduled = ObjectProperty(None, allownone=True) clear_scheduled = ObjectProperty(None, allownone=True) awaiting_label_display_completion = BooleanProperty(False) def __init__(self, *args, **kwargs): super(InterpreterGui, self).__init__(*args, **kwargs) self.animation = Animation(input_fail_alpha=0., t='out_expo', duration=0.5) self.interpreter = InterpreterWrapper(self) # Clock.schedule_interval(self._dequeue_output_label, 0.05) # Clock.schedule_interval(self._clear_output_label_queue, 1) def on_lock_input(self, instance, value): if value: self.input_focus_on_disable = self.code_input.focus self._lock_input = True else: self._lock_input = False self.code_input.focus = self.input_focus_on_disable self.ensure_no_ctrl_c_button() self.halting = False def ensure_ctrl_c_button(self): Clock.schedule_once(self._switch_to_ctrl_c_button, 0.4) def _switch_to_ctrl_c_button(self, *args): c = self.ids.carousel if c.index == 0: c.load_next() def ensure_no_ctrl_c_button(self): Clock.unschedule(self._switch_to_ctrl_c_button) c = self.ids.carousel if c.index == 1: c.load_previous() else: Animation.cancel_all(c) c._start_animation(new_offset=0) def on_interpreter_state(self, instance, value): if value == 'waiting': self.status_label_colour = '0000ff' elif value == 'interpreting': self.status_label_colour = '00ff00' elif value == 'not_responding': self.status_label_colour = 'ff0000' elif value == 'restarting': self.status_label_colour = 'ffA500' def interpret_line_from_code_input(self): text = self.code_input.text if text == '': self.flash_input_fail() return self.code_input.text = '' self.interpret_line(text) def flash_input_fail(self): self.animation.stop(self) self.input_fail_alpha = 1. self.animation.start(self) def interpret_line(self, text): index = self.interpreter.interpret_line(text) self.add_input_label(text, index) def add_input_label(self, text, index): l = InputLabel(text=text, index=index, root=self) self.output_window.add_widget(l) self.scrollview.scroll_to(l) def add_output_label(self, text, stream='stdout'): self._output_label_queue.append((text, stream)) # self._dequeue_output_label(0) def _add_output_label(self, text, stream='stdout', scroll_to=True): l = OutputLabel(text=text, stream=stream) self.output_window.add_widget(l) if scroll_to: self.scrollview.scroll_to(l) return l def _dequeue_output_label(self, dt): if not self._output_label_queue: return # print('dequeueing', self._output_label_queue) t = time() i = 0 while (time() - t) < 0.005: i += 1 if not self._output_label_queue: break label_text = self._output_label_queue.pop(0) label = self._add_output_label(*label_text, scroll_to=False) print('Rendered {} labels in {}'.format(i, time() - t)) Animation.stop_all(self.scrollview, 'scroll_x', 'scroll_y') self.scrollview.scroll_to(label) self.dequeue_scheduled.cancel() self.dequeue_scheduled = None if len(self._output_label_queue) == 0 and self.clear_scheduled: self.clear_scheduled.cancel() self.clear_scheduled = None elif len(self._output_label_queue) > 0: self.dequeue_scheduled = Clock.schedule_once( self._dequeue_output_label, 0.05) if (self.awaiting_label_display_completion and len(self._output_label_queue) == 0): self.awaiting_label_display_completion = False self._execution_complete() def _clear_output_label_queue(self, dt): labels = self._output_label_queue self._output_label_queue = [] if labels: self.add_missing_labels_marker(labels=labels) if self.dequeue_scheduled: self.dequeue_scheduled.cancel() self.dequeue_scheduled = None if self.clear_scheduled: self.clear_scheduled.cancel() self.clear_scheduled = None if self.awaiting_label_display_completion: self.awaiting_label_display_completion = False self._execution_complete() def on__output_label_queue(self, instance, values): # print('olq', self.dequeue_scheduled, self.clear_scheduled) if self.dequeue_scheduled: return if not self.dequeue_scheduled: self.dequeue_scheduled = Clock.schedule_once( self._dequeue_output_label, 0) if not self.clear_scheduled: self.clear_scheduled = Clock.schedule_once( self._clear_output_label_queue, 1) def add_missing_labels_marker(self, num_labels=None, labels=None): if labels is not None: num_labels = len(labels) l = UserMessageLabel( text='{} lines omitted (too many to render)'.format(num_labels), background_colour=(1, 0.6, 0, 1)) l.labels = labels self.output_window.add_widget(l) self.scrollview.scroll_to(l) def add_notification_label(self, text): self.add_break() l = NotificationLabel(text=text) self.output_window.add_widget(l) self.scrollview.scroll_to(l) self.add_break() def add_break(self): b = BreakMarker() self.output_window.add_widget(b) self.scrollview.scroll_to(b) def insert_previous_code(self, index, clear=False): if clear: self.code_input.text = '' code = self.interpreter.inputs[index] if self.code_input.text == '': self.code_input.text = code else: self.code_input.text += '\n' + code def send_sigint(self): self.halting = True self.interpreter.send_sigint() def restart_interpreter(self): self.interpreter.restart() def query_restart(self): popup = RestartPopup(interpreter_gui=self) popup.open() def execution_complete(self): '''Called when execution is complete so the TextInput should be unlocked etc., but first this is delayed until messages finish printing. ''' if len(self._output_label_queue) == 0: self._execution_complete() else: self.awaiting_label_display_completion = True def _execution_complete(self): self.add_break() self.lock_input = False self.halting = False self.ensure_no_ctrl_c_button()
class ProcessSelect(Widget): pid = NumericProperty(-1) chara = ObjectProperty(None) current_process_index = NumericProperty(-1) process_list = [] dive_clock = None finished = BooleanProperty(False) columns = 20 cell_interval = 120 cell_size = (64, 64) # instruction groups cells = None overwrap = None lebels = None # properties about overwrap rectangle overwrap_pos_x = NumericProperty(0) overwrap_pos_y = NumericProperty(0) overwrap_pos = ReferenceListProperty(overwrap_pos_x, overwrap_pos_y) overwrap_size_w = NumericProperty(0) overwrap_size_h = NumericProperty(62) overwrap_size = ReferenceListProperty(overwrap_size_w, overwrap_size_h) overwrap_r = NumericProperty(0.03) overwrap_g = NumericProperty(0.8) overwrap_b = NumericProperty(1) overwrap_a = NumericProperty(0) overwrap_color = ReferenceListProperty(overwrap_r, overwrap_g, overwrap_b, overwrap_a) overwrap_color_anim = None def index_to_pos(self, i): """Calculate position from index""" posx = (i % self.columns) * self.cell_interval posy = int(i / self.columns) * self.cell_interval return posx, posy def pos_to_index(self, pos): i = int(pos[0] / self.cell_interval) + int(pos[1] / self.cell_interval) * 20 if (pos[0] < 0 or pos[1] < 0 or pos[0] > self.columns * self.cell_interval or pos[0] % self.cell_interval > self.cell_size[0] or pos[1] % self.cell_interval > self.cell_size[1] or i >= len(self.process_list)): i = -1 # case of no select or invalid select return i def start(self): """Initialize function.""" try: self.process_list = Process.list() self.process_list.reverse() except: message("エラーが発生しました。管理者権限でこのゲームを実行してみてください。") return self.cells = InstructionGroup() self.overwrap = InstructionGroup() self.lebels = InstructionGroup() i = 0 pid_of_this_program = os.getpid() for proc in self.process_list: # calculate position posx, posy = self.index_to_pos(i) # draw squares if proc["pid"] == pid_of_this_program: self.cells.add(Color(0.8, 0.8, 0.8)) else: self.cells.add(Color(1, 1, 1)) self.cells.add(Rectangle(pos=(posx, posy), size=self.cell_size)) # draw text self.lebels.add(Color(0, 0, 0)) self.lebels.add( draw_text_on_canvas(str(proc["pid"]), pos=(posx, posy))) i += 1 self.canvas.add(self.cells) self.canvas.add(self.overwrap) self.canvas.add(self.lebels) def update(self, dt): """Frame update function.""" coord = self.chara.coordinate if self.finished is True: return # calculate the index of selected process i = self.pos_to_index(coord) # if selected process changed if i != self.current_process_index: self.time_left = 1 self.current_process_index = i # progress rectangle if i != -1: self.overwrap_color_anim = Animation(overwrap_a=1, overwrap_size_w=62, duration=2.) self.dive_clock = Clock.schedule_once(partial(self.dive, i), 2.) self.overwrap_color_anim.start(self) posx, posy = self.index_to_pos(i) self.overwrap_pos_x = posx + 1 self.overwrap_pos_y = posy + 1 else: self.overwrap_a = 0 self.overwrap_size_w = 0 self.overwrap_color_anim.stop(self) self.dive_clock.cancel() # draw overwrap self.overwrap.clear() self.overwrap.add(Color(*self.overwrap_color)) self.overwrap.add( Rectangle(pos=self.overwrap_pos, size=self.overwrap_size)) def dive(self, index, _): """Diving function.""" self.pid = self.process_list[index]["pid"] try: mw = MemWorker(pid=self.pid) regs = list(mw.process.iter_region()) mw.process.read_bytes(regs[0][0], bytes=1) except: posx, posy = self.index_to_pos(index) with self.canvas: Color(0.97, 0.1, 0) draw_text_on_canvas("ERROR", font_size=20, pos=(posx + 1, posy + 30)) else: self.finished = True anim = Animation(opacity=0) def tmp(*args): self.canvas.clear() anim.bind(on_complete=tmp) anim.start(self)
class Ball(TickingWidget): """The ball that the solver moves through the maze. The ball has two collision-detection methods: the standard collide_point() for the area where the ball accepts touches, and collide_zoc() for the “zone of control”. The zone of coltrol grows when the ball is touched, and shrinks when the touch is released. When touched, the ball will move towards the touch at BALL_SPEED tiles per second (but maze walls will block it). """ def __init__(self, parent, **kwargs): super(Ball, self).__init__(**kwargs) self.touch_uid = None self.target_pos = self.pos self.animation = None radius = self.radius = parent.cell_size * 0.3 self.handle_radius = parent.cell_size * BALL_TOUCH_RADIUS self.zoc_radius = self.radius with self.canvas: Color(0, 0, 1, 0.5) Ellipse(pos=(-radius, -radius), size=(radius * 2, radius * 2)) Color(0, 0, 0, 1) HollowCircle((0, 0), radius, 18) Color(0.5, 0.5, 0.5, 0.4) FilledCircle((0, 0), self.handle_radius) HollowCircle((0, 0), self.handle_radius, 18) self.scale_instruction = Scale(self.zoc_radius) Color(1, 1, 1, 0.2) FilledCircle((0, 0), 1) Color(0.5, 0.5, 0.5, 0.4) HollowCircle((0, 0), 1, 32) with self.canvas.before: PushMatrix() self.translation_instruction = Translate(0, 0, 0) with self.canvas.after: PopMatrix() def collide_point(self, x, y): px, py = self.pos return (px - x) ** 2 + (py - y) ** 2 < self.handle_radius ** 2 def collide_zoc(self, x, y): px, py = self.pos return (px - x) ** 2 + (py - y) ** 2 < self.zoc_radius ** 2 def tick(self, dt): if not self.parent: return # Try to cover the required distance. But if it's not done in 50 # iterations, give up. remaining_distance = dt * BALL_SPEED * self.parent.cell_size for i in range(50): if remaining_distance <= 0.01 or self.pos == self.target_pos: break distance_covered = self.move_step(remaining_distance) if distance_covered == 0: break remaining_distance -= distance_covered if self.translation_instruction.xy != self.pos: # Update the canvas if the ball moved self.translation_instruction.xy = self.pos self.canvas.ask_update() if self.scale_instruction.scale != self.zoc_radius: # Update the canvas if the ZOC was resized self.scale_instruction.scale = self.zoc_radius self.canvas.ask_update() if not self.parent.ball_source.collide_point(*self.pos): # If the ball is outside the initial area, add time to the player's # clock self.parent.add_time(dt) if self.x < self.parent.cell_size: # IF the ball is in the goal area, the round ends self.parent.win() def move_step(self, remaining_distance): """Move a little towards the touch position, return distance covered `remaining_distance` is the maximum distance to move This implements one iteration of the moving, and is called enough times for the sum of the returned values is big enough. Both remaining_distance and the return value are in pixels, not tiles. """ radius = self.radius pos = numpy.array(self.pos) # The distance we want to cover delta = self.target_pos - pos distance = numpy.sqrt(sum(delta ** 2)) # Only move a little bit each time, so walls are checked correctly max_distance = min(remaining_distance, radius / 2) if distance > max_distance: delta = delta / distance * max_distance distance = max_distance pos += delta # From now, we will deal with tile coordinates instead of pixels tile_coord = numpy.array(self.parent.pixel_to_tile(pos)) tile_radius = self.parent.pixel_to_tile((radius, radius)) # Check the upper/lower & left/right points of the circle # if one of them is in a wall, "snap" the ball to that wall for axis in (0, 1): pt = [0, 0] pt[axis] = tile_radius[axis] if self.parent.wall_at_tile(tile_coord + pt): tile_coord[axis] = int(tile_coord[axis]) + 1 - pt[axis] if self.parent.wall_at_tile(tile_coord - pt): tile_coord[axis] = int(tile_coord[axis]) + pt[axis] # Get the closest grid intersection corner = numpy.array(tile_coord).round() # Get a point in the tile "behind" this clocest corner tile_behind_corner = 2 * corner - tile_coord # Check if there is a wall on that tile if self.parent.wall_at_tile(tile_behind_corner): vector_to_corner = corner - tile_coord # If part of the ball is inside a corner wall, push it back # XXX: This doesn't take into account that the ball can be slightly # elliptical in tile coordinates. (This isn't likely to matter, # though.) avg_radius = sum(tile_radius) / 2 if sum(vector_to_corner ** 2) < avg_radius ** 2: distance_to_corner = numpy.sqrt(sum(vector_to_corner ** 2)) direction_to_corner = vector_to_corner / distance_to_corner pushback_distance = avg_radius - distance_to_corner tile_coord -= direction_to_corner * pushback_distance pushed_back = True # Convert back to pixel coordinates self.pos = self.parent.tile_to_pixel(tile_coord) return distance def on_touch_down(self, touch, force=False): """Called for a new touch `force`: don't check that the touch is near the ball; assume it is. """ if force or self.collide_point(touch.x, touch.y): if self.touch_uid: self.touch_uid = None self.touch_uid = touch.uid self.on_touch_move(touch) zoc_radius = self.parent.cell_size * BALL_ZOC_RADIUS if self.animation: self.animation.stop(self) self.animation = Animation(zoc_radius=zoc_radius, duration=0.5) self.animation.start(self) return True def on_touch_move(self, touch): if touch.uid == self.touch_uid: self.target_pos = touch.pos return True def on_touch_up(self, touch): if touch.uid == self.touch_uid: self.touch_uid = None self.target_pos = self.pos if self.animation: self.animation.stop(self) self.animation = Animation(zoc_radius=self.handle_radius, t='in_cubic', duration=1) self.animation.start(self) return True
class BoardTopLayer(RelativeLayout): is_locked = BooleanProperty(False) current_tile = ObjectProperty(None) board = ObjectProperty(None) def __init__(self, *args, **kwargs): super(BoardTopLayer, self).__init__(*args, **kwargs) self.show_anim = Animation(opacity=1, duration=0.2) self.hide_anim = Animation(opacity=0, duration=0.2) def on_touch_down(self, touch): if not self.is_locked: return False if self.collide_point(*touch.pos): touch.grab(self, exclusive=True) return True return False def on_touch_up(self, touch): if touch.grab_current is self: touch.ungrab(self) if self.collide_point(*touch.pos): self.hide_tile() return True def on_is_locked(self, instance, blocked): self.show_anim.stop(self) self.hide_anim.stop(self) if blocked: self.show_anim.start(self) else: self.hide_anim.start(self) def hide_tile(self): if self.current_tile is None: return self.is_locked = False self.remove_widget(self.current_tile) self._return_func(self.current_tile) def show_tile(self, tile, return_func): self._return_func = return_func self.current_tile = tile self.is_locked = True self.add_widget(tile) new_size = self.width * 0.75, self.height * 0.75 new_pos = self.to_local( self.center_x - new_size[0] / 2, self.center_y - new_size[1] / 2, ) tile.anim = Animation( x=new_pos[0], y=new_pos[0], width=new_size[0], height=new_size[1], transition='in_out_elastic', ) tile.anim.start(tile)
class Rotater(Scatter): def __init__(self, **kwargs): super(Rotater, self).__init__(**kwargs) self.size = (800, 800) self.pos = (270, 0) self.rotation = 0 self.init_x = self.x self.init_y = self.y self.trans = ac.Trans.trans self.saxis = 0 self.axis = 0 self.dir = 0 self.anim = None self.choice = None def on_touch_down(self, touch): ac.KEYFRAME = 0 self.axis = touch.spos[0] self.saxis = touch.spos[0] self.anim = None self.rotation = 0 self.dir = 0 self.x = self.init_x self.y = self.init_y def on_touch_move(self, touch): if self.dir == 0: self.anim = None tmp_dir = 1 if self.axis > self.saxis else -1 if self.axis < self.saxis else 0 if tmp_dir is not self.dir: self.anim = None self.dir = tmp_dir if self.anim is None: self.rotation = 0 if self.dir == -1: self.anim = Animation( x=self.init_x - 1500, rotation=25, transition=self.trans, duration=1000, ) self.anim.start(self) if self.dir == 1: self.anim = Animation( x=self.init_x + 1500, rotation=-25, transition=self.trans, duration=1000, ) self.anim.start(self) difference = self.saxis - touch.spos[0] ac.KEYFRAME = abs(difference) self.axis = touch.spos[0] def on_touch_up(self, touch): if self.anim: self.anim.stop(self) if ac.KEYFRAME > 0.1: self.anim_down = Animation( x=self.init_x + 1500 if self.dir == 1 else -1500, y=self.init_y - 1500, rotation=self.rotation + -90 if self.dir == 1 else 90, transition=AnimationTransition.linear, duration=1, ) self.anim_down += Animation( x=self.init_x, y=self.init_y, rotation=0, transition=AnimationTransition.linear, duration=0, ) self.anim_down.start(self) if self.dir == 1: self.choice(1) if self.dir == -1: self.choice(2) else: self.rotation = 0 self.anim_return = Animation( x=self.init_x, y=self.init_y, transition=AnimationTransition.linear, duration=0.2, ) self.anim_return.start(self) ac.KEYFRAME = 0 self.saxis = 0 self.axis = 0 self.dir = 0
class Sprite(RelativeLayout): register = False size_ratio = 1.0 def __init__(self, **kwargs): self.image_size = kwargs['size'] RelativeLayout.__init__(self, size_hint=(None,None), **kwargs) self.grid = kwargs['grid'] self.start_position = self.pos self.frozen = False self.alive = True self.animation = None self.safe_pos = self.pos self.path = [] self.moving = False self.last_to_tuple = None self.move_duration = 0.25 self.setup() self.init() def init(self): """ This method is called in initiliazation and on restart """ self.before_init() self.frozen = False self.alive = True self.animation = None self.safe_pos = self.pos self.path = [] self.moving = False self.last_to_tuple = None self.after_init() def before_init(self): pass def after_init(self): pass def setup(self): """ This method is called only in initiliazation """ raise Exception('This method should be overridden') def save_pos(self): self.safe_pos = self.pos def load_pos(self): self.pos = self.safe_pos def hide(self): self.canvas.clear() def die(self): if not self.alive or self.parent.frozen: return print '%s died' % self.__class__.__name__ self.alive = False self.stop() # hm? self.hide() self.on_death() return def on_death(self): pass def on_move_finish(self): pass def on_every_move(self): pass def freeze(self): self.frozen = True def face(self, direction): pass def continue_move(self): if not self.alive: return self.take_effect() if not self.path: self.moving = False if self.on_move_finish is not None: self.on_move_finish() self.on_move_finish = None self.on_every_move() return self.moving = True destination = self.path.pop() if destination.bomb is not None: # TODO maybe recalculate path? self.path = [] self.moving = False return delta_x = abs(destination.pos[0] + self.parent.tile_size[0]/10 - self.pos[0]) delta_y = abs(destination.pos[1] + self.parent.tile_size[1]/10 - self.pos[1]) move_duration = self.move_duration if delta_x > delta_y: move_duration *= delta_x / self.parent.tile_size[0] if destination.pos[0] > self.pos[0]: self.face('right') elif destination.pos[0] < self.pos[0]: self.face('left') else: move_duration *= delta_y / self.parent.tile_size[1] if destination.pos[1] > self.pos[1]: self.face('top') elif destination.pos[1] < self.pos[1]: self.face('bottom') self.stop_move() offset = (self.parent.tile_size[0] - self.size[0])/4 self.animation = Animation( pos=( destination.pos[0] + self.parent.tile_size[0]/10 + offset, destination.pos[1] + self.parent.tile_size[1]/10 + offset ), duration=move_duration ) self.animation.on_complete = lambda x: self.continue_move() self.animation.start(self) def move(self, to_tuple): if not self.alive: return from_tuple = self.grid.get_tile_indexes(*self.center_pos) if from_tuple == to_tuple: if not self.moving: self.grid.get_tile(*from_tuple).add_bomb(self) return if to_tuple == self.last_to_tuple: if self.is_bot(): raise Exception('kokotina?') tile = self.grid.get_tile(*to_tuple) self.on_move_finish = lambda : tile.add_bomb(self) return path = self.grid.find_path(from_tuple, to_tuple) if not path: return self.on_move_finish = None self.last_to_tuple = to_tuple self.stop_move() self.path = path self.continue_move() def take_effect(self): tile = self.get_tile() effect = tile.get_effect() if effect is not None: effect.apply(self) def stop_move(self): if self.animation is not None: self.animation.on_complete = lambda x: self.nothing() self.animation.stop(self) def restart(self): self.before_restart() print 'restarting... hmm' self.stop() self.init() self.pos = self.start_position self.after_restart() def before_restart(self): pass def after_restart(self): pass def stop(self): self.before_stop() self.stop_move() self.after_stop() def before_stop(self): pass def after_stop(self): pass @property def center_pos(self): return self.center def get_tile(self): #print 'cp', self.center_pos pos_tuple = self.parent.grid.get_tile_indexes(*self.center_pos) #print 'pt', pos_tuple return self.parent.grid.get_tile(*pos_tuple) @property def name(self): return self.__class__.__name__ def is_bot(self): return True def nothing(self): pass def collide(self, sprite): distance_x = abs(self.center_pos[0] - sprite.center_pos[0]) if distance_x > self.parent.tile_size[0]*0.7: return False distance_y = abs(self.center_pos[1] - sprite.center_pos[1]) if distance_y > self.parent.tile_size[1]*0.7: return False return True
class MDFloatingActionButton( ThemableBehavior, CircularRippleBehavior, RoundElevationBehaviour, ButtonBehavior, AnchorLayout ): _bg_color_down = ListProperty([]) background_color = ListProperty() background_color_down = ListProperty() background_color_disabled = ListProperty() theme_text_color = OptionProperty(None, allownone=True, options=["Primary", "Secondary", "Hint", "Error", "Custom"]) text_color = ListProperty(None, allownone=True) def _get_bg_color_down(self): return self._bg_color_down def _set_bg_color_down(self, color, alpha=None): if len(color) == 2: self._bg_color_down = get_color_from_hex(colors[color[0]][color[1]]) if alpha: self._bg_color_down[3] = alpha elif len(color) == 4: self._bg_color_down = color background_color_down = AliasProperty(_get_bg_color_down, _set_bg_color_down, bind=("_bg_color_down",)) _bg_color_disabled = ListProperty([]) def _get_bg_color_disabled(self): return self._bg_color_disabled def _set_bg_color_disabled(self, color, alpha=None): if len(color) == 2: self._bg_color_disabled = get_color_from_hex(colors[color[0]][color[1]]) if alpha: self._bg_color_disabled[3] = alpha elif len(color) == 4: self._bg_color_disabled = color background_color_disabled = AliasProperty( _get_bg_color_disabled, _set_bg_color_disabled, bind=("_bg_color_disabled",) ) icon = StringProperty("md-android") _elev_norm = NumericProperty(6) def _get_elev_norm(self): return self._elev_norm def _set_elev_norm(self, value): self._elev_norm = value if value <= 12 else 12 self._elev_raised = (value + 6) if value + 6 <= 12 else 12 self.elevation = self._elev_norm elevation_normal = AliasProperty(_get_elev_norm, _set_elev_norm, bind=("_elev_norm",)) _elev_raised = NumericProperty(12) def _get_elev_raised(self): return self._elev_raised def _set_elev_raised(self, value): self._elev_raised = value if value + self._elev_norm <= 12 else 12 elevation_raised = AliasProperty(_get_elev_raised, _set_elev_raised, bind=("_elev_raised",)) def __init__(self, **kwargs): if self.elevation_raised == 0 and self.elevation_normal + 6 <= 12: self.elevation_raised = self.elevation_normal + 6 elif self.elevation_raised == 0: self.elevation_raised = 12 super(MDFloatingActionButton, self).__init__(**kwargs) self.elevation_press_anim = Animation(elevation=self.elevation_raised, duration=0.2, t="out_quad") self.elevation_release_anim = Animation(elevation=self.elevation_normal, duration=0.2, t="out_quad") def _set_ellipse(self, instance, value): ellipse = self.ellipse ripple_rad = self.ripple_rad ellipse.size = (ripple_rad, ripple_rad) ellipse.pos = (self.center_x - ripple_rad / 2.0, self.center_y - ripple_rad / 2.0) def on_disabled(self, instance, value): super(MDFloatingActionButton, self).on_disabled(instance, value) if self.disabled: self.elevation = 0 else: self.elevation = self.elevation_normal def on_touch_down(self, touch): if not self.disabled: if touch.is_mouse_scrolling: return False if not self.collide_point(touch.x, touch.y): return False if self in touch.ud: return False self.elevation_press_anim.stop(self) self.elevation_press_anim.start(self) return super(MDFloatingActionButton, self).on_touch_down(touch) def on_touch_up(self, touch): if not self.disabled: if touch.grab_current is not self: return super(ButtonBehavior, self).on_touch_up(touch) self.elevation_release_anim.stop(self) self.elevation_release_anim.start(self) return super(MDFloatingActionButton, self).on_touch_up(touch) def on_elevation_normal(self, instance, value): self.elevation = value def on_elevation_raised(self, instance, value): if self.elevation_raised == 0 and self.elevation_normal + 6 <= 12: self.elevation_raised = self.elevation_normal + 6 elif self.elevation_raised == 0: self.elevation_raised = 12
class RootLayout(BoxLayout, HoveringBehavior): dlls_loaded = BooleanProperty(False) listed_dlls = ListProperty() path = StringProperty() # mouse highlight _mouse_highlight_pos = ListProperty((0, 0)) _highlight_alpha = NumericProperty() _mouse_highlight_anim = Animation() _can_highlight = BooleanProperty(False) # options mouse_highlight = BooleanProperty(Config.get('mouse_highlight', True)) def __init__(self, **kw): super().__init__(**kw) self.bar = self.ids.bar # mouse highlight self.on_mouse_highlight(None, self.mouse_highlight) def callback(*__): if self.mouse_highlight: self._highlight_alpha = 0 self._can_highlight = True self._show_highlight() Window.unbind(mouse_pos=callback) Window.bind(mouse_pos=callback) # sync def on_frame(*args): self.show_sync_popup() self.setup_updater() Clock.schedule_once(on_frame) # mouse highlight def _show_highlight(self, *__): self._mouse_highlight_anim.stop(self) self._mouse_highlight_anim = Animation(_highlight_alpha=1, d=.1) self._mouse_highlight_anim.start(self) def _hide_highlight(self, *__): self._mouse_highlight_anim.stop(self) self._mouse_highlight_anim = Animation(_highlight_alpha=0, d=.2) self._mouse_highlight_anim.start(self) def _update_highlight(self, __, pos): self._mouse_highlight_pos = pos[0] - 60, pos[1] - 60 def on_mouse_highlight(self, __, is_on): if is_on: Window.bind(mouse_pos=self._update_highlight, on_cursor_enter=self._show_highlight, on_cursor_leave=self._hide_highlight) self._update_highlight(Window, Window.mouse_pos) self._show_highlight() else: Window.unbind(mouse_pos=self._update_highlight, on_cursor_enter=self._show_highlight, on_cursor_leave=self._hide_highlight) self._hide_highlight() Config.mouse_highlight = is_on def show_sync_popup(self): self.sync_popup = SyncPopup() self.sync_popup.open() @new_thread def setup_updater(self): self.updater = DllUpdater() self.dlls_loaded = self.updater.load_available_dlls() if self.dlls_loaded: self.ids.refresh_button.disabled = True OverdrawLabel(widget=self.ids.quickupdate_content, icon='\uf12b', text='Select a directory') else: self.ids.refresh_button.disabled = False OverdrawLabel(widget=self.ids.quickupdate_content, icon='\uea6a', text='Error when syncing') Clock.schedule_once(self.update_common_paths) def update_common_paths(self, *args): self.ids.game_collection_view.update_local_games() self.after_synced() def after_synced(self): self.sync_popup.dismiss() if not IS_ADMIN: self.run_as_admin_shown = self.run_as_admin_shown if hasattr(self, 'admin_btn'): self.admin_btn.ping() def switch_animations_enabled(self, _, value): Config.animations = value Notification( title_='Restart required', message= f'A [color={theme.PRIM}]restart[/color] may be required to [color={theme.PRIM}]{"enable" if value else "disable"}[/color] animations.' ).open() @property def run_as_admin_shown(self): return Config.get('run_as_admin_shown', True) @run_as_admin_shown.setter def run_as_admin_shown(self, value): if value: self.admin_btn = RunAsAdminButton() self.admin_btn.open() elif hasattr(self, 'admin_btn'): self.admin_btn.dismiss() del self.admin_btn Config.run_as_admin_shown = value @new_thread def load_directory(self): self.request_load_dlls(easygui.diropenbox()) def request_load_dlls(self, path): if not path: return False path = os.path.abspath(path) self.launch_path = None self.path = path self.ids.path_info.text = path self.ids.selective_update_btn.disabled = True self.ids.update_all_btn.disabled = True if not os.path.isdir(path): return False notif = WorkingNotif(text='Searching for dlls') notif.open(animation=1) Clock.schedule_once(lambda *__: self._load_dlls(path, notif), .5) def _load_dlls(self, path, notif=None): try: self.ids.content_updater.remove_widget(self.launch_now_btn) except AttributeError: pass self.ids.quickupdate_content.overdrawer.dismiss() self.listed_dlls = [] for relative_path in self.updater.local_dlls(path): if relative_path.split('\\')[-1] in self.updater.available_dlls: self.listed_dlls.append(relative_path) if not self.listed_dlls: ErrorPopup( title='No dlls found here!', message= f'We are sorry. We have not found any dlls to update here in\n[color={theme.PRIM}]{path}[/color].' ).open() else: self.ids.selective_update_btn.disabled = False self.ids.update_all_btn.disabled = False if Config.get('show_disclaimer', True): Clock.schedule_once( lambda *args: Factory.DisclaimerPopup().open()) Config.show_disclaimer = False self.goto_page(0) self.bar.ping() if notif: notif.dismiss() def load_selective(self): self.goto_page(5) self.ids.dll_view.dlls = self.listed_dlls last_selected = ConfLastDlls.get_list(self.path) if last_selected: self.ids.dll_view.select_by_text(last_selected) elif not self.ids.dll_view.selected_nodes: self.ids.dll_view.select_all() @new_thread def update_callback(self, from_selection=False): self.goto_page(0) self.ids.invert_selection_button.disabled = True OverdrawLabel(widget=self.ids.quickupdate_content, icon='\ue896', text='Updating dlls..') if from_selection: dlls = [ item.get('text', '') for item in self.ids.dll_view.selected_nodes ] ConfLastDlls.set_list(self.path, dlls) else: dlls = self.listed_dlls Notification( title_=f'Updating {len(dlls)} dlls', message= f'This can take a [color={theme.PRIM}]while[/color] depending on your [color={theme.PRIM}]internet speed[/color].' ).open() try: self.updater.update_dlls(self.path, dlls) except Exception: ErrorPopup( title='Failed to update dlls!', message= f'Something happened and we are not sure what it was. Please contact our support from the settings.\n\n[color=f55]{format_exc()}[/color]' ).open() OverdrawLabel(widget=self.ids.quickupdate_content, icon='\uea39', text='Update failed') else: OverdrawLabel(widget=self.ids.quickupdate_content, icon='\ue930', text='Completed') if self.launch_path: self.launch_now_btn = LaunchNowButton() self.ids.content_updater.add_widget(self.launch_now_btn, index=0) self.ids.dll_view.data = [] def launch_updated(self): os.startfile(self.launch_path) def restore_callback(self): dlls = [ item.get('text', '') for item in self.ids.dll_view.selected_nodes ] restored, not_restored = self.updater.restore_dlls(self.path, dlls) Factory.RestorePopup(restored=restored, not_restored=not_restored).open() @silent_exc def clear_images_cache(self): shutil.rmtree(os.path.join(os.getcwd(), ImageCacher.CACHE_DIR)) @silent_exc def clear_common_paths_cache(self): os.remove(GameCollection.COMMON_PATHS_CACHE_PATH) def goto_page(self, index): self.ids.content.page = index def game_path_button_callback(self): path = easygui.diropenbox() if not path: path = '' self.ids.game_add_form_dir.text_ = path def game_launch_path_button_callback(self): path = easygui.fileopenbox(filetypes=['*.exe', '*.url'], default=self.ids.game_add_form_dir.text + '\\*.exe') if not path: path = '' self.ids.game_add_form_launch.text_ = path def add_game_callback(self): game_name = self.ids.game_name_input.text game_patch_dir = self.ids.game_add_form_dir.text game_launch_path = (self.ids.game_add_form_launch.text if self.ids.game_add_form_launch.text else self.ids.url_input.text) if not (game_name and game_patch_dir and game_launch_path): return os.makedirs('.config', exist_ok=True) if not game_launch_path: game_launch_path = self.ids.game_add_form_launch.text data = { 'path': game_patch_dir, 'launchPath': game_launch_path, } store = JsonStore(GameCollection.CUSTOM_PATHS_PATH) store.put(game_name, **data) self.cancel_add_game_callback() self.ids.game_collection_view.update_custom_games() def cancel_add_game_callback(self): self.ids.game_name_input.text = '' self.ids.game_add_form_dir.text_ = '' self.ids.game_add_form_launch.text_ = '' self.ids.url_input.text = '' self.goto_page(1) @silent_exc def reset_custom_paths(self): os.remove(GameCollection.CUSTOM_PATHS_PATH) def uninstall_prompt(self): self.uninstall_popup = Factory.UninstallPopup() self.uninstall_popup.open() @silent_exc def export_logs(self): OUTPUT = os.path.expanduser('~\\Desktop\\XtremeUpdater_Logs.zip') SOURCE = os.path.abspath('logs\\') Logger.info(f"Trying to export logs from {SOURCE} to {OUTPUT}") try: shutil.make_archive(OUTPUT, 'zip', SOURCE) except: Logger.error( f"Failed to export logs from {SOURCE} to {OUTPUT}\n{format_exc()}" ) raise else: Logger.info( f"Successfully exported logs from {SOURCE} to {OUTPUT}") Notification( title_='Logs exported', message= f'[color={theme.PRIM}]Logs[/color] were exported to [color={theme.PRIM}]{OUTPUT}[/color]', height=160).open()
class ZIScatter(Scatter): zoom_image = ObjectProperty(Widget()) init_pos = ObjectProperty((75, 70)) def __init__(self, **kwargs): super(ZIScatter, self).__init__(**kwargs) self.anim = Animation() # Physics simple animation on touch up Clock.schedule_interval(self.clear_canvas, 0) Clock.schedule_interval(self.control_pos, 0) def clear_canvas(self, dt): self.canvas.clear() def is_leaving_its_box(self): #check if scatter is leaving its box s = self.scale x, y = self.pos w, h = self.size container = c = self.zoom_image #check every corner limitx = limity = False if (x > c.x or x + w * s < c.x + c.width): limitx = True if (y > c.y or y + h * s < c.y + c.height): limity = True return (limitx, limity) def fix_after_leaving_its_box(self): #check if scatter is leaving its box s = self.scale x, y = self.pos w, h = self.size container = c = self.zoom_image #check every corner limitx = limity = False if x > c.x: x = c.x if x + w * s < c.x + c.width: x = c.x + c.width - w * s if y > c.y: y = c.y if y + h * s < c.y + c.height: y = c.y + c.height - h * s self.pos = (x, y) def control_pos(self, dt): if self.scale <= 1.03: self.reset() pass #avoid scatter leaving its box while physics animation is going on (after touch up) if len(self._touches) > 0: return limitx, limity = self.is_leaving_its_box() if limitx == True or limity == True: self.anim.cancel(self) self.fix_after_leaving_its_box() def transform_with_touch(self, touch): init_pos = self.center init_scale = self.scale init_touch_len = len(self._touches) #super(ZIScatter, self).transform__with__touch(touch) # just do a simple one finger drag if len(self._touches ) == 1 and self.scale > 1.05: #THIS IS NOT IN ORIGINAL SCATTER: # _last_touch_pos has last pos in correct parent space, # just like incoming touch dx = (touch.x - self._last_touch_pos[touch][0]) \ * self.do_translation_x dy = (touch.y - self._last_touch_pos[touch][1]) \ * self.do_translation_y self.apply_transform(Matrix().translate(dx, dy, 0)) #return elif len( self._touches ) == 1 and self.scale < 1.05: #THIS IS NOT IN ORIGINAL SCATTER: return else: #TO AVOID RETURN IN ORIGINAL SCATTER # we have more than one touch... points = [Vector(self._last_touch_pos[t]) for t in self._touches] # we only want to transform if the touch is part of the two touches # furthest apart! So first we find anchor, the point to transform # around as the touch farthest away from touch anchor = max(points, key=lambda p: p.distance(touch.pos)) # now we find the touch farthest away from anchor, if its not the # same as touch. Touch is not one of the two touches used to transform farthest = max(points, key=anchor.distance) if points.index(farthest) != self._touches.index(touch): return # ok, so we have touch, and anchor, so we can actually compute the # transformation old_line = Vector(*touch.ppos) - anchor new_line = Vector(*touch.pos) - anchor angle = radians(new_line.angle(old_line)) * self.do_rotation self.apply_transform(Matrix().rotate(angle, 0, 0, 1), anchor=anchor) if self.do_scale: scale = new_line.length() / old_line.length() new_scale = scale * self.scale if new_scale < self.scale_min or new_scale > self.scale_max: scale = 1.0 self.apply_transform(Matrix().scale(scale, scale, scale), anchor=anchor) #avoid scatter leaving its box limitx, limity = self.is_leaving_its_box() if limitx or limity: #cancel previous apply_transform if init_touch_len == 1: ddx = ddy = 0 if limitx: ddx = -dx if limity: ddy = -dy self.apply_transform(Matrix().translate(ddx, ddy, 0)) else: if self.do_scale: #self.apply_transform(Matrix().scale(scale/init_scale, scale/init_scale, scale/init_scale), # anchor=anchor) # control #limitx, limity = self.is_leaving_its_box() #if limitx or limity: self.fix_after_leaving_its_box() def on_touch_down(self, touch): ret = super(ZIScatter, self).on_touch_down(touch) x, y = touch.x, touch.y #if not self.zoom_image.image.collide_point(x,y): # # did not touch the mask area # return True # if the touch isnt on the widget we do nothing if self.zoom_image.collide_point(x, y): if touch.is_double_tap: self.reset() #if not self.parent.image.collide_point(x,y): # # did not touch the mask area # touch.ud["outside"] = True # return False return ret def on_touch_up(self, touch): if touch.grab_current is not self: return super(ZIScatter, self).on_touch_up(touch) """ x, y = touch.x, touch.y # if the touch isnt on the widget we do nothing if self.zoom_image.collide_point(x, y): super(ZIScatter, self).on_touch_up(touch) """ ###TAKEN FROM ORIGINAL SCATTER x, y = touch.x, touch.y # if the touch isnt on the widget we do nothing, just try children if self.zoom_image.collide_point(x, y): #MODIFIED ORIGINAL SCATTER !! if not touch.grab_current == self: touch.push() touch.apply_transform_2d(self.to_local) if super(Scatter, self).on_touch_up(touch): touch.pop() return True touch.pop() # remove it from our saved touches if touch in self._touches and touch.grab_state: touch.ungrab(self) del self._last_touch_pos[touch] self._touches.remove(touch) # stop propagating if its within our bounds if self.collide_point(x, y): pass #eturn True #MODIFIED ORIGINAL SCATTER !! # physics behaviour on touch up, fade speed down on the same direction return False duration = d = 1.5 dx = touch.dx * 3 * d dy = touch.dy * 3 * d #print dx, dy adx = abs(dx) ady = abs(dy) if adx > 0 and ady > 0: #if adx > 400 : #if ady > 400 : V = Vector(self.center) Vd = Vector((dx, dy)) destination = V + Vd anim = Animation(center=destination, d=d, t='out_expo', s=1 / 150.) self.anim.stop(self) self.anim = anim self.anim.start(self) self.previous_anim_dest = destination return False def reset(self): self.center = self.init_pos self.scale = 1
def on_main_button(self, button, main_screen, enermy_screen, dmg, attack_effect, gold_text, exp_text, lv_text): """ on_main_button turn """ # Turn if self.game_status == GAME_STATUS_OPEN: # text code self.load_enermy() enermy_screen.center_x = enermy_screen.parent.center_x + 35 anim = Animation( center_x=enermy_screen.parent.center_x, duration=.2) if anim: anim.stop(self) anim.start(enermy_screen) self._status_change(GAME_STATUS_BATTLE) elif self.game_status == GAME_STATUS_BATTLE: self._status_change(GAME_STATUS_TURN) elif self.game_status == GAME_STATUS_TURN: # print dir(app.sound['swing']) app.sound['swing'].play() # enermy left = Animation( center_x=enermy_screen.parent.center_x - 10, duration=.2) right = Animation( center_x=enermy_screen.parent.center_x, duration=.2) anim = left + right if anim: anim.stop(self) anim.start(enermy_screen) # player left = Animation( center_x=enermy_screen.parent.center_x - 10, duration=.2) right = Animation( center_x=enermy_screen.parent.center_x, duration=.2) anim = left + right if anim: anim.stop(self) anim.start(enermy_screen) # damage # 실제 공격데미지 만큼 커지면 재미있을 듯 dmg_anim = Animation( center_y=dmg.parent.center_y + 100, duration=.4, t='out_circ') dmg_anim &= Animation(opacity=0, duration=.5) dmg_anim &= Animation(font_size=60, duration=.5) if dmg_anim: dmg.font_size = 30 dmg.center_y = dmg.parent.center_y + 40 dmg.opacity = 1 dmg_anim.stop(self) dmg_anim.start(dmg) # attack_effect attack_effect_anim = Animation( center_y=dmg.parent.center_y + 100, duration=2.0, t='out_circ') attack_effect_anim &= Animation(opacity=0.0, duration=.5) attack_effect_anim &= Animation(size=(120, 120), duration=.5) if attack_effect_anim: attack_effect.size = (40, 40) attack_effect.center_y = attack_effect.parent.center_y attack_effect.opacity = 1.0 attack_effect_anim.stop(self) attack_effect_anim.start(attack_effect) # check for fight if self.player.hp == 0 or self.enermy.hp == 0: return # take dmg each other if self.player.fight(self.enermy.ap): # if dead pass if self.enermy.fight(self.player.ap): # if dead self._status_change(GAME_STATUS_REWARD) elif self.game_status == GAME_STATUS_REWARD: app.sound['coin'].play() is_levelup = self.player.get_reward(self.enermy) # gold_text gold_anim = Animation( center_y=gold_text.parent.center_y + 100, duration=.2, t='out_circ') gold_anim &= Animation(opacity=0, duration=1.0) if gold_anim: gold_text.center_y = gold_text.parent.center_y + 40 gold_text.opacity = 1 gold_anim.stop(self) gold_anim.start(gold_text) # exp exp_anim = Animation( center_y=exp_text.parent.center_y + 130, duration=.2, t='out_circ') exp_anim &= Animation(opacity=0, duration=1.0) if exp_anim: exp_text.center_y = exp_text.parent.center_y + 40 exp_text.opacity = 1 exp_anim.stop(self) exp_anim.start(exp_text) if is_levelup: # lv lv_anim = Animation( center_y=lv_text.parent.center_y + 150, duration=.2, t='out_circ') lv_anim &= Animation(opacity=0, duration=2.0) lv_anim &= Animation(font_size=100, duration=2.0) if lv_anim: dmg.font_size = 25 lv_text.center_y = lv_text.parent.center_y + 40 lv_text.opacity = 1 lv_anim.stop(self) lv_anim.start(lv_text) # # reward # reward.center_y = reward.parent.center_y # reward.opacity=0 # reward_anim = Animation(center_y=reward.parent.center_y+60, duration=1.0, t='out_circ') # reward_anim.bind(on_complete=self.wow) # # reward_anim &= Animation(opacity=1, duration=3.0) # reward_anim.start(reward) self._status_change(GAME_STATUS_OPEN) else: raise # Text button name change button.name = status_name(self.game_status)
class PanelWidget(FloatLayout): """ The implementation of the base panel widget. Other widgets could be inherited from ``Panel`` in order to get the standard visual decoration and show/hide transitions. """ def __init__(self, **kwargs): """Initialize the widget and its state variables.""" super(PanelWidget, self).__init__(**kwargs) self.bind(pos=self.redraw) self.bind(size=self.redraw) # hardcoded stuff self.color = (.1, .6, .6) self.border_width = 1 self.corner_width = 1.5 self.corner_len = 7 self.opacity = .85 # show/hide animations (hardcoded in half) self.hidden = True self.trans_duration_in = .6 self.trans_duration_out = .3 self.show_style = "out_elastic" self.hide_style = "in_back" scroll_x = self.pos[0] global_w = Window.size[0] hidden_x = global_w + 10 self.x = hidden_x self.show_animation = Animation(x=scroll_x, duration=self.trans_duration_in, t=self.show_style) self.hide_animation = Animation(x=hidden_x, duration=self.trans_duration_out, t=self.hide_style) def toggle(self): """Toggle show/hide the panel with its contents.""" if self.hidden: self.hide_animation.stop(self) self.show_animation.start(self) else: self.show_animation.stop(self) self.hide_animation.start(self) self.hidden = not self.hidden def redraw(self, *_args): """Redraw panel on the canvas.""" saved_children = self.children[:] self.clear_widgets() self.canvas.clear() with self.canvas: # background Color(self.color[0] / 2, self.color[1] / 2, self.color[2] / 2, self.opacity) Rectangle(pos=self.pos, size=self.size) # border Color(self.color[0], self.color[1], self.color[2], 1) pos_x, pos_y = self.pos width, height = self.size Line(points=[ pos_x, pos_y, pos_x + width, pos_y, pos_x + width, pos_y + height, pos_x, pos_y + height, pos_x, pos_y ], width=self.border_width, cap='square', joint='miter') # corners Color(1, 1, 1, .7) Line(points=[ pos_x + self.corner_len, pos_y, pos_x, pos_y, pos_x, pos_y + self.corner_len ], width=self.corner_width, cap='square', joint='miter') Line(points=[ pos_x + width - self.corner_len, pos_y, pos_x + width, pos_y, pos_x + width, pos_y + self.corner_len ], width=self.corner_width, cap='square', joint='miter') Line(points=[ pos_x, pos_y + height - self.corner_len, pos_x, pos_y + height, pos_x + self.corner_len, pos_y + height ], width=self.corner_width, cap='square', joint='miter') Line(points=[ pos_x + width, pos_y + height - self.corner_len, pos_x + width, pos_y + height, pos_x + width - self.corner_len, pos_y + height ], width=self.corner_width, cap='square', joint='miter') for widget in saved_children: self.add_widget(widget)
class AnimationTestCase(unittest.TestCase): def sleep(self, t): start = time() while time() < start + t: sleep(.01) Clock.tick() def setUp(self): self.assertEqual(len(Animation._instances), 0) self.a = Animation(x=100, d=1, t='out_bounce') self.w = Widget() def tearDown(self): self.assertEqual(len(Animation._instances), 0) def test_start_animation(self): self.a.start(self.w) self.sleep(1.5) self.assertAlmostEqual(self.w.x, 100) def test_animation_duration_0(self): a = Animation(x=100, d=0) a.start(self.w) self.sleep(.5) def test_stop_animation(self): self.a.start(self.w) self.sleep(.5) self.a.stop(self.w) self.assertNotAlmostEqual(self.w.x, 100) self.assertNotAlmostEqual(self.w.x, 0) def test_stop_all(self): self.a.start(self.w) self.sleep(.5) Animation.stop_all(self.w) def test_stop_all_2(self): self.a.start(self.w) self.sleep(.5) Animation.stop_all(self.w, 'x') def test_duration(self): self.assertEqual(self.a.duration, 1) def test_transition(self): self.assertEqual(self.a.transition, AnimationTransition.out_bounce) def test_animated_properties(self): self.assertEqual(self.a.animated_properties['x'], 100) def test_animated_instruction(self): instruction = Scale(3) self.a.start(instruction) self.assertEqual(self.a.animated_properties['x'], 100) self.assertAlmostEqual(instruction.x, 3) self.sleep(1.5) self.assertAlmostEqual(instruction.x, 100) def test_weakref(self): widget = Widget() anim = Animation(x=100) anim.start(widget.proxy_ref) del widget gc.collect() try: self.sleep(1.) except ReferenceError: pass
class BaseRaisedButton(CommonElevationBehavior, BaseButton): ''' Abstract base class for raised buttons which elevate from material. Raised buttons are to be used sparingly to emphasise primary/important actions. Implements elevation behavior as well as the recommended down/disabled colors for raised buttons. ''' def __init__(self, **kwargs): if self.elevation_raised == 0 and self.elevation_normal + 6 <= 12: self.elevation_raised = self.elevation_normal + 6 elif self.elevation_raised == 0: self.elevation_raised = 12 super(BaseRaisedButton, self).__init__(**kwargs) self.elevation_press_anim = Animation(elevation=self.elevation_raised, duration=.2, t='out_quad') self.elevation_release_anim = Animation( elevation=self.elevation_normal, duration=.2, t='out_quad') _elev_norm = NumericProperty(2) def _get_elev_norm(self): return self._elev_norm def _set_elev_norm(self, value): self._elev_norm = value if value <= 12 else 12 self._elev_raised = (value + 6) if value + 6 <= 12 else 12 self.elevation = self._elev_norm self.elevation_release_anim = Animation(elevation=value, duration=.2, t='out_quad') elevation_normal = AliasProperty(_get_elev_norm, _set_elev_norm, bind=('_elev_norm',)) _elev_raised = NumericProperty(8) def _get_elev_raised(self): return self._elev_raised def _set_elev_raised(self, value): self._elev_raised = value if value + self._elev_norm <= 12 else 12 self.elevation_press_anim = Animation(elevation=value, duration=.2, t='out_quad') elevation_raised = AliasProperty(_get_elev_raised, _set_elev_raised, bind=('_elev_raised',)) def on_disabled(self, instance, value): if value: self.elevation = 0 else: self.elevation = self.elevation_normal super(BaseRaisedButton, self).on_disabled(instance, value) def on_touch_down(self, touch): if not self.disabled: if touch.is_mouse_scrolling: return False if not self.collide_point(touch.x, touch.y): return False if self in touch.ud: return False self.elevation_press_anim.stop(self) self.elevation_press_anim.start(self) return super(BaseRaisedButton, self).on_touch_down(touch) def on_touch_up(self, touch): if not self.disabled: if touch.grab_current is not self: return super(ButtonBehavior, self).on_touch_up(touch) self.elevation_release_anim.stop(self) self.elevation_release_anim.start(self) return super(BaseRaisedButton, self).on_touch_up(touch) def _get_md_bg_color_down(self): t = self.theme_cls c = self.md_bg_color # Default to no change on touch # Material design specifies using darker hue when on Dark theme if t.theme_style == 'Dark': if self.md_bg_color == t.primary_color: c = t.primary_dark elif self.md_bg_color == t.accent_color: c = t.accent_dark return c def _get_md_bg_color_disabled(self): if self.theme_cls.theme_style == 'Dark': c = (1., 1., 1., 0.12) else: c = (0., 0., 0., 0.12) return c
class Metronome(FloatLayout): needle_angle = NumericProperty(0) num_beats = NumericProperty(4) bpm = NumericProperty(200) top_prop = NumericProperty(0) def __init__(self, **kwargs): self.box = BoxLayout() self.beatbar = FloatLayout() self.buttonbar = BoxLayout() self.max_needle_angle = 30 super().__init__(**kwargs) self.spb = 60 / self.bpm self.time_sig = 4 self.sine_file = "./sine.wav" # Written from NumPy array self.player = pyaudio.PyAudio() sine = wave.open(self.sine_file, "rb") self.sine_data = sine.readframes(2048) self.stream = self.player.open( format=self.player.get_format_from_width(sine.getsampwidth()), channels=sine.getnchannels(), rate=sine.getframerate(), output=True) self.stopped = True self.needle_animation = Animation() self.beatmarker_animation = Animation() Clock.schedule_once(self.hide) def hide(self, *args): self.top = 0 def slide(self, state): if state == 'down' and self.top_prop == 0: self.top_prop = self.height + 50 else: self.top_prop = 0 def on_size(self, *args): target_ratio = 0.75 width, height = self.size if width / height > target_ratio: self.box.height = height self.box.width = target_ratio * height else: self.box.width = width self.box.height = width / target_ratio self.size = self.box.size def on_bpm(self, instance, bpm): self.spb = 60 / self.bpm self.stop() def increment_bpm(self, val): if 40 <= self.bpm + val <= 300: self.bpm += val def play(self, *args): if self.stopped: self.stopped = False thread = Thread(target=self._play, daemon=True) thread.start() def _play(self): start = time.time() delta = self.spb goal = start + delta forward = True while not self.stopped: beat_num = (time.time() - start) // delta self.animate_needle(self.spb, forward) self.animate_beatmarker(0.1, beat_num) self.stream.write(self.sine_data) time.sleep(goal - time.time()) forward = not forward goal += delta def stop(self): if not self.stopped: self.beatmarker_animation.stop(self) self.needle_animation.stop(self) self.stopped = True self.needle_angle = 0 def animate_needle(self, duration, forward): self.needle_animation.stop(self) if forward: self.needle_angle = -self.max_needle_angle self.needle_animation = Animation(needle_angle=self.max_needle_angle, duration=duration) else: self.needle_angle = self.max_needle_angle self.needle_animation = Animation(needle_angle=-self.max_needle_angle, duration=duration) self.needle_animation.start(self) def animate_beatmarker(self, duration, beat_num): self.beatmarker_animation.stop(self) self.beatmarker_animation = Animation(duration=duration) beat_idx = int(beat_num % self.num_beats) beatmarker = self.beatbar.beatmarkers.children[beat_idx] self.beatmarker_animation.bind(on_progress=beatmarker.update_animation) self.beatmarker_animation.bind(on_complete=beatmarker.end_animation) self.beatmarker_animation.start(self)
class MDFloatingTempActionButton(ThemableBehavior, CircularRippleBehavior, RoundElevationBehavior, ButtonBehavior, AnchorLayout): _bg_color_down = ListProperty([]) background_color = ListProperty() background_color_down = ListProperty() background_color_disabled = ListProperty() theme_text_color = OptionProperty( None, allownone=True, options=['Primary', 'Secondary', 'Hint', 'Error', 'Custom']) text_color = ListProperty(None, allownone=True) def _get_bg_color_down(self): return self._bg_color_down def _set_bg_color_down(self, color, alpha=None): if len(color) == 2: self._bg_color_down = get_color_from_hex( colors[color[0]][color[1]]) if alpha: self._bg_color_down[3] = alpha elif len(color) == 4: self._bg_color_down = color background_color_down = AliasProperty(_get_bg_color_down, _set_bg_color_down, bind=('_bg_color_down', )) _bg_color_disabled = ListProperty([]) def _get_bg_color_disabled(self): return self._bg_color_disabled def _set_bg_color_disabled(self, color, alpha=None): if len(color) == 2: self._bg_color_disabled = get_color_from_hex( colors[color[0]][color[1]]) if alpha: self._bg_color_disabled[3] = alpha elif len(color) == 4: self._bg_color_disabled = color background_color_disabled = AliasProperty(_get_bg_color_disabled, _set_bg_color_disabled, bind=('_bg_color_disabled', )) icon = StringProperty('android') _elev_norm = NumericProperty(6) def _get_elev_norm(self): return self._elev_norm def _set_elev_norm(self, value): self._elev_norm = value if value <= 12 else 12 self._elev_raised = (value + 6) if value + 6 <= 12 else 12 self.elevation = self._elev_norm elevation_normal = AliasProperty(_get_elev_norm, _set_elev_norm, bind=('_elev_norm', )) # _elev_raised = NumericProperty(12) _elev_raised = NumericProperty(6) def _get_elev_raised(self): return self._elev_raised def _set_elev_raised(self, value): self._elev_raised = value if value + self._elev_norm <= 12 else 12 elevation_raised = AliasProperty(_get_elev_raised, _set_elev_raised, bind=('_elev_raised', )) def __init__(self, **kwargs): if self.elevation_raised == 0 and self.elevation_normal + 6 <= 12: self.elevation_raised = self.elevation_normal + 6 elif self.elevation_raised == 0: self.elevation_raised = 12 super(MDFloatingTempActionButton, self).__init__(**kwargs) self.elevation_press_anim = Animation(elevation=self.elevation_raised, duration=.2, t='out_quad') self.elevation_release_anim = Animation( elevation=self.elevation_normal, duration=.2, t='out_quad') def _set_ellipse(self, instance, value): ellipse = self.ellipse ripple_rad = self.ripple_rad ellipse.size = (ripple_rad, ripple_rad) ellipse.pos = (self.center_x - ripple_rad / 2., self.center_y - ripple_rad / 2.) def on_disabled(self, instance, value): super(MDFloatingTempActionButton, self).on_disabled(instance, value) if self.disabled: self.elevation = 0 else: self.elevation = self.elevation_normal def on_touch_down(self, touch): if not self.disabled: if touch.is_mouse_scrolling: return False if not self.collide_point(touch.x, touch.y): return False if self in touch.ud: return False self.elevation_press_anim.stop(self) self.elevation_press_anim.start(self) return super(MDFloatingTempActionButton, self).on_touch_down(touch) def on_touch_up(self, touch): if not self.disabled: if touch.grab_current is not self: return super(ButtonBehavior, self).on_touch_up(touch) self.elevation_release_anim.stop(self) self.elevation_release_anim.start(self) return super(MDFloatingTempActionButton, self).on_touch_up(touch) def on_elevation_normal(self, instance, value): self.elevation = value def on_elevation_raised(self, instance, value): if self.elevation_raised == 0 and self.elevation_normal + 6 <= 12: self.elevation_raised = self.elevation_normal + 6 elif self.elevation_raised == 0: self.elevation_raised = 12
class HoveringBehavior(EventDispatcher): hovering = BooleanProperty(False) hovering_attrs = DictProperty() anim_kw = DictProperty() _orig_attrs = {} _last_pos = (0, 0) def __init__(self, **kw): self.register_event_type('on_enter') self.register_event_type('on_leave') super().__init__(**kw) self.bind_hovering() def bind_hovering(self, *args): Window.bind(mouse_pos=self.on_mouse_pos) def unbind_hovering(self, *args): Window.unbind(mouse_pos=self.on_mouse_pos) def on_mouse_pos(self, __, pos): self._last_pos = pos if not self.get_root_window(): return self.hovering = self.collide_point(*self.to_widget(*pos)) def refresh_hovering(self): self.on_mouse_pos(None, self._last_pos) def on_hovering(self, __, hovering): self.dispatch('on_enter' if hovering else 'on_leave') def update_orig_attrs(self, *args): self._orig_attrs = { key: getattr(self, key) for key in self.hovering_attrs.keys() } def on_enter(self): if getattr(self, 'disabled', False): return if not self._orig_attrs: self.update_orig_attrs() try: self.on_leave_anim.stop(self) except AttributeError: pass self.on_enter_anim = Animation(**self.hovering_attrs, **self.anim_kw) self.on_enter_anim.start(self) def on_leave(self): try: self.on_enter_anim.stop(self) except AttributeError: pass self.on_leave_anim = Animation(**self._orig_attrs, **self.anim_kw) self.on_leave_anim.start(self) def on_hovering_attrs(self, *args): self.on_hovering(self, self.hovering)
class ConnectionScreen(Screen): def __init__(self, **kwargs): super(ConnectionScreen, self).__init__(**kwargs) self.cancelConnect = False layout = FloatLayout() self.spinner = ProgressBar( max=1000, value=0, pos=[50, 600], size=[620, 50], size_hint_x=None, size_hint_y=None, ) self.anim = Animation(value=0) + Animation(value=1000) self.anim.repeat = True self.anim.start(self.spinner) self.connectLabel = Label( text="Connecting to WiFi", font_size="60px", font_name="Raleway-Regular.ttf", color=[1, 1, 1, 1], size=[620, 100], pos=[50, 700], text_size=[620, 100], halign="center", size_hint_x=None, size_hint_y=None, ) self.hintLabel = Label( text="Press F2 to skip", font_size="40px", font_name="Raleway-Regular.ttf", color=[1, 1, 1, 0.7], size=[620, 100], pos=[50, 500], text_size=[620, 100], halign="center", size_hint_x=None, size_hint_y=None, ) layout.add_widget(self.spinner) layout.add_widget(self.connectLabel) layout.add_widget(self.hintLabel) self.add_widget(layout) return None def startConnect(self): _thread.start_new_thread(self.connect, ()) def startUpdateConnection(self): _thread.start_new_thread(self.updateConnection, ()) def updateConnection(self): try: print("Update connection to ", config["ssid"], ", ", config["password"]) self.connectLabel.text = "Updating network" self.hintLabel.opacity = 0.0 out = check_output(["wpa_cli", "list_networks"]).decode(sys.stdout.encoding) lines = out.splitlines() for i in range(len(lines)): if i > 1: parts = lines[i].split("\t") id = parts[0].strip() print("Removing existing network ", id) out = check_output(["wpa_cli", "remove_network", id]).decode(sys.stdout.encoding) print(out) print("Adding a new network") out = check_output(["wpa_cli", "add_network"]).decode(sys.stdout.encoding) parts = out.splitlines() id = parts[1] print(out) print("Setting SSID") out = check_output([ "wpa_cli", "set_network", id, "ssid", '"' + config["ssid"] + '"' ]).decode(sys.stdout.encoding) print(out) print("Setting password") out = check_output([ "wpa_cli", "set_network", id, "psk", '"' + config["password"] + '"' ]).decode(sys.stdout.encoding) print(out) print("Selecting network") out = check_output(["wpa_cli", "select_network", id]).decode(sys.stdout.encoding) print(out) print("Enabling network") out = check_output(["wpa_cli", "enable_network", id]).decode(sys.stdout.encoding) print(out) print("Saving configuration") out = check_output(["wpa_cli", "save_config"]).decode(sys.stdout.encoding) print(out) self.connectLabel.text = "Restarting" time.sleep(3) print("Restarting") out = check_output(["sudo", "reboot", "now"]).decode(sys.stdout.encoding) print(out) print("Reconfiguring supplicant") out = check_output(["wpa_cli", "reconfigure"]).decode(sys.stdout.encoding) print(out) print("Restarting daemon") out = check_output(["sudo", "systemctl", "daemon-reload"]).decode(sys.stdout.encoding) print(out) print("Stopping network") out = check_output(["sudo", "systemctl", "stop", "dhcpcd"]).decode(sys.stdout.encoding) print(out) print("Starting network") out = check_output(["sudo", "systemctl", "start", "dhcpcd"]).decode(sys.stdout.encoding) print(out) except: print(traceback.format_exc()) def connect(self): global config self.connectLabel.text = "Connecting to WiFi" self.hintLabel.opacity = 0.5 print("Connecting to ", config["ssid"], ", ", config["password"]) time.sleep(3) if self.cancelConnect: self.anim.stop(self.spinner) self.spinner.value = 1000 self.connectLabel.text = "Cancelled" time.sleep(3) self.parent.current = "settings" return connected = False for i in range(20): if self.cancelConnect: self.anim.stop(self.spinner) self.spinner.value = 1000 self.connectLabel.text = "Cancelled" time.sleep(3) self.parent.current = "settings" break print("Connecting " + str(i + 1)) out = check_output(["ifconfig", "wlan0"]).decode(sys.stdout.encoding) if "inet " in out: connected = True break time.sleep(1) if not self.cancelConnect: if connected: self.anim.stop(self.spinner) self.spinner.value = 1000 self.connectLabel.text = "Connected" time.sleep(3) if config["id"] == "": print("Switching to Login") self.parent.current = "login" else: print("Switching to Main") self.parent.current = "main" else: self.anim.stop(self.spinner) self.spinner.value = 1000 self.connectLabel.text = "Failed to connect" time.sleep(3) self.parent.current = "settings"
class InterpreterGui(ScatterLayout): c1 = NumericProperty() c2 = NumericProperty() c3 = NumericProperty() c4 = NumericProperty() fontSizer = NumericProperty() c1 = 1 c2 = .3 c3 = .4 c4 = .85 fontSizer = 24 output_window = ObjectProperty() code_input = ObjectProperty() #scrollview = ObjectProperty() scatter = ObjectProperty() b = ObjectProperty() subprocesses = [] off = False input_fail_alpha = NumericProperty(0.) lock_input = BooleanProperty(False) _lock_input = BooleanProperty(False) halting = BooleanProperty(False) '''True when the interpreter has been asked to stop but has not yet done so.''' interpreter_state = OptionProperty( 'waiting', options=['waiting', 'interpreting', 'not_responding', 'restarting']) status_label_colour = StringProperty('b2ade6') _output_label_queue = ListProperty([]) dequeue_scheduled = ObjectProperty(None, allownone=True) clear_scheduled = ObjectProperty(None, allownone=True) awaiting_label_display_completion = BooleanProperty(False) throttle_label_output = BooleanProperty() '''Whether to clear the output label queue regularly. If False, labels will always be displayed, but this *will* cause problems with e.g. a constantly printing while loop. ''' interpreted_lines = ListProperty([]) '''A list of the lines of code that have been executed so far.''' completion_threads = [] '''The threads running jedi completion functions.''' most_recent_completion_time = 0. '''The most recent timestamp from a completion. New completions with older timestamps will be ignored.''' move_lock = False scale_lock_left = False scale_lock_right = False scale_lock_top = False scale_lock_bottom = False disp = 1 outDisp = 3 def __init__(self, *args, **kwargs): super(InterpreterGui, self).__init__(*args, **kwargs) self.animation = Animation(input_fail_alpha=0., t='out_expo', duration=0.5) self.interpreter = InterpreterWrapper( 'Interpreter', use_thread=True, throttle_output=App.get_running_app().setting__throttle_output, thread_name='interpreter') self.interpreter.bind( interpreter_state=self.setter('interpreter_state')) self.interpreter.bind(lock_input=self.setter('lock_input')) self.interpreter.bind(on_execution_complete=self.execution_complete) self.interpreter.bind(on_stdout=self.on_stdout) self.interpreter.bind(on_stderr=self.on_stderr) self.interpreter.bind(on_notification=self.on_notification) self.interpreter.bind(on_user_message=self.on_user_message) self.interpreter.bind(on_missing_labels=self.on_missing_labels) self.interpreter.bind(on_request_input=self.on_request_input) self.size_hint = .5, .5 Clock.schedule_once(self.post_init_check, 0) def flip(self): if self.disp == 1: self.code_input.disabled = True self.output_window.disabled = True self.disp = 0 else: self.code_input.disabled = False self.output_window.disabled = False self.disp = 1 def post_init_check(self, *args): if App.get_running_app().ctypes_working: return self.add_user_message_label( ('Could not load ctypes on this device. Keyboard interrupt ' 'will not be available.'), background_colour=(1, 0.6, 0, 1)) def on_lock_input(self, instance, value): if value: self.input_focus_on_disable = self.code_input.focus self._lock_input = True else: self._lock_input = False self.code_input.focus = self.input_focus_on_disable self.ensure_no_ctrl_c_button() self.halting = False def on_stdout(self, interpreter, text): kids = self.output_window.children if len(kids) > self.outDisp: self.output_window.remove_widget(kids[-2]) self.add_output_label(text, 'stdout') def on_stderr(self, interpreter, text): kids = self.output_window.children if len(kids) > self.outDisp: self.output_window.remove_widget(kids[-2]) self.add_output_label(text, 'stderr') def on_notification(self, interpreter, text): self.add_notification_label(text) def on_user_message(self, interpreter, text): self.add_user_message_label(text, background_colour=(1, 0.6, 0, 1)) def on_request_input(self, interpreter, prompt): self.show_input_popup(prompt) def show_input_popup(self, prompt): # Window.softinput_mode = 'below_target' p = InputPopup(prompt=prompt, submit_func=self.send_input) p.open() def send_input(self, text): '''Send the given input to the Python interpreter.''' self.interpreter.send_input(text) def ensure_ctrl_c_button(self): if not App.get_running_app().ctypes_working: return Clock.schedule_once(self._switch_to_ctrl_c_button, 0.4) def _switch_to_ctrl_c_button(self, *args): c = self.ids.carousel if c.index == 0: c.load_next() def clear_output(self): for child in self.output_window.children[:-1]: self.output_window.remove_widget(child) def exec_file(self): App.get_running_app().root.switch_to('filechooser', open_method=self._exec_file, success_screen_name='interpreter', purpose='exec file') def _exec_file(self, filename): self.add_user_message_label('Executing {}...'.format(filename)) self.ensure_ctrl_c_button() self.interpreter.exec_file(filename) def ensure_no_ctrl_c_button(self): Clock.unschedule(self._switch_to_ctrl_c_button) c = self.ids.carousel if c.index == 1: c.load_previous() else: Animation.cancel_all(c) c._start_animation(new_offset=0) def on_interpreter_state(self, instance, value): if value == 'waiting': self.status_label_colour = 'b2ade6' elif value == 'interpreting': self.status_label_colour = 'ade6b4' elif value == 'not_responding': self.status_label_colour = 'e6adad' elif value == 'restarting': self.status_label_colour = 'e6adad' def interpret_line_from_code_input(self): text = self.code_input.text if text == '': self.flash_input_fail() return self.code_input.text = '' self.interpret_line(text) def flash_input_fail(self): self.animation.stop(self) self.input_fail_alpha = 1. self.animation.start(self) def interpret_line(self, text): self.interpreted_lines.append(text) index = self.interpreter.interpret_line(text) self.add_input_label(text, index) self.ensure_ctrl_c_button() def add_user_message_label(self, text, **kwargs): l = UserMessageLabel(text=text, **kwargs) kids = self.output_window.children if len(kids) > self.outDisp: self.output_window.remove_widget(kids[-2]) self.output_window.add_widget(l) #self.scrollview.scroll_to(l) def add_doc_label(self, text, **kwargs): l = DocLabel(text=text, **kwargs) kids = self.output_window.children if len(kids) > self.outDisp: self.output_window.remove_widget(kids[-2]) self.output_window.add_widget(l) #self.scrollview.scroll_to(l) def add_input_label(self, text, index): kids = self.output_window.children if len(kids) > self.outDisp: self.output_window.remove_widget(kids[-2]) l = InputLabel(text=text, index=index, root=self) self.output_window.add_widget(l) #self.scrollview.scroll_to(l) def add_output_label(self, text, stream='stdout'): kids = self.output_window.children if len(kids) > self.outDisp: self.output_window.remove_widget(kids[-2]) self._output_label_queue.append((text, stream)) # self._dequeue_output_label(0) def _add_output_label(self, text, stream='stdout', scroll_to=True): l = OutputLabel(text=text, stream=stream) kids = self.output_window.children if len(kids) > self.outDisp: self.output_window.remove_widget(kids[-2]) self.output_window.add_widget(l) return l def _dequeue_output_label(self, dt): if not self._output_label_queue: return # print('dequeueing', self._output_label_queue) t = time() i = 0 while (time() - t) < 0.005: i += 1 if not self._output_label_queue: break label_text = self._output_label_queue.pop(0) label = self._add_output_label(*label_text, scroll_to=False) print('Rendered {} labels in {}'.format(i, time() - t)) # Animation.stop_all(self.scrollview, 'scroll_x', 'scroll_y') # self.scrollview.scroll_to(label) self.dequeue_scheduled.cancel() self.dequeue_scheduled = None if len(self._output_label_queue) == 0 and self.clear_scheduled: self.clear_scheduled.cancel() self.clear_scheduled = None elif len(self._output_label_queue) > 0: self.dequeue_scheduled = Clock.schedule_once( self._dequeue_output_label, 0.05) if (self.awaiting_label_display_completion and len(self._output_label_queue) == 0): self.awaiting_label_display_completion = False self._execution_complete() def _clear_output_label_queue(self, dt): if not self.throttle_label_output: return labels = self._output_label_queue self._output_label_queue = [] if labels: self.add_missing_labels_marker(labels=labels) if self.dequeue_scheduled: self.dequeue_scheduled.cancel() self.dequeue_scheduled = None if self.clear_scheduled: self.clear_scheduled.cancel() self.clear_scheduled = None if self.awaiting_label_display_completion: self.awaiting_label_display_completion = False self._execution_complete() def on__output_label_queue(self, instance, values): # print('olq', self.dequeue_scheduled, self.clear_scheduled) if self.dequeue_scheduled: return if not self.dequeue_scheduled: self.dequeue_scheduled = Clock.schedule_once( self._dequeue_output_label, 0) if not self.clear_scheduled: self.clear_scheduled = Clock.schedule_once( self._clear_output_label_queue, 1) def on_throttle_label_output(self, instance, value): self.interpreter.set_service_output_throttling(value) def on_missing_labels(self, instance, number): self.add_missing_labels_marker(num_labels=number) def add_missing_labels_marker(self, num_labels=None, labels=None): if labels is not None: num_labels = len(labels) self.add_user_message_label( '{} lines omitted (too many to render)'.format(num_labels), background_colour=(1, 0.6, 0, 1)) # l.labels = labels def add_notification_label(self, text): self.add_break() l = NotificationLabel(text=text) self.output_window.add_widget(l) #self.scrollview.scroll_to(l) self.add_break() def add_break(self): b = BreakMarker() self.output_window.add_widget(b) #self.scrollview.scroll_to(b) def insert_previous_code(self, index, clear=False): if clear: self.code_input.text = '' code = self.interpreter.inputs[index] if self.code_input.text == '': self.code_input.text = code else: self.code_input.text += '\n' + code def send_sigint(self): self.halting = True self.interpreter.send_sigint() def restart_interpreter(self): self.interpreted_lines = [] self.interpreter.restart() def query_restart(self): popup = RestartPopup(interpreter_gui=self) popup.open() def execution_complete(self, *args): '''Called when execution is complete so the TextInput should be unlocked etc., but first this is delayed until messages finish printing. ''' if len(self._output_label_queue) == 0: self._execution_complete() else: self.awaiting_label_display_completion = True def _execution_complete(self): self.add_break() self.lock_input = False self.halting = False self.ensure_no_ctrl_c_button() def get_defs(self): previous_text = '\n'.join(self.interpreted_lines) num_previous_lines = len(previous_text.split('\n')) text = self.code_input.text row_index, line, col_index = self.code_input.currently_edited_line() get_defs('\n'.join([previous_text, text]), self.show_defs, line=row_index + num_previous_lines + 1, col=col_index) def show_defs(self, defs, sigs, error=None): print('docs are', defs) if error is not None: self.add_doc_label(error) return if not defs and not sigs: self.add_doc_label('No definition found at cursor') return if defs: d = defs[0] else: d = sigs[0] if hasattr(d, 'params'): text = '{}({})\n{}'.format( d.desc_with_module, ', '.join([p.description for p in d.params]), d.doc) else: text = '{}\n{}'.format(d.desc_with_module, d.doc) self.add_doc_label(text) def on_touch_up(self, touch): self.move_lock = False self.scale_lock_left = False self.scale_lock_right = False self.scale_lock_top = False self.scale_lock_bottom = False self.size_hint = None, None if touch.grab_current is self: touch.ungrab(self) x = self.pos[0] / 10 x = round(x, 0) x = x * 10 y = self.pos[1] / 10 y = round(y, 0) y = y * 10 self.pos = x, y return super(InterpreterGui, self).on_touch_up(touch) # def transform_with_touch(self, touch): # self.size_hint = None,None # changed = False # x = self.bbox[0][0] # y = self.bbox[0][1] # width = self.bbox[1][0] # height = self.bbox[1][1] # mid_x = x + width / 2 # mid_y = y + height / 2 # inner_width = width * 0.5 # inner_height = height * 0.5 # left = mid_x - (inner_width / 2) # right = mid_x + (inner_width / 2) # top = mid_y + (inner_height / 2) # bottom = mid_y - (inner_height / 2) # # just do a simple one finger drag # if len(self._touches) == self.translation_touches: # # _last_touch_pos has last pos in correct parent space, # # just like incoming touch # dx = (touch.x - self._last_touch_pos[touch][0]) \ # * self.do_translation_x # dy = (touch.y - self._last_touch_pos[touch][1]) \ # * self.do_translation_y # dx = dx / self.translation_touches # dy = dy / self.translation_touches # dx = dx # dy = dy # if (touch.x > left and touch.x < right and touch.y < top and touch.y > bottom or self.move_lock) and not self.scale_lock_left and not self.scale_lock_right and not self.scale_lock_top and not self.scale_lock_bottom: # self.move_lock = True # self.apply_transform(Matrix().translate(dx, dy, 0)) # changed = True # change_x = touch.x - self.prev_x # change_y = touch.y - self.prev_y # anchor_sign = 1 # sign = 1 # if abs(change_x) >= 9 and not self.move_lock and not self.scale_lock_top and not self.scale_lock_bottom: # if change_x < 0: # sign = -1 # if (touch.x < left or self.scale_lock_left) and not self.scale_lock_right: # self.scale_lock_left = True # self.pos = (self.pos[0] + (sign * 10), self.pos[1]) # anchor_sign = -1 # elif (touch.x > right or self.scale_lock_right) and not self.scale_lock_left: # self.scale_lock_right = True # self.size[0] = self.size[0] + (sign * anchor_sign * 10) # self.prev_x = touch.x # changed = True # if abs(change_y) >= 9 and not self.move_lock and not self.scale_lock_left and not self.scale_lock_right: # if change_y < 0: # sign = -1 # if (touch.y > top or self.scale_lock_top) and not self.scale_lock_bottom: # self.scale_lock_top = True # elif (touch.y < bottom or self.scale_lock_bottom) and not self.scale_lock_top: # self.scale_lock_bottom = True # self.pos = (self.pos[0], self.pos[1] + (sign * 10)) # anchor_sign = -1 # self.size[1] = self.size[1] + (sign * anchor_sign * 10) # self.prev_y = touch.y # changed = True # return changed def on_touch_down(self, touch): parent = self.parent.parent me = self.parent parent.remove_widget(me) parent.add_widget(me) self.size_hint = None, None x, y = touch.x, touch.y self.prev_x = touch.x self.prev_y = touch.y # if the touch isnt on the widget we do nothing if not self.do_collide_after_children: if not self.collide_point(x, y): return False # let the child widgets handle the event if they want touch.push() touch.apply_transform_2d(self.to_local) if super(Scatter, self).on_touch_down(touch): # ensure children don't have to do it themselves if 'multitouch_sim' in touch.profile: touch.multitouch_sim = True touch.pop() self._bring_to_front(touch) return True touch.pop() # if our child didn't do anything, and if we don't have any active # interaction control, then don't accept the touch. if not self.do_translation_x and \ not self.do_translation_y and \ not self.do_rotation and \ not self.do_scale: return False if self.do_collide_after_children: if not self.collide_point(x, y): return False if 'multitouch_sim' in touch.profile: touch.multitouch_sim = True # grab the touch so we get all it later move events for sure self._bring_to_front(touch) touch.grab(self) self._touches.append(touch) self._last_touch_pos[touch] = touch.pos return True def Save(self): lifeRaft = [] lifeRaft.append(self.interpreted_lines) lifeRaft.append(self.code_input.text) return lifeRaft def Load(self, prior, curr): self.restart_interpreter() t.sleep(1) self.clear_output() t.sleep(10 / 1000) for i in prior: self.interpret_line(i) t.sleep(10 / 1000) self.code_input.text = curr self.clear_output()
class AutoFontSizeTicker: """ mixin """ # abstracts bind: callable font_size: float text: str texture_size: tuple width: float height: float # public attributes text_spacing: float = 9.0 # internal attributes _font_size_anim: Optional[Animation] = None _font_anim_mode: int = 0 _last_font_size: float _length_anim: Optional[Animation] = None _ori_text: str = "" _offset_anim: Optional[Animation] = None _ticker_text_offset: int = 0 _ticker_text_length: int = 0 _ticker_texture_width: float = 0.0 def __init__(self, **kwargs): super().__init__(**kwargs) self.bind(text=self.text_resize) self.bind(width=self.text_resize) self.bind(height=self.text_resize) def font_size_adjustable(self): """ check if font size need/has to be adjustable. """ if self.texture_size[0] + self.text_spacing < self.width \ and self.texture_size[1] + self.text_spacing < self.height \ and self.font_size < min(MAX_FONT_SIZE, self.height): return 1 elif (self.texture_size[0] + self.text_spacing > self.width or self.texture_size[1] + self.text_spacing > self.height) \ and self.font_size > MIN_FONT_SIZE: return -1 return 0 def text_resize(self, *_args): """ called on label text/size changed """ print(f"text_resize texture_width={self.texture_size[0]}", _args, " mem=", resource.getrusage(resource.RUSAGE_SELF).ru_maxrss) # self._last_font_size = 0 Clock.schedule_once(self._start_font_anim, 0.03) # needed to update texture_size # self._start_font_anim() def _start_font_anim(self, *_args): """ delayed anim check """ if not self.texture_size[0] or self._length_anim or self._offset_anim: print( f" SKIP START FONT ANIM texture_width={self.texture_size[0]}" f" length_anim={self._length_anim} ticker_anim={self._offset_anim}" ) return self._stop_font_anim() print( f"font_anim: font={self.font_size:2.2f} text={self.texture_size} wid=({self.width}, {self.height})" ) self._font_anim_mode = self.font_size_adjustable() if self._font_anim_mode == 1: reach_size = min(MAX_FONT_SIZE, self.height) print(f" GROW font size to {reach_size}") elif self._font_anim_mode == -1: reach_size = MIN_FONT_SIZE print(f" SHRINK font size to {MIN_FONT_SIZE}") else: self._stop_font_anim() if self.font_size <= MIN_FONT_SIZE: self._start_length_anim() else: print(" SKIP font size (stopped)") return self._font_size_anim = Animation(font_size=reach_size) self._font_size_anim.bind(on_progress=self._font_size_progress) # self._font_size_anim.bind(on_complete=self._start_length_anim) self._font_size_anim.start(self) def _stop_font_anim(self): if self._font_size_anim: self._font_size_anim.stop(self) self._font_size_anim = None self._font_anim_mode = 0 def _font_size_progress(self, _anim: Animation, _self: Widget, _progress: float): """ animation on_progress event handler. """ print( f" ON SIZE PROGRESS: font={self.font_size:2.2f} texture={self.texture_size}" ) if self._font_anim_mode and self._font_anim_mode != self.font_size_adjustable( ): print( f" STOPPED {'GROW' if self._font_anim_mode == 1 else 'SHRINK'}" ) self._stop_font_anim() if self._last_font_size: print(f" correct font size to {self._last_font_size}") self.font_size = self._last_font_size self._start_length_anim() elif self.font_size <= MIN_FONT_SIZE: print(f" SWITCH TO TICKER ANIM") self._stop_font_anim() self._start_length_anim() self._last_font_size = self.font_size def _start_length_anim(self, *_args): # self._stop_length_anim() print( f"length_anim: len={self._ticker_text_length} text={self.text} width={self.texture_size[0]}" ) self._ticker_texture_width = self.width - self.text_spacing self._ori_text = self.text print( f" START text width={len(self.text)} at texture width={self.texture_size[0]}" f" -> {self._ticker_texture_width}") self._length_anim = Animation( _ticker_text_length=round((len(self.text) + 1) / 2)) self._length_anim.bind(on_progress=self._ticker_length_progress) self._length_anim.start(self) def _start_offset_anim(self, *_args): print( f"offset_anim: offset={self._ticker_text_offset} text={self.text}" ) self._offset_anim = Animation( _ticker_text_offset=self._ticker_text_offset * 2, d=9.9) self._offset_anim.bind(on_progress=self._ticker_offset_progress) self._offset_anim.start(self) def _stop_length_anim(self): if self._length_anim: self.text = self._ori_text self._length_anim.stop(self) self._length_anim = None self._ticker_text_length = 0 self._ticker_text_offset = 0 self._ticker_texture_width = 0 self._ori_text = "" def _ticker_length_progress(self, _anim: Animation, _self: Widget, _progress: float): print( f" ON LENGTH PROGRESS: len={self._ticker_text_length} off={self._ticker_text_offset} txt={self.text}" ) if self.texture_size[ 0] + self.text_spacing < self._ticker_texture_width and not self._ticker_text_length: print( f" FOUND text width={len(self.text)} at texture width={self.texture_size[0]}" ) self._ticker_text_length = len(self.text) self._ticker_text_offset = round(self._ticker_max_offset() / 2) self._length_anim.stop(self) self._start_offset_anim() else: self.text = self.text[1:-1] print( f" SHRUNK text width={len(self.text)} at texture width={self.texture_size[0]}" ) def _ticker_max_offset(self): return round(len(self._ori_text) + 1 - self._ticker_text_length) def _ticker_offset_progress(self, _anim: Animation, _self: Widget, _progress: float): print( f" ON OFFSET PROGRESS: len={self._ticker_text_length} off={self._ticker_text_offset} txt={self.text}" ) beg = int(self._ticker_text_offset) end = beg + self._ticker_text_length self.text = self._ori_text[beg:end] if _progress >= 1.0: print( f" {'BACKWARDS' if beg else 'FORWARDS'} {_progress}" f" mem={resource.getrusage(resource.RUSAGE_SELF).ru_maxrss}") self._offset_anim.stop(self) self._offset_anim = Animation( _ticker_text_offset=0 if beg else self._ticker_max_offset(), d=9.9) self._offset_anim.bind(on_progress=self._ticker_offset_progress) self._offset_anim.start(self)
class InterpreterGui(BoxLayout): subprocesses = [] output_window = ObjectProperty() code_input = ObjectProperty() scrollview = ObjectProperty() input_fail_alpha = NumericProperty(0.) lock_input = BooleanProperty(False) _lock_input = BooleanProperty(False) halting = BooleanProperty(False) '''True when the interpreter has been asked to stop but has not yet done so.''' interpreter_state = OptionProperty( 'waiting', options=['waiting', 'interpreting', 'not_responding', 'restarting']) status_label_colour = StringProperty('b2ade6') _output_label_queue = ListProperty([]) dequeue_scheduled = ObjectProperty(None, allownone=True) clear_scheduled = ObjectProperty(None, allownone=True) awaiting_label_display_completion = BooleanProperty(False) throttle_label_output = BooleanProperty() '''Whether to clear the output label queue regularly. If False, labels will always be displayed, but this *will* cause problems with e.g. a constantly printing while loop. ''' interpreted_lines = ListProperty([]) '''A list of the lines of code that have been executed so far.''' completion_threads = [] '''The threads running jedi completion functions.''' most_recent_completion_time = 0. '''The most recent timestamp from a completion. New completions with older timestamps will be ignored.''' def __init__(self, *args, **kwargs): super(InterpreterGui, self).__init__(*args, **kwargs) self.animation = Animation(input_fail_alpha=0., t='out_expo', duration=0.5) self.interpreter = InterpreterWrapper( 'Interpreter', use_thread=True, throttle_output=self.setting__throttle_output, thread_name='interpreter') self.interpreter.bind( interpreter_state=self.setter('interpreter_state')) self.interpreter.bind(lock_input=self.setter('lock_input')) self.interpreter.bind(on_execution_complete=self.execution_complete) self.interpreter.bind(on_stdout=self.on_stdout) self.interpreter.bind(on_stderr=self.on_stderr) self.interpreter.bind(on_notification=self.on_notification) self.interpreter.bind(on_user_message=self.on_user_message) self.interpreter.bind(on_missing_labels=self.on_missing_labels) self.interpreter.bind(on_request_input=self.on_request_input) # self.interpreter = DummyInterpreter() # Clock.schedule_interval(self._dequeue_output_label, 0.05) # Clock.schedule_interval(self._clear_output_label_queue, 1) Clock.schedule_once(self.post_init_check, 0) def post_init_check(self, *args): if self.ctypes_working: return self.add_user_message_label( ('Could not load ctypes on this device. Keyboard interrupt ' 'will not be available.'), background_colour=(1, 0.6, 0, 1)) def on_lock_input(self, instance, value): if value: self.input_focus_on_disable = self.code_input.focus self._lock_input = True else: self._lock_input = False self.code_input.focus = self.input_focus_on_disable self.ensure_no_ctrl_c_button() self.halting = False def on_stdout(self, interpreter, text): self.add_output_label(text, 'stdout') def on_stderr(self, interpreter, text): self.add_output_label(text, 'stderr') def on_notification(self, interpreter, text): self.add_notification_label(text) def on_user_message(self, interpreter, text): self.add_user_message_label(text, background_colour=(1, 0.6, 0, 1)) def on_request_input(self, interpreter, prompt): self.show_input_popup(prompt) def show_input_popup(self, prompt): # Window.softinput_mode = 'below_target' p = InputPopup(prompt=prompt, submit_func=self.send_input) p.open() def send_input(self, text): '''Send the given input to the Python interpreter.''' self.interpreter.send_input(text) def ensure_ctrl_c_button(self): if not self.ctypes_working: return Clock.schedule_once(self._switch_to_ctrl_c_button, 0.4) def _switch_to_ctrl_c_button(self, *args): c = self.ids.carousel if c.index == 0: c.load_next() def clear_output(self): for child in self.output_window.children[:-1]: self.output_window.remove_widget(child) def exec_file(self): self.root.switch_to('filechooser', open_method=self._exec_file, success_screen_name='interpreter', purpose='exec file') def _exec_file(self, filename): self.add_user_message_label('Executing {}...'.format(filename)) self.ensure_ctrl_c_button() self.interpreter.exec_file(filename) def ensure_no_ctrl_c_button(self): Clock.unschedule(self._switch_to_ctrl_c_button) c = self.ids.carousel if c.index == 1: c.load_previous() else: Animation.cancel_all(c) c._start_animation(new_offset=0) def on_interpreter_state(self, instance, value): if value == 'waiting': self.status_label_colour = 'b2ade6' elif value == 'interpreting': self.status_label_colour = 'ade6b4' elif value == 'not_responding': self.status_label_colour = 'e6adad' elif value == 'restarting': self.status_label_colour = 'e6adad' def interpret_line_from_code_input(self): text = self.code_input.text if text == '': self.flash_input_fail() return self.code_input.text = '' self.interpret_line(text) def flash_input_fail(self): self.animation.stop(self) self.input_fail_alpha = 1. self.animation.start(self) def interpret_line(self, text): self.interpreted_lines.append(text) index = self.interpreter.interpret_line(text) self.add_input_label(text, index) self.ensure_ctrl_c_button() def add_user_message_label(self, text, **kwargs): l = UserMessageLabel(text=text, **kwargs) self.output_window.add_widget(l) self.scrollview.scroll_to(l) def add_doc_label(self, text, **kwargs): l = DocLabel(text=text, **kwargs) self.output_window.add_widget(l) self.scrollview.scroll_to(l) def add_input_label(self, text, index): l = InputLabel(text=text, index=index, root=self) self.output_window.add_widget(l) self.scrollview.scroll_to(l) def add_output_label(self, text, stream='stdout'): self._output_label_queue.append((text, stream)) # self._dequeue_output_label(0) def _add_output_label(self, text, stream='stdout', scroll_to=True): l = OutputLabel(text=text, stream=stream) self.output_window.add_widget(l) if scroll_to: self.scrollview.scroll_to(l) return l def _dequeue_output_label(self, dt): if not self._output_label_queue: return # print('dequeueing', self._output_label_queue) t = time() i = 0 while (time() - t) < 0.005: i += 1 if not self._output_label_queue: break label_text = self._output_label_queue.pop(0) label = self._add_output_label(*label_text, scroll_to=False) print('Rendered {} labels in {}'.format(i, time() - t)) Animation.stop_all(self.scrollview, 'scroll_x', 'scroll_y') self.scrollview.scroll_to(label) self.dequeue_scheduled.cancel() self.dequeue_scheduled = None if len(self._output_label_queue) == 0 and self.clear_scheduled: self.clear_scheduled.cancel() self.clear_scheduled = None elif len(self._output_label_queue) > 0: self.dequeue_scheduled = Clock.schedule_once( self._dequeue_output_label, 0.05) if (self.awaiting_label_display_completion and len(self._output_label_queue) == 0): self.awaiting_label_display_completion = False self._execution_complete() def _clear_output_label_queue(self, dt): if not self.throttle_label_output: return labels = self._output_label_queue self._output_label_queue = [] if labels: self.add_missing_labels_marker(labels=labels) if self.dequeue_scheduled: self.dequeue_scheduled.cancel() self.dequeue_scheduled = None if self.clear_scheduled: self.clear_scheduled.cancel() self.clear_scheduled = None if self.awaiting_label_display_completion: self.awaiting_label_display_completion = False self._execution_complete() def on__output_label_queue(self, instance, values): # print('olq', self.dequeue_scheduled, self.clear_scheduled) if self.dequeue_scheduled: return if not self.dequeue_scheduled: self.dequeue_scheduled = Clock.schedule_once( self._dequeue_output_label, 0) if not self.clear_scheduled: self.clear_scheduled = Clock.schedule_once( self._clear_output_label_queue, 1) def on_throttle_label_output(self, instance, value): self.interpreter.set_service_output_throttling(value) def on_missing_labels(self, instance, number): self.add_missing_labels_marker(num_labels=number) def add_missing_labels_marker(self, num_labels=None, labels=None): if labels is not None: num_labels = len(labels) self.add_user_message_label( '{} lines omitted (too many to render)'.format(num_labels), background_colour=(1, 0.6, 0, 1)) # l.labels = labels def add_notification_label(self, text): self.add_break() l = NotificationLabel(text=text) self.output_window.add_widget(l) self.scrollview.scroll_to(l) self.add_break() def add_break(self): b = BreakMarker() self.output_window.add_widget(b) self.scrollview.scroll_to(b) def insert_previous_code(self, index, clear=False): if clear: self.code_input.text = '' code = self.interpreter.inputs[index] if self.code_input.text == '': self.code_input.text = code else: self.code_input.text += '\n' + code def send_sigint(self): self.halting = True self.interpreter.send_sigint() def restart_interpreter(self): self.interpreted_lines = [] self.interpreter.restart() def query_restart(self): popup = RestartPopup(interpreter_gui=self) popup.open() def execution_complete(self, *args): '''Called when execution is complete so the TextInput should be unlocked etc., but first this is delayed until messages finish printing. ''' if len(self._output_label_queue) == 0: self._execution_complete() else: self.awaiting_label_display_completion = True def _execution_complete(self): self.add_break() self.lock_input = False self.halting = False self.ensure_no_ctrl_c_button() def get_defs(self): previous_text = '\n'.join(self.interpreted_lines) num_previous_lines = len(previous_text.split('\n')) text = self.code_input.text row_index, line, col_index = self.code_input.currently_edited_line() get_defs('\n'.join([previous_text, text]), self.show_defs, line=row_index + num_previous_lines + 1, col=col_index) def show_defs(self, defs, sigs, error=None): print('docs are', defs) if error is not None: self.add_doc_label(error) return if not defs and not sigs: self.add_doc_label('No definition found at cursor') return if defs: d = defs[0] else: d = sigs[0] if hasattr(d, 'params'): text = '{}({})\n{}'.format( d.desc_with_module, ', '.join([p.description for p in d.params]), d.doc) else: text = '{}\n{}'.format(d.desc_with_module, d.doc) self.add_doc_label(text)
class AlarmScreen(Screen): r = NumericProperty(100) # Radio del círculo ra = NumericProperty(1.0) # Alfa del círculo r2 = NumericProperty(100) # Radio del círculo que marca pulsación r2a = NumericProperty(0) # Alfa del círculo que marca pulsación cd = NumericProperty(0.0) # Offset de los círculos que se desplazan ca = NumericProperty(1.0) # Alfa de los círculos que se desplazan R = 0.08 # Constante para calcular el tamaño motion_uid = None text = StringProperty('Alarma!') timetext = StringProperty('') inicio = datetime.now() # Se actualiza en sonar_alarma def on_height(self, widget, height): self.r = height*self.R self.r2 = height*self.R def on_enter(self, *args): Logger.debug("%s: AlarmScreen.on_enter self.width=%s %s" % ( APP, self.width, datetime.now())) if self.width < 100: width = 480 # No debería hacer falta, pero la primera vez falla else: width = self.width self.r2a = self.ca = self.cd = 0 self.anim = Animation(cd=width/2, ca=1, d=1, t='in_quad') \ + Animation(cd=0, ca=0, d=0) self.anim.repeat = True self.anim.start(self) self.app.reproducir_sonido_alarma() self.app.clock_callback = partial( self.app.cancelar_alarma, source='Clock', clock_date=datetime.now()) Clock.schedule_once(self.app.clock_callback, self.app.ACS) # segundos self.update_timetext() Clock.schedule_interval(self.update_timetext, 1) def on_pre_leave(self, *args): self.anim.stop(self) Logger.debug("%s: AlarmScreen.on_pre_leave %s" % (APP, datetime.now())) if platform == 'android': self.app.reset_window_flags() # Permitir apagado automático Clock.unschedule(self.update_timetext) def on_touch_down(self, touch): if Vector(touch.pos).distance( Vector(self.center_x, self.height*0.2)) < 2*self.r: self.motion_uid = touch.uid self.r2a = .2 self.r2 = self.height*self.R anim = Animation( r2=self.height*self.R*2.3, t='in_quad', d=0.3) anim.start(self) def on_touch_move(self, touch): if touch.uid == self.motion_uid: # Cancelar si de desplaza más de la mitad de la distancia al borde self.ra = 1-3.3*Vector(touch.pos).distance( Vector(self.center_x, self.height*0.2))/self.width self.r2a = 1 - self.ra * 0.8 if self.ra < 0.05: self.ra = 1 self.app.cancelar_alarma(source='user') def on_touch_up(self, touch): if touch.uid == self.motion_uid: self.ra = 1 self.r2a = 0 def update_timetext(self, *args): self.timetext = "%s - %s" % (self.inicio.strftime("%H:%M"), tdformat(self.inicio-datetime.now()))
class GraphCanvas(Widget): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._touches = [] self.delay = RESIZE_DELAY self._mouse_pos_disabled = True self._init_animations() self.load_graph() def _init_animations(self): self.scale_animation = ( Animation(size=(ANIMATION_WIDTH_2, ANIMATION_HEIGHT_2), duration=SCALE_SPEED_OUT, step=UPDATE_INTERVAL) + Animation(size=(ANIMATION_WIDTH, ANIMATION_HEIGHT), duration=SCALE_SPEED_IN, step=UPDATE_INTERVAL)) self.scale_animation.repeat = True self.scale_animation.bind(on_progress=self._reposition_animated_node) self.rotate_animation = Clock.schedule_interval( self._rotate_node, UPDATE_INTERVAL) self.rotate_animation.cancel() self.edge_color_animation = Animation(a=0) self.edge_animation = Animation(width=ANIMATED_EDGE_WIDTH) self.edge_animation.bind(on_start=self._edge_animation_start, on_complete=self._reschedule_edge_animation) # Schedule events self.edge_move = Clock.schedule_interval(self._move_edge, UPDATE_INTERVAL) self.edge_move.cancel() self.resize_event = Clock.schedule_once(self.update_canvas, self.delay) self.resize_event.cancel() self.layout_stepper = Clock.schedule_interval(self.step_layout, UPDATE_INTERVAL) self.layout_stepper.cancel() def load_graph(self): """Set initial graph. """ self._selecting_nnodes = True NewGameDialogue(self).open() def setup_canvas(self): """Populate the canvas with the initial instructions. """ self._selecting_nnodes = False self.G = Graph.Star(self.nnodes, mode="out") self._unscaled_layout = Layout([(0.0, 0.0), *circle_points(self.nnodes - 1)]) self.scale = INIT_SCALE self.offset_x, self.offset_y = INIT_OFFSET self._selected_edge = self._selected_node = None self._source_node = self._target_edge = None self.canvas.clear() with self.canvas.before: self.background_color = Color(*BACKGROUND_COLOR) self._background = Rectangle(size=self.size, pos=self.pos) with self.canvas: self.animated_edge_color = Color(*HIGHLIGHTED_EDGE) self.animated_edge_color.a = 0 self.animated_edge = Line(width=1.1) # Edge instructions before Node instructions so they're drawn underneath nodes. self._edge_instructions = CanvasBase() with self._edge_instructions: self.edges = { edge.tuple: Edge(edge.tuple, self) for edge in self.G.es } self.canvas.add(self._edge_instructions) # Animated node drawn above edges but below other nodes. with self.canvas: PushMatrix() self.rotation_instruction = Rotate() self.animated_node_color = Color(*ANIMATED_NODE_COLOR) self.animated_node_color.a = 0 self.animated_node = Rectangle(size=(ANIMATION_WIDTH, ANIMATION_HEIGHT), source=ANIMATED_NODE_SOURCE) PopMatrix() self._node_instructions = CanvasBase() with self._node_instructions: self.nodes = [Node(vertex.index, self) for vertex in self.G.vs] self.canvas.add(self._node_instructions) # TODO: Refactor so we only need to do this once self.bind(size=self._delayed_resize, pos=self._delayed_resize) Window.bind(mouse_pos=self.on_mouse_pos) self.step_layout() self.layout_stepper() self._mouse_pos_disabled = False def reset(self): self._mouse_pos_disabled = True # Stop all animations self.layout_stepper.cancel() self.edge_move.cancel() self.scale_animation.stop(self.animated_node) self.rotate_animation.cancel() self.edge_color_animation.stop(self.animated_edge_color) self.edge_animation.stop(self.animated_edge) self.canvas.clear() self.load_graph() @property def selected_edge(self): """While there is no source node, the selected edge is just the edge that is currently colliding with the mouse. """ return self._selected_edge @selected_edge.setter def selected_edge(self, edge): if self.selected_edge is not None: self.selected_edge.is_tail_selected = None self._selected_edge = edge @property def selected_node(self): """This is the end of selected edge that is closest to the mouse position. """ return self._selected_node @selected_node.setter def selected_node(self, node): edges = self.edges G = self.G if self._selected_node is not None: # Reset node and out-edges to their default colors self._selected_node.color.rgba = NODE_COLOR for edge in G.vs[self._selected_node.index].out_edges(): e = edges[edge.tuple] if e is not self.selected_edge: e.color.rgba = EDGE_COLOR e.head_color.rgba = HEAD_COLOR self._selected_node = node if node is not None: # Highlight this node and adjacent out-edges node.color.rgba = HIGHLIGHTED_NODE for edge in G.vs[node.index].out_edges(): e = edges[edge.tuple] if e is not self.selected_edge: e.color.rgba = HIGHLIGHTED_EDGE e.head_color.rgba = HIGHLIGHTED_HEAD self._selected_node_x, self._selected_node_y = self._unscaled_layout[ node.index] self.animated_node_color.a = 1 self.rotate_animation() self.scale_animation.start(self.animated_node) else: self.animated_node_color.a = 0 self.rotate_animation.cancel() self.scale_animation.stop(self.animated_node) @property def source_node(self): """Source node is set to selected node when the selected node is clicked. """ return self._source_node @source_node.setter def source_node(self, node): if self.source_node is not None: self.animated_node_color.rgba = HIGHLIGHTED_EDGE self._source_node = node if node is not None: self.animated_node_color.rgba = HIGHLIGHTED_NODE @property def target_edge(self): """The target edge is the edge we move along. """ return self._target_edge @target_edge.setter def target_edge(self, edge): if self.target_edge is not None: self.target_edge.color.rgba = HIGHLIGHTED_EDGE self.target_edge.head_color.rgba = HIGHLIGHTED_HEAD self._keep_animating = False self.edge_animation.stop(self.animated_edge) self._target_edge = edge if edge is not None: edge.color.rgba = HIGHLIGHTED_NODE edge.head_color.rgba = WHITE self._keep_animating = True self.edge_animation.start(self.animated_edge) def _transform_coords(self, coord): """Transform vertex coordinates to canvas coordinates. """ return ( (coord[0] * self.scale + self.offset_x) * self.width, (coord[1] * self.scale + self.offset_y) * self.height, ) def _invert_coords(self, x, y): """Transform canvas coordinates to vertex coordinates. """ return (x / self.width - self.offset_x) / self.scale, ( y / self.height - self.offset_y) / self.scale def _rotate_node(self, dt): """This rotates `animated_node` when called. `dt` does nothing, but is required for kivy's scheduler. """ self.rotation_instruction.origin = self.layout[ self.selected_node.index] self.rotation_instruction.angle = (self.rotation_instruction.angle + ROTATE_INCREMENT) % 360 def _reposition_animated_node(self, *args): x, y = self.layout[self.selected_node.index] w, h = self.animated_node.size self.animated_node.pos = x - w // 2, y - h // 2 def _delayed_resize(self, *args): self.resize_event.cancel() self.resize_event() self._background.size = self.size self._background.pos = self.pos def _edge_animation_start(self, *args): self.animated_edge.width = 1.1 self.animated_edge_color.a = 1 self.edge_color_animation.start(self.animated_edge_color) def _reschedule_edge_animation(self, *args): self.edge_color_animation.stop(self.animated_edge_color) self.animated_edge_color.a = 0 self.animated_edge.width = 1.1 if self._keep_animating: # Just calling edge_animation.start won't work as we're still animating, we must schedule the restart. Clock.schedule_once( lambda dt: self.edge_animation.start(self.animated_edge)) def _lerp_edge(self): """Generator that updates the selected edge position. """ # Before we reset the edge colors grab the information we need to lerp: selected_edge = self.selected_edge is_tail_selected = selected_edge.is_tail_selected sx, sy, tx, ty = selected_edge.points start_x, start_y, stop_x, stop_y = self.target_edge.points new_end = self.target_edge.edge[1] # Reset the colors: self.source_node = self.target_edge = self.selected_edge = None # WARNING: The order of these assignments is important. self.layout_stepper.cancel( ) # Need to turn off the layout_stepper while lerping self._mouse_pos_disabled = True yield for i in range(MOVE_STEPS): k = i / MOVE_STEPS x = start_x * (1 - k) + stop_x * k y = start_y * (1 - k) + stop_y * k selected_edge.update_points(*((x, y, tx, ty) if is_tail_selected else (sx, sy, x, y))) yield return selected_edge, is_tail_selected, new_end # _move_edge needs this information to update the underlying graph def _move_edge(self, dt): """Lerp the selected edge to it's new position and update the underlying graph when finished. """ try: next(self._edge_lerp) except StopIteration as e: selected_edge, is_tail_selected, new_end = e.value self.edge_move.cancel() self.G.delete_edges((selected_edge.edge, )) del self.edges[selected_edge.edge] source, target = selected_edge.edge if is_tail_selected: selected_edge.edge = self.G.add_edge(new_end, target).tuple self.edges[new_end, target] = selected_edge else: selected_edge.edge = self.G.add_edge(source, new_end).tuple self.edges[source, new_end] = selected_edge self.layout_stepper() self._mouse_pos_disabled = False def move_edge(self): self._edge_lerp = self._lerp_edge() next( self._edge_lerp ) # Prime the generator -- If we don't do this immediately it's possible to lose the # selected edge information before the scheduler calls `_move_edge` self.edge_move() def on_touch_move(self, touch): """Zoom if multitouch, else if a node is selected, drag it, else move the entire graph. """ if touch.grab_current is not self or touch.button == 'right': return if self._selecting_nnodes: return if len(self._touches) > 1: self.transform_on_touch(touch) elif self.selected_edge is not None: px, py = self._invert_coords(touch.px, touch.py) x, y = self._invert_coords(touch.x, touch.y) self._selected_node_x += x - px self._selected_node_y += y - py else: self.offset_x += touch.dx / self.width self.offset_y += touch.dy / self.height self.update_canvas() return True def transform_on_touch(self, touch): """Rescales the canvas. """ ax, ay = self._touches[-2].pos # Anchor coords x, y = self._invert_coords(ax, ay) cx = (touch.x - ax) / self.width cy = (touch.y - ay) / self.height current_length = hypot(cx, cy) px = (touch.px - ax) / self.width py = (touch.py - ay) / self.height previous_length = hypot(px, py) self.scale = max(self.scale + current_length - previous_length, MIN_SCALE) # Make sure the anchor is a fixed point: # Note we can't use `ax, ay` as `self.scale` has changed. x, y = self._transform_coords((x, y)) self.offset_x += (ax - x) / self.width self.offset_y += (ay - y) / self.height def on_touch_down(self, touch): if touch.is_mouse_scrolling: # REMOVE THIS: For testing only self.reset() return True if self._selecting_nnodes: return if not self.collide_point(*touch.pos): return touch.grab(self) self._touches.append(touch) self._mouse_pos_disabled = True # Change the color of multitouch dots to match our color scheme: if touch.button == 'right': touch.multitouch_sim = True with Window.canvas.after: touch.ud._drawelement = ( Color(*HIGHLIGHTED_EDGE), Ellipse(size=(20, 20), segments=15, pos=(touch.x - 10, touch.y - 10)), ) return True def on_touch_up(self, touch): if touch.grab_current is not self: return touch.ungrab(self) self._touches.remove(touch) self._mouse_pos_disabled = False if touch.time_end - touch.time_start > TOUCH_INTERVAL: return if self.source_node is not None: if self.target_edge is not None: self.move_edge() else: self.source_node = None # Recheck collision with edge: collides, is_tail_selected = self.selected_edge.collides( touch.x, touch.y) if collides: self.selected_edge.is_tail_selected = is_tail_selected else: self.selected_edge = None elif self.selected_node is not None: self.source_node = self.selected_node def on_mouse_pos(self, *args): mx, my = args[-1] if self._mouse_pos_disabled or not self.collide_point(mx, my): return # If source node is set, check collision with an adjacent out-edge. if self.source_node is not None: if self.target_edge is None: for edge in self.G.vs[self.source_node.index].out_edges(): target = self.edges[edge.tuple] if target is not self.selected_edge and target.collides( mx, my)[0]: self.target_edge = target break else: if not self.target_edge.collides(mx, my)[0]: self.target_edge = None # If an edge is selected, just check collision with that edge. elif self.selected_edge is not None: collides, is_tail_selected = self.selected_edge.collides(mx, my) if collides: self.selected_edge.is_tail_selected = is_tail_selected else: self.selected_edge = None # Check collision with all edges. else: for edge in self.edges.values(): collides, is_tail_selected = edge.collides(mx, my) if collides: self.selected_edge = edge # This should be set before `edge.is_tail_selected` edge.is_tail_selected = is_tail_selected break else: self.selected_edge = None def update_canvas(self, dt=0): """Update coordinates of all elements. `dt` is a dummy arg required for kivy's scheduler. """ if self.resize_event.is_triggered: # We use a delayed resize, this will make sure we're done resizing before we update. return for edge in self.edges.values(): edge.update() if self.target_edge is not None: self.animated_edge.points = self.target_edge.points for node in self.nodes: node.update() def step_layout(self, dt=0): """Iterate the graph layout algorithm. `dt` is a dummy arg required for kivy's scheduler. """ self._unscaled_layout = self.G.layout_graphopt( niter=1, seed=self._unscaled_layout, max_sa_movement=.1, node_charge=.00001) # Keep the selected node fixed: if self.selected_node is not None: self._unscaled_layout[ self.selected_node. index] = self._selected_node_x, self._selected_node_y self.layout = self._unscaled_layout.copy() self.layout.transform(self._transform_coords) self.update_canvas()
class FloatingActionButton(ThemeBehaviour, CircularRippleBehavior, ElevationBehaviour, ButtonBehavior, AnchorLayout): _bg_color_down = ListProperty([]) def _get_bg_color_down(self): return self._bg_color_down def _set_bg_color_down(self, color, alpha=None): if len(color) == 2: self._bg_color_down = get_rgba_color(color, control_alpha=alpha) elif len(color) == 4: self._bg_color_down = color background_color_down = AliasProperty(_get_bg_color_down, _set_bg_color_down, bind=('_bg_color_down', )) _bg_color_disabled = ListProperty([]) def _get_bg_color_disabled(self): return self._bg_color_disabled def _set_bg_color_disabled(self, color, alpha=None): if len(color) == 2: self._bg_color_disabled = get_rgba_color(color, control_alpha=alpha) elif len(color) == 4: self._bg_color_disabled = color background_color_disabled = AliasProperty(_get_bg_color_disabled, _set_bg_color_disabled, bind=('_bg_color_disabled', )) theme_style = OptionProperty(None, options=['Light', 'Dark', 'Custom'], allownone=True) icon_color = ListProperty(None, allownone=True) elevation_normal = BoundedNumericProperty(2, min=0, max=12) elevation_raised = BoundedNumericProperty(0, min=0, max=12) icon = StringProperty('md-android') def __init__(self, **kwargs): self.elevation = self.elevation_normal if self.elevation_raised == 0 and self.elevation_normal + 6 <= 12: self.elevation_raised = self.elevation_normal + 6 elif self.elevation_raised == 0: self.elevation_raised = 12 super(FloatingActionButton, self).__init__(**kwargs) self.background_color = self._theme_cls.primary_color self.background_color_down = self._theme_cls.primary_dark self.background_color_disabled = self._theme_cls.disabled_bg_color() self.elevation_press_anim = Animation(elevation=self.elevation_raised, duration=.2, t='out_quad') self.elevation_release_anim = Animation( elevation=self.elevation_normal, duration=.2, t='out_quad') def _set_ellipse(self, instance, value): ellipse = self.ellipse ripple_rad = self.ripple_rad ellipse.size = (ripple_rad, ripple_rad) ellipse.pos = (self.center_x - ripple_rad / 2., self.center_y - ripple_rad / 2.) def on_disabled(self, instance, value): super(FloatingActionButton, self).on_disabled(instance, value) if self.disabled: self.elevation = 0 else: self.elevation = self.elevation_normal def on_touch_down(self, touch): if not self.disabled: if touch.is_mouse_scrolling: return False if not self.collide_point(touch.x, touch.y): return False if self in touch.ud: return False self.elevation_press_anim.stop(self) self.elevation_press_anim.start(self) return super(FloatingActionButton, self).on_touch_down(touch) def on_touch_up(self, touch): if not self.disabled: if touch.grab_current is not self: return super(ButtonBehavior, self).on_touch_up(touch) self.elevation_release_anim.stop(self) self.elevation_release_anim.start(self) return super(FloatingActionButton, self).on_touch_up(touch) def on_elevation_normal(self, instance, value): self.elevation = value def on_elevation_raised(self, instance, value): if self.elevation_raised == 0 and self.elevation_normal + 6 <= 12: self.elevation_raised = self.elevation_normal + 6 elif self.elevation_raised == 0: self.elevation_raised = 12
class Pomodoro(BoxLayout): time_period = NumericProperty() rotation = NumericProperty(0) sprint_count = NumericProperty(0) time_display = StringProperty() time_minute = StringProperty() time_second = StringProperty() state = StringProperty() start = StringProperty() stop = StringProperty() logs = ListProperty([]) server_url = StringProperty() #"http://172.18.140.79:8000/api/v1.0/put/pomodoro" server_user = StringProperty("barbaros") server_send = BooleanProperty(False) count_start = BooleanProperty(False) connect_server = BooleanProperty(False) buttons = ListProperty() def __init__(self, *args, **kwargs): super(Pomodoro, self).__init__(*args, **kwargs) self.buttons = get_buttons(self, []) self.animation = None self.clock = SoundLoader.load('assets/clock.wav') self.alarm = SoundLoader.load('assets/alarm.wav') self.load_log_dates() #self.load_logs() try: last_session = DB.store_get('last_session') except KeyError: last_session = "" if last_session: self.set_restart_session(last_session) else: self.set_to_workstate() if ACTIVE_STYLE == "style1": self.sm.current = 'start' def show_log(self, index): """ show_log, to display specified log with more information and full log content. """ data = self.logs[index] self.switch_screen('logdetail_screen', side='down') floatlayout = self.sm.current_screen.children[0] floatlayout.log_detail.text = data['content'] floatlayout.date_distance.text = "[color=%s]%s - %s[/color]"%( floatlayout.date_distance.color_data, data['date_from'].split(' ')[1], data['date_to'].split(' ')[1]) def db_check(self): """ db_check, json data can be damaged as objects on it to make it back to json serializable dict function called. """ keys = DB.keys() for k in keys: value = DB.store_get(k) if type(value) == list: for v in value: if type(v) == dict: if 'date_from' in v: v['date_from'] = str(v['date_from']) if 'date_to' in v: v['date_to'] = str(v['date_to']) def load_log_dates(self): """ load_log_dates, is for picking log days only and serving as sorted reversely. """ days = list(set(filter(lambda x: len(x.split("-"))==3, DB.keys()))) days.sort(reverse=True) self.log_spinner.values = days def load_logs(self, date=""): """ load_logs, according to the given date required data set is taken and serves to app. """ logs = [] days = filter(lambda x: len(x.split("-"))==3, DB.keys()) for day in days: data = DB.store_get(day) DB.store_sync() for log in data: if type(log['date_from']) != datetime: log['date_from'] = datetime.strptime(log['date_from'], "%Y-%m-%d %H:%M:%S") if type(log['date_to']) != datetime: log['date_to'] = datetime.strptime(log['date_to'], "%Y-%m-%d %H:%M:%S") if day == date: logs.extend(data) logs.sort(key=lambda x: x['date_from'], reverse=True) counter = 0 for log in logs: log['index'] = logs.index(log) self.logs = logs self.db_check() def set_restart_session(self, dct): """ To restore previously stored, because of restart operation, data. Not to reach the same session after restoring operation the data must be deleted. """ self.message.text = dct['message_text'] self.message.disabled = dct['message_disabled'] self.server_url = dct['server_url'] self.count_start = dct['count_start'] self.state = dct['state'] self.time_period = dct['time_period'] self.start = dct['start'] self.stop = dct['stop'] state = self.state if self.state in ("paused", "work"): self.set_to_workstate(period=self.time_period) else: self.set_to_breakstate(period=self.time_period) if self.count_start: if self.state == "break": self.message.disabled = False self.message.focus = True if ACTIVE_STYLE == "style3": self.message.background_color = get_color_from_hex( "ffffff") else: self.start_animation() else: self.disable_buttons() if state == "paused": self.pause_but.disabled = True DB.store_delete('last_session') self.db_check() DB.store_sync() def disable_buttons(self): """ play, stop and pause buttons disability attr. handler. """ buttons = [self.play_but, self.stop_but, self.pause_but] for but in buttons: but.disabled = False def start_animation(self): """ starts animation for rotation, if state is on 'work' then start date should be taken to log pomodoro. other then stop date must be reset in each starting animation action. """ if self.state == "work": self.count_start = True self.start = str(datetime.now()).rsplit('.', 1)[0] elif self.state == "paused" and ACTIVE_STYLE in ("style2", "style3"): self.pause_but.disabled = False self.state = "work" self.count_start = True self.disable_buttons() self.stop = "" if self.state == "break": self.pause_but.disabled = True self.pause_but.inactive = True self.play_but.disabled = True self.play_but.inactive = True if not self.server_send: self.send_data() Clock.schedule_once(lambda dt: self.decrease_minutes(), 1) if ACTIVE_STYLE == "style1": self.start_button.disabled = True if ACTIVE_STYLE in ("style2", "style3"): self.play_but.disabled = True self.message.disabled = True self.message.focus = False if ACTIVE_STYLE == "style3": self.message.background = (0.078, 0.090, 0.094, 1) def decrease_minutes(self): """ until the rotation count reaches the end minutes and rotation should be continue. At the end pomodoro stop date must be taken too sounds should also be handled. """ if not self.count_start: pass elif self.time_period < 1: self.rotation = 0 self.reset_time() else: self.clock.stop() self.clock.play() self.rotation += self.perrotation self.time_period -= 1 self.time_display = strftime('%M:%S', gmtime(self.time_period)) self.time_minute, self.time_second = self.time_display.split(':') if not self.server_send: self.send_data() if ACTIVE_STYLE == "style1": if self.animation: self.animation.stop(self) self.animation = Animation(rotation=self.rotation, d=1) self.animation.start(self) if self.state == "work" and self.time_period < 1: self.stop = str(datetime.now()).rsplit('.', 1)[0] Clock.schedule_once(lambda dt: self.decrease_minutes(), 1) def send_data(self, state=""): """ To update server side info, if requested. """ result = True if self.server_url and self.server_user: state_time = {"work": 1500, "break": 300, "stop": 1800} total_time = state_time[state] if state else state_time[self.state] result = REQ_GET(self.server_url, params={"user": self.server_user, "total": total_time, "now": self.time_period, "state": self.state}, timeout=1,) self.server_send = result return result def pause_animation(self): """ To pause the timer. in this change of state if server side visuality wanted, updater should be called. """ self.count_start = False self.clock.stop() self.alarm.stop() state = self.state self.state = 'paused' self.disable_buttons() self.pause_but.disabled = True if self.server_url and self.server_user: while True: res_result = self.send_data(state=state) if res_result.status_code == 200: break self.server_send = False def stop_animation(self): """ To stop the timer. in this change of state if server side visuality wanted, updater should be called. """ self.count_start = False self.clock.stop() self.alarm.stop() if self.server_url and self.server_user: self.state = "stop" while True: res_result = self.send_data() if res_result.status_code == 200: break self.set_to_workstate() self.message.text = "" if ACTIVE_STYLE == "style3": self.message.background_color = (0.078, 0.090, 0.094, 1) self.message.disabled = True self.disable_buttons() self.server_send = False def switch_screen(self, screen, side=None): """ action screens rotation handling """ direction = 'up' if ACTIVE_STYLE in ("style2", "style3") and \ self.sm.current == self.sm.settings_screen.name: direction = 'down' if side: direction = side self.sm.transition = SlideTransition(direction=direction) self.sm.current = screen self.buttons = get_buttons(self, []) def set_clock(self): """ rotation for per second calculation. """ if self.time_period: self.perrotation = Vector(1, 1).angle( Vector(1, 1).rotate(360 / float(self.time_period))) def set_to_workstate(self, period=None): """ the state of pomodoro taken to 'work' time periods and required settings handled. for per work pomodoro counter counts this state until break state triggered """ self.state = "work" self.time_period = WORK_TIME_PERIOD if period == None else period self.time_display = strftime('%M:%S', gmtime(self.time_period)) self.time_minute, self.time_second = self.time_display.split(':') self.sprint_count += 1 self.set_clock() if ACTIVE_STYLE == "style1": self.switch_screen('start') def set_to_breakstate(self, period=None): """ Break pomodoro action handled, the time calculated by the work state counter then it resets """ self.state = "break" self.time_period = ( BREAK_TIME_PERIOD * self.sprint_count) if period == None else period self.sprint_count = 0 self.time_display = strftime('%M:%S', gmtime(self.time_period)) self.time_minute, self.time_second = self.time_display.split(':') self.set_clock() if ACTIVE_STYLE == "style1": self.switch_screen('start') def reset_time(self): """ triggered when the timer reaches the end, animation, clock sound, disability of button switched to other state of each. pomodoro state changes according to the current one. If the current state is 'break' then 'work' state trigger triggered """ if self.animation: self.animation.stop(self) self.rotation = 0 self.clock.stop() self.alarm.play() if ACTIVE_STYLE == "style1": self.start_button.disabled = False elif ACTIVE_STYLE in ("style2", "style3"): self.disable_buttons() if self.state == "work": self.state = "break" self.set_to_breakstate() self.message.disabled = False self.message.focus = True if ACTIVE_STYLE == "style3": self.message.background_color = get_color_from_hex( "ffffff") self.play_but.disabled = True self.pause_but.disabled = True self.play_but.inactive = True self.pause_but.inactive = True elif self.state == "break": self.set_to_workstate() self.message.text = "" if ACTIVE_STYLE == "style1": if self.state == "break": self.switch_screen('start') self.set_to_workstate() else: self.switch_screen('info') def set_volume(self, state): """ Volume operation handled except alarm sound. """ on_off = 0 if state == "down" else 1 if self.clock: self.clock.volume = on_off def keep_info(self, text=None): """ after each pomodoro in 'work' state an information is taken from user and to log that with other necessary informations collection is written in a file. """ if not text: text = self.message.text.strip() if text: if ACTIVE_STYLE == "style3": self.message.background_color = (0.078, 0.090, 0.094, 1) data = dict(date_from=self.start, date_to=self.stop, content=text, is_sent=False) if ACTIVE_STYLE == "style1": self.message.text = "" current_day = self.start.rsplit(" ", 1)[0] try: existing_data = DB.store_get(current_day) except KeyError: existing_data = [] existing_data.append(data) DB.store_put(current_day, existing_data) self.db_check() self.load_log_dates() DB.store_sync() if ACTIVE_STYLE == "style1": self.switch_screen('action') elif ACTIVE_STYLE in ("style2", "style3"): self.start_animation() def update_connect_server(self): self.connect_server = not self.connect_server if self.connect_server: self.switch_screen(server_screen, side='right') def set_server_user(self): pass def set_server_url(self): pass def on_mouse_pos(self, *args): """ hover action handling, for capable buttons. """ mouse_position = args[1] if self.sm.current == 'log_screen': #items = self.sm.children[0].children[0].logs.children[0].children[0].children pass else: buttons = filter(lambda x: x.pos[0] <= mouse_position[0] <= x.ranged[0] and x.pos[1] <= mouse_position[1] <= x.ranged[1], self.buttons) button = None if buttons and 149 > mouse_position[1] > 1 and \ 299 > mouse_position[0] > 1: button = buttons[0] for but in self.buttons: if but != button: but.inactive = True else: but.inactive = False def change_theme(self): theme = DB.store_get("theme") if theme == 'style3': theme = 'style2' self.toggle_source.source = "assets/icons/switches_to_dark.png" else: theme = 'style3' self.toggle_source.source = "assets/icons/switches_to_light.png" DB.store_put('theme', theme) self.db_check() DB.store_sync() Clock.schedule_once(lambda dt: self.restart(), .5) def to_dict(self): """ To store required data from restoring app and loosing all, values packed in a dict. """ return {'state': self.state, 'count_start': self.count_start, 'server_url': self.server_url, 'server_user': self.server_user, 'message_text': self.message.text, 'message_disabled': self.message.disabled, 'time_period': self.time_period, 'start': str(self.start), 'stop': str(self.stop)} def restart(self): """ Restoring current values to specified file on config.py and restart operation of app handled. """ DB.store_put('last_session', self.to_dict()) self.db_check() DB.store_sync() args = sys.argv[:] args.insert(0, sys.executable) os.execv(sys.executable, args) def log_converter(self, row_index, item): data = { 'log': item['content'], 'date': str(item['date_from'].split(" ")[1].strip()), 'index': item['index'] } return data
anim.start(widget) # animaçoes em paralelo, duas animacoes ao mesmo tempo anim = Animation(pos=(80, 10)) anim &= Animation(size=(800, 800), duration=2.) anim.start(widget) # animacao em que se repete ao terminar anim = Animation(x=50) + Animation(size=(80, 80), duration=2.) anim.repeat = True anim.start(widget) Animation.cancel_all(widget, 'x') #cancelar todas animacoes # CARACTERISTICAS anim.start(widget) #inicia uma animacao anim.stop(widget) #para uma animacao anim.cancel(widget) #apara a animacaoe evento on_complete noa sera executado Animation.stop_all(widget, 'x') #para e afeta todas a s animacoes Animation.cancel_all(widget, 'y') #para e afeta todas a s animacoes stop_property(widget, 'x') #para e remove a propriedade da animacao do widget cancel_property(widget, 'y') #para e remove a propriedade da animacao do widget anim.duration = 2.0 # tempo duracao de animacao anim.transition = 'in_quad' # funcao de transicao, pode ser da propeia classe que vc criou anim.step = 1.0 / 30.0 # 30 FPS na animacao anim.have_properties_to_animate #retona true se widget tem uma animacao #EVENTOS anim.on_start #dispara quando a niamacaoe iniciada anim.on_complete #dispara quandoa animacao termina anim.on_progress #dispara euqnato a animcao seta sendo executada
class MetronomeWidget(Widget): def __init__(self, dur, pulsing): Widget.__init__(self) self.dur = dur self.mode = 'Moving Block' self.pulsing = pulsing self.pulse_schedule = None self.text_feedback_label = Label(text='Welcome to Intuitive Metronome (IM)!', font_size=26, bold=True, pos=(350, 300), color=TEXT_COLOR) self.text_feedback = 'Welcome to Intuitive Metronome (IM)!' self.add_widget(self.text_feedback_label) with self.canvas: Color(BLOCK_COLOR[0], BLOCK_COLOR[1], BLOCK_COLOR[2], BLOCK_COLOR[3]) self.block = Rectangle(size_hint=(None, None)) self.accomp_file = None self.accomp_playing = False # Leap Variables self.controller = Leap.Controller() self.data, self.fs = sf.read('click.wav', dtype='float32') self.animation = None self.click_schedule = None self.controller = Leap.Controller() self.state = 'stop' self.is_running = False self.stop_time = None self.my_recording = [] self.gave_recording_feedback = False Clock.schedule_interval(self.update_label, 0.5) Clock.schedule_interval(self.on_update, 1) def update_label(self, *args): self.text_feedback_label.text = self.text_feedback def play_click(self, dt=None): sd.play(self.data, self.fs) def blink_square(self, dt=None): with self.canvas: Color(BLOCK_COLOR[0], BLOCK_COLOR[1], BLOCK_COLOR[2], BLOCK_COLOR[3]) flash = Rectangle(size=(900, 700)) def reset_blink(*args): self.canvas.remove(flash) Clock.schedule_once(reset_blink, self.dur*0.5) def process_leap(self): frame = self.controller.frame() self.hand = frame.hands[0] if self.state == 'start': if self.hand.grab_strength == 1: self.state = 'stop' elif self.state == 'stop': print('Looking for pinch to start: pinch strength is ', self.hand.pinch_strength) self.text_feedback = 'Looking for pinch to start: pinch strength is %s' % round(self.hand.pinch_strength, 2) if self.hand.pinch_strength == 1: self.state = 'start' def on_update(self, *args): if (self.stop_time is None) or (time.time() - self.stop_time > 3): if self.gave_recording_feedback: pass else: self.process_leap() if self.state == 'start' and not self.is_running: print('Metronome has started. It is listening for input tempo.') if self.accomp_file: self.text_feedback = 'Listening for input tempo. May take awhile to load MIDI file.' else: self.text_feedback = 'Metronome has started. It is listening for input tempo.' if not self.gave_recording_feedback: self.gave_recording_feedback = True else: t_avg, bpm = record_process_signal() self.gave_recording_feedback = False #t_avg, bpm = 0.46, 128 if self.accomp_file and not self.accomp_playing: #self.text_feedback = 'Playing MIDI file at your tempo, %s BPM.' % bpm self.py_game = run_midi_player(self.accomp_file, bpm, 'Piano', self.controller) self.accomp_playing = True Animation.cancel_all(self) self.dur = float(round(t_avg, 2)) if self.pulsing: if self.block: self.canvas.remove(self.block) self.block = None self.pulse_schedule = Clock.schedule_interval(self.blink_square, self.dur) else: self.animation = Animation(pos=(700, 0), duration=0.98 * self.dur) \ + Animation(pos=(0, 0), duration=0.98 * self.dur) self.animation.repeat = True if self.block is None: with self.canvas: Color(BLOCK_COLOR[0], BLOCK_COLOR[1], BLOCK_COLOR[2], BLOCK_COLOR[3]) self.block = Rectangle(size_hint=(None, None)) self.animation.start(self.block) self.click_schedule = Clock.schedule_interval(self.play_click, self.dur) self.is_running = True if self.accomp_playing: self.text_feedback = 'Playing MIDI file at your tempo, %s BPM. To stop, make a fist - %s' % (round(bpm), round(self.hand.grab_strength, 2)) else: self.text_feedback = 'Metronome is running. To stop, make a fist! Fist strength: %s' % round(self.hand.grab_strength, 2) print('Metronome is running. To stop, make a fist! Fist strength: %s' % round(self.hand.grab_strength, 2)) elif self.state == 'stop' and self.is_running: print('Metronome has stopped') self.text_feedback = 'Metronome has stopped.' if self.accomp_playing: self.py_game.mixer.music.stop() self.accomp_playing = False self.accomp_file = None if self.pulsing: if self.pulse_schedule: self.pulse_schedule.cancel() else: if self.animation: self.animation.stop(self.block) self.animation.repeat = False if self.click_schedule: self.click_schedule.cancel() self.is_running = False self.stop_time = time.time() else: print('Cycle ended! Short waiting before the next cycle!')
class AnimationTestCase(unittest.TestCase): def sleep(self, t): start = time() while time() < start + t: sleep(.01) Clock.tick() def setUp(self): self.a = Animation(x=100, d=1, t='out_bounce') self.w = Widget() def test_start_animation(self): self.a.start(self.w) self.sleep(1.5) self.assertAlmostEqual(self.w.x, 100) def test_animation_duration_0(self): a = Animation(x=100, d=0) a.start(self.w) self.sleep(.5) def test_stop_animation(self): self.a.start(self.w) self.sleep(.5) self.a.stop(self.w) self.assertNotAlmostEqual(self.w.x, 100) self.assertNotAlmostEqual(self.w.x, 0) def test_stop_all(self): self.a.start(self.w) self.sleep(.5) Animation.stop_all(self.w) def test_stop_all_2(self): self.a.start(self.w) self.sleep(.5) Animation.stop_all(self.w, 'x') def test_duration(self): self.assertEqual(self.a.duration, 1) def test_transition(self): self.assertEqual(self.a.transition, AnimationTransition.out_bounce) def test_animated_properties(self): self.assertEqual(self.a.animated_properties['x'], 100) def test_animated_instruction(self): instruction = Scale(3) self.a.start(instruction) self.assertEqual(self.a.animated_properties['x'], 100) self.assertAlmostEqual(instruction.x, 3) self.sleep(1.5) self.assertAlmostEqual(instruction.x, 100) def test_weakref(self): widget = Widget() anim = Animation(x=100) anim.start(widget.proxy_ref) del widget try: self.sleep(1.) except ReferenceError: pass
class ButtonBase(Button): """Basic button widget.""" warn = BooleanProperty(False) target_background = ListProperty() target_text = ListProperty() background_animation = ObjectProperty() text_animation = ObjectProperty() last_disabled = False menu = BooleanProperty(False) toggle = BooleanProperty(False) button_update = BooleanProperty() def __init__(self, **kwargs): self.background_animation = Animation() self.text_animation = Animation() app = App.get_running_app() self.background_color = app.theme.button_up self.target_background = self.background_color self.color = app.theme.button_text self.target_text = self.color super(ButtonBase, self).__init__(**kwargs) def on_button_update(self, *_): Clock.schedule_once(self.set_color_instant) def set_color_instant(self, *_): self.set_color(instant=True) def set_color(self, instant=False): app = App.get_running_app() if self.disabled: self.set_text(app.theme.button_disabled_text, instant=instant) self.set_background(app.theme.button_disabled, instant=instant) else: self.set_text(app.theme.button_text, instant=instant) if self.menu: if self.state == 'down': self.set_background(app.theme.button_menu_down, instant=True) else: self.set_background(app.theme.button_menu_up, instant=instant) elif self.toggle: if self.state == 'down': self.set_background(app.theme.button_toggle_true, instant=instant) else: self.set_background(app.theme.button_toggle_false, instant=instant) elif self.warn: if self.state == 'down': self.set_background(app.theme.button_warn_down, instant=True) else: self.set_background(app.theme.button_warn_up, instant=instant) else: if self.state == 'down': self.set_background(app.theme.button_down, instant=True) else: self.set_background(app.theme.button_up, instant=instant) def on_disabled(self, *_): self.set_color() def on_menu(self, *_): self.set_color(instant=True) def on_toggle(self, *_): self.set_color(instant=True) def on_warn(self, *_): self.set_color(instant=True) def on_state(self, *_): self.set_color() def set_background(self, color, instant=False): if self.target_background == color: return app = App.get_running_app() self.background_animation.stop(self) if app.animations and not instant: self.background_animation = Animation(background_color=color, duration=app.animation_length) self.background_animation.start(self) else: self.background_color = color self.target_background = color def set_text(self, color, instant=False): if self.target_text == color: return app = App.get_running_app() self.text_animation.stop(self) if app.animations and not instant: self.text_animation = Animation(color=color, duration=app.animation_length) self.text_animation.start(self) else: self.color = color self.target_text = color
class FloatingActionButton(ThemeBehaviour, CircularRippleBehavior, ElevationBehaviour, ButtonBehavior, AnchorLayout): _bg_color_down = ListProperty([]) def _get_bg_color_down(self): return self._bg_color_down def _set_bg_color_down(self, color, alpha=None): if len(color) == 2: self._bg_color_down = get_rgba_color(color, control_alpha=alpha) elif len(color) == 4: self._bg_color_down = color background_color_down = AliasProperty(_get_bg_color_down, _set_bg_color_down, bind=('_bg_color_down', )) _bg_color_disabled = ListProperty([]) def _get_bg_color_disabled(self): return self._bg_color_disabled def _set_bg_color_disabled(self, color, alpha=None): if len(color) == 2: self._bg_color_disabled = get_rgba_color(color, control_alpha=alpha) elif len(color) == 4: self._bg_color_disabled = color background_color_disabled = AliasProperty(_get_bg_color_disabled, _set_bg_color_disabled, bind=('_bg_color_disabled', )) theme_style = OptionProperty(None, options=['Light', 'Dark', 'Custom'], allownone=True) icon_color = ListProperty(None, allownone=True) elevation_normal = BoundedNumericProperty(2, min=0, max=12) elevation_raised = BoundedNumericProperty(0, min=0, max=12) icon = StringProperty('md-android') def __init__(self, **kwargs): self.elevation = self.elevation_normal if self.elevation_raised == 0 and self.elevation_normal + 6 <= 12: self.elevation_raised = self.elevation_normal + 6 elif self.elevation_raised == 0: self.elevation_raised = 12 super(FloatingActionButton, self).__init__(**kwargs) self.background_color = self._theme_cls.primary_color self.background_color_down = self._theme_cls.primary_dark self.background_color_disabled = self._theme_cls.disabled_bg_color() self.elevation_press_anim = Animation(elevation=self.elevation_raised, duration=.2, t='out_quad') self.elevation_release_anim = Animation(elevation=self.elevation_normal, duration=.2, t='out_quad') def _set_ellipse(self, instance, value): ellipse = self.ellipse ripple_rad = self.ripple_rad ellipse.size = (ripple_rad, ripple_rad) ellipse.pos = (self.center_x - ripple_rad / 2., self.center_y - ripple_rad / 2.) def on_disabled(self, instance, value): super(FloatingActionButton, self).on_disabled(instance, value) if self.disabled: self.elevation = 0 else: self.elevation = self.elevation_normal def on_touch_down(self, touch): if not self.disabled: if touch.is_mouse_scrolling: return False if not self.collide_point(touch.x, touch.y): return False if self in touch.ud: return False self.elevation_press_anim.stop(self) self.elevation_press_anim.start(self) return super(FloatingActionButton, self).on_touch_down(touch) def on_touch_up(self, touch): if not self.disabled: if touch.grab_current is not self: return super(ButtonBehavior, self).on_touch_up(touch) self.elevation_release_anim.stop(self) self.elevation_release_anim.start(self) return super(FloatingActionButton, self).on_touch_up(touch) def on_elevation_normal(self, instance, value): self.elevation = value def on_elevation_raised(self, instance, value): if self.elevation_raised == 0 and self.elevation_normal + 6 <= 12: self.elevation_raised = self.elevation_normal + 6 elif self.elevation_raised == 0: self.elevation_raised = 12
class Ball(TickingWidget): """The ball that the solver moves through the maze. The ball has two collision-detection methods: the standard collide_point() for the area where the ball accepts touches, and collide_zoc() for the “zone of control”. The zone of coltrol grows when the ball is touched, and shrinks when the touch is released. When touched, the ball will move towards the touch at BALL_SPEED tiles per second (but maze walls will block it). """ def __init__(self, parent, **kwargs): super(Ball, self).__init__(**kwargs) self.touch_uid = None self.target_pos = self.pos self.animation = None radius = self.radius = parent.cell_size * 0.3 self.handle_radius = parent.cell_size * BALL_TOUCH_RADIUS self.zoc_radius = self.radius with self.canvas: Color(0, 0, 1, 0.5) Ellipse(pos=(-radius, -radius), size=(radius * 2, radius * 2)) Color(0, 0, 0, 1) HollowCircle((0, 0), radius, 18) Color(0.5, 0.5, 0.5, 0.4) FilledCircle((0, 0), self.handle_radius) HollowCircle((0, 0), self.handle_radius, 18) self.scale_instruction = Scale(self.zoc_radius) Color(1, 1, 1, 0.2) FilledCircle((0, 0), 1) Color(0.5, 0.5, 0.5, 0.4) HollowCircle((0, 0), 1, 32) with self.canvas.before: PushMatrix() self.translation_instruction = Translate(0, 0, 0) with self.canvas.after: PopMatrix() def collide_point(self, x, y): px, py = self.pos return (px - x)**2 + (py - y)**2 < self.handle_radius**2 def collide_zoc(self, x, y): px, py = self.pos return (px - x)**2 + (py - y)**2 < self.zoc_radius**2 def tick(self, dt): if not self.parent: return # Try to cover the required distance. But if it's not done in 50 # iterations, give up. remaining_distance = dt * BALL_SPEED * self.parent.cell_size for i in range(50): if remaining_distance <= 0.01 or self.pos == self.target_pos: break distance_covered = self.move_step(remaining_distance) if distance_covered == 0: break remaining_distance -= distance_covered if self.translation_instruction.xy != self.pos: # Update the canvas if the ball moved self.translation_instruction.xy = self.pos self.canvas.ask_update() if self.scale_instruction.scale != self.zoc_radius: # Update the canvas if the ZOC was resized self.scale_instruction.scale = self.zoc_radius self.canvas.ask_update() if not self.parent.ball_source.collide_point(*self.pos): # If the ball is outside the initial area, add time to the player's # clock self.parent.add_time(dt) if self.x < self.parent.cell_size: # IF the ball is in the goal area, the round ends self.parent.win() def move_step(self, remaining_distance): """Move a little towards the touch position, return distance covered `remaining_distance` is the maximum distance to move This implements one iteration of the moving, and is called enough times for the sum of the returned values is big enough. Both remaining_distance and the return value are in pixels, not tiles. """ radius = self.radius pos = numpy.array(self.pos) # The distance we want to cover delta = self.target_pos - pos distance = numpy.sqrt(sum(delta**2)) # Only move a little bit each time, so walls are checked correctly max_distance = min(remaining_distance, radius / 2) if distance > max_distance: delta = delta / distance * max_distance distance = max_distance pos += delta # From now, we will deal with tile coordinates instead of pixels tile_coord = numpy.array(self.parent.pixel_to_tile(pos)) tile_radius = self.parent.pixel_to_tile((radius, radius)) # Check the upper/lower & left/right points of the circle # if one of them is in a wall, "snap" the ball to that wall for axis in (0, 1): pt = [0, 0] pt[axis] = tile_radius[axis] if self.parent.wall_at_tile(tile_coord + pt): tile_coord[axis] = int(tile_coord[axis]) + 1 - pt[axis] if self.parent.wall_at_tile(tile_coord - pt): tile_coord[axis] = int(tile_coord[axis]) + pt[axis] # Get the closest grid intersection corner = numpy.array(tile_coord).round() # Get a point in the tile "behind" this clocest corner tile_behind_corner = 2 * corner - tile_coord # Check if there is a wall on that tile if self.parent.wall_at_tile(tile_behind_corner): vector_to_corner = corner - tile_coord # If part of the ball is inside a corner wall, push it back # XXX: This doesn't take into account that the ball can be slightly # elliptical in tile coordinates. (This isn't likely to matter, # though.) avg_radius = sum(tile_radius) / 2 if sum(vector_to_corner**2) < avg_radius**2: distance_to_corner = numpy.sqrt(sum(vector_to_corner**2)) direction_to_corner = vector_to_corner / distance_to_corner pushback_distance = avg_radius - distance_to_corner tile_coord -= direction_to_corner * pushback_distance pushed_back = True # Convert back to pixel coordinates self.pos = self.parent.tile_to_pixel(tile_coord) return distance def on_touch_down(self, touch, force=False): """Called for a new touch `force`: don't check that the touch is near the ball; assume it is. """ if force or self.collide_point(touch.x, touch.y): if self.touch_uid: self.touch_uid = None self.touch_uid = touch.uid self.on_touch_move(touch) zoc_radius = self.parent.cell_size * BALL_ZOC_RADIUS if self.animation: self.animation.stop(self) self.animation = Animation(zoc_radius=zoc_radius, duration=0.5) self.animation.start(self) return True def on_touch_move(self, touch): if touch.uid == self.touch_uid: self.target_pos = touch.pos return True def on_touch_up(self, touch): if touch.uid == self.touch_uid: self.touch_uid = None self.target_pos = self.pos if self.animation: self.animation.stop(self) self.animation = Animation(zoc_radius=self.handle_radius, t='in_cubic', duration=1) self.animation.start(self) return True
def update(self, dt): """ 게임업데이트 """ if not self.is_start: self.txupdate(dt) # tip label if self.tip_label.opacity <= 1.0: self.tip_label.opacity += 0.005 return self.txupdate(dt) self.is_ycoll = False # Character's Move for brick in self.bricks: # meet Block if self.character.collide_widget(brick): z = Vector(self.character.center) - Vector(brick.center) if -0.70 < z.normalize()[0] < 0.70: # down if self.character_y_collision(brick): self.is_jumping = False self.is_ycoll = True else: self.character_x_collision(brick) self.character.move() # y 충돌이면 떨어지지 않는다. // 표면보다 같거나 낮으면 떨어지지 않는다. if not self.is_ycoll and not self.character.y <= self.y: self.character.y -= 5.0 # floor if self.character.y <= self.y and self.is_jumping: self.is_jumping = False self.character.y = self.y # next stage if self.character.center_x > self.width or self.character.center_x < 0: self.clear_stage() # monster for monster in self.monsters: if self.character.collide_widget(monster): if monster.status == 1: # Food anim = Animation( size=[self.character.player_image.width * 1.2, self.character.player_image.height * 1.2], duration=.2) if anim: anim.stop(self.character.player_image) anim.start(self.character.player_image) # remove monster self.remove_widget(monster) self.monsters.remove(monster) self.score += 1 elif monster.status == 2: # Enemy monster.source_dir = "data/mouse_dead.png" monster.status = 3 left = Animation( center_x=monster.center_x + 10, duration=.2) right = Animation(center_x=monster.center_x, duration=.2) anim = left + right if anim: anim.stop(self) anim.start(monster) self.score += 2 elif monster.status == 9: # Boss monster.source_dir = "data/spider_dead.png" monster.status = 3 self.score += 3 elif monster.status == 10: # Girl self.character.velocity = [0, 0] self.character.y += 6 monster.y += 1 if self.center[1] < self.character.y: self.clear_stage() # Letter elif monster.status in [12, 13, 14, 15, 16, 17, 18, 19, 20, 21]: left = Animation( center_y=monster.center_y + 400, duration=.2) right = Animation(center_y=monster.center_y, duration=.2) anim = left + right if anim: anim.stop(self) anim.start(monster) monster.status = 3 self.score += 1 # Hurt elif monster.status in [30, 31, 32, 33, 34]: left = Animation( center_y=monster.center_y + 400, duration=.2) right = Animation(center_y=monster.center_y, duration=.2) anim = left + right if anim: anim.stop(self) anim.start(monster) monster.source_dir = monster.source_dir.replace( "_hurt", "") monster.status = 3 self.score += 2 # 3. MESSAGE elif monster.status == 3: pass # 4. JUMP - SONIC elif monster.status == 4: self.character_jump(2) # jump time x 2 # 5. Gold Ring - SONIC elif monster.status == 5: self.remove_widget(monster) self.monsters.remove(monster) self.score += 1 # 6. speed up elif monster.status == 23: self.remove_widget(monster) self.monsters.remove(monster) self.score += 1 self.character.velocity = [ self.character.velocity[0] * 2, 0] # 6,7. FLAGS - MARIO elif monster.status == 6: self.character.velocity = [0, 0] self.score += 1 monster.status = 3 elif monster.status == 22: self.character.velocity = [ self.block_size / self.speed_ratio, 0] # 8. Gold coin - MARIO elif monster.status in [8, 24]: self.remove_widget(monster) self.monsters.remove(monster) self.score += 1 self.is_jumping = False # trap for trap in self.traps: if self.character.collide_widget(trap): self.failed_stage()
class AlarmScreen(Screen): r = NumericProperty(100) # Radio del círculo ra = NumericProperty(1.0) # Alfa del círculo r2 = NumericProperty(100) # Radio del círculo que marca pulsación r2a = NumericProperty(0) # Alfa del círculo que marca pulsación cd = NumericProperty(0.0) # Offset de los círculos que se desplazan ca = NumericProperty(1.0) # Alfa de los círculos que se desplazan R = 0.08 # Constante para calcular el tamaño motion_uid = None text = StringProperty('Alarma!') timetext = StringProperty('') inicio = datetime.now() # Se actualiza en sonar_alarma def on_height(self, widget, height): self.r = height * self.R self.r2 = height * self.R def on_enter(self, *args): Logger.debug("%s: AlarmScreen.on_enter self.width=%s %s" % (APP, self.width, datetime.now())) if self.width < 100: width = 480 # No debería hacer falta, pero la primera vez falla else: width = self.width self.r2a = self.ca = self.cd = 0 self.anim = Animation(cd=width / 2, ca=1, d=1, t='in_quad') \ + Animation(cd=0, ca=0, d=0) self.anim.repeat = True self.anim.start(self) self.app.reproducir_sonido_alarma() self.app.clock_callback = partial(self.app.cancelar_alarma, source='Clock', clock_date=datetime.now()) Clock.schedule_once(self.app.clock_callback, self.app.ACS) # segundos self.update_timetext() Clock.schedule_interval(self.update_timetext, 1) def on_pre_leave(self, *args): self.anim.stop(self) Logger.debug("%s: AlarmScreen.on_pre_leave %s" % (APP, datetime.now())) if platform == 'android': self.app.reset_window_flags() # Permitir apagado automático Clock.unschedule(self.update_timetext) def on_touch_down(self, touch): if Vector(touch.pos).distance(Vector(self.center_x, self.height * 0.2)) < 2 * self.r: self.motion_uid = touch.uid self.r2a = .2 self.r2 = self.height * self.R anim = Animation(r2=self.height * self.R * 2.3, t='in_quad', d=0.3) anim.start(self) def on_touch_move(self, touch): if touch.uid == self.motion_uid: # Cancelar si de desplaza más de la mitad de la distancia al borde self.ra = 1 - 3.3 * Vector(touch.pos).distance( Vector(self.center_x, self.height * 0.2)) / self.width self.r2a = 1 - self.ra * 0.8 if self.ra < 0.05: self.ra = 1 self.app.cancelar_alarma(source='user') def on_touch_up(self, touch): if touch.uid == self.motion_uid: self.ra = 1 self.r2a = 0 def update_timetext(self, *args): self.timetext = "%s - %s" % (self.inicio.strftime("%H:%M"), tdformat(self.inicio - datetime.now()))
class BaseRaisedButton(CommonElevationBehavior, BaseButton): ''' Abstract base class for raised buttons which elevate from material. Raised buttons are to be used sparingly to emphasise primary/important actions. Implements elevation behavior as well as the recommended down/disabled colors for raised buttons. ''' def __init__(self, **kwargs): if self.elevation_raised == 0 and self.elevation_normal + 6 <= 12: self.elevation_raised = self.elevation_normal + 6 elif self.elevation_raised == 0: self.elevation_raised = 12 super(BaseRaisedButton, self).__init__(**kwargs) self.elevation_press_anim = Animation(elevation=self.elevation_raised, duration=.2, t='out_quad') self.elevation_release_anim = Animation( elevation=self.elevation_normal, duration=.2, t='out_quad') _elev_norm = NumericProperty(2) def _get_elev_norm(self): return self._elev_norm def _set_elev_norm(self, value): self._elev_norm = value if value <= 12 else 12 self._elev_raised = (value + 6) if value + 6 <= 12 else 12 self.elevation = self._elev_norm self.elevation_release_anim = Animation(elevation=value, duration=.2, t='out_quad') elevation_normal = AliasProperty(_get_elev_norm, _set_elev_norm, bind=('_elev_norm', )) _elev_raised = NumericProperty(8) def _get_elev_raised(self): return self._elev_raised def _set_elev_raised(self, value): self._elev_raised = value if value + self._elev_norm <= 12 else 12 self.elevation_press_anim = Animation(elevation=value, duration=.2, t='out_quad') elevation_raised = AliasProperty(_get_elev_raised, _set_elev_raised, bind=('_elev_raised', )) def on_disabled(self, instance, value): if self.disabled: self.elevation = 0 else: self.elevation = self.elevation_normal super(BaseRaisedButton, self).on_disabled(instance, value) def on_touch_down(self, touch): if not self.disabled: if touch.is_mouse_scrolling: return False if not self.collide_point(touch.x, touch.y): return False if self in touch.ud: return False self.elevation_press_anim.stop(self) self.elevation_press_anim.start(self) return super(BaseRaisedButton, self).on_touch_down(touch) def on_touch_up(self, touch): if not self.disabled: if touch.grab_current is not self: return super(ButtonBehavior, self).on_touch_up(touch) self.elevation_release_anim.stop(self) self.elevation_release_anim.start(self) return super(BaseRaisedButton, self).on_touch_up(touch) def _get_md_bg_color_down(self): t = self.theme_cls c = self.md_bg_color # Default to no change on touch # Material design specifies using darker hue when on Dark theme if t.theme_style == 'Dark': if self.md_bg_color == t.primary_color: c = t.primary_dark elif self.md_bg_color == t.accent_color: c = t.accent_dark return c def _get_md_bg_color_disabled(self): if self.theme_cls.theme_style == 'Dark': c = (1., 1., 1., 0.12) else: c = (0., 0., 0., 0.12) return c
class Picture(Scatter): '''Picture is the class that will show the image with a white border and a shadow. They are nothing here because almost everything is inside the picture.kv. Check the rule named <Picture> inside the file, and you'll see how the Picture() is really constructed and used. The source property will be the filename to show. ''' source = StringProperty(None) image_type = StringProperty(None) selected = BooleanProperty(None) full = BooleanProperty(None) def __init__(self,post,sourcepos,**kw): self.downloader=None self.post= post self.download('preview') Scatter.__init__(self,**kw) self.back_animation=Animation(pos=sourcepos,scale=1.2, rotation=0.0) self.center_animation=Animation(pos=(0,0),scale=10.0, rotation=0.0) self.next_animation=Animation(pos=(2000,sourcepos[1]),scale=0.1) self.prev_animation=Animation(pos=(-1000,sourcepos[1]),scale=0.1) self.touches=set() def do_unfocus(self): self.back_animation.start(self) def on_scale(self,instance,value): if value <= 2.0: self.download('preview') elif value <=10.0: self.download('sample') else: self.download('full') def on_touch_down(self, touch): if not self.collide_point(touch.x, touch.y): return self.touches.add(touch.uid) if not self.selected: self.selected= True if touch.is_double_tap: self.full=True self.center_animation.start(self) return True self.back_animation.stop(self) return Scatter.on_touch_down(self, touch) def on_touch_up(self, touch): try: self.touches.remove(touch.uid) if not self.touches and not self.selected: self.back_animation.start(self) except: pass return Scatter.on_touch_up(self, touch) def on_selected(self,instance,value): if not value and not self.touches: self.back_animation.start(self) def download(self, image_type, path=None, pre=None): post= self.post if self.image_type == image_type: return self.image_type=image_type if pre == None: pre= '' if path == None: path= "" else: if not os.path.exists(path): os.mkdir(path) dir= os.path.join(path, post.domain) if not os.path.exists(dir): os.mkdir(dir) dir= os.path.join(dir, image_type) if not os.path.exists(dir): os.mkdir(dir) dest = os.path.join(dir, post.name(image_type)) if os.path.isfile(dest): if image_type != 'full': self.source = dest return if self.downloader is not None: pass if self.post.local_files[image_type]: Logger.warn( "Caching found of %d."%(self.post.index,)) self.source = dest return with open(dest, 'rb') as f: bin = f.read() if post.md5 == hashlib.md5(bin).hexdigest(): Logger.warn( "md5 correct found of %d: %s."%(self.post.index,dest)) self.post.local_files[image_type]=True self.source = dest return if self.downloader is not None: pass url = post.get_url(image_type) if not url: Logger.warn('invalid') return Logger.warn('downloading ') self.downloader= Downloader(url,dest,self.download_complete, self.download_progress) self.downloader.start() def download_progress(self, count, blockSize, totalSize,*k): progress = int(count * blockSize * 100 / totalSize) progress = min(100, progress) if count * blockSize == totalSize: Logger.warn("Total ok!") Logger.warn( '%s%%' % (progress,)) def download_complete(self,dl,*k): self.source = dl.dest self.downloader=None