[mousetrap/gnome3-wip: 119/240] Add basic configuration system.



commit 72b0144889f75540a2268e186764ca277d98bcc3
Author: Stoney Jackson <dr stoney gmail com>
Date:   Mon Jun 23 13:22:38 2014 -0400

    Add basic configuration system.
    
    * All constructors now take a `mousetrap.config.Config` as
      first parameter (after self).
    
    * `mousetrap.config` contians editable configuration (for now).
    
    * log.py went away since its configuration is now in config.py.

 src/mousetrap/config.py                |   41 +++++++++++++++++++++
 src/mousetrap/gui.py                   |   19 +++++++---
 src/mousetrap/image.py                 |    3 +-
 src/mousetrap/log.py                   |   18 ---------
 src/mousetrap/main.py                  |   62 +++++++++++++++----------------
 src/mousetrap/plugins/camera.py        |    3 ++
 src/mousetrap/plugins/display.py       |    3 ++
 src/mousetrap/plugins/eyes.py          |   60 +++++++++++++++++-------------
 src/mousetrap/plugins/nose.py          |   10 +++--
 src/mousetrap/plugins/nose_joystick.py |    5 ++-
 src/mousetrap/vision.py                |    9 +++--
 11 files changed, 141 insertions(+), 92 deletions(-)
