首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在QChartView对象上绘制光标

在QChartView对象上绘制光标
EN

Stack Overflow用户
提问于 2020-02-04 13:29:24
回答 1查看 447关注 0票数 1

我需要在QChartView对象上画一个光标。就像这样:

图表上的光标

每当用户单击图表时,光标都应该移到那里。我不知道这怎么可能。正如我所搜索的,这似乎不是QChartView的内置特性。那我该怎么做?顺便说一句,我是QT的新手。

EN

回答 1

Stack Overflow用户

发布于 2022-05-05 07:41:09

我也面临着同样的问题。通过引用这位律师,在鼠标位置绘制光标似乎很简单。请注意,我将x.setter中的x.setter代码从self.update()修改为self.scene().update()。这对于更新游标非常重要。老实说,我不知道为什么。如果你知道不同之处,你可以留下评论。

代码语言:javascript
复制
# refer to https://stackoverflow.com/a/67596291/9758790
import sys
import time
from PyQt5.QtCore import Qt, QPointF
from PyQt5.QtGui import QColor, QPainter, QPen
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtChart import (
    QChart,
    QChartView,
    QLineSeries,
)
from PyQt5.Qt import *


class ChartView(QChartView):
    _x = None

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, x):
        self._x = x
        # self.update()
        self.scene().update()

    def drawForeground(self, painter, rect):
        if self.x is None:
            return
        painter.save()

        pen = QPen(QColor("indigo"))
        pen.setWidth(3)
        painter.setPen(pen)

        # p = self.chart().mapToPosition(QPointF(self.x, 0))
        p = QPointF(self.x, 0)
        r = self.chart().plotArea()

        p1 = QPointF(p.x(), r.top())
        p2 = QPointF(p.x(), r.bottom())
        painter.drawLine(p1, p2)

        painter.restore()

    def mousePressEvent(self, env):
        # refer to https://stackoverflow.com/a/44078533/9758790
        scene_position = self.mapToScene(env.pos())
        chart_position = self.chart().mapFromScene(scene_position)
        value_at_position = self.chart().mapToValue(chart_position)
        if self.chart().axisX().min() < value_at_position.x() < self.chart().axisX().max():
            self.x = scene_position.x()


def main():
    app = QApplication(sys.argv)

    series1 = QLineSeries() << QPointF(0, 3) << QPointF(1, 1) << QPointF(3, 9) << QPointF(4, 1)
    series2 = QLineSeries() << QPointF(0, 9) << QPointF(1, 8) << QPointF(3, 6) << QPointF(4, 6)
    series3 = QLineSeries() << QPointF(0, 4) << QPointF(1, 2) << QPointF(3, 2) << QPointF(4, 3)

    chart = QChart()
    chart.addSeries(series1)
    chart.addSeries(series2)
    chart.addSeries(series3)

    chart.createDefaultAxes()

    chart.legend().setVisible(True)
    chart.legend().setAlignment(Qt.AlignBottom)

    chartView = ChartView(chart)
    chartView.setRenderHint(QPainter.Antialiasing)

    chartView.x = None

    window = QMainWindow()
    window.setCentralWidget(chartView)
    window.resize(420, 300)
    window.show()

    app.exec()


if __name__ == "__main__":
    main()

这意味着:

然而,我发现很难画出光标的交点和线图。正如这个论坛这个论坛中提到的,如果光标指向两个数据点之间的插值部分,则很难确定交点的y坐标,因为Qt没有提供内建函数来获得value_at_position.x()处的插值函数值。这个问题也与找到插值值有关。所以我试着用我自己的方式做插值。

下面的代码版本也将绘制交点。

代码语言:javascript
复制
# refer to https://stackoverflow.com/a/67596291/9758790
import sys
import time
from PyQt5.QtCore import Qt, QPointF
from PyQt5.QtGui import QColor, QPainter, QPen
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtChart import (
    QChart,
    QChartView,
    QLineSeries,
)
from PyQt5.Qt import *
import math

