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

每当用户单击图表时,光标都应该移到那里。我不知道这怎么可能。正如我所搜索的,这似乎不是QChartView的内置特性。那我该怎么做?顺便说一句,我是QT的新手。
发布于 2022-05-05 07:41:09
我也面临着同样的问题。通过引用这位律师,在鼠标位置绘制光标似乎很简单。请注意,我将x.setter中的x.setter代码从self.update()修改为self.scene().update()。这对于更新游标非常重要。老实说,我不知道为什么。如果你知道不同之处,你可以留下评论。
# 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()处的插值函数值。这个问题也与找到插值值有关。所以我试着用我自己的方式做插值。
下面的代码版本也将绘制交点。
# 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()这意味着:

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