From 62b167ad8355af69623e0bb5bc220adc55ae14bf Mon Sep 17 00:00:00 2001 From: Balthasar Reuter Date: Fri, 11 May 2018 21:46:36 +0200 Subject: [PATCH] Gui Postprocessing with basic printing support --- photobooth/Photobooth.py | 3 +- photobooth/gui/GuiPostprocess.py | 40 +++++++++ photobooth/gui/GuiState.py | 146 ++++++++++++++++++++++++++++++ photobooth/gui/PyQt5Gui.py | 25 ++++-- photobooth/gui/__init__.py | 149 ++----------------------------- photobooth/main.py | 68 +++++++------- photobooth/util.py | 23 +++++ 7 files changed, 272 insertions(+), 182 deletions(-) create mode 100644 photobooth/gui/GuiPostprocess.py create mode 100644 photobooth/gui/GuiState.py create mode 100644 photobooth/util.py diff --git a/photobooth/Photobooth.py b/photobooth/Photobooth.py index df657d8..3d73b9d 100644 --- a/photobooth/Photobooth.py +++ b/photobooth/Photobooth.py @@ -20,9 +20,10 @@ class TeardownException(Exception): class Photobooth: - def __init__(self, config, camera, conn): + def __init__(self, config, camera, conn, queue): self._conn = conn + self._queue = queue self.initCamera(config, camera()) self.initGpio(config) diff --git a/photobooth/gui/GuiPostprocess.py b/photobooth/gui/GuiPostprocess.py new file mode 100644 index 0000000..9fd9d7b --- /dev/null +++ b/photobooth/gui/GuiPostprocess.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from .. import printer +from ..util import lookup_and_import + +from PyQt5.QtWidgets import QMessageBox + +class GuiPostprocess: + + def __init__(self, **kwargs): + + assert not kwargs + + + def do(self, parent, picture): + + raise NotImplementedError() + + + + +class PrintPostprocess(GuiPostprocess): + + def __init__(self, printer_module, page_size, **kwargs): + + super().__init__(**kwargs) + + Printer = lookup_and_import(printer.modules, printer_module, 'printer') + self._printer = Printer(page_size, True) + + + def do(self, parent, picture): + + reply = QMessageBox.question(parent, 'Print?', + 'Do you want to print the picture?', + QMessageBox.Yes | QMessageBox.No, QMessageBox.No) + + if reply == QMessageBox.Yes: + self._printer.print(picture) diff --git a/photobooth/gui/GuiState.py b/photobooth/gui/GuiState.py new file mode 100644 index 0000000..4b83f2d --- /dev/null +++ b/photobooth/gui/GuiState.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + + +class GuiState: + + def __init__(self, **kwargs): + + assert not kwargs + + + +class ErrorState(GuiState): + + def __init__(self, title, message, **kwargs): + + super().__init__(**kwargs) + + self.title = title + self.message = message + + + @property + def title(self): + + return self._title + + + @title.setter + def title(self, title): + + self._title = title + + + @property + def message(self): + + return self._message + + + @message.setter + def message(self, message): + + self._message = message + + +class IdleState(GuiState): + + def __init__(self, **kwargs): + + super().__init__(**kwargs) + + + +class PictureState(GuiState): + + def __init__(self, picture, **kwargs): + + super().__init__(**kwargs) + + self.picture = picture + + + @property + def picture(self): + + return self._pic + + + @picture.setter + def picture(self, picture): + + self._pic = picture + + +class MessageState(GuiState): + + def __init__(self, message, **kwargs): + + super().__init__(**kwargs) + + self.message = message + + + @property + def message(self): + + return self._msg + + + @message.setter + def message(self, message): + + if not isinstance(message, str): + raise ValueError('Message must be a string') + + self._msg = message + + + +class TriggerState(GuiState): + + def __init__(self, **kwargs): + + super().__init__(**kwargs) + + +class GreeterState(GuiState): + + def __init__(self, **kwargs): + + super().__init__(**kwargs) + + +class PoseState(GuiState): + + def __init__(self, **kwargs): + + super().__init__(**kwargs) + + +class AssembleState(GuiState): + + def __init__(self, **kwargs): + + super().__init__(**kwargs) + + +class CountdownState(GuiState): + + def __init__(self, **kwargs): + + super().__init__(**kwargs) + + +class PreviewState(PictureState): + + def __init__(self, **kwargs): + + super().__init__(**kwargs) + +class TeardownState(GuiState): + + def __init__(self, **kwargs): + + super().__init__(**kwargs) diff --git a/photobooth/gui/PyQt5Gui.py b/photobooth/gui/PyQt5Gui.py index e744937..1352d94 100644 --- a/photobooth/gui/PyQt5Gui.py +++ b/photobooth/gui/PyQt5Gui.py @@ -33,17 +33,22 @@ class PyQt5Gui(Gui): self._app = QApplication(argv) self._p = PyQt5MainWindow() self._lastState = self.showStart - self._printer = Printer((cfg.getInt('Printer', 'width'), - cfg.getInt('Printer', 'height')), True) + + self._postprocessList = [] + + if cfg.getBool('Printer', 'enable'): + self._postprocessList.append( PrintPostprocess( cfg.get('Printer', 'module'), + (cfg.getInt('Printer', 'width'), cfg.getInt('Printer', 'height')) ) ) - def run(self, camera_conn): + def run(self, camera_conn, worker_queue): receiver = PyQt5Receiver([camera_conn]) receiver.notify.connect(self.handleState) receiver.start() self._conn = camera_conn + self._queue = worker_queue self.showStart() @@ -134,9 +139,11 @@ class PyQt5Gui(Gui): elif isinstance(state, PictureState): img = ImageQt.ImageQt(state.picture) self._p.setCentralWidget(PyQt5PictureMessage('', img)) - QTimer.singleShot(cfg.getInt('Photobooth', 'display_time') * 1000, lambda : self.sendAck()) + # QTimer.singleShot(cfg.getInt('Photobooth', 'display_time') * 1000, self.sendAck) + QTimer.singleShot(cfg.getInt('Photobooth', 'display_time') * 1000, + lambda : self.postprocessPicture(state.picture)) - self._printer.print(state.picture) + # self._printer.print(state.picture) elif isinstance(state, TeardownState): self._conn.send('teardown') @@ -149,6 +156,14 @@ class PyQt5Gui(Gui): raise ValueError('Unknown state') + def postprocessPicture(self, picture): + + for task in self._postprocessList: + task.do(self._p, picture) + + self.sendAck() + + def showStart(self): self._p.handleKeypressEvent = lambda event : None diff --git a/photobooth/gui/__init__.py b/photobooth/gui/__init__.py index 9bbdc19..56930fe 100644 --- a/photobooth/gui/__init__.py +++ b/photobooth/gui/__init__.py @@ -2,6 +2,10 @@ # -*- coding: utf-8 -*- +from .GuiState import * +from .GuiPostprocess import * + + # Available gui modules as tuples of (config name, module name, class name) modules = ( ('qt5', 'PyQt5Gui', 'PyQt5Gui'), ) @@ -13,151 +17,8 @@ class Gui: pass - def run(self, send, recv): + def run(self, camera_conn, worker_queue): raise NotImplementedError() - -class GuiState: - - def __init__(self, **kwargs): - - assert not kwargs - - - -class ErrorState(GuiState): - - def __init__(self, title, message, **kwargs): - - super().__init__(**kwargs) - - self.title = title - self.message = message - - - @property - def title(self): - - return self._title - - - @title.setter - def title(self, title): - - self._title = title - - - @property - def message(self): - - return self._message - - - @message.setter - def message(self, message): - - self._message = message - - -class IdleState(GuiState): - - def __init__(self, **kwargs): - - super().__init__(**kwargs) - - - -class PictureState(GuiState): - - def __init__(self, picture, **kwargs): - - super().__init__(**kwargs) - - self.picture = picture - - - @property - def picture(self): - - return self._pic - - - @picture.setter - def picture(self, picture): - - self._pic = picture - - -class MessageState(GuiState): - - def __init__(self, message, **kwargs): - - super().__init__(**kwargs) - - self.message = message - - - @property - def message(self): - - return self._msg - - - @message.setter - def message(self, message): - - if not isinstance(message, str): - raise ValueError('Message must be a string') - - self._msg = message - - - -class TriggerState(GuiState): - - def __init__(self, **kwargs): - - super().__init__(**kwargs) - - -class GreeterState(GuiState): - - def __init__(self, **kwargs): - - super().__init__(**kwargs) - - -class PoseState(GuiState): - - def __init__(self, **kwargs): - - super().__init__(**kwargs) - - -class AssembleState(GuiState): - - def __init__(self, **kwargs): - - super().__init__(**kwargs) - - -class CountdownState(GuiState): - - def __init__(self, **kwargs): - - super().__init__(**kwargs) - - -class PreviewState(PictureState): - - def __init__(self, **kwargs): - - super().__init__(**kwargs) - -class TeardownState(GuiState): - - def __init__(self, **kwargs): - - super().__init__(**kwargs) diff --git a/photobooth/main.py b/photobooth/main.py index 0de9fd0..df2e557 100644 --- a/photobooth/main.py +++ b/photobooth/main.py @@ -8,51 +8,33 @@ except DistributionNotFound: __version__ = 'unknown' import multiprocessing as mp -import importlib import sys from . import camera, gui from .Config import Config from .Photobooth import Photobooth - - -def lookup_and_import(module_list, name, package=None): - - result = next(((mod_name, class_name) - for config_name, mod_name, class_name in module_list - if name == config_name), None) - - if package == None: - import_module = importlib.import_module('photobooth.' + result[0]) - else: - import_module = importlib.import_module( - 'photobooth.' + package + '.' + result[0]) - - if result[1] == None: - return import_module - else: - return getattr(import_module, result[1]) - +from .util import lookup_and_import class CameraProcess(mp.Process): - def __init__(self, config, conn): + def __init__(self, config, conn, worker_queue): super().__init__() self.daemon = True self.cfg = config self.conn = conn + self.worker_queue = worker_queue def run_camera(self): - # while True: try: cap = lookup_and_import( camera.modules, self.cfg.get('Camera', 'module'), 'camera') - photobooth = Photobooth(self.cfg, cap, self.conn) + photobooth = Photobooth( + self.cfg, cap, self.conn, self.worker_queue) return photobooth.run() except BaseException as e: @@ -81,21 +63,39 @@ class CameraProcess(mp.Process): sys.exit(status_code) +class WorkerProcess(mp.Process): + + def __init__(self, config, queue): + + super().__init__() + self.daemon = True + + self.cfg = config + self.queue = queue + + + def run(self): + + print('Started Worker') + print('Exit Worker') + + class GuiProcess(mp.Process): - def __init__(self, argv, config, conn): + def __init__(self, argv, config, conn, queue): super().__init__() self.argv = argv self.cfg = config self.conn = conn + self.queue = queue def run(self): Gui = lookup_and_import(gui.modules, self.cfg.get('Gui', 'module'), 'gui') - sys.exit(Gui(self.argv, self.cfg).run(self.conn)) + sys.exit(Gui(self.argv, self.cfg).run(self.conn, self.queue)) def run(argv): @@ -105,19 +105,23 @@ def run(argv): config = Config('photobooth.cfg') gui_conn, camera_conn = mp.Pipe() + worker_queue = mp.SimpleQueue() - camera_worker = CameraProcess(config, camera_conn) - camera_worker.start() + camera_proc = CameraProcess(config, camera_conn, worker_queue) + camera_proc.start() - gui_worker = GuiProcess(argv, config, gui_conn) - gui_worker.start() + worker_proc = WorkerProcess(config, worker_queue) + worker_proc.start() + + gui_proc = GuiProcess(argv, config, gui_conn, worker_queue) + gui_proc.start() gui_conn.close() camera_conn.close() - gui_worker.join() - camera_worker.join(5) - return gui_worker.exitcode + gui_proc.join() + camera_proc.join(5) + return gui_proc.exitcode def main(argv): diff --git a/photobooth/util.py b/photobooth/util.py new file mode 100644 index 0000000..ad42e60 --- /dev/null +++ b/photobooth/util.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + + +import importlib + + +def lookup_and_import(module_list, name, package=None): + + result = next(((mod_name, class_name) + for config_name, mod_name, class_name in module_list + if name == config_name), None) + + if package == None: + import_module = importlib.import_module('photobooth.' + result[0]) + else: + import_module = importlib.import_module( + 'photobooth.' + package + '.' + result[0]) + + if result[1] == None: + return import_module + else: + return getattr(import_module, result[1])