class ErrorWidget(QtWidgets.QWidget): def __init__(self, session, location='', problemDescription='', parent=None): """ Initialize the base information box. :type session: Session :type parent: QtWidgets.QWidget """ super().__init__(parent) self.session = session msgLabel = QtWidgets.QLabel(self) msgLabel.setText('Problems encountered while verifying import:') self.locationLabel = QtWidgets.QLabel(self) self.locationLabel.setText('Location: ') self.locationText = StringField(self) self.locationText.setValue('{}'.format(location)) self.locationText.setReadOnly(True) self.problemLabel = QtWidgets.QLabel(self) self.problemLabel.setText('Problem: ') self.problemTextArea = QtWidgets.QPlainTextEdit(self) self.problemTextArea.setPlainText('{}'.format(problemDescription)) self.problemTextArea.setReadOnly(True) formlayout = QtWidgets.QFormLayout() formlayout.addRow(self.locationLabel,self.locationText) formlayout.addRow(self.problemLabel,self.problemTextArea) groupbox = QtWidgets.QGroupBox(parent=self) groupbox.setLayout(formlayout) boxlayout = QtWidgets.QHBoxLayout() boxlayout.setAlignment(QtCore.Qt.AlignCenter) boxlayout.addWidget(msgLabel) boxlayout.addWidget(groupbox) groupbox = QtWidgets.QGroupBox('Revision needed', self) groupbox.setLayout(boxlayout) outerFormLayout = QtWidgets.QFormLayout() outerFormLayout.addRow(groupbox) self.setLayout(outerFormLayout)
class ConfirmWidget(QtWidgets.QWidget): def __init__(self, session, location='', ontIri='', versionIri='', parent=None): """ Initialize the base information box. :type session: Session :type parent: QtWidgets.QWidget """ super().__init__(parent) self.session = session self.msgLabel = QtWidgets.QLabel(self) self.msgLabel.setText('The following ontology will be imported:') self.locationLabel = QtWidgets.QLabel(self) self.locationLabel.setText('Location: '.format(location)) self.locationText = StringField(self) self.locationText.setValue('{}'.format(location)) self.locationText.setReadOnly(True) self.iriLabel = QtWidgets.QLabel(self) self.iriLabel.setText('Ontology IRI: ') self.iriText = StringField(self) self.iriText.setValue('{}'.format(ontIri)) self.iriText.setReadOnly(True) self.versionLabel = QtWidgets.QLabel(self) self.versionLabel.setText('Version IRI: ') self.versionText = StringField(self) self.versionText.setValue('{}'.format(versionIri)) self.versionText.setReadOnly(True) formlayout = QtWidgets.QFormLayout() formlayout.addRow(self.locationLabel,self.locationText) formlayout.addRow(self.iriLabel,self.iriText) formlayout.addRow(self.versionLabel,self.versionText) groupbox = QtWidgets.QGroupBox(parent=self) groupbox.setLayout(formlayout) boxlayout = QtWidgets.QHBoxLayout() boxlayout.setAlignment(QtCore.Qt.AlignCenter) boxlayout.addWidget(self.msgLabel) boxlayout.addWidget(groupbox) groupbox = QtWidgets.QGroupBox('Finalize import', self) groupbox.setLayout(boxlayout) outerFormLayout = QtWidgets.QFormLayout() outerFormLayout.addRow(groupbox) self.setLayout(outerFormLayout)
def __init__(self, session): """ Initialize the Preferences dialog. :type session: Session """ super().__init__(session) settings = QtCore.QSettings() ############################################# # GENERAL TAB ################################# # WORKSPACE GROUP workspace = StringField(self, objectName='workspace_field') workspace.setMinimumWidth(400) workspace.setReadOnly(True) workspace.setText(settings.value('workspace/home', WORKSPACE, str)) self.addWidget(workspace) browse = QtWidgets.QPushButton(self, objectName='workspace_browse_button') browse.setText('Browse') connect(browse.clicked, self.browseWorkspace) self.addWidget(browse) boxLayout = QtWidgets.QHBoxLayout() boxLayout.addWidget(self.widget('workspace_field')) boxLayout.addWidget(self.widget('workspace_browse_button')) groupbox = QtWidgets.QGroupBox('Workspace', self, objectName='workspace_widget') groupbox.setLayout(boxLayout) self.addWidget(groupbox) ## EDITOR GROUP prefix = QtWidgets.QLabel(self, objectName='diagram_size_prefix') prefix.setText('Diagram size') self.addWidget(prefix) spinbox = SpinBox(self, objectName='diagram_size_field') spinbox.setRange(Diagram.MinSize, Diagram.MaxSize) spinbox.setSingleStep(100) spinbox.setToolTip('Default size of all the new created diagrams') spinbox.setValue(settings.value('diagram/size', 5000, int)) self.addWidget(spinbox) prefix = QtWidgets.QLabel(self, objectName='diagram_font_size_prefix') prefix.setText('Diagram font size (px)') self.addWidget(prefix) spinbox = SpinBox(self, objectName='diagram_font_size_field') spinbox.setRange(Diagram.MinFontSize, Diagram.MaxFontSize) spinbox.setSingleStep(1) spinbox.setToolTip('Default font size for diagram labels (px)') spinbox.setValue(settings.value('diagram/fontsize', QtWidgets.qApp.font().pixelSize(), int)) self.addWidget(spinbox) formlayout = QtWidgets.QFormLayout() formlayout.addRow(self.widget('diagram_size_prefix'), self.widget('diagram_size_field')) formlayout.addRow(self.widget('diagram_font_size_prefix'), self.widget('diagram_font_size_field')) groupbox = QtWidgets.QGroupBox('Editor', self, objectName='editor_widget') groupbox.setLayout(formlayout) self.addWidget(groupbox) ## UPDATE GROUP prefix = QtWidgets.QLabel(self, objectName='update_startup_prefix') prefix.setText('Check for updates on startup') self.addWidget(prefix) checkbox = CheckBox(self, objectName='update_startup_checkbox') checkbox.setChecked(settings.value('update/check_on_startup', True, bool)) checkbox.setToolTip('Whether or not application updates needs to be checked upon startup') self.addWidget(checkbox) prefix = QtWidgets.QLabel(self, objectName='update_channel_prefix') prefix.setText('Update channel') self.addWidget(prefix) combobox = ComboBox(objectName='update_channel_switch') combobox.setEditable(False) combobox.setFocusPolicy(QtCore.Qt.StrongFocus) combobox.setScrollEnabled(False) combobox.setToolTip('Update channel (current = %s)' % settings.value('update/channel', Channel.Stable.value, str)) combobox.addItems([x.value for x in Channel]) combobox.setCurrentText(settings.value('update/channel', Channel.Stable.value, str)) self.addWidget(combobox) formlayout = QtWidgets.QFormLayout() formlayout.addRow(self.widget('update_startup_prefix'), self.widget('update_startup_checkbox')) formlayout.addRow(self.widget('update_channel_prefix'), self.widget('update_channel_switch')) groupbox = QtWidgets.QGroupBox('Update', self, objectName='update_widget') groupbox.setLayout(formlayout) self.addWidget(groupbox) ## GENERAL TAB LAYOUT CONFIGURATION layout = QtWidgets.QVBoxLayout() layout.setAlignment(QtCore.Qt.AlignTop) layout.addWidget(self.widget('workspace_widget'), 0, QtCore.Qt.AlignTop) layout.addWidget(self.widget('editor_widget'), 0, QtCore.Qt.AlignTop) layout.addWidget(self.widget('update_widget'), 0, QtCore.Qt.AlignTop) widget = QtWidgets.QWidget() widget.setLayout(layout) widget.setObjectName('general_widget') self.addWidget(widget) ############################################# # EXPORT TAB ################################# self.checks = {x: CheckBox(x.value, self) for x in OWLAxiom} for axiom, checkbox in self.checks.items(): checkbox.setChecked(settings.value('export/axiom/{}'.format(axiom.value), True, bool)) ## NON-LOGICAL GROUP layout = QtWidgets.QGridLayout() layout.setColumnMinimumWidth(0, 230) layout.setColumnMinimumWidth(1, 230) layout.setColumnMinimumWidth(2, 230) layout.addWidget(self.checks[OWLAxiom.Annotation], 0, 0) layout.addWidget(self.checks[OWLAxiom.Declaration], 0, 1) layout.addWidget(QtWidgets.QWidget(self), 0, 2) widget = QtWidgets.QGroupBox('Non-Logical', self, objectName='axioms_non_logical') widget.setLayout(layout) self.addWidget(widget) ## INTENSIONAL GROUP layout = QtWidgets.QGridLayout() layout.setColumnMinimumWidth(0, 230) layout.setColumnMinimumWidth(1, 230) layout.setColumnMinimumWidth(2, 230) layout.addWidget(self.checks[OWLAxiom.AsymmetricObjectProperty], 0, 0) layout.addWidget(self.checks[OWLAxiom.DataPropertyDomain], 1, 0) layout.addWidget(self.checks[OWLAxiom.DataPropertyRange], 2, 0) layout.addWidget(self.checks[OWLAxiom.DisjointClasses], 3, 0) layout.addWidget(self.checks[OWLAxiom.DisjointDataProperties], 4, 0) layout.addWidget(self.checks[OWLAxiom.DisjointObjectProperties], 5, 0) layout.addWidget(self.checks[OWLAxiom.EquivalentClasses], 6, 0) layout.addWidget(self.checks[OWLAxiom.EquivalentDataProperties], 7, 0) layout.addWidget(self.checks[OWLAxiom.EquivalentObjectProperties], 0, 1) layout.addWidget(self.checks[OWLAxiom.FunctionalDataProperty], 1, 1) layout.addWidget(self.checks[OWLAxiom.FunctionalObjectProperty], 2, 1) layout.addWidget(self.checks[OWLAxiom.InverseFunctionalObjectProperty], 3, 1) layout.addWidget(self.checks[OWLAxiom.InverseObjectProperties], 4, 1) layout.addWidget(self.checks[OWLAxiom.IrreflexiveObjectProperty], 5, 1) layout.addWidget(self.checks[OWLAxiom.ObjectPropertyDomain], 6, 1) layout.addWidget(self.checks[OWLAxiom.ObjectPropertyRange], 7, 1) layout.addWidget(self.checks[OWLAxiom.ReflexiveObjectProperty], 0, 2) layout.addWidget(self.checks[OWLAxiom.SubClassOf], 1, 2) layout.addWidget(self.checks[OWLAxiom.SubDataPropertyOf], 2, 2) layout.addWidget(self.checks[OWLAxiom.SubObjectPropertyOf], 3, 2) layout.addWidget(self.checks[OWLAxiom.SymmetricObjectProperty], 4, 2) layout.addWidget(self.checks[OWLAxiom.TransitiveObjectProperty], 5, 2) widget = QtWidgets.QGroupBox('Intensional', self, objectName='axioms_intensional') widget.setLayout(layout) self.addWidget(widget) ## EXTENSIONAL GROUP layout = QtWidgets.QGridLayout() layout.setColumnMinimumWidth(0, 230) layout.setColumnMinimumWidth(1, 230) layout.setColumnMinimumWidth(2, 230) layout.addWidget(self.checks[OWLAxiom.ClassAssertion], 0, 0) layout.addWidget(self.checks[OWLAxiom.DataPropertyAssertion], 1, 0) layout.addWidget(self.checks[OWLAxiom.DifferentIndividuals], 2, 0) layout.addWidget(self.checks[OWLAxiom.NegativeDataPropertyAssertion], 0, 1) layout.addWidget(self.checks[OWLAxiom.NegativeObjectPropertyAssertion], 1, 1) layout.addWidget(self.checks[OWLAxiom.ObjectPropertyAssertion], 2, 1) layout.addWidget(self.checks[OWLAxiom.SameIndividual], 0, 2) widget = QtWidgets.QGroupBox('Extensional', self, objectName='axioms_extensional') widget.setLayout(layout) self.addWidget(widget) ## LOGICAL GROUP layout = QtWidgets.QVBoxLayout() layout.addWidget(self.widget('axioms_intensional')) layout.addWidget(self.widget('axioms_extensional')) widget = QtWidgets.QGroupBox('Logical', self, objectName='axioms_logical') widget.setLayout(layout) self.addWidget(widget) ## EXPORT TAB LAYOUT CONFIGURATION layout = QtWidgets.QVBoxLayout() layout.addWidget(self.widget('axioms_non_logical')) layout.addWidget(self.widget('axioms_logical')) groupbox = QtWidgets.QGroupBox('OWL 2 Axioms for which exporting is enabled', self) groupbox.setLayout(layout) layout = QtWidgets.QVBoxLayout() layout.setContentsMargins(10, 10, 10, 10) layout.addWidget(groupbox) widget = QtWidgets.QWidget(self, objectName='axioms_widget') widget.setLayout(layout) self.addWidget(widget) ############################################# # PLUGINS TAB ################################# table = QtWidgets.QTableWidget(len(self.session.plugins()), 5, self, objectName='plugins_table') table.setHorizontalHeaderLabels(['Name', 'Version', 'Author', 'Contact', 'Uninstall']) table.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) table.setFocusPolicy(QtCore.Qt.NoFocus) self.addWidget(table) header = table.horizontalHeader() header.setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch) header.setSectionResizeMode(1, QtWidgets.QHeaderView.Fixed) header.setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch) header.setSectionResizeMode(3, QtWidgets.QHeaderView.Stretch) header.setSectionResizeMode(4, QtWidgets.QHeaderView.Fixed) header.setSectionsClickable(False) header.setSectionsMovable(False) header = table.verticalHeader() header.setSectionResizeMode(QtWidgets.QHeaderView.Fixed) self.uninstall = dict() for row, plugin in enumerate(sorted(self.session.plugins(), key=lambda x: x.name())): item = QtWidgets.QTableWidgetItem(plugin.name()) item.setTextAlignment(QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable) table.setItem(row, 0, item) item = QtWidgets.QTableWidgetItem('v{0}'.format(plugin.version())) item.setTextAlignment(QtCore.Qt.AlignCenter) item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable) table.setItem(row, 1, item) item = QtWidgets.QTableWidgetItem(plugin.author()) item.setTextAlignment(QtCore.Qt.AlignCenter) item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable) table.setItem(row, 2, item) item = QtWidgets.QTableWidgetItem(plugin.contact()) item.setTextAlignment(QtCore.Qt.AlignCenter) item.setFlags(item.flags() ^ QtCore.Qt.ItemIsEditable) table.setItem(row, 3, item) p_widget = QtWidgets.QWidget() p_checkbox = CheckBox() p_checkbox.setEnabled(not plugin.isBuiltIn()) p_layout = QtWidgets.QHBoxLayout(p_widget) p_layout.addWidget(p_checkbox) p_layout.setAlignment(QtCore.Qt.AlignCenter) p_layout.setContentsMargins(0, 0, 0, 0) table.setCellWidget(row, 4, p_widget) self.uninstall[plugin] = p_checkbox button = QtWidgets.QToolButton(self, objectName='plugins_install_button') button.setDefaultAction(self.session.action('install_plugin')) button.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) self.addWidget(button) layout = QtWidgets.QVBoxLayout() layout.addWidget(self.widget('plugins_table'), 1) layout.addWidget(self.widget('plugins_install_button'), 0, QtCore.Qt.AlignRight) widget = QtWidgets.QWidget(objectName='plugins_widget') widget.setLayout(layout) self.addWidget(widget) ############################################# # CONFIRMATION BOX ################################# confirmation = QtWidgets.QDialogButtonBox(QtCore.Qt.Horizontal, self, objectName='confirmation_widget') confirmation.addButton(QtWidgets.QDialogButtonBox.Save) confirmation.addButton(QtWidgets.QDialogButtonBox.Cancel) confirmation.setContentsMargins(10, 0, 10, 10) self.addWidget(confirmation) ############################################# # MAIN WIDGET ################################# widget = QtWidgets.QTabWidget(self, objectName='main_widget') widget.addTab(self.widget('general_widget'), QtGui.QIcon(':/icons/24/ic_settings_black'), 'General') widget.addTab(self.widget('axioms_widget'), QtGui.QIcon(':/icons/24/ic_export_black'), 'Export') widget.addTab(self.widget('plugins_widget'), QtGui.QIcon(':/icons/24/ic_extension_black'), 'Plugins') self.addWidget(widget) layout = QtWidgets.QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.widget('main_widget')) layout.addWidget(self.widget('confirmation_widget'), 0, QtCore.Qt.AlignRight) self.setLayout(layout) self.setMinimumSize(740, 420) self.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) self.setWindowTitle('Preferences') connect(confirmation.accepted, self.accept) connect(confirmation.rejected, self.reject)
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 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 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 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 NewProjectDialog(QtWidgets.QDialog): """ This class is used to display a modal window to enter new project specific data. """ def __init__(self, parent=None): """ Initialize the project dialog. :type parent: QWidget """ super().__init__(parent) ############################################# # FORM AREA ################################# settings = QtCore.QSettings(ORGANIZATION, APPNAME) self.workspace = expandPath( settings.value('workspace/home', WORKSPACE, str)) self.workspace = '{0}{1}'.format(rstrip(self.workspace, os.path.sep), os.path.sep) self.nameLabel = QtWidgets.QLabel(self) self.nameLabel.setFont(Font('Roboto', 12)) self.nameLabel.setText('Name') self.nameField = StringField(self) self.nameField.setFont(Font('Roboto', 12)) self.nameField.setMinimumWidth(400) self.nameField.setMaxLength(64) self.prefixLabel = QtWidgets.QLabel(self) self.prefixLabel.setFont(Font('Roboto', 12)) self.prefixLabel.setText('Prefix') self.prefixField = StringField(self) self.prefixField.setFont(Font('Roboto', 12)) self.prefixField.setMinimumWidth(400) self.iriLabel = QtWidgets.QLabel(self) self.iriLabel.setFont(Font('Roboto', 12)) self.iriLabel.setText('IRI') self.iriField = StringField(self) self.iriField.setFont(Font('Roboto', 12)) self.iriField.setMinimumWidth(400) connect(self.prefixField.textChanged, self.doAcceptForm) connect(self.iriField.textChanged, self.doAcceptForm) connect(self.nameField.textChanged, self.doAcceptForm) connect(self.nameField.textChanged, self.onNameFieldChanged) self.pathLabel = QtWidgets.QLabel(self) self.pathLabel.setFont(Font('Roboto', 12)) self.pathLabel.setText('Location') self.pathField = StringField(self) self.pathField.setFont(Font('Roboto', 12)) self.pathField.setMinimumWidth(400) self.pathField.setReadOnly(True) self.pathField.setFocusPolicy(QtCore.Qt.NoFocus) self.pathField.setValue(self.workspace) spacer = QtWidgets.QFrame() spacer.setFrameShape(QtWidgets.QFrame.HLine) spacer.setFrameShadow(QtWidgets.QFrame.Sunken) self.formWidget = QtWidgets.QWidget(self) self.formLayout = QtWidgets.QFormLayout(self.formWidget) self.formLayout.addRow(self.nameLabel, self.nameField) self.formLayout.addRow(self.prefixLabel, self.prefixField) self.formLayout.addRow(self.iriLabel, self.iriField) self.formLayout.addWidget(spacer) self.formLayout.addRow(self.pathLabel, self.pathField) ############################################# # 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)) self.confirmationBox.button( QtWidgets.QDialogButtonBox.Ok).setEnabled(False) ############################################# # 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.gridLayout = QtWidgets.QVBoxLayout(self) self.gridLayout.setContentsMargins(0, 0, 0, 0) self.gridLayout.addWidget(self.formWidget) self.gridLayout.addWidget(self.caption) self.gridLayout.addWidget(self.confirmationBox, 0, QtCore.Qt.AlignRight) self.setFixedSize(self.sizeHint()) self.setWindowIcon(QtGui.QIcon(':/icons/128/ic_eddy')) self.setWindowTitle('New project') connect(self.confirmationBox.accepted, self.accept) connect(self.confirmationBox.rejected, self.reject) ############################################# # INTERFACE ################################# def iri(self): """ Returns the value of the iri field (trimmed). :rtype: str """ return self.iriField.value() def name(self): """ Returns the value of the name field (trimmed). :rtype: str """ return self.nameField.value() def path(self): """ Returns the value of the path field (expanded). :rtype: str """ return expandPath(self.pathField.value()) def prefix(self): """ Returns the value of the prefix field (trimmed). :rtype: str """ return self.prefixField.value() ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def accept(self): """ Accept the project form and creates a new empty project. """ project = Project(name=self.name(), path=self.path(), prefix=self.prefix(), iri=self.iri(), profile=OWL2Profile()) worker = GrapholProjectExporter(project) worker.run() super().accept() @QtCore.pyqtSlot() def doAcceptForm(self): """ Validate project settings. """ caption = '' enabled = True ############################################# # CHECK NAME ################################# if not self.name(): caption = '' enabled = False else: if isdir(self.path()): caption = "Project '{0}' already exists!".format(self.name()) enabled = False elif not isPathValid(self.path()): caption = "'{0}' is not a valid project name!".format( self.name()) enabled = False ############################################# # CHECK PREFIX ################################# if enabled: if not self.prefix(): caption = '' enabled = False ############################################# # CHECK IRI ################################# if enabled: if not self.iri(): caption = '' enabled = False self.caption.setText(caption) self.caption.setVisible(not isEmpty(caption)) self.confirmationBox.button( QtWidgets.QDialogButtonBox.Ok).setEnabled(enabled) self.setFixedSize(self.sizeHint()) @QtCore.pyqtSlot(str) def onNameFieldChanged(self, name): """ Update the project location field to reflect the new project name. :type name: str """ self.pathField.setValue('{0}{1}'.format(self.workspace, name.strip()))
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()))
class ProjectDialog(QtWidgets.QDialog): """ This class is used to display a modal window to enter project specific data. """ def __init__(self, parent=None): """ Initialize the project dialog. :type parent: QtWidgets.QWidget """ super().__init__(parent) ############################################# # FORM AREA ################################# settings = QtCore.QSettings(ORGANIZATION, APPNAME) self.workspace = expandPath(settings.value('workspace/home', WORKSPACE, str)) self.workspace = '{0}{1}'.format(rstrip(self.workspace, os.path.sep), os.path.sep) self.nameLabel = QtWidgets.QLabel(self) self.nameLabel.setFont(Font('Roboto', 12)) self.nameLabel.setText('Name') self.nameField = StringField(self) self.nameField.setFont(Font('Roboto', 12)) self.nameField.setMinimumWidth(400) self.nameField.setMaxLength(64) connect(self.nameField.textChanged, self.onNameFieldChanged) self.prefixLabel = QtWidgets.QLabel(self) self.prefixLabel.setFont(Font('Roboto', 12)) self.prefixLabel.setText('Prefix') self.prefixField = StringField(self) self.prefixField.setFont(Font('Roboto', 12)) self.prefixField.setMinimumWidth(400) self.iriLabel = QtWidgets.QLabel(self) self.iriLabel.setFont(Font('Roboto', 12)) self.iriLabel.setText('IRI') self.iriField = StringField(self) self.iriField.setFont(Font('Roboto', 12)) self.iriField.setMinimumWidth(400) connect(self.iriField.textChanged, self.doProjectPathValidate) connect(self.nameField.textChanged, self.doProjectPathValidate) connect(self.prefixField.textChanged, self.doProjectPathValidate) self.pathLabel = QtWidgets.QLabel(self) self.pathLabel.setFont(Font('Roboto', 12)) self.pathLabel.setText('Location') self.pathField = StringField(self) self.pathField.setFont(Font('Roboto', 12)) self.pathField.setMinimumWidth(400) self.pathField.setReadOnly(True) self.pathField.setFocusPolicy(QtCore.Qt.NoFocus) self.pathField.setValue(self.workspace) spacer = QtWidgets.QFrame() spacer.setFrameShape(QtWidgets.QFrame.HLine) spacer.setFrameShadow(QtWidgets.QFrame.Sunken) self.formWidget = QtWidgets.QWidget(self) self.formLayout = QtWidgets.QFormLayout(self.formWidget) self.formLayout.addRow(self.nameLabel, self.nameField) self.formLayout.addRow(self.prefixLabel, self.prefixField) self.formLayout.addRow(self.iriLabel, self.iriField) self.formLayout.addWidget(spacer) self.formLayout.addRow(self.pathLabel, self.pathField) ############################################# # 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)) self.confirmationBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False) ############################################# # 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('New project') connect(self.confirmationBox.accepted, self.accept) connect(self.confirmationBox.rejected, self.reject) ############################################# # INTERFACE ################################# def iri(self): """ Returns the value of the iri field (trimmed). :rtype: str """ return self.iriField.value() def path(self): """ Returns the value of the path field (expanded). :rtype: str """ return expandPath(self.pathField.value()) def prefix(self): """ Returns the value of the prefix field (trimmed). :rtype: str """ return self.prefixField.value() ############################################# # SLOTS ################################# @QtCore.pyqtSlot() def accept(self): """ Accept the project form and creates a new empty project. """ project = Project(self.path(), self.prefix(), self.iri(), OWL2Profile()) worker = GrapholProjectExporter(project) worker.run() super().accept() @QtCore.pyqtSlot() def doProjectPathValidate(self): """ Validate project settings. """ caption = '' enabled = True ############################################# # CHECK NAME ################################# name = self.nameField.value() path = self.pathField.value() if not name: caption = '' enabled = False else: if isdir(path): caption = "Project '{0}' already exists!".format(name) enabled = False elif not isPathValid(path): caption = "'{0}' is not a valid project name!".format(name) enabled = False ############################################# # CHECK PREFIX ################################# if enabled: if not self.prefixField.value(): caption = '' enabled = False ############################################# # CHECK IRI ################################# if enabled: if not self.iriField.value(): caption = '' enabled = False self.caption.setText(caption) self.caption.setVisible(not isEmpty(caption)) self.confirmationBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(enabled) self.setFixedSize(self.sizeHint()) @QtCore.pyqtSlot(str) def onNameFieldChanged(self, name): """ Update the project location field to reflect the new project name. :type name: str """ self.pathField.setValue('{0}{1}'.format(self.workspace, name.strip()))
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 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(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))