当前位置: 首页 > 知识库问答 >
问题:

线程和tkinter

杜苏燕
2023-03-14

我听说Python中的线程不容易处理,而且它们与tkinter的关系更加复杂。

我有以下问题。我有两个类,一个用于GUI,另一个用于无限进程。首先,我启动GUI类,然后启动无限进程类。我希望当您关闭GUI时,它也会完成无限过程,程序也会结束。

代码的简化版本如下:

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()

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

Tcl_AsyncDelete: async handler deleted by the wrong thread

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

共有2个答案

荆弘伟
2023-03-14

这里的修复很简单,但很难发现:

调用main窗口。在main窗口之后立即退出()。mainloop(),因此清理在创建tk UI的同一线程上进行,而不是在python退出时在主线程上进行。

邢雨华
2023-03-14

所有Tcl命令都需要来自同一个线程。由于tkinter依赖于Tcl,因此通常需要使所有tkintergui语句都来自同一个线程。出现此问题的原因是,mainWindowtkinterGui线程中实例化,但由于mainWindowtkinterGui的一个属性,只有在主线程中销毁tkinterGui后才会销毁。

这个问题可以通过不使main Window成为tkinterGui的属性来避免,即将self.mainWindow更改为main Window。这允许main WindowtkinterGui线程中的run方法结束时被销毁。但是,通常可以通过使用main Window.after调用来完全避免线程:

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,仍然可以这样做:

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:

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()
 类似资料:
  • 主要内容:一、MySql中的线程,二、主要方式,三、源码流程,四、总结一、MySql中的线程 在mysql中,每一个连接上来,就会分配给一个相关的THD数据类。在前面的分析中可以看到,连接器(Connectors)连接到的直接就是连接池,在连接池的线程处理中分为三部分,即一对一(一个连接对应一个线程),多对一(多个连接对应一个线程)和线程池(多对多)。 线程池和线程可以针对不同的具体场景来处理具体的事务,这样既兼顾了效率又提高了适应性,对于新手来说,这就是设计的一个

  • 线程(thread)是进程(process)中的一个实体,一个进程至少包含一个线程。比如,对于视频播放器,显示视频用一个线程,播放音频用另一个线程。如果我们把进程看成一个容器,则线程是此容器的工作单位。 进程和线程的区别主要有: 进程之间是相互独立的,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,但互不影响;而同一个进程的多个线程是内存共享的,所有变量都由所有线程共享; 由于进程间是独立的

  • Ruby给了你两个基本的方法来组织你的程序,使它同时能运行自己的不同部分。你可以使用线程在程序内部将任务分割,或者将任务分解为不同的程序,使用多进程来运行。下面我们轮流看一下这两种方法。 多线程 一般来说在Ruby中同时做两件事情最简单的是使用Ruby线程。线程在进程中,由Ruby解释器实现。这使得Ruby线程也能完全的可移至,因为它不需要依赖特定的操作系统,但是这样你也不能利用本地线程(nati

  • 很多同学都听说过,现代操作系统比如Mac OS X,UNIX,Linux,Windows等,都是支持“多任务”的操作系统。 什么叫“多任务”呢?简单地说,就是操作系统可以同时运行多个任务。打个比方,你一边在用浏览器上网,一边在听MP3,一边在用Word赶作业,这就是多任务,至少同时有3个任务正在运行。还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已。 现在,多核CPU已经非常普及了,但是

  • 很多同学都听说过,现代操作系统比如Mac OS X,UNIX,Linux,Windows等,都是支持“多任务”的操作系统。 什么叫“多任务”呢?简单地说,就是操作系统可以同时运行多个任务。打个比方,你一边在用浏览器上网,一边在听MP3,一边在用Word赶作业,这就是多任务,至少同时有3个任务正在运行。还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已。 现在,多核CPU已经非常普及了,但是

  • 线程和进程 基本概念 从源代码经过编译器一系列处理(编译、链接、优化等)得到的可执行文件,我们称为程序(Program)。而通俗地说,进程(Process)就是正在运行并使用计算机资源的程序,与放在磁盘中一动不动的程序不同:首先,进程得到了操作系统提供的资源:程序的代码、数据段被加载到内存中,程序所需的虚拟内存空间被真正构建出来。同时操作系统还给进程分配了程序所要求的各种其他资源,如我们上面几个章