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
|
||||
|
||||
|
||||
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()
|
||||
|
||||
while True:
|
||||
try:
|
||||
event = self._recv.recv()
|
||||
try:
|
||||
while True:
|
||||
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:
|
||||
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()
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ class CountdownState(GuiState):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
|
||||
class PreviewState(MessageState, PictureState):
|
||||
class PreviewState(PictureState):
|
||||
|
||||
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')
|
||||
|
||||
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):
|
||||
|
||||
Reference in New Issue
Block a user