Python wait for animation to be completed - python

I have been using Blender's Python API to animate an object falling from a height. The Python program will start the animation and then pause the animation at frame 250. However, I would like the Python program to wait until this process has finished before continuing.
The snippet of code is below, where I want the final line of code to wait for the condition to be set in the pause_at_frame handler. One solution I experimented with, was to create a threading event. I would then start a thread calling pause_at_frame and set the threading event when the current frame was 250. The program would then wait for the threading event to be set. I could not manage to get this working without the program hanging. Any suggestions or solutions would be appreciated.
bpy.context.scene.frame_current = 0
bpy.ops.screen.animation_play()
def pause_at_frame(scene):
frame_current = bpy.context.scene.frame_current
if frame_current == 250:
bpy.ops.screen.animation_cancel()
bpy.context.scene.frame_current = 250
# condition here
for i in range(len(bpy.app.handlers.frame_change_pre)):
bpy.app.handlers.frame_change_pre.pop()
bpy.app.handlers.frame_change_pre.append(pause_at_frame)
# Wait here for condition
OTHER SOLUTION:
The other solution to this scenario is to bake the animation first.
bpy.ops.nla.bake(frame_start=1, frame_end=250, bake_types={'OBJECT'}) removing the need for the program above.

Related

How can I make python wait for a specified amount of time before moving to the next line?

This is my code currently.
def attack(self, surface, target):
attacking_rect = pygame.Rect(self.rect.centerx - (2*self.rect.width*self.flip), self.rect.y, 2*self.rect.width, self.rect.height)
if attacking_rect.colliderect(target.rect) and self.char_type == 'Player':
time.sleep(.9) #wait for animation to finish
target.health -= random.randint(7, 15)
target.hit = True
elif attacking_rect.colliderect(target.rect) and self.char_type == 'Skeleton':
time.sleep(1.4) #wait for animation to finish
target.health -= random.randint(6, 10)
target.hit = True
Doing my first pygame project this is another issue I ran into. The issue here is that time pauses my whole program whereas I only want to pause this function from moving to the next line.
You can consider multithreading,.
Why don't you open a new thread for every time you run attack(self, surface, target), and then pause just the thread instead of making your whole program hang?
Check out the official documentation for the threading library as well as this guide for more information on how to actually use threading.
It is correct behaviour and it will freeze your whole application because there is only one main thread that is handling your code. In this case the only way will be as Ryan suggested, will be using the multithreading

Stop thread from tkinter mainloop

Using the button I m executing a script on a separate thread, which after completing needs to come back to the screen where I started from.
If I mention that snippet at the end of my script which I m running on a separate thread giving me a TCL error since I m trying to call the Tkinter thread from another thread.
I want to know how to achieve this goal.
I have tried these 2 way..
1st:
#imports
#from the screen1 button I m sending arg to this function to run on seperate thread.
def screen2(arg):
#this window show the status of script progress bar, label status etc#
window.geometry("1215x770")
window.configure(bg="#FFFFFF")
#background canvas#
#progressbar#
#label#
def task():
if arg = foo:
#I cant use join here or else it will wait for the script to complete without showing screen2#
#I tried putting the mainloop after starting the thread, but as it means the tkinter is on hault it wont execute the after command.
t1 = threading.Thread(target=script, args(ag1, ag2,)).start()
window.mainloop()
window.resize(false, false)
response = messagebox.showinfo("Task", "Script Ran!")
if response:
screen1()
else:
t1 = threading.Thread(target=script, args(ag3, ag4,)).start()
window.mainloop()
window.resize(false, false)
response = messagebox.showinfo("Task", "Script Ran!")
if response:
screen1()
task()
def script(arg1, arg2):
#Doing something here#
I tried using the while loop to keep checking the status of the thread if it's alive then keep looping with the main loop in it if not go back to screen1. which didn't work.
What else option do I have? I need to fix this temporarily since I m only learning it but I have to present it. I m planning to shift it to another GUI whats the best temporary fix for this?
I don't want to write the whole code again because it's multiple screens.

What is the best way to make a player move at every interval in pygame?

Is there a library or a simple way to only loop something every 0.5 seconds without interrupting the rest of the program?
I have just started using pygame and have made a simple platformer and a Pong replica so far. I decided to try and make a Snake replica (I only currently have the head) and I need the snake to only move every 0.5 seconds while inputs can be registered at the 30 fps which I have the rest of the game running at. This is my current workaround:
while running: #this is tabbed back in my code
# keep loop running at the right speed
clock.tick(FPS)
# get time at each iteration
currentTime = str(time.time()).split(".")[0]
gameTime = int (currentTime) - int (startTime)
# this is used to check for something every 0.5 second (500 ms)
currentTimeMs = str(time.time()).split(".")[1]
# snake will move evry 0.5 second in a direction
if currentTimeMs[0] in ["5","0"] and moveDone == False:
moveDone = True
player1.move(direction)
elif currentTimeMs[0] not in ["5","0"]:
moveDone = False
There is more code within the while running: loop to get the direction and display the sprites but its not necessary for this. My current code works fine and will repeat the move function for my player1 every time that x in mm:ss:x is 0 or 5 (0.5 seconds apart) and will not repeat if it is that multiple times over a few frames.
This code needs to work within the running loop and not stop the program so time.sleep() doesn't work. I have also tried using the schedule library but it will not work as it cannot seem to allow the direction variable to change when passing it into the function.
My question therefore is; Is there a library or a shorter way to accomplish what I need?
Thanks in advance and I can message you the whole code if you need.
I suggest using pygames event mechanics and pygame.time.set_timer() (see here for docs).
You would do something like this:
pygame.time.set_timer(pygame.USEREVENT, 500)
and in the event loop look for the event type.
if event.type == pygame.USEREVENT:
If this is the only user defined event that you are using in your program you can just use USEREVENT.
When you detect the event the timer has expired and you move your snake or whatever. A new timer can be set for another 1/2 second. If you need more accuracy you can keep tabs on the time and set the timer for the right amount of time, but for you situation just setting it for 1/2 sec each time is okay.
If you need multiple timers going and need to tell them apart, you can create an event with an attribute that you can set to different values to track them. Something like this (though I have not run this particular code snippet, so there could be a typo):
my_event = pygame.event.Event(pygame.USEREVENT, {"tracker": something})
pygame.time.set_timer(my_event , 500)
You can store the moment of last move and then compare to actual time. To do it you can use perf_counter from time. Here is an example
last_move_time = perf_counter()
while running:
if perf_counter() - last_move_time > 0.5:
# move player
last_move_time = perf_counter()

wxPython hanging at random points in sequential code

I have a simple app that presents you with a button, when the button is pressed, some long running computations are done(not in a separate thread, there is no need for a separate thread as these computations are the sole reason of the app). These computations take place in a for loop and at every iteration, the for look has a callback to the main frame that updates it so it shows the current iteration(same with a small gauge underneath that fills up).
def _delay(self):
for i in range(15000):
self.update_stats(f'{i}', i)
def on_button_click(self, event):
self.init_completion_gauge(15000)
self._delay()
self.set_state_comparison_done()
def init_completion_gauge(self, maxval):
self.gauge.SetRange(maxval)
self.Layout()
self.Update()
def update_stats(self, current_iter, count):
self.label_current_iter.SetLabelText(
'\n'.join((f'Iter:', current_iter)))
self.gauge.SetValue(count)
self.label_percent_complete.SetLabelText(
f'{(count + 1) * 100 // self.gauge.GetRange()}%')
self.Layout()
self.Update()
When this runs, everything goes smoothly for a few seconds before the app shows Not responding in the title bar and hangs until it is complete. I don't mean is just hands and shows no error. Since the computations are done in the main loop it is intended for the app to block the main loop until they are done, but I doubt it is normal to randomly show not responding without being interacted with. After the computations are done the app behaves normally but it will stop responding at completely random points.
Any advice as to why this happens and what should be done to fix this?
GUI updates are queued.
Let's suppose your code looks like:
myLongTask
{
loop:
... do something
UpdateGUI()
end loop
}
Each time you call UpdateGUI() a message is sent to the OS, and the control returns inmediately and continues with the loop.
When has the OS the oportunity to handle that message? It's too busy running that loop.
You could use wx.Yield or better set a thread for the long task, that sends messages to the OS requesting update.
See more here

Optimize Matplotlib plot that dynamically updates and remains interactive

Ok, so as the question suggests, I am chasing the best method to create a plot that:
1) Is independent of the main program/script, therefore making changes to the plot will have no adverse (locking or other) effect on the main program/script
2) Can be interacted with via matplotlib's default GUI or other custom GUI
Now, what I have so far for the above criteria:
1) Use the multiprocessing module and its multiprocessing Queue to pass X,Y information to a separate process that appends to current X,Y data and refreshes the plot. This basically addresses criteria 1.
2) Ensuring matplotlib's interactive mode is turned on (ion()), an infinite while True: loop checks the aforementioned queue and if there is no X,Y information, allow's processing of GUI events via pyplot.pause(interval in seconds)
To simplify things, below is my code to accept incoming X,Y data and plot it in pseudocode. This code is run in a different process with a Queue feeding it X,Y data.
from matplotlib import pyplot
def run(self):
p = pyplot
while True:
if there's nothing in the Queue:
redraw and allow GUI events (p.pause(0.0001))
continue
else:
command = get X,Y info from Queue
if command equals X,Y data:
update plots x data (axes.set_ydata())
update plots y data (axes.set_ydata())
update axis data limits (axes.relim())
update axis view limits (axes.autoscale_view())
redraw and allow GUI events i.e. interaction for 0.0001 seconds (p.pause(0.0001))
elif if command equals the "exit loop" value:
break
turn of interactive mode (p.ioff())
keep plot open and block process until closed by user (p.show())
So can the above be optimized to increase interactivity with the figure GUI? potentially by allowing GUI events to be serviced independently of the plot being updated? This method becomes slower the more updates it has to do (i.e. if there are more plots on the same figure to update)
Any clarifications, just ask :)
I would pass the x,y-data via queue (as already planned), but would use the get-command in combination with the block-argument. That way, your drawing-function blocks until there are elements in the queue. If there are not, the function pauses and all other events in your application can be processed.
Something like that:
def process_data(inqueue):
#create your plot
for infile in iter(inqueue.get(block=True), "STOP"):
#update plot until inqueue.get returns "STOP"
#waits for elements in the queue enter code here
def main():
inqueue = Queue()
process = Process(target=process_data, args=(inqueue,)
#can be stopped by putting "STOP" via inqueue.put into the queue
process.start()
#put some data in the queue

Categories