OnInit does not return and app hangs

I am migrating an OS X app from
wxPython 2.8.12.1 (Carbon) to wxPython 3.0.2.0 (Cocoa).

  When OnInit completes, it returns True, but the calling function

app.MainLoop() never seems to return.

  I'm running the app from the command line (python2.7 myapp.py

myargs), and I have to press ctrl-c to terminate the process.

  This was not an issue with wxpy2.8.

  Note the main windows are wx.ProgressDialog.  Other Frames can be

instantiated and closed depending on the arguments passed to the
app. I think I remember (a long time ago) someone posting a
solution that instantiated and frame and then destroyed in right
away (or at the end) and that “fixed” things. I tried that (from
memory) but it made no difference.

          Any ideas what the problem could be and how to

resolve it?

    Thanks, Brendan.

I’ve narrowed the issue down to
wx.ProgressDialog.Update().

  If I do:

      dlg = MyProgressDialog(title, message, maximum)

      dlg.Destroy()

      return True

  then the MainLoop() exits as expected and python exits :)

  If I do:

      dlg = MyProgressDialog(title, message, maximum)

      dlg.Update(0, new_message)

      dlg.Destroy()

      return True

  then the MainLoop() never exits :(

  Any ideas why this could be?

  Thanks, Brendan.
···

On 17/06/2015 9:44 pm, Brendan Simon
(eTRIX) wrote:

    I am migrating an OS X app from

wxPython 2.8.12.1 (Carbon) to wxPython 3.0.2.0 (Cocoa).

    When OnInit completes, it returns True, but the calling function

app.MainLoop() never seems to return.

    I'm running the app from the command line (python2.7 myapp.py

myargs), and I have to press ctrl-c to terminate the process.

    This was not an issue with wxpy2.8.



    Note the main windows are wx.ProgressDialog.  Other Frames can

be instantiated and closed depending on the arguments passed to
the app. I think I remember (a long time ago) someone posting a
solution that instantiated and frame and then destroyed in right
away (or at the end) and that “fixed” things. I tried that
(from memory) but it made no difference.

              Any ideas what the problem could be and how to

resolve it?

      Thanks, Brendan.

Here is a sample app. Why
does the program not exit properly?

    Running python-2.7.10 (i386/x86_64 build), with wxPython 3.0.2.0

Cocoa, on OS X 10.9.5.

    Thanks, Brendan.
import wx

<details class='elided'>
<summary title='Show trimmed content'>&#183;&#183;&#183;</summary>

