【发布时间】:2018-05-17 13:28:56
【问题描述】:
我尝试了几种不同的方法来停止此代码(在 [不停止它] 之后,退出 [关闭 GUI])。我发现“有效”的是它抛出了一个错误......这让我很困扰。抛出错误,然后重新启动图表并不理想。我想清理这段代码(因此也欢迎任何其他建议!)
我错过了什么吗?停止功能不应该很简单吗?我将发布主要代码,因为有很多功能,所以仍然很多。我想要停止的两个(基于打印出来的)是 update_graph 和 get_data。如果我说我想要 5 个文件,它会得到 5 个文件 (i=5),然后从 0 (i=0) 重新开始。
代码应该做什么:
首先,它在 GUI 上显示一个空白图形。人们可以设置他们希望拥有的文件数量并设置传感器集成时间。然后,他们可以开始数据采集,图表每隔一秒左右弹出一次,直到达到文件数量。最后一部分是我无法得到的。达到文件数时停止文件创建和绘图。 (注意:文件创建在另一个代码中是完美的。我只显示此代码,这样我就不会创建大量文件来解决这个问题)。
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)
【问题讨论】:
-
“停止函数”是什么意思? (通常很难停止一个函数,因为函数会一直运行直到它们返回)你能提供一个minimal reproducible example 的问题吗?你能解释一下代码应该做什么吗?
-
您可以尝试创建一个MCVE 吗?就像现在一样,没有人可以复制粘贴这段代码来查看它的作用并对其进行调试。如果您可以从
update_graph和get_data函数中去除重现问题所不需要的代码,并让它们返回随机数之类的东西,那么任何人都可以运行它并尝试帮助您。还请包括使用的导入语句。 -
提供了更好的描述。希望有帮助。我现在也将导入语句粘贴进去。这段代码下面是一个简单的 def main() 和 root = tk.Tk() 我也可以添加它。至于简化它,我使用的是串行端口。所以我不确定随机数据与真实数据涌入设备的关系如何(我使用的是树莓派)。但我可以试试! :)
-
感谢编辑!我的意思是问题不在于从串行端口获取数据,因此所有能够获取数据的代码都与这个问题无关。特别是由于人们没有您的文件和带有您的配置的树莓派,因此包含此代码会使其他人更难调试。返回随机的“虚拟”数据可以解决这个问题。基本上,这就是 MCVE 中的 Minimal 的全部意义所在。当然,如果取出这部分代码确实可以解决问题,你应该保留它(并告诉我们,因为这是非常有价值的信息)。
-
所以,在树莓派上尝试了这段代码(带有随机数据),问题仍然存在。目标是在设定的文件数量后停止绘图。它会自动设置为 50。但是,如果我想进行快速调试/故障排除,我通常会将其更改为 10 之类的其他值。如果这对任何人都不起作用,我可以尝试重做它,以便大家更轻松地进行调试。
标签: python-3.x function matplotlib tkinter