8000 Share FigureManager class between gtk3 and gtk4. by anntzer · Pull Request #22079 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

Share FigureManager class between gtk3 and gtk4. #22079

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 133 additions & 2 deletions lib/matplotlib/backends/_backend_gtk.py
8000
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
"""

import logging
import sys

import matplotlib as mpl
from matplotlib import _api, backend_tools, cbook
from matplotlib.backend_bases import _Backend, NavigationToolbar2, TimerBase
from matplotlib._pylab_helpers import Gcf
from matplotlib.backend_bases import (
_Backend, FigureManagerBase, NavigationToolbar2, TimerBase)
from matplotlib.backend_tools import Cursors

# The GTK3/GTK4 backends will have already called `gi.require_version` to set
# the desired GTK.
from gi.repository import Gio, GLib, Gtk
from gi.repository import Gdk, Gio, GLib, Gtk


_log = logging.getLogger(__name__)
Expand Down Expand Up @@ -109,6 +112,134 @@ def _on_timer(self):
return False


class _FigureManagerGTK(FigureManagerBase):
"""
Attributes
----------
canvas : `FigureCanvas`
The FigureCanvas instance
num : int or str
The Figure number
toolbar : Gtk.Toolbar or Gtk.Box
The toolbar
vbox : Gtk.VBox
The Gtk.VBox containing the canvas and toolbar
window : Gtk.Window
The Gtk.Window
"""

def __init__(self, canvas, num):
self._gtk_ver = gtk_ver = Gtk.get_major_version()

app = _create_application()
self.window = Gtk.Window()
app.add_window(self.window)
super().__init__(canvas, num)

if gtk_ver == 3:
self.window.set_wmclass("matplotlib", "Matplotlib")
icon_ext = "png" if sys.platform == "win32" else "svg"
self.window.set_icon_from_file(
str(cbook._get_data_path(f"images/matplotlib.{icon_ext}")))

self.vbox = Gtk.Box()
self.vbox.set_property("orientation", Gtk.Orientation.VERTICAL)

if gtk_ver == 3:
self.window.add(self.vbox)
self.vbox.show()
self.canvas.show()
self.vbox.pack_start(self.canvas, True, True, 0)
elif gtk_ver == 4:
self.window.set_child(self.vbox)
self.vbox.prepend(self.canvas)

# calculate size for window
w, h = self.canvas.get_width_height()

if self.toolbar is not None:
if gtk_ver == 3:
self.toolbar.show()
self.vbox.pack_end(self.toolbar, False, False, 0)
elif gtk_ver == 4:
sw = Gtk.ScrolledWindow(vscrollbar_policy=Gtk.PolicyType.NEVER)
sw.set_child(self.toolbar)
self.vbox.append(sw)
min_size, nat_size = self.toolbar.get_preferred_size()
h += nat_size.height

self.window.set_default_size(w, h)

self._destroying = False
self.window.connect("destroy", lambda *args: Gcf.destroy(self))
self.window.connect({3: "delete_event", 4: "close-request"}[gtk_ver],
lambda *args: Gcf.destroy(self))
if mpl.is_interactive():
self.window.show()
self.canvas.draw_idle()

self.canvas.grab_focus()

def destroy(self, *args):
if self._destroying:
# Otherwise, this can be called twice when the user presses 'q',
# which calls Gcf.destroy(self), then this destroy(), then triggers
# Gcf.destroy(self) once again via
# `connect("destroy", lambda *args: Gcf.destroy(self))`.
return
self._destroying = True
self.window.destroy()
self.canvas.destroy()

def show(self):
# show the figure window
self.window.show()
self.canvas.draw()
if mpl.rcParams["figure.raise_window"]:
meth_name = {3: "get_window", 4: "get_surface"}[self._gtk_ver]
if getattr(self.window, meth_name)():
self.window.present()
else:
# If this is called by a callback early during init,
# self.window (a GtkWindow) may not have an associated
# low-level GdkWindow (on GTK3) or GdkSurface (on GTK4) yet,
# and present() would crash.
_api.warn_external("Cannot raise window yet to be setup")

def full_screen_toggle(self):
is_fullscreen = {
3: lambda w: (w.get_window().get_state()
& Gdk.WindowState.FULLSCREEN),
4: lambda w: w.is_fullscreen(),
}[self._gtk_ver]
if is_fullscreen(self.window):
self.window.unfullscreen()
else:
self.window.fullscreen()

def get_window_title(self):
return self.window.get_title()

def set_window_title(self, title):
self.window.set_title(title)

def resize(self, width, height):
width = int(width / self.canvas.device_pixel_ratio)
height = int(height / self.canvas.device_pixel_ratio)
if self.toolbar:
toolbar_size = self.toolbar.size_request()
height += toolbar_size.height
canvas_size = self.canvas.get_allocation()
if self._gtk_ver >= 4 or canvas_size.width == canvas_size.height == 1:
# A canvas size of (1, 1) cannot exist in most cases, because
# window decorations would prevent such a small window. This call
# must be before the window has been mapped and widgets have been
# sized, so just change the window's starting size.
self.window.set_default_size(width, height)
else:
self.window.resize(width, height)


class _NavigationToolbar2GTK(NavigationToolbar2):
# Must be implemented in GTK3/GTK4 backends:
# * __init__
Expand Down
137 changes: 7 additions & 130 deletions lib/matplotlib/backends/backend_gtk3.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@

import matplotlib as mpl
from matplotlib import _api, backend_tools, cbook
from matplotlib._pylab_helpers import Gcf
from matplotlib.backend_bases import (
FigureCanvasBase, FigureManagerBase, ToolContainerBase)
from matplotlib.backend_bases import FigureCanvasBase, ToolContainerBase
from matplotlib.backend_tools import Cursors
from matplotlib.figure import Figure

Expand All @@ -29,7 +27,7 @@
from gi.repository import Gio, GLib, GObject, Gtk, Gdk
from . import _backend_gtk
from ._backend_gtk import (
backend_version, _BackendGTK, _NavigationToolbar2GTK,
backend_version, _BackendGTK, _FigureManagerGTK, _NavigationToolbar2GTK,
TimerGTK as TimerGTK3,
)

Expand Down Expand Up @@ -293,130 +291,6 @@ def flush_events(self):
context.iteration(True)


class FigureManagerGTK3(FigureManagerBase):
"""
Attributes
----------
canvas : `FigureCanvas`
The FigureCanvas instance
num : int or str
The Figure number
toolbar : Gtk.Toolbar
The toolbar
vbox : Gtk.VBox
The Gtk.VBox containing the canvas and toolbar
window : Gtk.Window
The Gtk.Window
"""

def __init__(self, canvas, num):
app = _backend_gtk._create_application()
self.window = Gtk.Window()
app.add_window(self.window)
super().__init__(canvas, num)

self.window.set_wmclass("matplotlib", "Matplotlib")
icon_ext = "png" if sys.platform == "win32" else "svg"
self.window.set_icon_from_file(
str(cbook._get_data_path(f"images/matplotlib.{icon_ext}")))

self.vbox = Gtk.Box()
self.vbox.set_property("orientation", Gtk.Orientation.VERTICAL)
self.window.add(self.vbox)
self.vbox.show()

self.canvas.show()

self.vbox.pack_start(self.canvas, True, True, 0)
# calculate size for window
w, h = self.canvas.get_width_height()

if self.toolbar is not None:
self.toolbar.show()
self.vbox.pack_end(self.toolbar, False, False, 0)
min_size, nat_size = self.toolbar.get_preferred_size()
h += nat_size.height

self.window.set_default_size(w, h)

self._destroying = False
self.window.connect("destroy", lambda *args: Gcf.destroy(self))
self.window.connect("delete_event", lambda *args: Gcf.destroy(self))
if mpl.is_interactive():
self.window.show()
self.canvas.draw_idle()

self.canvas.grab_focus()

def destroy(self, *args):
if self._destroying:
# Otherwise, this can be called twice when the user presses 'q',
# which calls Gcf.destroy(self), then this destroy(), then triggers
# Gcf.destroy(self) once again via
# `connect("destroy", lambda *args: Gcf.destroy(self))`.
return
self._destroying = True
self.vbox.destroy()
self.window.destroy()
self.canvas.destroy()
if self.toolbar:
self.toolbar.destroy()

def show(self):
# show the figure window
self.window.show()
self.canvas.draw()
if mpl.rcParams['figure.raise_window']:
if self.window.get_window():
self.window.present()
else:
# If this is called by a callback early during init,
# self.window (a GtkWindow) may not have an associated
# low-level GdkWindow (self.window.get_window()) yet, and
# present() would crash.
_api.warn_external("Cannot raise window yet to be setup")

def full_screen_toggle(self):
if self.window.get_window().get_state() & Gdk.WindowState.FULLSCREEN:
self.window.unfullscreen()
else:
self.window.fullscreen()

def _get_toolbar(self):
# must be inited after the window, drawingArea and figure
# attrs are set
if mpl.rcParams['toolbar'] == 'toolbar2':
toolbar = NavigationToolbar2GTK3(self.canvas)
elif mpl.rcParams['toolbar'] == 'toolmanager':
toolbar = ToolbarGTK3(self.toolmanager)
else:
toolbar = None
return toolbar

def get_window_title(self):
return self.window.get_title()

def set_window_title(self, title):
self.window.set_title(title)

def resize(self, width, height):
"""Set the canvas size in pixels."""
width = int(width / self.canvas.device_pixel_ratio)
height = int(height / self.canvas.device_pixel_ratio)
if self.toolbar:
toolbar_size = self.toolbar.size_request()
height += toolbar_size.height
canvas_size = self.canvas.get_allocation()
if canvas_size.width == canvas_size.height == 1:
# A canvas size of (1, 1) cannot exist in most cases, because
# window decorations would prevent such a small window. This call
# must be before the window has been mapped and widgets have been
# sized, so just change the window's starting size.
self.window.set_default_size(width, height)
else:
self.window.resize(width, height)


class NavigationToolbar2GTK3(_NavigationToolbar2GTK, Gtk.Toolbar):
@_api.delete_parameter("3.6", "window")
def __init__(self, canvas, window=None):
Expand Down Expand Up @@ -730,8 +604,11 @@ def error_msg_gtk(msg, parent=None):
FigureCanvasGTK3, _backend_gtk.ConfigureSubplotsGTK)
backend_tools._register_tool_class(
FigureCanvasGTK3, _backend_gtk.RubberbandGTK)
FigureManagerGTK3._toolbar2_class = NavigationToolbar2GTK3
FigureManagerGTK3._toolmanager_toolbar_class = ToolbarGTK3


class FigureManagerGTK3(_FigureManagerGTK):
_toolbar2_class = NavigationToolbar2GTK3
_toolmanager_toolbar_class = ToolbarGTK3


@_BackendGTK.export
Expand Down
Loading
31A6
0