Package conduit :: Module Main
[hide private]

Source Code for Module conduit.Main

  1  import os 
  2  import getopt 
  3  import sys 
  4  import dbus, dbus.service, dbus.mainloop.glib 
  5  import gobject 
  6   
  7  dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) 
  8  dbus.mainloop.glib.threads_init() 
  9   
 10  import logging 
 11  log = logging.getLogger("Main") 
 12   
 13  import conduit 
 14  import conduit.utils as Utils 
 15  from conduit.Module import ModuleManager 
 16  from conduit.MappingDB import MappingDB 
 17  from conduit.TypeConverter import TypeConverter 
 18  from conduit.SyncSet import SyncSet 
 19  from conduit.Synchronization import SyncManager 
 20  from conduit.DBus import DBusInterface 
 21  from conduit.Settings import Settings 
 22   
 23  APPLICATION_DBUS_IFACE="org.conduit.Application" 
 24   
25 -class Application(dbus.service.Object):
26 - def __init__(self):
27 """ 28 Conduit application class 29 Parses command line arguments. Sets up the views and models; 30 restores application settings, shows the splash screen and the UI 31 32 Notes: 33 1) If conduit is launched without --console switch then the gui 34 and the console interfaces are started 35 2) If launched with --console and then later via the gui then set 36 up the gui and connect all the appropriate signal handlers 37 """ 38 self.splash = None 39 self.gui = None 40 self.statusIcon = None 41 self.dbus = None 42 self.guiSyncSet = None 43 self.dbusSyncSet = None 44 self.uiLib = None 45 46 gobject.set_application_name("Conduit") 47 self.settingsFile = os.path.join(conduit.USER_DIR, "settings.xml") 48 self.dbFile = os.path.join(conduit.USER_DIR, "mapping.db") 49 50 #initialize application settings 51 conduit.GLOBALS.settings = Settings(conduit.SETTINGS_IMPL) 52 53 buildGUI = True 54 iconify = False 55 whitelist = None 56 blacklist = None 57 self.ui = "gtk" 58 settings = {} 59 try: 60 opts, args = getopt.getopt( 61 sys.argv[1:], 62 "hvf:ciu:w:x:s:", 63 ("help", "version", "config-file=", 64 "console", "iconify", "ui=", "with-modules=", 65 "without-modules=", "settings=")) 66 #parse args 67 for o, a in opts: 68 if o in ("-h", "--help"): 69 self._usage() 70 sys.exit(0) 71 if o in ("-v", "--version"): 72 print "Conduit %s" % conduit.VERSION 73 sys.exit(0) 74 if o in ("-f", "--config-file"): 75 self.settingsFile = os.path.join(os.getcwd(), a) 76 if o in ("-c", "--console"): 77 buildGUI = False 78 if o in ("-i", "--iconify"): 79 iconify = True 80 if o in ("-u", "--ui"): 81 self.ui = a 82 if o in ("-w", "--with-modules"): 83 whitelist = a.split(",") 84 if o in ("-x", "--without-modules"): 85 blacklist = a.split(",") 86 if o in ("-s", "--settings"): 87 try: 88 for i in a.split(','): 89 k,v = i.split('=') 90 settings[k] = v 91 except: pass 92 conduit.GLOBALS.settings.set_overrides(**settings) 93 94 except getopt.GetoptError: 95 log.warn("Unknown command line option") 96 self._usage() 97 sys.exit(1) 98 99 log.info("Conduit v%s Installed: %s" % (conduit.VERSION, conduit.IS_INSTALLED)) 100 log.info("Python: %s" % sys.version) 101 if settings: 102 log.info("Settings have been overridden: %s" % settings) 103 104 #Make conduit single instance. If conduit is already running then 105 #make the original process build or show the gui 106 sessionBus = dbus.SessionBus() 107 if Utils.dbus_service_available(APPLICATION_DBUS_IFACE, sessionBus): 108 log.info("Conduit is already running") 109 obj = sessionBus.get_object(APPLICATION_DBUS_IFACE, "/activate") 110 conduitApp = dbus.Interface(obj, APPLICATION_DBUS_IFACE) 111 if buildGUI: 112 if conduitApp.HasGUI(): 113 conduitApp.ShowGUI() 114 else: 115 conduitApp.ImportGUI() 116 conduitApp.ShowSplash() 117 conduitApp.ShowStatusIcon() 118 conduitApp.BuildGUI() 119 conduitApp.ShowGUI() 120 conduitApp.HideSplash() 121 sys.exit(0) 122 123 # Initialise dbus stuff here as any earlier will interfere 124 # with Conduit already running check. 125 bus_name = dbus.service.BusName(APPLICATION_DBUS_IFACE, bus=sessionBus) 126 dbus.service.Object.__init__(self, bus_name, "/activate") 127 128 #Throw up a splash screen ASAP. Dont show anything if launched via --console. 129 if buildGUI: 130 log.info("Using UI: %s" % self.ui) 131 self.ImportGUI() 132 if not iconify: 133 self.ShowSplash() 134 self.ShowStatusIcon() 135 136 #Dynamically load all datasources, datasinks and converters 137 dirs_to_search = [ 138 conduit.SHARED_MODULE_DIR, 139 os.path.join(conduit.USER_DIR, "modules") 140 ] 141 142 #Initialize all globals variables 143 conduit.GLOBALS.app = self 144 conduit.GLOBALS.moduleManager = ModuleManager(dirs_to_search) 145 conduit.GLOBALS.moduleManager.load_all(whitelist, blacklist) 146 conduit.GLOBALS.typeConverter = TypeConverter(conduit.GLOBALS.moduleManager) 147 conduit.GLOBALS.syncManager = SyncManager(conduit.GLOBALS.typeConverter) 148 conduit.GLOBALS.mappingDB = MappingDB(self.dbFile) 149 conduit.GLOBALS.mainloop = gobject.MainLoop() 150 151 #Build both syncsets and put on the bus as early as possible 152 self.guiSyncSet = SyncSet( 153 moduleManager=conduit.GLOBALS.moduleManager, 154 syncManager=conduit.GLOBALS.syncManager, 155 xmlSettingFilePath=self.settingsFile 156 ) 157 self.dbusSyncSet = SyncSet( 158 moduleManager=conduit.GLOBALS.moduleManager, 159 syncManager=conduit.GLOBALS.syncManager 160 ) 161 162 #Set the view models 163 if buildGUI: 164 self.BuildGUI() 165 if not iconify: 166 self.ShowGUI() 167 168 #Dbus view... 169 self.dbus = DBusInterface( 170 conduitApplication=self, 171 moduleManager=conduit.GLOBALS.moduleManager, 172 typeConverter=conduit.GLOBALS.typeConverter, 173 syncManager=conduit.GLOBALS.syncManager, 174 guiSyncSet=self.guiSyncSet, 175 dbusSyncSet=self.dbusSyncSet 176 ) 177 178 if self.statusIcon: 179 self.dbusSyncSet.connect("conduit-added", self.statusIcon.on_conduit_added) 180 self.dbusSyncSet.connect("conduit-removed", self.statusIcon.on_conduit_removed) 181 182 #hide the splash screen 183 self.HideSplash() 184 try: 185 conduit.GLOBALS.mainloop.run() 186 except KeyboardInterrupt: 187 self.Quit()
188
189 - def _usage(self):
190 print """Usage: conduit [OPTIONS] - Synchronize things 191 OPTIONS: 192 -h, --help Show this message. 193 -c, --console Launch with no GUI. 194 (default=no) 195 -f, --config-file=FILE Save dataprovider configuration to FILE. 196 (default=$XDG_CONFIG_DIR/.conduit/settings.xml) 197 -i, --iconify Iconify on startup. 198 (default=no) 199 -u, --ui=NAME Run with the specified UI. 200 (default=gtk) 201 -w, --with-modules Only load modules in the named files. 202 (default=load all modules) 203 -x, --without-modules Do not load modules in the named files. 204 (default=load all modules) 205 -s, --settings=key=val,.. Explicitly set internal Conduit settings (keys) 206 to the given values for this session. 207 -v, --version Show version information."""
208 209 @dbus.service.method(APPLICATION_DBUS_IFACE, in_signature='', out_signature='b')
210 - def HasGUI(self):
211 return self.gui != None
212 213 @dbus.service.method(APPLICATION_DBUS_IFACE, in_signature='', out_signature='')
214 - def BuildGUI(self):
215 self.gui = self.uiLib.MainWindow( 216 conduitApplication=self, 217 moduleManager=conduit.GLOBALS.moduleManager, 218 typeConverter=conduit.GLOBALS.typeConverter, 219 syncManager=conduit.GLOBALS.syncManager 220 ) 221 222 #reload the saved sync set 223 self.guiSyncSet.restore_from_xml() 224 self.gui.set_model(self.guiSyncSet) 225 226 if self.statusIcon: 227 self.guiSyncSet.connect("conduit-added", self.statusIcon.on_conduit_added) 228 self.guiSyncSet.connect("conduit-removed", self.statusIcon.on_conduit_removed)
229 230 @dbus.service.method(APPLICATION_DBUS_IFACE, in_signature='', out_signature='')
231 - def ImportGUI(self):
232 if self.uiLib == None: 233 self.uiLib = __import__("conduit.%sui.UI" % self.ui, {}, {}, ['UI'])
234 235 @dbus.service.method(APPLICATION_DBUS_IFACE, in_signature='', out_signature='')
236 - def ShowGUI(self):
237 self.gui.present()
238 239 @dbus.service.method(APPLICATION_DBUS_IFACE, in_signature='', out_signature='')
240 - def ShowStatusIcon(self):
241 #The status icon is shared between the GUI and the Dbus iface 242 if conduit.GLOBALS.settings.get("show_status_icon") == True: 243 self.statusIcon = self.uiLib.StatusIcon(self)
244 245 @dbus.service.method(APPLICATION_DBUS_IFACE, in_signature='', out_signature='')
246 - def ShowSplash(self):
247 if conduit.GLOBALS.settings.get("show_splashscreen") == True: 248 self.splash = self.uiLib.SplashScreen() 249 self.splash.show()
250 251 @dbus.service.method(APPLICATION_DBUS_IFACE, in_signature='', out_signature='')
252 - def HideSplash(self):
253 if self.splash != None: 254 self.splash.destroy()
255 256 @dbus.service.method(APPLICATION_DBUS_IFACE, in_signature='', out_signature='')
257 - def IconifyGUI(self):
258 self.gui.minimize_to_tray()
259 260 @dbus.service.method(APPLICATION_DBUS_IFACE, in_signature='', out_signature='')
261 - def Quit(self):
262 #Hide the GUI first, so we feel responsive 263 log.info("Closing application") 264 if self.gui != None: 265 self.gui.mainWindow.hide() 266 if conduit.GLOBALS.settings.get("save_on_exit") == True: 267 self.gui.save_settings(None) 268 269 #Cancel all syncs 270 self.Cancel() 271 272 #give the dataprovider factories time to shut down 273 log.info("Closing dataprovider factories") 274 conduit.GLOBALS.moduleManager.quit() 275 276 #unitialize all dataproviders 277 log.info("Unitializing dataproviders") 278 self.guiSyncSet.quit() 279 log.info("GUI Quit") 280 self.dbusSyncSet.quit() 281 log.info("DBus Quit") 282 283 #Save the mapping DB 284 conduit.GLOBALS.mappingDB.save() 285 conduit.GLOBALS.mappingDB.close() 286 287 #Save the application settings 288 conduit.GLOBALS.settings.save() 289 290 log.info("Main Loop Quitting") 291 conduit.GLOBALS.mainloop.quit()
292 293 @dbus.service.method(APPLICATION_DBUS_IFACE, in_signature='', out_signature='')
294 - def Synchronize(self):
295 for cond in self.guiSyncSet.get_all_conduits(): 296 if cond.datasource is not None and len(cond.datasinks) > 0: 297 conduit.GLOBALS.syncManager.sync_conduit(cond) 298 else: 299 log.info("Conduit must have a datasource and a datasink")
300 301 @dbus.service.method(APPLICATION_DBUS_IFACE, in_signature='', out_signature='')
302 - def Cancel(self):
303 #flag the global cancellation object 304 log.info("Setting global cancel flag") 305 conduit.GLOBALS.cancelled = True 306 307 #cancel all conduits 308 log.info("Stopping Synchronization threads") 309 conduit.GLOBALS.syncManager.cancel_all()
310