Matplotlib Widgets — How to Make Your Plot Interactive With Buttons

This article presents different types of widgets that can be embedded within a matplotlib figure, in order to create and personalize highly interactive plots. Exploiting the matplotlib package .widget(), it is hence possible to create personalized buttons that allows controlling different properties of the graphs that are plotted in the main window. This represents a practical and creative solution to change some of your plot properties while it keeps being displayed in the active matplotlib window. More specifically, three different types of widgets will be presented in this article:

  • Button
  • Radio Buttons
  • Check Buttons

Matplotlib Widgets — An Interactive Jupyter Notebook

I’ve created an interactive Jupyter Notebook for you to run the code discussed in this article interactively:

Matplotlib Widgets Example Code

Here’s the code discussed in this article for copy&paste:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button, RadioButtons, CheckButtons

# x and y arrays definition
x = np.linspace(0, 10, 50)
y = np.sin(x**2)*np.exp(x)

# Plotting
fig = plt.figure()
ax = fig.subplots()
plt.subplots_adjust(left = 0.3, bottom = 0.25)
p, = ax.plot(x, y, color = 'b', label = 'Plot 1')

#---BUTTON-----

# xposition, yposition, width, height
ax_button = plt.axes([0.25, 0.1, 0.08, 0.05])

# properties of the button
grid_button = Button(ax_button, 'Grid', color='white', hovercolor='grey')

# enabling/disabling the grid
def grid(val):
    ax.grid()
    fig.canvas.draw() #redraw the figure


# triggering event is the clicking
grid_button.on_clicked(grid)


#-----RADIO BUTTONS----

ax_color = plt.axes([0.02, 0.5, 0.2, 0.3])
color_button = RadioButtons(ax_color, ['red', 'green', 'blue', 'black'],
                            [False, False, True, False], activecolor= 'r')

# function for changing the plot color
def color(labels):
    p.set_color(labels)
    fig.canvas.draw()
color_button.on_clicked(color)


#----CHECK BUTTONS----

# defining a second function
y1 = -1*np.sin(x**2)*np.exp(x)
p1, = ax.plot(x, y1, color = 'b', label = 'Plot 2', visible = False)
plots =[p, p1]
activated = [True, False]
labels = ['Plot 1', 'Plot 2']

# instance the axes
ax_check = plt.axes([0.7, 0.05, 0.08, 0.1])
plot_button = CheckButtons(ax_check,labels, activated)


# function for displaying/hiding the plots
def select_plot(label):
    
    # get the index that corresponds to the word "label"
    index = labels.index(label)
    
    # set the plot to visible
    plots[index].set_visible(not plots[index].get_visible())
    fig.canvas.draw()

    
plot_button.on_clicked(select_plot)
plt.show()

Importing Packages and Libraries

As usual, we start the script by importing the different libraries and packages that will be needed for creating our interactive plots. In addition to the classical Numpy and matplotlib.pyplot, we have also to import the functions that account for to the buttons that will be then created. As anticipated in the previous part, the functions are Button, RadioButtons and CheckButtons; all of them belong to the matplotlib package .widgets.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button, RadioButtons, CheckButtons

Creating a Function

We start by defining a mathematical function that will be plotted in the matplotlib window; it is described by the arrays “x” and “y”. For the definition of the “x” array, the .linspace() function, from Numpy, is used to obtain an array of 50 equally spaced numbers from 0 to 10. The “y” array contains the values of the function:

y = sin(x2)ex

the following code-lines describe the definition of the two arrays.

# x and y arrays for function definition
x = np.linspace(0, 10,50)
y = np.sin(x**2) * np.exp(x)

Defining the Initial Plot

Once we have defined the function, we proceed further by creating the matplotlib window in which we will create our plot; to do this, we exploit the classical matplotlib functions .figure() and .subplots(). In order to have sufficient space for inserting the different buttons, the size and position of the plot are adjusted by exploiting the function .subplots_adjust() and specifying (in the input parameters) the space that is needed along each border of the plot. In this case, it is sufficient to create some space on the left and on the bottom sides of the plot. In the end, the “x” and “y” array are plotted as a continuous blue line; the label “Plot 1” is also assigned to this first graph. The important thing is to remember to put a comma after the name of the variable that refers to the plot (“p”), in order to be able to modify its properties in a second time.

#Definition of the initial plot
fig = plt.figure()
ax = fig.subplots()
plt.subplots_adjust(left = 0.3, bottom = 0.25)
p, = ax.plot(x,y, color = 'b', label = 'Plot 1')

Button Widget