class ChartView(QChartView):
    _cursor = None
    _y = []

    @property
    def cursor(self):
        return self._cursor

    @cursor.setter
    def cursor(self, point):
        # refer to https://stackoverflow.com/a/44078533/9758790
        scene_position = self.mapToScene(point)
        chart_position = self.chart().mapFromScene(scene_position)
        value_at_position = self.chart().mapToValue(chart_position)
        if self.chart().axisX().min() < value_at_position.x() < self.chart().axisX().max():
            self._cursor = scene_position
            # self.update()
            self.scene().update()

    def drawForeground(self, painter, rect):
        if self.cursor is None:
            return
        painter.save()

        pen = QPen(QColor("indigo"))
        pen.setWidth(1)
        painter.setPen(pen)

        p = self.cursor
        r = self.chart().plotArea()

        p1 = QPointF(p.x(), r.top())
        p2 = QPointF(p.x(), r.bottom())
        painter.drawLine(p1, p2)

        chart_position = self.chart().mapFromScene(self.cursor)
        value_at_position = self.chart().mapToValue(chart_position)
        for series_i in self.chart().series():
            pen2 = QPen(series_i.color())
            pen2.setWidth(10)
            painter.setPen(pen2)

            # find the nearest points
            min_distance_left = math.inf
            min_distance_right = math.inf
            nearest_point_left = None
            nearest_point_right = None
            exact_point = None
            for p_i in series_i.pointsVector():
                if p_i.x() > value_at_position.x():
                    if p_i.x() - value_at_position.x() < min_distance_right:
                        min_distance_right = p_i.x() - value_at_position.x()
                        nearest_point_right = p_i
                elif p_i.x() < value_at_position.x():
                    if value_at_position.x() - p_i.x() < min_distance_right:
                        min_distance_left = value_at_position.x() - p_i.x()
                        nearest_point_left = p_i
                else:
                    exact_point = p_i
                    nearest_point_left = None
                    nearest_point_right = None
                    break
            if nearest_point_right is not None and nearest_point_left is not None:
                # do linear interpolated by my self
                k = ((nearest_point_right.y() - nearest_point_left.y()) / (nearest_point_right.x() - nearest_point_left.x()))
                point_interpolated_y = nearest_point_left.y() + k * (value_at_position.x() - nearest_point_left.x())
                point_interpolated_x = value_at_position.x()

                point_interpolated = QPointF(point_interpolated_x, point_interpolated_y)

                painter.drawPoint(self.chart().mapToScene(self.chart().mapToPosition(point_interpolated)))
            if exact_point is not None:
                painter.drawPoint(self.chart().mapToScene(self.chart().mapToPosition(exact_point)))

        painter.restore()

    def mousePressEvent(self, env):
        self.cursor = env.pos()


def main():
    app = QApplication(sys.argv)

    series1 = QLineSeries() << QPointF(0, 3) << QPointF(1, 1) << QPointF(3, 9) << QPointF(4, 1)
    series2 = QLineSeries() << QPointF(0, 9) << QPointF(1, 8) << QPointF(3, 6) << QPointF(4, 6)
    series3 = QLineSeries() << QPointF(0, 4) << QPointF(1, 2) << QPointF(3, 2) << QPointF(4, 3)

    chart = QChart()
    chart.addSeries(series1)
    chart.addSeries(series2)
    chart.addSeries(series3)

    chart.createDefaultAxes()

    chart.legend().setVisible(True)
    chart.legend().setAlignment(Qt.AlignBottom)

    chartView = ChartView(chart)
    chartView.setRenderHint(QPainter.Antialiasing)

    chartView.x = None

    window = QMainWindow()
    window.setCentralWidget(chartView)
    window.resize(420, 300)
    window.show()

    app.exec()


if __name__ == "__main__":
    main()

这意味着:

这个链接提供了一种解决方案,可以找到离鼠标位置最近的数据点,并在那里画一条线,从而避免了插值。我的代码也可以修改以这种方式绘制。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60058507

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档