Timings moved to GUI. Thread infrastructure for photobooth thread improved

This commit is contained in:
Balthasar Reuter
2018-05-10 01:04:20 +02:00
parent b30412960a
commit 8b2cc27647
4 changed files with 154 additions and 125 deletions

View File

@@ -10,13 +10,23 @@ from .PictureDimensions import PictureDimensions
from . import gui
class TeardownException(Exception):
def __init__(self):
super().__init__()
class Photobooth:
def __init__(self, config, camera):
def __init__(self, config, camera, send, recv):
self._send = send
self._recv = recv
self.initCamera(config, camera)
self.initGpio(config)
self.initTimings(config)
self.triggerOff()
@@ -55,19 +65,44 @@ class Photobooth:
self._lampOff = lambda : None
def initTimings(self, config):
self._greeter_time = config.getInt('Photobooth', 'greeter_time')
self._countdown_time = config.getInt('Photobooth', 'countdown_time')
self._display_time = config.getInt('Photobooth', 'display_time')
def teardown(self):
print('Camera teardown')
self.triggerOff()
self.setCameraIdle()
def recvEvent(self, expected):
event = self._recv.recv()
try:
event_idx = expected.index(str(event))
except ValueError:
print('Photobooth: Unknown event received: ' + str(event))
raise ValueError('Unknown event received', str(event))
return event_idx
def recvAck(self):
events = ['ack', 'cancel', 'teardown']
if self.recvEvent(events) != 0:
print('Teardown of Photobooth requested')
raise TeardownException()
def recvTriggered(self):
events = ['triggered', 'teardown']
if self.recvEvent(events) != 0:
print('Teardown of Photobooth requested')
raise TeardownException()
@property
def getNextFilename(self):
@@ -80,24 +115,6 @@ class Photobooth:
return self._show_counter
@property
def greeterTime(self):
return self._greeter_time
@property
def countdownTime(self):
return self._countdown_time
@property
def displayTime(self):
return self._display_time
def initRun(self):
self.setCameraIdle()
@@ -105,45 +122,26 @@ class Photobooth:
self.triggerOn()
def run(self, send, recv):
def run(self):
self._send = send
self._recv = recv
self.initRun()
try:
while True:
try:
event = self._recv.recv()
if str(event) == 'start':
print('Camera already started')
self.initRun()
continue
elif str(event) == 'teardown':
self.teardown()
return -1
elif str(event) != 'triggered':
print('Unknown event received: ' + str(event))
raise RuntimeError('Unknown event received', str(event))
self.recvTriggered()
except EOFError:
return 1
else:
return 0
try:
self.trigger()
except RuntimeError as e:
print('Camera error: ' + str(e))
self._send.send( gui.ErrorState('Camera error', str(e)) )
event = self._recv.recv()
if str(event) == 'cancel':
self.teardown()
return 1
elif str(event) == 'ack':
pass
else:
print('Unknown event received: ' + str(event))
raise RuntimeError('Unknown event received', str(event))
self.recvAck()
return 0
except TeardownException:
return -1
def setCameraActive(self):
@@ -159,46 +157,20 @@ class Photobooth:
def showCounterPreview(self):
tic, toc = time(), 0
self._send.send(gui.CountdownState())
while not self._recv.poll():
toc = time() - tic
self._send.send( gui.PreviewState(
message = str(self.countdownTime - int(toc)),
picture = ImageOps.mirror(self._cap.getPreview()) ) )
self._send.send(
gui.PreviewState(picture = ImageOps.mirror(self._cap.getPreview())) )
event = self._recv.recv()
if str(event) == 'cancel':
self.teardown()
return 1
elif str(event) == 'ack':
pass
else:
print('Unknown event received: ' + str(event))
raise RuntimeError('Unknown event received', str(event))
self.recvAck()
def showCounterNoPreview(self):
self._send.send(gui.CountdownState())
for i in range(self.countdownTime):
self._send.send( gui.PreviewState(
message = str(self.countdownTime - i),
picture = Image.new('RGB', (1,1), 'white') ) )
sleep(1)
event = self._recv.recv()
if str(event) == 'cancel':
self.teardown()
return 1
elif str(event) == 'ack':
pass
else:
print('Unknown event received: ' + str(event))
raise RuntimeError('Unknown event received', str(event))
self.recvAck()
print('ack received')
def showPose(self):
@@ -213,6 +185,11 @@ class Photobooth:
return self._cap.getPicture()
def capturePictures(self):
return [ self.captureSinglePicture() for _ in range(self._pic_dims.totalNumPictures) ]
def assemblePictures(self, pictures):
output_image = Image.new('RGB', self._pic_dims.outputSize, (255, 255, 255))
@@ -224,26 +201,13 @@ class Photobooth:
return output_image
def capturePictures(self):
return [ self.captureSinglePicture() for _ in range(self._pic_dims.totalNumPictures) ]
def trigger(self):
self._send.send(gui.GreeterState())
self.triggerOff()
self.setCameraActive()
event = self._recv.recv()
if str(event) == 'cancel':
self.teardown()
return 1
elif str(event) == 'ack':
pass
else:
print('Unknown event received: ' + str(event))
raise RuntimeError('Unknown event received', str(event))
self.recvAck()
pics = self.capturePictures()
self._send.send(gui.AssembleState())
@@ -254,15 +218,7 @@ class Photobooth:
self.setCameraIdle()
event = self._recv.recv()
if str(event) == 'cancel':
self.teardown()
return 1
elif str(event) == 'ack':
pass
else:
print('Unknown event received: ' + str(event))
raise RuntimeError('Unknown event received', str(event))
self.recvAck()
self._send.send(gui.IdleState())
self.triggerOn()

