From cb5c2d687016b4ba57de4c7647378a7a4f071c6a Mon Sep 17 00:00:00 2001 From: Balthasar Reuter Date: Tue, 17 Apr 2018 23:45:17 +0200 Subject: [PATCH] Flexible module system for Camera and Gui implementations added --- photobooth/Photobooth.py | 68 +++++++++++-------- photobooth/{ => camera}/CameraGphoto2.py | 2 +- photobooth/{ => camera}/CameraGphoto2Cffi.py | 2 +- .../{ => camera}/CameraGphoto2CommandLine.py | 4 +- photobooth/{ => camera}/CameraOpenCV.py | 5 +- photobooth/{Camera.py => camera/__init__.py} | 11 ++- photobooth/defaults.cfg | 2 + photobooth/{ => gui}/PyQt5Gui.py | 20 +++--- photobooth/{Gui.py => gui/__init__.py} | 5 ++ 9 files changed, 73 insertions(+), 46 deletions(-) rename photobooth/{ => camera}/CameraGphoto2.py (98%) rename photobooth/{ => camera}/CameraGphoto2Cffi.py (97%) rename photobooth/{ => camera}/CameraGphoto2CommandLine.py (97%) rename photobooth/{ => camera}/CameraOpenCV.py (97%) rename photobooth/{Camera.py => camera/__init__.py} (79%) rename photobooth/{ => gui}/PyQt5Gui.py (97%) rename photobooth/{Gui.py => gui/__init__.py} (93%) diff --git a/photobooth/Photobooth.py b/photobooth/Photobooth.py index b242903..4fd31da 100644 --- a/photobooth/Photobooth.py +++ b/photobooth/Photobooth.py @@ -1,20 +1,19 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from Config import Config - -from PictureList import PictureList -from PictureDimensions import PictureDimensions - -import Gui -from PyQt5Gui import PyQt5Gui - -from PIL import Image, ImageOps - from multiprocessing import Pipe, Process from time import time, sleep, localtime, strftime +import importlib + +from PIL import Image, ImageOps + +from Config import Config +from PictureList import PictureList +from PictureDimensions import PictureDimensions + +import camera, gui class Photobooth: @@ -88,7 +87,7 @@ class Photobooth: self.trigger() except RuntimeError as e: print('Camera error: ' + str(e)) - self._send.send( Gui.ErrorState('Camera error', str(e)) ) + self._send.send( gui.ErrorState('Camera error', str(e)) ) return 0 @@ -109,7 +108,7 @@ class Photobooth: tic, toc = time(), 0 while toc < self.countdownTime: - self._send.send( Gui.PreviewState( + self._send.send( gui.PreviewState( message = str(self.countdownTime - int(toc)), picture = ImageOps.mirror(self._cap.getPreview()) ) ) toc = time() - tic @@ -118,7 +117,7 @@ class Photobooth: def showCounterNoPreview(self): for i in range(self.countdownTime): - self._send.send( Gui.PreviewState( + self._send.send( gui.PreviewState( message = str(i), picture = Image.new('RGB', (1,1), 'white') ) ) sleep(1) @@ -126,7 +125,7 @@ class Photobooth: def showPose(self): - self._send.send( Gui.PoseState() ) + self._send.send( gui.PoseState() ) def captureSinglePicture(self): @@ -155,43 +154,52 @@ class Photobooth: def trigger(self): - self._send.send(Gui.GreeterState()) + self._send.send(gui.GreeterState()) self.setCameraActive() sleep(self.greeterTime) img = self.capturePictures() img.save(self.getNextFilename(), 'JPEG') - self._send.send(Gui.PictureState(img)) + self._send.send(gui.PictureState(img)) self.setCameraIdle() sleep(self.displayTime) - self._send.send(Gui.IdleState()) + self._send.send(gui.IdleState()) + + +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) + print(result) + + if package == None: + import_module = importlib.import_module(result[0]) + else: + import_module = importlib.import_module('.' + result[0], package) + + if result[1] == None: + return import_module + else: + return getattr(import_module, result[1]) def main_photobooth(config, send, recv): while True: try: - if config.get('Camera', 'module') == 'python-gphoto2': - from CameraGphoto2 import CameraGphoto2 as Camera - elif config.get('Camera', 'module') == 'gphoto2-cffi': - from CameraGphoto2Cffi import CameraGphoto2Cffi as Camera - elif config.get('Camera', 'module') == 'gphoto2-commandline': - from CameraGphoto2CommandLine import CameraGphoto2CommandLine as Camera - elif config.get('Camera', 'module') == 'opencv': - from CameraOpenCV import CameraOpenCV as Camera - else: - raise ModuleNotFoundError('Unknown camera module "' + config.get('Camera', 'module') + '"') + Camera = lookup_and_import(camera.modules, config.get('Camera', 'module'), 'camera') with Camera() as cap: photobooth = Photobooth(config, cap) return photobooth.run(send, recv) except BaseException as e: - send.send( Gui.ErrorState('Camera error', str(e), True) ) + send.send( gui.ErrorState('Camera error', str(e)) ) event = recv.recv() if str(event) != 'ack': print('Unknown event received: ' + str(event)) @@ -208,8 +216,8 @@ def run(argv): photobooth = Process(target=main_photobooth, args=(config, gui_send, event_recv), daemon=True) photobooth.start() - gui = PyQt5Gui(argv, config) - return gui.run(event_send, gui_recv) + Gui = lookup_and_import(gui.modules, config.get('Gui', 'module'), 'gui') + return Gui(argv, config).run(event_send, gui_recv) def main(argv): diff --git a/photobooth/CameraGphoto2.py b/photobooth/camera/CameraGphoto2.py similarity index 98% rename from photobooth/CameraGphoto2.py rename to photobooth/camera/CameraGphoto2.py index 8fee51a..949a5d8 100644 --- a/photobooth/CameraGphoto2.py +++ b/photobooth/camera/CameraGphoto2.py @@ -7,7 +7,7 @@ from PIL import Image import gphoto2 as gp -from Camera import Camera +from . import Camera class CameraGphoto2(Camera): diff --git a/photobooth/CameraGphoto2Cffi.py b/photobooth/camera/CameraGphoto2Cffi.py similarity index 97% rename from photobooth/CameraGphoto2Cffi.py rename to photobooth/camera/CameraGphoto2Cffi.py index 6503427..2c32d1a 100644 --- a/photobooth/CameraGphoto2Cffi.py +++ b/photobooth/camera/CameraGphoto2Cffi.py @@ -7,7 +7,7 @@ from PIL import Image import gphoto2cffi as gp -from Camera import Camera +from . import Camera class CameraGphoto2Cffi(Camera): diff --git a/photobooth/CameraGphoto2CommandLine.py b/photobooth/camera/CameraGphoto2CommandLine.py similarity index 97% rename from photobooth/CameraGphoto2CommandLine.py rename to photobooth/camera/CameraGphoto2CommandLine.py index cb430eb..30513b7 100644 --- a/photobooth/CameraGphoto2CommandLine.py +++ b/photobooth/camera/CameraGphoto2CommandLine.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from Camera import Camera - from PIL import Image import os, subprocess +from . import Camera + class CameraGphoto2CommandLine(Camera): def __init__(self): diff --git a/photobooth/CameraOpenCV.py b/photobooth/camera/CameraOpenCV.py similarity index 97% rename from photobooth/CameraOpenCV.py rename to photobooth/camera/CameraOpenCV.py index 94f6816..8d56294 100644 --- a/photobooth/CameraOpenCV.py +++ b/photobooth/camera/CameraOpenCV.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -from Camera import Camera - from PIL import Image + import cv2 +from . import Camera + class CameraOpenCV(Camera): def __init__(self): diff --git a/photobooth/Camera.py b/photobooth/camera/__init__.py similarity index 79% rename from photobooth/Camera.py rename to photobooth/camera/__init__.py index 891823f..f83a877 100644 --- a/photobooth/Camera.py +++ b/photobooth/camera/__init__.py @@ -1,6 +1,15 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- + +# Available camera modules as tuples of (config name, module name, class name) +modules = ( + ('python-gphoto2', 'CameraGphoto2', 'CameraGphoto2'), + ('gphoto2-cffi', 'CameraGphoto2Cffi', 'CameraGphoto2Cffi'), + ('gphoto2-commandline', 'CameraGphoto2CommandLine', 'CameraGphoto2CommandLine'), + ('opencv', 'CameraOpenCV', 'CameraOpenCV') ) + + class Camera: def __init__(self): @@ -87,4 +96,4 @@ class Camera: if not self.hasIdle: raise RuntimeError('Camera does not support idle state') - raise NotImplementedError() \ No newline at end of file + raise NotImplementedError() diff --git a/photobooth/defaults.cfg b/photobooth/defaults.cfg index c4d1427..738b0f4 100644 --- a/photobooth/defaults.cfg +++ b/photobooth/defaults.cfg @@ -1,4 +1,6 @@ [Gui] +# Gui module to use (qt5) +module = qt5 # Start Photobooth in fullscreen mode (True/False) fullscreen = False # Width of Photobooth (if not fullscreen) diff --git a/photobooth/PyQt5Gui.py b/photobooth/gui/PyQt5Gui.py similarity index 97% rename from photobooth/PyQt5Gui.py rename to photobooth/gui/PyQt5Gui.py index bd2c514..59e4c35 100644 --- a/photobooth/PyQt5Gui.py +++ b/photobooth/gui/PyQt5Gui.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -import Gui +# import Gui from PIL import ImageQt @@ -9,7 +9,9 @@ from PyQt5.QtCore import Qt, QObject, QThread, pyqtSignal from PyQt5.QtWidgets import (QApplication, QCheckBox, QComboBox, QFormLayout, QFrame, QGridLayout, QGroupBox, QHBoxLayout, QLabel, QLayout, QLineEdit, QMainWindow, QMessageBox, QPushButton, QVBoxLayout) from PyQt5.QtGui import QImage, QPainter, QPixmap -class PyQt5Gui(Gui.Gui): +from . import * + +class PyQt5Gui(Gui): def __init__(self, argv, config): @@ -67,26 +69,26 @@ class PyQt5Gui(Gui.Gui): def handleState(self, state): - if not isinstance(state, Gui.GuiState): + if not isinstance(state, GuiState): raise ValueError('Invalid data received') - if isinstance(state, Gui.IdleState): + if isinstance(state, IdleState): self.showIdle() - elif isinstance(state, Gui.GreeterState): + elif isinstance(state, GreeterState): global cfg num_pictures = ( cfg.getInt('Picture', 'num_x') * cfg.getInt('Picture', 'num_y') ) self._p.setCentralWidget( PyQt5PictureMessage('Will capture {} pictures!'.format(num_pictures))) - elif isinstance(state, Gui.PreviewState): + elif isinstance(state, PreviewState): img = ImageQt.ImageQt(state.picture) self._p.setCentralWidget(PyQt5PictureMessage(state.message, img)) - elif isinstance(state, Gui.PoseState): + elif isinstance(state, PoseState): self._p.setCentralWidget(PyQt5PictureMessage('Pose!')) - elif isinstance(state, Gui.PictureState): + elif isinstance(state, PictureState): img = ImageQt.ImageQt(state.picture) self._p.setCentralWidget(PyQt5PictureMessage('', img)) - elif isinstance(state, Gui.ErrorState): + elif isinstance(state, ErrorState): self.showError(state.title, state.message) else: raise ValueError('Unknown state') diff --git a/photobooth/Gui.py b/photobooth/gui/__init__.py similarity index 93% rename from photobooth/Gui.py rename to photobooth/gui/__init__.py index 6b91fb0..3ef54bc 100644 --- a/photobooth/Gui.py +++ b/photobooth/gui/__init__.py @@ -1,6 +1,11 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- + +# Available gui modules as tuples of (config name, module name, class name) +modules = ( ('qt5', 'PyQt5Gui', 'PyQt5Gui'), ) + + class Gui: def __init__(self):