def build(self): self._current_lang = None self.current_lang_ulubione_file = None self.current_lang_dict = None self.current_lang_ulubione = None sm = ScreenManager() # Ustawienie tła with sm.canvas: Color(1, 1, 1) rect = Rectangle(source="img/tlo.png", size=Window.size) # Skalowanie obrazka przy zmianie wymiarów okna (orientacji telefonu) def resize_action(size, pos): sm.size = Window.size rect.size = Window.size sm.bind(pos=resize_action, size=resize_action) # Aplikacje którymi zarządzamy sm.add_widget(MenuScreen(name='menu')) sm.add_widget(LearnScreen(name='game')) sm.add_widget(MemoryScreen(name='memory')) sm.add_widget(SettingsScreen(name='settings')) return sm
class MainApp(App): show_add_bottle_popup = BooleanProperty(False) scanned_upc = StringProperty("") def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.bottle_adder = BottleAdder() self.bind(scanned_upc=self.add_new_bottle) def build(self): self.sm = ScreenManager() self.sm.add_widget(StartScreen()) self.sm.add_widget(LiquorScreen()) self.sm.add_widget(MixingScreen()) self.sm.add_widget(DrinkSelectScreen()) def set_canvas(inst, value): with self.sm.canvas.before: from kivy.graphics import Color, Rectangle from scorpion.ui.textures import radial_gradient Color(1, 1, 1) Rectangle(texture=radial_gradient(), size=value) self.sm.bind(size=set_canvas) return self.sm def set_screen(self, screen_name): self.sm.current = screen_name def get_screen(self, screen = None): if screen is None: return self.sm.current_screen else: return self.sm.get_screen(screen) def add_new_bottle(self, inst, value): if value == "" and inst != None: return self.bottle_adder.open(value) self.scanned_upc = ""
def add_scrim(self, instance_manager: ScreenManager) -> None: with instance_manager.canvas.after: self._scrim_color = Color(rgba=[0, 0, 0, 0]) self._scrim_rectangle = Rectangle(pos=instance_manager.pos, size=instance_manager.size) instance_manager.bind( pos=self.update_scrim_rectangle, size=self.update_scrim_rectangle, )
class SmoothieHost(App): is_connected = BooleanProperty(False) status = StringProperty("Not Connected") wpos = ListProperty([0, 0, 0]) mpos = ListProperty([0, 0, 0, 0, 0, 0]) fr = NumericProperty(0) frr = NumericProperty(0) fro = NumericProperty(100) sr = NumericProperty(0) lp = NumericProperty(0) is_inch = BooleanProperty(False) is_spindle_on = BooleanProperty(False) is_abs = BooleanProperty(True) is_desktop = NumericProperty(0) is_cnc = BooleanProperty(False) tab_top = BooleanProperty(False) main_window = ObjectProperty() gcode_file = StringProperty() is_show_camera = BooleanProperty(False) is_spindle_camera = BooleanProperty(False) manual_tool_change = BooleanProperty(False) is_v2 = BooleanProperty(True) wait_on_m0 = BooleanProperty(False) # Factory.register('Comms', cls=Comms) def __init__(self, **kwargs): super(SmoothieHost, self).__init__(**kwargs) if len(sys.argv) > 1: # override com port self.use_com_port = sys.argv[1] else: self.use_com_port = None self.webserver = False self._blanked = False self.blank_timeout = 0 self.last_touch_time = 0 self.camera_url = None self.loaded_modules = [] self.secs = 0 self.fast_stream = False self.last_probe = {'X': 0, 'Y': 0, 'Z': 0, 'status': False} self.tool_scripts = ToolScripts() self.desktop_changed = False def build_config(self, config): config.setdefaults( 'General', { 'last_gcode_path': os.path.expanduser("~"), 'last_print_file': '', 'serial_port': 'serial:///dev/ttyACM0', 'report_rate': '1.0', 'blank_timeout': '0', 'manual_tool_change': 'false', 'wait_on_m0': 'false', 'fast_stream': 'false', 'v2': 'false', 'is_spindle_camera': 'false' }) config.setdefaults( 'UI', { 'display_type': "RPI Touch", 'cnc': 'false', 'tab_top': 'false', 'screen_size': 'auto', 'screen_pos': 'auto', 'filechooser': 'default' }) config.setdefaults( 'Extruder', { 'last_bed_temp': '60', 'last_hotend_temp': '185', 'length': '20', 'speed': '300', 'hotend_presets': '185 (PLA), 230 (ABS)', 'bed_presets': '60 (PLA), 110 (ABS)' }) config.setdefaults('Jog', {'xy_feedrate': '3000'}) config.setdefaults( 'Web', { 'webserver': 'false', 'show_video': 'false', 'camera_url': 'http://localhost:8080/?action=snapshot' }) def build_settings(self, settings): jsondata = """ [ { "type": "title", "title": "UI Settings" }, { "type": "options", "title": "Desktop Layout", "desc": "Select Display layout, RPI is for 7in touch screen layout", "section": "UI", "key": "display_type", "options": ["RPI Touch", "Small Desktop", "Large Desktop", "Wide Desktop", "RPI Full Screen"] }, { "type": "bool", "title": "CNC layout", "desc": "Turn on for a CNC layout, otherwise it is a 3D printer Layout", "section": "UI", "key": "cnc" }, { "type": "bool", "title": "Tabs on top", "desc": "TABS are on top of the screen", "section": "UI", "key": "tab_top" }, { "type": "options", "title": "File Chooser", "desc": "Which filechooser to use in desktop mode", "section": "UI", "key": "filechooser", "options": ["default", "wx", "zenity", "kdialog"] }, { "type": "title", "title": "General Settings" }, { "type": "numeric", "title": "Report rate", "desc": "Rate in seconds to query for status from Smoothie", "section": "General", "key": "report_rate" }, { "type": "numeric", "title": "Blank Timeout", "desc": "Inactive timeout in seconds before screen will blank", "section": "General", "key": "blank_timeout" }, { "type": "bool", "title": "Manual Tool change", "desc": "On M6 let user do a manual tool change", "section": "General", "key": "manual_tool_change" }, { "type": "bool", "title": "Wait on M0", "desc": "On M0 popup a dialog and pause until it is dismissed", "section": "General", "key": "wait_on_m0" }, { "type": "bool", "title": "Spindle Camera", "desc": "Enable the spindle camera screen", "section": "General", "key": "is_spindle_camera" }, { "type": "bool", "title": "Version 2 Smoothie", "desc": "Select for version 2 smoothie", "section": "General", "key": "v2" }, { "type": "bool", "title": "Fast Stream", "desc": "Allow fast stream for laser over network", "section": "General", "key": "fast_stream" }, { "type": "title", "title": "Web Settings" }, { "type": "bool", "title": "Web Server", "desc": "Turn on Web server to remotely check progress", "section": "Web", "key": "webserver" }, { "type": "bool", "title": "Show Video", "desc": "Display mjpeg video in web progress", "section": "Web", "key": "show_video" }, { "type": "string", "title": "Camera URL", "desc": "URL for camera stream", "section": "Web", "key": "camera_url" }, { "type": "title", "title": "Extruder Settings" }, { "type": "string", "title": "Hotend Presets", "desc": "Set the comma separated presets for the hotend temps", "section": "Extruder", "key": "hotend_presets" }, { "type": "string", "title": "Bed Presets", "desc": "Set the comma separated presets for the bed temps", "section": "Extruder", "key": "bed_presets" } ] """ settings.add_json_panel('SmooPie application', self.config, data=jsondata) def on_config_change(self, config, section, key, value): # print("config changed: {} - {}: {}".format(section, key, value)) token = (section, key) if token == ('UI', 'cnc'): self.is_cnc = value == "1" elif token == ('UI', 'display_type'): self.desktop_changed = True self.main_window.display("NOTICE: Restart is needed") elif token == ('UI', 'tab_top'): self.tab_top = value == "1" elif token == ('Extruder', 'hotend_presets'): self.main_window.ids.extruder.ids.set_hotend_temp.values = value.split( ',') elif token == ('Extruder', 'bed_presets'): self.main_window.ids.extruder.ids.set_bed_temp.values = value.split( ',') elif token == ('General', 'blank_timeout'): self.blank_timeout = float(value) elif token == ('General', 'manual_tool_change'): self.manual_tool_change = value == '1' elif token == ('General', 'wait_on_m0'): self.wait_on_m0 = value == '1' elif token == ('General', 'v2'): self.is_v2 = value == '1' elif token == ('Web', 'camera_url'): self.camera_url = value else: self.main_window.display("NOTICE: Restart is needed") def on_stop(self): # The Kivy event loop is about to stop, stop the async main loop self.comms.stop() # stop the aysnc loop if self.is_webserver: self.webserver.stop() if self.blank_timeout > 0: # unblank if blanked self.unblank_screen() # stop any loaded modules for m in self.loaded_modules: m.stop() def on_start(self): # in case we added something to the defaults, make sure they are written to the ini file self.config.update_config('smoothiehost.ini') def window_request_close(self, win): if self.desktop_changed: # if the desktop changed we reset the window size and pos self.config.set('UI', 'screen_size', 'auto') self.config.set('UI', 'screen_pos', 'auto') self.config.write() elif self.is_desktop == 2 or self.is_desktop == 3: # Window.size is automatically adjusted for density, must divide by density when saving size self.config.set( 'UI', 'screen_size', "{}x{}".format(int(Window.size[0] / Metrics.density), int(Window.size[1] / Metrics.density))) self.config.set('UI', 'screen_pos', "{},{}".format(Window.top, Window.left)) Logger.info( 'close: Window.size: {}, Window.top: {}, Window.left: {}'. format(Window.size, Window.top, Window.left)) self.config.write() return False def build(self): lt = self.config.get('UI', 'display_type') dtlut = { "RPI Touch": 0, "Small Desktop": 1, "Large Desktop": 2, "Wide Desktop": 3, "RPI Full Screen": 4 } self.is_desktop = dtlut.get(lt, 0) # load the layouts for the desktop screen if self.is_desktop == 1: Builder.load_file('desktop.kv') Window.size = (1024, 768) elif self.is_desktop == 2 or self.is_desktop == 3 or self.is_desktop == 4: Builder.load_file('desktop_large.kv' if self.is_desktop == 2 else 'desktop_wide.kv') if self.is_desktop != 4: # because rpi_egl does not like to be told the size s = self.config.get('UI', 'screen_size') if s == 'auto': Window.size = (1280, 1024) if self.is_desktop == 2 else (1280, 800) elif 'x' in s: (w, h) = s.split('x') Window.size = (int(w), int(h)) p = self.config.get('UI', 'screen_pos') if p != 'auto' and ',' in p: (t, l) = p.split(',') Window.top = int(t) Window.left = int(l) Window.bind(on_request_close=self.window_request_close) else: self.is_desktop = 0 # load the layouts for rpi 7" touch screen Builder.load_file('rpi.kv') self.is_cnc = self.config.getboolean('UI', 'cnc') self.tab_top = self.config.getboolean('UI', 'tab_top') self.is_webserver = self.config.getboolean('Web', 'webserver') self.is_show_camera = self.config.getboolean('Web', 'show_video') self.is_spindle_camera = self.config.getboolean( 'General', 'is_spindle_camera') self.manual_tool_change = self.config.getboolean( 'General', 'manual_tool_change') self.wait_on_m0 = self.config.getboolean('General', 'wait_on_m0') self.is_v2 = self.config.getboolean('General', 'v2') self.comms = Comms(App.get_running_app(), self.config.getfloat('General', 'report_rate')) self.gcode_file = self.config.get('General', 'last_print_file') self.sm = ScreenManager() ms = MainScreen(name='main') self.main_window = ms.ids.main_window self.sm.add_widget(ms) self.sm.add_widget(GcodeViewerScreen(name='viewer', comms=self.comms)) self.config_editor = ConfigEditor(name='config_editor') self.sm.add_widget(self.config_editor) self.gcode_help = GcodeHelp(name='gcode_help') self.sm.add_widget(self.gcode_help) if self.is_desktop == 0: self.text_editor = TextEditor(name='text_editor') self.sm.add_widget(self.text_editor) self.blank_timeout = self.config.getint('General', 'blank_timeout') Logger.info("SmoothieHost: screen blank set for {} seconds".format( self.blank_timeout)) self.sm.bind(on_touch_down=self._on_touch) Clock.schedule_interval(self._every_second, 1) # select the file chooser to use # select which one we want from config filechooser = self.config.get('UI', 'filechooser') if self.is_desktop > 0: if filechooser != 'default': NativeFileChooser.type_name = filechooser Factory.register('filechooser', cls=NativeFileChooser) try: f = Factory.filechooser() except Exception: Logger.error( "SmoothieHost: can't use selected file chooser: {}". format(filechooser)) Factory.unregister('filechooser') Factory.register('filechooser', cls=FileDialog) else: # use Kivy filechooser Factory.register('filechooser', cls=FileDialog) # we want to capture arrow keys Window.bind(on_key_down=self._on_keyboard_down) else: # use Kivy filechooser Factory.register('filechooser', cls=FileDialog) # setup for cnc or 3d printer if self.is_cnc: if self.is_desktop < 3: # remove Extruder panel from tabpanel and tab self.main_window.ids.tabs.remove_widget( self.main_window.ids.tabs.extruder_tab) # if not CNC mode then do not show the ZABC buttons in jogrose if not self.is_cnc: self.main_window.ids.tabs.jog_rose.jogrosemain.remove_widget( self.main_window.ids.tabs.jog_rose.abc_panel) if self.is_webserver: self.webserver = ProgressServer() self.webserver.start(self, 8000) if self.is_show_camera: self.camera_url = self.config.get('Web', 'camera_url') self.sm.add_widget(CameraScreen(name='web cam')) self.main_window.tools_menu.add_widget( ActionButton(text='Web Cam', on_press=lambda x: self._show_web_cam())) if self.is_spindle_camera: if self.is_desktop in [0, 4]: try: self.sm.add_widget(SpindleCamera(name='spindle camera')) except Exception as err: self.main_window.display( 'ERROR: failed to load spindle camera. Check logs') Logger.error( 'Main: spindle camera exception: {}'.format(err)) self.main_window.tools_menu.add_widget( ActionButton(text='Spindle Cam', on_press=lambda x: self._show_spindle_cam())) # load any modules specified in config self._load_modules() if self.blank_timeout > 0: # unblank if blanked self.unblank_screen() return self.sm def _show_spindle_cam(self): if self.is_desktop in [0, 4]: self.sm.current = "spindle camera" else: # we run it as a separate program so it is in its own window subprocess.Popen(['python3', 'spindle_camera.py'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) def _show_web_cam(self): self.sm.current = "web cam" def _on_keyboard_down(self, instance, key, scancode, codepoint, modifiers): # print("key: {}, scancode: {}, codepoint: {}, modifiers: {}".format(key, scancode, codepoint, modifiers)) # control uses finer move, shift uses coarse move v = 0.1 if len(modifiers) == 1: if modifiers[0] == 'ctrl': v = 0.01 elif modifiers[0] == 'shift': v = 1 choices = { 273: "Y{}".format(v), 275: "X{}".format(v), 274: "Y{}".format(-v), 276: "X{}".format(-v), 280: "Z{}".format(v), 281: "Z{}".format(-v) } s = choices.get(key, None) if s is not None: self.comms.write('$J {}\n'.format(s)) return True # handle command history if in desktop mode if self.is_desktop > 0: if v == 0.01: # it is a control key if codepoint == 'p': # get previous history by finding all the recently sent commands history = [ x['text'] for x in self.main_window.ids.log_window.data if x['text'].startswith('<< ') ] if history: last = history.pop() self.main_window.ids.entry.text = last[3:] elif codepoint == 'n': # get next history pass elif codepoint == 'c': # clear console self.main_window.ids.log_window.data = [] return False def command_input(self, s): if s.startswith('!'): # shell command send to unix shell self.main_window.display('> {}'.format(s)) try: p = subprocess.Popen(s[1:], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, universal_newlines=True) result, err = p.communicate() for l in result.splitlines(): self.main_window.display(l) for l in err.splitlines(): self.main_window.display(l) if p.returncode != 0: self.main_window.display('returncode: {}'.format( p.returncode)) except Exception as err: self.main_window.display('> command exception: {}'.format(err)) elif s == '?': self.gcode_help.populate() self.sm.current = 'gcode_help' else: self.main_window.display('<< {}'.format(s)) self.comms.write('{}\n'.format(s)) # when we hit enter it refocuses the the input def _refocus_text_input(self, *args): Clock.schedule_once(self._refocus_it) def _refocus_it(self, *args): self.main_window.ids.entry.focus = True def _load_modules(self): if not self.config.has_section('modules'): return try: for key in self.config['modules']: Logger.info("load_modules: loading module {}".format(key)) mod = importlib.import_module('modules.{}'.format(key)) if mod.start(self.config['modules'][key]): Logger.info("load_modules: loaded module {}".format(key)) self.loaded_modules.append(mod) else: Logger.info( "load_modules: module {} failed to start".format(key)) except Exception: Logger.warn("load_modules: exception: {}".format( traceback.format_exc())) def _every_second(self, dt): ''' called every second ''' self.secs += 1 if self.blank_timeout > 0 and not self.main_window.is_printing: self.last_touch_time += 1 if self.last_touch_time >= self.blank_timeout: self.last_touch_time = 0 self.blank_screen() def blank_screen(self): try: with open('/sys/class/backlight/rpi_backlight/bl_power', 'w') as f: f.write('1\n') self._blanked = True except Exception: Logger.warning("SmoothieHost: unable to blank screen") def unblank_screen(self): try: with open('/sys/class/backlight/rpi_backlight/bl_power', 'w') as f: f.write('0\n') except Exception: pass def _on_touch(self, a, b): self.last_touch_time = 0 if self._blanked: self._blanked = False self.unblank_screen() return True return False
class Main(App): # These are to handle Android switching away from the app or locking the screen. # I don't want to do anything on pause, but I do want to go back to the main screen if it was paused for a while. def on_pause(self): kivy.logger.Logger.debug('Main: pausing') self.pause_time = time.time() return True def on_resume(self): kivy.logger.Logger.debug('Main: resuming') if time.time()-self.pause_time > 5: self.screen_manager.current = 'chooser' # This disables kivy's settings panel, normally accessible by pressing F1 def open_settings(self, *args): pass ## "Navbar" button functions def pressed_win(self, *args): if 'btn_functions' in dir(self.screen_manager.current_screen) and self.screen_manager.current_screen.btn_functions[0] != None: self.screen_manager.current_screen.btn_functions[0]() return False # I'm returning false here so that the button still triggers the next on_release event def pressed_home(self, *args): if 'btn_functions' in dir(self.screen_manager.current_screen) and self.screen_manager.current_screen.btn_functions[1] != None: self.screen_manager.current_screen.btn_functions[1]() return False # I'm returning false here so that the button still triggers the next on_release event def pressed_back(self, *args): if 'btn_functions' in dir(self.screen_manager.current_screen) and self.screen_manager.current_screen.btn_functions[2] != None: self.screen_manager.current_screen.btn_functions[2]() return False # I'm returning false here so that the button still triggers the next on_release event def update_buttons(self, *args): if 'btns' in dir(self.screen_manager.current_screen): win, home, back = self.screen_manager.current_screen.btns if win == None: self.win_btn.opacity = 0 else: self.win_btn.opacity = 1 self.win_btn.background_normal = win self.win_btn.background_down = self.win_btn.background_normal if home == None: self.home_btn.opacity = 0 else: self.home_btn.opacity = 1 self.home_btn.background_normal = home self.home_btn.background_down = self.home_btn.background_normal if back == None: self.back_btn.opacity = 0 else: self.back_btn.opacity = 1 self.back_btn.background_normal = back self.back_btn.background_down = self.back_btn.background_normal ## These functions should probably be put somewhat inside the painter widget. def finish_paint(self, good = True): self.paint_screen.image.label.text = '' if good: self.goto_screen('photostrip', 'right') def save_painter(self): savedir = self.paint_screen.image.source+'.overlays' try: filesystem.listdir(savedir) except: if not filesystem.is_dir(savedir): filesystem.mkdir(savedir) index = 0 filename = '%02d.png' try: dircontents = os.listdir(savedir) except: # Probably using the HTTP filesystem. tmpused = True filepath = tempfile.mktemp() + '.png' else: tmpused = False while filename % index in dircontents: index += 1 filepath = filesystem.join(savedir, filename % index) if self.paint_screen.painter.save_png(filepath) and (not tmpused or filesystem.savefile(savedir, 'png', filepath)): # If saving the file works and either the tmpfile was not used ot the second stage of saving works. self.paint_screen.painter.do_drawing = False # Stop drawing on the image until the canvas gets cleared (by switching screen) self.paint_screen.image.label.color = (0,0.75,0,1) self.paint_screen.image.label.text = 'Saved' Clock.schedule_once(lambda arg: self.finish_paint(True), 2) else: self.paint_screen.image.label.color = (0.75,0,0,1) self.paint_screen.image.label.text = 'FAILED to save the drawing, sorry.' Clock.schedule_once(lambda arg: self.finish_paint(False), 3) def goto_screen(self, screen_name, direction): self.screen_manager.transition.direction = direction self.screen_manager.current = screen_name # This function is just to keep the screen_manager size with window resizing def update_size(self, root, value): self.screen_manager.size = (root.width-192, root.height) def build(self): root = FloatLayout() ## Screen Manager self.screen_manager = ScreenManager(transition=SlideTransition(),size_hint=[None,None], pos_hint={'left': 1}) root.bind(size=self.update_size) root.add_widget(self.screen_manager) ## "Navbar" Buttons ## These are meant to work like the Android navbar would, and I have named them as such. # These two functions are used for a indicator for the button being pressed. def rend_circle(btn): with btn.canvas: Color(1,1,1,0.25) size = (btn.size[0], btn.size[1]*1.5) btn.hl = Ellipse(pos=( btn.pos[0]+((btn.size[0]/2)-(size[0]/2)), btn.pos[1]+((btn.size[1]/2)-(size[1]/2)) ), size=size) Color(1,1,1,1) # Reset the color, otherwise the background goes dark def derend_circle(btn): btn.canvas.remove(btn.hl) self.win_btn = Button(text='', size_hint=[None,None],size=[192,192],height=192,pos_hint={'right': 1, 'center_y': 0.8}) self.win_btn.bind(on_press=rend_circle) self.win_btn.bind(on_release=derend_circle) self.win_btn.bind(on_release=self.pressed_win) self.home_btn = Button(text='', size_hint=[None,None],size=[192,192],pos_hint={'right': 1, 'center_y': 0.5}) self.home_btn.bind(on_press=rend_circle) self.home_btn.bind(on_release=derend_circle) self.home_btn.bind(on_release=self.pressed_home) self.back_btn = Button(text='', size_hint=[None,None],size=[192,192],pos_hint={'right': 1, 'center_y': 0.2}) self.back_btn.bind(on_press=rend_circle) self.back_btn.bind(on_release=derend_circle) self.back_btn.bind(on_release=self.pressed_back) root.add_widget(self.win_btn) root.add_widget(self.home_btn) root.add_widget(self.back_btn) # Render the background def update_rect(instance, value): instance.bg.pos = instance.pos instance.bg.size = instance.size self.screen_manager.bind(size=update_rect,pos=update_rect) with self.screen_manager.canvas.before: self.screen_manager.bg = Rectangle(source='background.png') Loader.loading_image = 'loading.png' ## FileChooser chooser_screen = Screen(name='chooser') chooser = FileChooserGalleryView(rootpath=PHOTOS_PATH) chooser.file_system = filesystem def select_folder(chooser, photostrip): photostrip.set_path(chooser.current_entry.path) self.goto_screen('photostrip', 'left') chooser.bind(on_select_folder=lambda args:select_folder(chooser, photostrip)) chooser_screen.add_widget(chooser) ## Painter self.paint_screen = PaintScreen(name='painter') self.paint_screen.bind(on_leave=lambda src: self.paint_screen.painter.clear()) ## ImageViewer viewer_screen = ViewerScreen(name='viewer') viewer_screen.bind(on_enter=lambda src:setattr(self.paint_screen.image,'source',viewer_screen.image.source)) ## Photo strip def foo(a): print type(a), a photostrip_screen = Screen(name='photostrip') photostrip = PhotoStrip() photostrip_screen.add_widget(photostrip) photostrip.bind( on_press=lambda src,fn:viewer_screen.set_image(fn), on_release=lambda src,fn: self.goto_screen('painter', 'left'), ) # Set up the icons and functions for the navbar buttons chooser_screen.btns = [None, 'ic_action_refresh.png', None] chooser_screen.btn_functions = [None, chooser._trigger_update, None] photostrip_screen.btns = [None, None, 'ic_sysbar_back.png'] photostrip_screen.btn_functions = [None, None, lambda:self.goto_screen('chooser', 'right')] viewer_screen.btns = ['ic_action_edit.png', None, 'ic_sysbar_back.png'] viewer_screen.btn_functions = [lambda:self.goto_screen('painter', 'left'), viewer_screen.drawing_toggle, lambda:self.goto_screen('photostrip', 'right')] self.paint_screen.btns = ['ic_action_save.png', None, 'ic_action_discard.png'] self.paint_screen.btn_functions = [self.save_painter, None, lambda:self.goto_screen('photostrip', 'right')] # Finally, add the screens to the manager self.screen_manager.add_widget(chooser_screen) self.screen_manager.add_widget(photostrip_screen) self.screen_manager.add_widget(viewer_screen) self.screen_manager.add_widget(self.paint_screen) # Set the navbar buttons from the variables set above self.screen_manager.transition.bind(on_complete=self.update_buttons) self.update_buttons() return root
class KivyBabelComic(App): def __init__(self, **kwargs): super(KivyBabelComic,self).__init__(**kwargs) def build(self): self.sm = ScreenManager() self.sm.add_widget(KivyVisor()) self.sm.bind(on_touch_down=self.on_touch_down) return self.sm def on_touch_down(self, obj, event): ''' vamos a capturar eventos en estas zonas ************************* * * * * * * * * * * * * *3* *4* * * * * * * * * * * *1* *2* *3* * * * * * * ************************* :param widget: :param event: :return: ''' if obj != self.sm: return #zona1 = ((0, Window.width * 0.1), (Window.height * 0.1 + Window.height * 0.5,Window.height * -0.1 + Window.height * 0.5 )) zona2 = ((Window.width * 0.5 - Window.width * 0.1, Window.width * 0.5 + Window.width * 0.1), (0, Window.height * 0.1)) #zona3 = ((Window.width - Window.width * 0.1, Window.width), (Window.height * 0.1 + Window.height * 0.5 , Window.height * -0.1 + Window.height * 0.5)) if (zona2[0][0]< event.pos[0] and event.pos[0] < zona2[0][1]) and (zona2[1][0]<event.pos[1] and event.pos[1]<zona2[1][1]): box = GridLayout(cols=5) botonAncho = Button(text="nada") botonAncho.bind(on_press=self.nada) box.add_widget(botonAncho) botonCentrado = Button(text="nada") botonCentrado.bind(on_press=self.nada) box.add_widget(botonCentrado) botonSalir = Button(text="salir") botonSalir.bind(on_press=self.salir) box.add_widget(botonSalir) p = Popup(title='Test popup', size_hint=(None, None), size=(400, 150)) p.add_widget(box) p.open() def nada(self, event): print("Falta implementar") def salir(self, event): self.stop()
class CivMapScreen(BoxLayout): def __init__(self, **kwargs): super(CivMapScreen, self).__init__(**kwargs) self.pos = (0, 0) self.nations = [] self.ms = MapScatter(pos_hint={'x': 0, 'y': 0}) self.add_widget(self.ms) self.im = Image(source='civ_board.png', allow_stretch=True, keep_ratio=False, opacity=1.0) self.ms.add_widget(self.im) self.fl = FloatLayout(size=self.im.size, pos_hint={'x': 0, 'y': 0}) self.ms.bind(size=self.fl.setter('size')) self.im.add_widget(self.fl) self.bind(size=self.im.setter('size')) self.sm = ScreenManager(pos_hint={ 'x': 1660 / 4058.0, 'y': 3 / 2910.0 }, size_hint=((3367 - 1660) / 4058.0, (2907 - 2105) / 2910.0)) self.fl.add_widget(self.sm) self.st = StockPanel(pos_hint={'x': 0, 'y': 0}, size_hint=(1, 1)) self.st.ccc_btn.bind(on_press=self.change_screen) self.sm.bind(size=self.st.lab.setter('size')) screen = Screen(name="Stock and Treasury", pos_hint={ 'x': 0, 'y': 0 }, size_hint=(1, 1)) screen.add_widget(self.st) self.sm.add_widget(screen) self.ccc = CivCardCreditsPanel(pos_hint={ 'x': 0, 'y': 0 }, size_hint=(1, 1)) self.ccc.st_btn.bind(on_press=self.change_screen) screen = Screen(name='Civ Card Credits', pos_hint={ 'x': 0, 'y': 0 }, size_hint=(1, 1)) screen.add_widget(self.ccc) self.sm.add_widget(screen) def change_screen(self, instance, *args): if instance == self.st.ccc_btn: self.sm.current = 'Civ Card Credits' elif instance == self.ccc.st_btn: self.sm.current = 'Stock and Treasury' for obj in [self, self.sm, self.st, self.st.lab]: print(obj, obj.pos, obj.pos_hint, obj.size, obj.size_hint) def add_spotter(self, spotter): print(f'Adding {spotter} to {self.fl}') self.fl.add_widget(spotter)
class Main(App): # These are to handle Android switching away from the app or locking the screen. # I don't want to do anything on pause, but I do want to go back to the main screen if it was paused for a while. def on_pause(self): kivy.logger.Logger.debug('Main: pausing') self.pause_time = time.time() return True def on_resume(self): kivy.logger.Logger.debug('Main: resuming') if time.time() - self.pause_time > 5: self.screen_manager.current = 'chooser' # This disables kivy's settings panel, normally accessible by pressing F1 def open_settings(self, *args): pass ## "Navbar" button functions def pressed_win(self, *args): if 'btn_functions' in dir( self.screen_manager.current_screen ) and self.screen_manager.current_screen.btn_functions[0] != None: self.screen_manager.current_screen.btn_functions[0]() return False # I'm returning false here so that the button still triggers the next on_release event def pressed_home(self, *args): if 'btn_functions' in dir( self.screen_manager.current_screen ) and self.screen_manager.current_screen.btn_functions[1] != None: self.screen_manager.current_screen.btn_functions[1]() return False # I'm returning false here so that the button still triggers the next on_release event def pressed_back(self, *args): if 'btn_functions' in dir( self.screen_manager.current_screen ) and self.screen_manager.current_screen.btn_functions[2] != None: self.screen_manager.current_screen.btn_functions[2]() return False # I'm returning false here so that the button still triggers the next on_release event def update_buttons(self, *args): if 'btns' in dir(self.screen_manager.current_screen): win, home, back = self.screen_manager.current_screen.btns if win == None: self.win_btn.opacity = 0 else: self.win_btn.opacity = 1 self.win_btn.background_normal = win self.win_btn.background_down = self.win_btn.background_normal if home == None: self.home_btn.opacity = 0 else: self.home_btn.opacity = 1 self.home_btn.background_normal = home self.home_btn.background_down = self.home_btn.background_normal if back == None: self.back_btn.opacity = 0 else: self.back_btn.opacity = 1 self.back_btn.background_normal = back self.back_btn.background_down = self.back_btn.background_normal ## These functions should probably be put somewhat inside the painter widget. def finish_paint(self, good=True): self.paint_screen.image.label.text = '' if good: self.goto_screen('photostrip', 'right') def save_painter(self): savedir = self.paint_screen.image.source + '.overlays' try: filesystem.listdir(savedir) except: if not filesystem.is_dir(savedir): filesystem.mkdir(savedir) index = 0 filename = '%02d.png' try: dircontents = os.listdir(savedir) except: # Probably using the HTTP filesystem. tmpused = True filepath = tempfile.mktemp() + '.png' else: tmpused = False while filename % index in dircontents: index += 1 filepath = filesystem.join(savedir, filename % index) if self.paint_screen.painter.save_png(filepath) and ( not tmpused or filesystem.savefile(savedir, 'png', filepath) ): # If saving the file works and either the tmpfile was not used ot the second stage of saving works. self.paint_screen.painter.do_drawing = False # Stop drawing on the image until the canvas gets cleared (by switching screen) self.paint_screen.image.label.color = (0, 0.75, 0, 1) self.paint_screen.image.label.text = 'Saved' Clock.schedule_once(lambda arg: self.finish_paint(True), 2) else: self.paint_screen.image.label.color = (0.75, 0, 0, 1) self.paint_screen.image.label.text = 'FAILED to save the drawing, sorry.' Clock.schedule_once(lambda arg: self.finish_paint(False), 3) def goto_screen(self, screen_name, direction): self.screen_manager.transition.direction = direction self.screen_manager.current = screen_name # This function is just to keep the screen_manager size with window resizing def update_size(self, root, value): self.screen_manager.size = (root.width - 192, root.height) def build(self): root = FloatLayout() ## Screen Manager self.screen_manager = ScreenManager(transition=SlideTransition(), size_hint=[None, None], pos_hint={'left': 1}) root.bind(size=self.update_size) root.add_widget(self.screen_manager) ## "Navbar" Buttons ## These are meant to work like the Android navbar would, and I have named them as such. # These two functions are used for a indicator for the button being pressed. def rend_circle(btn): with btn.canvas: Color(1, 1, 1, 0.25) size = (btn.size[0], btn.size[1] * 1.5) btn.hl = Ellipse( pos=(btn.pos[0] + ((btn.size[0] / 2) - (size[0] / 2)), btn.pos[1] + ((btn.size[1] / 2) - (size[1] / 2))), size=size) Color(1, 1, 1, 1) # Reset the color, otherwise the background goes dark def derend_circle(btn): btn.canvas.remove(btn.hl) self.win_btn = Button(text='', size_hint=[None, None], size=[192, 192], height=192, pos_hint={ 'right': 1, 'center_y': 0.8 }) self.win_btn.bind(on_press=rend_circle) self.win_btn.bind(on_release=derend_circle) self.win_btn.bind(on_release=self.pressed_win) self.home_btn = Button(text='', size_hint=[None, None], size=[192, 192], pos_hint={ 'right': 1, 'center_y': 0.5 }) self.home_btn.bind(on_press=rend_circle) self.home_btn.bind(on_release=derend_circle) self.home_btn.bind(on_release=self.pressed_home) self.back_btn = Button(text='', size_hint=[None, None], size=[192, 192], pos_hint={ 'right': 1, 'center_y': 0.2 }) self.back_btn.bind(on_press=rend_circle) self.back_btn.bind(on_release=derend_circle) self.back_btn.bind(on_release=self.pressed_back) root.add_widget(self.win_btn) root.add_widget(self.home_btn) root.add_widget(self.back_btn) # Render the background def update_rect(instance, value): instance.bg.pos = instance.pos instance.bg.size = instance.size self.screen_manager.bind(size=update_rect, pos=update_rect) with self.screen_manager.canvas.before: self.screen_manager.bg = Rectangle(source='background.png') Loader.loading_image = 'loading.png' ## FileChooser chooser_screen = Screen(name='chooser') chooser = FileChooserGalleryView(rootpath=PHOTOS_PATH) chooser.file_system = filesystem def select_folder(chooser, photostrip): photostrip.set_path(chooser.current_entry.path) self.goto_screen('photostrip', 'left') chooser.bind( on_select_folder=lambda args: select_folder(chooser, photostrip)) chooser_screen.add_widget(chooser) ## Painter self.paint_screen = PaintScreen(name='painter') self.paint_screen.bind( on_leave=lambda src: self.paint_screen.painter.clear()) ## ImageViewer viewer_screen = ViewerScreen(name='viewer') viewer_screen.bind(on_enter=lambda src: setattr( self.paint_screen.image, 'source', viewer_screen.image.source)) ## Photo strip def foo(a): print type(a), a photostrip_screen = Screen(name='photostrip') photostrip = PhotoStrip() photostrip_screen.add_widget(photostrip) photostrip.bind( on_press=lambda src, fn: viewer_screen.set_image(fn), on_release=lambda src, fn: self.goto_screen('painter', 'left'), ) # Set up the icons and functions for the navbar buttons chooser_screen.btns = [None, 'ic_action_refresh.png', None] chooser_screen.btn_functions = [None, chooser._trigger_update, None] photostrip_screen.btns = [None, None, 'ic_sysbar_back.png'] photostrip_screen.btn_functions = [ None, None, lambda: self.goto_screen('chooser', 'right') ] viewer_screen.btns = ['ic_action_edit.png', None, 'ic_sysbar_back.png'] viewer_screen.btn_functions = [ lambda: self.goto_screen('painter', 'left'), viewer_screen.drawing_toggle, lambda: self.goto_screen('photostrip', 'right') ] self.paint_screen.btns = [ 'ic_action_save.png', None, 'ic_action_discard.png' ] self.paint_screen.btn_functions = [ self.save_painter, None, lambda: self.goto_screen('photostrip', 'right') ] # Finally, add the screens to the manager self.screen_manager.add_widget(chooser_screen) self.screen_manager.add_widget(photostrip_screen) self.screen_manager.add_widget(viewer_screen) self.screen_manager.add_widget(self.paint_screen) # Set the navbar buttons from the variables set above self.screen_manager.transition.bind(on_complete=self.update_buttons) self.update_buttons() return root