From 61f2e47ea779b7bd03661edaf20e133be8ffbb9b Mon Sep 17 00:00:00 2001 From: smp55 Date: Sat, 3 Aug 2024 11:36:32 -0700 Subject: [PATCH 1/3] feat: modify Tk backend managers to use ttk buttons and separators Add a new class NavigationToolbar2Ttk which uses ttk buttons and separators instead of standard Tk. Modify backend managers to use this new class by default for Tk. Original NavigationToolbar2Tk still exists. Closes issue #28607 --- lib/matplotlib/backends/_backend_tk.py | 37 ++++++++++++++++++------ lib/matplotlib/backends/backend_tkagg.py | 2 +- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/lib/matplotlib/backends/_backend_tk.py b/lib/matplotlib/backends/_backend_tk.py index df06440a9826..bd03f4db3442 100644 --- a/lib/matplotlib/backends/_backend_tk.py +++ b/lib/matplotlib/backends/_backend_tk.py @@ -11,6 +11,7 @@ import tkinter.font import tkinter.messagebox from tkinter.simpledialog import SimpleDialog +from tkinter import ttk import numpy as np from PIL import Image, ImageTk @@ -661,7 +662,7 @@ def _rescale(self): scale correctly to pixels. """ for widget in self.winfo_children(): - if isinstance(widget, (tk.Button, tk.Checkbutton)): + if isinstance(widget, (tk.Button, tk.Checkbutton, ttk.Button)): if hasattr(widget, '_image_file'): # Explicit class because ToolbarTk calls _rescale. NavigationToolbar2Tk._set_image_for_button(self, widget) @@ -894,6 +895,24 @@ def set_history_buttons(self): if "Forward" in self._buttons: self._buttons['Forward']['state'] = state_map[can_forward] +class NavigationToolbar2Ttk(NavigationToolbar2Tk): + def _Button(self, text, image_file, toggle, command): + im = tk.PhotoImage(master=self, file=image_file) + b = ttk.Button(self, text=text, padding=(2, 2), image=im, command=command) + b._ntimage = im + b.pack(side='left') + return b + def _Spacer(self): + s = ttk.Separator(master=self, orient='vertical') + s.pack(side='left', padx='3p', pady='5p', fill='y') + return s + def _update_buttons_checked(self): + for text in ['Zoom', 'Pan']: + if text in self._buttons: + if self.mode.name == text.upper(): + self._buttons[text].state(['pressed']) + else: + self._buttons[text].state(['!pressed']) def add_tooltip(widget, text): tipwindow = None @@ -932,11 +951,11 @@ def hidetip(event): @backend_tools._register_tool_class(FigureCanvasTk) class RubberbandTk(backend_tools.RubberbandBase): def draw_rubberband(self, x0, y0, x1, y1): - NavigationToolbar2Tk.draw_rubberband( + NavigationToolbar2Ttk.draw_rubberband( self._make_classic_style_pseudo_toolbar(), None, x0, y0, x1, y1) def remove_rubberband(self): - NavigationToolbar2Tk.remove_rubberband( + NavigationToolbar2Ttk.remove_rubberband( self._make_classic_style_pseudo_toolbar()) @@ -967,7 +986,7 @@ def __init__(self, toolmanager, window=None): self._groups = {} def _rescale(self): - return NavigationToolbar2Tk._rescale(self) + return NavigationToolbar2Ttk._rescale(self) def add_toolitem( self, name, group, position, image_file, description, toggle): @@ -977,7 +996,7 @@ def add_toolitem( before = None else: before = buttons[position] - button = NavigationToolbar2Tk._Button(frame, name, image_file, toggle, + button = NavigationToolbar2Ttk._Button(frame, name, image_file, toggle, lambda: self._button_click(name)) button.pack_configure(before=before) if description is not None: @@ -996,7 +1015,7 @@ def _get_groupframe(self, group): return self._groups[group] def _add_separator(self): - return NavigationToolbar2Tk._Spacer(self) + return NavigationToolbar2Ttk._Spacer(self) def _button_click(self, name): self.trigger_tool(name) @@ -1021,14 +1040,14 @@ def set_message(self, s): @backend_tools._register_tool_class(FigureCanvasTk) class SaveFigureTk(backend_tools.SaveFigureBase): def trigger(self, *args): - NavigationToolbar2Tk.save_figure( + NavigationToolbar2Ttk.save_figure( self._make_classic_style_pseudo_toolbar()) @backend_tools._register_tool_class(FigureCanvasTk) class ConfigureSubplotsTk(backend_tools.ConfigureSubplotsBase): def trigger(self, *args): - NavigationToolbar2Tk.configure_subplots(self) + NavigationToolbar2Ttk.configure_subplots(self) @backend_tools._register_tool_class(FigureCanvasTk) @@ -1040,7 +1059,7 @@ def trigger(self, *args): Toolbar = ToolbarTk -FigureManagerTk._toolbar2_class = NavigationToolbar2Tk +FigureManagerTk._toolbar2_class = NavigationToolbar2Ttk FigureManagerTk._toolmanager_toolbar_class = ToolbarTk diff --git a/lib/matplotlib/backends/backend_tkagg.py b/lib/matplotlib/backends/backend_tkagg.py index f95b6011eadf..6356cee7785e 100644 --- a/lib/matplotlib/backends/backend_tkagg.py +++ b/lib/matplotlib/backends/backend_tkagg.py @@ -2,7 +2,7 @@ from .backend_agg import FigureCanvasAgg from ._backend_tk import _BackendTk, FigureCanvasTk from ._backend_tk import ( # noqa: F401 # pylint: disable=W0611 - FigureManagerTk, NavigationToolbar2Tk) + FigureManagerTk, NavigationToolbar2Tk, NavigationToolbar2Ttk) class FigureCanvasTkAgg(FigureCanvasAgg, FigureCanvasTk): From 4963bf4b2ea158d0537c09dd16d29caf6540cce1 Mon Sep 17 00:00:00 2001 From: smp55 Date: Wed, 11 Dec 2024 22:35:03 -0800 Subject: [PATCH 2/3] Fixed flake8 linting errors and added 1 class docstring --- lib/matplotlib/backends/_backend_tk.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/backends/_backend_tk.py b/lib/matplotlib/backends/_backend_tk.py index 84200f3e0ebd..0e1c5aed06fb 100644 --- a/lib/matplotlib/backends/_backend_tk.py +++ b/lib/matplotlib/backends/_backend_tk.py @@ -920,17 +920,21 @@ def set_history_buttons(self): if "Forward" in self._buttons: self._buttons['Forward']['state'] = state_map[can_forward] + class NavigationToolbar2Ttk(NavigationToolbar2Tk): + """Subclass of NavigationToolbar2Tk that implements ttk widgets""" def _Button(self, text, image_file, toggle, command): im = tk.PhotoImage(master=self, file=image_file) b = ttk.Button(self, text=text, padding=(2, 2), image=im, command=command) b._ntimage = im b.pack(side='left') return b + def _Spacer(self): s = ttk.Separator(master=self, orient='vertical') s.pack(side='left', padx='3p', pady='5p', fill='y') return s + def _update_buttons_checked(self): for text in ['Zoom', 'Pan']: if text in self._buttons: @@ -939,6 +943,7 @@ def _update_buttons_checked(self): else: self._buttons[text].state(['!pressed']) + def add_tooltip(widget, text): tipwindow = None @@ -1022,7 +1027,7 @@ def add_toolitem( else: before = buttons[position] button = NavigationToolbar2Ttk._Button(frame, name, image_file, toggle, - lambda: self._button_click(name)) + lambda: self._button_click(name)) button.pack_configure(before=before) if description is not None: add_tooltip(button, description) From c7131599e4fa749ac66964c5a17a3f342197fe0c Mon Sep 17 00:00:00 2001 From: smp55 Date: Wed, 11 Dec 2024 22:40:28 -0800 Subject: [PATCH 3/3] Fixed MORE flake8 linting errors. --- lib/matplotlib/backends/_backend_tk.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/backends/_backend_tk.py b/lib/matplotlib/backends/_backend_tk.py index 0e1c5aed06fb..1be2f340cb92 100644 --- a/lib/matplotlib/backends/_backend_tk.py +++ b/lib/matplotlib/backends/_backend_tk.py @@ -929,12 +929,12 @@ def _Button(self, text, image_file, toggle, command): b._ntimage = im b.pack(side='left') return b - + def _Spacer(self): s = ttk.Separator(master=self, orient='vertical') s.pack(side='left', padx='3p', pady='5p', fill='y') return s - + def _update_buttons_checked(self): for text in ['Zoom', 'Pan']: if text in self._buttons: