tick_params doesn't work properly for 2nd y-axis

128 Views Asked by At

I am designing a GUI in which whenever I click a button, the figure inside the GUI will be updated. This figure contains two y-axes with a common x-axis. I am using twinx function inside matplotlib. Everything is working as expected, except the ytick of the 2nd y-axis.

Here is my code:

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_())

and here is the output:

After 1st click

After 1st click

After multiple clicks

After multiple clicks

After 1st click, I can get both axis in the format that I want. However, after the 2nd click, the 2nd axis start to behave strangely. Its range changes from (-1,1) to (0,1); in addition, its ticks (0,0.2, ..., 1) starts to print on top of each other. As a result, its font becomes thicker.

I tried clearing the axis and both axes using ax.cla() and ax.clf() but it didn't work.

1

There are 1 best solutions below

1
jared On BEST ANSWER

It looks like you keep making a new twinx axis every click. In setupUi function, I'd change self.figure, self.ax = plt.subplots() to self.figure, self.ax1 = plt.subplots() and then add a line afterwards that is self.ax2 = self.ax1.twinx(). This will only use twinx once in the setup.

Now that you have those two axes defined, you can change, your PlotOnCanvas function to only reference those two axes. To do this, you can create convenience variables ax1 = self.ax1 and ax2 = self.ax2, removing ax2 = ax1.twinx(), which was dealt with in the other function. You can then call ax1.clear() and ax2.clear() before running the rest of the function. With those changes, I think it should now update properly because it is referencing the proper axes and not adding a new twinx axis every click.

To address the twinx's y-axis label moving to the wrong side, add ax2.yaxis.set_label_position("right") to the PlotOnCanvas function before calling ax2.set_ylabel().

Putting it all together, we have

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        # everything is the same until the plot creation
        # use twinx in the setup and reference the same ax2 afterward
        self.figure, self.ax1 = plt.subplots()
        self.ax2 = self.ax1.twinx()
        
        # everything else in this function is the same


    def PlotOnCanvas(self):
        # use the saved ax1 and ax2
        ax1 = self.ax1
        ax2 = self.ax2
        ax1.clear()
        ax2.clear()
        
        # other code until the axis labels
        # add this line before setting the y-label
        ax2.yaxis.set_label_position("right")
        ax2.set_ylabel('y2 axis', color=colory2, fontsize=8)

        # everything else unchanged