74

I used matplotlib to create some plot, which depends on 8 variables. I would like to study how the plot changes when I change some of them. I created some script that calls the matplotlib one and generates different snapshots that later I convert into a movie, it is not bad, but a bit clumsy.

  1. I wonder if somehow I could interact with the plot regeneration using keyboard keys to increase / decrease values of some of the variables and see instantly how the plot changes.

  2. What is the best approach for this?

  3. Also if you can point me to interesting links or a link with a plot example with just two sliders?

gundamlh
  • 183
  • 1
  • 9
Open the way
  • 26,225
  • 51
  • 142
  • 196

7 Answers7

83

In addition to what @triplepoint mentioned, have a look at the slider widget.

There's an example on the matplotlib examples page. It's a graphical slider bar rather than keyboard bindings, but it works quite well for what you want to do.

Also note that to guarantee the sliders and buttons remain responsive and not garbage-collected, references to the objects (amp_slider, freq_slider, etc.) should be maintained by yourself.

(I'm making this community wiki, as I'm just copy-pasting from the example. This particular example teaches bad habits (e.g. from pylab import *), but it gets the point across. The example has been fixed to avoid the use of pylab.)

from numpy import pi, sin
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons

def signal(amp, freq):
    return amp * sin(2 * pi * freq * t)

axis_color = 'lightgoldenrodyellow'

fig = plt.figure()
ax = fig.add_subplot(111)

# Adjust the subplots region to leave some space for the sliders and buttons
fig.subplots_adjust(left=0.25, bottom=0.25)

t = np.arange(0.0, 1.0, 0.001)
amp_0 = 5
freq_0 = 3

# Draw the initial plot
# The 'line' variable is used for modifying the line later
[line] = ax.plot(t, signal(amp_0, freq_0), linewidth=2, color='red')
ax.set_xlim([0, 1])
ax.set_ylim([-10, 10])

# Add two sliders for tweaking the parameters

# Define an axes area and draw a slider in it
amp_slider_ax  = fig.add_axes([0.25, 0.15, 0.65, 0.03], facecolor=axis_color)
amp_slider = Slider(amp_slider_ax, 'Amp', 0.1, 10.0, valinit=amp_0)

# Draw another slider
freq_slider_ax = fig.add_axes([0.25, 0.1, 0.65, 0.03], facecolor=axis_color)
freq_slider = Slider(freq_slider_ax, 'Freq', 0.1, 30.0, valinit=freq_0)

# Define an action for modifying the line when any slider's value changes
def sliders_on_changed(val):
    line.set_ydata(signal(amp_slider.val, freq_slider.val))
    fig.canvas.draw_idle()
amp_slider.on_changed(sliders_on_changed)
freq_slider.on_changed(sliders_on_changed)

# Add a button for resetting the parameters
reset_button_ax = fig.add_axes([0.8, 0.025, 0.1, 0.04])
reset_button = Button(reset_button_ax, 'Reset', color=axis_color, hovercolor='0.975')
def reset_button_on_clicked(mouse_event):
    freq_slider.reset()
    amp_slider.reset()
reset_button.on_clicked(reset_button_on_clicked)

# Add a set of radio buttons for changing color
color_radios_ax = fig.add_axes([0.025, 0.5, 0.15, 0.15], facecolor=axis_color)
color_radios = RadioButtons(color_radios_ax, ('red', 'blue', 'green'), active=0)
def color_radios_on_clicked(label):
    line.set_color(label)
    fig.canvas.draw_idle()
color_radios.on_clicked(color_radios_on_clicked)

plt.show()

Example

Amit Amola
  • 2,301
  • 2
  • 22
  • 37
Joe Kington
  • 275,208
  • 71
  • 604
  • 463
  • Is it possible to actually interact with this tool or does it just generate an image? If so, what do I need to run it? I'm currently using IPython – triplebig Feb 15 '14 at 08:12
  • 2
    @triplebig - Yes, it's interactive. If nothing is happening when you call show(), then you're missing av interactive backend in your matplotlib install. How did you install matplotlib? – Joe Kington Feb 15 '14 at 17:15
  • @Joe Kington matplotlib came with the winpython overall installation i tried using it both on Ipython and on Spyder. IPython just gave me a png and spyder let me move the bars, but nothing happened when I did and it didn't let me click the buttons – triplebig Feb 15 '14 at 17:21
  • 1
    @triplebig - By "ipython"do you man an ipython notebook? (Instead of the normal ipython shell.) If so, ipython notebooks can't use an interactive backend. You'll need to run things either in a regular ipython shell, or directly (e.g. "python name_of_your_file.py"). – Joe Kington Feb 15 '14 at 17:54
  • @JoeKington Yes, by the notebook. What sort of interactive backend should I be looking for? Does it normally come with the standard matplotlib libraries? – triplebig Feb 15 '14 at 17:59
  • 1
    @triplebig - Yes, by default matplotlib will be built with an interactive backend of some sort. If you're using pre-built binaries (as you would be on windows), presumably the person who built them did it correctly. If you're using winpython, matplotlib definitely has an interactive backend (probably TkAgg). Try running the script directly and see what happens. (i.e. type "python name_of_the_file.py" in a terminal/cmd window.) – Joe Kington Feb 15 '14 at 18:17
  • 4
    To explain more about what's going on, an ipython notebook is not the same as ipython. `ipython` is an interactive python shell. An ipython notebook is a web-based interface that basically sends snippets of code back to `ipython` to evaluate and return the results of. For that reason, ipython notebooks just render matplotlib figures as static .png's, instead of popping up an interactive window. `ipython` itself (or just running the script directly with `python`) will show an interactive gui window for each matplotlib figure. – Joe Kington Feb 15 '14 at 18:23
  • `AttributeError: 'Axes' object has no property 'axisbg'` – mLstudent33 Feb 25 '20 at 08:37
  • Just like the demo here: https://matplotlib.org/3.1.1/gallery/widgets/slider_demo.html nothing responds on click. – mLstudent33 Feb 25 '20 at 08:39
  • Tried to get backend going but `TypeError: interactive() missing 1 required positional argument: 'b' ` – mLstudent33 Feb 25 '20 at 08:48
