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

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()已完成渲染才让首次点击看起来正常,但后续新创建的轴会与旧轴持续叠加。

修复方案

关键修改点

  1. 初始化时仅创建一次双轴:在setupUi中创建ax2,避免每次点击重复创建新轴。
  2. 点击时清除已有轴内容:每次更新图表时,同时清除ax1和ax2的内容,而非重新创建轴。
  3. 移除绘制后的轴清除操作:删除末尾的ax2.clear(),避免刚绘制的内容被清除。
  4. 使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 21:24:57