10000 Deduplicate implementation of per-backend Tools. · matplotlib/matplotlib@6a843e0 · GitHub
[go: up one dir, main page]

Skip to content

Commit 6a843e0

Browse files
committed
Deduplicate implementation of per-backend Tools.
... by reusing the old Toolbar-based implementations. If toolmanager ever becomes the default we can move the implementations there and conversely point the old Toolbar-based implementations to use the Tools.
1 parent 4dbbd22 commit 6a843e0

File tree

5 files changed

+46
-164
lines changed

5 files changed

+46
-164
lines changed

lib/matplotlib/backend_tools.py

Lines 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

Lines changed: 10 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,8 @@ class NavigationToolbar2Tk(NavigationToolbar2, tk.Frame):
609609
"""
610610
def __init__(self, canvas, window):
611611
self.canvas = canvas
612+
# Avoid using this (prefer self.canvas.manager.window), so that Tool
613+
# implementations can reuse the methods.
612614
self.window = window
613615
NavigationToolbar2.__init__(self, canvas)
614616

@@ -627,16 +629,15 @@ def draw_rubberband(self, event, x0, y0, x1, y1):
627629
self.canvas._tkcanvas.delete(self.lastrect)
628630
self.lastrect = self.canvas._tkcanvas.create_rectangle(x0, y0, x1, y1)
629631

630-
#self.canvas.draw()
631-
632632
def release(self, event):
633633
if hasattr(self, "lastrect"):
634634
self.canvas._tkcanvas.delete(self.lastrect)
635635
del self.lastrect
636636

637637
def set_cursor(self, cursor):
638-
self.window.configure(cursor=cursord[cursor])
639-
self.window.update_idletasks()
638+
window = self.canvas.manager.window
639+
window.configure(cursor=cursord[cursor])
640+
window.update_idletasks()
640641

641642
def _Button(self, text, file, command, extension='.gif'):
642643
img_file = os.path.join(
@@ -709,7 +710,7 @@ def save_figure(self, *args):
709710
initialdir = os.path.expanduser(rcParams['savefig.directory'])
710711
initialfile = self.canvas.get_default_filename()
711712
fname = tkinter.filedialog.asksaveasfilename(
712-
master=self.window,
713+
master=self.canvas.manager.window,
713714
title='Save the figure',
714715
filetypes=tk_filetypes,
715716
defaultextension=defaultextension,
@@ -790,9 +791,6 @@ def hidetip(self):
790791

791792

792793
class RubberbandTk(backend_tools.RubberbandBase):
793-
def __init__(self, *args, **kwargs):
794-
backend_tools.RubberbandBase.__init__(self, *args, **kwargs)
795-
796794
def draw_rubberband(self, x0, y0, x1, y1):
797795
height = self.figure.canvas.figure.bbox.height
798796
y0 = height - y0
@@ -810,7 +808,8 @@ def remove_rubberband(self):
810808

811809
class SetCursorTk(backend_tools.SetCursorBase):
812810
def set_cursor(self, cursor):
813-
self.figure.canvas.manager.window.configure(cursor=cursord[cursor])
811+
NavigationToolbar2Tk.set_cursor(
812+
self._make_classic_style_pseudo_toolbar(), cursor)
814813

815814

816815
class ToolbarTk(ToolContainerBase, tk.Frame):
@@ -910,47 +909,8 @@ def set_message(self, s):
910909

911910
class SaveFigureTk(backend_tools.SaveFigureBase):
912911
def trigger(self, *args):
913-
filetypes = self.figure.canvas.get_supported_filetypes().copy()
914-
default_filetype = self.figure.canvas.get_default_filetype()
915-
916-
# Tk doesn't provide a way to choose a default filetype,
917-
# so we just have to put it first
918-
default_filetype_name = filetypes.pop(default_filetype)
919-
sorted_filetypes = ([(default_filetype, default_filetype_name)]
920-
+ sorted(filetypes.items()))
921-
tk_filetypes = [(name, '*.%s' % ext) for ext, name in sorted_filetypes]
922-
923-
# adding a default extension seems to break the
924-
# asksaveasfilename dialog when you choose various save types
925-
# from the dropdown. Passing in the empty string seems to
926-
# work - JDH!
927-
# defaultextension = self.figure.canvas.get_default_filetype()
928-
defaultextension = ''
929-
initialdir = os.path.expanduser(rcParams['savefig.directory'])
930-
initialfile = self.figure.canvas.get_default_filename()
931-
fname = tkinter.filedialog.asksaveasfilename(
932-
master=self.figure.canvas.manager.window,
933-
title='Save the figure',
934-
filetypes=tk_filetypes,
935-
defaultextension=defaultextension,
936-
initialdir=initialdir,
937-
initialfile=initialfile,
938-
)
939-
940-
if fname == "" or fname == ():
941-
return
942-
else:
943-
if initialdir == '':
944-
# explicitly missing key or empty str signals to use cwd
945-
rcParams['savefig.directory'] = initialdir
946-
else:
947-
# save dir for next time
948-
rcParams['savefig.directory'] = os.path.dirname(str(fname))
949-
try:
950-
# This method will handle the delegation to the correct type
951-
self.figure.savefig(fname)
952-
except Exception as e:
953-
tkinter.messagebox.showerror("Error saving file", str(e))
912+
NavigationToolbar2Tk.save_figure(
913+
self._make_classic_style_pseudo_toolbar())
954914

955915

956916
class ConfigureSubplotsTk(backend_tools.ConfigureSubplotsBase):

lib/matplotlib/backends/backend_gtk3.py

Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -649,34 +649,6 @@ def get_filename_from_user(self):
649649
return None, self.ext
650650

651651

652-
class RubberbandGTK3(backend_tools.RubberbandBase):
653-
def __init__(self, *args, **kwargs):
654-
backend_tools.RubberbandBase.__init__(self, *args, **kwargs)
655-
self.ctx = None
656-
657-
def draw_rubberband(self, x0, y0, x1, y1):
658-
# 'adapted from http://aspn.activestate.com/ASPN/Cookbook/Python/
659-
# Recipe/189744'
660-
self.ctx = self.figure.canvas.get_property("window").cairo_create()
661-
662-
# todo: instead of redrawing the entire figure, copy the part of
663-
# the figure that was covered by the previous rubberband rectangle
664-
self.figure.canvas.draw()
665-
666-
height = self.figure.bbox.height
667-
y1 = height - y1
668-
y0 = height - y0
669-
w = abs(x1 - x0)
670-
h = abs(y1 - y0)
671-
rect = [int(val) for val in (min(x0, x1), min(y0, y1), w, h)]
672-
673-
self.ctx.new_path()
674-
self.ctx.set_line_width(0.5)
675-
self.ctx.rectangle(rect[0], rect[1], rect[2], rect[3])
676-
self.ctx.set_source_rgb(0, 0, 0)
677-
self.ctx.stroke()
678-
679-
680652
class ToolbarGTK3(ToolContainerBase, Gtk.Box):
681653
_icon_extension = '.png'
682654

@@ -766,6 +738,12 @@ def set_message(self, s):
766738
self.push(self._context, s)
767739

768740

741+
class RubberbandGTK3(backend_tools.RubberbandBase):
742+
def draw_rubberband(self, x0, y0, x1, y1):
743+
NavigationToolbar2GTK3.draw_rubberband(
744+
self._make_classic_style_pseudo_toolbar(), None, x0, y0, x1, y1)
745+
746+
769747
class SaveFigureGTK3(backend_tools.SaveFigureBase):
770748

771749
def get_filechooser(self):
@@ -798,7 +776,8 @@ def trigger(self, *args, **kwargs):
798776

799777
class SetCursorGTK3(backend_tools.SetCursorBase):
800778
def set_cursor(self, cursor):
801-
self.figure.canvas.get_property("window").set_cursor(cursord[cursor])
779+
NavigationToolbar2GTK3.set_cursor(
780+
self._make_classic_style_pseudo_toolbar(), cursor)
802781

803782

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

lib/matplotlib/backends/backend_qt5.py

Lines changed: 12 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -819,7 +819,7 @@ def remove_rubberband(self):
819819
def configure_subplots(self):
820820
image = os.path.join(matplotlib.rcParams['datapath'],
821821
'images', 'matplotlib.png')
822-
dia = SubplotToolQt(self.canvas.figure, self.parent)
822+
dia = SubplotToolQt(self.canvas.figure, self.canvas.parent())
823823
dia.setWindowIcon(QtGui.QIcon(image))
824824
dia.exec_()
825825

@@ -841,7 +841,7 @@ def save_figure(self, *args):
841841
filters.append(filter)
842842
filters = ';;'.join(filters)
843843

844-
fname, filter = _getSaveFileName(self.parent,
844+
fname, filter = _getSaveFileName(self.canvas.parent(),
845845
"Choose a filename to save to",
846846
start, filters, selectedFilter)
847847
if fname:
@@ -1001,65 +1001,30 @@ def set_message(self, s):
10011001

10021002
class ConfigureSubplotsQt(backend_tools.ConfigureSubplotsBase):
10031003
def trigger(self, *args):
1004-
image = os.path.join(matplotlib.rcParams['datapath'],
1005-
'images', 'matplotlib.png')
1006-
parent = self.canvas.manager.window
1007-
dia = SubplotToolQt(self.figure, parent)
1008-
dia.setWindowIcon(QtGui.QIcon(image))
1009-
dia.exec_()
1004+
NavigationToolbar2QT.configure_subplots(
1005+
self._make_classic_style_pseudo_toolbar())
10101006

10111007

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

10471013

10481014
class SetCursorQt(backend_tools.SetCursorBase):
10491015
def set_cursor(self, cursor):
1050-
self.canvas.setCursor(cursord[cursor])
1016+
NavigationToolbar2QT.set_cursor(
1017+
self._make_classic_style_pseudo_toolbar(), cursor)
10511018

10521019

10531020
class RubberbandQt(backend_tools.RubberbandBase):
10541021
def draw_rubberband(self, x0, y0, x1, y1):
1055-
height = self.canvas.figure.bbox.height
1056-
y1 = height - y1
1057-
y0 = height - y0
1058-
rect = [int(val) for val in (x0, y0, x1 - x0, y1 - y0)]
1059-
self.canvas.drawRectangle(rect)
1022+
NavigationToolbar2QT.draw_rubberband(
1023+
self._make_classic_style_pseudo_toolbar(), None, x0, y0, x1, y1)
10601024

10611025
def remove_rubberband(self):
1062-
self.canvas.drawRectangle(None)
1026+
NavigationToolbar2QT.remove_rubberband(
1027+
self._make_classic_style_pseudo_toolbar())
10631028

10641029

10651030
class HelpQt(backend_tools.ToolHelpBase):

lib/matplotlib/backends/backend_wx.py

Lines changed: 6 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,8 +1558,8 @@ def save_figure(self, *args):
15581558
# Fetch the required filename and file type.
15591559
filetypes, exts, filter_index = self.canvas._get_imagesave_wildcards()
15601560
default_file = self.canvas.get_default_filename()
1561-
dlg = wx.FileDialog(self._parent, "Save to file", "", default_file,
1562-
filetypes,
1561+
dlg = wx.FileDialog(self.canvas.GetParent(),
1562+
"Save to file", "", default_file, filetypes,
15631563
wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
15641564
dlg.SetFilterIndex(filter_index)
15651565
if dlg.ShowModal() == wx.ID_OK:
@@ -1816,46 +1816,14 @@ def get_canvas(self, frame, fig):
18161816

18171817
class SaveFigureWx(backend_tools.SaveFigureBase):
18181818
def trigger(self, *args):
1819-
# Fetch the required filename and file type.
1820-
filetypes, exts, filter_index = self.canvas._get_imagesave_wildcards()
1821-
default_dir = os.path.expanduser(
1822-
matplotlib.rcParams['savefig.directory'])
1823-
default_file = self.canvas.get_default_filename()
1824-
dlg = wx.FileDialog(self.canvas.GetTopLevelParent(), "Save to file",
1825-
default_dir, default_file, filetypes,
1826-
wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
1827-
dlg.SetFilterIndex(filter_index)
1828-
if dlg.ShowModal() != wx.ID_OK:
1829-
return
1830-
1831-
dirname = dlg.GetDirectory()
1832-
filename = dlg.GetFilename()
1833-
DEBUG_MSG('Save file dir:%s name:%s' % (dirname, filename), 3, self)
1834-
format = exts[dlg.GetFilterIndex()]
1835-
basename, ext = os.path.splitext(filename)
1836-
if ext.startswith('.'):
1837-
ext = ext[1:]
1838-
if ext in ('svg', 'pdf', 'ps', 'eps', 'png') and format != ext:
1839-
# looks like they forgot to set the image type drop
1840-
# down, going with the extension.
1841-
_log.warning('extension %s did not match the selected '
1842-
'image type %s; going with %s',
1843-
ext, format, ext)
1844-
format = ext
1845-
if default_dir != "":
1846-
matplotlib.rcParams['savefig.directory'] = dirname
1847-
try:
1848-
self.canvas.figure.savefig(
1849-
os.path.join(dirname, filename), format=format)
1850-
except Exception as e:
1851-
error_msg_wx(str(e))
1819+
NavigationToolbar2Wx.save_figure(
1820+
self._make_classic_style_pseudo_toolbar())
18521821

18531822

18541823
class SetCursorWx(backend_tools.SetCursorBase):
18551824
def set_cursor(self, cursor):
1856-
cursor = wx.Cursor(cursord[cursor])
1857-
self.canvas.SetCursor(cursor)
1858-
self.canvas.Update()
1825+
NavigationToolbar2Wx.set_cursor(
1826+
self._make_classic_style_pseudo_toolbar(), cursor)
18591827

18601828

18611829
if 'wxMac' not in wx.PlatformInfo:

0 commit comments

Comments
 (0)
0