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

Matplotlib点击散图图例切换元素可见性 代码运行异常求助

核心问题定位

原代码点击图例没效果,是几个逻辑错误叠加导致的:

  1. 你用的是单个散点对象+颜色映射实现的分类散点图,所有点都归同一个PathCollection对象管理,直接对整个散点调用set_visible()只能全显/全藏,根本无法单独隐藏某一类点
  2. 拾取回调里写的self.scatter.picked_legend是不存在的属性,代码运行到这行直接抛出属性错误,后续逻辑完全没有执行,只是PyQt默认不会把回调内的错误弹窗提示,看起来就像点了没反应
  3. 仅给第一个标签为'10'的图例项开启了拾取功能,剩下两个图例项点击后根本不会触发事件
  4. 没有建立图例项和对应数据类别的映射关系,就算事件正常触发,也无法定位到需要隐藏的点集合,也没有实现图例淡显的状态逻辑
修改后完整可运行代码
import sys
import pandas as pd
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QVBoxLayout, QWidget
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as canvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
from matplotlib.colors import ListedColormap
import matplotlib.colors as colors
import matplotlib.cm as cm
import numpy as np


class MplCanvas(canvas):
    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super().__init__(fig)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        df = pd.DataFrame({
            "X": [1, 2, 3, 4, 5],
            "Y": [1, 2, 3, 4, 5],
            "pitch": [10, 10, 20, 20, 30],
        })
        layout_main = QVBoxLayout()

        self.sc = MplCanvas(self, width=5, height=4, dpi=100)
        color = cm.jet
        self.u, div = np.unique(df.pitch.values, return_inverse=True)
        self.div = div  # 保存每个点对应的类别索引
        colorlist = [colors.rgb2hex(color(i)) for i in np.linspace(1, 0, len(self.u))]
        cmap = ListedColormap(colorlist)

        self.scatter = self.sc.axes.scatter(df['X'], df['Y'], c=div, cmap=cmap)
        # 保存原始点颜色,用于状态恢复
        self.original_facecolor = self.scatter.get_facecolors().copy()
        legend_handles, _ = self.scatter.legend_elements(prop='colors', num=len(colorlist))
        legend = self.sc.axes.legend(legend_handles, self.u, loc=2)

        # 给所有图例项绑定拾取,建立图例和类别索引的映射
        self.legend_to_idx = {}
        for idx, leg_item in enumerate(legend.get_lines()):
            leg_item.set_picker(True)
            leg_item.set_pickradius(10)
            self.legend_to_idx[leg_item] = idx

        self.scatter.figure.canvas.mpl_connect('pick_event', self.on_pick)

        toolbar = NavigationToolbar(self.sc, self)
        layout_main.addWidget(toolbar)
        layout_main.addWidget(self.sc)

        widget = QWidget()
        widget.setLayout(layout_main)
        self.setCentralWidget(widget)

    def on_pick(self, event):
        leg_artist = event.artist
        # 过滤非图例的拾取事件
        if leg_artist not in self.legend_to_idx:
            return

        cat_idx = self.legend_to_idx[leg_artist]
        # 切换图例淡显状态
        current_alpha = leg_artist.get_alpha()
        new_alpha = 0.2 if (current_alpha == 1.0 or current_alpha is None) else 1.0
        leg_artist.set_alpha(new_alpha)

        # 切换对应类别点的显示状态
        point_colors = self.scatter.get_facecolors()
        for point_idx, point_cat in enumerate(self.div):
            if point_cat == cat_idx:
                if new_alpha < 1:
                    # 隐藏:将点透明度设为0
                    point_colors[point_idx, -1] = 0
                else:
                    # 显示:还原原始颜色
                    point_colors[point_idx] = self.original_facecolor[point_idx]
        self.scatter.set_facecolors(point_colors)
        # 重绘画布生效
        self.sc.draw()


app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
关键修改点说明
  • 提前缓存每个点的类别索引、原始颜色参数,切换状态时可以直接还原,不会出现显示错乱
  • 给所有图例项统一开启拾取,用字典绑定图例对象和对应类别索引的关系,点击时可以直接定位需要操作的点集合
  • 单类点的隐藏通过修改对应点颜色的alpha通道实现,不需要拆分多个散点对象绘制,点量大的时候也能保持流畅
  • 点击图例时同步修改图例自身透明度,0.2为淡显状态、1.0为正常状态,完全匹配预期交互逻辑
  • 每次状态修改后主动调用画布重绘方法,确保界面即时更新

运行效果:点击任意图例项,对应分类的散点会完全隐藏,被点击的图例变为淡显状态;再次点击同个图例项,散点和图例都会恢复到初始显示状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 07:42:13