Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Backport PR #20321 on branch v3.5.x (Add a GTK4 backend.) #20969

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
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
8 changes: 6 additions & 2 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,12 @@ per-file-ignores =
examples/ticks_and_spines/date_concise_formatter.py: E402
examples/user_interfaces/embedding_in_gtk3_panzoom_sgskip.py: E402
examples/user_interfaces/embedding_in_gtk3_sgskip.py: E402
examples/user_interfaces/gtk_spreadsheet_sgskip.py: E402
examples/user_interfaces/embedding_in_gtk4_panzoom_sgskip.py: E402
examples/user_interfaces/embedding_in_gtk4_sgskip.py: E402
examples/user_interfaces/gtk3_spreadsheet_sgskip.py: E402
examples/user_interfaces/gtk4_spreadsheet_sgskip.py: E402
examples/user_interfaces/mpl_with_glade3_sgskip.py: E402
examples/user_interfaces/pylab_with_gtk_sgskip.py: E402
examples/user_interfaces/pylab_with_gtk3_sgskip.py: E402
examples/user_interfaces/pylab_with_gtk4_sgskip.py: E402
examples/user_interfaces/toolmanager_sgskip.py: E402
examples/userdemo/pgf_preamble_sgskip.py: E402
16 changes: 16 additions & 0 deletions doc/api/backend_gtk4_api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
**NOTE** These backends are not documented here, to avoid adding a dependency
to building the docs.

.. redirect-from:: /api/backend_gtk4agg_api
.. redirect-from:: /api/backend_gtk4cairo_api


:mod:`matplotlib.backends.backend_gtk4agg`
==========================================

.. module:: matplotlib.backends.backend_gtk4agg

:mod:`matplotlib.backends.backend_gtk4cairo`
============================================

.. module:: matplotlib.backends.backend_gtk4cairo
1 change: 1 addition & 0 deletions doc/api/index_backend_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
backend_agg_api.rst
backend_cairo_api.rst
backend_gtk3_api.rst
backend_gtk4_api.rst
backend_nbagg_api.rst
backend_pdf_api.rst
backend_pgf_api.rst
Expand Down
4 changes: 2 additions & 2 deletions doc/devel/dependencies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ and the capabilities they provide.

