CMS 3D CMS Logo

Application.py
Go to the documentation of this file.
1 from __future__ import absolute_import
2 from builtins import range
3 import os
4 import sys
5 import string
6 import commands
7 import platform
8 import logging
9 import logging.handlers
10 import ConfigParser
11 import optparse
12 import webbrowser
13 import subprocess
14 
15 from PyQt4.QtCore import SIGNAL,qVersion,QString,QVariant, Qt
16 from PyQt4.QtGui import QApplication,QMenu,QPixmap,QAction,QFileDialog,QIcon,QMessageBox
17 
18 from Vispa.Main.Directories import logDirectory,pluginDirectory,baseDirectory,homeDirectory,iniFileName,applicationName,docDirectory,websiteUrl
19 from Vispa.Main.MainWindow import MainWindow
20 from Vispa.Main.AbstractTab import AbstractTab
21 from Vispa.Main.Filetype import Filetype
22 from Vispa.Main.Exceptions import *
23 from Vispa.Main.AboutDialog import AboutDialog
24 from Vispa.Main.RotatingIcon import RotatingIcon
25 #from PreferencesEditor import PreferencesEditor
26 import Vispa.__init__
27 
28 from . import Resources
29 
30 class Application(QApplication):
31 
32  MAX_RECENT_FILES = 30
33  MAX_VISIBLE_RECENT_FILES = 10
34  MAX_VISIBLE_UNDO_EVENTS = 10
35  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"
36  TAB_PREMATURELY_CLOSED_WARNING = "Tab was closed before user request could be handled."
37  NO_PROCESS_EVENTS = False
38 
39  def __init__(self, argv):
40  QApplication.__init__(self, argv)
41  self._version = None
42  self._plugins = []
43  self._closeAllFlag = False
46  self._pluginMenus = []
47  self._pluginToolBars = []
48  self._recentFiles = []
49  self._ini = None
50  self._iniFileName = iniFileName
51  self._zoomToolBar = None
52  self._undoToolBar = None
53  self._messageId=0
54  self._loadablePlugins = {}
55  self._logFile = None
56 
57  self._initLogging()
58 
59  logging.debug('Running with Qt-Version ' + str(qVersion()))
60 
62 
63  self._loadIni()
64 
65  self.setVersion(Vispa.__init__.__version__)
66 
67  self._window = MainWindow(self, applicationName)
68  self._window.show()
69 
70  self._loadPlugins()
71 
72  self._fillFileMenu()
73  self._fillEditMenu()
74  self._fillHelpMenu()
75 
76  self.createUndoToolBar()
77  self.createZoomToolBar()
78  self.hidePluginMenus()
79  self.hidePluginToolBars()
80  self.createStatusBar()
81  self.updateMenu()
82 
84  self._connectSignals()
85 
86  def commandLineParser(self):
87  return self._commandLineParser
88 
89  def commandLineOptions(self):
90  return self._commandLineOptions
91 
92  def setVersion(self, version):
93  self._version = version
94 
95  def version(self):
96  """ Returns version string.
97  """
98  return self._version
99 
100  def atLeastQtVersion(self, versionString):
101  """ Returns True if given versionString is newer than current used version of Qt.
102  """
103  [majorV, minorV, revisionV] = versionString.split(".")
104  [majorQ, minorQ, revisionQ] = str(qVersion()).split(".")
105  if majorV > majorQ:
106  return True
107  elif majorV < majorQ:
108  return False
109  elif majorV == majorQ:
110  if minorV > minorQ:
111  return True
112  elif minorV < minorQ:
113  return False
114  elif minorV == minorQ:
115  if revisionV > revisionQ:
116  return True
117  elif revisionV < revisionQ:
118  return False
119  elif revisionV == revisionQ:
120  return True
121  return False
122 
124  """ Set the available command line options.
125  """
126  self._commandLineParser.add_option("-f", "--file", dest="filename", help="open a FILE", metavar="FILE")
127  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")
128 
130  """ Initialize command line parser.
131 
132  After calling this function, plugins may add options.
133  """
134  class QuiteOptionParser(optparse.OptionParser):
135  def __init__(self):
136  optparse.OptionParser.__init__(self,add_help_option=False)
137  def error(self,message=""):
138  pass
139  self._commandLineParser = QuiteOptionParser()
140  self._setCommandLineOptions()
141  (self._commandLineOptions, self._args) = self._commandLineParser.parse_args()
142  if self._commandLineOptions.loglevel:
143  logging.root.setLevel(self._commandLineOptions.loglevel)
144  self._commandLineParser = optparse.OptionParser()
145  self._setCommandLineOptions()
146 
148  """ Analyzes the command line attributes and print usage summary if required.
149  """
150  (self._commandLineOptions, self._args) = self._commandLineParser.parse_args()
151  if self._commandLineOptions.filename:
152  self.mainWindow().setStartupScreenVisible(False)
153  self.openFile(self._commandLineOptions.filename)
154  if len(self._args) > 0:
155  self.mainWindow().setStartupScreenVisible(False)
156  self.openFile(self._args[0])
157 
158  def _checkFile(self, filename):
159  """ Check if logfile is closed correctly
160  """
161  finished = True
162  file = open(filename, "r")
163  for line in file.readlines():
164  if "INFO Start logging" in line:
165  finished = False
166  if "INFO Stop logging" in line:
167  finished = True
168  return finished
169 
170  def _initLogging(self):
171  """ Add logging handlers for a log file as well as stderr.
172  """
173  instance = 0
174  done = False
175  while not done:
176  # iterate name of log file for several instances of vispa
177  instance += 1
178  logfile = os.path.join(logDirectory, "log" + str(instance) + ".txt")
179  # do not create more than 10 files
180  if instance > 10:
181  instance = 1
182  logfile = os.path.join(logDirectory, "log" + str(instance) + ".txt")
183  done = True
184  break
185  if not os.path.exists(logfile):
186  done = True
187  break
188  done = self._checkFile(logfile)
189 
190  # clean up old logs
191  nextlogfile = os.path.join(logDirectory, "log" + str(instance + 1) + ".txt")
192  if os.path.exists(nextlogfile):
193  if not self._checkFile(nextlogfile):
194  file = open(nextlogfile, "a")
195  file.write("Cleaning up logfile after abnormal termination: INFO Stop logging\n")
196 
197  if os.path.exists(logDirectory):
198  handler1 = logging.handlers.RotatingFileHandler(logfile, maxBytes=100000, backupCount=1)
199  formatter1 = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
200  handler1.setFormatter(formatter1)
201  self._logFile = logfile
202 
203  handler2 = logging.StreamHandler(sys.stderr)
204  formatter2 = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
205  handler2.setFormatter(formatter2)
206 
207  logging.root.handlers = []
208  if os.path.exists(logDirectory):
209  logging.root.addHandler(handler1)
210  logging.root.addHandler(handler2)
211  #logging.root.setLevel(logging.INFO)
212 
213  self._infologger = logging.getLogger("info")
214  self._infologger.setLevel(logging.INFO)
215  self._infologger.handlers = []
216  if self._logFile:
217  self._infologger.info("Start logging to " + self._logFile)
218 
219  def run(self):
220  """ Show the MainWindow and run the application.
221  """
222  #logging.debug('Application: run()')
223 
224  self.exec_()
225  if self._logFile:
226  self._infologger.info("Stop logging to " + self._logFile)
227 
228  def _connectSignals(self):
229  """ Connect signal to observe the TabWidget in the MainWindow.
230  """
231  logging.debug('Application: _connectSignals()')
232  self.connect(self._window.tabWidget(), SIGNAL("currentChanged(int)"), self.tabChanged)
233  self.connect(self._window, SIGNAL("windowActivated()"), self.tabChanged)
234  self.connect(self._window.tabWidget(), SIGNAL("tabCloseRequested(int)"), self.tabCloseRequest)
235 
236  def _loadPlugins(self):
237  """ Search all subfolders of the plugin directory for vispa plugins and registers them.
238  """
239  logging.debug('Application: _loadPlugins()')
240  dirs = ["Vispa.Plugins." + str(f) for f in os.listdir(pluginDirectory)
241  if os.path.isdir(os.path.join(pluginDirectory, f)) and not f.startswith(".") and not f.startswith("CVS")]
242  failedToLoad = []
243  for di in dirs:
244  try:
245  module = __import__(di, globals(), locals(), "Vispa.Plugins")
246  self._loadablePlugins[module.plugin.__name__] = module.plugin
247  except ImportError:
248  logging.warning('Application: cannot load plugin ' + di + ': ' + exception_traceback())
249  failedToLoad.append(di)
250  except PluginIgnoredException as e:
251  logging.info('Application: plugin ' + di + ' cannot be loaded and is ignored: ' + str(e))
252  except AttributeError as e:
253  logging.info('Application: plugin ' + di + ' is deactivated (define plugin in __init__.py to activate): ' + str(e))
254 
255  for pluginName in self._loadablePlugins.keys():
256  # loop over all loadable plugins
257  # this mechanism enables plugins to call initializePlugin() for plugins they depend on
258  if not self.initializePlugin(pluginName):
259  failedToLoad.append(pluginName)
260 
261  if len(failedToLoad) > 0:
262  self.errorMessage(self.FAILED_LOADING_PLUGINS_ERROR + "\n".join(failedToLoad))
263 
265 
266  def initializePlugin(self, name):
267  if name in [plugin.__class__.__name__ for plugin in self._plugins]:
268  logging.info("%s: initalizePlugin(): Plugin '%s' already loaded. Aborting..." % (self.__class__.__name__, name))
269  return True
270  if not name in self._loadablePlugins.keys():
271  logging.error("%s: initalizePlugin(): Unknown plugin '%s'. Aborting..." % (self.__class__.__name__, name))
272  return False
273 
274  try:
275  pluginObject = self._loadablePlugins[name](self)
276  self._plugins.append(pluginObject)
277  logging.debug('Application: added plugin ' + name)
278  return True
279  except ValueError:
280  logging.warning('Application: ' + name + ' is not a plugin: ' + exception_traceback())
281  return False
282 
283 
284  def plugins(self):
285  return self._plugins
286 
287  def plugin(self, name):
288  """ Returns plugin with given name or None if there is no such one.
289  """
290  if not name.endswith("Plugin"):
291  name += "Plugin"
292 
293  for plugin in self._plugins:
294  if name == plugin.__class__.__name__:
295  return plugin
296  return None
297 
298  def tabControllers(self):
299  controllers=[self._window.tabWidget().widget(i).controller() for i in range(0, self._window.tabWidget().count())]
300  controllers+=[tab.controller() for tab in self.mainWindow().tabWidgets()]
301  return controllers
302 
303  def setCurrentTabController(self, controller):
304  if controller.tab().tabWidget():
305  self._window.activateWindow()
306  self._window.tabWidget().setCurrentIndex(self.tabControllers().index(controller))
307  else:
308  controller.tab().activateWindow()
309 
311  """ Return the TabController that belongs to the tab selected in the MainWindow.
312  """
313  #logging.debug('Application: currentTabController()')
314  if isinstance(self.activeWindow(),AbstractTab):
315  return self.activeWindow().controller()
316  else:
317  currentWidget = self._window.tabWidget().currentWidget()
318  if isinstance(currentWidget, AbstractTab):
319  return currentWidget.controller()
320  raise NoCurrentTabControllerException
321 
322  def mainWindow(self):
323  return self._window
324 
325  #def editPreferences(self):
326  # self.preferencesEditor = PreferencesEditor()
327  # self.preferencesEditor.show()
328 
329  def createAction(self, name, slot=None, shortcut=None, image=None, enabled=True):
330  """ create an action with name and icon and connect it to a slot.
331  """
332  #logging.debug('Application: createAction() - ' + name)
333  if image:
334  image0 = QPixmap()
335  image0.load(":/resources/" + image + ".svg")
336  action = QAction(QIcon(image0), name, self._window)
337  else:
338  action = QAction(name, self._window)
339  action.setEnabled(enabled)
340  if slot:
341  self.connect(action, SIGNAL("triggered()"), slot)
342  if shortcut:
343  if isinstance(shortcut, list):
344  action.setShortcuts(shortcut)
345  else:
346  action.setShortcut(shortcut)
347  return action
348 
349  def _fillFileMenu(self):
350  """Called for the first time this function creates the file menu and fill it.
351 
352  The function is written in a way that it recreates the whole menu, if it
353  is called again later during execution. So it is possible to aad new
354  plugins and use them (which means they appear in the menus) without
355  restarting the program.
356  """
357  logging.debug('Application: _fillFileMenu()')
358  self._fileMenuItems = {}
359  if not self._window.fileMenu().isEmpty():
360  self._window.fileMenu().clear()
361 
362  # New
363  newFileActions = []
364  for plugin in self._plugins:
365  newFileActions += plugin.getNewFileActions()
366 
367  if len(newFileActions) == 1:
368  newFileActions[0].setShortcut('Ctrl+N')
369 
370  self._window.fileMenu().addActions(newFileActions)
371 
372  # Open
373  openFileAction = self.createAction('&Open File', self.openFileDialog, 'Ctrl+O', "fileopen")
374  self._window.fileMenu().addAction(openFileAction)
375  self._window.fileToolBar().addAction(openFileAction)
376 
377  # Reload
378  self._fileMenuItems['reloadFileAction'] = self.createAction('&Reload File', self.reloadFile, ['Ctrl+R', 'F5'], "reload")
379  self._window.fileMenu().addAction(self._fileMenuItems['reloadFileAction'])
380  #self._window.fileToolBar().addAction(self._fileMenuItems['reloadFileAction'])
381 
382  # Recent files
383  if not hasattr(self, 'recentFilesMenu'):
384  self._recentFilesMenu = QMenu('&Recent Files', self._window)
386  for i in range(0, self.MAX_VISIBLE_RECENT_FILES):
387  action = self.createAction("recent file " + str(i), self.openRecentFileSlot)
388  action.setVisible(False)
389  self._recentFilesMenu.addAction(action)
390  self._recentFilesMenuActions.append(action)
391  self._recentFilesMenu.addSeparator()
392  self._fileMenuItems['clearMissingRecentFilesAction'] = self.createAction("Clear missing files", self.clearMissingRecentFiles)
393  self._recentFilesMenu.addAction(self._fileMenuItems['clearMissingRecentFilesAction'])
394  self._fileMenuItems['clearRecentFilesAction'] = self.createAction("Clear list", self.clearRecentFiles)
395  self._recentFilesMenu.addAction(self._fileMenuItems['clearRecentFilesAction'])
396 
397  self._window.fileMenu().addMenu(self._recentFilesMenu)
398 
399  self._window.fileMenu().addSeparator()
400 
401  # Close
402  self._fileMenuItems['closeFileAction'] = self.createAction('&Close', self.closeFile, 'Ctrl+W', "closefile")
403  self._window.fileMenu().addAction(self._fileMenuItems['closeFileAction'])
404 
405  # Close all
406  self._fileMenuItems['closeAllAction'] = self.createAction('Close All', self.closeAllFiles, 'Ctrl+Shift+W', "closefileall")
407  self._window.fileMenu().addAction(self._fileMenuItems['closeAllAction'])
408 
409  self._window.fileMenu().addSeparator()
410 
411  # Save
412  self._fileMenuItems['saveFileAction'] = self.createAction('&Save', self.saveFile, 'Ctrl+S', "filesave")
413  self._window.fileMenu().addAction(self._fileMenuItems['saveFileAction'])
414  self._window.fileToolBar().addAction(self._fileMenuItems['saveFileAction'])
415 
416  # Save as
417  self._fileMenuItems['saveFileAsAction'] = self.createAction('Save As...', self.saveFileAsDialog, 'Ctrl+Shift+S', image="filesaveas")
418  self._window.fileMenu().addAction(self._fileMenuItems['saveFileAsAction'])
419 
420  # Save all
421  self._fileMenuItems['saveAllFilesAction'] = self.createAction('Save &All', self.saveAllFiles, "Ctrl+Alt+S", "filesaveall")
422  self._window.fileMenu().addAction(self._fileMenuItems['saveAllFilesAction'])
423 
424  self._window.fileMenu().addSeparator()
425 
426  #editPreferencesAction = self.createAction('Preferences',self.editPreferences)
427  #self._window.fileMenu().addAction(editPreferencesAction)
428  # Exit
429  exit = self.createAction('&Exit', self.exit, "Ctrl+Q", "exit")
430  self._window.fileMenu().addAction(exit)
431 
432  def _fillEditMenu(self):
433  """Called for the first time this function creates the edit menu and fills it.
434 
435  The function is written in a way that it recreates the whole menu, if it
436  is called again later during execution. So it is possible to aad new
437  plugins and use them (which means they appear in the menus) without
438  restarting the program.
439  """
440  logging.debug('Application: _fillEditMenu()')
441  self._editMenuItems = {}
442  if not self._window.editMenu().isEmpty():
443  self._window.editMenu().clear()
444 
445  # Undo / Redo
446  self._editMenuItems["undoAction"] = self.createAction("Undo", self.undoEvent, "Ctrl+Z", "edit-undo")
447  self._editMenuItems["redoAction"] = self.createAction("Redo", self.redoEvent, "Ctrl+Y", "edit-redo")
448  self._editMenuItems["undoAction"].setData(QVariant(1))
449  self._editMenuItems["redoAction"].setData(QVariant(1))
450  self._editMenuItems["undoAction"].setEnabled(False)
451  self._editMenuItems["redoAction"].setEnabled(False)
452  self._window.editMenu().addAction(self._editMenuItems["undoAction"])
453  self._window.editMenu().addAction(self._editMenuItems["redoAction"])
454  #self._editMenuItems["undoAction"].menu().addAction(self.createAction("test"))
455  #self._editMenuItems["undoAction"].menu().setEnabled(False)
456  #self._editMenuItems["undoAction"].menu().setVisible(False)
457 
458  self._undoActionsMenu = QMenu(self._window)
460  for i in range(0, self.MAX_VISIBLE_UNDO_EVENTS):
461  action = self.createAction("undo " + str(i), self.undoEvent)
462  action.setVisible(False)
463  self._undoActionsMenu.addAction(action)
464  self._undoMenuActions.append(action)
465 
466  self._redoActionsMenu = QMenu(self._window)
468  for i in range(0, self.MAX_VISIBLE_UNDO_EVENTS):
469  action = self.createAction("redo " + str(i), self.redoEvent)
470  action.setVisible(False)
471  self._redoActionsMenu.addAction(action)
472  self._redoMenuActions.append(action)
473 
474  # Cut
475  self._editMenuItems['cutAction'] = self.createAction('&Cut', self.cutEvent, 'Ctrl+X', 'editcut')
476  self._window.editMenu().addAction(self._editMenuItems['cutAction'])
477  self._editMenuItems['cutAction'].setEnabled(False)
478 
479  # Copy
480  self._editMenuItems['copyAction'] = self.createAction('C&opy', self.copyEvent, 'Ctrl+C', 'editcopy')
481  self._window.editMenu().addAction(self._editMenuItems['copyAction'])
482  self._editMenuItems['copyAction'].setEnabled(False)
483 
484  # Paste
485  self._editMenuItems['pasteAction'] = self.createAction('&Paste', self.pasteEvent, 'Ctrl+V', 'editpaste')
486  self._window.editMenu().addAction(self._editMenuItems['pasteAction'])
487  self._editMenuItems['pasteAction'].setEnabled(False)
488 
489  # Select all
490  self._editMenuItems['selectAllAction'] = self.createAction("Select &all", self.selectAllEvent, "Ctrl+A", "selectall")
491  self._window.editMenu().addAction(self._editMenuItems['selectAllAction'])
492  self._editMenuItems['selectAllAction'].setVisible(False)
493 
494  self._window.editMenu().addSeparator()
495 
496  # Find
497  self._editMenuItems['findAction'] = self.createAction('&Find', self.findEvent, 'Ctrl+F', "edit-find")
498  self._window.editMenu().addAction(self._editMenuItems['findAction'])
499  self._editMenuItems['findAction'].setEnabled(False)
500 
501  def _fillHelpMenu(self):
502  logging.debug('Application: _fillHelpMenu()')
503  self._helpMenuItems = {}
504 
505  # About
506  self._helpMenuItems['aboutAction'] = self.createAction('&About', self.aboutBoxSlot, 'F1')
507  self._window.helpMenu().addAction(self._helpMenuItems['aboutAction'])
508 
509  # open log file
510  if self._logFile:
511  self._helpMenuItems['openLogFile'] = self.createAction("Open log file", self.openLogFileSlot)
512  self._window.helpMenu().addAction(self._helpMenuItems['openLogFile'])
513 
514  # Offline Documentation
515  if os.path.exists(os.path.join(docDirectory,"index.html")):
516  self._window.helpMenu().addAction(self.createAction('Offline Documentation', self._openDocumentation, "CTRL+F1"))
517 
518  # Vispa Website
519  self._window.helpMenu().addAction(self.createAction('Website', self._openWebsite, "Shift+F1"))
520 
521  def updateMenu(self):
522  """ Update recent files and enable disable menu entries in file and edit menu.
523  """
524  #logging.debug('Application: updateMenu()')
525  if self.mainWindow().startupScreen():
526  self.updateStartupScreen()
527  # Recent files
528  num_recent_files = min(len(self._recentFiles), self.MAX_VISIBLE_RECENT_FILES)
529  for i in range(0, num_recent_files):
530  filename = self._recentFiles[i]
531  self._recentFilesMenuActions[i].setText(os.path.basename(filename))
532  self._recentFilesMenuActions[i].setToolTip(filename)
533  self._recentFilesMenuActions[i].setStatusTip(filename)
534  self._recentFilesMenuActions[i].setData(QVariant(filename))
535  self._recentFilesMenuActions[i].setVisible(True)
536 
537  for i in range(num_recent_files, self.MAX_VISIBLE_RECENT_FILES):
538  self._recentFilesMenuActions[i].setVisible(False)
539 
540  if num_recent_files == 0:
541  self._fileMenuItems['clearRecentFilesAction'].setEnabled(False)
542  self._fileMenuItems['clearMissingRecentFilesAction'].setEnabled(False)
543  else:
544  self._fileMenuItems['clearRecentFilesAction'].setEnabled(True)
545  self._fileMenuItems['clearMissingRecentFilesAction'].setEnabled(True)
546 
547  # Enabled / disable menu entries depending on number of open files
548  at_least_one_flag = False
549  at_least_two_flag = False
550  if len(self.tabControllers()) > 1:
551  at_least_one_flag = True
552  at_least_two_flag = True
553  elif len(self.tabControllers()) > 0:
554  at_least_one_flag = True
555 
556  self._fileMenuItems['saveFileAction'].setEnabled(at_least_one_flag)
557  self._fileMenuItems['saveFileAsAction'].setEnabled(at_least_one_flag)
558  self._fileMenuItems['reloadFileAction'].setEnabled(at_least_one_flag)
559  self._fileMenuItems['closeFileAction'].setEnabled(at_least_one_flag)
560 
561  self._fileMenuItems['saveAllFilesAction'].setEnabled(at_least_two_flag)
562  self._fileMenuItems['closeAllAction'].setEnabled(at_least_two_flag)
563 
564  try:
565  if at_least_one_flag:
566  if not self.currentTabController().isEditable():
567  self._fileMenuItems['saveFileAction'].setEnabled(False)
568  self._fileMenuItems['saveFileAsAction'].setEnabled(False)
569  if not self.currentTabController().isModified():
570  self._fileMenuItems['saveFileAction'].setEnabled(False)
571 
572  # Copy / Cut / Paste
573  copy_paste_enabled_flag = at_least_one_flag and self.currentTabController().isCopyPasteEnabled()
574  self._editMenuItems['cutAction'].setEnabled(copy_paste_enabled_flag)
575  self._editMenuItems['copyAction'].setEnabled(copy_paste_enabled_flag)
576  self._editMenuItems['pasteAction'].setEnabled(copy_paste_enabled_flag)
577 
578  self._editMenuItems['selectAllAction'].setVisible(self.currentTabController().allowSelectAll())
579 
580  self._editMenuItems['findAction'].setEnabled(at_least_one_flag and self.currentTabController().isFindEnabled())
581 
582  # Undo / Redo
583  undo_supported_flag = at_least_one_flag and self.currentTabController().supportsUndo()
584  self._editMenuItems["undoAction"].setEnabled(undo_supported_flag)
585  self._editMenuItems["undoAction"].setVisible(undo_supported_flag)
586  self._editMenuItems["redoAction"].setEnabled(undo_supported_flag)
587  self._editMenuItems["redoAction"].setVisible(undo_supported_flag)
588  self.showPluginToolBar(self._undoToolBar, undo_supported_flag)
589 
590  if undo_supported_flag:
591  undo_events = self.currentTabController().undoEvents()
592  num_undo_events = min(len(undo_events), self.MAX_VISIBLE_UNDO_EVENTS)
593  self._editMenuItems["undoAction"].setEnabled(num_undo_events > 0)
594  if num_undo_events > 1:
595  self._editMenuItems["undoAction"].setMenu(self._undoActionsMenu)
596  else:
597  self._editMenuItems["undoAction"].setMenu(None)
598  for i in range(0, num_undo_events):
599  undo_event = undo_events[num_undo_events - i - 1] # iterate backwards
600  self._undoMenuActions[i].setText(undo_event.LABEL)
601  self._undoMenuActions[i].setToolTip(undo_event.description())
602  self._undoMenuActions[i].setStatusTip(undo_event.description())
603  self._undoMenuActions[i].setData(QVariant(i+1))
604  self._undoMenuActions[i].setVisible(True)
605  for i in range(num_undo_events, self.MAX_VISIBLE_UNDO_EVENTS):
606  self._undoMenuActions[i].setVisible(False)
607 
608  redo_events = self.currentTabController().redoEvents()
609  num_redo_events = min(len(redo_events), self.MAX_VISIBLE_UNDO_EVENTS)
610  self._editMenuItems["redoAction"].setEnabled(num_redo_events > 0)
611  if num_redo_events > 1:
612  self._editMenuItems["redoAction"].setMenu(self._redoActionsMenu)
613  else:
614  self._editMenuItems["redoAction"].setMenu(None)
615  for i in range(0, num_redo_events):
616  redo_event = redo_events[num_redo_events - i - 1] # iterate backwards
617  self._redoMenuActions[i].setText(redo_event.LABEL)
618  self._redoMenuActions[i].setToolTip(redo_event.description())
619  self._redoMenuActions[i].setStatusTip(redo_event.description())
620  self._redoMenuActions[i].setData(QVariant(i+1))
621  self._redoMenuActions[i].setVisible(True)
622  for i in range(num_redo_events, self.MAX_VISIBLE_UNDO_EVENTS):
623  self._redoMenuActions[i].setVisible(False)
624 
625  except NoCurrentTabControllerException:
626  pass
627 
629  """ Opens Vispa Offline Documentation
630  """
631  webbrowser.open(os.path.join(docDirectory,"index.html"), 2, True)
632 
633  def _openWebsite(self):
634  """ Open new browser tab and opens Vispa Project Website.
635  """
636  webbrowser.open(websiteUrl, 2, True)
637 
638  def createPluginMenu(self, name):
639  """ Creates menu in main window's menu bar before help menu and adds it to _pluginMenus list.
640  """
641  menu = QMenu(name)
642  self._window.menuBar().insertMenu(self._window.helpMenu().menuAction(), menu)
643  self._pluginMenus.append(menu)
644  return menu
645 
646  def showPluginMenu(self, menuObject, show=True):
647  """ Shows given menu if it is in _pluginMenus list.
648  """
649  #logging.debug(self.__class__.__name__ +": showPluginMenu()")
650  if menuObject in self._pluginMenus:
651  # show all actions and activate their shortcuts
652  if show:
653  for action in menuObject.actions():
654  if hasattr(action,"_wasVisible") and action._wasVisible!=None:
655  action.setVisible(action._wasVisible)
656  action._wasVisible=None
657  else:
658  action.setVisible(True) # has to be after actions() loop to prevent permanant invisibility on Mac OS X
659  menuObject.menuAction().setVisible(show)
660 
661  def hidePluginMenu(self, menuObject):
662  """ Hides given menu object if it it in _pluginMenus list.
663  """
664  self.showPluginMenu(menuObject, False)
665 
666  def hidePluginMenus(self):
667  """ Hides all menus in _pluginMenus list.
668  """
669  for menuObject in self._pluginMenus:
670  # hide all actions and deactivate their shortcuts
671  menuObject.menuAction().setVisible(False)
672  for action in menuObject.actions():
673  if not hasattr(action,"_wasVisible") or action._wasVisible==None:
674  action._wasVisible=action.isVisible()
675  action.setVisible(False) # setVisible() here hides plugin menu forever on Mac OS X (10.5.7), Qt 4.5.
676 
677  def createPluginToolBar(self, name):
678  """ Creates tool bar in main window and adds it to _pluginToolBars list.
679  """
680  toolBar = self._window.addToolBar(name)
681  self._pluginToolBars.append(toolBar)
682  return toolBar
683 
684  def showPluginToolBar(self, toolBarObject, show=True):
685  """ Shows given toolbar if it is in _pluginToolBars list.
686  """
687  if toolBarObject in self._pluginToolBars:
688  toolBarObject.setVisible(show)
689 
690  def hidePluginMenu(self, toolBarObject):
691  """ Hides given toolbar object if it it in _pluginToolBars list.
692  """
693  self.showPluginToolBar(toolBarObject, False)
694  #if toolBarObject in self._pluginToolBars:
695  # toolBarObject.menuAction().setVisible(False)
696 
698  """ Hides all toolbars in _toolBarMenus list.
699  """
700  for toolBar in self._pluginToolBars:
701  toolBar.hide()
702 
703  def createZoomToolBar(self):
704  """ Creates tool bar with three buttons "user", "100 %" and "all".
705 
706  See TabController's documentation of zoomUser(), zoomHundred() and zoomAll() to find out more on the different zoom levels.
707  """
708  self._zoomToolBar = self.createPluginToolBar('Zoom ToolBar')
709  self._zoomToolBar.addAction(self.createAction('Revert Zoom', self.zoomUserEvent, image='zoomuser'))
710  self._zoomToolBar.addAction(self.createAction('Zoom to 100 %', self.zoomHundredEvent, image='zoom100'))
711  self._zoomToolBar.addAction(self.createAction('Zoom to all', self.zoomAllEvent, image='zoomall'))
712 
713  def showZoomToolBar(self):
714  """ Makes zoom tool bar visible.
715 
716  Should be called from TabController's selected() function, if the controller wants to use the tool bar.
717  """
719 
720  def hideZoomToolBar(self):
721  """ Makes zoom tool bar invisible.
722  """
723  self._zoomToolBar.hide()
724 
725  def createUndoToolBar(self):
726  """ Creates tool bar with buttons to invoke undo and redo events.
727 
728  Needs to be called after _fillEditMenu() as actions are defined there.
729  """
730  self._undoToolBar = self.createPluginToolBar("Undo ToolBar")
731  self._undoToolBar.addAction(self._editMenuItems["undoAction"])
732  self._undoToolBar.addAction(self._editMenuItems["redoAction"])
733 
734  def showUndoToolBar(self):
735  """ Makes undo tool bar visible.
736  """
738 
739  def hideUndoToolBar(self):
740  """ Hides undo tool bar.
741  """
742  self._undoToolBar.hide()
743 
744  def clearRecentFiles(self):
745  """ Empties list of recent files and updates main menu.
746  """
747  self._recentFiles = []
748  self._saveIni()
749  self.updateMenu()
750 
752  """ Removes entries from recent files menu if file does no longer exist.
753  """
754  newList = []
755  for file in self._recentFiles:
756  if os.path.exists(file):
757  newList.append(file)
758  self._recentFiles = newList
759  self._saveIni()
760  self.updateMenu()
761 
762  def addRecentFile(self, filename):
763  """ Adds given filename to list of recent files.
764  """
765  logging.debug('Application: addRecentFile() - ' + filename)
766  if isinstance(filename, QString):
767  filename = str(filename) # Make sure filename is a python string not a QString
768  leftCount = self.MAX_RECENT_FILES - 1
769  if filename in self._recentFiles:
770  del self._recentFiles[self._recentFiles.index(filename)]
771  self._recentFiles = [filename] + self._recentFiles[:leftCount]
772  self._saveIni()
773 
774  def recentFiles(self):
775  """ Returns list of recently opened files.
776  """
777  return self._recentFiles
778 
780  """ Returns directory name of first entry of recent files list.
781  """
782  # if current working dir is vispa directory use recentfile or home
783  if os.path.abspath(os.getcwd()) in [os.path.abspath(baseDirectory),os.path.abspath(os.path.join(baseDirectory,"bin"))] or platform.system() == "Darwin":
784  if len(self._recentFiles) > 0:
785  return os.path.dirname(self._recentFiles[0])
786  elif platform.system() == "Darwin":
787  # Mac OS X
788  return homeDirectory + "/Documents"
789  else:
790  return homeDirectory
791  # if user navigated to another directory use this
792  else:
793  return os.getcwd()
794 
795  def recentFilesFromPlugin(self,plugin):
796  files=[]
797  filetypes = plugin.filetypes()
798  extension=None
799  if len(filetypes) > 0:
800  extension=filetypes[0].extension().lower()
801  for file in self._recentFiles:
802  if os.path.splitext(os.path.basename(file))[1][1:].lower()==extension:
803  files+=[file]
804  return files
805 
807  screen=self.mainWindow().startupScreen()
808  screen.analysisDesignerRecentFilesList().clear()
809  screen.analysisDesignerRecentFilesList().addItem("...")
810  screen.analysisDesignerRecentFilesList().setCurrentRow(0)
811  plugin=self.plugin("AnalysisDesignerPlugin")
812  if plugin:
813  files = self.recentFilesFromPlugin(plugin)
814  for file in files:
815  screen.analysisDesignerRecentFilesList().addItem(os.path.basename(file))
816 
817  screen.pxlEditorRecentFilesList().clear()
818  screen.pxlEditorRecentFilesList().addItem("...")
819  screen.pxlEditorRecentFilesList().setCurrentRow(0)
820  plugin=self.plugin("PxlPlugin")
821  if plugin:
822  files = self.recentFilesFromPlugin(plugin)
823  for file in files:
824  screen.pxlEditorRecentFilesList().addItem(os.path.basename(file))
825 
826  def exit(self):
827  self._window.close()
828 
829  def shutdownPlugins(self):
830  logging.debug('Application: shutting down plugins' )
831  for plugin in self._plugins:
832  plugin.shutdown()
833 
834 
836  """ Loop over all plugins and remember their file extensions.
837  """
839  self._knownFiltersList = []
840  self._knownFiltersList.append('All files (*.*)')
841  for plugin in self.plugins():
842  for ft in plugin.filetypes():
843  self._knownExtensionsDictionary[ft.extension()] = plugin
844  self._knownFiltersList.append(ft.fileDialogFilter())
845  if len(self._knownFiltersList) > 0:
846  allKnownFilter = 'All known files (*.' + " *.".join(self._knownExtensionsDictionary.keys()) + ')'
847  self._knownFiltersList.insert(1, allKnownFilter)
848  logging.debug('Application: _collectFileExtensions() - ' + allKnownFilter)
849  else:
850  logging.debug('Application: _collectFileExtensions()')
851 
852 
853  def openFileDialog(self, defaultFileFilter=None):
854  """Displays a common open dialog for all known file types.
855  """
856  logging.debug('Application: openFileDialog()')
857 
858  if not defaultFileFilter:
859  if len(self._knownFiltersList) > 1:
860  # Set defaultFileFilter to all known files
861  defaultFileFilter = self._knownFiltersList[1]
862  else:
863  # Set dfaultFileFilter to any file type
864  defaultFileFilter = self._knownFiltersList[0]
865 
866  # Dialog
867  filename = QFileDialog.getOpenFileName(
868  self._window,
869  'Select a file',
870  self.getLastOpenLocation(),
871  ";;".join(self._knownFiltersList),
872  defaultFileFilter)
873  if not filename.isEmpty():
874  self.openFile(filename)
875 
876  def openFile(self, filename):
877  """ Decides which plugin should handle opening of the given file name.
878  """
879  logging.debug('Application: openFile()')
880  statusMessage = self.startWorking("Opening file " + filename)
881  if isinstance(filename, QString):
882  filename = str(filename) # convert QString to Python String
883 
884  # Check whether file is already opened
885  for controller in self.tabControllers():
886  if filename == controller.filename():
887  self.setCurrentTabController(controller)
888  self.stopWorking(statusMessage, "already open")
889  return
890 
891  baseName = os.path.basename(filename)
892  ext = os.path.splitext(baseName)[1].lower().strip(".")
893  errormsg = None
894 
895  if self._knownExtensionsDictionary == {}:
897 
898  foundCorrectPlugin = False
899  if os.path.exists(filename):
900  if ext in self._knownExtensionsDictionary:
901  foundCorrectPlugin = True
902  try:
903  if self._knownExtensionsDictionary[ext].openFile(filename):
904  self.addRecentFile(filename)
905  else:
906  logging.error(self.__class__.__name__ + ": openFile() - Error while opening '" + str(filename) + "'.")
907  self.errorMessage("Failed to open file.")
908  except Exception:
909  logging.error(self.__class__.__name__ + ": openFile() - Error while opening '" + str(filename) + "' : " + exception_traceback())
910  self.errorMessage("Exception while opening file. See log for details.")
911 
912  if not foundCorrectPlugin:
913  errormsg = 'Unknown file type (.' + ext + '). Aborting.'
914  else:
915  errormsg = 'File does not exist: ' + filename
916 
917  self.updateMenu()
918 
919  # Error messages
920  if not errormsg:
921  self.stopWorking(statusMessage)
922  else:
923  logging.error(errormsg)
924  self.stopWorking(statusMessage, "failed")
925  self.warningMessage(errormsg)
926 
927  def reloadFile(self):
928  """ Tells current tab controller to refresh.
929  """
930  logging.debug('Application: reloadFile()')
931  try:
932  if self.currentTabController().filename() and self.currentTabController().allowClose():
933  self.currentTabController().refresh()
934  self.currentTabController().setModified(False)
935  except NoCurrentTabControllerException:
936  pass
937  # call tabChanged instead of updateMenu to be qt 4.3 compatible
938  self.tabChanged()
939 
940  def closeFile(self):
941  """ Tells current tab controller to close.
942  """
943  logging.debug('Application: closeCurrentFile()')
944  try:
945  self.currentTabController().close()
946  except NoCurrentTabControllerException:
947  pass
948  # call tabChanged instead of updateMenu to be qt 4.3 compatible
949  self.tabChanged()
950 
951  def closeAllFiles(self):
952  """ Closes all open tabs unless user aborts closing.
953  """
954  logging.debug('Application: closeAllFiles()')
955  # to prevent unneeded updates set flag
956  self._closeAllFlag = True
957  while len(self.tabControllers())>0:
958  controller=self.tabControllers()[0]
959  if controller.close() == False:
960  break
961  self._closeAllFlag = False
962 
963  # call tabChanged instead of updateMenu to be qt 4.3 compatible
964  self.tabChanged()
965 
966  def saveFile(self):
967  """ Tells current tab controller to save its file.
968  """
969  logging.debug('Application: saveFile()')
970  try:
971  self.currentTabController().save()
972  except NoCurrentTabControllerException:
973  logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
974 
975  def saveFileAsDialog(self):
976  """This functions asks the user for a file name.
977  """
978  logging.debug('Application: saveFileAsDialog()')
979  try:
980  currentTabController = self.currentTabController()
981  except NoCurrentTabControllerException:
982  logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
983  return
984 
985  if currentTabController.filename():
986  startDirectory = currentTabController.filename()
987  else:
988  startDirectory = self.getLastOpenLocation()
989 
990  filetypesList = []
991  for filetype in currentTabController.supportedFileTypes():
992  filetypesList.append(Filetype(filetype[0], filetype[1]).fileDialogFilter())
993  filetypesList.append('Any (*.*)')
994 
995  selectedFilter = QString("")
996  filename = str(QFileDialog.getSaveFileName(
997  self._window,
998  'Select a file',
999  startDirectory,
1000  ";;".join(filetypesList), selectedFilter))
1001  if filename != "":
1002  # add extension if necessary
1003  if os.path.splitext(filename)[1].strip(".") == "" and str(selectedFilter) != 'Any (*.*)':
1004  ext = currentTabController.supportedFileTypes()[filetypesList.index(str(selectedFilter))][0]
1005  filename = os.path.splitext(filename)[0] + "." + ext
1006  return currentTabController.save(filename)
1007  return False
1008 
1009  def saveAllFiles(self):
1010  """ Tells tab controllers of all tabs to save.
1011  """
1012  logging.debug('Application: saveAllFiles()')
1013 
1014  for controller in self.tabControllers():
1015  if controller.filename() or controller == self.currentTabController():
1016  controller.save()
1017 
1018  def cutEvent(self):
1019  """ Called when cut action is triggered (e.g. from menu entry) and forwards it to current tab controller.
1020  """
1021  try:
1022  self.currentTabController().cut()
1023  except NoCurrentTabControllerException:
1024  logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
1025 
1026  def copyEvent(self):
1027  """ Called when copy action is triggered (e.g. from menu entry) and forwards it to current tab controller.
1028  """
1029  try:
1030  self.currentTabController().copy()
1031  except NoCurrentTabControllerException:
1032  logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
1033 
1034  def pasteEvent(self):
1035  """ Called when paste action is triggered (e.g. from menu entry) and forwards it to current tab controller.
1036  """
1037  try:
1038  self.currentTabController().paste()
1039  except NoCurrentTabControllerException:
1040  logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
1041 
1042  def selectAllEvent(self):
1043  """ Called when selectAll action is triggered (e.g. from menu entry) and forwards it to current tab controller.
1044  """
1045  try:
1046  self.currentTabController().selectAll()
1047  except NoCurrentTabControllerException:
1048  logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
1049 
1050  def findEvent(self):
1051  """ Called when find action is triggered (e.g. from menu entry) and forwards it to current tab controller.
1052  """
1053  try:
1054  self.currentTabController().find()
1055  except NoCurrentTabControllerException:
1056  logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
1057 
1058  def zoomUserEvent(self):
1059  """ Handles button pressed event from zoom tool bar and forwards it to current tab controller.
1060  """
1061  try:
1062  self.currentTabController().zoomUser()
1063  except NoCurrentTabControllerException:
1064  logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
1065 
1066  def zoomHundredEvent(self):
1067  """ Handles button pressed event from zoom tool bar and forwards it to current tab controller.
1068  """
1069  try:
1070  self.currentTabController().zoomHundred()
1071  except NoCurrentTabControllerException:
1072  logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
1073 
1074  def zoomAllEvent(self):
1075  """ Handles button pressed event from zoom tool bar and forwards it to current tab controller.
1076  """
1077  try:
1078  self.currentTabController().zoomAll()
1079  except NoCurrentTabControllerException:
1080  logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
1081 
1082  def undoEvent(self):
1083  """ Handles undo action for buttons in undo tool bar and edit menu.
1084  """
1085  try:
1086  num = 1
1087  sender = self.sender()
1088  if sender:
1089  num = sender.data().toInt()
1090  if len(num) > 1:
1091  # strange: toInt returns tuple like (1, True), QT 4.6.0, Mac OS X 10.6.4, 2010-06-28
1092  num = num[0]
1093  self.currentTabController().undo(num)
1094  except NoCurrentTabControllerException:
1095  logging.warning(self.__class__.__name__ + ": "+ self.TAB_PREMATURELY_CLOSED_WARNING)
1096 
1097  def redoEvent(self):
1098  """ Handles redo action for buttons in undo tool bar and edit menu.
1099  """
1100  try:
1101  num = 1
1102  sender = self.sender()
1103  if sender:
1104  num = sender.data().toInt()
1105  if len(num) > 1:
1106  # strange: toInt returns tuple like (1, True), QT 4.6.0, Mac OS X 10.6.4, 2010-06-28
1107  num = num[0]
1108  self.currentTabController().redo(num)
1109  except NoCurrentTabControllerException:
1110  logging.warning(self.__class__.__name__ + ": "+ self.TAB_PREMATURELY_CLOSED_WARNING)
1111 
1112  def aboutBoxSlot(self):
1113  """ Displays about box.
1114  """
1115  logging.debug('Application: aboutBoxSlot()')
1116  about = AboutDialog(self)
1117  about.onScreen()
1118 
1119  def openLogFileSlot(self):
1120  if self._logFile:
1121  self.doubleClickOnFile(self._logFile)
1122  else:
1123  logging.warning("%s: openLogFileSlot(): _logFile not set. Aborting..." % self.__class__.__name__)
1124 
1126  """ Slot for opening recent file.
1127 
1128  Called from recent file menu action. Filename is set as data object (QVariant) of action.
1129  """
1130  filename = self.sender().data().toString()
1131  logging.debug('Application: openRecentFileSlot() - ' + filename)
1132  self.openFile(filename)
1133 
1134  def tabChanged(self, tab=None):
1135  """ when a different tab is activated update menu
1136  """
1137  logging.debug('Application: tabChanged()')
1138  # only update once when closing all files at once
1139  if not self._closeAllFlag:
1140  self.hidePluginMenus()
1141  self.hidePluginToolBars()
1142  self.updateWindowTitle()
1143  self.updateMenu()
1144  try:
1145  self.currentTabController().activated()
1146  self.currentTabController().checkModificationTimestamp()
1147  except NoCurrentTabControllerException:
1148  pass
1149 
1150  self.mainWindow().setStartupScreenVisible(self.mainWindow().tabWidget().count() == 0)
1151 
1153  """ Update menu and window title.
1154  """
1155  self.updateMenu()
1156  self.updateWindowTitle()
1157 
1158  def windowTitle(self):
1159  return str(self._window.windowTitle()).split("-")[0].strip()
1160 
1162  """ update window caption
1163  """
1164  #logging.debug('Application: updateWindowTitle()')
1165  name = self.windowTitle()
1166 
1167  try:
1168  filename = self.currentTabController().filename()
1169  except NoCurrentTabControllerException:
1170  filename = None
1171 
1172  if filename:
1173  dirName = os.path.dirname(sys.argv[0])
1174  if os.path.abspath(dirName) in filename:
1175  filename = filename[len(os.path.abspath(dirName)) + 1:]
1176  name = name + " - " + filename
1177  self._window.setWindowTitle(name)
1178 
1179  def ini(self):
1180  if not self._ini:
1181  self._ini = ConfigParser.ConfigParser()
1182  self._ini.read(self._iniFileName)
1183  return self._ini
1184 
1185  def writeIni(self):
1186  try:
1187  configfile = open(self._iniFileName, "w")
1188  self._ini.write(configfile)
1189  configfile.close()
1190  except IOError:
1191  pass
1192  self._ini = None
1193 
1194  def _loadIni(self):
1195  """ Save the list of recent files.
1196  """
1197  logging.debug('Application: _loadIni()')
1198  ini = self.ini()
1199  self._recentFiles = []
1200  if ini.has_section("history"):
1201  for i in range(0, self.MAX_RECENT_FILES):
1202  if ini.has_option("history", str(i)):
1203  self._recentFiles+=[ini.get("history", str(i))]
1204 
1205  def _saveIni(self):
1206  """ Load the list of recent files.
1207  """
1208  logging.debug('Application: _saveIni()')
1209  ini = self.ini()
1210  if ini.has_section("history"):
1211  ini.remove_section("history")
1212  ini.add_section("history")
1213  for i in range(len(self._recentFiles)):
1214  ini.set("history", str(i), self._recentFiles[i])
1215 
1216  self.writeIni()
1217 
1218  def errorMessage(self, message):
1219  """ Displays error message.
1220  """
1221  QMessageBox.critical(self.mainWindow(), 'Error', message)
1222 
1223  def warningMessage(self, message):
1224  """ Displays warning message.
1225  """
1226  QMessageBox.warning(self.mainWindow(), 'Warning', message)
1227 
1228  def infoMessage(self, message):
1229  """ Displays info message.
1230  """
1231  QMessageBox.about(self.mainWindow(), 'Info', message)
1232 
1233  def showMessageBox(self, text, informativeText="", standardButtons=QMessageBox.Ok | QMessageBox.Cancel | QMessageBox.Ignore, defaultButton=QMessageBox.Ok, extraButtons=None):
1234  """ Shows a standardized message box and returns the pressed button.
1235 
1236  See documentation on Qt's QMessageBox for a list of possible standard buttons.
1237  """
1238 
1239  msgBox = QMessageBox(self.mainWindow())
1240  msgBox.setParent(self.mainWindow(), Qt.Sheet) # Qt.Sheet: Indicates that the widget is a Macintosh sheet.
1241  msgBox.setText(text)
1242  msgBox.setInformativeText(informativeText)
1243  msgBox.setStandardButtons(standardButtons)
1244  if extraButtons!=None:
1245  for button,role in extraButtons:
1246  msgBox.addButton(button,role)
1247  msgBox.setDefaultButton(defaultButton)
1248  return msgBox.exec_()
1249 
1250  def doubleClickOnFile(self, filename):
1251  """ Opens file given as argument if possible in Vispa.
1252 
1253  If Vispa cannot handle the file type the file will be opened in it's default application.
1254  """
1255  logging.debug(self.__class__.__name__ + ": doubleClickOnFile() - " + str(filename))
1256 
1257  if filename == "":
1258  return
1259 
1260  baseName = os.path.basename(filename)
1261  ext = os.path.splitext(baseName)[1].lower().strip(".")
1262  if self._knownExtensionsDictionary == {}:
1263  self._collectFileExtensions()
1264  if os.path.exists(filename):
1265  if ext in self._knownExtensionsDictionary:
1266  return self.openFile(filename)
1267 
1268  # open file in default application
1269  try:
1270  if 'Windows' in platform.system():
1271  os.startfile(filename)
1272  elif 'Darwin' in platform.system():
1273  if os.access(filename, os.X_OK):
1274  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.")
1275  else:
1276  subprocess.call(("open", filename))
1277  elif 'Linux' in platform.system():
1278  # Linux comes with many Desktop Enviroments
1279  if os.access(filename, os.X_OK):
1280  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.")
1281  else:
1282  try:
1283  #Freedesktop Standard
1284  subprocess.call(("xdg-open", filename))
1285  except:
1286  try:
1287  subprocess.call(("gnome-open", filename))
1288  except:
1289  logging.error(self.__class__.__name__ + ": doubleClickOnFile() - Platform '" + platform.platform() + "'. Cannot open file. I Don't know how!")
1290  except:
1291  logging.error(self.__class__.__name__ + ": doubleClickOnFile() - Platform '" + platform.platform() + "'. Error while opening file: " + str(filename))
1292 
1293  def createStatusBar(self):
1295 
1296  self._progressWidget = RotatingIcon(":/resources/vispabutton.png")
1297  self._window.statusBar().addPermanentWidget(self._progressWidget)
1298 
1299  def startWorking(self, message=""):
1300  if len(self._workingMessages) == 0:
1301  self._progressWidget.start()
1302  self._window.statusBar().showMessage(message + "...")
1303  self._messageId+=1
1304  self._workingMessages[self._messageId] = message
1305  self._progressWidget.setToolTip(message)
1306  return self._messageId
1307 
1308  def stopWorking(self, id, end="done"):
1309  if not id in self._workingMessages.keys():
1310  logging.error(self.__class__.__name__ +": stopWorking() - Unknown id %s. Aborting..." % str(id))
1311  return
1312  if len(self._workingMessages) > 1:
1313  self._window.statusBar().showMessage(self._workingMessages[self._workingMessages.keys()[0]] + "...")
1314  self._progressWidget.setToolTip(self._workingMessages[self._workingMessages.keys()[0]])
1315  else:
1316  self._progressWidget.stop()
1317  self._progressWidget.setToolTip("")
1318  self._window.statusBar().showMessage(self._workingMessages[id] + "... " + end + ".")
1319  del self._workingMessages[id]
1320 
1321  def tabCloseRequest(self, i):
1322  self.mainWindow().tabWidget().setCurrentIndex(i)
1323  self.closeFile()
1324 
1325  def showStatusMessage(self, message, timeout=0):
1326  self._window.statusBar().showMessage(message, timeout)
1327 
1328  def cancel(self):
1329  """ Cancel operations in current tab.
1330  """
1331  logging.debug(__name__ + ": cancel")
1332  try:
1333  self.currentTabController().cancel()
1334  except NoCurrentTabControllerException:
1335  pass
std::vector< std::string_view > split(std::string_view, const char *)
def doubleClickOnFile(self, filename)
def showPluginMenu(self, menuObject, show=True)
Definition: Application.py:646
def showPluginToolBar(self, toolBarObject, show=True)
Definition: Application.py:684
def openFileDialog(self, defaultFileFilter=None)
Definition: Application.py:853
def atLeastQtVersion(self, versionString)
Definition: Application.py:100
void find(edm::Handle< EcalRecHitCollection > &hits, DetId thisDet, std::vector< EcalRecHitCollection::const_iterator > &hit, bool debug=false)
Definition: FindCaloHit.cc:19
def createAction(self, name, slot=None, shortcut=None, image=None, enabled=True)
Definition: Application.py:329
def _checkFile(self, filename)
Definition: Application.py:158
def stopWorking(self, id, end="done")
void clear(CLHEP::HepGenMatrix &m)
Helper function: Reset all elements of a matrix to 0.
Definition: matutil.cc:151
std::string toString(const char *format,...)
Definition: xdaq_compat.cc:4
def setCurrentTabController(self, controller)
Definition: Application.py:303
T min(T a, T b)
Definition: MathUtil.h:58
def recentFilesFromPlugin(self, plugin)
Definition: Application.py:795
def showStatusMessage(self, message, timeout=0)
def startWorking(self, message="")
static std::string join(char **cmd)
Definition: RemoteFile.cc:17
char data[epos_bytes_allocation]
Definition: EPOS_Wrapper.h:79
def showMessageBox(self, text, informativeText="", standardButtons=QMessageBox.Ok|QMessageBox.Cancel|QMessageBox.Ignore, defaultButton=QMessageBox.Ok, extraButtons=None)
def addRecentFile(self, filename)
Definition: Application.py:762
#define str(s)
save
Definition: cuy.py:1165
def hidePluginMenu(self, menuObject)
Definition: Application.py:661