【tkGo】使用線程和裝飾器

1 背景

在使用tkinter進行GUI編程時,有時會碰到界面未響應的狀況,以下: python

2 解決辦法

緣由頗有多是執行的某個動做阻塞了線程,可經過使用threading.Thread解決(本例中是點擊了Go菜單下的開始選項致使的界面卡死)git

2.1 解決方法1 - 使用線程封裝該動做

修改前代碼:github

class MenuGo(EMenu):

    LABEL_GO = "Go"
    LABEL_START = "開始"
    LABEL_END = "終止"
    
    def __init__(self, master=None, cnf={}, **kw):
        super().__init__(master=master, cnf=cnf, **kw)
        self.go = None
        master.add_cascade(label=self.LABEL_GO, menu=self)
        self.add_command(label=self.LABEL_START, command=self.start)
        self.add_command(label=self.LABEL_END, command=self.end)

    def start(self):
        self.go = True
        while self.go:
            self.stdout("biu biu biu!", with_time=" - ")
            time.sleep(1)
    
    def end(self):
        self.go = False
        self.stdout("zi zi zi!", with_time=" - ")
複製代碼

修改後代碼:編程

class MenuGo(EMenu):

    LABEL_GO = "Go"
    LABEL_START = "開始"
    LABEL_END = "終止"
    
    def __init__(self, master=None, cnf={}, **kw):
        super().__init__(master=master, cnf=cnf, **kw)
        self.go = None
        master.add_cascade(label=self.LABEL_GO, menu=self)
        self.add_command(label=self.LABEL_START, command=self.start)
        self.add_command(label=self.LABEL_END, command=self.end)

    def start(self):
        def run():
            self.go = True
            while self.go:
                self.stdout("biu biu biu!", with_time=" - ")
                time.sleep(1)
        t = Thread(target=run)
        t.start()
    
    def end(self):
        self.go = False
        self.stdout("zi zi zi!", with_time=" - ")
複製代碼

2.2 解決方法2 - 使用裝飾器類

from functools import wraps
from threading import Thread


class ThreadRun(object):
    def __init__(self):
        pass

    def __call__(self, f):
 @wraps(f)
        def wrapped_f(*args, **kw):
            t = Thread(
                target=f,
                args=args,
                kwargs=kw
            )
            t.start()

        return wrapped_f
複製代碼
class MenuGo(EMenu):

    LABEL_GO = "Go"
    LABEL_START = "開始"
    LABEL_END = "終止"
    
    def __init__(self, master=None, cnf={}, **kw):
        super().__init__(master=master, cnf=cnf, **kw)
        self.go = None
        master.add_cascade(label=self.LABEL_GO, menu=self)
        self.add_command(label=self.LABEL_START, command=self.start)
        self.add_command(label=self.LABEL_END, command=self.end)

 @ThreadRun()
    def start(self):
        self.go = True
        while self.go:
            self.stdout("biu biu biu!", with_time=" - ")
            time.sleep(1)
    
    def end(self):
        self.go = False
        self.stdout("zi zi zi!", with_time=" - ")
複製代碼

2.3 解決方法3 - 繼承類,添加裝飾器函數

from tkinter import Menu
from tkinter import DISABLED, NORMAL
from threading import Thread
from functools import wraps


class EMenu(Menu):
    def __init__(self, *args, **kw):
        self.stdout = kw["stdout"] if "stdout" in kw else print
        self.stderr = kw["stderr"] if "stderr" in kw else print
        for key in ["stdout", "stderr"]:
            if key in kw:
                del kw[key]
        super().__init__(*args, **kw)

    def thread_run(label_name):
        def run_decorator(f):
 @wraps(f)
            def wrapped_f(self, *args, **kw):
                def call():
                    self.entryconfig(label_name, state=DISABLED)  # 設置菜單選項禁用
                    f(self, *args, **kw)
                    self.entryconfig(label_name, state=NORMAL)  # 設置菜單選項啓用
                t = Thread(target=call)
                t.start()
            return wrapped_f
        return run_decorator
複製代碼
class MenuGo(EMenu):

    LABEL_GO = "Go"
    LABEL_START = "開始"
    LABEL_END = "終止"
    
    def __init__(self, master=None, cnf={}, **kw):
        super().__init__(master=master, cnf=cnf, **kw)
        self.go = None
        master.add_cascade(label=self.LABEL_GO, menu=self)
        self.add_command(label=self.LABEL_START, command=self.start)
        self.add_command(label=self.LABEL_END, command=self.end)

 @EMenu.thread_run(LABEL_START)
    def start(self):
        self.go = True
        while self.go:
            self.stdout("biu biu biu!", with_time=" - ")
            time.sleep(1)
    
    def end(self):
        self.go = False
        self.stdout("zi zi zi!", with_time=" - ")
複製代碼

3 演示效果

4 完整代碼

github.com/TheUncleWho…app

相關文章
相關標籤/搜索