Async process not switching a boolean trigger - python

I have the following steps I need to follow:
1 - I need to start a camera instance on OpenCV
2 - I need to send the camera's data to some outside source every 2 seconds, but the video feed obviously cannot stop in the meantime
So I made two main async functions: "flip_trigger", which switches a boolean variable every 2 seconds, and "camera_feed", which also consumes the same "send_image" trigger that s switched by "flip_trigger". The two must run at the same time.
send_image = False
async def flip_trigger():
global send_image
while True:
await asyncio.sleep(2)
send_image = not send_image
print("Awaiting image")
async def camera_feed():
global send_image
face_names = []
face_usernames = []
video_capture = cv2.VideoCapture(0)
while True:
if cv2.waitKey(1) & 0xFF == ord('q'):
break
if(send_image):
ret, frame = video_capture.read()
#(...) some other code
else:
ret, frame = video_capture.read()
cv2.imshow('Video', frame)
continue
#(...) some other code
ret, frame = video_capture.read()
cv2.imshow('Video', frame)
video_capture.release()
cv2.destroyAllWindows()
break
async def start_camera():
task1 = asyncio.create_task(flip_trigger())
task2 = asyncio.create_task(camera_feed())
await asyncio.wait({task1, task2}, return_when=asyncio.FIRST_COMPLETED)
asyncio.run(start_camera())
The problem is: while debugging the code on VSCode, it seemingly never gets past the "await asyncio.sleep(2)" line, and if I remove the "await" parameter, the code seems to get stuck inside the "flip_trigger" function.
How do I make these functions work at the same time, and make "camera_feed" capture the "send_image" boolean switches in real time?

When you call await asyncio tries to continue other tasks in the loop.
Imagine when await especially in combination with asyncio.sleep is called it pauses the execution and it hops to another await section region where it can continue.
There normal python code is executed sequentially until the next await is reached.
Your camera_feed has no await, that means it will continuously loop forever/until the break.
It won't go back to flip_trigger.
You can use asyncio.sleep(0) to enable a ping pong between both functions.

Related

Python Asyncio - running multiple infinite loops with different "pauses"

Im trying to figure out how to run multiple infinite loops with asyncio - each loop with it's own delays:
import asyncio
async def do_something(delay, message):
await asyncio.sleep(delay)
print(message)
def main():
loop = asyncio.get_event_loop()
loop.create_task(do_something(1, "delay equals 1"))
loop.create_task(do_something(3, "delay equals 3"))
loop.run_forever()
if __name__ == '__main__':
try:
main()
except Exception as f:
print('main error: ', f)
It returns:
delay equals 1
delay equals 3
and I would suspect it to return:
delay equals 1
delay equals 1
delay equals 1
delay equals 3
delay equals 1
delay equals 1
delay equals 3
(or similar)
How should I modify this simple routine?
SOLUTION
async def do_something(delay, message):
while True:
await asyncio.sleep(delay)
print(message)
There's no reason why a simple task would loop forever.
Depending on what you eventually want to do, you can add a while True: in the async functions, or have them schedule another task at the end.

How to make a certain function stop executing when a certain condition is met

