TKinter在多線程時刷新GUI的一些碎碎念

  首先要講的是一個TKinter使用時常常遇到的問題,因爲TKinter自身刷新GUI是單線程的,用戶在調用mainloop方法後,主線程會一直不停循環刷新GUI,但是如果用戶給某個widget綁定了一個很耗時的方法A時,這個方法A也是在主線程裏調用,於是這個耗時的方法A會阻塞住刷新GUI的主線程,表現就是整個GUI卡住了,只有等這個耗時的方法結束後,GUI纔會對用戶操作響應。
代碼如下:

from Tkinter import
from ttk import

import time

class GUI():

def __init__(self, root):

    self.initGUI(root)

def initGUI(self, root):

    root.title("test")
    root.geometry("400x200+700+500")
    root.resizable = False

    self.button_1 = Button(root, text="run A", width=10, command=self.A)
    self.button_1.pack(side="top")

    self.button_2 = Button(root, text="run B", width=10, command=self.B)
    self.button_2.pack(side="top")

    root.mainloop()

def A(self):
    print "start to run proc A"
    time.sleep(3)
    print "proc A finished"

def B(self):
    print "start to run proc B"
    time.sleep(3)
    print "proc B finished"

if name == "main":

root = Tk()
myGUI = GUI(root)
function(){   //外匯返傭:http://www.kaifx.cn

      很簡單一段代碼,GUI裏只有兩個button,分別綁定了兩個耗時3秒的方法A和方法B,首先我點擊了上面一個button調用方法A,在A運行期間,我又調用了方法B,可以看到在方法A運行期間,方法B並沒有運行,而是等A運行完之後,才運行了方法B,而且在方法A運行過程中,整個GUI沒有響應我對下面一個button的點擊,而且GUI界面無法拖拽,只有兩個方法結束後才拖拽起來。
最簡單的解決上述問題的辦法就是利用多線程,把兩個耗時方法丟到兩個子線程裏運行,就可以避開主線程被阻塞的問題,所以對上面代碼稍稍修改一下,如下所示:

from Tkinter import
from ttk import

import threading
import time

class GUI():

def __init__(self, root):

    self.initGUI(root)

def initGUI(self, root):

    root.title("test")
    root.geometry("400x200+700+500")
    root.resizable = False

    self.button_1 = Button(root, text="run A", width=10, command=self.A)
    self.button_1.pack(side="top")

    self.button_2 = Button(root, text="run B", width=10, command=self.B)
    self.button_2.pack(side="top")

    root.mainloop()

def __A(self):
    print "start to run proc A"
    time.sleep(3)
    print "proc A finished"

def A(self):
    T = threading.Thread(target=self.__A)
    T.start()

def __B(self):
    print "start to run proc B"
    time.sleep(3)
    print "proc B finished"

def B(self):

    T = threading.Thread(target=self.__B)
    T.start()

if name == "main":

root = Tk()
myGUI = GUI(root)

然而,事情並不會就這麼簡單結束了,實際應用中,我們的GUI不可能只有兩個簡單的Button,有時候,我們也有需要即時刷新GUI自身的情況,比如我們假設有這麼一個簡單的GUI程序,界面只有一個Button和一個Text,點擊Button後,每隔一秒將當前時間打印到Text上,也就說,在這個程序裏,我們需要動態修改widget。
本來,一般理想情況下,用戶點擊了button之後,應該會立即能在Text裏顯示出當前時間,然而在這個例子裏,我們可以看到,用戶點擊Button之後,隔了三秒,纔將所有的輸出一次顯示出來,原因還是之前提到的, TKinter本身是單線程的,顯示時間這個方法耗時3秒,會卡住主線程,主線程在這三秒內去執行顯示時間的方法去了,GUI不會立即刷新,代碼如下:

from Tkinter import
from ttk import

import threading
import time
import sys

def fmtTime(timeStamp):
timeArray = time.localtime(timeStamp)
dateTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)
return dateTime

class re_Text():

def __init__(self, widget):

    self.widget = widget

def write(self, content):

    self.widget.insert(INSERT, content)
    self.widget.see(END)

class GUI():

def __init__(self, root):

    self.initGUI(root)

def initGUI(self, root):

    root.title("test")
    root.geometry("400x200+700+500")
    root.resizable = False

    self.button = Button(root, text="click", width=10, command=self.show)
    self.button.pack(side="top")

    self.scrollBar = Scrollbar(root)
    self.scrollBar.pack(side="right", fill="y")

    self.text = Text(root, height=10, width=45, yscrollcommand=self.scrollBar.set)
    self.text.pack(side="top", fill=BOTH, padx=10, pady=10)
    self.scrollBar.config(command=self.text.yview)

    sys.stdout = re_Text(self.text)
    root.mainloop()

def show(self):

    i = 0
    while i < 3:
        print fmtTime(time.time())
        time.sleep(1)
        i += 1

if name == "main":

root = Tk()
myGUI = GUI(root)

           上面這段代碼的GUI裏只有一個button和一個Text,我將標準輸出stdout重定向到了一個自定義的類裏,這個類的write方法可以更改Text的內容,具體就不細說了,如果對重定向標準輸出還不瞭解的,可以自行百度或者Google。

           這個時候,如果對Tkinter不熟悉的同學肯定會想,我把上面代碼裏的show方法丟到子線程裏去跑,不就可以解決這個問題了,那我們就先嚐試一下,再改一改代碼:

from Tkinter import
from ttk import

import threading
import time
import sys

def fmtTime(timeStamp):
timeArray = time.localtime(timeStamp)
dateTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)
return dateTime

class re_Text():

def __init__(self, widget):

    self.widget = widget

def write(self, content):

    self.widget.insert(INSERT, content)
    self.widget.see(END)

class GUI():

def __init__(self, root):

    self.initGUI(root)

def initGUI(self, root):

    root.title("test")
    root.geometry("400x200+700+500")
    root.resizable = False

    self.button = Button(root, text="click", width=10, command=self.show)
    self.button.pack(side="top")

    self.scrollBar = Scrollbar(root)
    self.scrollBar.pack(side="right", fill="y")

    self.text = Text(root, height=10, width=45, yscrollcommand=self.scrollBar.set)
    self.text.pack(side="top", fill=BOTH, padx=10, pady=10)
    self.scrollBar.config(command=self.text.yview)

    sys.stdout = re_Text(self.text)
    root.mainloop()

def __show(self):

    i = 0
    while i < 3:
        print fmtTime(time.time())
        time.sleep(1)
        i += 1

def show(self):
    T = threading.Thread(target=self.__show, args=())
    T.start()

if name == "main":

root = Tk()
myGUI = GUI(root)

原文鏈接:https://blog.csdn.net/u013700771/article/details/103321783

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章