wxPython -- drawing a line, relatively slowly - python

I want to draw a line, but not instantaneously as it usually appears with DrawLine(). I want it to be fast, but just slow enough that you can tell it's being drawn.
I've tried testing a for loop that draws a line a pixel at a time with a sleep of .05 seconds, but it doesn't seem to draw any of it until the for loop ends at which point it draws it all, instantly. I figured this had to do with it not getting drawing actions through the mainloop, so I used CallAfter on the drawline, that didn't work, either.
It seems like there should be a way to simply tell drawline to draw it over the course of x seconds or send it through an animation object in which you can specify # of frames, etc. Not really sure where to look. Any suggestions?

The issue is that time.sleep(...) will also block GUI as the for loop itself. wx.Yield should normally signal "GUI, I have halted the time-consuming process, do whatever you nned to do NOW". But by experience, this basically never works (I have not found out yet why).
So instead of using sleep you could use wx.CallLater (Link to wxPython Phoenix docs) which is like wx.CallAfter, but a delay can be specified. By calling it at the end of the inner for loop drawing the line part draw events should not pile up.

You will probably want to draw a series of lines. That would likely be the easiest. I would probably use a wx.Timer. It runs in it's own main loop. You can set up the timer to fire every so often, say every 0.5 second. When it fires, it can call your draw method, which will draw the next line. You can read more about timers here:
http://wiki.wxpython.org/Timer
http://www.blog.pythonlibrary.org/2009/08/25/wxpython-using-wx-timers/
http://www.wxpython.org/docs/api/wx.Timer-class.html

Related

Turtle graphics - screen updates with tracer not working as expected

I'm trying to put in scheduled refreshes for a pong game, so the screen updates after a time interval so the paddles (which I have segmented) move in sync on the screen. What is the functional difference between these two blocks of code? I was trying to get the first one to work but wasn't able to get it to do what I wanted but after some experimenting I landed at the second one which does:
pongscreen.tracer(0)
def balltiming():
pongscreen.update()
time.sleep(0.5)
balltiming()
balltiming()
pongscreen.listen()
pongscreen.mainloop()
This second one works, but functionally they seem pretty much the same in what they're doing. Why doesn't the first one work but this does?
pongscreen.tracer(0)
def balltiming():
pongscreen.update()
pongscreen.ontimer(balltiming, 300)
balltiming()
pongscreen.listen()
pongscreen.mainloop()
The first block is genuinely recursive, so it'll blow the call stack after ~1000 frames on a typical CPython implementation.
The second block isn't recursive; the initial balltiming function is completely cleared from the call stack after it sets an ontimer event. Turtle internally calls the next iteration of balltiming that has been registered as a callback to ontimer without unbounded stack growth.
If you want to use sleep manually, use a while loop rather than recursion. However, sleep blocks the thread and is less amenable to precise framerate control, so generally ontimer is used.
A consequence of the blocking recursive calls and sleep is that in the first code block, pongscreen.listen() and pongscreen.mainloop() won't be reached, while in the second version, both will be reached and the main thread will block at pongscreen.mainloop(); the typical flow for a turtle program.

Pyglet: How do I only call draw when needed?

I have two processes communicating over queues. One is altering the application state, the other one is running the pyglet event-loop. (just a side note, this is likely not a multiprocessing issue). I only want to draw when the state has changed or possibly with interpolated steps in between.
I'm confused about two things here.
I could write my own event-loop and check in there, if the state has changed. However even the minimal one from the docs is way less performant than the default app.run(). Why is that?
This is the loop from the documentation:
while True:
pyglet.clock.tick()
for window in pyglet.app.windows:
window.switch_to()
window.dispatch_events()
window.dispatch_event('on_draw')
window.flip()
If I use app.run() (for said performance reasons) and I check for state-changes inside of on_draw(), then I get weird stuttering images between frames. It looks like the window is changing back and forth between multiple frames while I'm not drawing anything. Why?
My on_draw() would look something like this:
def on_draw(self):
if self.check_if_state_changed():
self.draw_the_new_state()
I know that I could prevent the stuttering like this:
def on_draw(self):
if self.check_if_state_changed():
self.draw_the_new_state()
else:
self.draw_the_old_state_again()
But I don't want to do that, because re-drawing the old state requires time and performance is crucial for this application.
I did not find out why the minimal loop is slower than app.run(), but it seems like the documentation is contradicting itself. The recommended way to change the event-loop is actually to subclass app.EventLoop and override the idle() method.
The stuttering is due to the window being double buffered, which is probably for the better.
My solution to both problems is this, which will repeatedly call on_draw() as fast as possible:
class CustomLoop(app.EventLoop):
def idle(self):
dt = self.clock.update_time()
self.clock.call_scheduled_functions(dt)
# Redraw all windows
for window in app.windows:
window.switch_to()
window.dispatch_event('on_draw')
window.flip()
window._legacy_invalid = False
# no timout (sleep-time between idle()-calls)
return 0
app.event_loop = CustomLoop()
app.run() # locks the thread
I think their documentation should add a best-practice example for changing the event-loop.

