Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit f346d6e

Browse filesBrowse files
authored
Merge pull request #13707 from meeseeksmachine/auto-backport-of-pr-12760-on-v3.1.x
Backport PR #12760 on branch v3.1.x (Deduplicate implementation of per-backend Tools.)
2 parents fc6d9dc + b472472 commit f346d6e
Copy full SHA for f346d6e

File tree

Expand file treeCollapse file tree

5 files changed

+46
-164
lines changed
Filter options
Expand file treeCollapse file tree

5 files changed

+46
-164
lines changed

‎lib/matplotlib/backend_tools.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backend_tools.py
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import re
1515
import time
1616
import logging
17+
from types import SimpleNamespace
1718
from weakref import WeakKeyDictionary
1819

1920
import numpy as np
@@ -102,6 +103,15 @@ def canvas(self):
102103
def toolmanager(self):
103104
return self._toolmanager
104105

106+
def _make_classic_style_pseudo_toolbar(self):
107+
"""
108+
Return a placeholder object with a single `canvas` attribute.
109+
110+
This is useful to reuse the implementations of tools already provided
111+
by the classic Toolbars.
112+
"""
113+
return SimpleNamespace(canvas=self.canvas)
114+
105115
def set_figure(self, figure):
106116
"""
107117
Assign a figure to the tool

‎lib/matplotlib/backends/_backend_tk.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/_backend_tk.py
+10-50Lines changed: 10 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,8 @@ class NavigationToolbar2Tk(NavigationToolbar2, tk.Frame):
584584
"""
585585
def __init__(self, canvas, window):
586586
self.canvas = canvas
587+
# Avoid using self.window (prefer self.canvas.manager.window), so that
588+
# Tool implementations can reuse the methods.
587589
self.window = window
588590
NavigationToolbar2.__init__(self, canvas)
589591

@@ -602,16 +604,15 @@ def draw_rubberband(self, event, x0, y0, x1, y1):
602604
self.canvas._tkcanvas.delete(self.lastrect)
603605
self.lastrect = self.canvas._tkcanvas.create_rectangle(x0, y0, x1, y1)
604606

605-
#self.canvas.draw()
606-
607607
def release(self, event):
608608
if hasattr(self, "lastrect"):
609609
self.canvas._tkcanvas.delete(self.lastrect)
610610
del self.lastrect
611611

612612
def set_cursor(self, cursor):
613-
self.window.configure(cursor=cursord[cursor])
614-
self.window.update_idletasks()
613+
window = self.canvas.manager.window
614+
window.configure(cursor=cursord[cursor])
615+
window.update_idletasks()
615616

