How to share data between two threads [duplicate] - python-3.x

This question already has answers here:
Call a function defined in another function
(4 answers)
Closed 3 years ago.
I am creating an application using Tkinter. I have two Threads, the first is an algorithm that looks for results and the second is my Tkinter window that displays the number of results found.
import time
from tkinter import *
from threading import Thread
global results
results = []
class thread(Thread):
def __init__(self, name):
Thread.__init__(self)
self.name = name
def run(self):
global results
if self.name == "algo":
algo()
elif self.name == "window":
window = Tk()
tmp = StringVar()
tmp.set(str(len(results)))
text = Label(window, textvariable=tmp, font=(None, 12))
text.place(x=10, y=15)
window.mainloop()
def algo():
global results
while True:
time.sleep(1)
results += [1]
print(len(results))
thread_1 = thread("algo")
thread_2 = thread("window")
thread_1.start()
thread_2.start()
thread_1.join()
thread_2.join()
The problem is that the textvariable does not update.

You can run the tkinter mainloop in the mainthread, and your function in a specially spawned new Thread. It does not appear necessary to have more:
import time
import tkinter as tk
from threading import Thread
def algo():
while True:
time.sleep(1)
result.set(result.get() + 1)
print(result.get())
root = tk.Tk()
result = tk.IntVar(value=0)
label = tk.Label(root, textvariable=result)
label.pack()
thread_1 = Thread(target=algo)
thread_1.start()
root.mainloop()

Related

tkinter e Adafruit IO Why the cycle while it is not working?

I've recently been using python....
I cannot understand why the while loop prevents the creation of the tkinter window.
If I move the while loop before the mainloop I display the tkinter window but the loop stops.
import tkinter as tk
import time
from Adafruit_IO import Client, Feed, RequestError
ADAFRUIT_IO_USERNAME = "***********"
ADAFRUIT_IO_KEY = "**********************"
aio = Client(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY)
loop_delay = 5
temp = 25
try:
temperature = aio.feeds('temperature')
except RequestError:
feed = Feed(name="temperature")
temperature = aio.create_feed(feed)
def sendtemp(temp):
aio.send_data(temperature.key,temp)
data = aio.receive(temperature.key)
print(data.value)
window = tk.Tk()
window.title ("Thermometer")
window.geometry("300x100")
label = tk.Label(window, text = temp)
label.pack()
window.mainloop
while True:
sendtemp(temp)
time.sleep(loop_delay)
I solved it this way, what do you think?
import tkinter as tk
from Adafruit_IO import Client, Feed, RequestError
ADAFRUIT_IO_USERNAME = "**********"
ADAFRUIT_IO_KEY = "***************"
aio = Client(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY)
try:
temperature = aio.feeds('temperature')
except RequestError:
feed = Feed(name="temperature")
temperature = aio.create_feed(feed)
class Timer:
def __init__(self, parent):
self.temp = 25
self.label = tk.Label(text="--,- °C", font="Arial 30", width=10)
self.label.pack()
self.label.after(5000, self.sendtemp)
def sendtemp(self):
aio.send_data(temperature.key,self.temp)
data = aio.receive(temperature.key)
print(data.value)
self.label.configure(text="%i°C" % self.temp)
self.temp +=1
self.label.after(5000, self.sendtemp)
if __name__ == "__main__":
window = tk.Tk()
window.title ("Thermometer")
window.geometry("300x100")
timer = Timer(window)
window.mainloop()
Your while loop is blocking the program. It literally does nothing but calling sendtemp and sleeping.
while True:
sendtemp(temp)
time.sleep(loop_delay)
There is no room for reacting to events on the TK window, Python is busy doing those two things.
To do actions periodically, you need to set up a timer that runs on TK's main event loop (the one you start with .mainloop()). This is done with the .after() method.
This method takes a millisecond delay and a function you want to call.
window = tk.Tk()
window.title ("Thermometer")
window.geometry("300x100")
timer_id = None
timer_delay = 5000
def sendtemp(temp):
aio.send_data(temperature.key,temp)
data = aio.receive(temperature.key)
print(data.value)
if timer_id is not None:
start_timer()
def start_timer():
global timer_id
timer_id = window.after(timer_delay, sendtemp)
def stop_timer():
global timer_id
if timer_id is not None:
window.after_cancel(timer_id)
timer_id = None
start_timer()
window.mainloop()
You could bind stopping and starting the timer to a button.

how to update tkinter lables

