python – How to customize matplotlib canvas navbar options?

I am using the matplotlib canvas in PyQt5 by creating a MplWidget like this:

from PyQt5.QtWidgets import QMenu, QWidget, QVBoxLayout
from matplotlib.backends.backend_qt5agg import (FigureCanvasQTAgg as
        FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure


class MplWidget(QWidget):

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        self.canvas = FigureCanvas(Figure())
        vertical_layout = QVBoxLayout()
        vertical_layout.addWidget(self.canvas)
        self.params = plot_parameters()

        self.canvas.axes = self.canvas.figure.add_subplot(111)
        self.setLayout(vertical_layout)
        self.canvas.toolbar = NavigationToolbar(self.canvas, self)
        self.layout().addWidget(self.canvas.toolbar)
        self.layout().addWidget(self.canvas)
        self.canvas.axes.grid(b=True, which="both", axis="both")
        self.canvas.figure.set_tight_layout(True)

    def plot_data(self, t, x, hold=False, legends=None):
        if not hold:
            self.canvas.axes.clear()
        self.canvas.axes.plot(t, x, label=legends)
        self.canvas.axes.grid(b=True, which="both", axis="both")
        self.canvas.axes.set_xlabel(self.params.xlabel, fontsize=10)
        self.canvas.axes.set_ylabel(self.params.ylabel, fontsize=10)
        self.canvas.axes.set_title(self.params.title, fontsize=10)
        handles, labels = self.canvas.axes.get_legend_handles_labels()
        self.canvas.axes.legend(handles, labels)
        self.canvas.draw()

    def set_xlabel(self, label):
        self.params.xlabel = label

    def set_ylabel(self, label):
        self.params.ylabel = label

    def set_title(self, title):
        self.params.title = title


class plot_parameters:
    def __init__(self):
        self.xlabel = ""
        self.ylabel = ""
        self.title = ""

I know I can add new options or remove them (for example: embed matplotlib with custom Navigation toolbar actions in the Mainwindow toolbar), but would it be possible to modify the existing? For example here:

I would like to add/remove some scaling options for both axes. I also would want to tick the (Re-)Generate automatic legend option by default.

Is there a way to do this? I include a runnable example on the off chance someone want to see the an exact instance of the use of MplWidget.

dummy example to run:

main.py:

import random
from typing import Optional
import numpy as np
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QMainWindow
from simple_navi import Ui_MainWindow


class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)


class MainPresenter:
    def __init__(self, view: MainWindow, model: Optional[int] = None):
        self.view = view
        self.model = model

        self.view.show()

        self.view.sel.currentIndexChanged.connect(self.plot_type_change)
        self.view.amp.valueChanged.connect(self.amp_changed)
        self.view.freq.valueChanged.connect(self.freq_changed)

    def plot_type_change(self, idx):
        freq = self.view.freq.value()
        amp = self.view.amp.value()
        self.plot_new(idx, freq, amp)

    def amp_changed(self, amp):
        freq = self.view.freq.value()
        idx = self.view.sel.currentIndex()
        self.plot_new(idx, freq, amp)

    def freq_changed(self, freq):
        amp = self.view.amp.value()
        idx = self.view.sel.currentIndex()
        self.plot_new(idx, freq, amp)

    def plot_new(self, idx, freq, amp, hold=False):
        t = np.arange(0, 2 * np.pi, 0.001)
        self.view.plot_area.set_xlabel("Time")
        self.view.plot_area.set_title("Different phase")
        if idx == 0:
            fg = np.sin
            self.view.plot_area.set_ylabel("Sin values")
        elif idx == 1:
            fg = np.cos
            self.view.plot_area.set_ylabel("Cos values")
        elif idx == 2:
            fg = np.exp
            self.view.plot_area.set_ylabel("Exp values")
        else:
            t = 0
            fg = np.sin
        x, legends = self.add_lines(fg, t, amp, freq)
        self.view.plot_area.plot_data(t, x, hold=hold, legends=legends)

    @staticmethod
    def add_lines(fg, t, amp, freq):
        x = np.empty((4, t.shape[0]))
        legends = []
        for i in range(4):
            phi = random.uniform(0.1, np.pi * 2 - 0.1)
            x[i, :] = amp * fg(t * freq + phi)
            legends.append(str(phi))
        return np.transpose(x), legends


if __name__ == '__main__':
    # Create the application (with optional system arguments)
    app = QApplication(sys.argv)

    # Create model, view and presenter objects
    model = None
    view = MainWindow()
    presenter = MainPresenter(view=view, model=model)

    # Start the main app event loop
    exit_code = app.exec_()

    # Perform system exit to safely quit (relay the app exit code)
    sys.exit(exit_code)

simple_navi.py:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'simple_navi.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.setObjectName("gridLayout")
        self.plot_area = MplWidget(self.centralwidget)
        self.plot_area.setObjectName("plot_area")
        self.gridLayout.addWidget(self.plot_area, 1, 0, 1, 7)
        self.sel = QtWidgets.QComboBox(self.centralwidget)
        self.sel.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.sel.setInsertPolicy(QtWidgets.QComboBox.InsertAtBottom)
        self.sel.setObjectName("sel")
        self.sel.addItem("")
        self.sel.addItem("")
        self.sel.addItem("")
        self.gridLayout.addWidget(self.sel, 0, 0, 1, 7)
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setObjectName("label_2")
        self.gridLayout.addWidget(self.label_2, 2, 4, 1, 1)
        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.gridLayout.addItem(spacerItem, 2, 0, 1, 1)
        spacerItem1 = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum)
        self.gridLayout.addItem(spacerItem1, 2, 3, 1, 1)
        self.freq = QtWidgets.QSpinBox(self.centralwidget)
        self.freq.setMinimum(1)
        self.freq.setMaximum(20)
        self.freq.setObjectName("freq")
        self.gridLayout.addWidget(self.freq, 2, 5, 1, 1)
        spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.gridLayout.addItem(spacerItem2, 2, 6, 1, 1)
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setObjectName("label")
        self.gridLayout.addWidget(self.label, 2, 1, 1, 1)
        self.amp = QtWidgets.QSpinBox(self.centralwidget)
        self.amp.setMinimum(1)
        self.amp.setMaximum(50)
        self.amp.setObjectName("amp")
        self.gridLayout.addWidget(self.amp, 2, 2, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.sel.setItemText(0, _translate("MainWindow", "Sin"))
        self.sel.setItemText(1, _translate("MainWindow", "Cos"))
        self.sel.setItemText(2, _translate("MainWindow", "Exp"))
        self.label_2.setText(_translate("MainWindow", "Frequency: "))
        self.label.setText(_translate("MainWindow", "Amplitude: "))
from mpl_widget import MplWidget

Leave a Comment