我想将函数的值传递或存储到变量,然后使用该变量放入标签。
我尝试对变量进行全局赋值,但它给出了错误:未定义名称“num”。您的意思是:'sum'?.
global num
def takeWeight():
ser = serial.Serial('COM3', 57600, timeout=1)
while True:
line = ser.readline() # read a byte
if line:
num = line.decode().strip()
print(num)
ser.close()
weightDisplay = ttk.Label(startScan_frame, text=num)
weightDisplay.grid(column=2, row=4)
weightDisplay.grid_configure(pady=20)
所以你走对了!但是您对全局变量的处理方式有点错误。
您在函数外定义的任何变量都存在于全局范围内。您使用 global
关键字inside 允许该函数修改该全局变量的值
def takeWeight():
global num # allow this function to modify the value of 'num'
ser = serial.Serial('COM3', 57600, timeout=1)
while True:
line = ser.readline() # read a byte
if line:
num = line.decode().strip()
print(num)
ser.close()
num = '' # declare 'num' as a placeholder in the global scope
weightDisplay = ttk.Label(startScan_frame, text=num)
weightDisplay.grid(column=2, row=4)
weightDisplay.grid_configure(pady=20)
就是说,因为这是 tkinter,您真正想要的是将您的Label
的
textvariable
属性绑定到
tk.StringVar()
那是什么意思?
def takeWeight():
ser = serial.Serial('COM3', 57600, timeout=1)
while True:
line = ser.readline() # read a byte
if line:
num.set(line.decode().strip()) # call 'set' to update the value of 'num'
print(num.get()) # call 'get' to retrieve the current value of 'num'
ser.close()
num = tk.StringVar() # create an instance of 'StringVar' to store your value
# and then bind that variable to your label - this way, whenever you update 'num'
# using 'num.set('whatever'), your label will update automatically!
weightDisplay = ttk.Label(startScan_frame, textvariable=num)
weightDisplay.grid(column=2, row=4)
weightDisplay.grid_configure(pady=20)
到目前为止一切顺利!不幸的是,由于 While
循环,您仍然会在这里遇到问题!阻塞操作和循环不能很好地与 tkinter 一起使用,并且会导致您的 GUI 挂起。幸运的是,这也是可以修复的!tkinter 有一个名为
after
的方法,它让您基本上可以将函数调用插入到 GUI 事件循环中。这样,您可以在保持 GUI 运行的同时轮询串行端口以获取新信息。您只需要修改
takeWeight
即可使用
after
# 'ser' doesn't need to be declared *every* time 'takeWeight' is called,
# so let's move it into the global scope (this also lets us refer to 'ser'
# in the 'close' method described below!)
ser = serial.Serial('COM3', 57600, timeout=1)
# let's also declare a placeholder that will store the id of the 'after' call
serial_loop_id = None
def takeWeight():
global serial_loop_id
line = ser.readline() # read a byte
if line:
num.set(line.decode().strip())
# use 'after' to call this function again after 100mS
serial_loop_id = root.after(100, takeWeight)
现在请注意,ser.close()
不再被
takeWeight
调用 - 你必须稍微不同地处理这个问题!在这种情况下,您可以只定义一个
close
函数来停止
takeWeight
循环,然后调用
ser.close
。准备就绪后,只需致电
close()
即可。
def close():
root.after_cancel(serial_loop_id) # stop the 'takeWeight' polling loop
ser.close() # close the serial port
注意:root.after()
和
root.after_cancel()
假设您的
tk.Tk
的主要实例称为
root
。如果你做了像
mainwindow = tk.Tk()
这样的事情,请改用
mainwindow.after()
!最后一点,
takeWeight
中的串行轮询不会开始,直到您在某处实际调用
takeWeight
函数,a la
takeWeight()
.
num = None
def takeWeight():
global num
...
通过进行此更改,您可以从函数内部更新全局变量,而无需更改函数的功能。问题是您只会更新全局变量而不更新标签内的文本。要解决此问题,您可以通过更改其文本来引用标签本身,将其添加回网格,然后更新窗口。下面是可以使用的代码。
def takeWeight():
ser = serial.Serial('COM3', 57600, timeout=1)
while True:
line = ser.readline() # read a byte
if line:
weightDisplay.grid_forget()
weightDisplay.configure(text = line.decode().strip())
weightDisplay.grid(column=2, row=4, pady=20)
# then add your_window_name.update() and your_window_name.update_idletasks()
ser.close()
weightDisplay = ttk.Label(startScan_frame, text="default text for label")
weightDisplay.grid(column=2, row=4, pady=20)
通过删除标签并将其重新添加到网格中,您可以确保它得到更新并正确显示。您也不需要全局变量。只要确保你有办法停止在函数中创建的无限 while 循环。