Turtle graphics - screen updates with tracer not working as expected - python

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.

Related

python tkinter window going unresponsive and hanging

MyButton1 =Button(master, text='Quit',bg="grey",width=20,
command=master.quit)
MyButton1.place(x=200, y=100)
MyButton2 =Button(master, text='Propagate', bg="grey",width=20,
command=mainmethod)
MyButton2.place(x=1000, y=100)
master.geometry("1500x1500")
master.mainloop( )
In the above code after pressing propagate button mainmethod is invoking..
I wrote my logic in main method where this method alone taking 2minutes to execute in the mean time GUI going unresponsive state for few min and later displaying all my required output on text box i inserted
whether any away to avoid the unresponsive issue apart from using multi threading
and i am looking such that after pressing propagate button button should disabled and window should not go unresponsive and display text.insert statements continuously which i added in main method ?????
To prevent hanging, you need to separate the calculations in the mainmethod from Tkinter's main loop by executing them in different threads. However, threading system in Python is not that well-developed as in other languages (AFAIK) because of GIL (Global Interpreter Lock), but there is an alternative - using processes just like threads. This is possible with multiprocessing library.
In order to just prevent hanging, you could create another function
from multiprocessing import Process
def mainmethodLaunch():
global mainmethodProcess
mainmethodProcess = Process(target=mainmethod)
mainmethodProcess.start()
And bind this function to MyButton2 instead of mainmethod itself.
Docs: https://docs.python.org/2/library/multiprocessing.html#the-process-class
You can see p.join in the example. join method will cause your main process to wait for the other one to complete, which you don't want.
So when you press the button, mainmethodLaunch function will be invoked, and it will create another process executing mainmethod. mainmethodLaunch function's own run duration should be insignificant. Due to usage of another process, Tkinter window will not hang. However, if you do just this, you will not be able to interact with mainmethod process in any kind while it will be working.
In order to let these processes communicate with each other, you could use pipes (https://docs.python.org/2/library/multiprocessing.html#exchanging-objects-between-processes)
I guess the example is quite clear.
In order to receive some data from the mainmethod process over time, you will have to poll the parent_conn once a little time, let's say, second. This can be achieved with Tkinter's after method
(tkinter: how to use after method)
IMPORTANT NOTE: when using multiprocessing, you MUST initialize the program in if __name__ == '__main__': block. I mean, there should be no doing-something code outside functions and this block, no doing-something code with zero indent.
This is because multiprocessing is going to fork the same Python executable file, and it will have to distinguish the main process from the forked one, and not do initializing stuff in the forked one.
Check twice if you have done that because if you make such a mistake, it can cost you hanging of not just Tkinter window, but the whole system :)
Because the process will be going to fork itself endlessly, consuming all RAM you have, regardless of how much you have.

Python, Threading with Tkinter

So I'm pretty deep into a program I am doing and I realized that with my program, I think I need to implement threading in some manner in order to stop it from locking up.
My program uses Tkinter for it's GUI, and when the program starts, I have a process that is running every second uses Tkinter's after() function. The method in question reads data and appends it to a fixed length deque(). In other parts of my program I have methods which read the last appended value, and process it.
My issue however is a simple loop that goes like this:
value = valDeque.getLastAppendedValue()
while value != "this specific value":
value = valDeque.getLastAppendedValue()
When the inital variable call before the while loop sets value as something other than the specific value I am looking for the program goes into an endless loop and it seems everything else stops functioning.
I am assuming this is because the while loop keeps executing while my after() function that would append the value I am looking for is sitting in limbo waiting for it's turn to execute. This is why I believe I need to use threading, since I can set up my update/append function to run separate of the rest of the processes so I don't have this error.
With that said, I am not super experienced with Python, and have no idea how to integrate threading with the Tkinter after() function since I know Tkinter's mainloop() doesn't interact with with something like time.sleep().
I've tried looking online for some examples to get some headway, but I can't really make heads or tails or what I am finding.

Python's Tkinter after() function jumps CPU

