Gui refactored
This commit is contained in:
@@ -7,7 +7,7 @@ from PIL import Image, ImageOps
|
|||||||
|
|
||||||
from .PictureDimensions import PictureDimensions
|
from .PictureDimensions import PictureDimensions
|
||||||
|
|
||||||
from . import gui
|
from .gui import GuiState
|
||||||
|
|
||||||
from .Worker import PictureSaver
|
from .Worker import PictureSaver
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ class Photobooth:
|
|||||||
def initRun(self):
|
def initRun(self):
|
||||||
|
|
||||||
self.setCameraIdle()
|
self.setCameraIdle()
|
||||||
self._conn.send(gui.IdleState())
|
self._conn.send(GuiState.IdleState())
|
||||||
self.triggerOn()
|
self.triggerOn()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@@ -131,7 +131,7 @@ class Photobooth:
|
|||||||
self.trigger()
|
self.trigger()
|
||||||
except RuntimeError as e:
|
except RuntimeError as e:
|
||||||
logging.error('Camera error: %s', str(e))
|
logging.error('Camera error: %s', str(e))
|
||||||
self._conn.send(gui.ErrorState('Camera error', str(e)))
|
self._conn.send(GuiState.ErrorState('Cam error', str(e)))
|
||||||
self.recvAck()
|
self.recvAck()
|
||||||
|
|
||||||
except TeardownException:
|
except TeardownException:
|
||||||
@@ -149,22 +149,22 @@ class Photobooth:
|
|||||||
|
|
||||||
def showCountdownPreview(self):
|
def showCountdownPreview(self):
|
||||||
|
|
||||||
self._conn.send(gui.CountdownState())
|
self._conn.send(GuiState.CountdownState())
|
||||||
|
|
||||||
while not self._conn.poll():
|
while not self._conn.poll():
|
||||||
picture = ImageOps.mirror(self._cap.getPreview())
|
picture = ImageOps.mirror(self._cap.getPreview())
|
||||||
self._conn.send(gui.PreviewState(picture=picture))
|
self._conn.send(GuiState.PreviewState(picture=picture))
|
||||||
|
|
||||||
self.recvAck()
|
self.recvAck()
|
||||||
|
|
||||||
def showCountdownNoPreview(self):
|
def showCountdownNoPreview(self):
|
||||||
|
|
||||||
self._conn.send(gui.CountdownState())
|
self._conn.send(GuiState.CountdownState())
|
||||||
self.recvAck()
|
self.recvAck()
|
||||||
|
|
||||||
def showPose(self, num_picture):
|
def showPose(self, num_picture):
|
||||||
|
|
||||||
self._conn.send(gui.PoseState(num_picture))
|
self._conn.send(GuiState.PoseState(num_picture))
|
||||||
|
|
||||||
def captureSinglePicture(self, num_picture):
|
def captureSinglePicture(self, num_picture):
|
||||||
|
|
||||||
@@ -199,24 +199,24 @@ class Photobooth:
|
|||||||
|
|
||||||
logging.info('Photobooth triggered')
|
logging.info('Photobooth triggered')
|
||||||
|
|
||||||
self._conn.send(gui.GreeterState())
|
self._conn.send(GuiState.GreeterState())
|
||||||
self.triggerOff()
|
self.triggerOff()
|
||||||
|
|
||||||
self.setCameraActive()
|
self.setCameraActive()
|
||||||
self.recvAck()
|
self.recvAck()
|
||||||
|
|
||||||
pics = self.capturePictures()
|
pics = self.capturePictures()
|
||||||
self._conn.send(gui.AssembleState())
|
self._conn.send(GuiState.AssembleState())
|
||||||
|
|
||||||
img = self.assemblePictures(pics)
|
img = self.assemblePictures(pics)
|
||||||
self._conn.send(gui.PictureState(img))
|
self._conn.send(GuiState.ReviewState(picture=img))
|
||||||
|
|
||||||
self.enqueueWorkerTasks(img)
|
self.enqueueWorkerTasks(img)
|
||||||
|
|
||||||
self.setCameraIdle()
|
self.setCameraIdle()
|
||||||
self.recvAck()
|
self.recvAck()
|
||||||
|
|
||||||
self._conn.send(gui.IdleState())
|
self._conn.send(GuiState.IdleState())
|
||||||
self.triggerOn()
|
self.triggerOn()
|
||||||
|
|
||||||
def gpioTrigger(self):
|
def gpioTrigger(self):
|
||||||
@@ -225,7 +225,7 @@ class Photobooth:
|
|||||||
|
|
||||||
def gpioExit(self):
|
def gpioExit(self):
|
||||||
|
|
||||||
self._conn.send(gui.TeardownState())
|
self._conn.send(GuiState.TeardownState())
|
||||||
|
|
||||||
def triggerOff(self):
|
def triggerOff(self):
|
||||||
|
|
||||||
@@ -235,4 +235,4 @@ class Photobooth:
|
|||||||
def triggerOn(self):
|
def triggerOn(self):
|
||||||
|
|
||||||
self._lampOn()
|
self._lampOn()
|
||||||
self._gpioTrigger = lambda: self._conn.send(gui.TriggerState())
|
self._gpioTrigger = lambda: self._conn.send(GuiState.TriggerState())
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class PrintPostprocess(GuiPostprocess):
|
|||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
Printer = lookup_and_import(printer.modules, printer_module, 'printer')
|
Printer = lookup_and_import(printer.modules, printer_module, 'printer')
|
||||||
self._printer = Printer(page_size)
|
self._printer = Printer(page_size, True)
|
||||||
|
|
||||||
def get(self, picture):
|
def get(self, picture):
|
||||||
|
|
||||||
|
|||||||
170
photobooth/gui/GuiSkeleton.py
Normal file
170
photobooth/gui/GuiSkeleton.py
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import GuiState
|
||||||
|
|
||||||
|
|
||||||
|
class GuiSkeleton:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def idle(self):
|
||||||
|
|
||||||
|
return self._idle
|
||||||
|
|
||||||
|
@idle.setter
|
||||||
|
def idle(self, handle):
|
||||||
|
|
||||||
|
if not callable(handle):
|
||||||
|
raise ValueError('Function handle for "idle" must be callable')
|
||||||
|
|
||||||
|
self._idle = handle
|
||||||
|
|
||||||
|
@property
|
||||||
|
def trigger(self):
|
||||||
|
|
||||||
|
return self._trigger
|
||||||
|
|
||||||
|
@trigger.setter
|
||||||
|
def trigger(self, handle):
|
||||||
|
|
||||||
|
if not callable(handle):
|
||||||
|
raise ValueError('Function handle for "trigger" must be callable')
|
||||||
|
|
||||||
|
self._trigger = handle
|
||||||
|
|
||||||
|
@property
|
||||||
|
def greeter(self):
|
||||||
|
|
||||||
|
return self._greeter
|
||||||
|
|
||||||
|
@greeter.setter
|
||||||
|
def greeter(self, handle):
|
||||||
|
|
||||||
|
if not callable(handle):
|
||||||
|
raise ValueError('Function handle for "greeter" must be callable')
|
||||||
|
|
||||||
|
self._greeter = handle
|
||||||
|
|
||||||
|
@property
|
||||||
|
def countdown(self):
|
||||||
|
|
||||||
|
return self._countdown
|
||||||
|
|
||||||
|
@countdown.setter
|
||||||
|
def countdown(self, handle):
|
||||||
|
|
||||||
|
if not callable(handle):
|
||||||
|
raise ValueError(('Function handle for "countdown" must be '
|
||||||
|
'callable'))
|
||||||
|
|
||||||
|
self._countdown = handle
|
||||||
|
|
||||||
|
@property
|
||||||
|
def preview(self):
|
||||||
|
|
||||||
|
return self._preview
|
||||||
|
|
||||||
|
@preview.setter
|
||||||
|
def preview(self, handle):
|
||||||
|
|
||||||
|
if not callable(handle):
|
||||||
|
raise ValueError('Function handle for "preview" must be callable')
|
||||||
|
|
||||||
|
self._preview = handle
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pose(self):
|
||||||
|
|
||||||
|
return self._pose
|
||||||
|
|
||||||
|
@pose.setter
|
||||||
|
def pose(self, handle):
|
||||||
|
|
||||||
|
if not callable(handle):
|
||||||
|
raise ValueError('Function handle for "pose" must be callable')
|
||||||
|
|
||||||
|
self._pose = handle
|
||||||
|
|
||||||
|
@property
|
||||||
|
def assemble(self):
|
||||||
|
|
||||||
|
return self._assemble
|
||||||
|
|
||||||
|
@assemble.setter
|
||||||
|
def assemble(self, handle):
|
||||||
|
|
||||||
|
if not callable(handle):
|
||||||
|
raise ValueError('Function handle for "assemble" must be callable')
|
||||||
|
|
||||||
|
self._assemble = handle
|
||||||
|
|
||||||
|
@property
|
||||||
|
def review(self):
|
||||||
|
|
||||||
|
return self._review
|
||||||
|
|
||||||
|
@review.setter
|
||||||
|
def review(self, handle):
|
||||||
|
|
||||||
|
if not callable(handle):
|
||||||
|
raise ValueError('Function handle for "review" must be callable')
|
||||||
|
|
||||||
|
self._review = handle
|
||||||
|
|
||||||
|
@property
|
||||||
|
def teardown(self):
|
||||||
|
|
||||||
|
return self._teardown
|
||||||
|
|
||||||
|
@teardown.setter
|
||||||
|
def teardown(self, handle):
|
||||||
|
|
||||||
|
if not callable(handle):
|
||||||
|
raise ValueError('Function handle for "teardown" must be callable')
|
||||||
|
|
||||||
|
self._teardown = handle
|
||||||
|
|
||||||
|
@property
|
||||||
|
def error(self):
|
||||||
|
|
||||||
|
return self._error
|
||||||
|
|
||||||
|
@error.setter
|
||||||
|
def error(self, handle):
|
||||||
|
|
||||||
|
if not callable(handle):
|
||||||
|
raise ValueError('Function handle for "error" must be callable')
|
||||||
|
|
||||||
|
self._error = handle
|
||||||
|
|
||||||
|
def handleState(self, state):
|
||||||
|
|
||||||
|
if not isinstance(state, GuiState.GuiState):
|
||||||
|
raise ValueError('Not a GuiState object received')
|
||||||
|
|
||||||
|
if isinstance(state, GuiState.IdleState):
|
||||||
|
self.idle(state)
|
||||||
|
elif isinstance(state, GuiState.TriggerState):
|
||||||
|
self.trigger(state)
|
||||||
|
elif isinstance(state, GuiState.GreeterState):
|
||||||
|
self.greeter(state)
|
||||||
|
elif isinstance(state, GuiState.CountdownState):
|
||||||
|
self.countdown(state)
|
||||||
|
elif isinstance(state, GuiState.PreviewState):
|
||||||
|
self.preview(state)
|
||||||
|
elif isinstance(state, GuiState.PoseState):
|
||||||
|
self.pose(state)
|
||||||
|
elif isinstance(state, GuiState.AssembleState):
|
||||||
|
self.assemble(state)
|
||||||
|
elif isinstance(state, GuiState.ReviewState):
|
||||||
|
self.review(state)
|
||||||
|
elif isinstance(state, GuiState.TeardownState):
|
||||||
|
self.teardown(state)
|
||||||
|
elif isinstance(state, GuiState.ErrorState):
|
||||||
|
self.error(state)
|
||||||
|
else:
|
||||||
|
raise ValueError('Unknown state received')
|
||||||
@@ -144,6 +144,13 @@ class PreviewState(PictureState):
|
|||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class ReviewState(PictureState):
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
class TeardownState(GuiState):
|
class TeardownState(GuiState):
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
|||||||
@@ -1,318 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import multiprocessing as mp
|
|
||||||
import queue
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from PIL import ImageQt
|
|
||||||
|
|
||||||
from PyQt5 import QtGui, QtCore, QtWidgets
|
|
||||||
|
|
||||||
import math
|
|
||||||
|
|
||||||
from .Qt5Gui import Frames
|
|
||||||
|
|
||||||
from .PyQt5GuiHelpers import QRoundProgressBar
|
|
||||||
|
|
||||||
from . import *
|
|
||||||
|
|
||||||
|
|
||||||
class PyQt5Gui(Gui):
|
|
||||||
|
|
||||||
def __init__(self, argv, config):
|
|
||||||
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
global cfg
|
|
||||||
cfg = config
|
|
||||||
|
|
||||||
self._app = QtWidgets.QApplication(argv)
|
|
||||||
self._p = PyQt5MainWindow()
|
|
||||||
self._lastState = self.showStart
|
|
||||||
|
|
||||||
self._postprocessList = []
|
|
||||||
self._postprocessQueue = queue.Queue()
|
|
||||||
|
|
||||||
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, worker_queue):
|
|
||||||
|
|
||||||
receiver = PyQt5Receiver([camera_conn])
|
|
||||||
receiver.notify.connect(self.handleState)
|
|
||||||
receiver.start()
|
|
||||||
|
|
||||||
self._conn = camera_conn
|
|
||||||
self._queue = worker_queue
|
|
||||||
|
|
||||||
self.showStart()
|
|
||||||
|
|
||||||
exit_code = self._app.exec_()
|
|
||||||
self._p = None
|
|
||||||
|
|
||||||
return exit_code
|
|
||||||
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
|
|
||||||
self._p.close()
|
|
||||||
|
|
||||||
|
|
||||||
def restart(self):
|
|
||||||
|
|
||||||
self._app.exit(123)
|
|
||||||
|
|
||||||
|
|
||||||
def sendAck(self):
|
|
||||||
|
|
||||||
self._conn.send('ack')
|
|
||||||
|
|
||||||
|
|
||||||
def sendCancel(self):
|
|
||||||
|
|
||||||
self._conn.send('cancel')
|
|
||||||
|
|
||||||
|
|
||||||
def sendTrigger(self):
|
|
||||||
|
|
||||||
self._conn.send('triggered')
|
|
||||||
|
|
||||||
|
|
||||||
def sendTeardown(self):
|
|
||||||
|
|
||||||
self._conn.send('teardown')
|
|
||||||
|
|
||||||
|
|
||||||
def handleKeypressEvent(self, event):
|
|
||||||
|
|
||||||
if event.key() == QtCore.Qt.Key_Escape:
|
|
||||||
self.handleState(TeardownState())
|
|
||||||
elif event.key() == QtCore.Qt.Key_Space:
|
|
||||||
self.handleState(TriggerState())
|
|
||||||
|
|
||||||
|
|
||||||
def handleKeypressEventNoTrigger(self, event):
|
|
||||||
|
|
||||||
if event.key() == QtCore.Qt.Key_Escape:
|
|
||||||
self.handleState(TeardownState())
|
|
||||||
|
|
||||||
|
|
||||||
def handleState(self, state):
|
|
||||||
|
|
||||||
if not isinstance(state, GuiState):
|
|
||||||
raise ValueError('Invalid data received')
|
|
||||||
|
|
||||||
if isinstance(state, IdleState):
|
|
||||||
self.showIdle()
|
|
||||||
|
|
||||||
elif isinstance(state, TriggerState):
|
|
||||||
self.sendTrigger()
|
|
||||||
|
|
||||||
elif isinstance(state, GreeterState):
|
|
||||||
global cfg
|
|
||||||
self._p.handleKeypressEvent = self.handleKeypressEventNoTrigger
|
|
||||||
# self._p.setCentralWidget( PyQt5GreeterMessage(
|
|
||||||
self._p.setCentralWidget( Frames.GreeterMessage(
|
|
||||||
cfg.getInt('Picture', 'num_x'), cfg.getInt('Picture', 'num_y') ) )
|
|
||||||
QtCore.QTimer.singleShot(cfg.getInt('Photobooth', 'greeter_time') * 1000, self.sendAck)
|
|
||||||
|
|
||||||
elif isinstance(state, CountdownState):
|
|
||||||
# self._p.setCentralWidget(PyQt5CountdownMessage(cfg.getInt('Photobooth', 'countdown_time'), self.sendAck))
|
|
||||||
countdown_time = cfg.getInt('Photobooth', 'countdown_time')
|
|
||||||
self._p.setCentralWidget(Frames.CountdownMessage(countdown_time,
|
|
||||||
self.sendAck))
|
|
||||||
|
|
||||||
elif isinstance(state, PreviewState):
|
|
||||||
self._p.centralWidget().picture = ImageQt.ImageQt(state.picture)
|
|
||||||
self._p.centralWidget().update()
|
|
||||||
|
|
||||||
elif isinstance(state, PoseState):
|
|
||||||
# self._p.setCentralWidget(PyQt5PoseMessage())
|
|
||||||
self._p.setCentralWidget(Frames.PoseMessage(state.num_picture,
|
|
||||||
cfg.getInt('Picture', 'num_x'), cfg.getInt('Picture', 'num_y')))
|
|
||||||
|
|
||||||
elif isinstance(state, AssembleState):
|
|
||||||
self._p.setCentralWidget(Frames.WaitMessage('Processing picture...'))
|
|
||||||
# self._p.setCentralWidget(PyQt5WaitMessage('Processing picture...'))
|
|
||||||
|
|
||||||
elif isinstance(state, PictureState):
|
|
||||||
img = ImageQt.ImageQt(state.picture)
|
|
||||||
# self._p.setCentralWidget(PyQt5PictureMessage(img))
|
|
||||||
self._p.setCentralWidget(Frames.PictureMessage(img))
|
|
||||||
QtCore.QTimer.singleShot(cfg.getInt('Photobooth', 'display_time') * 1000,
|
|
||||||
lambda : self.postprocessPicture(state.picture))
|
|
||||||
|
|
||||||
elif isinstance(state, TeardownState):
|
|
||||||
self._conn.send('teardown')
|
|
||||||
self.showStart()
|
|
||||||
|
|
||||||
elif isinstance(state, ErrorState):
|
|
||||||
self.showError(state.title, state.message)
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise ValueError('Unknown state')
|
|
||||||
|
|
||||||
|
|
||||||
def postprocessPicture(self, picture):
|
|
||||||
|
|
||||||
for task in self._postprocessList:
|
|
||||||
self._postprocessQueue.put(task.get(picture))
|
|
||||||
|
|
||||||
self.handleQueue()
|
|
||||||
|
|
||||||
|
|
||||||
def handleQueue(self):
|
|
||||||
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
task = self._postprocessQueue.get(block = False)
|
|
||||||
except queue.Empty:
|
|
||||||
self.sendAck()
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
if isinstance(task, PrintState):
|
|
||||||
reply = QtWidgets.QMessageBox.question(self._p, 'Print picture?',
|
|
||||||
'Do you want to print the picture?',
|
|
||||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
|
|
||||||
if reply == QtWidgets.QMessageBox.Yes:
|
|
||||||
task.handler()
|
|
||||||
QtWidgets.QMessageBox.information(self._p, 'Printing',
|
|
||||||
'Picture sent to printer.', QtWidgets.QMessageBox.Ok)
|
|
||||||
else:
|
|
||||||
raise ValueError('Unknown task')
|
|
||||||
|
|
||||||
|
|
||||||
def showStart(self):
|
|
||||||
|
|
||||||
self._p.handleKeypressEvent = lambda event : None
|
|
||||||
self._lastState = self.showStart
|
|
||||||
self._p.setCentralWidget(Frames.Start(self.showStartPhotobooth, self.showSettings, self.close))
|
|
||||||
if QtWidgets.QApplication.overrideCursor() != 0:
|
|
||||||
QtWidgets.QApplication.restoreOverrideCursor()
|
|
||||||
|
|
||||||
|
|
||||||
def showSettings(self):
|
|
||||||
|
|
||||||
global cfg
|
|
||||||
self._p.handleKeypressEvent = lambda event : None
|
|
||||||
self._lastState = self.showSettings
|
|
||||||
self._p.setCentralWidget(Frames.Settings(cfg, self.showSettings, self.showStart, self.restart))
|
|
||||||
|
|
||||||
|
|
||||||
def showStartPhotobooth(self):
|
|
||||||
|
|
||||||
self._lastState = self.showStartPhotobooth
|
|
||||||
self._conn.send('start')
|
|
||||||
# self._p.setCentralWidget(PyQt5WaitMessage('Starting the photobooth...'))
|
|
||||||
self._p.setCentralWidget(Frames.WaitMessage('Starting the photobooth...'))
|
|
||||||
if cfg.getBool('Gui', 'hide_cursor'):
|
|
||||||
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.BlankCursor)
|
|
||||||
|
|
||||||
|
|
||||||
def showIdle(self):
|
|
||||||
|
|
||||||
self._p.handleKeypressEvent = self.handleKeypressEvent
|
|
||||||
self._lastState = self.showIdle
|
|
||||||
self._p.setCentralWidget(Frames.IdleMessage())
|
|
||||||
# self._p.setCentralWidget(PyQt5IdleMessage())
|
|
||||||
|
|
||||||
|
|
||||||
def showError(self, title, message):
|
|
||||||
|
|
||||||
logging.error('%s: %s', title, message)
|
|
||||||
reply = QtWidgets.QMessageBox.warning(self._p, title, message,
|
|
||||||
QtWidgets.QMessageBox.Close | QtWidgets.QMessageBox.Retry, QtWidgets.QMessageBox.Retry)
|
|
||||||
if reply == QtWidgets.QMessageBox.Retry:
|
|
||||||
self.sendAck()
|
|
||||||
self._lastState()
|
|
||||||
else:
|
|
||||||
self.sendCancel()
|
|
||||||
self.showStart()
|
|
||||||
|
|
||||||
|
|
||||||
class PyQt5Receiver(QtCore.QThread):
|
|
||||||
|
|
||||||
notify = QtCore.pyqtSignal(object)
|
|
||||||
|
|
||||||
def __init__(self, conn):
|
|
||||||
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self._conn = conn
|
|
||||||
|
|
||||||
|
|
||||||
def handle(self, state):
|
|
||||||
|
|
||||||
self.notify.emit(state)
|
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
|
|
||||||
while self._conn:
|
|
||||||
for c in mp.connection.wait(self._conn):
|
|
||||||
try:
|
|
||||||
state = c.recv()
|
|
||||||
except EOFError:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
self.handle(state)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PyQt5MainWindow(QtWidgets.QMainWindow):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self.handleKeypressEvent = lambda event : None
|
|
||||||
|
|
||||||
self.initUI()
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def handleKeypressEvent(self):
|
|
||||||
|
|
||||||
return self._handle_key
|
|
||||||
|
|
||||||
|
|
||||||
@handleKeypressEvent.setter
|
|
||||||
def handleKeypressEvent(self, func):
|
|
||||||
|
|
||||||
if not callable(func):
|
|
||||||
raise ValueError('Keypress event handler must be callable')
|
|
||||||
|
|
||||||
self._handle_key = func
|
|
||||||
|
|
||||||
|
|
||||||
def initUI(self):
|
|
||||||
|
|
||||||
global cfg
|
|
||||||
|
|
||||||
self.setWindowTitle('Photobooth')
|
|
||||||
|
|
||||||
if cfg.getBool('Gui', 'fullscreen'):
|
|
||||||
self.showFullScreen()
|
|
||||||
else:
|
|
||||||
self.resize(cfg.getInt('Gui', 'width'),
|
|
||||||
cfg.getInt('Gui', 'height'))
|
|
||||||
self.show()
|
|
||||||
|
|
||||||
|
|
||||||
def closeEvent(self, e):
|
|
||||||
|
|
||||||
reply = QtWidgets.QMessageBox.question(self, 'Confirmation', "Quit Photobooth?",
|
|
||||||
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No)
|
|
||||||
|
|
||||||
if reply == QtWidgets.QMessageBox.Yes:
|
|
||||||
e.accept()
|
|
||||||
else:
|
|
||||||
e.ignore()
|
|
||||||
|
|
||||||
|
|
||||||
def keyPressEvent(self, event):
|
|
||||||
|
|
||||||
self.handleKeypressEvent(event)
|
|
||||||
@@ -1,310 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Adaptation of QRoundProgressBar from
|
|
||||||
# https://sourceforge.net/projects/qroundprogressbar/
|
|
||||||
# to PyQt5, using the PyQt4-version offered at
|
|
||||||
# https://stackoverflow.com/a/33583019
|
|
||||||
|
|
||||||
from math import ceil
|
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, Qt, QtWidgets
|
|
||||||
|
|
||||||
|
|
||||||
class QRoundProgressBar(QtWidgets.QWidget):
|
|
||||||
|
|
||||||
StyleDonut = 1
|
|
||||||
StylePie = 2
|
|
||||||
StyleLine = 3
|
|
||||||
|
|
||||||
PositionLeft = 180
|
|
||||||
PositionTop = 90
|
|
||||||
PositionRight = 0
|
|
||||||
PositionBottom = -90
|
|
||||||
|
|
||||||
UF_VALUE = 1
|
|
||||||
UF_PERCENT = 2
|
|
||||||
UF_MAX = 4
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
self.min = 0
|
|
||||||
self.max = 100
|
|
||||||
self.value = 25
|
|
||||||
|
|
||||||
self.nullPosition = self.PositionTop
|
|
||||||
self.barStyle = self.StyleDonut
|
|
||||||
self.outlinePenWidth = 1
|
|
||||||
self.dataPenWidth = 1
|
|
||||||
self.rebuildBrush = False
|
|
||||||
self.format = "%p%"
|
|
||||||
self.decimals = 1
|
|
||||||
self.updateFlags = self.UF_PERCENT
|
|
||||||
self.gradientData = []
|
|
||||||
self.donutThicknessRatio = 0.75
|
|
||||||
|
|
||||||
def setRange(self, min, max):
|
|
||||||
self.min = min
|
|
||||||
self.max = max
|
|
||||||
|
|
||||||
if self.max < self.min:
|
|
||||||
self.max, self.min = self.min, self.max
|
|
||||||
|
|
||||||
if self.value < self.min:
|
|
||||||
self.value = self.min
|
|
||||||
elif self.value > self.max:
|
|
||||||
self.value = self.max
|
|
||||||
|
|
||||||
if not self.gradientData:
|
|
||||||
self.rebuildBrush = True
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def setMinimum(self, min):
|
|
||||||
self.setRange(min, self.max)
|
|
||||||
|
|
||||||
def setMaximum(self, max):
|
|
||||||
self.setRange(self.min, max)
|
|
||||||
|
|
||||||
def setValue(self, val):
|
|
||||||
if self.value != val:
|
|
||||||
if val < self.min:
|
|
||||||
self.value = self.min
|
|
||||||
elif val > self.max:
|
|
||||||
self.value = self.max
|
|
||||||
else:
|
|
||||||
self.value = val
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def setNullPosition(self, position):
|
|
||||||
if position != self.nullPosition:
|
|
||||||
self.nullPosition = position
|
|
||||||
if not self.gradientData:
|
|
||||||
self.rebuildBrush = True
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def setBarStyle(self, style):
|
|
||||||
if style != self.barStyle:
|
|
||||||
self.barStyle = style
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def setOutlinePenWidth(self, penWidth):
|
|
||||||
if penWidth != self.outlinePenWidth:
|
|
||||||
self.outlinePenWidth = penWidth
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def setDataPenWidth(self, penWidth):
|
|
||||||
if penWidth != self.dataPenWidth:
|
|
||||||
self.dataPenWidth = penWidth
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def setDataColors(self, stopPoints):
|
|
||||||
if stopPoints != self.gradientData:
|
|
||||||
self.gradientData = stopPoints
|
|
||||||
self.rebuildBrush = True
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def setFormat(self, format):
|
|
||||||
if format != self.format:
|
|
||||||
self.format = format
|
|
||||||
self.valueFormatChanged()
|
|
||||||
|
|
||||||
def resetFormat(self):
|
|
||||||
self.format = ''
|
|
||||||
self.valueFormatChanged()
|
|
||||||
|
|
||||||
def setDecimals(self, count):
|
|
||||||
if count >= 0 and count != self.decimals:
|
|
||||||
self.decimals = count
|
|
||||||
self.valueFormatChanged()
|
|
||||||
|
|
||||||
def setDonutThicknessRatio(self, val):
|
|
||||||
self.donutThicknessRatio = max(0., min(val, 1.))
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def paintEvent(self, event):
|
|
||||||
outerRadius = min(self.width(), self.height())
|
|
||||||
baseRect = QtCore.QRectF(1, 1, outerRadius-2, outerRadius-2)
|
|
||||||
|
|
||||||
buffer = QtGui.QImage(outerRadius, outerRadius,
|
|
||||||
QtGui.QImage.Format_ARGB32)
|
|
||||||
buffer.fill(0)
|
|
||||||
|
|
||||||
p = QtGui.QPainter(buffer)
|
|
||||||
p.setRenderHint(QtGui.QPainter.Antialiasing)
|
|
||||||
|
|
||||||
# data brush
|
|
||||||
self.rebuildDataBrushIfNeeded()
|
|
||||||
|
|
||||||
# background
|
|
||||||
# self.drawBackground(p, buffer.rect())
|
|
||||||
|
|
||||||
# base circle
|
|
||||||
self.drawBase(p, baseRect)
|
|
||||||
|
|
||||||
# data circle
|
|
||||||
arcStep = 360.0 / (self.max - self.min) * self.value
|
|
||||||
self.drawValue(p, baseRect, self.value, arcStep)
|
|
||||||
|
|
||||||
# center circle
|
|
||||||
innerRect, innerRadius = self.calculateInnerRect(baseRect, outerRadius)
|
|
||||||
self.drawInnerBackground(p, innerRect)
|
|
||||||
|
|
||||||
# text
|
|
||||||
self.drawText(p, innerRect, innerRadius, ceil(self.value))
|
|
||||||
|
|
||||||
# finally draw the bar
|
|
||||||
p.end()
|
|
||||||
|
|
||||||
painter = QtGui.QPainter(self)
|
|
||||||
painter.drawImage(0, 0, buffer)
|
|
||||||
|
|
||||||
def drawBackground(self, p, baseRect):
|
|
||||||
p.fillRect(baseRect, self.palette().window())
|
|
||||||
|
|
||||||
def drawBase(self, p, baseRect):
|
|
||||||
bs = self.barStyle
|
|
||||||
if bs == self.StyleDonut:
|
|
||||||
p.setPen(QtGui.QPen(self.palette().shadow().color(),
|
|
||||||
self.outlinePenWidth))
|
|
||||||
p.setBrush(self.palette().base())
|
|
||||||
p.drawEllipse(baseRect)
|
|
||||||
elif bs == self.StylePie:
|
|
||||||
p.setPen(QtGui.QPen(self.palette().base().color(),
|
|
||||||
self.outlinePenWidth))
|
|
||||||
p.setBrush(self.palette().base())
|
|
||||||
p.drawEllipse(baseRect)
|
|
||||||
elif bs == self.StyleLine:
|
|
||||||
color = self.palette().base().color()
|
|
||||||
color.setAlpha(100)
|
|
||||||
brush = self.palette().base()
|
|
||||||
brush.setColor(color)
|
|
||||||
p.setPen(QtGui.QPen(self.palette().base().color(),
|
|
||||||
self.outlinePenWidth))
|
|
||||||
p.setBrush(brush)
|
|
||||||
# p.drawEllipse(baseRect)
|
|
||||||
# p.setPen(QtGui.QPen(self.palette().base().color(),
|
|
||||||
# self.outlinePenWidth))
|
|
||||||
# p.setBrush(Qt.Qt.NoBrush)
|
|
||||||
p.drawEllipse(baseRect.adjusted(self.outlinePenWidth/2,
|
|
||||||
self.outlinePenWidth/2,
|
|
||||||
-self.outlinePenWidth/2,
|
|
||||||
-self.outlinePenWidth/2))
|
|
||||||
|
|
||||||
def drawValue(self, p, baseRect, value, arcLength):
|
|
||||||
# nothing to draw
|
|
||||||
if value == self.min:
|
|
||||||
return
|
|
||||||
|
|
||||||
# for Line style
|
|
||||||
if self.barStyle == self.StyleLine:
|
|
||||||
p.setPen(QtGui.QPen(self.palette().highlight().color(),
|
|
||||||
self.dataPenWidth))
|
|
||||||
p.setBrush(Qt.Qt.NoBrush)
|
|
||||||
p.drawArc(baseRect.adjusted(self.outlinePenWidth/2,
|
|
||||||
self.outlinePenWidth/2,
|
|
||||||
-self.outlinePenWidth/2,
|
|
||||||
-self.outlinePenWidth/2),
|
|
||||||
self.nullPosition * 16,
|
|
||||||
-arcLength * 16)
|
|
||||||
return
|
|
||||||
|
|
||||||
# for Pie and Donut styles
|
|
||||||
dataPath = QtGui.QPainterPath()
|
|
||||||
dataPath.setFillRule(Qt.Qt.WindingFill)
|
|
||||||
|
|
||||||
# pie segment outer
|
|
||||||
dataPath.moveTo(baseRect.center())
|
|
||||||
dataPath.arcTo(baseRect, self.nullPosition, -arcLength)
|
|
||||||
dataPath.lineTo(baseRect.center())
|
|
||||||
|
|
||||||
p.setBrush(self.palette().highlight())
|
|
||||||
p.setPen(QtGui.QPen(self.palette().shadow().color(),
|
|
||||||
self.dataPenWidth))
|
|
||||||
p.drawPath(dataPath)
|
|
||||||
|
|
||||||
def calculateInnerRect(self, baseRect, outerRadius):
|
|
||||||
# for Line style
|
|
||||||
if self.barStyle == self.StyleLine:
|
|
||||||
innerRadius = outerRadius - self.outlinePenWidth
|
|
||||||
else: # for Pie and Donut styles
|
|
||||||
innerRadius = outerRadius * self.donutThicknessRatio
|
|
||||||
|
|
||||||
delta = (outerRadius - innerRadius) / 2.
|
|
||||||
innerRect = QtCore.QRectF(delta, delta, innerRadius, innerRadius)
|
|
||||||
return innerRect, innerRadius
|
|
||||||
|
|
||||||
def drawInnerBackground(self, p, innerRect):
|
|
||||||
if self.barStyle == self.StyleDonut:
|
|
||||||
p.setBrush(self.palette().alternateBase())
|
|
||||||
|
|
||||||
cmod = p.compositionMode()
|
|
||||||
p.setCompositionMode(QtGui.QPainter.CompositionMode_Source)
|
|
||||||
|
|
||||||
p.drawEllipse(innerRect)
|
|
||||||
|
|
||||||
p.setCompositionMode(cmod)
|
|
||||||
|
|
||||||
def drawText(self, p, innerRect, innerRadius, value):
|
|
||||||
if not self.format:
|
|
||||||
return
|
|
||||||
|
|
||||||
text = self.valueToText(value)
|
|
||||||
|
|
||||||
# !!! to revise
|
|
||||||
f = self.font()
|
|
||||||
f.setPixelSize(innerRadius * 0.8 / len(text))
|
|
||||||
p.setFont(f)
|
|
||||||
|
|
||||||
textRect = innerRect
|
|
||||||
p.setPen(self.palette().text().color())
|
|
||||||
p.drawText(textRect, Qt.Qt.AlignCenter, text)
|
|
||||||
|
|
||||||
def valueToText(self, value):
|
|
||||||
textToDraw = self.format
|
|
||||||
|
|
||||||
format_string = '{' + ':.{}f'.format(self.decimals) + '}'
|
|
||||||
|
|
||||||
if self.updateFlags & self.UF_VALUE:
|
|
||||||
textToDraw = textToDraw.replace("%v", format_string.format(value))
|
|
||||||
|
|
||||||
if self.updateFlags & self.UF_PERCENT:
|
|
||||||
perc = (value - self.min) / (self.max - self.min) * 100.0
|
|
||||||
textToDraw = textToDraw.replace("%p", format_string.format(perc))
|
|
||||||
|
|
||||||
if self.updateFlags & self.UF_MAX:
|
|
||||||
m = self.max - self.min + 1
|
|
||||||
textToDraw = textToDraw.replace("%m", format_string.format(m))
|
|
||||||
|
|
||||||
return textToDraw
|
|
||||||
|
|
||||||
def valueFormatChanged(self):
|
|
||||||
self.updateFlags = 0
|
|
||||||
|
|
||||||
if "%v" in self.format:
|
|
||||||
self.updateFlags |= self.UF_VALUE
|
|
||||||
|
|
||||||
if "%p" in self.format:
|
|
||||||
self.updateFlags |= self.UF_PERCENT
|
|
||||||
|
|
||||||
if "%m" in self.format:
|
|
||||||
self.updateFlags |= self.UF_MAX
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
def rebuildDataBrushIfNeeded(self):
|
|
||||||
if self.rebuildBrush:
|
|
||||||
self.rebuildBrush = False
|
|
||||||
|
|
||||||
dataBrush = QtGui.QConicalGradient()
|
|
||||||
dataBrush.setCenter(0.5, 0.5)
|
|
||||||
dataBrush.setCoordinateMode(QtGui.QGradient.StretchToDeviceMode)
|
|
||||||
|
|
||||||
for pos, color in self.gradientData:
|
|
||||||
dataBrush.setColorAt(1.0 - pos, color)
|
|
||||||
|
|
||||||
# angle
|
|
||||||
dataBrush.setAngle(self.nullPosition)
|
|
||||||
|
|
||||||
p = self.palette()
|
|
||||||
p.setBrush(QtGui.QPalette.Highlight, dataBrush)
|
|
||||||
self.setPalette(p)
|
|
||||||
45
photobooth/gui/Qt5Gui/Postprocessor.py
Normal file
45
photobooth/gui/Qt5Gui/Postprocessor.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import queue
|
||||||
|
|
||||||
|
from .. import GuiState
|
||||||
|
from ..GuiPostprocess import PrintPostprocess
|
||||||
|
|
||||||
|
|
||||||
|
class Postprocessor:
|
||||||
|
|
||||||
|
def __init__(self, config):
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._task_list = []
|
||||||
|
self._queue = queue.Queue()
|
||||||
|
|
||||||
|
if config.getBool('Printer', 'enable'):
|
||||||
|
module = config.get('Printer', 'module')
|
||||||
|
size = (config.getInt('Printer', 'width'),
|
||||||
|
config.getInt('Printer', 'height'))
|
||||||
|
self._task_list.append(PrintPostprocess(module, size))
|
||||||
|
|
||||||
|
def fill(self, picture):
|
||||||
|
|
||||||
|
for task in self._task_list:
|
||||||
|
self._queue.put(task.get(picture))
|
||||||
|
|
||||||
|
def work(self, msg_box):
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
task = self._queue.get(block=False)
|
||||||
|
except queue.Empty:
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(task, GuiState.PrintState):
|
||||||
|
if msg_box.question('Print picture?',
|
||||||
|
'Do you want to print the picture?'):
|
||||||
|
task.handler()
|
||||||
|
msg_box.information('Printing...',
|
||||||
|
'Picture sent to printer.')
|
||||||
|
else:
|
||||||
|
raise ValueError('Unknown task')
|
||||||
278
photobooth/gui/Qt5Gui/PyQt5Gui.py
Normal file
278
photobooth/gui/Qt5Gui/PyQt5Gui.py
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from PyQt5 import QtCore
|
||||||
|
from PyQt5 import QtWidgets
|
||||||
|
|
||||||
|
from PIL import ImageQt
|
||||||
|
|
||||||
|
from .. import GuiState
|
||||||
|
from ..GuiSkeleton import GuiSkeleton
|
||||||
|
|
||||||
|
from . import Frames
|
||||||
|
from . import Postprocessor
|
||||||
|
from . import Receiver
|
||||||
|
|
||||||
|
|
||||||
|
class PyQt5Gui(GuiSkeleton):
|
||||||
|
|
||||||
|
def __init__(self, argv, config, camera_conn, worker_queue):
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._cfg = config
|
||||||
|
self._conn = camera_conn
|
||||||
|
|
||||||
|
self._registerCallbacks()
|
||||||
|
self._initUI(argv)
|
||||||
|
self._initReceiver()
|
||||||
|
|
||||||
|
self._postprocess = Postprocessor.Postprocessor(self._cfg)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
|
||||||
|
self._showWelcomeScreen()
|
||||||
|
exit_code = self._app.exec_()
|
||||||
|
self._gui = None
|
||||||
|
return exit_code
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
|
||||||
|
self._gui.close()
|
||||||
|
|
||||||
|
def restart(self):
|
||||||
|
|
||||||
|
self._app.exit(123)
|
||||||
|
|
||||||
|
def _registerCallbacks(self):
|
||||||
|
|
||||||
|
self.idle = self._showIdle
|
||||||
|
self.trigger = self._sendTrigger
|
||||||
|
self.greeter = self._showGreeter
|
||||||
|
self.countdown = self._showCountdown
|
||||||
|
self.preview = self._showPreview
|
||||||
|
self.pose = self._showPose
|
||||||
|
self.assemble = self._showAssemble
|
||||||
|
self.review = self._showReview
|
||||||
|
self.teardown = self._sendTeardown
|
||||||
|
self.error = self._showError
|
||||||
|
|
||||||
|
def _initUI(self, argv):
|
||||||
|
|
||||||
|
self._disableTrigger()
|
||||||
|
|
||||||
|
self._app = QtWidgets.QApplication(argv)
|
||||||
|
self._gui = PyQt5MainWindow(self._cfg, self._handleKeypressEvent)
|
||||||
|
|
||||||
|
def _initReceiver(self):
|
||||||
|
|
||||||
|
self._receiver = Receiver.Receiver([self._conn])
|
||||||
|
self._receiver.notify.connect(self.handleState)
|
||||||
|
self._receiver.start()
|
||||||
|
|
||||||
|
def _setWidget(self, widget):
|
||||||
|
|
||||||
|
self._gui.setCentralWidget(widget)
|
||||||
|
|
||||||
|
def _enableEscape(self):
|
||||||
|
|
||||||
|
self._is_escape = True
|
||||||
|
|
||||||
|
def _disableEscape(self):
|
||||||
|
|
||||||
|
self._is_escape = False
|
||||||
|
|
||||||
|
def _enableTrigger(self):
|
||||||
|
|
||||||
|
self._is_trigger = True
|
||||||
|
|
||||||
|
def _disableTrigger(self):
|
||||||
|
|
||||||
|
self._is_trigger = False
|
||||||
|
|
||||||
|
def _sendStart(self):
|
||||||
|
|
||||||
|
self._conn.send('start')
|
||||||
|
|
||||||
|
def _sendTrigger(self, state):
|
||||||
|
|
||||||
|
self._conn.send('triggered')
|
||||||
|
|
||||||
|
def _sendAck(self):
|
||||||
|
|
||||||
|
self._conn.send('ack')
|
||||||
|
|
||||||
|
def _sendCancel(self):
|
||||||
|
|
||||||
|
self._conn.send('cancel')
|
||||||
|
|
||||||
|
def _sendTeardown(self, state):
|
||||||
|
|
||||||
|
self._conn.send('teardown')
|
||||||
|
self._showWelcomeScreen()
|
||||||
|
|
||||||
|
def _handleKeypressEvent(self, event):
|
||||||
|
|
||||||
|
if self._is_escape and event.key() == QtCore.Qt.Key_Escape:
|
||||||
|
self.handleState(GuiState.TeardownState())
|
||||||
|
elif self._is_trigger and event.key() == QtCore.Qt.Key_Space:
|
||||||
|
self.handleState(GuiState.TriggerState())
|
||||||
|
|
||||||
|
def _postprocessPicture(self, picture):
|
||||||
|
|
||||||
|
self._postprocess.fill(picture)
|
||||||
|
self._postprocess.work(MessageBox(self._gui))
|
||||||
|
self._sendAck()
|
||||||
|
|
||||||
|
def _showWelcomeScreen(self):
|
||||||
|
|
||||||
|
self._disableTrigger()
|
||||||
|
self._disableEscape()
|
||||||
|
self._lastHandle = self._showWelcomeScreen
|
||||||
|
self._setWidget(Frames.Start(self._showStart, self._showSettings,
|
||||||
|
self.close))
|
||||||
|
if QtWidgets.QApplication.overrideCursor() != 0:
|
||||||
|
QtWidgets.QApplication.restoreOverrideCursor()
|
||||||
|
|
||||||
|
def _showSettings(self):
|
||||||
|
|
||||||
|
self._disableTrigger()
|
||||||
|
self._disableEscape()
|
||||||
|
self._lastHandle = self._showSettings
|
||||||
|
self._setWidget(Frames.Settings(self._cfg, self._showSettings,
|
||||||
|
self._showWelcomeScreen, self.restart))
|
||||||
|
|
||||||
|
def _showStart(self, state):
|
||||||
|
|
||||||
|
self._disableTrigger()
|
||||||
|
self._enableEscape()
|
||||||
|
self._lastHandle = self._showWelcomeScreen
|
||||||
|
self._sendStart()
|
||||||
|
self._setWidget(Frames.WaitMessage('Starting the photobooth...'))
|
||||||
|
if self._cfg.getBool('Gui', 'hide_cursor'):
|
||||||
|
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.BlankCursor)
|
||||||
|
|
||||||
|
def _showIdle(self, state):
|
||||||
|
|
||||||
|
self._enableEscape()
|
||||||
|
self._enableTrigger()
|
||||||
|
self._lastHandle = self._showIdle
|
||||||
|
self._setWidget(Frames.IdleMessage())
|
||||||
|
|
||||||
|
def _showGreeter(self, state):
|
||||||
|
|
||||||
|
self._enableEscape()
|
||||||
|
self._disableTrigger()
|
||||||
|
|
||||||
|
num_pic = (self._cfg.getInt('Picture', 'num_x'),
|
||||||
|
self._cfg.getInt('Picture', 'num_x'))
|
||||||
|
greeter_time = self._cfg.getInt('Photobooth', 'greeter_time') * 1000
|
||||||
|
|
||||||
|
self._setWidget(Frames.GreeterMessage(*num_pic))
|
||||||
|
QtCore.QTimer.singleShot(greeter_time, self._sendAck)
|
||||||
|
|
||||||
|
def _showCountdown(self, state):
|
||||||
|
|
||||||
|
countdown_time = self._cfg.getInt('Photobooth', 'countdown_time')
|
||||||
|
self._setWidget(Frames.CountdownMessage(countdown_time, self._sendAck))
|
||||||
|
|
||||||
|
def _showPreview(self, state):
|
||||||
|
|
||||||
|
self._gui.centralWidget().picture = ImageQt.ImageQt(state.picture)
|
||||||
|
self._gui.centralWidget().update()
|
||||||
|
|
||||||
|
def _showPose(self, state):
|
||||||
|
|
||||||
|
num_pic = (self._cfg.getInt('Picture', 'num_x'),
|
||||||
|
self._cfg.getInt('Picture', 'num_x'))
|
||||||
|
self._setWidget(Frames.PoseMessage(state.num_picture, *num_pic))
|
||||||
|
|
||||||
|
def _showAssemble(self, state):
|
||||||
|
|
||||||
|
self._setWidget(Frames.WaitMessage('Processing picture...'))
|
||||||
|
|
||||||
|
def _showReview(self, state):
|
||||||
|
|
||||||
|
img = ImageQt.ImageQt(state.picture)
|
||||||
|
review_time = self._cfg.getInt('Photobooth', 'display_time') * 1000
|
||||||
|
self._setWidget(Frames.PictureMessage(img))
|
||||||
|
QtCore.QTimer.singleShot(review_time, lambda:
|
||||||
|
self._postprocessPicture(state.picture))
|
||||||
|
|
||||||
|
def _showError(self, state):
|
||||||
|
|
||||||
|
logging.error('%s: %s', state.title, state.message)
|
||||||
|
reply = QtWidgets.QMessageBox.warning(self._gui, state.title,
|
||||||
|
state.message,
|
||||||
|
QtWidgets.QMessageBox.Close |
|
||||||
|
QtWidgets.QMessageBox.Retry,
|
||||||
|
QtWidgets.QMessageBox.Retry)
|
||||||
|
if reply == QtWidgets.QMessageBox.Retry:
|
||||||
|
self._sendAck()
|
||||||
|
self._lastState()
|
||||||
|
else:
|
||||||
|
self._sendCancel()
|
||||||
|
self._showWelcomeScreen()
|
||||||
|
|
||||||
|
|
||||||
|
class PyQt5MainWindow(QtWidgets.QMainWindow):
|
||||||
|
|
||||||
|
def __init__(self, config, keypress_handler):
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._cfg = config
|
||||||
|
self._handle_key = keypress_handler
|
||||||
|
self._initUI()
|
||||||
|
|
||||||
|
def _initUI(self):
|
||||||
|
|
||||||
|
self.setWindowTitle('Photobooth')
|
||||||
|
|
||||||
|
if self._cfg.getBool('Gui', 'fullscreen'):
|
||||||
|
self.showFullScreen()
|
||||||
|
else:
|
||||||
|
self.resize(self._cfg.getInt('Gui', 'width'),
|
||||||
|
self._cfg.getInt('Gui', 'height'))
|
||||||
|
self.show()
|
||||||
|
|
||||||
|
def closeEvent(self, e):
|
||||||
|
|
||||||
|
reply = QtWidgets.QMessageBox.question(self, 'Confirmation',
|
||||||
|
"Quit Photobooth?",
|
||||||
|
QtWidgets.QMessageBox.Yes |
|
||||||
|
QtWidgets.QMessageBox.No,
|
||||||
|
QtWidgets.QMessageBox.No)
|
||||||
|
|
||||||
|
if reply == QtWidgets.QMessageBox.Yes:
|
||||||
|
e.accept()
|
||||||
|
else:
|
||||||
|
e.ignore()
|
||||||
|
|
||||||
|
def keyPressEvent(self, event):
|
||||||
|
|
||||||
|
self._handle_key(event)
|
||||||
|
|
||||||
|
|
||||||
|
class MessageBox:
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._parent = parent
|
||||||
|
|
||||||
|
def question(self, title, message):
|
||||||
|
|
||||||
|
reply = QtWidgets.QMessageBox.question(self._parent, title, message,
|
||||||
|
QtWidgets.QMessageBox.Yes |
|
||||||
|
QtWidgets.QMessageBox.No,
|
||||||
|
QtWidgets.QMessageBox.No)
|
||||||
|
return reply == QtWidgets.QMessageBox.Yes
|
||||||
|
|
||||||
|
def information(self, title, message):
|
||||||
|
|
||||||
|
QtWidgets.QMessageBox.information(self._parent, title, message,
|
||||||
|
QtWidgets.QMessageBox.Ok)
|
||||||
31
photobooth/gui/Qt5Gui/Receiver.py
Normal file
31
photobooth/gui/Qt5Gui/Receiver.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import multiprocessing as mp
|
||||||
|
|
||||||
|
from PyQt5 import QtCore
|
||||||
|
|
||||||
|
|
||||||
|
class Receiver(QtCore.QThread):
|
||||||
|
|
||||||
|
notify = QtCore.pyqtSignal(object)
|
||||||
|
|
||||||
|
def __init__(self, conn):
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
self._conn = conn
|
||||||
|
|
||||||
|
def handle(self, state):
|
||||||
|
|
||||||
|
self.notify.emit(state)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
|
||||||
|
while self._conn:
|
||||||
|
for c in mp.connection.wait(self._conn):
|
||||||
|
try:
|
||||||
|
state = c.recv()
|
||||||
|
except EOFError:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.handle(state)
|
||||||
4
photobooth/gui/Qt5Gui/__init__.py
Normal file
4
photobooth/gui/Qt5Gui/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from .PyQt5Gui import PyQt5Gui # noqa
|
||||||
@@ -1,19 +1,21 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from .GuiState import * # noqa
|
# from .GuiState import * # noqa
|
||||||
from .GuiPostprocess import * # noqa
|
# from .GuiPostprocess import * # noqa
|
||||||
|
|
||||||
|
from . import GuiState # noqa
|
||||||
|
|
||||||
# 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 = (('PyQt5', 'PyQt5Gui', 'PyQt5Gui'), )
|
modules = (('PyQt5', 'Qt5Gui', 'PyQt5Gui'), )
|
||||||
|
|
||||||
|
|
||||||
class Gui:
|
# class Gui:
|
||||||
|
|
||||||
def __init__(self):
|
# def __init__(self):
|
||||||
|
|
||||||
pass
|
# pass
|
||||||
|
|
||||||
def run(self, camera_conn, worker_queue):
|
# def run(self, camera_conn, worker_queue):
|
||||||
|
|
||||||
raise NotImplementedError()
|
# raise NotImplementedError()
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class CameraProcess(mp.Process):
|
|||||||
|
|
||||||
def run_camera(self):
|
def run_camera(self):
|
||||||
|
|
||||||
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')
|
||||||
|
|
||||||
@@ -40,14 +40,14 @@ class CameraProcess(mp.Process):
|
|||||||
self.cfg, cap, self.conn, self.worker_queue)
|
self.cfg, cap, self.conn, self.worker_queue)
|
||||||
return photobooth.run()
|
return photobooth.run()
|
||||||
|
|
||||||
except BaseException as e:
|
# except BaseException as e:
|
||||||
self.conn.send(gui.ErrorState('Camera error', str(e)))
|
# self.conn.send(gui.GuiState.ErrorState('Camera error', str(e)))
|
||||||
event = self.conn.recv()
|
# event = self.conn.recv()
|
||||||
if str(event) in ('cancel', 'ack'):
|
# if str(event) in ('cancel', 'ack'):
|
||||||
return 123
|
# return 123
|
||||||
else:
|
# else:
|
||||||
logging.error('Unknown event received: %s', str(event))
|
# logging.error('Unknown event received: %s', str(event))
|
||||||
raise RuntimeError('Unknown event received', str(event))
|
# raise RuntimeError('Unknown event received', str(event))
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ class GuiProcess(mp.Process):
|
|||||||
|
|
||||||
Gui = lookup_and_import(gui.modules, self.cfg.get('Gui', 'module'),
|
Gui = lookup_and_import(gui.modules, self.cfg.get('Gui', 'module'),
|
||||||
'gui')
|
'gui')
|
||||||
sys.exit(Gui(self.argv, self.cfg).run(self.conn, self.queue))
|
sys.exit(Gui(self.argv, self.cfg, self.conn, self.queue).run())
|
||||||
|
|
||||||
|
|
||||||
def run(argv):
|
def run(argv):
|
||||||
|
|||||||
Reference in New Issue
Block a user