I am designing a discord bot that loops certain text when a certain conditions are met, which then triggers this function to run.
async def inform(self,message):
while flag==1:
await message.channel.send("text")
await asyncio.sleep(5)
await message.channel.send("text2")
await asyncio.sleep(5)
await message.channel.send("text3")
await asyncio.sleep(5)
Now the problem is when the conditions are not met anymore the function completes the entire cycle before haulting. I want it to stop the moment the condition is not satisfied anymore.
I thought of adding an
if flag==0:
return
After every line but that is not the elegant solution I am looking for.
I don't want the entire code to stop running, but just this function.
I am a beginner to python and any insights are welcome :)
Thank You!
A while loop, before each iteration will look if the condition is True, if it is, it will continue, if not it will stop.
while flag == 1:
if 'some condition met':
flag = 0
# And the loop will stop
The loop will stop by itself, nothing more. Also it's better to use booleans.
Here's your code a bit improved
# Defining variables
flag = True
iteration = 0
while flag:
# Adding `1` each iteration
iteration += 1
# Sending the message
await message.channel.send(f'text{iteration}')
# Sleeping
await asyncio.sleep(5)
If I got it right, You want to check if a condition is met. Here is an infinite loop until some criteria is met. If this is not what you wanted please explain it again.
import random
msgs = ['msg1','msg2','msg3']
condition_met = False
while True:
if condition_met:
break
# also return works here
else:
await message.channel.send(random.choise(msgs))
await asyncio.sleep(5)
if something == 'Condition met here':
condition_met = True
Each iteration of the loop should only send one message, to keep track of the order of messages sent, I used the variable i which is incremented every time a message is sent.
async def inform(self,message):
i = 0
while flag==1:
if i == 0:
await message.channel.send("text")
await asyncio.sleep(5)
elif i == 1:
await message.channel.send("text2")
await asyncio.sleep(5)
elif i == 2:
await message.channel.send("text3")
await asyncio.sleep(5)
i = (i + 1) % 3

How to set a background timer, and stop a function when it finishes while still waiting for reactions

So I'm making some minigame using discord.py, and this is what I got:
asyncio.create_task(self.stream_message_timer(ctx, correct, total), name=f"stream message timer {ctx.author.id}")
while bad_count > 0:
done, pending = await asyncio.wait([
self.client.wait_for('reaction_add', check=lambda reaction, user: str(reaction.emoji) in number_emojis and reaction.message.id == discord_message.id and user == ctx.author),
self.client.wait_for('message', check=lambda m: m.author == self.client.user and m.content == f"Times up!\n{correct}/{total} tasks successful.")
], return_when=asyncio.FIRST_COMPLETED)
try:
result = done.pop().result()
except Exception as e:
raise e
for future in done:
future.exception()
for future in pending:
future.cancel()
if type(result) == discord.Message:
return False
else:
reaction = result
# process the reaction, edit a message
await ctx.send(f"You deleted all the bad messages!\n{correct+1}/{total} tasks successful.")
for task in asyncio.all_tasks():
if task.get_name() == f"stream message timer {ctx.author.id}":
task.cancel()
break
return True
async def stream_message_timer(self, ctx, correct, total):
await asyncio.sleep(5)
await ctx.send(f"Times up!\n{correct}/{total} tasks successful.") # message linked to delete_chat, change there if this is changed
return False
Basically, I'm trying to make some kind of 5 second background timer as I wait for reactions at the same time.
No, I am not looking for timeout=5
The code that I have here works, but its very hacky. I'm either waiting for a reaction from the user, or just waiting for the bot to message "Times up".
Is there a cleaner way to do this? I would like to have the timer start right before the while loop, and forcefully stop anything inside the loop and make the function return False right when the timer finishes
Also, if the function does stop, I still want some way to cancel the timer, and that timer only.
I've been using this method for quite some time and it's making my code very unorganized. Thanks.
Here’s some sort of example that is independent from discord.py:
import asyncio
import random
async def main():
asyncio.create_task(timer(), name="some task name")
# simulates waiting for user input
await asyncio.sleep(random.uniform(2, 5))
return True
async def timer():
await asyncio.sleep(5)
# somehow make this return statement stop the main() function
return False
asyncio.run(main())
wait(return_when=FIRST_COMPLETED) is the correct way to do it, but you can simplify the invocation (and subsequent cancellation) by using the return value of create_task:
async def main():
timer_task = asyncio.create_task(timer())
user_input_task = asyncio.create_task(asyncio.sleep(random.uniform(2, 5)))
await asyncio.wait([timer_task, user_input_task], return_when=asyncio.FIRST_COMPLETED)
if not timer_task.done():
timer_task.cancel()
if user_input_task.done():
# we've finished the user input
result = await user_input_task
...
else:
# we've timed out
await timer_task # to propagate exceptions, if any
...
If this pattern repeats a lot in your code base, you can easily abstract it into a utility function.

Thread is not opening the target function in python

I want to open a function which shows the Loading VIDEO on the screen by cv2.imshow() method called.
First I want to demostrat the code and then the problem.
import cv2
import threading
def Load():
video = cv2.VideoCapture('Loading.mov')
if video.isOpened() == True:
cv2.namedWindow("The Video")
cv2.moveWindow("The Video", 500,200)
elif video.isOpened() == False:
print('No Data For Loading Video')
return 0
while video.isOpened():
_, frame = video.read()
if _ == True:
cv2.imshow("The Video",frame)
if cv2.waitKey(10) & 0xff == 27:
break
if _ == False :
break
cv2.destroyAllWindows()
video.release()
t = threading.Thread(target = Load)
t.start()
Now, Problem :
When I call the t.start() FOR THE FIRST TIME the thread gets started and shows the video properly.
After the loop breaks, if I try again to make a new t as a thread and .start() it again, it doesn't show anything at all ! Not errors, nothing !
I am using spyder to re run the codes.
And I want to re run the video whenever I needed .
Now, Where is the problem ?
I tried your code with an mp4-Video (http://techslides.com/demos/sample-videos/small.mp4) and it works. I converted said video to mov with https://video.online-convert.com/convert-to-mov and it still works...
Although I may have an educated guess:
you should try to do every call with a fresh instance of cv2.
I assume the problem could be, that the second call of the thread inherits the state of the first call (especially the internal state of cv2), since it's only a function an therefor the video is in state "already played" or something and doesn't show anything more.
so if you put everything in a class and call with a new instance of cv2 everytime Load() is called, it might work.
import cv2
import threading
class Video:
def play(self):
video = cv2.VideoCapture('small.mov')
if video.isOpened() == True:
cv2.namedWindow("The Video")
cv2.moveWindow("The Video", 500,200)
elif video.isOpened() == False:
print('No Data For Loading Video')
return 0
while video.isOpened():
_, frame = video.read()
if _ == True:
cv2.imshow("The Video",frame)
if cv2.waitKey(10) & 0xff == 27:
break
if _ == False :
break
cv2.destroyAllWindows()
video.release()
def Load():
v=Video()
v.play()
del v
t = threading.Thread(target = Load)
t.start()

Starting a new async function while the old one is still running

The all function creates a list of stocks who have no available shares. It then runs alert, a looping function, to alert me when the the stock becomes available. I need to run all these alert loops simultaneously but all waits for the first stock to become available before starting the next loop.
I've tried using threading to create a thread for each stock but I cannot await a Thread.start()
async def all(self, ctx):
stocks = requests.get(f'https://api.torn.com/torn/?
selections=stocks&key={api}').json()['stocks']
zero = []
acronymz = []
for items in stocks:
if stocks[items]['available_shares'] == 0:
zero.append(items)
acronymz.append(stocks[items]['acronym'])
await ctx.send(f'Zero: {zero}')
for acronyms in zero:
print(acronyms)
# Thread(target=alert, args=(ctx, acronyms)).start()
await alert(ctx, acronyms)
# await asyncio.sleep(0.5)
async def alert(ctx, items):
stocks = requests.get(f'https://api.torn.com/torn/?selections=stocks&key={api}').json()['stocks'][items]
if stocks['available_shares'] == 0:
await ctx.send(f'I am now watching {stocks["acronym"]}. I will let you know when there are shares available!')
while stocks['available_shares'] == 0:
stocks = requests.get(f'https://api.torn.com/torn/?selections=stocks&key={api}').json()['stocks'][items]
print(stocks)
await asyncio.sleep(5)
await ctx.send(f'There are {stocks["available_shares"]} in {stocks["acronym"]}')
stocks = https://pastebin.com/FhuR4d4R ["stocks"]
You can schedule tasks for the event loop without awaiting them immediately. Here's an example using asyncio.gather
await asyncio.gather(*(alert(ctx, acronyms) for acronyms in zero))

Categories

Resources