1

I'm trying to use a horizontal slider to change the xlim of my plot. But first, I can't figure out how to get the slider to update using the on_changed() method. I don't have a strong understanding of how classes, and objects interact with each other.

I'm using this slider example as a template:

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

fig, ax = plt.subplots()
plt.subplots_adjust(left=0.25, bottom=0.25)
t = np.arange(0.0, 1.0, 0.001)
a0 = 5
f0 = 3
delta_f = 5.0
s = a0 * np.sin(2 * np.pi * f0 * t)
l, = plt.plot(t, s, lw=2)
ax.margins(x=0)

axcolor = 'lightgoldenrodyellow'
axfreq = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)
axamp = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=axcolor)

sfreq = Slider(axfreq, 'Freq', 0.1, 30.0, valinit=f0, valstep=delta_f)
samp = Slider(axamp, 'Amp', 0.1, 10.0, valinit=a0)


def update(val):
    amp = samp.val
    freq = sfreq.val
    l.set_ydata(amp*np.sin(2*np.pi*freq*t))
    fig.canvas.draw_idle()


sfreq.on_changed(update)
samp.on_changed(update)

resetax = plt.axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', color=axcolor, hovercolor='0.975')


def reset(event):
    sfreq.reset()
    samp.reset()
button.on_clicked(reset)

rax = plt.axes([0.025, 0.5, 0.15, 0.15], facecolor=axcolor)
radio = RadioButtons(rax, ('red', 'blue', 'green'), active=0)


def colorfunc(label):
    l.set_color(label)
    fig.canvas.draw_idle()
radio.on_clicked(colorfunc)

plt.show()

The part that I'm having trouble implementing in my app is:

sfreq.on_changed(update)
samp.on_changed(update)

It works fine if you're opening a plot using plt.show(), but if you're packing it into a canvas like I'm doing below, it stops working. Any ideas why?

Here is the code for my app: UPDATE: I simplified the code to focus on the problem.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import(FigureCanvasTkAgg, NavigationToolbar2Tk)

from tkinter import *
import tkinter as tk

global running
running = True

global graph_exists
graph_exists = False

class MyApp:
    def __init__(self, parent):
        self.myParent = parent ###remember my parent, the root
        self.sfreq = 0
        self.samp = 0

        
        
    def run(self):
        self.fig, self.ax = plt.subplots()
        plt.subplots_adjust(left=0.25, bottom=0.25)
        self.t = np.arange(0.0, 1.0, 0.001)
        a0 = 5
        f0 = 3
        delta_f = 5.0
        s = a0 * np.sin(2 * np.pi * f0 * self.t)
        self.l, = plt.plot(self.t, s, lw=2)
        self.ax.margins(x=0)

        axcolor = 'lightgoldenrodyellow'
        axfreq = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)
        axamp = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=axcolor)

        self.sfreq = Slider(axfreq, 'Freq', 0.1, 30.0, valinit=f0, valstep=delta_f)
        self.samp = Slider(axamp, 'Amp', 0.1, 10.0, valinit=a0)

        canvas = FigureCanvasTkAgg(self.fig, root)
        canvas.draw()
        canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
        graph_exists = True

        

    def update(self, val):
        amp = self.samp.val
        freq = self.sfreq.val
        self.l.set_ydata(amp*np.sin(2*np.pi*freq*self.t))
        self.fig.canvas.draw_idle()

    if graph_exists:
        self.sfreq.on_changed(update)
        self.samp.on_changed(update)
        

#Run the event loop
root = tk.Tk()
myapp = MyApp(root)
myapp.run()
root.mainloop()
6
  • Missing prefix self. on sfreq and samp. Commented Nov 19, 2020 at 23:24
  • Thanks. Didn't fix the problem, but one step closer. I updated the question to reflect this. Commented Nov 20, 2020 at 2:49
  • You called self.main_graph.update_on_change() inside MyApp.__init__(), but at that time, self.sfreq and self.samp are not created yet. Try removing that line. Commented Nov 20, 2020 at 3:05
  • ah. I see what you mean. So that eliminated the error, but that's because the slider just doesnt' do anything now. How can I tell it to check if graph_exists, and then list for on_changed()? Commented Nov 20, 2020 at 3:28
  • 1
    You can call the two .on_changed(self.update) after creating the two Slider. Also you need to define update() like def update(self, val) instead. However, according to the answer to other question, you need to add the figure into canvas before plotting. Commented Nov 20, 2020 at 6:15

2 Answers 2

3

As said in the link in my comment, you need to add the figure to canvas before plotting, otherwise the Sliders don't work. Also you should call on_changed(...) after creating the Sliders.

    def run(self):
        self.fig, self.ax = plt.subplots()
        canvas = FigureCanvasTkAgg(self.fig, root)  # add the figure to canvas before plotting
        plt.subplots_adjust(left=0.25, bottom=0.25)
        self.t = np.arange(0.0, 1.0, 0.001)
        a0 = 5
        f0 = 3
        delta_f = 5.0
        s = a0 * np.sin(2 * np.pi * f0 * self.t)
        self.l, = plt.plot(self.t, s, lw=2)
        self.ax.margins(x=0)

        axcolor = 'lightgoldenrodyellow'
        axfreq = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)
        axamp = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=axcolor)

        self.sfreq = Slider(axfreq, 'Freq', 0.1, 30.0, valinit=f0, valstep=delta_f)
        self.samp = Slider(axamp, 'Amp', 0.1, 10.0, valinit=a0)
        ### setup on_changed callbacks
        self.sfreq.on_changed(self.update) # use 'self.update' instead of 'update'
        self.samp.on_changed(self.update)
        ###
        #canvas = FigureCanvasTkAgg(self.fig, root)
        canvas.draw()
        canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
        graph_exists = True
Sign up to request clarification or add additional context in comments.

Comments

2
+50

The problem with your code is the method update is never called... You need to put the part if graph_exists into the run method. Besides, as suggested in the post @acw1668 shared, you also need to add the figure into canvas before plotting.

Try the code below

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import(FigureCanvasTkAgg, NavigationToolbar2Tk)

from tkinter import *
import tkinter as tk

global running
running = True

global graph_exists
graph_exists = False

class MyApp:

    def __init__(self, parent):
        self.myParent = parent ###remember my parent, the root
        self.sfreq = 0
        self.samp = 0

    def run(self):
        self.fig, self.ax = plt.subplots()
        canvas = FigureCanvasTkAgg(self.fig, root)
        canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
        
        plt.subplots_adjust(left=0.25, bottom=0.25)
        self.t = np.arange(0.0, 1.0, 0.001)
        a0 = 5
        f0 = 3
        delta_f = 5.0
        s = a0 * np.sin(2 * np.pi * f0 * self.t)
        self.l, = plt.plot(self.t, s, lw=2)
        self.ax.margins(x=0)

        axcolor = 'lightgoldenrodyellow'
        axfreq = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)
        axamp = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=axcolor)

        self.sfreq = Slider(axfreq, 'Freq', 0.1, 30.0, valinit=f0, valstep=delta_f)
        self.samp = Slider(axamp, 'Amp', 0.1, 10.0, valinit=a0)
        graph_exists = True
        
        if graph_exists:
            self.sfreq.on_changed(self.update)
            self.samp.on_changed(self.update)

    def update(self, val):
        print('inside update')
        amp = self.samp.val
        freq = self.sfreq.val
        self.l.set_ydata(amp*np.sin(2*np.pi*freq*self.t))
        
        self.fig.canvas.draw_idle()
        

#Run the event loop
root = tk.Tk()
myapp = MyApp(root)
myapp.run()
root.mainloop()

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.