I am trying to get my head around threading in python, I have pieced together a simple tkinter UI that can run a background task to better understand how it all works.
I seem to have got to the point where I can create a seperate thread and interact with the GUI but the problem comes when I try to carry out the task the second time.
I believe what I am missing is a way to tell the daemon when to stop the thread.
Here is my code thus far:
import threading
import tkinter as tk
from collections import deque
from threading import Thread
from random import randint
from time import sleep
class Emitter:
def __init__(self):
self.stop_event = threading.Event()
self.thread = Thread(target=self.emit_text)
self.running = False
def emit_text(self):
sleep(3)
messageQueue.append(f'Random number: {randint(0, 100)}')
# self.thread.set()
def start_emit(self):
ui.consume_text()
self.thread.setDaemon(True)
self.thread.start()
class UI:
def __init__(self):
self.root = tk.Tk()
self.label = tk.Label(self.root, text='Original text')
self.label.pack()
start = tk.Button(self.root, text='Start Download', command=lambda x=Emitter(): x.start_emit())
start.pack()
def consume_text(self):
try:
self.label['text'] = messageQueue.popleft()
except IndexError:
self.root.after(ms=1000, func=self.consume_text)
messageQueue = deque()
ui = UI()
ui.root.mainloop()
I have tried to understand other answers but I can't grasp how you tell the daemon/thread what it's condition to stop is.
The prgram works as intended the first time you click the button, but then if you try to click it again it gives the error RuntimeError: cannot set daemon status of active thread
even if I have waited for the Emitter to finish it's task
CodePudding user response:
Thanks to @Solomon Slow for their comment, the problem I had was I was trying to restart the thread but in fact I should have been creating a new one each time.
To fix the problem, I changed my class Emitter class to create a new thread each time start_emit() is called.
The fixed class:
class Emitter:
def __init__(self):
self.stop_event = threading.Event()
def emit_text(self):
sleep(3)
messageQueue.append(f'Random number: {randint(0, 100)}')
def start_emit(self):
ui.consume_text()
thread = Thread(target=self.emit_text)
thread.start()
I also removed the self.thread.setDaemon(True)
line.