GUI loops and another loops - python

I confused about loops. For example in python3:
import pygame
pygame.init()
....
....
while True:
....
....
pygame.display.update()
When i use this, the program using, about %110 CPU. But when i use a GUI toolkit like tkinter:
import tkinter
root = tkinter.Tk()
....
....
....
root.mainloop()
It using about %0.3 CPU. I think both are infinite loops. How can I optimize the first code?

The main difference is, that your typical GUI toolkit will blockingly wait for the arrival of new events and the loop body, including drawing operations, only gets executed in reaction to such events.
A typical game loop however does not wait for events to arrive, since there's a simulation going on, that needs to update continuously followed by updating the display.
I.e. the pygame loop is designed to use up as much CPU cycles as possible, to give the most smooth simulation. The tkinter loop however is designed to spend as much CPU cycles as possible to conserve system resources.

Related

Force update GUI in kivy

I am writing an app in kivy which does cpu-heavy calculations at launch. I want the app to display what it's doing at the moment along with the progress, however, since the main loop is not reached yet, it just displays empty white screen until it finishes working. Can I force kivy to update the interface?
Basically I'm looking for kivy's equivalent of Tkinter's root.update()
I could create a workaround by defining a series of functions with each calling the next one through Clock.schedule_once(nextFunction, 1), but that would be very sloppy.
Thanks in advance.
Leaving aside the question of whether you should be using threading or something instead (which possibly you should), the answer is just that you should move your cpu calculations to somewhere else. Display something simple initially (i.e. returning a simple widget from your build method), then do the calculations after that, such as by clock scheduling them.
Your calculations will still block the gui in this case. You can work around this by doing them in a thread or by manually breaking them up into small pieces that can be sequentially scheduled.
It might be possible to update the gui by manually calling something like Clock.tick(), but I'm not sure if this will work right, and even if so it won't be able to display graphics before they have been initialised.

Running Tkinter dependent code alongside mainloop without GUI freeze

