在tkinter循环中连续访问线程
我正在尝试制作一个GUI,它可以连续绘制从微处理器接收到的信号。我曾尝试只使用类来实现这一点,但由于只使用GUI类,所以失败了。现在我已经实现了线程(或者至少我认为我实现了!?),但是每个线程只运行一次。这让我相信我不了解tkinter中的main循环是如何工作的,所以我可以重新编写代码,让线程变为活动的吗在tkinter循环中连续访问线程,tkinter,serial-port,updating,Tkinter,Serial Port,Updating,我正在尝试制作一个GUI,它可以连续绘制从微处理器接收到的信号。我曾尝试只使用类来实现这一点,但由于只使用GUI类,所以失败了。现在我已经实现了线程(或者至少我认为我实现了!?),但是每个线程只运行一次。这让我相信我不了解tkinter中的main循环是如何工作的,所以我可以重新编写代码,让线程变为活动的吗 import Tkinter import tkMessageBox as messagebox from serial import * from matplotlib.backends.
import Tkinter
import tkMessageBox as messagebox
from serial import *
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import numpy as np
import time
import threading
go=0
x=[0.0001,0.0002,0.0003]
y=[3600,1000,2000]
stid=time.time()
root = Tkinter.Tk()
root.title("Serial gui")
class SensorThread(threading.Thread):
def run(self):
global run
global x
global y
global stid
print "run er ok"
if go==1:
print "go er ok"
ser = Serial(5, 9600, timeout=1)
f=ser.read(4)
ser.close()
x.append(time.time()-stid)
y.append(f)
class Foo:
def __init__(self, master):
print "foo ok"
frame = Tkinter.Frame(root)
self.button_left = Tkinter.Button(frame,text="Start",
command=self.start)
self.button_left.pack(side="left")
self.button_right = Tkinter.Button(frame,text="Stop",
command=self.stop)
self.button_right.pack(side="right")
self.button_midt = Tkinter.Button(frame, text='Quit', command=self.terminate)
self.button_midt.pack(side="bottom")
fig = Figure()
ax = fig.add_subplot(111, axisbg='black')
canvas = FigureCanvasTkAgg(fig,master=master)
canvas.show()
canvas.get_tk_widget().pack(side='top', fill='both', expand=1)
frame.pack()
line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma
line1.set_ydata(y)
fig.canvas.draw()
def start(self):
global go
go=1
print go
def stop(self):
global go
go=0
print go
def terminate(self):
root.quit() # stops mainloop
root.destroy() # this is necessary on Windows to prevent
# Fatal Python Error: PyEval_RestoreThread: NULL tstate
if __name__ == "__main__":
SensorThread().run()
Foo(root)
root.mainloop()
我希望你们中的一些人能帮助我把这段代码变成一个实时更新绘图的程序
超级我在程序中更改了以下内容
class SensorThread(threading.Thread):
def run(self):
global run
global x
global y
global stid
#print "run er ok"
if go==1:
print "go er ok"
ser = Serial(17, 9600, timeout=1)
f=ser.read(4)
ser.close()
x.append(time.time()-stid)
y.append(f)
SensorThread().start()
else:
SensorThread().start()
class Foo:
....
if __name__ == "__main__":
SensorThread().start()
Foo(root)
root.mainloop()
但是它仍然没有更新plottet的图形,它不应该在Foo类中这样做吗?现在,当我退出python脚本或完全退出python脚本时,它仍然使用50%的CPU功率,可能是因为传感器线程现在永远运行 看看这个食谱,它展示了如何做:
您误用了
线程
模块;您不应该调用.run()
,而应该调用.start()
。有关更多信息,请参阅。谢谢mmgp我已尝试实现我编辑的内容,但我的图形仍保持初始化状态。但是我仍然有一些问题,这可以从我编辑的问题中看出。现在你在run
方法中调用start
:/有一些误解。例如,在\uuuu init\uuuuu
内只调用一次绘图
;为什么仅仅因为您更改了列表/在/执行了绘图之后/绘图,并且再也没有更新它,您就希望绘图自动更新?如果您可以在几百毫秒内读取传感器,那么线程是完全不必要的,并且增加了不必要的复杂性。在stackoverflow中搜索大量与之后的使用相关的问题。这实际上是不相关的,这里的另一个线程没有与Tk GUI元素交互。实际上不需要使用sys.exit(1)
,只需为其他创建的线程设置daemon=True
。下面是一个使用队列在线程之间进行通信的简化答案(因为它有一堆用于执行其他操作的其他代码,所以答案更长:。如果所有的东西都是用线程构建的,那么你也可以使用event\u generate
和void来为这个特定的任务使用队列。这可能有点扭曲,但对我来说这是一个起点。当然,您必须在示例中有“print msg”的GuiPart.processIncoming中添加实际的工作部件。“thread1.daemon=True”当然是一个很好的提示。我不是说它应该包含实际工作部分的代码。我的意思是这里显示的代码,就像你说的,“扭曲”,而另一个链接的答案更直接,提供了相同的通信方法。此外,为了强调,目前的问题与这些刚刚联系起来的答案所提供的内容无关。