线程和 tkinter [英] Threads and tkinter

查看:30
本文介绍了线程和 tkinter的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我听说 Python 中的线程不容易处理,而且它们与 tkinter 更加纠缠不清.

I've heard that threads in Python are not easy to handle and they become more tangled with tkinter.

我有以下问题.我有两个类,一个用于 GUI,另一个用于无限进程.首先,我启动 GUI 类,然后是无限进程的类.我希望当你关闭 GUI 时,它也完成了无限进程并且程序结束.

I have the following problem. I have two classes, one for the GUI and another for an infinite process. First, I start the GUI class and then the infinite process' class. I want that when you close the GUI, it also finishes the infinite process and the program ends.

代码的简化版本如下:

import time, threading
from tkinter import *
from tkinter import messagebox

finish = False

class tkinterGUI(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):  
        global finish
        #Main Window
        self.mainWindow = Tk()
        self.mainWindow.geometry("200x200")
        self.mainWindow.title("My GUI Title")
        #Label
        lbCommand = Label(self.mainWindow, text="Hello world", font=("Courier New", 16)).place(x=20, y=20)
        #Start
        self.mainWindow.mainloop()
        #When the GUI is closed we set finish to "True"
        finish = True

class InfiniteProcess(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        global finish
        while not finish:
            print("Infinite Loop")
            time.sleep(3)

GUI = tkinterGUI()
GUI.start()
Process = InfiniteProcess()
Process.start()

当我单击关闭按钮(右上角)时,控制台中出现以下错误:

When I click in the close button (in the upper right corner) the following error appears in the console:

Tcl_AsyncDelete: async handler deleted by the wrong thread

我不知道为什么会这样,也不知道这意味着什么.

I don't know why it happens or what it means.

推荐答案

所有 Tcl 命令都需要源自同一个线程.由于 tkinter 的对Tcl的依赖,一般需要制作所有tkinter gui语句源自同一个线程.出现问题是因为mainWindowtkinterGui 线程中实例化,但是 -- 因为 mainWindowtkinterGui 的一个属性 -- 不是直到 tkinterGui 在主线程中被销毁.

All Tcl commands need to originate from the same thread. Due to tkinter's dependence on Tcl, it's generally necessary to make all tkinter gui statements originate from the same thread. The problem occurs because mainWindow is instantiated in the tkinterGui thread, but -- because mainWindow is an attribute of tkinterGui -- is not destroyed until tkinterGui is destroyed in the main thread.

不把mainWindow作为tkinterGui的属性可以避免这个问题-- 即,将 self.mainWindow 更改为 mainWindow.这允许 mainWindowrun 方法在 tkinterGui 线程结束时被销毁.但是,通常您可以通过使用 mainWindow.after 调用来完全避免线程:

The problem can be avoided by not making mainWindow an attribute of tkinterGui -- i.e. changing self.mainWindow to mainWindow. This allows mainWindow to be destroyed when the run method ends in the tkinterGui thread. However, often you can avoid threads entirely by using mainWindow.after calls instead:

import time, threading
from tkinter import *
from tkinter import messagebox

def infinite_process():
    print("Infinite Loop")
    mainWindow.after(3000, infinite_process)


mainWindow = Tk()
mainWindow.geometry("200x200")
mainWindow.title("My GUI Title")
lbCommand = Label(mainWindow, text="Hello world", font=("Courier New", 16)).place(x=20, y=20)
mainWindow.after(3000, infinite_process)
mainWindow.mainloop()

<小时>

如果你想在一个类中定义 GUI,你仍然可以这样做:


If you want to define the GUI inside a class, you can still do so:

import time, threading
from tkinter import *
from tkinter import messagebox

class App(object):
    def __init__(self, master):
        master.geometry("200x200")
        master.title("My GUI Title")
        lbCommand = Label(master, text="Hello world", 
                          font=("Courier New", 16)).place(x=20, y=20)

def tkinterGui():  
    global finish
    mainWindow = Tk()
    app = App(mainWindow)
    mainWindow.mainloop()
    #When the GUI is closed we set finish to "True"
    finish = True

def InfiniteProcess():
    while not finish:
        print("Infinite Loop")
        time.sleep(3)

finish = False
GUI = threading.Thread(target=tkinterGui)
GUI.start()
Process = threading.Thread(target=InfiniteProcess)
Process.start()
GUI.join()
Process.join()

或者更简单,直接使用主线程运行GUI主循环:

or even simpler, just use the main thread to run the GUI mainloop:

import time, threading
from tkinter import *
from tkinter import messagebox

class App(object):
    def __init__(self, master):
        master.geometry("200x200")
        master.title("My GUI Title")
        lbCommand = Label(master, text="Hello world", 
                          font=("Courier New", 16)).place(x=20, y=20)

def InfiniteProcess():
    while not finish:
        print("Infinite Loop")
        time.sleep(3)

finish = False
Process = threading.Thread(target=InfiniteProcess)
Process.start()

mainWindow = Tk()
app = App(mainWindow)
mainWindow.mainloop()
#When the GUI is closed we set finish to "True"
finish = True
Process.join()

这篇关于线程和 tkinter的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