def _add_expanded_image (self, wid): for child in self._expansion_cont.children: if type (child) == FloatLayout: self._expansion_cont.remove_widget (child) self.pre_cont = BoxLayout (size_hint = (1, 1), padding=10, orientation='horizontal') self.pre_cont.canvas.add (Color (0,0,0,0.75)) self.pre_cont.canvas.add (Rectangle (size=self._expansion_cont.size)) _handler = Scatter () _image = Image (source = self._img.source, keep_ratio =False, allow_stretch=True) _btn = Button (text='Hide', size_hint = (0.05, 1)) def _center (wid, val): img_width = val[0]*0.75 img_height = img_width / _image.image_ratio _image.size = (img_width, img_height) _image.pos = ((val[0] - img_width)/2, (val[1] - img_height) / 2) _handler.bind (size=_center) _btn.bind (on_release =self._rmv_expanded_image) _handler.add_widget (_image) self.pre_cont.add_widget (_btn) self.pre_cont.add_widget (_handler) self._expansion_cont.add_widget (self.pre_cont)
def create_controller(): #create controller for swiping and pinching in the center # few parameters for scaling # this defines the active area of dragging/zooming normal_scale = 4.5 # amount of pinch for zoom in and out zoom_in_scale = 5.5 zoom_out_scale = 3.5 # distance to drag in x and y direction for action x_sensitive= Window.center[0]/3 y_sensitive = Window.center[1]/3 # Controller(scatter object) that can be scaled and translate controller = Scatter(size_hint = (None,None), do_rotation=False, do_translation=True, do_scale=True, scale = normal_scale, scale_min= zoom_out_scale, scale_max=zoom_in_scale, center = Window.center) # a reference object to show the active area #control_object = Image() #controller.add_widget(control_object) #create microscope control parts microscope_control = microscope_controller() mc.step = 100 # this provides actions when moving and zooming the scatter objects def control_feedback(arg1, arg2): 'the callback functions for controller scatter object' if controller.center[0] - Window.center[0] > x_sensitive: microscope_control.drag_right() elif controller.center[0] - Window.center[0] < -1* x_sensitive: microscope_control.drag_left() elif controller.center[1] - Window.center[1] > y_sensitive: microscope_control.drag_top() elif controller.center[1] - Window.center[1] < -1*y_sensitive: microscope_control.drag_bot() elif controller.scale < zoom_out_scale*1.1: microscope_control.pinch_out() elif controller.scale > zoom_in_scale*0.9: microscope_control.pinch_in() 'after taking actions, reset scatter location and scale' controller.center = Window.center controller.scale = normal_scale # bind a function to controller when release touch controller.bind(on_touch_up = control_feedback) return controller
def create_map_controller(self): """ A scatter object to manipulate motor movement""" map_controller = Scatter( size_hint = (1,1), do_rotation=False, do_translation=True, do_scale=True,auto_bring_to_front = False) map_controller.center = Window.center # automatically determine the size based on screen size default_scale = Window.height / map_controller.height*1 # determine the size of active area map_controller.scale = default_scale # determine the amount of zoom and drag needed before moving map_controller.scale_max = default_scale*1.2 map_controller.scale_min = default_scale*0.8 # dummy object showing active area if debug_mode is True: map_controller.add_widget(Image()) def map_control_feedback(instance, value): """the callback functions for map_controller scatter object""" if debug_mode is True: if map_controller.center[0] - Window.center[0] > self.drag_x_sensitive: print('moving x+') elif map_controller.center[0] - Window.center[0] < -1* self.drag_x_sensitive: print('moving x-') elif map_controller.center[1] - Window.center[1] > self.drag_y_sensitive: print('moving y+') elif map_controller.center[1] - Window.center[1] < -1* self.drag_y_sensitive: print('moving y-') elif map_controller.scale < default_scale*0.9: print('pinch -') elif map_controller.scale > default_scale*1.1: print('pinch +') elif debug_mode is False: if map_controller.center[0] - Window.center[0] > self.drag_x_sensitive: mc.stage_library('move_x', '-') elif map_controller.center[0] - Window.center[0] < -1* self.drag_x_sensitive: mc.stage_library('move_x', '+') elif map_controller.center[1] - Window.center[1] > self.drag_y_sensitive: mc.stage_library('move_y', '-') elif map_controller.center[1] - Window.center[1] < -1*self.drag_y_sensitive: mc.stage_library('move_y', '+') elif map_controller.scale < default_scale*0.9: mc.camera_library('zoom_out') elif map_controller.scale > default_scale*1.1: mc.camera_library('zoom_in') #after taking actions, reset scatter location and scale to default map_controller.center = Window.center map_controller.scale = default_scale map_controller.bind(on_touch_up = map_control_feedback) return map_controller
class ViewerScreen(Screen): def drawing_toggle(self, *args): if self.btns[1] == 'ic_action_view_image.png': self.btns[1] = 'ic_action_view_drawing.png' self.both.remove_widget(self.overlay) elif self.btns[1] == 'ic_action_view_drawing.png': self.btns[1] = 'ic_action_view_image.png' self.both.add_widget(self.overlay) app.update_buttons() def set_image(self, img_fn): self.image.source = img_fn app.paint_screen.image.source = img_fn try: filesystem.listdir(img_fn + '.overlays') except: app.paint_screen.painter.color = (0, 0, 0, 0) self.btns[1] = None self.both.remove_widget(self.overlay) else: self.btns[1] = 'ic_action_view_image.png' self.overlay.source = filesystem.join( img_fn + '.overlays', filesystem.listdir(img_fn + '.overlays')[-1]) app.paint_screen.painter.color = (1, 1, 1, 1) app.paint_screen.painter.source = self.overlay.source try: self.both.add_widget(self.overlay) except WidgetException: pass # The overlay widget may already be added, in which case I don't care. self.both.scale = 1 self.both.rotation = 0 self.both.pos = [0, 0] def __init__(self, *args, **kwargs): super(ViewerScreen, self).__init__(*args, **kwargs) self.layout = FloatLayout() self.add_widget(self.layout) self.image = ImageCanvas(pos_hint={'center_x': 0.5, 'center_y': 0.5}) self.overlay = AsyncImage(pos_hint={'center_x': 0.5, 'center_y': 0.5}) self.both = Scatter(size_hint=[1, 1], do_rotation=False) self.both.add_widget(self.image) self.layout.add_widget(self.both) self.both.bind(size=self.fix_size) def fix_size(self, *args): self.image.size = self.both.size self.overlay.size = self.both.size
def show_gems(self, dt=None, dummy=None): self.gem_scatters = [] filler = (8 - len(self.weapons)) / 2.0 for i in range(len(self.weapons)): gem = self.gems[self.weapons[i]] scatter = Scatter(do_rotation=False, do_scale=False, color=(0,0,0,0), size_hint=(0.1,0.1)) scatter.add_widget(gem) gem.bind(on_touch_down=self.flash_hero) scatter.bind(on_touch_up=self.drop_gem) scatter.pos = ((filler + i) * X_BLOCK, 0) scatter.scale = 1 try: self.graphics_widget.add_widget(scatter) self.gem_scatters.append(scatter) except: pass
def create_map_controller(): map_controller = Scatter( size_hint = (1,1), do_rotation=False, do_translation=True, do_scale=True, scale = 1, center = Window.center) # automatically determine the size based on screen size default_scale = Window.height / map_controller.height*0.75 # determine the size of active area map_controller.scale = default_scale # determine the amount of zoom and drag needed before moving map_controller.scale_max = default_scale*1.2 map_controller.scale_min = default_scale*0.8 x_sensitive = Window.width*0.3 y_sensitive = Window.height*0.3 # dummy object showing active area map_controller.add_widget(Image()) def map_control_feedback(instance, value): """the callback functions for map_controller scatter object""" if map_controller.center[0] - Window.center[0] > x_sensitive: #microscope_control.drag_right() print('moving x+') elif map_controller.center[0] - Window.center[0] < -1* x_sensitive: #microscope_control.drag_left() pass elif map_controller.center[1] - Window.center[1] > y_sensitive: #microscope_control.drag_top() print('moving y+') elif map_controller.center[1] - Window.center[1] < -1*y_sensitive: #microscope_control.drag_bot() pass elif map_controller.scale < default_scale*0.9: #microscope_control.pinch_out() print('pinch') elif map_controller.scale > default_scale*1.1: #microscope_control.pinch_in() pass #after taking actions, reset scatter location and scale to default map_controller.center = Window.center map_controller.scale = default_scale map_controller.bind(on_touch_up = map_control_feedback) return map_controller
class ViewerScreen(Screen): def drawing_toggle(self, *args): if self.btns[1] == 'ic_action_view_image.png': self.btns[1] = 'ic_action_view_drawing.png' self.both.remove_widget(self.overlay) elif self.btns[1] == 'ic_action_view_drawing.png': self.btns[1] = 'ic_action_view_image.png' self.both.add_widget(self.overlay) app.update_buttons() def set_image(self, img_fn): self.image.source = img_fn app.paint_screen.image.source = img_fn try: filesystem.listdir(img_fn+'.overlays') except: app.paint_screen.painter.color = (0,0,0,0) self.btns[1] = None self.both.remove_widget(self.overlay) else: self.btns[1] = 'ic_action_view_image.png' self.overlay.source = filesystem.join(img_fn+'.overlays', filesystem.listdir(img_fn+'.overlays')[-1]) app.paint_screen.painter.color = (1,1,1,1) app.paint_screen.painter.source = self.overlay.source try: self.both.add_widget(self.overlay) except WidgetException: pass # The overlay widget may already be added, in which case I don't care. self.both.scale = 1 self.both.rotation = 0 self.both.pos = [0,0] def __init__(self, *args, **kwargs): super(ViewerScreen, self).__init__(*args, **kwargs) self.layout = FloatLayout() self.add_widget(self.layout) self.image = ImageCanvas(pos_hint={'center_x': 0.5, 'center_y': 0.5}) self.overlay = AsyncImage(pos_hint={'center_x': 0.5, 'center_y': 0.5}) self.both = Scatter(size_hint=[1,1], do_rotation = False) self.both.add_widget(self.image) self.layout.add_widget(self.both) self.both.bind(size=self.fix_size) def fix_size(self, *args): self.image.size = self.both.size self.overlay.size = self.both.size
def show_popup(self): if self.popup is not None: self.popup.dismiss() desc = FrescoDescription(item=self.item) count = 0 content = self.item.get('content', '') if content: label = Label(text=content) label.bind(size=update_size) desc.layout.add_widget(label) count += 1 media = self.item.get('media', '') if media: ext = media.rsplit('.', 1)[-1].lower() media = join(dirname(__file__), 'data', media) mediawidget = None if ext in ('mp3', 'wav', 'ogg'): mediawidget = FrescoAudio(source=media) elif ext in ('jpg', 'png', 'jpeg', 'gif', 'bmp', 'tga'): mediawidget = Image(source=media) else: pass if mediawidget: count += 1 scatter = Scatter(do_translation=False, do_rotation=False, min_scale=.4, max_scale=2) scatter.add_widget(mediawidget) scatter.bind(size=mediawidget.setter('size')) desc.layout.add_widget(mediawidget) desc.media = mediawidget desc.layout.cols = max(1, count) desc.layout.height = 500 self.popup = popup = Popup( title=self.item['title'], content=desc, size_hint=(.7, .7)) self.popup.bind(on_dismiss=self.stop_media) popup.open()
def build(self): b = BoxLayout(orientation='vertical') t = TextInput(font_size=150, size_hint_y=None, height=200, text='default') f = FloatLayout() s = Scatter() l = Label(text='default', font_size=150) t.bind(text=l.setter('text')) s.bind(pos=self.callback_pos) f.add_widget(s) s.add_widget(l) b.add_widget(t) b.add_widget(f) return b
def create_map_controller(): map_controller = Scatter( size_hint = (1,1), do_rotation=False, do_translation=True, do_scale=True) map_controller.center = Window.center # automatically determine the size based on screen size default_scale = Window.height / map_controller.height*1 # determine the size of active area map_controller.scale = default_scale # determine the amount of zoom and drag needed before moving map_controller.scale_max = default_scale*1.1 map_controller.scale_min = default_scale*0.9 x_sensitive = Window.width*0.1 y_sensitive = Window.height*0.2 # dummy object showing active area #map_controller.add_widget(Image()) def map_control_feedback(instance, value): """the callback functions for map_controller scatter object""" if map_controller.center[0] - Window.center[0] > x_sensitive: mc.stage_library('move_x', '-') elif map_controller.center[0] - Window.center[0] < -1* x_sensitive: mc.stage_library('move_x', '+') elif map_controller.center[1] - Window.center[1] > y_sensitive: mc.stage_library('move_y', '-') elif map_controller.center[1] - Window.center[1] < -1*y_sensitive: mc.stage_library('move_y', '+') elif map_controller.scale < default_scale*0.9: mc.camera_library('zoom_out') elif map_controller.scale > default_scale*1.1: mc.camera_library('zoom_in') #after taking actions, reset scatter location and scale to default map_controller.center = Window.center map_controller.scale = default_scale map_controller.bind(on_touch_up = map_control_feedback) return map_controller
class Point(): def __init__(self, pos): self.scatter = Scatter(size_hint=(0.05,0.05), do_rotation=False, do_scale=False) self.widget = MyPaintWidget() self.widget.setClass('unclassified') self.widget.setActive() self.scatter.x = pos[0] self.scatter.y = pos[1] self.scatter.add_widget(self.widget) def on_move(ob, touch): ob.x = min(max(ob.x, -10), ob.parent.size[0] + 10) ob.y = min(max(ob.y, -10), ob.parent.size[1] + 10) self.scatter.bind(on_touch_up=on_move) def setOnPress(self, callback): self.scatter.bind(on_touch_down=callback) def setInactive(self): self.widget.setInactive() def setActive(self): self.widget.setActive() def setClassification(self, classification): self.classification = classification self.widget.setClass(classification) def setPos(self, pos): self.scatter.x = pos[0] self.scatter.y = pos[1] def getDisplay(self): return self.scatter def getPos(self): return (self.scatter.x, self.scatter.y)
def show_popup(self): if self.popup is not None: self.popup.dismiss() desc = FrescoDescription(item=self.item) count = 0 content = self.item.get("content", "") if content: label = Label(text=content) label.bind(size=update_size) desc.layout.add_widget(label) count += 1 media = self.item.get("media", "") if media: ext = media.rsplit(".", 1)[-1].lower() media = join(dirname(__file__), "data", media) mediawidget = None if ext in ("mp3", "wav", "ogg"): mediawidget = FrescoAudio(source=media) elif ext in ("jpg", "png", "jpeg", "gif", "bmp", "tga"): mediawidget = Image(source=media) else: pass if mediawidget: count += 1 scatter = Scatter(do_translation=False, do_rotation=False, min_scale=0.4, max_scale=2) scatter.add_widget(mediawidget) scatter.bind(size=mediawidget.setter("size")) desc.layout.add_widget(mediawidget) desc.media = mediawidget desc.layout.cols = max(1, count) desc.layout.height = 500 self.popup = popup = Popup(title=self.item["title"], content=desc, size_hint=(0.7, 0.7)) self.popup.bind(on_dismiss=self.stop_media) popup.open()
def _update_tabs(self, *l): self_content = self.content if not self_content: return # cache variables for faster access tab_pos = self.tab_pos tab_layout = self._tab_layout tab_layout.clear_widgets() scrl_v = ScrollView(size_hint=(None, 1)) tabs = self._tab_strip parent = tabs.parent if parent: parent.remove_widget(tabs) scrl_v.add_widget(tabs) scrl_v.pos = (0, 0) self_update_scrollview = self._update_scrollview # update scrlv width when tab width changes depends on tab_pos if self._partial_update_scrollview is not None: tabs.unbind(width=self._partial_update_scrollview) self._partial_update_scrollview = partial( self_update_scrollview, scrl_v) tabs.bind(width=self._partial_update_scrollview) # remove all widgets from the tab_strip self.clear_widgets(do_super=True) tab_height = self.tab_height widget_list = [] tab_list = [] pos_letter = tab_pos[0] if pos_letter == 'b' or pos_letter == 't': # bottom or top positions # one col containing the tab_strip and the content self.cols = 1 self.rows = 2 # tab_layout contains the scrollview containing tabs and two blank # dummy widgets for spacing tab_layout.rows = 1 tab_layout.cols = 3 tab_layout.size_hint = (1, None) tab_layout.height = tab_height self_update_scrollview(scrl_v) if pos_letter == 'b': # bottom if tab_pos == 'bottom_mid': tab_list = (Widget(), scrl_v, Widget()) widget_list = (self_content, tab_layout) else: if tab_pos == 'bottom_left': tab_list = (scrl_v, Widget(), Widget()) elif tab_pos == 'bottom_right': #add two dummy widgets tab_list = (Widget(), Widget(), scrl_v) widget_list = (self_content, tab_layout) else: # top if tab_pos == 'top_mid': tab_list = (Widget(), scrl_v, Widget()) elif tab_pos == 'top_left': tab_list = (scrl_v, Widget(), Widget()) elif tab_pos == 'top_right': tab_list = (Widget(), Widget(), scrl_v) widget_list = (tab_layout, self_content) elif pos_letter == 'l' or pos_letter == 'r': # left ot right positions # one row containing the tab_strip and the content self.cols = 2 self.rows = 1 # tab_layout contains two blank dummy widgets for spacing # "vertically" and the scatter containing scrollview # containing tabs tab_layout.rows = 3 tab_layout.cols = 1 tab_layout.size_hint = (None, 1) tab_layout.width = tab_height scrl_v.height = tab_height self_update_scrollview(scrl_v) # rotate the scatter for vertical positions rotation = 90 if tab_pos[0] == 'l' else -90 sctr = Scatter(do_translation=False, rotation=rotation, do_rotation=False, do_scale=False, size_hint=(None, None), auto_bring_to_front=False, size=scrl_v.size) sctr.add_widget(scrl_v) lentab_pos = len(tab_pos) # Update scatter's top when it's pos changes. # Needed for repositioning scatter to the correct place after its # added to the parent. Use clock_schedule_once to ensure top is # calculated after the parent's pos on canvas has been calculated. # This is needed for when tab_pos changes to correctly position # scatter. Without clock.schedule_once the positions would look # fine but touch won't translate to the correct position if tab_pos[lentab_pos - 4:] == '_top': #on positions 'left_top' and 'right_top' sctr.bind(pos=partial(self._update_top, sctr, 'top', None)) tab_list = (sctr, ) elif tab_pos[lentab_pos - 4:] == '_mid': #calculate top of scatter sctr.bind(pos=partial(self._update_top, sctr, 'mid', scrl_v.width)) tab_list = (Widget(), sctr, Widget()) elif tab_pos[lentab_pos - 7:] == '_bottom': tab_list = (Widget(), Widget(), sctr) if pos_letter == 'l': widget_list = (tab_layout, self_content) else: widget_list = (self_content, tab_layout) # add widgets to tab_layout add = tab_layout.add_widget for widg in tab_list: add(widg) # add widgets to self add = self.add_widget for widg in widget_list: add(widg)
def _update_tabs(self, *l): self_content = self.content if not self_content: return # cache variables for faster access tab_pos = self.tab_pos tab_layout = self._tab_layout tab_layout.clear_widgets() scrl_v = ScrollView(size_hint=(None, 1)) tabs = self._tab_strip parent = tabs.parent if parent: parent.remove_widget(tabs) scrl_v.add_widget(tabs) scrl_v.pos = (0, 0) self_update_scrollview = self._update_scrollview # update scrlv width when tab width changes depends on tab_pos if self._partial_update_scrollview is not None: tabs.unbind(width=self._partial_update_scrollview) self._partial_update_scrollview = partial(self_update_scrollview, scrl_v) tabs.bind(width=self._partial_update_scrollview) # remove all widgets from the tab_strip self.clear_widgets(do_super=True) tab_height = self.tab_height widget_list = [] tab_list = [] pos_letter = tab_pos[0] if pos_letter == 'b' or pos_letter == 't': # bottom or top positions # one col containing the tab_strip and the content self.cols = 1 self.rows = 2 # tab_layout contains the scrollview containing tabs and two blank # dummy widgets for spacing tab_layout.rows = 1 tab_layout.cols = 3 tab_layout.size_hint = (1, None) tab_layout.height = tab_height self_update_scrollview(scrl_v) if pos_letter == 'b': # bottom if tab_pos == 'bottom_mid': tab_list = (Widget(), scrl_v, Widget()) widget_list = (self_content, tab_layout) else: if tab_pos == 'bottom_left': tab_list = (scrl_v, Widget(), Widget()) elif tab_pos == 'bottom_right': #add two dummy widgets tab_list = (Widget(), Widget(), scrl_v) widget_list = (self_content, tab_layout) else: # top if tab_pos == 'top_mid': tab_list = (Widget(), scrl_v, Widget()) elif tab_pos == 'top_left': tab_list = (scrl_v, Widget(), Widget()) elif tab_pos == 'top_right': tab_list = (Widget(), Widget(), scrl_v) widget_list = (tab_layout, self_content) elif pos_letter == 'l' or pos_letter == 'r': # left ot right positions # one row containing the tab_strip and the content self.cols = 2 self.rows = 1 # tab_layout contains two blank dummy widgets for spacing # "vertically" and the scatter containing scrollview # containing tabs tab_layout.rows = 3 tab_layout.cols = 1 tab_layout.size_hint = (None, 1) tab_layout.width = tab_height scrl_v.height = tab_height self_update_scrollview(scrl_v) # rotate the scatter for vertical positions rotation = 90 if tab_pos[0] == 'l' else -90 sctr = Scatter(do_translation=False, rotation=rotation, do_rotation=False, do_scale=False, size_hint=(None, None), auto_bring_to_front=False, size=scrl_v.size) sctr.add_widget(scrl_v) lentab_pos = len(tab_pos) # Update scatter's top when it's pos changes. # Needed for repositioning scatter to the correct place after its # added to the parent. Use clock_schedule_once to ensure top is # calculated after the parent's pos on canvas has been calculated. # This is needed for when tab_pos changes to correctly position # scatter. Without clock.schedule_once the positions would look # fine but touch won't translate to the correct position if tab_pos[lentab_pos - 4:] == '_top': #on positions 'left_top' and 'right_top' sctr.bind(pos=partial(self._update_top, sctr, 'top', None)) tab_list = (sctr, ) elif tab_pos[lentab_pos - 4:] == '_mid': #calculate top of scatter sctr.bind( pos=partial(self._update_top, sctr, 'mid', scrl_v.width)) tab_list = (Widget(), sctr, Widget()) elif tab_pos[lentab_pos - 7:] == '_bottom': tab_list = (Widget(), Widget(), sctr) if pos_letter == 'l': widget_list = (tab_layout, self_content) else: widget_list = (self_content, tab_layout) # add widgets to tab_layout add = tab_layout.add_widget for widg in tab_list: add(widg) # add widgets to self add = self.add_widget for widg in widget_list: add(widg)
class DeckWidget(Widget): def recalc_pos(self, *args): """ Used whenever the Window size changes. The window size sometimes changes rapidly from full screen when the program is first launched, so this is also ran .5 seconds after __init__. """ # Window -> self.parent self.parent.size = Window.size self.card_size = (self.parent.size[0] - 40, float(self.parent.size[1] - 40)) self.card_pos = ( (self.parent.size[0] - self.card_size[0]) / 4, (self.parent.size[1] - self.card_size[1]) / 4, ) # Centered in self.parent self.card_center = self.parent.center self.text_position = self.card_center self.text_content = str(self.deck.selected) if len(self.deck) > 0 else "(Deck is empty)" self.widgets["card_text"].center = self.card_center self.widgets["card_text"].text_size = (20, 20) self.widgets["card_text"].size = (20, 20) self.widgets["card_text_ref"].center = self.card_center self.widgets["card_text_ref"].text_size = (20, 20) self.widgets["card_text_ref"].size = (20, 20) self.touch_move_counter = 0 self.card_base.size = self.card_size self.zoom_out_size = (self.card_size[0] * 0.9, self.card_size[1] * 0.9) self.cardflip_animations = { "zoom_in": Animation(size=self.card_size, duration=0.5), "zoom_out": Animation(size=self.zoom_out_size, duration=0.3), "text_zoom_in": Animation(font_size=sp(15), duration=0.5), "text_zoom_out": Animation(font_size=sp(12), duration=0.3), "zoom_in_out": ( Animation(size=self.zoom_out_size, duration=0.3) + Animation(size=self.card_size, duration=0.5) ), "flip_start": Animation(size=(0, self.zoom_out_size[1]), duration=0.2), "flip_end": Animation(size=self.zoom_out_size, duration=0.2), "is_playing": False, "down_success": False, } self.card_snapback_animation = Animation(center=self.card_center, duration=0.1) self.offscreen_animations = { "rightward_leave": Animation(center_x=self.card_center[0] * 3, duration=0.2), "leftward_leave": Animation(center_x=-self.card_center[0], duration=0.2), "from_side_to_center": Animation(center=self.card_center, duration=0.15), "fade_out": Animation(opacity=0.5, duration=0.2), "fade_in": Animation(opacity=1, duration=0.2), "shrink": Animation(size=(self.card_size[0] * 0.5, self.card_size[1] * 0.5), duration=0.2), "grow": Animation(size=self.card_size, duration=0.45), "is_playing": False, } self.redraw_card_base() def __init__(self, f=None): # COMMENTED OUT aND MANUALLY ADDED FOR ANDROID TESTING PURPOSES if type(f) == type("a"): self.deck = Deck([]) if self.deck.add_from_txt(f) == False: print("LOOOOL: ", f) # self.deck += Card("Q#Failed to Open File", "A#Try Again...") else: self.deck = Deck([]) # print(self.deck) Widget.__init__(self) self.card_size = (float(Window.size[0] - 40), float(Window.size[1] - 40)) self.card_center = Window.center self.text_position = self.card_center self.text_content = str(self.deck.selected) if not self.deck.is_empty else "(Deck is empty)" # Creation of Scatter-Rectangle self.card_base = Scatter( size=self.card_size, center=self.card_center, do_rotation=False, do_translation_y=False, scale_min=0.9, scale_max=1, ) self.shape = {"card_base_rect": Rectangle(size=self.card_base.size, center=self.card_base.center)} self.card_base.canvas.before.add(Color(255, 255, 255)) self.card_base.canvas.before.add(self.shape["card_base_rect"]) # Creation of Label Widget self.widgets = { "card_text": Label( center=self.card_center, halign="center", valign="middle", text_size=self.card_size, font_size=sp(15), text=self.text_content, color=[0, 0, 0, 1], ), "card_text_ref": Label( center=self.card_center, halign="center", valign="middle", text_size=self.card_size, font_size=sp(15), text=self.text_content, color=[0, 0, 0, 1], ), } # Bindings def update_text(*args): self.widgets["card_text"].center = self.card_base.center self.card_base.bind(width=self.redraw_card_base, center_x=update_text) Window.bind(on_resize=self.recalc_pos, on_rotate=self.recalc_pos) self.add_widget(self.card_base) self.add_widget(self.widgets["card_text"], len(self.children)) Clock.schedule_once(self.recalc_pos, 0.5) def change_card_text(self, new_text): self.text_content = new_text self.widgets["card_text"].text = self.text_content def update_selected_text(self): self.change_card_text(str(self.deck.get_selected())) def redraw_card_base(self, *args): """ Redraws the card. I had issues with canvas ordering and the text would never be drawn on top of the rectangle as the rectangle seemed to be always put on top during an animation. So the label widget is removed and redrawn after the rectangle is removed and redrawn. This is called every time an animation occurs that modifies the rectangle size. """ self.card_base.center = self.card_center self.remove_widget(self.widgets["card_text"]) self.card_base.canvas.before.remove(self.shape["card_base_rect"]) self.shape["card_base_rect"] = Rectangle(size=self.card_base.size, center=self.card_base.center) self.card_base.canvas.before.add(self.shape["card_base_rect"]) self.widgets["card_text"] = Label( center=self.card_center, size=(20, 20), font_size=self.widgets["card_text_ref"].font_size, text=self.text_content, color=[0, 0, 0, 1], ) self.add_widget(self.widgets["card_text"]) self.widgets["card_text"].center = self.card_center def flip_animation(self): """ Plays a series of animations that will cause the rectangle to appear to flip. Local functions are defined for the purpose of being bound when certain animations are complete. This way I would be able to do things in the background after certain animations are complete (i.e. when card is at the apex of the flip animation and is hardly visible, I want to change the stuff on the card in the at_flip_end() function.) """ # self.cardflip_animations['zoom_in_out'].start(self.card_base) card_zoom_a = self.cardflip_animations["zoom_out"] card_zoom_b = self.cardflip_animations["zoom_in"] text_zoom = self.cardflip_animations["text_zoom_out"] + self.cardflip_animations["text_zoom_in"] text_zoom.start(self.widgets["card_text_ref"]) f_e = p = self.cardflip_animations["flip_end"] f_s = self.cardflip_animations["flip_start"] def toggle_is_playing(*args): self.cardflip_animations["is_playing"] = not self.cardflip_animations["is_playing"] card_zoom_a.stop(self.card_base) card_zoom_b.stop(self.card_base) text_zoom.stop(self.card_base) f_e.stop(self.card_base) f_s.stop(self.card_base) card_zoom_b.unbind(on_complete=toggle_is_playing) card_zoom_a.unbind(on_complete=at_zoom_end) def final_zoom(*args): f_e.unbind(on_complete=final_zoom) card_zoom_b.start(self.card_base) def at_flip_end(*args): self.deck.get_selected().flip() self.update_selected_text() f_s.unbind(on_complete=at_flip_end) f_e.bind(on_complete=final_zoom) f_e.start(self.card_base) def at_zoom_end(*args): f_s.bind(on_complete=at_flip_end) f_s.start(self.card_base) card_zoom_b.bind(on_complete=toggle_is_playing) card_zoom_a.bind(on_complete=at_zoom_end) card_zoom_a.start(self.card_base) # (self.cardflip_animations['zoom_out']+self.cardflip_animations['zoom_in']).start(self.card_base) def offscreen_animation(self, right_mode): """ Similar to flip animation. When at the end of the first offscreen transition, I'd want to change the stuff on the card which could be done through the local begin_entrance() functions """ ret_to_center = self.offscreen_animations["grow"] & self.offscreen_animations["from_side_to_center"] z = self.offscreen_animations["shrink"] & self.offscreen_animations["rightward_leave"] y = self.offscreen_animations["shrink"] & self.offscreen_animations["leftward_leave"] def toggle_is_playing(*args): self.offscreen_animations["is_playing"] = False if right_mode: def begin_entrance_z(*args): self.deck.get_selected().reset() self.deck.prev(self.deck.mode) self.update_selected_text() self.card_base.center_x = -self.card_center[0] * 0.4 ret_to_center.bind(on_complete=toggle_is_playing) ret_to_center.start(self.card_base) z.bind(on_complete=begin_entrance_z) z.start(self.card_base) else: # left mode def begin_entrance_y(*args): self.deck.get_selected().reset() self.deck.next(self.deck.mode) self.update_selected_text() self.card_base.center_x = self.card_center[0] * 2.5 ret_to_center.bind(on_complete=toggle_is_playing) ret_to_center.start(self.card_base) y.bind(on_complete=begin_entrance_y) y.start(self.card_base) def on_touch_up(self, touch): """ Goals: -If the card is touch-released very close (<5px) to the original position, it should play a flip animation. -When the card is released a bit further, it should transition back to the original position. -If the card is released at a point where half of it's face is offscreen, the card should transition off screen and another card should transition on screen. All of this should not be able to happen again if any animation is already playing. """ if not self.deck.is_empty(): if ( self.card_base.center[0] < self.card_center[0] + 5 and self.card_base.center[0] > self.card_center[0] - 5 ): # Do flip animation if not self.cardflip_animations["is_playing"] and self.cardflip_animations["down_success"]: self.cardflip_animations["is_playing"] = True self.flip_animation() elif self.card_base.center[0] > (self.card_center[0] * 1.65): # Do offscreen/onscreen transition rightwards if not self.offscreen_animations["is_playing"]: self.offscreen_animations["is_playing"] = True self.offscreen_animation(True) elif self.card_base.center[0] < (self.card_center[0] * 0.35): # Do offscreen/onscreen transition leftwards if self.deck.mode != None: self.card_snapback_animation.start(self.card_base) elif not self.offscreen_animations["is_playing"]: self.offscreen_animations["is_playing"] = True self.offscreen_animation(False) else: # Do transition-to-origin animation self.card_snapback_animation.start(self.card_base) # Swipe Emulation - See DeckWidget.on_touch_move() if self.touch_move_counter < 7 and self.touch_move_counter > 1: x_difference = self.touch_move_current[0] - self.touch_move_init[0] y_difference = self.touch_move_current[1] - self.touch_move_init[1] if x_difference > 5 and x_difference > y_difference: if not self.offscreen_animations["is_playing"] and not self.cardflip_animations["is_playing"]: self.offscreen_animations["is_playing"] = True self.offscreen_animation(True) elif x_difference < -5 and x_difference < y_difference: if not self.offscreen_animations["is_playing"] and not self.cardflip_animations["is_playing"]: self.offscreen_animations["is_playing"] = True self.offscreen_animation(False) self.touch_move_counter = 0 def on_touch_move(self, touch): """ At the last minute, I noticed how my launcher on my android phone behaves when you swap the screens. Even if your finger doesn't cover a significant distance. If you swipe quickly with a clear direction yet very short distance, the screen will change. It's only when you move your finger slowly with a short distances will the screen bounce back to the original position. I wanted to try emulating this to an extent; swiping fast should play the offscreen animations while swiping slow would cause the snapback animation effect. Also, the swipe must be bigger in the x-direction than it is in the y-direction. """ if not self.deck.is_empty(): if self.touch_move_counter == 0: self.touch_move_init = touch.pos else: self.touch_move_current = touch.pos self.touch_move_counter += 1 def on_touch_down(self, touch): """ Dictates what happens when the DeckWidget is clicked/touched but not released yet. Scatter.on_touch_down() handles sliding (card_base is a Scatter). Also, I only want this to work if no animations are playing. """ if not self.deck.is_empty(): if self.offscreen_animations["is_playing"] == False and self.cardflip_animations["is_playing"] == False: self.card_base.on_touch_down(touch) self.cardflip_animations["down_success"] = True else: self.cardflip_animations["down_success"] = False if not self.cardflip_animations["is_playing"]: self.remove_widget(self.widgets["card_text"]) self.widgets["card_text"] = Label( center=self.card_base.center, size=(20, 20), font_size=sp(15), text=self.text_content, color=[0, 0, 0, 1], ) self.add_widget(self.widgets["card_text"]) self.widgets["card_text"].center = self.card_base.center
class KivySprite: def __init__(self, parent_widget): self._parent_widget = parent_widget self._scatter_widget = Scatter(#self._tapped, # callbacks on all areas do_rotation=False, do_scale=False, do_translation=True) self._image_widget = Image(source='a.png') self._scatter_widget.add_widget(self._image_widget) self._parent_widget.add_widget(self._scatter_widget) with self._scatter_widget.canvas.before: Color(1,0,0) Rectangle(pos=self._scatter_widget.pos, size=self._scatter_widget.size) self.layer = None self.visible = True self.rotation_style = 'all around' self._scatter_widget.bind(pos=lambda w, p: self._moved()) self._image_widget.bind(on_touch_down=lambda w, t: self._tapped()) # to be overriden by Sprite def _tapped(self): pass def _moved(self): pass def _rotated(self): pass def _sized(self): pass @property def image(self): return self._image_widget.source @image.setter def image(self, value): self._image_widget.source = value @property def position(self): return self._scatter_widget.pos @position.setter def position(self, value): self._scatter_widget.pos = value @property def x(self): return self.position[0] @x.setter def x(self, value): self._scatter_widget.pos = value - (self._image_widget.size[0]/2), self.y @property def y(self): return self.position[1] @y.setter def y(self, value): self._scatter_widget.pos = self.x, value - (self._image_widget.size[1]/2) @property def size(self): return self._scatter_widget.scale * 100 @size.setter def size(self, value): self._scatter_widget.scale = value * 0.01 @property def direction(self): return (((self._scatter_widget.rotation + 90) + 179) % 360) - 179 @direction.setter def direction(self, value): self._scatter_widget.rotation = value - 90 @property def draggable(self): return self._scatter_widget.do_translation @draggable.setter def draggable(self, value): self._scatter_widget.do_translation = value def glide(self, x, y, time=None): # either x, y, time or sprite, time if time is None: time = y y = x.y x = x.x anim = Animation(x=x, y=y, duration=time) anim.start(self._scatter_widget) def move(self, steps): self.x += steps * math.sin(math.radians(self.direction)) self.y += steps * math.cos(math.radians(self.direction)) self._moved() def point_towards(self, x, y=None): # test if y is None: # x is a sprite y = x.y x = x.x dy = y - self.y dx = x - self.x angle = (-90 if dx < 0 else 90) if dy == 0 else math.degrees(math.atan(dx / dy)) if dy < 0: angle += 180 self.direction = angle def touching(self, other_sprite): return self._image_widget.collide_widget(other_sprite._image_widget)