Im trying to display user inputs in a lable on a tkinter tab using a function that makes a lable, but instead of it updating ever time I run it, it prints a new lable. How do I stop this? My code looks like this:
import time
from tkinter import *
import tkinter as tk
calNums = []
root = Tk()
def key(event):
if event.keysym == '1':
calNums.append(1)
time.sleep(0.05)
displayScreenProgress()
root.bind_all('<Key>', key)
def displayScreenProgress():
cal_display = StringVar()
label = Label(root, textvariable=cal_display, relief=RAISED)
cal_display.set(calNums)
label.pack()
root.mainloop()
I have nine more of these for the other nine numbers:
if event.keysym == '1':
calNums.append(1)
time.sleep(0.05)
Try following:
import time
from tkinter import *
root = Tk()
calNums = []
cal_display = StringVar()
mylabel = Label(root, textvariable=cal_display, relief=RAISED)
mylabel.pack()
def displayScreenProgress():
print(calNums)
cal_display.set(calNums)
def key(event):
if event.keysym == '1':
calNums.append(1)
time.sleep(0.05)
displayScreenProgress()
root.bind_all('<Key>', key)
root.mainloop()
You should create label only once and use that in the function.
What you were doing was creating a new label every time the function was called.

How to use multithreading with tkinter to update a label and simultaneously perform calculations in a thread controlled by button events?

