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.
We can use the plt. subplots_adjust() method to change the space between Matplotlib subplots. The parameters wspace and hspace specify the space reserved between Matplotlib subplots. They are the fractions of axis width and height, respectively.
Often you may use subplots to display multiple plots alongside each other in Matplotlib. Unfortunately, these subplots tend to overlap each other by default. The easiest way to resolve this issue is by using the Matplotlib tight_layout() function.
tight_layout automatically adjusts subplot params so that the subplot(s) fits in to the figure area.
Let me first start with the size of tkinter-Window
which is showing matplotlib figure e.g subplots
. Actually, it the fixed window size master.geometry("%dx%d+0+0" % (800, 500))
which is bothering you.
When you simply use tkinter window, It will automatically
resize itself to the figure size. And inversely, if it is resized, the figure will resize as well. There is simple workaround for the above mentioned problem.
Replace the following line :
master.geometry("%dx%d+0+0" % (800, 500))
with:
master.resizable(width=False, height=False)
To provent tkinter window from being resized .
Now to make your size of subplots bigger or smaller, you can change it by changing the figsize=()
parameter accordingly.
fig, axes = plt.subplots(ncols=n_col, nrows=n_row, figsize=(7.5, 25))
I'll must refer you to use PyQT5
, which is more feasible in your given scenario. A working example is given below, where instead of calling tkinter
custom operations such as pack()
and config()
etc.
Qt5
puts the figure into a canvas with scrollbars, such that the figure retains it's original size and can be scrolled within the Qt window. You wouldn't have to deal with the details inside the class but only the call at the end of the script.
Visit reference: Developing GUIs in Python: Tkinter vs PyQt.
import matplotlib
from PyQt5 import QtWidgets
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
matplotlib.use('Qt5Agg')
matplotlib.use('TkAgg')
class ScrollableWindow(QtWidgets.QMainWindow):
def __init__(self, fig):
self.q_app = QtWidgets.QApplication([])
QtWidgets.QMainWindow.__init__(self)
# To set this size of the Display Window
self.setFixedSize(800, 500)
self.widget = QtWidgets.QWidget()
self.setCentralWidget(self.widget)
self.widget.setLayout(QtWidgets.QVBoxLayout())
self.widget.layout().setContentsMargins(0, 0, 0, 0)
self.widget.layout().setSpacing(10)
self.fig = fig
self.canvas = FigureCanvas(self.fig)
self.canvas.draw()
self.scroll = QtWidgets.QScrollArea(self.widget)
self.scroll.setWidget(self.canvas)
self.nav = NavigationToolbar(self.canvas, self.widget)
self.widget.layout().addWidget(self.nav)
self.widget.layout().addWidget(self.scroll)
self.show()
exit(self.q_app.exec_())
fig, axes = plt.subplots(ncols=3, nrows=11, figsize=(7.5, 25))
for i in range(axes.shape[0]):
for j in range(axes.shape[1]):
axes[i, j].set_xlabel("x-label")
axes[i, j].set_ylabel("y-label")
fig.tight_layout()
ScrollableWindow(fig)
Here is a result using PyQT5
:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With