diff options
Diffstat (limited to 'examples/widgets/graphicsview/elasticnodes/elasticnodes.py')
-rw-r--r-- | examples/widgets/graphicsview/elasticnodes/elasticnodes.py | 372 |
1 files changed, 174 insertions, 198 deletions
diff --git a/examples/widgets/graphicsview/elasticnodes/elasticnodes.py b/examples/widgets/graphicsview/elasticnodes/elasticnodes.py index 1eefdc0bb..d41981dce 100644 --- a/examples/widgets/graphicsview/elasticnodes/elasticnodes.py +++ b/examples/widgets/graphicsview/elasticnodes/elasticnodes.py @@ -1,88 +1,53 @@ - -############################################################################# -## -## 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$ -## -############################################################################# +# Copyright (C) 2013 Riverbank Computing Limited. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +from __future__ import annotations import sys import weakref import math -from PySide6 import QtCore, QtGui, QtWidgets +from PySide6.QtCore import (QLineF, QPointF, QRandomGenerator, QRectF, QSizeF, + Qt, qAbs) +from PySide6.QtGui import (QColor, QBrush, QLinearGradient, QPainter, QPainterPath, QPen, + QPolygonF, QRadialGradient) +from PySide6.QtWidgets import (QApplication, QGraphicsItem, QGraphicsScene, + QGraphicsView, QStyle) -def random(boundary): - return QtCore.QRandomGenerator.global_().bounded(boundary) +def random(boundary): + return QRandomGenerator.global_().bounded(boundary) -class Edge(QtWidgets.QGraphicsItem): - Pi = math.pi - TwoPi = 2.0 * Pi - Type = QtWidgets.QGraphicsItem.UserType + 2 +class Edge(QGraphicsItem): def __init__(self, sourceNode, destNode): - QtWidgets.QGraphicsItem.__init__(self) + super().__init__() - self.arrowSize = 10.0 - self.sourcePoint = QtCore.QPointF() - self.destPoint = QtCore.QPointF() - self.setAcceptedMouseButtons(QtCore.Qt.NoButton) + self._arrow_size = 10.0 + self._source_point = QPointF() + self._dest_point = QPointF() + self.setAcceptedMouseButtons(Qt.NoButton) self.source = weakref.ref(sourceNode) self.dest = weakref.ref(destNode) - self.source().addEdge(self) - self.dest().addEdge(self) + self.source().add_edge(self) + self.dest().add_edge(self) self.adjust() - def type(self): - return Edge.Type + def item_type(self): + return QGraphicsItem.UserType + 2 - def sourceNode(self): + def source_node(self): return self.source() - def setSourceNode(self, node): + def set_source_node(self, node): self.source = weakref.ref(node) self.adjust() - def destNode(self): + def dest_node(self): return self.dest() - def setDestNode(self, node): + def set_dest_node(self, node): self.dest = weakref.ref(node) self.adjust() @@ -90,88 +55,94 @@ class Edge(QtWidgets.QGraphicsItem): if not self.source() or not self.dest(): return - line = QtCore.QLineF(self.mapFromItem(self.source(), 0, 0), self.mapFromItem(self.dest(), 0, 0)) + line = 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) + edge_offset = QPointF((line.dx() * 10) / length, (line.dy() * 10) / length) self.prepareGeometryChange() - self.sourcePoint = line.p1() + edgeOffset - self.destPoint = line.p2() - edgeOffset + self._source_point = line.p1() + edge_offset + self._dest_point = line.p2() - edge_offset def boundingRect(self): if not self.source() or not self.dest(): - return QtCore.QRectF() + return QRectF() - penWidth = 1 - extra = (penWidth + self.arrowSize) / 2.0 + pen_width = 1 + extra = (pen_width + self._arrow_size) / 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) + width = self._dest_point.x() - self._source_point.x() + height = self._dest_point.y() - self._source_point.y() + rect = QRectF(self._source_point, QSizeF(width, height)) + return rect.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) + line = QLineF(self._source_point, self._dest_point) if line.length() == 0.0: return - painter.setPen(QtGui.QPen(QtCore.Qt.black, 1, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)) + painter.setPen(QPen(Qt.black, 1, Qt.SolidLine, Qt.RoundCap, 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 + angle = 2 * math.pi - 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) + arrow_head1 = QPointF(math.sin(angle + math.pi / 3) * self._arrow_size, + math.cos(angle + math.pi / 3) * self._arrow_size) + source_arrow_p1 = self._source_point + arrow_head1 + arrow_head2 = QPointF(math.sin(angle + math.pi - math.pi / 3) * self._arrow_size, + math.cos(angle + math.pi - math.pi / 3) * self._arrow_size) + source_arrow_p2 = self._source_point + arrow_head2 - painter.setBrush(QtCore.Qt.black) - painter.drawPolygon(QtGui.QPolygonF([line.p1(), sourceArrowP1, sourceArrowP2])) - painter.drawPolygon(QtGui.QPolygonF([line.p2(), destArrowP1, destArrowP2])) + arrow_head1 = QPointF(math.sin(angle - math.pi / 3) * self._arrow_size, + math.cos(angle - math.pi / 3) * self._arrow_size) + dest_arrow_p1 = self._dest_point + arrow_head1 + arrow_head2 = QPointF(math.sin(angle - math.pi + math.pi / 3) * self._arrow_size, + math.cos(angle - math.pi + math.pi / 3) * self._arrow_size) + dest_arrow_p2 = self._dest_point + arrow_head2 + painter.setBrush(Qt.black) + painter.drawPolygon(QPolygonF([line.p1(), source_arrow_p1, source_arrow_p2])) + painter.drawPolygon(QPolygonF([line.p2(), dest_arrow_p1, dest_arrow_p2])) -class Node(QtWidgets.QGraphicsItem): - Type = QtWidgets.QGraphicsItem.UserType + 1 + +class Node(QGraphicsItem): def __init__(self, graphWidget): - QtWidgets.QGraphicsItem.__init__(self) + super().__init__() 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._edge_list = [] + self._new_pos = QPointF() + self.setFlag(QGraphicsItem.ItemIsMovable) + self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) + self.setCacheMode(QGraphicsItem.DeviceCoordinateCache) self.setZValue(-1) - def type(self): - return Node.Type + def item_type(self): + return QGraphicsItem.UserType + 1 - def addEdge(self, edge): - self.edgeList.append(weakref.ref(edge)) + def add_edge(self, edge): + self._edge_list.append(weakref.ref(edge)) edge.adjust() def edges(self): - return self.edgeList + return self._edge_list - def calculateForces(self): + def calculate_forces(self): if not self.scene() or self.scene().mouseGrabberItem() is self: - self.newPos = self.pos() + self._new_pos = self.pos() return # Sum up all forces pushing this item away. @@ -181,105 +152,107 @@ class Node(QtWidgets.QGraphicsItem): if not isinstance(item, Node): continue - line = QtCore.QLineF(self.mapFromItem(item, 0, 0), QtCore.QPointF(0, 0)) + line = QLineF(self.mapFromItem(item, 0, 0), QPointF(0, 0)) dx = line.dx() dy = line.dy() - l = 2.0 * (dx * dx + dy * dy) + l = 2.0 * (dx * dx + dy * dy) # noqa: E741 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) + weight = (len(self._edge_list) + 1) * 10.0 + for edge in self._edge_list: + if edge().source_node() is self: + pos = self.mapFromItem(edge().dest_node(), 0, 0) else: - pos = self.mapFromItem(edge().sourceNode(), 0, 0) + pos = self.mapFromItem(edge().source_node(), 0, 0) xvel += pos.x() / weight yvel += pos.y() / weight - if QtCore.qAbs(xvel) < 0.1 and QtCore.qAbs(yvel) < 0.1: + if qAbs(xvel) < 0.1 and 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)) + scene_rect = self.scene().sceneRect() + self._new_pos = self.pos() + QPointF(xvel, yvel) + self._new_pos.setX(min(max(self._new_pos.x(), scene_rect.left() + 10), + scene_rect.right() - 10)) + self._new_pos.setY(min(max(self._new_pos.y(), scene_rect.top() + 10), + scene_rect.bottom() - 10)) def advance(self): - if self.newPos == self.pos(): + if self._new_pos == self.pos(): return False - self.setPos(self.newPos) + self.setPos(self._new_pos) return True def boundingRect(self): adjust = 2.0 - return QtCore.QRectF(-10 - adjust, -10 - adjust, - 23 + adjust, 23 + adjust) + return QRectF(-10 - adjust, -10 - adjust, + 23 + adjust, 23 + adjust) def shape(self): - path = QtGui.QPainterPath() + path = 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.setPen(Qt.NoPen) + painter.setBrush(Qt.darkGray) painter.drawEllipse(-7, -7, 20, 20) - gradient = QtGui.QRadialGradient(-3, -3, 10) - if option.state & QtWidgets.QStyle.State_Sunken: + gradient = QRadialGradient(-3, -3, 10) + if option.state & 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)) + gradient.setColorAt(1, QColor(Qt.yellow).lighter(120)) + gradient.setColorAt(0, QColor(Qt.darkYellow).lighter(120)) else: - gradient.setColorAt(0, QtCore.Qt.yellow) - gradient.setColorAt(1, QtCore.Qt.darkYellow) + gradient.setColorAt(0, Qt.yellow) + gradient.setColorAt(1, Qt.darkYellow) - painter.setBrush(QtGui.QBrush(gradient)) - painter.setPen(QtGui.QPen(QtCore.Qt.black, 0)) + painter.setBrush(QBrush(gradient)) + painter.setPen(QPen(Qt.black, 0)) painter.drawEllipse(-10, -10, 20, 20) def itemChange(self, change, value): - if change == QtWidgets.QGraphicsItem.ItemPositionChange: - for edge in self.edgeList: + if change == QGraphicsItem.ItemPositionChange: + for edge in self._edge_list: edge().adjust() - self.graph().itemMoved() + self.graph().item_moved() - return QtWidgets.QGraphicsItem.itemChange(self, change, value) + return QGraphicsItem.itemChange(self, change, value) def mousePressEvent(self, event): self.update() - QtWidgets.QGraphicsItem.mousePressEvent(self, event) + QGraphicsItem.mousePressEvent(self, event) def mouseReleaseEvent(self, event): self.update() - QtWidgets.QGraphicsItem.mouseReleaseEvent(self, event) + QGraphicsItem.mouseReleaseEvent(self, event) -class GraphWidget(QtWidgets.QGraphicsView): +class GraphWidget(QGraphicsView): def __init__(self): - QtWidgets.QGraphicsView.__init__(self) + super().__init__() - self.timerId = 0 + self._timer_id = 0 - scene = QtWidgets.QGraphicsScene(self) - scene.setItemIndexMethod(QtWidgets.QGraphicsScene.NoIndex) + scene = QGraphicsScene(self) + scene.setItemIndexMethod(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) + self.setCacheMode(QGraphicsView.CacheBackground) + self.setRenderHint(QPainter.Antialiasing) + self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) + self.setResizeAnchor(QGraphicsView.AnchorViewCenter) node1 = Node(self) node2 = Node(self) node3 = Node(self) node4 = Node(self) - self.centerNode = Node(self) + self._center_node = Node(self) node6 = Node(self) node7 = Node(self) node8 = Node(self) @@ -288,19 +261,19 @@ class GraphWidget(QtWidgets.QGraphicsView): scene.addItem(node2) scene.addItem(node3) scene.addItem(node4) - scene.addItem(self.centerNode) + scene.addItem(self._center_node) 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(node2, self._center_node)) 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(node4, self._center_node)) + scene.addItem(Edge(self._center_node, node6)) + scene.addItem(Edge(self._center_node, node8)) scene.addItem(Edge(node6, node9)) scene.addItem(Edge(node7, node4)) scene.addItem(Edge(node8, node7)) @@ -310,7 +283,7 @@ class GraphWidget(QtWidgets.QGraphicsView): node2.setPos(0, -50) node3.setPos(50, -50) node4.setPos(-50, 0) - self.centerNode.setPos(0, 0) + self._center_node.setPos(0, 0) node6.setPos(50, 0) node7.setPos(-50, 50) node8.setPos(0, 50) @@ -320,72 +293,74 @@ class GraphWidget(QtWidgets.QGraphicsView): self.setMinimumSize(400, 400) self.setWindowTitle(self.tr("Elastic Nodes")) - def itemMoved(self): - if not self.timerId: - self.timerId = self.startTimer(1000 / 25) + def item_moved(self): + if not self._timer_id: + self._timer_id = 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: + if key == Qt.Key_Up: + self._center_node.moveBy(0, -20) + elif key == Qt.Key_Down: + self._center_node.moveBy(0, 20) + elif key == Qt.Key_Left: + self._center_node.moveBy(-20, 0) + elif key == Qt.Key_Right: + self._center_node.moveBy(20, 0) + elif key == Qt.Key_Plus: + self.scale_view(1.2) + elif key == Qt.Key_Minus: + self.scale_view(1 / 1.2) + elif key == Qt.Key_Space or key == Qt.Key_Enter: for item in self.scene().items(): if isinstance(item, Node): item.setPos(-150 + random(300), -150 + random(300)) else: - QtWidgets.QGraphicsView.keyPressEvent(self, event) - + 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() + node.calculate_forces() - itemsMoved = False + items_moved = False for node in nodes: if node.advance(): - itemsMoved = True + items_moved = True - if not itemsMoved: - self.killTimer(self.timerId) - self.timerId = 0 + if not items_moved: + self.killTimer(self._timer_id) + self._timer_id = 0 def wheelEvent(self, event): - self.scaleView(math.pow(2.0, -event.delta() / 240.0)) + delta = event.angleDelta().y() + self.scale_view(math.pow(2.0, -delta / 240.0)) - def drawBackground(self, painter, rect): + def draw_background(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) + scene_rect = self.sceneRect() + right_shadow = QRectF(scene_rect.right(), scene_rect.top() + 5, + 5, scene_rect.height()) + bottom_shadow = QRectF(scene_rect.left() + 5, scene_rect.bottom(), + scene_rect.width(), 5) + if right_shadow.intersects(rect) or right_shadow.contains(rect): + painter.fillRect(right_shadow, Qt.darkGray) + if bottom_shadow.intersects(rect) or bottom_shadow.contains(rect): + painter.fillRect(bottom_shadow, 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) + gradient = QLinearGradient(scene_rect.topLeft(), scene_rect.bottomRight()) + gradient.setColorAt(0, Qt.white) + gradient.setColorAt(1, Qt.lightGray) + painter.fillRect(rect.intersected(scene_rect), QBrush(gradient)) + painter.setBrush(Qt.NoBrush) + painter.drawRect(scene_rect) # Text. - textRect = QtCore.QRectF(sceneRect.left() + 4, sceneRect.top() + 4, - sceneRect.width() - 4, sceneRect.height() - 4) + text_rect = QRectF(scene_rect.left() + 4, scene_rect.top() + 4, + scene_rect.width() - 4, scene_rect.height() - 4) message = self.tr("Click and drag the nodes around, and zoom with the " "mouse wheel or the '+' and '-' keys") @@ -393,13 +368,14 @@ class GraphWidget(QtWidgets.QGraphicsView): 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) + painter.setPen(Qt.lightGray) + painter.drawText(text_rect.translated(2, 2), message) + painter.setPen(Qt.black) + painter.drawText(text_rect, message) - def scaleView(self, scaleFactor): - factor = self.matrix().scale(scaleFactor, scaleFactor).mapRect(QtCore.QRectF(0, 0, 1, 1)).width() + def scale_view(self, scaleFactor): + factor = self.transform().scale(scaleFactor, scaleFactor).mapRect( + QRectF(0, 0, 1, 1)).width() if factor < 0.07 or factor > 100: return @@ -408,9 +384,9 @@ class GraphWidget(QtWidgets.QGraphicsView): if __name__ == "__main__": - app = QtWidgets.QApplication(sys.argv) + app = QApplication(sys.argv) widget = GraphWidget() widget.show() - sys.exit(app.exec_()) + sys.exit(app.exec()) |