class PiApplication(object): def __init__(self, config): self.config = config # Clean directory where pictures are saved self.savedir = config.getpath('GENERAL', 'directory') if not osp.isdir(self.savedir): os.makedirs(self.savedir) if osp.isdir(self.savedir) and config.getboolean('GENERAL', 'clear_on_startup'): shutil.rmtree(self.savedir) os.makedirs(self.savedir) # Prepare GPIO, physical pins mode GPIO.setmode(GPIO.BOARD) # Prepare the pygame module for use os.environ['SDL_VIDEO_CENTERED'] = '1' pygame.init() # Dont catch mouse motion to avoid filling the queue during long actions pygame.event.set_blocked(pygame.MOUSEMOTION) # Create window of (width, height) self.window = PtbWindow('Pibooth', config.gettyped('WINDOW', 'size')) self.state_machine = StateMachine(self) self.state_machine.add_state(StateWait()) self.state_machine.add_state(StateChoose(30)) # 30s before going back to the start self.state_machine.add_state(StateChosen(4)) self.state_machine.add_state(StateCapture()) self.state_machine.add_state(StateProcessing()) self.state_machine.add_state(StatePrint()) self.state_machine.add_state(StateFinish(0.5)) if config.getboolean('GENERAL', 'failsafe'): self.state_machine.add_failsafe_state(StateFailSafe(2)) # Initialize the camera if camera.gp_camera_connected() and camera.rpi_camera_connected(): cam_class = camera.HybridCamera elif camera.gp_camera_connected(): cam_class = camera.GpCamera elif camera.rpi_camera_connected(): cam_class = camera.RpiCamera else: raise EnvironmentError("Neither PiCamera nor GPhoto2 camera detected") self.camera = cam_class(config.getint('CAMERA', 'iso'), config.gettyped('CAMERA', 'resolution'), config.getint('CAMERA', 'rotation'), config.getboolean('CAMERA', 'flip')) self.led_picture = PtbLed(config.getint('CONTROLS', 'picture_led_pin')) self.button_picture = PtbButton(config.getint('CONTROLS', 'picture_btn_pin'), config.getfloat('CONTROLS', 'debounce_delay')) self.led_print = PtbLed(config.getint('CONTROLS', 'print_led_pin')) self.button_print = PtbButton(config.getint('CONTROLS', 'print_btn_pin'), config.getfloat('CONTROLS', 'debounce_delay')) self.led_startup = PtbLed(config.getint('CONTROLS', 'startup_led_pin')) self.led_preview = PtbLed(config.getint('CONTROLS', 'preview_led_pin')) self.printer = PtbPrinter(config.get('PRINTER', 'printer_name')) # Variables shared between states self.dirname = None self.nbr_captures = None self.nbr_printed = 0 self.previous_picture = None self.previous_picture_file = None self.capture_choices = config.gettyped('PICTURE', 'captures') if isinstance(self.capture_choices, int): self.capture_choices = (self.capture_choices,) for chx in self.capture_choices: if chx not in [1, 2, 3, 4]: raise ValueError("Invalid captures number '{}'".format(chx)) def find_quit_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == pygame.QUIT or\ (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE): return event return None def find_fullscreen_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == pygame.KEYDOWN and\ event.key == pygame.K_f and pygame.key.get_mods() & pygame.KMOD_CTRL: return event return None def find_resize_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == pygame.VIDEORESIZE: return event return None def find_picture_event(self, events): """Return the first found event if found in the list. """ for event in events: if (event.type == pygame.KEYDOWN and event.key == pygame.K_p) or \ (event.type == BUTTON_DOWN and event.pin == self.button_picture): return event elif event.type == pygame.MOUSEBUTTONUP: rect = self.window.get_rect() if pygame.Rect(0, 0, rect.width // 2, rect.height).collidepoint(event.pos): return event return None def find_print_event(self, events): """Return the first found event if found in the list. """ for event in events: if (event.type == pygame.KEYDOWN and event.key == pygame.K_e and pygame.key.get_mods() & pygame.KMOD_CTRL) or \ (event.type == BUTTON_DOWN and event.pin == self.button_print): return event elif event.type == pygame.MOUSEBUTTONUP: rect = self.window.get_rect() if pygame.Rect(rect.width // 2, 0, rect.width // 2, rect.height).collidepoint(event.pos): return event return None def find_choice_event(self, events): """Return the first found event if found in the list. """ for event in events: if (event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT) or \ (event.type == BUTTON_DOWN and event.pin == self.button_picture): event.key = pygame.K_LEFT return event elif (event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT) or \ (event.type == BUTTON_DOWN and event.pin == self.button_print): event.key = pygame.K_RIGHT return event elif event.type == pygame.MOUSEBUTTONUP: rect = self.window.get_rect() if pygame.Rect(0, 0, rect.width // 2, rect.height).collidepoint(event.pos): event.key = pygame.K_LEFT else: event.key = pygame.K_RIGHT return event return None def main_loop(self): """Run the main game loop. """ try: self.led_startup.switch_on() self.state_machine.set_state('wait') clock = pygame.time.Clock() while True: events = list(reversed(pygame.event.get())) # Take all events, most recent first if self.find_quit_event(events): break if self.find_fullscreen_event(events): self.window.toggle_fullscreen() event = self.find_resize_event(events) if event: self.window.resize(event.size) self.state_machine.process(events) clock.tick(40) # Ensure the program will never run at more than x frames per second finally: self.led_startup.quit() self.led_preview.quit() self.led_picture.quit() self.led_print.quit() GPIO.cleanup() self.camera.quit() self.printer.quit() pygame.quit()
def __init__(self, config): self.config = config # Clean directory where pictures are saved self.savedir = config.getpath('GENERAL', 'directory') if not osp.isdir(self.savedir): os.makedirs(self.savedir) if osp.isdir(self.savedir) and config.getboolean('GENERAL', 'clear_on_startup'): shutil.rmtree(self.savedir) os.makedirs(self.savedir) # Prepare GPIO, physical pins mode GPIO.setmode(GPIO.BOARD) # Prepare the pygame module for use os.environ['SDL_VIDEO_CENTERED'] = '1' pygame.init() # Dont catch mouse motion to avoid filling the queue during long actions pygame.event.set_blocked(pygame.MOUSEMOTION) # Create window of (width, height) self.window = PtbWindow('Pibooth', config.gettyped('WINDOW', 'size')) self.state_machine = StateMachine(self) self.state_machine.add_state(StateWait()) self.state_machine.add_state(StateChoose(30)) # 30s before going back to the start self.state_machine.add_state(StateChosen(4)) self.state_machine.add_state(StateCapture()) self.state_machine.add_state(StateProcessing()) self.state_machine.add_state(StatePrint()) self.state_machine.add_state(StateFinish(0.5)) if config.getboolean('GENERAL', 'failsafe'): self.state_machine.add_failsafe_state(StateFailSafe(2)) # Initialize the camera if camera.gp_camera_connected() and camera.rpi_camera_connected(): cam_class = camera.HybridCamera elif camera.gp_camera_connected(): cam_class = camera.GpCamera elif camera.rpi_camera_connected(): cam_class = camera.RpiCamera else: raise EnvironmentError("Neither PiCamera nor GPhoto2 camera detected") self.camera = cam_class(config.getint('CAMERA', 'iso'), config.gettyped('CAMERA', 'resolution'), config.getint('CAMERA', 'rotation'), config.getboolean('CAMERA', 'flip')) self.led_picture = PtbLed(config.getint('CONTROLS', 'picture_led_pin')) self.button_picture = PtbButton(config.getint('CONTROLS', 'picture_btn_pin'), config.getfloat('CONTROLS', 'debounce_delay')) self.led_print = PtbLed(config.getint('CONTROLS', 'print_led_pin')) self.button_print = PtbButton(config.getint('CONTROLS', 'print_btn_pin'), config.getfloat('CONTROLS', 'debounce_delay')) self.led_startup = PtbLed(config.getint('CONTROLS', 'startup_led_pin')) self.led_preview = PtbLed(config.getint('CONTROLS', 'preview_led_pin')) self.printer = PtbPrinter(config.get('PRINTER', 'printer_name')) # Variables shared between states self.dirname = None self.nbr_captures = None self.nbr_printed = 0 self.previous_picture = None self.previous_picture_file = None self.capture_choices = config.gettyped('PICTURE', 'captures') if isinstance(self.capture_choices, int): self.capture_choices = (self.capture_choices,) for chx in self.capture_choices: if chx not in [1, 2, 3, 4]: raise ValueError("Invalid captures number '{}'".format(chx))
class PiApplication(object): def __init__(self, config): self._config = config # Clean directory where pictures are saved savedir = config.getpath('GENERAL', 'directory') if not osp.isdir(savedir): os.makedirs(savedir) elif osp.isdir(savedir) and config.getboolean('GENERAL', 'debug'): shutil.rmtree(savedir) os.makedirs(savedir) # Prepare the pygame module for use os.environ['SDL_VIDEO_CENTERED'] = '1' pygame.init() # Dont catch mouse motion to avoid filling the queue during long actions pygame.event.set_blocked(pygame.MOUSEMOTION) # Create window of (width, height) init_size = self._config.gettyped('WINDOW', 'size') init_debug = self._config.getboolean('GENERAL', 'debug') init_color = self._config.gettyped('WINDOW', 'background') init_text_color = self._config.gettyped('WINDOW', 'text_color') if not isinstance(init_color, (tuple, list)): init_color = self._config.getpath('WINDOW', 'background') title = 'Pibooth v{}'.format(pibooth.__version__) if not isinstance(init_size, str): self._window = PtbWindow(title, init_size, color=init_color, text_color=init_text_color, debug=init_debug) else: self._window = PtbWindow(title, color=init_color, text_color=init_text_color, debug=init_debug) # Create plugin manager and defined hooks specification self._plugin_manager = pluggy.PluginManager( hookspecs.hookspec.project_name) self._plugin_manager.add_hookspecs(hookspecs) self._plugin_manager.load_setuptools_entrypoints( hookspecs.hookspec.project_name) # Register plugins custom_paths = [ p for p in self._config.gettuple('GENERAL', 'plugins', 'path') if p ] load_plugins(self._plugin_manager, *custom_paths) # Define states of the application self._machine = StateMachine(self._plugin_manager, self._config, self, self._window) self._machine.add_state('wait') self._machine.add_state('choose') self._machine.add_state('chosen') self._machine.add_state('preview') self._machine.add_state('capture') self._machine.add_state('processing') self._machine.add_state('print') self._machine.add_state('finish') # --------------------------------------------------------------------- # Variables shared with plugins # Change them may break plugins compatibility self.dirname = None self.capture_nbr = None self.capture_choices = (4, 1) self.nbr_duplicates = 0 self.previous_picture = None self.previous_animated = None self.previous_picture_file = None self.camera = camera.get_camera( config.getint('CAMERA', 'iso'), config.gettyped('CAMERA', 'resolution'), config.getint('CAMERA', 'rotation'), config.getboolean('CAMERA', 'flip'), config.getboolean('CAMERA', 'delete_internal_memory')) self.button_capture = Button( "BOARD" + config.get('CONTROLS', 'picture_btn_pin'), bounce_time=config.getfloat('CONTROLS', 'debounce_delay'), pull_up=True, hold_time=1) self.button_print = Button( "BOARD" + config.get('CONTROLS', 'print_btn_pin'), bounce_time=config.getfloat('CONTROLS', 'debounce_delay'), pull_up=True, hold_time=1) self.leds = LEDBoard( capture="BOARD" + config.get('CONTROLS', 'picture_led_pin'), printer="BOARD" + config.get('CONTROLS', 'print_led_pin'), preview="BOARD" + config.get('CONTROLS', 'preview_led_pin'), start="BOARD" + config.get('CONTROLS', 'startup_led_pin')) self.printer = PtbPrinter(config.get('PRINTER', 'printer_name')) # --------------------------------------------------------------------- def _initialize(self): """Restore the application with initial parameters defined in the configuration file. Only parameters that can be changed at runtime are restored. """ # Handle the language configuration language.CURRENT = self._config.get('GENERAL', 'language') fonts.CURRENT = fonts.get_filename( self._config.gettuple('PICTURE', 'text_fonts', str)[0]) # Set the captures choices choices = self._config.gettuple('PICTURE', 'captures', int) for chx in choices: if chx not in [1, 2, 3, 4]: LOGGER.warning( "Invalid captures number '%s' in config, fallback to '%s'", chx, self.capture_choices) choices = self.capture_choices break self.capture_choices = choices # Handle autostart of the application self._config.enable_autostart( self._config.getboolean('GENERAL', 'autostart')) self._window.arrow_location = self._config.get('WINDOW', 'arrows') self._window.arrow_offset = self._config.getint( 'WINDOW', 'arrows_x_offset') self._window.text_color = self._config.gettyped('WINDOW', 'text_color') self._window.drop_cache() # Handle window size size = self._config.gettyped('WINDOW', 'size') if isinstance(size, str) and size.lower() == 'fullscreen': if not self._window.is_fullscreen: self._window.toggle_fullscreen() else: if self._window.is_fullscreen: self._window.toggle_fullscreen() self._window.debug = self._config.getboolean('GENERAL', 'debug') # Handle debug mode if not self._config.getboolean('GENERAL', 'debug'): set_logging_level() # Restore default level self._machine.add_failsafe_state('failsafe') else: set_logging_level(logging.DEBUG) self._machine.remove_state('failsafe') # Initialize state machine self._machine.set_state('wait') @property def printer_unavailable(self): """Return True is paper/ink counter is reached or printing is disabled """ if self._config.getint('PRINTER', 'max_pages') < 0: # No limit return False return self.printer.nbr_printed >= self._config.getint( 'PRINTER', 'max_pages') def find_quit_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == pygame.QUIT: return event return None def find_settings_event(self, events, type_filter=None): """Return the first found event if found in the list. """ for event in events: if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE and \ (type_filter is None or type_filter == event.type): return event if (type_filter is None or type_filter == BUTTON_DOWN) and\ self.button_capture.is_pressed and self.button_print.is_pressed: return event return None def find_fullscreen_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == pygame.KEYDOWN and \ event.key == pygame.K_f and pygame.key.get_mods() & pygame.KMOD_CTRL: return event return None def find_resize_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == pygame.VIDEORESIZE: return event return None def find_capture_event(self, events, type_filter=None): """Return the first found event if found in the list. """ for event in events: if (event.type == pygame.KEYDOWN and event.key == pygame.K_p) and \ (type_filter is None or type_filter == event.type): return event elif (type_filter is None or type_filter == BUTTON_DOWN) and\ self.button_capture.is_pressed: return event elif event.type == pygame.MOUSEBUTTONUP: rect = self._window.get_rect() if pygame.Rect(0, 0, rect.width // 2, rect.height).collidepoint(event.pos): if type_filter is None or type_filter == event.type: return event return None def find_print_event(self, events, type_filter=None): """Return the first found event if found in the list. """ for event in events: if (event.type == pygame.KEYDOWN and event.key == pygame.K_e and pygame.key.get_mods() & pygame.KMOD_CTRL) and \ (type_filter is None or type_filter == event.type): return event elif (type_filter is None or type_filter == BUTTON_DOWN) and\ self.button_print.is_pressed: return event elif event.type == pygame.MOUSEBUTTONUP: rect = self._window.get_rect() if pygame.Rect(rect.width // 2, 0, rect.width // 2, rect.height).collidepoint(event.pos): if type_filter is None or type_filter == event.type: return event return None def find_print_status_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == PRINTER_TASKS_UPDATED: return event return None def find_choice_event(self, events): """Return the first found event if found in the list. """ for event in events: if (event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT) or \ self.button_capture.is_pressed: event.key = pygame.K_LEFT return event elif (event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT) or \ self.button_print.is_pressed: event.key = pygame.K_RIGHT return event elif event.type == pygame.MOUSEBUTTONUP: rect = self._window.get_rect() if pygame.Rect(0, 0, rect.width // 2, rect.height).collidepoint(event.pos): event.key = pygame.K_LEFT else: event.key = pygame.K_RIGHT return event return None def main_loop(self): """Run the main game loop. """ try: clock = pygame.time.Clock() self._initialize() self._plugin_manager.hook.pibooth_startup(app=self) menu = None fps = 40 while True: events = list(pygame.event.get()) if self.find_quit_event(events): break if self.find_fullscreen_event(events): self._window.toggle_fullscreen() event = self.find_resize_event(events) if event: self._window.resize(event.size) if not menu and self.find_settings_event(events): menu = PiConfigMenu(self._window, self._config, fps, version=pibooth.__version__) menu.show() if menu and menu.is_shown(): # Convert HW button events to keyboard events for menu if self.find_settings_event(events, BUTTON_DOWN): events.insert(0, menu.create_back_event()) if self.find_capture_event(events, BUTTON_DOWN): events.insert(0, menu.create_next_event()) elif self.find_print_event(events, BUTTON_DOWN): events.insert(0, menu.create_click_event()) menu.process(events) elif menu and not menu.is_shown(): self._initialize() menu = None else: self._machine.process(events) pygame.display.update() clock.tick( fps ) # Ensure the program will never run at more than <fps> frames per second finally: self._plugin_manager.hook.pibooth_cleanup(app=self) pygame.quit()
def __init__(self, config): self._config = config # Clean directory where pictures are saved savedir = config.getpath('GENERAL', 'directory') if not osp.isdir(savedir): os.makedirs(savedir) elif osp.isdir(savedir) and config.getboolean('GENERAL', 'debug'): shutil.rmtree(savedir) os.makedirs(savedir) # Prepare the pygame module for use os.environ['SDL_VIDEO_CENTERED'] = '1' pygame.init() # Dont catch mouse motion to avoid filling the queue during long actions pygame.event.set_blocked(pygame.MOUSEMOTION) # Create window of (width, height) init_size = self._config.gettyped('WINDOW', 'size') init_debug = self._config.getboolean('GENERAL', 'debug') init_color = self._config.gettyped('WINDOW', 'background') init_text_color = self._config.gettyped('WINDOW', 'text_color') if not isinstance(init_color, (tuple, list)): init_color = self._config.getpath('WINDOW', 'background') title = 'Pibooth v{}'.format(pibooth.__version__) if not isinstance(init_size, str): self._window = PtbWindow(title, init_size, color=init_color, text_color=init_text_color, debug=init_debug) else: self._window = PtbWindow(title, color=init_color, text_color=init_text_color, debug=init_debug) # Create plugin manager and defined hooks specification self._plugin_manager = pluggy.PluginManager( hookspecs.hookspec.project_name) self._plugin_manager.add_hookspecs(hookspecs) self._plugin_manager.load_setuptools_entrypoints( hookspecs.hookspec.project_name) # Register plugins custom_paths = [ p for p in self._config.gettuple('GENERAL', 'plugins', 'path') if p ] load_plugins(self._plugin_manager, *custom_paths) # Define states of the application self._machine = StateMachine(self._plugin_manager, self._config, self, self._window) self._machine.add_state('wait') self._machine.add_state('choose') self._machine.add_state('chosen') self._machine.add_state('preview') self._machine.add_state('capture') self._machine.add_state('processing') self._machine.add_state('print') self._machine.add_state('finish') # --------------------------------------------------------------------- # Variables shared with plugins # Change them may break plugins compatibility self.dirname = None self.capture_nbr = None self.capture_choices = (4, 1) self.nbr_duplicates = 0 self.previous_picture = None self.previous_animated = None self.previous_picture_file = None self.camera = camera.get_camera( config.getint('CAMERA', 'iso'), config.gettyped('CAMERA', 'resolution'), config.getint('CAMERA', 'rotation'), config.getboolean('CAMERA', 'flip'), config.getboolean('CAMERA', 'delete_internal_memory')) self.button_capture = Button( "BOARD" + config.get('CONTROLS', 'picture_btn_pin'), bounce_time=config.getfloat('CONTROLS', 'debounce_delay'), pull_up=True, hold_time=1) self.button_print = Button( "BOARD" + config.get('CONTROLS', 'print_btn_pin'), bounce_time=config.getfloat('CONTROLS', 'debounce_delay'), pull_up=True, hold_time=1) self.leds = LEDBoard( capture="BOARD" + config.get('CONTROLS', 'picture_led_pin'), printer="BOARD" + config.get('CONTROLS', 'print_led_pin'), preview="BOARD" + config.get('CONTROLS', 'preview_led_pin'), start="BOARD" + config.get('CONTROLS', 'startup_led_pin')) self.printer = PtbPrinter(config.get('PRINTER', 'printer_name'))
class PiApplication(object): def __init__(self, config, plugin_manager): self._pm = plugin_manager self._config = config # Create directories where pictures are saved for savedir in config.gettuple('GENERAL', 'directory', 'path'): if osp.isdir(savedir) and config.getboolean('GENERAL', 'debug'): shutil.rmtree(savedir) if not osp.isdir(savedir): os.makedirs(savedir) # Prepare the pygame module for use os.environ['SDL_VIDEO_CENTERED'] = '1' pygame.init() # Create window of (width, height) init_size = self._config.gettyped('WINDOW', 'size') init_debug = self._config.getboolean('GENERAL', 'debug') init_color = self._config.gettyped('WINDOW', 'background') init_text_color = self._config.gettyped('WINDOW', 'text_color') if not isinstance(init_color, (tuple, list)): init_color = self._config.getpath('WINDOW', 'background') title = 'Pibooth v{}'.format(pibooth.__version__) if not isinstance(init_size, str): self._window = PtbWindow(title, init_size, color=init_color, text_color=init_text_color, debug=init_debug) else: self._window = PtbWindow(title, color=init_color, text_color=init_text_color, debug=init_debug) self._menu = None self._multipress_timer = PoolingTimer( config.getfloat('CONTROLS', 'multi_press_delay'), False) # Define states of the application self._machine = StateMachine(self._pm, self._config, self, self._window) self._machine.add_state('wait') self._machine.add_state('choose') self._machine.add_state('chosen') self._machine.add_state('preview') self._machine.add_state('capture') self._machine.add_state('processing') self._machine.add_state('filter') self._machine.add_state('print') self._machine.add_state('finish') # --------------------------------------------------------------------- # Variables shared with plugins # Change them may break plugins compatibility self.capture_nbr = None self.capture_date = None self.capture_choices = (4, 1) self.previous_picture = None self.previous_animated = None self.previous_picture_file = None self.count = Counters(self._config.join_path("counters.pickle"), taken=0, printed=0, forgotten=0, remaining_duplicates=self._config.getint( 'PRINTER', 'max_duplicates')) self.camera = camera.get_camera( config.getint('CAMERA', 'iso'), config.gettyped('CAMERA', 'resolution'), config.getint('CAMERA', 'rotation'), config.getboolean('CAMERA', 'flip'), config.getboolean('CAMERA', 'delete_internal_memory')) self.buttons = ButtonBoard( capture="BOARD" + config.get('CONTROLS', 'picture_btn_pin'), printer="BOARD" + config.get('CONTROLS', 'print_btn_pin'), hold_time=config.getfloat('CONTROLS', 'debounce_delay'), pull_up=True) self.buttons.capture.when_held = self._on_button_capture_held self.buttons.printer.when_held = self._on_button_printer_held self.leds = LEDBoard( capture="BOARD" + config.get('CONTROLS', 'picture_led_pin'), printer="BOARD" + config.get('CONTROLS', 'print_led_pin')) self.printer = Printer(config.get('PRINTER', 'printer_name'), config.getint('PRINTER', 'max_pages'), self.count) # --------------------------------------------------------------------- def _initialize(self): """Restore the application with initial parameters defined in the configuration file. Only parameters that can be changed at runtime are restored. """ # Handle the language configuration language.CURRENT = self._config.get('GENERAL', 'language') fonts.CURRENT = fonts.get_filename( self._config.gettuple('PICTURE', 'text_fonts', str)[0]) # Set the captures choices choices = self._config.gettuple('PICTURE', 'captures', int) for chx in choices: if chx not in [1, 2, 3, 4]: LOGGER.warning( "Invalid captures number '%s' in config, fallback to '%s'", chx, self.capture_choices) choices = self.capture_choices break self.capture_choices = choices # Handle autostart of the application self._config.handle_autostart() self._window.arrow_location = self._config.get('WINDOW', 'arrows') self._window.arrow_offset = self._config.getint( 'WINDOW', 'arrows_x_offset') self._window.text_color = self._config.gettyped('WINDOW', 'text_color') self._window.drop_cache() # Handle window size size = self._config.gettyped('WINDOW', 'size') if isinstance(size, str) and size.lower() == 'fullscreen': if not self._window.is_fullscreen: self._window.toggle_fullscreen() else: if self._window.is_fullscreen: self._window.toggle_fullscreen() self._window.debug = self._config.getboolean('GENERAL', 'debug') # Handle debug mode if not self._config.getboolean('GENERAL', 'debug'): set_logging_level() # Restore default level self._machine.add_failsafe_state('failsafe') else: set_logging_level(logging.DEBUG) self._machine.remove_state('failsafe') # Reset the print counter (in case of max_pages is reached) self.printer.max_pages = self._config.getint('PRINTER', 'max_pages') def _on_button_capture_held(self): """Called when the capture button is pressed. """ if all(self.buttons.value): self.buttons.capture.hold_repeat = True if self._multipress_timer.elapsed() == 0: self._multipress_timer.start() if self._multipress_timer.is_timeout(): # Capture was held while printer was pressed if self._menu and self._menu.is_shown(): # Convert HW button events to keyboard events for menu event = self._menu.create_back_event() LOGGER.debug("BUTTONDOWN: generate MENU-ESC event") else: event = pygame.event.Event(BUTTONDOWN, capture=1, printer=1, button=self.buttons) LOGGER.debug("BUTTONDOWN: generate DOUBLE buttons event") self.buttons.capture.hold_repeat = False self._multipress_timer.reset() pygame.event.post(event) else: # Capture was held but printer not pressed if self._menu and self._menu.is_shown(): # Convert HW button events to keyboard events for menu event = self._menu.create_next_event() LOGGER.debug("BUTTONDOWN: generate MENU-NEXT event") else: event = pygame.event.Event(BUTTONDOWN, capture=1, printer=0, button=self.buttons.capture) LOGGER.debug("BUTTONDOWN: generate CAPTURE button event") self.buttons.capture.hold_repeat = False self._multipress_timer.reset() pygame.event.post(event) def _on_button_printer_held(self): """Called when the printer button is pressed. """ if all(self.buttons.value): # Printer was held while capture was pressed # but don't do anything here, let capture_held handle it instead pass else: # Printer was held but capture not pressed if self._menu and self._menu.is_shown(): # Convert HW button events to keyboard events for menu event = self._menu.create_click_event() LOGGER.debug("BUTTONDOWN: generate MENU-APPLY event") else: event = pygame.event.Event(BUTTONDOWN, capture=0, printer=1, button=self.buttons.printer) LOGGER.debug("BUTTONDOWN: generate PRINTER event") pygame.event.post(event) @property def picture_filename(self): """Return the final picture file name. """ if not self.capture_date: raise EnvironmentError( "The 'capture_date' attribute is not set yet") return "{}_pibooth.jpg".format(self.capture_date) def find_quit_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == pygame.QUIT: return event return None def find_settings_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: return event if event.type == BUTTONDOWN and event.capture and event.printer: return event return None def find_fullscreen_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == pygame.KEYDOWN and \ event.key == pygame.K_f and pygame.key.get_mods() & pygame.KMOD_CTRL: return event return None def find_resize_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == pygame.VIDEORESIZE: return event return None def find_capture_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == pygame.KEYDOWN and event.key == pygame.K_p: return event if event.type == pygame.MOUSEBUTTONUP and event.button in (1, 2, 3): # Don't consider the mouse wheel (button 4 & 5): rect = self._window.get_rect() if pygame.Rect(0, 0, rect.width // 2, rect.height).collidepoint(event.pos): return event if event.type == BUTTONDOWN and event.capture: return event return None def find_print_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == pygame.KEYDOWN and event.key == pygame.K_e\ and pygame.key.get_mods() & pygame.KMOD_CTRL: return event if event.type == pygame.MOUSEBUTTONUP and event.button in (1, 2, 3): # Don't consider the mouse wheel (button 4 & 5): rect = self._window.get_rect() if pygame.Rect(rect.width // 2, 0, rect.width // 2, rect.height).collidepoint(event.pos): return event if event.type == BUTTONDOWN and event.printer: return event return None def find_print_status_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == PRINTER_TASKS_UPDATED: return event return None def find_choice_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT: return event if event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT: return event if event.type == pygame.MOUSEBUTTONUP and event.button in (1, 2, 3): # Don't consider the mouse wheel (button 4 & 5): print("MOUSEBUTTONUP") rect = self._window.get_rect() x, y = event.pos if pygame.Rect(0, 0, rect.width // 2, rect.height).collidepoint(event.pos): event.key = pygame.K_LEFT else: event.key = pygame.K_RIGHT return event if event.type == BUTTONDOWN: if event.capture: event.key = pygame.K_LEFT else: event.key = pygame.K_RIGHT return event return None def main_loop(self): """Run the main game loop. """ try: fps = 40 clock = pygame.time.Clock() self._initialize() self._pm.hook.pibooth_startup(cfg=self._config, app=self) self._machine.set_state('wait') while True: events = list(pygame.event.get()) if self.find_quit_event(events): break if self.find_fullscreen_event(events): self._window.toggle_fullscreen() event = self.find_resize_event(events) if event: self._window.resize(event.size) if not self._menu and self.find_settings_event(events): self.camera.stop_preview() self.leds.off() self._menu = PiConfigMenu(self._window, self._config, self.count) self._menu.show() self.leds.blink(on_time=0.1, off_time=1) elif self._menu and self._menu.is_shown(): self._menu.process(events) elif self._menu and not self._menu.is_shown(): self.leds.off() self._initialize() self._machine.set_state('wait') self._menu = None else: self._machine.process(events) pygame.display.update() clock.tick( fps ) # Ensure the program will never run at more than <fps> frames per second except Exception as ex: LOGGER.error(str(ex), exc_info=True) LOGGER.error(get_crash_message()) finally: self._pm.hook.pibooth_cleanup(app=self) pygame.quit()
def __init__(self, config, plugin_manager): self._pm = plugin_manager self._config = config # Create directories where pictures are saved for savedir in config.gettuple('GENERAL', 'directory', 'path'): if osp.isdir(savedir) and config.getboolean('GENERAL', 'debug'): shutil.rmtree(savedir) if not osp.isdir(savedir): os.makedirs(savedir) # Prepare the pygame module for use os.environ['SDL_VIDEO_CENTERED'] = '1' pygame.init() # Create window of (width, height) init_size = self._config.gettyped('WINDOW', 'size') init_debug = self._config.getboolean('GENERAL', 'debug') init_color = self._config.gettyped('WINDOW', 'background') init_text_color = self._config.gettyped('WINDOW', 'text_color') if not isinstance(init_color, (tuple, list)): init_color = self._config.getpath('WINDOW', 'background') title = 'Pibooth v{}'.format(pibooth.__version__) if not isinstance(init_size, str): self._window = PtbWindow(title, init_size, color=init_color, text_color=init_text_color, debug=init_debug) else: self._window = PtbWindow(title, color=init_color, text_color=init_text_color, debug=init_debug) self._menu = None self._multipress_timer = PoolingTimer( config.getfloat('CONTROLS', 'multi_press_delay'), False) # Define states of the application self._machine = StateMachine(self._pm, self._config, self, self._window) self._machine.add_state('wait') self._machine.add_state('choose') self._machine.add_state('chosen') self._machine.add_state('preview') self._machine.add_state('capture') self._machine.add_state('processing') self._machine.add_state('filter') self._machine.add_state('print') self._machine.add_state('finish') # --------------------------------------------------------------------- # Variables shared with plugins # Change them may break plugins compatibility self.capture_nbr = None self.capture_date = None self.capture_choices = (4, 1) self.previous_picture = None self.previous_animated = None self.previous_picture_file = None self.count = Counters(self._config.join_path("counters.pickle"), taken=0, printed=0, forgotten=0, remaining_duplicates=self._config.getint( 'PRINTER', 'max_duplicates')) self.camera = camera.get_camera( config.getint('CAMERA', 'iso'), config.gettyped('CAMERA', 'resolution'), config.getint('CAMERA', 'rotation'), config.getboolean('CAMERA', 'flip'), config.getboolean('CAMERA', 'delete_internal_memory')) self.buttons = ButtonBoard( capture="BOARD" + config.get('CONTROLS', 'picture_btn_pin'), printer="BOARD" + config.get('CONTROLS', 'print_btn_pin'), hold_time=config.getfloat('CONTROLS', 'debounce_delay'), pull_up=True) self.buttons.capture.when_held = self._on_button_capture_held self.buttons.printer.when_held = self._on_button_printer_held self.leds = LEDBoard( capture="BOARD" + config.get('CONTROLS', 'picture_led_pin'), printer="BOARD" + config.get('CONTROLS', 'print_led_pin')) self.printer = Printer(config.get('PRINTER', 'printer_name'), config.getint('PRINTER', 'max_pages'), self.count)
def __init__(self, config): self.config = config # Clean directory where pictures are saved self.savedir = config.getpath('GENERAL', 'directory') if not osp.isdir(self.savedir): os.makedirs(self.savedir) if osp.isdir(self.savedir) and config.getboolean( 'GENERAL', 'clear_on_startup'): shutil.rmtree(self.savedir) os.makedirs(self.savedir) # Prepare GPIO, physical pins mode GPIO.setmode(GPIO.BOARD) # Prepare the pygame module for use os.environ['SDL_VIDEO_CENTERED'] = '1' pygame.init() # Dont catch mouse motion to avoid filling the queue during long actions pygame.event.set_blocked(pygame.MOUSEMOTION) # Create window of (width, height) init_size = self.config.gettyped('WINDOW', 'size') init_debug = self.config.getboolean('GENERAL', 'debug') init_color = self.config.gettyped('WINDOW', 'background') init_text_color = self.config.gettyped('WINDOW', 'text_color') if not isinstance(init_color, (tuple, list)): init_color = self.config.getpath('WINDOW', 'background') if not isinstance(init_size, str): self.window = PtbWindow('Pibooth', init_size, color=init_color, text_color=init_text_color, debug=init_debug) else: self.window = PtbWindow('Pibooth', color=init_color, text_color=init_text_color, debug=init_debug) self.state_machine = StateMachine(self) self.state_machine.add_state(StateWait()) self.state_machine.add_state( StateChoose(30)) # 30s before going back to the start self.state_machine.add_state(StateChosen(4)) self.state_machine.add_state(StateCapture()) self.state_machine.add_state(StateProcessing()) self.state_machine.add_state(StatePrint()) self.state_machine.add_state(StateFinish(0.5)) self.camera = camera.get_camera( config.getint('CAMERA', 'iso'), config.gettyped('CAMERA', 'resolution'), config.getint('CAMERA', 'rotation'), config.getboolean('CAMERA', 'flip'), config.getboolean('CAMERA', 'delete_internal_memory')) # Initialize the hardware buttons self.led_capture = PtbLed(config.getint('CONTROLS', 'picture_led_pin')) self.button_capture = PtbButton( config.getint('CONTROLS', 'picture_btn_pin'), config.getfloat('CONTROLS', 'debounce_delay')) self.led_print = PtbLed(config.getint('CONTROLS', 'print_led_pin')) self.button_print = PtbButton( config.getint('CONTROLS', 'print_btn_pin'), config.getfloat('CONTROLS', 'debounce_delay')) self.led_startup = PtbLed(config.getint('CONTROLS', 'startup_led_pin')) self.led_preview = PtbLed(config.getint('CONTROLS', 'preview_led_pin')) # Initialize the printer self.printer = PtbPrinter(config.get('PRINTER', 'printer_name')) # Variables shared between states self.dirname = None self.makers_pool = PicturesMakersPool() self.capture_nbr = None self.capture_choices = (4, 1) self.nbr_duplicates = 0 self.previous_picture = None self.previous_animated = [] self.previous_picture_file = None
class PiApplication(object): def __init__(self, config): self.config = config # Clean directory where pictures are saved self.savedir = config.getpath('GENERAL', 'directory') if not osp.isdir(self.savedir): os.makedirs(self.savedir) if osp.isdir(self.savedir) and config.getboolean( 'GENERAL', 'clear_on_startup'): shutil.rmtree(self.savedir) os.makedirs(self.savedir) # Prepare GPIO, physical pins mode GPIO.setmode(GPIO.BOARD) # Prepare the pygame module for use os.environ['SDL_VIDEO_CENTERED'] = '1' pygame.init() # Dont catch mouse motion to avoid filling the queue during long actions pygame.event.set_blocked(pygame.MOUSEMOTION) # Create window of (width, height) init_size = self.config.gettyped('WINDOW', 'size') init_debug = self.config.getboolean('GENERAL', 'debug') init_color = self.config.gettyped('WINDOW', 'background') init_text_color = self.config.gettyped('WINDOW', 'text_color') if not isinstance(init_color, (tuple, list)): init_color = self.config.getpath('WINDOW', 'background') if not isinstance(init_size, str): self.window = PtbWindow('Pibooth', init_size, color=init_color, text_color=init_text_color, debug=init_debug) else: self.window = PtbWindow('Pibooth', color=init_color, text_color=init_text_color, debug=init_debug) self.state_machine = StateMachine(self) self.state_machine.add_state(StateWait()) self.state_machine.add_state( StateChoose(30)) # 30s before going back to the start self.state_machine.add_state(StateChosen(4)) self.state_machine.add_state(StateCapture()) self.state_machine.add_state(StateProcessing()) self.state_machine.add_state(StatePrint()) self.state_machine.add_state(StateFinish(0.5)) self.camera = camera.get_camera( config.getint('CAMERA', 'iso'), config.gettyped('CAMERA', 'resolution'), config.getint('CAMERA', 'rotation'), config.getboolean('CAMERA', 'flip'), config.getboolean('CAMERA', 'delete_internal_memory')) # Initialize the hardware buttons self.led_capture = PtbLed(config.getint('CONTROLS', 'picture_led_pin')) self.button_capture = PtbButton( config.getint('CONTROLS', 'picture_btn_pin'), config.getfloat('CONTROLS', 'debounce_delay')) self.led_print = PtbLed(config.getint('CONTROLS', 'print_led_pin')) self.button_print = PtbButton( config.getint('CONTROLS', 'print_btn_pin'), config.getfloat('CONTROLS', 'debounce_delay')) self.led_startup = PtbLed(config.getint('CONTROLS', 'startup_led_pin')) self.led_preview = PtbLed(config.getint('CONTROLS', 'preview_led_pin')) # Initialize the printer self.printer = PtbPrinter(config.get('PRINTER', 'printer_name')) # Variables shared between states self.dirname = None self.makers_pool = PicturesMakersPool() self.capture_nbr = None self.capture_choices = (4, 1) self.nbr_duplicates = 0 self.previous_picture = None self.previous_animated = [] self.previous_picture_file = None def initialize(self): """Restore the application with initial parameters defined in the configuration file. Only parameters that can be changed at runtime are restored. """ # Handle the language configuration language.CURRENT = self.config.get('GENERAL', 'language') fonts.CURRENT = fonts.get_filename( self.config.gettuple('PICTURE', 'text_fonts', str)[0]) # Set the captures choices choices = self.config.gettuple('PICTURE', 'captures', int) for chx in choices: if chx not in [1, 2, 3, 4]: LOGGER.warning( "Invalid captures number '%s' in config, fallback to '%s'", chx, self.capture_choices) choices = self.capture_choices break self.capture_choices = choices # Reset printed pages number self.printer.nbr_printed = 0 # Handle autostart of the application self.config.enable_autostart( self.config.getboolean('GENERAL', 'autostart')) self.window.arrow_location = self.config.get('WINDOW', 'arrows') self.window.arrow_offset = self.config.getint('WINDOW', 'arrows_x_offset') self.window.drop_cache() # Handle window size size = self.config.gettyped('WINDOW', 'size') if isinstance(size, str) and size.lower() == 'fullscreen': if not self.window.is_fullscreen: self.window.toggle_fullscreen() else: if self.window.is_fullscreen: self.window.toggle_fullscreen() self.window.debug = self.config.getboolean('GENERAL', 'debug') # Handle debug mode if not self.config.getboolean('GENERAL', 'debug'): set_logging_level() # Restore default level self.state_machine.add_failsafe_state(StateFailSafe(2)) else: set_logging_level(logging.DEBUG) self.state_machine.remove_state('failsafe') # Initialize state machine self.state_machine.set_state('wait') @property def printer_unavailable(self): """Return True is paper/ink counter is reached or printing is disabled """ if self.config.getint('PRINTER', 'max_pages') < 0: # No limit return False return self.printer.nbr_printed >= self.config.getint( 'PRINTER', 'max_pages') def find_quit_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == pygame.QUIT: return event return None def find_settings_event(self, events, type_filter=None): """Return the first found event if found in the list. """ event_capture = None event_print = None for event in events: if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE and \ (type_filter is None or type_filter == event.type): return event if event.type == BUTTON_DOWN: if event.pin == self.button_capture and ( type_filter is None or type_filter == event.type): event_capture = event elif event.pin == self.button_print and ( type_filter is None or type_filter == event.type): event_print = event if event_capture and event_print: return event_capture # One of both (return != None is enough) return None def find_fullscreen_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == pygame.KEYDOWN and \ event.key == pygame.K_f and pygame.key.get_mods() & pygame.KMOD_CTRL: return event return None def find_resize_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == pygame.VIDEORESIZE: return event return None def find_capture_event(self, events, type_filter=None): """Return the first found event if found in the list. """ for event in events: if (event.type == pygame.KEYDOWN and event.key == pygame.K_p) or \ (event.type == BUTTON_DOWN and event.pin == self.button_capture): if type_filter is None or type_filter == event.type: return event elif event.type == pygame.MOUSEBUTTONUP: rect = self.window.get_rect() if pygame.Rect(0, 0, rect.width // 2, rect.height).collidepoint(event.pos): if type_filter is None or type_filter == event.type: return event return None def find_print_event(self, events, type_filter=None): """Return the first found event if found in the list. """ for event in events: if (event.type == pygame.KEYDOWN and event.key == pygame.K_e and pygame.key.get_mods() & pygame.KMOD_CTRL) or \ (event.type == BUTTON_DOWN and event.pin == self.button_print): if type_filter is None or type_filter == event.type: return event elif event.type == pygame.MOUSEBUTTONUP: rect = self.window.get_rect() if pygame.Rect(rect.width // 2, 0, rect.width // 2, rect.height).collidepoint(event.pos): if type_filter is None or type_filter == event.type: return event return None def find_print_status_event(self, events): """Return the first found event if found in the list. """ for event in events: if event.type == PRINTER_TASKS_UPDATED: return event return None def find_choice_event(self, events): """Return the first found event if found in the list. """ for event in events: if (event.type == pygame.KEYDOWN and event.key == pygame.K_LEFT) or \ (event.type == BUTTON_DOWN and event.pin == self.button_capture): event.key = pygame.K_LEFT return event elif (event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT) or \ (event.type == BUTTON_DOWN and event.pin == self.button_print): event.key = pygame.K_RIGHT return event elif event.type == pygame.MOUSEBUTTONUP: rect = self.window.get_rect() if pygame.Rect(0, 0, rect.width // 2, rect.height).collidepoint(event.pos): event.key = pygame.K_LEFT else: event.key = pygame.K_RIGHT return event return None def main_loop(self): """Run the main game loop. """ try: clock = pygame.time.Clock() self.led_startup.switch_on() self.initialize() menu = None fps = 40 while True: events = list(pygame.event.get()) if self.find_quit_event(events): break if self.find_fullscreen_event(events): self.window.toggle_fullscreen() event = self.find_resize_event(events) if event: self.window.resize(event.size) if not menu and self.find_settings_event(events): menu = PiConfigMenu(self.window, self.config, fps) menu.show() if menu and menu.is_shown(): # Convert HW button events to keyboard events for menu if self.find_settings_event(events, BUTTON_DOWN): events.insert(0, menu.create_back_event()) if self.find_capture_event(events, BUTTON_DOWN): events.insert(0, menu.create_next_event()) elif self.find_print_event(events, BUTTON_DOWN): events.insert(0, menu.create_click_event()) menu.process(events) elif menu and not menu.is_shown(): self.initialize() menu = None else: self.state_machine.process(events) pygame.display.update() clock.tick( fps ) # Ensure the program will never run at more than x frames per second finally: self.makers_pool.quit() self.led_startup.quit() self.led_preview.quit() self.led_capture.quit() self.led_print.quit() GPIO.cleanup() self.camera.quit() self.printer.quit() pygame.quit()
def __init__(self, config): self.config = config # Clean directory where pictures are saved self.savedir = osp.expanduser(config.get('GENERAL', 'directory')) if not osp.isdir(self.savedir): os.makedirs(self.savedir) if osp.isdir(self.savedir) and config.getboolean( 'GENERAL', 'clear_on_startup'): shutil.rmtree(self.savedir) os.makedirs(self.savedir) # Prepare GPIO, physical pins mode GPIO.setmode(GPIO.BOARD) # Prepare the pygame module for use os.environ['SDL_VIDEO_CENTERED'] = '1' pygame.init() # Dont catch mouse motion to avoid filling the queue during long actions pygame.event.set_blocked(pygame.MOUSEMOTION) # Create window of (width, height) self.window = PtbWindow('Pibooth', config.gettyped('WINDOW', 'size'), config.getboolean('WINDOW', 'buffer')) self.state_machine = StateMachine(self) self.state_machine.add_state(StateWait()) self.state_machine.add_state( StateChoose(30)) # 30s before going back to the start self.state_machine.add_state(StateChosen(4)) self.state_machine.add_state(StateCapture()) self.state_machine.add_state(StateProcessing()) self.state_machine.add_state(StatePrint()) self.state_machine.add_state(StateFinish()) # Initialize the camera if camera.rpi_camera_connected(): cam_class = camera.RpiCamera elif camera.gp_camera_connected(): cam_class = camera.GpCamera else: raise EnvironmentError( "Neither PiCamera nor GPhoto2 camera detected") self.camera = cam_class(config.getint('CAMERA', 'iso'), config.gettyped('CAMERA', 'resolution'), config.getint('CAMERA', 'rotation'), config.getboolean('CAMERA', 'flip')) self.led_picture = PtbLed(7) self.button_picture = PtbButton( 11, config.getfloat('GENERAL', 'debounce_delay')) self.led_print = PtbLed(15) self.button_print = PtbButton( 13, config.getfloat('GENERAL', 'debounce_delay')) self.printer = PtbPrinter(config.get('PRINTER', 'printer_name')) # Variables shared between states self.dirname = None self.captures = [] self.max_captures = None self.previous_picture = None self.previous_picture_file = None
class PtbApplication(object): def __init__(self, config): self.config = config # Clean directory where pictures are saved self.savedir = osp.expanduser(config.get('GENERAL', 'directory')) if not osp.isdir(self.savedir): os.makedirs(self.savedir) if osp.isdir(self.savedir) and config.getboolean( 'GENERAL', 'clear_on_startup'): shutil.rmtree(self.savedir) os.makedirs(self.savedir) # Prepare GPIO, physical pins mode GPIO.setmode(GPIO.BOARD) # Prepare the pygame module for use os.environ['SDL_VIDEO_CENTERED'] = '1' pygame.init() # Dont catch mouse motion to avoid filling the queue during long actions pygame.event.set_blocked(pygame.MOUSEMOTION) # Create window of (width, height) self.window = PtbWindow('Pibooth', config.gettyped('WINDOW', 'size'), config.getboolean('WINDOW', 'buffer')) self.state_machine = StateMachine(self) self.state_machine.add_state(StateWait()) self.state_machine.add_state( StateChoose(30)) # 30s before going back to the start self.state_machine.add_state(StateChosen(4)) self.state_machine.add_state(StateCapture()) self.state_machine.add_state(StateProcessing()) self.state_machine.add_state(StatePrint()) self.state_machine.add_state(StateFinish()) # Initialize the camera if camera.rpi_camera_connected(): cam_class = camera.RpiCamera elif camera.gp_camera_connected(): cam_class = camera.GpCamera else: raise EnvironmentError( "Neither PiCamera nor GPhoto2 camera detected") self.camera = cam_class(config.getint('CAMERA', 'iso'), config.gettyped('CAMERA', 'resolution'), config.getint('CAMERA', 'rotation'), config.getboolean('CAMERA', 'flip')) self.led_picture = PtbLed(7) self.button_picture = PtbButton( 11, config.getfloat('GENERAL', 'debounce_delay')) self.led_print = PtbLed(15) self.button_print = PtbButton( 13, config.getfloat('GENERAL', 'debounce_delay')) self.printer = PtbPrinter(config.get('PRINTER', 'printer_name')) # Variables shared between states self.dirname = None self.captures = [] self.max_captures = None self.previous_picture = None self.previous_picture_file = None def find_quit_event(self, events): """Return the event if found in the list. """ for event in events: if event.type == pygame.QUIT or\ (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE): return event def find_fullscreen_event(self, events): """Return the event if found in the list. """ for event in events: if event.type == pygame.KEYDOWN and\ event.key == pygame.K_f and pygame.key.get_mods() & pygame.KMOD_CTRL: return event def find_picture_event(self, events): """Return the event if found in the list. """ for event in events: if (event.type == pygame.KEYDOWN and event.key == pygame.K_p) or \ (event.type == BUTTON_DOWN and event.pin == self.button_picture): return event def find_print_event(self, events): """Return the event if found in the list. """ for event in events: if (event.type == pygame.KEYDOWN and event.key == pygame.K_e and pygame.key.get_mods() & pygame.KMOD_CTRL) or \ (event.type == BUTTON_DOWN and event.pin == self.button_print): return event def find_resize_event(self, events): """Return the event if found in the list. """ for event in events: if event.type == pygame.VIDEORESIZE: return event def find_choice_event(self, events): """Return the event if found in the list. """ for event in events: if (event.type == pygame.KEYDOWN and event.key in (pygame.K_LEFT, pygame.K_RIGHT)) or \ (event.type == BUTTON_DOWN and event.pin == self.button_picture) or \ (event.type == BUTTON_DOWN and event.pin == self.button_print): return event def main_loop(self): """Run the main game loop. """ try: self.state_machine.set_state('wait') while True: events = list(reversed( pygame.event.get())) # Take all events, most recent first if self.find_quit_event(events): break if self.find_fullscreen_event(events): self.window.toggle_fullscreen() event = self.find_resize_event(events) if event: self.window.resize(event.size) self.state_machine.process(events) finally: self.led_picture.quit() self.led_print.quit() GPIO.cleanup() self.camera.quit() self.printer.quit() pygame.quit()
def __init__(self, config): self.config = config # Clean directory where pictures are saved self.savedir = config.getpath('GENERAL', 'directory') if not osp.isdir(self.savedir): os.makedirs(self.savedir) if osp.isdir(self.savedir) and config.getboolean( 'GENERAL', 'clear_on_startup'): shutil.rmtree(self.savedir) os.makedirs(self.savedir) # Prepare GPIO, physical pins mode GPIO.setmode(GPIO.BOARD) # Prepare the pygame module for use os.environ['SDL_VIDEO_CENTERED'] = '1' pygame.init() # Dont catch mouse motion to avoid filling the queue during long actions pygame.event.set_blocked(pygame.MOUSEMOTION) # Create window of (width, height) init_size = self.config.gettyped('WINDOW', 'size') if not isinstance(init_size, str): self.window = PtbWindow('Pibooth', init_size) else: self.window = PtbWindow('Pibooth') self.state_machine = StateMachine(self) self.state_machine.add_state(StateWait()) self.state_machine.add_state( StateChoose(30)) # 30s before going back to the start self.state_machine.add_state(StateChosen(4)) self.state_machine.add_state(StateCapture()) self.state_machine.add_state(StateProcessing()) self.state_machine.add_state(StatePrint()) self.state_machine.add_state(StateFinish(0.5)) # Initialize the camera. If gPhoto2 camera is used, try to kill # any process using gPhoto2 as it may block camera access if camera.gp_camera_connected() and camera.rpi_camera_connected(): cam_class = camera.HybridCamera pkill('*gphoto2*') elif camera.gp_camera_connected(): cam_class = camera.GpCamera pkill('*gphoto2*') elif camera.rpi_camera_connected(): cam_class = camera.RpiCamera elif camera.cv_camera_connected(): cam_class = camera.CvCamera else: raise EnvironmentError( "Neither Raspberry Pi nor GPhoto2 nor OpenCV camera detected") self.camera = cam_class(config.getint('CAMERA', 'iso'), config.gettyped('CAMERA', 'resolution'), config.getint('CAMERA', 'rotation'), config.getboolean('CAMERA', 'flip')) # Initialize the hardware buttons self.led_capture = PtbLed(config.getint('CONTROLS', 'picture_led_pin')) self.button_capture = PtbButton( config.getint('CONTROLS', 'picture_btn_pin'), config.getfloat('CONTROLS', 'debounce_delay')) self.led_print = PtbLed(config.getint('CONTROLS', 'print_led_pin')) self.button_print = PtbButton( config.getint('CONTROLS', 'print_btn_pin'), config.getfloat('CONTROLS', 'debounce_delay')) self.led_startup = PtbLed(config.getint('CONTROLS', 'startup_led_pin')) self.led_preview = PtbLed(config.getint('CONTROLS', 'preview_led_pin')) # Initialize the printer self.printer = PtbPrinter(config.get('PRINTER', 'printer_name')) # Variables shared between states self.dirname = None self.makers_pool = PicturesMakersPool() self.capture_nbr = None self.capture_choices = (4, 1) self.nbr_duplicates = 0 self.previous_picture = None self.previous_animated = [] self.previous_picture_file = None