1 # Copyright (C) 2010-2012 W. Trevor King <wking@tremily.us>
3 # This file is part of Hooke.
5 # Hooke is free software: you can redistribute it and/or modify it under the
6 # terms of the GNU Lesser General Public License as published by the Free
7 # Software Foundation, either version 3 of the License, or (at your option) any
10 # Hooke is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15 # You should have received a copy of the GNU Lesser General Public License
16 # along with Hooke. If not, see <http://www.gnu.org/licenses/>.
18 """The `ui` module provides :class:`UserInterface` and various subclasses.
21 import ConfigParser as configparser
24 from .. import version
25 from ..config import Setting
26 from ..engine import CommandMessage
27 from ..util.pluggable import IsSubclass, construct_odict
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):
38 USER_INTERFACE_MODULES = [
42 """List of user interface modules. TODO: autodiscovery
45 USER_INTERFACE_SETTING_SECTION = 'user interfaces'
46 """Name of the config section which controls UI selection.
50 class UserInterface (object):
51 """A user interface to drive the :class:`hooke.engine.CommandEngine`.
53 def __init__(self, name):
55 self.setting_section = '%s user interface' % (self.name)
58 def default_settings(self):
59 """Return a list of :class:`hooke.config.Setting`\s for any
60 configurable UI settings.
62 The suggested section setting is::
64 Setting(section=self.setting_section, help=self.__doc__)
68 def reload_config(self, config):
69 """Update the user interface for new config settings.
71 Should be called with the new `config` upon recipt of
72 `ReloadUserInterfaceConfig` from the `CommandEngine` or when
73 loading the initial configuration.
76 self.config = dict(config.items(self.setting_section))
77 except configparser.NoSectionError:
80 def run(self, commands, ui_to_command_queue, command_to_ui_queue):
83 # Assorted useful tidbits for subclasses
85 def _submit_command(self, command_message, ui_to_command_queue,
86 explicit_user_call=True):
87 log = logging.getLogger('hooke')
88 if explicit_user_call == True:
92 command_message.explicit_user_call = explicit_user_call
93 log.debug('executing (for the %s) %s' % (executor, command_message))
94 ui_to_command_queue.put(command_message)
96 def _set_config(self, option, value, ui_to_command_queue, response_handler,
99 section = self.setting_section
100 if section in [self.setting_section, 'conditions']:
101 if self.config[option] == value:
102 return # No change, so no need to push the new value.
103 self.config[option] = value
105 command='set config',
106 arguments={'section': section, 'option': option, 'value': value})
107 self._submit_command(command_message=cm,
108 ui_to_command_queue=ui_to_command_queue,
109 explicit_user_call=False)
110 response_handler(command_message=cm)
112 def _splash_text(self, extra_info, **kwargs):
118 """ % (version(), short_license(extra_info, **kwargs))).strip()
120 def _playlist_status(self, playlist):
121 if len(playlist) > 0:
122 return '%s (%s/%s)' % (playlist.name, playlist.index() + 1,
124 return 'The playlist %s does not contain any valid force curve data.' \
128 USER_INTERFACES = construct_odict(
129 this_modname=__name__,
130 submodnames=USER_INTERFACE_MODULES,
131 class_selector=IsSubclass(UserInterface, blacklist=[UserInterface]))
132 """:class:`hooke.compat.odict.odict` of :class:`UserInterface`
133 instances keyed by `.name`.
136 def default_settings():
137 settings = [Setting(USER_INTERFACE_SETTING_SECTION,
138 help='Select the user interface (only one).')]
139 for i,ui in enumerate(USER_INTERFACES.values()):
140 help = ui.__doc__.split('\n', 1)[0]
141 settings.append(Setting(USER_INTERFACE_SETTING_SECTION,
142 ui.name, str(i==0), help=help))
143 # i==0 to enable the first by default
144 for ui in USER_INTERFACES.values():
145 settings.extend(ui.default_settings())
148 def load_ui(config, name=None):
150 uis = [c for c,v in config.items(USER_INTERFACE_SETTING_SECTION) if v == 'True']
151 assert len(uis) == 1, 'Can only select one UI, not %d: %s' % (len(uis),uis)
153 ui = USER_INTERFACES[name]
154 ui.reload_config(config)