使用QGraphicsItem

时间:2018-07-02 09:34:16

标签: pyqt qt5 pyqt5 graphics2d

我实现了自定义图表。但是我坚持使用QPainterPath进行鼠标击中检测。

我尝试使用graphicsitem的shape(),boundingRect()。但这只会检查边界的大致形状。

我想检查QPainterPath路径实例上确切位置的鼠标命中系统。但是似乎没有像该功能这样的api。

我的应用的QGraphicsScene与视图的resizeEvent()中的QGraphicsView设置了相同的坐标。

scene: MyScene = self.scene()
scene.setSceneRect(self.rect().x(), self.rect().y(),
                   self.rect().width(), self.rect().height())

同时,我的绘图QGraphicsItem通过QTransform缩放。

plot: QGraphicsItem = scene.plot
trans = QTransform()
data = plot.df['data']
data = data - data.min()
data_max = data.max()
data_min = data.min()
trans.scale(self.width() / len(data),
            self.height() / (data_max - data_min))
plot.trans = trans
plot.setTransform(trans)

然后在MyScene中,添加矩形项mouse_rec。因此,我使用mouse_rec

检查了plotmouse_rec.collidesWithPath(path)项目的路径

它仅适用于原始路径。

这是所有代码。只需复制并粘贴,就可以运行它。

红色图是原始路径,黄色图是缩放路径。鼠标点击检查仅适用于红色情节...

import numpy
import pandas

from PyQt5 import QtGui
from PyQt5.QtCore import Qt, QRectF, QRect
from PyQt5.QtGui import QRadialGradient, QGradient, QPen, QPainterPath, QTransform, QPainter, QColor
from PyQt5.QtWidgets import QApplication, QGraphicsScene, QGraphicsView, QGraphicsSceneMouseEvent, QGraphicsItem, \
    QStyleOptionGraphicsItem, QWidget, QGraphicsRectItem


class MyItem(QGraphicsItem):
    def __init__(self, df, parent=None):
        QGraphicsItem.__init__(self, parent)
        self.num = 1
        self.df = df
        self.path = QPainterPath()
        self.trans = QTransform()
        self.cached = False
        self.printed = False
        self.setZValue(0)

    def paint(self, painter: QtGui.QPainter, option: 'QStyleOptionGraphicsItem', widget: QWidget = ...):
        data = self.df['data']
        data = data - data.min()
        data_max = data.max()
        data_min = data.min()

        if not self.cached:
            for i in range(data.size - 1):
                self.path.moveTo(i, data[i])
                self.path.lineTo(i+1, data[i+1])

            self.cached = True

        pen = QPen(Qt.white)
        pen.setCosmetic(True)
        painter.setPen(pen)
        painter.drawRect(0, 0, data.size, data_max - data_min)

        pen.setColor(Qt.yellow)
        painter.setPen(pen)
        painter.drawPath(self.path)

        if not self.printed:
            rec_item = self.scene().addPath(self.path, QPen(Qt.red))
            rec_item.setZValue(-10)
            self.printed = True

    def boundingRect(self):
        data = self.df['data']
        data_max = data.max()
        data_min = data.min()

        return QRectF(0, 0, data.size, data_max - data_min)


class MyScene(QGraphicsScene):
    def __init__(self, data, parent=None):
        QGraphicsScene.__init__(self, parent)
        self.data = data
        self.mouse_rect = QGraphicsRectItem()
        self.plot: MyItem(data) = None
        self.bounding_rect = QGraphicsRectItem()
        self.setBackgroundBrush(QColor('#14161f'))

        self.addItem(self.bounding_rect)
        self.printed = False

    def mouseMoveEvent(self, event: 'QGraphicsSceneMouseEvent'):
        print()

        print("rec rect : ", self.mouse_rect.rect())
        print("Scene rect : ", self.sceneRect())
        print("ItemBounding rect : ", self.itemsBoundingRect())
        print("transform : ", self.plot.transform().m11(), ", ", self.plot.transform().m22())
        item = self.itemAt(event.scenePos(), self.plot.transform())

        if item and isinstance(item, MyItem):
            print()
            print('collides path : ', self.mouse_rect.collidesWithPath(item.path))
            print('collides item : ', self.mouse_rect.collidesWithItem(item))

        super().mouseMoveEvent(event)

    def print_bound(self, rect):
        self.bounding_rect.setPen(QPen(Qt.green))
        self.bounding_rect.setRect(rect.x() + 5, rect.y() + 5,
                                   rect.width() - 10, rect.height() - 10)


class MyView(QGraphicsView):
    def __init__(self, data, parent=None):
        QGraphicsView.__init__(self, parent)
        self.data = data
        self.setMouseTracking(True)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

    def wheelEvent(self, event: QtGui.QWheelEvent):
        print("pixel / Data : {}".format(self.width() / len(self.data)))

    def resizeEvent(self, event: QtGui.QResizeEvent):
        scene: MyScene = self.scene()
        scene.setSceneRect(self.rect().x(), self.rect().y(),
                           self.rect().width(), self.rect().height())

        scene.print_bound(self.rect())

        plot: QGraphicsItem = scene.plot
        trans = QTransform()
        data = plot.df['data']
        data = data - data.min()
        data_max = data.max()
        data_min = data.min()
        trans.scale(self.width() / len(data),
                    self.height() / (data_max - data_min))
        plot.trans = trans
        plot.setTransform(trans)

    def mouseMoveEvent(self, event: QtGui.QMouseEvent):
        mouse_rect: QGraphicsRectItem = self.scene().mouse_rect
        mouse_rect.setRect(event.pos().x() - 2, event.pos().y() - 2, 4, 4)

        super().mouseMoveEvent(event)


if __name__ == '__main__':
    df = pandas.DataFrame({'data': numpy.random.randint(0, 20, 50)})

    app = QApplication([])
    scene = MyScene(df)
    view = MyView(df)
    view.setScene(scene)

    rec = QGraphicsRectItem(-2, -2, 4, 4)
    rec.setPen(Qt.white)
    scene.mouse_rect = rec
    scene.addItem(rec)

    plot = MyItem(df)
    scene.addItem(plot)
    scene.plot = plot

    view.show()

    app.exec_()

检查带有路径的鼠标点的任何想法?我首先尝试了自定义数学函数来计算[点<->线]距离,但是这需要很多时间并使应用程序滞后。.

我不仅要绘制线图,还要绘制条形图,面积图,点图,烛形图。.有解决这个问题的主意吗?

1 个答案:

答案 0 :(得分:1)

您必须使用https://gitlab.com/itsleon/demo将路径相对于项目的位置转换为缩放至相对于场景的位置:

if item and isinstance(item, MyItem):
    print('collides path : ', self.mouse_rect.collidesWithPath(item.mapToScene(item.path)))
    print('collides item : ', self.mouse_rect.collidesWithItem(item))