Skip to content

Commit 1d6fd65

Browse files
committed
Combine common GTK toolbar code.
1 parent e135227 commit 1d6fd65

File tree

3 files changed

+90
-140
lines changed

3 files changed

+90
-140
lines changed

lib/matplotlib/backends/_backend_gtk.py

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
import logging
66

77
import matplotlib as mpl
8-
from matplotlib import cbook
8+
from matplotlib import backend_tools, cbook
99
from matplotlib.backend_bases import (
10-
_Backend, TimerBase,
10+
_Backend, NavigationToolbar2, TimerBase,
1111
)
1212

1313
# The GTK3/GTK4 backends will have already called `gi.require_version` to set
@@ -98,6 +98,68 @@ def _on_timer(self):
9898
return False
9999

100100

101+
class _NavigationToolbar2GTK(NavigationToolbar2):
102+
# Must be implemented in GTK3/GTK4 backends:
103+
# * __init__
104+
# * save_figure
105+
106+
def set_message(self, s):
107+
escaped = GLib.markup_escape_text(s)
108+
self.message.set_markup(f'<small>{escaped}</small>')
109+
110+
def draw_rubberband(self, event, x0, y0, x1, y1):
111+
height = self.canvas.figure.bbox.height
112+
y1 = height - y1
113+
y0 = height - y0
114+
rect = [int(val) for val in (x0, y0, x1 - x0, y1 - y0)]
115+
self.canvas._draw_rubberband(rect)
116+
117+
def remove_rubberband(self):
118+
self.canvas._draw_rubberband(None)
119+
120+
def _update_buttons_checked(self):
121+
for name, active in [("Pan", "PAN"), ("Zoom", "ZOOM")]:
122+
button = self._gtk_ids.get(name)
123+
if button:
124+
with button.handler_block(button._signal_handler):
125+
button.set_active(self.mode.name == active)
126+
127+
def pan(self, *args):
128+
super().pan(*args)
129+
self._update_buttons_checked()
130+
131+
def zoom(self, *args):
132+
super().zoom(*args)
133+
self._update_buttons_checked()
134+
135+
def set_history_buttons(self):
136+
can_backward = self._nav_stack._pos > 0
137+
can_forward = self._nav_stack._pos < len(self._nav_stack._elements) - 1
138+
if 'Back' in self._gtk_ids:
139+
self._gtk_ids['Back'].set_sensitive(can_backward)
140+
if 'Forward' in self._gtk_ids:
141+
self._gtk_ids['Forward'].set_sensitive(can_forward)
142+
143+
144+
class RubberbandGTK(backend_tools.RubberbandBase):
145+
def draw_rubberband(self, x0, y0, x1, y1):
146+
_NavigationToolbar2GTK.draw_rubberband(
147+
self._make_classic_style_pseudo_toolbar(), None, x0, y0, x1, y1)
148+
149+
def remove_rubberband(self):
150+
_NavigationToolbar2GTK.remove_rubberband(
151+
self._make_classic_style_pseudo_toolbar())
152+
153+
154+
class ConfigureSubplotsGTK(backend_tools.ConfigureSubplotsBase, Gtk.Window):
155+
def _get_canvas(self, fig):
156+
return self.canvas.__class__(fig)
157+
158+
def trigger(self, *args):
159+
_NavigationToolbar2GTK.configure_subplots(
160+
self._make_classic_style_pseudo_toolbar(), None)
161+
162+
101163
class _BackendGTK(_Backend):
102164
@staticmethod
103165
def mainloop():

lib/matplotlib/backends/backend_gtk3.py

Lines changed: 21 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@
3131
from gi.repository import Gio, GLib, GObject, Gtk, Gdk
3232
from ._backend_gtk import (
3333
_create_application, _shutdown_application,
34-
backend_version, _BackendGTK,
34+
backend_version, _BackendGTK, _NavigationToolbar2GTK,
3535
TimerGTK as TimerGTK3,
36+
ConfigureSubplotsGTK as ConfigureSubplotsGTK3,
37+
RubberbandGTK as RubberbandGTK3,
3638
)
3739

3840

