我的GUI拥有8个按钮。每个Button单击调用事件处理程序,然后转发到另一个函数。所有这些操作大约需要4秒钟。我的问题是,这会导致Button在处理事件时保持在SUNKEN状态,并且还会导致其他按钮无响应。
我想要的是在点击后立即从SUNKEN状态释放Button,并在后台继续事件处理。
如何解决这个问题?有没有办法在事件处理程序完成其工作之前释放按钮?
编辑后:
这是我的代码:
from tkinter import Tk, Menu, Button
import telnetlib
from time import sleep
On_Color = '#53d411'
Off_Color = '#e78191'
def Power_On_Off (PowerSwitchPort, Action):
'''
Control the Power On Off Switch
'''
on_off = telnetlib.Telnet("10.0.5.9", 2016)
if Action == 'On' or Action =='ON' or Action == 'on':
StringDict = {'1': b"S00D1DDDDDDDE", '2': b"S00DD1DDDDDDE", '3': b"S00DDD1DDDDDE", '4': b"S00DDDD1DDDDE",
'5': b"S00DDDDD1DDDE", '6': b"S00DDDDDD1DDE", '7': b"S00DDDDDDD1DE", '8': b"S00DDDDDDDD1E"}
elif Action == 'Off' or Action =='OFF' or Action == 'off':
StringDict = {'1': b"S00D0DDDDDDDE", '2': b"S00DD0DDDDDDE", '3': b"S00DDD0DDDDDE", '4': b"S00DDDD0DDDDE",
'5': b"S00DDDDD0DDDE", '6': b"S00DDDDDD0DDE", '7': b"S00DDDDDDD0DE", '8': b"S00DDDDDDDD0E"}
PowerSwitchPort = str(PowerSwitchPort)
on_off.read_eager()
on_off.write(b"S00QLE\n")
sleep(4)
on_off.write(StringDict[PowerSwitchPort])
on_off.close()
def OnButtonClick(button_id):
if button_id == 1:
# What to do if power_socket1 was clicked
Power_On_Off('1', 'Off')
elif button_id == 2:
# What to do if power_socket2 was clicked
Power_On_Off('1', 'On')
def main ():
root = Tk()
root.title("Power Supply Control") #handling the application's Window title
root.iconbitmap(r'c:\Users\alpha_2.PL\Desktop\Power.ico') # Handling the application icon
power_socket1 = Button(root, text = 'Socket 1 Off', command=lambda: OnButtonClick(1), bg = On_Color)
power_socket1.pack()
power_socket2 = Button(root, text = 'Socket 1 On', command=lambda: OnButtonClick(2), bg = On_Color)
power_socket2.pack()
'''
Menu Bar
'''
menubar = Menu(root)
file = Menu(menubar, tearoff = 0) # tearoff = 0 is required in order to cancel the dashed line in the menu
file.add_command(label='Settings')
menubar.add_cascade(label='Options', menu = file)
root.config(menu=menubar)
root.mainloop()
if __name__ == '__main__':
main()
可以看出,我创建了2个按钮1打开一个开关,另一个将其关闭。开/关动作延迟约4秒。例如,这只是我的应用程序的一小部分。在我的原始代码中,我使用一个类来创建GUI并控制它
答案 0 :(得分:1)
你的问题来自这些代码:
on_off.write(b"S00QLE\n")
sleep(4)
尤其来自sleep
。这是一个非常弱的模式,因为你期望在4秒内完成S00QLE
。不多也不少!
虽然telnet
实际上适用于这四秒钟,但GUI sleep
。
因此,您GUI is in unresponsive state并且无法重绘按钮的浮雕。
sleep
的最佳选择是after
- 因此您可以安排执行:
# crude and naive fix via closure-function and root.after
def Power_On_Off (PowerSwitchPort, Action):
...
def write_switch_port_and_close():
on_off.write(StringDict[PowerSwitchPort])
on_off.close()
on_off.write(b"S00QLE\n")
# sleep(4)
root.after(4000, write_switch_port_and_close)
...
要解决此问题,您可以使用通用after自动调度循环。
在我的示例中,我连接到公共telnet服务器which broadcasts Star Wars Episode IV(不是添加!),以模拟长时间运行的进程。
当然,你在write
中的执行(telnet
)两个命令,为了表示这种行为,我们会在着名的"远离"找到行(第一次长期操作(write
))。之后,我们更新标签计数器,然后再次连接到广播(第二次长期运行(write
))。
试试这段代码:
import tkinter as tk
import telnetlib
class App(tk.Tk):
def __init__(self):
super().__init__()
# store telnet client without opening any host
self.tn_client = telnetlib.Telnet()
# label to count "far aways"
self.far_aways_encountered = tk.Label(text='Far aways counter: 0')
self.far_aways_encountered.pack()
# start button - just an open telnet command ("o towel.blinkenlights.nl 23")
self.button_start = tk.Button(self, text='Start Telnet Star Wars', command=self.start_wars)
self.button_start.pack()
# start button - just an close telnet command ("c")
self.button_stop = tk.Button(self, text='Stop Telnet Star Wars', command=self.close_wars, state='disabled')
self.button_stop.pack()
# simple counter
self.count = 0
def start_wars(self):
# "o towel.blinkenlights.nl 23"
self.tn_client.open('towel.blinkenlights.nl', 23)
# enabling/disabling buttons to prevent mass start/stop
self.button_start.config(state='disabled')
self.button_stop.config(state='normal')
# scheduling
self.after(100, self.check_wars_continiously)
def close_wars(self):
# "c"
self.tn_client.close()
# enabling/disabling buttons to prevent mass start/stop
self.button_start.config(state='normal')
self.button_stop.config(state='disabled')
def check_wars_continiously(self):
try:
# we're expect an end of a long-run proccess with a "A long time ago in a galaxy far far away...." line
response = self.tn_client.expect([b'(?s)A long time ago in a galaxy far,.*?far away'], .01)
except EOFError:
# end of file is found and no text was read
self.close_wars()
except ValueError:
# telnet session was closed by the user
pass
else:
if response[1]:
# The "A long time ago in a galaxy far far away...." line is reached!
self.count += 1
self.far_aways_encountered.config(text='Far aways counter: %d' % self.count)
# restart via close/open commands (overhead)
self.close_wars()
self.start_wars()
else:
if response[2] != b'':
# just debug-print line
print(response[2])
# self-schedule again
self.after(100, self.check_wars_continiously)
app = App()
app.mainloop()
所以答案是:对特定sleep
命令的最简单替代方案是两个函数的组合:after
和expect
(如果它是expect
,则只有Application
。控制台应用程序)!
答案 1 :(得分:0)
如果您必须在Gui中处理计算繁重的过程,同时允许其余的运行,您可以进行多处理:
import multiprocess as mp
def OnButtonClick(bid):
mp.Process(target=Power_On_Off,args=('1',('Off','On')[bid-1])).start()
评论回答
mp.Process
定义了一个Process对象。一旦我.start()
,该过程将使用参数target
调用args
中的函数。例如,您可以看到使用here。id
on
选项传递每个off
。我实现了你所写的内容。你的问题并非如此"按钮不会快速上升"。相反,Gui在Power_On_Off
完成之前就会陷入困境。如果我在不同的过程中运行该功能,那么我的Gui不会被卡住任何明显的时间。
如果您希望主进程中的某些内容在另一个进程结束时发生,您可能需要将Process
保留在某种列表中,以便将来进行ping操作。否则,这很好。