I sumarize my problem through this piece of code. When I end my program by closing the tkinter main window, I need the whole program ends, but the loops goes on executing until the functions is over. I suppose there is a way to force these functions ends too. I think there is a way to detect the program was ended, so I could end the functions.
import threading
import time
from tkinter import *
def loop1_10():
for i in range(1, 11):
time.sleep(1)
print(i)
def loop1_10_b():
for i in range(1, 11):
time.sleep(2)
print(i)
threading.Thread(target=loop1_10).start()
threading.Thread(target=loop1_10_b).start()
MainWindow = Tk()
MainWindow.mainloop()
The other way to handle this is to make the threads "daemons". A daemon thread will be forcibly closed when the app exits; it doesn't block the app.
threading.Thread(target=loop1_10, daemon=True).start()
threading.Thread(target=loop1_10_b, daemon=True).start()
Note that I'm not saying one is better or worse than the other. Each option has its uses.
Add a protocol, WM_DELETE_WINDOW, to your MainWindow, where you use define a function you defined, on_close() that gets called once the tkinter window is closed.
The on_close() function will redefine the global variable end from False into True, and in each for loop, if the end variable's value is True, return out of them:
import threading
import time
from tkinter import *
def loop1_10():
for i in range(1, 11):
if end:
return
time.sleep(1)
print(i)
def loop1_10_b():
for i in range(1, 11):
if end:
return
time.sleep(2)
print(i)
end = False
def on_closing():
global end
end = True
MainWindow.destroy()
threading.Thread(target=loop1_10).start()
threading.Thread(target=loop1_10_b).start()
MainWindow = Tk()
MainWindow.protocol("WM_DELETE_WINDOW", on_closing)
MainWindow.mainloop()
But there is still a problem with the above code; if the end = True happened right before the time.sleep() call(s), the last time.sleep()(s) will still make the program wait for a second or two before terminating.
To fix this, use time.time() and a while loop to manually check how much time has passed before continuing each for loop:
import threading
import time
from tkinter import *
def loop1_10():
for i in range(1, 11):
old_time = time.time()
while True:
if end:
return
if time.time() - old_time < 1:
continue
break
print(i)
def loop1_10_b():
for i in range(1, 11):
old_time = time.time()
while True:
if end:
return
if time.time() - old_time < 2:
continue
break
print(i)
end = False
def on_closing():
global end
end = True
MainWindow.destroy()
threading.Thread(target=loop1_10).start()
threading.Thread(target=loop1_10_b).start()
MainWindow = Tk()
MainWindow.protocol("WM_DELETE_WINDOW", on_closing)
MainWindow.mainloop()
But do note from this comment by #kindall:
use time.time() and a while loop -- don't do this, it'll use up an entire CPU core waiting for the loop to exit. this will not only eat battery unnecessarily, but since Python only uses one CPU core due to the Global Interpreter Lock, it will make the rest of the program sluggish, too
Related
I want to start thread multiple times, but only when it is not running.
There is a simple model what I am trying:
import threading
import time
def up (x, r):
time.sleep(3)
r['h'] = x + 1
hum = {'h' : 0}
while True:
print(hum['h'])
H = threading.Thread(target = up, args=(hum['h'],hum))
H.daemon=True
if not H.isAlive():
H.start()
print(threading.active_count())
Also what i don't understand is that:
When I run program it prints: 0. Then after 3 seconds it prints:1 and so on after every 3 second it is increased by 1.
But I thought it would print: 0. Then after 3 second it would print: 1. And then immediately increasing fast.
Because after starting first thread it would immediately start the next one and so on. why does this happen?
How not to start a thread 'up', if it's already running?
Not sure if I got your question completely, but here are some thoughts.
When I run your code I get an increasing number of active threads, as you are creating a new thread every time, checking its status (which will always be not alive) and then starting it.
What you want to do instead is to check the status of the last run thread and if that's not alive start a new one. In order to do that you should create a new thread if the old one is done:
def up (x, r):
time.sleep(3)
r['h'] = x + 1
def main():
hum = {'h' : 0}
H = threading.Thread(target = up, args=(hum['h'],hum))
H.daemon=True
while True:
# print(hum['h'])
if not H.isAlive():
H = threading.Thread(target = up, args=(hum['h'],hum))
H.daemon=True
H.start()
print(threading.active_count())
What happens in your code:
Print the value of hum['h']
Create a thread (note you create it, you are not starting it yet)
Set the value of a property
If the thread is not started then start it
Print the count of active threads (active, NOT started)
Since you replace the H variable every time, you'll have a new thread every time that gets immediately started.
If you add a print that says "starting" in the if for the is alive, you'll see that it gets called every time.
You can use join() to wait for the thread to finish:
import threading
import time
def up (x, r):
time.sleep(3)
r['h'] = x + 1
hum = {'h' : 0}
while True:
print(hum['h'])
H = threading.Thread(target = up, args=(hum['h'],hum))
H.daemon=True
H.start()
H.join()
print(threading.active_count())
If you don't want to wait, you can just save the current running thread in a variable and check it in the loop:
import threading
import time
def up (x, r):
time.sleep(3)
r['h'] = x + 1
hum = {'h' : 0}
current_thread = None
while True:
print(hum['h'])
if current_thread is None:
current_thread = threading.Thread(target = up, args=(hum['h'],hum))
current_thread.daemon=True
current_thread.start()
elif not current_thread.isAlive():
current_thread = threading.Thread(target = up, args=(hum['h'],hum))
current_thread.daemon=True
current_thread.start()
The story begin with two threads and a global variable that change.. a lot of time :)
Thread number one (for simplicity we will call t1) generates a random number and store it in a global variable GLB.
Thread number two (aka t2) check the value of the global variable and when it reaches a value starts to print his value until a period of time.
BUT if t1 changes the value of that global variable, also change the value inside the loop and I don't want this!
I try to write pseudocode:
import random
import time
import threading
GLB = [0,0]
#this is a thread
def t1():
while True:
GLB[0] = random.randint(0, 100)
GLB[1] = 1
print GLB
time.sleep(5)
#this is a thread
def t2():
while True:
if GLB[0]<=30:
static = GLB
for i in range(50):
print i," ",static
time.sleep(1)
a = threading.Thread(target=t1)
a.start()
b = threading.Thread(target=t2)
b.start()
while True:
time.sleep(1)
The question is: why variable static change inside the loop for? It should be remain constant unitl it escapes from loop!
Could I create a lock to the variable? Or there is any other way to solve the problem?
Thanks regards.
GLB is a mutable object. To let one thread see a consistent value while another thread modifies it you can either protect the object temporarily with a lock (the modifier will wait) or copy the object. In your example, a copy seems the best option. In python, a slice copy is atomic so does not need any other locking.
import random
import time
import threading
GLB = [0,0]
#this is a thread
def t1():
while True:
GLB[0] = random.randint(0, 100)
GLB[1] = 1
print GLB
time.sleep(5)
#this is a thread
def t2():
while True:
static = GLB[:]
if static[0]<=30:
for i in range(50):
print i," ",static
time.sleep(1)
a = threading.Thread(target=t1)
a.start()
b = threading.Thread(target=t2)
b.start()
while True:
time.sleep(1)
Well i am a bit of newb at python, and i am getting hard to make a thread in Tkinter , as you all know using while in Tkinter makes it Not Responding and the script still running.
def scheduler():
def wait():
schedule.run_pending()
time.sleep(1)
return
Hours = ScheduleTest()
if len(Hours) == 0:
print("You need to write Hours, Example: 13:30,20:07")
if len(Hours) > 0:
print("Scheduled: ", str(Hours))
if len(Hours) == 1:
schedule.every().day.at(Hours[0]).do(Jumper)
print("Will jump 1 time")
elif len(Hours) == 2:
schedule.every().day.at(Hours[0]).do(Jumper)
schedule.every().day.at(Hours[1]).do(Jumper)
print("Will jump 2 times")
elif len(Hours) == 3:
schedule.every().day.at(Hours[0]).do(Jumper)
schedule.every().day.at(Hours[1]).do(Jumper)
schedule.every().day.at(Hours[2]).do(Jumper)
print("Will jump 3 times")
while True:
t = threading.Thread(target=wait)
t.start()
return
scheduler()
i have tried to do something like this but it still makes tkinter not responding
Thanks in advance.
When to use the after method; faking while without threading
As mentioned in a comment, In far most cases, you do not need threading to run a "fake" while loop. You can use the after() method to schedule your actions, using tkinter's mainloop as a "coat rack" to schedule things, pretty much exactly like you would in a while loop.
This works in all situations where you can simply throw out commands with e.g. subprocess.Popen(), update widgets, show messages etc.
It does not work when the scheduled process takes a lot of time, running inside the mainloop. Therefore time.sleep() is a bummer; it will simply hold the mainloop.
How it works
Within that limitation however, you can run complicated tasks, schedule actions even set break (-equivalent) conditions.
Simply create a function, initiate it with window.after(0, <function>). Inside the function, (re-) schedule the function with window.after(<time_in_milliseconds>, <function>).
To apply a break- like condition, simply rout the process (inside the function) not to be scheduled again.
An example
This is best illustrated with a simplified example:
from tkinter import *
import time
class TestWhile:
def __init__(self):
self.window = Tk()
shape = Canvas(width=200, height=0).grid(column=0, row=0)
self.showtext = Label(text="Wait and see...")
self.showtext.grid(column=0, row=1)
fakebutton = Button(
text="Useless button"
)
fakebutton.grid(column=0, row=2)
# initiate fake while
self.window.after(0, self.fakewhile)
self.cycles = 0
self.window.minsize(width=200, height=50)
self.window.title("Test 123(4)")
self.window.mainloop()
def fakewhile(self):
# You can schedule anything in here
if self.cycles == 5:
self.showtext.configure(text="Five seconds passed")
elif self.cycles == 10:
self.showtext.configure(text="Ten seconds passed...")
elif self.cycles == 15:
self.showtext.configure(text="I quit...")
"""
If the fake while loop should only run a limited number of times,
add a counter
"""
self.cycles = self.cycles+1
"""
Since we do not use while, break will not work, but simply
"routing" the loop to not being scheduled is equivalent to "break":
"""
if self.cycles <= 15:
self.window.after(1000, self.fakewhile)
else:
# start over again
self.cycles = 0
self.window.after(1000, self.fakewhile)
# or: fakebreak, in that case, uncomment below and comment out the
# two lines above
# pass
TestWhile()
In the example above, we run a scheduled process for fifteen seconds. While the loop runs, several simple tasks are performed, in time, by the function fakewhile().
After these fivteen seconds, we can start over again or "break". Just uncomment the indicated section to see...
I'd like my Python program to run an algorithm for a given number of seconds and then to print the best result so far and to end.
What is the best way to do so?
I tried the following but it did not work(the program kept running after the printing):
def printBestResult(self):
print(self.bestResult)
sys.exit()
def findBestResult(self,time):
self.t = threading.Timer(time, self.printBestResult)
self.t.start()
while(1):
# find best result
Untested code, but something like this?
import time
threshold = 60
start = time.time()
best_run = threshold
while time.time()-start < threshold:
run_start = time.time()
doSomething()
run_time = time.time() - start
if run_time < best_run:
best_run = run_time
On unix, you can use signals -- This code times out after 1 second and counts how many times it iterates through the while loop in that time:
import signal
import sys
def handle_alarm(args):
print args.best_val
sys.exit()
class Foo(object):
pass
self=Foo() #some mutable object to mess with in the loop
self.best_val=0
signal.signal(signal.SIGALRM,lambda *args: handle_alarm(self))
signal.alarm(1) #timeout after 1 second
while True:
self.best_val+=1 # do something to mutate "self" here.
Or, you could easily have your alarm_handler raise an exception which you then catch outside the while loop, printing your best result.
If you want to do this with threads, a good way is to use an Event. Note that signal.alarm won't work in Windows, so I think threading is your best bet unless in that case.
import threading
import time
import random
class StochasticSearch(object):
def __init__(self):
self.halt_event = threading.Event()
def find_best_result(self, duration):
halt_thread = threading.Timer(duration, self.halt_event.set)
halt_thread.start()
best_result = 0
while not self.halt_event.is_set():
result = self.search()
best_result = result if result > best_result else best_result
time.sleep(0.5)
return best_result
def search(self):
val = random.randrange(0, 10000)
print 'searching for something; found {}'.format(val)
return val
print StochasticSearch().find_best_result(3)
You need an exit condition, or the program will run forever (or until it runs out of memory). Add one yourself.
I've read a lot of posts about using threads, subprocesses, etc.. A lot of it seems over complicated for what I'm trying to do...
All I want to do is stop executing a function after X amount of time has elapsed.
def big_loop(bob):
x = bob
start = time.time()
while True:
print time.time()-start
This function is an endless loop that never throws any errors or exceptions, period.
I"m not sure the difference between "commands, shells, subprocesses, threads, etc.." and this function, which is why I'm having trouble manipulating subprocesses.
I found this code here, and tried it but as you can see it keeps printing after 10 seconds have elapsed:
import time
import threading
import subprocess as sub
import time
class RunCmd(threading.Thread):
def __init__(self, cmd, timeout):
threading.Thread.__init__(self)
self.cmd = cmd
self.timeout = timeout
def run(self):
self.p = sub.Popen(self.cmd)
self.p.wait()
def Run(self):
self.start()
self.join(self.timeout)
if self.is_alive():
self.p.terminate()
self.join()
def big_loop(bob):
x = bob
start = time.time()
while True:
print time.time()-start
RunCmd(big_loop('jimijojo'), 10).Run() #supposed to quit after 10 seconds, but doesn't
x = raw_input('DONEEEEEEEEEEEE')
What's a simple way this function can be killed. As you can see in my attempt above, it doesn't terminate after 20 seconds and just keeps on going...
***OH also, I've read about using signal, but I"m on windows so I can't use the alarm feature.. (python 2.7)
**assume the "infinitely running function" can't be manipulated or changed to be non-infinite, if I could change the function, well I'd just change it to be non infinite wouldn't I?
Here are some similar questions, which I haven't able to port over their code to work with my simple function:
Perhaps you can?
Python: kill or terminate subprocess when timeout
signal.alarm replacement in Windows [Python]
Ok I tried an answer I received, it works.. but how can I use it if I remove the if __name__ == "__main__": statement? When I remove this statement, the loop never ends as it did before..
import multiprocessing
import Queue
import time
def infinite_loop_function(bob):
var = bob
start = time.time()
while True:
time.sleep(1)
print time.time()-start
print 'this statement will never print'
def wrapper(queue, bob):
result = infinite_loop_function(bob)
queue.put(result)
queue.close()
#if __name__ == "__main__":
queue = multiprocessing.Queue(1) # Maximum size is 1
proc = multiprocessing.Process(target=wrapper, args=(queue, 'var'))
proc.start()
# Wait for TIMEOUT seconds
try:
timeout = 10
result = queue.get(True, timeout)
except Queue.Empty:
# Deal with lack of data somehow
result = None
finally:
proc.terminate()
print 'running other code, now that that infinite loop has been defeated!'
print 'bla bla bla'
x = raw_input('done')
Use the building blocks in the multiprocessing module:
import multiprocessing
import Queue
TIMEOUT = 5
def big_loop(bob):
import time
time.sleep(4)
return bob*2
def wrapper(queue, bob):
result = big_loop(bob)
queue.put(result)
queue.close()
def run_loop_with_timeout():
bob = 21 # Whatever sensible value you need
queue = multiprocessing.Queue(1) # Maximum size is 1
proc = multiprocessing.Process(target=wrapper, args=(queue, bob))
proc.start()
# Wait for TIMEOUT seconds
try:
result = queue.get(True, TIMEOUT)
except Queue.Empty:
# Deal with lack of data somehow
result = None
finally:
proc.terminate()
# Process data here, not in try block above, otherwise your process keeps running
print result
if __name__ == "__main__":
run_loop_with_timeout()
You could also accomplish this with a Pipe/Connection pair, but I'm not familiar with their API. Change the sleep time or TIMEOUT to check the behaviour for either case.
There is no straightforward way to kill a function after a certain amount of time without running the function in a separate process. A better approach would probably be to rewrite the function so that it returns after a specified time:
import time
def big_loop(bob, timeout):
x = bob
start = time.time()
end = start + timeout
while time.time() < end:
print time.time() - start
# Do more stuff here as needed
Can't you just return from the loop?
start = time.time()
endt = start + 30
while True:
now = time.time()
if now > endt:
return
else:
print end - start
import os,signal,time
cpid = os.fork()
if cpid == 0:
while True:
# do stuff
else:
time.sleep(10)
os.kill(cpid, signal.SIGKILL)
You can also check in the loop of a thread for an event, which is more portable and flexible as it allows other reactions than brute killing. However, this approach fails if # do stuff can take time (or even wait forever on some event).