Matplotlib twinx第二Y轴tick_params多次点击后失效问题求助
PyQt5双Y轴Matplotlib图表重复点击后第二Y轴刻度异常问题解决
问题描述
我正在开发一个PyQt5 GUI程序,点击按钮会更新界面内的Matplotlib双Y轴图表(共享X轴,通过twinx()实现)。首次点击后双Y轴显示正常,但重复点击按钮后,第二Y轴出现异常:刻度范围变为(0,1),且刻度值重叠导致字体变粗。尝试用ax.cla()和ax.clf()清除轴无效。
原代码
import sys import matplotlib.pyplot as plt import numpy as np from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure from numpy import * from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(650, 400) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.pushButton_15 = QtWidgets.QPushButton(self.centralwidget) self.pushButton_15.setGeometry(QtCore.QRect(430, 50, 191, 23)) self.pushButton_15.setObjectName("pushButton_15") self.pushButton_15.clicked.connect(self.PlotOnCanvas) self.frame = QtWidgets.QFrame(self.centralwidget) self.frame.setGeometry(QtCore.QRect(20, 25, 421, 290)) self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) self.frame.setFrameShadow(QtWidgets.QFrame.Raised) self.frame.setObjectName("frame") self.frame_2 = QtWidgets.QFrame(self.frame) self.frame_2.setGeometry(QtCore.QRect(9, 10, 401, 271)) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.frame_2.sizePolicy().hasHeightForWidth()) self.frame_2.setSizePolicy(sizePolicy) self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel) self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised) self.frame_2.setObjectName("frame_2") self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.frame_2) self.horizontalLayout_2.setObjectName("HorizontalLayout_2") self.figure, self.ax = plt.subplots() self.canvas = FigureCanvas(self.figure) self.horizontalLayout_2.addWidget(self.canvas) MainWindow.setCentralWidget(self.centralwidget) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) self.pushButton_15.setText(_translate("MainWindow", "test")) def PlotOnCanvas(self): self.ax.clear() x = np.linspace(0, 4 * pi, 1000) y1 = np.sin(2 * pi * np.random.rand() * 2 * x) y2 = np.sin(2 * pi * np.random.rand() * 2 * x) colory2 = 'tab:red' colory1 = 'tab:blue' ax1 = self.ax ax2 = ax1.twinx() ax1.set_xlabel('x axis', fontsize=8) ax1.xaxis.set_tick_params(labelsize=8) ax1.set_ylabel('y1 axis', color=colory1, fontsize=8) ax2.set_ylabel('y2 axis', color=colory2, fontsize=8) ax1.yaxis.set_tick_params(labelcolor=colory1, labelsize=8) ax2.yaxis.set_tick_params(labelcolor=colory2, labelsize=8) ax1.set_yticks([-1, -0.5, 0, 0.5, 1]) ax2.set_yticks([-1, -0.5, 0, 0.5, 1]) ax1.set_ylim(-1.2, 1.2) ax2.set_ylim(-1.2, 1.2) l1, = ax1.plot(x, y1, lw=2, color=colory1) l2, = ax2.plot(x, y2, lw=2, color=colory2) plt.legend([l1, l2], ['y1', 'y2'], fontsize=8, loc="upper right") plt.title('y1/y2', fontsize=8) plt.tight_layout() self.canvas.draw() ax2.clear() if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) MainWindow = QtWidgets.QMainWindow() ui = Ui_MainWindow() ui.setupUi(MainWindow) MainWindow.show() sys.exit(app.exec_())
问题现象
- 首次点击后效果:

- 多次点击后效果:

