利用多執行緒解決Tkinter,在button事件中執行高io,高耗時操作,視窗無響應問題
阿新 • • 發佈:2019-02-07
昨天利用python自己寫了一個微型伺服器,突發奇想用Tkinter寫一個GUI介面,並將監聽開始的函式繫結到其中的一個button上,但是當我點選開始服務button時,視窗立馬陷入無響應狀態。搜尋一番發現,是伺服器函式中的while迴圈阻塞了GUI的響應,同理而言,做高I/O操作也一樣會面臨這種情況,同時用一個執行緒處理GUI響應與功能函式極易導致資源緊缺,進而導致視窗無響應。
解決方法:
(1)多執行緒方法
我們利用多執行緒來分別執行GUI和功能函式是一個非常好的方法,好在python中多執行緒是極其容易使用的。多執行緒方法如下:
import threading t= threading.Thread(target = func) t.start()
先引入threading包,利用threading.Thread建立執行緒,func是你需要執行的函式。
(2)分類GUI和功能類
你需要將GUI與功能類分離,具體示例程式碼如下:
from socket import * from tkinter import * import time import threading import re #GUI視窗類 class Control(): #定義GUI介面 def __init__(self, master, fuc): self.parent = master self.parent.title("伺服器配置器") self.frame = Frame(self.parent) self.frame.pack(fill=BOTH, expand=3) self.parent.resizable(width=False, height=False) self.label = Label(self.frame, text="埠設定") self.label.grid(row=0, column=0, sticky=E) self.entry = Entry(self.frame) self.entry.grid(row=0, column=1, sticky=W + E) self.label2 = Label(self.frame, text="訪問日誌") self.label2.grid(row=1, column=0, rowspan=6, sticky=W + N) self.list = Listbox(self.frame, width = 100,height = 40) self.list.grid(row=3, column=1, rowspan=6, sticky=E + S) self.stBtn = Button(self.frame, text="開始服務", command=fuc) self.stBtn.grid(row=10, column=1, sticky=W + E) #具體功能類 class ThreadClient(): def __init__(self, master): self.master = master self.gui = Control(master, self.starting) #將我們定義的GUI類賦給服務類的屬性,將執行的功能函式作為引數傳入 #獲取請求日誌 def get_log(self, addr, message): timenow = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())) way = message.decode('utf-8').split()[0] resorce = message.decode('utf-8').split()[1][1:] info = timenow+": " + "client:" + str(addr) + " way: "+ way + " resorce: " + resorce return info #監聽方法 def start_listen(self): host = '' #對應本機所有ip地址 port = int(self.gui.entry.get()) #TCP socket埠 if not port: port = 8086 address = (host, port) serverSocket = socket(AF_INET, SOCK_STREAM) #建立TCP socket serverSocket.bind(address) #繫結地址 serverSocket.listen(1) #開始監聽 keepalive = False while True: try: # if not keepalive: connectionSocket, clientAddr = serverSocket.accept() #獲取「連線套接字」 print("create------") print("recv the http request") message = connectionSocket.recv(1024) #獲得http報文 print("request", message.decode('utf-8')) info = self.get_log(clientAddr, message) self.gui.list.insert(END, info) filename = message.split()[1] #獲得URI,去掉首部'/'就是檔名 keepalive = len(re.findall(r'keep-alive', message.decode('utf-8'))) f = open(filename[1:], 'rb') outputdata = f.readlines() #逐行讀出檔案內容並存到list中 connectionSocket.send('HTTP/1.1 200 OK\r\n\r\n'.encode('utf-8')) #發response行 for i in range(0, len(outputdata)): connectionSocket.send(outputdata[i]) #把檔案各行資料塞到response中 send只能是string型別 # if not keepalive: connectionSocket.close() #關閉資料連線 except IOError: connectionSocket.send("404 not found".encode('utf-8')) #檔案不存在時異常處理 connectionSocket.close() serverSocket.close() #為方法開一個單獨的執行緒 def starting(self): self.thread = threading.Thread(target = self.start_listen) self.thread.start() if __name__ == '__main__': root = Tk() tool = ThreadClient(root) root.mainloop()