在Matplotlib与PyQt5中为Line2D添加标签失败的问题求助
解决Matplotlib Line2D标签不显示的问题
我明白你遇到的困扰了——你已经给Line2D对象设置了label参数,但图例就是没显示出来,没法区分后续要添加的多条监控曲线。别担心,这是因为Matplotlib不会自动生成图例,你需要显式触发这个操作才行。
问题原因
你创建Line2D时设置label='cpu'的操作是正确的,但Matplotlib需要你调用legend()方法来根据这些label生成并渲染图例,否则它不会自动显示。
修复步骤
- 保留正确的Line2D label设置:你原来的代码里,把
self.line1 = Line2D([], [], color='blue')改成self.line1 = Line2D([], [], color='blue', label='CPU Usage')(或者你想要的标签文本),这部分没问题。 - 添加图例生成代码:在
CustomFigCanvas类的__init__方法中,所有Line2D对象添加到坐标轴之后,调用self.ax1.legend()来生成图例。 - 后续添加多条曲线:只需要给每条新的Line2D都设置
label参数,图例会自动包含所有带label的线条。
修改后的完整代码
################################################################### # # # PLOT A LIVE GRAPH (PyQt5) # # ----------------------------- # # EMBED A MATPLOTLIB ANIMATION INSIDE YOUR # # OWN GUI! # # # ################################################################### import sys import os from PyQt5.QtWidgets import * from PyQt5.QtCore import * from PyQt5.QtGui import * import functools import numpy as np import random as rd import matplotlib matplotlib.use("Qt5Agg") from matplotlib.figure import Figure from matplotlib.animation import TimedAnimation from matplotlib.lines import Line2D from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas import time import threading import subprocess import platform from os import linesep import psutil class CustomMainWindow(QMainWindow): def __init__(self): super(CustomMainWindow, self).__init__() # Define the geometry of the main window self.setGeometry(300, 300, 800, 400) self.setWindowTitle("System Monitor") # Create FRAME_A self.FRAME_A = QFrame(self) self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QColor(210,210,235,255).name()) self.LAYOUT_A = QGridLayout() self.FRAME_A.setLayout(self.LAYOUT_A) self.setCentralWidget(self.FRAME_A) # Place the matplotlib figure self.myFig = CustomFigCanvas() self.LAYOUT_A.addWidget(self.myFig, *(0,1)) # Add the callbackfunc to .. myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, daemon = True, args = (self.addData_callbackFunc,)) myDataLoop.start() self.show() return def addData_callbackFunc(self, value): # print("Add data: " + str(value)) self.myFig.addData(value) return ''' End Class ''' class CustomFigCanvas(FigureCanvas, TimedAnimation): def __init__(self): self.addedData = [] print(matplotlib.__version__) # The data self.xlim = 200 self.n = np.linspace(0, self.xlim - 1, self.xlim) a = [] b = [] a.append(2.0) a.append(4.0) a.append(2.0) b.append(4.0) b.append(3.0) b.append(4.0) self.y = (self.n * 0.0) + 50 # The window self.fig = Figure(figsize=(5,5), dpi=100) self.ax1 = self.fig.add_subplot(111) # self.ax1 settings self.ax1.set_xlabel('time in 1/10 of a second') self.ax1.set_ylabel('usage in %') # 1. 给Line2D添加label参数 self.line1 = Line2D([], [], color='blue', label='CPU Usage') self.line1_tail = Line2D([], [], color='blue', linewidth=2) self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r') self.ax1.add_line(self.line1) self.ax1.add_line(self.line1_tail) self.ax1.add_line(self.line1_head) self.ax1.set_xlim(0, self.xlim) self.ax1.set_ylim(0, 100) # 2. 添加图例生成代码 self.ax1.legend() FigureCanvas.__init__(self, self.fig) TimedAnimation.__init__(self, self.fig, interval = 50, blit = True) return def new_frame_seq(self): return iter(range(self.n.size)) def _init_draw(self): lines = [self.line1, self.line1_tail, self.line1_head] for l in lines: l.set_data([], []) return def addData(self, value): self.addedData.append(value) return def zoomIn(self, value): bottom = self.ax1.get_ylim()[0] top = self.ax1.get_ylim()[1] bottom += value top -= value self.ax1.set_ylim(bottom,top) self.draw() return def _step(self, *args): # Extends the _step() method for the TimedAnimation class. try: TimedAnimation._step(self, *args) except Exception as e: self.abc += 1 print(str(self.abc)) TimedAnimation._stop(self) pass return def _draw_frame(self, framedata): margin = 2 while(len(self.addedData) > 0): self.y = np.roll(self.y, -1) self.y[-1] = self.addedData[0] del(self.addedData[0]) self.line1.set_data(self.n[ 0 : self.n.size - margin ], self.y[ 0 : self.n.size - margin ]) self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin])) self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin]) self._drawn_artists = [self.line1, self.line1_tail, self.line1_head] return ''' End Class ''' # You need to setup a signal slot mechanism, to # send data to your GUI in a thread-safe way. # Believe me, if you don't do this right, things # go very very wrong.. class Communicate(QObject): data_signal = pyqtSignal(float) ''' End Class ''' def dataSendLoop(addData_callbackFunc): # Setup the signal-slot mechanism. mySrc = Communicate() mySrc.data_signal.connect(addData_callbackFunc) # Simulate some data n = np.linspace(0, 499, 500) y = 50 + 25*(np.sin(n / 8.3)) + 10*(np.sin(n / 7.5)) - 5*(np.sin(n / 1.5)) i = 0 while(True): if(i > 499): i = 0 time.sleep(0.1) #mySrc.data_signal.emit(y[i]) # <- Here you emit a signal! mySrc.data_signal.emit(psutil.cpu_percent()) i += 1 ### ### if __name__== '__main__': app = QApplication(sys.argv) QApplication.setStyle(QStyleFactory.create('Plastique')) myGUI = CustomMainWindow() sys.exit(app.exec_())
额外提示
如果后续要添加内存监控之类的第二条曲线,只需要:
- 新增一个
self.y_memory数组来存储内存数据 - 创建新的Line2D对象:
self.line2 = Line2D([], [], color='green', label='Memory Usage') - 将其添加到坐标轴:
self.ax1.add_line(self.line2) - 在
_draw_frame方法中更新这条线的数据
这样图例就会自动显示两条曲线的标签了。
内容的提问来源于stack exchange,提问作者Matthijs990
相关产品推荐
相关产品推荐

