Why subplots are not taking all space allowed by figure in Matplotlib embedded with Tkinter?

matplotlib subplot space between rows
plt tight_layout not working
matplotlib subplot spacing
figurecanvastkagg

I want to show a lot of subplots in a Tkinter window and be able to scrolldown to see all the plots with a good size. However, it is all packed and it seems that the subplots don't take all the space allowed in my figure and only restricts to the space of the window. How can I make it more spaced out and make the subplots bigger?

I've tried different paddings with the tight_layout() option, changing the figure size and other parameters of Tkinter like the fill or expand in my widgets.

import tkinter as tk
from tkinter import Scrollbar
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

import matplotlib.pyplot as plt

class ScrollableWindow:

    def __init__(self, master, fig, **options):

        master.geometry("%dx%d+0+0" % (800, 500))
        master.focus_set()

        fig_wrapper = tk.Frame(master, width=800, height=fig.get_figheight())
        fig_wrapper.pack(fill=tk.BOTH, expand=True)

        fig_canvas = FigureCanvasTkAgg(fig, master=fig_wrapper)

        scrollbar = Scrollbar(fig_wrapper, orient=tk.VERTICAL, command=fig_canvas.get_tk_widget().yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        fig_canvas.get_tk_widget().pack(fill=tk.BOTH, side=tk.LEFT, expand=True)
        fig_canvas.get_tk_widget().config(yscrollcommand = scrollbar.set, scrollregion=fig_canvas.get_tk_widget().bbox("all"), width=800, height=1000)



n_col, n_row = 3, 11

fig, axes = plt.subplots(figsize=(n_col,n_row*2), ncols=n_col, nrows=n_row)
for i in range(axes.shape[0]):
    for j in range(axes.shape[1]):
        axes[i,j].set_xlabel("xlabel")
        axes[i,j].set_ylabel("ylabel")
fig.tight_layout()
showStatsWindow = tk.Tk()
showStatsWindow_ = ScrollableWindow(showStatsWindow, fig)
showStatsWindow.mainloop()

Here's an example with empty plots of what it looks like. I want to have 3 or 4 subplots per row, but it is all squeezed together. As you can see, I have more space down in my window and it changes with the figsize parameter but it's all blank.


Usage Guide — Matplotlib 3.3.0 documentation, However, it is all packed and it seems that the subplots don't take all the all space allowed by figure in Matplotlib embedded with Tkinter? For more advanced use cases you can use GridSpec for a more general subplot layout or Figure.add_subplot for adding subplots at arbitrary locations within the figure. # sphinx_gallery_thumbnail_number = 11 import matplotlib.pyplot as plt import numpy as np # Some example data to display x = np . linspace ( 0 , 2 * np . pi , 400 ) y = np . sin


The problem was that your scrollbar wasn't set the correct way if you want to see how to set it correctly ckeckout this Vertical scrollbar for frame in Tkinter, Python and How to get frame in canvas window to expand to the size of the canvas?

The frame where you drawing your figure wasn't expanding ....

here is how I fixed it

import tkinter as tk
from tkinter import Scrollbar
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt


class ScrollableWindow:

    def __init__(self, master, fig, **options):
        def on_configure(event):
            # update scrollregion after starting 'mainloop'
            # when all widgets are in canvas
            canvas.configure(scrollregion=canvas.bbox('all'))

            # expand canvas_frame when canvas changes its size
            canvas_width = event.width
            canvas.itemconfig(canvas_frame, width=canvas_width)


        # --- create canvas with scrollbar ---
        canvas = tk.Canvas(master, )
        canvas.pack(side=tk.LEFT, fill='both', expand=True)

        scrollbar = tk.Scrollbar(master, command=canvas.yview)
        scrollbar.pack(side=tk.RIGHT, fill='both')

        canvas.configure(yscrollcommand=scrollbar.set)

        # update scrollregion after starting 'mainloop'
        # when all widgets are in canvas
        canvas.bind('<Configure>', on_configure)

        # --- put frame in canvas ---

        fig_wrapper = tk.Frame(canvas)

        canvas_frame= canvas.create_window((0, 0), window=fig_wrapper,)

        master.geometry("%dx%d+0+0" % (800, 500))
        master.focus_set()

        fig_canvas = FigureCanvasTkAgg(fig, master=fig_wrapper)
        fig_canvas.get_tk_widget().pack(fill=tk.BOTH, side=tk.LEFT, expand=True)


n_col, n_row = 3, 11

fig, axes = plt.subplots(figsize=(n_col*2,n_row*2), ncols=n_col, nrows=n_row,)
for i in range(axes.shape[0]):
    for j in range(axes.shape[1]):
        axes[i,j].set_xlabel("xlabel")
        axes[i,j].set_ylabel("ylabel")
fig.tight_layout()
showStatsWindow = tk.Tk()
showStatsWindow_ = ScrollableWindow(showStatsWindow, fig)
showStatsWindow.mainloop()

here is the result I got

Tight Layout guide — Matplotlib 3.1.2 documentation, The most simple way of creating a figure with an axes is using pyplot.subplots . Many other plotting libraries or languages do not require you to explicitly This is what you think of as 'a plot', it is the region of the image with the data space. All of plotting functions expect numpy.array or numpy.ma.masked_array as input. But the ticklabels themselves do not share properties. This is a feature and not a bug, because you may want to make the tick labels smaller on the upper axes, e.g., in the example below. If you want to turn off the ticklabels for a given axes (e.g., on subplot(211) or subplot(212), you cannot do the standard trick:


more optional customization if you need call this function by tkinter grid.

import tkinter as tk
from tkinter import *
from tkinter import Scrollbar
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg


class ScrollableTkAggY(FigureCanvasTkAgg):
    def __init__(self, figure, master, *args, **kwargs):
        # --- create canvas with scrollbar ---
        self.canvas = tk.Canvas(master)
        self.canvas.grid(row=0, column=0, sticky=tk.NSEW)
        self.canvas.rowconfigure(0, weight=1)
        self.canvas.columnconfigure(0, weight=1)

        self.fig_wrapper = tk.Frame(self.canvas)
        self.fig_wrapper.grid(row=0, column=0, sticky=tk.NSEW)
        self.fig_wrapper.rowconfigure(0, weight=1)
        self.fig_wrapper.columnconfigure(0, weight=1)

        super(ScrollableTkAggY, self).__init__(figure, master=self.fig_wrapper, *args, **kwargs)
        self.tkagg = self.get_tk_widget()
        self.tkagg.grid(row=0, column=0, sticky=tk.NSEW)

        self.vbar = Scrollbar(self.canvas, orient=tk.VERTICAL, command=self.canvas.yview)
        self.vbar.grid(row=0, column=1, sticky=tk.NS)

        self.canvas.configure(yscrollcommand=self.vbar.set, scrollregion=self.canvas.bbox(tk.ALL))

        # when all widgets are in canvas
        self.canvas.bind('<Configure>', self.on_configure)
        # --- put frame in canvas ---
        self.canvas_frame = self.canvas.create_window((0, 0), window=self.fig_wrapper, anchor=tk.NW)

        ScrollableTkAggY_meths = vars(ScrollableTkAggY).keys()
        methods = vars(Pack).keys() | vars(Grid).keys() | vars(Place).keys()
        methods = methods.difference(ScrollableTkAggY_meths)

        for m in methods:
            if m[0] != '_' and m != 'config' and m != 'configure':
                setattr(self, m, getattr(self.canvas, m))

    def __str__(self):
        return str(self.canvas)

    # expand canvas_frame when canvas changes its size
    def on_configure(self, event):
        # update scrollregion after starting 'mainloop'

        self.canvas.configure(scrollregion=self.canvas.bbox(tk.ALL))
        # when all widgets are in canvas
        canvas_width = event.width
        self.canvas.itemconfig(self.canvas_frame, width=canvas_width - 20)


class ScrollableTkAggX(FigureCanvasTkAgg):
    def __init__(self, figure, master, *args, **kwargs):
        # --- create canvas with scrollbar ---
        self.canvas = tk.Canvas(master)
        self.canvas.grid(row=0, column=0, sticky=tk.NSEW)
        self.canvas.rowconfigure(0, weight=1)
        self.canvas.columnconfigure(0, weight=1)

        self.fig_wrapper = tk.Frame(self.canvas)
        self.fig_wrapper.grid(row=0, column=0, sticky=tk.NSEW)
        self.fig_wrapper.rowconfigure(0, weight=1)
        self.fig_wrapper.columnconfigure(0, weight=1)

        super(ScrollableTkAggX, self).__init__(figure, master=self.fig_wrapper, *args, **kwargs)
        self.tkagg = self.get_tk_widget()
        self.tkagg.grid(row=0, column=0, sticky=tk.NSEW)

        self.hbar = Scrollbar(self.canvas, orient=tk.HORIZONTAL, command=self.canvas.xview)
        self.hbar.grid(row=1, column=0, sticky=tk.EW)

        self.canvas.configure(xscrollcommand=self.hbar.set, scrollregion=self.canvas.bbox(tk.ALL))

        # when all widgets are in canvas
        self.canvas.bind('<Configure>', self.on_configure)
        # --- put frame in canvas ---
        self.canvas_frame = self.canvas.create_window((0, 0), window=self.fig_wrapper, anchor=tk.NW)

        ScrollableTkAggX_meths = vars(ScrollableTkAggX).keys()
        methods = vars(Pack).keys() | vars(Grid).keys() | vars(Place).keys()
        methods = methods.difference(ScrollableTkAggX_meths)

        for m in methods:
            if m[0] != '_' and m != 'config' and m != 'configure':
                setattr(self, m, getattr(self.canvas, m))

    def __str__(self):
        return str(self.canvas)

        # expand canvas_frame when canvas changes its size

    def on_configure(self, event):
        # update scrollregion after starting 'mainloop'

        self.canvas.configure(scrollregion=self.canvas.bbox(tk.ALL))
        # when all widgets are in canvas
        canvas_height = event.height
        self.canvas.itemconfig(self.canvas_frame, height=canvas_height - 20)


class mclass:
    def __init__(self, window):
        self.figY = Figure(figsize=(10, 30))
        self.axesY = self.figY.subplots(ncols=3, nrows=10)

        self.figX = Figure(figsize=(30, 10))
        self.axesX = self.figX.subplots(ncols=10, nrows=3)

        self.canvasY = ScrollableTkAggY(figure=self.figY, master=window)
        self.canvasY.grid(row=0, column=0, sticky='nsew')
        self.canvasY.rowconfigure(0, weight=1)
        self.canvasY.columnconfigure(0, weight=1)

        self.canvasX = ScrollableTkAggX(figure=self.figX, master=window)
        self.canvasX.grid(row=1, column=0, sticky='nsew')
        self.canvasX.rowconfigure(0, weight=1)
        self.canvasX.columnconfigure(0, weight=1)

        window.geometry("%dx%d+0+0" % (800, 600))
        window.focus_set()

        self.do_plot()

    def do_plot(self):
        # Color used in mpl online documentation.
        mpl_grey_rvb = (51. / 255., 51. / 255., 51. / 255.)
        self.figY.suptitle("Matplotlib's math rendering engine ScrollableTkAggY",
                           color=mpl_grey_rvb, fontsize=14, weight='bold')

        self.figX.suptitle("Matplotlib's math rendering engine ScrollableTkAggX",
                           color=mpl_grey_rvb, fontsize=14, weight='bold')

        # Plotting features demonstration formulae

        for i in range(self.axesY.shape[0]):
            for j in range(self.axesY.shape[1]):
                self.axesY[i, j].set_xlabel("xlabel")
                self.axesY[i, j].set_ylabel("ylabel")
        self.figY.tight_layout()
        self.canvasY.draw()

        for i in range(self.axesX.shape[0]):
            for j in range(self.axesX.shape[1]):
                self.axesX[i, j].set_xlabel("xlabel")
                self.axesX[i, j].set_ylabel("ylabel")
        self.figX.tight_layout()
        self.canvasX.draw()


if __name__ == '__main__':
    window = Tk()
    start = mclass(window)
    window.rowconfigure(0, weight=1)
    window.rowconfigure(1, weight=1)
    window.columnconfigure(0, weight=1)
    # window.columnconfigure(1, weight=1)
    window.mainloop()

and this the result. enter image description here

[PDF] Here, How to use tight-layout to fit plots within your figure cleanly. This is an experimental feature and may not work for some cases. ax.set_title('Title', fontsize=fontsize) plt.close('all') fig, ax = plt.subplots() example_plot(ax, fontsize= 24) tight_layout() can take keyword arguments of pad, w_pad and h_pad. matplotlib.pyplot.subplots¶ matplotlib.pyplot.subplots (nrows=1, ncols=1, sharex=False, sharey=False, squeeze=True, subplot_kw=None, gridspec_kw=None, **fig_kw) [source] ¶ Create a figure and a set of subplots. This utility wrapper makes it convenient to create common layouts of subplots, including the enclosing figure object, in a single call.


Embedding in Tk — Matplotlib 3.2.1 documentation, If not using pkg-config (in particular on Windows), you may need to set the include path (to the fig, ax_lst = plt.subplots(2, 2) # a figure with a 2x2 grid of Axes All of plotting functions expect np.array or np.ma.masked_array as input. Adjust the subplot layout, because the logit one may take more space. In addition to the above described arguments, this function can take a data keyword argument. If such a data argument is given, the following arguments are replaced by data[<arg>]: All positional and all keyword arguments. Objects passed as data must support item access (data[<arg>]) and membership test (<arg> in data).


python, import tkinter from matplotlib.backends.backend_tkagg import tkinter.Tk() root. wm_title("Embedding in Tk") fig = Figure(figsize=(5, 4), dpi=100) t Widgets are processed sequentially and if there # is no space left, because the window is too small, they are not displayed. Created using Sphinx 3.0.0. pyplot.subplots creates a figure and a grid of subplots with a single call, while providing reasonable control over how the individual plots are created. For more advanced use cases you can use GridSpec for a more general subplot layout or Figure.add_subplot for adding subplots at arbitrary locations within the figure.


[PDF] Matplotlib, Why subplots are not taking all space allowed by figure in Matplotlib embedded with Tkinter? 发表于 2019-09-13 19:46:54. 活跃于 2019-11-19 13:07:36. subplot(m,n,p) divides the current figure into an m-by-n grid and creates axes in the position specified by p.MATLAB ® numbers subplot positions by row. The first subplot is the first column of the first row, the second subplot is the second column of the first row, and so on.