在同一个 GraphicsScene 中加入 QGraphicsItem 和 QPainter

Join QGraphicsItem and QPainter in same GraphicsScene

我目前正在使用 PyQt5 和 Graphicscene 构建交互式 canvas,目前正在修改在这些帖子中找到的代码:

我已经制作了两个单独的示例来说明我想要的内容,但到目前为止我还无法将两者合并为一个代码。

第一段代码我在屏幕上点击的位置插入一个节点和对应的边。以双击鼠标左键开始和结束。

import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import *

class WindowClass(QMainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.view = ViewClass()
        self.setCentralWidget(self.view)

class ViewClass(QGraphicsView):
    def __init__(self, parent=None):
        QGraphicsView.__init__(self, parent)
        self.s = SceneClass()
        self.setScene(self.s)
        self.setRenderHint(QPainter.Antialiasing)

class SceneClass(QGraphicsScene):
    def __init__(self, parent=None):
        QGraphicsScene.__init__(self, QRectF(-1000, -1000, 2000, 2000), parent)

        self.node_start = None
        self.node_end = None
        self.pos = None
        self.pos_end = None

    def mouseDoubleClickEvent(self, event):
        if event.button() == Qt.LeftButton and self.node_start is None:
            node = Node()
            self.addItem(node)
            node.setPos(event.scenePos() + QPointF(10, 10))
            self.node_start = node
        else:
            self.node_start = None

    def mouseMoveEvent(self, event):
        super(SceneClass, self).mouseMoveEvent(event)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton and self.node_start:
            "nodo final"
            node = Node()
            self.addItem(node)
            node.setPos(event.scenePos() + QPointF(10, 10))
            self.node_end = node
            edge = Edge(self.node_start, self.node_end)
            self.addItem(edge)
            "nodo final se convierte en nodo inicial"
            self.node_start = self.node_end
        super(SceneClass, self).mousePressEvent(event)

class Node(QGraphicsEllipseItem):
    def __init__(self, rect=QRectF(-20, -20, 20, 20), parent=None):
        QGraphicsEllipseItem.__init__(self, rect, parent)
        self.edges = []
        self.setZValue(1)
        self.setBrush(Qt.darkGray)
        self.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)

    def addEdge(self, edge):
        self.edges.append(edge)

    def itemChange(self, change, value):
        if change == QGraphicsItem.ItemSelectedChange:
            self.setBrush(Qt.green if value else Qt.darkGray)

        if change == QGraphicsItem.ItemPositionHasChanged:
            for edge in self.edges:
                edge.adjust()

        return QGraphicsItem.itemChange(self, change, value)

class Edge(QGraphicsLineItem):
    def __init__(self, source, dest, parent=None):
        QGraphicsLineItem.__init__(self, parent)
        self.source = source
        self.dest = dest
        self.source.addEdge(self)
        self.dest.addEdge(self)
        self.setPen(QPen(Qt.red, 3))
        self.adjust()

    def adjust(self):
        self.prepareGeometryChange()
        self.setLine(QLineF(self.dest.pos() + QPointF(-10, -10), self.source.pos() + QPointF(-10, -10)))


if __name__ == '__main__':
    app = QApplication(sys.argv)
    wd = WindowClass()
    wd.show()
    sys.exit(app.exec_())

第二个代码绘制一条线,从上次单击鼠标左键的位置到屏幕上的当前鼠标位置。

import sys
from PyQt5.QtWidgets import (QApplication, QLabel, QWidget)
from PyQt5.QtGui import QPainter, QColor, QPen
from PyQt5.QtCore import Qt

