You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PySide6中点击QChartView绘制十字光标问题求助

解决QChart散点图鼠标在数据点上时十字光标无法正常显示的问题

问题描述

在QChart创建的散点图上,希望鼠标移动到绘图区任意位置(无论有无数据点)都显示完整的十字光标。参考相关实现后,无数据点的区域可正常显示十字光标,但鼠标移到散点数据点上时,仅在点周围出现短小的水平/垂直线条,无法显示预期的完整十字光标。

问题原因

QScatterSeries的标记点(Marker)默认会接收并处理鼠标悬停事件,导致QChartView的鼠标事件处理逻辑无法正确获取鼠标在绘图区的全局位置,进而使得十字光标绘制时的坐标计算出现偏差。同时原代码中用QPoint存储坐标值会丢失浮点精度,进一步影响绘制效果。

解决方案

  1. 禁用散点序列的鼠标悬停事件,让鼠标事件直接传递给QChartView处理
  2. 将存储坐标的变量改为QPointF以保留浮点精度
  3. 调整绘图逻辑,确保十字光标覆盖整个绘图区

修改后的完整代码

#!/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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 08:54:58