I'm building a Python GUI app using tkinter.
Basically I'm starting and integrating with a different thread, while communication goes using input and output queues.
In the GUI side (the main thread where tkinter's mainloop() goes) I want to add a function which will be called on every iteration of the mainloop (I'm processing and displaying information on real-time).
So my function does something like that:
def loop(self):
try:
output_type, data = wlbt.output_q.get_nowait()
pass # if got something out of the queue, display it!
except Queue.Empty:
pass
self.loop_id = self.after(1, self.loop)
While when starting the program I just call self.loop_id = self.after(1, self.loop).
So two things that bother me:
The loop function raise the CPU usage by 30%-50%. If I disable it then it's good.
I want to be able to use after_idle() to maximize the refresh-rate, but I wasn't able to just replace it - got and error.
I'm sensing there's something I don't fully understand. What can be done to address these issues?
When you call self.after(1, self.loop) you are asking for a function to be run roughly once per millisecond. It's not at all surprising that the CPU usage goes up since you are making 1000 function calls per second.
Given that humans cannot perceive that many changes, if all you're doing is updating the display then there's no reason to do that more than 20-30 times per second.

Python minecraft pyglet glClear() skipping frames

I recently downloaded fogleman's excellent "Minecraft in 500 lines" demo from https://github.com/fogleman/Craft. I used the 2to3 tool and corrected some details by hand to make it runnable under python3. I am now wondering about a thing with the call of self.clear() in the render method. This is my modified rendering method that is called every frame by pyglet:
def on_draw(self):
""" Called by pyglet to draw the canvas.
"""
frameStart = time.time()
self.clear()
clearTime = time.time()
self.set_3d()
glColor3d(1, 1, 1)
self.model.batch.draw()
self.draw_focused_block()
self.set_2d()
self.draw_label()
self.draw_reticle()
renderTime = time.time()
self.clearBuffer.append(str(clearTime - frameStart))
self.renderBuffer.append(str(renderTime - clearTime))
As you can see, I took the execution times of self.clear() and the rest of the rendering method. The call of self.clear() calls this method of pyglet, that can be found at .../pyglet/window/__init__.py:
def clear(self):
'''Clear the window.
This is a convenience method for clearing the color and depth
buffer. The window must be the active context (see `switch_to`).
'''
gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
So I basically do make a call to glClear().
I noticed some frame drops while testing the game (at 60 FPS), so I added the above code to measure the execution time of the commands, and especially that one of glClear(). I found out that the rendering itself never takes longer than 10 ms. But the duration of glClear() is a bit of a different story, here is the distribution for 3 measurements under different conditions:
Duration of glClear() under different conditions.
The magenta lines show the time limit of a frame. So everything behind the first line means there was a frame drop.
The execution time of glClear() seems to have some kind of "echo" after the first frame expires. Can you explain me why? And how can I make the call faster?
Unfortunately I am not an OpenGL expert, so I am thankful for every advice guys. ;)
Your graph is wrong. Well, at least it's not a suitable graph for the purpose of measuring performance. Don't ever trust a gl* function to execute when you tell it to, and don't ever trust it to execute as fast as you'd expect it to.
Most gl* functions aren't executed right away, they couldn't be. Remember, we're dealing with the GPU, telling it to do stuff directly is slow. So, instead, we write a to-do list (a command queue) for the GPU, and dump it into VRAM when we really need the GPU to output something. This "dump" is part of a process called synchronisation, and we can trigger one with glFlush. Though, OpenGL is user friendly (compared to, say, Vulkan, at least), and as such it doesn't rely on us to explicitly flush the command queue. Many gl* functions, which exactly depending on your graphics driver, will implicitly synchronise the CPU and GPU state, which includes a flush.
Since glClear usually initiates a frame, it is possible that your driver thinks it'd be good to perform such an implicit synchronisation. As you might imagine, synchronisation is a very slow process and blocks CPU execution until it's finished.
And this is probably what's going on here. Part of the synchronisation process is to perform memory transactions (like glBufferData, glTexImage*), which are probably queued up until they're flushed with your glClear call. Which makes sense in your example; the spikes that we can observe are probably the frames after you've uploaded a lot of block data.
But bear in mind, this is just pure speculation, and I'm not a total expert on this sort of stuff either, so don't trust me on the exact details. This page on the OpenGL wiki is a good resource on this sort of thing.
Though one thing is certain, your glClear call does not take as long as your profiler says it does. You should be using a profiler dedicated to profiling graphics.

wxPython -- drawing a line, relatively slowly

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

Categories