I'm pretty new to Python, and programming in general and I'm creating a virtual pet style game for my little sister.
Is it possible to run 2 while loops parallel to each other in python?
eg:
while 1:
input_event_1 = gui.buttonbox(
msg = 'Hello, what would you like to do with your Potato Head?',
title = 'Main Screen',
choices = ('Check Stats', 'Feed', 'Exercise', 'Teach', 'Play', 'Go to Doctor', 'Sleep', 'Change Favourite Thing', 'Get New Toy', 'Quit'))
if input_event_1 == 'Check Stats':
myPotatoHead.check_p_h_stats()
elif input_event_1 == 'Feed':
myPotatoHead.feed_potato_head()
elif input_event_1 == 'Exercise':
myPotatoHead.exercise_potato_head()
elif input_event_1 == 'Teach':
myPotatoHead.teach_potato_head(myPotatoHead)
elif input_event_1 == 'Play':
myPotatoHead.play_with_toy()
elif input_event_1 == 'Sleep':
myPotatoHead.put_p_h_asleep()
elif input_event_1 == 'Go to Doctor':
myPotatoHead.doctor_check_up()
elif input_event_1 == 'Change Favourite Thing':
myPotatoHead.change_favourite_thing()
elif input_event_1 == 'Quit':
input_quit = gui.ynbox(
msg = 'Are you sure you want to quit?',
title = 'Confirm quit',
choices = ('Quit', 'Cancel'))
if input_quit == 1:
sys.exit(0)
while 1:
time.sleep(20)
myPotatoHead.hunger = str(float(myPotatoHead.hunger) + 1.0)
myPotatoHead.happiness = str(float(myPotatoHead.happiness) - 1.0)
myPotatoHead.tiredness = str(float(myPotatoHead.tiredness) + 1.0)
If not, is there some way that I can turn this into one loop?
I want the stuff in the second loop to happen every 20 seconds, but the stuff in the first loop to by constantly happening.
Thanks for any help
Have a look at Threading.Timer.
There is a code recipe here to schedule a function to run every 5 seconds.
import thread
import threading
class Operation(threading._Timer):
def __init__(self, *args, **kwargs):
threading._Timer.__init__(self, *args, **kwargs)
self.setDaemon(True)
def run(self):
while True:
self.finished.clear()
self.finished.wait(self.interval)
if not self.finished.isSet():
self.function(*self.args, **self.kwargs)
else:
return
self.finished.set()
class Manager(object):
ops = []
def add_operation(self, operation, interval, args=[], kwargs={}):
op = Operation(interval, operation, args, kwargs)
self.ops.append(op)
thread.start_new_thread(op.run, ())
def stop(self):
for op in self.ops:
op.cancel()
self._event.set()
if __name__ == '__main__':
# Print "Hello World!" every 5 seconds
import time
def hello():
print "Hello World!"
timer = Manager()
timer.add_operation(hello, 5)
while True:
time.sleep(.1)
The only way to "have two while loops in parallel" would be to place them on different threads, but then you need to tackle the synchronization and coordination problems between them since they're reaching into the same object.
I suggest you instead put a time check in the first (and single) loop and perform the increases that you now have in the second loop proportionately to that time-check; not quite satisfactory since the buttonbox call might take an indefinite amount of time to return, but way simpler to arrange, esp. for a beginner, than proper threading coordination.
Once you do have the basic logic in place and working, then you can consider threads again (with a periodic timer for what you'd like in the 2nd loop in one thread, the blocking buttonbox call in the main thread [[I think in easygui it has to be]], both feeding events into a Queue.Queue [[intrinsically thread-safe]] with another thread getting them and operating accordingly, i.e. most of what you now have in the 1st loop). But that's quite an advanced architectural problem, which is why I recommend you don't try to deal w/it right now!-)
put one of them into a function, the threading.Thread class supports a target attribute:
import threading
threading.Thread(target=yourFunc).start()
Will start yourFunc() running in the background.
You should use State Machines for this (see the Apress pygame book - downloads here: http://apress.com/book/downloadfile/3765 ), see chapter 7.
A simplified state machine:
def do_play(pet, time_passed):
pet.happiness += time_pass*4.0
def do_feed(pet, time_passed):
pet.hunger -= time_passed*4.0
def do_sleep(pet, time_passed):
pet.tiredness += time_passed*4.0
if pet.tiredness <= 0:
return 'Waiting'
def do_waiting(pet, time_passed):
pass
def do_howl(pet, time_passed):
print 'Hoooowl'
def do_beg(pet, time_passed):
print "I'm bored!"
def do_dead(pet, time_passed):
print '...'
STATE_TO_FUNC = dict(Waiting=do_waiting,
Sleeping=do_sleep,
Feeding=do_feed,
Playing=do_play,
Howling=do_howl,
Begging=do_beg,
Dead=do_dead
)
class Pet:
def __init__(self):
self.state = 'Waiting'
self.hunger = 1.0
self.tiredness = 1.0
self.happiness = 1.0
def process(self, time_passed):
self.hunger +=1*time_passed
self.tiredness +=1*time_passed
self.happiness -= 1*time_passed
func = STATE_TO_FUNC[self.state]
new_state = func(self, time_passed)
if new_state is not None:
self.set_state(new_state)
if self.hunger >10:
self.set_state('Dead')
elif self.hunger > 5 and not (self.state == 'Feeding'):
self.set_state('Howling')
elif self.tiredness > 5:
self.set_state('Sleeping')
elif self.happiness < 0 and not (self.state == 'Playing'):
self.set_state('Begging')
def set_state(self,state):
if not self.state == 'Dead':
self.state = state
from msvcrt import getch
import time
pet = Pet()
while True:
time0 = time.time()
cmd = getch() # what command?
pet.process(time.time()-time0)
if cmd == 'a':
pet.set_state('Feeding')
if cmd == 's':
pet.set_state('Sleeping')
if cmd == 'd':
pet.set_state('Playing')
Essentially to have processing to happen in parallel you have several solutions
1- Separate processes (ie: programs) running independently that speak to one another through a specific protocol (eg: Sockets)
2- Or you can have the one process spawn off multiple threads
3- Build an event queue internally and process them one by one
That is the general picture.
As for the specific answer to your question, you said "the stuff in the first loop to b[e] constantly happening". The reality is you never want this to happen all the time, because all that will do is use up 100% of the CPU and nothing else will ever get done
The simplest solution is probably number 3.
The way I would implement it is in my main loop have a thread that goes through an event queue and sets a timer for each event. Once all the timers have been sent the main loop then goes to sleep.
When a timer times out, an other function will then run the corresponding function for the event that triggered that timer.
In your case, you have two events. One for displaying the selection menu (first loop) and the second for changing myPotatoHead. The timer associated with the first one, I would set to 0.5sec, making it larger reduces CPU usage but slows down responsivness, increasing it usses up more CPU, for the second event I would set a 20 second timer.
Ofcourse when the timer expires, you would not do while 1 but you will just go through your while loop body once (ie get rid of while).
There is also a package called SimPy that you could also look at. The threading and multiprocessing libraries may also help.
i think they cannot be coupled in to one while loop.
maybe you need to check the threading or multiprocessing library.
Related
I Have a counting function that I would like to start and restart while getting the live variables to use in another function my problem is while using threading it seams like even global variables don't seem to work to pass variables around. What I want the code to do is have a counter be triggered as needed or maybe free running I'm not sure yet. To be able to reset the counter and get the value of the counter.
Right now the counter will start and run fine but the print_stuff function keeps telling me that there is no attribute countval.
The count thread gets started at startup but I don't necessarily want it to start up immediately, I would like to trigger it as needed but I cant put count_thread.start() twice or it will through a error so I'm calling the thread at startup and then calling the function again to restart it as needed. Maybe there is a more elegant way of doing that.?
import threading
import time
def count():
global countval
for countval in range(3):
print('looping')
time.sleep(1)
def print_stuff():
global countval
e = input("press enter to start")
count()
while True:
if countval == 3:
print("time out")
count_thread = threading.Thread(target=count)
print_thread = threading.Thread(target=print_stuff)
print_thread.start()
count_thread.start()
print_stuff is getting to the if statement before the count function is able to create the variable. Just do them in the opposite order. Either that, or create a global countval = 0 to start things off.
To solve the no attribute problem you can use Queue,
and if you want to stop your counting thread you can set a global variable or you can pass a function (using lambda or inner function or ...) to do that.
Here is one way to do that:
import threading
import time
from queue import Queue
from typing import Callable
def count(q, stop_counting):
# type: (Queue, Callable[[], bool]) -> None
for countval in range(3):
if stop_counting():
print('stopped')
break
print(f'looping {countval}')
q.put(countval)
time.sleep(1)
def print_stuff(q):
# type: (Queue) -> None
while True:
countval = q.get()
print(f'countval gotten: {countval}')
if countval == 3:
print("time out")
def main():
flag_stop_counting = False
q = Queue()
def stop_counting():
return flag_stop_counting
count_thread = threading.Thread(target=count, args=(q, stop_counting,))
print_thread = threading.Thread(target=print_stuff, args=(q,))
print_thread.start()
count_thread.start()
time.sleep(1.25)
flag_stop_counting = True
if __name__ == '__main__':
main()
In this code:
counter checks if it should stop counting or not
counter puts the value that it made to q
print_stuff get the value from q (Note: he waits until counter puts his value in q)
To check that program works:
after 1.25 seconds we change the value of flag_stop_counting
But if you want your counter to only have a for, i guess it's better to don't make it as a thread and run it whenever you want.
Hope it was helpful.
I'm using empty while loops a lot, for example:
I have a thread running in the background that will change a value called "a" in 5 seconds. however, I'm using a different function at the same time, and I want to let the second function know that the value has changed, so what I always did was:
import threading, time
class example:
def __init__(self):
self.a = 0
def valchange(self):
time.sleep(5)
self.a += 1
time.sleep(1)
print("im changing the a value to " + str(self.a))
print("those print commands needs to run after notifier stopped his while and started printing")
def notifier(exam :example, num :int):
while(exam.a != num):
pass
print("it changed to " + str(num))
exa = example()
i = 1
while(i <= 16):
temp= threading.Thread(target=notifier, args=(exa, i, ))
temp.start()
i += 3
i = 1
while(i <= 16):
exa.valchange()
i += 1
It's important to mention, that example could not use wait and set to an event, because there is no indication to when you need to run set, and how much threads are running in the background, and even what numbers will have a thread waiting for them to change.
And also you can't use join because changing 'a' is not a sign to print, only the condition is the sign.
Async and select can't help me as well because of the last reason.
Is there any way to create something, that will stop the program fromrunning until the condition will become true? you can provide your solution with any programming language you want, but mainly I'm using python 3.
EDIT: please remember that I need it to work with every condition. And my code example- is only an example, so if something works there, it doesn't necessarily will work with a different condition.
Thank you very much in advance :)
Idea:
wait(a == 5) // will do nothing until a == 5
You need to use select or epoll system calls if you're waiting for some system operation to finish. In case you're waiting for a certain IO event, then you can use asyncio (provided your Python version > 3.3), otherwise you could consider twisted.
If you're doing some CPU bound operations you need to consider multiple processes or threads, only then you can do any such monitoring effectively. Having a while loop running infinitely without any interruption is a disaster waiting to happen.
If your thread only changes a's value once, at the end of its life, then you can use .join() to wait for the thread to terminate.
import threading
import time
class example:
def __init__(self):
self.a = 0
self.temp = threading.Thread(target=self.valchange)
self.temp.start()
self.notifier()
def valchange(self):
time.sleep(5)
self.a = 1
def notifier(self):
self.temp.join()
print("the value of a has changed")
example()
If the thread might change a's value at any point in its lifetime, then you can use one of the threading module's more generalized control flow objects to coordinate execution. For instance, the Event object.
import threading
import time
class example:
def __init__(self):
self.a = 0
self.event = threading.Event()
temp = threading.Thread(target=self.valchange)
temp.start()
self.notifier()
def valchange(self):
time.sleep(5)
self.a = 1
self.event.set()
def notifier(self):
self.event.wait()
print("the value of a has changed")
example()
One drawback to this Event approach is that the thread target has to explicitly call set() whenever it changes the value of a, which can be irritating if you change a several times in your code. You could automate this away using a property:
import threading
import time
class example(object):
def __init__(self):
self._a = 0
self._a_event = threading.Event()
temp = threading.Thread(target=self.valchange)
temp.start()
self.notifier()
#property
def a(self):
return self._a
#a.setter
def a(self, value):
self._a = value
self._a_event.set()
def valchange(self):
time.sleep(5)
self.a = 1
def notifier(self):
self._a_event.wait()
print("the value of a has changed")
example()
Now valchange doesn't have to do anything special after setting a's value.
What you are describing is a spin lock, and might be fine, depending on your use case.
The alternative approach is to have the code you are waiting on call you back when it reaches a certain condition. This would require an async framework such as https://docs.python.org/3/library/asyncio-task.html
There are some nice simple examples in those docs so I won't insult your intelligence by pasting them here.
I have a water pump with pressure sensors. One on the input (low) and one on the output (high). My problem is my low pressure sensor. Sometimes the low pressure is just at the cut-off point causing the motor to start and stop quickly - this is not desirable. The system is running on a home-made PLS.
I'm a beginner at programming, 3 months, but the system is working for the most part. I need help on creating a timer between low pressure alarm events. I am thinking that the system can have 3 events within 30 seconds, but if any one event occurs in less than 5 seconds the system should shut down.
So if less than 5 seconds between the first event and second event the motor shuts down for good. The same goes for for second to third and third to fourth event. On the fourth event if less than 30 seconds occurs between first event and the fourth, the system also shuts down for good. Keep in mind that this is a part of a much larger loop. Here is the code I was able to create:
def Systemofftimer():
EventCounter = (0)
OneTimeLoopVarable = (0)
While True
if (is_low_pressure_alarm_on() and (OneTimeLoopVarable ==0)):
Timer = time.time()
EventCounter = EventCounter + (1)
OneTimeLoopVarable = 1
if EventCounter == (2) and (time.time() - Timer >= (10))
EventCounter = EventCounter + (1)
stop_motor()
if EventCounter == (3) and (time.time() - Timer >= (20))
EventCounter = EventCounter + (1)
stop_motor()
if EventCounter == (4) and (time.time() - Timer >= (30))
EventCounter = EventCounter + (1)
stop_motor()
else:
start_motor()
I would actually use a different approach for this: simply make your threshold for turning on larger than your threshold for turning of. For example:
That way you don't need to deal with the timing of it and can still eliminate the jittery nature around your state transition. You can also tune this to account for how noisy your sensors are.
Edit:
Below I've mocked up the piece of your system you're asking about. It's probably way more than you were initially looking for, but I wanted to test make sure it all worked properly before I posted so you're welcome to use it in whole or in part. As for the timer you asked about, it's based on Hans Then's post from this thread. To trigger the alarm, you just call TriggerAlarm() on the PumpSystem class. It will log that an alarm was triggered and then check the two conditions you mentioned in your question (5 sec and 30 sec errors). Each element of self.alarms contains the number of alarms that happened in a particular second, and each second the timer triggers to remove the oldest second from the list and create a fresh one. If you run the program, you can trigger alarms by pressing spacebar and see how the list is updated. The MockUp class is just meant to test and demonstrate how this works. I imagine you'll remove it if you decide to plug some portion of this into what you're working on. Anyway, here's the code.
from threading import Timer, Thread, Event
class PumpSystem():
def __init__(self):
self.alarms = [0 for x in range(30)]
self.Start()
return None
def SetUpdateFlag(self, flag):
self.update_flag = flag
return True
def Start(self):
self.stop_flag = Event()
self.thread = ClockTimer(self.OnTimerExpired, self.stop_flag)
self.thread.start()
return True
def Stop(self):
self.stop_flag.set()
return True
def TriggerAlarmEvent(self):
self.alarms[-1] += 1
self.CheckConditions()
self.update_flag.set()
return True
def OnTimerExpired(self):
self.UpdateRunningAverage()
def CheckConditions(self):
# Check if another error has triggered in the past 5 seconds
if sum(self.alarms[-5:]) > 1:
print('5 second error')
# Check if more than 3 errors have triggered in the past 30 seconds
if sum(self.alarms) > 3:
print('30 second error')
return True
def UpdateRunningAverage(self):
self.alarms.append(0)
self.alarms.pop(0)
self.update_flag.set()
return True
class ClockTimer(Thread):
def __init__(self, callback, event):
Thread.__init__(self)
self.callback = callback
self.stopped = event
return None
def SetInterval(self, time_in_seconds):
self.delay_period = time_in_seconds
return True
def run(self):
while not self.stopped.wait(1.0):
self.callback()
return True
## START MOCKUP CODE ##
import tkinter as tk
class MockUp():
def __init__(self):
self.pump_system = PumpSystem()
self.update_flag = Event()
self.pump_system.SetUpdateFlag(self.update_flag)
self.StartSensor()
return None
def StartSensor(self):
self.root = tk.Tk()
self.root.protocol("WM_DELETE_WINDOW", self.Exit)
self.alarms = tk.StringVar()
w = tk.Label(self.root, textvariable=self.alarms, width=100, height=15)
self.alarms.set(self.pump_system.alarms)
w.pack()
self.root.after('idle', self.ManageUpdate)
self.root.bind_all('<Key>', self.ManageKeypress)
self.root.mainloop()
return True
def ManageUpdate(self):
if self.update_flag.isSet():
self.alarms.set(self.pump_system.alarms)
self.update_flag.clear()
self.root.after(1, self.ManageUpdate)
return True
def ManageKeypress(self, event):
if event.keysym == 'Escape':
self.Exit()
if event.keysym == 'space':
self.pump_system.TriggerAlarmEvent()
return True
def Exit(self):
self.pump_system.Stop()
self.root.destroy()
mockup = MockUp()
This may look like a lot, but half is the mockup class that you can probably just ignore. Let me know if there's anything that you're confused about and I'd be happy to explain what's happening.
I want to have an app where if I click a button I add X amount of time to my running countdown timer.
I'm guessing I have to use threads for this but am not sure how to implement it..
Here is the code I have so far:
def countdown_controller(add_time):
end_it = False
def timer(time_this):
start = time.time()
lastprinted = 0
finish = start + time_this
while time.time() < finish:
now = int(time.time())
if now != lastprinted:
time_left = int(finish - now)
print time_left
lastprinted = now
if end_it == True:
now = finish
time.sleep(0.1)
# Check if the counter is running otherwise just add time.
try:
time_left
except NameError:
timer(add_time)
else:
if time_left == 0:
timer(add_time)
else:
add_this = time_left
end_it = True
while now != finish:
time.sleep(0.1)
timer(add_time + add_this)
Obviously this will not work, because every time I call countdown_controller(15) fx, it will start counting down for 15 seconds and if I click my button nothing happens until the timer is ended.
Help would be greatly appreciated.
I would say that there is a flaw in the design of the code, because your screen output blocks down the entire program doing nothing (time.sleep(0.1)).
Typically what you want to to do in these cases is having a main loop in your program that cycles through the various operations that make your program run. This guarantees a sensible distribution of system resources between the various tasks.
In your specific case, what you would like to have in your main loop is:
Check user input (has extra time been added?)
Update output of the countdown
Example implementation:
import time
import curses
# The timer class
class Timer():
def __init__(self):
self.target = time.time() + 5
def add_five(self):
self.target += 5
def get_left(self):
return int(self.target-time.time())
# The main program
t = Timer()
stdscr = curses.initscr()
stdscr.nodelay(True)
curses.noecho()
# This is the main loop done in curses, but you can implement it with
# a GUI toolkit or any other method you wish.
while True:
left = t.get_left()
if left <= 0:
break
stdscr.addstr(0, 0, 'Seconds left: %s ' % str(left).zfill(3))
c = stdscr.getch()
if c == ord('x') :
t.add_five()
# Final operations start here
stdscr.keypad(0)
curses.echo()
curses.endwin()
print '\nTime is up!\n'
The above program will increase the counter of 5 seconds if you press the x key (lowercase). Most of the code is boilerplate to use the curses module, but of course if you use PyGTK, PySide or any other graphical toolkit, it will be different.
EDIT: As a rule of thumb, in python you want to avoid threading as much as you can, both because it often (but not always) slows down programs (see "Global Interpreter Lock") and because it makes software harder to debug/maintain.
HTH!
I would probably have a Timer object with a finish attribute that I could simply add an int to. Have that timer running in another thread that you can then query for the current time remaining from your GUI.
class Timer(object):
def __init__(self, length):
self.finish = time.time() + length
def get_time(self):
return time.time() >= self.finish
I need to wait in a script until a certain number of conditions become true?
I know I can roll my own eventing using condition variables and friends, but I don't want to go through all the trouble of implementing it, since some object property changes come from external thread in a wrapped C++ library (Boost.Python), so I can't just hijack __setattr__ in a class and put a condition variable there, which leaves me with either trying to create and signal a Python condition variable from C++, or wrap a native one and wait on it in Python, both of which sound fiddly, needlessly complicated and boring.
Is there an easier way to do it, barring continuous polling of the condition?
Ideally it would be along the lines of
res = wait_until(lambda: some_predicate, timeout)
if (not res):
print 'timed out'
Unfortunately the only possibility to meet your constraints is to periodically poll, e.g....:
import time
def wait_until(somepredicate, timeout, period=0.25, *args, **kwargs):
mustend = time.time() + timeout
while time.time() < mustend:
if somepredicate(*args, **kwargs): return True
time.sleep(period)
return False
or the like. This can be optimized in several ways if somepredicate can be decomposed (e.g. if it's known to be an and of several clauses, especially if some of the clauses are in turn subject to optimization by being detectable via threading.Events or whatever, etc, etc), but in the general terms you ask for, this inefficient approach is the only way out.
Another nice package is waiting - https://pypi.org/project/waiting/
install:
pip install waiting
Usage:
You pass a function that will be called every time as a condition, a timeout, and (this is useful) you can pass a description for the waiting, which will be displayed if you get TimeoutError.
using function:
from waiting import wait
def is_something_ready(something):
if something.ready():
return True
return False
# wait for something to be ready
something = # whatever
wait(lambda: is_something_ready(something), timeout_seconds=120, waiting_for="something to be ready")
# this code will only execute after "something" is ready
print("Done")
Note: the function must return a boolean - True when the wait is over, False otherwise
Here is another solution. The goal was to make threads to wait on each other before doing some work in a very precise order. The work can take unknown amount of time. Constant polling is not good for two reasons: it eats CPU time and action does not start immediately after condition is met.
class Waiter():
def __init__(self, init_value):
self.var = init_value
self.var_mutex = threading.Lock()
self.var_event = threading.Event()
def WaitUntil(self, v):
while True:
self.var_mutex.acquire()
if self.var == v:
self.var_mutex.release()
return # Done waiting
self.var_mutex.release()
self.var_event.wait(1) # Wait 1 sec
def Set(self, v):
self.var_mutex.acquire()
self.var = v
self.var_mutex.release()
self.var_event.set() # In case someone is waiting
self.var_event.clear()
And the way to test it
class TestWaiter():
def __init__(self):
self.waiter = Waiter(0)
threading.Thread(name='Thread0', target=self.Thread0).start()
threading.Thread(name='Thread1', target=self.Thread1).start()
threading.Thread(name='Thread2', target=self.Thread2).start()
def Thread0(self):
while True:
self.waiter.WaitUntil(0)
# Do some work
time.sleep(np.random.rand()*2)
self.waiter.Set(1)
def Thread1(self):
while True:
self.waiter.WaitUntil(1)
# Do some work
time.sleep(np.random.rand())
self.waiter.Set(2)
def Thread2(self):
while True:
self.waiter.WaitUntil(2)
# Do some work
time.sleep(np.random.rand()/10)
self.waiter.Set(0)
Waiter for multiprocessing:
import multiprocessing as mp
import ctypes
class WaiterMP():
def __init__(self, init_value, stop_value=-1):
self.var = mp.Value(ctypes.c_int, init_value)
self.stop_value = stop_value
self.event = mp.Event()
def Terminate(self):
self.Set(self.stop_value)
def Restart(self):
self.var.value = self.init_value
def WaitUntil(self, v):
while True:
if self.var.value == v or self.var.value == self.stop_value:
return
# Wait 1 sec and check aiagn (in case event was missed)
self.event.wait(1)
def Set(self, v):
exit = self.var.value == self.stop_value
if not exit: # Do not set var if threads are exiting
self.var.value = v
self.event.set() # In case someone is waiting
self.event.clear()
Please comment if this is still not the best solution.
You've basically answered your own question: no.
Since you're dealing with external libraries in boost.python, which may change objects at their leisure, you need to either have those routines call an event handler refresh, or work with a condition.
Here is the threading extention to Alex's solution:
import time
import threading
# based on https://stackoverflow.com/a/2785908/1056345
def wait_until(somepredicate, timeout, period=0.25, *args, **kwargs):
must_end = time.time() + timeout
while time.time() < must_end:
if somepredicate(*args, **kwargs):
return True
time.sleep(period)
return False
def wait_until_par(*args, **kwargs):
t = threading.Thread(target=wait_until, args=args, kwargs=kwargs)
t.start()
print ('wait_until_par exits, thread runs in background')
def test():
print('test')
wait_until_par(test, 5)
From the computational perspective there must be a check for all conditions somewhere, sometime. If you have two parts of code, one that generates conditions changes and the other one that should be executed when some are true, you can do the following:
Have the code that changes conditions in, say, main thread, and the code that should be launched when some conditions are true, in a worker thread.
from threading import Thread,Event
locker = Event()
def WhenSomeTrue(locker):
locker.clear() # To prevent looping, see manual, link below
locker.wait(2.0) # Suspend the thread until woken up, or 2s timeout is reached
if not locker.is_set(): # when is_set() false, means timeout was reached
print('TIMEOUT')
else:
#
# Code when some conditions are true
#
worker_thread = Thread(target=WhenSomeTrue, args=(locker,))
worker_thread.start()
cond1 = False
cond2 = False
cond3 = False
def evaluate():
true_conditions = 0
for i in range(1,4):
if globals()["cond"+str(i)]: #access a global condition variable one by one
true_conditions += 1 #increment at each true value
if true_conditions > 1:
locker.set() # Resume the worker thread executing the else branch
#Or just if true_conditions > 1: locker.set();
#true_conditions would need be incremented when 'True' is written to any of those variables
#
# some condition change code
#
evaluate()
For more information concerning this method, visit: https://docs.python.org/3/library/threading.html#event-objects
Proposed solution:
def wait_until(delegate, timeout: int):
end = time.time() + timeout
while time.time() < end:
if delegate():
return True
else:
time.sleep(0.1)
return False
Usage:
wait_until(lambda: True, 2)
I once used this in my code:
while not condition:
pass
Hope this helps
In 2022 now you could use https://trio-util.readthedocs.io/en/latest/#trio_util.AsyncValue
I think this comes closest to what you want in its "smoothest" form
This worked for me
direction = ''
t = 0
while direction == '' and t <= 1:
sleep(0.1)
t += 0.1
This is for waiting for a signal while making sure time limit of 1 second
here's how:
import time
i = false
while i == false:
if (condition):
i = true
break
Here's my Code I used during one of my Projects :
import time
def no() :
if (Condition !!!) :
it got true
oh()
else:
time.sleep(1) /Don't remove or don't blame me if ur system gets ""DEAD""
no()
def oh() : /Ur main program
while True:
if(bla) :
.......
no()
else :
time.sleep(1)
oh()
oh()
Hope it Helps