Обнаружение попадания мыши с помощью QPainterPath в QGraphicsItem - PullRequest
0 голосов
/ 02 июля 2018

Я реализовал пользовательский график. Но я застрял с обнаружением удара мыши с помощью QPainterPath.

Я пытался использовать shapeitem (), boundingRect (). но это только проверяет грубую форму границы.

Я хочу проверить систему попадания мыши с точной позицией в экземпляре пути QPainterPath. Но, похоже, нет API, как эта функциональность.

QGraphicsScene моего приложения устанавливается с той же координатой, что и QGraphicsView в представлении resizeEvent ().

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 и plot путь элемента с помощью mouse_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 голосов
/ 02 июля 2018

Вы должны преобразовать положение пути относительно элемента, который масштабируется к положению относительно сцены, используя mapToScene():

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