def mousePressEvent(self, event): item = self.scene().itemAt(self.mapToScene(event.pos())) if event.button() == QtCore.Qt.RightButton: event.ignore() elif item and item.type() == StepPort.Type: centre = item.boundingRect().center() self._connectSourceNode = item self._connectLine = ArrowLine(QtCore.QLineF(item.mapToScene(centre), self.mapToScene(event.pos()))) self.scene().addItem(self._connectLine) else: QtGui.QGraphicsView.mousePressEvent(self, event) self._selectionStartPos = event.pos()
class WorkflowGraphicsView(QtGui.QGraphicsView): def __init__(self, parent=None): QtGui.QGraphicsView.__init__(self, parent) self._selectedNodes = [] self._errorIconTimer = QtCore.QTimer() self._errorIconTimer.setInterval(2000) self._errorIconTimer.setSingleShot(True) self._errorIconTimer.timeout.connect(self.errorIconTimeout) self._errorIcon = None self._undoStack = None self._location = '' self._connectLine = None self._connectSourceNode = None self._selectionStartPos = None self.setCacheMode(QtGui.QGraphicsView.CacheBackground) self.setRenderHint(QtGui.QPainter.Antialiasing) grid_pic = QtGui.QPixmap(':/workflow/images/grid.png') self._grid_brush = QtGui.QBrush(grid_pic) # self.setTransformationAnchor(QtGui.QGraphicsView.AnchorUnderMouse) # self.setResizeAnchor(QtGui.QGraphicsView.AnchorViewCenter) self.setAcceptDrops(True) def clear(self): self.scene().clear() def setUndoStack(self, stack): self._undoStack = stack def setLocation(self, location): self._location = location def connectNodes(self, node1, node2): # Check if nodes are already connected if not node1.hasArcToDestination(node2): if node1.canConnect(node2): command = CommandAdd(self.scene(), Arc(node1, node2)) self._undoStack.push(command) else: # add temporary line ??? if self._errorIconTimer.isActive(): self._errorIconTimer.stop() self.errorIconTimeout() self._errorIcon = ErrorItem(node1, node2) self.scene().addItem(self._errorIcon) self._errorIconTimer.start() def selectionChanged(self): currentSelection = self.scene().selectedItems() previousSelection = self.scene().previouslySelectedItems() command = CommandSelection(self.scene(), currentSelection, previousSelection) self._undoStack.push(command) self.scene().setPreviouslySelectedItems(currentSelection) def nodeSelected(self, node, state): if state == True and node not in self._selectedNodes: self._selectedNodes.append(node) elif state == False and node in self._selectedNodes: found = self._selectedNodes.index(node) del self._selectedNodes[found] if len(self._selectedNodes) == 2: self.connectNodes(self._selectedNodes[0], self._selectedNodes[1]) def keyPressEvent(self, event): # super(WorkflowGraphicsView, self).keyPressEvent(event) if event.key() == QtCore.Qt.Key_Backspace or event.key() == QtCore.Qt.Key_Delete: command = CommandRemove(self.scene(), self.scene().selectedItems()) self._undoStack.push(command) event.accept() else: event.ignore() def contextMenuEvent(self, event): item = self.itemAt(event.pos()) if item and item.type() == Node.Type: item.showContextMenu(event.globalPos()) def mousePressEvent(self, event): item = self.scene().itemAt(self.mapToScene(event.pos())) if event.button() == QtCore.Qt.RightButton: event.ignore() elif item and item.type() == StepPort.Type: centre = item.boundingRect().center() self._connectSourceNode = item self._connectLine = ArrowLine(QtCore.QLineF(item.mapToScene(centre), self.mapToScene(event.pos()))) self.scene().addItem(self._connectLine) else: QtGui.QGraphicsView.mousePressEvent(self, event) self._selectionStartPos = event.pos() def mouseMoveEvent(self, event): if self._connectLine: newLine = QtCore.QLineF(self._connectLine.line().p1(), self.mapToScene(event.pos())) self._connectLine.setLine(newLine) else: QtGui.QGraphicsView.mouseMoveEvent(self, event) def mouseReleaseEvent(self, event): if self._connectLine: item = self.scene().itemAt(self.mapToScene(event.pos())) if item and item.type() == StepPort.Type: self.connectNodes(self._connectSourceNode, item) self.scene().removeItem(self._connectLine) self._connectLine = None self._connectSourceNode = None else: QtGui.QGraphicsView.mouseReleaseEvent(self, event) if self._selectionStartPos: diff = event.pos() - self._selectionStartPos if diff.x() != 0 and diff.y() != 0: self._undoStack.beginMacro('Move Step(s)') for item in self.scene().selectedItems(): if item.type() == Node.Type: self._undoStack.push(CommandMove(item, item.pos() - diff, item.pos())) self._undoStack.endMacro() def errorIconTimeout(self): self.scene().removeItem(self._errorIcon) del self._errorIcon def changeEvent(self, event): if event.type() == QtCore.QEvent.EnabledChange: self.invalidateScene(self.sceneRect()) def drawBackground(self, painter, rect): # Shadow. sceneRect = self.sceneRect() rightShadow = QtCore.QRectF(sceneRect.right(), sceneRect.top() + 5, 5, sceneRect.height()) bottomShadow = QtCore.QRectF(sceneRect.left() + 5, sceneRect.bottom(), sceneRect.width(), 5) if rightShadow.intersects(rect) or rightShadow.contains(rect): painter.fillRect(rightShadow, QtCore.Qt.darkGray) if bottomShadow.intersects(rect) or bottomShadow.contains(rect): painter.fillRect(bottomShadow, QtCore.Qt.darkGray) painter.setBrush(self._grid_brush) # QtCore.Qt.NoBrush painter.drawRect(sceneRect) def dropEvent(self, event): if event.mimeData().hasFormat("image/x-workflow-step"): pieceData = event.mimeData().data("image/x-workflow-step") stream = QtCore.QDataStream(pieceData, QtCore.QIODevice.ReadOnly) hotspot = QtCore.QPoint() nameLen = stream.readUInt32() name = stream.readRawData(nameLen).decode(sys.stdout.encoding) stream >> hotspot scene = self.scene() position = self.mapToScene(event.pos() - hotspot) metastep = MetaStep(workflowStepFactory(name, self._location)) node = Node(metastep) metastep._step.registerConfiguredObserver(scene.stepConfigured) metastep._step.registerDoneExecution(scene.doneExecution) metastep._step.registerOnExecuteEntry(scene.setCurrentWidget) metastep._step.registerIdentifierOccursCount(scene.identifierOccursCount) self._undoStack.beginMacro('Add node') self._undoStack.push(CommandAdd(scene, node)) # Set the position after it has been added to the scene self._undoStack.push(CommandMove(node, position, scene.ensureItemInScene(node, position))) scene.clearSelection() node.setSelected(True) self._undoStack.endMacro() self.setFocus() event.accept() else: event.ignore() def dragMoveEvent(self, event): if event.mimeData().hasFormat("image/x-workflow-step"): event.setDropAction(QtCore.Qt.MoveAction) event.accept() else: event.ignore() self.update() # def dragLeaveEvent(self, event): # event.accept() def dragEnterEvent(self, event): if event.mimeData().hasFormat("image/x-workflow-step"): event.accept() else: event.ignore() def resizeEvent(self, event): QtGui.QGraphicsView.resizeEvent(self, event) scene = self.scene() event_size = event.size() view_rect = QtCore.QRectF(0, 0, event_size.width(), event_size.height()) items_rect = scene.itemsBoundingRect() items_rect.adjust(0, 0, 10, 10) scene_rect = view_rect.united(items_rect) scene.setSceneRect(10, 10, scene_rect.width() - 20, scene_rect.height() - 20) def wheelEvent(self, event): self._scaleView(math.pow(2.0, -event.delta() / 240.0)) def _scaleView(self, scaleFactor): factor = self.matrix().scale(scaleFactor, scaleFactor).mapRect(QtCore.QRectF(0, 0, 1, 1)).width() if factor < 0.07 or factor > 100: return transformation_anchor = self.transformationAnchor() self.setTransformationAnchor(QtGui.QGraphicsView.AnchorUnderMouse) self.scale(scaleFactor, scaleFactor) self.setTransformationAnchor(transformation_anchor)