diff options
Diffstat (limited to 'examples/widgets/graphicsview/elasticnodes.py')
-rw-r--r-- | examples/widgets/graphicsview/elasticnodes.py | 413 |
1 files changed, 0 insertions, 413 deletions
diff --git a/examples/widgets/graphicsview/elasticnodes.py b/examples/widgets/graphicsview/elasticnodes.py deleted file mode 100644 index 48feffc85..000000000 --- a/examples/widgets/graphicsview/elasticnodes.py +++ /dev/null @@ -1,413 +0,0 @@ - -############################################################################# -## -## Copyright (C) 2013 Riverbank Computing Limited. -## Copyright (C) 2016 The Qt Company Ltd. -## Contact: http://www.qt.io/licensing/ -## -## This file is part of the Qt for Python examples of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:BSD$ -## You may use this file under the terms of the BSD license as follows: -## -## "Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are -## met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in -## the documentation and/or other materials provided with the -## distribution. -## * Neither the name of The Qt Company Ltd nor the names of its -## contributors may be used to endorse or promote products derived -## from this software without specific prior written permission. -## -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -## -## $QT_END_LICENSE$ -## -############################################################################# - -import sys -import weakref -import math -from PySide2 import QtCore, QtGui, QtWidgets - - -class Edge(QtWidgets.QGraphicsItem): - Pi = math.pi - TwoPi = 2.0 * Pi - - Type = QtWidgets.QGraphicsItem.UserType + 2 - - def __init__(self, sourceNode, destNode): - QtWidgets.QGraphicsItem.__init__(self) - - self.arrowSize = 10.0 - self.sourcePoint = QtCore.QPointF() - self.destPoint = QtCore.QPointF() - self.setAcceptedMouseButtons(QtCore.Qt.NoButton) - self.source = weakref.ref(sourceNode) - self.dest = weakref.ref(destNode) - self.source().addEdge(self) - self.dest().addEdge(self) - self.adjust() - - def type(self): - return Edge.Type - - def sourceNode(self): - return self.source() - - def setSourceNode(self, node): - self.source = weakref.ref(node) - self.adjust() - - def destNode(self): - return self.dest() - - def setDestNode(self, node): - self.dest = weakref.ref(node) - self.adjust() - - def adjust(self): - if not self.source() or not self.dest(): - return - - line = QtCore.QLineF(self.mapFromItem(self.source(), 0, 0), self.mapFromItem(self.dest(), 0, 0)) - length = line.length() - - if length == 0.0: - return - - edgeOffset = QtCore.QPointF((line.dx() * 10) / length, (line.dy() * 10) / length) - - self.prepareGeometryChange() - self.sourcePoint = line.p1() + edgeOffset - self.destPoint = line.p2() - edgeOffset - - def boundingRect(self): - if not self.source() or not self.dest(): - return QtCore.QRectF() - - penWidth = 1 - extra = (penWidth + self.arrowSize) / 2.0 - - return QtCore.QRectF(self.sourcePoint, - QtCore.QSizeF(self.destPoint.x() - self.sourcePoint.x(), - self.destPoint.y() - self.sourcePoint.y())).normalized().adjusted(-extra, -extra, extra, extra) - - def paint(self, painter, option, widget): - if not self.source() or not self.dest(): - return - - # Draw the line itself. - line = QtCore.QLineF(self.sourcePoint, self.destPoint) - - if line.length() == 0.0: - return - - painter.setPen(QtGui.QPen(QtCore.Qt.black, 1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) - painter.drawLine(line) - - # Draw the arrows if there's enough room. - angle = math.acos(line.dx() / line.length()) - if line.dy() >= 0: - angle = Edge.TwoPi - angle - - sourceArrowP1 = self.sourcePoint + QtCore.QPointF(math.sin(angle + Edge.Pi / 3) * self.arrowSize, - math.cos(angle + Edge.Pi / 3) * self.arrowSize) - sourceArrowP2 = self.sourcePoint + QtCore.QPointF(math.sin(angle + Edge.Pi - Edge.Pi / 3) * self.arrowSize, - math.cos(angle + Edge.Pi - Edge.Pi / 3) * self.arrowSize) - destArrowP1 = self.destPoint + QtCore.QPointF(math.sin(angle - Edge.Pi / 3) * self.arrowSize, - math.cos(angle - Edge.Pi / 3) * self.arrowSize) - destArrowP2 = self.destPoint + QtCore.QPointF(math.sin(angle - Edge.Pi + Edge.Pi / 3) * self.arrowSize, - math.cos(angle - Edge.Pi + Edge.Pi / 3) * self.arrowSize) - - painter.setBrush(QtCore.Qt.black) - painter.drawPolygon(QtGui.QPolygonF([line.p1(), sourceArrowP1, sourceArrowP2])) - painter.drawPolygon(QtGui.QPolygonF([line.p2(), destArrowP1, destArrowP2])) - - -class Node(QtWidgets.QGraphicsItem): - Type = QtWidgets.QGraphicsItem.UserType + 1 - - def __init__(self, graphWidget): - QtWidgets.QGraphicsItem.__init__(self) - - self.graph = weakref.ref(graphWidget) - self.edgeList = [] - self.newPos = QtCore.QPointF() - self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable) - self.setFlag(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges) - self.setCacheMode(self.DeviceCoordinateCache) - self.setZValue(-1) - - def type(self): - return Node.Type - - def addEdge(self, edge): - self.edgeList.append(weakref.ref(edge)) - edge.adjust() - - def edges(self): - return self.edgeList - - def calculateForces(self): - if not self.scene() or self.scene().mouseGrabberItem() is self: - self.newPos = self.pos() - return - - # Sum up all forces pushing this item away. - xvel = 0.0 - yvel = 0.0 - for item in self.scene().items(): - if not isinstance(item, Node): - continue - - line = QtCore.QLineF(self.mapFromItem(item, 0, 0), QtCore.QPointF(0, 0)) - dx = line.dx() - dy = line.dy() - l = 2.0 * (dx * dx + dy * dy) - if l > 0: - xvel += (dx * 150.0) / l - yvel += (dy * 150.0) / l - - # Now subtract all forces pulling items together. - weight = (len(self.edgeList) + 1) * 10.0 - for edge in self.edgeList: - if edge().sourceNode() is self: - pos = self.mapFromItem(edge().destNode(), 0, 0) - else: - pos = self.mapFromItem(edge().sourceNode(), 0, 0) - xvel += pos.x() / weight - yvel += pos.y() / weight - - if QtCore.qAbs(xvel) < 0.1 and QtCore.qAbs(yvel) < 0.1: - xvel = yvel = 0.0 - - sceneRect = self.scene().sceneRect() - self.newPos = self.pos() + QtCore.QPointF(xvel, yvel) - self.newPos.setX(min(max(self.newPos.x(), sceneRect.left() + 10), sceneRect.right() - 10)) - self.newPos.setY(min(max(self.newPos.y(), sceneRect.top() + 10), sceneRect.bottom() - 10)) - - def advance(self): - if self.newPos == self.pos(): - return False - - self.setPos(self.newPos) - return True - - def boundingRect(self): - adjust = 2.0 - return QtCore.QRectF(-10 - adjust, -10 - adjust, - 23 + adjust, 23 + adjust) - - def shape(self): - path = QtGui.QPainterPath() - path.addEllipse(-10, -10, 20, 20) - return path - - def paint(self, painter, option, widget): - painter.setPen(QtCore.Qt.NoPen) - painter.setBrush(QtCore.Qt.darkGray) - painter.drawEllipse(-7, -7, 20, 20) - - gradient = QtGui.QRadialGradient(-3, -3, 10) - if option.state & QtWidgets.QStyle.State_Sunken: - gradient.setCenter(3, 3) - gradient.setFocalPoint(3, 3) - gradient.setColorAt(1, QtGui.QColor(QtCore.Qt.yellow).lighter(120)) - gradient.setColorAt(0, QtGui.QColor(QtCore.Qt.darkYellow).lighter(120)) - else: - gradient.setColorAt(0, QtCore.Qt.yellow) - gradient.setColorAt(1, QtCore.Qt.darkYellow) - - painter.setBrush(QtGui.QBrush(gradient)) - painter.setPen(QtGui.QPen(QtCore.Qt.black, 0)) - painter.drawEllipse(-10, -10, 20, 20) - - def itemChange(self, change, value): - if change == QtWidgets.QGraphicsItem.ItemPositionChange: - for edge in self.edgeList: - edge().adjust() - self.graph().itemMoved() - - return QtWidgets.QGraphicsItem.itemChange(self, change, value) - - def mousePressEvent(self, event): - self.update() - QtWidgets.QGraphicsItem.mousePressEvent(self, event) - - def mouseReleaseEvent(self, event): - self.update() - QtWidgets.QGraphicsItem.mouseReleaseEvent(self, event) - - -class GraphWidget(QtWidgets.QGraphicsView): - def __init__(self): - QtWidgets.QGraphicsView.__init__(self) - - self.timerId = 0 - - scene = QtWidgets.QGraphicsScene(self) - scene.setItemIndexMethod(QtWidgets.QGraphicsScene.NoIndex) - scene.setSceneRect(-200, -200, 400, 400) - self.setScene(scene) - self.setCacheMode(QtWidgets.QGraphicsView.CacheBackground) - self.setRenderHint(QtGui.QPainter.Antialiasing) - self.setTransformationAnchor(QtWidgets.QGraphicsView.AnchorUnderMouse) - self.setResizeAnchor(QtWidgets.QGraphicsView.AnchorViewCenter) - - node1 = Node(self) - node2 = Node(self) - node3 = Node(self) - node4 = Node(self) - self.centerNode = Node(self) - node6 = Node(self) - node7 = Node(self) - node8 = Node(self) - node9 = Node(self) - scene.addItem(node1) - scene.addItem(node2) - scene.addItem(node3) - scene.addItem(node4) - scene.addItem(self.centerNode) - scene.addItem(node6) - scene.addItem(node7) - scene.addItem(node8) - scene.addItem(node9) - scene.addItem(Edge(node1, node2)) - scene.addItem(Edge(node2, node3)) - scene.addItem(Edge(node2, self.centerNode)) - scene.addItem(Edge(node3, node6)) - scene.addItem(Edge(node4, node1)) - scene.addItem(Edge(node4, self.centerNode)) - scene.addItem(Edge(self.centerNode, node6)) - scene.addItem(Edge(self.centerNode, node8)) - scene.addItem(Edge(node6, node9)) - scene.addItem(Edge(node7, node4)) - scene.addItem(Edge(node8, node7)) - scene.addItem(Edge(node9, node8)) - - node1.setPos(-50, -50) - node2.setPos(0, -50) - node3.setPos(50, -50) - node4.setPos(-50, 0) - self.centerNode.setPos(0, 0) - node6.setPos(50, 0) - node7.setPos(-50, 50) - node8.setPos(0, 50) - node9.setPos(50, 50) - - self.scale(0.8, 0.8) - self.setMinimumSize(400, 400) - self.setWindowTitle(self.tr("Elastic Nodes")) - - def itemMoved(self): - if not self.timerId: - self.timerId = self.startTimer(1000 / 25) - - def keyPressEvent(self, event): - key = event.key() - - if key == QtCore.Qt.Key_Up: - self.centerNode.moveBy(0, -20) - elif key == QtCore.Qt.Key_Down: - self.centerNode.moveBy(0, 20) - elif key == QtCore.Qt.Key_Left: - self.centerNode.moveBy(-20, 0) - elif key == QtCore.Qt.Key_Right: - self.centerNode.moveBy(20, 0) - elif key == QtCore.Qt.Key_Plus: - self.scaleView(1.2) - elif key == QtCore.Qt.Key_Minus: - self.scaleView(1 / 1.2) - elif key == QtCore.Qt.Key_Space or key == QtCore.Qt.Key_Enter: - for item in self.scene().items(): - if isinstance(item, Node): - item.setPos(-150 + QtCore.qrand() % 300, -150 + QtCore.qrand() % 300) - else: - QtWidgets.QGraphicsView.keyPressEvent(self, event) - - - def timerEvent(self, event): - nodes = [item for item in self.scene().items() if isinstance(item, Node)] - - for node in nodes: - node.calculateForces() - - itemsMoved = False - for node in nodes: - if node.advance(): - itemsMoved = True - - if not itemsMoved: - self.killTimer(self.timerId) - self.timerId = 0 - - def wheelEvent(self, event): - self.scaleView(math.pow(2.0, -event.delta() / 240.0)) - - 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) - - # Fill. - gradient = QtGui.QLinearGradient(sceneRect.topLeft(), sceneRect.bottomRight()) - gradient.setColorAt(0, QtCore.Qt.white) - gradient.setColorAt(1, QtCore.Qt.lightGray) - painter.fillRect(rect.intersected(sceneRect), QtGui.QBrush(gradient)) - painter.setBrush(QtCore.Qt.NoBrush) - painter.drawRect(sceneRect) - - # Text. - textRect = QtCore.QRectF(sceneRect.left() + 4, sceneRect.top() + 4, - sceneRect.width() - 4, sceneRect.height() - 4) - message = self.tr("Click and drag the nodes around, and zoom with the " - "mouse wheel or the '+' and '-' keys") - - font = painter.font() - font.setBold(True) - font.setPointSize(14) - painter.setFont(font) - painter.setPen(QtCore.Qt.lightGray) - painter.drawText(textRect.translated(2, 2), message) - painter.setPen(QtCore.Qt.black) - painter.drawText(textRect, message) - - 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 - - self.scale(scaleFactor, scaleFactor) - - -if __name__ == "__main__": - app = QtWidgets.QApplication(sys.argv) - QtCore.qsrand(QtCore.QTime(0,0,0).secsTo(QtCore.QTime.currentTime())) - - widget = GraphWidget() - widget.show() - - sys.exit(app.exec_()) |