Issue
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 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.
Solution
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
Answered By - jared
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.