PySide6中点击QChartView绘制十字光标问题求助
解决QChart散点图鼠标在数据点上时十字光标无法正常显示的问题
问题描述
在QChart创建的散点图上,希望鼠标移动到绘图区任意位置(无论有无数据点)都显示完整的十字光标。参考相关实现后,无数据点的区域可正常显示十字光标,但鼠标移到散点数据点上时,仅在点周围出现短小的水平/垂直线条,无法显示预期的完整十字光标。
问题原因
QScatterSeries的标记点(Marker)默认会接收并处理鼠标悬停事件,导致QChartView的鼠标事件处理逻辑无法正确获取鼠标在绘图区的全局位置,进而使得十字光标绘制时的坐标计算出现偏差。同时原代码中用QPoint存储坐标值会丢失浮点精度,进一步影响绘制效果。
解决方案
- 禁用散点序列的鼠标悬停事件,让鼠标事件直接传递给QChartView处理
- 将存储坐标的变量改为
QPointF以保留浮点精度 - 调整绘图逻辑,确保十字光标覆盖整个绘图区
修改后的完整代码
#!/usr/bin/env python # coding: utf-8 import random import sys from PySide6.QtCharts import ( QChart, QChartView, QScatterSeries, QValueAxis, ) from PySide6.QtCore import ( QPointF, QRectF, Qt, ) from PySide6.QtGui import ( QColor, QMouseEvent, QPainter, QPen, ) from PySide6.QtWidgets import ( QApplication, QMainWindow, ) class ScatterView(QChartView): def __init__(self, list_data): super().__init__() self.list_data = list_data self.init_ui() self.setRenderHint(QPainter.Antialiasing) # 改用QPointF存储坐标,保留浮点精度 self._value_pos = QPointF() self.setMouseTracking(True) def generate_scatter_series(self): series = QScatterSeries() series.setMarkerShape(QScatterSeries.MarkerShape.MarkerShapeCircle) series.setMarkerSize(10) series.setPen(QPen(Qt.PenStyle.NoPen)) # 禁用序列的鼠标悬停事件,让事件传递给ChartView series.setAcceptHoverEvents(False) for xy in self.list_data: series.append(*xy) return series def generate_value_axis(self): axis = QValueAxis() axis.setRange(0, 1) return axis def init_ui(self): chrt = QChart() self.setChart(chrt) chrt.setTitle('Scatter Plot example') chrt.setDropShadowEnabled(False) chrt.legend().hide() # X axis axis_x = self.generate_value_axis() chrt.addAxis(axis_x, Qt.AlignmentFlag.AlignBottom) # Y axis axis_y = self.generate_value_axis() chrt.addAxis(axis_y, Qt.AlignmentFlag.AlignLeft) # Series series = self.generate_scatter_series() series.attachAxis(axis_x) series.attachAxis(axis_y) chrt.addSeries(series) # Override methods def drawForeground(self, painter: QPainter, rect: QRectF): super().drawForeground(painter, rect) if self.chart() is None or self._value_pos.isNull(): return pen = QPen(QColor('red')) pen.setWidth(1) painter.setPen(pen) area = self.chart().plotArea() point = self.chart().mapToPosition(self._value_pos) # 绘制贯穿整个绘图区的十字线 painter.drawLine(QPointF(point.x(), area.top()), QPointF(point.x(), area.bottom())) painter.drawLine(QPointF(area.left(), point.y()), QPointF(area.right(), point.y())) def mouseMoveEvent(self, event: QMouseEvent): super().mouseMoveEvent(event) if self.chart() is None: return # 获取鼠标在ChartView中的位置,转换为场景坐标 point_scene = self.mapToScene(event.position().toPoint()) area = self.chart().plotArea() if area.contains(point_scene): self._value_pos = self.chart().mapToValue(point_scene) self.setCursor(Qt.CursorShape.PointingHandCursor) else: self.unsetCursor() self._value_pos = QPointF() self.update() class Example(QMainWindow): def __init__(self): super().__init__() # Data prep. list_xy_pair = [[random.random(), random.random()] for i in range(100)] scatter = ScatterView(list_xy_pair) self.setCentralWidget(scatter) self.resize(500, 500) self.setWindowTitle('ScatterView') def main(): app = QApplication(sys.argv) ex = Example() ex.show() sys.exit(app.exec()) if __name__ == '__main__': main()
关键修改点说明
- 在
generate_scatter_series中添加series.setAcceptHoverEvents(False),禁止散点序列处理鼠标悬停事件,确保QChartView能接收完整的鼠标事件 - 将
self._value_pos的类型从QPoint改为QPointF,避免浮点坐标转整数时丢失精度 - 简化
drawForeground中的绘图逻辑,直接绘制贯穿绘图区的十字线,无需额外的边界判断(因为已经在mouseMoveEvent中判断了鼠标是否在绘图区内)
内容的提问来源于stack exchange,提问作者Fuhito Suguri
相关产品推荐
相关产品推荐