@@ -291,7 +293,7 @@ class FigureManagerGTK3(FigureManagerBase):
291293
num : int or str
292294
The Figure number
293295
toolbar : Gtk.Toolbar
294-
The Gtk.Toolbar
296+
The toolbar
295297
vbox : Gtk.VBox
296298
The Gtk.VBox containing the canvas and toolbar
297299
window : Gtk.Window
@@ -418,7 +420,7 @@ def resize(self, width, height):
418420
self.window.resize(width, height)
419421

420422

421-
class NavigationToolbar2GTK3(NavigationToolbar2, Gtk.Toolbar):
423+
class NavigationToolbar2GTK3(_NavigationToolbar2GTK, Gtk.Toolbar):
422424
def __init__(self, canvas, window):
423425
self.win = window
424426
GObject.GObject.__init__(self)
@@ -435,21 +437,16 @@ def __init__(self, canvas, window):
435437
str(cbook._get_data_path('images',
436438
f'{image_file}-symbolic.svg'))),
437439
Gtk.IconSize.LARGE_TOOLBAR)
438-
self._gtk_ids[text] = tbutton = (
440+
self._gtk_ids[text] = button = (
439441
Gtk.ToggleToolButton() if callback in ['zoom', 'pan'] else
440442
Gtk.ToolButton())
441-
tbutton.set_label(text)
442-
tbutton.set_icon_widget(image)
443-
self.insert(tbutton, -1)
443+
button.set_label(text)
444+
button.set_icon_widget(image)
444445
# Save the handler id, so that we can block it as needed.
445-
tbutton._signal_handler = tbutton.connect(
446+
button._signal_handler = button.connect(
446447
'clicked', getattr(self, callback))
447-
tbutton.set_tooltip_text(tooltip_text)
448-
449-
toolitem = Gtk.SeparatorToolItem()
450-
self.insert(toolitem, -1)
451-
toolitem.set_draw(False)
452-
toolitem.set_expand(True)
448+
button.set_tooltip_text(tooltip_text)
449+
self.insert(button, -1)
453450

454451
# This filler item ensures the toolbar is always at least two text
455452
# lines high. Otherwise the canvas gets redrawn as the mouse hovers
@@ -460,6 +457,7 @@ def __init__(self, canvas, window):
460457
label = Gtk.Label()
461458
label.set_markup(
462459
'<small>\N{NO-BREAK SPACE}\n\N{NO-BREAK SPACE}</small>')
460+
toolitem.set_expand(True) # Push real message to the right.
463461
toolitem.add(label)
464462

465463
toolitem = Gtk.ToolItem()
@@ -471,35 +469,6 @@ def __init__(self, canvas, window):
471469

472470
NavigationToolbar2.__init__(self, canvas)
473471

474-
def set_message(self, s):
475-
escaped = GLib.markup_escape_text(s)
476-
self.message.set_markup(f'<small>{escaped}</small>')
477-
478-
def draw_rubberband(self, event, x0, y0, x1, y1):
479-
height = self.canvas.figure.bbox.height
480-
y1 = height - y1
481-
y0 = height - y0
482-
rect = [int(val) for val in (x0, y0, x1 - x0, y1 - y0)]
483-
self.canvas._draw_rubberband(rect)
484-
485-
def remove_rubberband(self):
486-
self.canvas._draw_rubberband(None)
487-
488-
def _update_buttons_checked(self):
489-
for name, active in [("Pan", "PAN"), ("Zoom", "ZOOM")]:
490-
button = self._gtk_ids.get(name)
491-
if button:
492-
with button.handler_block(button._signal_handler):
493-
button.set_active(self.mode.name == active)
494-
495-
def pan(self, *args):
496-
super().pan(*args)
497-
self._update_buttons_checked()
498-
499-
def zoom(self, *args):
500-
super().zoom(*args)
501-
self._update_buttons_checked()
502-
503472
def save_figure(self, *args):
504473
dialog = Gtk.FileChooserDialog(
505474
title="Save the figure",
@@ -544,14 +513,6 @@ def on_notify_filter(*args):
544513
except Exception as e:
545514
error_msg_gtk(str(e), parent=self)
546515

547-
def set_history_buttons(self):
548-
can_backward = self._nav_stack._pos > 0
549-
can_forward = self._nav_stack._pos < len(self._nav_stack._elements) - 1
550-
if 'Back' in self._gtk_ids:
551-
self._gtk_ids['Back'].set_sensitive(can_backward)
552-
if 'Forward' in self._gtk_ids:
553-
self._gtk_ids['Forward'].set_sensitive(can_forward)
554-
555516

556517
class ToolbarGTK3(ToolContainerBase, Gtk.Box):
557518
_icon_extension = '-symbolic.svg'
@@ -569,26 +530,26 @@ def __init__(self, toolmanager):
569530
def add_toolitem(self, name, group, position, image_file, description,
570531
toggle):
571532
if toggle:
572-
tbutton = Gtk.ToggleToolButton()
533+
button = Gtk.ToggleToolButton()
573534
else:
574-
tbutton = Gtk.ToolButton()
575-
tbutton.set_label(name)
535+
button = Gtk.ToolButton()
536+
button.set_label(name)
576537

577538
if image_file is not None:
578539
image = Gtk.Image.new_from_gicon(
579540
Gio.Icon.new_for_string(image_file),
580541
Gtk.IconSize.LARGE_TOOLBAR)
581-
tbutton.set_icon_widget(image)
542+
button.set_icon_widget(image)
582543

583544
if position is None:
584545
position = -1
585546

586-
self._add_button(tbutton, group, position)
587-
signal = tbutton.connect('clicked', self._call_tool, name)
588-
tbutton.set_tooltip_text(description)
589-
tbutton.show_all()
547+
self._add_button(button, group, position)
548+
signal = button.connect('clicked', self._call_tool, name)
549+
button.set_tooltip_text(description)
550+
button.show_all()
590551
self._toolitems.setdefault(name, [])
591-
self._toolitems[name].append((tbutton, signal))
552+
self._toolitems[name].append((button, signal))
592553

593554
def _add_button(self, button, group, position):
594555
if group not in self._groups:
@@ -633,16 +594,6 @@ def set_message(self, s):
633594
self._message.set_label(s)
634595

635596

636-
class RubberbandGTK3(backend_tools.RubberbandBase):
637-
def draw_rubberband(self, x0, y0, x1, y1):
638-
NavigationToolbar2GTK3.draw_rubberband(
639-
self._make_classic_style_pseudo_toolbar(), None, x0, y0, x1, y1)
640-
641-
def remove_rubberband(self):
642-
NavigationToolbar2GTK3.remove_rubberband(
643-
self._make_classic_style_pseudo_toolbar())
644-
645-
646597
class SaveFigureGTK3(backend_tools.SaveFigureBase):
647598
def trigger(self, *args, **kwargs):
648599

@@ -659,15 +610,6 @@ def set_cursor(self, cursor):
659610
self._make_classic_style_pseudo_toolbar(), cursor)
660611

661612

662-
class ConfigureSubplotsGTK3(backend_tools.ConfigureSubplotsBase, Gtk.Window):
663-
def _get_canvas(self, fig):
664-
return self.canvas.__class__(fig)
665-
666-
def trigger(self, *args):
667-
NavigationToolbar2GTK3.configure_subplots(
668-
self._make_classic_style_pseudo_toolbar(), None)
669-
670-
671613
class HelpGTK3(backend_tools.ToolHelpBase):
672614
def _normalize_shortcut(self, key):
673615
"""

lib/matplotlib/backends/backend_gtk4.py

Lines changed: 5 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@
3232
from gi.repository import Gio, GLib, GObject, Gtk, Gdk, GdkPixbuf
3333
from ._backend_gtk import (
3434
_create_application, _shutdown_application,
35-
backend_version, _BackendGTK,
35+
backend_version, _BackendGTK, _NavigationToolbar2GTK,
3636
TimerGTK as TimerGTK4,
37+
ConfigureSubplotsGTK as ConfigureSubplotsGTK4,
38+
RubberbandGTK as RubberbandGTK4,
3739
)
3840

3941

@@ -249,7 +251,7 @@ class FigureManagerGTK4(FigureManagerBase):
249251
num : int or str
250252
The Figure number
251253
toolbar : Gtk.Box
252-
The Gtk.Box
254+
The toolbar
253255
vbox : Gtk.VBox
254256
The Gtk.VBox containing the canvas and toolbar
255257
window : Gtk.Window
@@ -368,7 +370,7 @@ def resize(self, width, height):
368370
self.window.resize(width, height)
369371

370372

371-
class NavigationToolbar2GTK4(NavigationToolbar2, Gtk.Box):
373+
class NavigationToolbar2GTK4(_NavigationToolbar2GTK, Gtk.Box):
372374
def __init__(self, canvas, window):
373375
self.win = window
374376
Gtk.Box.__init__(self)
@@ -411,35 +413,6 @@ def __init__(self, canvas, window):
411413

412414
NavigationToolbar2.__init__(self, canvas)
413415

414-
def set_message(self, s):
415-
escaped = GLib.markup_escape_text(s)
416-
self.message.set_markup(f'<small>{escaped}</small>')
417-
418-
def draw_rubberband(self, event, x0, y0, x1, y1):
419-
height = self.canvas.figure.bbox.height
420-
y1 = height - y1
421-
y0 = height - y0
422-
rect = [int(val) for val in (x0, y0, x1 - x0, y1 - y0)]
423-
self.canvas._draw_rubberband(rect)
424-
425-
def remove_rubberband(self):
426-
self.canvas._draw_rubberband(None)
427-
428-
def _update_buttons_checked(self):
429-
for name, active in [("Pan", "PAN"), ("Zoom", "ZOOM")]:
430-
button = self._gtk_ids.get(name)
431-
if button:
432-
with button.handler_block(button._signal_handler):
433-
button.set_active(self.mode.name == active)
434-
435-
def pan(self, *args):
436-
super().pan(*args)
437-
self._update_buttons_checked()
438-
439-
def zoom(self, *args):
440-
super().zoom(*args)
441-
self._update_buttons_checked()
442-
443416
def save_figure(self, *args):
444417
dialog = Gtk.FileChooserNative(
445418
title='Save the figure',
@@ -502,14 +475,6 @@ def on_response(dialog, response):
502475

503476
dialog.show()
504477

505-
def set_history_buttons(self):
506-
can_backward = self._nav_stack._pos > 0
507-
can_forward = self._nav_stack._pos < len(self._nav_stack._elements) - 1
508-
if 'Back' in self._gtk_ids:
509-
self._gtk_ids['Back'].set_sensitive(can_backward)
510-
if 'Forward' in self._gtk_ids:
511-
self._gtk_ids['Forward'].set_sensitive(can_forward)
512-
513478

514479
class ToolbarGTK4(ToolContainerBase, Gtk.Box):
515480
_icon_extension = '-symbolic.svg'
@@ -611,16 +576,6 @@ def set_message(self, s):
611576
self._message.set_label(s)
612577

613578

614-
class RubberbandGTK4(backend_tools.RubberbandBase):
615-
def draw_rubberband(self, x0, y0, x1, y1):
616-
NavigationToolbar2GTK4.draw_rubberband(
617-
self._make_classic_style_pseudo_toolbar(), None, x0, y0, x1, y1)
618-
619-
def remove_rubberband(self):
620-
NavigationToolbar2GTK4.remove_rubberband(
621-
self._make_classic_style_pseudo_toolbar())
622-
623-
624579
class SaveFigureGTK4(backend_tools.SaveFigureBase):
625580
def trigger(self, *args, **kwargs):
626581

@@ -630,15 +585,6 @@ class PseudoToolbar:
630585
return NavigationToolbar2GTK4.save_figure(PseudoToolbar())
631586

632587

633-
class ConfigureSubplotsGTK4(backend_tools.ConfigureSubplotsBase, Gtk.Window):
634-
def _get_canvas(self, fig):
635-
return self.canvas.__class__(fig)
636-
637-
def trigger(self, *args):
638-
NavigationToolbar2GTK4.configure_subplots(
639-
self._make_classic_style_pseudo_toolbar(), None)
640-
641-
642588
class HelpGTK4(backend_tools.ToolHelpBase):
643589
def _normalize_shortcut(self, key):
644590
"""

0 commit comments

Comments
 (0)