Source code for GUIBRUSHR.GUI.WIDGET.MyMultiFigure

import tkinter as tk
import os
import matplotlib
matplotlib.rcParams['agg.path.chunksize'] = 10000
if not os.environ.get('SPHINX_BUILD'):
    matplotlib.use("TkAgg")
from matplotlib import pyplot as plt
from matplotlib.backends._backend_tk import NavigationToolbar2Tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from typing import List, Optional

from GUIBRUSHR.General_Constants.FunctionsAndConstants.Constant_Variables import ConstantVariables
from GUIBRUSHR.core.functions.figure_pickle import load_figures


[docs] class MyMultiFigure(tk.Toplevel): """ A window that displays a list of matplotlib figures as navigable pages. Each page is embedded with an interactive NavigationToolbar2Tk (zoom, pan, reset and a Save button that exports the current view). Prev/Next buttons move between pages. This is the interactive counterpart of the static multi-page PDF shown by MyPDFWindow. """
[docs] def __init__( self, figures: List, title: str = "Figures", ) -> None: """ Initialize the multi-figure window. Args: figures: Ordered list of matplotlib Figures, one per page. title: The window title. """ super().__init__() self.title(title) self.protocol("WM_DELETE_WINDOW", self.close_window) self.geometry(ConstantVariables.figure_geometry()) self.figures = list(figures) self.index = 0 self._canvas = None self._toolbar = None # Navigation bar (fixed, top) nav = tk.Frame(self) nav.pack(side="top", fill="x") self.prev_button = tk.Button(nav, text="< Prev", command=self.show_prev) self.prev_button.pack(side="left") self.page_label = tk.Label(nav, text="") self.page_label.pack(side="left", expand=True) self.next_button = tk.Button(nav, text="Next >", command=self.show_next) self.next_button.pack(side="right") # Reserve the fixed Close button (bottom) BEFORE the expanding container, # otherwise the canvas claims all space and clips it until the user resizes. self.close_button = tk.Button(self, text="Close", command=self.close_window) self.close_button.pack(side="bottom", fill="x", ipady=4) # Container that holds the current page's canvas and toolbar (expands last) self.container = tk.Frame(self) self.container.pack(side="top", fill="both", expand=True) self._render_current()
[docs] @classmethod def from_pdf(cls, pdf_path: str, title: str = "Figures") -> "Optional[MyMultiFigure]": """ Build the viewer from the figures pickled next to ``pdf_path``. Args: pdf_path: Path of the multi-page PDF whose ``.figs.pkl`` holds the pickled figures. title: The window title. Returns: A MyMultiFigure if the pickled figures were found and loaded, else None so the caller can fall back to the static PDF viewer. """ figures = load_figures(pdf_path) if not figures: return None try: return cls(figures, title=title) except Exception: return None
@staticmethod def _normalize_display_dpi(figure) -> None: """Lower a high-dpi figure's dpi for on-screen display (font proportions).""" try: if figure.get_dpi() > 150: figure.set_dpi(100) except Exception: pass def _render_current(self) -> None: """Embed the current page's figure, replacing any previous one.""" # Tear down the previous canvas/toolbar if self._toolbar is not None: self._toolbar.destroy() self._toolbar = None if self._canvas is not None: self._canvas.get_tk_widget().destroy() self._canvas = None figure = self.figures[self.index] self._normalize_display_dpi(figure) self._canvas = FigureCanvasTkAgg(figure, self.container) # Toolbar packed before the expanding canvas so it stays visible. self._toolbar = NavigationToolbar2Tk(self._canvas, self.container) self._toolbar.update() self._canvas.draw() self._canvas.get_tk_widget().pack(fill="both", expand=True) self.page_label.configure(text=f"Page {self.index + 1}/{len(self.figures)}") self.prev_button.configure(state="normal" if self.index > 0 else "disabled") self.next_button.configure( state="normal" if self.index < len(self.figures) - 1 else "disabled" )
[docs] def show_prev(self) -> None: """Show the previous page, if any.""" if self.index > 0: self.index -= 1 self._render_current()
[docs] def show_next(self) -> None: """Show the next page, if any.""" if self.index < len(self.figures) - 1: self.index += 1 self._render_current()
[docs] def close_window(self) -> None: """Close the window and release all embedded figures.""" for figure in self.figures: try: plt.close(figure) except Exception: pass self.destroy()