StateMachine for global photobooth state started to implement. States and Events defined, state actions still missing
This commit is contained in:
351
photobooth/StateMachine.py
Normal file
351
photobooth/StateMachine.py
Normal file
@@ -0,0 +1,351 @@
|
||||
#!/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/>.
|
||||
|
||||
|
||||
class Context:
|
||||
|
||||
def __init__(self, initial_state):
|
||||
|
||||
super().__init__()
|
||||
|
||||
self.state = initial_state
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
|
||||
return self._state
|
||||
|
||||
@state.setter
|
||||
def state(self, new_state):
|
||||
|
||||
if not isinstance(new_state, State):
|
||||
raise TypeError('new_state must implement State')
|
||||
|
||||
def handleEvent(self, event):
|
||||
|
||||
if not isinstance(event, Event):
|
||||
raise TypeError('event must implement Event')
|
||||
|
||||
if isinstance(event, ErrorEvent):
|
||||
self.state = ErrorState(event.exception, self.state)
|
||||
elif isinstance(event, TeardownEvent):
|
||||
self.state = TeardownState(event.target)
|
||||
else:
|
||||
self.state.handleEvent(event, self)
|
||||
|
||||
|
||||
class Event:
|
||||
|
||||
def __init__(self, name):
|
||||
|
||||
super().__init__()
|
||||
self.name = name
|
||||
|
||||
def __str__(self):
|
||||
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
|
||||
if not isinstance(name, str):
|
||||
raise TypeError('name must be a str')
|
||||
|
||||
self._name = name
|
||||
|
||||
|
||||
class ErrorEvent(Event):
|
||||
|
||||
def __init__(self, exception):
|
||||
|
||||
super().__init__('Error')
|
||||
self.exception = exception
|
||||
|
||||
def __str__(self):
|
||||
|
||||
return str(self.exception)
|
||||
|
||||
@property
|
||||
def exception(self):
|
||||
|
||||
return self._exception
|
||||
|
||||
@exception.setter
|
||||
def exception(self, exception):
|
||||
|
||||
if not isinstance(exception, Exception):
|
||||
raise TypeError('exception must be derived from Exception')
|
||||
|
||||
self._exception = exception
|
||||
|
||||
|
||||
class TeardownEvent(Event):
|
||||
|
||||
EXIT = 0
|
||||
RESTART = 1
|
||||
WELCOME = 2
|
||||
|
||||
def __init__(self, target):
|
||||
|
||||
self._target = target
|
||||
super().__init__('Teardown')
|
||||
|
||||
@property
|
||||
def target(self):
|
||||
|
||||
return self._target
|
||||
|
||||
|
||||
class GuiEvent(Event):
|
||||
|
||||
def __init__(self, name):
|
||||
|
||||
super().__init__(name)
|
||||
|
||||
|
||||
class GpioEvent(Event):
|
||||
|
||||
def __init__(self, name):
|
||||
|
||||
super().__init__(name)
|
||||
|
||||
|
||||
class CameraEvent(Event):
|
||||
|
||||
def __init__(self, name):
|
||||
|
||||
super().__init__(name)
|
||||
|
||||
|
||||
class WorkerEvent(Event):
|
||||
|
||||
def __init__(self, name):
|
||||
|
||||
super().__init__(name)
|
||||
|
||||
|
||||
class State:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
self.update()
|
||||
|
||||
def update(self):
|
||||
|
||||
pass
|
||||
|
||||
def handleEvent(self, event, context):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class ErrorState(State):
|
||||
|
||||
def __init__(self, exception, old_state):
|
||||
|
||||
self.old_state = old_state
|
||||
super().__init__()
|
||||
|
||||
@property
|
||||
def old_state(self):
|
||||
|
||||
return self._old_state
|
||||
|
||||
@old_state.setter
|
||||
def old_state(self, old_state):
|
||||
|
||||
if not isinstance(old_state, State):
|
||||
raise TypeError('old_state must be derived from State')
|
||||
|
||||
self._old_state = old_state
|
||||
|
||||
def handleEvent(self, event, context):
|
||||
|
||||
if isinstance(event, GuiEvent) and event.name == 'retry':
|
||||
context.state = self.old_state
|
||||
context.state.update()
|
||||
elif isinstance(event, GuiEvent) and event.name == 'abort':
|
||||
context.state = TeardownState()
|
||||
else:
|
||||
raise TypeError('Unknown Event type "{}"'.format(event))
|
||||
|
||||
|
||||
class TeardownState(State):
|
||||
|
||||
def __init__(self, target):
|
||||
|
||||
super().__init__()
|
||||
|
||||
|
||||
class WelcomeState(State):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
|
||||
def handleEvent(self, event, context):
|
||||
|
||||
if isinstance(event, GuiEvent):
|
||||
if event.name == 'start':
|
||||
context.state = StartupState()
|
||||
elif event.name == 'settings':
|
||||
context.state = SettingsState()
|
||||
elif event.name == 'exit':
|
||||
context.state = TeardownState()
|
||||
else:
|
||||
raise ValueError('Unknown GuiEvent "{}"'.format(event.name))
|
||||
else:
|
||||
raise TypeError('Unknown Event type "{}"'.format(event))
|
||||
|
||||
|
||||
class SettingsState(State):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
|
||||
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):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
|
||||
def handleEvent(self, event, context):
|
||||
|
||||
if isinstance(event, CameraEvent) and event.name == 'ready':
|
||||
context.state = IdleState()
|
||||
else:
|
||||
raise TypeError('Unknown Event type "{}"'.format(event))
|
||||
|
||||
|
||||
class IdleState(State):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
|
||||
def handleEvent(self, event, context):
|
||||
|
||||
if ((isinstance(event, GuiEvent) or isinstance(event, GpioEvent)) and
|
||||
event.name == 'trigger'):
|
||||
context.state = GreeterState()
|
||||
else:
|
||||
raise TypeError('Unknown Event type "{}"'.format(event))
|
||||
|
||||
|
||||
class GreeterState(State):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
|
||||
def handleEvent(self, event, context):
|
||||
|
||||
if ((isinstance(event, GuiEvent) or isinstance(event, GpioEvent)) and
|
||||
event.name == 'countdown'):
|
||||
context.state = CountdownState()
|
||||
else:
|
||||
raise TypeError('Unknown Event type "{}"'.format(event))
|
||||
|
||||
|
||||
class CountdownState(State):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
|
||||
def handleEvent(self, event, context):
|
||||
|
||||
if isinstance(event, GuiEvent) and event.name == 'capture':
|
||||
context.state == CaptureState()
|
||||
else:
|
||||
raise TypeError('Unknown Event type "{}"'.format(event))
|
||||
|
||||
|
||||
class CaptureState(State):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
|
||||
def handleEvent(self, event, context):
|
||||
|
||||
if isinstance(event, CameraEvent) and event.name == 'next':
|
||||
context.state = CountdownState()
|
||||
elif isinstance(event, CameraEvent) and event.name == 'assemble':
|
||||
context.state = AssembleState()
|
||||
else:
|
||||
raise TypeError('Unknown Event type "{}"'.format(event))
|
||||
|
||||
|
||||
class AssembleState(State):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
|
||||
def handleEvent(self, event, context):
|
||||
|
||||
if isinstance(event, CameraEvent) and event.name == 'review':
|
||||
context.state = ReviewState()
|
||||
else:
|
||||
raise TypeError('Unknown Event type "{}"'.format(event))
|
||||
|
||||
|
||||
class ReviewState(State):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
|
||||
def handleEvent(self, event, context):
|
||||
|
||||
if isinstance(event, GuiEvent) and event.name == 'postprocess':
|
||||
context.state == PostprocessState()
|
||||
else:
|
||||
raise TypeError('Unknown Event type "{}"'.format(event))
|
||||
|
||||
|
||||
class PostprocessState(State):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
|
||||
def handleEvent(self, event, context):
|
||||
|
||||
if ((isinstance(event, GuiEvent) or isinstance(event, GpioEvent)) and
|
||||
event.name == 'idle'):
|
||||
context.state == IdleState()
|
||||
else:
|
||||
raise TypeError('Unknown Event type "{}"'.format(event))
|
||||
Reference in New Issue
Block a user