Moved QueueMessage and subclasses from hooke.ui to the more central hooke.engine.
[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
24 from .. import version
25 from ..config import Setting
26 from ..engine import CloseEngine, CommandMessage
27 from ..util.pluggable import IsSubclass, construct_odict
28
29 try:
30     from ..license import short_license
31 except ImportError, e:
32     import logging
33     logging.warn('Could not load short_license from hooke.license')
34     from .. import __license__
35     def short_license(extra_info, **kwargs):
36         return __license__
37
38
39 USER_INTERFACE_MODULES = [
40     'commandline',
41     'gui',
42     ]
43 """List of user interface modules.  TODO: autodiscovery
44 """
45
46 USER_INTERFACE_SETTING_SECTION = 'user interfaces'
47 """Name of the config section which controls UI selection.
48 """
49
50
51 class UserInterface (object):
52     """A user interface to drive the :class:`hooke.engine.CommandEngine`.
53     """
54     def __init__(self, name):
55         self.name = name
56         self.setting_section = '%s user interface' % (self.name)
57         self.config = {}
58
59     def default_settings(self):
60         """Return a list of :class:`hooke.config.Setting`\s for any
61         configurable UI settings.
62
63         The suggested section setting is::
64
65             Setting(section=self.setting_section, help=self.__doc__)
66         """
67         return []
68
69     def reload_config(self, config):
70         """Update the user interface for new config settings.
71
72         Should be called with the new `config` upon recipt of
73         `ReloadUserInterfaceConfig` from the `CommandEngine` or when
74         loading the initial configuration.
75         """
76         try:
77             self.config = dict(config.items(self.setting_section))
78         except configparser.NoSectionError:
79             self.config = {}
80
81     def run(self, commands, ui_to_command_queue, command_to_ui_queue):
82         return
83
84     # Assorted useful tidbits for subclasses
85
86     def _splash_text(self, extra_info, **kwargs):
87         return ("""
88 Hooke version %s
89
90 %s
91 ----
92 """ % (version(), short_license(extra_info, **kwargs))).strip()
93
94     def _playlist_status(self, playlist):
95         if len(playlist) > 0:
96             return '%s (%s/%s)' % (playlist.name, playlist.index() + 1,
97                                    len(playlist))
98         return 'The playlist %s does not contain any valid force curve data.' \
99             % self.name
100
101
102 USER_INTERFACES = construct_odict(
103     this_modname=__name__,
104     submodnames=USER_INTERFACE_MODULES,
105     class_selector=IsSubclass(UserInterface, blacklist=[UserInterface]))
106 """:class:`hooke.compat.odict.odict` of :class:`UserInterface`
107 instances keyed by `.name`.
108 """
109
110 def default_settings():
111     settings = [Setting(USER_INTERFACE_SETTING_SECTION,
112                         help='Select the user interface (only one).')]
113     for i,ui in enumerate(USER_INTERFACES.values()):
114         help = ui.__doc__.split('\n', 1)[0]
115         settings.append(Setting(USER_INTERFACE_SETTING_SECTION,
116                                 ui.name, str(i==0), help=help))
117         # i==0 to enable the first by default
118     for ui in USER_INTERFACES.values():
119         settings.extend(ui.default_settings())
120     return settings
121
122 def load_ui(config, name=None):
123     if name == None:
124         uis = [c for c,v in config.items(USER_INTERFACE_SETTING_SECTION) if v == 'True']
125         assert len(uis) == 1, 'Can only select one UI, not %d: %s' % (len(uis),uis)
126         name = uis[0]
127     ui = USER_INTERFACES[name]
128     ui.reload_config(config)
129     return ui