Join QGraphicsItem and QPainter in same GraphicsScene

可紊 提交于 2020-05-17 06:14:28

问题


I am currently building an interactive canvas using PyQt5 and Graphicscene, so far modifying the codes found on those posts:

  • PySide Node Graph connect items
  • How to draw a line from mouse to a point in PyQt5?

I have made two separates examples of what I would like , but so far I have not been able to merge the two into a single code.

The first code I inserts a node and corresponding edge in the position clicked on the screen. Begins and ends with a double click of the left button of the mouse.

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_())

The second code paints a line from the last position where the left mouse button was clicked to the current mouse position on the screen.

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_())

The behavior I would like to see in mi GUI code is the union of the two meaning the following: being able to insert node with its corresponding edge by a left mouse click and then when the left mouse click is released start the QPaint line of the second code from the last node insert to the current position on the screen. I have tried to join the two code but the paintevent does not triggered when is written in any of the classes of the first code, even if ti is its own class.


回答1:


QPainter is the low-level tool used for painting and the different high-level tools use it as the Qt Graphics Framework, but in this case they should not be used.

In your case it is preferable to use the items since it simplifies the task:

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()


来源:https://stackoverflow.com/questions/61809809/join-qgraphicsitem-and-qpainter-in-same-graphicsscene

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!