class INPropertyBox(QDialog, Ui_INPropertyBox): """ Provide a modal dialog that allows the user to edit an instance node on an instance diagram. """ treeViewUpdate = pyqtSignal() def __init__(self, parent=None, diagramInstance=None, model=None): """ Constructor @param parent reference to the parent widget @type QWidget """ super(INPropertyBox, self).__init__(parent) self.startUp = True self.parent = parent self.schemaModel = self.parent.schemaObject self.helper = Helper() self.diagramInstance = diagramInstance # reload the NodeInstance dictionary values in case they've been changed on another diagram self.diagramInstance.reloadDictValues() self.model = model self.modelData = self.model.modelData self.node = None # self.rc = None self.msg = None self.formatChanged = False self.setupUi(self) self.initUI() self.populateUIfromObject() self.loadTemplateDropdown() self.startUp = False def initUI(self, ): # label grid self.gridLabels.setModel(self.createLabelModel()) comboLblList = [""] comboLblList.extend( sorted( set( self.model.instanceList("Label") + self.schemaModel.instanceList("Label")))) # comboLblList = self.model.loadComboBox(topLevel='Label', objectName=None, selectMsg="" ) self.gridLabels.setItemDelegateForColumn( LABEL, CBDelegate(self, comboLblList, setEditable=True)) self.gridLabels.setColumnWidth(LABEL, 200) self.gridLabels.setSelectionBehavior(QAbstractItemView.SelectItems) self.gridLabels.setSelectionMode(QAbstractItemView.SingleSelection) header = self.gridLabels.horizontalHeader() header.setSectionResizeMode(LABEL, QHeaderView.Interactive) # property grid self.gridProps.setSortingEnabled(False) self.gridProps.setModel(self.createPropModel()) self.gridProps.setSortingEnabled(False) comboPropList = [""] comboPropList.extend( sorted( set( self.model.instanceList("Property") + self.schemaModel.instanceList("Property")))) dataTypeList = [dataType.value for dataType in DataType] self.gridProps.setItemDelegate(NeoEditDelegate(self)) self.gridProps.setItemDelegateForColumn( DATATYPE, CBDelegate(self, dataTypeList, setEditable=False)) self.gridProps.setItemDelegateForColumn( PROPERTY, CBDelegate(self, comboPropList, setEditable=True)) self.gridProps.setColumnWidth(PROPERTY, 200) self.gridProps.setColumnWidth(DATATYPE, 125) self.gridProps.setColumnWidth(VALUE, 300) self.gridProps.setSelectionBehavior(QAbstractItemView.SelectItems) self.gridProps.setSelectionMode(QAbstractItemView.SingleSelection) header = self.gridProps.horizontalHeader() header.setSectionResizeMode(PROPERTY, QHeaderView.Interactive) header.setSectionResizeMode(DATATYPE, QHeaderView.Fixed) header.setSectionResizeMode(VALUE, QHeaderView.Stretch) def populateUIfromObject(self, ): try: # print("NZID: {}".format(self.diagramInstance.NZID)) self.editNZID.insert(str(self.diagramInstance.NZID)) except: self.editNZID.insert("No NZID") # print("no NZID") try: # print("NeoID: {}".format(self.diagramInstance.neoID)) self.editNeoID.insert(str(self.diagramInstance.neoID)) except: self.editNeoID.insert("No NeoID") # print("no neoID") #load Label grid for nodeLbl in self.diagramInstance.labelList: self.addLabel(self.gridLabels.model(), nodeLbl[LABEL]) #load Property grid for nodeProp in self.diagramInstance.propList: self.addProp(self.gridProps.model(), nodeProp[PROPERTY], nodeProp[DATATYPE], nodeProp[VALUE]) def loadTemplateDropdown(self, ): # load node template dropdown dropdownList = ["No Template Selected"] dropdownList.extend(sorted(self.model.instanceList("Node Template"))) self.cboTemplate.addItems(dropdownList) if not self.diagramInstance.nodeTemplate is None: index = self.cboTemplate.findText( self.diagramInstance.nodeTemplate) if index >= 0: self.cboTemplate.setCurrentIndex(index) def logMsg(self, msg): # add message to the log if logging: logging.info(msg) def createLabelModel(self): model = QStandardItemModel(0, 1) model.setHeaderData(LABEL, Qt.Horizontal, "Label") return model def createPropModel(self): model = QStandardItemModel(0, 3) model.setHeaderData(PROPERTY, Qt.Horizontal, "Property") model.setHeaderData(DATATYPE, Qt.Horizontal, "Data Type") model.setHeaderData(VALUE, Qt.Horizontal, "Value") # connect model slots model.itemChanged.connect(self.propModelItemChanged) return model def mergeTemplateWithInstanceNode(self, templateName): # don't merge template if we're just starting up the UI if self.startUp: return saveIndex, nodeTemplateDict = self.model.getDictByName( topLevel="Node Template", objectName=templateName) if nodeTemplateDict is None: self.logMsg( "Merge Template Error - no node template dictionary for {}". format(templateName)) return # append any labels from the template not already on the instance node to the end of the label grid existingLabelList = [ self.gridLabels.model().item(row, LABEL).data(Qt.EditRole) for row in range(0, self.gridLabels.model().rowCount()) ] templateLabelList = [ nodeLbl[LABEL] for nodeLbl in nodeTemplateDict["labels"] ] newLabelList = list(set(templateLabelList) - set(existingLabelList)) for lbl in newLabelList: self.addLabel(self.gridLabels.model(), lbl) # properties # what's on the form now existingPropList = [ self.gridProps.model().item(row, PROPERTY).data(Qt.EditRole) for row in range(0, self.gridProps.model().rowCount()) ] existingValueList = [ self.gridProps.model().item(row, VALUE).data(Qt.EditRole) for row in range(0, self.gridProps.model().rowCount()) ] # property list from the template newPropList = [ nodeProp[PROPERTY] for nodeProp in nodeTemplateDict["properties"] ] newValueList = [ nodeProp[PROPDEF] for nodeProp in nodeTemplateDict["properties"] ] # this should get default values some day # add new properties to the end of the list mergePropList = (list(set(newPropList) - set(existingPropList))) for prop in mergePropList: val = "" # get default value from template first if prop in newPropList: val = newValueList[newPropList.index(prop)] # override with existing value for same property if it exists if prop in existingPropList: val = existingValueList[existingPropList.index(prop)] # set Null so editor delegates will work if val == "" or val is None: val = "Null" dataType = self.model.getPropertyDataType(prop) self.addProp(self.gridProps.model(), prop, dataType, val) def validate(self, ): # label grid checks if self.helper.gridNoNameError( grid=self.gridLabels, col=LABEL, txtMsg="You must supply a name for each Label"): self.gridLabels.setFocus() return False if self.helper.gridDupEntryError( self.gridLabels, col=LABEL, txtMsg= "has been entered more than once. You can only use a Label once" ): self.gridLabels.setFocus() return False # property grid checks if self.helper.gridNoNameError( grid=self.gridProps, col=PROPERTY, txtMsg="You must supply a name for each Property"): self.gridProps.setFocus() return False if self.helper.gridDupEntryError( self.gridProps, col=PROPERTY, txtMsg= "has been entered more than once. You can only use a Property once" ): self.gridProps.setFocus() return False model = self.gridProps.model() for row in range(0, model.rowCount()): nodeProp = [ model.item(row, PROPERTY).data(Qt.EditRole), model.item(row, DATATYPE).data(Qt.EditRole), model.item(row, VALUE).data(Qt.EditRole) ] # property datatype matches property definition if self.model.propertyDataTypeValid( name=nodeProp[PROPERTY], dataType=nodeProp[DATATYPE]) == False: self.helper.displayErrMsg( "Validate", "You entered datatype {} for property {} which does not match the property definition. Please enter the correct datatype." .format(nodeProp[DATATYPE], nodeProp[PROPERTY])) self.gridProps.setFocus() return False # can't add/update a value for an Unknown datatype property if nodeProp[DATATYPE] == "Unknown" and nodeProp[VALUE] != "Null": self.helper.displayErrMsg( "Validate", "Property {} has Unknown datatype. You can't add/update a value for this property. Set the value to Null" .format(nodeProp[PROPERTY])) self.gridProps.setFocus() return False # template defined required property has a value templateName = self.cboTemplate.currentText() saveIndex, nodeTemplateDict = self.model.getDictByName( topLevel="Node Template", objectName=templateName) if not nodeTemplateDict is None: model = self.gridProps.model() numrows = model.rowCount() for row in range(0, numrows): nodeProp = [ model.item(row, PROPERTY).data(Qt.EditRole), model.item(row, DATATYPE).data(Qt.EditRole), model.item(row, VALUE).data(Qt.EditRole) ] if nodeProp[VALUE] == "Null": # the value is null so see if it is required for templateProp in nodeTemplateDict["properties"]: if templateProp[PROPERTY] == nodeProp[PROPERTY]: if templateProp[PROPREQ] == Qt.Checked: # this property must have a value self.helper.displayErrMsg( "Validate", "The property {} is required. Please enter a value." .format(nodeProp[PROPERTY])) return False return True def apply(self, ): # update the diagramInstance object with values from the UI. # save the labels self.diagramInstance.labelList = [] model = self.gridLabels.model() numrows = model.rowCount() for row in range(0, numrows): nodeLbl = [model.item(row, LABEL).data(Qt.EditRole)] self.model.newLabel( nodeLbl[LABEL] ) # check to see if this is a new Label and create a Label object in the dictionary self.diagramInstance.labelList.append(nodeLbl) # #save the attributes self.diagramInstance.propList = [] model = self.gridProps.model() numrows = model.rowCount() for row in range(0, numrows): nodeProp = [ model.item(row, PROPERTY).data(Qt.EditRole), model.item(row, DATATYPE).data(Qt.EditRole), model.item(row, VALUE).data(Qt.EditRole) ] # print("save prop {}".format(nodeProp)) self.model.newProperty(nodeProp[PROPERTY], nodeProp[DATATYPE]) self.diagramInstance.propList.append(nodeProp) #save the template selectedTemplate = self.cboTemplate.currentText() self.diagramInstance.nodeTemplate = selectedTemplate # save the node itself in Neo4j rc, msg = self.updateNeo() return rc, msg def updateNeo(self, ): QApplication.setOverrideCursor(Qt.WaitCursor) if self.modelData["SyncMode"] == "On": rc, msg = self.diagramInstance.syncToDB(self.logMsg) if rc is True: returnmsg = "Update Node Succesful" else: returnmsg = "Update Node Failed: {}".format(msg) self.helper.displayErrMsg("Update Node", returnmsg) else: rc = True returnmsg = "Database Sync is off - Node not saved to graph" QApplication.restoreOverrideCursor() return rc, returnmsg @pyqtSlot() def on_btnCreateTemplate_clicked(self): """ Create a new Node Template based on the labels and properties entered into this Instance Node. This is the same logic as in the reverse engineering dialog """ text, ok = QInputDialog.getText(self, 'New Node Template', 'Enter the Node Template Name:') if ok: # make sure they entered something if len(text) < 1: self.helper.displayErrMsg( "Create New Node Template Error", "You must enter a name.".format(text)) #make sure entered name doesn't existing index, nodeDict = self.model.getDictByName("Node Template", text) #create a node template dictionary if not nodeDict is None: self.helper.displayErrMsg( "Create New Node Template Error", "The node template {} already exists".format(text)) return labelList = [] model = self.gridLabels.model() numrows = model.rowCount() for row in range(0, numrows): nodeLbl = [ model.item(row, LABEL).data(Qt.EditRole), Qt.Checked, Qt.Unchecked ] self.model.newLabel( nodeLbl[LABEL] ) # check to see if this is a new Label and create a Label object in the dictionary labelList.append(nodeLbl) propList = [] model = self.gridProps.model() numrows = model.rowCount() for row in range(0, numrows): nodeProp = [ model.item(row, PROPERTY).data(Qt.EditRole), model.item(row, DATATYPE).data(Qt.EditRole), Qt.Unchecked, "", Qt.Unchecked, Qt.Unchecked, Qt.Unchecked ] self.model.newProperty(nodeProp[PROPERTY], nodeProp[DATATYPE]) propList.append(nodeProp) # check for constraints that match label/prop # generate the constraints and indexes conList = [] # look at each constraint in the schemaModel and see if it belongs to the node template self.schemaModel.matchConstraintNodeTemplate( conList, [lbl[0] for lbl in labelList], [prop[0] for prop in propList]) # look at each index in the schemaModel and see if it belongs to the node template idxList = [] self.schemaModel.matchIndexNodeTemplate( idxList, [lbl[0] for lbl in labelList], [prop[0] for prop in propList]) #save it to the model nodeDict = self.model.newNodeTemplate( name=text, labelList=labelList, propList=propList, conList=conList, idxList=idxList, desc="Template generated from Instance Node.") self.model.modelData["Node Template"].append(nodeDict) # refresh the treeview self.model.updateTV() # set combo box self.loadTemplateDropdown() if not (self.cboTemplate.findText(text) is None): self.cboTemplate.setCurrentIndex( self.cboTemplate.findText(text)) @pyqtSlot() def on_btnLabelUp_clicked(self): """ User clicks on label up button """ self.helper.moveTableViewRowUp(self.gridLabels) @pyqtSlot() def on_btnLabelDown_clicked(self): """ User clicks on label down button """ self.helper.moveTableViewRowDown(self.gridLabels) @pyqtSlot() def on_btnLabelAdd_clicked(self): """ Slot documentation goes here. """ self.addLabel(self.gridLabels.model(), "") # common function to adjust grid after appending a row self.helper.adjustGrid(grid=self.gridLabels) # # scroll to bottom to insure the new row is visible # self.gridLabels.scrollToBottom() @pyqtSlot() def on_btnLabelRemove_clicked(self): """ Slot documentation goes here. """ indexes = self.gridLabels.selectionModel().selectedIndexes() for index in sorted(indexes): # print('Row %d is selected' % index.row()) self.gridLabels.model().removeRows(index.row(), 1) @pyqtSlot() def on_btnPropUp_clicked(self): """ User clicks on property up button """ self.helper.moveTableViewRowUp(self.gridProps) @pyqtSlot() def on_btnPropDown_clicked(self): """ User clicks on property down button """ self.helper.moveTableViewRowDown(self.gridProps) @pyqtSlot() def on_btnPropAdd_clicked(self): """ User clicks Add Property """ self.addProp(self.gridProps.model(), "", "String", "Null") # common function to adjust grid after appending a row self.helper.adjustGrid(grid=self.gridProps) # # scroll to bottom to insure the new row is visible # self.gridProps.scrollToBottom() @pyqtSlot() def on_btnMakeNull_clicked(self): """ User requests to set a property value to Null """ # grid only allows single selection indexes = self.gridProps.selectionModel().selectedIndexes() for index in indexes: valueIndex = self.gridProps.model().index(index.row(), VALUE) self.gridProps.model().setData(valueIndex, "Null", Qt.DisplayRole) @pyqtSlot() def on_btnPropRemove_clicked(self): """ Slot documentation goes here. """ # grid only allows single selection indexes = self.gridProps.selectionModel().selectedIndexes() for index in indexes: self.gridProps.model().removeRows(index.row(), 1) @pyqtSlot() def on_okButton_clicked(self): """ User clicked OK button, validate the data then sync to graph """ if self.validate(): rc, msg = self.apply() if rc == True: QDialog.accept(self) @pyqtSlot() def on_cancelButton_clicked(self): """ Slot documentation goes here. """ QDialog.reject(self) def addLabel(self, model, label): self.gridLabels.setSortingEnabled(False) item1 = QStandardItem(label) item1.setEditable(True) model.appendRow([ item1, ]) # self.gridLabels.resizeColumnsToContents() # self.gridLabels.setSortingEnabled(True) def addProp(self, model, prop, dataType, value): ''' add a row to the property grid ''' self.gridProps.setSortingEnabled(False) item1 = QStandardItem(prop) item1.setEditable(True) item11 = QStandardItem(dataType) item11.setEditable(True) item2 = QStandardItem(value) item2.setData(dataType, Qt.UserRole + 1) item2.setEditable(True) model.appendRow([item1, item11, item2]) @pyqtSlot(int) def on_cboTemplate_currentIndexChanged(self, index): """ Slot documentation goes here. @param index DESCRIPTION @type int """ # print("template changed {}".format(str(index))) # the new node template might have a custom format so set this to true which will force # a diagram redraw when the dialog closes. self.formatChanged = True if index > 0: self.mergeTemplateWithInstanceNode(self.cboTemplate.currentText()) def propModelItemChanged(self, item): # print("item data changed {} at {} {}".format(str(item.checkState()), item.index().row(), item.index().column())) templateName = self.cboTemplate.currentText() columnNum = item.index().column() if columnNum == PROPERTY: # if property has changed then change the datatype propName = self.gridProps.model().item(item.index().row(), PROPERTY).data(Qt.EditRole) dataType = self.model.getPropertyDataType(propName) self.gridProps.model().item(item.index().row(), DATATYPE).setText(dataType) # see if this property has a default value defined in the template defaultVal = self.model.getTemplatePropDefVal( templateName=templateName, propName=propName) if not defaultVal is None: self.gridProps.model().item(item.index().row(), VALUE).setText(defaultVal) if columnNum == DATATYPE: # if datatype has changed then change value to "Null" self.gridProps.model().item(item.index().row(), VALUE).setText("Null") dataType = self.gridProps.model().item(item.index().row(), DATATYPE).data(Qt.EditRole) self.gridProps.model().item(item.index().row(), VALUE).setData(dataType, Qt.UserRole + 1) @pyqtSlot(int) def on_tabNodeInspector_currentChanged(self, index): """ User has switched to another tab @param index DESCRIPTION @type int """ # user switched to the description tab. must regenerate description if there is a node template selected if index == DESCRIPTION: if self.cboTemplate.currentIndex() > 0: saveIndex, objectDict = self.model.getDictByName( topLevel="Node Template", objectName=self.cboTemplate.currentText()) if not objectDict is None: self.brwsrGeneratedDesc.setText( self.model.getNodeDescription( self.cboTemplate.currentText())) else: self.helper.displayErrMsg( "Get Description", "Error - could not find node template: {}".format( self.cboTemplate.currentText()))
class PathTemplateTab(QWidget, Ui_PathTemplateTab): """ Class documentation goes here. """ def __init__(self, parent=None, model=None, name=None, tabType=None): """ Constructor @param parent reference to the parent widget @type QWidget """ super(PathTemplateTab, self).__init__(parent) self.setupUi(self) self.parent = parent self.model = model self.tabType = tabType self.tabName = name saveIndex, pathTemplateDict = self.model.getDictByName( topLevel=self.tabType, objectName=name) self.pathTemplateDict = pathTemplateDict self.helper = Helper() self.lastTreeViewWidgetItem = None # add cypher CypherEditGridWidget global unNamedFileCounter unNamedFileCounter = unNamedFileCounter + 1 fileName = "{}".format("Unsaved-0{}".format(unNamedFileCounter)) self.cypherEditGrid = CypherEditGridWidget(parent=parent, fileName=fileName, fileText="", mode=MODENEW) self.frmDataGridLayout = QVBoxLayout(self.frmDataGrid) self.frmDataGridLayout.setObjectName("frmDataGridLayout") self.frmDataGridLayout.setContentsMargins(1, 1, 1, 1) self.frmDataGridLayout.setSpacing(1) self.frmDataGridLayout.addWidget(self.cypherEditGrid) # setup output grid model self.gridOutput.setModel(self.createOutputModel()) self.gridOutput.setSelectionBehavior(QAbstractItemView.SelectItems) self.gridOutput.setSelectionMode(QAbstractItemView.SingleSelection) # SOURCE, SOURCENAME, COLTYPE, COLVAL, COLNAME, DISPLAYIT, SORT, GROUP self.gridOutput.setColumnWidth(SOURCE, 200) self.gridOutput.setColumnWidth(SOURCENAME, 200) self.gridOutput.setColumnWidth(COLTYPE, 100) self.gridOutput.setColumnWidth(COLVAL, 200) self.gridOutput.setColumnWidth(COLNAME, 200) self.gridOutput.setColumnWidth(DISPLAYIT, 75) self.gridOutput.setColumnWidth(SORT, 75) self.gridOutput.setColumnWidth(GROUP, 75) # header header = self.gridOutput.horizontalHeader() header.setSectionResizeMode(SOURCE, QHeaderView.Interactive) header.setSectionResizeMode(SOURCENAME, QHeaderView.Interactive) header.setSectionResizeMode(COLTYPE, QHeaderView.Fixed) header.setSectionResizeMode(COLVAL, QHeaderView.Interactive) header.setSectionResizeMode(COLNAME, QHeaderView.Interactive) header.setSectionResizeMode(DISPLAYIT, QHeaderView.Fixed) header.setSectionResizeMode(SORT, QHeaderView.Fixed) header.setSectionResizeMode(GROUP, QHeaderView.Fixed) # column type colTypeList = ["", "Property", "Label", "Function"] self.gridOutput.setItemDelegateForColumn( COLTYPE, CBDelegate(self, colTypeList, setEditable=True)) # sort sortList = ["No", "Ascending", "Descending"] self.gridOutput.setItemDelegateForColumn( SORT, CBDelegate(self, sortList, setEditable=True)) # group groupList = ["No", "Yes"] self.gridOutput.setItemDelegateForColumn( GROUP, CBDelegate(self, groupList, setEditable=True)) # path treeview setup self.tvPath.setContextMenuPolicy(Qt.CustomContextMenu) self.tvPath.customContextMenuRequested.connect(self.openMenu) self.tvPath.setDragDropMode(QAbstractItemView.DragOnly) # initiliaze UI from pathTemplateDict self.initUI() def save(self, ): # print("save path {}".format(self.tabName)) nodeList = [] # iterate through the treeview tvPathIterator = QTreeWidgetItemIterator( self.tvPath, flags=QTreeWidgetItemIterator.All) while tvPathIterator: if not tvPathIterator.value() is None: # print("save node {}".format(tvPathIterator.value().text(0))) queryPathNodeItem = tvPathIterator.value() queryPathNodeDict = queryPathNodeItem.data(0, Qt.UserRole).dict() nodeList.append(queryPathNodeDict) # if its the root node then save the description. if queryPathNodeDict["type"] == "Path Template": self.pathTemplateDict["description"] = queryPathNodeDict[ "description"] tvPathIterator.__iadd__(1) else: break self.pathTemplateDict["queryPath"] = nodeList self.model.setModelDirty() #----------------------------------------------------------------------------------------------------------------------- # metabox grid methods #----------------------------------------------------------------------------------------------------------------------- def createMetaBoxModel(self): # ATTRNAME, ATTRVALUE gridNodeMeta model = QStandardItemModel(0, 2) model.setHeaderData(ATTRNAME, Qt.Horizontal, "Attribute") model.setHeaderData(ATTRVALUE, Qt.Horizontal, "Value") return model def clearMetaBox(self): self.lblMetaHeader.setText("Definition") # metabox grid setup # ATTRNAME, ATTRVALUE gridMetaBox self.gridMetaBox.setModel(None) self.metaBoxModel = self.createMetaBoxModel() self.gridMetaBox.setModel(self.metaBoxModel) self.gridMetaBox.setSelectionBehavior(QAbstractItemView.SelectItems) self.gridMetaBox.setSelectionMode(QAbstractItemView.SingleSelection) self.gridMetaBox.setColumnWidth(ATTRNAME, 200) self.gridMetaBox.setColumnWidth(ATTRVALUE, 300) # header header = self.gridMetaBox.horizontalHeader() header.setSectionResizeMode(ATTRNAME, QHeaderView.Fixed) header.setSectionResizeMode(ATTRVALUE, QHeaderView.Stretch) # set editor delegate self.gridMetaBox.setItemDelegateForColumn(ATTRVALUE, MetaBoxDelegate(self)) # self.gridMetaBox.itemDelegateForColumn(ATTRVALUE).closeEditor.connect(self.metaBoxEditorClosed) # connect model slots self.metaBoxModel.itemChanged.connect(self.metaBoxModelItemChanged) # connect grid slots self.gridMetaBox.selectionModel().selectionChanged.connect( self.metaBoxGridSelectionChanged) def addMetaRow(self, attribute=None, value=None, editable=None): ''' add a row to the metabox grid ''' # print("add metarow {}-{}-{}".format(attribute, value, editable)) self.gridMetaBox.setSortingEnabled(False) # attribute if attribute is None: attribute = "" item1 = QStandardItem(attribute) item1.setEditable(False) # value if value is None: value = "" item2 = QStandardItem(str(value)) # save the attribute name in the value item so the custom editor MetaBoxDelegate can find it item2.setData(attribute, Qt.UserRole) item2.setEditable(editable) self.gridMetaBox.model().appendRow([item1, item2]) def populateMetaBox(self, queryPathNode=None): if not queryPathNode is None: self.clearMetaBox() self.lblMetaHeader.setText("{} Definition".format( queryPathNode.type)) for attr in queryPathNode.attributes(): self.addMetaRow(attribute=attr[0], value=attr[1], editable=attr[2]) def rePopulateMetaBox(self, queryPathNode=None): '''update the displayname''' model = self.gridMetaBox.model() numrows = model.rowCount() for row in range(0, numrows): name = model.item(row, ATTRNAME).data(Qt.EditRole) if name == "Display Name": # update displayName in the grid model.setData(model.item(row, ATTRVALUE).index(), queryPathNode.displayName, role=Qt.EditRole) # update displayName on the tree view queryPathNode.treeItem.setData(0, Qt.EditRole, queryPathNode.displayName) # def metaBoxEditorClosed(self, ): # print("editor closed") def metaBoxModelItemChanged(self, item): # print("item data changed {} at row:{} col:{}".format(str(item.checkState()), item.index().row(), item.index().column())) # a cell changed so see if other changes are needed # update the queryPathNode object selected = self.tvPath.currentItem() if not (selected is None): queryPathNode = selected.data(0, Qt.UserRole) name = self.gridMetaBox.model().item(item.index().row(), ATTRNAME).data(Qt.EditRole) value = self.gridMetaBox.model().item(item.index().row(), ATTRVALUE).data(Qt.EditRole) queryPathNode.updateAttr(name=name, value=value) # update displayName self.rePopulateMetaBox(queryPathNode=queryPathNode) # force selection of this cell self.gridMetaBox.setCurrentIndex(item.index()) def metaBoxGridSelectionChanged(self): # not used # print("metabox grid selection changed") return def initUI(self): self.populateTree() #----------------------------------------------------------------------------------------------------------------------- # tree view methods #----------------------------------------------------------------------------------------------------------------------- def clearTree(self): self.tvPath.clear() self.tvPath.setColumnCount(1) self.tvPath.setHeaderLabels(["Query Path"]) self.tvPath.setItemsExpandable(True) def getMaxTreeOrder(self, ): '''scan the tree and find the max order attribute. This is used to increment and create the next highest order number''' max = 0 # iterate through the treeview tvPathIterator = QTreeWidgetItemIterator( self.tvPath, flags=QTreeWidgetItemIterator.All) while tvPathIterator: if not tvPathIterator.value() is None: queryPathNodeItem = tvPathIterator.value() queryPathNodeDict = queryPathNodeItem.data(0, Qt.UserRole).dict() order = queryPathNodeDict.get("order", 0) # save the value for order if it's greater if order > max: max = order tvPathIterator.__iadd__(1) else: break return max def findParentWidget(self, findOrder=None): '''scan the tree and find the treeviewwidget with the matching parentOrder''' # if the find id is None then this is the root so return the tree view itself if findOrder is None: return self.tvPath # find the parent tree view widget parentWidget = None # iterate through the treeview tvPathIterator = QTreeWidgetItemIterator( self.tvPath, flags=QTreeWidgetItemIterator.All) while tvPathIterator: if not tvPathIterator.value() is None: queryPathNodeItem = tvPathIterator.value() queryPathNodeDict = queryPathNodeItem.data(0, Qt.UserRole).dict() order = queryPathNodeDict.get("order", 0) # save the value for order if it's greater if order == findOrder: parentWidget = queryPathNodeItem break tvPathIterator.__iadd__(1) else: break return parentWidget def populateTree(self, ): self.clearTree() # print("path dict {}".format(self.pathTemplateDict)) # add tree items if len(self.pathTemplateDict["queryPath"]) > 0: for tvPathDict in self.pathTemplateDict["queryPath"]: # create the tree view path item object pathItem = QueryPathNode(nodeDict=tvPathDict) # print("add path item {}".format(pathItem.displayName)) # figure out who the parent is parent = self.findParentWidget(findOrder=pathItem.parentOrder) # add the treewidgetitem to the tree pathItem.treeItem = self.addTreeNode(parent=parent, pathItem=pathItem) else: # add a new root node pathItem = QueryPathNode(root=True, parentOrder=None, order=0, type="Path Template") pathItem.treeItem = self.addTreeNode(parent=self.tvPath, pathItem=pathItem) self.tvPath.resizeColumnToContents(0) self.tvPath.setCurrentItem(self.tvPath.topLevelItem(0)) def addTreeNode(self, parent=None, pathItem=None): # print("add tree node {}".format(pathItem.displayName)) item = QTreeWidgetItem(parent, [pathItem.displayName]) item.setData(0, Qt.UserRole, pathItem) item.setChildIndicatorPolicy(QTreeWidgetItem.ShowIndicator) item.setExpanded(True) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) return item ########################################################################################### # query path tree view methods ########################################################################################### def openMenu(self, position): selected = self.tvPath.currentItem() if not (selected is None): tvPathNode = selected.data(0, Qt.UserRole) if (tvPathNode.type == "Path Template"): menu = QMenu() addNodeAction = menu.addAction("Add Node") addNodeAction.triggered.connect(self.addNodeTreeNode) addNodeAction = menu.addAction("Add Relationship") addNodeAction.triggered.connect(self.addRelTreeNode) menu.exec_(self.tvPath.mapToGlobal(position)) return if (tvPathNode.type == "Node"): menu = QMenu() # addNodeAction = menu.addAction("Add Relationship") # addNodeAction.triggered.connect(self.addRelTreeNode) subOutMenu = QMenu("Out Bound Relationships", parent=menu) # generate outboundrel menu items outBoundList = self.parent.model.getOutboundRelTemplates( nodeTemplateName=tvPathNode.templateName) for relTemplate in outBoundList: aSubAction = subOutMenu.addAction(relTemplate["name"]) aSubAction.setData(relTemplate) aSubAction.triggered.connect(self.addOutboundRel) if len(outBoundList) == 0: aSubAction = subOutMenu.addAction( "No Outbound Relationship Templates") menu.addMenu(subOutMenu) subInMenu = QMenu("In Bound Relationships", parent=menu) # generate outboundrel menu items inBoundList = self.parent.model.getInboundRelTemplates( nodeTemplateName=tvPathNode.templateName) for relTemplate in inBoundList: aSubAction = subInMenu.addAction(relTemplate["name"]) aSubAction.setData(relTemplate) aSubAction.triggered.connect(self.addInboundRel) if len(inBoundList) == 0: aSubAction = subInMenu.addAction( "No Inbound Relationship Templates") menu.addMenu(subInMenu) removeNodeAction = menu.addAction("Remove this Node") removeNodeAction.triggered.connect(self.removeTreeNode) menu.exec_(self.tvPath.mapToGlobal(position)) return if (tvPathNode.type == "Relationship"): menu = QMenu() addNodeAction = menu.addAction("Add Node") addNodeAction.triggered.connect(self.addNodeTreeNode) addNodeAction = menu.addAction("Remove This Relationship") addNodeAction.triggered.connect(self.removeTreeNode) menu.exec_(self.tvPath.mapToGlobal(position)) return def addOutboundRel(self): # get the action that called this method aSubAction = QObject.sender(self) relTemplateDict = aSubAction.data() if not relTemplateDict is None: # add the rel template to the path self.addRelTreeNode(templateName=relTemplateDict["name"]) # add the node template to the path self.addNodeTreeNode(templateName=relTemplateDict["toTemplate"]) def addInboundRel(self): # get the action that called this method aSubAction = QObject.sender(self) relTemplateDict = aSubAction.data() if not relTemplateDict is None: # add the rel template to the path self.addRelTreeNode(templateName=relTemplateDict["name"]) # add the node template to the path self.addNodeTreeNode(templateName=relTemplateDict["fromTemplate"]) def addRelTreeNode(self, templateName=None): '''add a relationship tree-node into the query path tree''' parentItem = self.tvPath.currentItem() parentDict = parentItem.data(0, Qt.UserRole).dict() parentOrder = parentDict.get("order", 0) order = self.getMaxTreeOrder() + 1 # print("add rel node {}-{}".format(order, parentOrder)) queryPathNode = QueryPathNode(parentOrder=parentOrder, order=order, type="Relationship", templateName=templateName) queryPathNode.treeItem = self.addTreeNode(parent=parentItem, pathItem=queryPathNode) self.tvPath.setCurrentItem(queryPathNode.treeItem) def addNodeTreeNode(self, templateName=None): '''add a node tree-node into the query path tree''' # don't know why this happens... if type(templateName) is bool: templateName = None parentItem = self.tvPath.currentItem() parentDict = parentItem.data(0, Qt.UserRole).dict() parentOrder = parentDict.get("order", 0) order = self.getMaxTreeOrder() + 1 # print("add node node {}-{}".format(order, parentOrder)) queryPathNode = QueryPathNode(parentOrder=parentOrder, order=order, type="Node", templateName=templateName) queryPathNode.treeItem = self.addTreeNode(parent=parentItem, pathItem=queryPathNode) self.tvPath.setCurrentItem(queryPathNode.treeItem) def removeTreeNode(self): '''remove a node from the tree and all descedants''' # print("remove item") currentItem = self.tvPath.currentItem() parentItem = currentItem.parent() parentItem.removeChild(currentItem) self.tvPath.takeTopLevelItem( self.tvPath.indexOfTopLevelItem(currentItem)) #----------------------------------------------------------------------------------------------------------------------- # return grid methods #----------------------------------------------------------------------------------------------------------------------- def createOutputModel(self): # SOURCE, SOURCENAME, COLTYPE, COLVAL, COLNAME, DISPLAYIT, SORT, GROUP model = QStandardItemModel(0, 8) model.setHeaderData(SOURCE, Qt.Horizontal, "Source") model.setHeaderData(SOURCENAME, Qt.Horizontal, "Source Name") model.setHeaderData(COLTYPE, Qt.Horizontal, "Column Type") model.setHeaderData(COLVAL, Qt.Horizontal, "Column Value") model.setHeaderData(COLNAME, Qt.Horizontal, "Column Name") model.setHeaderData(DISPLAYIT, Qt.Horizontal, "Display ?") model.setHeaderData(SORT, Qt.Horizontal, "Sort") model.setHeaderData(GROUP, Qt.Horizontal, "Group") return model def addOutputRow(self, source=None, sourceName=None, colType=None, colVal=None, colName=None, displayIt=None, sort=None, group=None): ''' add a row to the output grid SOURCE, SOURCENAME, COLTYPE, COLVAL, COLNAME, DISPLAYIT, SORT, GROUP ''' self.gridOutput.setSortingEnabled(False) # SOURCE if source is None: source = "" item1 = QStandardItem(source) item1.setEditable(True) # SOURCENAME if sourceName is None: sourceName = "" item2 = QStandardItem(sourceName) item2.setEditable(True) # COLTYPE if colType is None: colType = "" item3 = QStandardItem(colType) item3.setEditable(True) # COLVAL if colVal is None: colVal = "" item4 = QStandardItem(colVal) item4.setEditable(True) # COLNAME if colName is None: colName = "" item5 = QStandardItem(colName) item5.setEditable(True) # DISPLAYIT if displayIt is None: displayIt = Qt.Checked item6 = QStandardItem() item6.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) item6.setText("Display") if displayIt in [0, 1, 2]: item6.setCheckState(displayIt) else: item2.setCheckState(Qt.Unchecked) item6.setEditable(True) # SORT if sort is None: sort = "No" item7 = QStandardItem(sort) item7.setEditable(True) # GROUP if group is None: group = "No" item8 = QStandardItem(group) item8.setEditable(True) self.gridOutput.model().appendRow( [item1, item2, item3, item4, item5, item6, item7, item8]) @pyqtSlot() def on_btnSaveAs_clicked(self): """ User clicks button to save the generated query """ return @pyqtSlot() def on_btnAdd_clicked(self): """ User clicks Add Property """ self.addOutputRow() @pyqtSlot() def on_btnRemove_clicked(self): """ User clicks remove row button """ indexes = self.gridOutput.selectionModel().selectedIndexes() for index in indexes: self.gridOutput.model().removeRows(index.row(), 1) @pyqtSlot() def on_btnUp_clicked(self): """ User clicks Up button """ self.helper.moveTableViewRowUp(self.gridOutput) @pyqtSlot() def on_btnDown_clicked(self): """ User clicks Down button """ self.helper.moveTableViewRowDown(self.gridOutput) @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) def on_tvPath_currentItemChanged(self, current, previous): """ Slot documentation goes here. @param current DESCRIPTION @type QTreeWidgetItem @param previous DESCRIPTION @type QTreeWidgetItem """ return @pyqtSlot(QModelIndex) def on_tvPath_clicked(self, index): """ Slot documentation goes here. @param index DESCRIPTION @type QModelIndex """ # print("on_tvPath_clicked") return @pyqtSlot(QModelIndex) def on_tvPath_activated(self, index): """ Slot documentation goes here. @param index DESCRIPTION @type QModelIndex """ # print("on_tvPath_activated") return @pyqtSlot(QTreeWidgetItem, int) def on_tvPath_itemClicked(self, item, column): """ Slot documentation goes here. @param item DESCRIPTION @type QTreeWidgetItem @param column DESCRIPTION @type int """ # print("on_tvPath_itemClicked") return @pyqtSlot(QTreeWidgetItem, int) def on_tvPath_itemActivated(self, item, column): """ Slot documentation goes here. @param item DESCRIPTION @type QTreeWidgetItem @param column DESCRIPTION @type int """ # print("on_tvPath_itemActivated") return @pyqtSlot(QTreeWidgetItem, int) def on_tvPath_itemEntered(self, item, column): """ Slot documentation goes here. @param item DESCRIPTION @type QTreeWidgetItem @param column DESCRIPTION @type int """ # print("on_tvPath_itemEntered") return @pyqtSlot() def on_tvPath_itemSelectionChanged(self): """ Slot documentation goes here. """ # print("on_tvPath_itemSelectionChanged") selected = self.tvPath.currentItem() if not (selected is None): # parent = self.tvPath.currentItem().parent() queryPathNode = selected.data(0, Qt.UserRole) self.populateMetaBox(queryPathNode=queryPathNode)
class TRPropertyBox(QDialog, Ui_TRPropertyBox): """ This displays and manages the Relationship Template Dialog box """ def __init__(self, parent=None, mode=None, objectDict=None, designModel = None): """ objectDict - Relationship Template object dictionary. For creating a new Relationship Template this will be None @param parent reference to the parent widget @type QWidget """ super(TRPropertyBox, self).__init__(parent) self.parent = parent self.schemaModel = self.parent.schemaObject self.settings = QSettings() self.formatChanged = False self.helper = Helper() self.setupUi(self) self.designModel = designModel self.modelData = self.designModel.modelData if objectDict is None: self.objectDict = self.designModel.newRelTemplateDict() else: self.objectDict = objectDict self.mode = mode # get the class that controls the data grid for relationship templates self.RelTemplateCypher = RelTemplateCypher(templateDict=self.objectDict) # get neocon object for this project page self.neoCon = NeoDriver(name=self.parent.pageItem.neoConName, promptPW=self.parent.pageItem.promptPW) # Properties Grid self.gridProps.setModel(self.createPropModel()) comboPropList = [""] comboPropList.extend(sorted(set(self.designModel.instanceList("Property") + self.schemaModel.instanceList("Property")))) dataTypeList = [dataType.value for dataType in DataType] self.gridProps.setItemDelegateForColumn(DATATYPE, CBDelegate(self, dataTypeList, setEditable=False )) self.gridProps.setItemDelegateForColumn(PROPERTY, CBDelegate(self, comboPropList, setEditable=True )) self.gridProps.setItemDelegateForColumn(PROPDEF, NeoEditDelegate(self)) self.gridProps.setColumnWidth(PROPERTY, 350) self.gridProps.setColumnWidth(DATATYPE, 125) self.gridProps.setColumnWidth(PROPREQ, 100) self.gridProps.setColumnWidth(PROPDEF, 150) self.gridProps.setColumnWidth(EXISTS, 100) self.gridProps.setSelectionBehavior(QAbstractItemView.SelectItems) self.gridProps.setSelectionMode(QAbstractItemView.SingleSelection) header = self.gridProps.horizontalHeader() header.setSectionResizeMode(PROPERTY, QHeaderView.Interactive) header.setSectionResizeMode(DATATYPE, QHeaderView.Fixed) header.setSectionResizeMode(PROPREQ, QHeaderView.Fixed) header.setSectionResizeMode(PROPDEF, QHeaderView.Interactive) header.setSectionResizeMode(EXISTS, QHeaderView.Fixed) # constraints grid self.gridConstraints.setModel(self.createConstraintsModel()) conTypeList = ["Property Exists"] self.gridConstraints.setItemDelegateForColumn(CONTYPE, CBDelegate(self, conTypeList, setEditable=False )) self.gridConstraints.setColumnWidth(CONTYPE, 120) self.gridConstraints.setColumnWidth(CONPROP, 350) header = self.gridConstraints.horizontalHeader() header.setSectionResizeMode(CONTYPE, QHeaderView.Fixed) header.setSectionResizeMode(CONPROP, QHeaderView.Interactive) # populate the rel template dropdowns self.loadTemplateDropdowns() # add the data grid widget. self.relGrid = DataGridWidget(self, neoCon=self.neoCon, genCypher=self.RelTemplateCypher) self.relGridLayout = QVBoxLayout(self.dataTabFrame) self.relGridLayout.setObjectName("relGridLayout") self.relGridLayout.addWidget(self.relGrid) #populate ui data from object self.populateUIfromObject() # populate combo boxes used on constraint and index grids self.updateComboBoxes() # sync definition checkboxes with constraints self.syncDefCheckBoxes() if self.mode == "NEW": self.txtRelTemplateName.setFocus() else: # disable definition fields self.txtRelTemplateName.setEnabled(False) self.cboRelName.setEnabled(False) # disable from and to nodes if they have been defined if self.cmbFromTemplate.currentIndex() > 0: self.cmbFromTemplate.setEnabled(False) if self.cmbToTemplate.currentIndex() > 0: self.cmbToTemplate.setEnabled(False) def updateComboBoxes(self): propList = [""] + [ self.gridProps.model().item(row,PROPERTY).data(Qt.EditRole) for row in range(0,self.gridProps.model().rowCount())] self.gridConstraints.setItemDelegateForColumn(CONPROP, CBDelegate(self, propList, setEditable=True )) def loadTemplateDropdowns(self, ): # load source node template dropdown dropdownList = [] dropdownList.append("No Template Selected") nodeList = dropdownList + self.designModel.instanceList("Node Template") self.cmbFromTemplate.addItems(nodeList) self.cmbToTemplate.addItems(nodeList) def createPropModel(self): model = QStandardItemModel(0, 5) model.setHeaderData(PROPERTY, Qt.Horizontal, "Property") model.setHeaderData(DATATYPE, Qt.Horizontal, "Data Type") model.setHeaderData(PROPREQ, Qt.Horizontal, "Required") model.setHeaderData(PROPDEF, Qt.Horizontal, "Default Value") model.setHeaderData(EXISTS, Qt.Horizontal, "Exists") # connect model slots model.itemChanged.connect(self.propModelItemChanged) return model def createConstraintsModel(self): # CONSTRAINT GRID - CONTYPE, CONPROP, model = QStandardItemModel(0,2) model.setHeaderData(CONTYPE, Qt.Horizontal, "Constraint Type") model.setHeaderData(CONPROP, Qt.Horizontal, "Property") # connect model slots model.itemChanged.connect(self.constraintModelItemChanged) return model def populateUIfromObject(self, ): # default the combo boxes to nothing selected self.cmbFromTemplate.setCurrentIndex(0) self.cmbToTemplate.setCurrentIndex(0) # get the custom template format if any self.TRFormatDict = self.objectDict.get("TRformat", None) if self.TRFormatDict == None: self.rbTemplateDefaultFormat.setChecked(True) self.btnDefineTemplateFormat.setEnabled(False) else: self.rbTemplateCustomFormat.setChecked(True) self.btnDefineTemplateFormat.setEnabled(True) # get the custom instance format if any self.IRFormatDict = self.objectDict.get("IRformat", None) if self.IRFormatDict == None: self.rbInstanceDefaultFormat.setChecked(True) self.btnDefineInstanceFormat.setEnabled(False) else: self.rbInstanceCustomFormat.setChecked(True) self.btnDefineInstanceFormat.setEnabled(True) # name, relname, desc self.txtRelTemplateName.insert(str(self.objectDict["name"])) self.loadRelationshipNameDropDown() self.editDescription.appendPlainText(self.objectDict["desc"]) # from and to node templates index = self.cmbFromTemplate.findText(self.objectDict["fromTemplate"]) if index >= 0: self.cmbFromTemplate.setCurrentIndex(index) index = self.cmbToTemplate.findText(self.objectDict["toTemplate"]) if index >= 0: self.cmbToTemplate.setCurrentIndex(index) # from and to cardinalities index = self.cmbFromCardinality.findText(self.objectDict["fromCardinality"]) if index >= 0: self.cmbFromCardinality.setCurrentIndex(index) index = self.cmbToCardinality.findText(self.objectDict["toCardinality"]) if index >= 0: self.cmbToCardinality.setCurrentIndex(index) # cardinality description self.editToFromType.setText("Node Type {}".format(self.cmbFromTemplate.currentText())) self.editFromToType.setText("Node Type {}".format(self.cmbToTemplate.currentText())) #properties for relProp in self.objectDict["properties"]: dataType = self.designModel.getPropertyDataType(relProp[PROPERTY]) self.addProp(self.gridProps.model(), relProp[PROPERTY], dataType, relProp[PROPREQ], relProp[PROPDEF], relProp[EXISTS]) # load constraints try: #CONTYPE, CONLBL, CONPROP, CONPROPLIST for nodeCon in self.objectDict["constraints"]: self.addConstraint(self.gridConstraints.model(), nodeCon[CONTYPE], nodeCon[CONPROP]) except: pass def loadRelationshipNameDropDown(self, ): # load relationship name dropdown dropdownList = [] dropdownList.append("") # blank option for user to enter a new rel type # get relationship types from the project model templateRelList = [relDict["relname"] for relDict in self.modelData["Relationship Template"] ] # get relationship types from the schemamodel - this needs to be fixed, need common access to schemamodel from project page and instance diagram schemaRelList = [rel["name"] for rel in self.schemaModel.schemaData["Relationship"] ] # schemaRelList = [] # merge, dedup, and sort the two lists and put them in the dropdown dropdownList.extend(sorted(set(templateRelList + schemaRelList))) self.cboRelName.addItems(dropdownList) # if no relationship name has been set then display enabled combo box if str(self.objectDict["relname"]) is None: self.cboRelName.setCurrentIndex(0) elif (str(self.objectDict["relname"]) == "NoRelationshipName" or len(self.objectDict["relname"]) < 1): self.cboRelName.setCurrentIndex(0) else: # if the relationship name has been set then display the rel name index = self.cboRelName.findText(str(self.objectDict["relname"])) if index >= 0: self.cboRelName.setCurrentIndex(index) else: # its not an existing name so just set the text in the combo box self.cboRelName.setEditText(str(self.objectDict["relname"])) # # PROPERTY GRID BUTTONS # @pyqtSlot() def on_btnPropUp_clicked(self): """ User clicks on property up button """ self.helper.moveTableViewRowUp(self.gridProps) @pyqtSlot() def on_btnPropDown_clicked(self): """ User clicks on property down button """ self.helper.moveTableViewRowDown(self.gridProps) @pyqtSlot() def on_btnPropAdd_clicked(self): """ Slot documentation goes here. """ self.gridProps.setSortingEnabled(False) self.addProp(self.gridProps.model(), "","Unknown","N","Null","N") # generic function to adjust grid after adding a row self.helper.adjustGrid(grid=self.gridProps) # # scroll to bottom to insure the new row is visible # self.gridProps.scrollToBottom() @pyqtSlot() def on_btnPropRemove_clicked(self): """ Slot documentation goes here. """ # indexes = self.gridProps.selectionModel().selectedIndexes() # for index in sorted(indexes): # print('Row %d is selected' % index.row()) # self.gridProps.model().removeRows(index.row(),1) indexes = self.gridProps.selectionModel().selectedIndexes() if len(indexes) > 0: for index in sorted(indexes): self.gridProps.model().removeRows(index.row(),1) else: self.helper.displayErrMsg("Remove Property", "You must select a row to remove") def addProp(self,model,prop,dataType, propreq, propdef, exists): item1 = QStandardItem(prop) item1.setEditable(True) item11 = QStandardItem(dataType) item11.setEditable(False) item12 = QStandardItem(propreq) item12.setEditable(True) item12.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) item12.setText("Required") item13 = QStandardItem(propdef) # store the datatype in role + 1 item13.setData(dataType, Qt.UserRole+1) item13.setEditable(True) item2 = QStandardItem() item2.setFlags(Qt.ItemIsEnabled) item2.setText("Exists") if exists in [0, 1, 2]: item2.setCheckState(exists) else: item2.setCheckState(Qt.Unchecked) if propreq in [0, 1, 2]: item12.setCheckState(propreq) else: item12.setCheckState(Qt.Unchecked) model.appendRow([item1, item11, item12,item13, item2]) def handleItemClicked(self, item): return def addConstraint(self,model, conType, prop): # CONTYPE, CONPROP item1 = QStandardItem(conType) item1.setEditable(True) item3 = QStandardItem(prop) item3.setEditable(True) model.appendRow([item1, item3]) def syncDefCheckBoxes(self): # clear all checkboxes on the definition tab self.clearCheckBoxes() # loop through the constraints grid model=self.gridConstraints.model() numrows = model.rowCount() for row in range(0, numrows): relCon = [model.item(row,CONTYPE).data(Qt.EditRole), model.item(row,CONPROP).data(Qt.EditRole)] # process Property Exists if relCon[CONTYPE] == "Property Exists": self.setPropCheckBox(prop=relCon[CONPROP], chkBox=EXISTS ) self.setPropCheckBox(prop=relCon[CONPROP], chkBox=PROPREQ ) def setPropCheckBox(self, prop=None, chkBox=None): try: model = self.gridProps.model() numrows = model.rowCount() if not prop is None: for row in range(0,numrows): if prop == model.item(row,PROPERTY).data(Qt.EditRole): model.item(row,chkBox).setCheckState(Qt.Checked) except: pass def clearCheckBoxes(self): model = self.gridProps.model() numrows = model.rowCount() for row in range(0,numrows): model.item(row,EXISTS).setCheckState(0) ################################################################################# # DIALOG BUTTONS ################################################################################# @pyqtSlot() def on_okButton_clicked(self): """ Slot documentation goes here. """ if self.validate(): self.apply() QDialog.accept(self) @pyqtSlot() def on_cancelButton_clicked(self): """ User Selects the Cancel button """ QDialog.reject(self) def validate(self): if self.objectDict is None: self.objectDict = {} templateName = self.txtRelTemplateName.text() # template name if self.helper.NoTextValueError(templateName, "Must enter a Relationship Template Name"): self.txtRelTemplateName.setFocus() return False # relationship name # name = self.txtRelName.text() name = self.cboRelName.currentText() if self.helper.NoTextValueError(name, "Must enter a Relationship Name"): # self.txtRelName.setFocus() self.cboRelName.setFocus() return False # dup check if self.mode == 'NEW': if self.helper.DupObjectError(designModel = self.designModel, objName=templateName, topLevel = "Relationship Template", txtMsg = "A Relationship Template named {} already exists".format(name)): self.txtRelTemplateName.setFocus() return False # must have a From Node Template if self.helper.NoComboBoxSelectionError(self.cmbFromTemplate, "Must enter a From Node Template"): self.cmbFromTemplate.setFocus() return False # must have a To Node Template if self.helper.NoComboBoxSelectionError(self.cmbToTemplate, "Must enter a To Node Template"): self.cmbToTemplate.setFocus() return False # property name must exist if self.helper.gridNoNameError(grid=self.gridProps, col=PROPERTY, txtMsg="You must supply a name for each Property"): self.gridProps.setFocus() return False # dup property if self.helper.gridDupEntryError(self.gridProps, col=PROPERTY, txtMsg="has been entered more than once. You can only use a Property once"): self.gridProps.setFocus() return False # # required property must have a default value # model = self.gridProps.model() # numrows = model.rowCount() # for row in range(0,numrows): # nodeProp = [model.item(row,PROPERTY).data(Qt.EditRole), # model.item(row,DATATYPE).data(Qt.EditRole), # model.item(row,PROPREQ).checkState(), # model.item(row,PROPDEF).data(Qt.EditRole), # model.item(row,EXISTS).checkState()] # if nodeProp[PROPREQ] == Qt.Checked and nodeProp[PROPDEF] == "": # self.helper.displayErrMsg("Validate", "Required property {} must have a default value.".format(nodeProp[PROPERTY])) # self.gridProps.setFocus() # return False # constraint type exists if self.helper.gridNoNameError(grid=self.gridConstraints, col=CONTYPE, txtMsg="You must select a constraint type"): self.tabRelTemplate.setCurrentIndex(CONSTRAINT) self.gridConstraints.setFocus() return False model =self.gridConstraints.model() numrows = model.rowCount() for row in range(0, numrows): nodeCon = [model.item(row,CONTYPE).data(Qt.EditRole), model.item(row,CONPROP).data(Qt.EditRole)] if nodeCon[CONTYPE] in ["Property Exists", "Property Unique"]: if self.helper.NoTextValueError(nodeCon[CONPROP], "Row {} - You must select a property".format(row+1)): self.tabRelTemplate.setCurrentIndex(CONSTRAINT) self.gridConstraints.setFocus() return False # passed all edits so return True return True def apply(self, ): self.objectDict["TRformat"] = self.TRFormatDict self.objectDict["IRformat"] = self.IRFormatDict self.objectDict["name"] = self.txtRelTemplateName.text() # self.objectDict["relname"] = self.txtRelName.text() self.objectDict["relname"] = self.cboRelName.currentText() # if its a new rel type then add it to the model self.designModel.newRelationship(self.objectDict["relname"]) if self.cmbFromTemplate.currentIndex() > 0: self.objectDict["fromTemplate"] = self.cmbFromTemplate.currentText() else: self.objectDict["fromTemplate"] = '' if self.cmbToTemplate.currentIndex() > 0: self.objectDict["toTemplate"] = self.cmbToTemplate.currentText() else: self.objectDict["toTemplate"] = '' self.objectDict["fromCardinality"] = self.cmbFromCardinality.currentText() self.objectDict["toCardinality"] = self.cmbToCardinality.currentText() desc = self.editDescription.toPlainText() if desc is not None: self.objectDict["desc"] = desc #save the properties self.objectDict["properties"] = [] model = self.gridProps.model() numrows = model.rowCount() for row in range(0,numrows): relProp = [model.item(row,PROPERTY).data(Qt.EditRole), model.item(row,DATATYPE).data(Qt.EditRole), model.item(row,PROPREQ).checkState(), model.item(row,PROPDEF).data(Qt.EditRole), model.item(row,EXISTS).checkState()] self.designModel.newProperty(relProp[PROPERTY], relProp[DATATYPE]) # check to see if this is a new Property and create a Property object in the dictionary self.objectDict["properties"].append(relProp) # save the constraints # CONTYPE, CONPROP self.objectDict["constraints"] = [] model =self.gridConstraints.model() numrows = model.rowCount() for row in range(0, numrows): relCon = [model.item(row,CONTYPE).data(Qt.EditRole), model.item(row,CONPROP).data(Qt.EditRole)] self.objectDict["constraints"].append(relCon) @pyqtSlot() def on_rbDefaultFormat_clicked(self): """ Slot documentation goes here. """ self.btnDefineFormat.setEnabled(False) @pyqtSlot() def on_rbCustomFormat_clicked(self): """ Slot documentation goes here. """ self.btnDefineFormat.setEnabled(True) @pyqtSlot() def on_btnDefineFormat_clicked(self): """ Slot documentation goes here. """ d = IRelFormatDlg(self, modelData = None, relFormat = IRelFormat(formatDict=self.TRFormatDict)) if d.exec_(): self.TRFormatDict = IRelFormat(formatDict=d.relFormat.formatDict).formatDict # tell diagrams to redraw self.formatChanged = True def constraintModelItemChanged(self, item): self.syncDefCheckBoxes() @pyqtSlot() def on_btnAddConstraint_clicked(self): """ Slot documentation goes here. """ self.gridConstraints.setSortingEnabled(False) self.addConstraint(self.gridConstraints.model(), "", "") # generic function to adjust grid after adding a row self.helper.adjustGrid(grid=self.gridConstraints) # # scroll to bottom to insure the new row is visible # self.gridConstraints.scrollToBottom() @pyqtSlot() def on_btnRemoveConstraint_clicked(self): """ User clicked the remove row button """ indexes = self.gridConstraints.selectionModel().selectedIndexes() if len(indexes) > 0: for index in sorted(indexes): self.gridConstraints.model().removeRows(index.row(),1) self.syncDefCheckBoxes() else: self.helper.displayErrMsg("Remove Constraint", "You must select a row to remove") # indexes = self.gridConstraints.selectionModel().selectedRows() # for index in sorted(indexes): # self.gridConstraints.model().removeRows(index.row(),1) def propModelItemChanged(self, item): # print("item data changed {} at {} {}".format(str(item.checkState()), item.index().row(), item.index().column())) # this fires when checkbox is selected or deselected. columnIndex = item.index().column() propName = self.gridProps.model().item(item.index().row(),PROPERTY).data(Qt.EditRole) dataType = self.designModel.getPropertyDataType(propName) if columnIndex == PROPERTY: # if property has changed then change the datatype propName = self.gridProps.model().item(item.index().row(),PROPERTY).data(Qt.EditRole) # get the defined datatype for the property dataType = self.designModel.getPropertyDataType(propName) self.gridProps.model().item(item.index().row(), DATATYPE).setText(dataType) # store the datatype in role + 1 for the default value self.gridProps.model().item(item.index().row(), PROPDEF).setData(dataType, Qt.UserRole+1) # set default value to a null string self.gridProps.model().item(item.index().row(), PROPDEF).setText("") # if the property doesn't exist yet then allow the datatype to be changed if self.designModel.objectExists(topLevel="Property",objectName=propName ) == False: self.gridProps.model().item(item.index().row(), DATATYPE).setEditable(True) else: self.gridProps.model().item(item.index().row(), DATATYPE).setEditable(False) if columnIndex == DATATYPE: # datatype changed so reset default value and store new datatype dataType = self.gridProps.model().item(item.index().row(),DATATYPE).data(Qt.EditRole) # store the datatype in role + 1 self.gridProps.model().item(item.index().row(), PROPDEF).setData(dataType, Qt.UserRole+1) self.gridProps.model().item(item.index().row(), PROPDEF).setText("") @pyqtSlot(int) def on_tabRelTemplate_currentChanged(self, index): """ If the user has switched to the data tab then update the object dictionary @param index DESCRIPTION @type int """ # user switched to the description tab. must regenerate description if index == DESCRIPTION: self.apply() self.brwsrGeneratedDesc.setText(self.designModel.getRelationshipDescription(self.objectDict)) # user switched to the definition tab. must resync checkboxes with the current values on the constraints tab if index == DEFINITION: self.syncDefCheckBoxes() if index == DATAGRID: if self.validate(): self.apply() self.relGrid.refreshGrid() else: self.tabRelTemplate.setCurrentIndex(0) # user switched to the constraint tab so update the combo boxes to get latest values from def if index == CONSTRAINT: self.updateComboBoxes() @pyqtSlot() def on_btnDefineTemplateFormat_clicked(self): """ Display the Node Template Format Editor """ myTemplateRelFormatDict = self.TRFormatDict # if the template doesn't have a specific instance node format then get the project default if myTemplateRelFormatDict is None: myTemplateRelFormatDict = deepcopy(self.modelData["TRformat"]) d = TRelFormatDlg(self, modelData = None, relFormat = TRelFormat(formatDict=myTemplateRelFormatDict)) if d.exec_(): # self.TRFormatDict = TRelFormat(formatDict=d.relFormat.formatDict).formatDict self.TRFormatDict = d.relFormat.formatDict self.formatChanged = True @pyqtSlot() def on_btnDefineInstanceFormat_clicked(self): """ Display the Instance Node Format Editor """ myInstanceRelFormatDict = self.IRFormatDict # if the template doesn't have a specific instance rel format then get the project default if myInstanceRelFormatDict is None: myInstanceRelFormatDict = deepcopy(self.modelData["IRformat"]) d = IRelFormatDlg(self, modelData = None, relFormat = IRelFormat(formatDict=myInstanceRelFormatDict)) if d.exec_(): # self.IRFormatDict = IRelFormat(formatDict=d.relFormat.formatDict).formatDict self.IRFormatDict = d.relFormat.formatDict self.formatChanged = True @pyqtSlot() def on_rbTemplateDefaultFormat_clicked(self): """ If default radio button selected, then disable the define format button """ self.btnDefineTemplateFormat.setEnabled(False) @pyqtSlot() def on_rbTemplateCustomFormat_clicked(self): """ If custom radio button selected, then enable the define format button """ self.btnDefineTemplateFormat.setEnabled(True) @pyqtSlot() def on_rbInstanceDefaultFormat_clicked(self): """ If default radio button selected, then disable the define format button """ self.btnDefineInstanceFormat.setEnabled(False) @pyqtSlot() def on_rbInstanceCustomFormat_clicked(self): """ If custom radio button selected, then enable the define format button """ self.btnDefineInstanceFormat.setEnabled(True) @pyqtSlot() def on_btnSetDefaultNull_clicked(self): """ User requests to set a property default to Null """ # grid only allows single selection indexes = self.gridProps.selectionModel().selectedIndexes() for index in indexes: valueIndex = self.gridProps.model().index(index.row(), PROPDEF) self.gridProps.model().setData(valueIndex, "Null", Qt.DisplayRole) @pyqtSlot(int) def on_cmbToTemplate_currentIndexChanged(self, index): """ The to template changed @param index DESCRIPTION @type int """ self.editFromToType.setText("Node Type {}".format(self.cmbToTemplate.currentText())) # print(self.editFromToType.text()) @pyqtSlot(int) def on_cmbFromTemplate_currentIndexChanged(self, index): """ The from template changed @param index DESCRIPTION @type int """ self.editToFromType.setText("Node Type {}".format(self.cmbFromTemplate.currentText())) # print(self.editToFromType.text()) @pyqtSlot(int) def on_cmbFromCardinality_currentIndexChanged(self, index): """ Slot documentation goes here. @param index DESCRIPTION @type int """ self.editFromToType.setText("Node Type {}".format(self.cmbToTemplate.currentText()))
class IRPropertyBox(QDialog, Ui_IRPropertyBox): """ Class documentation goes here. """ def __init__(self, parent=None, diagramInstance=None, model=None): """ Constructor @param parent reference to the parent widget @type QWidget """ super(IRPropertyBox, self).__init__(parent) self.appStartup = True self.mergeTemplate = False self.parent = parent self.schemaModel = self.parent.schemaObject self.helper = Helper() # this is the RelationInstance object - called generically diagramInstance. self.diagramInstance = diagramInstance self.diagramInstance.reloadDictValues() self.model = model self.modelData = self.model.modelData self.rel = None self.setupUi(self) # self.appSetToTemplate = False # self.appSetFromTemplate = False self.initUI() self.populateUIfromObject() self.appStartup = False self.msg = "" def initUI(self, ): # property grid self.gridProps.setSortingEnabled(False) self.gridProps.setModel(self.createPropModel()) self.gridProps.setSortingEnabled(False) comboPropList = [""] comboPropList.extend( sorted( set( self.model.instanceList("Property") + self.schemaModel.instanceList("Property")))) dataTypeList = [dataType.value for dataType in DataType] self.gridProps.setItemDelegate(NeoEditDelegate(self)) self.gridProps.setItemDelegateForColumn( DATATYPE, CBDelegate(self, dataTypeList, setEditable=False)) self.gridProps.setItemDelegateForColumn( PROPERTY, CBDelegate(self, comboPropList, setEditable=True)) self.gridProps.setColumnWidth(PROPERTY, 200) self.gridProps.setColumnWidth(DATATYPE, 125) self.gridProps.setColumnWidth(VALUE, 300) self.gridProps.setSelectionBehavior(QAbstractItemView.SelectItems) self.gridProps.setSelectionMode(QAbstractItemView.SingleSelection) header = self.gridProps.horizontalHeader() header.setSectionResizeMode(PROPERTY, QHeaderView.Interactive) header.setSectionResizeMode(DATATYPE, QHeaderView.Fixed) header.setSectionResizeMode(VALUE, QHeaderView.Stretch) def populateUIfromObject(self, ): # load rel name list, select rel name self.loadRelationshipNameDropDown() # load from nodes, select the from node and load the from node template if any self.loadFromNodeDropdown() # load to nodes, select the to node and load the to node template if any self.loadToNodeDropdown() # load rel template names, select rel template name if any self.loadRelTemplateDropDown() #NZID try: self.EditNZID.insert(str(self.diagramInstance.NZID)) except: print("no NZID") # this shouldn't happen # neoID try: self.editNeoID.insert(str(self.diagramInstance.neoID)) except: self.editNeoID.insert("None") # disable rel name if already defined if self.editNeoID.text() == "None": self.cboRelName.setEnabled(True) else: self.cboRelName.setEnabled(False) # disable dropdowns if start and end nodes defined if self.cboFromNode.currentIndex() > 0: self.cboFromNode.setEnabled(False) if self.cboToNode.currentIndex() > 0: self.cboToNode.setEnabled(False) # load the properties for nodeProp in self.diagramInstance.propList: self.addProp(self.gridProps.model(), nodeProp[PROPERTY], nodeProp[DATATYPE], nodeProp[VALUE]) def loadRelationshipNameDropDown(self, ): # load relationship name (type) dropdown dropdownList = [] dropdownList.append("Enter or Select Relationship Type") # get relationship types from the project model templateRelList = [ relDict["name"] for relDict in self.modelData["Relationship"] ] # templateRelList = [relDict["relname"] for relDict in self.modelData["Relationship Template"] ] # get relationship types from the schemamodel - this needs to be fixed, need common access to schemamodel from project page and instance diagram schemaRelList = [ rel["name"] for rel in self.schemaModel.schemaData["Relationship"] ] # schemaRelList = [] # merge, dedup, and sort the two lists and put them in the dropdown dropdownList.extend(sorted(set(templateRelList + schemaRelList))) self.cboRelName.addItems(dropdownList) # if no relationship name has been set then display enabled combo box if (self.diagramInstance.relName is None or self.diagramInstance.relName == "NoRelationshipName"): self.cboRelName.setCurrentIndex(0) else: # if the relationship name has been set then display the rel name index = self.cboRelName.findText(self.diagramInstance.relName) if index >= 0: self.cboRelName.setCurrentIndex(index) else: # its not a template rel name so just set the text value of the combo box self.cboRelName.setEditText(self.diagramInstance.relName) def loadFromNodeDropdown(self, ): # load from node dropdown dropdownList = [] dropdownList.append("No From Node Selected") nodeList = self.model.instanceDisplayNameList("Instance Node") for node in nodeList: dropdownList.append(node) self.cboFromNode.addItems(dropdownList) if not self.diagramInstance.startNZID is None: item, objectDict = self.model.getDictByName( "Instance Node", self.diagramInstance.startNZID) if not objectDict is None: index = self.cboFromNode.findText( objectDict.get("displayName", "")) if index >= 0: self.cboFromNode.setCurrentIndex(index) self.cboFromNode.setEnabled(False) def loadToNodeDropdown(self, ): # load from node dropdown dropdownList = [] dropdownList.append("No To Node Selected") nodeList = self.model.instanceDisplayNameList("Instance Node") for node in nodeList: dropdownList.append(node) self.cboToNode.addItems(dropdownList) if not self.diagramInstance.endNZID is None: item, objectDict = self.model.getDictByName( "Instance Node", self.diagramInstance.endNZID) if not objectDict is None: index = self.cboToNode.findText( objectDict.get("displayName", "")) if index >= 0: self.cboToNode.setCurrentIndex(index) self.cboToNode.setEnabled(False) def loadRelTemplateDropDown(self, ): # load rel template dropdown self.cboTemplate.clear() startNodeTemplate = self.txtFromTemplate.text() endNodeTemplate = self.txtToTemplate.text() # relName = self.diagramInstance.relName relName = self.cboRelName.currentText() dropdownList = ["No Template Selected"] dropdownList.extend( sorted( self.model.matchAllRelTemplates( startNodeTemplate=startNodeTemplate, endNodeTemplate=endNodeTemplate, relName=relName))) self.cboTemplate.addItems(dropdownList) if not self.diagramInstance.relTemplate is None: index = self.cboTemplate.findText(self.diagramInstance.relTemplate) if index >= 0: self.cboTemplate.setCurrentIndex(index) def createPropModel(self): model = QStandardItemModel(0, 3) model.setHeaderData(PROPERTY, Qt.Horizontal, "Property") model.setHeaderData(DATATYPE, Qt.Horizontal, "Data Type") model.setHeaderData(VALUE, Qt.Horizontal, "Value") # connect model slots model.itemChanged.connect(self.propModelItemChanged) return model def getNodeInstance(self, NZID): ''' Using an NZID, create a NodeInstanceObject and return it if it doesn't exist, return None ''' index, nodeDict = self.model.getDictByName(topLevel="Instance Node", objectName=NZID) nodeInstance = NodeInstance(model=self.model, nodeInstanceDict=nodeDict) return nodeInstance def logMsg(self, msg): # add message to the log if logging: logging.info(msg) def validate(self, ): if self.helper.gridNoNameError( grid=self.gridProps, col=PROPERTY, txtMsg="You must supply a name for each Property"): self.gridProps.setFocus() return False if self.helper.gridDupEntryError( self.gridProps, col=PROPERTY, txtMsg= "has been entered more than once. You can only use a Property once" ): self.gridProps.setFocus() return False if self.helper.NoTextValueError(self.cboRelName.currentText(), "You must enter a relationship name."): self.cboRelName.setFocus() return False if self.cboRelName.currentText( ) == "Enter or Select Relationship Type": self.helper.displayErrMsg("Instance Relationship", "You must enter a relationship name.") self.cboRelName.setFocus() return False if self.cboFromNode.currentIndex() == 0: self.helper.displayErrMsg("Instance Relationship", "You must select a from node.") self.cboFromNode.setFocus() return False if self.cboToNode.currentIndex() == 0: self.helper.displayErrMsg("Instance Relationship", "You must select a to node.") self.cboToNode.setFocus() return False # property datatype matches property definition model = self.gridProps.model() for row in range(0, model.rowCount()): nodeProp = [ model.item(row, PROPERTY).data(Qt.EditRole), model.item(row, DATATYPE).data(Qt.EditRole), model.item(row, VALUE).data(Qt.EditRole) ] if self.model.propertyDataTypeValid( name=nodeProp[PROPERTY], dataType=nodeProp[DATATYPE]) == False: self.helper.displayErrMsg( "Validate", "You entered datatype {} for property {} which does not match the property definition. Please enter the correct datatype." .format(nodeProp[DATATYPE], nodeProp[PROPERTY])) self.gridProps.setFocus() return False # template defined required property has a value templateName = self.cboTemplate.currentText() saveIndex, relTemplateDict = self.model.getDictByName( topLevel="Relationship Template", objectName=templateName) if not relTemplateDict is None: model = self.gridProps.model() numrows = model.rowCount() for row in range(0, numrows): nodeProp = [ model.item(row, PROPERTY).data(Qt.EditRole), model.item(row, DATATYPE).data(Qt.EditRole), model.item(row, VALUE).data(Qt.EditRole) ] if nodeProp[VALUE] == "Null": # the value is null so see if it is required for templateProp in relTemplateDict["properties"]: if templateProp[PROPERTY] == nodeProp[PROPERTY]: if templateProp[PROPREQ] == Qt.Checked: # this property must have a value self.helper.displayErrMsg( "Validate", "The property {} is required. Please enter a value." .format(nodeProp[PROPERTY])) return False return True def apply(self, ): #update the diagramInstance object with values from the UI. self.diagramInstance.relName = self.cboRelName.currentText() # if its a new rel type then add it to the model self.model.newRelationship(self.diagramInstance.relName) # update property list self.diagramInstance.propList = [] model = self.gridProps.model() numrows = model.rowCount() for row in range(0, numrows): nodeProp = [ model.item(row, PROPERTY).data(Qt.EditRole), model.item(row, DATATYPE).data(Qt.EditRole), model.item(row, VALUE).data(Qt.EditRole) ] # print(nodeProp) # if its a new property add it to the model self.model.newProperty(nodeProp[PROPERTY], nodeProp[DATATYPE]) self.diagramInstance.propList.append(nodeProp) #save the template selectedTemplate = self.cboTemplate.currentText() self.diagramInstance.relTemplate = selectedTemplate # save the relationship itself in Neo4j rc, msg = self.updateNeo() self.msg = msg return rc, msg def updateNeo(self, ): QApplication.setOverrideCursor(Qt.WaitCursor) if self.modelData["SyncMode"] == "On": rc, msg = self.diagramInstance.syncToDB(self.logMsg) if rc is True: returnmsg = "Update Relationship Succesful" else: returnmsg = "Update Relationship Failed: {}".format(msg) self.helper.displayErrMsg("Update Relationship", returnmsg) else: rc = True returnmsg = "Database Sync is off - Relationship not saved to graph" QApplication.restoreOverrideCursor() return rc, returnmsg @pyqtSlot() def on_btnPropAdd_clicked(self): """ Slot documentation goes here. """ self.addProp(self.gridProps.model(), "", "String", "Null") # common function to adjust grid after appending a row self.helper.adjustGrid(grid=self.gridProps) # # scroll to bottom to insure the new row is visible # self.gridProps.scrollToBottom() @pyqtSlot() def on_btnPropRemove_clicked(self): """ Slot documentation goes here. """ indexes = self.gridProps.selectionModel().selectedIndexes() if len(indexes) > 0: for index in sorted(indexes): self.gridProps.model().removeRows(index.row(), 1) else: self.helper.displayErrMsg("Remove Property", "You must select a row to remove") @pyqtSlot() def on_btnPropUp_clicked(self): """ User clicks on property up button """ self.helper.moveTableViewRowUp(self.gridProps) @pyqtSlot() def on_btnPropDown_clicked(self): """ User clicks on property down button """ self.helper.moveTableViewRowDown(self.gridProps) @pyqtSlot() def on_okButton_clicked(self): """ User clicked on OK button, validate data and sync to graph """ if self.validate(): rc, msg = self.apply() if rc == True: QDialog.accept(self) @pyqtSlot() def on_cancelButton_clicked(self): """ Slot documentation goes here. """ QDialog.reject(self) def addProp(self, model, prop, dataType, value): self.gridProps.setSortingEnabled(False) item1 = QStandardItem(prop) item1.setEditable(True) item11 = QStandardItem(dataType) item11.setEditable(True) item2 = QStandardItem(value) item2.setData(dataType, Qt.UserRole + 1) item2.setEditable(True) model.appendRow([item1, item11, item2]) @pyqtSlot() def on_btnCreateTemplate_clicked(self): """ Create a new Relationship Template based on this instance relationship. """ text, ok = QInputDialog.getText( self, 'New Relationship Template', 'Enter the Relationship Template Name:') if ok: # make sure they entered something if len(text) < 1: self.helper.displayErrMsg( "Create New Relationship Template Error", "You must enter a name.".format(text)) #make sure entered name doesn't exist index, relDict = self.model.getDictByName("Relationship Template", text) if not relDict is None: self.helper.displayErrMsg( "Create New Relationship Template Error", "The Relationship template {} already exists".format(text)) return # validate the data first, then add the new relationship template if self.validate(): # if its a new rel type then add it to the model self.model.newRelationship(self.cboRelName.currentText()) # save the properties propList = [] model = self.gridProps.model() numrows = model.rowCount() for row in range(0, numrows): nodeProp = [ model.item(row, PROPERTY).data(Qt.EditRole), model.item(row, DATATYPE).data(Qt.EditRole), Qt.Unchecked, "", Qt.Unchecked ] self.model.newProperty(nodeProp[PROPERTY], nodeProp[DATATYPE]) propList.append(nodeProp) # generate the constraints conList = [] # look at each constraint in the schemaModel and see if it belongs to the rel template self.schemaModel.matchConstraintRelTemplate( conList, [prop[0] for prop in propList], relName=self.cboRelName.currentText()) # get the source/target node template if any # can only save a source target if both instance nodes are based on a node template try: startNodeInstance = self.getNodeInstance( self.diagramInstance.startNZID) fromTemplate = startNodeInstance.nodeTemplate endNodeInstance = self.getNodeInstance( self.diagramInstance.endNZID) toTemplate = endNodeInstance.nodeTemplate if (not fromTemplate == "No Template Selected" and not toTemplate == "No Template Selected"): #save it to the model relDict = self.model.newRelTemplateDict( name=text, relname=self.cboRelName.currentText(), propList=propList, desc= "Template generated from Instance Relationship.", conList=conList, fromTemplate=fromTemplate, toTemplate=toTemplate) self.model.modelData["Relationship Template"].append( relDict) # refresh the treeview self.model.updateTV() # update dropdown and select the newly created template self.loadRelTemplateDropDown() index = self.cboTemplate.findText(text) if index >= 0: self.cboTemplate.setCurrentIndex(index) else: self.helper.displayErrMsg( "Create Template", "Cannot create a relationship template without a from node template and a to node template." ) except Exception as e: self.helper.displayErrMsg( "Create Template", "Error creating relationship template: {}".format( str(e))) def templateChange(self, index): if index > 0: self.mergeTemplateWithInstanceRel(self.cboTemplate.currentText()) def mergeTemplateWithInstanceRel(self, templateName): '''this merges the properties from the relationship template with the properties that already exist on the instance relationship ''' # if app is just starting up don't do this if self.appStartup: return # tell the other functions we're merging the template self.mergeTemplate = True saveIndex, relDict = self.model.getDictByName( topLevel="Relationship Template", objectName=templateName) if relDict is None: self.helper.displayErrMsg( "Merge Relationship Template", "Error - Relationship Template {} doesn't exist".format( templateName)) return # properties # what's on the form now existingPropList = [ self.gridProps.model().item(row, PROPERTY).data(Qt.EditRole) for row in range(0, self.gridProps.model().rowCount()) ] existingValueList = [ self.gridProps.model().item(row, VALUE).data(Qt.EditRole) for row in range(0, self.gridProps.model().rowCount()) ] # property list from the template newPropList = [ nodeProp[PROPERTY] for nodeProp in relDict["properties"] ] newValueList = [ nodeProp[PROPDEF] for nodeProp in relDict["properties"] ] # this should get default values some day # merge them together mergePropList = (list(set(existingPropList + newPropList))) mergePropList.sort() self.gridProps.model().removeRows(0, self.gridProps.model().rowCount()) for prop in mergePropList: val = "" # get default value from template first if prop in newPropList: val = newValueList[newPropList.index(prop)] # override with existing value for same property if it exists if prop in existingPropList: val = existingValueList[existingPropList.index(prop)] # set Null so editor delegates will work if val == "" or val is None: val = "Null" dataType = self.model.getPropertyDataType(prop) self.addProp(self.gridProps.model(), prop, dataType, val) self.diagramInstance.relName = relDict["relname"] index = self.cboRelName.findText(self.diagramInstance.relName) if index >= 0: self.cboRelName.setCurrentIndex(index) # we're done merging self.mergeTemplate = False @pyqtSlot(int) def on_cboTemplate_currentIndexChanged(self, index): """ The User has selected a relationship template to use. @param index DESCRIPTION @type int """ # if they selected a rel template then merge it with whatever is on the gui if index > 0: self.mergeTemplateWithInstanceRel(self.cboTemplate.currentText()) @pyqtSlot(int) def on_cboFromNode_currentIndexChanged(self, index): """ The user has selected a new from instance node for the relationship @param index DESCRIPTION @type int """ if index > 0: # create NodeInstance object displayName = self.cboFromNode.currentText() NZID = self.model.lookupNZIDfromDisplayName( displayName=displayName, topLevel="Instance Node") if not NZID is None: self.diagramInstance.startNode = self.getNodeInstance(NZID) self.diagramInstance.startNZID = NZID # if the instance node is based on a node template then set that fromTemplate = self.diagramInstance.startNode.nodeTemplate self.txtFromTemplate.setText(fromTemplate) else: self.txtFromTemplate.setText("") # force rel template dropdown to refill valid choices if self.mergeTemplate == False: self.loadRelTemplateDropDown() @pyqtSlot(int) def on_cboToNode_currentIndexChanged(self, index): """ The user has selected a new to instance node for the relationship @param index DESCRIPTION @type int """ if index > 0: # create NodeInstance object displayName = self.cboToNode.currentText() NZID = self.model.lookupNZIDfromDisplayName( displayName=displayName, topLevel="Instance Node") if not NZID is None: self.diagramInstance.endNode = self.getNodeInstance(NZID) self.diagramInstance.endNZID = NZID # if the instance node is based on a node template then set that toTemplate = self.diagramInstance.endNode.nodeTemplate self.txtToTemplate.setText(toTemplate) else: self.txtToTemplate.setText("") # force node template dropdown to refill valid choices if self.mergeTemplate == False: self.loadRelTemplateDropDown() @pyqtSlot(int) def on_cboRelName_currentIndexChanged(self, index): """ The relationship type was changed @param index DESCRIPTION @type int """ # user changed the relationship type name so reset the template dropdown to new list of valid templates and pick the "none selected" option which forces the user to reselect a template if self.mergeTemplate == False: self.loadRelTemplateDropDown() self.cboTemplate.setCurrentIndex(0) # if index > 0: ## relName = self.cboRelName.currentText() # # reload rel template dropdown to reflect newly selected rel type ## relTemplate = self.cboTemplate.currentText() # self.loadRelTemplateDropDown() # self.cboTemplate.setCurrentIndex(0) ## relList = ["select a relationship template"] + [relDict["name"] for relDict in self.modelData["Relationship Template"] if relDict["relname"] == relName] ## self.cboTemplate.clear() ## self.cboTemplate.addItems(relList) # # set to no rel template selected. ## self.cboTemplate.setCurrentIndex(0) # else: # # user switched back to no rel name selected so relaod all rel templates # self.loadRelTemplateDropDown() # self.cboTemplate.setCurrentIndex(0) def propModelItemChanged(self, item): # print("item data changed {} at {} {}".format(str(item.checkState()), item.index().row(), item.index().column())) # this checks to see if property name changed and updates the data type accordingly columnIndex = item.index().column() if columnIndex == PROPERTY: # if property has changed then change the datatype propName = self.gridProps.model().item(item.index().row(), PROPERTY).data(Qt.EditRole) dataType = self.model.getPropertyDataType(propName) self.gridProps.model().item(item.index().row(), DATATYPE).setText(dataType) if columnIndex == DATATYPE: # if datatype has changed then change value to "Null" self.gridProps.model().item(item.index().row(), VALUE).setText("Null") dataType = self.gridProps.model().item(item.index().row(), DATATYPE).data(Qt.EditRole) self.gridProps.model().item(item.index().row(), VALUE).setData(dataType, Qt.UserRole + 1) @pyqtSlot() def on_btnSetNull_clicked(self): """ User clicks button to set a property value to Null """ indexes = self.gridProps.selectionModel().selectedIndexes() for index in indexes: valueIndex = self.gridProps.model().index(index.row(), VALUE) self.gridProps.model().setData(valueIndex, "Null", Qt.DisplayRole) @pyqtSlot(int) def on_tabRelInspector_currentChanged(self, index): """ User has switched to another tab @param index DESCRIPTION @type int """ # user switched to the description tab. must regenerate description if there is a node template selected if index == DESCRIPTION: if self.cboTemplate.currentIndex() > 0: saveIndex, objectDict = self.model.getDictByName( topLevel="Relationship Template", objectName=self.cboTemplate.currentText()) if not objectDict is None: self.brwsrGeneratedDesc.setText( self.model.getRelationshipDescription(objectDict)) else: self.helper.displayErrMsg( "Get Description", "Error - could not find node template: {}".format( self.cboTemplate.currentText))