wxPython "Frozen" GUI

Hi Everone,

I am relatively new to wxPython, and whilst I have had some experience with it before, I have not before tried to implement it into a real application, which is what I am currently trying to do.

I am using a combination of wxGlade and manual editing to create the GUI that I want, and so far it looks great! Though I am having problems with one of the event handlers. Firstly, I will explain how I am working with things, and then explain the problems that I have run into.

I have my command line application, which is in one python file, lets say application.py. Then I have my wxPython GUI, which I have in gui.py. From gui.py, I have imported both wx (obviously) and application. With the event handler of one of the buttons within the applcation, the event that it calls (say self.OnButtonClick()) does a few things, and then send a whole lot of information (collected from user input fields in the GUI) to application.MyFunction(). This function then does what it needs to do.

Now, the application I am writing a GUI for is an application that downloads many images from a website (based on the information given by the user), and it works fine via the command line. As it is downloading, it does take some time, and with this there is a period where the application is unresponsive and appears frozen (the button that triggers the event also appears pressed down until all the downloading has stopped). Now, this does have the potential to need to download 100’s of images (depends on the user, I have only been testing it with about 10), and the user could easily force quit the application and give up if it appears frozen. My first question is how do I stop it from appearing frozen? I was looking at page 77 of wxPython in action, and it mentions Dispatch(), though I cannot work out how to use that.

My second question is, as I want to keep my GUI seperate from the rest of the program, I am importing the application.py file that contains all the functions that make up the program. These functions can be called by the GUI, but I am wondering how I would get information from application.py to gui.py. The thing I would like to do next would be to have a window popup with a progress bar. I know that I can make a progress bar with wx.Gauge (page 210 of wxpython in action), and that should not be a problem, but how do I get information from application.py to gui.py such as how many images there are, and what image it is up to? The main problem I am having is been able to communicate not from gui.py to application.py, but from application.py to gui.py, which could contain some information that is getting updated regually.

Also, am I implementing the GUI correctly? For the command line application, I basically had main.py import application.py, and that did all the user interaction with the command line, and I had no problems printing the progress to the command line, but when implementing any GUI it seems to get a bit more difficult.

Thanks, Josh.

···


Joshua Henderson
+61 449 128 074
joshhendo@gmail.com

Thanks Tim,

These were more than enough to get me started. I have so far got threading working how I want it: not too hard, yet, as I have heard that it can get complicated, but I will get there one day.

I am currently looking into using wx.CallAfter and making a call back function etc, to help communication between the GUI and backend, and I will report back how that goes (whether it is successful or whether I need help!)

Again, thanks for taking the time to reply.

  • Josh
···

On Mon, Apr 21, 2008 at 8:26 PM, Tim van der Leeuw tnleeuw@gmail.com wrote:

Hi Josh,

On Mon, Apr 21, 2008 at 11:21 AM, Joshua Henderson joshhendo@gmail.com wrote:

Hi Everone,

[…]

I am using a combination of wxGlade and manual editing to create the GUI that I want, and so far it looks great!

I found somewhere the tip to subclass the generated code from wxGlade in a different file, to be more isolated from wxGlade overwriting or not overwriting the files. That works very well for me. But you don’t have to do it that way; it’s only a tip I received and followed.

[…]

With the event handler of one of the buttons within the applcation, the event that it calls (say self.OnButtonClick()) does a few things, and then send a whole lot of information (collected from user input fields in the GUI) to application.MyFunction(). This function then does what it needs to do.

Now, the application I am writing a GUI for is an application that downloads many images from a website (based on the information given by the user), and it works fine via the command line. As it is downloading, it does take some time, and with this there is a period where the application is unresponsive and appears frozen

What you need to do here, is dive into the realms of multi-threading. Fortunately, that sounds a lot more scary than it really is.
I don’t have any quick samples for you, but it looks like kinda like:

import threading
threading.Thread(target=application.MyFunction, args=(MyFuncArg1, MyFuncArg2, etc)).start()

My second question is, as I want to keep my GUI seperate from the rest of the program, I am importing the application.py file that contains all the functions that make up the program. These functions can be called by the GUI, but I am wondering how I would get information from application.py to gui.py.

There’s a progressbar widget that displays the entire progress bar window and all. I use that.

One important thing is that you can not directly update the GUI from your background-thread. You have to use a speciall function, wx.CallAfter(…):

wx.CallAfter(App.ProgressBarUpdateFunc, new_progress_bar_value, some_text_msg)

And in this ProgressBarUpdateFunc you have the code to set the value of the prog. bar or wx.Gauge or what not.
The parameters to your function are extra parameters to wx.CallAfter.

To keep the application independant from the actual output-mechanism (console or wxGUI or other GUI or web-page or you name it), give your application, or expensive function, or whatever works for you, some kind of callback-method or object. Whenever the expensive function needs to report on it’s progress, it calls the callback-function, or a method on the callback-object.

This callback function or object has different implementations for updating GUI, printing to console, etc.

I hope that I’ve explained the principles clear enough for you to continue with it.

Thanks, Josh.

Regards,

–Tim


wxpython-users mailing list

wxpython-users@lists.wxwidgets.org

http://lists.wxwidgets.org/mailman/listinfo/wxpython-users


Joshua Henderson
+61 449 128 074
joshhendo@gmail.com

Try this:

http://pastie.textmate.org/185915

I think you’ll find it a little cleaner, easier to read, and has less overhead.

Gre7g

