我有一个GUI,它会做一些事情,然后重新启动面板,然后等待面板完成重新启动。这个想法是用户按下";PROGRAM HMI";它将执行操作((。我的大问题是在重新启动过程中,GUI需要等待10秒才能重新启动HMI。在这10秒内,GUI冻结,我得到一个";窗口没有响应";顶部的消息。正如我在终端中看到的那样,代码正在执行,它的GUI最终解冻了。我读到我不应该在这里浪费时间。sleep((。但是我该怎么写才不会冻结呢?我使用的是python 3.8。
import requests
import json
import time
import warnings
import subprocess
import xml.etree.ElementTree as ET
from ftplib import FTP
import hashlib
from datetime import datetime
import sys
import tkinter as tk
import time
import threading
from tkinter import *
class main:
def __init__(self):
self.window = tk.Tk()
self.window.title("1750+ HMI Setup App")
self.window.geometry("550x675")
self.window.resizable(width=False, height=False)
self.window.configure(bg='#34aeeb')
self.action_updateIP = False
self.ipString = "192.168.0.20" # Should be found by DHCP server.
self.newIPString = "192.168.0.20" # Default.
self.url = "https://" + self.ipString + "/" # TODO: Get IP address based on DHCP of panel, or fixed 192.168.0.1 if it exists?
self.urlConfig = self.url + "machine_config/"
self.urlApi = self.url + "rest/api/v1/"
self.authData = ('admin', 'admin')
#OUTPUT TEXT BOX
self.outText = Text(self.window, width=50, height=20, wrap=WORD)
self.outText.grid(row=10, columnspan=3, padx=10)
self.make_widgets()
self.window.mainloop()
def make_widgets(self):
Button(self.window, text='Select All', font=("Arial", 12), width=10, command=self.select_all).grid(row=7, sticky=W, padx=10, pady=20)
ButtonShow = tk.Button(text="Program HMI", width=20, font=("Arial", 14), command=self.actions)
ButtonShow.grid(column=0, row=8, sticky=W, padx=10, pady=10)
def verboseSleep(self,seconds):
chars = len(str(seconds))
s = "{:" + str(chars) + "d}s"
cntdn = seconds + 1
for i in range(seconds, 0, -1):
sys.stdout.write(str("b" * (chars+1)) + s.format(i))
cntdn = cntdn - 1
msg_seconds = str(cntdn)
self.outText.insert(tk.END, str(cntdn))
pos = self.outText.index('end')
float_pos = float(pos) - 1.0
self.window.update_idletasks()
sys.stdout.flush()
self.window.after(1000)
self.outText.delete(str(float_pos), "end")
self.outText.insert(tk.END, "n")
sys.stdout.write(str("b" * (chars + 1))) # Remove all evidence of our countdown timer.
sys.stdout.flush()
def select_all(self):
self.action_updateIP = True
def actions(self):
##########################################################
ipString = "192.168.0.20" # Should be found by DHCP server.
newIPString = "192.168.0.20" # Default.
self.outText.delete(1.0, END)
#print (self.action_updateIP)
if self.action_updateIP == True:
print("Updating IP address...")
self.outText.insert(tk.END, "Updating IP address...")
self.window.update_idletasks()
postData = {"bridge":{"enabled":False,"interfaces":["eth0"],"list":[]},"wifi":{"interfaces":[]},"version":0,"dns":{"servers":[],"search":[]},"hostname":"HMI-2133","interfaces":[{"name":"eth0","label":"WAN","mac_address":"00:30:d8:06:21:33","dhcp":False,"configured":True,"readonly":False,"virtual":False,"hidden":False,"actual_netmask":"255.255.255.0","actual_ip_address":ipString,"ip_address":newIPString,"netmask":"255.255.255.0","gateway_ip":"192.168.0.100"},{"name":"lo","mac_address":"00:00:00:00:00:00","dhcp":False,"configured":True,"readonly":True,"virtual":True,"hidden":True,"actual_netmask":"255.0.0.0","actual_ip_address":"127.0.0.1"}]}
try:
# We expect this to fail due to the connection being abruptly ended by the panel...
r = requests.post(url = urlApi + 'network', data = json.dumps(postData), timeout = 10, headers={"content-type": "application/json"}, auth=authData, verify=False)
except:
print("Waiting 10s for panel to update.")
self.outText.insert(tk.END, "Waiting 10s for panel to update" + "n")
self.window.update_idletasks()
self.verboseSleep(10)
print("IP Updated. New IP: ", newIPString)
msg_newIP = "IP Updated. New IP: " + newIPString + "n"
self.outText.insert(tk.END, msg_newIP)
self.window.update_idletasks()
self.ipString = self.newIPString
else:
print (self.action_updateIP)
print ('Done!' + "n")
self.outText.insert(tk.END, "Done! DONE! I'm all DONE!!" +"n")
main()
exit(0)
查看tkinter中的after
方法。你基本上可以设置一个任务执行延迟,而不需要冻结UI
所以类似的东西
window = tk.Tk()
...
def do_actions():
some_function_calls
...
# delay is given in milliseconds, so you need to multiply with 1000 to get in seconds
window.after(10000, do_actions)
如果您希望将其创建为循环,可以执行以下操作:
def do_actions():
some_function_calls
return window.after(10000, do_actions)
after_loop = do_actions()
然后,如果你想取消后循环,你可以调用window.after_cancel(after_loop)
使用线程模块在python中使用多线程可以避免"不响应"问题。
如果你已经定义了任何函数,比如combine()
,作为命令添加到按钮中:
btn = Button(root, text="Click Me",command=combine)
由于窗口冻结,请编辑上述类型代码,如下所示:
import threading btn = Button(root,text="Click Me", command=threading.Thread(target=combine).start())
查找参考:stackoverflow-使用单行多线程代码避免tkinter窗口冻结