On 18/06/2015 9:53 am, Brendan Simon (eTRIX) wrote:
>         I've narrowed the issue down to
> wx.ProgressDialog.Update().
> 
> 
> 
>         If I do:
> 
>             dlg = MyProgressDialog(title, message, maximum)
> 
>             dlg.Destroy()
> 
>             return True
> 
> 
> 
>         then the MainLoop() exits as expected and python exits :)
> 
> 
> 
>         If I do:
> 
>             dlg = MyProgressDialog(title, message, maximum)
> 
>             dlg.Update(0, new_message)
> 
>             dlg.Destroy()
> 
>             return True
> 
> 
> 
>         then the MainLoop() never exits :(
> 
> 
> 
>         Any ideas why this could be?
> 
> 
> 
>         Thanks, Brendan.

#----------------------------------------------------------------------------
class MyProgressDialog(wx.ProgressDialog):
    """My Progress Dialog."""
   
    def __init__(self, *args, **kwargs):
        """Initialise class."""
        sty = 0
        sty |= wx.PD_APP_MODAL
        sty |= wx.PD_AUTO_HIDE
        #sty |= wx.PD_SMOOTH
        #sty |= wx.PD_CAN_ABORT
        #sty |= wx.PD_CAN_SKIP
        #sty |= wx.PD_LAPSED_TIMEE
        #sty |= wx.PD_ESTIMATED_TIME
        #sty |= wx.PD_REMAINING_TIME
        kwargs['style'] = kwargs.get('style', sty)
        wx.ProgressDialog.__init__(self, *args, **kwargs)
#----------------------------------------------------------------------------
class MyApp(wx.App):
    """My Application."""
    def __init__(self, argv):
        """Class constructor."""
        self.argv = argv
        wx.App.__init__(self, redirect=False)
    def OnInit(self):
        """OnInit() method."""
        tit = 'Test Title'
        msg = "This is a test message.  Please wait ..."
        max = 100
        dlg = MyProgressDialog(title=tit, message=msg, maximum=max)
        
        #dlg.Destroy()

        #return True

        (keepGoing, skip) = dlg.Update(50, "Half way there ...")
        wx.MilliSleep(1000)
        
        dlg.Destroy()
        return True
       
#----------------------------------------------------------------------------
def main_test():
    """Main test function."""
   
    app = MyApp("my arguments")
    print "DEBUG: MainLoop() starting."
    app.MainLoop()
    print "DEBUG: MainLoop() exited."
#----------------------------------------------------------------------------
if __name__ == "__main__":
    main_test()

You have kind of an unusual situation here. Remember that
wx.App.OnInit runs before the MainLoop starts. Thus, there is no
one monitoring for and dispatching messages for your main thread.
The main window has not been created yet, because no one has
processed the creation messages. The ProgressDialog should be
modal, which means it spawns its own message loop, but if it’s
waiting for interaction with a parent window, that would be Bad.
I know this doesn’t help you, and your app works fine on Windows,
but maybe it provides some insight into the mechanics.

···

Brendan Simon (eTRIX) wrote:

    I've narrowed the issue down to

wx.ProgressDialog.Update().

    If I do:

        dlg = MyProgressDialog(title, message, maximum)

        dlg.Destroy()

        return True



    then the MainLoop() exits as expected and python exits :)
-- Tim Roberts, Providenza & Boekelheide, Inc.

timr@probo.com

This doesn’t solve the problem. It’s just another way of doing the same thing I was doing.

···

On Thursday, 18 June 2015 18:48:15 UTC+10, Boštjan Mejak wrote:

What I would do is this:

with MyProgressDialog(title, message, maximum) as dlg:
dlg.Update(0, new_message)

I didn’t realise exactly what I was doing. I thought the application main code went in OnInit(). But now that you’ve informed me that MainLoop() needs to run, then it is clear that OnInit() needs to return and the main app code needs to run after MainLoop has exited (which seems weird as I assumed the no more processing of events would occur, but that’s obviously not the case).

So I tried renaming OnInit() to run() and having an empty (return True) OnInit(), and then calling app.run() after MainLoop(). Guess what? It seems to work and the ProgressDialog closes as expected :slight_smile:
Thanks heaps for the pointer.

···

On Friday, 19 June 2015 02:49:03 UTC+10, Tim Roberts wrote:

Brendan Simon (eTRIX) wrote:

    I've narrowed the issue down to

wx.ProgressDialog.Update().

    If I do:

        dlg = MyProgressDialog(title, message, maximum)

        dlg.Destroy()

        return True



    then the MainLoop() exits as expected and python exits :)
You have kind of an unusual situation here.  Remember that

wx.App.OnInit runs before the MainLoop starts. Thus, there is no
one monitoring for and dispatching messages for your main thread.
The main window has not been created yet, because no one has
processed the creation messages. The ProgressDialog should be
modal, which means it spawns its own message loop, but if it’s
waiting for interaction with a parent window, that would be Bad.

I know this doesn't help you, and your app works fine on Windows,

but maybe it provides some insight into the mechanics.


#----------------------------------------------------------------------------
def main_test():
“”“Main test function.”“”
app = MyApp(“my arguments”)
app.MainLoop()
app.run()
#----------------------------------------------------------------------------

