[mousetrap/gnome3-wip: 101/240] New core architecture.
- From: Heidi Ellis <heidiellis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mousetrap/gnome3-wip: 101/240] New core architecture.
- Date: Mon, 8 Sep 2014 15:21:03 +0000 (UTC)
commit fde960968a665ad603e9052bb51f451a4708abd1
Author: Stoney Jackson <dr stoney gmail com>
Date: Thu Jun 19 15:13:20 2014 -0400
New core architecture.
main.py had too many reasons to change. It was responsible
for instantiating all system components and passing messages
between compoents. So, anytime a new component was added or
a change in there interaction was desired, main.py had to
be changed in non-trivial ways.
main.py needs to provide a stable, predictable platform on
which to build new features.
New classes
mousetrap.main.App
mousetrap.main.Loop
mousetrap.main.Observable
mousetrap.main.parts.interface.Part
App - Provides shared state among Parts eliminating the need
for Main to pass messages between Parts.
Loop - Drives the application calling run() on each Part each
pass of the loop. It is the observable in an observer
pattern with Parts as observers. Use of the observer
pattern means that Loop does not need to know the
details of each specific Part.
Observable - Provides implementation of observable in the
observer pattern.
Part - The interface each part must implement.
To implement a new Part:
1. Implement the Part (e.g., see mosuetrap.parts.eyes).
2. Add part description to DEFAULT_PARTS in main.py.
This second step will go away when DEFAULT_PARTS is
converted to a configuration file in a future commit.
src/mousetrap/gui.py | 70 ++-----------
src/mousetrap/main.py | 110 +++++++++++++++-----
src/mousetrap/{pointers => parts}/__init__.py | 0
src/mousetrap/parts/camera.py | 6 +
src/mousetrap/parts/display.py | 7 ++
src/mousetrap/{pointers => parts}/eyes.py | 31 ++----
src/mousetrap/parts/interface.py | 4 +
src/mousetrap/{pointers => parts}/nose.py | 4 +-
src/mousetrap/{pointers => parts}/nose_joystick.py | 47 ++------
src/mousetrap/pointers/interface.py | 20 ----
10 files changed, 130 insertions(+), 169 deletions(-)
---
diff --git a/src/mousetrap/gui.py b/src/mousetrap/gui.py
index c36b594..0e22504 100644
--- a/src/mousetrap/gui.py
+++ b/src/mousetrap/gui.py
@@ -5,7 +5,7 @@ All things GUI.
from gi.repository import Gtk
from gi.repository import Gdk
-from Xlib.display import Display
+from Xlib.display import Display as XlibDisplay
from Xlib.ext import xtest
from Xlib import X
@@ -56,7 +56,7 @@ class Gui(object):
return Gtk.Window().get_screen().get_height()
-class ScreenPointer(object):
+class Pointer(object):
EVENT_MOVE = 'move'
EVENT_CLICK = 'click'
EVENT_DOUBLE_CLICK = 'double click'
@@ -64,9 +64,9 @@ class ScreenPointer(object):
EVENT_PRESS = 'press'
EVENT_RELEASE = 'release'
- BUTTON_LEFT = 'left button'
- BUTTON_RIGHT = 'right button'
- BUTTON_MIDDLE = 'middle button'
+ BUTTON_LEFT = X.Button1
+ BUTTON_RIGHT = X.Button3
+ BUTTON_MIDDLE = X.Button2
def __init__(self):
gdk_display = Gdk.Display.get_default()
@@ -74,19 +74,6 @@ class ScreenPointer(object):
self._pointer = device_manager.get_client_pointer()
self._screen = gdk_display.get_default_screen()
self._moved = False
- self._event_handlers = {}
- self._initialize_event_handlers()
-
- def _initialize_event_handlers(self):
- self._event_handlers[self.EVENT_MOVE] = self.set_position
- self._event_handlers[self.EVENT_CLICK] = self.click
- self._event_handlers[self.EVENT_DOUBLE_CLICK] = self.double_click
- self._event_handlers[self.EVENT_TRIPLE_CLICK] = self.triple_click
- self._event_handlers[self.EVENT_PRESS] = self.press
- self._event_handlers[self.EVENT_RELEASE] = self.release
-
- def trigger_event(self, event):
- self._event_handlers[event.name](event.button_or_position)
def set_position(self, position=None):
'''Move pointer to position (x, y). If position is None,
@@ -109,47 +96,8 @@ class ScreenPointer(object):
return (position[x_index], position[y_index])
def click(self, button=BUTTON_LEFT):
- if button != BUTTON_LEFT:
- raise NotImplementedError('Only left click is implemented.')
- display = Display()
- for action in LeftClick().get_actions():
- LOGGER.debug('%s %s', action.event, action.button)
- xtest.fake_input(display, action.event, action.button)
+ display = XlibDisplay()
+ for event, button in [(X.ButtonPress, button), (X.ButtonRelease, button)]:
+ LOGGER.debug('%s %s', event, button)
+ xtest.fake_input(display, event, button)
display.sync()
-
- def double_click(self, button=BUTTON_LEFT):
- raise NotImplementedError()
-
- def triple_click(self, button=BUTTON_LEFT):
- raise NotImplementedError()
-
- def triple_click(self, button=BUTTON_LEFT):
- raise NotImplementedError()
-
- def press(self, button=BUTTON_LEFT):
- raise NotImplementedError()
-
- def release(self, button=BUTTON_LEFT):
- raise NotImplementedError()
-
- def is_pressed(self, button=BUTTON_LEFT):
- raise NotImplementedError()
-
-
-class ScreenPointerEvent(object):
- def __init__(self, name, button_or_position):
- self.name = name
- self.button_or_position = button_or_position
-
-
-class LeftClick(object):
- def get_actions(self):
- press = Button(event=X.ButtonPress)
- release = Button(event=X.ButtonRelease)
- return [press, release]
-
-
-class Button(object):
- def __init__(self, event, button=1):
- self.event = event
- self.button = button
diff --git a/src/mousetrap/main.py b/src/mousetrap/main.py
index 99d2ac4..2f81644 100644
--- a/src/mousetrap/main.py
+++ b/src/mousetrap/main.py
@@ -4,53 +4,107 @@ Where it all begins.
# NOTE: import this first to set up logging properly.
import mousetrap.initialize_logging
-
import logging
from gi.repository import GObject, Gdk, Gtk
+from mousetrap.gui import Gui, Pointer
from mousetrap.vision import Camera
-from mousetrap.gui import ScreenPointer, Gui
-from mousetrap.pointers.nose_joystick import Pointer
-from mousetrap.pointers.eyes import Pointer as Eyes
LOGGER = logging.getLogger('mousetrap.main')
-class Main(object):
+#TODO: Should be a configuration file.
+DEFAULT_PARTS = [
+ ('camera', 'mousetrap.parts.camera'),
+ ('display', 'mousetrap.parts.display'),
+ ('nose_joystick', 'mousetrap.parts.nose_joystick'),
+ ('eye_click', 'mousetrap.parts.eyes'),
+ ]
+DEFAULT_LOOPS_PER_SECOND = 10
- FPS = 10
- INTERVAL = int(round(1000.0 / FPS))
+class Main(object):
def __init__(self):
- self.timeout_id = None
- self.camera = Camera()
- self.camera.set_dimensions(320, 240)
- self.gui = Gui()
- self.pointer = ScreenPointer()
- self.nose = Pointer()
- self.eyes = Eyes()
+ self._app = App()
def run(self):
- self.timeout_id = GObject.timeout_add(self.INTERVAL, self.on_timeout, None)
+ self._app.run()
+
+
+class App(object):
+ def __init__(self):
+ self.image = None
+ self.loop = Loop(self)
+ self.gui = Gui()
+ self.camera = Camera()
+ self.pointer = Pointer()
+ self.parts = []
+ self._assemble_parts()
+
+ def _assemble_parts(self):
+ self._load_parts(DEFAULT_PARTS)
+ self._register_parts_with_loop()
+
+ def _load_parts(self, part_descriptors):
+ for name, module in part_descriptors:
+ self.parts.append(self._load_part(module))
+
+ @staticmethod
+ def _load_part(module):
+ LOGGER.debug('loading %s', module)
+ module = __import__(module, globals(), locals(), ['Part'])
+ part = module.Part()
+ return part
+
+ def _register_parts_with_loop(self):
+ for part in self.parts:
+ self.loop.subscribe(part)
+
+ def run(self, app=None):
+ self.loop.start()
self.gui.start()
- def on_timeout(self, user_data):
- image = self.camera.read_image()
- self.gui.show_image('Raw', image)
- self.nose.update_image(image)
- for event in self.nose.get_pointer_events():
- self.pointer.trigger_event(event)
+class Observable(object):
+ def __init__(self):
+ self.__observers = []
+ self.__arguments = {}
+
+ def subscribe(self, observer):
+ self.__observers.append(observer)
+
+ def _add_argument(self, key, value):
+ self.__arguments[key] = value
+
+ def _fire(self, callback_name):
+ for observer in self.__observers:
+ callback = getattr(observer, callback_name)
+ callback(**self.__arguments)
- if self.pointer.is_moving():
- return True
- self.eyes.update_image(image)
- events = self.eyes.get_pointer_events()
- for event in events:
- self.pointer.trigger_event(event)
+class Loop(Observable):
+ MILLISECONDS_PER_SECOND = 1000.0
+ CALLBACK_RUN = 'run'
- return True
+ def __init__(self, app):
+ super(Loop, self).__init__()
+ self.set_loops_per_second(DEFAULT_LOOPS_PER_SECOND)
+ self._timeout_id = None
+ self._add_argument('app', app)
+
+ def set_loops_per_second(self, loops_per_second):
+ self._loops_per_second = loops_per_second
+ self._interval = int(round(
+ self.MILLISECONDS_PER_SECOND / self._loops_per_second))
+
+ def start(self):
+ self.timeout_id = GObject.timeout_add(self._interval, self.run)
+
+ def run(self):
+ CONTINUE = True
+ PAUSE = False
+ self._fire(self.CALLBACK_RUN)
+ return CONTINUE
if __name__ == '__main__':
diff --git a/src/mousetrap/pointers/__init__.py b/src/mousetrap/parts/__init__.py
similarity index 100%
rename from src/mousetrap/pointers/__init__.py
rename to src/mousetrap/parts/__init__.py
diff --git a/src/mousetrap/parts/camera.py b/src/mousetrap/parts/camera.py
new file mode 100644
index 0000000..11f2346
--- /dev/null
+++ b/src/mousetrap/parts/camera.py
@@ -0,0 +1,6 @@
+import mousetrap.parts.interface as interface
+
+
+class Part(interface.Part):
+ def run(self, app):
+ app.image = app.camera.read_image()
diff --git a/src/mousetrap/parts/display.py b/src/mousetrap/parts/display.py
new file mode 100644
index 0000000..2c39f36
--- /dev/null
+++ b/src/mousetrap/parts/display.py
@@ -0,0 +1,7 @@
+import mousetrap.parts.interface as interface
+import logging
+
+
+class Part(interface.Part):
+ def run(self, app):
+ app.gui.show_image('MouseTrap', app.image)
diff --git a/src/mousetrap/pointers/eyes.py b/src/mousetrap/parts/eyes.py
similarity index 66%
rename from src/mousetrap/pointers/eyes.py
rename to src/mousetrap/parts/eyes.py
index dfc67e8..b5f302f 100644
--- a/src/mousetrap/pointers/eyes.py
+++ b/src/mousetrap/parts/eyes.py
@@ -1,29 +1,28 @@
-import mousetrap.pointers.interface as interface
+import mousetrap.parts.interface as interface
from mousetrap.vision import FeatureDetector, FeatureNotFoundException
-from mousetrap.gui import ScreenPointer
import logging
LOGGER = logging.getLogger(__name__)
-class Pointer(interface.Pointer):
+class Part(interface.Part):
def __init__(self):
-
self._left_locator = LeftEyeLocator()
-
self._history = []
-
self._is_closed = False
- def update_image(self, image):
+ def run(self, app):
try:
- point_image = self._left_locator.locate(image)
-
+ point_image = self._left_locator.locate(app.image)
self._hit(point_image)
except FeatureNotFoundException:
self._miss()
+ if self._detect_closed():
+ self._history = []
+ app.pointer.click()
+
def _hit(self, point):
self._history.append(point)
@@ -38,20 +37,6 @@ class Pointer(interface.Pointer):
return misses > 12
- def get_new_position(self):
- return None
-
- def get_pointer_events(self):
- if self._detect_closed():
- self._is_closed = True
- if not self._is_closed:
- return [ScreenPointerEvent(
- ScreenPointer.EVENT_CLICK,
- ScreenPointer.BUTTON_LEFT)]
- else:
- self._is_closed = False
- return []
-
class LeftEyeLocator(object):
diff --git a/src/mousetrap/parts/interface.py b/src/mousetrap/parts/interface.py
new file mode 100644
index 0000000..c55cf48
--- /dev/null
+++ b/src/mousetrap/parts/interface.py
@@ -0,0 +1,4 @@
+class Part(object):
+ def run(self, app):
+ '''Called each pass of the loop.'''
+ raise NotImplementedError('Must implement.')
diff --git a/src/mousetrap/pointers/nose.py b/src/mousetrap/parts/nose.py
similarity index 95%
rename from src/mousetrap/pointers/nose.py
rename to src/mousetrap/parts/nose.py
index b4869d2..55bb5c4 100644
--- a/src/mousetrap/pointers/nose.py
+++ b/src/mousetrap/parts/nose.py
@@ -1,9 +1,9 @@
-import mousetrap.pointers.interface as interface
+import mousetrap.parts.interface as interface
from mousetrap.vision import FeatureDetector, FeatureNotFoundException
from mousetrap.gui import Gui
-class Pointer(interface.Pointer):
+class Part(interface.Part):
def __init__(self):
self._nose_locator = NoseLocator()
self._gui = Gui()
diff --git a/src/mousetrap/pointers/nose_joystick.py b/src/mousetrap/parts/nose_joystick.py
similarity index 52%
rename from src/mousetrap/pointers/nose_joystick.py
rename to src/mousetrap/parts/nose_joystick.py
index 9cee0a7..4162cac 100644
--- a/src/mousetrap/pointers/nose_joystick.py
+++ b/src/mousetrap/parts/nose_joystick.py
@@ -1,38 +1,27 @@
-import mousetrap.pointers.interface as interface
+import mousetrap.parts.interface as interface
from mousetrap.vision import FeatureDetector, FeatureNotFoundException
-from mousetrap.gui import Gui, ScreenPointer, ScreenPointerEvent
+from mousetrap.parts.nose import NoseLocator
-from mousetrap.pointers.nose import NoseLocator
-
-class Pointer(interface.Pointer):
+class Part(interface.Part):
THRESHOLD = 5
def __init__(self):
self._nose_locator = NoseLocator()
- self._image = None
-
- self._pointer = ScreenPointer()
-
self._initial_image_location = (0, 0)
self._last_delta = (0, 0)
- self._location = self._pointer.get_position()
-
- self._tracking = False
-
- def update_image(self, image):
- self._image = image
+ def run(self, app):
+ self._app = app
+ location = None
try:
- point_image = self._nose_locator.locate(image)
- self._tracking = True
+ point_image = self._nose_locator.locate(app.image)
point_screen = self._convert_image_to_screen_point(*point_image)
- self._location = point_screen
+ location = point_screen
except FeatureNotFoundException:
- self._tracking = False
- location = self._pointer.get_position()
- self._location = self._apply_delta_to_point(location,
- self._last_delta)
+ location = app.pointer.get_position()
+ location = self._apply_delta_to_point(location, self._last_delta)
+ app.pointer.set_position(location)
def _apply_delta_to_point(self, point, delta):
delta_x, delta_y = delta
@@ -67,18 +56,6 @@ class Pointer(interface.Pointer):
self._last_delta = delta
- location = self._pointer.get_position()
+ location = self._app.pointer.get_position()
return self._apply_delta_to_point(location, delta)
-
-# def get_new_position(self):
-# return self._location
-
- def is_tracking(self):
- return self._tracking
-
- def get_pointer_events(self):
- if self._location is None:
- return []
- else:
- return [ScreenPointerEvent(ScreenPointer.EVENT_MOVE, self._location)]
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]