From bd358030cd61bfb0a8d3a99d42622c3cfdeb3369 Mon Sep 17 00:00:00 2001 From: Balthasar Reuter Date: Mon, 16 Jul 2018 23:19:41 +0200 Subject: [PATCH] Worker process adapted to new state machine --- photobooth/PictureList.py | 2 +- photobooth/StateMachine.py | 2 + photobooth/Worker.py | 67 ++++++++++++++++++++++--------- photobooth/camera/__init__.py | 10 ++--- photobooth/defaults.cfg | 4 +- photobooth/gui/Qt5Gui/Frames.py | 7 ++++ photobooth/gui/Qt5Gui/PyQt5Gui.py | 6 +-- photobooth/main.py | 6 ++- 8 files changed, 69 insertions(+), 35 deletions(-) diff --git a/photobooth/PictureList.py b/photobooth/PictureList.py index cb5c69e..6df99fc 100644 --- a/photobooth/PictureList.py +++ b/photobooth/PictureList.py @@ -65,7 +65,7 @@ class PictureList: # Print initial infos logging.info('Number of last existing file: %d', self.counter) - logging.info('Saving assembled pictures as "%s%s.%s"', self.basename, + logging.info('Saving pictures as "%s%s.%s"', self.basename, self.count_width * 'X', 'jpg') def getFilename(self, count): diff --git a/photobooth/StateMachine.py b/photobooth/StateMachine.py index c26ffb3..dca2daa 100644 --- a/photobooth/StateMachine.py +++ b/photobooth/StateMachine.py @@ -56,8 +56,10 @@ class Context: elif isinstance(event, TeardownEvent): self.state = TeardownState(event.target) if event.target == TeardownEvent.EXIT: + self._comm.bcast(None) return 0 elif event.target == TeardownEvent.RESTART: + self._comm.bcast(None) return 123 else: self.state.handleEvent(event, self) diff --git a/photobooth/Worker.py b/photobooth/Worker.py index fbe137e..37a5575 100644 --- a/photobooth/Worker.py +++ b/photobooth/Worker.py @@ -19,12 +19,11 @@ import logging import os.path -import sys from time import localtime, strftime from .PictureList import PictureList -from .StateMachine import TeardownEvent, TeardownState +from . import StateMachine from .Threading import Workers @@ -34,32 +33,25 @@ class WorkerTask: assert not kwargs - def get(self, picture): + def do(self, picture): raise NotImplementedError() class PictureSaver(WorkerTask): - def __init__(self, config): + def __init__(self, basename): super().__init__() - path = os.path.join(config.get('Picture', 'basedir'), - config.get('Picture', 'basename')) - basename = strftime(path, localtime()) self._pic_list = PictureList(basename) - @staticmethod - def do(picture, filename): + def do(self, picture): + filename = self._pic_list.getNext() logging.info('Saving picture as %s', filename) picture.save(filename, 'JPEG') - def get(self, picture): - - return (self.do, (picture, self._pic_list.getNext())) - class Worker: @@ -67,19 +59,58 @@ class Worker: self._comm = comm + self.initPostprocessTasks(config) + self.initPictureTasks(config) + + def initPostprocessTasks(self, config): + + self._postprocess_tasks = [] + + # PictureSaver for assembled pictures + path = os.path.join(config.get('Picture', 'basedir'), + config.get('Picture', 'basename')) + basename = strftime(path, localtime()) + self._postprocess_tasks.append(PictureSaver(basename)) + + def initPictureTasks(self, config): + + self._picture_tasks = [] + + # PictureSaver for single shots + path = os.path.join(config.get('Picture', 'basedir'), + config.get('Picture', 'basename') + '_shot_') + basename = strftime(path, localtime()) + self._picture_tasks.append(PictureSaver(basename)) + def run(self): for state in self._comm.iter(Workers.WORKER): self.handleState(state) + return True + def handleState(self, state): - if isinstance(state, TeardownState): + if isinstance(state, StateMachine.TeardownState): self.teardown(state) + elif isinstance(state, StateMachine.ReviewState): + self.doPostprocessTasks(state.picture) + elif isinstance(state, StateMachine.CameraEvent): + if state.name == 'capture': + self.doPictureTasks(state.picture) + else: + raise ValueError('Unknown CameraEvent "{}"'.format(state)) def teardown(self, state): - if state.target == TeardownEvent.EXIT: - sys.exit(0) - elif state.target == TeardownEvent.RESTART: - sys.exit(123) + pass + + def doPostprocessTasks(self, picture): + + for task in self._postprocess_tasks: + task.do(picture) + + def doPictureTasks(self, picture): + + for task in self._picture_tasks: + task.do(picture) diff --git a/photobooth/camera/__init__.py b/photobooth/camera/__init__.py index 4c9989a..db40fb0 100644 --- a/photobooth/camera/__init__.py +++ b/photobooth/camera/__init__.py @@ -18,7 +18,6 @@ # along with this program. If not, see . import logging -import sys from PIL import Image, ImageOps @@ -51,8 +50,7 @@ class Camera: self._pic_dims = None self._is_preview = self._cfg.getBool('Photobooth', 'show_preview') - self._is_keep_pictures = self._cfg.getBool('Photobooth', - 'keep_pictures') + self._is_keep_pictures = self._cfg.getBool('Picture', 'keep_pictures') def startup(self): @@ -72,16 +70,14 @@ class Camera: if self._cap is not None: self._cap.cleanup() - if state.target == StateMachine.TeardownEvent.EXIT: - sys.exit(0) - elif state.target == StateMachine.TeardownEvent.RESTART: - sys.exit(123) def run(self): for state in self._comm.iter(Workers.CAMERA): self.handleState(state) + return True + def handleState(self, state): if isinstance(state, StateMachine.StartupState): diff --git a/photobooth/defaults.cfg b/photobooth/defaults.cfg index ee311cb..6b1a0ec 100644 --- a/photobooth/defaults.cfg +++ b/photobooth/defaults.cfg @@ -50,8 +50,6 @@ countdown_time = 8 display_time = 5 # Timeout for postprocessing (shown after review) postprocess_time = 60 -# Keep single pictures (True/False) -keep_pictures = False [Picture] # Basedir of output pictures @@ -70,3 +68,5 @@ size_y = 2362 min_dist_x = 20 # Minimum distance between thumbnails in vertical direction min_dist_y = 20 +# Keep single pictures (True/False) +keep_pictures = False \ No newline at end of file diff --git a/photobooth/gui/Qt5Gui/Frames.py b/photobooth/gui/Qt5Gui/Frames.py index 42321c0..cb6fc7b 100644 --- a/photobooth/gui/Qt5Gui/Frames.py +++ b/photobooth/gui/Qt5Gui/Frames.py @@ -651,6 +651,10 @@ class Settings(QtWidgets.QFrame): self.add('Picture', 'basedir', basedir) self.add('Picture', 'basename', basename) + keep_pictures = QtWidgets.QCheckBox() + keep_pictures.setChecked(self._cfg.getBool('Picture', 'keep_pictures')) + self.add('Picture', 'keep_pictures', keep_pictures) + lay_num = QtWidgets.QHBoxLayout() lay_num.addWidget(num_x) lay_num.addWidget(QtWidgets.QLabel('x')) @@ -688,6 +692,7 @@ class Settings(QtWidgets.QFrame): lay_file) layout.addRow('Basename of files (strftime possible):', basename) + layout.addRow('Keep single shots:', keep_pictures) widget = QtWidgets.QWidget() widget.setLayout(layout) @@ -808,6 +813,8 @@ class Settings(QtWidgets.QFrame): self.get('Picture', 'basedir').text()) self._cfg.set('Picture', 'basename', self.get('Picture', 'basename').text()) + self._cfg.set('Picture', 'keep_pictures', + str(self.get('Picture', 'keep_pictures').isChecked())) self._cfg.set('Gpio', 'enable', str(self.get('Gpio', 'enable').isChecked())) diff --git a/photobooth/gui/Qt5Gui/PyQt5Gui.py b/photobooth/gui/Qt5Gui/PyQt5Gui.py index d6a6196..939222b 100644 --- a/photobooth/gui/Qt5Gui/PyQt5Gui.py +++ b/photobooth/gui/Qt5Gui/PyQt5Gui.py @@ -128,11 +128,7 @@ class PyQt5Gui(GuiSkeleton): def teardown(self, state): - if state.target == TeardownEvent.EXIT: - self._app.exit(0) - elif state.target == TeardownEvent.RESTART: - self._app.exit(123) - elif state.target == TeardownEvent.WELCOME: + if state.target == TeardownEvent.WELCOME: self._comm.send(Workers.MASTER, GuiEvent('welcome')) def showError(self, state): diff --git a/photobooth/main.py b/photobooth/main.py index f740722..b4ba397 100644 --- a/photobooth/main.py +++ b/photobooth/main.py @@ -54,7 +54,8 @@ class CameraProcess(mp.Process): while True: try: - cap.run() + if cap.run(): + break except Exception as e: self._comm.send(Workers.MASTER, ErrorEvent(e)) @@ -73,7 +74,8 @@ class WorkerProcess(mp.Process): while True: try: - Worker(self.cfg, self.comm).run() + if Worker(self.cfg, self.comm).run(): + break except Exception as e: self._comm.send(Workers.MASTER, ErrorEvent(e))