8000 Tool Manager: adding buttons to toolbar fails with matplotlib version 3.3.1 using Qt backend · Issue #18327 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content
Tool Manager: adding buttons to toolbar fails with matplotlib version 3.3.1 using Qt backend #18327
Closed
@Ptrskay3

Description

@Ptrskay3

Bug report

Adding buttons on the toolbar in matplotlib version 3.3.1 produces AttributeError with Qt5Agg backend.

Code for reproduction
The easiest way to reproduce: from the original example gallery:

import matplotlib.pyplot as plt
plt.rcParams['toolbar'] = 'toolmanager'
from matplotlib.backend_tools import ToolBase, ToolToggleBase

plt.switch_backend("Qt5Agg")

class ListTools(ToolBase):
    """List all the tools controlled by the `ToolManager`."""
    # keyboard shortcut
    default_keymap = 'm'
    description = 'List Tools'

    def trigger(self, *args, **kwargs):
        print('_' * 80)
        print("{0:12} {1:45} {2}".format(
            'Name (id)', 'Tool description', 'Keymap'))
        print('-' * 80)
        tools = self.toolmanager.tools
        for name in sorted(tools):
            if not tools[name].description:
                continue
            keys = ', '.join(sorted(self.toolmanager.get_tool_keymap(name)))
            print("{0:12} {1:45} {2}".format(
                name, tools[name].description, keys))
        print('_' * 80)
        print("Active Toggle tools")
        print("{0:12} {1:45}".format("Group", "Active"))
        print('-' * 80)
        for group, active in self.toolmanager.active_toggle.items():
            print("{0:12} {1:45}".format(str(group), str(active)))


class GroupHideTool(ToolToggleBase):
    """Show lines with a given gid."""
    default_keymap = 'G'
    description = 'Show by gid'
    default_toggled = True

    def __init__(self, *args, gid, **kwargs):
        self.gid = gid
        super().__init__(*args, **kwargs)

    def enable(self, *args):
        self.set_lines_visibility(True)

    def disable(self, *args):
        self.set_lines_visibility(False)

    def set_lines_visibility(self, state):
        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()


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('Show', 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')

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

plt.show()

Actual outcome

Treat the new Tool classes introduced in v1.5 as experimental for now, the API will likely change in version 2.1 and perhaps the rcParam as well
E:\mpl_toolbar_issue.py:57: UserWarning: The new Tool classes introduced in v1.5 are experimental; their API (including names) will likely change in future versions.
  fig = plt.figure()
E:\mpl_toolbar_issue.py:63: UserWarning: The new Tool classes introduced in v1.5 are experimental; their API (including names) will likely change in future versions.
  fig.canvas.manager.toolmanager.add_tool('List', ListTools)
E:\mpl_toolbar_issue.py:41: UserWarning: The new Tool classes introduced in v1.5 are experimental; their API (including names) will likely change in future versions.
  super().__init__(*args, **kwargs)
E:\mpl_toolbar_issue.py:64: UserWarning: Key G changed from grid_minor to Show
  fig.canvas.manager.toolmanager.add_tool('Show', GroupHideTool, gid='mygroup')
Traceback (most recent call last):
  File "E:\mpl_toolbar_issue.py", line 76, in <module>
    fig.canvas.manager.toolbar.add_tool('Show', 'navigation', 1)
  File "C:\Program Files\Python37\lib\site-packages\matplotlib\backend_bases.py", line 3333, in add_tool
    image, tool.description, toggle)
  File "C:\Program Files\Python37\lib\site-packages\matplotlib\backends\backend_qt5.py", line 919, in add_toolitem
    button.setIcon(NavigationToolbar2QT._icon(self, image_file))
  File "C:\Program Files\Python37\lib\site-packages\matplotlib\backends\backend_qt5.py", line 711, in _icon
    name = name.replace('.png', '_large.png')
AttributeError: 'NoneType' object has no attribute 'replace'

However, running it on for example TkAgg backend works fine. I also downgraded to 3.3.0 and it seems to fix the issue, so I think something is broken in release 3.3.1.

Matplotlib version

  • Operating system: Windows 10
  • Matplotlib version: 3.3.1
  • Matplotlib backend: Qt5Agg
  • Python version: 3.7.6

I installed matplotlib 3.3.1 on pip and conda as well, and it seems broken on both.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0