Ignore keyboard event on waitKey() in OpenCV

I'm learning OpenCV and I decided to make a snake game using it. It's almost done but there is a slight problem that seems simple but I couldn't find a solution.
while True:
move()
cv2.imshow('Snake Game', frame)
cv2.waitKey(250)
It's supposed to wait 250 miliseconds before the next frame but key presses break the waiting so game speeds up when I hold down a key. How can I make it ignore the keyboard events and only use time?
I would be very surprised if waitKey didn't stop the waiting after key presses. In fact the name itself suggest that. So basically it's like calling a function called max and then expect the minimum.
From your code and what you've described, you're using waitKey for two reasons:
waiting for some fixed time. That means you're using it to synchronize your game loop.
using it (maybe) to handle key presses for user interaction with the game.
In my opinion, first thing to do is to stop waiting and just keep showing frames continuously as soon as it is ready. And for synchronization you just need to save time for each frame printing. And using that time you update after user interaction or deciding how to process frame or ... One place to help you in that is to look at how game loops are implemented.Take a look here : https://gamedev.stackexchange.com/questions/651/how-should-i-write-a-main-game-loop

Pyglet application framerate appears to speed up each time it is restarted

I am working on a basic simulation in pyglet, which I need to re-run and visualise multiple times. The simulation adjusts the focus "f" of a lens until it reaches the desired location (see gif here https://imgur.com/Ui06B1c). I have put an instance of the simulation inside a for loop along with a pyglet.app.run() loop running inside it also, so that it can be reset and run for 10 episodes.
num_episodes=10
for i in range(num_episodes):
update_rate=1/59
simulation=PivotPointSimulation()
pyglet.clock.schedule_interval(update, update_rate)
pyglet.app.run()
I need to make an instance of the "simulation" so that the draw and update methods can be called by pyglet.app.run(). By making a new instance each loop, I can also reset the variables when the simulation starts again. I terminate the simulation when the focus "f" of the lens reaches the desired position with pyglet.app.exit().
The problem is each time the for loop iterates, the framerate of my pyglet application speeds up (see gif for example https://imgur.com/Ui06B1c). I have checked if the update_rate is changing and it is staying the same. Peculiarly enough, when I use spyders stop button to stop the code, and run the simulation again with "play" the framerate of the simulation also gets faster and faster each time I hit stop and play.
I am wondering if there is a way to properly terminate an app, so that each time it runs again it is reset fully? I am unsure if pyglet.app.exit() is resetting everything properly. But I am not sure how the app is speeding up, since pyglet.clock.schedule_interval(update, update_rate) should update at a constant update rate.
I have tried playing around with Vsync settings (setting from false to true) and it hasn't helped. I have also tried using pyglet.clock.set_fps_limit(60) to no avail. So I am not sure what is going on here.
Has anyone encountered this problem before?
Every time you call pyglet.clock.schedule_interval(update, update_rate), it adds the callback to be called at the rate.
So at the end of your loop of 10, you have the same callback scheduled ten times.
One way to prevent this is to unschedule the function before so that it isn't scheduled a second time:
num_episodes=10
for i in range(num_episodes):
update_rate=1/59
simulation=PivotPointSimulation()
pyglet.clock.unschedule(update)
pyglet.clock.schedule_interval(update, update_rate)
pyglet.app.run()

Update live plot in SLOW loop without plot greying out

Sometimes, I do live updates to a plot in a loop. Normally, this works fine, but when the processing within the loop takes a long time, the plot 'greys out'/sleeps, for all but the first 10 seconds of this time. This can be quite annoying, as it makes it typically not possible to distinguish the curves (I could use dotted lines of course, but...). I'm using Ubuntu, and about 10 seconds is the threshold where this starts to happen for me.
Below is some toy code to reproduce the problem, and some pictures to demonstrate what happens.
Is there an easy way to prevent this 'greying out' behaviour?
import numpy as np
import pylab as p
import time
def create_data(i):
time.sleep(10) # INCREASE THIS VALUE TO MAKE THE PLOT GREY OUT WHILE IT WAITS
return np.sin(np.arange(i) * 0.1)
def live_plot(y):
p.cla()
p.plot(y)
p.plot(y**2)
p.draw()
p.pause(0.01)
for i in xrange(1000):
y = create_data(i)
live_plot(y)
The issue is that the GUI window becomes non-responsive from the PoV of the window manager so it 'helpfully' grays it out to tell you this. There may be a setting in gnome/unity (not sure which you are using) to disable this. (system dependent, maybe impossible :( )
One solution is to push the computation to another thread/process which will allow you to use a blocking show to leave the GUI main loop responsive. (complex :( )
Another solution is to in your slow loop periodically poke the GUI loop. I think calling fig.canvas.flush_events() should be sufficient. (dirty :( )
In short, GUIs (and asynchronous stuff in general) are hard to get right.
In Ubuntu 18.04, go to "search program" and type in "compiz" in the search box. When the ccsm window comes up, look for and uncheck the "fade window" box.

Categories