I'm trying to start a counter that displays the value in a label on a separate display window.
The main window has a START, STOP and DISPLAY button.
The START must start the counter, STOP must stop it and the display window must open only when I click on DISPLAY button.
Here's what I have so far. The buttons seem to be unresponsive and the display window pops up without user intervention. How can I fix this?
import tkinter as tk
import time
import threading
import queue
def Run_Device(disp_q,flaq_q):
temp_q = queue.Queue()
temp_q.put(0)
while(flaq_q.empty()):
#time.sleep(0.2)
count = temp_q.get()
count += 1
temp_q.put(count)
disp_q.put(count)
else:
flaq_q.queue.clear()
def P_Window(disp_q):
pw = tk.Tk()
value_label = tk.Label(pw, text=disp_q.get(), relief='sunken', bg='lemon chiffon', font='Helvetica 16 bold')
value_label.pack()
def update_values():
value_label.config(text=disp_q.get())
value_label.after(1000,update_values)
update_values()
pw.mainloop()
def Stop_Dev(flaq_q):
flaq_q.put("Stop")
if __name__ == "__main__":
disp_q = queue.Queue()
flaq_q = queue.Queue()
t_device = threading.Thread(target=Run_Device, args=(disp_q, flaq_q), name="Device 1")
t_disp = threading.Thread(target=P_Window, args=(disp_q, ), name="Display 1")
window = tk.Tk()
start_button = tk.Button(window, text='Start', command=t_device.start(), bg='spring green', font='Helvetica 12 bold', width=20, state='normal', relief='raised')
start_button.pack()
stop_button = tk.Button(window, text='Stop', command=lambda: Stop_Dev(flaq_q), bg='OrangeRed2', font='Helvetica 12 bold', width=20, state='normal', relief='raised')
stop_button.pack()
disp_param_button = tk.Button(window, text='Display', command=t_disp.start(), bg='sky blue', font='Helvetica 12 bold', width=20, state='normal', relief='raised')
disp_param_button.pack()
window.mainloop()
I'm trying to learn how to use multithreading in tkinter so any feedback would be appreciated
There are two issues I see with your code. The first is just simple bugs, e.g.:
start_button = tk.Button(window, text='Start', command=t_device.start(), ...
here it should be command=t_device.start otherwise the command is the function returned by t_device.start()
The second is you've not dealt with various "What if ...?" scenarios, e.g. What if the user pushes 'Start' or 'Display' multiple times?
I've tried to address the above in my rework below:
import tkinter as tk
from time import sleep
from queue import Queue, Empty
from threading import Thread
FONT = 'Helvetica 16 bold'
def run_device():
count = 0
while flaq_q.empty():
count += 1
disp_q.put(count)
sleep(0.5)
while not flaq_q.empty(): # flaq_q.queue.clear() not documented
flaq_q.get(False)
def p_window():
global pw
if pw is None:
pw = tk.Toplevel()
value_label = tk.Label(pw, text=disp_q.get(), width=10, font=FONT)
value_label.pack()
def update_values():
if not disp_q.empty():
try:
value_label.config(text=disp_q.get(False))
except Empty:
pass
pw.after(250, update_values)
update_values()
elif pw.state() == 'normal':
pw.withdraw()
else:
pw.deiconify()
def stop_device():
if flaq_q.empty():
flaq_q.put("Stop")
def start_device():
global device
if device and device.is_alive():
return
while not disp_q.empty():
disp_q.get(False)
disp_q.put(0)
device = Thread(target=run_device)
device.start()
if __name__ == "__main__":
disp_q = Queue()
flaq_q = Queue()
root = tk.Tk()
pw = None
device = None
tk.Button(root, text='Start', command=start_device, width=20, font=FONT).pack()
tk.Button(root, text='Stop', command=stop_device, width=20, font=FONT).pack()
tk.Button(root, text='Display', command=p_window, width=20, font=FONT).pack()
root.mainloop()
I've left out some details to simplify the example. Even with its additional checks, it's still nowhere near perfect. (E.g. it hangs if you don't 'Stop' before you close the window, etc.)
You might be able to adapt something like the following:
import tkinter as tk
import threading
import queue
import time
def run_device():
for i in range(4):
print(i)
pass
def process_queue(MyQueue):
"""Check if we got a complete message from our thread.
Start the next operation if we are ready"""
try:
# when our thread completes its target function (task),
# the supplied message is added to the queue
msg = MyQueue.get(0)
# check message
print(msg)
except queue.Empty:
# .get failed, come back in 100 and check again
print('no message')
threading.Timer(0.001, lambda q=MyQueue: process_queue(q)).start()
class ThreadedTask(threading.Thread):
"""threaded task handler"""
def __init__(self, queue, target, msg):
threading.Thread.__init__(self)
# message to add to queue when the task (target function) completes
self.msg = msg
# function to run
self._target = target
# queue to store completion message
self.queue = queue
def run(self):
"""called when object is instantiated"""
# start users task
try:
self._target()
except Exception as e:
self.queue.put('Thread Fail')
return
# we are done, pass the completion message
self.queue.put(self.msg)
if __name__ == '__main__':
MyQueue = queue.Queue()
MyThread = ThreadedTask(MyQueue, run_device, 'Thread Task: Run')
MyThread.start()
process_queue(MyQueue)

Update a progressbar in a thread

I have a Python code where I create a progressbar. The Tkinter environment is created in the Gui function with the progressbar and it is launched as a thread. Then in an other thread I calculate the value that the progressbar must have, but the problem is that I dont know how to update the Gui thread with the new value of the progressbar. Here is my code:
import tkinter as tk
from tkinter import ttk
import thread
def Gui():
root = tk.Tk()
root.geometry('450x450')
root.title('Hanix Downloader')
button1 = tk.Button(root, text='Salir', width=25,command=root.destroy)
button1.pack()
s = ttk.Style()
s.theme_use('clam')
s.configure("green.Horizontal.TProgressbar", foreground='green', background='green')
mpb = ttk.Progressbar(root,style="green.Horizontal.TProgressbar",orient ="horizontal",length = 200, mode ="determinate")
mpb.pack()
mpb["maximum"] = 3620
mpb["value"] = 1000
root.mainloop()
def main():
while True:
#Calculate the new value of the progress bar.
mpb["value"] = 100 #Does not work
root.update_idletasks()#Does not work
#Do some other tasks.
if __name__ == '__main__':
thread.start_new_thread( Gui,() )
thread.start_new_thread( main,() )
The error I get is that mpb and root do no exist. Thanks in advance.
You should get error because mpb and root are local variables which exist only in Gui but not in main. You have to use global to inform both functions to use global variables - and then main will have access to mpb created in Gui
I also add time.sleep(1) before while True: because sometimes main may start faster then Gui and it may not find mpb (because Gui had no time to create progressbar)
import tkinter as tk
from tkinter import ttk
import _thread
import time
def Gui():
global root, mpb
root = tk.Tk()
button1 = tk.Button(root, text='Exit', command=root.destroy)
button1.pack()
mpb = ttk.Progressbar(root, mode="determinate")
mpb.pack()
mpb["maximum"] = 3000
mpb["value"] = 1000
root.mainloop()
def main():
global root, mpb
time.sleep(1)
while True:
mpb["value"] += 100
#root.update_idletasks() # works without it
#Do some other tasks.
time.sleep(0.2)
if __name__ == '__main__':
_thread.start_new_thread(Gui, ())
_thread.start_new_thread(main, ())
Tested on Python 3.6.2, Linux Mint 18.2
EDIT: more precisely: you need global only in Gui because it assigns values to variables
root = ..., mpb = ....

Updating a tk ProgressBar from a multiprocess.proccess in python3

I have successfully created a threading example of a thread which can update a Progressbar as it goes. However doing the same thing with multiprocessing has so far eluded me.
I'm beginning to wonder if it is possible to use tkinter in this way. Has anyone done this?
I am running on OS X 10.7. I know from looking around that different OS's may behave very differently, especially with multiprocessing and tkinter.
I have tried a producer which talks directly to the widget, through both namespaces and event.wait, and event.set. I have done the same thing with a producer talking to a consumer which is either a method or function which talks to the widget. All of these things successfully run, but do not update the widget visually. Although I have done a get() on the IntVar the widget is bound to and seen it change, both when using widget.step() and/or widget.set(). I have even tried running a separate tk() instance inside the sub process. Nothing updates the Progressbar.
Here is one of the simpler versions. The sub process is a method on an object that is a wrapper for the Progressbar widget. The tk GUI runs as the main process. I also find it a little odd that the widget does not get destroyed at the end of the loop, which is probably a clue I'm not understanding the implications of.
import multiprocessing
from tkinter import *
from tkinter import ttk
import time
root = Tk()
class main_window:
def __init__(self):
self.dialog_count = 0
self.parent = root
self.parent.title('multiprocessing progess bar')
frame = ttk.Labelframe(self.parent)
frame.pack(pady=10, padx=10)
btn = ttk.Button(frame, text="Cancel")
btn.bind("<Button-1>", self.cancel)
btn.grid(row=0, column=1, pady=10)
btn = ttk.Button(frame, text="progress_bar")
btn.bind("<Button-1>", self.pbar)
btn.grid(row=0, column=2, pady=10)
self.parent.mainloop()
def pbar(self, event):
name="producer %d" % self.dialog_count
self.dialog_count += 1
pbar = pbar_dialog(self.parent, title=name)
event = multiprocessing.Event()
p = multiprocessing.Process(target=pbar.consumer, args=(None, event))
p.start()
def cancel(self, event):
self.parent.destroy()
class pbar_dialog:
toplevel=None
pbar_count = 0
def __init__(self, parent, ns=None, event=None, title=None, max=100):
self.ns = ns
self.pbar_value = IntVar()
self.max = max
pbar_dialog.pbar_count += 1
self.pbar_value.set(0)
if not pbar_dialog.toplevel:
pbar_dialog.toplevel= Toplevel(parent)
self.frame = ttk.Labelframe(pbar_dialog.toplevel, text=title)
#self.frame.pack()
self.pbar = ttk.Progressbar(self.frame, length=300, variable=self.pbar_value)
self.pbar.grid(row=0, column=1, columnspan=2, padx=5, pady=5)
btn = ttk.Button(self.frame, text="Cancel")
btn.bind("<Button-1>", self.cancel)
btn.grid(row=0, column=3, pady=10)
self.frame.pack()
def set(self,value):
self.pbar_value.set(value)
def step(self,increment=1):
self.pbar.step(increment)
print ("Current", self.pbar_value.get())
def cancel(self, event):
self.destroy()
def destroy(self):
self.frame.destroy()
pbar_dialog.pbar_count -= 1
if pbar_dialog.pbar_count == 0:
pbar_dialog.toplevel.destroy()
def consumer(self, ns, event):
for i in range(21):
#event.wait(2)
self.step(5)
#self.set(i)
print("Consumer", i)
self.destroy()
if __name__ == '__main__':
main_window()
For contrast, here is the threading version which works perfectly.
import threading
from tkinter import *
from tkinter import ttk
import time
root = Tk()
class main_window:
def __init__(self):
self.dialog_count = 0
self.parent = root
self.parent.title('multiprocessing progess bar')
frame = ttk.Labelframe(self.parent)
frame.pack(pady=10, padx=10)
btn = ttk.Button(frame, text="Cancel")
btn.bind("<Button-1>", self.cancel)
btn.grid(row=0, column=1, pady=10)
btn = ttk.Button(frame, text="progress_bar")
btn.bind("<Button-1>", self.pbar)
btn.grid(row=0, column=2, pady=10)
self.parent.mainloop()
def producer(self, pbar):
i=0
while i < 101:
time.sleep(1)
pbar.step(1)
i += 1
pbar.destroy()
def pbar(self, event):
name="producer %d" % self.dialog_count
self.dialog_count += 1
pbar = pbar_dialog(self.parent, title=name)
p = threading.Thread(name=name, target=self.producer, args=(pbar,))
p.start()
#p.join()
def cancel(self, event):
self.parent.destroy()
class pbar_dialog:
toplevel=None
pbar_count = 0
def __init__(self, parent, ns=None, event=None, title=None, max=100):
self.ns = ns
self.pbar_value = IntVar()
self.title = title
self.max = max
pbar_dialog.pbar_count += 1
if not pbar_dialog.toplevel:
pbar_dialog.toplevel= Toplevel(parent)
self.frame = ttk.Labelframe(pbar_dialog.toplevel, text=title)
#self.frame.pack()
self.pbar = ttk.Progressbar(self.frame, length=300, variable=self.pbar_value)
self.pbar.grid(row=0, column=1, columnspan=2, padx=5, pady=5)
btn = ttk.Button(self.frame, text="Cancel")
btn.bind("<Button-1>", self.cancel)
btn.grid(row=0, column=3, pady=10)
self.frame.pack()
self.set(0)
def set(self,value):
self.pbar_value.set(value)
def step(self,increment=1):
self.pbar.step(increment)
def cancel(self, event):
self.destroy()
def destroy(self):
self.frame.destroy()
pbar_dialog.pbar_count -= 1
if pbar_dialog.pbar_count == 0:
pbar_dialog.toplevel.destroy()
pbar_dialog.toplevel = None
def automatic(self, ns, event):
for i in range(1,100):
self.step()
if __name__ == '__main__':
main_window()
Doing something similar, I ended up having to use a combination of threads and processes - the GUI front end had two threads: one for tkinter, and one reading from a multiprocessing.Queue and calling gui.update() - then the back-end processes would write updates into that Queue
This might be a strange approach, but it works for me. Copy and paste this code to a file and run it to see the result. It's ready to run.
I don't have the patience to explain my code right now, I might edit it another day.
Oh, and this is in Python 2.7 I started programming two months ago, so I have not idea if the difference is relevant.
# -*- coding: utf-8 -*-
# threadsandprocesses.py
# Importing modules
import time
import threading
import multiprocessing
import Tkinter as tki
import ttk
class Master(object):
def __init__(self):
self.mainw = tki.Tk()
self.mainw.protocol("WM_DELETE_WINDOW", self.myclose)
self.mainw.title("Progressbar")
self.mainw.geometry('300x100+300+300')
self.main = tki.Frame(self.mainw)
self.RunButton = ttk.Button(self.main, text='Run',
command=self.dostuff)
self.EntryBox = ttk.Entry(self.main)
self.EntryBox.insert(0, "Enter a number")
self.progress = ttk.Progressbar(self.main,
mode='determinate', value=0)
self.main.pack(fill=tki.BOTH, expand=tki.YES)
self.progress.pack(expand=tki.YES)
self.EntryBox.pack(expand=tki.YES)
self.RunButton.pack()
print "The Master was created"
def dostuff(self):
print "The Master does no work himself"
data = range(int(self.EntryBox.get()))
S = Slave(self, data)
print "The Master created a Slave to do his stuff"
print "The Slave gets told to start his work"
S.start()
def myclose(self):
self.mainw.destroy()
return
def nextstep(self):
print "Good job, Slave, I see the result is"
print Master.results.get()
class Slave(threading.Thread):
def __init__(self, guest, data):
print "This is the Slave."
print "Nowdays, Work is outsourced!"
self.data = data
self.guest = guest
threading.Thread.__init__(self)
def run(self):
print "The Slave is outsourcing his work to Calcualte inc."
time.sleep(1)
Outsourcing = Calculate()
Results = Outsourcing.run(self.guest, self.data)
return Results
# unwrapping outside a class
def calc(arg, **kwarg):
return Calculate.calculate(*arg, **kwarg)
class Calculate(object):
def run(self, guest, data):
print"This is Calculate inc. ... how can I help you?"
time.sleep(1)
maximum = int(guest.EntryBox.get())
guest.progress.configure(maximum=maximum, value=0)
manager = multiprocessing.Manager()
queue = manager.Queue()
lock = manager.Lock()
print "Things are setup and good to go"
# Counting the number of available CPUs in System
pool_size = multiprocessing.cpu_count()
print "Your system has %d CPUs" % (pool_size)
# Creating a pool of processes with the maximal number of CPUs possible
pool = multiprocessing.Pool(processes=pool_size)
Master.results = pool.map_async(calc, (zip([self]*len(data), [lock]*len(data),
[queue]*len(data), data)))
for job in range(1, maximum+1):
queue.get() # this is an abuse I think, but works for me
guest.progress.configure(value=job)
# Properly close and end all processes, once we're done
pool.close()
pool.join()
print "All done"
guest.nextstep()
return
def calculate(self, lock, queue, indata):
lock.acquire()
print 'Reading values and starting work'
lock.release()
time.sleep(3) # some work
results = indata # The works results
lock.acquire()
print 'Done'
lock.release()
queue.put("Finished!")
return results
if __name__ == '__main__':
TheMaster = Master()
TheMaster.mainw.mainloop()

Resources