Gui Postprocessing with basic printing support

This commit is contained in:
Balthasar Reuter
2018-05-11 21:46:36 +02:00
parent 4d34d49e51
commit 62b167ad83
7 changed files with 272 additions and 182 deletions

View File

@@ -20,9 +20,10 @@ class TeardownException(Exception):
class Photobooth: class Photobooth:
def __init__(self, config, camera, conn): def __init__(self, config, camera, conn, queue):
self._conn = conn self._conn = conn
self._queue = queue
self.initCamera(config, camera()) self.initCamera(config, camera())
self.initGpio(config) self.initGpio(config)

View File

@@ -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)

146
photobooth/gui/GuiState.py Normal file
View File

@@ -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)

View File

@@ -33,17 +33,22 @@ class PyQt5Gui(Gui):
self._app = QApplication(argv) self._app = QApplication(argv)
self._p = PyQt5MainWindow() self._p = PyQt5MainWindow()
self._lastState = self.showStart 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 = PyQt5Receiver([camera_conn])
receiver.notify.connect(self.handleState) receiver.notify.connect(self.handleState)
receiver.start() receiver.start()
self._conn = camera_conn self._conn = camera_conn
self._queue = worker_queue
self.showStart() self.showStart()
@@ -134,9 +139,11 @@ class PyQt5Gui(Gui):
elif isinstance(state, PictureState): elif isinstance(state, PictureState):
img = ImageQt.ImageQt(state.picture) img = ImageQt.ImageQt(state.picture)
self._p.setCentralWidget(PyQt5PictureMessage('', img)) 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): elif isinstance(state, TeardownState):
self._conn.send('teardown') self._conn.send('teardown')
@@ -149,6 +156,14 @@ class PyQt5Gui(Gui):
raise ValueError('Unknown state') raise ValueError('Unknown state')
def postprocessPicture(self, picture):
for task in self._postprocessList:
task.do(self._p, picture)
self.sendAck()
def showStart(self): def showStart(self):
self._p.handleKeypressEvent = lambda event : None self._p.handleKeypressEvent = lambda event : None

View File

@@ -2,6 +2,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .GuiState import *
from .GuiPostprocess import *
# Available gui modules as tuples of (config name, module name, class name) # Available gui modules as tuples of (config name, module name, class name)
modules = ( ('qt5', 'PyQt5Gui', 'PyQt5Gui'), ) modules = ( ('qt5', 'PyQt5Gui', 'PyQt5Gui'), )
@@ -13,151 +17,8 @@ class Gui:
pass pass
def run(self, send, recv): def run(self, camera_conn, worker_queue):
raise NotImplementedError() 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)

View File

@@ -8,51 +8,33 @@ except DistributionNotFound:
__version__ = 'unknown' __version__ = 'unknown'
import multiprocessing as mp import multiprocessing as mp
import importlib
import sys import sys
from . import camera, gui from . import camera, gui
from .Config import Config from .Config import Config
from .Photobooth import Photobooth from .Photobooth import Photobooth
from .util import lookup_and_import
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])
class CameraProcess(mp.Process): class CameraProcess(mp.Process):
def __init__(self, config, conn): def __init__(self, config, conn, worker_queue):
super().__init__() super().__init__()
self.daemon = True self.daemon = True
self.cfg = config self.cfg = config
self.conn = conn self.conn = conn
self.worker_queue = worker_queue
def run_camera(self): def run_camera(self):
# while True:
try: try:
cap = lookup_and_import( cap = lookup_and_import(
camera.modules, self.cfg.get('Camera', 'module'), 'camera') 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() return photobooth.run()
except BaseException as e: except BaseException as e:
@@ -81,21 +63,39 @@ class CameraProcess(mp.Process):
sys.exit(status_code) 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): class GuiProcess(mp.Process):
def __init__(self, argv, config, conn): def __init__(self, argv, config, conn, queue):
super().__init__() super().__init__()
self.argv = argv self.argv = argv
self.cfg = config self.cfg = config
self.conn = conn self.conn = conn
self.queue = queue
def run(self): def run(self):
Gui = lookup_and_import(gui.modules, self.cfg.get('Gui', 'module'), 'gui') 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): def run(argv):
@@ -105,19 +105,23 @@ def run(argv):
config = Config('photobooth.cfg') config = Config('photobooth.cfg')
gui_conn, camera_conn = mp.Pipe() gui_conn, camera_conn = mp.Pipe()
worker_queue = mp.SimpleQueue()
camera_worker = CameraProcess(config, camera_conn) camera_proc = CameraProcess(config, camera_conn, worker_queue)
camera_worker.start() camera_proc.start()
gui_worker = GuiProcess(argv, config, gui_conn) worker_proc = WorkerProcess(config, worker_queue)
gui_worker.start() worker_proc.start()
gui_proc = GuiProcess(argv, config, gui_conn, worker_queue)
gui_proc.start()
gui_conn.close() gui_conn.close()
camera_conn.close() camera_conn.close()
gui_worker.join() gui_proc.join()
camera_worker.join(5) camera_proc.join(5)
return gui_worker.exitcode return gui_proc.exitcode
def main(argv): def main(argv):

23
photobooth/util.py Normal file
View File

@@ -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])