Rudimentary support for new communication scheme in GUI

This commit is contained in:
Balthasar Reuter
2018-07-16 00:31:24 +02:00
parent ce9597ceed
commit 2a59d27437
9 changed files with 178 additions and 186 deletions

View File

@@ -55,6 +55,10 @@ class Context:
self.state = ErrorState(event.exception, self.state) self.state = ErrorState(event.exception, self.state)
elif isinstance(event, TeardownEvent): elif isinstance(event, TeardownEvent):
self.state = TeardownState(event.target) self.state = TeardownState(event.target)
if event.target == TeardownEvent.EXIT:
return 0
elif event.target == TeardownEvent.RESTART:
return 123
else: else:
self.state.handleEvent(event, self) self.state.handleEvent(event, self)
@@ -206,7 +210,7 @@ class ErrorState(State):
context.state = self.old_state context.state = self.old_state
context.state.update() context.state.update()
elif isinstance(event, GuiEvent) and event.name == 'abort': elif isinstance(event, GuiEvent) and event.name == 'abort':
context.state = TeardownState() context.state = TeardownState(TeardownEvent.WELCOME)
else: else:
raise TypeError('Unknown Event type "{}"'.format(event)) raise TypeError('Unknown Event type "{}"'.format(event))
@@ -216,11 +220,27 @@ class TeardownState(State):
def __init__(self, target): def __init__(self, target):
super().__init__() super().__init__()
self._target = target
def __str__(self): def __str__(self):
return 'TeardownState' return 'TeardownState'
@property
def target(self):
return self._target
def handleEvent(self, event, context):
if self._target == TeardownEvent.WELCOME:
if isinstance(event, GuiEvent) and event.name == 'welcome':
context.state = WelcomeState()
else:
raise ValueError('Unknown GuiEvent "{}"'.format(event.name))
else:
raise TypeError('Unknown Event type "{}"'.format(event))
class WelcomeState(State): class WelcomeState(State):
@@ -237,34 +257,14 @@ class WelcomeState(State):
if isinstance(event, GuiEvent): if isinstance(event, GuiEvent):
if event.name == 'start': if event.name == 'start':
context.state = StartupState() context.state = StartupState()
elif event.name == 'settings':
context.state = SettingsState()
elif event.name == 'exit': elif event.name == 'exit':
context.state = TeardownState() context.state = TeardownState(TeardownEvent.EXIT)
else: else:
raise ValueError('Unknown GuiEvent "{}"'.format(event.name)) raise ValueError('Unknown GuiEvent "{}"'.format(event.name))
else: else:
raise TypeError('Unknown Event type "{}"'.format(event)) raise TypeError('Unknown Event type "{}"'.format(event))
class SettingsState(State):
def __init__(self):
super().__init__()
def __str__(self):
return 'SettingsState'
def handleEvent(self, event, context):
if isinstance(event, GuiEvent) and event.name == 'welcome':
context.state = WelcomeState()
else:
raise TypeError('Unknown Event type "{}"'.format(event))
class StartupState(State): class StartupState(State):
def __init__(self): def __init__(self):

View File

@@ -19,10 +19,13 @@
import logging import logging
import os.path import os.path
import sys
from time import localtime, strftime from time import localtime, strftime
from .PictureList import PictureList from .PictureList import PictureList
from .StateMachine import TeardownEvent, TeardownState
from .Threading import Workers
class WorkerTask: class WorkerTask:
@@ -60,13 +63,23 @@ class PictureSaver(WorkerTask):
class Worker: class Worker:
def __init__(self, config, queue): def __init__(self, config, comm):
self._queue = queue self._comm = comm
def run(self): def run(self):
for func, args in iter(self._queue.get, 'teardown'): for state in self._comm.iter(Workers.WORKER):
func(*args) self.handleState(state)
return 0 def handleState(self, state):
if isinstance(state, TeardownState):
self.teardown(state)
def teardown(self, state):
if state.target == TeardownEvent.EXIT:
sys.exit(0)
elif state.target == TeardownEvent.RESTART:
sys.exit(123)

View File