class MouseTracker(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.setMouseTracking(True)

    def initUI(self):
        self.setGeometry(200, 200, 1000, 500)
        self.setWindowTitle('Mouse Tracker')
        self.label = QLabel(self)
        self.label.resize(500, 40)
        self.show()
        self.pos = None
        self.pos_end = None

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.pos_end = event.pos()

    def mouseMoveEvent(self, event):
        self.pos = event.pos()
        self.update()

    def paintEvent(self, event):
        if self.pos and self.pos_end:
            "Estilo de Linea"
            pen = QPen(Qt.red)
            pen.setWidth(3)
            pen.setStyle(Qt.CustomDashLine)
            pen.setDashPattern([10, 10])
            pen.setJoinStyle(Qt.RoundJoin)
            "Objeto de QPainter"
            q = QPainter(self)
            q.setRenderHint(QPainter.Antialiasing, True)
            "Aplicar estilo de Linea"
            q.setPen(pen)
            "Dibujar Linea"
            q.drawLine(self.pos.x(), self.pos.y(), self.pos_end.x(), self.pos_end.y())

app = QApplication(sys.argv)
ex = MouseTracker()
sys.exit(app.exec_())

我希望在 mi GUI 代码中看到的行为是两者的结合,意思如下:能够通过单击鼠标左键插入节点及其相应的边,然后在释放鼠标左键时开始QPaint 行的第二个代码从最后一个节点插入到屏幕上的当前位置。我试图加入这两个代码,但是当在第一个代码的任何 classes 中写入时,即使 ti 是它自己的 class,也不会触发 paintevent。

QPainter 是用于绘画的低级工具,不同的高级工具将其用作 Qt 图形框架,但在这种情况下不应使用它们。

在您的情况下,最好使用这些项目,因为它可以简化任务:

class WindowClass(QMainWindow):
    def __init__(self, parent=None):
        super(WindowClass, self).__init__(parent)
        view = QGraphicsView()
        view.setMouseTracking(True)
        view.setRenderHint(QPainter.Antialiasing)
        scene = SceneClass(self)
        view.setScene(scene)
        self.setCentralWidget(view)
        self.resize(640, 480)


class SceneClass(QGraphicsScene):
    def __init__(self, parent=None):
        super(SceneClass, self).__init__(QRectF(-1000, -1000, 2000, 2000), parent)
        self._edge_item = None

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            node = Node()
            node.setPos(event.scenePos())
            self.addItem(node)

            if self._edge_item:
                self._edge_item.dst = node
                self._edge_item = None
            else:
                self._edge_item = Edge()
                self._edge_item.src = node
                self.addItem(self._edge_item)

    def mouseMoveEvent(self, event):
        if self._edge_item:
            self._edge_item.p2 = event.scenePos()
        super(SceneClass, self).mouseMoveEvent(event)


class Node(QGraphicsEllipseItem):
    def __init__(self, rect=QRectF(-10, -10, 20, 20), parent=None):
        super(Node, self).__init__(rect, parent)
        self.edges = []
        self.setZValue(1)
        self.setBrush(Qt.darkGray)
        self.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)

    def addEdge(self, edge):
        self.edges.append(edge)

    def itemChange(self, change, value):
        if change == QGraphicsItem.ItemSelectedChange:
            self.setBrush(Qt.green if value else Qt.darkGray)
        if change == QGraphicsItem.ItemPositionHasChanged:
            for edge in self.edges:
                edge.adjust()
        return super(Node, self).itemChange(change, value)


class Edge(QGraphicsLineItem):
    def __init__(self, parent=None):
        super(Edge, self).__init__(parent)
        self.setPen(QPen(Qt.red, 3))
        self._src = None
        self._dst = None

    @property
    def src(self):
        return self._src

    @src.setter
    def src(self, node):
        self._src = node
        self._src.addEdge(self)
        self.adjust()

    @property
    def dst(self):
        return self._dst

    @dst.setter
    def dst(self, node):
        self._dst = node
        self._dst.addEdge(self)
        self.adjust()

    @property
    def p1(self):
        return self.line().p1()

    @p1.setter
    def p1(self, p):
        line = self.line()
        line.setP1(p)
        self.setLine(line)

    @property
    def p2(self):
        return self.line().p2()

    @p2.setter
    def p2(self, p):
        line = self.line()
        line.setP2(p)
        self.setLine(line)

    def adjust(self):
        self.prepareGeometryChange()
        if self.src:
            self.p1 = self.src.pos()
            self.p2 = self.src.pos()
        if self.dst:
            self.p2 = self.dst.pos()