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

PyQtGraph中批量ScatterplotItem(spots)的旋转实现问题

实现PyQtGraph批量散点的单个旋转功能

我之前在处理PyQtGraph大型散点数据集时也遇到过完全一样的问题——批量绘制spots确实性能拉满,但默认不支持单个散点旋转,单独加散点又直接卡成PPT。摸索了一阵后,找到了一个通过子类化ScatterPlotItem来实现的方案,分享给你:

核心思路

PyQtGraph的ScatterPlotItem在批量模式下通过复用绘制路径来提升性能,但默认逻辑没有考虑旋转角度。我们可以重写它的路径生成和绘制逻辑,给每个spot添加自定义旋转属性,同时保持批量绘制的高效性。

实现步骤

1. 自定义带旋转支持的ScatterPlotItem子类

我们需要重写两个关键方法:

  • generateSpotPath:根据每个spot的旋转角度生成对应的路径
  • paint:遍历所有spot,使用旋转后的路径批量绘制
import pyqtgraph as pg
from PyQt5.QtGui import QPainter, QTransform

class RotatableScatterPlotItem(pg.ScatterPlotItem):
    def generateSpotPath(self, symbol, size, spot=None):
        # 先调用父类生成基础符号路径
        base_path = super().generateSpotPath(symbol, size)
        # 如果当前spot有angle属性,应用旋转变换
        if spot and 'angle' in spot:
            transform = QTransform().rotate(spot['angle'])
            return transform.map(base_path)
        return base_path

    def paint(self, painter, opt, widget):
        painter.setRenderHint(QPainter.Antialiasing)
        if not self.scatterPlot:
            return
        
        spots = self.scatterPlot.spots()
        if not spots:
            return

        # 遍历每个spot,绘制带旋转的符号
        for spot in spots:
            pos = spot['pos']
            size = spot['size']
            symbol = spot.get('symbol', self.symbol())
            pen = spot.get('pen', self.pen())
            brush = spot.get('brush', self.brush())

            # 设置当前绘制的笔刷
            painter.setPen(pen)
            painter.setBrush(brush)

            # 获取带旋转的路径并绘制
            rotated_path = self.generateSpotPath(symbol, size, spot)
            # 平移到散点位置绘制
            painter.translate(pos.x(), pos.y())
            painter.drawPath(rotated_path)
            painter.translate(-pos.x(), -pos.y())

2. 使用自定义子类创建带旋转的散点

在构建spots数据时,给需要旋转的散点添加angle属性(单位:度),然后用我们的子类实例化散点项:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout

if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = QMainWindow()
    central_widget = QWidget()
    win.setCentralWidget(central_widget)
    layout = QVBoxLayout(central_widget)

    # 创建绘图组件
    plot_widget = pg.PlotWidget()
    layout.addWidget(plot_widget)

    # 生成1000个带旋转角度的散点数据
    spots = []
    for idx in range(1000):
        # 每个散点的旋转角度从0到360循环
        rotation_angle = idx % 360
        spots.append({
            'pos': (idx / 50, pg.gaussianFilter([idx / 50], sigma=3)[0]),
            'size': 10,
            'brush': pg.intColor(idx, 1000),
            'angle': rotation_angle
        })

    # 使用自定义子类创建散点项并添加到绘图
    scatter_item = RotatableScatterPlotItem(spots=spots)
    plot_widget.addItem(scatter_item)

    win.show()
    sys.exit(app.exec_())

性能说明

这个方案虽然是逐个绘制散点,但直接操作QPainter的路径对象,避免了创建多个ScatterPlotItem带来的资源开销,性能和原生批量绘制模式接近,拖动时不会出现严重卡顿。如果你的场景需要极致性能(比如十万级以上散点),可以考虑基于GLScatterPlotItem做OpenGL层面的改造,修改顶点着色器来处理旋转,但上述方案对大多数大型数据集已经足够。

内容的提问来源于stack exchange,提问作者frodo4fingers

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:01:06