Button
Syntax:Button()
Parameters:ax (variable)Axes defining the space in which the button will be located
label (str)Label that appears on the button
color (str or float)The color of the button
hovercolor (str or float)The color of the button when it gets clicked
Return ValueNone

Table 1: The function Button() and the parameters defined in this script.

The first widget that will be implemented in the plot is a simple button. This type of buttons provides the possibility of switching from ON to OFF and vice versa, a single property of the plot. This means that this button can be responsible for just one property. If we want to control another property of the plot, we have to create a second button. In this example, the widget Button is created in order to be able to display/hide the grid of the plot. We start by defining the position and the size of our button, this is done by creating a so-called “axes”, which in Python represents a space that can be filled with other information (the button’s properties). The matplotlib function that is exploited for this purpose is called .axes() and accepts as input, a list of values corresponding to the horizontal, vertical position, width and height of the button.

#---BUTTON----
#Buttons
ax_button = plt.axes([0.25, 0.1, 0.08,0.05]) #xposition, yposition, width and height

After defining the position and the size of the button, its different properties can be defined by calling the specific function Button(). As illustrated in Table 1, the inputs of this function are the place in which the button will be created, the label and the color (it is also possible to customize the color displayed when hovering on the button with the cursor). The variable that refers to the just defined button is called “grid_button”.

#Properties of the button
grid_button = Button(ax_button, 'Grid', color = 'white', hovercolor = 'grey')

At this point, we have to specify which task should be done each time we click on the button. As said before, we want to use this button to display/hide the grid; to accomplish this task. We define a function called grid(), in which we define the command ax.grid() for displaying the grid in the plotting window. At the end, we redraw the figure by using the command .canvas.draw().

#enabling/disabling the grid
def grid(val):
    ax.grid()
    fig.canvas.draw() #redraw the figure

To conclude this first part, we have to specify the event that will trigger the execution of the grid() function. We apply the method .on_clicked() to the variable “grid_button”, specifying as input the function grid; in this way, each time we click on the button, the script runs the function grid.

#calling the function "grid" when the button gets clicked
grid_button.on_clicked(grid)

Figure 1 displays the final output of this first script (if you want to obtain the outcome shown in Figure1, just add another line to your code, writing “plt.show()”, to display the plot that you just created; I will display the plot at the end, in order to have all the widgets included).

Figure 1: Matplotlib window that appears as the outcome of the first part of the script. The plot has been shifted upwards and towards the left border in order to create some space for the widgets. On the bottom-left part of the figure, the widget Button has been included; its function is to display/hide the grid every time it gets clicked.

RadioButtons Widget

RadioButtons
Syntax:RadioButtons
Parameters:ax (variable)Axes defining the space in which the radio buttons will be located
labels (list)Labels of each button
active (list)list of booleans describing the state of each button
activecolor (str or float)The color of the active button
Return ValueNone

Table 2: The function RadioButtons and the parameters defined in this script.

The second widget that will be implemented in our plot is the so-called Radio Buttons. It consists of a series of circular buttons that can be used to enable/disable one among different properties of our plot. In this case, three Radio Buttons will be used for giving the user the possibility to choose among four different colors for the displayed plot. Every time one of the Radio Buttons will be clicked, the plot will change its color according to the selected one. As in the first part, the first thing to do is to define the location and the size of our widget, instancing the so-called “axes”; after that, we define the properties of these buttons by using the dedicated function RadioButtons, and we assign them to the variable “color_button”. As can be seen from the code-lines, the function RadioButtons takes as input the axes in which we want to place the buttons, the labels of each button and their activation state (given by the Boolean operators True or False). It is also possible to specify the “activecolor” option, which indicates the color of the currently active Radio Button; all these options are summarized in Table 2.

#---RADIO BUTTONS----
ax_color = plt.axes([0.02, 0.5, 0.2,0.3]) #xposition, yposition, width and height
#Properties of the Radio buttons
color_button = RadioButtons(ax_color, ['red', 'green', 'blue', 'black'], active = [True, False, False, False], activecolor = 'r')

Once the Radio Buttons have been correctly defined, they have to be linked to the function describing the task to be performed at each click. For this example, the function should change the color of the plot according to the options displayed in the widget. To do this, the method .set_color() is applied to the variable “p”, which accounts for the plot; this method takes as input the name of the color (a string) that has to be applied to the plot. The input variable of the function is “labels” and contains the label of the clicked button; we hence pass this variable to .set_color() as well. To complete the function definition, we redraw the figure and specify when it should be run, i.e. every time the button “color_button” gets clicked. The following code-lines describe these procedures.

#function for changing the color of the plot
def color(labels):
    p.set_color(labels)
    fig.canvas.draw ()
