Reputation: 61
I've tried a couple different things to get this code to stop (after [doesn't stop it], quit[closes GUI]). What I found that "worked" is it throwing an error..... This bothers me. Throwing an error, then just restarting the graph is not ideal. I want to clean this code up (so any other suggestions are appreciated as well!)
Am I missing something? Shouldn't stopping a function be simple? I will post the main code, which is a lot still since there are a lot of functions. The two that I want to stop (based off what prints out) are the update_graph and get_data. If I say I want 5 files, it gets to 5 files (i=5), then restarts at 0 (i=0).
What the code is supposed to do:
First, it shows a blank graph on a GUI. Person can set the number of files they wish to have and set up the sensor integration time. Then, they can start the data acquisition and the graph pops up every second or so until the number of files is reached. This last part is what I cannot get. Stopping the file creation and the graphing when the number of files is reached. (Note: the file creation is perfect in another code. I am only showing this code so that I don't create a ton of files troubleshooting this).
try:
import Tkinter as tk
except ImportError:
import tkinter as tk
from PIL import ImageTk, Image
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
import matplotlib.animation as animation
import serial
import random
import os
import datetime
import numpy as np
after_id = None
def get_data():
#i=0
rand_x = list(range(100))
rand_y = [random.randrange(100) for _ in range(100)]
#create a file
## In my code, this function send data through serial port from the Raspberry pi to the arduino and the arduino send the y axis data back.
##I then create the appropriate x axis
return rand_x, rand_y
class App(tk.Frame):
def __init__ (self, master=None, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
self.running = False
self.ani = None
btns = tk.Frame(self)
btns.pack()
lbl = tk.Label(btns, text="Number of times to run")
lbl.pack(side=tk.LEFT)
self.points_ent = tk.Entry(btns, width=5)
self.points_ent.insert(0,'50')
self.points_ent.pack(side=tk.LEFT)
lbl = tk.Label(btns, text="Intergration Time")
lbl.pack(side=tk.LEFT)
self.interval = tk.Entry(btns, width=5)
self.interval.insert(0, '100')
self.interval.pack(side=tk.LEFT)
self.btn = tk.Button(btns, text='Start', command=self.on_click)
self.btn.pack(side=tk.LEFT)
self.fig = plt.Figure()
self.ax1 = self.fig.add_subplot(111)
self.line, = self.ax1.plot([], [], lw=2)
self.canvas = FigureCanvasTkAgg(self.fig, master=self)
self.canvas.show()
self.canvas.get_tk_widget().pack()
self.ax1.set_ylim(0, 500)
self.ax1.set_xlim(0, 100)
def on_click(self):
print('onclick')
if self.ani is None:
return self.start()
if self.running:
self.ani.event_source.stop()
self.btn.config(text='Un-Pause')
print('pause')
else:
self.ani.event_source.start()
self.btn.config(text='Pause', command=get_data())
print('unpause')
self.running = not self.running
def start(self):
global interval
self.points = int(self.points_ent.get()) + 1
print(self.points)
self.ani = animation.FuncAnimation(
self.fig, self.update_graph,
frames=self.points,
interval=int(self.interval.get()),
repeat=True)
self.running = True
self.btn.config(text='Pause')
self.ani._start()
print('started animation')
def update_graph(self, i):
while os.path.exists(str(directory) + "/Data" + str(i) + "/"):
i +=1
subfolder = (str(directory) + "/Data" + str(i) + "/")
global filename
filename = "/home/pi/Documents/Serial" + str(i) + ".txt"
self.line.set_data(*get_data())
print('update_graph')
print(i)
global after_id
after_id = self.after(1, get_data)
if i >= self.points - 1:
self.running = False
self.ani = None
self.btn.config(text='Start', command=self.stop())
def stop(self):
global after_id
self.after_cancel(after_id)
Upvotes: 1
Views: 407
Reputation: 10542
I think you're missing one very important statement in your update_graph
function and have tried a lot with after
and changing button commands to try and fix it.
What you're missing in update_graph
is simply self.ani._stop()
to stop the animation when the desired number of iterations is met.
So putting that in, and removing all of the after
calls which I don't think did any good, leaving the button command alone I come to this, which I feel does exactly what you want:
try:
import Tkinter as tk
except ImportError:
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
import matplotlib.animation as animation
import random
def get_data():
#i=0
rand_x = list(range(100))
rand_y = [random.randrange(100) for _ in range(100)]
#create a file
## In my code, this function send data through serial port from the Raspberry pi to the arduino and the arduino send the y axis data back.
##I then create the appropriate x axis
return rand_x, rand_y
class App(tk.Frame):
def __init__ (self, master=None, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
self.running = False
self.ani = None
btns = tk.Frame(self)
btns.pack()
lbl = tk.Label(btns, text="Number of times to run")
lbl.pack(side=tk.LEFT)
self.points_ent = tk.Entry(btns, width=5)
self.points_ent.insert(0,'50')
self.points_ent.pack(side=tk.LEFT)
lbl = tk.Label(btns, text="Intergration Time")
lbl.pack(side=tk.LEFT)
self.interval = tk.Entry(btns, width=5)
self.interval.insert(0, '100')
self.interval.pack(side=tk.LEFT)
self.btn = tk.Button(btns, text='Start', command=self.on_click)
self.btn.pack(side=tk.LEFT)
self.fig = plt.Figure()
self.ax1 = self.fig.add_subplot(111)
self.line, = self.ax1.plot([], [], lw=2)
self.canvas = FigureCanvasTkAgg(self.fig, master=self)
self.canvas.show()
self.canvas.get_tk_widget().pack()
self.ax1.set_ylim(0, 500)
self.ax1.set_xlim(0, 100)
def on_click(self):
print('onclick')
if self.ani is None:
return self.start()
if self.running:
self.ani.event_source.stop()
self.btn.config(text='Un-Pause')
print('pause')
else:
self.ani.event_source.start()
self.btn.config(text='Pause')
print('unpause')
self.running = not self.running
def start(self):
self.points = int(self.points_ent.get()) + 1
print(self.points)
self.ani = animation.FuncAnimation(
self.fig, self.update_graph,
frames=self.points,
interval=int(self.interval.get()),
repeat=True)
self.running = True
self.btn.config(text='Pause')
self.ani._start()
print('started animation')
def update_graph(self, i):
self.line.set_data(*get_data())
print('update_graph')
print(i)
if i >= self.points - 1:
self.running = False
self.ani._stop()
self.ani = None
self.btn.config(text='Start')
root = tk.Tk()
app = App(root)
app.pack(expand=1, fill=tk.BOTH)
root.mainloop()
Upvotes: 1