Interactive matplotlib plot with two sliders

97,649

Solution 1

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

Solution 2

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

Solution 3

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

Solution 4

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

Solution 5

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

Share:
97,649

Related videos on Youtube

Open the way
Author by

Open the way

Updated on July 05, 2022

Comments

  • Open the way
    Open the way almost 2 years

    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?

  • ahelm
    ahelm almost 13 years
    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!
  • triplebig
    triplebig over 10 years
    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
  • Joe Kington
    Joe Kington over 10 years
    @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?
  • triplebig
    triplebig over 10 years
    @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
  • Joe Kington
    Joe Kington over 10 years
    @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").
  • triplebig
    triplebig over 10 years
    @JoeKington Yes, by the notebook. What sort of interactive backend should I be looking for? Does it normally come with the standard matplotlib libraries?
  • Joe Kington
    Joe Kington over 10 years
    @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
    Joe Kington over 10 years
    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.
  • benni
    benni over 7 years
    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.
  • mLstudent33
    mLstudent33 about 4 years
    AttributeError: 'Axes' object has no property 'axisbg'
  • mLstudent33
    mLstudent33 about 4 years
    Just like the demo here: matplotlib.org/3.1.1/gallery/widgets/slider_demo.html nothing responds on click.
  • mLstudent33
    mLstudent33 about 4 years
    Tried to get backend going but TypeError: interactive() missing 1 required positional argument: 'b'
  • David Buck
    David Buck about 4 years
    Could you please add your code as text so that it's of more use to other people.
  • Alex Poca
    Alex Poca about 4 years
    Very easy and intuitive. Thank you
  • mins
    mins over 3 years
    The code seems incomplete. Where are the lines of code which create the widgets?
  • Leo
    Leo over 3 years
    Widgets are imported. No extra lines are needed to use them. You can also follow the link to the original tutorial