Search code examples
python-3.xmatplotlibtkinter

Python: Using Figure.set_size_inches on a Figure contained in a tkinter GUI does not update the display


I am trying to embed a pyplot Figure object in a tkinter GUI and then later on change its size by calling the set_size_inches() method.

A miniumum (not) working example is provided below. Interestingly, the debug-prints of the Figure and Canvas sizes indicate, that resizing acutally worked. However, the visible size in the GUI stays the same. I hence believe the issue lies somewhere in updating the respective widgets and/or GUI after resizing, but I could not find any solution to change the display of the Figure to its new size.

import tkinter as tk
from tkinter import Frame

from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg


class Gui(tk.Tk):

    def __init__(self):

        # initialize tkinter
        super().__init__()

        # enable fullscreen mode
        self.state('zoomed')

        # create frame
        graph_frame = Frame(self)

        # create graph
        fig = Figure(figsize=(2, 4), frameon=False, tight_layout=True)
        ax = fig.add_subplot(111)
        canvas = FigureCanvasTkAgg(fig, master=graph_frame)

        # place frame
        graph_frame.grid(row=8, column=1, columnspan=8)
        graph_frame.columnconfigure(0, weight=1)
        graph_frame.columnconfigure(1, weight=1)
        graph_frame.columnconfigure(2, weight=1)
        graph_frame.rowconfigure(0, weight=1)
        graph_frame.rowconfigure(1, weight=1)
        graph_frame.rowconfigure(2, weight=2)

        # place canvas in grid
        canvas_widget = canvas.get_tk_widget()
        canvas_widget.grid(row=0, column=0, sticky=tk.NSEW)
        canvas_widget.update_idletasks()

        # enabling this makes it two graphs:
        # canvas_widget.update()

        # before info
        print('Before resize in inches:')
        print(fig.get_size_inches())
        print(canvas.get_width_height())

        # resize graph
        fig.set_size_inches(1, 1)

        # after info
        print('After resize in inches:')
        print(fig.get_size_inches())
        print(canvas.get_width_height())

        # tried any combination and sequence of these, but none work:
        canvas.draw()
        canvas.get_tk_widget().update()
        canvas.draw_idle()
        canvas.flush_events()
        graph_frame.update()
        self.update()


if __name__ == '__main__':
    # run GUI
    gui = Gui()
    gui.mainloop()

Results:

Without the additional canvas_widget.update() call:

With the additional canvas_widget.update() call:

I tried updating and flushing each and every possible widget and root in existance, but none worked.

Happy for any hint about what I am doing wrong or how I am misusing stuff :-)


Solution

  • You can change the canvas size according to the desired figsize:

    fig.set_size_inches(1, 1, forward = True)
    canvas.get_tk_widget().configure(width=fig.get_figwidth()*100, height=fig.get_figheight()*100)
    

    Here the whole code. I implemented a BUtton for testing and put the resizing into a function which is called by the button:

    import tkinter as tk
    from tkinter import Frame
    
    from matplotlib.figure import Figure
    import matplotlib.pyplot as plt
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
    
    
    class Gui(tk.Tk):
    
        def __init__(self):
    
            # initialize tkinter
            super().__init__()
    
            # enable fullscreen mode
    
    
            # create frame
            self.graph_frame = Frame(self)
            self.graph_frame.grid(row=0, column=0)
    
    
            # create graph
            fig = Figure(figsize=(2, 4), frameon=False, tight_layout=True)
            ax = fig.add_subplot(111)
            canvas = FigureCanvasTkAgg(fig, master=self.graph_frame)
    
            # place frame
            
    
            # place canvas in grid
            canvas_widget = canvas.get_tk_widget()
            canvas_widget.grid(row=0, column=0, sticky=tk.NSEW)
            canvas_widget.update_idletasks()
    
            # enabling this makes it two graphs:
            # canvas_widget.update()
    
            # before info
            print('Before resize in inches:')
            print(fig.get_size_inches())
            print(canvas.get_width_height())
            
            # resize graph
            print(fig.get_figwidth(), fig.get_figheight())
            btn = tk.Button(self, text='Test', command=lambda : self.resize(fig, canvas))
            btn.grid(row = 1, column= 0)
    
            # tried any combination and sequence of these, but none work:
            
            
        def resize(self, fig, canvas):
            fig.set_size_inches(1, 1, forward = True)
            canvas.get_tk_widget().configure(width=fig.get_figwidth()*100, height=fig.get_figheight()*100)
            # after info
            print('After resize in inches:')
            print(fig.get_size_inches())
            print(canvas.get_width_height())
            canvas.co
            canvas.draw()
            canvas.get_tk_widget().update()
            canvas.draw_idle()
            canvas.flush_events()
            self.graph_frame.update()
            self.update()
    
    
    
    if __name__ == '__main__':
        # run GUI
        gui = Gui()
        gui.mainloop()