hooke.ui.gui was getting complicated, so I stripped it down for a moment.
[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
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation, either
8 # version 3 of the License, or (at your option) any later version.
9 #
10 # Hooke is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU Lesser General 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 ..util.pluggable import IsSubclass, construct_odict
27
28
29 USER_INTERFACE_MODULES = [
30     'commandline',
31     'gui',
32     ]
33 """List of user interface modules.  TODO: autodiscovery
34 """
35
36 USER_INTERFACE_SETTING_SECTION = 'user interfaces'
37 """Name of the config section which controls UI selection.
38 """
39
40
41 class QueueMessage (object):
42     def __str__(self):
43         return self.__class__.__name__
44
45 class CloseEngine (QueueMessage):
46     pass
47
48 class CommandMessage (QueueMessage):
49     """A message storing a command to run, `command` should be a
50     :class:`hooke.command.Command` instance, and `arguments` should be
51     a :class:`dict` with `argname` keys and `value` values to be
52     passed to the command.
53     """
54     def __init__(self, command, arguments=None):
55         self.command = command
56         if arguments == None:
57             arguments = {}
58         self.arguments = arguments
59
60 class UserInterface (object):
61     """A user interface to drive the :class:`hooke.engine.CommandEngine`.
62     """
63     def __init__(self, name):
64         self.name = name
65         self.setting_section = '%s user interface' % (self.name)
66         self.config = {}
67
68     def default_settings(self):
69         """Return a list of :class:`hooke.config.Setting`\s for any
70         configurable UI settings.
71
72         The suggested section setting is::
73
74             Setting(section=self.setting_section, help=self.__doc__)
75         """
76         return []
77
78     def reload_config(self, config):
79         """Update the user interface for new config settings.
80
81         Should be called with the new `config` upon recipt of
82         `ReloadUserInterfaceConfig` from the `CommandEngine` or when
83         loading the initial configuration.
84         """
85         try:
86             self.config = dict(config.items(self.setting_section))
87         except configparser.NoSectionError:
88             self.config = {}
89
90     def run(self, commands, ui_to_command_queue, command_to_ui_queue):
91         return
92
93     # Assorted useful tidbits for subclasses
94
95     def _splash_text(self):
96         return ("""
97 Hooke version %s
98
99 COPYRIGHT
100 ----
101 """ % version()).strip()
102
103     def _playlist_status(self, playlist):
104         if len(playlist) > 0:
105             return '%s (%s/%s)' % (playlist.name, playlist._index + 1,
106                                    len(playlist))
107         return 'The playlist %s does not contain any valid force curve data.' \
108             % self.name
109
110
111 USER_INTERFACES = construct_odict(
112     this_modname=__name__,
113     submodnames=USER_INTERFACE_MODULES,
114     class_selector=IsSubclass(UserInterface, blacklist=[UserInterface]))
115 """:class:`hooke.compat.odict.odict` of :class:`UserInterface`
116 instances keyed by `.name`.
117 """
118
119 def default_settings():
120     settings = [Setting(USER_INTERFACE_SETTING_SECTION,
121                         help='Select the user interface (only one).')]
122     for i,ui in enumerate(USER_INTERFACES.values()):
123         help = ui.__doc__.split('\n', 1)[0]
124         settings.append(Setting(USER_INTERFACE_SETTING_SECTION,
125                                 ui.name, str(i==0), help=help))
126         # i==0 to enable the first by default
127     for ui in USER_INTERFACES.values():
128         settings.extend(ui.default_settings())
129     return settings
130
131 def load_ui(config, name=None):
132     if name == None:
133         uis = [c for c,v in config.items(USER_INTERFACE_SETTING_SECTION) if v == 'True']
134         assert len(uis) == 1, 'Can only select one UI, not %d: %s' % (len(uis),uis)
135         name = uis[0]
136     ui = USER_INTERFACES[name]
137     ui.reload_config(config)
138     return ui