@@ -18,6 +18,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging import logging
import sys
from PIL import Image, ImageOps from PIL import Image, ImageOps
@@ -51,13 +52,17 @@ class Camera:
self._is_keep_pictures = config.getBool('Photobooth', 'keep_pictures') self._is_keep_pictures = config.getBool('Photobooth', 'keep_pictures')
logging.info('Using camera {} preview functionality'.format( logging.info('Using camera {} preview functionality'.format(
'with' if self.is_preview else 'without')) 'with' if self._is_preview else 'without'))
self.setIdle() self.setIdle()
def teardown(self): def teardown(self, state):
self._cap.cleanup() self._cap.cleanup()
if state.target == StateMachine.TeardownEvent.EXIT:
sys.exit(0)
elif state.target == StateMachine.TeardownEvent.RESTART:
sys.exit(123)
def run(self): def run(self):
@@ -75,7 +80,7 @@ class Camera:
elif isinstance(state, StateMachine.AssembleState): elif isinstance(state, StateMachine.AssembleState):
self.assemblePicture() self.assemblePicture()
elif isinstance(state, StateMachine.TeardownState): elif isinstance(state, StateMachine.TeardownState):
self.teardown() self.teardown(state)
def setActive(self): def setActive(self):

View File

@@ -48,6 +48,8 @@ countdown_time = 8
display_time = 5 display_time = 5
# Timeout for postprocessing (shown after review) # Timeout for postprocessing (shown after review)
postprocess_time = 60 postprocess_time = 60
# Keep single pictures (True/False)
keep_pictures = False
[Picture] [Picture]
# Basedir of output pictures # Basedir of output pictures

View File

@@ -17,7 +17,6 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# from . import GuiState
from .. import StateMachine from .. import StateMachine
@@ -86,8 +85,6 @@ class GuiSkeleton:
self.showWelcome(state) self.showWelcome(state)
elif isinstance(state, StateMachine.StartupState): elif isinstance(state, StateMachine.StartupState):
self.showStartup(state) self.showStartup(state)
elif isinstance(state, StateMachine.SettingsState):
self.showSettings(state)
elif isinstance(state, StateMachine.IdleState): elif isinstance(state, StateMachine.IdleState):
self.showIdle(state) self.showIdle(state)
elif isinstance(state, StateMachine.GreeterState): elif isinstance(state, StateMachine.GreeterState):

View File

@@ -34,7 +34,7 @@ from . import Widgets
from . import styles from . import styles
class Start(QtWidgets.QFrame): class Welcome(QtWidgets.QFrame):
def __init__(self, start_action, set_date_action, settings_action, def __init__(self, start_action, set_date_action, settings_action,
exit_action): exit_action):
@@ -79,7 +79,7 @@ class Start(QtWidgets.QFrame):
class IdleMessage(QtWidgets.QFrame): class IdleMessage(QtWidgets.QFrame):
def __init__(self): def __init__(self, trigger_action):
super().__init__() super().__init__()
self.setObjectName('IdleMessage') self.setObjectName('IdleMessage')
@@ -87,12 +87,13 @@ class IdleMessage(QtWidgets.QFrame):
self._message_label = 'Hit the' self._message_label = 'Hit the'
self._message_button = 'Button!' self._message_button = 'Button!'
self.initFrame() self.initFrame(trigger_action)
def initFrame(self): def initFrame(self, trigger_action):
lbl = QtWidgets.QLabel(self._message_label) lbl = QtWidgets.QLabel(self._message_label)
btn = QtWidgets.QPushButton(self._message_button) btn = QtWidgets.QPushButton(self._message_button)
btn.clicked.connect(trigger_action)
lay = QtWidgets.QVBoxLayout() lay = QtWidgets.QVBoxLayout()
lay.addWidget(lbl) lay.addWidget(lbl)
@@ -102,7 +103,7 @@ class IdleMessage(QtWidgets.QFrame):
class GreeterMessage(QtWidgets.QFrame): class GreeterMessage(QtWidgets.QFrame):
def __init__(self, num_x, num_y): def __init__(self, num_x, num_y, countdown_action):
super().__init__() super().__init__()
self.setObjectName('GreeterMessage') self.setObjectName('GreeterMessage')
@@ -114,14 +115,15 @@ class GreeterMessage(QtWidgets.QFrame):
else: else:
self._text_label = '' self._text_label = ''
self.initFrame() self.initFrame(countdown_action)
def initFrame(self): def initFrame(self, countdown_action):
ttl = QtWidgets.QLabel(self._text_title) ttl = QtWidgets.QLabel(self._text_title)
ttl.setObjectName('title') ttl.setObjectName('title')
btn = QtWidgets.QPushButton(self._text_button) btn = QtWidgets.QPushButton(self._text_button)
btn.setObjectName('button') btn.setObjectName('button')
btn.clicked.connect(countdown_action)
lbl = QtWidgets.QLabel(self._text_label) lbl = QtWidgets.QLabel(self._text_label)
lbl.setObjectName('message') lbl.setObjectName('message')
@@ -132,7 +134,7 @@ class GreeterMessage(QtWidgets.QFrame):
self.setLayout(lay) self.setLayout(lay)
class PoseMessage(QtWidgets.QFrame): class CaptureMessage(QtWidgets.QFrame):
def __init__(self, num_picture, num_x, num_y): def __init__(self, num_picture, num_x, num_y):
@@ -325,6 +327,7 @@ class PostprocessMessage(Widgets.TransparentOverlay):
def disableAndCall(button, handle): def disableAndCall(button, handle):
button.setEnabled(False) button.setEnabled(False)
button.update()
handle() handle()
def createButton(task): def createButton(task):