So this begs the question: what should go in OnInit() and what should not, or be run outside of OnInit() ??
I wonder if using CallAfter(run) might be the recommended solution, instead of calling run() explicitly after MainLoop() ??

Cheers, Brendan.

Hi,

What I would do is this:

with MyProgressDialog(title, message, maximum) as dlg:
    dlg.Update(0, new_message)

This doesn't solve the problem. It's just another way of doing the same
thing I was doing.

Can't you just create a hidden wx.Frame and make you progress dialog a child?

Thank you.

···

On Fri, Jun 19, 2015 at 2:44 AM, Brendan <brendanjsimon@gmail.com> wrote:

On Thursday, 18 June 2015 18:48:15 UTC+10, Boštjan Mejak wrote:

--
You received this message because you are subscribed to the Google Groups
"wxPython-users" group.
To unsubscribe from this group and stop receiving emails from it, send an
email to wxpython-users+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

I hesitate to offer advice on “should” and “shouldn’t”, just because
I’m not immersed in wxPython on a daily basis.
I can, however, draw parallels between a wxPython app and a
traditional Windows SDK GUI app. In a traditional Windows app, your
main program is typically rather small. You do command-line
processing, you might set up the log file, you might load some DLLs
– mostly non-UI-related stuff. That corresponds to wx.App.OnInit.
Once that non-UI setup is complete, you create the main window, and
enter the message loop. That’s the call to wx.App.MainLoop. All of
the UI-related setup goes in the main window’s WM_CREATE handler.
I should also say that it is POSSIBLE you have simply encountered a
bug that only manifests itself in OS X. As I mentioned, the code
you posted runs perfectly fine in Windows.

···

Brendan wrote:

      I didn't realise

exactly what I was doing. I thought the application main code
went in OnInit(). But now that you’ve informed me that
MainLoop() needs to run, then it is clear that OnInit() needs
to return and the main app code needs to run after MainLoop
has exited (which seems weird as I assumed the no more
processing of events would occur, but that’s obviously not the
case).

      So I tried renaming OnInit() to run() and having an empty

(return True) OnInit(), and then calling app.run() after
MainLoop(). Guess what? It seems to work and the
ProgressDialog closes as expected :slight_smile:

      ...

              So this begs the question:

what should go in OnInit() and what should not, or be run
outside of OnInit() ??

      I wonder if using CallAfter(run) might be the recommended

solution, instead of calling run() explicitly after MainLoop()
??

-- Tim Roberts, Providenza & Boekelheide, Inc.

timr@probo.com

Make sure you are destroying all top level "frames"/windows/dialogs
including the ones that are -not Shown- or are -hidden-.
Also if you have threads running that you didn't shut down or other system
resources like wx.Timer() that need explicit "destruction" or stopping are
taken care of. Also try adding a def OnExit(self,...) method to your
"myapp" with a print() to see if that gets hit.

Tim Roberts wrote:

I should also say that it is POSSIBLE you have simply encountered a bug
that only manifests itself in OS X. As I mentioned, the code you posted
runs perfectly fine in Windows.

Yes, wxOSX has some glitches when dialogs are used within OnInit and there are not any other top-level windows created. But I thought it was just for native dialogs like wx.MessageDialog (the progress dialog is generic on OSX last I checked) but perhaps it is a related issue in this case.

This workflow could probably work and ends up being a little cleaner IMO than calling a method after MainLoop returns:

* Create a Frame in OnInit (or before MainLoop is called if using wx.App directly.)

* Also do wx.CallAfter(someFunctionOrMethod)

* Call MainLoop. After the main loop starts it will process pending events and then someFunctionOrMethod will be called, and you can show the progress dialog and do your work there.

* When the task is finished then someFunctionOrMethod can Destroy() the progress dialog and Close() the frame. After someFunctionOrMethod returns those two pending events will be processed and then MainLoop should exit normally.

···

--
Robin Dunn
Software Craftsman