Package flumotion :: Package admin :: Package gtk :: Module componentview
[hide private]

Source Code for Module flumotion.admin.gtk.componentview

  1  # -*- Mode: Python -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3  # 
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2004,2005,2006,2007,2008 Fluendo, S.L. (www.fluendo.com). 
  6  # All rights reserved. 
  7   
  8  # This file may be distributed and/or modified under the terms of 
  9  # the GNU General Public License version 2 as published by 
 10  # the Free Software Foundation. 
 11  # This file is distributed without any warranty; without even the implied 
 12  # warranty of merchantability or fitness for a particular purpose. 
 13  # See "LICENSE.GPL" in the source distribution for more information. 
 14   
 15  # Licensees having purchased or holding a valid Flumotion Advanced 
 16  # Streaming Server license may use this file in accordance with the 
 17  # Flumotion Advanced Streaming Server Commercial License Agreement. 
 18  # See "LICENSE.Flumotion" in the source distribution for more information. 
 19   
 20  # Headers in this file shall remain intact. 
 21   
 22  """widget holder displaying a component specific views""" 
 23   
 24  import gettext 
 25  import os 
 26   
 27  import gobject 
 28  import gtk 
 29   
 30  from flumotion.common import componentui, log 
 31  from flumotion.common.common import pathToModuleName 
 32  from flumotion.common.errors import NoBundleError, SleepingComponentError 
 33  from flumotion.common.planet import AdminComponentState, moods 
 34   
 35  # ensure unjellier registered 
 36  componentui # pyflakes 
 37   
 38  __version__ = "$Rev: 6990 $" 
 39  _ = gettext.gettext 
 40  _DEBUG_ONLY_PAGES = ['Eaters', 'Feeders', 'Properties'] 
 41  (COMPONENT_UNSET, 
 42   COMPONENT_INACTIVE, 
 43   COMPONENT_ACTIVE) = range(3) 
 44   
 45   
