deftb_click(self): self.progress() self.prog_bar.start() # Simulate long running process t = threading.Thread(target=time.sleep, args=(5,)) t.start() t.join() self.prog_bar.stop()
Основываясь на информации от Брайана Оукли здесь, я понимаю, что мне нужно использовать потоки. Я попытался создать поток, но я предполагаю, что, поскольку поток запускается из основного потока, это не помогает.
У меня была идея поместить логическую часть в другой класс и создать экземпляр GUI из этого класса, аналогично примеру кода А. Родаса здесь.
Мой вопрос:
Я не могу понять, как закодировать это так, чтобы эта команда:
вызывает функцию, которая находится в другом классе. Это плохо или это вообще возможно? Как бы мне создать 2-й класс, который может обрабатывать self.tb_click? Я попытался следовать примеру A. Пример кода Родаса, который прекрасно работает. Но я не могу понять, как реализовать его решение в случае виджета Button, который запускает действие.
Если бы я вместо этого обрабатывал поток из одного класса GUI, как бы создать поток, который не мешает основному потоку?
Переведено автоматически
Ответ 1
Когда вы присоединяетесь к новому потоку в основном потоке, он будет ждать завершения потока, поэтому графический интерфейс будет заблокирован, даже если вы используете многопоточность.
Если вы хотите поместить логическую часть в другой класс, вы можете напрямую создать подкласс Thread, а затем запустить новый объект этого класса при нажатии кнопки. Конструктор этого подкласса Thread может получить объект Queue, и тогда вы сможете передать его в графическую часть. Итак, мое предложение таково:
Создайте объект очереди в основном потоке
Создайте новый поток с доступом к этой очереди
Периодически проверяйте очередь в главном потоке
Тогда вам нужно решить проблему того, что произойдет, если пользователь дважды нажмет одну и ту же кнопку (при каждом нажатии будет создаваться новый поток), но вы можете это исправить, отключив кнопку "Пуск" и включив ее снова после вызова self.prog_bar.stop().
defprocess_queue(self): try: msg = self.queue.get_nowait() # Show result of the task if needed self.prog_bar.stop() except queue.Empty: self.master.after(100, self.process_queue)
classThreadedTask(threading.Thread): def__init__(self, queue): super().__init__() self.queue = queue defrun(self): time.sleep(5) # Simulate long running process self.queue.put("Task finished")
Ответ 2
I will submit the basis for an alternate solution. It is not specific to a Tk progress bar per se, but it can certainly be implemented very easily for that.
Here are some classes that allow you to run other tasks in the background of Tk, update the Tk controls when desired, and not lock up the gui!
Here's a Tk test which demos the use of these. Just append this to the bottom of the module with those classes in it if you want to see the demo in action:
deftkThreadingTest():
from tkinter import Tk, Label, Button, StringVar from time import sleep
Two import points I'll stress about BackgroundTask:
1) The function you run in the background task needs to take a function pointer it will both invoke and respect, which allows the task to be cancelled mid way through - if possible.
2) You need to make sure the background task is stopped when you exit your application. That thread will still run even if your gui is closed if you don't address that!
Ответ 3
I have used RxPY which has some nice threading functions to solve this in a fairly clean manner. No queues, and I have provided a function that runs on the main thread after completion of the background thread. Here is a working example:
import rx from rx.scheduler import ThreadPoolScheduler import time import tkinter as tk
classUI: def__init__(self): self.root = tk.Tk() self.pool_scheduler = ThreadPoolScheduler(1) # thread pool with 1 worker thread self.button = tk.Button(text="Do Task", command=self.do_task).pack()
deflong_running_task(self): # your long running task here... eg: time.sleep(3) # if you want a callback on the main thread: self.root.after(5, self.on_task_complete)
defon_task_complete(self): pass# runs on main thread
if __name__ == "__main__": ui = UI() ui.root.mainloop()
Another way to use this construct which might be cleaner (depending on preference):