Skip to content
Closed
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
30 changes: 17 additions & 13 deletions doc/devel/MEP/MEP27.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@

Status
======
**Discussion**
**Progress**

Branches and Pull requests
==========================
Main PR (including GTK3):

+ https://github.com/matplotlib/matplotlib/pull/4143

Backend specific branch diffs:

+ https://github.com/OceanWolf/matplotlib/compare/backend-refactor...OceanWolf:backend-refactor-tkagg
+ https://github.com/OceanWolf/matplotlib/compare/backend-refactor...OceanWolf:backend-refactor-qt
+ https://github.com/OceanWolf/matplotlib/compare/backend-refactor...backend-refactor-wx
Expand Down Expand Up @@ -79,7 +81,7 @@ The description of this MEP gives us most of the solution:
1. To remove the windowing aspect out of ``FigureManagerBase`` letting
it simply wrap this new class along with the other backend classes.
Create a new ``WindowBase`` class that can handle this
functionality, with pass-through methods (:arrow_right:) to
functionality, with pass-through methods (->) to
``WindowBase``. Classes that subclass ``WindowBase`` should also
subclass the GUI specific window class to ensure backward
compatibility (``manager.window == manager.window``).
Expand All @@ -103,30 +105,30 @@ The description of this MEP gives us most of the solution:
|FigureManagerBase(canvas, num) |FigureManager(figure, num) |``WindowBase(title)``|Notes |
| | | | |
+======================================+==============================+=====================+================================+
|show | |show | |
|show |-> |show | |
+--------------------------------------+------------------------------+---------------------+--------------------------------+
|destroy |calls destroy on all |destroy | |
| |components | | |
+--------------------------------------+------------------------------+---------------------+--------------------------------+
|full_screen_toggle |handles logic |set_fullscreen | |
+--------------------------------------+------------------------------+---------------------+--------------------------------+
|resize | |resize | |
|resize |-> |resize | |
+--------------------------------------+------------------------------+---------------------+--------------------------------+
|key_press |key_press | | |
|key_press |key_press |X | |
+--------------------------------------+------------------------------+---------------------+--------------------------------+
|show_popup |show_poup | |Not used anywhere in mpl, and |
|show_popup |show_poup |X |Not used anywhere in mpl, and |
| | | |does nothing. |
+--------------------------------------+------------------------------+---------------------+--------------------------------+
|get_window_title | |get_window_title | |
|get_window_title |-> |get_window_title | |
+--------------------------------------+------------------------------+---------------------+--------------------------------+
|set_window_title | |set_window_title | |
|set_window_title |-> |set_window_title | |
+--------------------------------------+------------------------------+---------------------+--------------------------------+
| |_get_toolbar | |A common method to all |
|X |_get_toolbar |X |A common method to all |
| | | |subclasses of FigureManagerBase |
+--------------------------------------+------------------------------+---------------------+--------------------------------+
| | |set_default_size | |
|X |X |set_default_size | |
+--------------------------------------+------------------------------+---------------------+--------------------------------+
| | |add_element_to_window| |
|X |X |add_element | |
+--------------------------------------+------------------------------+---------------------+--------------------------------+


Expand All @@ -135,14 +137,14 @@ The description of this MEP gives us most of the solution:
+==========+============+=============+
|mainloop |begin | |
+----------+------------+-------------+
| |end |Gets called |
|X |end |Gets called |
| | |automagically|
| | |when no more |
| | |instances of |
| | |the subclass |
| | |exist |
+----------+------------+-------------+
|__call__ | |Method moved |
|__call__ |X |Method moved |
| | |to |
| | |Gcf.show_all |
+----------+------------+-------------+
Expand Down Expand Up @@ -191,6 +193,8 @@ in the same manner as everything else.
| | |window, so this also |
| | |breaks BC. |
+-------------------------+-------------------------+-------------------------+
|WebAgg |canvas | |
+-------------------------+-------------------------+-------------------------+


Alternatives
Expand Down
39 changes: 39 additions & 0 deletions examples/user_interfaces/reuse_figuremanager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import matplotlib
matplotlib.use('GTK3AGG')
matplotlib.rcParams['toolbar'] = 'toolmanager'
from matplotlib.figure import Figure
from matplotlib.backend_managers import FigureManager
from matplotlib.backends import get_backend
from matplotlib.backend_tools import ToolBase


backend = get_backend()

fig1 = Figure()
canvas1 = backend.FigureCanvas(fig1)
ax1 = fig1.add_subplot(111)
ax1.plot([1, 2, 3])

fig2 = Figure()
canvas2 = backend.FigureCanvas(fig2)
ax2 = fig2.add_subplot(111)
ax2.plot([3, 2, 1])


class SwitchFigure(ToolBase):
description = "change Figure"
default_keymap = 'f'

def __init__(self, *args, **kwargs):
self.fig = kwargs.pop('newfig')
self.figmanager = kwargs.pop('figuremanager')
ToolBase.__init__(self, *args, **kwargs)

def trigger(self, *args, **kwargs):
self.figmanager.figure = self.fig

manager = FigureManager(fig1, 1)
manager.toolmanager.add_tool('f1', SwitchFigure, newfig=fig2, figuremanager=manager)
manager.show()
manager.mpl_connect('window_destroy_event', manager.destroy)
manager._mainloop()
51 changes: 46 additions & 5 deletions examples/user_interfaces/toolmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
matplotlib.use('GTK3Cairo')
matplotlib.rcParams['toolbar'] = 'toolmanager'
import matplotlib.pyplot as plt
from matplotlib.backend_tools import ToolBase, ToolToggleBase
from matplotlib.backend_tools import (ToolBase, ToolToggleBase,
add_tools_to_container)
from gi.repository import Gtk, Gdk
from random import uniform


