Updated copyright blurbs in all files to '# Copyright'
[hooke.git] / hooke / ui / __init__.py
1 # Copyright
2
3 """The `ui` module provides :class:`UserInterface` and various subclasses.
4 """
5
6 import ConfigParser as configparser
7
8 from .. import version
9 from ..compat.odict import odict
10 from ..config import Setting
11 from ..util.pluggable import IsSubclass
12
13
14 USER_INTERFACE_MODULES = [
15     'commandline',
16     #'gui',
17     ]
18 """List of user interface modules.  TODO: autodiscovery
19 """
20
21 USER_INTERFACE_SETTING_SECTION = 'user interfaces'
22 """Name of the config section which controls UI selection.
23 """
24
25
26 class QueueMessage (object):
27     def __str__(self):
28         return self.__class__.__name__
29
30 class CloseEngine (QueueMessage):
31     pass
32
33 class CommandMessage (QueueMessage):
34     """A message storing a command to run, `command` should be a
35     :class:`hooke.command.Command` instance, and `arguments` should be
36     a :class:`dict` with `argname` keys and `value` values to be
37     passed to the command.
38     """
39     def __init__(self, command, arguments):
40         self.command = command
41         self.arguments = arguments
42
43 class UserInterface (object):
44     """A user interface to drive the :class:`hooke.engine.CommandEngine`.
45     """
46     def __init__(self, name):
47         self.name = name
48         self.setting_section = '%s user interface' % (self.name)
49         self.config = {}
50
51     def default_settings(self):
52         """Return a list of :class:`hooke.config.Setting`\s for any
53         configurable UI settings.
54
55         The suggested section setting is::
56
57             Setting(section=self.setting_section, help=self.__doc__)
58         """
59         return []
60
61     def reload_config(self, config):
62         """Update the user interface for new config settings.
63
64         Should be called with the new `config` upon recipt of
65         `ReloadUserInterfaceConfig` from the `CommandEngine`.
66         """
67         try:
68             self.config = dict(config.items(self.setting_section))
69         except configparser.NoSectionError:
70             self.config = {}
71
72     def run(self, hooke, ui_to_command_queue, command_to_ui_queue):
73         return
74
75     # Assorted useful tidbits for subclasses
76
77     def _splash_text(self):
78         return ("""
79 Hooke version %s
80
81 COPYRIGHT
82 ----
83 """ % version()).strip()
84
85     def _playlist_status(self, playlist):
86         if len(playlist) > 0:
87             return '%s (%s/%s)' % (playlist.name, playlist._index + 1,
88                                    len(playlist))
89         return 'The playlist %s does not contain any valid force curve data.' \
90             % self.name
91
92 def construct_odict(this_modname, submodnames, class_selector):
93     """Search the submodules `submodnames` of a module `this_modname`
94     for class objects for which `class_selector(class)` returns
95     `True`.  These classes are instantiated and stored in the returned
96     :class:`hooke.compat.odict.odict` in the order in which they were
97     discovered.
98     """
99     instances = odict()
100     for submodname in submodnames:
101         count = len([s for s in submodnames if s == submodname])
102         assert count > 0, 'No %s entries: %s' % (submodname, submodnames)
103         assert count == 1, 'Multiple (%d) %s entries: %s' \
104             % (count, submodname, submodnames)
105         this_mod = __import__(this_modname, fromlist=[submodname])
106         submod = getattr(this_mod, submodname)
107         for objname in dir(submod):
108             obj = getattr(submod, objname)
109             if class_selector(obj):
110                 instance = obj()
111                 instances[instance.name] = instance
112     return instances
113
114 USER_INTERFACES = construct_odict(
115     this_modname=__name__,
116     submodnames=USER_INTERFACE_MODULES,
117     class_selector=IsSubclass(UserInterface, blacklist=[UserInterface]))
118 """:class:`hooke.compat.odict.odict` of :class:`UserInterface`
119 instances keyed by `.name`.
120 """
121
122 def default_settings():
123     settings = [Setting(USER_INTERFACE_SETTING_SECTION,
124                         help='Select the user interface (only one).')]
125     for i,ui in enumerate(USER_INTERFACES.values()):
126         help = ui.__doc__.split('\n', 1)[0]
127         settings.append(Setting(USER_INTERFACE_SETTING_SECTION,
128                                 ui.name, str(i==0), help=help))
129         # i==0 to enable the first by default
130     for ui in USER_INTERFACES.values():
131         settings.extend(ui.default_settings())
132     return settings
133
134 def load_ui(config, name=None):
135     if name == None:
136         uis = [c for c,v in config.items(USER_INTERFACE_SETTING_SECTION) if v == 'True']
137         assert len(uis) == 1, 'Can only select one UI, not %d: %s' % (len(uis),uis)
138         name = uis[0]
139     ui = USER_INTERFACES[name]
140     try:
141         ui.config = dict(config.items(ui.setting_section))
142     except configparser.NoSectionError:
143         pass
144     return ui