46 -class Placeholder(object):
47 """A placeholder contains a Widget subclass of a specific 48 component. 49 """ 50
51 - def getWidget(self):
52 raise NotImplementedError( 53 "%r must implement a getWidget() method")
54
55 - def setDebugEnabled(self, enabled):
56 """Set if debug should be enabled. 57 Not all pages are visible unless debugging is set to true 58 @param enable: if debug should be enabled 59 """
60
61 - def removed(self):
62 """Called when the placeholder is inactivated, eg 63 detached from the parent"""
64 65
66 -class NotebookPlaceholder(Placeholder, log.Loggable):
67 """This is a placeholder containing a notebook with tabs 68 """ 69 logCategory = 'nodebook' 70
71 - def __init__(self, admingtk):
72 """ 73 @param admingtk: the GTK Admin with its nodes 74 @type admingtk: L{flumotion.component.base.admin_gtk.BaseAdminGtk} 75 """ 76 self._debugEnabled = False 77 self._admingtk = admingtk 78 self._notebook = None 79 self._pageWidgets = {} 80 81 self._notebook = gtk.Notebook() 82 admingtk.setup() 83 self.nodes = admingtk.getNodes() 84 self._appendPages() 85 self._notebook.show()
86 87 # BaseComponentHolder
88 - def getWidget(self):
89 return self._notebook
90
91 - def removed(self):
92 if self._admingtk: 93 # needed for compatibility with managers with old code 94 if hasattr(self._admingtk, 'cleanup'): 95 self._admingtk.cleanup() 96 self._admingtk = None
97
98 - def setDebugEnabled(self, enabled):
99 self._debugEnabled = enabled 100 if self._admingtk: 101 self._admingtk.setDebugEnabled(enabled) 102 for name in _DEBUG_ONLY_PAGES: 103 widget = self._pageWidgets.get(name) 104 if widget is None: 105 continue 106 widget.set_property('visible', enabled)
107
108 - def _renderWidget(self, widget, table):
109 # dumb dumb dumb dumb 110 old_parent = widget.get_parent() 111 if old_parent: 112 old_parent.remove(widget) 113 map(table.remove, table.get_children()) 114 table.add(widget) 115 widget.show()
116
117 - def _addPage(self, name):
118 node = self.nodes.get(name) 119 assert node is not None, name 120 121 table = gtk.Table(1, 1) 122 table.add(gtk.Label(_('Loading UI for %s...') % name)) 123 label = self._getTitleLabel(node, name) 124 label.show() 125 self._notebook.append_page(table, label) 126 127 d = node.render() 128 d.addCallback(self._renderWidget, table) 129 return table
130
131 - def _appendPages(self):
132 for name in self.nodes.keys(): 133 table = self._addPage(name) 134 self._pageWidgets[name] = table 135 136 if name in _DEBUG_ONLY_PAGES: 137 if self._debugEnabled: 138 continue 139 table.show()
140
141 - def _getTitleLabel(self, node, name):
142 title = node.title 143 if not title: 144 # FIXME: we have no way of showing an error message ? 145 # This should be added so users can file bugs. 146 self.warning("Component node %s does not have a " 147 "translatable title. Please file a bug." % name) 148 149 # fall back for now 150 title = name 151 152 return gtk.Label(title)
153 154
155 -class LabelPlaceholder(Placeholder):
156 """This is a placeholder with a label, with or without a text"""
157 - def __init__(self, text=''):
158 self._label = gtk.Label(text)
159
160 - def getWidget(self):
161 return self._label
162 163
164 -class PlanetPlaceholder(Placeholder):
165 """This is a placeholder used to display a Planet"""
166 - def __init__(self):
167 self._widget = gtk.Label('')
168
169 - def getWidget(self):
170 return self._widget
171 172
173 -class ComponentView(gtk.VBox, log.Loggable):
174 logCategory = 'componentview' 175
176 - def __init__(self):
177 gtk.VBox.__init__(self) 178 self._admin = None 179 self._currentComponentState = None 180 self._currentPlaceholder = None 181 self._debugEnabled = False 182 self._state = COMPONENT_UNSET 183 184 self._planetPlaceholder = PlanetPlaceholder() 185 self._addPlaceholder(self._planetPlaceholder)
186 187 # Public API 188
189 - def getDebugEnabled(self):
190 """Find out if debug is enabled 191 @returns: if debug is enabled 192 @rtype: bool 193 """ 194 return self._debugEnabled
195
196 - def setDebugEnabled(self, enabled):
197 """Sets if debug should be enabled 198 @param enabled: if debug should be enabled 199 @type enabled: bool 200 """ 201 self._debugEnabled = enabled 202 if self._currentPlaceholder: 203 self._currentPlaceholder.setDebugEnabled(enabled)
204
205 - def activateComponent(self, component):
206 """Activates a component in the view 207 @param component: component to show 208 @type component: L{flumotion.common.component.AdminComponentState} 209 """ 210 self._setState(COMPONENT_UNSET) 211 if component: 212 self._currentComponentState = component 213 self._setState(COMPONENT_INACTIVE)
214
215 - def setSingleAdmin(self, admin):
216 """Sets a single global admin for the component view 217 @param admin: the admin 218 @type admin: L{flumotion.admin.model.AdminModel} 219 """ 220 self._admin = admin
221
222 - def getAdminForComponent(self, component):
223 """Get the admin for a specific component 224 @param component: component 225 @type component: L{flumotion.common.component.AdminComponentState} 226 @returns: the admin 227 @rtype: L{flumotion.admin.model.AdminModel} 228 """ 229 # override me to do e.g. multi.getAdminForComponent 230 return self._admin
231 232 # Private 233
234 - def _addPlaceholder(self, placeholder):
235 if not isinstance(placeholder, Placeholder): 236 raise AssertionError( 237 "placeholder must be a Placeholder subclass, not %r" % ( 238 placeholder,)) 239 240 widget = placeholder.getWidget() 241 widget.show() 242 self.pack_start(widget, True, True) 243 244 placeholder.setDebugEnabled(self._debugEnabled) 245 self._currentPlaceholder = placeholder
246
247 - def _removePlaceholder(self, placeholder):
248 widget = placeholder.getWidget() 249 self.remove(widget) 250 251 placeholder.removed()
252
253 - def _getWidgetConstructor(self, componentState):
254 if not isinstance(componentState, AdminComponentState): 255 return LabelPlaceholder() 256 257 def noBundle(failure): 258 failure.trap(NoBundleError) 259 self.debug( 260 'No specific GTK admin for this component, using default') 261 return ("flumotion/component/base/admin_gtk.py", "BaseAdminGtk")
262 263 def oldVersion(failure): 264 # This is compatibility with platform-3 265 # FIXME: It would be better to do this using strict 266 # version checking of the manager 267 268 # File ".../flumotion/manager/admin.py", line 278, in 269 # perspective_getEntryByType 270 # exceptions.AttributeError: 'str' object has no attribute 'get' 271 failure.trap(AttributeError) 272 273 return admin.callRemote( 274 'getEntryByType', componentState, 'admin/gtk')
275 276 def gotEntryPoint((filename, procname)): 277 # The manager always returns / as a path separator, replace them 278 # with the separator since the rest of our infrastructure depends 279 # on that. 280 filename = filename.replace('/', os.path.sep) 281 # getEntry for admin/gtk returns a factory function for creating 282 # flumotion.component.base.admin_gtk.BaseAdminGtk subclass instances 283 modname = pathToModuleName(filename) 284 return admin.getBundledFunction(modname, procname) 285 286 def gotFactory(factory): 287 # instantiate from factory and wrap in a NodeBook 288 return NotebookPlaceholder(factory(componentState, admin)) 289 290 def sleepingComponent(failure): 291 failure.trap(SleepingComponentError) 292 return LabelPlaceholder(_('Component %s is still sleeping') % 293 componentState.get('name')) 294 295 admin = self.getAdminForComponent(componentState) 296 componentType = componentState.get('type') 297 d = admin.callRemote('getEntryByType', componentType, 'admin/gtk') 298 d.addErrback(oldVersion) 299 d.addErrback(noBundle) 300 d.addCallback(gotEntryPoint) 301 d.addCallback(gotFactory) 302 d.addErrback(sleepingComponent) 303 return d 304
305 - def _componentUnsetToInactive(self):
306 def invalidate(_): 307 self._setState(COMPONENT_UNSET)
308 def set_(state, key, value): 309 if key != 'mood': 310 return 311 if value not in [moods.lost.value, 312 moods.sleeping.value, 313 moods.sad.value]: 314 self._setState(COMPONENT_ACTIVE) 315 else: 316 self._setState(COMPONENT_INACTIVE) 317 318 current = self._currentComponentState 319 assert current is not None 320 current.addListener(self, invalidate=invalidate, set_=set_) 321 if current.hasKey('mood'): 322 set_(current, 'mood', current.get('mood')) 323
324 - def _componentInactiveToActive(self):
325 def gotWidgetConstructor(placeholder, oldComponentState): 326 if oldComponentState != self._currentComponentState: 327 # in the time that _get_widget_constructor was running, 328 # perhaps the user selected another component; only update 329 # the ui if that did not happen 330 self.debug('ignoring component %r, state %d, state %r/%r' % ( 331 placeholder, self._state, 332 oldComponentState, self._currentComponentState)) 333 return 334 self._removePlaceholder(self._planetPlaceholder) 335 self._addPlaceholder(placeholder)
336 337 d = self._getWidgetConstructor(self._currentComponentState) 338 d.addCallback(gotWidgetConstructor, self._currentComponentState) 339
340 - def _componentActiveToInactive(self):
341 self._removePlaceholder(self._currentPlaceholder) 342 self._addPlaceholder(self._planetPlaceholder)
343
344 - def _componentInactiveToUnset(self):
345 if self._currentComponentState: 346 self._currentComponentState.removeListener(self) 347 self._currentComponentState = None
348
349 - def _setState(self, state):
350 uptable = [self._componentUnsetToInactive, 351 self._componentInactiveToActive] 352 downtable = [self._componentInactiveToUnset, 353 self._componentActiveToInactive] 354 if self._state < state: 355 while self._state < state: 356 self.log('component %r state change: %d++', 357 self._currentComponentState, self._state) 358 self._state += 1 359 uptable[self._state - 1]() 360 else: 361 while self._state > state: 362 self.log('component %r state change: %d--', 363 self._currentComponentState, self._state) 364 self._state -= 1 365 downtable[self._state]()
366 367 gobject.type_register(ComponentView) 368