ProgressDialog + threads

34 views
Skip to first unread message

Chris Weisiger

unread,
May 16, 2013, 2:15:18 PM5/16/13
to wx-u...@googlegroups.com
I have a function that loads a large amount of data off of the hard drive and transfers it to the graphics card using OpenGL. OpenGL calls must happen in the main thread, while this function must run in its own thread. Thus, my code looks something like the following:

images = loadImages(filename)
for image in images:
    wx.CallAfter(self.addImage, image)

I want to have a ProgressDialog that updates as images are actually loaded. Thus I modify the above to look like this:
http://pastebin.com/rwY2aLc8

This results in the odd behavior that the progress dialog doesn't change at all until after all of the images are loaded, at which point it updates *backwards* (starting out full and rapidly emptying).

Note that if I put the dialog.Update() calls into the while loop, then the dialog never seems to update at all; it just waits until all images are loaded and then gets destroyed. So that's not desirable either. My guess is that the Update() calls result in their own events being added to the queue, which is full of addImage calls already; by the time the Update calls happen, loading is finished and the dialog is destroyed.

How do I achieve the results I'm looking for? Apologies for not providing a working example; this is part of a rather large project and disentangling the code would take significant effort.

Thank you for your time.

-Chris

Igor Korot

unread,
May 16, 2013, 2:38:44 PM5/16/13
to wx-u...@googlegroups.com
Hi, Chris,

Are you saying you are trying to call wxProgressDialog in a thread?

Thank you.
 

Thank you for your time.

-Chris
--
Please read http://www.wxwidgets.org/support/mlhowto.htm before posting.
 
To unsubscribe, send email to wx-users+u...@googlegroups.com
or visit http://groups.google.com/group/wx-users
 
 

Chris Weisiger

unread,
May 16, 2013, 2:53:32 PM5/16/13
to wx-u...@googlegroups.com
I am creating a wx.ProgressDialog instance in a thread, yes, though I don't have visibility into how wxPython handles that in the backend. I wouldn't be surprised if the library handles "transferring" the call to the main thread.

-Chris

gadgetsteve

unread,
May 16, 2013, 4:00:19 PM5/16/13
to wx-u...@googlegroups.com

Try creating the progress dialogue in the main process and in the thread call after for add image and for update dialogue.   Sorry for top post this phone doesn't seem to allow anything else OT If anybody knows of a well behaved android mail client. ...

Sent from Samsung Mobile

Vadim Zeitlin

unread,
May 16, 2013, 4:01:06 PM5/16/13
to wx-u...@googlegroups.com
On Thu, 16 May 2013 11:53:32 -0700 Chris Weisiger wrote:

CW> I am creating a wx.ProgressDialog instance in a thread, yes, though I don't
CW> have visibility into how wxPython handles that in the backend. I wouldn't
CW> be surprised if the library handles "transferring" the call to the main
CW> thread.

It doesn't because it can't (to the best of my knowledge, anyhow). As all
the other GUI classes, wxProgressDialog can only be used from the main
thread.

Regards,
VZ

--
TT-Solutions: wxWidgets consultancy and technical support
http://www.tt-solutions.com/

Stefano Mtangoo

unread,
May 17, 2013, 3:52:47 AM5/17/13
to wx-users
Why not create it in main thread. You can do it this way
1. Thread created sends event to tell the main thread to created
dialog and do whatever init works
2. Send any progress events to update dialog in the main thread
3. Tell main thread you are done and it should destroy the dialog

So events, events, events!

Chris Weisiger

unread,
May 17, 2013, 11:15:12 AM5/17/13
to wx-u...@googlegroups.com
On Fri, May 17, 2013 at 12:52 AM, Stefano Mtangoo <mwinj...@gmail.com> wrote:
Why not create it in main thread.

As I understand it, that should be what doing wx.CallAfter() for all of the dialog-related calls does -- it creates an event to call the specified function later in the main thread. The difficulty I'm running into is twofold:

1) I also have to use wx.CallAfter() to load my image data in the main thread, creating even more events, and
2) events don't seem to be executed in the order in which they are added to the event queue.

My loop that does this:

for i, image in enumerate(images):
    wx.CallAfter(self.addImage, image)
    wx.CallAfter(statusDialog.Update, i)

*should* result in the event queue alternating between "add image" and "update dialog" calls. In practice, however, it does all of the "add image" calls first, and only then does the "update dialog" calls, in reverse order! So the dialog shows no "progress" while all of the work is actually done (as I can monitor through other means), but once it is complete you see the progress bar instantly fill and then empty out!

In fact it's even more pernicious, since I have to end this function by destroying the progress dialog ("wx.CallAfter(statusDialog.Destroy)") -- but this sometimes causes a segfault, I assume because there are still "update dialog" calls in the event queue even after the dialog has been destroyed.

Is there some reasonably plausible way I can enforce ordering in the event queue?

-Chris
 
You can do it this way
1. Thread created sends event to tell the main thread to created
dialog and do whatever init works
2. Send any progress events to update dialog in the main thread
3. Tell main thread you are done and it should destroy the dialog

So events, events, events!

