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
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
def keyPressEvent(self, keyEvent): """ Executed when a combination of key is pressed. :type keyEvent: QKeyEvent """ key = keyEvent.key() modifiers = keyEvent.modifiers() if self.diagram.mode is DiagramMode.Idle and \ modifiers & QtCore.Qt.ControlModifier and \ key in {QtCore.Qt.Key_Minus, QtCore.Qt.Key_Plus, QtCore.Qt.Key_0}: ############################################# # ZOOM SHORTCUT ################################# zoom = DiagramView.ZoomDefault if key in {QtCore.Qt.Key_Minus, QtCore.Qt.Key_Plus}: zoom = self.zoom zoom += +DiagramView.ZoomStep if key == QtCore.Qt.Key_Plus else -DiagramView.ZoomStep zoom = clamp(zoom, DiagramView.ZoomMin, DiagramView.ZoomMax) if zoom != self.zoom: self.scaleView(zoom) else: super().keyPressEvent(keyEvent)
def viewportEvent(self, viewportEvent): """ Perform pinch to zoom feature to scale the viewport. :type viewportEvent: QTouchEvent """ if viewportEvent.type() in {QtCore.QEvent.TouchBegin, QtCore.QEvent.TouchUpdate, QtCore.QEvent.TouchEnd}: if viewportEvent.type() in {QtCore.QEvent.TouchBegin, QtCore.QEvent.TouchEnd}: self.pinchFactor = 1.0 pts = viewportEvent.touchPoints() if len(pts) == 2: p0 = pts[0] p1 = pts[1] p2 = midpoint(p0.pos(), p1.pos()) pinchFactor = QtCore.QLineF(p0.pos(), p1.pos()).length() / QtCore.QLineF(p0.startPos(), p1.startPos()).length() pinchFactor = snapF(pinchFactor, DiagramView.PinchSize) if pinchFactor < DiagramView.PinchGuard[0] or pinchFactor > DiagramView.PinchGuard[1]: if pinchFactor != self.pinchFactor: zoom = self.zoom zoom += +DiagramView.ZoomStep if pinchFactor > self.pinchFactor else -DiagramView.ZoomStep zoom = clamp(zoom, DiagramView.ZoomMin, DiagramView.ZoomMax) self.pinchFactor = pinchFactor if zoom != self.zoom: self.scaleViewOnPoint(zoom, p2.toPoint()) return super().viewportEvent(viewportEvent)
def setLevel(self, level): """ Set the zoom level according to the given value. :type level: float """ level = clamp(level, DiagramView.ZoomMin, DiagramView.ZoomMax) if level != self.level: self.level = level self.refresh(enabled=True) self.sgnChanged.emit(self.level)
def test_clamp(): assert 0.0 == clamp(val=-4.0, minval=0.0) assert 0.0 == clamp(val=-4.0, minval=0.0, maxval=8.0) assert 5.0 == clamp(val=5.0, maxval=10.0) assert 5.0 == clamp(val=5.0, minval=0.0, maxval=10.0) assert 10.0 == clamp(val=12.0, maxval=10.0) assert 10.0 == clamp(val=12.0, minval=0.0, maxval=10.0)
def test_clamp(self): self.assertEqual(0.0, clamp(val=-4.0, minval=0.0)) self.assertEqual(0.0, clamp(val=-4.0, minval=0.0, maxval=8.0)) self.assertEqual(5.0, clamp(val=5.0, maxval=10.0)) self.assertEqual(5.0, clamp(val=5.0, minval=0.0, maxval=10.0)) self.assertEqual(10.0, clamp(val=12.0, maxval=10.0)) self.assertEqual(10.0, clamp(val=12.0, minval=0.0, maxval=10.0))
def wheelEvent(self, wheelEvent): """ Executed when the mouse wheel is rotated on the diagram. :type wheelEvent: QWheelEvent """ if wheelEvent.modifiers() & QtCore.Qt.ControlModifier: pos = wheelEvent.pos() angle = wheelEvent.angleDelta() zoom = self.zoom zoom += +DiagramView.ZoomStep if angle.y() > 0 else -DiagramView.ZoomStep zoom = clamp(zoom, DiagramView.ZoomMin, DiagramView.ZoomMax) if zoom != self.zoom: self.scaleViewOnPoint(zoom, pos) else: super().wheelEvent(wheelEvent)
def redraw(self): """ Redraw the content of the widget. """ width = self.width() scrollbar = self.verticalScrollBar() if scrollbar.isVisible(): width -= scrollbar.width() widget = self.stacked.currentWidget() if widget: widget.setFixedWidth(width) sizeHint = widget.sizeHint() height = sizeHint.height() self.stacked.setFixedWidth(width) self.stacked.setFixedHeight(clamp(height, 0))
def wheelEvent(self, wheelEvent): """ Executed when the mouse wheel is rotated on the diagram. :type wheelEvent: QWheelEvent """ if wheelEvent.modifiers() & QtCore.Qt.ControlModifier: pos = wheelEvent.pos() angle = wheelEvent.angleDelta() zoom = self.zoom zoom += +DiagramView.ZoomStep if angle.y( ) > 0 else -DiagramView.ZoomStep zoom = clamp(zoom, DiagramView.ZoomMin, DiagramView.ZoomMax) if zoom != self.zoom: self.scaleViewOnPoint(zoom, pos) else: super().wheelEvent(wheelEvent)
def setGridSize(self, size): """ Sets the grid size. """ action = self.session.action('toggle_grid') size = clamp(size, 0) if size <= 0 or not action.isChecked(): brush = QtGui.QBrush(QtCore.Qt.NoBrush) else: image = QtGui.QImage(size, size, QtGui.QImage.Format_RGB32) image.fill(QtCore.Qt.white) painter = QtGui.QPainter(image) painter.setPen(QtGui.QPen(QtGui.QBrush(QtGui.QColor(80, 80, 80, 255)), 1, QtCore.Qt.SolidLine)) painter.drawPoint(QtCore.QPointF(0, 0)) painter.end() brush = QtGui.QBrush(image) self.setBackgroundBrush(brush)
def setGridSize(self, size): """ Sets the grid size. """ action = self.session.action('toggle_grid') size = clamp(size, 0) if size <= 0 or not action.isChecked(): brush = QtGui.QBrush(QtCore.Qt.NoBrush) else: image = QtGui.QImage(size, size, QtGui.QImage.Format_RGB32) image.fill(QtCore.Qt.white) painter = QtGui.QPainter(image) painter.setPen( QtGui.QPen(QtGui.QBrush(QtGui.QColor(80, 80, 80, 255)), 1, QtCore.Qt.SolidLine)) painter.drawPoint(QtCore.QPointF(0, 0)) painter.end() brush = QtGui.QBrush(image) self.setBackgroundBrush(brush)
def viewportEvent(self, viewportEvent): """ Perform pinch to zoom feature to scale the viewport. :type viewportEvent: QTouchEvent """ if viewportEvent.type() in { QtCore.QEvent.TouchBegin, QtCore.QEvent.TouchUpdate, QtCore.QEvent.TouchEnd }: if viewportEvent.type() in { QtCore.QEvent.TouchBegin, QtCore.QEvent.TouchEnd }: self.pinchFactor = 1.0 pts = viewportEvent.touchPoints() if len(pts) == 2: p0 = pts[0] p1 = pts[1] p2 = midpoint(p0.pos(), p1.pos()) pinchFactor = QtCore.QLineF( p0.pos(), p1.pos()).length() / QtCore.QLineF( p0.startPos(), p1.startPos()).length() pinchFactor = snapF(pinchFactor, DiagramView.PinchSize) if pinchFactor < DiagramView.PinchGuard[ 0] or pinchFactor > DiagramView.PinchGuard[1]: if pinchFactor != self.pinchFactor: zoom = self.zoom zoom += +DiagramView.ZoomStep if pinchFactor > self.pinchFactor else -DiagramView.ZoomStep zoom = clamp(zoom, DiagramView.ZoomMin, DiagramView.ZoomMax) self.pinchFactor = pinchFactor if zoom != self.zoom: self.scaleViewOnPoint(zoom, p2.toPoint()) return super().viewportEvent(viewportEvent)
def test_clamp_with_exception(): with pytest.raises(ValueError): clamp(val=4.0, minval=10.0, maxval=0.0)
def mouseMoveEvent(self, mouseEvent): """ Executed when then mouse is moved on the view. :type mouseEvent: QGraphicsSceneMouseEvent """ mousePos = mouseEvent.pos() mouseButtons = mouseEvent.buttons() viewport = self.viewport() if mouseButtons & QtCore.Qt.RightButton: if self.mp_Pos is not None: if (mousePos - self.mp_Pos).manhattanLength( ) >= QtWidgets.QApplication.startDragDistance(): ############################################# # SCENE DRAG ################################# if self.diagram.mode is not DiagramMode.SceneDrag: self.diagram.setMode(DiagramMode.SceneDrag) viewport.setCursor(QtCore.Qt.ClosedHandCursor) mousePos /= self.zoom mousePressPos = self.mp_Pos / self.zoom self.centerOn(self.mp_CenterPos - mousePos + mousePressPos) else: super().mouseMoveEvent(mouseEvent) if mouseButtons & QtCore.Qt.LeftButton: self.stopMove() if self.diagram.mode is DiagramMode.RubberBandDrag: ############################################# # RUBBERBAND SELECTION ################################# originPos = self.mapFromScene(self.rubberBandOrigin) area = QtCore.QRectF(originPos, mousePos).normalized() self.rubberBand.setGeometry(area.toRect()) if self.diagram.mode in DiagramView.MoveModes: ############################################# # VIEW MOVE ################################# R = viewport.rect() if not R.contains(mousePos): move = QtCore.QPointF(0, 0) if mousePos.x() < R.left(): move.setX(mousePos.x() - R.left()) elif mousePos.x() > R.right(): move.setX(mousePos.x() - R.right()) if mousePos.y() < R.top(): move.setY(mousePos.y() - R.top()) elif mousePos.y() > R.bottom(): move.setY(mousePos.y() - R.bottom()) if move: move.setX( clamp(move.x(), -DiagramView.MoveBound, DiagramView.MoveBound)) move.setY( clamp(move.y(), -DiagramView.MoveBound, DiagramView.MoveBound)) self.startMove(move, DiagramView.MoveRate)
def keyPressEvent(self, keyEvent): """ Executed when a combination of key is pressed. :type keyEvent: QKeyEvent """ key = keyEvent.key() modifiers = keyEvent.modifiers() if self.diagram.mode is DiagramMode.Idle: if modifiers & QtCore.Qt.ControlModifier and \ key in {QtCore.Qt.Key_Minus, QtCore.Qt.Key_Plus, QtCore.Qt.Key_0}: ############################################# # ZOOM SHORTCUT ################################# zoom = DiagramView.ZoomDefault if key in {QtCore.Qt.Key_Minus, QtCore.Qt.Key_Plus}: zoom = self.zoom zoom += +DiagramView.ZoomStep if key == QtCore.Qt.Key_Plus else -DiagramView.ZoomStep zoom = clamp(zoom, DiagramView.ZoomMin, DiagramView.ZoomMax) if zoom != self.zoom: self.scaleView(zoom) else: ############################################# # NODE MOVEMENT ################################# # NOTE: while usually node movement is handled in the Diagram class, # movements performed using the keyboard needs to be handled right here. # The reason behind is that keyboard arrows are used to scroll the DiagramView # viewport, we if we intercept the event in the Diagram class (by calling # super().keyPressEvent()) to perform the node move, we will also see the # viewport moving, and this is not the desired behavior. We intercept the # event here instead and perform the node move. selected = self.diagram.selectedNodes() if selected and key in { QtCore.Qt.Key_Up, QtCore.Qt.Key_Down, QtCore.Qt.Key_Left, QtCore.Qt.Key_Right }: self.diagram.setMode(DiagramMode.NodeMove) offset = QtCore.QPointF(0, 0) if keyEvent.key() == QtCore.Qt.Key_Up: offset += QtCore.QPointF(0, -Diagram.KeyMoveFactor) if keyEvent.key() == QtCore.Qt.Key_Down: offset += QtCore.QPointF(0, +Diagram.KeyMoveFactor) if keyEvent.key() == QtCore.Qt.Key_Left: offset += QtCore.QPointF(-Diagram.KeyMoveFactor, 0) if keyEvent.key() == QtCore.Qt.Key_Right: offset += QtCore.QPointF(+Diagram.KeyMoveFactor, 0) initData = self.diagram.setupMove(selected) moveData = self.diagram.completeMove(initData, offset) self.session.undostack.push( CommandNodeMove(self.diagram, initData, moveData)) self.diagram.setMode(DiagramMode.Idle) else: super().keyPressEvent(keyEvent) else: super().keyPressEvent(keyEvent)
def mouseMoveEvent(self, mouseEvent): """ Executed when then mouse is moved on the view. :type mouseEvent: QGraphicsSceneMouseEvent """ mousePos = mouseEvent.pos() mouseButtons = mouseEvent.buttons() viewport = self.viewport() if mouseButtons & QtCore.Qt.RightButton: if (mouseEvent.pos() - self.mp_Pos).manhattanLength() >= QtWidgets.QApplication.startDragDistance(): ############################################# # SCENE DRAG ################################# if self.diagram.mode is not DiagramMode.SceneDrag: self.diagram.setMode(DiagramMode.SceneDrag) viewport.setCursor(QtCore.Qt.ClosedHandCursor) mousePos /= self.zoom mousePressPos = self.mp_Pos / self.zoom self.centerOn(self.mp_CenterPos - mousePos + mousePressPos) else: super().mouseMoveEvent(mouseEvent) if mouseButtons & QtCore.Qt.LeftButton: self.stopMove() if self.diagram.mode is DiagramMode.RubberBandDrag: ############################################# # RUBBERBAND SELECTION ################################# originPos = self.mapFromScene(self.rubberBandOrigin) area = QtCore.QRectF(originPos, mousePos).normalized() self.rubberBand.setGeometry(area.toRect()) if self.diagram.mode in DiagramView.MoveModes: ############################################# # VIEW MOVE ################################# R = viewport.rect() if not R.contains(mousePos): move = QtCore.QPointF(0, 0) if mousePos.x() < R.left(): move.setX(mousePos.x() - R.left()) elif mousePos.x() > R.right(): move.setX(mousePos.x() - R.right()) if mousePos.y() < R.top(): move.setY(mousePos.y() - R.top()) elif mousePos.y() > R.bottom(): move.setY(mousePos.y() - R.bottom()) if move: move.setX(clamp(move.x(), -DiagramView.MoveBound, DiagramView.MoveBound)) move.setY(clamp(move.y(), -DiagramView.MoveBound, DiagramView.MoveBound)) self.startMove(move, DiagramView.MoveRate)