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

Source Code for Module flumotion.admin.gtk.adminwindow

   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  """admin window interface, the main interface of flumotion-admin. 
  23   
  24  Here is an overview of the different parts of the admin interface:: 
  25   
  26   +--------------[ AdminWindow ]-------------+ 
  27   | Menubar                                  | 
  28   +------------------------------------------+ 
  29   | Toolbar                                  | 
  30   +--------------------+---------------------+ 
  31   |                    |                     | 
  32   |                    |                     | 
  33   |                    |                     | 
  34   |                    |                     | 
  35   |  ComponentList     |   ComponentView     | 
  36   |                    |                     | 
  37   |                    |                     | 
  38   |                    |                     | 
  39   |                    |                     | 
  40   |                    |                     | 
  41   +--------------------+---------------------+ 
  42   | AdminStatusbar                           | 
  43   +------------------------------------------- 
  44   
  45  The main class which builds everything together is a L{AdminWindow}, 
  46  which is defined in this file: 
  47   
  48    - L{AdminWindow} creates the other UI parts internally, see the 
  49      L{AdminWindow._createUI}. 
  50    - Menubar and Toolbar are created by a GtkUIManager, see 
  51      L{AdminWindow._createUI} and L{MAIN_UI}. 
  52    - L{ComponentList<flumotion.admin.gtk.componentlist.ComponentList>} 
  53      is a list of all components, and is created in the 
  54      L{flumotion.admin.gtk.componentlist} module. 
  55    - L{ComponentView<flumotion.admin.gtk.componentview.ComponentView>} 
  56      contains a component specific view, usually a set of tabs, it is 
  57      created in the L{flumotion.admin.gtk.componentview} module. 
  58    - L{AdminStatus<flumotion.admin.gtk.statusbar.AdminStatus>} is a 
  59      statusbar displaying context specific hints and is defined in the 
  60      L{flumotion.admin.gtk.statusbar} module. 
  61   
  62  """ 
  63   
  64  import gettext 
  65  import os 
  66  import sys 
  67   
  68  import gobject 
  69  import gtk 
  70  from gtk import gdk 
  71  from gtk import keysyms 
  72  from kiwi.ui.delegates import GladeDelegate 
  73  from kiwi.ui.dialogs import yesno 
  74  from twisted.internet import defer, reactor 
  75  from zope.interface import implements 
  76   
  77  from flumotion.admin.admin import AdminModel 
  78  from flumotion.admin.assistant.models import AudioProducer, Porter, \ 
  79       VideoProducer 
  80  from flumotion.admin.connections import getRecentConnections, \ 
  81       hasRecentConnections 
  82  from flumotion.admin.gtk.dialogs import AboutDialog, ErrorDialog, \ 
  83       ProgressDialog, showConnectionErrorDialog 
  84  from flumotion.admin.gtk.connections import ConnectionsDialog 
  85  from flumotion.admin.gtk.componentlist import getComponentLabel, ComponentList 
  86  from flumotion.admin.gtk.debugmarkerview import DebugMarkerDialog 
  87  from flumotion.admin.gtk.statusbar import AdminStatusbar 
  88  from flumotion.common.common import componentId 
  89  from flumotion.common.connection import PBConnectionInfo 
  90  from flumotion.common.errors import ConnectionCancelledError, \ 
  91       ConnectionRefusedError, ConnectionFailedError, BusyComponentError 
  92  from flumotion.common.i18n import N_, gettexter 
  93  from flumotion.common.log import Loggable 
  94  from flumotion.common.planet import AdminComponentState, moods 
  95  from flumotion.common.pygobject import gsignal 
  96  from flumotion.configure import configure 
  97  from flumotion.manager import admin # Register types 
  98  from flumotion.twisted.flavors import IStateListener 
  99  from flumotion.ui.trayicon import FluTrayIcon 
 100   
 101  admin # pyflakes 
 102   
 103  __version__ = "$Rev: 8152 $" 
 104  _ = gettext.gettext 
 105  T_ = gettexter() 
 106   
 107  MAIN_UI = """ 
 108  <ui> 
 109    <menubar name="Menubar"> 
 110      <menu action="Connection"> 
 111        <menuitem action="OpenRecent"/> 
 112        <menuitem action="OpenExisting"/> 
 113        <menuitem action="ImportConfig"/> 
 114        <menuitem action="ExportConfig"/> 
 115        <separator name="sep-conn1"/> 
 116        <placeholder name="Recent"/> 
 117        <separator name="sep-conn2"/> 
 118        <menuitem action="Quit"/> 
 119      </menu> 
 120      <menu action="Manage"> 
 121        <menuitem action="StartComponent"/> 
 122        <menuitem action="StopComponent"/> 
 123        <menuitem action="DeleteComponent"/> 
 124        <separator name="sep-manage1"/> 
 125        <menuitem action="StartAll"/> 
 126        <menuitem action="StopAll"/> 
 127        <menuitem action="ClearAll"/> 
 128        <separator name="sep-manage2"/> 
 129        <menuitem action="AddFormat"/> 
 130        <separator name="sep-manage3"/> 
 131        <menuitem action="RunConfigurationAssistant"/> 
 132      </menu> 
 133      <menu action="Debug"> 
 134        <menuitem action="EnableDebugging"/> 
 135        <separator name="sep-debug1"/> 
 136        <menuitem action="StartShell"/> 
 137        <menuitem action="DumpConfiguration"/> 
 138        <menuitem action="WriteDebugMarker"/> 
 139      </menu> 
 140      <menu action="Help"> 
 141        <menuitem action="Contents"/> 
 142        <menuitem action="About"/> 
 143      </menu> 
 144    </menubar> 
 145    <toolbar name="Toolbar"> 
 146      <toolitem action="OpenRecent"/> 
 147      <separator name="sep-toolbar1"/> 
 148      <toolitem action="StartComponent"/> 
 149      <toolitem action="StopComponent"/> 
 150      <toolitem action="DeleteComponent"/> 
 151      <separator name="sep-toolbar2"/> 
 152      <toolitem action="RunConfigurationAssistant"/> 
 153    </toolbar> 
 154    <popup name="ComponentContextMenu"> 
 155      <menuitem action="StartComponent"/> 
 156      <menuitem action="StopComponent"/> 
 157      <menuitem action="DeleteComponent"/> 
 158      <menuitem action="KillComponent"/> 
 159    </popup> 
 160  </ui> 
 161  """ 
 162   
 163  RECENT_UI_TEMPLATE = '''<ui> 
 164    <menubar name="Menubar"> 
 165      <menu action="Connection"> 
 166        <placeholder name="Recent"> 
 167        %s 
 168        </placeholder> 
 169      </menu> 
 170    </menubar> 
 171  </ui>''' 
 172   
 173  MAX_RECENT_ITEMS = 4 
 174   
 175   