View File

@@ -27,10 +27,9 @@ from PyQt5 import QtWidgets
from PIL import ImageQt from PIL import ImageQt
# from ... import StateMachine from ...StateMachine import GuiEvent, TeardownEvent
# from ...Threading import Workers from ...Threading import Workers
from .. import GuiState
from ..GuiSkeleton import GuiSkeleton from ..GuiSkeleton import GuiSkeleton
from ..GuiPostprocessor import GuiPostprocessor from ..GuiPostprocessor import GuiPostprocessor
@@ -41,70 +40,54 @@ from . import Receiver
class PyQt5Gui(GuiSkeleton): class PyQt5Gui(GuiSkeleton):
def __init__(self, argv, config, camera_conn, worker_queue, communicator): def __init__(self, argv, config, communicator):
super().__init__(communicator) super().__init__(communicator)
self._cfg = config self._cfg = config
self._conn = camera_conn
parser = argparse.ArgumentParser() is_start, unparsed_args = self._parseArgs()
parser.add_argument('--run', action='store_true',
help='omit welcome screen and run photobooth')
parsed_args, unparsed_args = parser.parse_known_args()
self._omit_welcome = parsed_args.run
self._registerCallbacks()
self._initUI(argv[:1] + unparsed_args) self._initUI(argv[:1] + unparsed_args)
self._initReceiver() self._initReceiver()
self._picture = None
self._postprocess = GuiPostprocessor(self._cfg) self._postprocess = GuiPostprocessor(self._cfg)
if is_start:
self._comm.send(Workers.MASTER, GuiEvent('start'))
def run(self): def run(self):
if self._omit_welcome:
self._showStart(None)
else:
self._showWelcomeScreen()
exit_code = self._app.exec_() exit_code = self._app.exec_()
self._gui = None self._gui = None
return exit_code return exit_code
def close(self): def _parseArgs(self):
self._gui.close() # Add parameter for direct startup
parser = argparse.ArgumentParser()
parser.add_argument('--run', action='store_true',
help='omit welcome screen and run photobooth')
parsed_args, unparsed_args = parser.parse_known_args()
def restart(self): return (parsed_args.run, unparsed_args)
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): def _initUI(self, argv):
self._disableTrigger() self._disableTrigger()
# Load stylesheet
style = self._cfg.get('Gui', 'style') style = self._cfg.get('Gui', 'style')
filename = next((file for name, file in styles if name == style)) filename = next((file for name, file in styles if name == style))
with open(os.path.join(os.path.dirname(__file__), filename), 'r') as f: with open(os.path.join(os.path.dirname(__file__), filename), 'r') as f:
stylesheet = f.read() stylesheet = f.read()
# Create application and main window
self._app = QtWidgets.QApplication(argv) self._app = QtWidgets.QApplication(argv)
self._app.setStyleSheet(stylesheet) self._app.setStyleSheet(stylesheet)
self._gui = PyQt5MainWindow(self._cfg, self._handleKeypressEvent) self._gui = PyQt5MainWindow(self._cfg, self._handleKeypressEvent)
# Load additional fonts
fonts = ['photobooth/gui/Qt5Gui/fonts/AmaticSC-Regular.ttf', fonts = ['photobooth/gui/Qt5Gui/fonts/AmaticSC-Regular.ttf',
'photobooth/gui/Qt5Gui/fonts/AmaticSC-Bold.ttf'] 'photobooth/gui/Qt5Gui/fonts/AmaticSC-Bold.ttf']
self._fonts = QtGui.QFontDatabase() self._fonts = QtGui.QFontDatabase()
@@ -113,14 +96,11 @@ class PyQt5Gui(GuiSkeleton):
def _initReceiver(self): def _initReceiver(self):
self._receiver = Receiver.Receiver([self._conn]) # Create receiver thread
self._receiver = Receiver.Receiver(self._comm)
self._receiver.notify.connect(self.handleState) self._receiver.notify.connect(self.handleState)
self._receiver.start() self._receiver.start()
def _setWidget(self, widget):
self._gui.setCentralWidget(widget)
def _enableEscape(self): def _enableEscape(self):
self._is_escape = True self._is_escape = True
@@ -137,82 +117,58 @@ class PyQt5Gui(GuiSkeleton):
self._is_trigger = False self._is_trigger = False
def _sendStart(self): def _setWidget(self, widget):
self._conn.send('start') self._gui.setCentralWidget(widget)
def _sendTrigger(self, state): def close(self):
self._conn.send('triggered') if self._gui.close():
self._comm.send(Workers.MASTER, TeardownEvent(TeardownEvent.EXIT))
def _sendAck(self): def teardown(self, state):
self._conn.send('ack') if state.target == TeardownEvent.EXIT:
self._app.exit(0)
elif state.target == TeardownEvent.RESTART:
self._app.exit(123)
elif state.target == TeardownEvent.WELCOME:
self._comm.send(Workers.MASTER, GuiEvent('welcome'))
def _sendCancel(self): def showError(self, state):
self._conn.send('cancel') logging.error('%s: %s', state.title, state.message)
def _sendTeardown(self, state): MessageBox(self, MessageBox.RETRY, state.title, state.message,
lambda: self._comm.send(Workers.MASTER, GuiEvent('retry')),
lambda: self._comm.send(Workers.MASTER, GuiEvent('abort')))
self._conn.send('teardown') def showWelcome(self, state):
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 _showWelcomeScreen(self):
self._disableTrigger() self._disableTrigger()
self._disableEscape() self._disableEscape()
self._lastHandle = self._showWelcomeScreen self._setWidget(Frames.Welcome(
self._setWidget(Frames.Start(self._showStart, self._showSetDateTime, lambda: self._comm.send(Workers.MASTER, GuiEvent('start')),
self._showSettings, self.close)) self._showSetDateTime, self._showSettings, self.close))
if QtWidgets.QApplication.overrideCursor() != 0: if QtWidgets.QApplication.overrideCursor() != 0:
QtWidgets.QApplication.restoreOverrideCursor() QtWidgets.QApplication.restoreOverrideCursor()
def _showSetDateTime(self): def showStartup(self, state):
self._disableTrigger()
self._disableEscape()
self._lastHandle = self._showSetDateTime
self._setWidget(Frames.SetDateTime(self._showWelcomeScreen,
self.restart))
def _showSettings(self):
# self._comm.send(Workers.MASTER, StateMachine.GuiEvent('settings'))
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._comm.send(Workers.MASTER, StateMachine.GuiEvent('start'))
self._disableTrigger() self._disableTrigger()
self._enableEscape() self._enableEscape()
self._lastHandle = self._showWelcomeScreen
self._sendStart()
self._setWidget(Frames.WaitMessage('Starting the photobooth...')) self._setWidget(Frames.WaitMessage('Starting the photobooth...'))
if self._cfg.getBool('Gui', 'hide_cursor'): if self._cfg.getBool('Gui', 'hide_cursor'):
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.BlankCursor) QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.BlankCursor)
def _showIdle(self, state): def showIdle(self, state):
self._enableEscape() self._enableEscape()
self._enableTrigger() self._enableTrigger()
self._lastHandle = self._showIdle self._setWidget(Frames.IdleMessage(
self._setWidget(Frames.IdleMessage()) lambda: self._comm.send(Workers.MASTER, GuiEvent('trigger'))))
def _showGreeter(self, state): def showGreeter(self, state):
self._enableEscape() self._enableEscape()
self._disableTrigger() self._disableTrigger()
@@ -221,56 +177,79 @@ class PyQt5Gui(GuiSkeleton):
self._cfg.getInt('Picture', 'num_y')) self._cfg.getInt('Picture', 'num_y'))
greeter_time = self._cfg.getInt('Photobooth', 'greeter_time') * 1000 greeter_time = self._cfg.getInt('Photobooth', 'greeter_time') * 1000
self._setWidget(Frames.GreeterMessage(*num_pic)) self._setWidget(Frames.GreeterMessage(
QtCore.QTimer.singleShot(greeter_time, self._sendAck) *num_pic,
lambda: self._comm.send(Workers.MASTER, GuiEvent('countdown'))))
QtCore.QTimer.singleShot(
greeter_time,
lambda: self._comm.send(Workers.MASTER, GuiEvent('countdown')))
def _showCountdown(self, state): def showCountdown(self, state):
countdown_time = self._cfg.getInt('Photobooth', 'countdown_time') countdown_time = self._cfg.getInt('Photobooth', 'countdown_time')
self._setWidget(Frames.CountdownMessage(countdown_time, self._sendAck)) self._setWidget(Frames.CountdownMessage(
countdown_time,
lambda: self._comm.send(Workers.MASTER, GuiEvent('capture'))))
def _showPreview(self, state): def updateCountdown(self, event):
self._gui.centralWidget().picture = ImageQt.ImageQt(state.picture) self._gui.centralWidget().picture = ImageQt.ImageQt(event.picture)
self._gui.centralWidget().update() self._gui.centralWidget().update()
def _showPose(self, state): def showCapture(self, state):
num_pic = (self._cfg.getInt('Picture', 'num_x'), num_pic = (self._cfg.getInt('Picture', 'num_x'),
self._cfg.getInt('Picture', 'num_y')) self._cfg.getInt('Picture', 'num_y'))
self._setWidget(Frames.PoseMessage(state.num_picture, *num_pic)) self._setWidget(Frames.CaptureMessage(state.num_picture, *num_pic))
def _showAssemble(self, state): def showAssemble(self, state):
self._setWidget(Frames.WaitMessage('Processing picture...')) self._setWidget(Frames.WaitMessage('Processing picture...'))
def _showReview(self, state): def showReview(self, state):
img = ImageQt.ImageQt(state.picture) self._picture = ImageQt.ImageQt(state.picture)
review_time = self._cfg.getInt('Photobooth', 'display_time') * 1000 review_time = self._cfg.getInt('Photobooth', 'display_time') * 1000
self._setWidget(Frames.PictureMessage(img)) self._setWidget(Frames.PictureMessage(self._picture))
QtCore.QTimer.singleShot(review_time, lambda: QtCore.QTimer.singleShot(
self._showPostprocess(state.picture)) review_time,
lambda: self._comm.send(Workers.MASTER, GuiEvent('postprocess')))
def _showPostprocess(self, picture): def showPostprocess(self, state):
tasks = self._postprocess.get(picture) tasks = self._postprocess.get(self._picture)
postproc_t = self._cfg.getInt('Photobooth', 'postprocess_time') postproc_t = self._cfg.getInt('Photobooth', 'postprocess_time')
Frames.PostprocessMessage(self._gui.centralWidget(), tasks, Frames.PostprocessMessage(
self._sendAck, postproc_t * 1000) self._gui.centralWidget(), tasks,
lambda: self._comm.send(Workers.MASTER, GuiEvent('idle')),
postproc_t * 1000)
def _showError(self, state): def _handleKeypressEvent(self, event):
logging.error('%s: %s', state.title, state.message) if self._is_escape and event.key() == QtCore.Qt.Key_Escape:
self._comm.send(Workers.MASTER,
TeardownEvent(TeardownEvent.WELCOME))
elif self._is_trigger and event.key() == QtCore.Qt.Key_Space:
self._comm.send(Workers.MASTER, GuiEvent('trigger'))
def exec(*handles): def _showSetDateTime(self):
for handle in handles:
handle()
MessageBox(self, MessageBox.RETRY, state.title, state.message, self._disableTrigger()
exec(self._sendAck, self._lastState), self._disableEscape()
exec(self._sendCancel, self._showWelcomeScreen)) self._setWidget(Frames.SetDateTime(
self.showWelcome,
lambda: self._comm.send(Workers.MASTER,
TeardownEvent(TeardownEvent.RESTART))))
def _showSettings(self):
self._disableTrigger()
self._disableEscape()
self._setWidget(Frames.Settings(
self._cfg, self._showSettings, self.showWelcome,
lambda: self._comm.send(Workers.MASTER,
TeardownEvent(TeardownEvent.RESTART))))
class PyQt5MainWindow(QtWidgets.QMainWindow): class PyQt5MainWindow(QtWidgets.QMainWindow):

