Search code examples
pythonqtpython-3.xmatplotlibpyqt5

How to change default filename from Matplotlib NavigationToolbar in a PyQt5 application?


I am writing an application in Python 3.5 & PyQT 5. In this application I have an embedded matplotlib canvas and an embedded matplotlib NavigationToolbar. The application measures some data and then plots them. The user at some point wants to export the plot and save it into a ".png" file somewhere and then measure something new.

For this, I have been using the "floppy"/"save" icon on the NavigationToolbar and it works well. However, when I click it, it opens a dialog at the root folder of the program (or last opened folder) and pre-fills the name of the saved image as "image.png".

I would like to change this pre-filled default name to something custom to my program, such as "measurement_7453243.png" or "2017_01_16_measurement.png". Is there a way that would allow me to do this?

Idea #1: What I found is the option to use matplotlib.rcParams['savefig.directory'] and use that to set the directory of where the dialog should be opened - not the filename though. I cannot seem to find a 'savefig.XXXX' property that would change the default filename.

Idea #2: I could make a custom button in my app outside of the NavigationToolbar, which would run a custom file saving dialog with Qt and then use savefig() to save the figure. But why do that if there is already a nice button that does ALMOST what I want?

Any tips and ideas, as well as "this cannot be done" with proper sources will be greatly appreciated.


Solution

  • The default file name is provided by the method FigureCanvasBase.get_default_filename. You can just monkey patch it after the canvas has been created:

    canvas = FigureCanvas(fig)
    canvas.get_default_filename = lambda: 'new_default_name.png'
    

    I guess the proper way is subclassing FigureCanvas though, override the method and maybe provide an API to set the default file name.

    Here is an example for Qt5Agg using monkey patching, adapted from the matplotlib docs:

    from PyQt5.QtWidgets import (
        QMainWindow, QApplication, QWidget, QVBoxLayout)
    
    from matplotlib.figure import Figure
    from matplotlib.backends.backend_qt5agg import (
        FigureCanvasQTAgg as FigureCanvas,
        NavigationToolbar2QT as NavigationToolbar)
    
    
    class AppForm(QMainWindow):
        def __init__(self, parent=None):
            QMainWindow.__init__(self, parent)
            main_frame = QWidget()
    
            fig = Figure((5.0, 4.0), dpi=100)
            ax = fig.add_subplot(111)
            ax.plot([1, 2, 4, 8])
    
            canvas = FigureCanvas(fig)
            canvas.get_default_filename = lambda: 'new_default_name.png'
            mpl_toolbar = NavigationToolbar(canvas, main_frame)
    
            vbox = QVBoxLayout()
            vbox.addWidget(canvas)  # the matplotlib canvas
            vbox.addWidget(mpl_toolbar)
            main_frame.setLayout(vbox)
            self.setCentralWidget(main_frame)
    
    
    app = QApplication(['Test'])
    form = AppForm()
    form.show()
    app.exec_()