Skip to content

Commit 5056729

Browse files
committed
tk backend
1 parent 704c717 commit 5056729

File tree

4 files changed

+207
-11
lines changed

4 files changed

+207
-11
lines changed

examples/user_interfaces/navigation.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import matplotlib
2-
matplotlib.use('GTK3Cairo')
2+
# matplotlib.use('GTK3Cairo')
3+
matplotlib.use('TkAGG')
34
matplotlib.rcParams['toolbar'] = 'navigation'
45
import matplotlib.pyplot as plt
56
from matplotlib.backend_tools import ToolBase

lib/matplotlib/backend_bases.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3421,6 +3421,9 @@ def add_tool(self, tool):
34213421
"""
34223422

34233423
tool_cls = self._get_cls_to_instantiate(tool)
3424+
if tool_cls is False:
3425+
warnings.warn('Impossible to find class for %s' % str(tool))
3426+
return
34243427
name = tool_cls.name
34253428

34263429
if name is None:
@@ -3442,9 +3445,11 @@ def add_tool(self, tool):
34423445
self._keys[k] = name
34433446

34443447
if self.toolbar and tool_cls.position is not None:
3448+
# TODO: better search for images, they are not always in the
3449+
# datapath
34453450
basedir = os.path.join(rcParams['datapath'], 'images')
34463451
if tool_cls.image is not None:
3447-
fname = os.path.join(basedir, tool_cls.image + '.png')
3452+
fname = os.path.join(basedir, tool_cls.image)
34483453
else:
34493454
fname = None
34503455
toggle = issubclass(tool_cls, tools.ToolToggleBase)

lib/matplotlib/backend_tools.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ class ToolHome(ToolBase):
296296

297297
description = 'Reset original view'
298298
name = 'Home'
299-
image = 'home'
299+
image = 'home.png'
300300
keymap = rcParams['keymap.home']
301301
position = -1
302302

@@ -312,7 +312,7 @@ class ToolBack(ToolBase):
312312

313313
description = 'Back to previous view'
314314
name = 'Back'
315-
image = 'back'
315+
image = 'back.png'
316316
keymap = rcParams['keymap.back']
317317
position = -1
318318

@@ -328,7 +328,7 @@ class ToolForward(ToolBase):
328328

329329
description = 'Forward to next view'
330330
name = 'Forward'
331-
image = 'forward'
331+
image = 'forward.png'
332332
keymap = rcParams['keymap.forward']
333333
position = -1
334334

@@ -344,7 +344,7 @@ class ConfigureSubplotsBase(ToolPersistentBase):
344344

345345
description = 'Configure subplots'
346346
name = 'Subplots'
347-
image = 'subplots'
347+
image = 'subplots.png'
348348
position = -1
349349

350350

@@ -353,7 +353,7 @@ class SaveFigureBase(ToolBase):
353353

354354
description = 'Save the figure'
355355
name = 'Save'
356-
image = 'filesave'
356+
image = 'filesave.png'
357357
position = -1
358358
keymap = rcParams['keymap.save']
359359

@@ -363,7 +363,7 @@ class ToolZoom(ToolToggleBase):
363363

364364
description = 'Zoom to rectangle'
365365
name = 'Zoom'
366-
image = 'zoom_to_rect'
366+
image = 'zoom_to_rect.png'
367367
position = -1
368368
keymap = rcParams['keymap.zoom']
369369
cursor = cursors.SELECT_REGION
@@ -597,7 +597,7 @@ class ToolPan(ToolToggleBase):
597597
keymap = rcParams['keymap.pan']
598598
name = 'Pan'
599599
description = 'Pan axes with left mouse, zoom with right'
600-
image = 'move'
600+
image = 'move.png'
601601
position = -1
602602
cursor = cursors.MOVE
603603

lib/matplotlib/backends/backend_tkagg.py

Lines changed: 192 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
from matplotlib.backend_bases import RendererBase, GraphicsContextBase
2121
from matplotlib.backend_bases import FigureManagerBase, FigureCanvasBase
2222
from matplotlib.backend_bases import NavigationToolbar2, cursors, TimerBase
23-
from matplotlib.backend_bases import ShowBase
23+
from matplotlib.backend_bases import ShowBase, ToolbarBase, NavigationBase
24+
from matplotlib.backend_tools import SaveFigureBase, ConfigureSubplotsBase
2425
from matplotlib._pylab_helpers import Gcf
2526

2627
from matplotlib.figure import Figure
@@ -541,9 +542,23 @@ def __init__(self, canvas, num, window):
541542

542543
def notify_axes_change(fig):
543544
'this will be called whenever the current axes is changed'
544-
if self.toolbar != None: self.toolbar.update()
545+
if self.navigation is not None:
546+
self.navigation.update()
547+
elif self.toolbar is not None:
548+
self.toolbar.update()
545549
self.canvas.figure.add_axobserver(notify_axes_change)
546550

551+
def _get_toolbar(self, canvas):
552+
if matplotlib.rcParams['toolbar'] == 'toolbar2':
553+
toolbar = NavigationToolbar2TkAgg(canvas, self.window)
554+
elif matplotlib.rcParams['toolbar'] == 'navigation':
555+
self.navigation = NavigationTk(canvas, ToolbarTk)
556+
toolbar = self.navigation.toolbar
557+
else:
558+
self.navigation = NavigationTk(canvas, None)
559+
toolbar = None
560+
return toolbar
561+
547562
def resize(self, width, height=None):
548563
# before 09-12-22, the resize method takes a single *event*
549564
# parameter. On the other hand, the resize method of other
@@ -871,5 +886,180 @@ def hidetip(self):
871886
if tw:
872887
tw.destroy()
873888

889+
890+
class NavigationTk(NavigationBase):
891+
def __init__(self, *args, **kwargs):
892+
NavigationBase.__init__(self, *args, **kwargs)
893+
894+
def set_cursor(self, cursor):
895+
self.canvas.manager.window.configure(cursor=cursord[cursor])
896+
897+
def draw_rubberband(self, event, caller, x0, y0, x1, y1):
898+
if not self.canvas.widgetlock.available(caller):
899+
return
900+
height = self.canvas.figure.bbox.height
901+
y0 = height - y0
902+
y1 = height - y1
903+
try:
904+
self.lastrect
905+
except AttributeError:
906+
pass
907+
else:
908+
self.canvas._tkcanvas.delete(self.lastrect)
909+
self.lastrect = self.canvas._tkcanvas.create_rectangle(x0, y0, x1, y1)
910+
911+
def remove_rubberband(self, event, caller):
912+
try:
913+
self.lastrect
914+
except AttributeError:
915+
pass
916+
else:
917+
self.canvas._tkcanvas.delete(self.lastrect)
918+
del self.lastrect
919+
920+
921+
class ToolbarTk(ToolbarBase, Tk.Frame):
922+
def __init__(self, manager):
923+
ToolbarBase.__init__(self, manager)
924+
xmin, xmax = self.manager.canvas.figure.bbox.intervalx
925+
height, width = 50, xmax - xmin
926+
Tk.Frame.__init__(self, master=self.manager.window,
927+
width=int(width), height=int(height),
928+
borderwidth=2)
929+
self._toolitems = {}
930+
self._add_message()
931+
932+
def _add_toolitem(self, name, tooltip_text, image_file, position,
933+
toggle):
934+
935+
button = self._Button(name, image_file, toggle)
936+
if tooltip_text is not None:
937+
ToolTip.createToolTip(button, tooltip_text)
938+
self._toolitems[name] = button
939+
940+
def _Button(self, text, file, toggle):
941+
if file is not None:
942+
img_file = os.path.join(rcParams['datapath'], 'images', file)
943+
im = Tk.PhotoImage(master=self, file=img_file)
944+
else:
945+
im = None
946+
947+
if not toggle:
948+
b = Tk.Button(
949+
master=self, text=text, padx=2, pady=2, image=im,
950+
command=lambda: self._button_click(text))
951+
else:
952+
b = Tk.Checkbutton(master=self, text=text, padx=2, pady=2,
953+
image=im, indicatoron=False,
954+
command=lambda: self._button_click(text))
955+
b._ntimage = im
956+
b.pack(side=Tk.LEFT)
957+
return b
958+
959+
def _button_click(self, name):
960+
self.manager.navigation._toolbar_callback(name)
961+
962+
def _toggle(self, name, callback=False):
963+
if name not in self._toolitems:
964+
self.set_message('%s Not in toolbar' % name)
965+
return
966+
self._toolitems[name].toggle()
967+
if callback:
968+
self._button_click(name)
969+
970+
def _add_message(self):
971+
self.message = Tk.StringVar(master=self)
972+
self._message_label = Tk.Label(master=self, textvariable=self.message)
973+
self._message_label.pack(side=Tk.RIGHT)
974+
self.pack(side=Tk.BOTTOM, fill=Tk.X)
975+
976+
def set_message(self, s):
977+
self.message.set(s)
978+
979+
def _remove_toolitem(self, name):
980+
self._toolitems[name].pack_forget()
981+
del self._toolitems[name]
982+
983+
def set_toolitem_visibility(self, name, visible):
984+
pass
985+
986+
987+
class SaveFigureTk(SaveFigureBase):
988+
def trigger(self, *args):
989+
from six.moves import tkinter_tkfiledialog, tkinter_messagebox
990+
filetypes = self.figure.canvas.get_supported_filetypes().copy()
991+
default_filetype = self.figure.canvas.get_default_filetype()
992+
993+
# Tk doesn't provide a way to choose a default filetype,
994+
# so we just have to put it first
995+
default_filetype_name = filetypes[default_filetype]
996+
del filetypes[default_filetype]
997+
998+
sorted_filetypes = list(six.iteritems(filetypes))
999+
sorted_filetypes.sort()
1000+
sorted_filetypes.insert(0, (default_filetype, default_filetype_name))
1001+
1002+
tk_filetypes = [
1003+
(name, '*.%s' % ext) for (ext, name) in sorted_filetypes]
1004+
1005+
# adding a default extension seems to break the
1006+
# asksaveasfilename dialog when you choose various save types
1007+
# from the dropdown. Passing in the empty string seems to
1008+
# work - JDH!
1009+
#defaultextension = self.figure.canvas.get_default_filetype()
1010+
defaultextension = ''
1011+
initialdir = rcParams.get('savefig.directory', '')
1012+
initialdir = os.path.expanduser(initialdir)
1013+
initialfile = self.figure.canvas.get_default_filename()
1014+
fname = tkinter_tkfiledialog.asksaveasfilename(
1015+
master=self.figure.canvas.manager.window,
1016+
title='Save the figure',
1017+
filetypes=tk_filetypes,
1018+
defaultextension=defaultextension,
1019+
initialdir=initialdir,
1020+
initialfile=initialfile,
1021+
)
1022+
1023+
if fname == "" or fname == ():
1024+
return
1025+
else:
1026+
if initialdir == '':
1027+
# explicitly missing key or empty str signals to use cwd
1028+
rcParams['savefig.directory'] = initialdir
1029+
else:
1030+
# save dir for next time
1031+
rcParams['savefig.directory'] = os.path.dirname(
1032+
six.text_type(fname))
1033+
try:
1034+
# This method will handle the delegation to the correct type
1035+
self.figure.canvas.print_figure(fname)
1036+
except Exception as e:
1037+
tkinter_messagebox.showerror("Error saving file", str(e))
1038+
1039+
1040+
class ConfigureSubplotsTk(ConfigureSubplotsBase):
1041+
def __init__(self, *args, **kwargs):
1042+
ConfigureSubplotsBase.__init__(self, *args, **kwargs)
1043+
toolfig = Figure(figsize=(6, 3))
1044+
self.window = Tk.Tk()
1045+
1046+
canvas = FigureCanvasTkAgg(toolfig, master=self.window)
1047+
toolfig.subplots_adjust(top=0.9)
1048+
_tool = SubplotTool(self.figure, toolfig)
1049+
canvas.show()
1050+
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
1051+
self.window.protocol("WM_DELETE_WINDOW", self.destroy)
1052+
1053+
def trigger(self, event):
1054+
self.window.lift()
1055+
1056+
def destroy(self, *args, **kwargs):
1057+
self.unregister()
1058+
self.window.destroy()
1059+
1060+
1061+
SaveFigure = SaveFigureTk
1062+
ConfigureSubplots = ConfigureSubplotsTk
1063+
8741064
FigureCanvas = FigureCanvasTkAgg
8751065
FigureManager = FigureManagerTkAgg

0 commit comments

Comments
 (0)