* Tk_ (>= 8.4, != 8.6.0 or 8.6.1) [#]_: for the Tk-based backends.
* PyQt6_ (>= 6.1), PySide6_, PyQt5_, or PySide2_: for the Qt-based backends.
* PyGObject_: for the GTK3-based backends [#]_.
* PyGObject_: for the GTK-based backends [#]_.
* wxPython_ (>= 4) [#]_: for the wx-based backends.
* pycairo_ (>= 1.11.0) or cairocffi_ (>= 0.8): for the GTK3 and/or cairo-based
* pycairo_ (>= 1.11.0) or cairocffi_ (>= 0.8): for the GTK and/or cairo-based
backends.
* Tornado_: for the WebAgg backend.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
win = Gtk.Window()
win.connect("delete-event", Gtk.main_quit)
win.set_default_size(400, 300)
win.set_title("Embedding in GTK")
win.set_title("Embedding in GTK3")

fig = Figure(figsize=(5, 4), dpi=100)
ax = fig.add_subplot(1, 1, 1)
Expand Down
2 changes: 1 addition & 1 deletion examples/user_interfaces/embedding_in_gtk3_sgskip.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
win = Gtk.Window()
win.connect("delete-event", Gtk.main_quit)
win.set_default_size(400, 300)
win.set_title("Embedding in GTK")
win.set_title("Embedding in GTK3")

fig = Figure(figsize=(5, 4), dpi=100)
ax = fig.add_subplot()
Expand Down
51 changes: 51 additions & 0 deletions examples/user_interfaces/embedding_in_gtk4_panzoom_sgskip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
===========================================
Embedding in GTK4 with a navigation toolbar
===========================================

Demonstrate NavigationToolbar with GTK4 accessed via pygobject.
"""

import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk

from matplotlib.backends.backend_gtk4 import (
NavigationToolbar2GTK4 as NavigationToolbar)
from matplotlib.backends.backend_gtk4agg import (
FigureCanvasGTK4Agg as FigureCanvas)
from matplotlib.figure import Figure
import numpy as np


def on_activate(app):
win = Gtk.ApplicationWindow(application=app)
win.set_default_size(400, 300)
win.set_title("Embedding in GTK4")

fig = Figure(figsize=(5, 4), dpi=100)
ax = fig.add_subplot(1, 1, 1)
t = np.arange(0.0, 3.0, 0.01)
s = np.sin(2*np.pi*t)
ax.plot(t, s)

vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
win.set_child(vbox)

# Add canvas to vbox
canvas = FigureCanvas(fig) # a Gtk.DrawingArea
canvas.set_hexpand(True)
canvas.set_vexpand(True)
vbox.append(canvas)

# Create toolbar
toolbar = NavigationToolbar(canvas, win)
vbox.append(toolbar)

win.show()


app = Gtk.Application(
application_id='org.matplotlib.examples.EmbeddingInGTK4PanZoom')
app.connect('activate', on_activate)
app.run(None)
45 changes: 45 additions & 0 deletions examples/user_interfaces/embedding_in_gtk4_sgskip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""
=================
Embedding in GTK4
=================

Demonstrate adding a FigureCanvasGTK4Agg widget to a Gtk.ScrolledWindow using
GTK4 accessed via pygobject.
"""

import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk

from matplotlib.backends.backend_gtk4agg import (
FigureCanvasGTK4Agg as FigureCanvas)
from matplotlib.figure import Figure
import numpy as np


def on_activate(app):
win = Gtk.ApplicationWindow(application=app)
win.set_default_size(400, 300)
win.set_title("Embedding in GTK4")

fig = Figure(figsize=(5, 4), dpi=100)
ax = fig.add_subplot()
t = np.arange(0.0, 3.0, 0.01)
s = np.sin(2*np.pi*t)
ax.plot(t, s)

# A scrolled margin goes outside the scrollbars and viewport.
sw = Gtk.ScrolledWindow(margin_top=10, margin_bottom=10,
margin_start=10, margin_end=10)
win.set_child(sw)

canvas = FigureCanvas(fig) # a Gtk.DrawingArea
canvas.set_size_request(800, 600)
sw.set_child(canvas)

win.show()


app = Gtk.Application(application_id='org.matplotlib.examples.EmbeddingInGTK4')
app.connect('activate', on_activate)
app.run(None)
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
===============
GTK Spreadsheet
===============
================
GTK3 Spreadsheet
================

Example of embedding Matplotlib in an application and interacting with a
treeview to store data. Double click on an entry to update plot data.
Expand Down
91 changes: 91 additions & 0 deletions examples/user_interfaces/gtk4_spreadsheet_sgskip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"""
================
GTK4 Spreadsheet
================

Example of embedding Matplotlib in an application and interacting with a
treeview to store data. Double click on an entry to update plot data.
"""

import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Gdk', '4.0')
from gi.repository import Gtk

from matplotlib.backends.backend_gtk4agg import FigureCanvas # or gtk4cairo.

from numpy.random import random
from matplotlib.figure import Figure


class DataManager(Gtk.ApplicationWindow):
num_rows, num_cols = 20, 10

data = random((num_rows, num_cols))

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_default_size(600, 600)

self.set_title('GtkListStore demo')

vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, homogeneous=False,
spacing=8)
self.set_child(vbox)

label = Gtk.Label(label='Double click a row to plot the data')
vbox.append(label)

sw = Gtk.ScrolledWindow()
sw.set_has_frame(True)
sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
sw.set_hexpand(True)
sw.set_vexpand(True)
vbox.append(sw)

model = self.create_model()
self.treeview = Gtk.TreeView(model=model)
self.treeview.connect('row-activated', self.plot_row)
sw.set_child(self.treeview)

# Matplotlib stuff
fig = Figure(figsize=(6, 4), constrained_layout=True)

self.canvas = FigureCanvas(fig) # a Gtk.DrawingArea
self.canvas.set_hexpand(True)
self.canvas.set_vexpand(True)
vbox.append(self.canvas)
ax = fig.add_subplot()
self.line, = ax.plot(self.data[0, :], 'go') # plot the first row

self.add_columns()

def plot_row(self, treeview, path, view_column):
ind, = path # get the index into data
points = self.data[ind, :]
self.line.set_ydata(points)
self.canvas.draw()

def add_columns(self):
for i in range(self.num_cols):
column = Gtk.TreeViewColumn(str(i), Gtk.CellRendererText(), text=i)
self.treeview.append_column(column)

def create_model(self):
types = [float] * self.num_cols
store = Gtk.ListStore(*types)
for row in self.data:
# Gtk.ListStore.append is broken in PyGObject, so insert manually.
it = store.insert(-1)
store.set(it, {i: val for i, val in enumerate(row)})
return store


def on_activate(app):
manager = DataManager(application=app)
manager.show()


app = Gtk.Application(application_id='org.matplotlib.examples.GTK4Spreadsheet')
app.connect('activate', on_activate)
app.run()
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
===============
pyplot with GTK
===============
================
pyplot with GTK3
================

An example of how to use pyplot to manage your figure windows, but modify the
GUI by accessing the underlying GTK widgets.
Expand Down
51 changes: 51 additions & 0 deletions examples/user_interfaces/pylab_with_gtk4_sgskip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
================
pyplot with GTK4
================

An example of how to use pyplot to manage your figure windows, but modify the
GUI by accessing the underlying GTK widgets.
"""

import matplotlib
matplotlib.use('GTK4Agg') # or 'GTK4Cairo'
import matplotlib.pyplot as plt

import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk


fig, ax = plt.subplots()
ax.plot([1, 2, 3], 'ro-', label='easy as 1 2 3')
ax.plot([1, 4, 9], 'gs--', label='easy as 1 2 3 squared')
ax.legend()

manager = fig.canvas.manager
# you can access the window or vbox attributes this way
toolbar = manager.toolbar
vbox = manager.vbox

# now let's add a button to the toolbar
button = Gtk.Button(label='Click me')
button.connect('clicked', lambda button: print('hi mom'))
button.set_tooltip_text('Click me for fun and profit')
toolbar.append(button)

# now let's add a widget to the vbox
label = Gtk.Label()
label.set_markup('Drag mouse over axes for position')
vbox.insert_child_after(label, fig.canvas)


def update(event):
if event.xdata is None:
label.set_markup('Drag mouse over axes for position')
else:
label.set_markup(
f'<span color="#ef0000">x,y=({event.xdata}, {event.ydata})</span>')


fig.canvas.mpl_connect('motion_notify_event', update)

plt.show()
4 changes: 2 additions & 2 deletions lib/matplotlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1098,8 +1098,8 @@ def use(backend, *, force=True):
backend names, which are case-insensitive:

- interactive backends:
GTK3Agg, GTK3Cairo, MacOSX, nbAgg, QtAgg, QtCairo,
TkAgg, TkCairo, WebAgg, WX, WXAgg, WXCairo, Qt5Agg, Qt5Cairo
GTK3Agg, GTK3Cairo, GTK4Agg, GTK4Cairo, MacOSX, nbAgg, QtAgg,
QtCairo, TkAgg, TkCairo, WebAgg, WX, WXAgg, WXCairo, Qt5Agg, Qt5Cairo

- non-interactive backends:
agg, cairo, pdf, pgf, ps, svg, template
Expand Down
5 changes: 3 additions & 2 deletions lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ def _safe_pyplot_import():
backend_mapping = {
'qt': 'qtagg',
'gtk3': 'gtk3agg',
'gtk4': 'gtk4agg',
'wx': 'wxagg',
'tk': 'tkagg',
'macosx': 'macosx',
Expand Down Expand Up @@ -1656,7 +1657,7 @@ class FigureCanvasBase:
A high-level figure instance.
"""

# Set to one of {"qt", "gtk3", "wx", "tk", "macosx"} if an
# Set to one of {"qt", "gtk3", "gtk4", "wx", "tk", "macosx"} if an
# interactive framework is required, or None otherwise.
required_interactive_framework = None

Expand Down Expand Up @@ -1732,7 +1733,7 @@ def _fix_ipython_backend2gui(cls):
# don't break on our side.
return
rif = getattr(cls, "required_interactive_framework", None)
backend2gui_rif = {"qt": "qt", "gtk3": "gtk3",
backend2gui_rif = {"qt": "qt", "gtk3": "gtk3", "gtk4": "gtk4",
"wx": "wx", "macosx": "osx"}.get(rif)
if backend2gui_rif:
if _is_non_interactive_terminal_ipython(ip):
Expand Down
Loading