如何根据来自单独线程的特定值更新小部件?
How can I update a widget according to a certain value that comes from a seperate thread?
我的 GUI 应用程序有多个屏幕 (3),每个屏幕都包含用户可以更改的某些文本小部件(我根据 Whosebug 中众所周知的解决方案建立了这个多屏幕 GUI)。填写某个屏幕的字段后,用户可以 "Burn" 这些值到某个硬件。为了能够 "Burn" 到 HW,我在应用 运行.
后立即启动到 HW 的 Telnet 会话(IP 是硬编码的)
每个框架都显示状态栏,我想用 Telnet 连接到 HW 的当前状态更新它。为了维护 Telnet 连接,我使用了一个单独的线程。而且我还使用了一个队列来更新当前状态。
当 Telnet 会话状态更改时,我可以通过简单地打印到控制台来更新。如果 Telnet 会话因任何好的(或坏的)原因断开连接,我也能够恢复它。
我的问题是我无法用当前状态更新状态栏(状态标签)。在下面的代码中,您可以看到我已尝试在状态更改时生成一个事件。但是,它没有完成工作。如何使用实际状态更新状态栏?
编辑后
(我费了很大的劲,去掉了200多行代码):
from tkinter import font, ttk
import tkinter as tk
from re import match
import telnetlib
import threading
import queue
import time
LARGE_FONT= ("Verdana", 12)
Current_PN = '123456789' # This global ver is used for the purpose of automatic PN fill
HOST = '10.0.1.235'
PORT = 23
telnet_session = None # After I create the Telnet session I will keep track with this variable
connected = False
class BurningApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.title('Burning App') #handling the application's Window title
w = 1000 # Windows width
h = 600 # Windows height
ws = self.winfo_screenwidth() # Screen resolution width
hs = self.winfo_screenheight() # Screen resolution height
# w = ws * 0.8 # Fit the GUI width to 80% percent of the screen
# h = hs * 0.8 # Fit the GUI height to 80% percent of the screen
x = (ws/2) - (w/2) # X coordinate for the purpose of GUI placement
y = (hs/2) - (h/2) # X coordinate for the purpose of GUI placement
self.resizable(width=False, height=False)
self.geometry('%dx%d+%d+%d'%(w,h,x,y))
self.frames = {}
for F in (MainScreen, FirstScreen, SecondScreen):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(MainScreen)
# Start the Telnet session
self.connect_to_uut()
# Create the queue that will hold the status
self.status_queue = queue.Queue()
# Set up the thread to do asynchronous I/O
self.running = 1 # True
self.thread = threading.Thread(target=self.workerThread)
self.thread.start()
# Start the periodic call in the GUI to check if the queue contains
# anything
self.periodicCall()
def show_frame(self, cont):
'''
This function is being used in order to raise a frame on demand
'''
frame = self.frames[cont]
frame.tkraise()
def connect_to_uut(self, Retry=5):
'''
This functions is used for the purpose of connecting to the UUT
'''
global telnet_session
global connected
for _ in range(Retry):
try:
telnet_session = telnetlib.Telnet(HOST, PORT, timeout=5)
connected = True
self.event_generate("<<StatusChange>>")
break
except:
connected = False
self.event_generate("<<StatusChange>>")
continue
def periodicCall(self):
"""
Check every 10 sec if there is something new in the queue.
This is actually Telnet connection status check
"""
self.processIncoming()
if not self.running:
# This is the brutal stop of the system. You may want to do
# some cleanup before actually shutting it down.
import sys
sys.exit(1)
self.after(10000, self.periodicCall)
def processIncoming(self):
"""
Handle all the messages currently in the queue (if any).
"""
# global connected
while self.status_queue.qsize():
try:
msg = self.status_queue.get(0)
# Check contents of message and do what it says
# As a test, I simply print it
print(msg)
# if not connected:
# self.connect_to_uut()
except queue.Empty:
pass
def workerThread(self):
"""
This is where we handle the asynchronous I/O.
"""
global telnet_session
global connected
while self.running:
time.sleep(5)
try:
telnet_session.sock.send(telnetlib.IAC + telnetlib.NOP)
connected = True
msg = 'Connected'
except:
connected = False
msg = 'Disconnected' #The Actual Status of the Telnet session
self.event_generate("<<StatusChange>>")
if not connected:
self.connect_to_uut()
self.status_queue.put(msg)
class MainScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.Button_Font_Style1 = font.Font(family='Helvetica', size=30, weight='bold')
self.Status_BasicStyle = font.Font(family='Helvetica', size=10, weight='bold')
self.my_string_var = tk.StringVar()
self.button1 = tk.Button(self, text="PROGRAM 1",
command=lambda: controller.show_frame(FirstScreen),
width=30, font=self.Button_Font_Style1, bd=5)
self.button1.pack(pady=8)
self.button2 = tk.Button(self, text="PROGRAM 2",
command=lambda: controller.show_frame(FirstScreen),
width=30, font=self.Button_Font_Style1, bd=5)
self.button2.pack(pady=8)
self.button3 = tk.Button(self, text="PROGRAM 3",
command=lambda: controller.show_frame(FirstScreen),
width=30, font=self.Button_Font_Style1, bd=5)
self.button3.pack(pady=8)
self.button4 = tk.Button(self, text="PROGRAM 4",
command=lambda: controller.show_frame(SecondScreen),
width=30, font=self.Button_Font_Style1, bd=5)
self.button4.pack(pady=8)
self.button5 = tk.Button(self, text="PROGRAM FAN ",
command=lambda: controller.show_frame(FirstScreen),
width=30, font=self.Button_Font_Style1, bd=5)
self.button5.pack(pady=8)
self.status = tk.Label(self, textvariable=self.my_string_var, bd=2, relief=tk.SUNKEN, anchor=tk.W, font=self.Status_BasicStyle, fg="black")
self.my_string_var.set('Connecting...')
self.status.pack(side="bottom" , fill="x")
class FirstScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.valid_string_color = "springgreen3"
self.invalid_string_color = "red2"
self.main_frame = tk.Frame(self)
self.Button_Font_Style1 = font.Font(family='Helvetica', size=20, weight='bold')
self.Lable_Font_Style1 = font.Font(family='Helvetica', size=20, weight='bold')
self.Status_BasicStyle = font.Font(family='Helvetica', size=10, weight='bold')
self.SN_Label = tk.Label(self.main_frame, text="Serial Number", font=self.Lable_Font_Style1)
self.SN_Label.grid(row=0, column=0, pady=10) # Y axis padding was added only to the label. This padding effects the whole line
self.SN_field = tk.Text(self.main_frame, height=1, width=30, font=self.Lable_Font_Style1)
self.SN_field.grid(row=0, column=1)
self.PN_Label = tk.Label(self.main_frame, text="Part Number", font=self.Lable_Font_Style1)
self.PN_Label.grid(row=1, column=0, pady=10) # Y axis padding was added only to the label. This padding effects the whole line
self.PN_field = tk.Text(self.main_frame, height=1, width=30, font=self.Lable_Font_Style1)
self.PN_field.grid(row=1, column=1)
self.HwVer_Label = tk.Label(self.main_frame, text="HW Version", font=self.Lable_Font_Style1)
self.HwVer_Label.grid(row=2, column=0, pady=10) # Y axis padding was added only to the label. This padding effects the whole line
self.HwVer_field = tk.Text(self.main_frame, height=1, width=30, font=self.Lable_Font_Style1)
self.HwVer_field.grid(row=2, column=1)
self.button2 = tk.Button(self.main_frame, text="Burn",
font=self.Button_Font_Style1, bd=5)
self.button2.grid(row=3, columnspan=2, pady=(20,0))
self.main_frame.pack()
self.my_string_var = tk.StringVar()
self.status = tk.Label(self, textvariable=self.my_string_var, bd=2, relief=tk.SUNKEN, anchor=tk.W, font=self.Status_BasicStyle, fg='black')
self.my_string_var.set('Connecting...')
self.status.pack(side="bottom" , fill="x")
self.status.bind("<<StatusChange>>", self.statuschange) # React to the status change event and change the status label accordingly
self.button1 = tk.Button(self, text="Main Menu",
command=lambda: controller.show_frame(MainScreen),
font=self.Button_Font_Style1, bd=5)
self.button1.pack(side="bottom", pady=(0,20))
def statuschange(self):
global connected
if connected:
self.my_string_var.set('Connected')
self.status.config(font=self.Status_ConnectedStyle, fg='springgreen3')
else:
self.my_string_var.set('Disonnected')
self.status.config(font=self.Status_DisconnectedStyle, fg='red2')
class SecondScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.valid_string_color = "springgreen3"
self.invalid_string_color = "red2"
self.main_frame = tk.Frame(self)
self.Button_Font_Style1 = font.Font(family='Helvetica', size=20, weight='bold')
self.Lable_Font_Style1 = font.Font(family='Helvetica', size=20, weight='bold')
self.Status_BasicStyle = font.Font(family='Helvetica', size=5, weight='bold')
self.SN_Label = tk.Label(self.main_frame, text="Serial Number", font=self.Lable_Font_Style1)
self.SN_Label.grid(row=0, column=0, pady=10) # Y axis padding was added only to the label. This padding effects the whole line
self.SN_field = tk.Text(self.main_frame, height=1, width=30, font=self.Lable_Font_Style1)
self.SN_field.grid(row=0, column=1)
self.PN_Label = tk.Label(self.main_frame, text="Part Number", font=self.Lable_Font_Style1)
self.PN_Label.grid(row=1, column=0, pady=10) # Y axis padding was added only to the label. This padding effects the whole line
self.PN_field = tk.Text(self.main_frame, height=1, width=30, font=self.Lable_Font_Style1)
self.PN_field.grid(row=1, column=1)
self.HwVer_Label = tk.Label(self.main_frame, text="HW Version", font=self.Lable_Font_Style1)
self.HwVer_Label.grid(row=2, column=0, pady=10) # Y axis padding was added only to the label. This padding effects the whole line
self.HwVer_field = tk.Text(self.main_frame, height=1, width=30, font=self.Lable_Font_Style1)
self.HwVer_field.grid(row=2, column=1)
self.button2 = tk.Button(self.main_frame, text="Burn",
font=self.Button_Font_Style1, bd=5)
self.button2.grid(row=3, columnspan=2, pady=(20,0))
self.main_frame.pack()
self.my_string_var = tk.StringVar()
self.status = tk.Label(self, textvariable=self.my_string_var, bd=2, relief=tk.SUNKEN, anchor=tk.W, font=self.Status_BasicStyle, fg="black")
self.my_string_var.set('Connecting...')
self.status.pack(side="bottom" , fill="x")
self.status.bind("<<StatusChange>>", self.statuschange) # React to the status change event and change the status label accordingly
self.button1 = tk.Button(self, text="Main Menu",
command=lambda: controller.show_frame(MainScreen),
font=self.Button_Font_Style1, bd=5)
self.button1.pack(side="bottom", pady=(0,20))
def statuschange(self):
global connected
if connected:
self.my_string_var.set('Connected')
self.status.config(font=self.Status_ConnectedStyle, fg='springgreen3')
else:
self.my_string_var.set('Disonnected')
self.status.config(font=self.Status_DisconnectedStyle, fg='red2')
def main():
app = BurningApp()
app.mainloop()
if __name__ == '__main__':
main()
顺便说一下,我知道我缺少应该更新 MainScreen 中状态栏的方法 Class
正如我承诺的那样,这里是最精简的代码。我留下了一些"fraems",只是为了能够看到每一帧都显示正确的状态,我去掉了不相关的字段
from tkinter import font, ttk
import tkinter as tk
from re import match
import telnetlib
import threading
import queue
import time
LARGE_FONT= ("Verdana", 12)
Current_PN = '123456789' # This global ver is used for the purpose of automatic PN fill
HOST = '10.0.1.235'
PORT = 23
telnet_session = None # After I create the Telnet session I will keep track with this variable
connected = False
class BurningApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.title('Burning App') #handling the application's Window title
w = 1000 # Windows width
h = 600 # Windows height
ws = self.winfo_screenwidth() # Screen resolution width
hs = self.winfo_screenheight() # Screen resolution height
# w = ws * 0.8 # Fit the GUI width to 80% percent of the screen
# h = hs * 0.8 # Fit the GUI height to 80% percent of the screen
x = (ws/2) - (w/2) # X coordinate for the purpose of GUI placement
y = (hs/2) - (h/2) # X coordinate for the purpose of GUI placement
self.resizable(width=False, height=False)
self.geometry('%dx%d+%d+%d'%(w,h,x,y))
self.frames = {}
for F in (MainScreen, FirstScreen, SecondScreen):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(MainScreen)
# Start the Telnet session
self.connect_to_uut()
# Create the queue that will hold the status
self.status_queue = queue.Queue()
# Set up the thread to do asynchronous I/O
self.running = 1 # True
self.thread = threading.Thread(target=self.workerThread)
self.thread.start()
# Start the periodic call in the GUI to check if the queue contains
# anything
self.periodicCall()
def show_frame(self, cont):
'''
This function is being used in order to raise a frame on demand
'''
frame = self.frames[cont]
frame.tkraise()
def connect_to_uut(self, Retry=5):
'''
This functions is used for the purpose of connecting to the UUT
'''
global telnet_session
global connected
for _ in range(Retry):
try:
telnet_session = telnetlib.Telnet(HOST, PORT, timeout=5)
connected = True
self.event_generate("<<StatusChange>>")
break
except:
connected = False
self.event_generate("<<StatusChange>>")
continue
def periodicCall(self):
"""
Check every 10 sec if there is something new in the queue.
This is actually Telnet connection status check
"""
self.processIncoming()
if not self.running:
# This is the brutal stop of the system. You may want to do
# some cleanup before actually shutting it down.
import sys
sys.exit(1)
self.after(10000, self.periodicCall)
def processIncoming(self):
"""
Handle all the messages currently in the queue (if any).
"""
# global connected
while self.status_queue.qsize():
try:
msg = self.status_queue.get(0)
# Check contents of message and do what it says
# As a test, I simply print it
print(msg)
# if not connected:
# self.connect_to_uut()
except queue.Empty:
pass
def workerThread(self):
"""
This is where we handle the asynchronous I/O.
"""
global telnet_session
global connected
while self.running:
time.sleep(5)
try:
telnet_session.sock.send(telnetlib.IAC + telnetlib.NOP)
connected = True
msg = 'Connected'
except:
connected = False
msg = 'Disconnected' #The Actual Status of the Telnet session
self.event_generate("<<StatusChange>>")
if not connected:
self.connect_to_uut()
self.status_queue.put(msg)
class MainScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.Button_Font_Style1 = font.Font(family='Helvetica', size=30, weight='bold')
self.Status_BasicStyle = font.Font(family='Helvetica', size=10, weight='bold')
self.my_string_var = tk.StringVar()
self.button1 = tk.Button(self, text="PROGRAM 1",
command=lambda: controller.show_frame(FirstScreen),
width=30, font=self.Button_Font_Style1, bd=5)
self.button1.pack(pady=8)
self.button2 = tk.Button(self, text="PROGRAM 2",
command=lambda: controller.show_frame(FirstScreen),
width=30, font=self.Button_Font_Style1, bd=5)
self.button2.pack(pady=8)
self.status = tk.Label(self, textvariable=self.my_string_var, bd=2, relief=tk.SUNKEN, anchor=tk.W, font=self.Status_BasicStyle, fg="black")
self.my_string_var.set('Connecting...')
self.status.pack(side="bottom" , fill="x")
class FirstScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.Button_Font_Style1 = font.Font(family='Helvetica', size=20, weight='bold')
self.Status_BasicStyle = font.Font(family='Helvetica', size=10, weight='bold')
self.my_string_var = tk.StringVar()
self.status = tk.Label(self, textvariable=self.my_string_var, bd=2, relief=tk.SUNKEN, anchor=tk.W, font=self.Status_BasicStyle, fg='black')
self.my_string_var.set('Connecting...')
self.status.pack(side="bottom" , fill="x")
self.status.bind("<<StatusChange>>", self.statuschange) # React to the status change event and change the status label accordingly
self.button1 = tk.Button(self, text="Main Menu",
command=lambda: controller.show_frame(MainScreen),
font=self.Button_Font_Style1, bd=5)
self.button1.pack(side="bottom", pady=(0,20))
def statuschange(self):
global connected
if connected:
self.my_string_var.set('Connected')
self.status.config(font=self.Status_ConnectedStyle, fg='springgreen3')
else:
self.my_string_var.set('Disonnected')
self.status.config(font=self.Status_DisconnectedStyle, fg='red2')
class SecondScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.Button_Font_Style1 = font.Font(family='Helvetica', size=20, weight='bold')
self.Status_BasicStyle = font.Font(family='Helvetica', size=5, weight='bold')
self.my_string_var = tk.StringVar()
self.status = tk.Label(self, textvariable=self.my_string_var, bd=2, relief=tk.SUNKEN, anchor=tk.W, font=self.Status_BasicStyle, fg="black")
self.my_string_var.set('Connecting...')
self.status.pack(side="bottom" , fill="x")
self.status.bind("<<StatusChange>>", self.statuschange) # React to the status change event and change the status label accordingly
self.button1 = tk.Button(self, text="Main Menu",
command=lambda: controller.show_frame(MainScreen),
font=self.Button_Font_Style1, bd=5)
self.button1.pack(side="bottom", pady=(0,20))
def statuschange(self):
global connected
if connected:
self.my_string_var.set('Connected')
self.status.config(font=self.Status_ConnectedStyle, fg='springgreen3')
else:
self.my_string_var.set('Disonnected')
self.status.config(font=self.Status_DisconnectedStyle, fg='red2')
def main():
app = BurningApp()
app.mainloop()
if __name__ == '__main__':
main()
这些调整应该可以解决您的问题:
self.status.config(font=self.Status_ConnectedStyle,
text=self.my_string_var,fg='springgreen3')
我终于完成了,我可以更新状态栏,请参阅下面的信息。
我在每个框架中添加了以下方法。此方法的目的是定期检查保存 Telnet 会话状态(连接)的标志。所以我仍然使用相同的线程:主 GUI 线程和另一个维护 Telnet 会话的线程。为了"activate"这个方法,我在某个帧被提升后启动它("frame_was_raised"方法)
def update_statusbar(self):
'''Poll the Telnet Session Status for the purpose of update'''
global connected
if connected:
self.my_string_var.set('Connected')
self.status.config(fg='springgreen3')
else:
self.my_string_var.set('Disconnected')
self.status.config(fg='red2')
self.after(2000, self.update_statusbar)
我的 GUI 应用程序有多个屏幕 (3),每个屏幕都包含用户可以更改的某些文本小部件(我根据 Whosebug 中众所周知的解决方案建立了这个多屏幕 GUI)。填写某个屏幕的字段后,用户可以 "Burn" 这些值到某个硬件。为了能够 "Burn" 到 HW,我在应用 运行.
后立即启动到 HW 的 Telnet 会话(IP 是硬编码的)每个框架都显示状态栏,我想用 Telnet 连接到 HW 的当前状态更新它。为了维护 Telnet 连接,我使用了一个单独的线程。而且我还使用了一个队列来更新当前状态。
当 Telnet 会话状态更改时,我可以通过简单地打印到控制台来更新。如果 Telnet 会话因任何好的(或坏的)原因断开连接,我也能够恢复它。
我的问题是我无法用当前状态更新状态栏(状态标签)。在下面的代码中,您可以看到我已尝试在状态更改时生成一个事件。但是,它没有完成工作。如何使用实际状态更新状态栏?
编辑后
(我费了很大的劲,去掉了200多行代码):
from tkinter import font, ttk
import tkinter as tk
from re import match
import telnetlib
import threading
import queue
import time
LARGE_FONT= ("Verdana", 12)
Current_PN = '123456789' # This global ver is used for the purpose of automatic PN fill
HOST = '10.0.1.235'
PORT = 23
telnet_session = None # After I create the Telnet session I will keep track with this variable
connected = False
class BurningApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.title('Burning App') #handling the application's Window title
w = 1000 # Windows width
h = 600 # Windows height
ws = self.winfo_screenwidth() # Screen resolution width
hs = self.winfo_screenheight() # Screen resolution height
# w = ws * 0.8 # Fit the GUI width to 80% percent of the screen
# h = hs * 0.8 # Fit the GUI height to 80% percent of the screen
x = (ws/2) - (w/2) # X coordinate for the purpose of GUI placement
y = (hs/2) - (h/2) # X coordinate for the purpose of GUI placement
self.resizable(width=False, height=False)
self.geometry('%dx%d+%d+%d'%(w,h,x,y))
self.frames = {}
for F in (MainScreen, FirstScreen, SecondScreen):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(MainScreen)
# Start the Telnet session
self.connect_to_uut()
# Create the queue that will hold the status
self.status_queue = queue.Queue()
# Set up the thread to do asynchronous I/O
self.running = 1 # True
self.thread = threading.Thread(target=self.workerThread)
self.thread.start()
# Start the periodic call in the GUI to check if the queue contains
# anything
self.periodicCall()
def show_frame(self, cont):
'''
This function is being used in order to raise a frame on demand
'''
frame = self.frames[cont]
frame.tkraise()
def connect_to_uut(self, Retry=5):
'''
This functions is used for the purpose of connecting to the UUT
'''
global telnet_session
global connected
for _ in range(Retry):
try:
telnet_session = telnetlib.Telnet(HOST, PORT, timeout=5)
connected = True
self.event_generate("<<StatusChange>>")
break
except:
connected = False
self.event_generate("<<StatusChange>>")
continue
def periodicCall(self):
"""
Check every 10 sec if there is something new in the queue.
This is actually Telnet connection status check
"""
self.processIncoming()
if not self.running:
# This is the brutal stop of the system. You may want to do
# some cleanup before actually shutting it down.
import sys
sys.exit(1)
self.after(10000, self.periodicCall)
def processIncoming(self):
"""
Handle all the messages currently in the queue (if any).
"""
# global connected
while self.status_queue.qsize():
try:
msg = self.status_queue.get(0)
# Check contents of message and do what it says
# As a test, I simply print it
print(msg)
# if not connected:
# self.connect_to_uut()
except queue.Empty:
pass
def workerThread(self):
"""
This is where we handle the asynchronous I/O.
"""
global telnet_session
global connected
while self.running:
time.sleep(5)
try:
telnet_session.sock.send(telnetlib.IAC + telnetlib.NOP)
connected = True
msg = 'Connected'
except:
connected = False
msg = 'Disconnected' #The Actual Status of the Telnet session
self.event_generate("<<StatusChange>>")
if not connected:
self.connect_to_uut()
self.status_queue.put(msg)
class MainScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.Button_Font_Style1 = font.Font(family='Helvetica', size=30, weight='bold')
self.Status_BasicStyle = font.Font(family='Helvetica', size=10, weight='bold')
self.my_string_var = tk.StringVar()
self.button1 = tk.Button(self, text="PROGRAM 1",
command=lambda: controller.show_frame(FirstScreen),
width=30, font=self.Button_Font_Style1, bd=5)
self.button1.pack(pady=8)
self.button2 = tk.Button(self, text="PROGRAM 2",
command=lambda: controller.show_frame(FirstScreen),
width=30, font=self.Button_Font_Style1, bd=5)
self.button2.pack(pady=8)
self.button3 = tk.Button(self, text="PROGRAM 3",
command=lambda: controller.show_frame(FirstScreen),
width=30, font=self.Button_Font_Style1, bd=5)
self.button3.pack(pady=8)
self.button4 = tk.Button(self, text="PROGRAM 4",
command=lambda: controller.show_frame(SecondScreen),
width=30, font=self.Button_Font_Style1, bd=5)
self.button4.pack(pady=8)
self.button5 = tk.Button(self, text="PROGRAM FAN ",
command=lambda: controller.show_frame(FirstScreen),
width=30, font=self.Button_Font_Style1, bd=5)
self.button5.pack(pady=8)
self.status = tk.Label(self, textvariable=self.my_string_var, bd=2, relief=tk.SUNKEN, anchor=tk.W, font=self.Status_BasicStyle, fg="black")
self.my_string_var.set('Connecting...')
self.status.pack(side="bottom" , fill="x")
class FirstScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.valid_string_color = "springgreen3"
self.invalid_string_color = "red2"
self.main_frame = tk.Frame(self)
self.Button_Font_Style1 = font.Font(family='Helvetica', size=20, weight='bold')
self.Lable_Font_Style1 = font.Font(family='Helvetica', size=20, weight='bold')
self.Status_BasicStyle = font.Font(family='Helvetica', size=10, weight='bold')
self.SN_Label = tk.Label(self.main_frame, text="Serial Number", font=self.Lable_Font_Style1)
self.SN_Label.grid(row=0, column=0, pady=10) # Y axis padding was added only to the label. This padding effects the whole line
self.SN_field = tk.Text(self.main_frame, height=1, width=30, font=self.Lable_Font_Style1)
self.SN_field.grid(row=0, column=1)
self.PN_Label = tk.Label(self.main_frame, text="Part Number", font=self.Lable_Font_Style1)
self.PN_Label.grid(row=1, column=0, pady=10) # Y axis padding was added only to the label. This padding effects the whole line
self.PN_field = tk.Text(self.main_frame, height=1, width=30, font=self.Lable_Font_Style1)
self.PN_field.grid(row=1, column=1)
self.HwVer_Label = tk.Label(self.main_frame, text="HW Version", font=self.Lable_Font_Style1)
self.HwVer_Label.grid(row=2, column=0, pady=10) # Y axis padding was added only to the label. This padding effects the whole line
self.HwVer_field = tk.Text(self.main_frame, height=1, width=30, font=self.Lable_Font_Style1)
self.HwVer_field.grid(row=2, column=1)
self.button2 = tk.Button(self.main_frame, text="Burn",
font=self.Button_Font_Style1, bd=5)
self.button2.grid(row=3, columnspan=2, pady=(20,0))
self.main_frame.pack()
self.my_string_var = tk.StringVar()
self.status = tk.Label(self, textvariable=self.my_string_var, bd=2, relief=tk.SUNKEN, anchor=tk.W, font=self.Status_BasicStyle, fg='black')
self.my_string_var.set('Connecting...')
self.status.pack(side="bottom" , fill="x")
self.status.bind("<<StatusChange>>", self.statuschange) # React to the status change event and change the status label accordingly
self.button1 = tk.Button(self, text="Main Menu",
command=lambda: controller.show_frame(MainScreen),
font=self.Button_Font_Style1, bd=5)
self.button1.pack(side="bottom", pady=(0,20))
def statuschange(self):
global connected
if connected:
self.my_string_var.set('Connected')
self.status.config(font=self.Status_ConnectedStyle, fg='springgreen3')
else:
self.my_string_var.set('Disonnected')
self.status.config(font=self.Status_DisconnectedStyle, fg='red2')
class SecondScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.valid_string_color = "springgreen3"
self.invalid_string_color = "red2"
self.main_frame = tk.Frame(self)
self.Button_Font_Style1 = font.Font(family='Helvetica', size=20, weight='bold')
self.Lable_Font_Style1 = font.Font(family='Helvetica', size=20, weight='bold')
self.Status_BasicStyle = font.Font(family='Helvetica', size=5, weight='bold')
self.SN_Label = tk.Label(self.main_frame, text="Serial Number", font=self.Lable_Font_Style1)
self.SN_Label.grid(row=0, column=0, pady=10) # Y axis padding was added only to the label. This padding effects the whole line
self.SN_field = tk.Text(self.main_frame, height=1, width=30, font=self.Lable_Font_Style1)
self.SN_field.grid(row=0, column=1)
self.PN_Label = tk.Label(self.main_frame, text="Part Number", font=self.Lable_Font_Style1)
self.PN_Label.grid(row=1, column=0, pady=10) # Y axis padding was added only to the label. This padding effects the whole line
self.PN_field = tk.Text(self.main_frame, height=1, width=30, font=self.Lable_Font_Style1)
self.PN_field.grid(row=1, column=1)
self.HwVer_Label = tk.Label(self.main_frame, text="HW Version", font=self.Lable_Font_Style1)
self.HwVer_Label.grid(row=2, column=0, pady=10) # Y axis padding was added only to the label. This padding effects the whole line
self.HwVer_field = tk.Text(self.main_frame, height=1, width=30, font=self.Lable_Font_Style1)
self.HwVer_field.grid(row=2, column=1)
self.button2 = tk.Button(self.main_frame, text="Burn",
font=self.Button_Font_Style1, bd=5)
self.button2.grid(row=3, columnspan=2, pady=(20,0))
self.main_frame.pack()
self.my_string_var = tk.StringVar()
self.status = tk.Label(self, textvariable=self.my_string_var, bd=2, relief=tk.SUNKEN, anchor=tk.W, font=self.Status_BasicStyle, fg="black")
self.my_string_var.set('Connecting...')
self.status.pack(side="bottom" , fill="x")
self.status.bind("<<StatusChange>>", self.statuschange) # React to the status change event and change the status label accordingly
self.button1 = tk.Button(self, text="Main Menu",
command=lambda: controller.show_frame(MainScreen),
font=self.Button_Font_Style1, bd=5)
self.button1.pack(side="bottom", pady=(0,20))
def statuschange(self):
global connected
if connected:
self.my_string_var.set('Connected')
self.status.config(font=self.Status_ConnectedStyle, fg='springgreen3')
else:
self.my_string_var.set('Disonnected')
self.status.config(font=self.Status_DisconnectedStyle, fg='red2')
def main():
app = BurningApp()
app.mainloop()
if __name__ == '__main__':
main()
顺便说一下,我知道我缺少应该更新 MainScreen 中状态栏的方法 Class
正如我承诺的那样,这里是最精简的代码。我留下了一些"fraems",只是为了能够看到每一帧都显示正确的状态,我去掉了不相关的字段
from tkinter import font, ttk
import tkinter as tk
from re import match
import telnetlib
import threading
import queue
import time
LARGE_FONT= ("Verdana", 12)
Current_PN = '123456789' # This global ver is used for the purpose of automatic PN fill
HOST = '10.0.1.235'
PORT = 23
telnet_session = None # After I create the Telnet session I will keep track with this variable
connected = False
class BurningApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.title('Burning App') #handling the application's Window title
w = 1000 # Windows width
h = 600 # Windows height
ws = self.winfo_screenwidth() # Screen resolution width
hs = self.winfo_screenheight() # Screen resolution height
# w = ws * 0.8 # Fit the GUI width to 80% percent of the screen
# h = hs * 0.8 # Fit the GUI height to 80% percent of the screen
x = (ws/2) - (w/2) # X coordinate for the purpose of GUI placement
y = (hs/2) - (h/2) # X coordinate for the purpose of GUI placement
self.resizable(width=False, height=False)
self.geometry('%dx%d+%d+%d'%(w,h,x,y))
self.frames = {}
for F in (MainScreen, FirstScreen, SecondScreen):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(MainScreen)
# Start the Telnet session
self.connect_to_uut()
# Create the queue that will hold the status
self.status_queue = queue.Queue()
# Set up the thread to do asynchronous I/O
self.running = 1 # True
self.thread = threading.Thread(target=self.workerThread)
self.thread.start()
# Start the periodic call in the GUI to check if the queue contains
# anything
self.periodicCall()
def show_frame(self, cont):
'''
This function is being used in order to raise a frame on demand
'''
frame = self.frames[cont]
frame.tkraise()
def connect_to_uut(self, Retry=5):
'''
This functions is used for the purpose of connecting to the UUT
'''
global telnet_session
global connected
for _ in range(Retry):
try:
telnet_session = telnetlib.Telnet(HOST, PORT, timeout=5)
connected = True
self.event_generate("<<StatusChange>>")
break
except:
connected = False
self.event_generate("<<StatusChange>>")
continue
def periodicCall(self):
"""
Check every 10 sec if there is something new in the queue.
This is actually Telnet connection status check
"""
self.processIncoming()
if not self.running:
# This is the brutal stop of the system. You may want to do
# some cleanup before actually shutting it down.
import sys
sys.exit(1)
self.after(10000, self.periodicCall)
def processIncoming(self):
"""
Handle all the messages currently in the queue (if any).
"""
# global connected
while self.status_queue.qsize():
try:
msg = self.status_queue.get(0)
# Check contents of message and do what it says
# As a test, I simply print it
print(msg)
# if not connected:
# self.connect_to_uut()
except queue.Empty:
pass
def workerThread(self):
"""
This is where we handle the asynchronous I/O.
"""
global telnet_session
global connected
while self.running:
time.sleep(5)
try:
telnet_session.sock.send(telnetlib.IAC + telnetlib.NOP)
connected = True
msg = 'Connected'
except:
connected = False
msg = 'Disconnected' #The Actual Status of the Telnet session
self.event_generate("<<StatusChange>>")
if not connected:
self.connect_to_uut()
self.status_queue.put(msg)
class MainScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.Button_Font_Style1 = font.Font(family='Helvetica', size=30, weight='bold')
self.Status_BasicStyle = font.Font(family='Helvetica', size=10, weight='bold')
self.my_string_var = tk.StringVar()
self.button1 = tk.Button(self, text="PROGRAM 1",
command=lambda: controller.show_frame(FirstScreen),
width=30, font=self.Button_Font_Style1, bd=5)
self.button1.pack(pady=8)
self.button2 = tk.Button(self, text="PROGRAM 2",
command=lambda: controller.show_frame(FirstScreen),
width=30, font=self.Button_Font_Style1, bd=5)
self.button2.pack(pady=8)
self.status = tk.Label(self, textvariable=self.my_string_var, bd=2, relief=tk.SUNKEN, anchor=tk.W, font=self.Status_BasicStyle, fg="black")
self.my_string_var.set('Connecting...')
self.status.pack(side="bottom" , fill="x")
class FirstScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.Button_Font_Style1 = font.Font(family='Helvetica', size=20, weight='bold')
self.Status_BasicStyle = font.Font(family='Helvetica', size=10, weight='bold')
self.my_string_var = tk.StringVar()
self.status = tk.Label(self, textvariable=self.my_string_var, bd=2, relief=tk.SUNKEN, anchor=tk.W, font=self.Status_BasicStyle, fg='black')
self.my_string_var.set('Connecting...')
self.status.pack(side="bottom" , fill="x")
self.status.bind("<<StatusChange>>", self.statuschange) # React to the status change event and change the status label accordingly
self.button1 = tk.Button(self, text="Main Menu",
command=lambda: controller.show_frame(MainScreen),
font=self.Button_Font_Style1, bd=5)
self.button1.pack(side="bottom", pady=(0,20))
def statuschange(self):
global connected
if connected:
self.my_string_var.set('Connected')
self.status.config(font=self.Status_ConnectedStyle, fg='springgreen3')
else:
self.my_string_var.set('Disonnected')
self.status.config(font=self.Status_DisconnectedStyle, fg='red2')
class SecondScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
self.Button_Font_Style1 = font.Font(family='Helvetica', size=20, weight='bold')
self.Status_BasicStyle = font.Font(family='Helvetica', size=5, weight='bold')
self.my_string_var = tk.StringVar()
self.status = tk.Label(self, textvariable=self.my_string_var, bd=2, relief=tk.SUNKEN, anchor=tk.W, font=self.Status_BasicStyle, fg="black")
self.my_string_var.set('Connecting...')
self.status.pack(side="bottom" , fill="x")
self.status.bind("<<StatusChange>>", self.statuschange) # React to the status change event and change the status label accordingly
self.button1 = tk.Button(self, text="Main Menu",
command=lambda: controller.show_frame(MainScreen),
font=self.Button_Font_Style1, bd=5)
self.button1.pack(side="bottom", pady=(0,20))
def statuschange(self):
global connected
if connected:
self.my_string_var.set('Connected')
self.status.config(font=self.Status_ConnectedStyle, fg='springgreen3')
else:
self.my_string_var.set('Disonnected')
self.status.config(font=self.Status_DisconnectedStyle, fg='red2')
def main():
app = BurningApp()
app.mainloop()
if __name__ == '__main__':
main()
这些调整应该可以解决您的问题:
self.status.config(font=self.Status_ConnectedStyle,
text=self.my_string_var,fg='springgreen3')
我终于完成了,我可以更新状态栏,请参阅下面的信息。
我在每个框架中添加了以下方法。此方法的目的是定期检查保存 Telnet 会话状态(连接)的标志。所以我仍然使用相同的线程:主 GUI 线程和另一个维护 Telnet 会话的线程。为了"activate"这个方法,我在某个帧被提升后启动它("frame_was_raised"方法)
def update_statusbar(self):
'''Poll the Telnet Session Status for the purpose of update'''
global connected
if connected:
self.my_string_var.set('Connected')
self.status.config(fg='springgreen3')
else:
self.my_string_var.set('Disconnected')
self.status.config(fg='red2')
self.after(2000, self.update_statusbar)