Python:如何在保持GUI交互性的同时使用单独进程中的变量更新GUI
在阅读了大量关于多进程、管道等的内容后,我还没有找到答案,但如果答案已经存在,我深表歉意 我有一块外围硬件,我正试图为它创建一个GUI。我想让GUI不断地更新来自外围设备的数据,同时仍然保持用户的交互性。例如,我有一个增益参数,我用它来驱动一个条形图,当它不断被更新时,我希望用户能够点击一个按钮来进行一些操作。下面是一些示例代码。尽管我确信我在这里犯了一些严重的错误,但这实际上几乎起作用,但“退出”按钮仍然没有响应:Python:如何在保持GUI交互性的同时使用单独进程中的变量更新GUI,python,user-interface,tkinter,multiprocessing,pipe,Python,User Interface,Tkinter,Multiprocessing,Pipe,在阅读了大量关于多进程、管道等的内容后,我还没有找到答案,但如果答案已经存在,我深表歉意 我有一块外围硬件,我正试图为它创建一个GUI。我想让GUI不断地更新来自外围设备的数据,同时仍然保持用户的交互性。例如,我有一个增益参数,我用它来驱动一个条形图,当它不断被更新时,我希望用户能够点击一个按钮来进行一些操作。下面是一些示例代码。尽管我确信我在这里犯了一些严重的错误,但这实际上几乎起作用,但“退出”按钮仍然没有响应: #!/usr/bin/env python` # -*- coding: ut
#!/usr/bin/env python`
# -*- coding: utf-8 -*-
# 2014-07-24 S. Petit
import matplotlib.pyplot as plt
from serial import Serial
import serial, socket, time, datetime, sys, struct
from datetime import datetime
import numpy as np
import shutil
import os
from random import randint
from Tkinter import *
from multiprocessing import *
dcbSerialPort = 'COM10'
def getGainLNA(pipeToParent):
try:
S_dcb = Serial(dcbSerialPort, 115200, timeout=.1)
print 'Opened DCB at', dcbSerialPort
except:
print '\r\n'
print '*************************************************'
print 'ERROR: Unable to open', dcbSerialPort, 'serial connection.'
print '*************************************************'
print '\r\n'
raw_input()
exit()
while True:
promptFound = False
PICreturn = ''
S_dcb.write('gain\r')
while not promptFound:
PICreturn += S_dcb.read(S_dcb.inWaiting())
if 'DCB>' in PICreturn:
promptFound = True
gainLNA = float(PICreturn[20:28].strip())
gainLNA_scaled = int(100*(gainLNA/33))
pipeToParent.send(gainLNA_scaled)
return()
if __name__ == '__main__':
gainUpdaterPipe, gainUpdaterPipeChild = Pipe()
lnaGainUpdater = Process(target=getGainLNA, args=(gainUpdaterPipeChild,))
lnaGainUpdater.start()
root=Tk()
root.title = 'AGC'
while True:
if gainUpdaterPipe.poll():
gainLNA = gainUpdaterPipe.recv()
print gainLNA
quitButton = Button(text='Quit', command=quit)
quitButton.grid(row=1, column=0)
areaAGC = Canvas(width=120, height=100, bg='blue')
objectAGC = areaAGC.create_polygon(20,20, gainLNA,20, gainLNA,50, 20,50, outline='green', fill='yellow')
areaAGC.grid(row=0, column=0)
root.update_idletasks()
谢谢你的帮助。。。
史蒂夫P
编辑:好的,在尝试使用@ebarr的示例之后,我得到了以下内容。标签小部件会随着计数而更新,但条形图不会:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 2014-07-24 S. Petit
import matplotlib.pyplot as plt
from serial import Serial
import serial, socket, time, datetime, sys, struct
from datetime import datetime
import numpy as np
import shutil
import os
from random import randint
import Tkinter as tk
from multiprocessing import *
dcbSerialPort = 'COM10'
# count from 0 to infinity, writing the value to a pipe
def count(pipe,stop):
ii = 0
while not stop.is_set():
ii+=1
pipe.send(ii)
time.sleep(1)
class UpdatingGUI(tk.Frame):
def __init__(self,parent):
tk.Frame.__init__(self,parent)
self.parent = parent
self.parent_pipe, self.child_pipe = Pipe()
self.stop_event = Event()
# label to show count value
self.updating_int = tk.IntVar()
self.updating_int.set(0)
self.updating_lbl = tk.Label(self,textvariable=self.updating_int)
self.updating_lbl.pack()
# bargraph to show count value
self.area_barGraph = tk.Canvas(width=120, height=100, bg='blue')
self.bargraph = self.area_barGraph.create_polygon(10,10, (10+self.updating_int.get()),10, (10+self.updating_int.get()),20, 10,20, outline='green', fill='yellow')
self.area_barGraph.pack()
# button that will stay responsive to requests while count is on going
self.quit_btn = tk.Button(self,text="Quit",command=self.quit)
self.quit_btn.pack()
# launch count as a process
self.counter = Process(target=count,args=(self.child_pipe,self.stop_event))
self.counter.start()
# call an update method to check the pipe and update the label
self.update()
def quit(self):
self.stop_event.set()
self.parent.destroy()
def update(self):
# While the pipe has data, read and update the StringVar
while self.parent_pipe.poll():
self.updating_int.set(self.parent_pipe.recv())
# set the update method to run again in 1 seconds time
self.parent.after(1000,self.update)
def main():
root = tk.Tk()
gui = UpdatingGUI(root)
gui.pack()
root.mainloop()
# print __name__
if __name__ == "__main__":
main()
您非常接近一个有效的解决方案。如上所述,在之后使用tkinter
将解决大部分问题
下面是一个单独进程(运行简单计数器)传递可用于更新GUI的状态的最小示例:
import Tkinter as tk
from multiprocessing import Event,Process,Pipe
from time import sleep
# count from 0 to infinity, writing the value to a pipe
def count(pipe,stop):
ii = 0
while not stop.is_set():
ii+=1
pipe.send(ii)
sleep(1)
class UpdatingGUI(tk.Frame):
def __init__(self,parent):
tk.Frame.__init__(self,parent)
self.parent = parent
self.parent_pipe, self.child_pipe = Pipe()
self.stop_event = Event()
# label to show count value
self.updating_txt = tk.StringVar()
self.updating_txt.set("Waiting...")
self.updating_lbl = tk.Label(self,textvariable=self.updating_txt)
self.updating_lbl.pack()
# button that will stay responsive to requests while count is on going
self.quit_btn = tk.Button(self,text="Quit",command=self.quit)
self.quit_btn.pack()
# launch count as a process
self.counter = Process(target=count,args=(self.child_pipe,self.stop_event))
self.counter.start()
# call an update method to check the pipe and update the label
self.update()
def quit(self):
self.stop_event.set()
self.parent.destroy()
def update(self):
# While the pipe has data, read and update the StringVar
while self.parent_pipe.poll():
self.updating_txt.set(self.parent_pipe.recv())
# set the update method to run again in 1 seconds time
self.parent.after(1000,self.update)
def main():
root = tk.Tk()
gui = UpdatingGUI(root)
gui.pack()
root.mainloop()
if __name__ == "__main__":
main()
更新
作为对更新代码的回应:您已经基本完成了,唯一的问题是您只调用了一次条形图创建者,而它需要添加到您的update
函数中,如:
def update(self):
# While the pipe has data, read and update the StringVar
while self.parent_pipe.poll():
self.updating_int.set(self.parent_pipe.recv())
dx = self.updating_int.get()
self.area_barGraph.create_polygon(10,10, (10+dx),10, (10+dx),20, 10,20, outline='green', fill='yellow')
# set the update method to run again in 1 seconds time
self.parent.after(1000,self.update)
这将确保每次更新intVar时,图形也会相应地更新。只是说,tk
在大多数情况下都被认为是一个玩具gui,您可能想先看看类似于wxpython
的东西:不要在Tkinter中使用而在Tkinter中使用而在根之后(时间,函数)
要重复运行某个东西,第二:root.mainloop()在哪里?我认为这是一个很好的起点。所以我做了两件事。我将更新变量类更改为IntVar
,然后尝试使用它来驱动多边形的大小。在图形窗口中,标签小部件会随着计数器计数而更新,但条形图就在那里。我正试图在这里发布一些代码,但我愚蠢地不知道如何让评论窗口接受换行符…如果您想发布代码,请将其作为原始问题进行编辑或发布新问题。听起来您需要为图形部分调用一些更新方法。这很有效,谢谢。我在这里要补充的另一件事是,我必须在每次更新时也绘制一个“背景”矩形,否则,如果我的条形图缩小,则旧的、长的矩形没有被清理。