问题根源
每次点击按钮时,代码都会调用ax1.twinx()创建一个新的第二Y轴,旧的轴并未被销毁,而是保留在图表中。多次点击后,多个第二Y轴叠加,导致刻度重叠显示异常。同时,代码末尾的ax2.clear()会在绘制完成后立即清除当前轴内容,仅因为canvas.draw()已完成渲染才让首次点击看起来正常,但后续新创建的轴会与旧轴持续叠加。
修复方案
关键修改点
- 初始化时仅创建一次双轴:在
setupUi中创建ax2,避免每次点击重复创建新轴。 - 点击时清除已有轴内容:每次更新图表时,同时清除
ax1和ax2的内容,而非重新创建轴。 - 移除绘制后的轴清除操作:删除末尾的
ax2.clear(),避免刚绘制的内容被清除。 - 使用Figure对象调用方法:替换全局的
plt.*调用为self.figure.*,避免全局状态干扰。
修改后的代码
import sys import matplotlib.pyplot as plt import numpy as np from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure from numpy import * from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(650, 400) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.pushButton_15 = QtWidgets.QPushButton(self.centralwidget) self.pushButton_15.setGeometry(QtCore.QRect(430, 50, 191, 23)) self.pushButton_15.setObjectName("pushButton_15") self.pushButton_15.clicked.connect(self.PlotOnCanvas) self.frame = QtWidgets.QFrame(self.centralwidget) self.frame.setGeometry(QtCore.QRect(20, 25, 421, 290)) self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) self.frame.setFrameShadow(QtWidgets.QFrame.Raised) self.frame.setObjectName("frame") self.frame_2 = QtWidgets.QFrame(self.frame) self.frame_2.setGeometry(QtCore.QRect(9, 10, 401, 271)) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.frame_2.sizePolicy().hasHeightForWidth()) self.frame_2.setSizePolicy(sizePolicy) self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel) self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised) self.frame_2.setObjectName("frame_2") self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.frame_2) self.horizontalLayout_2.setObjectName("HorizontalLayout_2") self.figure, self.ax = plt.subplots() self.ax2 = self.ax.twinx() # 初始化时创建一次双轴 self.canvas = FigureCanvas(self.figure) self.horizontalLayout_2.addWidget(self.canvas) MainWindow.setCentralWidget(self.centralwidget) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) self.pushButton_15.setText(_translate("MainWindow", "test")) def PlotOnCanvas(self): self.ax.clear() self.ax2.clear() # 清除双轴已有内容 x = np.linspace(0, 4 * pi, 1000) y1 = np.sin(2 * pi * np.random.rand() * 2 * x) y2 = np.sin(2 * pi * np.random.rand() * 2 * x) colory2 = 'tab:red' colory1 = 'tab:blue' ax1 = self.ax ax2 = self.ax2 # 使用已创建的双轴 ax1.set_xlabel('x axis', fontsize=8) ax1.xaxis.set_tick_params(labelsize=8) ax1.set_ylabel('y1 axis', color=colory1, fontsize=8) ax2.set_ylabel('y2 axis', color=colory2, fontsize=8) ax1.yaxis.set_tick_params(labelcolor=colory1, labelsize=8) ax2.yaxis.set_tick_params(labelcolor=colory2, labelsize=8) ax1.set_yticks([-1, -0.5, 0, 0.5, 1]) ax2.set_yticks([-1, -0.5, 0, 0.5, 1]) ax1.set_ylim(-1.2, 1.2) ax2.set_ylim(-1.2, 1.2) l1, = ax1.plot(x, y1, lw=2, color=colory1) l2, = ax2.plot(x, y2, lw=2, color=colory2) # 使用figure对象创建图例,避免全局plt干扰 self.figure.legend([l1, l2], ['y1', 'y2'], fontsize=8, loc="upper right") self.figure.suptitle('y1/y2', fontsize=8) self.figure.tight_layout() self.canvas.draw() if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) MainWindow = QtWidgets.QMainWindow() ui = Ui_MainWindow() ui.setupUi(MainWindow) MainWindow.show() sys.exit(app.exec_())
内容的提问来源于stack exchange,提问作者Rex
相关产品推荐
相关产品推荐

