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

Tkinter GUI启动线程后无响应

百里阳平
2023-03-14

我写了一个程序的小例子,它应该在按下start_button后开始运行多批次并行线程。这个过程只能启动一次,在一批线程完成后,它应该检查是否是退出的时候了。

所以例如,它可以像这样:
-批处理1(10个线程)正在运行,在它<代码>stop_button被按下。完成批次1后,程序应该停止而不运行批次2并返回初始状态(再次具有启动此过程的选项)。


但是,在这个过程中,GUI似乎根本无法注册单击或任何东西。它只是看起来冻结了。所以我应该以某种方式将线程从GUI中分离出来,但我不知道具体如何。

import threading
import tkinter as tk
import time
import random


class Blocking():
    def __init__(self):
        self.master = tk.Tk()
        self.master.geometry("400x400")

        self.start_button = tk.Button(self.master, command=self.long_task, text='press me to start', state='normal')
        self.start_button.pack()

        self.stop_button = tk.Button(self.master, command=self.stop_func, text='press me to stop', state='normal')
        self.stop_button.pack()
        self.long_task_was_stopped = False

        self.master.mainloop()

    def one_thread(self, thread_index):
        time.sleep(random.randint(5, 10))

    def long_task(self): # will run many batches of parallel one_thread functions on press of start_button
        self.start_button["state"] = 'disabled'
        # first batch of threads
        threads = []
        for thread_number in range(0,10):
            thread = threading.Thread(target=self.one_thread, args=(thread_number,))
            threads.append(thread)
            thread.start()

        for thread in threads:
            thread.join()

        print("First batch over!")
        # batch over, check if it was stopped
        print("Stop variable value:", self.long_task_was_stopped)
        if self.long_task_was_stopped == True:
            # reset states, quit function
            self.long_task_was_stopped = False
            self.start_button["state"] = 'normal'
            print("Stopped, exiting!")
            return

        # second batch of threads
        threads = []
        for thread_number in range(0,10):
            thread = threading.Thread(target=self.one_thread, args=(thread_number,))
            threads.append(thread)
            thread.start()

        for thread in threads:
            thread.join()

        print("Second batch over!")
        self.long_task_was_stopped = False
        self.start_button["state"] = 'normal'
        print("Done.")
        return

    def stop_func(self):
        print("Trying to stop...")
        self.long_task_was_stopped = True



if __name__ == '__main__':
    block = Blocking()  



编辑:解决方案似乎是在线程启动后继续在Tkinter主窗口上调用update(),并在继续之前检查所有线程,直到所有线程都结束。为此需要使用Lock()。这是解决办法。

import threading
import tkinter as tk
import time
import random


class Blocking():
    def __init__(self):
        self.master = tk.Tk()
        self.master.geometry("400x400")

        self.start_button = tk.Button(self.master, command=self.long_task, text='press me to start', state='normal')
        self.start_button.pack()

        self.stop_button = tk.Button(self.master, command=self.stop_func, text='press me to stop', state='normal')
        self.stop_button.pack()
        self.long_task_was_stopped = False

        self.LOCK = threading.Lock()
        self.count_of_done_threads = 0
        self.master.mainloop()


    def one_thread(self, thread_index):
        time.sleep(random.randint(5, 10))
        with self.LOCK:
            print("Thread", thread_index, "done.")
            self.count_of_done_threads = self.count_of_done_threads +1

    def long_task(self): # will run many batches of parallel one_thread functions on press of start_button
        self.start_button["state"] = 'disabled'
        self.long_task_was_stopped = False

        # first batch of threads
        threads = []
        for thread_number in range(0,10):
            thread = threading.Thread(target=self.one_thread, args=(thread_number,))
            threads.append(thread)
            thread.start()

        # wait until threads are done
        while 1:
            self.master.update()
            if self.count_of_done_threads == 10: # 10 here is size of batch
                break
        self.count_of_done_threads = 0

        print("First batch over!")
        # batch over, check if it was stopped
        print("Stop variable value:", self.long_task_was_stopped)
        if self.long_task_was_stopped == True:
            # reset states, quit function
            self.long_task_was_stopped = False
            self.start_button["state"] = 'normal'
            print("Stopped, exiting!")
            return

        # second batch of threads
        threads = []
        for thread_number in range(0,10):
            thread = threading.Thread(target=self.one_thread, args=(thread_number,))
            threads.append(thread)
            thread.start()

        # wait until threads are done
        while 1:
            self.master.update()
            if self.count_of_done_threads == 10:
                break
        self.count_of_done_threads = 0

        print("Second batch over!")
        self.long_task_was_stopped = False
        self.start_button["state"] = 'normal'
        print("Done.")
        return

    def stop_func(self):
        print("Trying to stop...")
        self.long_task_was_stopped = True

if __name__ == '__main__':
    block = Blocking()  

共有1个答案

夏侯元忠
2023-03-14

