def test_extend_with_tuple(self): D1 = DistinctList([1, 2, 3, 4, 5, 6, 7, 8]) D1.extend((9, 10, 11, 12)) self.assertSequenceEqual(D1, DistinctList( [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]), seq_type=DistinctList)
def orderingChanged(self): """ Change the order of inputs edges. :rtype: QUndoCommand """ if self.node.inputs: inputs = DistinctList() for i in range(0, self.list.count()): item = self.list.item(i) inputs.append(item.data(QtCore.Qt.UserRole)) if self.node.inputs != inputs: return CommandNodeChangeInputsOrder(self.diagram, self.node, inputs) return None
def __init__(self, brush=None, inputs=None, **kwargs): """ Initialize the node. :type brush: QBrush """ super().__init__(brush=QtGui.QBrush(QtGui.QColor(252, 252, 252, 255)), **kwargs) self.inputs = inputs or DistinctList() self.label = NodeLabel('key', pos=self.center, editable=False, movable=False, parent=self)
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()
def run(self, path): """ Perform CSV file generation. :type path: str """ if self.diagrams is None: dialog = DiagramSelectionDialog(self.session) if not dialog.exec_(): return self.diagrams = dialog.selectedDiagrams() LOGGER.info( 'Exporting selected diagrams in project %s in CSV format: %s', self.project.name, path) collection = {x: {} for x in self.Types} for diagram in self.diagrams: nodes = self.project.predicates(diagram=diagram) for node in nodes: if node.type() in collection: if not node.text() in collection[node.type()]: meta = self.project.meta(node.type(), node.text()) collection[node.type()][node.text()] = { CsvExporter.KeyName: node.text().replace('\n', ''), CsvExporter.KeyType: node.shortName, CsvExporter.KeyDescription: self.plainText(meta.get(K_DESCRIPTION, '')), CsvExporter.KeyDiagrams: DistinctList() } collection[node.type()][node.text()][self.KeyDiagrams] += [ node.diagram.name ] buffer = io.StringIO() writer = csv.writer(buffer, delimiter=',', quotechar='"', quoting=csv.QUOTE_ALL) writer.writerow((self.KeyName, self.KeyType, self.KeyDescription, self.KeyDiagrams)) for i, j in sorted(((v, k) for k in collection for v in collection[k]), key=itemgetter(0)): writer.writerow(( collection[j][i][self.KeyName], collection[j][i][self.KeyType], collection[j][i][self.KeyDescription], sorted(collection[j][i][self.KeyDiagrams]), )) fwrite(buffer.getvalue(), path) if self.open: openPath(path)
def run(self, path): """ Perform CSV file generation. :type path: str """ diagrams_selection_dialog = DiagramsSelectionDialog( self.project, self.session) diagrams_selection_dialog.exec_() self.selected_diagrams = diagrams_selection_dialog.diagrams_selected LOGGER.info( 'Exporting selected diagrams in project %s in CSV format: %s', self.project.name, path) collection = {x: {} for x in self.Types} for diag in self.selected_diagrams: nodes = self.project.predicates(diagram=diag) for node in nodes: if node.type() in collection: if not node.text() in collection[node.type()]: meta = self.project.meta(node.type(), node.text()) collection[node.type()][node.text()] = { #CsvExporter.KeyName: lstrip(OWLShortIRI('', node.text()), ':'), CsvExporter.KeyName: node.text().replace('\n', ''), CsvExporter.KeyType: node.shortName, #CsvExporter.KeyDescription: meta.get(K_DESCRIPTION, ''), CsvExporter.KeyDescription: QtWidgets.QTextEdit(meta.get(K_DESCRIPTION, '')).toPlainText(), CsvExporter.KeyDiagrams: DistinctList() } collection[node.type()][node.text()][self.KeyDiagrams] += [ node.diagram.name ] buffer = io.StringIO() writer = csv.writer(buffer) writer.writerow((self.KeyName, self.KeyType, self.KeyDescription, self.KeyDiagrams)) for i, j in sorted(((v, k) for k in collection for v in collection[k]), key=itemgetter(0)): writer.writerow(( collection[j][i][self.KeyName], collection[j][i][self.KeyType], collection[j][i][self.KeyDescription], sorted(collection[j][i][self.KeyDiagrams]), )) fwrite(buffer.getvalue(), path) openPath(path)
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)
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)
def __init__(self, width=52, height=30, brush=None, inputs=None, **kwargs): """ Initialize the node. :type width: int :type height: int :type brush: QBrush :type inputs: DistinctList """ super().__init__(**kwargs) brush = PropertyAssertionNode.DefaultBrush pen = PropertyAssertionNode.DefaultPen self.inputs = inputs or DistinctList() self.background = Polygon(QtCore.QRectF(-34, -19, 68, 38)) self.selection = Polygon(QtCore.QRectF(-34, -19, 68, 38)) self.polygon = Polygon(QtCore.QRectF(-26, -15, 52, 30), brush, pen)
def run(self, path): """ Perform CSV file generation. :type path: str """ LOGGER.info('Exporting project %s in CSV format: %s', self.project.name, path) collection = {x: {} for x in self.Types} for node in self.project.predicates(): if node.type() in collection: if not node.text() in collection[node.type()]: meta = self.project.meta(node.type(), node.text()) collection[node.type()][node.text()] = { CsvExporter.KeyName: lstrip(OWLShortIRI('', node.text()), ':'), CsvExporter.KeyType: node.shortName, CsvExporter.KeyDescription: meta.get(K_DESCRIPTION, ''), CsvExporter.KeyDiagrams: DistinctList() } collection[node.type()][node.text()][self.KeyDiagrams] += [ node.diagram.name ] buffer = io.StringIO() writer = csv.writer(buffer) writer.writerow((self.KeyName, self.KeyType, self.KeyDescription, self.KeyDiagrams)) for i, j in sorted(((v, k) for k in collection for v in collection[k]), key=itemgetter(0)): writer.writerow(( collection[j][i][self.KeyName], collection[j][i][self.KeyType], collection[j][i][self.KeyDescription], sorted(collection[j][i][self.KeyDiagrams]), )) fwrite(buffer.getvalue(), path) openPath(path)
def test_append(self): D1 = DistinctList([1, 2, 3, 4, 5, 6, 7, 8]) D1.append(9) self.assertSequenceEqual(D1, DistinctList([1, 2, 3, 4, 5, 6, 7, 8, 9]), seq_type=DistinctList)
def test_constructor_with_tuple(self): D1 = DistinctList((1, 2, 3, 3, 4, 1, 4, 5, 6, 7, 7, 8, 2)) self.assertSequenceEqual(D1, DistinctList((1, 2, 3, 4, 5, 6, 7, 8)), seq_type=DistinctList)
def test_constructor_with_set(self): self.assertEqual(8, len(DistinctList({1, 2, 3, 4, 5, 6, 7, 8})))
def test_append(self): D1 = DistinctList([1, 2, 3, 4, 5, 6, 7, 8]) D1.append(9) assert D1 == DistinctList([1, 2, 3, 4, 5, 6, 7, 8, 9])
def test_constructor_with_list(self): D1 = DistinctList([1, 2, 3, 3, 4, 1, 4, 5, 6, 7, 7, 8, 2]) self.assertSequenceEqual(D1, DistinctList([1, 2, 3, 4, 5, 6, 7, 8]), seq_type=DistinctList)
def test_remove_with_no_match(self): D1 = DistinctList([1, 2, 3, 4, 5, 6, 7, 8]) D1.remove(9) self.assertSequenceEqual(D1, DistinctList([1, 2, 3, 4, 5, 6, 7, 8]), seq_type=DistinctList)
def test_remove_with_no_match(self): D1 = DistinctList([1, 2, 3, 4, 5, 6, 7, 8]) D1.remove(9) assert D1 == DistinctList([1, 2, 3, 4, 5, 6, 7, 8])
def test_insert(self): D1 = DistinctList([1, 2, 3, 4, 5, 6, 7, 8]) D1.insert(5, 9) self.assertSequenceEqual(D1, DistinctList([1, 2, 3, 4, 5, 9, 6, 7, 8]), seq_type=DistinctList)
def test_insert(self): D1 = DistinctList([1, 2, 3, 4, 5, 6, 7, 8]) D1.insert(5, 9) assert D1 == DistinctList([1, 2, 3, 4, 5, 9, 6, 7, 8])
def test_constructor_with_set(self): assert 8 == len(DistinctList({1, 2, 3, 4, 5, 6, 7, 8}))
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()
def test_extend_with_tuple(self): D1 = DistinctList([1, 2, 3, 4, 5, 6, 7, 8]) D1.extend((9, 10, 11, 12)) assert D1 == DistinctList([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
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()
def test_extend_with_tuple(self): D1 = DistinctList([1, 2, 3, 4, 5, 6, 7, 8]) D1.extend((9, 10, 11, 12)) self.assertSequenceEqual(D1, DistinctList([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]), seq_type=DistinctList)
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()
def test_constructor_with_tuple(self): D1 = DistinctList((1, 2, 3, 3, 4, 1, 4, 5, 6, 7, 7, 8, 2)) assert D1 == DistinctList((1, 2, 3, 4, 5, 6, 7, 8))
def test_constructor_with_list(self): D1 = DistinctList([1, 2, 3, 3, 4, 1, 4, 5, 6, 7, 7, 8, 2]) assert D1 == DistinctList([1, 2, 3, 4, 5, 6, 7, 8])