Search code examples
pythonclassmatplotlibpython-class

Python - pass arguments when adding a button to plot's toolbar (matplotlib)


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.


Solution

  • 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])