00001 import os
00002 import sys
00003 import string
00004 import commands
00005 import platform
00006 import logging
00007 import logging.handlers
00008 import ConfigParser
00009 import optparse
00010 import webbrowser
00011 import subprocess
00012
00013 from PyQt4.QtCore import SIGNAL,qVersion,QString,QVariant, Qt
00014 from PyQt4.QtGui import QApplication,QMenu,QPixmap,QAction,QFileDialog,QIcon,QMessageBox
00015
00016 from Vispa.Main.Directories import logDirectory,pluginDirectory,baseDirectory,homeDirectory,iniFileName,applicationName,docDirectory,websiteUrl
00017 from Vispa.Main.MainWindow import MainWindow
00018 from Vispa.Main.AbstractTab import AbstractTab
00019 from Vispa.Main.Filetype import Filetype
00020 from Vispa.Main.Exceptions import *
00021 from Vispa.Main.AboutDialog import AboutDialog
00022 from Vispa.Main.RotatingIcon import RotatingIcon
00023
00024 import Vispa.__init__
00025
00026 import Resources
00027
00028 class Application(QApplication):
00029
00030 MAX_RECENT_FILES = 30
00031 MAX_VISIBLE_RECENT_FILES = 10
00032 MAX_VISIBLE_UNDO_EVENTS = 10
00033 FAILED_LOADING_PLUGINS_ERROR = "Errors while loading plugins. For details see error output or log file.\n\nThe following plugins won't work correctly:\n\n"
00034 TAB_PREMATURELY_CLOSED_WARNING = "Tab was closed before user request could be handled."
00035 NO_PROCESS_EVENTS = False
00036
00037 def __init__(self, argv):
00038 QApplication.__init__(self, argv)
00039 self._version = None
00040 self._plugins = []
00041 self._closeAllFlag = False
00042 self._knownFiltersList = []
00043 self._knownExtensionsDictionary = {}
00044 self._pluginMenus = []
00045 self._pluginToolBars = []
00046 self._recentFiles = []
00047 self._ini = None
00048 self._iniFileName = iniFileName
00049 self._zoomToolBar = None
00050 self._undoToolBar = None
00051 self._messageId=0
00052 self._loadablePlugins = {}
00053 self._logFile = None
00054
00055 self._initLogging()
00056
00057 logging.debug('Running with Qt-Version ' + str(qVersion()))
00058
00059 self._initCommandLineAttributes()
00060
00061 self._loadIni()
00062
00063 self.setVersion(Vispa.__init__.__version__)
00064
00065 self._window = MainWindow(self, applicationName)
00066 self._window.show()
00067
00068 self._loadPlugins()
00069
00070 self._fillFileMenu()
00071 self._fillEditMenu()
00072 self._fillHelpMenu()
00073
00074 self.createUndoToolBar()
00075 self.createZoomToolBar()
00076 self.hidePluginMenus()
00077 self.hidePluginToolBars()
00078 self.createStatusBar()
00079 self.updateMenu()
00080
00081 self._readCommandLineAttributes()
00082 self._connectSignals()
00083
00084 def commandLineParser(self):
00085 return self._commandLineParser
00086
00087 def commandLineOptions(self):
00088 return self._commandLineOptions
00089
00090 def setVersion(self, version):
00091 self._version = version
00092
00093 def version(self):
00094 """ Returns version string.
00095 """
00096 return self._version
00097
00098 def atLeastQtVersion(self, versionString):
00099 """ Returns True if given versionString is newer than current used version of Qt.
00100 """
00101 [majorV, minorV, revisionV] = versionString.split(".")
00102 [majorQ, minorQ, revisionQ] = str(qVersion()).split(".")
00103 if majorV > majorQ:
00104 return True
00105 elif majorV < majorQ:
00106 return False
00107 elif majorV == majorQ:
00108 if minorV > minorQ:
00109 return True
00110 elif minorV < minorQ:
00111 return False
00112 elif minorV == minorQ:
00113 if revisionV > revisionQ:
00114 return True
00115 elif revisionV < revisionQ:
00116 return False
00117 elif revisionV == revisionQ:
00118 return True
00119 return False
00120
00121 def _setCommandLineOptions(self):
00122 """ Set the available command line options.
00123 """
00124 self._commandLineParser.add_option("-f", "--file", dest="filename", help="open a FILE", metavar="FILE")
00125 self._commandLineParser.add_option("-l", "--loglevel", dest="loglevel", help="set LOGLEVEL to 10=DEBUG, 20=INFO, 30=WARNING, 40=ERROR, 50=CRITICAL", metavar="LOGLEVEL", type="int")
00126
00127 def _initCommandLineAttributes(self):
00128 """ Initialize command line parser.
00129
00130 After calling this function, plugins may add options.
00131 """
00132 class QuiteOptionParser(optparse.OptionParser):
00133 def __init__(self):
00134 optparse.OptionParser.__init__(self,add_help_option=False)
00135 def error(self,message=""):
00136 pass
00137 self._commandLineParser = QuiteOptionParser()
00138 self._setCommandLineOptions()
00139 (self._commandLineOptions, self._args) = self._commandLineParser.parse_args()
00140 if self._commandLineOptions.loglevel:
00141 logging.root.setLevel(self._commandLineOptions.loglevel)
00142 self._commandLineParser = optparse.OptionParser()
00143 self._setCommandLineOptions()
00144
00145 def _readCommandLineAttributes(self):
00146 """ Analyzes the command line attributes and print usage summary if required.
00147 """
00148 (self._commandLineOptions, self._args) = self._commandLineParser.parse_args()
00149 if self._commandLineOptions.filename:
00150 self.mainWindow().setStartupScreenVisible(False)
00151 self.openFile(self._commandLineOptions.filename)
00152 if len(self._args) > 0:
00153 self.mainWindow().setStartupScreenVisible(False)
00154 self.openFile(self._args[0])
00155
00156 def _checkFile(self, filename):
00157 """ Check if logfile is closed correctly
00158 """
00159 finished = True
00160 file = open(filename, "r")
00161 for line in file.readlines():
00162 if "INFO Start logging" in line:
00163 finished = False
00164 if "INFO Stop logging" in line:
00165 finished = True
00166 return finished
00167
00168 def _initLogging(self):
00169 """ Add logging handlers for a log file as well as stderr.
00170 """
00171 instance = 0
00172 done = False
00173 while not done:
00174
00175 instance += 1
00176 logfile = os.path.join(logDirectory, "log" + str(instance) + ".txt")
00177
00178 if instance > 10:
00179 instance = 1
00180 logfile = os.path.join(logDirectory, "log" + str(instance) + ".txt")
00181 done = True
00182 break
00183 if not os.path.exists(logfile):
00184 done = True
00185 break
00186 done = self._checkFile(logfile)
00187
00188
00189 nextlogfile = os.path.join(logDirectory, "log" + str(instance + 1) + ".txt")
00190 if os.path.exists(nextlogfile):
00191 if not self._checkFile(nextlogfile):
00192 file = open(nextlogfile, "a")
00193 file.write("Cleaning up logfile after abnormal termination: INFO Stop logging\n")
00194
00195 if os.path.exists(logDirectory):
00196 handler1 = logging.handlers.RotatingFileHandler(logfile, maxBytes=100000, backupCount=1)
00197 formatter1 = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
00198 handler1.setFormatter(formatter1)
00199 self._logFile = logfile
00200
00201 handler2 = logging.StreamHandler(sys.stderr)
00202 formatter2 = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
00203 handler2.setFormatter(formatter2)
00204
00205 logging.root.handlers = []
00206 if os.path.exists(logDirectory):
00207 logging.root.addHandler(handler1)
00208 logging.root.addHandler(handler2)
00209
00210
00211 self._infologger = logging.getLogger("info")
00212 self._infologger.setLevel(logging.INFO)
00213 self._infologger.handlers = []
00214 if self._logFile:
00215 self._infologger.info("Start logging to " + self._logFile)
00216
00217 def run(self):
00218 """ Show the MainWindow and run the application.
00219 """
00220
00221
00222 self.exec_()
00223 if self._logFile:
00224 self._infologger.info("Stop logging to " + self._logFile)
00225
00226 def _connectSignals(self):
00227 """ Connect signal to observe the TabWidget in the MainWindow.
00228 """
00229 logging.debug('Application: _connectSignals()')
00230 self.connect(self._window.tabWidget(), SIGNAL("currentChanged(int)"), self.tabChanged)
00231 self.connect(self._window, SIGNAL("windowActivated()"), self.tabChanged)
00232 self.connect(self._window.tabWidget(), SIGNAL("tabCloseRequested(int)"), self.tabCloseRequest)
00233
00234 def _loadPlugins(self):
00235 """ Search all subfolders of the plugin directory for vispa plugins and registers them.
00236 """
00237 logging.debug('Application: _loadPlugins()')
00238 dirs = ["Vispa.Plugins." + str(f) for f in os.listdir(pluginDirectory)
00239 if os.path.isdir(os.path.join(pluginDirectory, f)) and not f.startswith(".") and not f.startswith("CVS")]
00240 failedToLoad = []
00241 for di in dirs:
00242 try:
00243 module = __import__(di, globals(), locals(), "Vispa.Plugins")
00244 self._loadablePlugins[module.plugin.__name__] = module.plugin
00245 except ImportError:
00246 logging.warning('Application: cannot load plugin ' + di + ': ' + exception_traceback())
00247 failedToLoad.append(di)
00248 except PluginIgnoredException,e:
00249 logging.info('Application: plugin ' + di + ' cannot be loaded and is ignored: ' + str(e))
00250 except AttributeError,e:
00251 logging.info('Application: plugin ' + di + ' is deactivated (define plugin in __init__.py to activate): ' + str(e))
00252
00253 for pluginName in self._loadablePlugins.keys():
00254
00255
00256 if not self.initializePlugin(pluginName):
00257 failedToLoad.append(pluginName)
00258
00259 if len(failedToLoad) > 0:
00260 self.errorMessage(self.FAILED_LOADING_PLUGINS_ERROR + "\n".join(failedToLoad))
00261
00262 self._collectFileExtensions()
00263
00264 def initializePlugin(self, name):
00265 if name in [plugin.__class__.__name__ for plugin in self._plugins]:
00266 logging.info("%s: initalizePlugin(): Plugin '%s' already loaded. Aborting..." % (self.__class__.__name__, name))
00267 return True
00268 if not name in self._loadablePlugins.keys():
00269 logging.error("%s: initalizePlugin(): Unknown plugin '%s'. Aborting..." % (self.__class__.__name__, name))
00270 return False
00271
00272 try:
00273 pluginObject = self._loadablePlugins[name](self)
00274 self._plugins.append(pluginObject)
00275 logging.debug('Application: added plugin ' + name)
00276 return True
00277 except ValueError:
00278 logging.warning('Application: ' + name + ' is not a plugin: ' + exception_traceback())
00279 return False
00280
00281
00282 def plugins(self):
00283 return self._plugins
00284
00285 def plugin(self, name):
00286 """ Returns plugin with given name or None if there is no such one.
00287 """
00288 if not name.endswith("Plugin"):
00289 name += "Plugin"
00290
00291 for plugin in self._plugins:
00292 if name == plugin.__class__.__name__:
00293 return plugin
00294 return None
00295
00296 def tabControllers(self):
00297 controllers=[self._window.tabWidget().widget(i).controller() for i in range(0, self._window.tabWidget().count())]
00298 controllers+=[tab.controller() for tab in self.mainWindow().tabWidgets()]
00299 return controllers
00300
00301 def setCurrentTabController(self, controller):
00302 if controller.tab().tabWidget():
00303 self._window.activateWindow()
00304 self._window.tabWidget().setCurrentIndex(self.tabControllers().index(controller))
00305 else:
00306 controller.tab().activateWindow()
00307
00308 def currentTabController(self):
00309 """ Return the TabController that belongs to the tab selected in the MainWindow.
00310 """
00311
00312 if isinstance(self.activeWindow(),AbstractTab):
00313 return self.activeWindow().controller()
00314 else:
00315 currentWidget = self._window.tabWidget().currentWidget()
00316 if isinstance(currentWidget, AbstractTab):
00317 return currentWidget.controller()
00318 raise NoCurrentTabControllerException
00319
00320 def mainWindow(self):
00321 return self._window
00322
00323
00324
00325
00326
00327 def createAction(self, name, slot=None, shortcut=None, image=None, enabled=True):
00328 """ create an action with name and icon and connect it to a slot.
00329 """
00330
00331 if image:
00332 image0 = QPixmap()
00333 image0.load(":/resources/" + image + ".svg")
00334 action = QAction(QIcon(image0), name, self._window)
00335 else:
00336 action = QAction(name, self._window)
00337 action.setEnabled(enabled)
00338 if slot:
00339 self.connect(action, SIGNAL("triggered()"), slot)
00340 if shortcut:
00341 if isinstance(shortcut, list):
00342 action.setShortcuts(shortcut)
00343 else:
00344 action.setShortcut(shortcut)
00345 return action
00346
00347 def _fillFileMenu(self):
00348 """Called for the first time this function creates the file menu and fill it.
00349
00350 The function is written in a way that it recreates the whole menu, if it
00351 is called again later during execution. So it is possible to aad new
00352 plugins and use them (which means they appear in the menus) without
00353 restarting the program.
00354 """
00355 logging.debug('Application: _fillFileMenu()')
00356 self._fileMenuItems = {}
00357 if not self._window.fileMenu().isEmpty():
00358 self._window.fileMenu().clear()
00359
00360
00361 newFileActions = []
00362 for plugin in self._plugins:
00363 newFileActions += plugin.getNewFileActions()
00364
00365 if len(newFileActions) == 1:
00366 newFileActions[0].setShortcut('Ctrl+N')
00367
00368 self._window.fileMenu().addActions(newFileActions)
00369
00370
00371 openFileAction = self.createAction('&Open File', self.openFileDialog, 'Ctrl+O', "fileopen")
00372 self._window.fileMenu().addAction(openFileAction)
00373 self._window.fileToolBar().addAction(openFileAction)
00374
00375
00376 self._fileMenuItems['reloadFileAction'] = self.createAction('&Reload File', self.reloadFile, ['Ctrl+R', 'F5'], "reload")
00377 self._window.fileMenu().addAction(self._fileMenuItems['reloadFileAction'])
00378
00379
00380
00381 if not hasattr(self, 'recentFilesMenu'):
00382 self._recentFilesMenu = QMenu('&Recent Files', self._window)
00383 self._recentFilesMenuActions = []
00384 for i in range(0, self.MAX_VISIBLE_RECENT_FILES):
00385 action = self.createAction("recent file " + str(i), self.openRecentFileSlot)
00386 action.setVisible(False)
00387 self._recentFilesMenu.addAction(action)
00388 self._recentFilesMenuActions.append(action)
00389 self._recentFilesMenu.addSeparator()
00390 self._fileMenuItems['clearMissingRecentFilesAction'] = self.createAction("Clear missing files", self.clearMissingRecentFiles)
00391 self._recentFilesMenu.addAction(self._fileMenuItems['clearMissingRecentFilesAction'])
00392 self._fileMenuItems['clearRecentFilesAction'] = self.createAction("Clear list", self.clearRecentFiles)
00393 self._recentFilesMenu.addAction(self._fileMenuItems['clearRecentFilesAction'])
00394
00395 self._window.fileMenu().addMenu(self._recentFilesMenu)
00396
00397 self._window.fileMenu().addSeparator()
00398
00399
00400 self._fileMenuItems['closeFileAction'] = self.createAction('&Close', self.closeFile, 'Ctrl+W', "closefile")
00401 self._window.fileMenu().addAction(self._fileMenuItems['closeFileAction'])
00402
00403
00404 self._fileMenuItems['closeAllAction'] = self.createAction('Close All', self.closeAllFiles, 'Ctrl+Shift+W', "closefileall")
00405 self._window.fileMenu().addAction(self._fileMenuItems['closeAllAction'])
00406
00407 self._window.fileMenu().addSeparator()
00408
00409
00410 self._fileMenuItems['saveFileAction'] = self.createAction('&Save', self.saveFile, 'Ctrl+S', "filesave")
00411 self._window.fileMenu().addAction(self._fileMenuItems['saveFileAction'])
00412 self._window.fileToolBar().addAction(self._fileMenuItems['saveFileAction'])
00413
00414
00415 self._fileMenuItems['saveFileAsAction'] = self.createAction('Save As...', self.saveFileAsDialog, 'Ctrl+Shift+S', image="filesaveas")
00416 self._window.fileMenu().addAction(self._fileMenuItems['saveFileAsAction'])
00417
00418
00419 self._fileMenuItems['saveAllFilesAction'] = self.createAction('Save &All', self.saveAllFiles, "Ctrl+Alt+S", "filesaveall")
00420 self._window.fileMenu().addAction(self._fileMenuItems['saveAllFilesAction'])
00421
00422 self._window.fileMenu().addSeparator()
00423
00424
00425
00426
00427 exit = self.createAction('&Exit', self.exit, "Ctrl+Q", "exit")
00428 self._window.fileMenu().addAction(exit)
00429
00430 def _fillEditMenu(self):
00431 """Called for the first time this function creates the edit menu and fills it.
00432
00433 The function is written in a way that it recreates the whole menu, if it
00434 is called again later during execution. So it is possible to aad new
00435 plugins and use them (which means they appear in the menus) without
00436 restarting the program.
00437 """
00438 logging.debug('Application: _fillEditMenu()')
00439 self._editMenuItems = {}
00440 if not self._window.editMenu().isEmpty():
00441 self._window.editMenu().clear()
00442
00443
00444 self._editMenuItems["undoAction"] = self.createAction("Undo", self.undoEvent, "Ctrl+Z", "edit-undo")
00445 self._editMenuItems["redoAction"] = self.createAction("Redo", self.redoEvent, "Ctrl+Y", "edit-redo")
00446 self._editMenuItems["undoAction"].setData(QVariant(1))
00447 self._editMenuItems["redoAction"].setData(QVariant(1))
00448 self._editMenuItems["undoAction"].setEnabled(False)
00449 self._editMenuItems["redoAction"].setEnabled(False)
00450 self._window.editMenu().addAction(self._editMenuItems["undoAction"])
00451 self._window.editMenu().addAction(self._editMenuItems["redoAction"])
00452
00453
00454
00455
00456 self._undoActionsMenu = QMenu(self._window)
00457 self._undoMenuActions = []
00458 for i in range(0, self.MAX_VISIBLE_UNDO_EVENTS):
00459 action = self.createAction("undo " + str(i), self.undoEvent)
00460 action.setVisible(False)
00461 self._undoActionsMenu.addAction(action)
00462 self._undoMenuActions.append(action)
00463
00464 self._redoActionsMenu = QMenu(self._window)
00465 self._redoMenuActions = []
00466 for i in range(0, self.MAX_VISIBLE_UNDO_EVENTS):
00467 action = self.createAction("redo " + str(i), self.redoEvent)
00468 action.setVisible(False)
00469 self._redoActionsMenu.addAction(action)
00470 self._redoMenuActions.append(action)
00471
00472
00473 self._editMenuItems['cutAction'] = self.createAction('&Cut', self.cutEvent, 'Ctrl+X', 'editcut')
00474 self._window.editMenu().addAction(self._editMenuItems['cutAction'])
00475 self._editMenuItems['cutAction'].setEnabled(False)
00476
00477
00478 self._editMenuItems['copyAction'] = self.createAction('C&opy', self.copyEvent, 'Ctrl+C', 'editcopy')
00479 self._window.editMenu().addAction(self._editMenuItems['copyAction'])
00480 self._editMenuItems['copyAction'].setEnabled(False)
00481
00482
00483 self._editMenuItems['pasteAction'] = self.createAction('&Paste', self.pasteEvent, 'Ctrl+V', 'editpaste')
00484 self._window.editMenu().addAction(self._editMenuItems['pasteAction'])
00485 self._editMenuItems['pasteAction'].setEnabled(False)
00486
00487
00488 self._editMenuItems['selectAllAction'] = self.createAction("Select &all", self.selectAllEvent, "Ctrl+A", "selectall")
00489 self._window.editMenu().addAction(self._editMenuItems['selectAllAction'])
00490 self._editMenuItems['selectAllAction'].setVisible(False)
00491
00492 self._window.editMenu().addSeparator()
00493
00494
00495 self._editMenuItems['findAction'] = self.createAction('&Find', self.findEvent, 'Ctrl+F', "edit-find")
00496 self._window.editMenu().addAction(self._editMenuItems['findAction'])
00497 self._editMenuItems['findAction'].setEnabled(False)
00498
00499 def _fillHelpMenu(self):
00500 logging.debug('Application: _fillHelpMenu()')
00501 self._helpMenuItems = {}
00502
00503
00504 self._helpMenuItems['aboutAction'] = self.createAction('&About', self.aboutBoxSlot, 'F1')
00505 self._window.helpMenu().addAction(self._helpMenuItems['aboutAction'])
00506
00507
00508 if self._logFile:
00509 self._helpMenuItems['openLogFile'] = self.createAction("Open log file", self.openLogFileSlot)
00510 self._window.helpMenu().addAction(self._helpMenuItems['openLogFile'])
00511
00512
00513 if os.path.exists(os.path.join(docDirectory,"index.html")):
00514 self._window.helpMenu().addAction(self.createAction('Offline Documentation', self._openDocumentation, "CTRL+F1"))
00515
00516
00517 self._window.helpMenu().addAction(self.createAction('Website', self._openWebsite, "Shift+F1"))
00518
00519 def updateMenu(self):
00520 """ Update recent files and enable disable menu entries in file and edit menu.
00521 """
00522
00523 if self.mainWindow().startupScreen():
00524 self.updateStartupScreen()
00525
00526 num_recent_files = min(len(self._recentFiles), self.MAX_VISIBLE_RECENT_FILES)
00527 for i in range(0, num_recent_files):
00528 filename = self._recentFiles[i]
00529 self._recentFilesMenuActions[i].setText(os.path.basename(filename))
00530 self._recentFilesMenuActions[i].setToolTip(filename)
00531 self._recentFilesMenuActions[i].setStatusTip(filename)
00532 self._recentFilesMenuActions[i].setData(QVariant(filename))
00533 self._recentFilesMenuActions[i].setVisible(True)
00534
00535 for i in range(num_recent_files, self.MAX_VISIBLE_RECENT_FILES):
00536 self._recentFilesMenuActions[i].setVisible(False)
00537
00538 if num_recent_files == 0:
00539 self._fileMenuItems['clearRecentFilesAction'].setEnabled(False)
00540 self._fileMenuItems['clearMissingRecentFilesAction'].setEnabled(False)
00541 else:
00542 self._fileMenuItems['clearRecentFilesAction'].setEnabled(True)
00543 self._fileMenuItems['clearMissingRecentFilesAction'].setEnabled(True)
00544
00545
00546 at_least_one_flag = False
00547 at_least_two_flag = False
00548 if len(self.tabControllers()) > 1:
00549 at_least_one_flag = True
00550 at_least_two_flag = True
00551 elif len(self.tabControllers()) > 0:
00552 at_least_one_flag = True
00553
00554 self._fileMenuItems['saveFileAction'].setEnabled(at_least_one_flag)
00555 self._fileMenuItems['saveFileAsAction'].setEnabled(at_least_one_flag)
00556 self._fileMenuItems['reloadFileAction'].setEnabled(at_least_one_flag)
00557 self._fileMenuItems['closeFileAction'].setEnabled(at_least_one_flag)
00558
00559 self._fileMenuItems['saveAllFilesAction'].setEnabled(at_least_two_flag)
00560 self._fileMenuItems['closeAllAction'].setEnabled(at_least_two_flag)
00561
00562 try:
00563 if at_least_one_flag:
00564 if not self.currentTabController().isEditable():
00565 self._fileMenuItems['saveFileAction'].setEnabled(False)
00566 self._fileMenuItems['saveFileAsAction'].setEnabled(False)
00567 if not self.currentTabController().isModified():
00568 self._fileMenuItems['saveFileAction'].setEnabled(False)
00569
00570
00571 copy_paste_enabled_flag = at_least_one_flag and self.currentTabController().isCopyPasteEnabled()
00572 self._editMenuItems['cutAction'].setEnabled(copy_paste_enabled_flag)
00573 self._editMenuItems['copyAction'].setEnabled(copy_paste_enabled_flag)
00574 self._editMenuItems['pasteAction'].setEnabled(copy_paste_enabled_flag)
00575
00576 self._editMenuItems['selectAllAction'].setVisible(self.currentTabController().allowSelectAll())
00577
00578 self._editMenuItems['findAction'].setEnabled(at_least_one_flag and self.currentTabController().isFindEnabled())
00579
00580
00581 undo_supported_flag = at_least_one_flag and self.currentTabController().supportsUndo()
00582 self._editMenuItems["undoAction"].setEnabled(undo_supported_flag)
00583 self._editMenuItems["undoAction"].setVisible(undo_supported_flag)
00584 self._editMenuItems["redoAction"].setEnabled(undo_supported_flag)
00585 self._editMenuItems["redoAction"].setVisible(undo_supported_flag)
00586 self.showPluginToolBar(self._undoToolBar, undo_supported_flag)
00587
00588 if undo_supported_flag:
00589 undo_events = self.currentTabController().undoEvents()
00590 num_undo_events = min(len(undo_events), self.MAX_VISIBLE_UNDO_EVENTS)
00591 self._editMenuItems["undoAction"].setEnabled(num_undo_events > 0)
00592 if num_undo_events > 1:
00593 self._editMenuItems["undoAction"].setMenu(self._undoActionsMenu)
00594 else:
00595 self._editMenuItems["undoAction"].setMenu(None)
00596 for i in range(0, num_undo_events):
00597 undo_event = undo_events[num_undo_events - i - 1]
00598 self._undoMenuActions[i].setText(undo_event.LABEL)
00599 self._undoMenuActions[i].setToolTip(undo_event.description())
00600 self._undoMenuActions[i].setStatusTip(undo_event.description())
00601 self._undoMenuActions[i].setData(QVariant(i+1))
00602 self._undoMenuActions[i].setVisible(True)
00603 for i in range(num_undo_events, self.MAX_VISIBLE_UNDO_EVENTS):
00604 self._undoMenuActions[i].setVisible(False)
00605
00606 redo_events = self.currentTabController().redoEvents()
00607 num_redo_events = min(len(redo_events), self.MAX_VISIBLE_UNDO_EVENTS)
00608 self._editMenuItems["redoAction"].setEnabled(num_redo_events > 0)
00609 if num_redo_events > 1:
00610 self._editMenuItems["redoAction"].setMenu(self._redoActionsMenu)
00611 else:
00612 self._editMenuItems["redoAction"].setMenu(None)
00613 for i in range(0, num_redo_events):
00614 redo_event = redo_events[num_redo_events - i - 1]
00615 self._redoMenuActions[i].setText(redo_event.LABEL)
00616 self._redoMenuActions[i].setToolTip(redo_event.description())
00617 self._redoMenuActions[i].setStatusTip(redo_event.description())
00618 self._redoMenuActions[i].setData(QVariant(i+1))
00619 self._redoMenuActions[i].setVisible(True)
00620 for i in range(num_redo_events, self.MAX_VISIBLE_UNDO_EVENTS):
00621 self._redoMenuActions[i].setVisible(False)
00622
00623 except NoCurrentTabControllerException:
00624 pass
00625
00626 def _openDocumentation(self):
00627 """ Opens Vispa Offline Documentation
00628 """
00629 webbrowser.open(os.path.join(docDirectory,"index.html"), 2, True)
00630
00631 def _openWebsite(self):
00632 """ Open new browser tab and opens Vispa Project Website.
00633 """
00634 webbrowser.open(websiteUrl, 2, True)
00635
00636 def createPluginMenu(self, name):
00637 """ Creates menu in main window's menu bar before help menu and adds it to _pluginMenus list.
00638 """
00639 menu = QMenu(name)
00640 self._window.menuBar().insertMenu(self._window.helpMenu().menuAction(), menu)
00641 self._pluginMenus.append(menu)
00642 return menu
00643
00644 def showPluginMenu(self, menuObject, show=True):
00645 """ Shows given menu if it is in _pluginMenus list.
00646 """
00647
00648 if menuObject in self._pluginMenus:
00649
00650 if show:
00651 for action in menuObject.actions():
00652 if hasattr(action,"_wasVisible") and action._wasVisible!=None:
00653 action.setVisible(action._wasVisible)
00654 action._wasVisible=None
00655 else:
00656 action.setVisible(True)
00657 menuObject.menuAction().setVisible(show)
00658
00659 def hidePluginMenu(self, menuObject):
00660 """ Hides given menu object if it it in _pluginMenus list.
00661 """
00662 self.showPluginMenu(menuObject, False)
00663
00664 def hidePluginMenus(self):
00665 """ Hides all menus in _pluginMenus list.
00666 """
00667 for menuObject in self._pluginMenus:
00668
00669 menuObject.menuAction().setVisible(False)
00670 for action in menuObject.actions():
00671 if not hasattr(action,"_wasVisible") or action._wasVisible==None:
00672 action._wasVisible=action.isVisible()
00673 action.setVisible(False)
00674
00675 def createPluginToolBar(self, name):
00676 """ Creates tool bar in main window and adds it to _pluginToolBars list.
00677 """
00678 toolBar = self._window.addToolBar(name)
00679 self._pluginToolBars.append(toolBar)
00680 return toolBar
00681
00682 def showPluginToolBar(self, toolBarObject, show=True):
00683 """ Shows given toolbar if it is in _pluginToolBars list.
00684 """
00685 if toolBarObject in self._pluginToolBars:
00686 toolBarObject.setVisible(show)
00687
00688 def hidePluginMenu(self, toolBarObject):
00689 """ Hides given toolbar object if it it in _pluginToolBars list.
00690 """
00691 self.showPluginToolBar(toolBarObject, False)
00692
00693
00694
00695 def hidePluginToolBars(self):
00696 """ Hides all toolbars in _toolBarMenus list.
00697 """
00698 for toolBar in self._pluginToolBars:
00699 toolBar.hide()
00700
00701 def createZoomToolBar(self):
00702 """ Creates tool bar with three buttons "user", "100 %" and "all".
00703
00704 See TabController's documentation of zoomUser(), zoomHundred() and zoomAll() to find out more on the different zoom levels.
00705 """
00706 self._zoomToolBar = self.createPluginToolBar('Zoom ToolBar')
00707 self._zoomToolBar.addAction(self.createAction('Revert Zoom', self.zoomUserEvent, image='zoomuser'))
00708 self._zoomToolBar.addAction(self.createAction('Zoom to 100 %', self.zoomHundredEvent, image='zoom100'))
00709 self._zoomToolBar.addAction(self.createAction('Zoom to all', self.zoomAllEvent, image='zoomall'))
00710
00711 def showZoomToolBar(self):
00712 """ Makes zoom tool bar visible.
00713
00714 Should be called from TabController's selected() function, if the controller wants to use the tool bar.
00715 """
00716 self.showPluginToolBar(self._zoomToolBar)
00717
00718 def hideZoomToolBar(self):
00719 """ Makes zoom tool bar invisible.
00720 """
00721 self._zoomToolBar.hide()
00722
00723 def createUndoToolBar(self):
00724 """ Creates tool bar with buttons to invoke undo and redo events.
00725
00726 Needs to be called after _fillEditMenu() as actions are defined there.
00727 """
00728 self._undoToolBar = self.createPluginToolBar("Undo ToolBar")
00729 self._undoToolBar.addAction(self._editMenuItems["undoAction"])
00730 self._undoToolBar.addAction(self._editMenuItems["redoAction"])
00731
00732 def showUndoToolBar(self):
00733 """ Makes undo tool bar visible.
00734 """
00735 self.showPluginToolBar(self._undoToolBar)
00736
00737 def hideUndoToolBar(self):
00738 """ Hides undo tool bar.
00739 """
00740 self._undoToolBar.hide()
00741
00742 def clearRecentFiles(self):
00743 """ Empties list of recent files and updates main menu.
00744 """
00745 self._recentFiles = []
00746 self._saveIni()
00747 self.updateMenu()
00748
00749 def clearMissingRecentFiles(self):
00750 """ Removes entries from recent files menu if file does no longer exist.
00751 """
00752 newList = []
00753 for file in self._recentFiles:
00754 if os.path.exists(file):
00755 newList.append(file)
00756 self._recentFiles = newList
00757 self._saveIni()
00758 self.updateMenu()
00759
00760 def addRecentFile(self, filename):
00761 """ Adds given filename to list of recent files.
00762 """
00763 logging.debug('Application: addRecentFile() - ' + filename)
00764 if isinstance(filename, QString):
00765 filename = str(filename)
00766 leftCount = self.MAX_RECENT_FILES - 1
00767 if filename in self._recentFiles:
00768 del self._recentFiles[self._recentFiles.index(filename)]
00769 self._recentFiles = [filename] + self._recentFiles[:leftCount]
00770 self._saveIni()
00771
00772 def recentFiles(self):
00773 """ Returns list of recently opened files.
00774 """
00775 return self._recentFiles
00776
00777 def getLastOpenLocation(self):
00778 """ Returns directory name of first entry of recent files list.
00779 """
00780
00781 if os.path.abspath(os.getcwd()) in [os.path.abspath(baseDirectory),os.path.abspath(os.path.join(baseDirectory,"bin"))] or platform.system() == "Darwin":
00782 if len(self._recentFiles) > 0:
00783 return os.path.dirname(self._recentFiles[0])
00784 elif platform.system() == "Darwin":
00785
00786 return homeDirectory + "/Documents"
00787 else:
00788 return homeDirectory
00789
00790 else:
00791 return os.getcwd()
00792
00793 def recentFilesFromPlugin(self,plugin):
00794 files=[]
00795 filetypes = plugin.filetypes()
00796 extension=None
00797 if len(filetypes) > 0:
00798 extension=filetypes[0].extension().lower()
00799 for file in self._recentFiles:
00800 if os.path.splitext(os.path.basename(file))[1][1:].lower()==extension:
00801 files+=[file]
00802 return files
00803
00804 def updateStartupScreen(self):
00805 screen=self.mainWindow().startupScreen()
00806 screen.analysisDesignerRecentFilesList().clear()
00807 screen.analysisDesignerRecentFilesList().addItem("...")
00808 screen.analysisDesignerRecentFilesList().setCurrentRow(0)
00809 plugin=self.plugin("AnalysisDesignerPlugin")
00810 if plugin:
00811 files = self.recentFilesFromPlugin(plugin)
00812 for file in files:
00813 screen.analysisDesignerRecentFilesList().addItem(os.path.basename(file))
00814
00815 screen.pxlEditorRecentFilesList().clear()
00816 screen.pxlEditorRecentFilesList().addItem("...")
00817 screen.pxlEditorRecentFilesList().setCurrentRow(0)
00818 plugin=self.plugin("PxlPlugin")
00819 if plugin:
00820 files = self.recentFilesFromPlugin(plugin)
00821 for file in files:
00822 screen.pxlEditorRecentFilesList().addItem(os.path.basename(file))
00823
00824 def exit(self):
00825 self._window.close()
00826
00827 def shutdownPlugins(self):
00828 logging.debug('Application: shutting down plugins' )
00829 for plugin in self._plugins:
00830 plugin.shutdown()
00831
00832
00833 def _collectFileExtensions(self):
00834 """ Loop over all plugins and remember their file extensions.
00835 """
00836 self._knownExtensionsDictionary = {}
00837 self._knownFiltersList = []
00838 self._knownFiltersList.append('All files (*.*)')
00839 for plugin in self.plugins():
00840 for ft in plugin.filetypes():
00841 self._knownExtensionsDictionary[ft.extension()] = plugin
00842 self._knownFiltersList.append(ft.fileDialogFilter())
00843 if len(self._knownFiltersList) > 0:
00844 allKnownFilter = 'All known files (*.' + " *.".join(self._knownExtensionsDictionary.keys()) + ')'
00845 self._knownFiltersList.insert(1, allKnownFilter)
00846 logging.debug('Application: _collectFileExtensions() - ' + allKnownFilter)
00847 else:
00848 logging.debug('Application: _collectFileExtensions()')
00849
00850
00851 def openFileDialog(self, defaultFileFilter=None):
00852 """Displays a common open dialog for all known file types.
00853 """
00854 logging.debug('Application: openFileDialog()')
00855
00856 if not defaultFileFilter:
00857 if len(self._knownFiltersList) > 1:
00858
00859 defaultFileFilter = self._knownFiltersList[1]
00860 else:
00861
00862 defaultFileFilter = self._knownFiltersList[0]
00863
00864
00865 filename = QFileDialog.getOpenFileName(
00866 self._window,
00867 'Select a file',
00868 self.getLastOpenLocation(),
00869 ";;".join(self._knownFiltersList),
00870 defaultFileFilter)
00871 if not filename.isEmpty():
00872 self.openFile(filename)
00873
00874 def openFile(self, filename):
00875 """ Decides which plugin should handle opening of the given file name.
00876 """
00877 logging.debug('Application: openFile()')
00878 statusMessage = self.startWorking("Opening file " + filename)
00879 if isinstance(filename, QString):
00880 filename = str(filename)
00881
00882
00883 for controller in self.tabControllers():
00884 if filename == controller.filename():
00885 self.setCurrentTabController(controller)
00886 self.stopWorking(statusMessage, "already open")
00887 return
00888
00889 baseName = os.path.basename(filename)
00890 ext = os.path.splitext(baseName)[1].lower().strip(".")
00891 errormsg = None
00892
00893 if self._knownExtensionsDictionary == {}:
00894 self._collectFileExtensions()
00895
00896 foundCorrectPlugin = False
00897 if os.path.exists(filename):
00898 if ext in self._knownExtensionsDictionary:
00899 foundCorrectPlugin = True
00900 try:
00901 if self._knownExtensionsDictionary[ext].openFile(filename):
00902 self.addRecentFile(filename)
00903 else:
00904 logging.error(self.__class__.__name__ + ": openFile() - Error while opening '" + str(filename) + "'.")
00905 self.errorMessage("Failed to open file.")
00906 except Exception:
00907 logging.error(self.__class__.__name__ + ": openFile() - Error while opening '" + str(filename) + "' : " + exception_traceback())
00908 self.errorMessage("Exception while opening file. See log for details.")
00909
00910 if not foundCorrectPlugin:
00911 errormsg = 'Unknown file type (.' + ext + '). Aborting.'
00912 else:
00913 errormsg = 'File does not exist: ' + filename
00914
00915 self.updateMenu()
00916
00917
00918 if not errormsg:
00919 self.stopWorking(statusMessage)
00920 else:
00921 logging.error(errormsg)
00922 self.stopWorking(statusMessage, "failed")
00923 self.warningMessage(errormsg)
00924
00925 def reloadFile(self):
00926 """ Tells current tab controller to refresh.
00927 """
00928 logging.debug('Application: reloadFile()')
00929 try:
00930 if self.currentTabController().filename() and self.currentTabController().allowClose():
00931 self.currentTabController().refresh()
00932 self.currentTabController().setModified(False)
00933 except NoCurrentTabControllerException:
00934 pass
00935
00936 self.tabChanged()
00937
00938 def closeFile(self):
00939 """ Tells current tab controller to close.
00940 """
00941 logging.debug('Application: closeCurrentFile()')
00942 try:
00943 self.currentTabController().close()
00944 except NoCurrentTabControllerException:
00945 pass
00946
00947 self.tabChanged()
00948
00949 def closeAllFiles(self):
00950 """ Closes all open tabs unless user aborts closing.
00951 """
00952 logging.debug('Application: closeAllFiles()')
00953
00954 self._closeAllFlag = True
00955 while len(self.tabControllers())>0:
00956 controller=self.tabControllers()[0]
00957 if controller.close() == False:
00958 break
00959 self._closeAllFlag = False
00960
00961
00962 self.tabChanged()
00963
00964 def saveFile(self):
00965 """ Tells current tab controller to save its file.
00966 """
00967 logging.debug('Application: saveFile()')
00968 try:
00969 self.currentTabController().save()
00970 except NoCurrentTabControllerException:
00971 logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
00972
00973 def saveFileAsDialog(self):
00974 """This functions asks the user for a file name.
00975 """
00976 logging.debug('Application: saveFileAsDialog()')
00977 try:
00978 currentTabController = self.currentTabController()
00979 except NoCurrentTabControllerException:
00980 logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
00981 return
00982
00983 if currentTabController.filename():
00984 startDirectory = currentTabController.filename()
00985 else:
00986 startDirectory = self.getLastOpenLocation()
00987
00988 filetypesList = []
00989 for filetype in currentTabController.supportedFileTypes():
00990 filetypesList.append(Filetype(filetype[0], filetype[1]).fileDialogFilter())
00991 filetypesList.append('Any (*.*)')
00992
00993 selectedFilter = QString("")
00994 filename = str(QFileDialog.getSaveFileName(
00995 self._window,
00996 'Select a file',
00997 startDirectory,
00998 ";;".join(filetypesList), selectedFilter))
00999 if filename != "":
01000
01001 if os.path.splitext(filename)[1].strip(".") == "" and str(selectedFilter) != 'Any (*.*)':
01002 ext = currentTabController.supportedFileTypes()[filetypesList.index(str(selectedFilter))][0]
01003 filename = os.path.splitext(filename)[0] + "." + ext
01004 return currentTabController.save(filename)
01005 return False
01006
01007 def saveAllFiles(self):
01008 """ Tells tab controllers of all tabs to save.
01009 """
01010 logging.debug('Application: saveAllFiles()')
01011
01012 for controller in self.tabControllers():
01013 if controller.filename() or controller == self.currentTabController():
01014 controller.save()
01015
01016 def cutEvent(self):
01017 """ Called when cut action is triggered (e.g. from menu entry) and forwards it to current tab controller.
01018 """
01019 try:
01020 self.currentTabController().cut()
01021 except NoCurrentTabControllerException:
01022 logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
01023
01024 def copyEvent(self):
01025 """ Called when copy action is triggered (e.g. from menu entry) and forwards it to current tab controller.
01026 """
01027 try:
01028 self.currentTabController().copy()
01029 except NoCurrentTabControllerException:
01030 logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
01031
01032 def pasteEvent(self):
01033 """ Called when paste action is triggered (e.g. from menu entry) and forwards it to current tab controller.
01034 """
01035 try:
01036 self.currentTabController().paste()
01037 except NoCurrentTabControllerException:
01038 logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
01039
01040 def selectAllEvent(self):
01041 """ Called when selectAll action is triggered (e.g. from menu entry) and forwards it to current tab controller.
01042 """
01043 try:
01044 self.currentTabController().selectAll()
01045 except NoCurrentTabControllerException:
01046 logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
01047
01048 def findEvent(self):
01049 """ Called when find action is triggered (e.g. from menu entry) and forwards it to current tab controller.
01050 """
01051 try:
01052 self.currentTabController().find()
01053 except NoCurrentTabControllerException:
01054 logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
01055
01056 def zoomUserEvent(self):
01057 """ Handles button pressed event from zoom tool bar and forwards it to current tab controller.
01058 """
01059 try:
01060 self.currentTabController().zoomUser()
01061 except NoCurrentTabControllerException:
01062 logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
01063
01064 def zoomHundredEvent(self):
01065 """ Handles button pressed event from zoom tool bar and forwards it to current tab controller.
01066 """
01067 try:
01068 self.currentTabController().zoomHundred()
01069 except NoCurrentTabControllerException:
01070 logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
01071
01072 def zoomAllEvent(self):
01073 """ Handles button pressed event from zoom tool bar and forwards it to current tab controller.
01074 """
01075 try:
01076 self.currentTabController().zoomAll()
01077 except NoCurrentTabControllerException:
01078 logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
01079
01080 def undoEvent(self):
01081 """ Handles undo action for buttons in undo tool bar and edit menu.
01082 """
01083 try:
01084 num = 1
01085 sender = self.sender()
01086 if sender:
01087 num = sender.data().toInt()
01088 if len(num) > 1:
01089
01090 num = num[0]
01091 self.currentTabController().undo(num)
01092 except NoCurrentTabControllerException:
01093 logging.warning(self.__class__.__name__ + ": "+ self.TAB_PREMATURELY_CLOSED_WARNING)
01094
01095 def redoEvent(self):
01096 """ Handles redo action for buttons in undo tool bar and edit menu.
01097 """
01098 try:
01099 num = 1
01100 sender = self.sender()
01101 if sender:
01102 num = sender.data().toInt()
01103 if len(num) > 1:
01104
01105 num = num[0]
01106 self.currentTabController().redo(num)
01107 except NoCurrentTabControllerException:
01108 logging.warning(self.__class__.__name__ + ": "+ self.TAB_PREMATURELY_CLOSED_WARNING)
01109
01110 def aboutBoxSlot(self):
01111 """ Displays about box.
01112 """
01113 logging.debug('Application: aboutBoxSlot()')
01114 about = AboutDialog(self)
01115 about.onScreen()
01116
01117 def openLogFileSlot(self):
01118 if self._logFile:
01119 self.doubleClickOnFile(self._logFile)
01120 else:
01121 logging.warning("%s: openLogFileSlot(): _logFile not set. Aborting..." % self.__class__.__name__)
01122
01123 def openRecentFileSlot(self):
01124 """ Slot for opening recent file.
01125
01126 Called from recent file menu action. Filename is set as data object (QVariant) of action.
01127 """
01128 filename = self.sender().data().toString()
01129 logging.debug('Application: openRecentFileSlot() - ' + filename)
01130 self.openFile(filename)
01131
01132 def tabChanged(self, tab=None):
01133 """ when a different tab is activated update menu
01134 """
01135 logging.debug('Application: tabChanged()')
01136
01137 if not self._closeAllFlag:
01138 self.hidePluginMenus()
01139 self.hidePluginToolBars()
01140 self.updateWindowTitle()
01141 self.updateMenu()
01142 try:
01143 self.currentTabController().activated()
01144 self.currentTabController().checkModificationTimestamp()
01145 except NoCurrentTabControllerException:
01146 pass
01147
01148 self.mainWindow().setStartupScreenVisible(self.mainWindow().tabWidget().count() == 0)
01149
01150 def updateMenuAndWindowTitle(self):
01151 """ Update menu and window title.
01152 """
01153 self.updateMenu()
01154 self.updateWindowTitle()
01155
01156 def windowTitle(self):
01157 return str(self._window.windowTitle()).split("-")[0].strip()
01158
01159 def updateWindowTitle(self):
01160 """ update window caption
01161 """
01162
01163 name = self.windowTitle()
01164
01165 try:
01166 filename = self.currentTabController().filename()
01167 except NoCurrentTabControllerException:
01168 filename = None
01169
01170 if filename:
01171 dirName = os.path.dirname(sys.argv[0])
01172 if os.path.abspath(dirName) in filename:
01173 filename = filename[len(os.path.abspath(dirName)) + 1:]
01174 name = name + " - " + filename
01175 self._window.setWindowTitle(name)
01176
01177 def ini(self):
01178 if not self._ini:
01179 self._ini = ConfigParser.ConfigParser()
01180 self._ini.read(self._iniFileName)
01181 return self._ini
01182
01183 def writeIni(self):
01184 try:
01185 configfile = open(self._iniFileName, "w")
01186 self._ini.write(configfile)
01187 configfile.close()
01188 except IOError:
01189 pass
01190 self._ini = None
01191
01192 def _loadIni(self):
01193 """ Save the list of recent files.
01194 """
01195 logging.debug('Application: _loadIni()')
01196 ini = self.ini()
01197 self._recentFiles = []
01198 if ini.has_section("history"):
01199 for i in range(0, self.MAX_RECENT_FILES):
01200 if ini.has_option("history", str(i)):
01201 self._recentFiles+=[ini.get("history", str(i))]
01202
01203 def _saveIni(self):
01204 """ Load the list of recent files.
01205 """
01206 logging.debug('Application: _saveIni()')
01207 ini = self.ini()
01208 if ini.has_section("history"):
01209 ini.remove_section("history")
01210 ini.add_section("history")
01211 for i in range(len(self._recentFiles)):
01212 ini.set("history", str(i), self._recentFiles[i])
01213
01214 self.writeIni()
01215
01216 def errorMessage(self, message):
01217 """ Displays error message.
01218 """
01219 QMessageBox.critical(self.mainWindow(), 'Error', message)
01220
01221 def warningMessage(self, message):
01222 """ Displays warning message.
01223 """
01224 QMessageBox.warning(self.mainWindow(), 'Warning', message)
01225
01226 def infoMessage(self, message):
01227 """ Displays info message.
01228 """
01229 QMessageBox.about(self.mainWindow(), 'Info', message)
01230
01231 def showMessageBox(self, text, informativeText="", standardButtons=QMessageBox.Ok | QMessageBox.Cancel | QMessageBox.Ignore, defaultButton=QMessageBox.Ok, extraButtons=None):
01232 """ Shows a standardized message box and returns the pressed button.
01233
01234 See documentation on Qt's QMessageBox for a list of possible standard buttons.
01235 """
01236
01237 msgBox = QMessageBox(self.mainWindow())
01238 msgBox.setParent(self.mainWindow(), Qt.Sheet)
01239 msgBox.setText(text)
01240 msgBox.setInformativeText(informativeText)
01241 msgBox.setStandardButtons(standardButtons)
01242 if extraButtons!=None:
01243 for button,role in extraButtons:
01244 msgBox.addButton(button,role)
01245 msgBox.setDefaultButton(defaultButton)
01246 return msgBox.exec_()
01247
01248 def doubleClickOnFile(self, filename):
01249 """ Opens file given as argument if possible in Vispa.
01250
01251 If Vispa cannot handle the file type the file will be opened in it's default application.
01252 """
01253 logging.debug(self.__class__.__name__ + ": doubleClickOnFile() - " + str(filename))
01254
01255 if filename == "":
01256 return
01257
01258 baseName = os.path.basename(filename)
01259 ext = os.path.splitext(baseName)[1].lower().strip(".")
01260 if self._knownExtensionsDictionary == {}:
01261 self._collectFileExtensions()
01262 if os.path.exists(filename):
01263 if ext in self._knownExtensionsDictionary:
01264 return self.openFile(filename)
01265
01266
01267 try:
01268 if 'Windows' in platform.system():
01269 os.startfile(filename)
01270 elif 'Darwin' in platform.system():
01271 if os.access(filename, os.X_OK):
01272 logging.warning("It seems that executing the python program is the default action on this system, which is processed when double clicking a file. Please change that to open the file witrh your favourite editor, to use this feature.")
01273 else:
01274 subprocess.call(("open", filename))
01275 elif 'Linux' in platform.system():
01276
01277 if os.access(filename, os.X_OK):
01278 logging.warning("It seems that executing the python program is the default action on this system, which is processed when double clicking a file. Please change that to open the file witrh your favourite editor, to use this feature.")
01279 else:
01280 try:
01281
01282 subprocess.call(("xdg-open", filename))
01283 except:
01284 try:
01285 subprocess.call(("gnome-open", filename))
01286 except:
01287 logging.error(self.__class__.__name__ + ": doubleClickOnFile() - Platform '" + platform.platform() + "'. Cannot open file. I Don't know how!")
01288 except:
01289 logging.error(self.__class__.__name__ + ": doubleClickOnFile() - Platform '" + platform.platform() + "'. Error while opening file: " + str(filename))
01290
01291 def createStatusBar(self):
01292 self._workingMessages = {}
01293
01294 self._progressWidget = RotatingIcon(":/resources/vispabutton.png")
01295 self._window.statusBar().addPermanentWidget(self._progressWidget)
01296
01297 def startWorking(self, message=""):
01298 if len(self._workingMessages.keys()) == 0:
01299 self._progressWidget.start()
01300 self._window.statusBar().showMessage(message + "...")
01301 self._messageId+=1
01302 self._workingMessages[self._messageId] = message
01303 self._progressWidget.setToolTip(message)
01304 return self._messageId
01305
01306 def stopWorking(self, id, end="done"):
01307 if not id in self._workingMessages.keys():
01308 logging.error(self.__class__.__name__ +": stopWorking() - Unknown id %s. Aborting..." % str(id))
01309 return
01310 if len(self._workingMessages.keys()) > 1:
01311 self._window.statusBar().showMessage(self._workingMessages[self._workingMessages.keys()[0]] + "...")
01312 self._progressWidget.setToolTip(self._workingMessages[self._workingMessages.keys()[0]])
01313 else:
01314 self._progressWidget.stop()
01315 self._progressWidget.setToolTip("")
01316 self._window.statusBar().showMessage(self._workingMessages[id] + "... " + end + ".")
01317 del self._workingMessages[id]
01318
01319 def tabCloseRequest(self, i):
01320 self.mainWindow().tabWidget().setCurrentIndex(i)
01321 self.closeFile()
01322
01323 def showStatusMessage(self, message, timeout=0):
01324 self._window.statusBar().showMessage(message, timeout)
01325
01326 def cancel(self):
01327 """ Cancel operations in current tab.
01328 """
01329 logging.debug(__name__ + ": cancel")
01330 try:
01331 self.currentTabController().cancel()
01332 except NoCurrentTabControllerException:
01333 pass