1 """The `ui` module provides :class:`UserInterface` and various subclasses.
4 import ConfigParser as configparser
7 from ..compat.odict import odict
8 from ..config import Setting
9 from ..plugin import IsSubclass
12 USER_INTERFACE_MODULES = [
16 """List of user interface modules. TODO: autodiscovery
19 USER_INTERFACE_SETTING_SECTION = 'user interfaces'
20 """Name of the config section which controls UI selection.
23 class QueueMessage (object):
25 return self.__class__.__name__
27 class CloseEngine (QueueMessage):
30 class CommandMessage (QueueMessage):
31 """A message storing a command to run, `command` should be a
32 :class:`hooke.command.Command` instance, and `arguments` should be
33 a :class:`dict` with `argname` keys and `value` values to be
34 passed to the command.
36 def __init__(self, command, arguments):
37 self.command = command
38 self.arguments = arguments
40 class UserInterface (object):
41 """A user interface to drive the :class:`hooke.engine.CommandEngine`.
43 def __init__(self, name):
45 self.setting_section = '%s user interface' % self.name
48 def default_settings(self):
49 """Return a list of :class:`hooke.config.Setting`\s for any
50 configurable UI settings.
52 The suggested section setting is::
54 Setting(section=self.setting_section, help=self.__doc__)
58 def run(self, hooke, ui_to_command_queue, command_to_ui_queue):
61 # Assorted useful tidbits for subclasses
63 def _splash_text(self):
65 This is Hooke, version %s
69 """ % version()).strip()
71 def _playlist_status(self, playlist):
73 return '%s (%s/%s)' % (playlist.name, playlist._index + 1,
75 return 'The playlist %s does not contain any valid force curve data.' \
78 def construct_odict(this_modname, submodnames, class_selector):
79 """Search the submodules `submodnames` of a module `this_modname`
80 for class objects for which `class_selector(class)` returns
81 `True`. These classes are instantiated and stored in the returned
82 :class:`hooke.compat.odict.odict` in the order in which they were
86 for submodname in submodnames:
87 count = len([s for s in submodnames if s == submodname])
88 assert count > 0, 'No %s entries: %s' % (submodname, submodnames)
89 assert count == 1, 'Multiple (%d) %s entries: %s' \
90 % (count, submodname, submodnames)
91 this_mod = __import__(this_modname, fromlist=[submodname])
92 submod = getattr(this_mod, submodname)
93 for objname in dir(submod):
94 obj = getattr(submod, objname)
95 if class_selector(obj):
97 instances[instance.name] = instance
100 USER_INTERFACES = construct_odict(
101 this_modname=__name__,
102 submodnames=USER_INTERFACE_MODULES,
103 class_selector=IsSubclass(UserInterface, blacklist=[UserInterface]))
104 """:class:`hooke.compat.odict.odict` of :class:`UserInterface`
105 instances keyed by `.name`.
108 def default_settings():
109 settings = [Setting(USER_INTERFACE_SETTING_SECTION,
110 help='Select the user interface (only one).')]
111 for i,ui in enumerate(USER_INTERFACES.values()):
112 help = ui.__doc__.split('\n', 1)[0]
113 settings.append(Setting(USER_INTERFACE_SETTING_SECTION,
114 ui.name, str(i==0), help=help))
115 # i==0 to enable the first by default
116 for ui in USER_INTERFACES.values():
117 settings.extend(ui.default_settings())
121 uis = [c for c,v in config.items(USER_INTERFACE_SETTING_SECTION) if v == 'True']
122 assert len(uis) == 1, 'Can only select one UI, not %d: %s' % (len(uis),uis)
124 ui = USER_INTERFACES[ui_name]
126 ui.config = dict(config.items(ui.setting_section))
127 except configparser.NoSectionError: