Можете ли вы убить приложение Tk из другого потока?

Я писал приложение Tk (мое первое) и, как вы можете сказать, я новичок в Python. Мне нужен был поток, чтобы не блокировать основной поток, в котором работает экземпляр Tk, поэтому я загружаю файл в другом потоке. Вот мой код:

from tkinter import Toplevel, FLAT, BOTH, X, IntVar, StringVar, HORIZONTAL, Text, END, Tk, LEFT, RIGHT
from tkinter.ttk import Label, Frame, Style, Button, Checkbutton, Progressbar, Entry
import tkinter.messagebox as messagebox
import os
import sys
import threading
from shutil import copyfile, rmtree
class DownloadWindow:
    def __init__(self, root):
        self.root = root

        self.master = Toplevel(self.root)
        self.master.protocol('WM_DELETE_WINDOW', self.close)
        self.master.geometry("600x400")
        self.master.resizable(False, False)
        frame = Frame(self.master, relief=FLAT)
        frame.pack(padx=10, pady=5, fill=BOTH)
        self.mode = 'unksize'
        self.progressbar = Progressbar(frame, orient=HORIZONTAL, length=100, mode='indeterminate')
        self.progressbar.pack(fill=X)
        self.progressbar.start(20)
        self.downloaded = 0
        self.progressv = Text(frame, bd=0, insertborderwidth=0, state='disabled', background='#f0f0ed', font=('Segoe UI', 10))
        self.progressv.pack(fill=BOTH, pady=(10,5))
        self.downloading = threading.Thread(target=self.download)
        self.downloading.start()
    def download_progress(self, count, block_size, total_size):
        if total_size != -1 and self.mode != 'ksize':
            self.mode = 'ksize'
            self.progressbar.stop()
            self.progressbar.configure(mode='determinate', maximum=total_size)
        elif total_size == -1 and self.mode != 'unksize':
            self.mode = 'unksize'
            self.progressbar.configure(mode='indeterminate')
        if self.mode == 'ksize':
            self.downloaded += block_size
            self.progressbar.step(block_size)
            if self.downloaded == total_size:
                self.progress('Download ended.')

    def download(self):
        self.pathres = False
        self.progress('Downloading...', False)
        # Let's suppose this failed
        messagebox.showerror(title='uh oh this failed', message='Download failed, error bla bla bla')
        self.error_close()
        self.progressbar.stop()
        self.finish()
    def finish(self):
        self.progress('The installation is finished...')
    def progress(self, what, lb=True):
        self.progressv.configure(state='normal')
        self.progressv.insert(END, ('
' if lb else '')+what)
        self.progressv.configure(state='disabled')
    def error_close(self):
        # This is technically the method that's called when the download fails with an error
        # This closes the window, but not the console itself... You can check that in taskmanager or from console if running from it...
        self.root.destroy()
    def close(self):
        pass
if __name__ == "__main__":
    root = Tk()
    DownloadWindow(root)
    root.withdraw()
    root.mainloop()

^ Runnable пример
Проблема: как я могу выйти из приложения на части, когда я поймал ошибку? В основном я пытался self.root.destroy (), но, как вы можете сказать, он не работал (он закрыл окно, но основной цикл продолжал работать), попытался sys.exit (), он не работал ни (я думаю, что убил новый поток вместо "основного"), и я передал корень Tk () в другой поток, но он тоже не работал.
Вы можете запустить приведенный выше пример, чтобы понять больше.

Я поместил печать сразу после метода root.mainloop, и как только поток вызвал root.destroy, как ни странно, он напечатал строку выхода ... Итак, я понятия не имею, как это решить

Всего 1 ответ


Если я понимаю ваш вопрос, тогда да. Вот короткий пример, когда кнопка в многопоточном окне убивает приложение в главном потоке.

import tkinter as tk
from threading import Thread


root = tk.Tk()


def threaded_window():
    top = tk.Toplevel(root)
    tk.Button(top, text='kill main application in main thread', command=root.destroy).pack()


tk.Button(root, text='Open threaded window', command=lambda: Thread(target=threaded_window).start()).pack()
root.mainloop()

Есть идеи?

10000