Chris Weisiger

unread,
May 17, 2013, 12:21:34 PM5/17/13
to wx-u...@googlegroups.com
Here, I put together a test application that demonstrates the problem:
http://pastebin.com/3nVdgDMT

If events were processed in the order in which they are created, then this would not segfault; as is, it does so semi-reliably on my computer. Removing the call to dialog.Destroy() prevents the crash, at the cost (of course) of the dialog sticking around. You can also see the way the progress bar moves backwards.

-Chris

Robin Dunn

unread,
May 17, 2013, 2:39:16 PM5/17/13
to wx-u...@googlegroups.com
Chris Weisiger wrote:
> On Fri, May 17, 2013 at 12:52 AM, Stefano Mtangoo <mwinj...@gmail.com
> <mailto:mwinj...@gmail.com>> wrote:
>
> Why not create it in main thread.
>
>
> As I understand it, that should be what doing wx.CallAfter() for all of
> the dialog-related calls does -- it creates an event to call the
> specified function later in the main thread.

Correct.

> The difficulty I'm running
> into is twofold:
>
> 1) I also have to use wx.CallAfter() to load my image data in the main
> thread, creating even more events, and
> 2) events don't seem to be executed in the order in which they are added
> to the event queue.

That can happen when one of the called functions does something that
will cause the pending event queue to be processed, like calling
wx.Yield. So basically what happens is that the first function is
called, and then it yields and then any pending CallAfters are called,
and then control eventually returns to the first one and it continues
running, but because the other(s) have already been run it turns out
that the first one is executed last.


>
> My loop that does this:
>
> for i, image in enumerate(images):
> wx.CallAfter(self.addImage, image)
> wx.CallAfter(statusDialog.Update, i)
>
> *should* result in the event queue alternating between "add image" and
> "update dialog" calls. In practice, however, it does all of the "add
> image" calls first, and only then does the "update dialog" calls, in
> reverse order! So the dialog shows no "progress" while all of the work
> is actually done (as I can monitor through other means), but once it is
> complete you see the progress bar instantly fill and then empty out!

Normally I recommend that people combine the two tasks into one function
and then use CallAfter just once to call that one function. However in
your case it will still likely end up with a similar problem since you
are stacking up CallAfters rather quickly. Then most likely they are
all waiting to be processed when the first one is called and if it is
still doing a yield or whatever then the others will still be processed
first.

In this case I would recommend that you don't use CallAfter for this,
but instead add your images and any other info you need to a Queue.Queue
from the worker thread and then call wx.WakeUpIdle(). In an EVT_IDLE
handler you can check if there is anything in that Queue and if there is
pull an item out and process it, update the progress dialog, call
event.RequestMore, and then return. In the next idle event you can get
the next item from the queue and take care of it. Since order is
important then I would also do something to ensure that you don't
reenter and try processing the next item while it is still processing
the prior one.



--
Robin Dunn
Software Craftsman
http://wxPython.org

Robin Dunn

unread,
May 17, 2013, 2:42:11 PM5/17/13
to wx-u...@googlegroups.com
Chris Weisiger wrote:
> Here, I put together a test application that demonstrates the problem:
> http://pastebin.com/3nVdgDMT


Do NOT create and destroy the progress dialog from the worker thread.
That must happen in the UI thread. For example, in the situation I
described with the Queue in my last message it could be created in the
EVT_IDLE handler when you process an image from the queue and there
isn't already a progress dialog. Then when you empty the queue, or
otherwise know that you are finished, then you can destroy it.

Robin Dunn

unread,
May 17, 2013, 2:58:33 PM5/17/13
to wx-u...@googlegroups.com
Robin Dunn wrote:
> Chris Weisiger wrote:
>> 1) I also have to use wx.CallAfter() to load my image data in the main
>> thread, creating even more events, and
>> 2) events don't seem to be executed in the order in which they are added
>> to the event queue.
>
> That can happen when one of the called functions does something that
> will cause the pending event queue to be processed, like calling
> wx.Yield. So basically what happens is that the first function is
> called, and then it yields and then any pending CallAfters are called,
> and then control eventually returns to the first one and it continues
> running, but because the other(s) have already been run it turns out
> that the first one is executed last.
>

For completeness I should add that when the 2nd item is called and it
yields then the 3rd item is called and it yields and then the 4th item
is called... So what is really happening is that it will recursively
process the CallAfter events until it gets to the last one, which can
then finish processing and return up through the stacked up
ProcessEvents to the 2nd to last one which can then finish processing
and return to the 3rd to last, and so on. So essentially the whole
chain is being processed in reverse order. When you mix in some
CallAfter's that are yielding with some that are not then the order can
get even more mixed up.

Chris Weisiger

unread,
May 17, 2013, 3:36:48 PM5/17/13
to wx-u...@googlegroups.com
Thanks for the detailed explanation, Robin. Sounds like a proper solution will be significantly more complicated that what I have, but oh well, not much to be done about that. :)

-Chris


--
Please read http://www.wxwidgets.org/support/mlhowto.htm before posting.

Reply all
Reply to author
Forward
0 new messages