I am writing a simple image viewer that lets the user flip very quickly through tens of thousands of images, about 100 at a time. The images are files on disk.
In order for the viewer to function, it must continuously preload images ahead of the user's current one (or the viewer would be unusably sluggish).
The basic recipe that I'm using to display the images in a grid of Tkinter labels, is the following (this has been tested and works):
def load_image(fn):
image = Image.open(fn)
print "Before photoimage"
img = ImageTk.PhotoImage(image)
print "After photoimage"
label.config(image=load_image("some_image.png")
I need the ImageTk.PhotoImage instance to display the image on a label. I have implemented two different approaches, each with an associated problem.
First approach:
Launch a separate thread which pre-loads the images:
def load_ahead():
for fn in images:
cache[fn] = load_image()
threading.Thread(target=load_ahead).start()
top.mainloop()
This works quite well on my Linux machine. However, on another machine (which happens to be running Windows, and compiled with pyinstaller), a deadlock seems to happen. "Before Photoimage" is printed, and then the program freezes, which suggests that the loader thread gets stuck at creating the ImageTk.PhotoImage object. Musst the creation of an ImageTk.PhotoImage object happen within the main (Tkinter mainloop's) thread? Is the creation of PhotoImage computationally expensive, or is negligible compared to actually loading the image from disk?
Second approach:
In order to circumvent this possible requirement of PhotoImage objects being created from within Tkiner's mainloop thread, I resorted to Tk.after:
def load_some_images():
#load only 10 images. function must return quickly to prevent freezing GUI
for i in xrange(10):
fn = get_next_image()
cache[fn] = load_image(fn)
top.after_idle(load_some_images)
top.after_idle(load_some_images)
The problem with this is that, appart from creating additional overhead (ie the image-loading procedure must be broken up into very small chunks since it is competing with the GUI) that it periodically freezes the GUI for the duration of the call, and it seems to consume any keyboard events that happened during its execution.
Third approach
Is there a way I can detect pending user events? How can I accomplish something like this?
def load_some_images():
while True:
try: top.pending_gui_events.get_nowait()
except: break
#user is still idle! continuing caching of images
fn = get_next_image()
cache[fn] = load_image(fn)
top.after_idle(load_some_images)
top.after(5,load_some_images)
Edit: I have tried using top.tk.call('after','info') to check pending keyboard events. This doesn't always reliably, and the interface is still sluggish/unresponsive.
Thanks in advance for any ideas
I recommend creating an load_one_image function rather than a load_some_images function. It will be less likely to interfere with the event loop.
Also, as a rule of thumb, a function called via after_idle shouldn't reschedule it self with after_idle. The reason is that after_idle will block until the idle event queue is drained. If you keep adding stuff on to the queue while the queue is being processed, it never gets completely drained. This could be the reason why your GUI seems to hang once in a while with your second approach.
Try after(5, ...) rather than after_idle(...). If your system can create an image in less than 5ms, you can process 100 images in about half a second, which is probably fast enough to give a pretty snappy interface. You can tweak the delay to see how it affects the overall feel of the app.

Pygame and Multiprocessing Strategy

I'm developing a game in pygame and i don't know which tasks should go to which process.
I have two processes connected by a pipe, one will have the window another will do calculations.
My question is: Which parts of the main loop should go to the other process?
In my game i will have to do event handling, collision detection, AI, drawing and heavy calculations(2D lighting system).
I'm afraid that if i put to much stuff on the other process the main one will have to wait for input and the FPS will freeze.
PS: For now i'm just starting to code the game so i can't show you much code.
There is the observer pattern
I would suggest the following architecture for creating a PyGame with two processes:
You divide your program into two parts:
model
all the game logic is kept in the subprocess, computing the whole game.
Whenever there is something noteworthy changed, it notifies the other process.
responsibilities:
update the game e.g. in a loop
do physics
send updates to the gui
gui
The gui is in the main process because it starts several games.
When a game is started it starts to observe important parts of the game.
responsibilities
handle user input e.g. right arrow pressed
send modifications to the model e.g. player walks righth
render views of model elements when updates are received
Note that I do not really know much of PyGame.
But keeping model and view apart is possible.
You can have a look at the MVC pattern, too. But it is really heavy. Just merging View and Controller is enough if the program shall not be distributed across computers.
Then I heard about MVVM pattern. Not sure whether this is too much again since you only need to split your game into two parts and not three.

Running Python graphics and code

I have a program that I'm just adding graphics to, but I'm having trouble running my main code along with the graphics. Basically I have something like this:
def mainFunction():
while True:
run code in here
root = Tk()
board = Canvas(root, height=710, width=1000)
board_image = PhotoImage(file="/path/example.jpg")
photo = board.create_image(0,0, anchor=NW, image=board_image)
board.pack()
mainFunction
root.mainloop()
I can only run either the mainFunction or the graphics because whichever one I make run first in the code is the only that runs. It doesn't stop to allow the next code to run. There has to be a simple way to get graphics and code to run together side by side. Thanks!
Use Tk.after_idle() to register a function that will do a piece of the work required. Keep doing piece after piece until all the work is done.
Generally speaking, you cannot put an infinite loop inside a Tkinter application. Why? It's already running an infinite loop: the event loop. Your code is the equivalent of this:
while <there are more events to service>:
while True:
<run code in here>
<get the next event>
<service the event>
See the problem? You're preventing the code from ever servicing events, and events are the life blood of a GUI.
Instead, you need to take advantage of the already-running infinite loop by adding code to be run inside the loop. You do this with after (and after_idle). This will put one even on the queue. If, during the processing of that event you again call after_idle, you've effectively set up an infinite loop that works within the event loop.
For example:
def do_one_iteration(self):
<run code in here>
self.after(100, self.do_one_iteration)
Then, somewhere in your main logic, or in response to a button, you call do_one_iteration. It will do one iteration of your previously-infinite-loop. When it is done it instructs Tkinter to call itself again 100 milliseconds later. When that time period elapses your code is run, it schedules another iteration in 100 milliseconds, etc. etc. You can change the interval to whatever you want; the smaller the interval the faster your code runs, but the greater the chance that you starve the GUI for events.
Note that this only works if <run code in here> runs relatively fast. While it is running your GUI will freeze. If it can complete one iteration in a couple hundred milliseconds then the user will never know. If it takes a second or more it will be noticeable.
Note: this example assumes your main application is an object that inherits from a Tkinter widget. If that's not the case it will still work, you just have to remove the self parameter. An even better solution is to refactor your GUI to use objects -- it's a much more flexible way of implementing GUIs.

GTK Progressbar pulsing python

How can I get a Progressbar to "pulse" while another function is run?
There is an example of how to do this here.
Push that another function into a separate thread. As long as your main thread runs any code, GUI is frozen. This is not a problem for short code pieces, but obviously a problem in your case.
Also read what PyGTK FAQ has to say about using threads in PyGTK program.
If your function runs in many iterations that don't take too long by themselves, then you don't necessarily need to mess around with separate threads. You can also cause the GUI to update itself during your long calculation:
def long_function(some_args):
while task_is_not_finished():
do_some_stuff_that_doesnt_take_too_long()
progress_bar.pulse()
while gtk.events_pending():
gtk.main_iteration()

Categories

Resources