Replace .config rather than reconstructing plugins, drivers, and UIs.
[hooke.git] / hooke / ui / __init__.py
1 # Copyright (C) 2010 W. Trevor King <wking@drexel.edu>
2 #
3 # This file is part of Hooke.
4 #
5 # Hooke is free software: you can redistribute it and/or modify it
6 # under the terms of the GNU Lesser General Public License as
7 # published by the Free Software Foundation, either version 3 of the
8 # License, or (at your option) any later version.
9 #
10 # Hooke is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General
13 # Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with Hooke.  If not, see
17 # <http://www.gnu.org/licenses/>.
18
19 """The `ui` module provides :class:`UserInterface` and various subclasses.
20 """
21
22 import ConfigParser as configparser
23 import logging
24
25 from .. import version
26 from ..config import Setting
27 from ..util.pluggable import IsSubclass, construct_odict
28
29 try:
30     from ..license import short_license
31 except ImportError, e:
32     logging.warn('Could not load short_license from hooke.license')
33     from .. import __license__
34     def short_license(extra_info, **kwargs):
35         return __license__
36
37
38 USER_INTERFACE_MODULES = [
39     'commandline',
40     'gui',
41     ]
42 """List of user interface modules.  TODO: autodiscovery
43 """
44
45 USER_INTERFACE_SETTING_SECTION = 'user interfaces'
46 """Name of the config section which controls UI selection.
47 """
48
49
50 class UserInterface (object):
51     """A user interface to drive the :class:`hooke.engine.CommandEngine`.
52     """
53     def __init__(self, name):
54         self.name = name
55         self.setting_section = '%s user interface' % (self.name)
56         self.config = {}
57
58     def default_settings(self):
59         """Return a list of :class:`hooke.config.Setting`\s for any
60         configurable UI settings.
61
62         The suggested section setting is::
63
64             Setting(section=self.setting_section, help=self.__doc__)
65         """
66         return []
67
68     def reload_config(self, config):
69         """Update the user interface for new config settings.
70
71         Should be called with the new `config` upon recipt of
72         `ReloadUserInterfaceConfig` from the `CommandEngine` or when
73         loading the initial configuration.
74         """
75         try:
76             self.config = dict(config.items(self.setting_section))
77         except configparser.NoSectionError:
78             self.config = {}
79
80     def run(self, commands, ui_to_command_queue, command_to_ui_queue):
81         return
82
83     # Assorted useful tidbits for subclasses
84
85     def _submit_command(self, command_message, ui_to_command_queue):
86         log = logging.getLogger('hooke')
87         log.debug('executing %s' % command_message)
88         ui_to_command_queue.put(command_message)
89
90     def _splash_text(self, extra_info, **kwargs):
91         return ("""
92 Hooke version %s
93
94 %s
95 ----
96 """ % (version(), short_license(extra_info, **kwargs))).strip()
97
98     def _playlist_status(self, playlist):
99         if len(playlist) > 0:
100             return '%s (%s/%s)' % (playlist.name, playlist.index() + 1,
101                                    len(playlist))
102         return 'The playlist %s does not contain any valid force curve data.' \
103             % self.name
104
105
106 USER_INTERFACES = construct_odict(
107     this_modname=__name__,
108     submodnames=USER_INTERFACE_MODULES,
109     class_selector=IsSubclass(UserInterface, blacklist=[UserInterface]))
110 """:class:`hooke.compat.odict.odict` of :class:`UserInterface`
111 instances keyed by `.name`.
112 """
113
114 def default_settings():
115     settings = [Setting(USER_INTERFACE_SETTING_SECTION,
116                         help='Select the user interface (only one).')]
117     for i,ui in enumerate(USER_INTERFACES.values()):
118         help = ui.__doc__.split('\n', 1)[0]
119         settings.append(Setting(USER_INTERFACE_SETTING_SECTION,
120                                 ui.name, str(i==0), help=help))
121         # i==0 to enable the first by default
122     for ui in USER_INTERFACES.values():
123         settings.extend(ui.default_settings())
124     return settings
125
126 def load_ui(config, name=None):
127     if name == None:
128         uis = [c for c,v in config.items(USER_INTERFACE_SETTING_SECTION) if v == 'True']
129         assert len(uis) == 1, 'Can only select one UI, not %d: %s' % (len(uis),uis)
130         name = uis[0]
131     ui = USER_INTERFACES[name]
132     ui.reload_config(config)
133     return ui