13

I followed the advice to check widgets in jupyter, and they work very well. The example script is uploaded in GitHub https://github.com/LeonidBystrykh/course-python-for-beginners/blob/master/Interactive_dots.ipynb

from __future__ import print_function
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
import matplotlib.pyplot as plt, random

def series(dots, colr):
    a,b=[],[]
    for i in range(dots):
        a.append(random.randint(1,100))
        b.append(random.randint(1,100))
    plt.scatter(a,b, c=colr)
    return()
interact(series, dots=(1,100,1), colr=["red","orange","brown"]);

The image copy is below

I can change number of dots or their color interactively

Leo
  • 199
  • 2
  • 4
7

Matplotlib has some fairly nice gui functionality. There are some documentation examples in the source tarball of matplotlib, in /examples/user_interfaces and matplotlib>/examples/event_handling. Specifically on keyhandling is: http://matplotlib.sourceforge.net/examples/event_handling/keypress_demo.html

I have done something kind of similar to what you are aiming for:

import numpy as np
import pylab

class plotter:
    def __init__(self, initial_values):
        self.values
        self.fig = pylab.figure()
        pylab.gray()
        self.ax = self.fig.add_subplot(111)
        self.draw()
        self.fig.canvas.mpl_connect('key_press_event',self.key)

    def draw(self):
        im = your_function(self.values)
        pylab.show()
        self.ax.imshow(im)

    def key(self, event):
        if event.key=='right':
            self.values = modify()
        elif event.key == 'left':
            self.values = modify()

        self.draw()
        self.fig.canvas.draw()

I was using this to shift through displaying different images in a stack on keypresses, but you should be able to put logic in to modify your values given keyboard input.

If you want to do things like have the user input values, I think the examples have options for dialog boxes, but if you just want to increment/decrement a number of variables, just defining keyboard pairs for them in this manner might work well

triplepoint217
  • 462
  • 3
  • 10
5

For ipython or jupyter notebooks you can use ipywidgets:

from ipywidgets import *
def update(w=0,h=0):
    print(h+w)

interact(update, w= widgets.IntSlider(value=1, min=0, max=7, step=1) , 
                 h= widgets.IntSlider(value=1, min=0, max=7, step=1) );

See documentation here: https://ipywidgets.readthedocs.io/en/stable/examples/Using%20Interact.html

Alexey Birukov
  • 1,565
  • 15
  • 22
2

Use waitforbuttonpress(timeout=0.001) then plot will see your mouse ticks.

sema
  • 31
  • 6
1

You could also consider using the Quibbler package.

Quibbler automatically makes your normal matplotlib graphics interactive. There is no need to write callback functions.

See some examples in the Quibbler docs. In particular, see a specific example of a plot with two sliders.

from pyquibbler import iquib, initialize_quibbler
initialize_quibbler()
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
import numpy as np
%matplotlib tk

# Figure setup:
fig = plt.figure(figsize=(4, 6))

axs = fig.add_axes([0, 0.36, 1, 0.64])
axs.axis('square')
axs.axis((-10, 10, -10, 10))
axs.axis('off')

axs_slider1 = fig.add_axes([0.3, 0.16, 0.55, 0.03])
axs_slider2 = fig.add_axes([0.3, 0.06, 0.55, 0.03])

# Defining quib input variables:
nPoints = iquib(120)
nCycles = iquib(30)

# Define downstream function quib calculations based on the quib inputs:
phi = np.linspace(0, 2 * np.pi * nCycles, nPoints)
r = np.linspace(0, 10, nPoints)

# Plot the data:
axs.plot(r * np.cos(phi), r * np.sin(phi), 'r-')

# Create quib-linked slider widgets:
Slider(ax=axs_slider1, label='nCycles', valmin=0, valmax=200, valstep=1, valinit=nCycles)
Slider(ax=axs_slider2, label='nPoints', valmin=0, valmax=200, valstep=1, valinit=nPoints)

For transparency: I am one of the developers of Quibbler.

Quibbler implementation of matplotlib Sliders

royk
  • 85
  • 7
-5

I don't think that simply plotting graphs using plt.plot will allow you to do this. You will need to yourself make a custom GUI script/app by embedding Matplotlib into it. Currently, Matplotlib supports all the major GUI toolkits - PyGTK+, PyQt4 and wxPython.
I use wxPython and embedding matplotlib in it is fairly easy. Similar should be case with the other GUI toolkits. You can get all the information you need for this in the book - enter image description here

It is available on amazon here.

Pushpak Dagade
  • 6,280
  • 7
  • 28
  • 41
  • 2
    If you want to do it fast and simple, then the method of the slider inside matplotlib is much better and easier. Just copy the snippet and change a few things. This book is also good, but more for advance and if you want to make it correct! – ahelm Jul 14 '11 at 19:26
  • 4
    Looks a bit like an advertisment and its contribution is questionable. It is true that a solution via a GUI toolkit is possible but it was not asked for such. – benni Jan 14 '17 at 11:29