Skip to content

Commit

Permalink
Threading module with Communicator added
Browse files Browse the repository at this point in the history
  • Loading branch information
reuterbal committed Jul 13, 2018
1 parent 2925b3e commit f8918e8
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 11 deletions.
63 changes: 59 additions & 4 deletions photobooth/StateMachine.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
# 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/>.

import logging


class Context:

def __init__(self, initial_state):
def __init__(self):

super().__init__()

self.state = initial_state
self.state = WelcomeState()

@property
def state(self):
Expand All @@ -37,11 +38,17 @@ def state(self, new_state):
if not isinstance(new_state, State):
raise TypeError('new_state must implement State')

logging.debug('New state is "{}"'.format(new_state))

self._state = new_state

def handleEvent(self, event):

if not isinstance(event, Event):
raise TypeError('event must implement Event')

logging.debug('Handling event "{}"'.format(event))

if isinstance(event, ErrorEvent):
self.state = ErrorState(event.exception, self.state)
elif isinstance(event, TeardownEvent):
Expand Down Expand Up @@ -109,7 +116,7 @@ class TeardownEvent(Event):
def __init__(self, target):

self._target = target
super().__init__('Teardown')
super().__init__('Teardown({})'.format(target))

@property
def target(self):
Expand Down Expand Up @@ -168,6 +175,10 @@ def __init__(self, exception, old_state):
self.old_state = old_state
super().__init__()

def __str__(self):

return 'ErrorState'

@property
def old_state(self):

Expand Down Expand Up @@ -198,13 +209,21 @@ def __init__(self, target):

super().__init__()

def __str__(self):

return 'TeardownState'


class WelcomeState(State):

def __init__(self):

super().__init__()

def __str__(self):

return 'WelcomeState'

def handleEvent(self, event, context):

if isinstance(event, GuiEvent):
Expand All @@ -226,6 +245,10 @@ def __init__(self):

super().__init__()

def __str__(self):

return 'SettingsState'

def handleEvent(self, event, context):

if isinstance(event, GuiEvent) and event.name == 'welcome':
Expand All @@ -240,6 +263,10 @@ def __init__(self):

super().__init__()

def __str__(self):

return 'StartupState'

def handleEvent(self, event, context):

if isinstance(event, CameraEvent) and event.name == 'ready':
Expand All @@ -254,6 +281,10 @@ def __init__(self):

super().__init__()

def __str__(self):

return 'IdleState'

def handleEvent(self, event, context):