616617
def _Button(self, text, file, command, extension='.gif'):
617618
img_file = os.path.join(
@@ -684,7 +685,7 @@ def save_figure(self, *args):
684685
initialdir = os.path.expanduser(rcParams['savefig.directory'])
685686
initialfile = self.canvas.get_default_filename()
686687
fname = tkinter.filedialog.asksaveasfilename(
687-
master=self.window,
688+
master=self.canvas.manager.window,
688689
title='Save the figure',
689690
filetypes=tk_filetypes,
690691
defaultextension=defaultextension,
@@ -765,9 +766,6 @@ def hidetip(self):
765766

766767

767768
class RubberbandTk(backend_tools.RubberbandBase):
768-
def __init__(self, *args, **kwargs):
769-
backend_tools.RubberbandBase.__init__(self, *args, **kwargs)
770-
771769
def draw_rubberband(self, x0, y0, x1, y1):
772770
height = self.figure.canvas.figure.bbox.height
773771
y0 = height - y0
@@ -785,7 +783,8 @@ def remove_rubberband(self):
785783

786784
class SetCursorTk(backend_tools.SetCursorBase):
787785
def set_cursor(self, cursor):
788-
self.figure.canvas.manager.window.configure(cursor=cursord[cursor])
786+
NavigationToolbar2Tk.set_cursor(
787+
self._make_classic_style_pseudo_toolbar(), cursor)
789788

790789

791790
class ToolbarTk(ToolContainerBase, tk.Frame):
@@ -885,47 +884,8 @@ def set_message(self, s):
885884

886885
class SaveFigureTk(backend_tools.SaveFigureBase):
887886
def trigger(self, *args):
888-
filetypes = self.figure.canvas.get_supported_filetypes().copy()
889-
default_filetype = self.figure.canvas.get_default_filetype()
890-
891-
# Tk doesn't provide a way to choose a default filetype,
892-
# so we just have to put it first
893-
default_filetype_name = filetypes.pop(default_filetype)
894-
sorted_filetypes = ([(default_filetype, default_filetype_name)]
895-
+ sorted(filetypes.items()))
896-
tk_filetypes = [(name, '*.%s' % ext) for ext, name in sorted_filetypes]
897-
898-
# adding a default extension seems to break the
899-
# asksaveasfilename dialog when you choose various save types
900-
# from the dropdown. Passing in the empty string seems to
901-
# work - JDH!
902-
# defaultextension = self.figure.canvas.get_default_filetype()
903-
defaultextension = ''
904-
initialdir = os.path.expanduser(rcParams['savefig.directory'])
905-
initialfile = self.figure.canvas.get_default_filename()
906-
fname = tkinter.filedialog.asksaveasfilename(
907-
master=self.figure.canvas.manager.window,
908-
title='Save the figure',
909-
filetypes=tk_filetypes,
910-
defaultextension=defaultextension,
911-
initialdir=initialdir,
912-
initialfile=initialfile,
913-
)
914-
915-
if fname == "" or fname == ():
916-
return
917-
else:
918-
if initialdir == '':
919-
# explicitly missing key or empty str signals to use cwd
920-
rcParams['savefig.directory'] = initialdir
921-
else:
922-
# save dir for next time
923-
rcParams['savefig.directory'] = os.path.dirname(str(fname))
924-
try:
925-
# This method will handle the delegation to the correct type
926-
self.figure.savefig(fname)
927-
except Exception as e:
928-
tkinter.messagebox.showerror("Error saving file", str(e))
887+
NavigationToolbar2Tk.save_figure(
888+
self._make_classic_style_pseudo_toolbar())
929889

930890

931891
class ConfigureSubplotsTk(backend_tools.ConfigureSubplotsBase):

‎lib/matplotlib/backends/backend_gtk3.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_gtk3.py
+8-29Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -692,34 +692,6 @@ def get_filename_from_user(self):
692692
return None, self.ext
693693

694694

695-
class RubberbandGTK3(backend_tools.RubberbandBase):
696-
def __init__(self, *args, **kwargs):
697-
backend_tools.RubberbandBase.__init__(self, *args, **kwargs)
698-
self.ctx = None
699-
700-
def draw_rubberband(self, x0, y0, x1, y1):
701-
# 'adapted from http://aspn.activestate.com/ASPN/Cookbook/Python/
702-
# Recipe/189744'
703-
self.ctx = self.figure.canvas.get_property("window").cairo_create()
704-
705-
# todo: instead of redrawing the entire figure, copy the part of
706-
# the figure that was covered by the previous rubberband rectangle
707-
self.figure.canvas.draw()
708-
709-
height = self.figure.bbox.height
710-
y1 = height - y1
711-
y0 = height - y0
712-
w = abs(x1 - x0)
713-
h = abs(y1 - y0)
714-
rect = [int(val) for val in (min(x0, x1), min(y0, y1), w, h)]
715-
716-
self.ctx.new_path()
717-
self.ctx.set_line_width(0.5)
718-
self.ctx.rectangle(rect[0], rect[1], rect[2], rect[3])
719-
self.ctx.set_source_rgb(0, 0, 0)
720-
self.ctx.stroke()
721-
722-
723695
class ToolbarGTK3(ToolContainerBase, Gtk.Box):
724696
_icon_extension = '.png'
725697

@@ -809,6 +781,12 @@ def set_message(self, s):
809781
self.push(self._context, s)
810782

811783

784+
class RubberbandGTK3(backend_tools.RubberbandBase):
785+
def draw_rubberband(self, x0, y0, x1, y1):
786+
NavigationToolbar2GTK3.draw_rubberband(
787+
self._make_classic_style_pseudo_toolbar(), None, x0, y0, x1, y1)
788+
789+
812790
class SaveFigureGTK3(backend_tools.SaveFigureBase):
813791

814792
@cbook.deprecated("3.1")
@@ -832,7 +810,8 @@ class PseudoToolbar:
832810

833811
class SetCursorGTK3(backend_tools.SetCursorBase):
834812
def set_cursor(self, cursor):
835-
self.figure.canvas.get_property("window").set_cursor(cursord[cursor])
813+
NavigationToolbar2GTK3.set_cursor(
814+
self._make_classic_style_pseudo_toolbar(), cursor)
836815

837816

838817
class ConfigureSubplotsGTK3(backend_tools.ConfigureSubplotsBase, Gtk.Window):

‎lib/matplotlib/backends/backend_qt5.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_qt5.py
+12-47Lines changed: 12 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,7 @@ def remove_rubberband(self):
806806
def configure_subplots(self):
807807
image = os.path.join(matplotlib.rcParams['datapath'],
808808
'images', 'matplotlib.png')
809-
dia = SubplotToolQt(self.canvas.figure, self.parent)
809+
dia = SubplotToolQt(self.canvas.figure, self.canvas.parent())
810810
dia.setWindowIcon(QtGui.QIcon(image))
811811
dia.exec_()
812812

@@ -828,7 +828,7 @@ def save_figure(self, *args):
828828
filters.append(filter)
829829
filters = ';;'.join(filters)
830830

831-
fname, filter = _getSaveFileName(self.parent,
831+
fname, filter = _getSaveFileName(self.canvas.parent(),
832832
"Choose a filename to save to",
833833
start, filters, selectedFilter)
834834
if fname:
@@ -994,65 +994,30 @@ def set_message(self, s):
994994

995995
class ConfigureSubplotsQt(backend_tools.ConfigureSubplotsBase):
996996
def trigger(self, *args):
997-
image = os.path.join(matplotlib.rcParams['datapath'],
998-
'images', 'matplotlib.png')
999-
parent = self.canvas.manager.window
1000-
dia = SubplotToolQt(self.figure, parent)
1001-
dia.setWindowIcon(QtGui.QIcon(image))
1002-
dia.exec_()
997+
NavigationToolbar2QT.configure_subplots(
998+
self._make_classic_style_pseudo_toolbar())
1003999

10041000

10051001
class SaveFigureQt(backend_tools.SaveFigureBase):
10061002
def trigger(self, *args):
1007-
filetypes = self.canvas.get_supported_filetypes_grouped()
1008-
sorted_filetypes = sorted(filetypes.items())
1009-
default_filetype = self.canvas.get_default_filetype()
1010-
1011-
startpath = os.path.expanduser(
1012-
matplotlib.rcParams['savefig.directory'])
1013-
start = os.path.join(startpath, self.canvas.get_default_filename())
1014-
filters = []
1015-
selectedFilter = None
1016-
for name, exts in sorted_filetypes:
1017-
exts_list = " ".join(['*.%s' % ext for ext in exts])
1018-
filter = '%s (%s)' % (name, exts_list)
1019-
if default_filetype in exts:
1020-
selectedFilter = filter
1021-
filters.append(filter)
1022-
filters = ';;'.join(filters)
1023-
1024-
parent = self.canvas.manager.window
1025-
fname, filter = _getSaveFileName(parent,
1026-
"Choose a filename to save to",
1027-
start, filters, selectedFilter)
1028-
if fname:
1029-
# Save dir for next time, unless empty str (i.e., use cwd).
1030-
if startpath != "":
1031-
matplotlib.rcParams['savefig.directory'] = (
1032-
os.path.dirname(fname))
1033-
try:
1034-
self.canvas.figure.savefig(fname)
1035-
except Exception as e:
1036-
QtWidgets.QMessageBox.critical(
1037-
self, "Error saving file", str(e),
1038-
QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.NoButton)
1003+
NavigationToolbar2QT.save_figure(
1004+
self._make_classic_style_pseudo_toolbar())
10391005

10401006

10411007
class SetCursorQt(backend_tools.SetCursorBase):
10421008
def set_cursor(self, cursor):
1043-
self.canvas.setCursor(cursord[cursor])
1009+
NavigationToolbar2QT.set_cursor(
1010+
self._make_classic_style_pseudo_toolbar(), cursor)
10441011

10451012

10461013
class RubberbandQt(backend_tools.RubberbandBase):
10471014
def draw_rubberband(self, x0, y0, x1, y1):
1048-
height = self.canvas.figure.bbox.height
1049-
y1 = height - y1
1050-
y0 = height - y0
1051-
rect = [int(val) for val in (x0, y0, x1 - x0, y1 - y0)]
1052-
self.canvas.drawRectangle(rect)
1015+
NavigationToolbar2QT.draw_rubberband(
1016+
self._make_classic_style_pseudo_toolbar(), None, x0, y0, x1, y1)
10531017

10541018
def remove_rubberband(self):
1055-
self.canvas.drawRectangle(None)
1019+
NavigationToolbar2QT.remove_rubberband(
1020+
self._make_classic_style_pseudo_toolbar())
10561021

10571022

10581023
class HelpQt(backend_tools.ToolHelpBase):

‎lib/matplotlib/backends/backend_wx.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_wx.py
+6-38Lines changed: 6 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,8 +1445,8 @@ def save_figure(self, *args):
14451445
# Fetch the required filename and file type.
14461446
filetypes, exts, filter_index = self.canvas._get_imagesave_wildcards()
14471447
default_file = self.canvas.get_default_filename()
1448-
dlg = wx.FileDialog(self._parent, "Save to file", "", default_file,
1449-
filetypes,
1448+
dlg = wx.FileDialog(self.canvas.GetParent(),
1449+
"Save to file", "", default_file, filetypes,
14501450
wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
14511451
dlg.SetFilterIndex(filter_index)
14521452
if dlg.ShowModal() == wx.ID_OK:
@@ -1692,46 +1692,14 @@ def get_canvas(self, frame, fig):
16921692

16931693
class SaveFigureWx(backend_tools.SaveFigureBase):
16941694
def trigger(self, *args):
1695-
# Fetch the required filename and file type.
1696-
filetypes, exts, filter_index = self.canvas._get_imagesave_wildcards()
1697-
default_dir = os.path.expanduser(
1698-
matplotlib.rcParams['savefig.directory'])
1699-
default_file = self.canvas.get_default_filename()
1700-
dlg = wx.FileDialog(self.canvas.GetTopLevelParent(), "Save to file",
1701-
default_dir, default_file, filetypes,
1702-
wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
1703-
dlg.SetFilterIndex(filter_index)
1704-
if dlg.ShowModal() != wx.ID_OK:
1705-
return
1706-
1707-
dirname = dlg.GetDirectory()
1708-
filename = dlg.GetFilename()
1709-
DEBUG_MSG('Save file dir:%s name:%s' % (dirname, filename), 3, self)
1710-
format = exts[dlg.GetFilterIndex()]
1711-
basename, ext = os.path.splitext(filename)
1712-
if ext.startswith('.'):
1713-
ext = ext[1:]
1714-
if ext in ('svg', 'pdf', 'ps', 'eps', 'png') and format != ext:
1715-
# looks like they forgot to set the image type drop
1716-
# down, going with the extension.
1717-
_log.warning('extension %s did not match the selected '
1718-
'image type %s; going with %s',
1719-
ext, format, ext)
1720-
format = ext
1721-
if default_dir != "":
1722-
matplotlib.rcParams['savefig.directory'] = dirname
1723-
try:
1724-
self.canvas.figure.savefig(
1725-
os.path.join(dirname, filename), format=format)
1726-
except Exception as e:
1727-
error_msg_wx(str(e))
1695+
NavigationToolbar2Wx.save_figure(
1696+
self._make_classic_style_pseudo_toolbar())
17281697

17291698

17301699
class SetCursorWx(backend_tools.SetCursorBase):
17311700
def set_cursor(self, cursor):
1732-
cursor = wx.Cursor(cursord[cursor])
1733-
self.canvas.SetCursor(cursor)
1734-
self.canvas.Update()
1701+
NavigationToolbar2Wx.set_cursor(
1702+
self._make_classic_style_pseudo_toolbar(), cursor)
17351703

17361704

17371705
if 'wxMac' not in wx.PlatformInfo:

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.