12

I want to remove some of the buttons from plot toolbar (matplotlib).

I saw that there are some old solutions:

How to modify the navigation toolbar easily in a matplotlib figure window?

But all the answers uses GUI frameworks (QT, TKinter).

Is there a new solution which doesn't use GUI frameworks ?

enter image description here

DYZ
  • 55,249
  • 10
  • 64
  • 93
user3668129
  • 4,318
  • 6
  • 45
  • 87

3 Answers3

16

You can do it by adding following lines of code before creating a plot object:

import matplotlib as mpl
mpl.rcParams['toolbar'] = 'None' 

If you want to delete some buttons selectively, you need to redefine the toolitems variable instead:

from matplotlib import backend_bases
# mpl.rcParams['toolbar'] = 'None'
backend_bases.NavigationToolbar2.toolitems = (
        ('Home', 'Reset original view', 'home', 'home'),
        ('Back', 'Back to  previous view', 'back', 'back'),
        ('Forward', 'Forward to next view', 'forward', 'forward'),
        (None, None, None, None),
        ('Zoom', 'Zoom to rectangle', 'zoom_to_rect', 'zoom'),
        (None, None, None, None),
        ('Save', 'Save the figure', 'filesave', 'save_figure'),
      )

I have removed two lines from the original variable mpl.backend_bases.NavigationToolbar2.toolitems which normally reads:

toolitems = (
    ('Home', 'Reset original view', 'home', 'home'),
    ('Back', 'Back to  previous view', 'back', 'back'),
    ('Forward', 'Forward to next view', 'forward', 'forward'),
    (None, None, None, None),
    ('Pan', 'Pan axes with left mouse, zoom with right', 'move', 'pan'),
    ('Zoom', 'Zoom to rectangle', 'zoom_to_rect', 'zoom'),
    ('Subplots', 'Configure subplots', 'subplots', 'configure_subplots'),
    (None, None, None, None),
    ('Save', 'Save the figure', 'filesave', 'save_figure'),
  )

EDIT

I have realized that it works with backend 'TkAgg'. For the backend 'Qt5Agg' we need to do some additional monkey patching just after modifying toolitems. Namely:

if matplotlib.get_backend() == 'Qt5Agg':
    from matplotlib.backends.backend_qt5 import NavigationToolbar2QT
    def _update_buttons_checked(self):
        # sync button checkstates to match active mode (patched)
        if 'pan' in self._actions:
            self._actions['pan'].setChecked(self._active == 'PAN')
        if 'zoom' in self._actions:
            self._actions['zoom'].setChecked(self._active == 'ZOOM')
    NavigationToolbar2QT._update_buttons_checked = _update_buttons_checked
freude
  • 3,632
  • 3
  • 32
  • 51
  • your solution remove all the buttons. – user3668129 Apr 21 '19 at 05:34
  • 1
    in this solution we can display all or none of the buttons. cant select which buttons to show or hide – user3668129 Apr 21 '19 at 05:41
  • I have modified the answer to address your question – freude Apr 21 '19 at 05:51
  • I edited the answer: The previous solution was not guaranteed to work with any matplotlib version. Importing backend_bases explictely gets around that. In general, wouldn't this answer be better placed at the original question. Noone's going to find it here. – ImportanceOfBeingErnest Apr 21 '19 at 14:25
  • Oh and actually, it seems it doesn't even work at all. Testing with matplotlib 3.0.2 and `Qt5Agg` as backend, this results in an error trying to use the zoom tool. In the end this means it's still GUI toolkit dependent. – ImportanceOfBeingErnest Apr 21 '19 at 14:29
  • Yes, it does. But the point is when it only works with TkAgg anyway, it is again depending on the GUI framework in use. So then you need to determine upfront which backend is in use and implement the solution for each possible backend. – ImportanceOfBeingErnest Apr 21 '19 at 14:52
  • I have edited my answer. Now It pass the test you have mentioned. Indeed it requires additional patching for the backend Qt5Agg. I don't know whether there are other backends causing problems. – freude Apr 21 '19 at 15:28
  • I think that patch has to be added to the official version of the backend. – freude Apr 21 '19 at 15:37
  • E.g. for the wx backend, removing forward or backward buttons may cause problems ([see here](https://github.com/matplotlib/matplotlib/blob/44d503e8b4394eedf53151d7577e05621b6a24e9/lib/matplotlib/backends/backend_wx.py#L1570)), though I haven't tested in which case this function is actually being called. I did think a bit about whether this should be fixed and I'm not sure... The code is fine as it is. And you can in general not support every possible case where someone monkey-patches a base class and removes an attribute from it. Still, feel welcome to open a PR or issue. – ImportanceOfBeingErnest Apr 21 '19 at 21:20
3

After trying many solutions, I have found that this works very well.

toolbar = plt.get_current_fig_manager().toolbar
unwanted_buttons = ['Subplots','Save']
for x in toolbar.actions():
    if x.text() in unwanted_buttons:
        toolbar.removeAction(x)
Aking
  • 474
  • 2
  • 11
0

I couldn't get any answer here to work for the webagg backend. Since these buttons don't work correctly on mobile I just wanted to remove them.

I was able to use this:

toolbar = self.manager.toolbar
unwanted_buttons = ['Back','Forward','Home','Pan','Zoom']
to_remove = []
for x in toolbar.toolitems:
    print(x[0])
    if x[0] in unwanted_buttons:
        print(f'removing {x[0]}')
        to_remove.append(x)

for x in to_remove:
    toolbar.toolitems.remove(x)

The context of this is based on this webagg tornado example. The code is inserted within the __init__ function just after self.manager is defined.

poleguy
  • 523
  • 7
  • 11