#calling the function "color" when the radio button gets clicked
color_button.on_clicked(color) 

The outcome of this second part is displayed in Figure 2, together with the “Button” widget, specified in the first part.

Figure 2: The RadioButtons widget has been implemented on the left side of the plot window. It features four different buttons, corresponding to different colors that can be applied to the displayed plot; in the figure, the “green” option is the active one and is indicated by the red filling.

CheckButtons Widget

CheckButtons 
Syntax:CheckButtons 
Parameters:ax (variable)Axes defining the space in which the check buttons will be located
 labels (list)Labels of each button
actives (list)list of booleans describing the state of each button
Return ValueNone 

Table 3: The function CheckButtons and the parameters defined in this script.

The third widget that will be implemented in the plot is the so-called CheckButtons. This widget is similar to the previous one but it features some important differences. Beside the visual appearance, here we have that the buttons are rectangular and they get crossed when activated; the most substantial difference regards the working principle: with RadioButtons, it was possible to select just one option at a time, enabling an option would automatically disable the currently active one; with CheckButtons instead, it is possible to activate multiple buttons at a time. This feature can be useful whenever we want to control two or more properties of our plot that could be active at the same time. In the following example, the CheckButtons widget will be used for enabling the visualization of a second graph in our plot. This task could not be accomplished by RadioButtons, since it only allows activating one of its options at a time. Before defining the widget, the second graph has to be defined (the function is called y1); it will be assigned to the variable “p1”. As you can see in the following code-lines, we also specify the initial visibility of the plot, setting it to False, in order not to show it automatically. We then define three lists, “plots”, “activated” and “labels”, which contain the two plots, their status of visibility and their labels, respectively.

#-----CHECK BUTTON------
#defining a second plot
y1 = -1*np.sin(x**2)*np.exp(x)
p1, = ax.plot(x,y1, color = 'b', label = 'Plot 2', visible = False)
plots = [p, p1]
activated = [True, False]
labels = ['Plot 1', 'Plot 2']

After this, we define the location, the size and the properties of the Radio Buttons. This is very similar to what has been already shown in the two previous parts. The properties of the widget are defined through the appropriate function CheckButtons() which takes as input the axes (space in which the button will be created), the list containing the labels of the various buttons and a list accounting for their activation state, related (in this example) to the visibility of the plots; all these features are summarized within Table 3.

#Properties of the Check buttons
ax_check = plt.axes([0.7, 0.05, 0.08,0.1]) #xposition, yposition, width and height
plot_button = CheckButtons(ax_check, labels , activated)

At this point, we have to define the function that will perform the desired task, i.e. enabling/disabling the graphs. The function is defined by the name select_plot() and takes as input the label that corresponds to the clicked option. Once obtained the label of the selected button, we have to determine the index of the element, within the list “labels”, it corresponds to. To do that, we apply the method .index() to the list “labels”, and we store this value in the variable index. Now that we know which of the two plots we want to enable/disable, we change its visibility by typing the following command: plots[index].set_visible(not plots[index].get_visible()); where we first refer to the desired plot through plots[index] and then we apply the method .set_visible() to access the property “visible” (more documentation here: https://www.geeksforgeeks.org/matplotlib-axes-axes-set_visible-in-python/ ); to change its value, we first get its current status, using the method .get_visible() and then take its opposite (additional documentation here: https://www.geeksforgeeks.org/matplotlib-axes-axes-get_visible-in-python/ ). All the procedures are described in the following code-lines.

#function for displaying the plots
def select_plot(label):
    #get the index that corresponds to the word "label" within the list labels
    index = labels.index(label)
    #set the selected plot to visible
    plots[index].set_visible(not plots[index].get_visible()) 
    fig.canvas.draw()   

We conclude by specifying the triggering event for this function, i.e. every time the Check Buttons in the widgets get clicked.

plot_button.on_clicked(select_plot)
plt.show() 

Figure 3 describes the final output of this script, also including the previous two widgets. 

Figure 3:  The final matplotlib window, featuring all the three widgets. Both the plots are set to visible, as can be seen in the CheckButtons widget, as well as the grid. The color of the first plot is set to green through the RadioButtons widget. 

Conclusions

In this article, we have seen how to implement three different types of widget within a matplotlib window. All the widgets have distinct properties, making them more appropriate for specific tasks. They represent a practical solution for changing some of the properties within a plot, without having to close its window, changing the script and compiling again. Of course, there are lots of different tasks that can be assigned to each of these buttons, it just depends on your needs; if you want to know more about the matplotlib.widget package, you can find additional information at this link: https://matplotlib.org/3.3.3/api/widgets_api.html .