Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ac32c78

Browse files
authored
Merge pull request #24531 from daniilS/tk_savefig_formats
Use user-selected format in Tk savefig, rather than inferring it from the filename
2 parents 92b4b95 + b876de8 commit ac32c78

File tree

4 files changed

+47
-17
lines changed

4 files changed

+47
-17
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Tk backend respects file format selection when saving figures
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
When saving a figure from a Tkinter GUI to a filename without an
5+
extension, the file format is now selected based on the value of
6+
the dropdown menu, rather than defaulting to PNG. When the filename
7+
contains an extension, or the OS automatically appends one, the
8+
behavior remains unchanged.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Increase to minimum supported optional dependencies
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
For Matplotlib 3.8, the :ref:`minimum supported versions of optional dependencies
5+
<optional_dependencies>` are being bumped:
6+
7+
+------------+-----------------+---------------+
8+
| Dependency | min in mpl3.7 | min in mpl3.8 |
9+
+============+=================+===============+
10+
| Tk | 8.4 | 8.5 |
11+
+------------+-----------------+---------------+
12+
13+
This is consistent with our :ref:`min_deps_policy`

doc/devel/dependencies.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ Matplotlib figures can be rendered to various user interfaces. See
4646
:ref:`what-is-a-backend` for more details on the optional Matplotlib backends
4747
and the capabilities they provide.
4848

49-
* Tk_ (>= 8.4, != 8.6.0 or 8.6.1): for the Tk-based backends. Tk is part of
49+
* Tk_ (>= 8.5, != 8.6.0 or 8.6.1): for the Tk-based backends. Tk is part of
5050
most standard Python installations, but it's not part of Python itself and
5151
thus may not be present in rare cases.
5252
* PyQt6_ (>= 6.1), PySide6_, PyQt5_, or PySide2_: for the Qt-based backends.

lib/matplotlib/backends/_backend_tk.py

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import logging
55
import math
66
import os.path
7+
import pathlib
78
import sys
89
import tkinter as tk
910
import tkinter.filedialog
@@ -499,11 +500,8 @@ def create_with_canvas(cls, canvas_class, figure, num):
499500
'images/matplotlib_large.png'))
500501
icon_img_large = ImageTk.PhotoImage(
501502
file=icon_fname_large, master=window)
502-
try:
503-
window.iconphoto(False, icon_img_large, icon_img)
504-
except Exception as exc:
505-
# log the failure (due e.g. to Tk version), but carry on
506-
_log.info('Could not load matplotlib icon: %s', exc)
503+
504+
window.iconphoto(False, icon_img_large, icon_img)
507505

508506
canvas = canvas_class(figure, master=window)
509507
manager = cls(canvas, num, window)
@@ -846,15 +844,15 @@ def _Spacer(self):
846844
return s
847845

848846
def save_figure(self, *args):
849-
filetypes = self.canvas.get_supported_filetypes().copy()
850-
default_filetype = self.canvas.get_default_filetype()
847+
filetypes = self.canvas.get_supported_filetypes_grouped()
848+
tk_filetypes = [
849+
(name, " ".join(f"*.{ext}" for ext in exts))
850+
for name, exts in sorted(filetypes.items())
851+
]
851852

852-
# Tk doesn't provide a way to choose a default filetype,
853-
# so we just have to put it first
854-
default_filetype_name = filetypes.pop(default_filetype)
855-
sorted_filetypes = ([(default_filetype, default_filetype_name)]
856-
+ sorted(filetypes.items()))
857-
tk_filetypes = [(name, '*.%s' % ext) for ext, name in sorted_filetypes]
853+
default_extension = self.canvas.get_default_filetype()
854+
default_filetype = self.canvas.get_supported_filetypes()[default_extension]
855+
filetype_variable = tk.StringVar(self, default_filetype)
858856

859857
# adding a default extension seems to break the
860858
# asksaveasfilename dialog when you choose various save types
@@ -863,14 +861,18 @@ def save_figure(self, *args):
863861
# defaultextension = self.canvas.get_default_filetype()
864862
defaultextension = ''
865863
initialdir = os.path.expanduser(mpl.rcParams['savefig.directory'])
866-
initialfile = self.canvas.get_default_filename()
864+
# get_default_filename() contains the default extension. On some platforms,
865+
# choosing a different extension from the dropdown does not overwrite it,
866+
# so we need to remove it to make the dropdown functional.
867+
initialfile = pathlib.Path(self.canvas.get_default_filename()).stem
867868
fname = tkinter.filedialog.asksaveasfilename(
868869
master=self.canvas.get_tk_widget().master,
869870
title='Save the figure',
870871
filetypes=tk_filetypes,
871872
defaultextension=defaultextension,
872873
initialdir=initialdir,
873874
initialfile=initialfile,
875+
typevariable=filetype_variable
874876
)
875877

876878
if fname in ["", ()]:
@@ -879,9 +881,16 @@ def save_figure(self, *args):
879881
if initialdir != "":
880882
mpl.rcParams['savefig.directory'] = (
881883
os.path.dirname(str(fname)))
884+
885+
# If the filename contains an extension, let savefig() infer the file
886+
# format from that. If it does not, use the selected dropdown option.
887+
if pathlib.Path(fname).suffix[1:] != "":
888+
extension = None
889+
else:
890+
extension = filetypes[filetype_variable.get()][0]
891+
882892
try:
883-
# This method will handle the delegation to the correct type
884-
self.canvas.figure.savefig(fname)
893+
self.canvas.figure.savefig(fname, format=extension)
885894
except Exception as e:
886895
tkinter.messagebox.showerror("Error saving file", str(e))
887896

0 commit comments

Comments
 (0)