View File

@@ -61,7 +61,8 @@ class PyQt5Gui(Gui):
def handleKeypressEvent(self, event):
if event.key() == Qt.Key_Escape:
self.showStart()
# self.showStart()
self.handleState(TeardownState())
elif event.key() == Qt.Key_Space:
# self._transport.send('triggered')
self.handleState(TriggerState())
@@ -70,7 +71,8 @@ class PyQt5Gui(Gui):
def handleKeypressEventNoTrigger(self, event):
if event.key() == Qt.Key_Escape:
self.showStart()
# self.showStart()
self.handleState(TeardownState())
def handleState(self, state):
@@ -95,11 +97,11 @@ class PyQt5Gui(Gui):
QTimer.singleShot(cfg.getInt('Photobooth', 'greeter_time') * 1000, lambda : self._transport.send('ack'))
elif isinstance(state, CountdownState):
QTimer.singleShot(cfg.getInt('Photobooth', 'countdown_time') * 1000, lambda : self._transport.send('ack'))
self._p.setCentralWidget(PyQt5CountdownMessage(cfg.getInt('Photobooth', 'countdown_time'), lambda : self._transport.send('ack')))
elif isinstance(state, PreviewState):
img = ImageQt.ImageQt(state.picture)
self._p.setCentralWidget(PyQt5PictureMessage(state.message, img))
self._p.centralWidget().picture = ImageQt.ImageQt(state.picture)
self._p.centralWidget().update()
elif isinstance(state, PoseState):
self._p.setCentralWidget(PyQt5PictureMessage('Pose!'))
@@ -631,6 +633,74 @@ class PyQt5WaitMessage(QFrame):
self.update()
class PyQt5CountdownMessage(QFrame):
def __init__(self, time, action):
super().__init__()
self._counter = time
self._action = action
self._picture = None
self.initFrame()
def initFrame(self):
self.setStyleSheet('background-color: white;')
@property
def counter(self):
return self._counter
@property
def picture(self):
return self._picture
@picture.setter
def picture(self, pic):
if not isinstance(pic, QImage):
raise ValueError('picture must be a QImage')
self._picture = pic
def paintEvent(self, event):
painter = QPainter(self)
if self._picture != None:
pix = QPixmap.fromImage(self._picture)
pix = pix.scaled(self.rect().size(), Qt.KeepAspectRatio, Qt.FastTransformation)
origin = ( (self.rect().width() - pix.width()) // 2,
(self.rect().height() - pix.height()) // 2 )
painter.drawPixmap(QPoint(*origin), pix)
painter.drawText(event.rect(), Qt.AlignCenter, str(self.counter))
painter.end()
def showEvent(self, event):
self._timer = self.startTimer(1000)
def timerEvent(self, event):
self._counter -= 1
self.update()
if self._counter == 0:
self.killTimer(self._timer)
self._action()
class PyQt5PictureMessage(QFrame):

View File

@@ -150,7 +150,7 @@ class CountdownState(GuiState):
super().__init__(**kwargs)
class PreviewState(MessageState, PictureState):
class PreviewState(PictureState):
def __init__(self, **kwargs):

View File

@@ -39,8 +39,8 @@ def start_photobooth(config, send, recv):
Camera = lookup_and_import(camera.modules, config.get('Camera', 'module'), 'camera')
with Camera() as cap:
photobooth = Photobooth(config, cap)
return photobooth.run(send, recv)
photobooth = Photobooth(config, cap, send, recv)
return photobooth.run()
except BaseException as e:
send.send( gui.ErrorState('Camera error', str(e)) )
@@ -58,13 +58,13 @@ def main_photobooth(config, send, recv):
event = recv.recv()
if str(event) != 'start':
print('Unknown event received: ' + str(event))
raise RuntimeError('Unknown event received', str(event))
continue
exit_status = start_photobooth(config, send, recv)
status_code = start_photobooth(config, send, recv)
print('Camera exit')
if exit_status != -1:
return exit_status
if status_code != -1:
return status_code
def run(argv):
@@ -80,7 +80,10 @@ def run(argv):
photobooth.start()
Gui = lookup_and_import(gui.modules, config.get('Gui', 'module'), 'gui')
return Gui(argv, config).run(event_send, gui_recv)
status_code = Gui(argv, config).run(event_send, gui_recv)
photobooth.join(1)
return status_code
def main(argv):