我有一个Python3.11.5程序,它使用Tkinter图形用户界面接收用户输入,并在后台运行许多进程.我想知道当用户想要在后台进程仍在运行时取消它时,首选的处理方法是什么.我试图制作取消按钮,使用单独的线程和threading.Event来表示取消,但我无法让它干净利落地退出.我希望能够点击取消,如果需要的话,花几秒钟的时间,但看到程序结束,我的终端恢复正常,没有错误.

这里有一个最小的例子:

import tkinter as tk
import time

class app(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("An app")
        self.create_widgets()
        self.a_process()

    def create_widgets(self):
        self.text = tk.Label(self, text = "Hello World!")
        self.text.pack()
        self.cancelButton = tk.Button(self, text = "Cancel", command = self.destroy)
        self.cancelButton.pack()

    def a_process(self):
        for i in range(100):
            self.text.config(text = f"Message # {i}")
            self.text.update()
            time.sleep(1)

    def destroy(self):
        super().destroy()

if __name__ == "__main__":
    root = app()
    root.mainloop()

在消息循环运行时点击Cancel会出现以下错误:

Traceback (most recent call last):
  File "C:\Users\M.Modeler\geoSieve 1.1\test.py", line 27, in <module>
    root = app()
           ^^^^^
  File "C:\Users\M.Modeler\geoSieve 1.1\test.py", line 9, in __init__
    self.a_process()
  File "C:\Users\M.Modeler\geoSieve 1.1\test.py", line 19, in a_process
    self.text.config(text = f"Message # {i}")
  File "C:\Users\M.Modeler\AppData\Local\miniconda3\envs\geoSieve\Lib\tkinter\__init__.py", line 1702, in configure
    return self._configure('configure', cnf, kw)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\M.Modeler\AppData\Local\miniconda3\envs\geoSieve\Lib\tkinter\__init__.py", line 1692, in _configure
    self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: invalid command name ".!label"

要结束后台进程并干净地退出,我的destroy方法缺少什么?

推荐答案

您不应该在您的图形用户界面中使用阻塞无限循环,而是使用self.after来调度稍后要调用的函数,现在您不需要调用update来重画图形用户界面,并且当图形用户界面退出时不再调用该函数.

import tkinter as tk
import time

class app(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("An app")
        self.create_widgets()
        self.after(1000, lambda: self.a_process(0))

    def create_widgets(self):
        self.text = tk.Label(self, text = "Hello World!")
        self.text.pack()
        self.cancelButton = tk.Button(self, text = "Cancel", command = self.destroy)
        self.cancelButton.pack()

    def a_process(self, i):
        self.text.config(text = f"Message # {i}")
        if i+1 < 100:  # call this function later with different args
            self.after(1000,lambda: self.a_process(i+1))

    def destroy(self):
        super().destroy()

if __name__ == "__main__":
    root = app()
    root.mainloop()

或者,如果您有一个很长的任务想要在后台运行,您可以将它调度在一个threading.Thread守护进程上(将在python退出时终止),并让它在更新图形用户界面的函数上调用self.after.

因为在主循环结束时不会发生用after调用的函数,所以当图形用户界面退出时,您不会try 更新图形用户界面.

import tkinter as tk
import time
import threading

class app(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("An app")
        self.create_widgets()
        self.counter = 0
        thread = threading.Thread(target = self.a_process, args=[], daemon=True)
        thread.start()
        
    def create_widgets(self):
        self.text = tk.Label(self, text = "Hello World!")
        self.text.pack()
        self.cancelButton = tk.Button(self, text = "Cancel", command = self.destroy)
        self.cancelButton.pack()

    def a_process(self):
        for i in range(100):
            self.counter += 1
            self.after(0,self.update_counter)
            time.sleep(1)
            
    def update_counter(self):
        self.text.config(text = f"Message # {self.counter}")

    def destroy(self):
        super().destroy()

if __name__ == "__main__":
    root = app()
    root.mainloop()

Python相关问答推荐

Docker-compose:为不同项目创建相同的容器

使用Python C API重新启动Python解释器

如何使用Selenium访问svg对象内部的元素

如何从FDaGrid实例中删除某些函数?

如何在Python中使用io.BytesIO写入现有缓冲区?

我必须将Sigmoid函数与r2值的两种类型的数据集(每种6个数据集)进行匹配,然后绘制匹配函数的求导.我会犯错

Python json.转储包含一些UTF-8字符的二元组,要么失败,要么转换它们.我希望编码字符按原样保留

如何让剧作家等待Python中出现特定cookie(然后返回它)?

如何更改分组条形图中条形图的 colored颜色 ?

如何使用pytest来查看Python中是否存在class attribution属性?

Python解析整数格式说明符的规则?

如果条件不满足,我如何获得掩码的第一个索引并获得None?

DataFrames与NaN的条件乘法

使用密钥字典重新配置嵌套字典密钥名

在www.example.com中使用`package_data`包含不包含__init__. py的非Python文件

如何在Pyplot表中舍入值

Python Tkinter为特定样式调整所有ttkbootstrap或ttk Button填充的大小,适用于所有主题

Matplotlib中的字体权重

LocaleError:模块keras._' tf_keras. keras没有属性__internal_'''

jsonschema日期格式