class ListTools(ToolBase):
Expand Down Expand Up @@ -60,33 +62,72 @@ def disable(self, *args):
self.set_lines_visibility(True)

def set_lines_visibility(self, state):
gr_lines = []
for ax in self.figure.get_axes():
for line in ax.get_lines():
if line.get_gid() == self.gid:
line.set_visible(state)
self.figure.canvas.draw()


class LineTool(ToolBase):
description = 'Draw a random line'

def __init__(self, *args, **kwargs):
self.color = kwargs.pop('color')
ToolBase.__init__(self, *args, **kwargs)

def trigger(self, *args, **kwargs):
x0, y0, x1, y1 = (uniform(0, 2), uniform(1, 4), uniform(0, 2),
uniform(1, 4))
fig = self.figure
fig.gca().plot([x0, x1], [y0, y1], color=self.color, gid=self.color)
fig.canvas.draw_idle()


class DotTool(ToolBase):
description = 'Draw a random dot'

def __init__(self, *args, **kwargs):
self.color = kwargs.pop('color')
ToolBase.__init__(self, *args, **kwargs)

def trigger(self, *args, **kwargs):
x0, y0 = uniform(0, 2), uniform(1, 4)
fig = self.figure
fig.gca().plot([x0], [y0], 'o', color=self.color, gid=self.color)
fig.canvas.draw_idle()


fig = plt.figure()
plt.plot([1, 2, 3], gid='mygroup')
plt.plot([2, 3, 4], gid='unknown')
plt.plot([3, 2, 1], gid='mygroup')

# Add the custom tools that we created
fig.canvas.manager.toolmanager.add_tool('List', ListTools)
fig.canvas.manager.toolmanager.add_tool('Hide', GroupHideTool, gid='mygroup')
tool_mgr = fig.canvas.manager.toolmanager
tool_mgr.add_tool('List', ListTools)
tool_mgr.add_tool('Hide', GroupHideTool, gid='mygroup')


# Add an existing tool to new group `foo`.
# It can be added as many times as we want
fig.canvas.manager.toolbar.add_tool('zoom', 'foo')

# Remove the forward button
fig.canvas.manager.toolmanager.remove_tool('forward')
tool_mgr.remove_tool('forward')

# To add a custom tool to the toolbar at specific location inside
# the navigation group
fig.canvas.manager.toolbar.add_tool('Hide', 'navigation', 1)

for i, c in enumerate(['yellowgreen', 'forestgreen']):
sidebar = fig.canvas.manager._get_toolbar()
sidebar.set_flow('vertical')
tools = [['shapes', [tool_mgr.add_tool('L%s' % i, LineTool, color=c),
tool_mgr.add_tool('D%s' % i, DotTool, color=c)]],
['hide', [tool_mgr.add_tool('H%s' % i, GroupHideTool, gid=c)]]]

fig.canvas.manager.window.add_element(sidebar, False, 'west')
add_tools_to_container(sidebar, tools)

plt.show()
60 changes: 59 additions & 1 deletion lib/matplotlib/_pylab_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import gc
import atexit

from matplotlib import is_interactive


def error_msg(msg):
print(msg, file=sys.stderr)
Expand All @@ -35,6 +37,16 @@ class Gcf(object):
_activeQue = []
figs = {}

@classmethod
def add_figure_manager(cls, manager):
cls.figs[manager.num] = manager
try: # TODO remove once all backends converted to use the new manager.
manager.mpl_connect('window_destroy_event', cls.destroy_cbk)
except:
pass

cls.set_active(manager)

@classmethod
def get_fig_manager(cls, num):
"""
Expand All @@ -46,6 +58,50 @@ def get_fig_manager(cls, num):
cls.set_active(manager)
return manager

@classmethod
def show_all(cls, block=None):
"""
Show all figures. If *block* is not None, then
it is a boolean that overrides all other factors
determining whether show blocks by calling mainloop().
The other factors are:
it does not block if run inside ipython's "%pylab" mode
it does not block in interactive mode.
"""

managers = cls.get_all_fig_managers()
if not managers:
return

for manager in managers:
manager.show()

if block is not None:
if block:
manager._mainloop()
return

from matplotlib import pyplot
try:
ipython_pylab = not pyplot.show._needmain
# IPython versions >= 0.10 tack the _needmain
# attribute onto pyplot.show, and always set
# it to False, when in %pylab mode.
ipython_pylab = ipython_pylab and get_backend() != 'WebAgg'
# TODO: The above is a hack to get the WebAgg backend
# working with ipython's `%pylab` mode until proper
# integration is implemented.
except AttributeError:
ipython_pylab = False

# Leave the following as a separate step in case we
# want to control this behavior with an rcParam.
if ipython_pylab:
block = False

if not is_interactive() or get_backend() == 'WebAgg':
manager._mainloop()

@classmethod
def destroy(cls, num):
"""
Expand Down Expand Up @@ -137,7 +193,9 @@ def set_active(cls, manager):
if m != manager:
cls._activeQue.append(m)
cls._activeQue.append(manager)
cls.figs[manager.num] = manager

@classmethod
def destroy_cbk(cls, event):
cls.destroy(event.figure_manager.num)

atexit.register(Gcf.destroy_all)
Loading