您应该使用非阻塞线程。只是线程。start()正常。在正式文件中:

其他线程可以调用线程的join()方法。这将阻止调用线程,直到其join()方法被调用的线程终止。

这意味着只有当您的函数one_thread完成其工作时,您的代码才会运行。

你的代码也许应该是:

import threading
import tkinter as tk
import time
import random


class Blocking():
    def __init__(self):
        self.master = tk.Tk()
        self.master.geometry("400x400")

        self.start_button = tk.Button(self.master, command=self.long_task, text='press me to start', state='normal')
        self.start_button.pack()

        self.stop_button = tk.Button(self.master, command=self.stop_func, text='press me to stop', state='normal')
        self.stop_button.pack()
        self.long_task_was_stopped = False

        self.master.mainloop()

    def one_thread(self, thread_index):
        time.sleep(random.randint(5, 10))

    def long_task(self): # will run many batches of parallel one_thread functions on press of start_button
        self.start_button["state"] = 'disabled'
        # first batch of threads
        threads = []
        for thread_number in range(0,10):
            thread = threading.Thread(target=self.one_thread, args=(thread_number,))
            threads.append(thread)
            thread.start()

        print("First batch over!")
        # batch over, check if it was stopped
        print("Stop variable value:", self.long_task_was_stopped)
        if self.long_task_was_stopped == True:
            # reset states, quit function
            self.long_task_was_stopped = False
            self.start_button["state"] = 'normal'
            print("Stopped, exiting!")
            return

        # second batch of threads
        threads = []
        for thread_number in range(0,10):
            thread = threading.Thread(target=self.one_thread, args=(thread_number,))
            threads.append(thread)
            thread.start()

        print("Second batch over!")
        self.long_task_was_stopped = False
        self.start_button["state"] = 'normal'
        print("Done.")
        return

    def stop_func(self):
        print("Trying to stop...")
        self.long_task_was_stopped = True

if __name__ == '__main__':
    block = Blocking()  

我刮网络图片的最小例子。当你运行这段代码时,它会显示加载...当完成刮擦时,它会显示图片)。

import tkinter
from PIL import ImageTk,Image
import threading,requests
from io import BytesIO

def scrapeImage():
    image = ImageTk.PhotoImage(Image.open(BytesIO(requests.get('https://s2.ax1x.com/2020/02/08/1Wl4mT.md.jpg').content)).resize((200,200))) # crawl my image
    imagePos.image = image # keep reference.
    imagePos['image'] = image


root = tkinter.Tk()
thread = threading.Thread(target=scrapeImage)
thread.start()

imagePos = tkinter.Label(text="loading.....")
imagePos.grid()

root.mainloop()
 类似资料:
  • 我是Android的新手,所以我正在制作一个涂色书应用程序,只是为了让自己熟悉Android编程。我已经广泛地查找了我的问题并实施了解决方案,但仍然没有进展。 我有一个活动“着色活动”,它调用了一个扩展表面视图的类“绘画视图”。我试图在一个单独的线程中更新画布。我在布局中也有一个按钮,将用户带到另一个活动中选择颜色。问题是,当用户选择颜色后返回时,画布变成空的,我不能再在画布上画画了。我想我不知何

  • 当运行带有4个API调用作为线程组的一部分的Jeter脚本时,我收到了这个错误。我已经开始看到这个错误,通常是在触发10到15个请求并且执行不到20秒的脚本时。这是随机出现的,有时5%的调用失败,有时高达75%到80%。也将Timeout值设置为100000 ms。

  • 问题内容: 我有一个运行以下配置的网站: Django + mod-wsgi + Apache 在一个用户的请求中,我向另一个服务发送另一个HTTP请求,并通过python的httplib库解决了这个问题。 但是有时此服务不会得到太长的答案,并且httplib的超时不起作用。因此,我创建了线程,在该线程中,我向服务发送请求,并在20秒(20秒-是请求超时)后加入。它是这样工作的: 像这样的东西 并

  • 报告如下: 我没有运行任何其他Postgres安装。通过运行以下命令确认了这一点: 端口5432上也没有运行的应用程序。通过跑步证实了这一点 有什么想法吗?

  • 我不能登录与默认管理员或测试到生成的JHipster(4.2.0)单片应用程序(maven,mysql在开发 控制台日志: 尝试将请求/api/配置文件信息从localhost:9060代理到时发生[HPM]错误http://127.0.0.1:8080 (经济学)(https://nodejs.org/api/errors.html#errors_common_system_errors)尝试将

  • 当我的应用程序启动时,将创建一个executor服务(在java.util.concurrent中使用Executors.NewFixedThreadPool(maxThreadNum))对象。当请求到来时,executor服务将创建线程来处理它们。 当应用程序启动时,它将在executorService池中创建200个线程。 只是想知道当应用程序启动时,这是一种正确的创建线程的方法吗?还是有更好