def __init__(self, config, camera): self.conf = config self.camera = camera self.printer = PrinterProxy(self.conf) # platform and pygame logger.info("PLATFORM: %s" % platform_devs.running_platform) platform_devs.platform_init() pygame.init() pygame.mouse.set_visible(False) self.clock = pygame.time.Clock() # peripherials self.button = platform_devs.Button() self.lights = platform_devs.Lights(self.conf['devices']['lights_external']) self.button.register_callback(self.button_callback) # view and model self.is_running = False self.live_view_running = False self.live_view_still_img = None self.view = view.PygView(self, self.conf, self.camera) self.model = model.PhotoBoothModel(self) to_upload_sessions = self.model.load_from_disk() # capture thread self.capture_names = Queue(maxsize=0) self.thread_capture = Thread(target=self.capture_image_worker) self.thread_capture.setDaemon(True) # upload background process (creating GIF is cpu-intensive, make it happen in other process to bypass GIL) self.upload_pipe = None if self.conf['upload']['enabled']: pipe = multiprocessing.Pipe() self.upload_pipe = pipe[0] self.process_upload = multiprocessing.Process(target=upload.run, args=(self.conf, pipe)) self.process_upload.daemon = True # try to reupload not yet uploaded sessions if self.conf['upload']['retrying']: for sess in to_upload_sessions: self.upload_pipe.send((sess.id, sess.get_medium_img_paths(),\ sess.get_full_img_paths(), sess.random_tags)) self.next_fps_update_ticks = 0
def __init__(self, config): self.conf = config self.cam = picam.PiCam(config) self.printer = PrinterProxy(self.conf) # platform and picam logger.info("PLATFORM: %s" % platform_devs.running_platform) platform_devs.platform_init() self.cam.start() # peripherials self.button = platform_devs.Button() self.button_pressed = False self.lights = platform_devs.Lights(self.conf['devices']['lights_external']) self.button.register_callback(self.button_callback) # uploader self.upload = UploadProxy(self.conf) # model self.is_running = False self.model = model.VideoBoothModel(self)
class VideoBoothController(object): """ controlling the logic flow around the whole application """ def __init__(self, config): self.conf = config self.cam = picam.PiCam(config) self.printer = PrinterProxy(self.conf) # platform and picam logger.info("PLATFORM: %s" % platform_devs.running_platform) platform_devs.platform_init() self.cam.start() # peripherials self.button = platform_devs.Button() self.button_pressed = False self.lights = platform_devs.Lights(self.conf['devices']['lights_external']) self.button.register_callback(self.button_callback) # uploader self.upload = UploadProxy(self.conf) # model self.is_running = False self.model = model.VideoBoothModel(self) def run(self): """Main loop""" self.is_running = True self.button.start() self.upload.start() while self.is_running: # if camera stopped working, exit if not self.cam.update(): break button_pressed = self.process_events() # detecting LONG PRESS for poweroff: self.button.update_state() self.model.update(button_pressed) # TODO: polling/fps instead of this? time.sleep(0.3) self.quit() def __del__(self): self.cam.stop() platform_devs.platform_deinit() def quit(self): self.is_running = False if self.model: self.model.quit() self.model = None self.lights.pause() self.cam.stop() def button_callback(self): self.button_pressed = True def process_events(self): button_pressed = self.button_pressed self.button_pressed = False return button_pressed def get_external_ip(self): return platform_devs.get_ip() def set_info_text(self, text_arg, big=False, color="ffffff"): text = text_arg.strip().replace("\n", "\\n") if big: self.cam.set_text(text, pt=140, layout_align="center,center", color=color) else: self.cam.set_text(text, pt=60, color=color) def set_rec_text(self, time): text = "\\n".join([u"●REC", time]) self.cam.set_text(text, layout_align="top,right", horizontal_margin=30, vertical_margin=30, color="ff0000") def start_recording(self): self.cam.start_recording() def stop_recording(self): self.cam.stop_recording() def check_recording_state(self, post_res): if not self.cam.is_recording(): new_movie_fn = self.cam.last_rec_filename() logger.info("NEW MOVIE: %s", new_movie_fn) (upload_url, frontend_url) = post_res or ("", "") self.upload.async_process(upload_url, new_movie_fn) if len(frontend_url) > 0: logger.info("frontend URL: %s", frontend_url) self.printer.print_video(frontend_url) return True return False
class PhotoBoothController(object): """ controlling the logic flow around the whole application """ QUIT_KEYS = pygame.K_ESCAPE, pygame.K_q BUTTON_KEY= pygame.K_SPACE, BUTTONPUSHEVENT = pygame.USEREVENT + 2 def __init__(self, config, camera): self.conf = config self.camera = camera self.printer = PrinterProxy(self.conf) # platform and pygame logger.info("PLATFORM: %s" % platform_devs.running_platform) platform_devs.platform_init() pygame.init() pygame.mouse.set_visible(False) self.clock = pygame.time.Clock() # peripherials self.button = platform_devs.Button() self.lights = platform_devs.Lights(self.conf['devices']['lights_external']) self.button.register_callback(self.button_callback) # view and model self.is_running = False self.live_view_running = False self.live_view_still_img = None self.view = view.PygView(self, self.conf, self.camera) self.model = model.PhotoBoothModel(self) to_upload_sessions = self.model.load_from_disk() # capture thread self.capture_names = Queue(maxsize=0) self.thread_capture = Thread(target=self.capture_image_worker) self.thread_capture.setDaemon(True) # upload background process (creating GIF is cpu-intensive, make it happen in other process to bypass GIL) self.upload_pipe = None if self.conf['upload']['enabled']: pipe = multiprocessing.Pipe() self.upload_pipe = pipe[0] self.process_upload = multiprocessing.Process(target=upload.run, args=(self.conf, pipe)) self.process_upload.daemon = True # try to reupload not yet uploaded sessions if self.conf['upload']['retrying']: for sess in to_upload_sessions: self.upload_pipe.send((sess.id, sess.get_medium_img_paths(),\ sess.get_full_img_paths(), sess.random_tags)) self.next_fps_update_ticks = 0 def __del__(self): platform_devs.platform_deinit() def run(self): """Main loop""" self.is_running = True self.thread_capture.start() self.button.start() if self.conf['upload']['enabled']: self.process_upload.start() # first session is for setting up, fire lights at full brightness, or not #self.lights.set_brightness(self.conf["devices"]["lights_full"]) while self.is_running: self.clock.tick(self.view.fps) button_pressed = self.process_events() self.button.update_state() self.view.update() self.model.update(button_pressed) if self.next_fps_update_ticks < pygame.time.get_ticks(): fps_str = "[FPS]: %.2f" % (self.clock.get_fps()) pygame.display.set_caption(fps_str) #logger.debug(fps_str) self.next_fps_update_ticks = pygame.time.get_ticks() + self.conf['debug']['fps_update_ms'] self.quit() def quit(self): self.is_running = False if self.model: self.model.quit() self.model = None if self.upload_pipe: self.upload_pipe.close() self.upload_pipe = None self.lights.pause() pygame.quit() def button_callback(self): button_event = pygame.event.Event(self.BUTTONPUSHEVENT) pygame.event.post(button_event) def process_events(self): """ Returns wheter "THE BUTTON" has been pressed """ for event in pygame.event.get(): if event.type == pygame.QUIT: self.is_running = False elif event.type == self.BUTTONPUSHEVENT: return True elif event.type == pygame.KEYDOWN: if event.key in PhotoBoothController.QUIT_KEYS: self.is_running = False elif event.key in PhotoBoothController.BUTTON_KEY: return True elif event.key == pygame.K_p: # for debugging self.print_camera_preview() return False return False def start_live_view(self): self.live_view_running = True self.camera.start_preview() self.view.lv.start() def resume_live_view(self): self.view.lv.start() def stop_live_view(self, still_img=None): self.live_view_running = False self.camera.stop_preview() self.view.lv.stop() if still_img: self.view.lv.set_image(still_img) def schedule_stop_live_view(self, still_img=None): """ does not stop camera previews """ self.live_view_running = False self.live_view_still_img = still_img def is_live_view_overlay_finished(self): return self.view.lv.is_started and not self.view.lv.is_overlay def live_view_show_arrow(self): self.view.lv.show_arrow = True def live_view_hide_arrow(self): self.view.lv.show_arrow = False def set_text(self, text_lines, big_font=False): self.view.textbox.draw_text(text_lines, big_font) def capture_image_worker(self): while self.is_running: image_number, image_name, medium_name, prev_name = self.capture_names.get() # (1) capture the image logger.info("capture_image_worker: capturing image to: %s", image_name) self.camera.pause_preview() self.camera.capture_image(image_name) # this blocks # (2) view: start the 'end animation overlay' and resume LV if self.live_view_running: self.camera.start_preview() # resume previews ASAP self.view.lv.start() else: # "pending" stop_live_view self.stop_live_view(self.live_view_still_img) self.view.lv.end_overlay() # (3) lights - default brightness (only during live view) if self.live_view_running: self.lights.set_brightness(self.conf["devices"]["lights_default"]) else: self.lights.pause() # (4) load captured images and scale them logger.debug("capture_image_worker: reading and scalling images") img = pygame.image.load(image_name).convert() img_lv = pygame.transform.scale(img, (view.LivePreview.WIDTH, view.LivePreview.HEIGHT)) img_prev = pygame.transform.scale(img_lv, (view.SmallPhotoPreview.WIDTH, view.SmallPhotoPreview.HEIGHT)) # (5) view: set the preview image self.view.main_previews[image_number].set_image(img_prev) self.view.main_previews[image_number].end_overlay() # (6) save the scalled images pygame.image.save(img_prev, prev_name) pygame.image.save(img_lv, medium_name) # (7) finish the task and send the results logger.debug("capture_image_worker: DONE") self.capture_names.task_done() self.model.set_current_session_imgs(image_number, (img, img_lv, img_prev)) def capture_image(self, image_number, file_paths): # lights - maximum brightness self.lights.set_brightness(self.conf["devices"]["lights_full"]) # view: capture begin animation self.view.lv.pause() self.view.lv.begin_overlay() self.view.main_previews[image_number].begin_overlay() # schedule worker thread to capture image obj = (image_number, file_paths[0], file_paths[1], file_paths[2]) self.capture_names.put(obj) def print_camera_preview(self): img = self.view.lv.image self.printer.print_image(img) @staticmethod def load_captured_image(file_path): img = pygame.image.load(file_path).convert() return img def enqueue_animate_montage(self, img_list): self.view.lv.enqueue_animate_montage(img_list, self.conf["control"]["montage_fps"]) def notify_idle_previews_changed(self): prev_num = 1 for img_list in self.model.get_idle_previews_image_lists(): #logger.debug("preview[%d] = %s <- %s" % (prev_num, self.view.idle_previews[prev_num], img_list)) self.view.idle_previews[prev_num].start_animate(img_list, 0) # if fps == 0 -> sync whith display FPS prev_num += 1 def notify_finished_session(self, sess): """ Start work related with finished session processing - uploading and printing""" self.printer.print_session(sess.id, sess.medium_img_list, sess.random_tags) if self.conf["upload"]["enabled"]: self.upload_pipe.send((sess.id, sess.get_medium_img_paths(), sess.get_full_img_paths(), sess.random_tags))