176 -class AdminWindow(Loggable, GladeDelegate):
177 '''Creates the GtkWindow for the user interface. 178 Also connects to the manager on the given host and port. 179 ''' 180 181 # GladeDelegate 182 gladefile = 'admin.glade' 183 toplevel_name = 'main_window' 184 185 # Loggable 186 logCategory = 'adminwindow' 187 188 # Interfaces we implement 189 implements(IStateListener) 190 191 # Signals 192 gsignal('connected') 193
194 - def __init__(self):
195 GladeDelegate.__init__(self) 196 197 self._adminModel = None 198 self._currentComponentStates = None 199 self._componentContextMenu = None 200 self._componentList = None # ComponentList 201 self._componentStates = None # name -> planet.AdminComponentState 202 self._componentView = None 203 self._componentNameToSelect = None 204 self._debugEnabled = False 205 self._debugActions = None 206 self._debugEnableAction = None 207 self._disconnectedDialog = None # set to a dialog when disconnected 208 self._planetState = None 209 self._recentMenuID = None 210 self._trayicon = None 211 self._configurationAssistantIsRunning = False 212 213 self._createUI() 214 self._appendRecentConnections() 215 self.setDebugEnabled(False)
216 217 # Public API 218 219 #FIXME: This function may not be called ever. 220 # It has not been properly tested 221 # with the multiselection (ticket #795). 222 # A ticket for reviewing that has been opened #961 223
224 - def stateSet(self, state, key, value):
225 # called by model when state of something changes 226 if not isinstance(state, AdminComponentState): 227 return 228 229 if key == 'message': 230 self.statusbar.set('main', value) 231 elif key == 'mood': 232 self.debug('state %r has mood set to %r' % (state, value)) 233 self._updateComponentActions() 234 current = self.components_view.getSelectedNames() 235 if value == moods.sleeping.value: 236 if state.get('name') in current: 237 self._messageView.clearMessage(value.id)
238 239 #FIXME: This function may not be called ever. 240 # It has not been properly tested 241 # with the multiselection (ticket #795). 242 # A ticket for reviewing that has been opened #961 243
244 - def componentCallRemoteStatus(self, state, pre, post, fail, 245 methodName, *args, **kwargs):
246 247 def cb(result, self, mid): 248 if mid: 249 self.statusbar.remove('main', mid) 250 if post: 251 self.statusbar.push('main', post % label)
252 253 def eb(failure, self, mid): 254 if mid: 255 self.statusbar.remove('main', mid) 256 self.warning("Failed to execute %s on component %s: %s" 257 % (methodName, label, failure)) 258 if fail: 259 self.statusbar.push('main', fail % label)
260 if not state: 261 states = self.components_view.getSelectedStates() 262 if not states: 263 return 264 for state in states: 265 self.componentCallRemoteStatus(state, pre, post, fail, 266 methodName, args, kwargs) 267 else: 268 label = getComponentLabel(state) 269 if not label: 270 return 271 272 mid = None 273 if pre: 274 mid = self.statusbar.push('main', pre % label) 275 d = self._adminModel.componentCallRemote( 276 state, methodName, *args, **kwargs) 277 d.addCallback(cb, self, mid) 278 d.addErrback(eb, self, mid) 279
280 - def componentCallRemote(self, state, methodName, *args, **kwargs):
281 self.componentCallRemoteStatus(None, None, None, None, 282 methodName, *args, **kwargs)
283
284 - def whsAppend(self, state, key, value):
285 if key == 'names': 286 self._componentList.workerAppend(value) 287 self._clearLastStatusbarText() 288 self._setStatusbarText(_('Worker %s logged in.') % value)
289
290 - def whsRemove(self, state, key, value):
291 if key == 'names': 292 self._componentList.workerRemove(value) 293 self._clearLastStatusbarText() 294 self._setStatusbarText(_('Worker %s logged out.') % value)
295
296 - def show(self):
297 self._window.show()
298
299 - def setDebugEnabled(self, enabled):
300 """Set if debug should be enabled for the admin client window 301 @param enable: if debug should be enabled 302 """ 303 self._debugEnabled = enabled 304 self._debugActions.set_sensitive(enabled) 305 self._debugEnableAction.set_active(enabled) 306 self._componentView.setDebugEnabled(enabled) 307 self._killComponentAction.set_property('visible', enabled)
308
309 - def getWindow(self):
310 """Get the gtk window for the admin interface. 311 312 @returns: window 313 @rtype: L{gtk.Window} 314 """ 315 return self._window
316
317 - def openConnection(self, info):
318 """Connects to a manager given a connection info. 319 320 @param info: connection info 321 @type info: L{PBConnectionInfo} 322 """ 323 assert isinstance(info, PBConnectionInfo), info 324 return self._openConnection(info)
325 326 # Private 327
328 - def _createUI(self):
329 self.debug('creating UI') 330 331 # Widgets created in admin.glade 332 self._window = self.toplevel 333 self._componentList = ComponentList(self.component_list) 334 del self.component_list 335 self._componentView = self.component_view 336 del self.component_view 337 self._statusbar = AdminStatusbar(self.statusbar) 338 del self.statusbar 339 self._messageView = self.messages_view 340 del self.messages_view 341 342 self._window.set_name("AdminWindow") 343 self._window.connect('delete-event', 344 self._window_delete_event_cb) 345 self._window.connect('key-press-event', 346 self._window_key_press_event_cb) 347 348 uimgr = gtk.UIManager() 349 uimgr.connect('connect-proxy', 350 self._on_uimanager__connect_proxy) 351 uimgr.connect('disconnect-proxy', 352 self._on_uimanager__disconnect_proxy) 353 354 # Normal actions 355 group = gtk.ActionGroup('Actions') 356 group.add_actions([ 357 # Connection 358 ('Connection', None, _("_Connection")), 359 ('OpenRecent', gtk.STOCK_OPEN, _('_Open Recent Connection...'), 360 None, _('Connect to a recently used connection'), 361 self._connection_open_recent_cb), 362 ('OpenExisting', None, _('Connect to _running manager...'), None, 363 _('Connect to a previously used connection'), 364 self._connection_open_existing_cb), 365 ('ImportConfig', None, _('_Import Configuration...'), None, 366 _('Import a configuration from a file'), 367 self._connection_import_configuration_cb), 368 ('ExportConfig', None, _('_Export Configuration...'), None, 369 _('Export the current configuration to a file'), 370 self._connection_export_configuration_cb), 371 ('Quit', gtk.STOCK_QUIT, _('_Quit'), None, 372 _('Quit the application and disconnect from the manager'), 373 self._connection_quit_cb), 374 375 # Manage 376 ('Manage', None, _('_Manage')), 377 ('StartComponent', gtk.STOCK_MEDIA_PLAY, _('_Start Component(s)'), 378 None, _('Start the selected component(s)'), 379 self._manage_start_component_cb), 380 ('StopComponent', gtk.STOCK_MEDIA_STOP, _('St_op Component(s)'), 381 None, _('Stop the selected component(s)'), 382 self._manage_stop_component_cb), 383 ('DeleteComponent', gtk.STOCK_DELETE, _('_Delete Component(s)'), 384 None, _('Delete the selected component(s)'), 385 self._manage_delete_component_cb), 386 ('StartAll', None, _('Start _All'), None, 387 _('Start all components'), 388 self._manage_start_all_cb), 389 ('StopAll', None, _('Stop A_ll'), None, 390 _('Stop all components'), 391 self._manage_stop_all_cb), 392 ('ClearAll', gtk.STOCK_CLEAR, _('_Clear All'), None, 393 _('Remove all components'), 394 self._manage_clear_all_cb), 395 ('AddFormat', gtk.STOCK_ADD, _('Add new encoding _format...'), 396 None, 397 _('Add a new format to the current stream'), 398 self._manage_add_format_cb), 399 ('RunConfigurationAssistant', 'flumotion.admin.gtk', 400 _('Run _Assistant'), None, 401 _('Run the configuration assistant'), 402 self._manage_run_assistant_cb), 403 404 # Debug 405 ('Debug', None, _('_Debug')), 406 407 # Help 408 ('Help', None, _('_Help')), 409 ('Contents', gtk.STOCK_HELP, _('_Contents'), 'F1', 410 _('Open the Flumotion manual'), 411 self._help_contents_cb), 412 ('About', gtk.STOCK_ABOUT, _('_About'), None, 413 _('About this software'), 414 self._help_about_cb), 415 416 # Only in context menu 417 ('KillComponent', None, _('_Kill Component'), None, 418 _('Kills the currently selected component'), 419 self._kill_component_cb), 420 421 ]) 422 group.add_toggle_actions([ 423 ('EnableDebugging', None, _('Enable _Debugging'), None, 424 _('Enable debugging in the admin interface'), 425 self._debug_enable_cb), 426 ]) 427 self._debugEnableAction = group.get_action('EnableDebugging') 428 uimgr.insert_action_group(group, 0) 429 430 # Debug actions 431 self._debugActions = gtk.ActionGroup('Actions') 432 self._debugActions.add_actions([ 433 # Debug 434 ('StartShell', gtk.STOCK_EXECUTE, _('Start _Shell'), None, 435 _('Start an interactive debugging shell'), 436 self._debug_start_shell_cb), 437 ('DumpConfiguration', gtk.STOCK_EXECUTE, 438 _('Dump configuration'), None, 439 _('Dumps the current manager configuration'), 440 self._debug_dump_configuration_cb), 441 ('WriteDebugMarker', gtk.STOCK_EXECUTE, 442 _('Write debug marker...'), None, 443 _('Writes a debug marker to all the logs'), 444 self._debug_write_debug_marker_cb)]) 445 uimgr.insert_action_group(self._debugActions, 0) 446 self._debugActions.set_sensitive(False) 447 448 uimgr.add_ui_from_string(MAIN_UI) 449 self._window.add_accel_group(uimgr.get_accel_group()) 450 451 menubar = uimgr.get_widget('/Menubar') 452 self.main_vbox.pack_start(menubar, expand=False) 453 self.main_vbox.reorder_child(menubar, 0) 454 455 toolbar = uimgr.get_widget('/Toolbar') 456 toolbar.set_icon_size(gtk.ICON_SIZE_SMALL_TOOLBAR) 457 toolbar.set_style(gtk.TOOLBAR_ICONS) 458 self.main_vbox.pack_start(toolbar, expand=False) 459 self.main_vbox.reorder_child(toolbar, 1) 460 461 self._componentContextMenu = uimgr.get_widget('/ComponentContextMenu') 462 self._componentContextMenu.show() 463 464 menubar.show_all() 465 466 self._actiongroup = group 467 self._uimgr = uimgr 468 self._openRecentAction = group.get_action("OpenRecent") 469 self._startComponentAction = group.get_action("StartComponent") 470 self._stopComponentAction = group.get_action("StopComponent") 471 self._deleteComponentAction = group.get_action("DeleteComponent") 472 self._stopAllAction = group.get_action("StopAll") 473 assert self._stopAllAction 474 self._startAllAction = group.get_action("StartAll") 475 assert self._startAllAction 476 self._clearAllAction = group.get_action("ClearAll") 477 assert self._clearAllAction 478 self._addFormatAction = group.get_action("AddFormat") 479 self._addFormatAction.set_sensitive(False) 480 self._runConfigurationAssistantAction = ( 481 group.get_action("RunConfigurationAssistant")) 482 self._runConfigurationAssistantAction.set_sensitive(False) 483 self._killComponentAction = group.get_action("KillComponent") 484 assert self._killComponentAction 485 486 self._trayicon = FluTrayIcon(self._window) 487 self._trayicon.connect("quit", self._trayicon_quit_cb) 488 self._trayicon.set_tooltip(_('Flumotion: Not connected')) 489 490 self._componentList.connect('selection_changed', 491 self._components_selection_changed_cb) 492 self._componentList.connect('show-popup-menu', 493 self._components_show_popup_menu_cb) 494 495 self._updateComponentActions() 496 self._componentList.connect( 497 'notify::can-start-any', 498 self._components_start_stop_notify_cb) 499 self._componentList.connect( 500 'notify::can-stop-any', 501 self._components_start_stop_notify_cb) 502 self._updateComponentActions() 503 504 self._messageView.hide()
505
506 - def _connectActionProxy(self, action, widget):
507 tooltip = action.get_property('tooltip') 508 if not tooltip: 509 return 510 511 if isinstance(widget, gtk.MenuItem): 512 cid = widget.connect('select', self._on_menu_item__select, 513 tooltip) 514 cid2 = widget.connect('deselect', self._on_menu_item__deselect) 515 widget.set_data('pygtk-app::proxy-signal-ids', (cid, cid2)) 516 elif isinstance(widget, gtk.ToolButton): 517 cid = widget.child.connect('enter', self._on_tool_button__enter, 518 tooltip) 519 cid2 = widget.child.connect('leave', self._on_tool_button__leave) 520 widget.set_data('pygtk-app::proxy-signal-ids', (cid, cid2))
521
522 - def _disconnectActionProxy(self, action, widget):
523 cids = widget.get_data('pygtk-app::proxy-signal-ids') 524 if not cids: 525 return 526 527 if isinstance(widget, gtk.ToolButton): 528 widget = widget.child 529 530 for cid in cids: 531 widget.disconnect(cid)
532
533 - def _setAdminModel(self, model):
534 'set the model to which we are a view/controller' 535 # it's ok if we've already been connected 536 self.debug('setting model') 537 538 if self._adminModel is not None: 539 self._adminModel.disconnectFromManager() 540 self.debug('Connecting to new model %r' % model) 541 542 self._adminModel = model 543 544 whs = self._adminModel.getWorkerHeavenState() 545 whs.addListener(self, append=self.whsAppend, remove=self.whsRemove) 546 for worker in whs.get('names'): 547 self._componentList.workerAppend(worker) 548 549 # window gets created after model connects initially, so check 550 # here 551 if self._adminModel.isConnected(): 552 self._connectionOpened(model) 553 554 self._adminModel.connect('connected', 555 self._admin_connected_cb) 556 self._adminModel.connect('disconnected', 557 self._admin_disconnected_cb) 558 self._adminModel.connect('update', self._admin_update_cb) 559 560 self._runConfigurationAssistantAction.set_sensitive(True)
561
562 - def _openConnection(self, info):
563 self._trayicon.set_tooltip(_("Flumotion: Connecting to %s:%s") % ( 564 info.host, info.port)) 565 566 def connected(model): 567 self._setAdminModel(model) 568 self._appendRecentConnections()
569 570 model = AdminModel() 571 d = model.connectToManager(info) 572 d.addCallback(connected) 573 return d 574
575 - def _openConnectionInternal(self, info):
576 d = self._openConnection(info) 577 578 def errorMessageDisplayed(unused): 579 self._window.set_sensitive(True)
580 581 def connected(model): 582 self._window.set_sensitive(True) 583 584 def errbackConnectionRefusedError(failure): 585 failure.trap(ConnectionRefusedError) 586 d = showConnectionErrorDialog(failure, info, parent=self._window) 587 d.addCallback(errorMessageDisplayed) 588 589 def errbackConnectionFailedError(failure): 590 failure.trap(ConnectionFailedError) 591 d = showConnectionErrorDialog(failure, info, parent=self._window) 592 d.addCallback(errorMessageDisplayed) 593 return d 594 595 d.addCallback(connected) 596 d.addErrback(errbackConnectionRefusedError) 597 d.addErrback(errbackConnectionFailedError) 598 self._window.set_sensitive(False) 599 return d 600
601 - def _appendRecentConnections(self):
602 if self._recentMenuID: 603 self._uimgr.remove_ui(self._recentMenuID) 604 self._uimgr.ensure_update() 605 606 ui = "" 607 connections = getRecentConnections()[:MAX_RECENT_ITEMS] 608 for conn in connections: 609 name = conn.host 610 ui += '<menuitem action="%s"/>' % name 611 action = gtk.Action(name, name, 612 _('Connect to the manager on %s') % conn.host, 613 '') 614 action.connect('activate', self._recent_action_activate_cb, conn) 615 self._actiongroup.add_action(action) 616 617 self._recentMenuID = self._uimgr.add_ui_from_string( 618 RECENT_UI_TEMPLATE % ui) 619 self._openRecentAction.set_sensitive(len(connections))
620
621 - def _quit(self):
622 """Quitting the application in a controlled manner""" 623 self._clearAdmin() 624 self._close()
625
626 - def _close(self, *args):
627 reactor.stop()
628
629 - def _dumpConfig(self, configation):
630 import pprint 631 import cStringIO 632 fd = cStringIO.StringIO() 633 pprint.pprint(configation, fd) 634 fd.seek(0) 635 self.debug('Configuration=%s' % fd.read())
636
637 - def _error(self, message):
638 errorDialog = ErrorDialog(message, self._window, 639 close_on_response=True) 640 errorDialog.show()
641
642 - def _setStatusbarText(self, text):
643 return self._statusbar.push('main', text)
644
645 - def _clearLastStatusbarText(self):
646 self._statusbar.pop('main')
647
648 - def _assistantFinshed(self, assistant, configuration):
649 assistant.destroy() 650 self._configurationAssistantIsRunning = False 651 self._dumpConfig(configuration) 652 self._adminModel.loadConfiguration(configuration) 653 self._clearMessages() 654 self._statusbar.clear(None) 655 self._updateComponentActions() 656 scenario = assistant.getScenario() 657 self._componentNameToSelect = scenario.getSelectComponentName() 658 self.show()
659
660 - def _getComponentsBy(self, componentType):
661 """ 662 Obtains the components according a given type. 663 664 @param componentType: The type of the components to get 665 @type componentType: str 666 667 @rtype : list of L{flumotion.common.component.AdminComponentState} 668 """ 669 if componentType is None: 670 raise ValueError 671 672 componentStates = [] 673 674 for state in self._componentStates.values(): 675 config = state.get('config') 676 if componentType and config['type'] == componentType: 677 componentStates.append(state) 678 679 return componentStates
680
681 - def _getHTTPPorters(self):
682 """ 683 Obtains the porters currently configured on the running flow. 684 685 @rtype : list of L{flumotion.admin.assistant.models.Porter} 686 """ 687 porterList = [] 688 porterStates = self._getComponentsBy(componentType='porter') 689 690 for porter in porterStates: 691 properties = porter.get('config')['properties'] 692 porterModel = Porter(worker=porter.get('workerName') or 693 porter.get('workerRequested'), 694 port=properties['port'], 695 username=properties['username'], 696 password=properties['password'], 697 socketPath=properties['socket-path']) 698 porterModel.exists = True 699 porterList.append(porterModel) 700 701 return porterList
702
703 - def _setMountPoints(self, wizard):
704 """ 705 Sets the mount points currently used on the flow so they can not 706 be used for others servers or streamers. 707 708 @param wizard : An assistant that wants to know the used mount_points 709 @type wizard : L{ConfigurationAssistant} 710 """ 711 streamerStates = self._getComponentsBy(componentType='http-streamer') 712 serverStates = self._getComponentsBy(componentType='http-server') 713 porterStates = self._getComponentsBy(componentType='porter') 714 715 for porter in porterStates: 716 properties = porter.get('config')['properties'] 717 for streamer in streamerStates + serverStates: 718 streamerProperties = streamer.get('config')['properties'] 719 socketPath = streamerProperties['porter-socket-path'] 720 721 if socketPath == properties['socket-path']: 722 worker = streamer.get('workerRequested') 723 port = int(properties['port']) 724 mount_point = streamerProperties['mount-point'] 725 wizard.addMountPoint(worker, port, mount_point)
726
727 - def _createComponentsByAssistantType(self, componentClass, entries):
728 729 def _getComponents(): 730 for componentState in self._componentStates.values(): 731 componentType = componentState.get('config')['type'] 732 for entry in entries: 733 if entry.componentType == componentType: 734 yield (componentState, entry)
735 736 for componentState, entry in _getComponents(): 737 component = componentClass() 738 component.componentType = entry.componentType 739 component.description = entry.description 740 component.exists = True 741 component.name = componentState.get('name') 742 config = componentState.get('config') 743 for key, value in config['properties'].items(): 744 component.properties[key] = value 745 yield component 746
747 - def _runAddNewFormatAssistant(self):
748 if not self._adminModel.isConnected(): 749 self._error( 750 _('Cannot run assistant without being connected to a manager')) 751 return 752 753 from flumotion.admin.gtk.configurationassistant import \ 754 ConfigurationAssistant 755 756 configurationAssistant = ConfigurationAssistant(self._window) 757 758 def gotWizardEntries(entries): 759 entryDict = {} 760 for entry in entries: 761 entryDict.setdefault(entry.type, []).append(entry) 762 763 audioProducers = self._createComponentsByAssistantType( 764 AudioProducer, entryDict['audio-producer'], ) 765 videoProducers = self._createComponentsByAssistantType( 766 VideoProducer, entryDict['video-producer']) 767 scenario = configurationAssistant.getScenario() 768 scenario.setAudioProducers(audioProducers) 769 scenario.setVideoProducers(videoProducers) 770 771 self._runAssistant(configurationAssistant)
772 773 def gotBundledFunction(function): 774 scenario = function() 775 scenario.setMode('addformat') 776 scenario.addSteps(configurationAssistant) 777 configurationAssistant.setScenario(scenario) 778 httpPorters = self._getHTTPPorters() 779 self._setMountPoints(configurationAssistant) 780 if httpPorters: 781 configurationAssistant.setHTTPPorters(httpPorters) 782 783 return self._adminModel.getWizardEntries( 784 wizardTypes=['audio-producer', 'video-producer']) 785 786 d = self._adminModel.getBundledFunction( 787 'flumotion.scenario.live.wizard_gtk', 788 'LiveAssistantPlugin') 789 790 d.addCallback(gotBundledFunction) 791 d.addCallback(gotWizardEntries) 792
793 - def _runConfigurationAssistant(self):
794 if not self._adminModel.isConnected(): 795 self._error( 796 _('Cannot run assistant without being connected to a manager')) 797 return 798 799 from flumotion.admin.gtk.configurationassistant import \ 800 ConfigurationAssistant 801 802 def runAssistant(): 803 configurationAssistant = ConfigurationAssistant(self._window) 804 configurationAssistant.addInitialSteps() 805 self._runAssistant(configurationAssistant)
806 807 if not self._componentStates: 808 runAssistant() 809 return 810 811 for componentState in self._componentList.getComponentStates(): 812 if componentState.get('mood') == moods.lost.value: 813 self._error( 814 _("Cannot run the configuration assistant since there " 815 "is at least one component in the lost state")) 816 return 817 818 if yesno(_("Running the Configuration Assistant again will remove " 819 "all components from the current stream and create " 820 "a new one."), 821 parent=self._window, 822 buttons=((_("Keep the current stream"), 823 gtk.RESPONSE_NO), 824 (_("Run the Assistant anyway"), 825 gtk.RESPONSE_YES))) != gtk.RESPONSE_YES: 826 return 827 828 d = self._clearAllComponents() 829 # The remote call returns a list with the results of the cleaning. 830 # None if there has been an error during the processs. 831 d.addCallback(lambda list: list and runAssistant()) 832
833 - def _runAssistant(self, assistant):
834 if self._adminModel is None: 835 return 836 837 workerHeavenState = self._adminModel.getWorkerHeavenState() 838 if not workerHeavenState.get('names'): 839 self._error( 840 _('The assistant cannot be run because no workers are ' 841 'logged in.')) 842 return 843 844 self._configurationAssistantIsRunning = True 845 assistant.setExistingComponentNames( 846 self._componentList.getComponentNames()) 847 assistant.setAdminModel(self._adminModel) 848 assistant.setWorkerHeavenState(workerHeavenState) 849 httpPorters = self._getHTTPPorters() 850 if httpPorters: 851 assistant.setHTTPPorters(httpPorters) 852 assistant.connect('finished', self._assistant_finished_cb) 853 assistant.connect('destroy', self.on_assistant_destroy) 854 855 assistant.run(main=False)
856
857 - def _clearAdmin(self):
858 if self._adminModel is None: 859 return 860 861 self._adminModel.disconnect_by_func(self._admin_connected_cb) 862 self._adminModel.disconnect_by_func(self._admin_disconnected_cb) 863 self._adminModel.disconnect_by_func(self._admin_update_cb) 864 self._adminModel = None 865 866 self._addFormatAction.set_sensitive(False) 867 self._runConfigurationAssistantAction.set_sensitive(False)
868
869 - def _updateUIStatus(self, connected):
870 self._window.set_sensitive(connected) 871 group = self._actiongroup 872 group.get_action('ImportConfig').set_sensitive(connected) 873 group.get_action('ExportConfig').set_sensitive(connected) 874 group.get_action('EnableDebugging').set_sensitive(connected) 875 876 self._clearLastStatusbarText() 877 if connected: 878 self._window.set_title(_('%s - Flumotion Administration') % 879 self._adminModel.adminInfoStr()) 880 self._trayicon.set_tooltip(_('Flumotion: %s') % ( 881 self._adminModel.adminInfoStr(), )) 882 else: 883 self._setStatusbarText(_('Not connected')) 884 self._trayicon.set_tooltip(_('Flumotion: Not connected'))
885
886 - def _updateConnectionActions(self):
887 self._openRecentAction.set_sensitive(hasRecentConnections())
888
889 - def _updateComponentActions(self):
890 canStart = self._componentList.canStart() 891 canStop = self._componentList.canStop() 892 canDelete = self._componentList.canDelete() 893 self._startComponentAction.set_sensitive(canStart) 894 self._stopComponentAction.set_sensitive(canStop) 895 self._deleteComponentAction.set_sensitive(canDelete) 896 self.debug('can start %r, can stop %r, can delete %r' % ( 897 canStart, canStop, canDelete)) 898 canStartAll = self._componentList.get_property('can-start-any') 899 canStopAll = self._componentList.get_property('can-stop-any') 900 901 # they're all in sleeping or lost 902 canClearAll = canStartAll and not canStopAll 903 self._stopAllAction.set_sensitive(canStopAll) 904 self._startAllAction.set_sensitive(canStartAll) 905 self._clearAllAction.set_sensitive(canClearAll) 906 self._killComponentAction.set_sensitive(canStop) 907 908 hasProducer = self._hasProducerComponent() 909 self._addFormatAction.set_sensitive(hasProducer)
910
911 - def _updateComponents(self):
912 self._componentList.clearAndRebuild(self._componentStates, 913 self._componentNameToSelect) 914 self._trayicon.update(self._componentStates)
915
916 - def _appendComponent(self, component):
917 self._componentStates[component.get('name')] = component 918 self._componentList.appendComponent(component, 919 self._componentNameToSelect) 920 self._trayicon.update(self._componentStates)
921
922 - def _hasProducerComponent(self):
923 for state in self._componentList.getComponentStates(): 924 if state is None: 925 continue 926 # FIXME: Not correct, should expose assistant state from 927 # the registry. 928 name = state.get('name') 929 if 'producer' in name: 930 return True 931 return False
932
933 - def _clearMessages(self):
934 self._messageView.clear() 935 pstate = self._planetState 936 if pstate and pstate.hasKey('messages'): 937 for message in pstate.get('messages').values(): 938 self._messageView.addMessage(message)
939
940 - def _setPlanetState(self, planetState):
941 942 def flowStateAppend(state, key, value): 943 self.debug('flow state append: key %s, value %r' % (key, value)) 944 if key == 'components': 945 self._appendComponent(value)
946 947 def flowStateRemove(state, key, value): 948 if key == 'components': 949 self._removeComponent(value) 950 951 def atmosphereStateAppend(state, key, value): 952 if key == 'components': 953 self._appendComponent(value) 954 955 def atmosphereStateRemove(state, key, value): 956 if key == 'components': 957 self._removeComponent(value) 958 959 def planetStateAppend(state, key, value): 960 if key == 'flows': 961 if value != state.get('flows')[0]: 962 self.warning('flumotion-admin can only handle one ' 963 'flow, ignoring /%s', value.get('name')) 964 return 965 self.debug('%s flow started', value.get('name')) 966 value.addListener(self, append=flowStateAppend, 967 remove=flowStateRemove) 968 969 self._componentStates.update( 970 dict((c.get('name'), c) for c in value.get('components'))) 971 self._updateComponents() 972 973 def planetStateRemove(state, key, value): 974 self.debug('something got removed from the planet') 975 976 def planetStateSetitem(state, key, subkey, value): 977 if key == 'messages': 978 self._messageView.addMessage(value) 979 980 def planetStateDelitem(state, key, subkey, value): 981 if key == 'messages': 982 self._messageView.clearMessage(value.id) 983 984 self.debug('parsing planetState %r' % planetState) 985 self._planetState = planetState 986 987 # clear and rebuild list of components that interests us 988 self._componentStates = {} 989 990 planetState.addListener(self, append=planetStateAppend, 991 remove=planetStateRemove, 992 setitem=planetStateSetitem, 993 delitem=planetStateDelitem) 994 995 self._clearMessages() 996 997 a = planetState.get('atmosphere') 998 a.addListener(self, append=atmosphereStateAppend, 999 remove=atmosphereStateRemove) 1000 1001 self._componentStates.update( 1002 dict((c.get('name'), c) for c in a.get('components'))) 1003 1004 for f in planetState.get('flows'): 1005 planetStateAppend(planetState, 'flows', f) 1006 1007 if not planetState.get('flows'): 1008 self._updateComponents() 1009
1010 - def _clearAllComponents(self):
1011 if not self._adminModel.isConnected(): 1012 return 1013 1014 d = self._adminModel.cleanComponents() 1015 1016 def busyComponentError(failure): 1017 failure.trap(BusyComponentError) 1018 self._error( 1019 _("Some component(s) are still busy and cannot be removed.\n" 1020 "Try again later."))
1021 d.addErrback(busyComponentError) 1022 return d 1023 1024 # component view activation functions 1025
1026 - def _removeComponent(self, state):
1027 name = state.get('name') 1028 self.debug('removing component %s' % name) 1029 del self._componentStates[name] 1030 1031 # if this component was selected, clear selection 1032 if self._currentComponentStates and state \ 1033 in self._currentComponentStates: 1034 self._currentComponentStates.remove(state) 1035 self._componentList.removeComponent(state) 1036 # a component being removed means our selected component could 1037 # have gone away 1038 self._updateComponentActions()
1039
1040 - def _componentStop(self, state):
1041 """ 1042 @returns: a L{twisted.internet.defer.Deferred} 1043 """ 1044 self.debug('stopping component %r' % state) 1045 return self._componentDo(state, 'componentStop', 1046 'Stop', 'Stopping', 'Stopped')
1047
1048 - def _componentStart(self, state):
1049 """ 1050 @returns: a L{twisted.internet.defer.Deferred} 1051 """ 1052 return self._componentDo(state, 'componentStart', 1053 'Start', 'Starting', 'Started')
1054
1055 - def _componentDelete(self, state):
1056 """ 1057 @returns: a L{twisted.internet.defer.Deferred} 1058 """ 1059 return self._componentDo(state, 'deleteComponent', 1060 'Delete', 'Deleting', 'Deleted')
1061
1062 - def _getStatesFromState(self, state):
1063 # componentDo can be called on a None state, which means 1064 # 'look at the current selection' 1065 if state is None: 1066 states = self._componentList.getSelectedStates() 1067 else: 1068 states = [state] 1069 1070 return states
1071
1072 - def _componentDo(self, state, methodName, action, doing, done):
1073 """Do something with a component and update the statusbar. 1074 1075 @param state: componentState; if not specified, will use the 1076 currently selected component(s) 1077 @type state: L{AdminComponentState} or None 1078 @param methodName: name of the method to call 1079 @type methodName: str 1080 @param action: string used to explain that to do 1081 @type action: str 1082 @param doing: string used to explain that the action started 1083 @type doing: str 1084 @param done: string used to explain that the action was completed 1085 @type done: str 1086 1087 @rtype: L{twisted.internet.defer.Deferred} 1088 @returns: a deferred that will fire when the action is completed. 1089 """ 1090 states = self._getStatesFromState(state) 1091 1092 if not states: 1093 return 1094 1095 def callbackSingle(result, self, mid, name): 1096 self._statusbar.remove('main', mid) 1097 self._setStatusbarText( 1098 _("%s component %s") % (done, name))
1099 1100 def errbackSingle(failure, self, mid, name): 1101 self._statusbar.remove('main', mid) 1102 self.warning("Failed to %s component %s: %s" % ( 1103 action.lower(), name, failure)) 1104 self._setStatusbarText( 1105 _("Failed to %(action)s component %(name)s.") % { 1106 'action': action.lower(), 1107 'name': name, 1108 }) 1109 1110 def callbackMultiple(results, self, mid): 1111 self._statusbar.remove('main', mid) 1112 self._setStatusbarText( 1113 _("%s components.") % (done, )) 1114 1115 def errbackMultiple(failure, self, mid): 1116 self._statusbar.remove('main', mid) 1117 self.warning("Failed to %s some components: %s." % ( 1118 action.lower(), failure)) 1119 self._setStatusbarText( 1120 _("Failed to %s some components.") % (action, )) 1121 1122 f = gettext.dngettext( 1123 configure.PACKAGE, 1124 # first %s is one of Stopping/Starting/Deleting 1125 # second %s is a component name like "audio-producer" 1126 N_("%s component %s"), 1127 # first %s is one of Stopping/Starting/Deleting 1128 # second %s is a list of component names, like 1129 # "audio-producer, video-producer" 1130 N_("%s components %s"), len(states)) 1131 statusText = f % (doing, 1132 ', '.join([getComponentLabel(s) for s in states])) 1133 mid = self._setStatusbarText(statusText) 1134 1135 if len(states) == 1: 1136 state = states[0] 1137 name = getComponentLabel(state) 1138 d = self._adminModel.callRemote(methodName, state) 1139 d.addCallback(callbackSingle, self, mid, name) 1140 d.addErrback(errbackSingle, self, mid, name) 1141 else: 1142 deferreds = [] 1143 for state in states: 1144 d = self._adminModel.callRemote(methodName, state) 1145 deferreds.append(d) 1146 d = defer.DeferredList(deferreds) 1147 d.addCallback(callbackMultiple, self, mid) 1148 d.addErrback(errbackMultiple, self, mid) 1149 return d 1150
1151 - def _killSelectedComponents(self):
1152 for state in self._componentList.getSelectedStates(): 1153 workerName = state.get('workerRequested') 1154 avatarId = componentId(state.get('parent').get('name'), 1155 state.get('name')) 1156 self._adminModel.callRemote( 1157 'workerCallRemote', workerName, 'killJob', avatarId)
1158
1159 - def _componentSelectionChanged(self, states):
1160 self.debug('component %s has selection', states) 1161 1162 def compSet(state, key, value): 1163 if key == 'mood': 1164 self.debug('state %r has mood set to %r' % (state, value)) 1165 self._updateComponentActions()
1166 1167 def compAppend(state, key, value): 1168 name = state.get('name') 1169 self.debug('stateAppend on component state of %s' % name) 1170 if key == 'messages': 1171 current = self._componentList.getSelectedNames() 1172 if name in current: 1173 self._messageView.addMessage(value) 1174 1175 def compRemove(state, key, value): 1176 name = state.get('name') 1177 self.debug('stateRemove on component state of %s' % name) 1178 if key == 'messages': 1179 current = self._componentList.getSelectedNames() 1180 if name in current: 1181 self._messageView.clearMessage(value.id) 1182 1183 if self._currentComponentStates: 1184 for currentComponentState in self._currentComponentStates: 1185 currentComponentState.removeListener(self) 1186 self._currentComponentStates = states 1187 if self._currentComponentStates: 1188 for currentComponentState in self._currentComponentStates: 1189 currentComponentState.addListener( 1190 self, set=compSet, append=compAppend, remove=compRemove) 1191 1192 self._updateComponentActions() 1193 self._clearMessages() 1194 state = None 1195 if states: 1196 if len(states) == 1: 1197 self.debug( 1198 "only one component is selected on the components view") 1199 state = states[0] 1200 elif states: 1201 self.debug("more than one components are selected in the " 1202 "components view") 1203 self._componentView.activateComponent(state) 1204 1205 statusbarMessage = " " 1206 for state in states: 1207 name = getComponentLabel(state) 1208 messages = state.get('messages') 1209 if messages: 1210 for m in messages: 1211 self.debug('have message %r', m) 1212 self.debug('message id %s', m.id) 1213 self._messageView.addMessage(m) 1214 1215 if state.get('mood') == moods.sad.value: 1216 self.debug('component %s is sad' % name) 1217 statusbarMessage = statusbarMessage + \ 1218 _("Component %s is sad. ") % name 1219 if statusbarMessage != " ": 1220 self._setStatusbarText(statusbarMessage) 1221 1222 1223 # FIXME: show statusbar things 1224 # self._statusbar.set('main', _('Showing UI for %s') % name) 1225 # self._statusbar.set('main', 1226 # _("Component %s is still sleeping") % name) 1227 # self._statusbar.set('main', _("Requesting UI for %s ...") % name) 1228 # self._statusbar.set('main', _("Loading UI for %s ...") % name) 1229 # self._statusbar.clear('main') 1230 # mid = self._statusbar.push('notebook', 1231 # _("Loading tab %s for %s ...") % (node.title, name)) 1232 # node.statusbar = self._statusbar # hack 1233
1234 - def _componentShowPopupMenu(self, event_button, event_time):
1235 self._componentContextMenu.popup(None, None, None, 1236 event_button, event_time)
1237
1238 - def _connectionOpened(self, admin):
1239 self.info('Connected to manager') 1240 if self._disconnectedDialog: 1241 self._disconnectedDialog.destroy() 1242 self._disconnectedDialog = None 1243 1244 self._updateUIStatus(connected=True) 1245 1246 self.emit('connected') 1247 1248 self._componentView.setSingleAdmin(admin) 1249 1250 self._setPlanetState(admin.planet) 1251 self._updateConnectionActions() 1252 self._updateComponentActions() 1253 1254 if (not self._componentStates and 1255 not self._configurationAssistantIsRunning): 1256 self.debug('no components detected, running assistant') 1257 # ensure our window is shown 1258 self._componentList.clearAndRebuild(self._componentStates) 1259 self.show() 1260 self._runConfigurationAssistant() 1261 else: 1262 self.show()
1263
1264 - def _showConnectionLostDialog(self):
1265 RESPONSE_REFRESH = 1 1266 1267 def response(dialog, response_id): 1268 if response_id == RESPONSE_REFRESH: 1269 1270 def errback(failure): 1271 # Swallow connection errors. We keep trying 1272 failure.trap(ConnectionCancelledError, 1273 ConnectionFailedError, 1274 ConnectionRefusedError)
1275 1276 d = self._adminModel.reconnect(keepTrying=True) 1277 d.addErrback(errback) 1278 else: 1279 self._disconnectedDialog.destroy() 1280 self._disconnectedDialog = None 1281 self._adminModel.disconnectFromManager() 1282 self._window.set_sensitive(True) 1283 1284 dialog = ProgressDialog( 1285 _("Reconnecting ..."), 1286 _("Lost connection to manager %s. Reconnecting ...") 1287 % (self._adminModel.adminInfoStr(), ), self._window) 1288 1289 dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) 1290 dialog.add_button(gtk.STOCK_REFRESH, RESPONSE_REFRESH) 1291 dialog.connect("response", response) 1292 dialog.start() 1293 self._disconnectedDialog = dialog 1294 self._window.set_sensitive(False) 1295
1296 - def _connectionLost(self):
1297 self._componentStates = {} 1298 self._updateComponents() 1299 self._clearMessages() 1300 if self._planetState: 1301 self._planetState.removeListener(self) 1302 self._planetState = None 1303 1304 self._showConnectionLostDialog() 1305 self._updateUIStatus(connected=False)
1306
1307 - def _openRecentConnection(self):
1308 d = ConnectionsDialog(parent=self._window) 1309 1310 def on_have_connection(d, connectionInfo): 1311 d.destroy() 1312 if connectionInfo: 1313 self._openConnectionInternal(connectionInfo.info) 1314 connectionInfo.updateTimestamp() 1315 self._updateConnectionActions()
1316 1317 d.connect('have-connection', on_have_connection) 1318 d.show() 1319
1320 - def _openExistingConnection(self):
1321 from flumotion.admin.gtk.greeter import ConnectExisting 1322 from flumotion.ui.simplewizard import WizardCancelled 1323 wiz = ConnectExisting(parent=self._window) 1324 1325 def got_state(state, g): 1326 g.set_sensitive(False) 1327 g.destroy() 1328 self._openConnectionInternal(state['connectionInfo'])
1329 1330 def cancel(failure): 1331 failure.trap(WizardCancelled) 1332 wiz.stop() 1333 1334 d = wiz.runAsync() 1335 d.addCallback(got_state, wiz) 1336 d.addErrback(cancel) 1337
1338 - def _importConfiguration(self):
1339 dialog = gtk.FileChooserDialog( 1340 _("Import Configuration..."), self._window, 1341 gtk.FILE_CHOOSER_ACTION_OPEN, 1342 (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, 1343 _('Import'), gtk.RESPONSE_ACCEPT)) 1344 dialog.set_modal(True) 1345 dialog.set_default_response(gtk.RESPONSE_ACCEPT) 1346 ffilter = gtk.FileFilter() 1347 ffilter.set_name(_("Flumotion XML configuration files")) 1348 ffilter.add_pattern("*.xml") 1349 dialog.add_filter(ffilter) 1350 ffilter = gtk.FileFilter() 1351 ffilter.set_name(_("All files")) 1352 ffilter.add_pattern("*") 1353 dialog.add_filter(ffilter) 1354 1355 def response(dialog, response): 1356 if response == gtk.RESPONSE_ACCEPT: 1357 name = dialog.get_filename() 1358 conf_xml = open(name, 'r').read() 1359 self._adminModel.loadConfiguration(conf_xml) 1360 dialog.destroy()
1361 1362 dialog.connect('response', response) 1363 dialog.show() 1364
1365 - def _exportConfiguration(self):
1366 d = gtk.FileChooserDialog( 1367 _("Export Configuration..."), self._window, 1368 gtk.FILE_CHOOSER_ACTION_SAVE, 1369 (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, 1370 _('Export'), gtk.RESPONSE_ACCEPT)) 1371 d.set_modal(True) 1372 d.set_default_response(gtk.RESPONSE_ACCEPT) 1373 d.set_current_name("configuration.xml") 1374 1375 def getConfiguration(conf_xml, name, chooser): 1376 if not name.endswith('.xml'): 1377 name += '.xml' 1378 1379 file_exists = True 1380 if os.path.exists(name): 1381 d = gtk.MessageDialog( 1382 self._window, gtk.DIALOG_MODAL, 1383 gtk.MESSAGE_ERROR, gtk.BUTTONS_YES_NO, 1384 _("File already exists.\nOverwrite?")) 1385 d.connect("response", lambda self, response: d.hide()) 1386 if d.run() == gtk.RESPONSE_YES: 1387 file_exists = False 1388 else: 1389 file_exists = False 1390 1391 if not file_exists: 1392 f = open(name, 'w') 1393 f.write(conf_xml) 1394 f.close() 1395 chooser.destroy()
1396 1397 def response(d, response): 1398 if response == gtk.RESPONSE_ACCEPT: 1399 deferred = self._adminModel.getConfiguration() 1400 name = d.get_filename() 1401 deferred.addCallback(getConfiguration, name, d) 1402 else: 1403 d.destroy() 1404 1405 d.connect('response', response) 1406 d.show() 1407
1408 - def _startShell(self):
1409 if sys.version_info >= (2, 4): 1410 from flumotion.extern import code 1411 code # pyflakes 1412 else: 1413 import code 1414 1415 ns = {"admin": self._adminModel, 1416 "components": self._componentStates} 1417 message = """Flumotion Admin Debug Shell 1418 1419 Local variables are: 1420 admin (flumotion.admin.admin.AdminModel) 1421 components (dict: name -> flumotion.common.planet.AdminComponentState) 1422 1423 You can do remote component calls using: 1424 admin.componentCallRemote(components['component-name'], 1425 'methodName', arg1, arg2) 1426 1427 """ 1428 code.interact(local=ns, banner=message)
1429
1430 - def _dumpConfiguration(self):
1431 1432 def gotConfiguration(xml): 1433 print xml
1434 d = self._adminModel.getConfiguration() 1435 d.addCallback(gotConfiguration) 1436
1437 - def _setDebugMarker(self):
1438 1439 def setMarker(_, marker, level): 1440 self._adminModel.callRemote('writeFluDebugMarker', level, marker)
1441 debugMarkerDialog = DebugMarkerDialog() 1442 debugMarkerDialog.connect('set-marker', setMarker) 1443 debugMarkerDialog.show() 1444
1445 - def _about(self):
1446 about = AboutDialog(self._window) 1447 about.run() 1448 about.destroy()
1449
1450 - def _showHelp(self):
1451 for path in os.environ['PATH'].split(':'): 1452 executable = os.path.join(path, 'gnome-help') 1453 if os.path.exists(executable): 1454 break 1455 else: 1456 self._error( 1457 _("Cannot find a program to display the Flumotion manual.")) 1458 return 1459 gobject.spawn_async([executable, 1460 'ghelp:%s' % (configure.PACKAGE, )])
1461 1462 ### admin model callbacks 1463
1464 - def _admin_connected_cb(self, admin):
1465 self._connectionOpened(admin)
1466
1467 - def _admin_disconnected_cb(self, admin):
1468 self._connectionLost()
1469
1470 - def _admin_update_cb(self, admin):
1471 self._updateComponents()
1472 1473 ### ui callbacks 1474
1475 - def _on_uimanager__connect_proxy(self, uimgr, action, widget):
1476 self._connectActionProxy(action, widget)
1477
1478 - def _on_uimanager__disconnect_proxy(self, uimgr, action, widget):
1479 self._disconnectActionProxy(action, widget)
1480
1481 - def _on_menu_item__select(self, menuitem, tooltip):
1482 self._setStatusbarText(tooltip)
1483
1484 - def _on_menu_item__deselect(self, menuitem):
1485 self._clearLastStatusbarText()
1486
1487 - def _on_tool_button__enter(self, toolbutton, tooltip):
1488 self._setStatusbarText(tooltip)
1489
1490 - def _on_tool_button__leave(self, toolbutton):
1491 self._clearLastStatusbarText()
1492
1493 - def _assistant_finished_cb(self, assistant, configuration):
1494 self._assistantFinshed(assistant, configuration)
1495
1496 - def on_assistant_destroy(self, assistant):
1497 self._configurationAssistantIsRunning = False
1498
1499 - def _window_delete_event_cb(self, window, event):
1500 self._quit()
1501
1502 - def _window_key_press_event_cb(self, window, event):
1503 # This should be removed if we're going to support connecting 1504 # to multiple managers in the same application (MDI/tabs) 1505 state = event.state & (gtk.gdk.MODIFIER_MASK ^ gtk.gdk.MOD2_MASK) 1506 1507 if state == gdk.CONTROL_MASK and event.keyval == keysyms.w: 1508 self._quit()
1509
1510 - def _trayicon_quit_cb(self, trayicon):
1511 self._quit()
1512
1513 - def _recent_action_activate_cb(self, action, conn):
1514 self._openConnectionInternal(conn.info)
1515
1516 - def _components_show_popup_menu_cb(self, clist, event_button, event_time):
1517 self._componentShowPopupMenu(event_button, event_time)
1518
1519 - def _components_selection_changed_cb(self, clist, state):
1520 self._componentSelectionChanged(state)
1521
1522 - def _components_start_stop_notify_cb(self, clist, pspec):
1523 self._updateComponentActions()
1524 1525 ### action callbacks 1526
1527 - def _debug_write_debug_marker_cb(self, action):
1528 self._setDebugMarker()
1529
1530 - def _connection_open_recent_cb(self, action):
1531 self._openRecentConnection()
1532
1533 - def _connection_open_existing_cb(self, action):
1534 self._openExistingConnection()
1535
1536 - def _connection_import_configuration_cb(self, action):
1537 self._importConfiguration()
1538
1539 - def _connection_export_configuration_cb(self, action):
1540 self._exportConfiguration()
1541
1542 - def _connection_quit_cb(self, action):
1543 self._quit()
1544
1545 - def _manage_start_component_cb(self, action):
1546 self._componentStart(None)
1547
1548 - def _manage_stop_component_cb(self, action):
1549 self._componentStop(None)
1550
1551 - def _manage_delete_component_cb(self, action):
1552 self._componentDelete(None)
1553
1554 - def _manage_start_all_cb(self, action):
1555 for c in self._componentStates.values(): 1556 self._componentStart(c)
1557
1558 - def _manage_stop_all_cb(self, action):
1559 for c in self._componentStates.values(): 1560 self._componentStop(c)
1561
1562 - def _manage_clear_all_cb(self, action):
1563 self._clearAllComponents()
1564
1565 - def _manage_add_format_cb(self, action):
1566 self._runAddNewFormatAssistant()
1567
1568 - def _manage_run_assistant_cb(self, action):
1569 self._runConfigurationAssistant()
1570
1571 - def _debug_enable_cb(self, action):
1572 self.setDebugEnabled(action.get_active())
1573
1574 - def _debug_start_shell_cb(self, action):
1575 self._startShell()
1576
1577 - def _debug_dump_configuration_cb(self, action):
1578 self._dumpConfiguration()
1579
1580 - def _help_contents_cb(self, action):
1581 self._showHelp()
1582
1583 - def _help_about_cb(self, action):
1584 self._about()
1585
1586 - def _kill_component_cb(self, action):
1587 self._killSelectedComponents()
1588 1589 gobject.type_register(AdminWindow) 1590