class Eddy(QtWidgets.QApplication): """ This class implements the main QtCore.Qt application. """ RestartCode = 8 sgnCreateSession = QtCore.pyqtSignal(str) sgnSessionCreated = QtCore.pyqtSignal('QMainWindow') sgnSessionClosed = QtCore.pyqtSignal('QMainWindow') def __init__(self, argv): """ Initialize Eddy. :type argv: list """ super().__init__(argv) self.openFilePath = None self.server = None self.sessions = DistinctList() self.started = False self.welcome = None # APPLICATION INFO self.setDesktopFileName('{}.{}'.format(ORGANIZATION_REVERSE_DOMAIN, APPNAME)) self.setOrganizationName(ORGANIZATION) self.setOrganizationDomain(ORGANIZATION_DOMAIN) self.setApplicationName(APPNAME) self.setApplicationDisplayName(APPNAME) self.setApplicationVersion(VERSION) # PARSE COMMAND LINE ARGUMENTS self.options = CommandLineParser() self.options.process(argv) # CHECK FOR A RUNNING INSTANCE self.socket = QtNetwork.QLocalSocket() self.socket.connectToServer(APPID) self.running = self.socket.waitForConnected() if not self.isRunning(): QtNetwork.QLocalServer.removeServer(APPID) self.server = QtNetwork.QLocalServer() self.server.listen(APPID) self.socket = None connect(self.sgnCreateSession, self.doCreateSession) connect(self.aboutToQuit, self.onAboutToQuit) ############################################# # EVENTS ################################# def event(self, event): """ Executed when an event is received. :type event: T <= QEvent | QFileOpenEvent :rtype: bool """ # HANDLE FILEOPEN EVENT (TRIGGERED BY MACOS WHEN DOUBLE CLICKING A FILE) #if event.type() == QtCore.QEvent.FileOpen and not __debug__: if event.type() == QtCore.QEvent.FileOpen: path = expandPath(event.file()) #fileName,fileExtension = os.path.splitext(path) type = File.forPath(path) if type and type is File.Graphol: if fexists(path): if self.started: self.sgnCreateSession.emit(path) else: # CACHE PATH UNTIL APPLICATION STARTUP HAS COMPLETED self.openFilePath = path return super().event(event) ############################################# # INTERFACE ################################# def configure(self): """ Perform initial configuration tasks for Eddy to work properly. """ ############################################# # CONFIGURE FONTS ################################# fontDB = QtGui.QFontDatabase() fonts = QtCore.QDirIterator(':/fonts/') while fonts.hasNext(): fontDB.addApplicationFont(fonts.next()) # FONT SUBSTITUTIONS QtGui.QFont.insertSubstitution('Sans Serif', 'Roboto') QtGui.QFont.insertSubstitution('Monospace', 'Roboto Mono') # APPLICATION DEFAULT FONT self.setFont(Font('Roboto', pixelSize=12)) ############################################# # CONFIGURE LAYOUT ################################# style = EddyProxyStyle('Fusion') self.setStyle(style) self.setStyleSheet(style.stylesheet) ############################################# # DRAW THE SPLASH SCREEN ################################# splash = None if not self.options.isSet(CommandLineParser.NO_SPLASH): splash = Splash(mtime=2) splash.show() ############################################# # CONFIGURE RECENT PROJECTS ################################# settings = QtCore.QSettings() if not settings.contains('project/recent'): # From PyQt5 documentation: if the value of the setting is a container (corresponding # to either QVariantList, QVariantMap or QVariantHash) then the type is applied to the # contents of the container. So we can't use an empty list as default value because # PyQt5 needs to know the type of the contents added to the collection: we avoid # this problem by placing the list of example projects as recent project list. examples = list(filter(lambda path: fexists(path) and faccess(path), [ expandPath('@examples/Animals{}'.format(File.Graphol.extension)), expandPath('@examples/Diet{}'.format(File.Graphol.extension)), expandPath('@examples/Family{}'.format(File.Graphol.extension)), expandPath('@examples/LUBM{}'.format(File.Graphol.extension)), expandPath('@examples/Pizza{}'.format(File.Graphol.extension)), ])) settings.setValue('project/recent', examples) else: # If we have some projects in our recent list, check whether they exists on the # filesystem. If they do not exists we remove them from our recent list. projects = [] for path in map(expandPath, settings.value('project/recent', None, str) or []): if fexists(path) and path not in projects: projects.append(path) settings.setValue('project/recent', projects) settings.sync() ############################################# # LOOKUP PLUGINS ################################# PluginManager.scan('@plugins/', '@home/plugins/') ############################################# # CLOSE THE SPLASH SCREEN ################################# if splash and not self.options.isSet(CommandLineParser.NO_SPLASH): splash.sleep() splash.close() def isRunning(self): """ Returns True if there is already another instance of Eddy which is running, False otherwise. :rtype: bool """ return self.running def start(self): """ Run the application by showing the welcome dialog. """ # CONFIGURE THE WORKSPACE #settings = QtCore.QSettings() #workspace = expandPath(settings.value('workspace/home', WORKSPACE, str)) ''' if not isdir(workspace): window = WorkspaceDialog() if window.exec_() == WorkspaceDialog.Rejected: raise SystemExit ''' # PROCESS COMMAND LINE ARGUMENTS args = self.options.positionalArguments() if self.openFilePath: args.append(self.openFilePath) self.openFilePath = None # SHOW WELCOME DIALOG self.welcome = Welcome(self) self.welcome.show() # PROCESS ADDITIONAL COMMAND LINE OPTIONS if self.options.isSet(CommandLineParser.OPEN): value = self.options.value(CommandLineParser.OPEN) if value: project = os.path.join(workspace, value) if project and isdir(os.path.join(workspace, project)): self.sgnCreateSession.emit(project) else: LOGGER.warning('Unable to open project: %s', project) # POSITIONAL ARGUMENTS elif args: fname = expandPath(args[0]) if fexists(fname): #project = os.path.dirname(fname) project = fname self.sgnCreateSession.emit(project) else: LOGGER.warning('Unable to open file: %s', fname) # COMPLETE STARTUP self.started = True ############################################# # SLOTS ################################# @QtCore.pyqtSlot(str) def doCreateSession(self, path): """ Create a session using the given project path. :type path: str """ for session in self.sessions: # Look among the active sessions and see if we already have # a session loaded for the given project: if so, focus it. if session.project.path == path: session.show() break else: # If we do not have a session for the given project we'll create one. with BusyProgressDialog('Loading project: {0}'.format(path)): try: session = Session(self, path) except ProjectStopLoadingError: pass except (ProjectNotFoundError, ProjectNotValidError, ProjectVersionError) as e: LOGGER.warning('Failed to create session for project %s: %s', path, e) msgbox = QtWidgets.QMessageBox() msgbox.setIconPixmap(QtGui.QIcon(':/icons/48/ic_error_outline_black').pixmap(48)) msgbox.setText('Failed to create session for project: <b>{0}</b>!'.format(path)) msgbox.setTextFormat(QtCore.Qt.RichText) msgbox.setDetailedText(format_exception(e)) msgbox.setStandardButtons(QtWidgets.QMessageBox.Close) msgbox.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) msgbox.setWindowTitle('Project Error!') msgbox.exec_() except Exception as e: raise e else: ############################################# # UPDATE RECENT PROJECTS ################################# settings = QtCore.QSettings() projects = settings.value('project/recent', None, str) or [] try: projects.remove(path) except ValueError: pass finally: projects.insert(0, path) projects = projects[:8] settings.setValue('project/recent', projects) settings.sync() ############################################# # CLOSE THE WELCOME SCREEN IF NECESSARY ################################# try: self.welcome.close() except (AttributeError, RuntimeError): pass ############################################# # STARTUP THE SESSION ################################# connect(session.sgnQuit, self.doQuit) connect(session.sgnClosed, self.onSessionClosed) self.sessions.append(session) self.sgnSessionCreated.emit(session) session.show() @QtCore.pyqtSlot(str, str, str) def doCreateSessionFromScratch(self, projName, ontIri, ontPrefix): """ Create a session for a new brand project. """ for session in self.sessions: # Look among the active sessions and see if we already have # a session loaded for the given project: if so, focus it. if not session.project.path and session.project.name == projName and session.project.ontologyIRI == ontIri: session.show() break else: # If we do not have a session for the given project we'll create one. with BusyProgressDialog('Creating new project with name: {0}'.format(projName)): try: session = Session(self, path=None, projName=projName, ontIri=ontIri, ontPrefix=ontPrefix) except ProjectStopLoadingError: pass except Exception as e: LOGGER.warning('Failed to create session for new project : %s', e) msgbox = QtWidgets.QMessageBox() msgbox.setIconPixmap(QtGui.QIcon(':/icons/48/ic_error_outline_black').pixmap(48)) msgbox.setText('Failed to create session for new project') msgbox.setTextFormat(QtCore.Qt.RichText) msgbox.setDetailedText(format_exception(e)) msgbox.setStandardButtons(QtWidgets.QMessageBox.Close) msgbox.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) msgbox.setWindowTitle('Project Error!') msgbox.exec_() else: ############################################# # CLOSE THE WELCOME SCREEN IF NECESSARY ################################# try: self.welcome.close() except (AttributeError, RuntimeError): pass ############################################# # STARTUP THE SESSION ################################# connect(session.sgnQuit, self.doQuit) connect(session.sgnClosed, self.onSessionClosed) self.sessions.append(session) self.sgnSessionCreated.emit(session) session.show() @QtCore.pyqtSlot() def doQuit(self): """ Quit Eddy. """ for session in self.sessions: if not session.close(): return self.quit() @QtCore.pyqtSlot() def doRestart(self): """ Restart Eddy. """ for session in self.sessions: if not session.close(): return self.exit(Eddy.RestartCode) @QtCore.pyqtSlot() def doFocusSession(self): """ Make the session specified in the action data the application active window. """ action = self.sender() if isinstance(action, QtWidgets.QAction): session = action.data() if session in self.sessions: self.setActiveWindow(session) @QtCore.pyqtSlot() def doFocusNextSession(self): """ Make the next session the application active window. """ session = self.activeWindow() if session and session in self.sessions: nextSession = self.sessions[(self.sessions.index(session) + 1) % len(self.sessions)] self.setActiveWindow(nextSession) @QtCore.pyqtSlot() def doFocusPreviousSession(self): """ Make the previous session the application active window. """ session = self.activeWindow() if session and session in self.sessions: prevSession = self.sessions[(self.sessions.index(session) - 1) % len(self.sessions)] self.setActiveWindow(prevSession) @QtCore.pyqtSlot() def onAboutToQuit(self): """ Executed when the application is about to quit. """ if self.server: self.server.close() @QtCore.pyqtSlot() def onSessionClosed(self): """ Quit Eddy. """ # SAVE SESSION STATE session = self.sender() if session: # noinspection PyUnresolvedReferences session.save() self.sessions.remove(session) self.sgnSessionClosed.emit(session) # CLEANUP POSSIBLE LEFTOVERS self.sessions = DistinctList(filter(None, self.sessions)) # SWITCH TO AN ACTIVE WINDOW OR WELCOME PANEL if self.sessions: session = self.sessions[-1] session.show() else: self.welcome = Welcome(self) self.welcome.show()
class Eddy(QtWidgets.QApplication): """ This class implements the main QtCore.Qt application. """ sgnCreateSession = QtCore.pyqtSignal(str) def __init__(self, options, argv): """ Initialize Eddy. :type options: Namespace :type argv: list """ super().__init__(argv) self.server = None self.socket = QtNetwork.QLocalSocket() self.socket.connectToServer(APPID) self.running = self.socket.waitForConnected() self.sessions = DistinctList() self.welcome = None if not self.isRunning() or options.tests: self.server = QtNetwork.QLocalServer() self.server.listen(APPID) self.socket = None connect(self.sgnCreateSession, self.doCreateSession) ############################################# # INTERFACE ################################# def configure(self, options): """ Perform initial configuration tasks for Eddy to work properly. :type options: Namespace """ ############################################# # DRAW THE SPLASH SCREEN ################################# splash = None if not options.nosplash: splash = Splash(mtime=4) splash.show() ############################################# # CONFIGURE RECENT PROJECTS ################################# settings = QtCore.QSettings(ORGANIZATION, APPNAME) examples = [ expandPath('@examples/Animals'), expandPath('@examples/Diet'), expandPath('@examples/Family'), expandPath('@examples/LUBM'), expandPath('@examples/Pizza'), ] if not settings.contains('project/recent'): # From PyQt5 documentation: if the value of the setting is a container (corresponding # to either QVariantList, QVariantMap or QVariantHash) then the type is applied to the # contents of the container. So we can't use an empty list as default value because # PyQt5 needs to know the type of the contents added to the collection: we avoid # this problem by placing the list of example projects as recent project list. settings.setValue('project/recent', examples) else: # If we have some projects in our recent list, check whether they exists on the # filesystem. If they do not exists we remove them from our recent list. projects = [] for path in map(expandPath, settings.value('project/recent')): if isdir(path) and path not in projects: projects.append(path) settings.setValue('project/recent', projects or examples) settings.sync() ############################################# # CONFIGURE FONTS ################################# fontDB = QtGui.QFontDatabase() fontDB.addApplicationFont(':/fonts/Roboto-Black') fontDB.addApplicationFont(':/fonts/Roboto-BlackItalic') fontDB.addApplicationFont(':/fonts/Roboto-Bold') fontDB.addApplicationFont(':/fonts/Roboto-BoldItalic') fontDB.addApplicationFont(':/fonts/Roboto-Italic') fontDB.addApplicationFont(':/fonts/Roboto-Light') fontDB.addApplicationFont(':/fonts/Roboto-LightItalic') fontDB.addApplicationFont(':/fonts/Roboto-Medium') fontDB.addApplicationFont(':/fonts/Roboto-MediumItalic') fontDB.addApplicationFont(':/fonts/Roboto-Regular') fontDB.addApplicationFont(':/fonts/Roboto-Thin') fontDB.addApplicationFont(':/fonts/Roboto-ThinItalic') fontDB.addApplicationFont(':/fonts/RobotoCondensed-Bold') fontDB.addApplicationFont(':/fonts/RobotoCondensed-BoldItalic') fontDB.addApplicationFont(':/fonts/RobotoCondensed-Italic') fontDB.addApplicationFont(':/fonts/RobotoCondensed-Light') fontDB.addApplicationFont(':/fonts/RobotoCondensed-LightItalic') fontDB.addApplicationFont(':/fonts/RobotoCondensed-Regular') fontDB.addApplicationFont(':/fonts/RobotoMono-Bold') fontDB.addApplicationFont(':/fonts/RobotoMono-BoldItalic') fontDB.addApplicationFont(':/fonts/RobotoMono-Italic') fontDB.addApplicationFont(':/fonts/RobotoMono-Light') fontDB.addApplicationFont(':/fonts/RobotoMono-LightItalic') fontDB.addApplicationFont(':/fonts/RobotoMono-Medium') fontDB.addApplicationFont(':/fonts/RobotoMono-MediumItalic') fontDB.addApplicationFont(':/fonts/RobotoMono-Regular') fontDB.addApplicationFont(':/fonts/RobotoMono-Thin') fontDB.addApplicationFont(':/fonts/RobotoMono-ThinItalic') self.setFont(Font('Roboto', 12)) ############################################# # CONFIGURE LAYOUT ################################# buffer = '' resources = expandPath('@resources/styles/') for name in os.listdir(resources): path = os.path.join(resources, name) if fexists(path) and File.forPath(path) is File.Qss: buffer += fread(path) self.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps) self.setStyle(EddyProxyStyle('Fusion')) self.setStyleSheet(buffer) ############################################# # LOOKUP PLUGINS ################################# PluginManager.scan('@plugins/', '@home/plugins/') ############################################# # CLOSE THE SPLASH SCREEN ################################# if splash and not options.nosplash: splash.sleep() splash.close() ############################################# # CONFIGURE THE WORKSPACE ################################# workspace = expandPath(settings.value('workspace/home', WORKSPACE, str)) if not isdir(workspace): window = WorkspaceDialog() if window.exec_() == WorkspaceDialog.Rejected: raise SystemExit def isRunning(self): """ Returns True if there is already another instance of Eddy which is running, False otherwise. :rtype: bool """ return self.running def start(self, options): """ Run the application by showing the welcome dialog. :type options: Namespace """ self.welcome = Welcome(self) self.welcome.show() if options.open and isdir(options.open): self.sgnCreateSession.emit(expandPath(options.open)) ############################################# # SLOTS ################################# @QtCore.pyqtSlot(str) def doCreateSession(self, path): """ Create a session using the given project path. :type path: str """ for session in self.sessions: # Look among the active sessions and see if we already have # a session loaded for the given project: if so, focus it. if session.project.path == path: session.show() break else: # If we do not have a session for the given project we'll create one. with BusyProgressDialog('Loading project: {0}'.format( os.path.basename(path))): try: session = Session(self, path) except ProjectStopLoadingError: pass except (ProjectNotFoundError, ProjectNotValidError, ProjectVersionError) as e: LOGGER.warning( 'Failed to create session for project %s: %s', path, e) msgbox = QtWidgets.QMessageBox() msgbox.setIconPixmap( QtGui.QIcon( ':/icons/48/ic_error_outline_black').pixmap(48)) msgbox.setText( 'Failed to create session for project: <b>{0}</b>!'. format(os.path.basename(path))) msgbox.setTextFormat(QtCore.Qt.RichText) msgbox.setDetailedText(format_exception(e)) msgbox.setStandardButtons(QtWidgets.QMessageBox.Close) msgbox.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) msgbox.setWindowTitle('Project Error!') msgbox.exec_() except Exception as e: raise e else: ############################################# # UPDATE RECENT PROJECTS ################################# settings = QtCore.QSettings(ORGANIZATION, APPNAME) projects = settings.value('project/recent', None, str) or [] try: projects.remove(path) except ValueError: pass finally: projects.insert(0, path) projects = projects[:8] settings.setValue('project/recent', projects) settings.sync() ############################################# # CLOSE THE WELCOME SCREEN IF NECESSARY ################################# try: self.welcome.close() except (AttributeError, RuntimeError): pass ############################################# # STARTUP THE SESSION ################################# connect(session.sgnQuit, self.doQuit) connect(session.sgnClosed, self.onSessionClosed) self.sessions.append(session) session.show() @QtCore.pyqtSlot() def doQuit(self): """ Quit Eddy. """ for session in self.sessions: session.save() self.quit() @QtCore.pyqtSlot() def onSessionClosed(self): """ Quit Eddy. """ ## SAVE SESSION STATE session = self.sender() if session: session.save() self.sessions.remove(session) ## CLEANUP POSSIBLE LEFTOVERS self.sessions = DistinctList(filter(None, self.sessions)) ## SWITCH TO AN ACTIVE WINDOW OR WELCOME PANEL if self.sessions: session = self.sessions[-1] session.show() else: self.welcome = Welcome(self) self.welcome.show()
class Eddy(QtWidgets.QApplication): """ This class implements the main QtCore.Qt application. """ sgnCreateSession = QtCore.pyqtSignal(str) def __init__(self, options, argv): """ Initialize Eddy. :type options: Namespace :type argv: list """ super().__init__(argv) self.server = None self.socket = QtNetwork.QLocalSocket() self.socket.connectToServer(APPID) self.running = self.socket.waitForConnected() self.sessions = DistinctList() self.welcome = None if not self.isRunning() or options.tests: self.server = QtNetwork.QLocalServer() self.server.listen(APPID) self.socket = None connect(self.sgnCreateSession, self.doCreateSession) ############################################# # INTERFACE ################################# def configure(self, options): """ Perform initial configuration tasks for Eddy to work properly. :type options: Namespace """ ############################################# # DRAW THE SPLASH SCREEN ################################# splash = None if not options.nosplash: splash = Splash(mtime=4) splash.show() ############################################# # CONFIGURE RECENT PROJECTS ################################# settings = QtCore.QSettings(ORGANIZATION, APPNAME) examples = [ expandPath('@examples/Animals'), expandPath('@examples/Diet'), expandPath('@examples/Family'), expandPath('@examples/LUBM'), expandPath('@examples/Pizza'), ] if not settings.contains('project/recent'): # From PyQt5 documentation: if the value of the setting is a container (corresponding # to either QVariantList, QVariantMap or QVariantHash) then the type is applied to the # contents of the container. So we can't use an empty list as default value because # PyQt5 needs to know the type of the contents added to the collection: we avoid # this problem by placing the list of example projects as recent project list. settings.setValue('project/recent', examples) else: # If we have some projects in our recent list, check whether they exists on the # filesystem. If they do not exists we remove them from our recent list. projects = [] for path in map(expandPath, settings.value('project/recent')): if isdir(path) and path not in projects: projects.append(path) settings.setValue('project/recent', projects or examples) settings.sync() ############################################# # CONFIGURE FONTS ################################# fontDB = QtGui.QFontDatabase() fontDB.addApplicationFont(':/fonts/Roboto-Black') fontDB.addApplicationFont(':/fonts/Roboto-BlackItalic') fontDB.addApplicationFont(':/fonts/Roboto-Bold') fontDB.addApplicationFont(':/fonts/Roboto-BoldItalic') fontDB.addApplicationFont(':/fonts/Roboto-Italic') fontDB.addApplicationFont(':/fonts/Roboto-Light') fontDB.addApplicationFont(':/fonts/Roboto-LightItalic') fontDB.addApplicationFont(':/fonts/Roboto-Medium') fontDB.addApplicationFont(':/fonts/Roboto-MediumItalic') fontDB.addApplicationFont(':/fonts/Roboto-Regular') fontDB.addApplicationFont(':/fonts/Roboto-Thin') fontDB.addApplicationFont(':/fonts/Roboto-ThinItalic') fontDB.addApplicationFont(':/fonts/RobotoCondensed-Bold') fontDB.addApplicationFont(':/fonts/RobotoCondensed-BoldItalic') fontDB.addApplicationFont(':/fonts/RobotoCondensed-Italic') fontDB.addApplicationFont(':/fonts/RobotoCondensed-Light') fontDB.addApplicationFont(':/fonts/RobotoCondensed-LightItalic') fontDB.addApplicationFont(':/fonts/RobotoCondensed-Regular') fontDB.addApplicationFont(':/fonts/RobotoMono-Bold') fontDB.addApplicationFont(':/fonts/RobotoMono-BoldItalic') fontDB.addApplicationFont(':/fonts/RobotoMono-Italic') fontDB.addApplicationFont(':/fonts/RobotoMono-Light') fontDB.addApplicationFont(':/fonts/RobotoMono-LightItalic') fontDB.addApplicationFont(':/fonts/RobotoMono-Medium') fontDB.addApplicationFont(':/fonts/RobotoMono-MediumItalic') fontDB.addApplicationFont(':/fonts/RobotoMono-Regular') fontDB.addApplicationFont(':/fonts/RobotoMono-Thin') fontDB.addApplicationFont(':/fonts/RobotoMono-ThinItalic') self.setFont(Font('Roboto', 12)) ############################################# # CONFIGURE LAYOUT ################################# buffer = '' resources = expandPath('@resources/styles/') for name in os.listdir(resources): path = os.path.join(resources, name) if fexists(path) and File.forPath(path) is File.Qss: buffer += fread(path) self.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps) self.setStyle(EddyProxyStyle('Fusion')) self.setStyleSheet(buffer) ############################################# # CLOSE THE SPLASH SCREEN ################################# if splash and not options.nosplash: splash.sleep() splash.close() ############################################# # CONFIGURE THE WORKSPACE ################################# workspace = expandPath(settings.value('workspace/home', WORKSPACE, str)) if not isdir(workspace): window = WorkspaceDialog() if window.exec_() == WorkspaceDialog.Rejected: raise SystemExit def isRunning(self): """ Returns True if there is already another instance of Eddy which is running, False otherwise. :rtype: bool """ return self.running def start(self, options): """ Run the application by showing the welcome dialog. :type options: Namespace """ self.welcome = Welcome(self) self.welcome.show() if options.open and isdir(options.open): self.sgnCreateSession.emit(expandPath(options.open)) ############################################# # SLOTS ################################# @QtCore.pyqtSlot(str) def doCreateSession(self, path): """ Create a session using the given project path. :type path: str """ for session in self.sessions: # Look among the active sessions and see if we already have # a session loaded for the given project: if so, focus it. if session.project.path == path: session.show() break else: # If we do not have a session for the given project we'll create one. with BusyProgressDialog('Loading project: {0}'.format(os.path.basename(path))): try: session = Session(self, path) except ProjectStopLoadingError: pass except (ProjectNotFoundError, ProjectNotValidError, ProjectVersionError) as e: LOGGER.warning('Failed to create session for project %s: %s', path, e) msgbox = QtWidgets.QMessageBox() msgbox.setIconPixmap(QtGui.QIcon(':/icons/48/ic_error_outline_black').pixmap(48)) msgbox.setText('Failed to create session for project: <b>{0}</b>!'.format(os.path.basename(path))) msgbox.setTextFormat(QtCore.Qt.RichText) msgbox.setDetailedText(format_exception(e)) msgbox.setStandardButtons(QtWidgets.QMessageBox.Close) msgbox.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) msgbox.setWindowTitle('Project Error!') msgbox.exec_() except Exception as e: raise e else: ############################################# # UPDATE RECENT PROJECTS ################################# settings = QtCore.QSettings(ORGANIZATION, APPNAME) projects = settings.value('project/recent', None, str) or [] try: projects.remove(path) except ValueError: pass finally: projects.insert(0, path) projects = projects[:8] settings.setValue('project/recent', projects) settings.sync() ############################################# # CLOSE THE WELCOME SCREEN IF NECESSARY ################################# try: self.welcome.close() except (AttributeError, RuntimeError): pass ############################################# # STARTUP THE SESSION ################################# connect(session.sgnQuit, self.doQuit) connect(session.sgnClosed, self.onSessionClosed) self.sessions.append(session) session.show() @QtCore.pyqtSlot() def doQuit(self): """ Quit Eddy. """ for session in self.sessions: session.save() self.quit() @QtCore.pyqtSlot() def onSessionClosed(self): """ Quit Eddy. """ ## SAVE SESSION STATE session = self.sender() if session: session.save() self.sessions.remove(session) ## CLEANUP POSSIBLE LEFTOVERS self.sessions = DistinctList(filter(None, self.sessions)) ## SWITCH TO AN ACTIVE WINDOW OR WELCOME PANEL if self.sessions: session = self.sessions[-1] session.show() else: self.welcome = Welcome(self) self.welcome.show()