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, errors, messages 
 31  from flumotion.common.common import pathToModuleName 
 32  from flumotion.common.planet import AdminComponentState, moods 
 33  from flumotion.common.i18n import N_, gettexter 
 34  from gettext import gettext as _ 
 35   
 36  T_ = gettexter() 
 37   
 38  # ensure unjellier registered 
 39  componentui # pyflakes 
 40   
 41  __version__ = "$Rev: 7572 $" 
 42  _ = gettext.gettext 
 43  _DEBUG_ONLY_PAGES = ['Eaters', 'Feeders', 'Properties'] 
 44  (COMPONENT_UNSET, 
 45   COMPONENT_INACTIVE, 
 46   COMPONENT_ACTIVE) = range(3) 
 47   
 48   
49 -class Placeholder(object):
50 """A placeholder contains a Widget subclass of a specific 51 component. 52 """ 53
54 - def getWidget(self):
55 raise NotImplementedError( 56 "%r must implement a getWidget() method")
57
58 - def setDebugEnabled(self, enabled):
59 """Set if debug should be enabled. 60 Not all pages are visible unless debugging is set to true 61 @param enable: if debug should be enabled 62 """
63
64 - def removed(self):
65 """Called when the placeholder is inactivated, eg 66 detached from the parent"""
67 68
69 -class NotebookPlaceholder(Placeholder, log.Loggable):
70 """This is a placeholder containing a notebook with tabs 71 """ 72 logCategory = 'nodebook' 73
74 - def __init__(self, admingtk):
75 """ 76 @param admingtk: the GTK Admin with its nodes 77 @type admingtk: L{flumotion.component.base.admin_gtk.BaseAdminGtk} 78 """ 79 self._debugEnabled = False 80 self._admingtk = admingtk 81 self._notebook = None 82 self._pageWidgets = {} 83 84 self._notebook = gtk.Notebook() 85 admingtk.setup() 86 self.nodes = admingtk.getNodes() 87 self._appendPages() 88 self._notebook.show()
89 90 # BaseComponentHolder 91
92 - def getWidget(self):
93 return self._notebook
94
95 - def removed(self):
96 if self._admingtk: 97 # needed for compatibility with managers with old code 98 if hasattr(self._admingtk, 'cleanup'): 99 self._admingtk.cleanup() 100 self._admingtk = None
101
102 - def setDebugEnabled(self, enabled):
103 self._debugEnabled = enabled 104 if self._admingtk: 105 self._admingtk.setDebugEnabled(enabled) 106 for name in _DEBUG_ONLY_PAGES: 107 widget = self._pageWidgets.get(name) 108 if widget is None: 109 continue 110 widget.set_property('visible', enabled)
111
112 - def _renderWidget(self, widget, table):
113 # dumb dumb dumb dumb 114 old_parent = widget.get_parent() 115 if old_parent: 116 old_parent.remove(widget) 117 map(table.remove, table.get_children()) 118 table.add(widget) 119 widget.show()
120
121 - def _addPage(self, name):
122 node = self.nodes.get(name) 123 assert node is not None, name 124 125 table = gtk.Table(1, 1) 126 table.add(gtk.Label(_('Loading UI for %s...') % name)) 127 label = self._getTitleLabel(node, name) 128 label.show() 129 self._notebook.append_page(table, label) 130 131 d = node.render() 132 d.addCallback(self._renderWidget, table) 133 return table
134
135 - def _appendPages(self):
136 for name in self.nodes.keys(): 137 table = self._addPage(name) 138 self._pageWidgets[name] = table 139 140 if name in _DEBUG_ONLY_PAGES: 141 if self._debugEnabled: 142 continue 143 table.show()
144
145 - def _getTitleLabel(self, node, name):
146 title = node.title 147 if not title: 148 # FIXME: we have no way of showing an error message ? 149 # This should be added so users can file bugs. 150 self.warning("Component node %s does not have a " 151 "translatable title. Please file a bug." % name) 152 153 # fall back for now 154 title = name 155 156 return gtk.Label(title)
157 158
159 -class LabelPlaceholder(Placeholder):
160 """This is a placeholder with a label, with or without a text""" 161
162 - def __init__(self, text=''):
163 self._label = gtk.Label(text)
164
165 - def getWidget(self):
166 return self._label
167 168
169 -class PlanetPlaceholder(Placeholder):
170 """This is a placeholder used to display a Planet""" 171
172 - def __init__(self):
173 self._widget = gtk.Label('')
174
175 - def getWidget(self):
176 return self._widget
177 178
179 -class ComponentView(gtk.VBox, log.Loggable):
180 logCategory = 'componentview' 181
182 - def __init__(self):
183 gtk.VBox.__init__(self) 184 self._admin = None 185 self._currentComponentState = None 186 self._currentPlaceholder = None 187 self._debugEnabled = False 188 self._state = COMPONENT_UNSET 189 190 self._planetPlaceholder = PlanetPlaceholder() 191 self._addPlaceholder(self._planetPlaceholder)
192 193 # Public API 194
195 - def getDebugEnabled(self):
196 """Find out if debug is enabled 197 @returns: if debug is enabled 198 @rtype: bool 199 """ 200 return self._debugEnabled
201
202 - def setDebugEnabled(self, enabled):
203 """Sets if debug should be enabled 204 @param enabled: if debug should be enabled 205 @type enabled: bool 206 """ 207 self._debugEnabled = enabled 208 if self._currentPlaceholder: 209 self._currentPlaceholder.setDebugEnabled(enabled)
210
211 - def activateComponent(self, component):
212 """Activates a component in the view 213 @param component: component to show 214 @type component: L{flumotion.common.component.AdminComponentState} 215 """ 216 self._setState(COMPONENT_UNSET) 217 if component: 218 self._currentComponentState = component 219 self._setState(COMPONENT_INACTIVE)
220
221 - def setSingleAdmin(self, admin):
222 """ 223 Sets a single global admin for the component view 224 225 @param admin: the admin 226 @type admin: L{flumotion.admin.admin.AdminModel} 227 """ 228 self._admin = admin
229
230 - def getAdminForComponent(self, component):
231 """ 232 Get the admin for a specific component 233 234 @param component: component 235 @type component: L{flumotion.common.component.AdminComponentState} 236 237 @returns: the admin 238 @rtype: L{flumotion.admin.admin.AdminModel} 239 """ 240 # override me to do e.g. multi.getAdminForComponent 241 return self._admin
242 243 # Private 244
245 - def _addPlaceholder(self, placeholder):
246 if not isinstance(placeholder, Placeholder): 247 raise AssertionError( 248 "placeholder must be a Placeholder subclass, not %r" % ( 249 placeholder, )) 250 251 widget = placeholder.getWidget() 252 widget.show() 253 map(self.remove, self.get_children()) 254 self.pack_start(widget, True, True) 255 256 placeholder.setDebugEnabled(self._debugEnabled) 257 self._currentPlaceholder = placeholder
258
259 - def _removePlaceholder(self, placeholder):
260 widget = placeholder.getWidget() 261 self.remove(widget) 262 263 placeholder.removed()
264
265 - def _getWidgetConstructor(self, componentState):
266 if not isinstance(componentState, AdminComponentState): 267 return LabelPlaceholder() 268 269 def noBundle(failure): 270 failure.trap(errors.NoBundleError) 271 self.debug( 272 'No specific GTK admin for this component, using default') 273 return ("flumotion/component/base/admin_gtk.py", "BaseAdminGtk")
274 275 def oldVersion(failure): 276 # This is compatibility with platform-3 277 # FIXME: It would be better to do this using strict 278 # version checking of the manager 279 280 # File ".../flumotion/manager/admin.py", line 278, in 281 # perspective_getEntryByType 282 # exceptions.AttributeError: 'str' object has no attribute 'get' 283 failure.trap(AttributeError) 284 285 return admin.callRemote( 286 'getEntryByType', componentState, 'admin/gtk')
287 288 def gotEntryPoint((filename, procname)): 289 # The manager always returns / as a path separator, replace them 290 # with the separator since the rest of our infrastructure depends 291 # on that. 292 filename = filename.replace('/', os.path.sep) 293 # getEntry for admin/gtk returns a factory function for creating 294 # flumotion.component.base.admin_gtk.BaseAdminGtk 295 # subclass instances 296 modname = pathToModuleName(filename) 297 298 # we call hostile code, so we should handle exceptions: 299 d = admin.getBundledFunction(modname, procname) 300 d.addErrback(admin.bundleErrback, filename) 301 302 def handleSyntaxError(failure): 303 failure.trap(errors.EntrySyntaxError) 304 msg = failure.value.args[0] 305 306 m = messages.Error(T_( 307 N_("This component has a UI bug.")), debug=msg) 308 componentState.observe_append('messages', m) 309 310 raise errors.HandledException(failure.value) 311 312 d.addErrback(handleSyntaxError) 313 314 return d 315 316 def gotFactory(factory): 317 # instantiate from factory and wrap in a NotebookPlaceHolder 318 widget = factory(componentState, admin) 319 return NotebookPlaceholder(widget) 320 321 def sleepingComponent(failure): 322 failure.trap(errors.SleepingComponentError) 323 return LabelPlaceholder(_("Component '%s' is still sleeping.") % 324 componentState.get('name')) 325 326 def handledExceptionErrback(failure): 327 # already handle, so let call chain short-circuit here and 328 # just return 329 failure.trap(errors.HandledException) 330 return LabelPlaceholder(_("Component '%s' has a UI bug.") % 331 componentState.get('name')) 332 333 admin = self.getAdminForComponent(componentState) 334 componentType = componentState.get('type') 335 d = admin.callRemote('getEntryByType', componentType, 'admin/gtk') 336 d.addErrback(oldVersion) 337 d.addErrback(noBundle) 338 d.addCallback(gotEntryPoint) 339 d.addCallback(gotFactory) 340 d.addErrback(sleepingComponent) 341 d.addErrback(handledExceptionErrback) 342 return d 343
344 - def _componentUnsetToInactive(self):
345 346 def invalidate(_): 347 self._setState(COMPONENT_UNSET)
348 349 def set_(state, key, value): 350 if key != 'mood': 351 return 352 if value not in [moods.lost.value, 353 moods.sleeping.value, 354 moods.sad.value]: 355 self._setState(COMPONENT_ACTIVE) 356 else: 357 self._setState(COMPONENT_INACTIVE) 358 359 current = self._currentComponentState 360 assert current is not None 361 current.addListener(self, invalidate=invalidate, set_=set_) 362 if current.hasKey('mood'): 363 set_(current, 'mood', current.get('mood')) 364
365 - def _componentInactiveToActive(self):
366 367 def gotWidgetConstructor(placeholder, oldComponentState): 368 if oldComponentState != self._currentComponentState: 369 # in the time that _get_widget_constructor was running, 370 # perhaps the user selected another component; only update 371 # the ui if that did not happen 372 self.debug('ignoring component %r, state %d, state %r/%r' % ( 373 placeholder, self._state, 374 oldComponentState, self._currentComponentState)) 375 return 376 self._removePlaceholder(self._planetPlaceholder) 377 self._addPlaceholder(placeholder)
378 379 d = self._getWidgetConstructor(self._currentComponentState) 380 d.addCallback(gotWidgetConstructor, self._currentComponentState) 381
382 - def _componentActiveToInactive(self):
383 self._removePlaceholder(self._currentPlaceholder) 384 self._addPlaceholder(self._planetPlaceholder)
385
386 - def _componentInactiveToUnset(self):
387 if self._currentComponentState: 388 self._currentComponentState.removeListener(self) 389 self._currentComponentState = None
390
391 - def _setState(self, state):
392 uptable = [self._componentUnsetToInactive, 393 self._componentInactiveToActive] 394 downtable = [self._componentInactiveToUnset, 395 self._componentActiveToInactive] 396 if self._state < state: 397 while self._state < state: 398 self.log('component %r state change: %d++', 399 self._currentComponentState, self._state) 400 self._state += 1 401 uptable[self._state - 1]() 402 else: 403 while self._state > state: 404 self.log('component %r state change: %d--', 405 self._currentComponentState, self._state) 406 self._state -= 1 407 downtable[self._state]()
408 409 gobject.type_register(ComponentView) 410