class EditableNodeProperty(PredicateNodeProperty): """ This class implements the property dialog for label editable predicate nodes. """ def __init__(self, scene, node, parent=None): """ Initialize the editable node properties dialog. :type scene: DiagramScene :type node: AbstractNode :type parent: QWidget """ super().__init__(scene, node, parent) ################################################################################################################ # # # LABEL TAB # # # ################################################################################################################ self.labelWidget = QWidget() self.labelLayout = QFormLayout(self.labelWidget) self.textField = StringField(self.labelWidget) self.textField.setFixedWidth(300) self.textField.setValue(self.node.text()) self.textField.setEnabled(self.node.label.editable) self.labelLayout.addRow('Text', self.textField) self.mainWidget.addTab(self.labelWidget, 'Label') #################################################################################################################### # # # SLOTS # # # #################################################################################################################### def completed(self, code): """ Executed when the dialog is terminated. :type code: int """ if code == QDialog.Accepted: super().completed(code) self.labelEdited() def labelEdited(self): """ Change the label of the node. """ data = self.textField.value().strip() data = data if not isEmpty(data) else self.node.label.template if self.node.text() != data: command = CommandNodeLabelChange(self.scene, self.node, self.node.text(), data) self.scene.undostack.push(command)
class PredicateNodeProperty(NodeProperty): """ This class implements the property dialog for predicate nodes. """ def __init__(self, scene, node, parent=None): """ Initialize the predicate node properties dialog. :type scene: DiagramScene :type node: AbstractNode :type parent: QWidget """ super().__init__(scene, node, parent) meta = scene.meta.metaFor(node.item, node.text()) self.urlField = StringField(self.generalWidget) self.urlField.setFixedWidth(300) self.urlField.setValue(meta.url) self.descriptionField = TextField(self.generalWidget) self.descriptionField.setFixedSize(300, 160) self.descriptionField.setValue(meta.description) self.generalLayout.addRow('URL', self.urlField) self.generalLayout.addRow('Description', self.descriptionField) #################################################################################################################### # # # SLOTS # # # #################################################################################################################### @pyqtSlot(int) def completed(self, code): """ Executed when the dialog is terminated. :type code: int """ if code == QDialog.Accepted: super().completed(code) self.metaDataChanged() def metaDataChanged(self): """ Change the url and description of the node. """ meta = self.scene.meta.metaFor(self.node.item, self.node.text()) copy = meta.copy() copy.description = self.descriptionField.value() copy.url = self.urlField.value() if copy != meta: command = CommandNodeChangeMeta(self.scene, self.node, meta, copy) self.scene.undostack.push(command)
class ValueForm(QDialog): """ This class implements the form used to select the Literal value of an Individual node. """ def __init__(self, node, parent=None): """ Initialize the form dialog. :type node: IndividualNode :type parent: QWidget """ super().__init__(parent) # DATATYPE COMBO BOX self.datatypeField = ComboBox(self) for datatype in XsdDatatype: self.datatypeField.addItem(datatype.value, datatype) # VALUE STRING FIELD self.valueField = StringField(self) self.valueField.setFixedWidth(300) # FILL FIELDS WITH DATA if node.identity is Identity.Value: datatype = node.datatype for i in range(self.datatypeField.count()): if self.datatypeField.itemData(i) is datatype: self.datatypeField.setCurrentIndex(i) break self.valueField.setValue(node.value) else: self.datatypeField.setCurrentIndex(0) self.valueField.setValue('') # CONFIRMATION BOX self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.mainLayout = QFormLayout(self) self.mainLayout.addRow('Datatype', self.datatypeField) self.mainLayout.addRow('Value', self.valueField) self.mainLayout.addRow(self.buttonBox) self.setWindowTitle('Compose value') self.setWindowIcon(QIcon(':/images/eddy')) self.setFixedSize(self.sizeHint()) connect(self.buttonBox.accepted, self.accept) connect(self.buttonBox.rejected, self.reject)
class ValueForm(QtWidgets.QDialog): """ This class implements the form used to select the Value of an Individual node. """ def __init__(self, node, session): """ Initialize the form dialog. :type node: IndividualNode :type session: Session """ super().__init__(session) self.node = node ############################################# # FORM AREA ################################# self.datatypeLabel = QtWidgets.QLabel(self) self.datatypeLabel.setFont(Font('Roboto', 12)) self.datatypeLabel.setText('Datatype') self.datatypeField = ComboBox(self) self.datatypeField.setFont(Font('Roboto', 12)) self.datatypeField.setFixedWidth(300) for datatype in sorted(Datatype.forProfile(self.project.profile.type()), key=attrgetter('value')): self.datatypeField.addItem(datatype.value, datatype) self.valueLabel = QtWidgets.QLabel(self) self.valueLabel.setFont(Font('Roboto', 12)) self.valueLabel.setText('Value') self.valueField = StringField(self) self.valueField.setFixedWidth(300) if node.identity() is Identity.Value: self.valueField.setValue(node.value) datatype = node.datatype for i in range(self.datatypeField.count()): if self.datatypeField.itemData(i) is datatype: self.datatypeField.setCurrentIndex(i) break else: self.valueField.setValue('') self.datatypeField.setCurrentIndex(0) self.formWidget = QtWidgets.QWidget(self) self.formLayout = QtWidgets.QFormLayout(self.formWidget) self.formLayout.addRow(self.datatypeLabel, self.datatypeField) self.formLayout.addRow(self.valueLabel, self.valueField) ############################################# # CONFIRMATION AREA ################################# self.confirmationBox = QtWidgets.QDialogButtonBox(QtCore.Qt.Horizontal, self) self.confirmationBox.addButton(QtWidgets.QDialogButtonBox.Ok) self.confirmationBox.addButton(QtWidgets.QDialogButtonBox.Cancel) self.confirmationBox.setContentsMargins(10, 0, 10, 10) self.confirmationBox.setFont(Font('Roboto', 12)) ############################################# # SETUP DIALOG LAYOUT ################################# self.mainLayout = QtWidgets.QVBoxLayout(self) self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.addWidget(self.formWidget) self.mainLayout.addWidget(self.confirmationBox, 0, QtCore.Qt.AlignRight) self.setFixedSize(self.sizeHint()) self.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) self.setWindowTitle('Compose value') connect(self.confirmationBox.accepted, self.accept) connect(self.confirmationBox.rejected, self.reject) ############################################# # PROPERTIES ################################# @property def project(self): """ Returns the reference to the active project. :rtype: Project """ return self.session.project @property def session(self): """ Returns the reference to the active session (alias for RefactorNameForm.parent()). :rtype: Session """ return self.parent() ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def accept(self): """ Accepts the form and set the new value. """ node = self.node diagram = node.diagram datatype = self.datatypeField.currentData() value = self.valueField.value() data = node.compose(value, datatype) if node.text() != data: name = 'change {0} to {1}'.format(node.text(), data) self.session.undostack.push(CommandLabelChange(diagram, node, node.text(), data, name=name)) super().accept()
class ValueNodeProperty(NodeProperty): """ This class implements the property dialog for value nodes. """ def __init__(self, diagram, node, session): """ Initialize the node properties dialog. :type diagram: Diagram :type node: AbstractNode :type session: Session """ super().__init__(diagram, node, session) ############################################# # VALUE TAB ################################# self.datatypeLabel = QtWidgets.QLabel(self) self.datatypeLabel.setFont(Font('Roboto', 12)) self.datatypeLabel.setText('Datatype') self.datatypeField = ComboBox(self) self.datatypeField.setFixedWidth(200) self.datatypeField.setFocusPolicy(QtCore.Qt.StrongFocus) self.datatypeField.setFont(Font('Roboto', 12)) for datatype in Datatype: self.datatypeField.addItem(datatype.value, datatype) datatype = self.node.datatype for i in range(self.datatypeField.count()): if self.datatypeField.itemData(i) is datatype: self.datatypeField.setCurrentIndex(i) break else: self.datatypeField.setCurrentIndex(0) self.valueLabel = QtWidgets.QLabel(self) self.valueLabel.setFont(Font('Roboto', 12)) self.valueLabel.setText('Value') self.valueField = StringField(self) self.valueField.setFixedWidth(200) self.valueField.setFont(Font('Roboto', 12)) self.valueField.setValue(self.node.value) self.valueWidget = QtWidgets.QWidget() self.valueLayout = QtWidgets.QFormLayout(self.valueWidget) self.valueLayout.addRow(self.datatypeLabel, self.datatypeField) self.valueLayout.addRow(self.valueLabel, self.valueField) self.mainWidget.addTab(self.valueWidget, 'Datatype') ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def complete(self): """ Executed when the dialog is accepted. """ commands = [self.positionChanged(), self.valueChanged()] if any(commands): self.session.undostack.beginMacro('edit {0} properties'.format( self.node.name)) for command in commands: if command: self.session.undostack.push(command) self.session.undostack.endMacro() super().accept() ############################################# # AUXILIARY METHODS ################################# def valueChanged(self): """ Change the value of the node. :rtype: QUndoCommand """ datatype = self.datatypeField.currentData() value = self.valueField.value() data = self.node.compose(value, datatype) if self.node.text() != data: return CommandLabelChange(self.diagram, self.node, self.node.text(), data) return None
class RefactorNameForm(QtWidgets.QDialog): """ This class implements the form used to rename nodes during refactor operations. """ def __init__(self, node, session): """ Initialize the form dialog. :type node: AbstractNode :type session: Session """ super().__init__(session) self.node = node ############################################# # FORM AREA ################################# self.renameLabel = QtWidgets.QLabel(self) self.renameLabel.setFont(Font('Roboto', 12)) self.renameLabel.setText('Name') self.renameField = StringField(self) self.renameField.setFixedWidth(200) self.renameField.setFont(Font('Roboto', 12)) self.renameField.setValue(self.node.text()) connect(self.renameField.textChanged, self.nameChanged) self.formWidget = QtWidgets.QWidget(self) self.formLayout = QtWidgets.QFormLayout(self.formWidget) self.formLayout.addRow(self.renameLabel, self.renameField) ############################################# # CONFIRMATION AREA ################################# self.confirmationBox = QtWidgets.QDialogButtonBox(QtCore.Qt.Horizontal, self) self.confirmationBox.addButton(QtWidgets.QDialogButtonBox.Ok) self.confirmationBox.addButton(QtWidgets.QDialogButtonBox.Cancel) self.confirmationBox.setContentsMargins(10, 0, 10, 10) self.confirmationBox.setFont(Font('Roboto', 12)) ############################################# # SETUP DIALOG LAYOUT ################################# self.caption = QtWidgets.QLabel(self) self.caption.setFont(Font('Roboto', 12)) self.caption.setContentsMargins(8, 0, 8, 0) self.caption.setProperty('class', 'invalid') self.caption.setVisible(False) self.mainLayout = QtWidgets.QVBoxLayout(self) self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.addWidget(self.formWidget) self.mainLayout.addWidget(self.caption) self.mainLayout.addWidget(self.confirmationBox, 0, QtCore.Qt.AlignRight) self.setFixedSize(self.sizeHint()) self.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) self.setWindowTitle('Rename') connect(self.confirmationBox.accepted, self.accept) connect(self.confirmationBox.rejected, self.reject) ############################################# # PROPERTIES ################################# @property def project(self): """ Returns the reference to the active project. :rtype: Project """ return self.session.project @property def session(self): """ Returns the reference to the active session (alias for RefactorNameForm.parent()). :rtype: Session """ return self.parent() ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def accept(self): """ Accepts the rename form and perform refactoring. """ name = self.renameField.value() self.session.undostack.beginMacro('change predicate "{0}" to "{1}"'.format(self.node.text(), name)) for node in self.project.predicates(self.node.type(), self.node.text()): command = CommandLabelChange(node.diagram, node, node.text(), name, refactor=True) self.session.undostack.push(command) self.session.undostack.endMacro() super().accept() @QtCore.pyqtSlot() def nameChanged(self): """ Executed whenever the text in the rename field changes. """ caption = '' enabled = True if isEmpty(self.renameField.value()): caption = "\'{0}\' is not a valid predicate name".format(self.renameField.value()) enabled = False self.caption.setText(caption) self.caption.setVisible(not isEmpty(caption)) self.confirmationBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(enabled) self.setFixedSize(self.sizeHint())
class OWLTranslationForm(QDialog): """ This class implements the form used to perform Graphol -> OWL ontology translation. """ def __init__(self, scene, filepath, parent=None): """ Initialize the form dialog. :type scene: DiagramScene :type filepath: str :type parent: QWidget """ super().__init__(parent) self.scene = scene self.filepath = filepath self.worker = None self.workerThread = None self.iriField = StringField(self) self.iriField.setFixedWidth(300) self.iriField.setValidator(QRegExpValidator(QRegExp('[\w:\/\[\]=?%#~\.\-\+]*'), self)) self.prefixField = StringField(self) self.prefixField.setFixedWidth(300) self.prefixField.setValidator(QRegExpValidator(QRegExp('[\w]*'), self)) self.syntaxField = ComboBox(self) for syntax in OWLSyntax: self.syntaxField.addItem(syntax.value, syntax) self.syntaxField.setCurrentIndex(0) self.progressBar = QProgressBar(self) self.progressBar.setAlignment(Qt.AlignHCenter) self.progressBar.setRange(0, 100) self.progressBar.setValue(0) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.buttonBox.button(QDialogButtonBox.Ok).setDisabled(True) self.mainLayout = QFormLayout(self) self.mainLayout.addRow('IRI', self.iriField) self.mainLayout.addRow('Prefix', self.prefixField) self.mainLayout.addRow('Syntax', self.syntaxField) self.mainLayout.addRow(self.progressBar) self.mainLayout.addRow(self.buttonBox) self.setWindowTitle('OWL Translation') self.setWindowIcon(QIcon(':/images/eddy')) self.setFixedSize(self.sizeHint()) connect(self.buttonBox.accepted, self.run) connect(self.buttonBox.rejected, self.reject) connect(self.iriField.textChanged, self.iriChanged) #################################################################################################################### # # # SLOTS # # # #################################################################################################################### @pyqtSlot(Exception) def errored(self, exception): """ Executed whenever the translation errors. :type exception: Exception """ if isinstance(exception, MalformedDiagramError): msgbox = QMessageBox(self) msgbox.setIconPixmap(QPixmap(':/icons/warning')) msgbox.setWindowIcon(QIcon(':/images/eddy')) msgbox.setWindowTitle('Malformed Diagram') msgbox.setText('Malformed expression detected on {}: {}'.format(exception.item, exception)) msgbox.setInformativeText('Do you want to see the error in the diagram?') msgbox.setStandardButtons(QMessageBox.Yes|QMessageBox.No) S = QSpacerItem(400, 0, QSizePolicy.Minimum, QSizePolicy.Expanding) L = msgbox.layout() L.addItem(S, L.rowCount(), 0, 1, L.columnCount()) msgbox.exec_() if msgbox.result() == QMessageBox.Yes: for view in self.scene.views(): if isinstance(view, MainView): view.centerOn(exception.item) else: msgbox = QMessageBox(self) msgbox.setIconPixmap(QPixmap(':/icons/error')) msgbox.setWindowIcon(QIcon(':/images/eddy')) msgbox.setWindowTitle('Unhandled exception!') msgbox.setStandardButtons(QMessageBox.Close) msgbox.setText('Diagram translation could not be completed!') msgbox.setInformativeText('Please <a href="{}">submit a bug report</a> with detailed information.'.format(BUG_TRACKER)) msgbox.setDetailedText(''.join(traceback.format_exception(type(exception), exception, exception.__traceback__))) S = QSpacerItem(400, 0, QSizePolicy.Minimum, QSizePolicy.Expanding) L = msgbox.layout() L.addItem(S, L.rowCount(), 0, 1, L.columnCount()) msgbox.exec_() self.workerThread.quit() self.reject() @pyqtSlot() def completed(self): """ Executed whenever the translation completes. """ self.workerThread.quit() file = File(path=self.filepath) file.write(string=self.worker.export(syntax=self.syntaxField.currentData())) msgbox = QMessageBox(self) msgbox.setIconPixmap(QPixmap(':/icons/info')) msgbox.setWindowIcon(QIcon(':/images/eddy')) msgbox.setText('Translation completed!') msgbox.setInformativeText('Do you want to open the OWL ontology?') msgbox.setStandardButtons(QMessageBox.Yes|QMessageBox.No) S = QSpacerItem(400, 0, QSizePolicy.Minimum, QSizePolicy.Expanding) L = msgbox.layout() L.addItem(S, L.rowCount(), 0, 1, L.columnCount()) msgbox.exec_() if msgbox.result() == QMessageBox.Yes: openPath(self.filepath) self.accept() @pyqtSlot(int, int) def progress(self, current, total): """ Update the progress bar showing the translation advancement. :type current: int :type total: int """ self.progressBar.setRange(0, total) self.progressBar.setValue(current) @pyqtSlot() def iriChanged(self): """ Executed whenever the value of the prefix field changes. """ button = self.buttonBox.button(QDialogButtonBox.Ok) button.setEnabled(not isEmpty(self.iriField.value())) @pyqtSlot() def run(self): """ Perform the Graphol -> OWL translation in a separate thread. """ ontoIRI = self.iriField.value() ontoPrefix = self.prefixField.value() self.buttonBox.setEnabled(False) self.syntaxField.setEnabled(False) if not ontoIRI.endswith('#'): ontoIRI = '{0}#'.format(ontoIRI) self.workerThread = QThread() self.worker = OWLExporter(scene=self.scene, ontoIRI=ontoIRI, ontoPrefix=ontoPrefix) self.worker.moveToThread(self.workerThread) connect(self.worker.completed, self.completed) connect(self.worker.errored, self.errored) connect(self.worker.progress, self.progress) connect(self.workerThread.started, self.worker.work) self.workerThread.start()
class PredicateNodeProperty(NodeProperty): """ This class implements the property dialog for predicate nodes. Note that this dialog window is not used for value-domain nodes even though they are predicate nodes. """ def __init__(self, diagram, node, session): """ Initialize the node properties dialog. :type diagram: Diagram :type node: AbstractNode :type session: Session """ super().__init__(diagram, node, session) meta = diagram.project.meta(node.type(), node.text()) self.urlLabel = QtWidgets.QLabel(self) self.urlLabel.setFont(Font('Roboto', 12)) self.urlLabel.setText('URL') self.urlField = StringField(self) self.urlField.setFixedWidth(300) self.urlField.setFont(Font('Roboto', 12)) self.urlField.setValue(meta.get(K_URL, '')) self.descriptionLabel = QtWidgets.QLabel(self) self.descriptionLabel.setFont(Font('Roboto', 12)) self.descriptionLabel.setText('Description') self.descriptionField = TextField(self) self.descriptionField.setFixedSize(300, 160) self.descriptionField.setFont(Font('Roboto', 12)) self.descriptionField.setValue(meta.get(K_DESCRIPTION, '')) self.generalLayout.addRow(self.urlLabel, self.urlField) self.generalLayout.addRow(self.descriptionLabel, self.descriptionField) ############################################# # LABEL TAB ################################# self.textLabel = QtWidgets.QLabel(self) self.textLabel.setFont(Font('Roboto', 12)) self.textLabel.setText('Text') self.textField = StringField(self) self.textField.setFixedWidth(300) self.textField.setFont(Font('Roboto', 12)) self.textField.setValue(self.node.text()) self.refactorLabel = QtWidgets.QLabel(self) self.refactorLabel.setFont(Font('Roboto', 12)) self.refactorLabel.setText('Refactor') self.refactorField = CheckBox(self) self.refactorField.setFont(Font('Roboto', 12)) self.refactorField.setChecked(False) if node.type() in { Item.AttributeNode, Item.ConceptNode, Item.RoleNode }: if node.special() is not None: self.refactorField.setEnabled(False) self.labelWidget = QtWidgets.QWidget() self.labelLayout = QtWidgets.QFormLayout(self.labelWidget) self.labelLayout.addRow(self.textLabel, self.textField) self.labelLayout.addRow(self.refactorLabel, self.refactorField) self.mainWidget.addTab(self.labelWidget, 'Label') ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def complete(self): """ Executed when the dialog is accepted. """ commands = [self.positionChanged(), self.metaDataChanged()] commands.extend(self.textChanged()) if any(commands): self.session.undostack.beginMacro('edit {0} properties'.format( self.node.name)) for command in commands: if command: self.session.undostack.push(command) self.session.undostack.endMacro() super().accept() ############################################# # AUXILIARY METHODS ################################# def metaDataChanged(self): """ Change the url and description of the node. :rtype: QUndoCommand """ undo = self.diagram.project.meta(self.node.type(), self.node.text()) redo = undo.copy() redo[K_DESCRIPTION] = self.descriptionField.value() redo[K_URL] = self.urlField.value() if redo != undo: return CommandNodeSetMeta(self.diagram.project, self.node.type(), self.node.text(), undo, redo) return None def textChanged(self): """ Change the label of the node. :rtype: list """ data = self.textField.value().strip() data = data if not isEmpty(data) else self.node.label.template if self.node.text() != data: if self.refactorField.isChecked(): item = self.node.type() name = self.node.text() project = self.diagram.project return [ CommandLabelChange(n.diagram, n, n.text(), data) for n in project.predicates(item, name) ] return [ CommandLabelChange(self.diagram, self.node, self.node.text(), data) ] return [None]
class RefactorNameForm(QtWidgets.QDialog): """ This class implements the form used to rename nodes during refactor operations. """ def __init__(self, node, session): """ Initialize the form dialog. :type node: AbstractNode :type session: Session """ super().__init__(session) self.node = node ############################################# # FORM AREA ################################# self.renameLabel = QtWidgets.QLabel(self) self.renameLabel.setFont(Font('Roboto', 12)) self.renameLabel.setText('Name') self.renameField = StringField(self) self.renameField.setFixedWidth(200) self.renameField.setFont(Font('Roboto', 12)) self.renameField.setValue(self.node.text()) connect(self.renameField.textChanged, self.nameChanged) self.formWidget = QtWidgets.QWidget(self) self.formLayout = QtWidgets.QFormLayout(self.formWidget) self.formLayout.addRow(self.renameLabel, self.renameField) ############################################# # CONFIRMATION AREA ################################# self.confirmationBox = QtWidgets.QDialogButtonBox( QtCore.Qt.Horizontal, self) self.confirmationBox.addButton(QtWidgets.QDialogButtonBox.Ok) self.confirmationBox.addButton(QtWidgets.QDialogButtonBox.Cancel) self.confirmationBox.setContentsMargins(10, 0, 10, 10) self.confirmationBox.setFont(Font('Roboto', 12)) ############################################# # SETUP DIALOG LAYOUT ################################# self.caption = QtWidgets.QLabel(self) self.caption.setFont(Font('Roboto', 12)) self.caption.setContentsMargins(8, 0, 8, 0) self.caption.setProperty('class', 'invalid') self.caption.setVisible(False) self.mainLayout = QtWidgets.QVBoxLayout(self) self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.addWidget(self.formWidget) self.mainLayout.addWidget(self.caption) self.mainLayout.addWidget(self.confirmationBox, 0, QtCore.Qt.AlignRight) self.setFixedSize(self.sizeHint()) self.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) self.setWindowTitle('Rename') connect(self.confirmationBox.accepted, self.accept) connect(self.confirmationBox.rejected, self.reject) ############################################# # PROPERTIES ################################# @property def project(self): """ Returns the reference to the active project. :rtype: Project """ return self.session.project @property def session(self): """ Returns the reference to the active session (alias for RefactorNameForm.parent()). :rtype: Session """ return self.parent() ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def accept(self): """ Accepts the rename form and perform refactoring. """ name = self.renameField.value() self.session.undostack.beginMacro( 'change predicate "{0}" to "{1}"'.format(self.node.text(), name)) for node in self.project.predicates(self.node.type(), self.node.text()): command = CommandLabelChange(node.diagram, node, node.text(), name, refactor=True) self.session.undostack.push(command) self.session.undostack.endMacro() super().accept() @QtCore.pyqtSlot() def nameChanged(self): """ Executed whenever the text in the rename field changes. """ caption = '' enabled = True if isEmpty(self.renameField.value()): caption = "\'{0}\' is not a valid predicate name".format( self.renameField.value()) enabled = False self.caption.setText(caption) self.caption.setVisible(not isEmpty(caption)) self.confirmationBox.button( QtWidgets.QDialogButtonBox.Ok).setEnabled(enabled) self.setFixedSize(self.sizeHint())
class NodeProperty(QDialog): """ This class implements the 'Node property' dialog. """ def __init__(self, scene, node, parent=None): """ Initialize the node properties dialog. :type scene: DiagramScene :type node: AbstractNode :type parent: QWidget """ super().__init__(parent) self.node = node self.scene = scene # GENERAL self.generalWidget = QWidget() self.generalLayout = QFormLayout(self.generalWidget) self.idField = StringField(self.generalWidget) self.idField.setReadOnly(True) self.idField.setFixedWidth(300) self.idField.setValue(self.node.id) self.itemField = StringField(self.generalWidget) self.itemField.setReadOnly(True) self.itemField.setFixedWidth(300) self.itemField.setValue(node.shortname.capitalize()) self.showIdentityField = StringField(self.generalWidget) self.showIdentityField.setReadOnly(True) self.showIdentityField.setFixedWidth(300) self.showIdentityField.setValue(self.node.identity.value) self.generalLayout.addRow('ID', self.idField) self.generalLayout.addRow('Type', self.itemField) self.generalLayout.addRow('Identity', self.showIdentityField) # GEOMETRY self.geometryWidget = QWidget() self.geometryLayout = QFormLayout(self.geometryWidget) P = self.node.pos() R = self.scene.sceneRect() self.xField = SpinBox(self.geometryWidget) self.xField.setFixedWidth(60) self.xField.setRange(R.left(), R.right()) self.xField.setValue(int(P.x())) self.yField = SpinBox(self.geometryWidget) self.yField.setFixedWidth(60) self.yField.setRange(R.top(), R.bottom()) self.yField.setValue(int(P.y())) self.wField = SpinBox(self.geometryWidget) self.wField.setRange(20, R.width()) self.wField.setReadOnly(True) self.wField.setFixedWidth(60) self.wField.setValue(int(self.node.width())) self.hField = SpinBox(self.geometryWidget) self.hField.setRange(20, R.height()) self.hField.setReadOnly(True) self.hField.setFixedWidth(60) self.hField.setValue(int(self.node.height())) self.geometryLayout.addRow('X', self.xField) self.geometryLayout.addRow('Y', self.yField) self.geometryLayout.addRow('Width', self.wField) self.geometryLayout.addRow('Height', self.hField) # MAIN self.mainWidget = QTabWidget(self) self.mainWidget.addTab(self.generalWidget, 'General') self.mainWidget.addTab(self.geometryWidget, 'Geometry') self.buttonBox = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.mainLayout = QVBoxLayout(self) self.mainLayout.addWidget(self.mainWidget) self.mainLayout.addWidget(self.buttonBox, 0, Qt.AlignRight) self.setWindowTitle('Properties') self.setWindowIcon(QIcon(':/images/eddy')) connect(self.finished, self.completed) connect(self.buttonBox.accepted, self.accept) connect(self.buttonBox.rejected, self.reject) #################################################################################################################### # # # SLOTS # # # #################################################################################################################### @pyqtSlot(int) def completed(self, code): """ Executed when the dialog is terminated. :type code: int """ if code == QDialog.Accepted: self.positionChanged() def positionChanged(self): """ Move the node properly if the position has been changed. """ R = self.scene.sceneRect() x = clamp(self.xField.value(), R.left(), R.right()) y = clamp(self.yField.value(), R.top(), R.bottom()) pos1 = self.node.pos() pos2 = QPointF(x, y) if pos1 != pos2: commandData = { 'redo': { 'nodes': { self.node: { 'anchors': { k: v + pos2 - pos1 for k, v in self.node.anchors.items() }, 'pos': pos2, } }, 'edges': {}, }, 'undo': { 'nodes': { self.node: { 'anchors': {k: v for k, v in self.node.anchors.items()}, 'pos': pos1, } }, 'edges': {} } } self.scene.undostack.push( CommandNodeMove(scene=self.scene, data=commandData))
class PredicateNodeProperty(NodeProperty): """ This class implements the property dialog for predicate nodes. Note that this dialog window is not used for value-domain nodes even though they are predicate nodes. """ def __init__(self, diagram, node, session): """ Initialize the node properties dialog. :type diagram: Diagram :type node: AbstractNode :type session: Session """ super().__init__(diagram, node, session) meta = diagram.project.meta(node.type(), node.text()) self.iriLabel = QtWidgets.QLabel(self) self.iriLabel.setText('IRI') self.iriField = StringField(self) self.iriField.setFixedWidth(300) self.iriField.setValue(self.diagram.project.get_iri_of_node(node)) """ self.iriversionLabel = QtWidgets.QLabel(self) self.iriversionLabel.setText('IRI version') self.iriversionField = StringField(self) self.iriversionField.setFixedWidth(300) self.iriversionField.setValue(self.node.IRI_version(diagram.project)) """ ############################################# # LABEL TAB ################################# self.textLabel = QtWidgets.QLabel(self) self.textLabel.setText('IRI Label') self.textField = StringField(self) self.textField.setFixedWidth(300) # if node.type() in {Item.AttributeNode, Item.ConceptNode, Item.RoleNode, Item.IndividualNode}: if (('AttributeNode' in str(type(node))) or ('ConceptNode' in str(type(node))) or ('IndividualNode' in str(type(node))) or ('RoleNode' in str(type(node)))): self.textField.setValue(self.node.remaining_characters) else: self.textField.setValue(self.node.text().replace('\n', '')) # if ((node.type() is Item.IndividualNode) and (node.identity() is Identity.Value)) or \ if (('IndividualNode' in str(type(node))) and (node.identity() is Identity.Value)) or \ (('IndividualNode' not in str(type(node))) and (node.special() is not None)): self.textField.setReadOnly(True) self.iriField.setReadOnly(True) self.refactorLabel = QtWidgets.QLabel(self) self.refactorLabel.setText('Refactor') self.refactorField = CheckBox(self) self.refactorField.setChecked(False) # if node.type() in {Item.AttributeNode, Item.ConceptNode, Item.RoleNode}: if (('AttributeNode' in str(type(node))) or ('ConceptNode' in str(type(node))) or ('RoleNode' in str(type(node)))): if node.special() is not None: self.refactorField.setEnabled(False) self.FulliriLabel = QtWidgets.QLabel(self) self.FulliriLabel.setText('Full IRI') self.FulliriField = StringField(self) self.FulliriField.setFixedWidth(300) full_iri = self.project.get_full_IRI(self.iriField.value(), None, self.textField.value().strip()) self.FulliriField.setValue(full_iri) # self.FulliriField.setValue(self.iriField.value()+'#'+self.textField.value().strip()) self.FulliriField.setReadOnly(True) self.labelWidget = QtWidgets.QWidget() self.labelLayout = QtWidgets.QFormLayout(self.labelWidget) self.labelLayout.addRow(self.iriLabel, self.iriField) self.labelLayout.addRow(self.textLabel, self.textField) self.labelLayout.addRow(self.FulliriLabel, self.FulliriField) self.labelLayout.addRow(self.refactorLabel, self.refactorField) self.mainWidget.addTab(self.labelWidget, 'IRI') self.metaDataChanged_ADD_OK_var = None self.metaDataChanged_REMOVE_OK_var = None self.metaDataChanged_IGNORE_var = None ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def complete(self): """ Executed when the dialog is accepted. """ commands = [self.positionChanged()] iri_changed_result = self.IRIChanged() if iri_changed_result is not None: if (str(type(iri_changed_result)) is '<class \'str\'>') and ('Error in' in iri_changed_result): super().reject() return else: commands.extend(iri_changed_result) text_changed_result = self.textChanged() if text_changed_result is not None: commands.extend(text_changed_result) if any(commands): self.session.undostack.beginMacro('edit {0} properties'.format( self.node.name)) for command in commands: if command: self.session.undostack.push(command) self.session.undostack.endMacro() super().accept() ############################################# # AUXILIARY METHODS ################################# def textChanged(self): unprocessed_new_text = self.textField.value().strip() unprocessed_new_text = unprocessed_new_text if not isEmpty( unprocessed_new_text) else self.node.label.template exception_list = ['-', '_', '.', '~', '\n'] new_rc = '' flag = False for i, c in enumerate(unprocessed_new_text): if c == '': pass elif i < (len(unprocessed_new_text) - 1) and ( c == '\\' and unprocessed_new_text[i + 1] == 'n'): new_rc = new_rc + '\n' elif i > 0 and (c == 'n' and unprocessed_new_text[i - 1] == '\\'): pass elif (not c.isalnum()) and (c not in exception_list): new_rc = new_rc + '_' flag = True else: new_rc = new_rc + c # new_rc = new_rc.replace('\n','') if flag is True: self.session.statusBar().showMessage( 'Spaces in between alphanumeric characters and special characters were replaced by an underscore character.', 15000) return_list = [] if (unprocessed_new_text != self.node.remaining_characters): # print('unprocessed_new_text',unprocessed_new_text) # print('self.node.remaining_characters',self.node.remaining_characters) # print(NewlineFeedInsensitive(new_rc, self.node.remaining_characters).result()) return_list.append( CommandProjectDisconnectSpecificSignals(self.project)) if self.refactorField.isChecked(): for n in self.project.nodes(): if n.text() == self.node.text(): return_list.append( CommandNodeSetRemainingCharacters( n.remaining_characters, new_rc, n, self.project, refactor=True)) else: # refactor_var = NewlineFeedInsensitive(new_rc, self.node.remaining_characters).result() # return_list.append( # CommandNodeSetRemainingCharacters(self.node.remaining_characters, new_rc, self.node, self.project, refactor=refactor_var)) return_list.append( CommandNodeSetRemainingCharacters( self.node.remaining_characters, new_rc, self.node, self.project)) return_list.append( CommandProjectConnectSpecificSignals(self.project)) return return_list return None def IRIChanged(self): # Change the iri of the node. #:rtype: Command IRI_valid = self.project.check_validity_of_IRI(self.iriField.value()) if IRI_valid is False: self.session.statusBar().showMessage('Invalid IRI.', 15000) return None else: old_iri = self.project.get_iri_of_node(self.node) new_iri = self.iriField.value() # if (self.iriField.value() != self.project.get_iri_of_node(node)) or (self.iriversionField.value() != self.node.IRI_version(self.project)): if new_iri != old_iri: connect(self.project.sgnIRINodeEntryAdded, self.metaDataChanged_ADD_OK) connect(self.project.sgnIRINodeEntryRemoved, self.metaDataChanged_REMOVE_OK) connect(self.project.sgnIRINodeEntryIgnored, self.metaDataChanged_IGNORE) # check for conflict in prefixes # transaction = remove(old) + add(new) # perform transaction on duplicate dict. # if successful, original_dict = duplicate_dict # else duplicate_dict = original_dict Duplicate_dict_1 = self.project.copy_IRI_prefixes_nodes_dictionaries( self.project.IRI_prefixes_nodes_dict, dict()) Duplicate_dict_2 = self.project.copy_IRI_prefixes_nodes_dictionaries( self.project.IRI_prefixes_nodes_dict, dict()) list_of_nodes_to_process = [] if self.refactorField.isChecked(): for n in self.project.nodes(): if (('AttributeNode' in str(type(n))) or ('ConceptNode' in str(type(n))) or ('IndividualNode' in str(type(n))) or ('RoleNode' in str(type(n)))): if (self.project.get_iri_of_node(n) == old_iri ) and (n.remaining_characters == self.node.remaining_characters): list_of_nodes_to_process.append(n) else: list_of_nodes_to_process.append(self.node) commands = [] for nd in list_of_nodes_to_process: self.project.removeIRINodeEntry(Duplicate_dict_1, old_iri, nd) self.project.addIRINodeEntry(Duplicate_dict_1, new_iri, nd) if (self.metaDataChanged_REMOVE_OK_var is True) and ( self.metaDataChanged_ADD_OK_var is True): self.metaDataChanged_REMOVE_OK_var = False self.metaDataChanged_ADD_OK_var = False self.metaDataChanged_IGNORE_var = False else: LOGGER.warning( 'redo != undo but transaction was not executed correctly' ) self.metaDataChanged_REMOVE_OK_var = False self.metaDataChanged_ADD_OK_var = False self.metaDataChanged_IGNORE_var = False return str('Error in ' + str(nd)) if len(Duplicate_dict_1[new_iri][0]) == 0: ### if 'display_in_widget' in Duplicate_dict_1[new_iri][2]: new_label = ':' + self.node.remaining_characters else: new_label = self.project.get_full_IRI( new_iri, None, self.node.remaining_characters) else: new_label = str(Duplicate_dict_1[new_iri][0][ len(Duplicate_dict_1[new_iri][0]) - 1] + ':' + self.node.remaining_characters) commands.append( CommandProjectDisconnectSpecificSignals(self.project)) for nd in list_of_nodes_to_process: commands.append( CommandLabelChange(nd.diagram, nd, nd.text(), new_label)) for nd in list_of_nodes_to_process: commands.append( CommandLabelChange(nd.diagram, nd, nd.text(), new_label)) commands.append( CommandProjectConnectSpecificSignals(self.project)) return commands self.metaDataChanged_REMOVE_OK_var = False self.metaDataChanged_ADD_OK_var = False self.metaDataChanged_IGNORE_var = False return None @QtCore.pyqtSlot(str, str, str) def metaDataChanged_REMOVE_OK(self, iri, node, message): # print('metaDataChanged_REMOVE_OK -', iri, ',', node, ',', message) self.metaDataChanged_REMOVE_OK_var = True @QtCore.pyqtSlot(str, str, str) def metaDataChanged_ADD_OK(self, iri, node, message): # print('metaDataChanged_ADD_OK -', iri, ',', node, ',', message) self.metaDataChanged_ADD_OK_var = True @QtCore.pyqtSlot(str, str, str) def metaDataChanged_IGNORE(self, iri, node, message): # if node.id is None: # print('metaDataChanged_IGNORE >', iri, '-', 'None', '-', message) # else: # print('metaDataChanged_IGNORE >', iri, '-', node, '-', message) self.metaDataChanged_IGNORE_var = True
class RefactorNameForm(QtWidgets.QDialog): """ This class implements the form used to rename nodes during refactor operations. """ def __init__(self, node, session): """ Initialize the form dialog. :type node: AbstractNode :type session: Session """ super().__init__(session) self.node = node ############################################# # FORM AREA ################################# self.renameLabel = QtWidgets.QLabel(self) self.renameLabel.setText('IRI label') self.renameField = StringField(self) self.renameField.setFixedWidth(200) self.renameLabel.setWordWrap(True) match = RE_VALUE.match(self.node.text()) if match: self.renameField.setValue(self.node.text()) else: self.renameField.setValue(self.node.remaining_characters) self.old_text = self.node.text() connect(self.renameField.textChanged, self.nameChanged) self.formWidget = QtWidgets.QWidget(self) self.formLayout = QtWidgets.QFormLayout(self.formWidget) self.formLayout.addRow(self.renameLabel, self.renameField) ############################################# # CONFIRMATION AREA ################################# self.confirmationBox = QtWidgets.QDialogButtonBox( QtCore.Qt.Horizontal, self) self.confirmationBox.addButton(QtWidgets.QDialogButtonBox.Ok) self.confirmationBox.addButton(QtWidgets.QDialogButtonBox.Cancel) self.confirmationBox.setContentsMargins(10, 0, 10, 10) ############################################# # SETUP DIALOG LAYOUT ################################# self.caption = QtWidgets.QLabel(self) self.caption.setContentsMargins(8, 0, 8, 0) self.caption.setProperty('class', 'invalid') self.caption.setVisible(False) self.mainLayout = QtWidgets.QVBoxLayout(self) self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.addWidget(self.formWidget) self.mainLayout.addWidget(self.caption) self.mainLayout.addWidget(self.confirmationBox, 0, QtCore.Qt.AlignRight) self.setFixedSize(self.sizeHint()) self.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) self.setWindowTitle('Rename') connect(self.confirmationBox.accepted, self.accept) connect(self.confirmationBox.rejected, self.reject) ############################################# # PROPERTIES ################################# @property def project(self): """ Returns the reference to the active project. :rtype: Project """ return self.session.project @property def session(self): """ Returns the reference to the active session (alias for RefactorNameForm.parent()). :rtype: Session """ return self.parent() ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def accept(self): """ Accepts the rename form and perform refactoring. """ currentData = self.renameField.value() if currentData and currentData != self.old_text: match = RE_VALUE.match(currentData) match_old = RE_VALUE.match(self.old_text) commands = [] if match: new_prefix = match.group( 'datatype')[0:match.group('datatype').index(':')] new_remaining_characters = match.group( 'datatype')[match.group('datatype').index(':') + 1:len(match.group('datatype'))] new_iri = None for namespace in Namespace: if namespace.name.lower() == new_prefix: new_iri = namespace.value Duplicate_dict_1 = self.project.copy_IRI_prefixes_nodes_dictionaries( self.project.IRI_prefixes_nodes_dict, dict()) Duplicate_dict_2 = self.project.copy_IRI_prefixes_nodes_dictionaries( self.project.IRI_prefixes_nodes_dict, dict()) old_iri = self.project.get_iri_of_node(self.node) list_of_nodes_to_process = [] commands_label_change_list_1 = [] commands_label_change_list_2 = [] commands_rc_change = [] for node in self.project.predicates(self.node.type(), self.node.text()): list_of_nodes_to_process.append(node) Duplicate_dict_1[old_iri][1].remove(node) Duplicate_dict_1[new_iri][1].add(node) commands_label_change_list_1.append( CommandLabelChange(node.diagram, node, self.old_text, currentData, refactor=True)) commands_rc_change.append( CommandNodeSetRemainingCharacters( node.remaining_characters, new_remaining_characters, node, self.project, refactor=True)) commands_label_change_list_2.append( CommandLabelChange(node.diagram, node, self.old_text, currentData, refactor=True)) commands.append( CommandProjectDisconnectSpecificSignals(self.project)) commands.extend(commands_label_change_list_1) commands.extend(commands_rc_change) commands.extend(commands_label_change_list_2) commands.append( CommandProjectConnectSpecificSignals(self.project)) else: exception_list = ['-', '_', '.', '~', '\n'] currentData_processed = '' flag = False for i, c in enumerate(currentData): if c == '': pass elif i < (len(currentData) - 1) and ( c == '\\' and currentData[i + 1] == 'n'): currentData_processed = currentData_processed + '\n' elif i > 0 and (c == 'n' and currentData[i - 1] == '\\'): pass elif (not c.isalnum()) and (c not in exception_list): currentData_processed = currentData_processed + '_' flag = True else: currentData_processed = currentData_processed + c if flag is True: self.session.statusBar().showMessage( 'Spaces in between alphanumeric characters and special characters were replaced by an ' 'underscore character.', 15000) if match_old: new_remaining_characters = currentData_processed new_iri = self.project.ontologyIRIString Duplicate_dict_1 = self.project.copy_IRI_prefixes_nodes_dictionaries( self.project.IRI_prefixes_nodes_dict, dict()) Duplicate_dict_2 = self.project.copy_IRI_prefixes_nodes_dictionaries( self.project.IRI_prefixes_nodes_dict, dict()) old_iri = self.project.get_iri_of_node(self.node) list_of_nodes_to_process = [] commands_label_change_list_1 = [] commands_label_change_list_2 = [] commands_rc_change = [] for node in self.project.predicates( self.node.type(), self.node.text()): list_of_nodes_to_process.append(node) Duplicate_dict_1[old_iri][1].remove(node) Duplicate_dict_1[new_iri][1].add(node) if len(Duplicate_dict_1[new_iri][0]) == 0: new_label = self.project.get_full_IRI( new_iri, None, new_remaining_characters) else: new_label = str(Duplicate_dict_1[new_iri][0][ len(Duplicate_dict_1[new_iri][0]) - 1] + ':' + new_remaining_characters) commands_label_change_list_1.append( CommandLabelChange(node.diagram, node, self.old_text, new_label, refactor=True)) commands_rc_change.append( CommandNodeSetRemainingCharacters( node.remaining_characters, new_remaining_characters, node, self.project, refactor=True)) commands_label_change_list_2.append( CommandLabelChange(node.diagram, node, self.old_text, new_label, refactor=True)) command_dict_change = CommandProjetSetIRIPrefixesNodesDict( self.project, Duplicate_dict_2, Duplicate_dict_1, [old_iri, new_iri], list_of_nodes_to_process) commands.append( CommandProjectDisconnectSpecificSignals(self.project)) commands.extend(commands_label_change_list_1) commands.append(command_dict_change) commands.extend(commands_rc_change) commands.extend(commands_label_change_list_2) commands.append( CommandProjectConnectSpecificSignals(self.project)) else: commands.append( CommandProjectDisconnectSpecificSignals(self.project)) for node in self.project.predicates( self.node.type(), self.node.text()): commands.append( CommandNodeSetRemainingCharacters( node.remaining_characters, currentData_processed, node, self.project, refactor=True)) commands.append( CommandProjectConnectSpecificSignals(self.project)) if any(commands): self.session.undostack.beginMacro( 'change predicate "{0}" to "{1}"'.format( self.node.text(), currentData)) for command in commands: if command: self.session.undostack.push(command) self.session.undostack.endMacro() else: pass super().accept() # not used @QtCore.pyqtSlot() def accept_2(self): """ Accepts the rename form and perform refactoring. """ name = self.renameField.value() self.session.undostack.beginMacro( 'change predicate "{0}" to "{1}"'.format(self.node.text(), name)) for node in self.project.predicates(self.node.type(), self.node.text()): command = CommandLabelChange(node.diagram, node, node.text(), name, refactor=True) self.session.undostack.push(command) self.session.undostack.endMacro() super().accept() @QtCore.pyqtSlot() def nameChanged(self): """ Executed whenever the text in the rename field changes. """ caption = '' enabled = True if isEmpty(self.renameField.value()): caption = "\'{0}\' is not a valid predicate name".format( self.renameField.value()) enabled = False self.caption.setText(caption) self.caption.setVisible(not isEmpty(caption)) self.confirmationBox.button( QtWidgets.QDialogButtonBox.Ok).setEnabled(enabled) self.setFixedSize(self.sizeHint())
class FacetNodeProperty(NodeProperty): """ This class implements the property dialog for facet nodes. """ def __init__(self, diagram, node, session): """ Initialize the node properties dialog. :type diagram: Diagram :type node: AbstractNode :type session: Session """ super().__init__(diagram, node, session) ############################################# # FACET TAB ################################# f1 = lambda x: x.type() is Item.InputEdge f2 = lambda x: x.type() is Item.DatatypeRestrictionNode f3 = lambda x: x.type() is Item.ValueDomainNode admissible = [x for x in Facet] restriction = first(self.node.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2)) if restriction: valuedomain = first(restriction.incomingNodes(filter_on_edges=f1, filter_on_nodes=f3)) if valuedomain: admissible = Facet.forDatatype(valuedomain.datatype) self.facetLabel = QtWidgets.QLabel(self) self.facetLabel.setFont(Font('Roboto', 12)) self.facetLabel.setText('Facet') self.facetField = ComboBox(self) self.facetField.setFixedWidth(200) self.facetField.setFocusPolicy(QtCore.Qt.StrongFocus) self.facetField.setFont(Font('Roboto', 12)) for facet in admissible: self.facetField.addItem(facet.value, facet) facet = self.node.facet for i in range(self.facetField.count()): if self.facetField.itemData(i) is facet: self.facetField.setCurrentIndex(i) break else: self.facetField.setCurrentIndex(0) self.valueLabel = QtWidgets.QLabel(self) self.valueLabel.setFont(Font('Roboto', 12)) self.valueLabel.setText('Value') self.valueField = StringField(self) self.valueField.setFixedWidth(200) self.valueField.setFont(Font('Roboto', 12)) self.valueField.setValue(self.node.value) self.facetWidget = QtWidgets.QWidget() self.facetLayout = QtWidgets.QFormLayout(self.facetWidget) self.facetLayout.addRow(self.facetLabel, self.facetField) self.facetLayout.addRow(self.valueLabel, self.valueField) self.mainWidget.addTab(self.facetWidget, 'Facet') ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def complete(self): """ Executed when the dialog is accepted. """ commands = [self.positionChanged(), self.facetChanged()] if any(commands): self.session.undostack.beginMacro('edit {0} properties'.format(self.node.name)) for command in commands: if command: self.session.undostack.push(command) self.session.undostack.endMacro() super().accept() ############################################# # AUXILIARY METHODS ################################# def facetChanged(self): """ Change the facet value of the node of the node. :rtype: QUndoCommand """ data = self.node.compose(self.facetField.currentData(), self.valueField.value()) if self.node.text() != data: return CommandLabelChange(self.diagram, self.node, self.node.text(), data) return None
class ValueNodeProperty(NodeProperty): """ This class implements the property dialog for value nodes. """ def __init__(self, diagram, node, session): """ Initialize the node properties dialog. :type diagram: Diagram :type node: AbstractNode :type session: Session """ super().__init__(diagram, node, session) ############################################# # VALUE TAB ################################# self.datatypeLabel = QtWidgets.QLabel(self) self.datatypeLabel.setFont(Font('Roboto', 12)) self.datatypeLabel.setText('Datatype') self.datatypeField = ComboBox(self) self.datatypeField.setFixedWidth(200) self.datatypeField.setFocusPolicy(QtCore.Qt.StrongFocus) self.datatypeField.setFont(Font('Roboto', 12)) for datatype in Datatype: self.datatypeField.addItem(datatype.value, datatype) datatype = self.node.datatype for i in range(self.datatypeField.count()): if self.datatypeField.itemData(i) is datatype: self.datatypeField.setCurrentIndex(i) break else: self.datatypeField.setCurrentIndex(0) self.valueLabel = QtWidgets.QLabel(self) self.valueLabel.setFont(Font('Roboto', 12)) self.valueLabel.setText('Value') self.valueField = StringField(self) self.valueField.setFixedWidth(200) self.valueField.setFont(Font('Roboto', 12)) self.valueField.setValue(self.node.value) self.valueWidget = QtWidgets.QWidget() self.valueLayout = QtWidgets.QFormLayout(self.valueWidget) self.valueLayout.addRow(self.datatypeLabel, self.datatypeField) self.valueLayout.addRow(self.valueLabel, self.valueField) self.mainWidget.addTab(self.valueWidget, 'Datatype') ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def complete(self): """ Executed when the dialog is accepted. """ commands = [self.positionChanged(), self.valueChanged()] if any(commands): self.session.undostack.beginMacro('edit {0} properties'.format(self.node.name)) for command in commands: if command: self.session.undostack.push(command) self.session.undostack.endMacro() super().accept() ############################################# # AUXILIARY METHODS ################################# def valueChanged(self): """ Change the value of the node. :rtype: QUndoCommand """ datatype = self.datatypeField.currentData() value = self.valueField.value() data = self.node.compose(value, datatype) if self.node.text() != data: return CommandLabelChange(self.diagram, self.node, self.node.text(), data) return None
class PredicateNodeProperty(NodeProperty): """ This class implements the property dialog for predicate nodes. Note that this dialog window is not used for value-domain nodes even though they are predicate nodes. """ def __init__(self, diagram, node, session): """ Initialize the node properties dialog. :type diagram: Diagram :type node: AbstractNode :type session: Session """ super().__init__(diagram, node, session) meta = diagram.project.meta(node.type(), node.text()) self.urlLabel = QtWidgets.QLabel(self) self.urlLabel.setFont(Font('Roboto', 12)) self.urlLabel.setText('URL') self.urlField = StringField(self) self.urlField.setFixedWidth(300) self.urlField.setFont(Font('Roboto', 12)) self.urlField.setValue(meta.get('url', '')) self.descriptionLabel = QtWidgets.QLabel(self) self.descriptionLabel.setFont(Font('Roboto', 12)) self.descriptionLabel.setText('Description') self.descriptionField = TextField(self) self.descriptionField.setFixedSize(300, 160) self.descriptionField.setFont(Font('Roboto', 12)) self.descriptionField.setValue(meta.get('description', '')) self.generalLayout.addRow(self.urlLabel, self.urlField) self.generalLayout.addRow(self.descriptionLabel, self.descriptionField) ############################################# # LABEL TAB ################################# self.textLabel = QtWidgets.QLabel(self) self.textLabel.setFont(Font('Roboto', 12)) self.textLabel.setText('Text') self.textField = StringField(self) self.textField.setFixedWidth(300) self.textField.setFont(Font('Roboto', 12)) self.textField.setValue(self.node.text()) self.refactorLabel = QtWidgets.QLabel(self) self.refactorLabel.setFont(Font('Roboto', 12)) self.refactorLabel.setText('Refactor') self.refactorField = CheckBox(self) self.refactorField.setFont(Font('Roboto', 12)) self.refactorField.setChecked(False) if node.type() in {Item.AttributeNode, Item.ConceptNode, Item.RoleNode}: if node.special() is not None: self.refactorField.setEnabled(False) self.labelWidget = QtWidgets.QWidget() self.labelLayout = QtWidgets.QFormLayout(self.labelWidget) self.labelLayout.addRow(self.textLabel, self.textField) self.labelLayout.addRow(self.refactorLabel, self.refactorField) self.mainWidget.addTab(self.labelWidget, 'Label') ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def complete(self): """ Executed when the dialog is accepted. """ commands = [self.positionChanged(), self.metaDataChanged()] commands.extend(self.textChanged()) if any(commands): self.session.undostack.beginMacro('edit {0} properties'.format(self.node.name)) for command in commands: if command: self.session.undostack.push(command) self.session.undostack.endMacro() super().accept() ############################################# # AUXILIARY METHODS ################################# def metaDataChanged(self): """ Change the url and description of the node. :rtype: QUndoCommand """ meta = self.diagram.project.meta(self.node.type(), self.node.text()) copy = meta.copy() copy['description'] = self.descriptionField.value() copy['url'] = self.urlField.value() if copy != meta: return CommandNodeChangeMeta(self.diagram, self.node, meta, copy) return None def textChanged(self): """ Change the label of the node. :rtype: list """ data = self.textField.value().strip() data = data if not isEmpty(data) else self.node.label.template if self.node.text() != data: if self.refactorField.isChecked(): item = self.node.type() name = self.node.text() project = self.diagram.project return [CommandLabelChange(n.diagram, n, n.text(), data) for n in project.predicates(item, name)] return [CommandLabelChange(self.diagram, self.node, self.node.text(), data)] return [None]
class NodeProperty(PropertyDialog): """ This class implements the 'Node property' dialog. """ def __init__(self, diagram, node, session): """ Initialize the node properties dialog. :type diagram: Diagram :type node: AbstractNode :type session: Session """ super().__init__(session) self.diagram = diagram self.node = node ############################################# # GENERAL TAB ################################# self.idLabel = QtWidgets.QLabel(self) self.idLabel.setFont(Font('Roboto', 12)) self.idLabel.setText('ID') self.idField = StringField(self) self.idField.setFont(Font('Roboto', 12)) self.idField.setFixedWidth(300) self.idField.setFocusPolicy(QtCore.Qt.NoFocus) self.idField.setReadOnly(True) self.idField.setValue(self.node.id) self.typeLabel = QtWidgets.QLabel(self) self.typeLabel.setFont(Font('Roboto', 12)) self.typeLabel.setText('Type') self.typeField = StringField(self) self.typeField.setFont(Font('Roboto', 12)) self.typeField.setFocusPolicy(QtCore.Qt.NoFocus) self.typeField.setFixedWidth(300) self.typeField.setReadOnly(True) self.typeField.setValue(node.shortName.capitalize()) self.identityLabel = QtWidgets.QLabel(self) self.identityLabel.setFont(Font('Roboto', 12)) self.identityLabel.setText('Identity') self.identityField = StringField(self) self.identityField.setFont(Font('Roboto', 12)) self.identityField.setFocusPolicy(QtCore.Qt.NoFocus) self.identityField.setFixedWidth(300) self.identityField.setReadOnly(True) self.identityField.setValue(self.node.identityName) self.neighboursLabel = QtWidgets.QLabel(self) self.neighboursLabel.setFont(Font('Roboto', 12)) self.neighboursLabel.setText('Neighbours') self.neighboursField = IntegerField(self) self.neighboursField.setFont(Font('Roboto', 12)) self.neighboursField.setFocusPolicy(QtCore.Qt.NoFocus) self.neighboursField.setFixedWidth(300) self.neighboursField.setReadOnly(True) self.neighboursField.setValue(len(self.node.adjacentNodes())) self.generalWidget = QtWidgets.QWidget() self.generalLayout = QtWidgets.QFormLayout(self.generalWidget) self.generalLayout.addRow(self.idLabel, self.idField) self.generalLayout.addRow(self.typeLabel, self.typeField) self.generalLayout.addRow(self.identityLabel, self.identityField) self.generalLayout.addRow(self.neighboursLabel, self.neighboursField) ############################################# # GEOMETRY TAB ################################# nodePos = self.node.pos() sceneRect = self.diagram.sceneRect() self.xLabel = QtWidgets.QLabel(self) self.xLabel.setFont(Font('Roboto', 12)) self.xLabel.setText('X') self.xField = SpinBox(self) self.xField.setFixedWidth(60) self.xField.setFont(Font('Roboto', 12)) self.xField.setRange(sceneRect.left(), sceneRect.right()) self.xField.setValue(int(nodePos.x())) self.yLabel = QtWidgets.QLabel(self) self.yLabel.setFont(Font('Roboto', 12)) self.yLabel.setText('Y') self.yField = SpinBox(self) self.yField.setFixedWidth(60) self.yField.setFont(Font('Roboto', 12)) self.yField.setRange(sceneRect.top(), sceneRect.bottom()) self.yField.setValue(int(nodePos.y())) self.widthLabel = QtWidgets.QLabel(self) self.widthLabel.setFont(Font('Roboto', 12)) self.widthLabel.setText('Width') self.widthField = SpinBox(self) self.widthField.setFixedWidth(60) self.widthField.setFont(Font('Roboto', 12)) self.widthField.setRange(20, sceneRect.width()) self.widthField.setReadOnly(True) self.widthField.setValue(int(self.node.width())) self.heightLabel = QtWidgets.QLabel(self) self.heightLabel.setFont(Font('Roboto', 12)) self.heightLabel.setText('Height') self.heightField = SpinBox(self) self.heightField.setFixedWidth(60) self.heightField.setFont(Font('Roboto', 12)) self.heightField.setRange(20, sceneRect.height()) self.heightField.setReadOnly(True) self.heightField.setValue(int(self.node.height())) self.geometryWidget = QtWidgets.QWidget() self.geometryLayout = QtWidgets.QFormLayout(self.geometryWidget) self.geometryLayout.addRow(self.xLabel, self.xField) self.geometryLayout.addRow(self.yLabel, self.yField) self.geometryLayout.addRow(self.widthLabel, self.widthField) self.geometryLayout.addRow(self.heightLabel, self.heightField) ############################################# # CONFIRMATION BOX ################################# self.confirmationBox = QtWidgets.QDialogButtonBox(QtCore.Qt.Horizontal, self) self.confirmationBox.addButton(QtWidgets.QDialogButtonBox.Ok) self.confirmationBox.addButton(QtWidgets.QDialogButtonBox.Cancel) self.confirmationBox.setContentsMargins(10, 0, 10, 10) self.confirmationBox.setFont(Font('Roboto', 12)) ############################################# # MAIN WIDGET ################################# self.mainWidget = QtWidgets.QTabWidget(self) self.mainWidget.addTab(self.generalWidget, 'General') self.mainWidget.addTab(self.geometryWidget, 'Geometry') self.mainLayout = QtWidgets.QVBoxLayout(self) self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.addWidget(self.mainWidget) self.mainLayout.addWidget(self.confirmationBox, 0, QtCore.Qt.AlignRight) self.setWindowTitle('Properties: {0}'.format(self.node)) self.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) connect(self.confirmationBox.accepted, self.complete) connect(self.confirmationBox.rejected, self.reject) ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def complete(self): """ Executed when the dialog is accepted. """ commands = [self.positionChanged()] if any(commands): self.session.undostack.beginMacro('edit {0} properties'.format(self.node.name)) for command in commands: if command: self.session.undostack.push(command) self.session.undostack.endMacro() super().accept() ############################################# # AUXILIARY METHODS ################################# def positionChanged(self): """ Move the node properly if the position has been changed. :rtype: QUndoCommand """ rect = self.diagram.sceneRect() xPos = clamp(self.xField.value(), rect.left(), rect.right()) yPos = clamp(self.yField.value(), rect.top(), rect.bottom()) pos1 = self.node.pos() pos2 = QtCore.QPointF(xPos, yPos) if pos1 != pos2: node = self.node data = { 'redo': { 'nodes': {node: {'anchors': {k: v + pos2 - pos1 for k, v in node.anchors.items()}, 'pos': pos2}}, 'edges': {}, }, 'undo': { 'nodes': {node: {'anchors': {k: v for k, v in node.anchors.items()}, 'pos': pos1}}, 'edges': {} } } return CommandNodeMove(self.diagram, data) return None
class ValueForm(QtWidgets.QDialog): """ This class implements the form used to select the Value of an Individual node. """ def __init__(self, node, session): """ Initialize the form dialog. :type node: IndividualNode :type session: Session """ super().__init__(session) self.node = node ############################################# # FORM AREA ################################# self.datatypeLabel = QtWidgets.QLabel(self) self.datatypeLabel.setFont(Font('Roboto', 12)) self.datatypeLabel.setText('Datatype') self.datatypeField = ComboBox(self) self.datatypeField.setFont(Font('Roboto', 12)) self.datatypeField.setFixedWidth(300) for datatype in sorted(Datatype.forProfile( self.project.profile.type()), key=attrgetter('value')): self.datatypeField.addItem(datatype.value, datatype) self.valueLabel = QtWidgets.QLabel(self) self.valueLabel.setFont(Font('Roboto', 12)) self.valueLabel.setText('Value') self.valueField = StringField(self) self.valueField.setFixedWidth(300) if node.identity() is Identity.Value: self.valueField.setValue(node.value) datatype = node.datatype for i in range(self.datatypeField.count()): if self.datatypeField.itemData(i) is datatype: self.datatypeField.setCurrentIndex(i) break else: self.valueField.setValue('') self.datatypeField.setCurrentIndex(0) self.formWidget = QtWidgets.QWidget(self) self.formLayout = QtWidgets.QFormLayout(self.formWidget) self.formLayout.addRow(self.datatypeLabel, self.datatypeField) self.formLayout.addRow(self.valueLabel, self.valueField) ############################################# # CONFIRMATION AREA ################################# self.confirmationBox = QtWidgets.QDialogButtonBox( QtCore.Qt.Horizontal, self) self.confirmationBox.addButton(QtWidgets.QDialogButtonBox.Ok) self.confirmationBox.addButton(QtWidgets.QDialogButtonBox.Cancel) self.confirmationBox.setContentsMargins(10, 0, 10, 10) self.confirmationBox.setFont(Font('Roboto', 12)) ############################################# # SETUP DIALOG LAYOUT ################################# self.mainLayout = QtWidgets.QVBoxLayout(self) self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.addWidget(self.formWidget) self.mainLayout.addWidget(self.confirmationBox, 0, QtCore.Qt.AlignRight) self.setFixedSize(self.sizeHint()) self.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) self.setWindowTitle('Compose value') connect(self.confirmationBox.accepted, self.accept) connect(self.confirmationBox.rejected, self.reject) ############################################# # PROPERTIES ################################# @property def project(self): """ Returns the reference to the active project. :rtype: Project """ return self.session.project @property def session(self): """ Returns the reference to the active session (alias for RefactorNameForm.parent()). :rtype: Session """ return self.parent() ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def accept(self): """ Accepts the form and set the new value. """ node = self.node diagram = node.diagram datatype = self.datatypeField.currentData() value = self.valueField.value() data = node.compose(value, datatype) if node.text() != data: name = 'change {0} to {1}'.format(node.text(), data) self.session.undostack.push( CommandLabelChange(diagram, node, node.text(), data, name=name)) super().accept()
class ValueNodeProperty(NodeProperty): """ This class implements the property dialog for value nodes. """ def __init__(self, diagram, node, session): """ Initialize the node properties dialog. :type diagram: Diagram :type node: AbstractNode :type session: Session """ super().__init__(diagram, node, session) self.node = node ############################################# # VALUE TAB ################################# self.datatypeLabel = QtWidgets.QLabel(self) self.datatypeLabel.setText('Datatype') self.datatypeField = ComboBox(self) self.datatypeField.setFixedWidth(200) self.datatypeField.setFocusPolicy(QtCore.Qt.StrongFocus) for datatype in Datatype: self.datatypeField.addItem(datatype.value, datatype) datatype = self.node.datatype for i in range(self.datatypeField.count()): if self.datatypeField.itemData(i) is datatype: self.datatypeField.setCurrentIndex(i) break else: self.datatypeField.setCurrentIndex(0) self.valueLabel = QtWidgets.QLabel(self) self.valueLabel.setText('Value') self.valueField = StringField(self) self.valueField.setFixedWidth(200) self.valueField.setValue(self.node.value) self.valueWidget = QtWidgets.QWidget() self.valueLayout = QtWidgets.QFormLayout(self.valueWidget) self.valueLayout.addRow(self.datatypeLabel, self.datatypeField) self.valueLayout.addRow(self.valueLabel, self.valueField) self.mainWidget.addTab(self.valueWidget, 'Datatype') ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def complete(self): """ Executed when the dialog is accepted. """ commands = [self.positionChanged()] commands_value_changed = self.valueChanged() if commands_value_changed is not None: commands.extend(commands_value_changed) if any(commands): self.session.undostack.beginMacro('edit {0} properties'.format( self.node.name)) for command in commands: if command: self.session.undostack.push(command) self.session.undostack.endMacro() super().accept() ############################################# # AUXILIARY METHODS ################################# def valueChanged(self): """ Change the value of the node. :rtype: QUndoCommand """ datatype = self.datatypeField.currentData() value = self.valueField.value() data = self.node.compose(value, datatype) if self.node.text() != data: new_prefix = datatype.value[0:datatype.value.index(':')] new_remaining_characters = datatype.value[datatype.value.index(':' ) + 1:len(datatype.value)] new_iri = None for namespace in Namespace: if namespace.name.lower() == new_prefix: new_iri = namespace.value Duplicate_dict_1 = self.project.copy_IRI_prefixes_nodes_dictionaries( self.project.IRI_prefixes_nodes_dict, dict()) Duplicate_dict_2 = self.project.copy_IRI_prefixes_nodes_dictionaries( self.project.IRI_prefixes_nodes_dict, dict()) old_iri = self.project.get_iri_of_node(self.node) Duplicate_dict_1[old_iri][1].remove(self.node) Duplicate_dict_1[new_iri][1].add(self.node) commands = [ CommandLabelChange(self.diagram, self.node, self.node.text(), data), CommandNodeSetRemainingCharacters( self.node.remaining_characters, new_remaining_characters, self.node, self.project), CommandLabelChange(self.diagram, self.node, self.node.text(), data) ] return commands return None
class PluginInstallDialog(QtWidgets.QDialog): """ Extends QtWidgets.QDialog providing an interface to install plugins. """ def __init__(self, session): """ Initialize the plugin install dialog. :type session: Session """ super().__init__(session) ############################################# # HEAD AREA ################################# self.headTitle = QtWidgets.QLabel('Install a plugin', self) self.headTitle.setFont(Font('Roboto', 12, bold=True)) self.headDescription = QtWidgets.QLabel( dedent(""" Plugins are software components that add specific features to {0}.<br/> Please select the plugin you wish to install.""".format(APPNAME)), self) self.headDescription.setFont(Font('Roboto', 12)) self.headPix = QtWidgets.QLabel(self) self.headPix.setPixmap( QtGui.QIcon(':/icons/48/ic_extension_black').pixmap(48)) self.headPix.setContentsMargins(0, 0, 0, 0) self.headWidget = QtWidgets.QWidget(self) self.headWidget.setProperty('class', 'head') self.headWidget.setContentsMargins(10, 10, 10, 10) self.headLayoutL = QtWidgets.QVBoxLayout() self.headLayoutL.addWidget(self.headTitle) self.headLayoutL.addWidget(self.headDescription) self.headLayoutL.setContentsMargins(0, 0, 0, 0) self.headLayoutR = QtWidgets.QVBoxLayout() self.headLayoutR.addWidget(self.headPix, 0, QtCore.Qt.AlignRight) self.headLayoutR.setContentsMargins(0, 0, 0, 0) self.headLayoutM = QtWidgets.QHBoxLayout(self.headWidget) self.headLayoutM.addLayout(self.headLayoutL) self.headLayoutM.addLayout(self.headLayoutR) self.headLayoutM.setContentsMargins(0, 0, 0, 0) ############################################# # SELECTION AREA ################################# self.pluginField = StringField(self) self.pluginField.setFont(Font('Roboto', 12)) self.pluginField.setFixedWidth(400) self.pluginField.setReadOnly(True) self.btnBrowse = QtWidgets.QPushButton(self) self.btnBrowse.setFont(Font('Roboto', 12)) self.btnBrowse.setFixedWidth(30) self.btnBrowse.setText('...') self.editLayout = QtWidgets.QHBoxLayout() self.editLayout.setContentsMargins(10, 10, 10, 10) self.editLayout.addWidget(self.pluginField) self.editLayout.addWidget(self.btnBrowse) ############################################# # CONFIRMATION AREA ################################# self.confirmationBox = QtWidgets.QDialogButtonBox( QtWidgets.QDialogButtonBox.Ok, self) self.confirmationBox.setContentsMargins(10, 0, 10, 10) self.confirmationBox.setEnabled(False) self.confirmationBox.setFont(Font('Roboto', 12)) ############################################# # SETUP DIALOG LAYOUT ################################# self.mainLayout = QtWidgets.QVBoxLayout(self) self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.addWidget(self.headWidget) self.mainLayout.addLayout(self.editLayout) self.mainLayout.addWidget(self.confirmationBox, 0, QtCore.Qt.AlignRight) self.setFixedSize(self.sizeHint()) self.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) self.setWindowTitle('Install a plugin') connect(self.btnBrowse.clicked, self.selectPlugin) connect(self.confirmationBox.accepted, self.accept) ############################################# # PROPERTIES ################################# @property def session(self): """ Returns the reference to the main session (alias for PluginInstallDialog.parent()). :rtype: Session """ return self.parent() ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def accept(self): """ Trigger the install of the selected plugin. """ try: spec = self.session.pmanager.install(self.pluginField.value()) except Exception as e: msgbox = QtWidgets.QMessageBox(self) msgbox.setIconPixmap( QtGui.QIcon(':/icons/48/ic_error_outline_black').pixmap(48)) msgbox.setStandardButtons(QtWidgets.QMessageBox.Close) msgbox.setText( '{0} could not install plugin archive <b>{1}</b>: {2}'.format( APPNAME, self.pluginField.value(), e)) msgbox.setDetailedText(format_exception(e)) msgbox.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) msgbox.setWindowTitle('Plugin install failed!') msgbox.exec_() else: plugin_name = spec.get('plugin', 'name') plugin_version = spec.get('plugin', 'version') plugin_author = spec.get('plugin', 'author', fallback='<unknown>') message = dedent( """Successfully installed plugin <b>{0} v{1}</b> by <b>{2}</b>. Please reboot {3} for the plugin to work.""".format( plugin_name, plugin_version, plugin_author, APPNAME)) msgbox = QtWidgets.QMessageBox(self) msgbox.setIconPixmap( QtGui.QIcon(':/icons/48/ic_done_black').pixmap(48)) msgbox.setStandardButtons(QtWidgets.QMessageBox.Close) msgbox.setText(message) msgbox.setTextFormat(QtCore.Qt.RichText) msgbox.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) msgbox.setWindowTitle('Plugin installed!') msgbox.exec_() super().accept() @QtCore.pyqtSlot() def selectPlugin(self): """ Bring up a modal window that allows the user to choose a valid plugin archive. """ path = os.path.dirname(self.pluginField.value()) if not isPathValid(path): path = expandPath('~') dialog = QtWidgets.QFileDialog(self) dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen) dialog.setDirectory(path) dialog.setFileMode(QtWidgets.QFileDialog.ExistingFile) dialog.setViewMode(QtWidgets.QFileDialog.Detail) dialog.setNameFilters([File.Zip.value]) if dialog.exec_() == QtWidgets.QFileDialog.Accepted: self.pluginField.setValue(first(dialog.selectedFiles())) self.confirmationBox.setEnabled( not isEmpty(self.pluginField.value()))
class NodeProperty(PropertyDialog): """ This class implements the 'Node property' dialog. """ def __init__(self, diagram, node, session): """ Initialize the node properties dialog. :type diagram: Diagram :type node: AbstractNode :type session: Session """ super().__init__(session) self.diagram = diagram self.node = node ############################################# # GENERAL TAB ################################# self.idLabel = QtWidgets.QLabel(self) self.idLabel.setText('ID') self.idField = StringField(self) self.idField.setFixedWidth(300) self.idField.setFocusPolicy(QtCore.Qt.NoFocus) self.idField.setReadOnly(True) self.idField.setValue(self.node.id) self.typeLabel = QtWidgets.QLabel(self) self.typeLabel.setText('Type') self.typeField = StringField(self) self.typeField.setFocusPolicy(QtCore.Qt.NoFocus) self.typeField.setFixedWidth(300) self.typeField.setReadOnly(True) self.typeField.setValue(node.shortName.capitalize()) self.identityLabel = QtWidgets.QLabel(self) self.identityLabel.setText('Identity') self.identityField = StringField(self) self.identityField.setFocusPolicy(QtCore.Qt.NoFocus) self.identityField.setFixedWidth(300) self.identityField.setReadOnly(True) self.identityField.setValue(self.node.identityName) self.neighboursLabel = QtWidgets.QLabel(self) self.neighboursLabel.setText('Neighbours') self.neighboursField = IntegerField(self) self.neighboursField.setFocusPolicy(QtCore.Qt.NoFocus) self.neighboursField.setFixedWidth(300) self.neighboursField.setReadOnly(True) self.neighboursField.setValue(len(self.node.adjacentNodes())) self.generalWidget = QtWidgets.QWidget() self.generalLayout = QtWidgets.QFormLayout(self.generalWidget) self.generalLayout.addRow(self.idLabel, self.idField) self.generalLayout.addRow(self.typeLabel, self.typeField) self.generalLayout.addRow(self.identityLabel, self.identityField) self.generalLayout.addRow(self.neighboursLabel, self.neighboursField) ############################################# # GEOMETRY TAB ################################# nodePos = self.node.pos() sceneRect = self.diagram.sceneRect() self.xLabel = QtWidgets.QLabel(self) self.xLabel.setText('X') self.xField = SpinBox(self) self.xField.setFixedWidth(60) self.xField.setRange(sceneRect.left(), sceneRect.right()) self.xField.setValue(int(nodePos.x())) self.yLabel = QtWidgets.QLabel(self) self.yLabel.setText('Y') self.yField = SpinBox(self) self.yField.setFixedWidth(60) self.yField.setRange(sceneRect.top(), sceneRect.bottom()) self.yField.setValue(int(nodePos.y())) self.widthLabel = QtWidgets.QLabel(self) self.widthLabel.setText('Width') self.widthField = SpinBox(self) self.widthField.setFixedWidth(60) self.widthField.setRange(20, sceneRect.width()) self.widthField.setReadOnly(True) self.widthField.setValue(int(self.node.width())) self.heightLabel = QtWidgets.QLabel(self) self.heightLabel.setText('Height') self.heightField = SpinBox(self) self.heightField.setFixedWidth(60) self.heightField.setRange(20, sceneRect.height()) self.heightField.setReadOnly(True) self.heightField.setValue(int(self.node.height())) self.geometryWidget = QtWidgets.QWidget() self.geometryLayout = QtWidgets.QFormLayout(self.geometryWidget) self.geometryLayout.addRow(self.xLabel, self.xField) self.geometryLayout.addRow(self.yLabel, self.yField) self.geometryLayout.addRow(self.widthLabel, self.widthField) self.geometryLayout.addRow(self.heightLabel, self.heightField) ############################################# # CONFIRMATION BOX ################################# self.confirmationBox = QtWidgets.QDialogButtonBox( QtCore.Qt.Horizontal, self) self.confirmationBox.addButton(QtWidgets.QDialogButtonBox.Ok) self.confirmationBox.addButton(QtWidgets.QDialogButtonBox.Cancel) self.confirmationBox.setContentsMargins(10, 0, 10, 10) ############################################# # MAIN WIDGET ################################# self.mainWidget = QtWidgets.QTabWidget(self) self.mainWidget.addTab(self.generalWidget, 'General') self.mainWidget.addTab(self.geometryWidget, 'Geometry') self.mainLayout = QtWidgets.QVBoxLayout(self) self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.addWidget(self.mainWidget) self.mainLayout.addWidget(self.confirmationBox, 0, QtCore.Qt.AlignRight) self.setWindowTitle('Properties: {0}'.format(self.node)) self.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) connect(self.confirmationBox.accepted, self.complete) connect(self.confirmationBox.rejected, self.reject) ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def complete(self): """ Executed when the dialog is accepted. """ commands = [self.positionChanged()] if any(commands): self.session.undostack.beginMacro('edit {0} properties'.format( self.node.name)) for command in commands: if command: self.session.undostack.push(command) self.session.undostack.endMacro() super().accept() ############################################# # AUXILIARY METHODS ################################# def positionChanged(self): """ Move the node properly if the position has been changed. :rtype: QUndoCommand """ rect = self.diagram.sceneRect() xPos = clamp(self.xField.value(), rect.left(), rect.right()) yPos = clamp(self.yField.value(), rect.top(), rect.bottom()) pos1 = self.node.pos() pos2 = QtCore.QPointF(xPos, yPos) if pos1 != pos2: initData = self.diagram.setupMove([self.node]) moveData = self.diagram.completeMove(initData, pos2 - pos1) return CommandNodeMove(self.diagram, initData, moveData) return None
class PluginInstallDialog(QtWidgets.QDialog): """ Extends QtWidgets.QDialog providing an interface to install plugins. """ def __init__(self, session): """ Initialize the plugin install dialog. :type session: Session """ super().__init__(session) ############################################# # HEAD AREA ################################# self.headTitle = QtWidgets.QLabel('Install a plugin', self) self.headTitle.setFont(Font('Roboto', 12, bold=True)) self.headDescription = QtWidgets.QLabel(dedent(""" Plugins are software components that add specific features to {0}.<br/> Please select the plugin you wish to install.""".format(APPNAME)), self) self.headDescription.setFont(Font('Roboto', 12)) self.headPix = QtWidgets.QLabel(self) self.headPix.setPixmap(QtGui.QIcon(':/icons/48/ic_extension_black').pixmap(48)) self.headPix.setContentsMargins(0, 0, 0, 0) self.headWidget = QtWidgets.QWidget(self) self.headWidget.setProperty('class', 'head') self.headWidget.setContentsMargins(10, 10, 10, 10) self.headLayoutL = QtWidgets.QVBoxLayout() self.headLayoutL.addWidget(self.headTitle) self.headLayoutL.addWidget(self.headDescription) self.headLayoutL.setContentsMargins(0, 0, 0, 0) self.headLayoutR = QtWidgets.QVBoxLayout() self.headLayoutR.addWidget(self.headPix, 0, QtCore.Qt.AlignRight) self.headLayoutR.setContentsMargins(0, 0, 0, 0) self.headLayoutM = QtWidgets.QHBoxLayout(self.headWidget) self.headLayoutM.addLayout(self.headLayoutL) self.headLayoutM.addLayout(self.headLayoutR) self.headLayoutM.setContentsMargins(0, 0, 0, 0) ############################################# # SELECTION AREA ################################# self.pluginField = StringField(self) self.pluginField.setFont(Font('Roboto', 12)) self.pluginField.setFixedWidth(400) self.pluginField.setReadOnly(True) self.btnBrowse = QtWidgets.QPushButton(self) self.btnBrowse.setFont(Font('Roboto', 12)) self.btnBrowse.setFixedWidth(30) self.btnBrowse.setText('...') self.editLayout = QtWidgets.QHBoxLayout() self.editLayout.setContentsMargins(10, 10, 10, 10) self.editLayout.addWidget(self.pluginField) self.editLayout.addWidget(self.btnBrowse) ############################################# # CONFIRMATION AREA ################################# self.confirmationBox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok, self) self.confirmationBox.setContentsMargins(10, 0, 10, 10) self.confirmationBox.setEnabled(False) self.confirmationBox.setFont(Font('Roboto', 12)) ############################################# # SETUP DIALOG LAYOUT ################################# self.mainLayout = QtWidgets.QVBoxLayout(self) self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.addWidget(self.headWidget) self.mainLayout.addLayout(self.editLayout) self.mainLayout.addWidget(self.confirmationBox, 0, QtCore.Qt.AlignRight) self.setFixedSize(self.sizeHint()) self.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) self.setWindowTitle('Install a plugin') connect(self.btnBrowse.clicked, self.selectPlugin) connect(self.confirmationBox.accepted, self.accept) ############################################# # PROPERTIES ################################# @property def session(self): """ Returns the reference to the main session (alias for PluginInstallDialog.parent()). :rtype: Session """ return self.parent() ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def accept(self): """ Trigger the install of the selected plugin. """ try: spec = self.session.pmanager.install(self.pluginField.value()) except Exception as e: msgbox = QtWidgets.QMessageBox(self) msgbox.setIconPixmap(QtGui.QIcon(':/icons/48/ic_error_outline_black').pixmap(48)) msgbox.setStandardButtons(QtWidgets.QMessageBox.Close) msgbox.setText('{0} could not install plugin archive <b>{1}</b>: {2}'.format(APPNAME, self.pluginField.value(), e)) msgbox.setDetailedText(format_exception(e)) msgbox.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) msgbox.setWindowTitle('Plugin install failed!') msgbox.exec_() else: plugin_name = spec.get('plugin', 'name') plugin_version = spec.get('plugin', 'version') plugin_author = spec.get('plugin', 'author', fallback='<unknown>') message = dedent("""Successfully installed plugin <b>{0} v{1}</b> by <b>{2}</b>. Please reboot {3} for the plugin to work.""".format(plugin_name, plugin_version, plugin_author, APPNAME)) msgbox = QtWidgets.QMessageBox(self) msgbox.setIconPixmap(QtGui.QIcon(':/icons/48/ic_done_black').pixmap(48)) msgbox.setStandardButtons(QtWidgets.QMessageBox.Close) msgbox.setText(message) msgbox.setTextFormat(QtCore.Qt.RichText) msgbox.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) msgbox.setWindowTitle('Plugin installed!') msgbox.exec_() super().accept() @QtCore.pyqtSlot() def selectPlugin(self): """ Bring up a modal window that allows the user to choose a valid plugin archive. """ path = os.path.dirname(self.pluginField.value()) if not isPathValid(path): path = expandPath('~') dialog = QtWidgets.QFileDialog(self) dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen) dialog.setDirectory(path) dialog.setFileMode(QtWidgets.QFileDialog.ExistingFile) dialog.setViewMode(QtWidgets.QFileDialog.Detail) dialog.setNameFilters([File.Zip.value]) if dialog.exec_() == QtWidgets.QFileDialog.Accepted: self.pluginField.setValue(first(dialog.selectedFiles())) self.confirmationBox.setEnabled(not isEmpty(self.pluginField.value()))
class FacetNodeProperty(NodeProperty): """ This class implements the property dialog for facet nodes. """ def __init__(self, diagram, node, session): """ Initialize the node properties dialog. :type diagram: Diagram :type node: AbstractNode :type session: Session """ super().__init__(diagram, node, session) ############################################# # FACET TAB ################################# f1 = lambda x: x.type() is Item.InputEdge f2 = lambda x: x.type() is Item.DatatypeRestrictionNode f3 = lambda x: x.type() is Item.ValueDomainNode admissible = [x for x in Facet] restriction = first( self.node.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2)) if restriction: valuedomain = first( restriction.incomingNodes(filter_on_edges=f1, filter_on_nodes=f3)) if valuedomain: admissible = Facet.forDatatype(valuedomain.datatype) self.facetLabel = QtWidgets.QLabel(self) self.facetLabel.setText('Facet') self.facetField = ComboBox(self) self.facetField.setFixedWidth(200) self.facetField.setFocusPolicy(QtCore.Qt.StrongFocus) for facet in admissible: self.facetField.addItem(facet.value, facet) facet = self.node.facet for i in range(self.facetField.count()): if self.facetField.itemData(i) is facet: self.facetField.setCurrentIndex(i) break else: self.facetField.setCurrentIndex(0) self.valueLabel = QtWidgets.QLabel(self) self.valueLabel.setText('Value') self.valueField = StringField(self) self.valueField.setFixedWidth(200) self.valueField.setValue(self.node.value) self.facetWidget = QtWidgets.QWidget() self.facetLayout = QtWidgets.QFormLayout(self.facetWidget) self.facetLayout.addRow(self.facetLabel, self.facetField) self.facetLayout.addRow(self.valueLabel, self.valueField) self.mainWidget.addTab(self.facetWidget, 'Facet') ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def complete(self): """ Executed when the dialog is accepted. """ commands = [self.positionChanged(), self.facetChanged()] if any(commands): self.session.undostack.beginMacro('edit {0} properties'.format( self.node.name)) for command in commands: if command: self.session.undostack.push(command) self.session.undostack.endMacro() super().accept() ############################################# # AUXILIARY METHODS ################################# def facetChanged(self): """ Change the facet value of the node of the node. :rtype: QUndoCommand """ data = self.node.compose(self.facetField.currentData(), self.valueField.value()) if self.node.text() != data: return CommandLabelChange(self.diagram, self.node, self.node.text(), data) return None
class RenameForm(QDialog): """ This class implements the form used to rename nodes during refactor operations. """ def __init__(self, node, parent=None): """ Initialize the form dialog. :type node: AbstractNode :type parent: QWidget """ super().__init__(parent) self.node = node self.renameField = StringField(self) self.renameField.setFixedWidth(200) self.renameField.setValue(self.node.text()) self.invalidName = QLabel('\'\' is not a valid predicate name', self) self.invalidName.setProperty('class', 'invalid') self.invalidName.setVisible(False) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.mainLayout = QFormLayout(self) self.mainLayout.addRow('Name', self.renameField) self.mainLayout.addRow(self.invalidName) self.mainLayout.addRow(self.buttonBox) self.setWindowTitle('Rename') self.setWindowIcon(QIcon(':/images/eddy')) self.setFixedSize(self.sizeHint()) connect(self.buttonBox.accepted, self.validate) connect(self.buttonBox.rejected, self.reject) connect(self.renameField.textChanged, self.nameChanged) #################################################################################################################### # # # SLOTS # # # #################################################################################################################### @pyqtSlot() def nameChanged(self): """ Executed whenever the text in the rename field changes. """ button = self.buttonBox.button(QDialogButtonBox.Ok) empty = isEmpty(self.renameField.value()) button.setDisabled(empty) self.invalidName.setVisible(empty) self.setFixedSize(self.sizeHint()) @pyqtSlot() def validate(self): """ Validate the form and trigger accept() if the form is valid. """ if isEmpty(self.renameField.value()): msgbox = QMessageBox(self) msgbox.setIconPixmap(QPixmap(':/icons/warning')) msgbox.setWindowIcon(QIcon(':/images/eddy')) msgbox.setWindowTitle('Invalid predicate') msgbox.setText('You specified an invalid predicate name!') msgbox.setStandardButtons(QMessageBox.Ok) msgbox.exec_() return # This will strip out leading/trailing whitespaces. self.renameField.setValue(self.renameField.value()) self.accept()
class ValueForm(QtWidgets.QDialog): """ This class implements the form used to select the Value of an Individual node. """ def __init__(self, node, session): """ Initialize the form dialog. :type node: IndividualNode :type session: Session """ super().__init__(session) self.node = node ############################################# # FORM AREA ################################# self.datatypeLabel = QtWidgets.QLabel(self) self.datatypeLabel.setText('Datatype') self.datatypeField = ComboBox(self) self.datatypeField.setFixedWidth(300) for datatype in sorted(Datatype.forProfile( self.project.profile.type()), key=attrgetter('value')): self.datatypeField.addItem(datatype.value, datatype) self.valueLabel = QtWidgets.QLabel(self) self.valueLabel.setText('Value') self.valueField = StringField(self) self.valueField.setFixedWidth(300) if node.identity() is Identity.Value: self.valueField.setValue(node.value) datatype = node.datatype for i in range(self.datatypeField.count()): if self.datatypeField.itemData(i) is datatype: self.datatypeField.setCurrentIndex(i) break else: self.valueField.setValue('') self.datatypeField.setCurrentIndex(0) self.formWidget = QtWidgets.QWidget(self) self.formLayout = QtWidgets.QFormLayout(self.formWidget) self.formLayout.addRow(self.datatypeLabel, self.datatypeField) self.formLayout.addRow(self.valueLabel, self.valueField) ############################################# # CONFIRMATION AREA ################################# self.confirmationBox = QtWidgets.QDialogButtonBox( QtCore.Qt.Horizontal, self) self.confirmationBox.addButton(QtWidgets.QDialogButtonBox.Ok) self.confirmationBox.addButton(QtWidgets.QDialogButtonBox.Cancel) self.confirmationBox.setContentsMargins(10, 0, 10, 10) ############################################# # SETUP DIALOG LAYOUT ################################# self.mainLayout = QtWidgets.QVBoxLayout(self) self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.addWidget(self.formWidget) self.mainLayout.addWidget(self.confirmationBox, 0, QtCore.Qt.AlignRight) self.setFixedSize(self.sizeHint()) self.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) self.setWindowTitle('Compose value') connect(self.confirmationBox.accepted, self.accept) connect(self.confirmationBox.rejected, self.reject) ############################################# # PROPERTIES ################################# @property def project(self): """ Returns the reference to the active project. :rtype: Project """ return self.session.project @property def session(self): """ Returns the reference to the active session (alias for RefactorNameForm.parent()). :rtype: Session """ return self.parent() ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def accept(self): """ Accepts the form and set the new value. """ # print('>>> ValueForm (accept)') node = self.node diagram = node.diagram datatype = self.datatypeField.currentData() value = self.valueField.value() data = node.compose(value, datatype) if node.text() != data: name = 'change {0} to {1}'.format(node.text(), data) new_prefix = datatype.value[0:datatype.value.index(':')] new_remaining_characters = datatype.value[datatype.value.index(':' ) + 1:len(datatype.value)] new_iri = None for namespace in Namespace: if namespace.name.lower() == new_prefix: new_iri = namespace.value if new_iri is None: LOGGER.error( '***************** failed to assign iri to node *******************' ) return Duplicate_dict_1 = self.project.copy_IRI_prefixes_nodes_dictionaries( self.project.IRI_prefixes_nodes_dict, dict()) Duplicate_dict_2 = self.project.copy_IRI_prefixes_nodes_dictionaries( self.project.IRI_prefixes_nodes_dict, dict()) old_iri = self.project.get_iri_of_node(node) Duplicate_dict_1[old_iri][1].remove(node) Duplicate_dict_1[new_iri][1].add(node) commands = [] commands.append( CommandProjectDisconnectSpecificSignals(self.project)) commands.append( CommandLabelChange(diagram, self.node, self.node.text(), data)) commands.append( CommandNodeSetRemainingCharacters(node.remaining_characters, new_remaining_characters, node, self.project)) commands.append( CommandLabelChange(diagram, self.node, self.node.text(), data)) commands.append(CommandProjectConnectSpecificSignals(self.project)) if any(commands): self.session.undostack.beginMacro( 'edit Forms >> accept() {0}'.format(node)) for command in commands: if command: self.session.undostack.push(command) self.session.undostack.endMacro() super().accept()
class ValueRestrictionForm(QDialog): """ This class implements the form used to select the restriction of a datatype. """ # noinspection PyUnresolvedReferences def __init__(self, node, parent=None): """ Initialize the form dialog. :type node: ValueRestrictionNode :type parent: QWidget """ super().__init__(parent) # DATATYPE COMBO BOX self.datatypeField = ComboBox(self) for datatype in XsdDatatype: # hide unrestrictable elements. if Facet.forDatatype(datatype): self.datatypeField.addItem(datatype.value, datatype) datatype = node.datatype for i in range(self.datatypeField.count()): if self.datatypeField.itemData(i) is datatype: self.datatypeField.setCurrentIndex(i) break # FACET COMBO BOX self.facetField = ComboBox(self) for facet in Facet.forDatatype(datatype): self.facetField.addItem(facet.value, facet) facet = node.facet for i in range(self.facetField.count()): if self.facetField.itemData(i) is facet: self.facetField.setCurrentIndex(i) break # VALUE STRING FIELD self.valueField = StringField(self) self.valueField.setFixedWidth(300) self.valueField.setValue(node.value) # CONFIRMATION BOX self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.mainLayout = QFormLayout(self) self.mainLayout.addRow('Datatype', self.datatypeField) self.mainLayout.addRow('Facet', self.facetField) self.mainLayout.addRow('Value', self.valueField) self.mainLayout.addRow(self.buttonBox) self.setWindowTitle('Compose value restriction') self.setWindowIcon(QIcon(':/images/eddy')) self.setFixedSize(self.sizeHint()) connect(self.buttonBox.accepted, self.accept) connect(self.buttonBox.rejected, self.reject) connect(self.datatypeField.currentIndexChanged[int], self.datatypeFieldChanged) #################################################################################################################### # # # SLOTS # # # #################################################################################################################### @pyqtSlot(int) def datatypeFieldChanged(self, index): """ Executed whenever the index of the datatype field changes. :type index: int """ currentFacet = self.facetField.currentData() self.facetField.clear() for facet in Facet.forDatatype(self.datatypeField.itemData(index)): self.facetField.addItem(facet.value, facet) for i in range(self.facetField.count()): if self.facetField.itemData(i) is currentFacet: self.facetField.setCurrentIndex(i) break else: self.facetField.setCurrentIndex(0)
class SceneProperty(QDialog): """ This class implements the 'Scene properties' dialog. """ def __init__(self, scene, parent=None): """ Initialize the scene properties dialog. :type scene: DiagramScene :type parent: QWidget """ super().__init__(parent) self.scene = scene self.mainWidget = QTabWidget(self) ################################################################################################################ # # # GENERAL TAB # # # ################################################################################################################ self.generalWidget = QWidget() self.generalLayout = QFormLayout(self.generalWidget) # Amount of nodes in the scene self.nodesField = IntField(self.generalWidget) self.nodesField.setReadOnly(True) self.nodesField.setFixedWidth(300) self.nodesField.setValue(len(self.scene.nodes())) # Amount of edges in the scene self.edgesField = IntField(self.generalWidget) self.nodesField.setReadOnly(True) self.edgesField.setFixedWidth(300) self.edgesField.setValue(len(self.scene.edges())) self.generalLayout.addRow('N° nodes', self.nodesField) self.generalLayout.addRow('N° edges', self.edgesField) self.mainWidget.addTab(self.generalWidget, 'General') ################################################################################################################ # # # GEOMETRY TAB # # # ################################################################################################################ self.geometryWidget = QWidget() self.geometryLayout = QFormLayout(self.geometryWidget) R = self.scene.sceneRect() self.sceneSizeField = SpinBox(self) self.sceneSizeField.setRange(self.scene.MinSize, self.scene.MaxSize) self.sceneSizeField.setSingleStep(100) self.sceneSizeField.setValue(R.width()) self.geometryLayout.addRow('Size', self.sceneSizeField) self.mainWidget.addTab(self.geometryWidget, 'Geometry') ################################################################################################################ # # # DOCUMENT WIDGET # # # ################################################################################################################ if self.scene.document.path: self.documentWidget = QWidget() self.documentLayout = QFormLayout(self.documentWidget) # Filepath of the saved document. self.pathField = StringField(self.documentWidget) self.pathField.setReadOnly(True) self.pathField.setFixedWidth(300) self.pathField.setValue(self.scene.document.path) # Timestamp when the document has been last modified. self.editedField = StringField(self.documentWidget) self.editedField.setReadOnly(True) self.editedField.setFixedWidth(300) self.editedField.setValue( datetime.fromtimestamp(int( self.scene.document.edited)).strftime('%Y/%m/%d %H:%M:%S')) self.documentLayout.addRow('File', self.pathField) self.documentLayout.addRow('Last edit', self.editedField) self.mainWidget.addTab(self.documentWidget, 'Document') ################################################################################################################ # # # BUTTON BOX # # # ################################################################################################################ self.buttonBox = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) ################################################################################################################ # # # MAIN LAYOUT # # # ################################################################################################################ self.mainLayout = QVBoxLayout(self) self.mainLayout.addWidget(self.mainWidget) self.mainLayout.addWidget(self.buttonBox, 0, Qt.AlignRight) self.setFixedSize(self.sizeHint()) self.setWindowTitle('Scene properties') self.setWindowIcon(QIcon(':/images/eddy')) ################################################################################################################ # # # CONFIGURE SIGNALS # # # ################################################################################################################ connect(self.finished, self.completed) connect(self.buttonBox.accepted, self.accept) connect(self.buttonBox.rejected, self.reject) #################################################################################################################### # # # SLOTS # # # #################################################################################################################### @pyqtSlot(int) def completed(self, code): """ Executed when the dialog is terminated. :type code: int """ if code == QDialog.Accepted: self.sceneSizeChanged() #################################################################################################################### # # # AUXILIARY METHODS # # # #################################################################################################################### def sceneSizeChanged(self): """ Change the size of the DiagramScene rectangle. """ size1 = self.scene.sceneRect().width() size2 = self.sceneSizeField.value() if size1 != size2: # see if the new size is sufficient to contain all the elements in the scene items = self.scene.items() if len(items) > 0: X = set() Y = set() for item in items: B = item.mapRectToScene(item.boundingRect()) X |= {B.left(), B.right()} Y |= {B.top(), B.bottom()} # clamp size2 so that all the elements in the scene stays visible size2 = max(size2, abs(min(X) * 2), abs(max(X) * 2), abs(min(Y) * 2), abs(max(Y) * 2)) self.scene.undostack.push( CommandSceneResize( self.scene, QRectF(-size2 / 2, -size2 / 2, size2, size2)))
class WorkspaceDialog(QtWidgets.QDialog): """ This class can be used to setup the workspace path. """ def __init__(self, parent=None): """ Initialize the workspace dialog. :type parent: QtWidgets.QWidget """ super().__init__(parent) ############################################# # HEAD AREA ################################# self.headTitle = QtWidgets.QLabel('Select a workspace', self) self.headTitle.setFont(Font('Roboto', 12, bold=True)) self.headDescription = QtWidgets.QLabel(dedent(""" {0} stores your projects in a directory called workspace.<br/> Please choose a workspace directory to use.""".format(APPNAME)), self) self.headDescription.setFont(Font('Roboto', 12)) self.headPix = QtWidgets.QLabel(self) self.headPix.setPixmap(QtGui.QIcon(':/icons/128/ic_eddy').pixmap(48)) self.headPix.setContentsMargins(0, 0, 0, 0) self.headWidget = QtWidgets.QWidget(self) self.headWidget.setProperty('class', 'head') self.headWidget.setContentsMargins(10, 10, 10, 10) self.headLayoutL = QtWidgets.QVBoxLayout() self.headLayoutL.addWidget(self.headTitle) self.headLayoutL.addWidget(self.headDescription) self.headLayoutL.setContentsMargins(0, 0, 0, 0) self.headLayoutR = QtWidgets.QVBoxLayout() self.headLayoutR.addWidget(self.headPix, 0, QtCore.Qt.AlignRight) self.headLayoutR.setContentsMargins(0, 0, 0, 0) self.headLayoutM = QtWidgets.QHBoxLayout(self.headWidget) self.headLayoutM.addLayout(self.headLayoutL) self.headLayoutM.addLayout(self.headLayoutR) self.headLayoutM.setContentsMargins(0, 0, 0, 0) ############################################# # EDIT AREA ################################# self.workspaceField = StringField(self) self.workspaceField.setFont(Font('Roboto', 12)) self.workspaceField.setFixedWidth(400) self.workspaceField.setReadOnly(True) self.workspaceField.setText(expandPath(WORKSPACE)) self.btnBrowse = QtWidgets.QPushButton(self) self.btnBrowse.setFont(Font('Roboto', 12)) self.btnBrowse.setFixedWidth(30) self.btnBrowse.setText('...') self.editLayout = QtWidgets.QHBoxLayout() self.editLayout.setContentsMargins(10, 10, 10, 10) self.editLayout.addWidget(self.workspaceField) self.editLayout.addWidget(self.btnBrowse) ############################################# # CONFIRMATION AREA ################################# self.confirmationBox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok, self) self.confirmationBox.setContentsMargins(10, 0, 10, 10) self.confirmationBox.setFont(Font('Roboto', 12)) ############################################# # SETUP DIALOG LAYOUT ################################# self.mainLayout = QtWidgets.QVBoxLayout(self) self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.addWidget(self.headWidget) self.mainLayout.addLayout(self.editLayout) self.mainLayout.addWidget(self.confirmationBox, 0, QtCore.Qt.AlignRight) self.setFixedSize(self.sizeHint()) self.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) self.setWindowTitle('Configure workspace') connect(self.btnBrowse.clicked, self.choosePath) connect(self.confirmationBox.accepted, self.accept) ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def accept(self): """ Create Eddy workspace (if necessary). """ path = self.workspaceField.value() try: mkdir(path) except Exception as e: msgbox = QtWidgets.QMessageBox(self) msgbox.setDetailedText(format_exception(e)) msgbox.setIconPixmap(QtGui.QIcon(':/icons/48/ic_error_outline_black').pixmap(48)) msgbox.setStandardButtons(QtWidgets.QMessageBox.Close) msgbox.setText('{0} could not create the specified workspace: {1}!'.format(APPNAME, path)) msgbox.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) msgbox.setWindowTitle('Workspace setup failed!') msgbox.exec_() super().reject() else: settings = QtCore.QSettings(ORGANIZATION, APPNAME) settings.setValue('workspace/home', path) settings.sync() super().accept() @QtCore.pyqtSlot() def choosePath(self): """ Bring up a modal window that allows the user to choose a valid workspace path. """ path = self.workspaceField.value() if not isPathValid(path): path = expandPath('~') dialog = QtWidgets.QFileDialog(self) dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen) dialog.setDirectory(path) dialog.setFileMode(QtWidgets.QFileDialog.Directory) dialog.setOption(QtWidgets.QFileDialog.ShowDirsOnly, True) dialog.setViewMode(QtWidgets.QFileDialog.Detail) if dialog.exec_() == QtWidgets.QFileDialog.Accepted: self.workspaceField.setValue(first(dialog.selectedFiles()))