---
diff --git a/src/mousetrap/config.py b/src/mousetrap/config.py
new file mode 100644
index 0000000..550bd28
--- /dev/null
+++ b/src/mousetrap/config.py
@@ -0,0 +1,41 @@
+class Config(dict):
+    def __init__(self):
+        self['loops_per_second'] = 10
+
+        self['assembly'] =  [
+            'mousetrap.plugins.camera.CameraPlugin',
+            'mousetrap.plugins.display.DisplayPlugin',
+            'mousetrap.plugins.nose_joystick.NoseJoystickPlugin',
+            'mousetrap.plugins.eyes.EyesPlugin',
+            ]
+
+        # See `logging` and `logging.config`
+        self['logging'] = {
+            'version': 1,
+            'root': {
+                'level': 'DEBUG',
+                'formatters': ['default'],
+                'handlers': ['console']
+                },
+            'formatters': {
+                'default': {
+                    'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+                    }
+                },
+            'handlers': {
+                'console': {
+                    'class': 'logging.StreamHandler',
+                    'level': 'DEBUG',
+                    'formatter' : 'default',
+                    'stream' : 'ext://sys.stdout'
+                    }
+                }
+            }
+
+        self['plugins'] = {
+            }
+
+
+    def for_plugin(self, plugin_object):
+        class_ = plugin_object.__class__
+        return self[class_.__module__ + '.' class_.__name__]
diff --git a/src/mousetrap/gui.py b/src/mousetrap/gui.py
index 72c54d2..1ed56cf 100644
--- a/src/mousetrap/gui.py
+++ b/src/mousetrap/gui.py
@@ -2,18 +2,23 @@
 All things GUI.
 '''
 
+
+import logging
+LOGGER = logging.getLogger(__name__)
+
+
 from gi.repository import Gtk
 from gi.repository import Gdk
 
+
 from Xlib.display import Display as XlibDisplay
 from Xlib.ext import xtest
 from Xlib import X
 
-import mousetrap.log as log
-LOGGER = log.get_logger(__name__)
 
 class ImageWindow(object):
-    def __init__(self, message):
+    def __init__(self, config, message):
+        self._config = config
         self._window = Gtk.Window(title=message)
         self._canvas = Gtk.Image()
         self._window.add(self._canvas)
@@ -34,7 +39,8 @@ class ImageWindow(object):
 
 
 class Gui(object):
-    def __init__(self):
+    def __init__(self, config):
+        self._config = config
         self._windows = {}
 
     def show_image(self, window_name, image):
@@ -42,7 +48,7 @@ class Gui(object):
            May reuse named windows.
            '''
         if window_name not in self._windows:
-            self._windows[window_name] = ImageWindow(window_name)
+            self._windows[window_name] = ImageWindow(self._config, window_name)
         self._windows[window_name].draw(image)
 
     def start(self):
@@ -59,7 +65,8 @@ class Gui(object):
 class Pointer(object):
     BUTTON_LEFT = X.Button1
 
-    def __init__(self):
+    def __init__(self, config):
+        self._config = config
         gdk_display = Gdk.Display.get_default()
         device_manager = gdk_display.get_device_manager()
         self._pointer = device_manager.get_client_pointer()
diff --git a/src/mousetrap/image.py b/src/mousetrap/image.py
index 100344e..f1c5ae4 100644
--- a/src/mousetrap/image.py
+++ b/src/mousetrap/image.py
@@ -10,7 +10,8 @@ _GDK_PIXBUF_BIT_PER_SAMPLE = 8
 
 
 class Image(object):
-    def __init__(self, image_cv, is_grayscale=False):
+    def __init__(self, config, image_cv, is_grayscale=False):
+        self._config = config
         self._image_cv = image_cv
         self._is_grayscale = is_grayscale
         self._image_cv_grayscale = None
diff --git a/src/mousetrap/main.py b/src/mousetrap/main.py
index 44b4899..1d0a209 100644
--- a/src/mousetrap/main.py
+++ b/src/mousetrap/main.py
@@ -2,49 +2,46 @@
 Where it all begins.
 '''
 
-import mousetrap.log as log
-from gi.repository import GObject, Gdk, Gtk
-from mousetrap.gui import Gui, Pointer
-from mousetrap.vision import Camera
+
+from mousetrap.config import Config
+CONFIG = Config()
 
 
-LOGGER = log.get_logger('mousetrap.main')
+import logging
+import logging.config
+logging.config.dictConfig(CONFIG['logging'])
+LOGGER = logging.getLogger('mousetrap.main')
 
 
-#TODO: Should be a configuration file.
-DEFAULT_PARTS = [
-        ('camera', 'mousetrap.plugins.camera.CameraPlugin'),
-        ('display', 'mousetrap.plugins.display.DisplayPlugin'),
-        ('nose_joystick', 'mousetrap.plugins.nose_joystick.NoseJoystickPlugin'),
-        ('eye_click', 'mousetrap.plugins.eyes.EyesPlugin'),
-        ]
-DEFAULT_LOOPS_PER_SECOND = 10
+from gi.repository import GObject, Gdk, Gtk
+from mousetrap.gui import Gui, Pointer
+from mousetrap.vision import Camera
 
 
 class App(object):
-    def __init__(self):
+    def __init__(self, config):
+        self.config = config
         self.image = None
-        self.loop = Loop(self)
-        self.gui = Gui()
-        self.camera = Camera()
-        self.pointer = Pointer()
+        self.loop = Loop(config, self)
+        self.gui = Gui(config)
+        self.camera = Camera(config)
+        self.pointer = Pointer(config)
         self.plugins = []
         self._assemble_plugins()
 
     def _assemble_plugins(self):
-        self._load_plugins(DEFAULT_PARTS)
+        self._load_plugins()
         self._register_plugins_with_loop()
 
-    def _load_plugins(self, plugin_descriptors):
-        for name, class_ in plugin_descriptors:
+    def _load_plugins(self):
+        for class_ in self.config['assembly']:
             self.plugins.append(self._load_plugin(class_))
 
-    @staticmethod
-    def _load_plugin(class_):
+    def _load_plugin(self, class_):
         LOGGER.debug('loading %s', class_)
         class_path = class_.split('.')
         module = __import__('.'.join(class_path[:-1]), {}, {}, class_path[-1])
-        return getattr(module, class_path[-1])()
+        return getattr(module, class_path[-1])(self.config)
 
     def _register_plugins_with_loop(self):
         for plugin in self.plugins:
@@ -56,7 +53,8 @@ class App(object):
 
 
 class Observable(object):
-    def __init__(self):
+    def __init__(self, config):
+        self._config = config
         self.__observers = []
         self.__arguments = {}
 
@@ -76,21 +74,21 @@ class Loop(Observable):
     MILLISECONDS_PER_SECOND = 1000.0
     CALLBACK_RUN = 'run'
 
-    def __init__(self, app):
-        super(Loop, self).__init__()
-        self.set_loops_per_second(DEFAULT_LOOPS_PER_SECOND)
+    def __init__(self, config, app):
+        super(Loop, self).__init__(config)
+        self._set_loops_per_second(app.config['loops_per_second'])
         self._timeout_id = None
         self._add_argument('app', app)
 
-    def set_loops_per_second(self, loops_per_second):
+    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)
+        self.timeout_id = GObject.timeout_add(self._interval, self._run)
 
-    def run(self):
+    def _run(self):
         CONTINUE = True
         PAUSE = False
         self._fire(self.CALLBACK_RUN)
@@ -98,4 +96,4 @@ class Loop(Observable):
 
 
 if __name__ == '__main__':
-    App().run()
+    App(CONFIG).run()
diff --git a/src/mousetrap/plugins/camera.py b/src/mousetrap/plugins/camera.py
index 1e79d8f..516ded4 100644
--- a/src/mousetrap/plugins/camera.py
+++ b/src/mousetrap/plugins/camera.py
@@ -2,5 +2,8 @@ import mousetrap.plugins.interface as interface
 
 
 class CameraPlugin(interface.Plugin):
+    def __init__(self, config):
+        self._config = config
+
     def run(self, app):
         app.image = app.camera.read_image()
diff --git a/src/mousetrap/plugins/display.py b/src/mousetrap/plugins/display.py
index 7bc7aae..2cc6804 100644
--- a/src/mousetrap/plugins/display.py
+++ b/src/mousetrap/plugins/display.py
@@ -3,5 +3,8 @@ import logging
 
 
 class DisplayPlugin(interface.Plugin):
+    def __init__(self, config):
+        self._config = config
+
     def run(self, app):
         app.gui.show_image('MouseTrap', app.image)
diff --git a/src/mousetrap/plugins/eyes.py b/src/mousetrap/plugins/eyes.py
index 6cf4543..1f2ce0d 100644
--- a/src/mousetrap/plugins/eyes.py
+++ b/src/mousetrap/plugins/eyes.py
@@ -1,15 +1,16 @@
-import mousetrap.plugins.interface as interface
-from mousetrap.vision import FeatureDetector, FeatureNotFoundException
-import mousetrap.log as log
+import logging
+LOGGER = logging.getLogger(__name__)
 
 
-LOGGER = log.get_logger(__name__)
+import mousetrap.plugins.interface as interface
+from mousetrap.vision import FeatureDetector, FeatureNotFoundException
 
 
 class EyesPlugin(interface.Plugin):
-    def __init__(self):
-        self._motion_detector = MotionDetector()
-        self._closed_detector = ClosedDetector()
+    def __init__(self, config):
+        self._config = config
+        self._motion_detector = MotionDetector(config)
+        self._closed_detector = ClosedDetector(config)
 
     def run(self, app):
         self._motion_detector.update(app.pointer)
@@ -22,9 +23,10 @@ class EyesPlugin(interface.Plugin):
 
 
 class MotionDetector(object):
-    def __init__(self):
+    def __init__(self, config):
+        self._config = config
         self._max_samples = 5
-        self._history = History(self._max_samples)
+        self._history = History(config, self._max_samples)
 
     def update(self, pointer):
         self._history.append(pointer.get_position())
@@ -35,13 +37,14 @@ class MotionDetector(object):
 
 
 class ClosedDetector(object):
-    def __init__(self):
+    def __init__(self, config):
+        self._config = config
         self._max_samples = 15
         self._min_fraction_to_be_closed = 0.8
         self._min_misses_to_be_closed = int(
                 self._min_fraction_to_be_closed * self._max_samples)
-        self._left_locator = LeftEyeLocator()
-        self._detection_history = History(self._max_samples)
+        self._left_locator = LeftEyeLocator(config)
+        self._detection_history = History(config, self._max_samples)
 
     def update(self, image):
         self._detection_history.append(self._left_locator.locate(image))
@@ -56,22 +59,26 @@ class ClosedDetector(object):
 
 class LeftEyeLocator(object):
 
-    def __init__(self):
+    def __init__(self, config):
+        self._config = config
         self._face_detector = FeatureDetector(
-            "face",
-            scale_factor=1.5,
-            min_neighbors=5,
-        )
+                config,
+                "face",
+                scale_factor=1.5,
+                min_neighbors=5,
+                )
         self._open_eye_detector = FeatureDetector(
-            "open_eye",
-            scale_factor=1.1,
-            min_neighbors=3,
-        )
+                config,
+                "open_eye",
+                scale_factor=1.1,
+                min_neighbors=3,
+                )
         self._left_eye_detector = FeatureDetector(
-            "left_eye",
-            scale_factor=1.5,
-            min_neighbors=10,
-        )
+                config,
+                "left_eye",
+                scale_factor=1.5,
+                min_neighbors=10,
+                )
 
     def locate(self, image):
         face = None
@@ -101,8 +108,9 @@ class LeftEyeLocator(object):
 
 
 class History(list):
-    def __init__(self, max_length):
+    def __init__(self, config, max_length):
         super(History, self).__init__()
+        self._config = config
         self._max_length = max_length
 
     def append(self, value):
diff --git a/src/mousetrap/plugins/nose.py b/src/mousetrap/plugins/nose.py
index 284494b..01e5a3b 100644
--- a/src/mousetrap/plugins/nose.py
+++ b/src/mousetrap/plugins/nose.py
@@ -4,7 +4,8 @@ from mousetrap.gui import Gui
 
 
 class NosePlugin(interface.Plugin):
-    def __init__(self):
+    def __init__(self, config):
+        self._config = config
         self._nose_locator = NoseLocator()
         self._gui = Gui()
         self._location = None
@@ -35,11 +36,12 @@ class NosePlugin(interface.Plugin):
 
 
 class NoseLocator(object):
-    def __init__(self):
+    def __init__(self, config):
+        self._config = config
         self._face_detector = FeatureDetector(
-                'face', scale_factor=1.5, min_neighbors=5)
+                config, 'face', scale_factor=1.5, min_neighbors=5)
         self._nose_detector = FeatureDetector(
-                'nose', scale_factor=1.1, min_neighbors=5)
+                config, 'nose', scale_factor=1.1, min_neighbors=5)
 
     def locate(self, image):
         face = self._face_detector.detect(image)
diff --git a/src/mousetrap/plugins/nose_joystick.py b/src/mousetrap/plugins/nose_joystick.py
index a735815..5f3cc20 100644
--- a/src/mousetrap/plugins/nose_joystick.py
+++ b/src/mousetrap/plugins/nose_joystick.py
@@ -6,8 +6,9 @@ from mousetrap.plugins.nose import NoseLocator
 class NoseJoystickPlugin(interface.Plugin):
     THRESHOLD = 5
 
-    def __init__(self):
-        self._nose_locator = NoseLocator()
+    def __init__(self, config):
+        self._config = config
+        self._nose_locator = NoseLocator(config)
         self._initial_image_location = (0, 0)
         self._last_delta = (0, 0)
 
diff --git a/src/mousetrap/vision.py b/src/mousetrap/vision.py
index c6eaae8..f130175 100644
--- a/src/mousetrap/vision.py
+++ b/src/mousetrap/vision.py
@@ -12,7 +12,8 @@ class Camera(object):
     S_CAPTURE_READ_ERROR = 'Error while capturing. Camera disconnected?'
     SEARCH_FOR_DEVICE = -1
 
-    def __init__(self, device_index=SEARCH_FOR_DEVICE, width=400, height=300):
+    def __init__(self, config, device_index=SEARCH_FOR_DEVICE, width=400, height=300):
+        self._config = config
         self._device = self._new_capture_device(device_index)
         self.set_dimensions(width, height)
 
@@ -37,7 +38,7 @@ class Camera(object):
         if not ret:
             raise IOError(self.S_CAPTURE_READ_ERROR)
 
-        return Image(image)
+        return Image(self._config, image)
 
 
 class HaarLoader(object):
@@ -86,7 +87,7 @@ class HaarNameError(Exception):
 
 
 class FeatureDetector(object):
-    def __init__(self, name, scale_factor=1.1, min_neighbors=3):
+    def __init__(self, config, name, scale_factor=1.1, min_neighbors=3):
         '''
         name - name of feature to detect
 
@@ -96,6 +97,7 @@ class FeatureDetector(object):
         min_neighbors - how many neighbors each candidate rectangle should have
                 to retain it. Default 3.
         '''
+        self._config = config
         self._name = name
         self._single = None
         self._plural = None
@@ -140,6 +142,7 @@ class FeatureDetector(object):
         to_x = single['x'] + single['width']
         image_cv_grayscale = self._image.to_cv_grayscale()
         single["image"] = Image(
+                self._config,
                 image_cv_grayscale[from_y:to_y, from_x:to_x],
                 is_grayscale=True)
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]