线程和tkinter [英] Threads and tkinter

查看:77
本文介绍了线程和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.

下面是代码的简化版本:

A simplified version of the code is the following:

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语句 源于同一线程.发生问题是因为 mainWindow是在tkinterGui线程中实例化的,但是-因为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.这样,当run方法在tkinterGui线程中结束时,可以销毁mainWindow.但是,通常可以通过使用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 mainloop:

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天全站免登陆