Search code examples
pythonmatplotlibtkinter

Vertically scroll multiple plots in Tkinter


I would like to draw multiple plots with Matplotlib, add them to a Tkinter frame (stack them one below the other), and be able to scroll vertically between the plots. Each plot shold fill the x-direction, and have a minimum y-height, so that if the total height of the multiple plots is above the height of the window, the scroll should be enabled.

I have found a way to scroll a single large figure (made of multiple subplots), but I am constrained to use a library which cannot produce subplots, so my idea was to stack the single plots one below the other, considering them as Tkinter widgets.

So far, I have the following, but as you can see the scrollbar is present but doesn't work. Any insight would be useful.

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

from matplotlib.figure import Figure

f = Figure()
a = f.add_subplot(111)

g = Figure()
b = g.add_subplot(111)

from tkinter import *
root=Tk()
frame=Frame(root)
frame.pack(expand=True, fill=BOTH)
canvas=Canvas(frame,bg='#FFFFFF',scrollregion=(0,0,500,500))
vbar=Scrollbar(frame,orient=VERTICAL)
vbar.pack(side=RIGHT,fill=Y)
vbar.config(command=canvas.yview)
canvas.config()
canvas.config(yscrollcommand=vbar.set)
canvas.pack(side=LEFT,expand=True,fill=BOTH)

middle = Frame(canvas, bg="yellow")
middle.pack(side="bottom", expand=True, fill="both")

canvas_1 = FigureCanvasTkAgg(f, middle)
canvas_1.get_tk_widget().pack(expand=True, fill="both")
canvas_1.draw()

bottom = Frame(canvas, bg="blue")
bottom.pack(side="bottom", expand=True, fill="both")

canvas_2 = FigureCanvasTkAgg(g, bottom)
canvas_2.get_tk_widget().pack(expand=True, fill="both")
canvas_2.draw()

root.mainloop()

which produces:

enter image description here

enter image description here


Solution

  • Using middle.pack() to put frame middle (same for frame bottom) will not activate the scrollbar.

    You need to put frame middle into the canvas using canvas.create_window() instead and put the two plots into frame middle (so frame bottom can be removed).

    Below is the modified code:

    from tkinter import *
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
    from matplotlib.figure import Figure
    
    f = Figure()
    a = f.add_subplot(111)
    
    g = Figure()
    b = g.add_subplot(111)
    
    root = Tk()
    
    frame = Frame(root)
    frame.pack(expand=True, fill=BOTH)
    
    canvas = Canvas(frame, bg='#FFFFFF', width=700, height=700,
                    highlightthickness=0)
    
    vbar = Scrollbar(frame, orient=VERTICAL, command=canvas.yview)
    vbar.pack(side=RIGHT, fill=Y)
    
    canvas.config(yscrollcommand=vbar.set)
    canvas.pack(side=LEFT, expand=True, fill=BOTH)
    
    middle = Frame(canvas, bg="yellow")
    # put the frame using .create_window()
    canvas.create_window(0, 0, window=middle, anchor='nw')
    
    # put the two plots into same frame "middle"
    canvas_1 = FigureCanvasTkAgg(f, middle)
    canvas_1.get_tk_widget().pack(expand=True, fill=BOTH)
    canvas_1.draw()
    
    canvas_2 = FigureCanvasTkAgg(g, middle)
    canvas_2.get_tk_widget().pack(expand=True, fill=BOTH)
    canvas_2.draw()
    
    # update canvas scrollregion option whenever the frame is resized
    middle.bind(
        '<Configure>',
        lambda e: canvas.config(scrollregion=canvas.bbox('all'))
    )
    
    root.mainloop()
    

    Result:

    enter image description here