if ((isinstance(event, GuiEvent) or isinstance(event, GpioEvent)) and
Expand All @@ -269,6 +300,10 @@ def __init__(self):

super().__init__()

def __str__(self):

return 'GreeterState'

def handleEvent(self, event, context):

if ((isinstance(event, GuiEvent) or isinstance(event, GpioEvent)) and
Expand All @@ -284,6 +319,10 @@ def __init__(self):

super().__init__()

def __str__(self):

return 'CountdownState'

def handleEvent(self, event, context):

if isinstance(event, GuiEvent) and event.name == 'capture':
Expand All @@ -298,6 +337,10 @@ def __init__(self):

super().__init__()

def __str__(self):

return 'CaptureState'

def handleEvent(self, event, context):

if isinstance(event, CameraEvent) and event.name == 'next':
Expand All @@ -314,6 +357,10 @@ def __init__(self):

super().__init__()

def __str__(self):

return 'AssembleState'

def handleEvent(self, event, context):

if isinstance(event, CameraEvent) and event.name == 'review':
Expand All @@ -328,6 +375,10 @@ def __init__(self):

super().__init__()

def __str__(self):

return 'ReviewState'

def handleEvent(self, event, context):

if isinstance(event, GuiEvent) and event.name == 'postprocess':
Expand All @@ -342,6 +393,10 @@ def __init__(self):

super().__init__()

def __str__(self):

return 'PostprocessState'

def handleEvent(self, event, context):

if ((isinstance(event, GuiEvent) or isinstance(event, GpioEvent)) and
Expand Down
59 changes: 59 additions & 0 deletions photobooth/Threading.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Photobooth - a flexible photo booth software
# Copyright (C) 2018 Balthasar Reuter <photobooth at re - web dot eu>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# 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/>.

from enum import IntEnum
from multiprocessing import Queue


class Communicator:

def __init__(self):

super().__init__()

self._queues = [Queue() for _ in Workers]

def send(self, target, message):

if not isinstance(target, Workers):
raise TypeError('target must be a member of Workers')

self._queues[target].put(message)

def recv(self, worker, block=True):

if not isinstance(worker, Workers):
raise TypeError('worker must be a member of Workers')

return self._queues[worker].get(block)

def iter(self, worker):

if not isinstance(worker, Workers):
raise TypeError('worker must be a member of Workers')

return iter(self._queues[worker].get, None)


class Workers(IntEnum):
MASTER = 0
GUI = 1
CAMERA = 2
GPIO = 3
WORKER = 4
5 changes: 4 additions & 1 deletion photobooth/gui/GuiSkeleton.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@

from . import GuiState

from .. import StateMachine


class GuiSkeleton:

def __init__(self):
def __init__(self, communicator):

super().__init__()
self._comm = communicator

@property
def idle(self):
Expand Down
11 changes: 9 additions & 2 deletions photobooth/gui/Qt5Gui/PyQt5Gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@

from PIL import ImageQt

from ... import StateMachine
from ...Threading import Workers

from .. import GuiState
from ..GuiSkeleton import GuiSkeleton
from ..GuiPostprocessor import GuiPostprocessor
Expand All @@ -38,9 +41,9 @@

class PyQt5Gui(GuiSkeleton):

def __init__(self, argv, config, camera_conn, worker_queue):
def __init__(self, argv, config, camera_conn, worker_queue, communicator):

super().__init__()
super().__init__(communicator)

self._cfg = config
self._conn = camera_conn
Expand Down Expand Up @@ -181,6 +184,8 @@ def _showSetDateTime(self):
self.restart))

def _showSettings(self):

self._comm.send(Workers.MASTER, StateMachine.GuiEvent('settings'))

self._disableTrigger()
self._disableEscape()
Expand All @@ -190,6 +195,8 @@ def _showSettings(self):

def _showStart(self, state):

self._comm.send(Workers.MASTER, StateMachine.GuiEvent('start'))

self._disableTrigger()
self._enableEscape()
self._lastHandle = self._showWelcomeScreen
Expand Down
17 changes: 13 additions & 4 deletions photobooth/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
from .Config import Config
from .Photobooth import Photobooth
from .util import lookup_and_import
from .StateMachine import Context
from .Threading import Communicator, Workers
from .Worker import Worker


Expand Down Expand Up @@ -99,20 +101,21 @@ def run(self):

class GuiProcess(mp.Process):

def __init__(self, argv, config, conn, queue):
def __init__(self, argv, config, conn, queue, communicator):

super().__init__()

self.argv = argv
self.cfg = config
self.conn = conn
self.queue = queue
self.comm = communicator

def run(self):

Gui = lookup_and_import(gui.modules, self.cfg.get('Gui', 'module'),
'gui')
sys.exit(Gui(self.argv, self.cfg, self.conn, self.queue).run())
sys.exit(Gui(self.argv, self.cfg, self.conn, self.queue, self.comm).run())


def run(argv):
Expand All @@ -122,6 +125,9 @@ def run(argv):
# Load configuration
config = Config('photobooth.cfg')

comm = Communicator()
context = Context()

# Create communication objects:
# 1. We use a pipe to connect GUI and camera process
# 2. We use a queue to feed tasks to the postprocessing process
Expand All @@ -138,9 +144,12 @@ def run(argv):
worker_proc = WorkerProcess(config, worker_queue)
worker_proc.start()

gui_proc = GuiProcess(argv, config, gui_conn, worker_queue)
gui_proc = GuiProcess(argv, config, gui_conn, worker_queue, comm)
gui_proc.start()

for event in comm.iter(Workers.MASTER):
context.handleEvent(event)

# Close endpoints
gui_conn.close()
camera_conn.close()
Expand All @@ -156,7 +165,7 @@ def run(argv):
def main(argv):

# Setup log level and format
log_level = logging.INFO
log_level = logging.DEBUG
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s')

Expand Down

0 comments on commit f8918e8

Please sign in to comment.