View File

@@ -17,19 +17,19 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import multiprocessing as mp
from PyQt5 import QtCore from PyQt5 import QtCore
from ...Threading import Workers
class Receiver(QtCore.QThread): class Receiver(QtCore.QThread):
notify = QtCore.pyqtSignal(object) notify = QtCore.pyqtSignal(object)
def __init__(self, conn): def __init__(self, comm):
super().__init__() super().__init__()
self._conn = conn self._comm = comm
def handle(self, state): def handle(self, state):
@@ -37,11 +37,5 @@ class Receiver(QtCore.QThread):
def run(self): def run(self):
while self._conn: for state in self._comm.iter(Workers.GUI):
for c in mp.connection.wait(self._conn): self.handle(state)
try:
state = c.recv()
except EOFError:
break
else:
self.handle(state)

View File

@@ -98,37 +98,34 @@ class CameraProcess(mp.Process):
class WorkerProcess(mp.Process): class WorkerProcess(mp.Process):
def __init__(self, config, queue): def __init__(self, config, comm):
super().__init__() super().__init__()
self.daemon = True self.daemon = True
self.cfg = config self.cfg = config
self.queue = queue self.comm = comm
def run(self): def run(self):
sys.exit(Worker(self.cfg, self.queue).run()) sys.exit(Worker(self.cfg, self.comm).run())
class GuiProcess(mp.Process): class GuiProcess(mp.Process):
def __init__(self, argv, config, conn, queue, communicator): def __init__(self, argv, config, communicator):
super().__init__() super().__init__()
self.argv = argv self.argv = argv
self.cfg = config self.cfg = config
self.conn = conn
self.queue = queue
self.comm = communicator self.comm = communicator
def run(self): def run(self):
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, self.conn, self.queue, sys.exit(Gui(self.argv, self.cfg, self.comm).run())
self.comm).run())
def run(argv): def run(argv):
@@ -144,8 +141,8 @@ def run(argv):
# Create communication objects: # Create communication objects:
# 1. We use a pipe to connect GUI and camera process # 1. We use a pipe to connect GUI and camera process
# 2. We use a queue to feed tasks to the postprocessing process # 2. We use a queue to feed tasks to the postprocessing process
gui_conn, camera_conn = mp.Pipe() # gui_conn, camera_conn = mp.Pipe()
worker_queue = mp.SimpleQueue() # worker_queue = mp.SimpleQueue()
# Initialize processes: We use three processes here: # Initialize processes: We use three processes here:
# 1. Camera processing # 1. Camera processing
@@ -154,25 +151,27 @@ def run(argv):
camera_proc = CameraProcess(config, comm) # camera_conn, worker_queue) camera_proc = CameraProcess(config, comm) # camera_conn, worker_queue)
camera_proc.start() camera_proc.start()
worker_proc = WorkerProcess(config, worker_queue) worker_proc = WorkerProcess(config, comm)
worker_proc.start() worker_proc.start()
gui_proc = GuiProcess(argv, config, gui_conn, worker_queue, comm) gui_proc = GuiProcess(argv, config, comm)
gui_proc.start() gui_proc.start()
for event in comm.iter(Workers.MASTER): for event in comm.iter(Workers.MASTER):
context.handleEvent(event) exit_code = context.handleEvent(event)
if exit_code in (0, 123):
break
# Close endpoints # Close endpoints
gui_conn.close() # gui_conn.close()
camera_conn.close() # camera_conn.close()
# Wait for processes to finish # Wait for processes to finish
gui_proc.join() gui_proc.join()
worker_queue.put('teardown') # worker_queue.put('teardown')
worker_proc.join() worker_proc.join()
camera_proc.join(1) camera_proc.join(1)
return gui_proc.exitcode return exit_code
def main(argv): def main(argv):