Timings moved to GUI. Thread infrastructure for photobooth thread improved
This commit is contained in:
@@ -10,13 +10,23 @@ from .PictureDimensions import PictureDimensions
|
|||||||
|
|
||||||
from . import gui
|
from . import gui
|
||||||
|
|
||||||
|
|
||||||
|
class TeardownException(Exception):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
|
||||||
class Photobooth:
|
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.initCamera(config, camera)
|
||||||
self.initGpio(config)
|
self.initGpio(config)
|
||||||
self.initTimings(config)
|
|
||||||
|
|
||||||
self.triggerOff()
|
self.triggerOff()
|
||||||
|
|
||||||
@@ -55,19 +65,44 @@ class Photobooth:
|
|||||||
self._lampOff = lambda : None
|
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):
|
def teardown(self):
|
||||||
|
|
||||||
|
print('Camera teardown')
|
||||||
self.triggerOff()
|
self.triggerOff()
|
||||||
self.setCameraIdle()
|
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
|
@property
|
||||||
def getNextFilename(self):
|
def getNextFilename(self):
|
||||||
|
|
||||||
@@ -80,24 +115,6 @@ class Photobooth:
|
|||||||
return self._show_counter
|
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):
|
def initRun(self):
|
||||||
|
|
||||||
self.setCameraIdle()
|
self.setCameraIdle()
|
||||||
@@ -105,45 +122,26 @@ class Photobooth:
|
|||||||
self.triggerOn()
|
self.triggerOn()
|
||||||
|
|
||||||
|
|
||||||
def run(self, send, recv):
|
def run(self):
|
||||||
|
|
||||||
self._send = send
|
|
||||||
self._recv = recv
|
|
||||||
self.initRun()
|
self.initRun()
|
||||||
|
|
||||||
while True:
|
try:
|
||||||
try:
|
while True:
|
||||||
event = self._recv.recv()
|
try:
|
||||||
|
self.recvTriggered()
|
||||||
|
except EOFError:
|
||||||
|
return 0
|
||||||
|
|
||||||
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))
|
|
||||||
except EOFError:
|
|
||||||
return 1
|
|
||||||
else:
|
|
||||||
try:
|
try:
|
||||||
self.trigger()
|
self.trigger()
|
||||||
except RuntimeError as e:
|
except RuntimeError as e:
|
||||||
print('Camera error: ' + str(e))
|
print('Camera error: ' + str(e))
|
||||||
self._send.send( gui.ErrorState('Camera error', str(e)) )
|
self._send.send( gui.ErrorState('Camera error', str(e)) )
|
||||||
event = self._recv.recv()
|
self.recvAck()
|
||||||
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))
|
|
||||||
|
|
||||||
return 0
|
except TeardownException:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
|
||||||
def setCameraActive(self):
|
def setCameraActive(self):
|
||||||
@@ -159,46 +157,20 @@ class Photobooth:
|
|||||||
|
|
||||||
def showCounterPreview(self):
|
def showCounterPreview(self):
|
||||||
|
|
||||||
tic, toc = time(), 0
|
|
||||||
|
|
||||||
self._send.send(gui.CountdownState())
|
self._send.send(gui.CountdownState())
|
||||||
|
|
||||||
while not self._recv.poll():
|
while not self._recv.poll():
|
||||||
toc = time() - tic
|
self._send.send(
|
||||||
self._send.send( gui.PreviewState(
|
gui.PreviewState(picture = ImageOps.mirror(self._cap.getPreview())) )
|
||||||
message = str(self.countdownTime - int(toc)),
|
|
||||||
picture = ImageOps.mirror(self._cap.getPreview()) ) )
|
|
||||||
|
|
||||||
event = self._recv.recv()
|
self.recvAck()
|
||||||
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))
|
|
||||||
|
|
||||||
|
|
||||||
def showCounterNoPreview(self):
|
def showCounterNoPreview(self):
|
||||||
|
|
||||||
self._send.send(gui.CountdownState())
|
self._send.send(gui.CountdownState())
|
||||||
|
self.recvAck()
|
||||||
for i in range(self.countdownTime):
|
print('ack received')
|
||||||
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))
|
|
||||||
|
|
||||||
|
|
||||||
def showPose(self):
|
def showPose(self):
|
||||||
@@ -213,6 +185,11 @@ class Photobooth:
|
|||||||
return self._cap.getPicture()
|
return self._cap.getPicture()
|
||||||
|
|
||||||
|
|
||||||
|
def capturePictures(self):
|
||||||
|
|
||||||
|
return [ self.captureSinglePicture() for _ in range(self._pic_dims.totalNumPictures) ]
|
||||||
|
|
||||||
|
|
||||||
def assemblePictures(self, pictures):
|
def assemblePictures(self, pictures):
|
||||||
|
|
||||||
output_image = Image.new('RGB', self._pic_dims.outputSize, (255, 255, 255))
|
output_image = Image.new('RGB', self._pic_dims.outputSize, (255, 255, 255))
|
||||||
@@ -224,26 +201,13 @@ class Photobooth:
|
|||||||
return output_image
|
return output_image
|
||||||
|
|
||||||
|
|
||||||
def capturePictures(self):
|
|
||||||
|
|
||||||
return [ self.captureSinglePicture() for _ in range(self._pic_dims.totalNumPictures) ]
|
|
||||||
|
|
||||||
|
|
||||||
def trigger(self):
|
def trigger(self):
|
||||||
|
|
||||||
self._send.send(gui.GreeterState())
|
self._send.send(gui.GreeterState())
|
||||||
self.triggerOff()
|
self.triggerOff()
|
||||||
self.setCameraActive()
|
self.setCameraActive()
|
||||||
|
|
||||||
event = self._recv.recv()
|
self.recvAck()
|
||||||
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))
|
|
||||||
|
|
||||||
pics = self.capturePictures()
|
pics = self.capturePictures()
|
||||||
self._send.send(gui.AssembleState())
|
self._send.send(gui.AssembleState())
|
||||||
@@ -254,15 +218,7 @@ class Photobooth:
|
|||||||
|
|
||||||
self.setCameraIdle()
|
self.setCameraIdle()
|
||||||
|
|
||||||
event = self._recv.recv()
|
self.recvAck()
|
||||||
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._send.send(gui.IdleState())
|
self._send.send(gui.IdleState())
|
||||||
self.triggerOn()
|
self.triggerOn()
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ class PyQt5Gui(Gui):
|
|||||||
def handleKeypressEvent(self, event):
|
def handleKeypressEvent(self, event):
|
||||||
|
|
||||||
if event.key() == Qt.Key_Escape:
|
if event.key() == Qt.Key_Escape:
|
||||||
self.showStart()
|
# self.showStart()
|
||||||
|
self.handleState(TeardownState())
|
||||||
elif event.key() == Qt.Key_Space:
|
elif event.key() == Qt.Key_Space:
|
||||||
# self._transport.send('triggered')
|
# self._transport.send('triggered')
|
||||||
self.handleState(TriggerState())
|
self.handleState(TriggerState())
|
||||||
@@ -70,7 +71,8 @@ class PyQt5Gui(Gui):
|
|||||||
def handleKeypressEventNoTrigger(self, event):
|
def handleKeypressEventNoTrigger(self, event):
|
||||||
|
|
||||||
if event.key() == Qt.Key_Escape:
|
if event.key() == Qt.Key_Escape:
|
||||||
self.showStart()
|
# self.showStart()
|
||||||
|
self.handleState(TeardownState())
|
||||||
|
|
||||||
|
|
||||||
def handleState(self, state):
|
def handleState(self, state):
|
||||||
@@ -80,7 +82,7 @@ class PyQt5Gui(Gui):
|
|||||||
|
|
||||||
if isinstance(state, IdleState):
|
if isinstance(state, IdleState):
|
||||||
self.showIdle()
|
self.showIdle()
|
||||||
|
|
||||||
elif isinstance(state, TriggerState):
|
elif isinstance(state, TriggerState):
|
||||||
self._transport.send('triggered')
|
self._transport.send('triggered')
|
||||||
|
|
||||||
@@ -95,11 +97,11 @@ class PyQt5Gui(Gui):
|
|||||||
QTimer.singleShot(cfg.getInt('Photobooth', 'greeter_time') * 1000, lambda : self._transport.send('ack'))
|
QTimer.singleShot(cfg.getInt('Photobooth', 'greeter_time') * 1000, lambda : self._transport.send('ack'))
|
||||||
|
|
||||||
elif isinstance(state, CountdownState):
|
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):
|
elif isinstance(state, PreviewState):
|
||||||
img = ImageQt.ImageQt(state.picture)
|
self._p.centralWidget().picture = ImageQt.ImageQt(state.picture)
|
||||||
self._p.setCentralWidget(PyQt5PictureMessage(state.message, img))
|
self._p.centralWidget().update()
|
||||||
|
|
||||||
elif isinstance(state, PoseState):
|
elif isinstance(state, PoseState):
|
||||||
self._p.setCentralWidget(PyQt5PictureMessage('Pose!'))
|
self._p.setCentralWidget(PyQt5PictureMessage('Pose!'))
|
||||||
@@ -631,6 +633,74 @@ class PyQt5WaitMessage(QFrame):
|
|||||||
self.update()
|
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):
|
class PyQt5PictureMessage(QFrame):
|
||||||
|
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ class CountdownState(GuiState):
|
|||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
class PreviewState(MessageState, PictureState):
|
class PreviewState(PictureState):
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ def start_photobooth(config, send, recv):
|
|||||||
Camera = lookup_and_import(camera.modules, config.get('Camera', 'module'), 'camera')
|
Camera = lookup_and_import(camera.modules, config.get('Camera', 'module'), 'camera')
|
||||||
|
|
||||||
with Camera() as cap:
|
with Camera() as cap:
|
||||||
photobooth = Photobooth(config, cap)
|
photobooth = Photobooth(config, cap, send, recv)
|
||||||
return photobooth.run(send, recv)
|
return photobooth.run()
|
||||||
|
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
send.send( gui.ErrorState('Camera error', str(e)) )
|
send.send( gui.ErrorState('Camera error', str(e)) )
|
||||||
@@ -58,13 +58,13 @@ def main_photobooth(config, send, recv):
|
|||||||
event = recv.recv()
|
event = recv.recv()
|
||||||
|
|
||||||
if str(event) != 'start':
|
if str(event) != 'start':
|
||||||
print('Unknown event received: ' + str(event))
|
continue
|
||||||
raise RuntimeError('Unknown event received', str(event))
|
|
||||||
|
|
||||||
exit_status = start_photobooth(config, send, recv)
|
status_code = start_photobooth(config, send, recv)
|
||||||
|
print('Camera exit')
|
||||||
|
|
||||||
if exit_status != -1:
|
if status_code != -1:
|
||||||
return exit_status
|
return status_code
|
||||||
|
|
||||||
|
|
||||||
def run(argv):
|
def run(argv):
|
||||||
@@ -80,7 +80,10 @@ def run(argv):
|
|||||||
photobooth.start()
|
photobooth.start()
|
||||||
|
|
||||||
Gui = lookup_and_import(gui.modules, config.get('Gui', 'module'), 'gui')
|
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):
|
def main(argv):
|
||||||
|
|||||||
Reference in New Issue
Block a user