Skip to content

[Bug]: TkAgg: Subplot layout fails for large initial figure sizes, works when resized smaller (inverted behavior) on Raspberry Pi OS / Matplotlib 3.10.1 #30083

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
chulomex3 opened this issue May 19, 2025 · 0 comments

Comments

@chulomex3
Copy link

Bug summary

Large initial window (1200x800): MRE fails to show all plots correctly.
Resizing MRE window smaller: MRE then shows all plots correctly.

On initial run I should get 4 graphs in a 2 x 2 grid on screen. Except instead I get 1 graph and the others off screen. When I resize the window to a much smaller window the other graphs show up as expected in the much smaller window.

Code for reproduction

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

logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")

# --- Test with these values ---
TARGET_WIDTH = 1200
TARGET_HEIGHT = 800
# --- If above fails, then try these ---
# TARGET_WIDTH = 700 
# TARGET_HEIGHT = 600
# --- ---

class MRE_App:
    def __init__(self, master):
        self.master = master
        master.title("Matplotlib Layout MRE")
        master.geometry(f"{TARGET_WIDTH}x{TARGET_HEIGHT}")

        self.fig_dpi = 96 # Fixed DPI
        
        # Option 1: Try with subplots_adjust (NO constrained_layout on Figure)
        self.fig = Figure(dpi=self.fig_dpi)
        logging.info("MRE: Using subplots_adjust approach.")

        # Option 2: Try with constrained_layout (comment out subplots_adjust in update_figure_size)
        # self.fig = Figure(dpi=self.fig_dpi, constrained_layout=True)
        # self.fig.set_constrained_layout_pads(w_pad=0.02, h_pad=0.02, wspace=0.03, hspace=0.03)
        # logging.info("MRE: Using constrained_layout approach.")


        self.ax1 = self.fig.add_subplot(221)
        self.ax2 = self.fig.add_subplot(222)
        self.ax3 = self.fig.add_subplot(223)
        self.ax4 = self.fig.add_subplot(224)

        for i, ax in enumerate([self.ax1, self.ax2, self.ax3, self.ax4]):
            ax.plot([0,1], [i, i+1]) 
            ax.set_title(f"Ax {i+1}")
            ax.grid(True)

        self.canvas = FigureCanvasTkAgg(self.fig, master=master)
        self.canvas_widget = self.canvas.get_tk_widget()
        self.canvas_widget.pack(side=tk.TOP, fill=tk.BOTH, expand=True)

        self.canvas_widget.bind("<Configure>", self.on_resize)
        
        self.master.after(300, self.initial_draw) # Slightly longer delay for MRE too
        
        logging.info(f"MRE: Matplotlib version: {matplotlib.__version__}")
        try:
            logging.info(f"MRE: Tkinter version: {tk.TkVersion}, TclVersion: {tk.TclVersion}")
        except:
            logging.info("MRE: Could not get Tk/Tcl versions easily.")


    def initial_draw(self):
        logging.info("MRE: Initial draw triggered")
        self.master.update_idletasks() 
        self.update_figure_size_and_layout() # Combined method
        # self.canvas.draw_idle() # update_figure_size_and_layout will call it

    def on_resize(self, event):
        logging.debug(f"MRE: Configure event: w={event.width}, h={event.height}")
        if event.width <= 1 or event.height <= 1: return
        self.update_figure_size_and_layout()
        
    def update_figure_size_and_layout(self):
        if not (hasattr(self, 'canvas_widget') and self.canvas_widget.winfo_exists()):
            return
            
        self.master.update_idletasks() # Ensure Tkinter sizes are accurate before query
        canvas_width_px = self.canvas_widget.winfo_width()
        canvas_height_px = self.canvas_widget.winfo_height()
        logging.debug(f"MRE: Canvas current pixel size: {canvas_width_px}x{canvas_height_px}")

        if canvas_width_px > 1 and canvas_height_px > 1:
            new_fig_width_inches = canvas_width_px / self.fig.get_dpi()
            new_fig_height_inches = canvas_height_px / self.fig.get_dpi()
            
            current_width, current_height = self.fig.get_size_inches()
            size_changed = abs(current_width - new_fig_width_inches) > 0.01 or \
                           abs(current_height - new_fig_height_inches) > 0.01
            is_initial_draw = not hasattr(self.fig, '_mre_drawn_once')


            if size_changed or is_initial_draw:
                self.fig.set_size_inches(new_fig_width_inches, new_fig_height_inches, forward=True)
                logging.info(f"MRE: Figure resized to: {new_fig_width_inches:.2f}x{new_fig_height_inches:.2f} in. Initial: {is_initial_draw}")
                if is_initial_draw:
                    self.fig._mre_drawn_once = True
            
            # --- If testing subplots_adjust (Option 1 for self.fig creation) ---
            try:
                self.fig.subplots_adjust(left=0.10, bottom=0.10, right=0.95, top=0.92, wspace=0.25, hspace=0.25)
                logging.debug("MRE: Applied subplots_adjust.")
            except Exception as e:
                logging.error(f"MRE: Error in subplots_adjust: {e}")

            # --- If testing constrained_layout (Option 2 for self.fig creation) ---
            # if not self.fig.get_constrained_layout():
            #     self.fig.set_constrained_layout(True)
            #     logging.info("MRE: Constrained layout re-enabled.")
            # try:
            #     self.fig.set_constrained_layout_pads(w_pad=0.02, h_pad=0.02, wspace=0.03, hspace=0.03) # Match your app
            # except Exception as e:
            #     logging.error(f"MRE: Error setting constrained_layout_pads: {e}")


            self.canvas.draw_idle()
            logging.debug("MRE: Canvas draw_idle called.")
        else:
            logging.debug("MRE: Canvas too small to resize figure.")


if __name__ == '__main__':
    root = tk.Tk()
    app = MRE_App(root)
    root.mainloop()

Actual outcome

system details:

Operating System: Debian GNU/Linux 12 (bookworm)
Kernel: Linux 6.12.25+rpt-rpi-v8
Architecture: arm64

Python version: 3.11.x (from your logs)

Matplotlib version: 3.10.1

Tkinter version: 8.6 (from your MRE logs)

Tcl version: 8.6 (from your MRE logs)

Expected outcome

As the window resizes the graphs should resize with the window and display all 4.

Additional information

No response

Operating system

Debian Bookworm

Matplotlib Version

3.10.1

Matplotlib Backend

No response

Python version

3.11.x

Jupyter version

No response

Installation

pip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant