question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Call a function every n millisecond for an animation in an asynchronous/non-blocking way (event loop?)

See original GitHub issue

I display a visualization with plot.show(interactive=True) and when the user clicks on some button, I want to play an animation. However I couldn’t find the proper mechanism for doing so. I tried using Threads or the scheduler module and then I tried a simple for loop like this:

for i in range(100):
    some_function_that_moves_an_object()
    plot.show()
    # time.sleep(0.1) # Is this useful?

But this doesn’t work.

Is there a way to have an interactive view and to play an animation at the same time? I see in vedo examples in the tunelling effect demo that a simple for loop calls multiple times plot.show() but in my case there was an initial call to plot.show(interactive=True) so how do I quit this interactive mode to let the animation play, that is, if interactive and animation cannot happen at the same time?

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:8 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
nantillecommented, Mar 2, 2021

Ah, thanks for the comment. It helps to know it should be ExitCallback() Good idea to add this as an example to vedo.

1reaction
nantillecommented, Mar 2, 2021

I solved it. There’s some unwanted code in vedo like the “allowInteraction” part in settings. As I thought, vtk has an accessible internal event loop. I thought listening to TimerEvent was enough but you have to trigger a repeating timer (so the user actually controls it).

In a class, I use the following to have a button that plays an animation:

import vtk
import vedo

class Viewer:

    def __init__():
        # your code
        self.plot = None
        self.timer_id = None
        self.button = None
        self.animation_playing = False

    def initialize_app():
        # Not sure what this does but make sure it's set to False (see below)
        # It doesn't keep you from interacting with the plot
        settings.allowInteraction = False
        self.plot = Plotter()
        self.plot.interactor.AddObserver('TimerEvent', self.handle_timer)
        self.button = self.plot.addButton(self.play_time_series, states=["Play", "Pause"])
        # Later on you will call self.plot.show() and interactive()

    def play_time_series(self, widget=None, event=None):
        self.button.switch()
        self.animation_playing = not self.animation_playing

        if self.timer_id is not None:
            self.plot.interactor.DestroyTimer(self.timer_id)
        if self.animation_playing:
            self.timer_id = self.plot.interactor.CreateRepeatingTimer(100)

    def handle_timer(self, iren, event):
        # animate your stuff here
        print('TimerEvent')

viewer = Viewer()
viewer.plot.show()
interactive()

If vedo.settings.allowInteraction = True, then the code below kicks in (from vedo/plotter.py) and kills the app (!) if a TimerEvent is caught.

        if settings.allowInteraction:
            self._update_observer = None
            self._update_win_clock = time.time()

            def win_interact(iren, event):  # flushing renderer events
                if event == "TimerEvent":
                    iren.TerminateApp()

            self.interactor.AddObserver("TimerEvent", win_interact)

            def _allowInteraction():
                timenow = time.time()
                if timenow - self._update_win_clock > 0.1:
                    self._update_win_clock = timenow
                    self._update_observer = self.interactor.CreateRepeatingTimer(1)

                    if hasattr(self, 'interactor') and self.interactor:
                        self.interactor.Start()

                    if hasattr(self, 'interactor') and self.interactor:
                        # twice otherwise it crashes when pressing Esc (??)
                        self.interactor.DestroyTimer(self._update_observer)
Read more comments on GitHub >

github_iconTop Results From Across the Web

Event loops, building smooth UIs, and handling high server ...
Building smooth UIs. Most of the code an iOS/macOS developer writes is called from an event loop iteration. All gesture recognisers, sensors ...
Read more >
javascript - Call a function each 100 milliseconds using ...
I want to execute a function every 100 milliseconds. it can be done using an interval like this: setInterval(function(){ console. log('done') } ...
Read more >
️ JavaScript Visualized: Event Loop
Time for the event loop to do its only task: connecting the queue with the call stack! If the call stack is empty,...
Read more >
libuv(1) - Arch manual pages
One can run multiple event loops as long as each runs in a different thread. ... This function is just a convenient way...
Read more >
Calling function every n second javascript game
You can pass an optional time parameter to your window.requestAnimationFrame() callback ( loop() in this case), and it will pass in the ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found