1

I am trying to add a button to toolbar. The button is a custom class that inherits ToolToggleBase. In my custom SelectButton class, I want to pass few arguments, but I getting an error:

import numpy as np
import matplotlib.pyplot as plt
plt.rcParams["toolbar"] = "toolmanager"
from matplotlib.backend_tools import ToolToggleBase


def simple_plot(mat):
    fig = plt.figure()
    ax = fig.add_subplot()
    ax.plot(np.reshape(mat, [-1, 1]))
    ax.grid(True)
    ax.legend()
    tm = fig.canvas.manager.toolmanager
    tm.add_tool("CustomButton", SelectButton(fig, ax))
    fig.canvas.manager.toolbar.add_tool(tm.get_tool("CustomButton"), "toolgroup")
    return fig, ax


class SelectButton(ToolToggleBase):
    default_toggled = False

    def __init__(self, fig1, ax1, *args, **kwarg):
        super().__init__(*args, **kwarg)
        print("fig: ", fig1)
        print("ax: ", ax1)


x = [1, 2, 3]
fig, ax = simple_plot(x)
plt.show()

Results in:

super().init(*args, **kwargs) TypeError: init() missing 2 required positional arguments: 'toolmanager' and 'name'

However, if I do not pass any arguments and pass only the class name, everything works as expected:

import numpy as np
import matplotlib.pyplot as plt
plt.rcParams["toolbar"] = "toolmanager"
from matplotlib.backend_tools import ToolToggleBase


def simple_plot(mat):
    fig = plt.figure()
    ax = fig.add_subplot()
    ax.plot(np.reshape(mat, [-1, 1]))
    ax.grid(True)
    ax.legend()
    tm = fig.canvas.manager.toolmanager
    tm.add_tool("CustomButton", SelectButton)
    fig.canvas.manager.toolbar.add_tool(tm.get_tool("CustomButton"), "toolgroup")
    return fig, ax


class SelectButton(ToolToggleBase):
    default_toggled = False

    def __init__(self, *args, **kwarg):
        super().__init__(*args, **kwarg)

x = [1, 2, 3]
fig, ax = simple_plot(x)
plt.show()

I understand I must call add_tool wrong when use an instance of a class rather then the class name, but I fail to understand what am I supposed to do if I want to pass arguments to SelectButton class.

2 Answers 2

1

You need to pass the class SelectButton itself, not an instance SelectButton() of it to add_tool. You should pass the additional parameters as kwargs:

def simple_plot(mat):
    fig = plt.figure()
    ax = fig.add_subplot()
    # ...
    tm = fig.canvas.manager.toolmanager
    tm.add_tool("CustomButton", SelectButton, fig=fig, ax=ax)
    fig.canvas.manager.toolbar.add_tool(tm.get_tool("CustomButton"), "toolgroup")
    return fig, ax


class SelectButton(ToolToggleBase):
    default_toggled = False

    def __init__(self, *args, fig, ax, **kwarg):
        super().__init__(*args, **kwarg)
        print("fig: ", fig)
        print("ax: ", ax) 

This prints:

fig:  Figure(640x480)
ax:  Axes(0.125,0.11;0.775x0.77)

If you for some reason really want to use args instead of kwargs you should use a partial as shown in the other answer. Passing only the first 2 args to super and using the rest by yourself is more a hack than a solution as it depends on matplotlib's implementation details:

tm.add_tool("CustomButton", SelectButton, fig, ax)

def __init__(self, *args, **kwarg):
    super().__init__(*args[:2], **kwarg)
    print("fig: ", args[2])
    print("ax: ", args[3])
Sign up to request clarification or add additional context in comments.

Comments

1

When utilizing an instance of the SelectButton class, you are using add_tool incorrectly, which results in the problem you are experiencing. A class instance, not a string representation of the class name, is what the add_tool method anticipates.

You can use a lambda function or partial function to build a callable object that accepts the necessary arguments to pass arguments to the SelectButton class. Here is an illustration of how you can change your code to accomplish this:


import numpy as np
import matplotlib.pyplot as plt
plt.rcParams["toolbar"] = "toolmanager"
from matplotlib.backend_tools import ToolToggleBase
from functools import partial


def simple_plot(mat):
    fig = plt.figure()
    ax = fig.add_subplot()
    ax.plot(np.reshape(mat, [-1, 1]))
    ax.grid(True)
    ax.legend()
    tm = fig.canvas.manager.toolmanager
    create_button = partial(SelectButton, fig, ax)
    tm.add_tool("CustomButton", create_button)
    fig.canvas.manager.toolbar.add_tool("CustomButton", "toolgroup")
    return fig, ax


class SelectButton(ToolToggleBase):
    default_toggled = False

    def __init__(self, fig1, ax1, *args, **kwargs):
        super().__init__(*args, **kwargs)
        print("fig:", fig1)
        print("ax:", ax1)


x = [1, 2, 3]
fig, ax = simple_plot(x)
plt.show()

The create_button variable in this code is given a partial function that accepts the arguments fig and ax and returns a brand-new instance of the SelectButton class. The tool name and this incomplete function are then supplied to add_tool.

The error should now be fixed when you execute the changed code since the SelectButton class will now correctly get the fig and ax arguments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.