···

On 4/23/08, Joshua Henderson joshhendo@gmail.com wrote:

I have managed to get everything working how I want it! Thanks. Below is what I did for my own reference, and for other people who wanted to do a similar thing (it may help).
Here is the code that I used to play around with, but it is effectively what I applied to my larger application:
number1.py

import wx
import number2
import time
import threading

class GaugeFrame(wx.Frame):

def __init__(self):
    wx.Frame.__init__(self, None, -1, 'Gauge Example',
                    size=(350, 150))
    panel = wx.Panel(self, -1)
    self.count = 0
    self.gauge = wx.Gauge(panel, -1, 50, (20, 50), (250, 25))

    self.gauge.SetBezelFace(3)
    self.gauge.SetShadowWidth(3)
    threading.Thread(target=number2.me).start()
    self.Bind(wx.EVT_IDLE, self.OnIdle)

def OnIdle(self, event):
    self.count = number2.returnB()

    self.gauge.SetValue(self.count)

if name == ‘main’:
app = wx.PySimpleApp()
GaugeFrame().Show()
app.MainLoop()

number2.py

import time

def me():
global b
b = 1
while (b < 50):
b = b + 1

    time.sleep(1)
    print "HELLO!!!"

def returnB():
print "returning b: " + str(b)
return b

Basically, whilst on idle, the gauge is constantly calling the function self.OnIdle. This function can do anything you want, but the most logical thing for it to do it to update the gauge if needed. The OnIdle function then calls the function in number2.py which gets the global variable b and returns it, and the global variable b is what the gauge should be up to

I also have in my application a returnNumber function in the backend (number2.py) and a returnProgress function that replaces returnB. returnNumber returns how many items there are in the gauge.

You will want to be careful using this method, and threading, as if you try and call returnNumber or returnProgress before the global variables have been set, it will cause an error. I have currently put a small sleep in, but I don’t think that is the way to do it. The reason is if you call the variables right after starting the thread, the thread may not be up to the point where it has set the values. If anyone knows of a better way to do this, please let me know!

  • Josh

On Wed, Apr 23, 2008 at 6:49 PM, Joshua Henderson joshhendo@gmail.com wrote:

Thanks Tim,

These were more than enough to get me started. I have so far got threading working how I want it: not too hard, yet, as I have heard that it can get complicated, but I will get there one day.

I am currently looking into using wx.CallAfter and making a call back function etc, to help communication between the GUI and backend, and I will report back how that goes (whether it is successful or whether I need help!)

Again, thanks for taking the time to reply.

  • Josh

On Mon, Apr 21, 2008 at 8:26 PM, Tim van der Leeuw tnleeuw@gmail.com wrote:

Hi Josh,

On Mon, Apr 21, 2008 at 11:21 AM, Joshua Henderson joshhendo@gmail.com wrote:

Hi Everone,

[…]

I am using a combination of wxGlade and manual editing to create the GUI that I want, and so far it looks great!

I found somewhere the tip to subclass the generated code from wxGlade in a different file, to be more isolated from wxGlade overwriting or not overwriting the files. That works very well for me. But you don’t have to do it that way; it’s only a tip I received and followed.

[…]

With the event handler of one of the buttons within the applcation, the event that it calls (say self.OnButtonClick()) does a few things, and then send a whole lot of information (collected from user input fields in the GUI) to application.MyFunction(). This function then does what it needs to do.

Now, the application I am writing a GUI for is an application that downloads many images from a website (based on the information given by the user), and it works fine via the command line. As it is downloading, it does take some time, and with this there is a period where the application is unresponsive and appears frozen

What you need to do here, is dive into the realms of multi-threading. Fortunately, that sounds a lot more scary than it really is.
I don’t have any quick samples for you, but it looks like kinda like:

import threading
threading.Thread(target=application.MyFunction, args=(MyFuncArg1, MyFuncArg2, etc)).start()

My second question is, as I want to keep my GUI seperate from the rest of the program, I am importing the application.py file that contains all the functions that make up the program. These functions can be called by the GUI, but I am wondering how I would get information from application.py to gui.py.

There’s a progressbar widget that displays the entire progress bar window and all. I use that.

One important thing is that you can not directly update the GUI from your background-thread. You have to use a speciall function, wx.CallAfter(…):

wx.CallAfter(App.ProgressBarUpdateFunc, new_progress_bar_value, some_text_msg)

And in this ProgressBarUpdateFunc you have the code to set the value of the prog. bar or wx.Gauge or what not.
The parameters to your function are extra parameters to wx.CallAfter.

To keep the application independant from the actual output-mechanism (console or wxGUI or other GUI or web-page or you name it), give your application, or expensive function, or whatever works for you, some kind of callback-method or object. Whenever the expensive function needs to report on it’s progress, it calls the callback-function, or a method on the callback-object.

This callback function or object has different implementations for updating GUI, printing to console, etc.

I hope that I’ve explained the principles clear enough for you to continue with it.

Thanks, Josh.

Regards,

–Tim


wxpython-users mailing list
wxpython-users@lists.wxwidgets.org

http://lists.wxwidgets.org/mailman/listinfo/wxpython-users


Joshua Henderson
+61 449 128 074
joshhendo@gmail.com


Joshua Henderson
+61 449 128 074
joshhendo@gmail.com


wxpython-users mailing list
wxpython-users@lists.wxwidgets.org

http://lists.wxwidgets.org/mailman/listinfo/wxpython-users