使用Python更新GUI显示的值:修订版(新难点)
我在开发一个符合我需求的图形用户界面(GUI)时遇到了一些麻烦。我正在使用Tkinter和Python,查阅了很多相关资料,但都没有成功。
下面是我的情况:
背景
我正在开发一个可以控制温度和压力的环境。我的程序已经写得比较复杂,能够控制和记录压力的变化。简单来说,它使用线程来同时处理用户输入、控制一个执行器、记录数据,这些都是针对压力的。最开始的想法是只偶尔检查一下温度,但在开发过程中,我发现同时记录和显示温度会比不这样做要有用得多。温度是通过热电偶测量的,然后通过数据采集设备(DAQ)获取数据。我是工程师,编程不是我的强项,但我完成了一些在线Python教程,觉得自己对这门语言有一些“工作”知识(这个词用得比较宽泛)。
程序
于是我开始开发我现有程序的新部分。我用和开发的功能似乎都能满足我的需求。我可以访问DAQ上的目标端口,读取并存储值,使用一个导入的函数转换电压(也就是说,我知道它是有效的)。现在我配置为打印每个值,以便我可以检查这些结果,并且在命令行中对结果感到满意。我把这个功能开发成一个独立的程序,之后会整合到已有的程序中,但由于一些奇怪的硬件限制,我先这样做,目前我正在处理的只是它自己的程序。
现在,问题来了:
我有了可用的功能,但需要把它们整合到我的GUI中。如果GUI能正常工作,它会简单地显示一个按钮“获取温度”,以及一个标签“温度:XX”,其中XX会显示程序获取并转换的温度值。我的问题在于如何更新XX的值。我可以运行必要的函数并将结果存储到一个变量中,但就是不知道怎么把它显示出来并更新。我查阅了Tkinter中的.get()和.set(value),但经过几次尝试都没有成功。我离得最近的结果是显示0.0或者PY_VARXX,而不是温度值。下面是GUI的样子:
GUI我怀疑我的问题可能和程序的结构有关。生成GUI的代码在这里:
import LabJackPython
import u3
import sys
import getopt
from threading import Thread
from time import time, sleep
from Tkinter import Tk, Frame, Button, Toplevel, Label, Entry, StringVar, DoubleVar
import tkMessageBox
from datetime import datetime, date
#define global variables
v1 = None
g_get = None
Gain = None
GainE = None
VoltE = None
g_Conv = None
vv1 = None
vr1 = None
v2 = None
vread = None
CJT = None
T1 = None
T1C = None
#configure DAQ
d = u3.U3()
d.configIO(FIOAnalog = 12)
#define functions
#voltage to temperature conversion
def VTT(v, voff, gain):
global v1, T1, vread, CJT, Gain
vread = d.getAIN(2)
v = vread
CJT = d.getTemperature()
gain
if v > 14.9:
print "Invalid Input"
else:
v1 = (v - voff)/gain
T1 = LabJackPython.TCVoltsToTemp(LabJackPython.LJ_ttJ, v1, CJT)
print "vread:", vread
print "v:", v
print "v1:", v1
print "CJT:", CJT
print "T1 (K):", T1
#run VTT on button click with fixed values
def assign(event):
global T1C
VTT(1, 1.25, 201)
T1C = T1 - 273.15
print "T (C):", T1C
#initiate GUI
def main():
global v1, T1, vread, CJT, Gain
root = Tk()
root.title("Temp")
Temp = Frame(root)
Temp.grid()
TempL = Label(Temp, text = "Temperature:", font = 20)
TempL.grid(row = 4, column = 0)
#Define variables
T1C = DoubleVar()
T1C1 = DoubleVar()
T1C1.set(T1C)
#variable
Temperature = Label(Temp, textvariable = T1C1, font = 20)
Temperature.grid(row = 4, column = 1)
#button
g_get = Button(Temp, text = "get temp", height = 2, width = 10)
g_get.bind("<Button-1>", assign)
g_get.grid(row = 2, column = 0)
try:
root.mainloop ()
except KeyboardInterrupt:
print "Keyboard Interrupt Caught!"
exit()
如果有人能提供帮助,那就太好了。如果我做的事情有更好的做法,我也很乐意听取意见。我对代码规范或最佳实践了解不多,肯定代码中有很多问题,但我最关心的是解决问题,让T1C的值在GUI中显示出来。
谢谢大家
编辑:关于使用类的方法而不是全局变量,我的回应是,当我把它整合回更大的程序时,我需要在多个地方访问T1C的值(或者T1,或T1C1,基本上是一样的)。一个函数生成这个值,另一个函数需要获取这个值并写入日志文件,同时这个值还需要在GUI中显示和更新。最终的整合不会是基于按钮的方式,而是按钮只是为了我在开发时使用。在重新整合时,VTT会持续运行,并大约每0.2秒写入一次日志文件,同时记录压力和读取时间。我可以提供这个更大程序的代码,但它相当复杂,我不知道在没有上下文的情况下它能否让人理解。如果这样能帮助我更好地解释,请告诉我。
编辑2:根据建议,我尝试用基于类的方法重新构建GUI,结果我觉得现在的程序逻辑更清晰了,虽然仍然有一些困难,但谢谢大家的帮助。
1 个回答
快速解决方案:
现在的代码会“破坏”在 T1C
变量中存储的准全局 DoubleVar()
,因为它通过重新赋值给了另一个值。
assign(event):
global T1C
VTT(1, 1.25, 201)
T1C = T1 - 273.15 # shall rather read T1C.set( T1 - 273.15 )
print "T (C):", T1C # shall rather read print "T [C]:", T1C.get()
干净且可扩展的GUI附加解决方案:
将代码重构为基于类的方式,这样可以:
- 将设备读取的
<controller>
部分和所有<state>
变量的<model>
部分封装在一起(类内可重复使用并受到保护) - 实现智能且流畅的GUI集成,因为这个类已经准备好为它的
<visual>
部分服务 - 避免使用
global
变量(可以并且应该依赖于类实例内部的变量,这样就不会有其他错误或问题破坏你的控制值)