wx.Frame.GetChildren() returns empty list with wxpython >= 2.9

Hi,

the following code relies on wx.Frame.GetChildren() to return the list of
all its children. I use this to wait for the event that a child object is
attached to the parent frame. The attachment of the object occurs in the
main wx.App thread and I wait for it from another thread. This worked fine
until at least the latest 2.8 release but with the latest 2.9 release and
any version later it does not work anymore. GetChildren() always returns an
empty list. Am I doing something wrong or was there an intended change of
anything related to my problem?

Regards, Christian

import wx
import wx.lib.newevent

from threading import Thread
import time
import pythoncom

(RunEvent, EVT_RUN) = wx.lib.newevent.NewEvent()

class App(wx.App):
def init(self):
wx.App.init(self, False)
self.result = None

def OnInit(self):
    self.evttarget = self.f = wx.Frame(None)
   
    self.f.Bind(wx.EVT_CLOSE, self.OnClose)
    self.f.Bind(EVT_RUN, self.OnRun)
   
    return True

def OnClose(self, evt):
    self.f.Show(False)
    self.f.DestroyChildren()
   
def OnRun(self, evt):
    callable = getattr(self, evt.callable)
    if hasattr(evt, 'arg'):
        self.result = callable(self.f, *evt.arg)
    else:
        self.result = callable(self.f)
       
def End(self, parent):
    self.f.Destroy()

def hello_world(self, parent):
    dlg = wx.MessageDialog(parent, 'hello world', style=wx.OK)
           
    dlg.Raise()
    if dlg.ShowModal() == wx.ID_OK:
        print 'ok'
       
    dlg.Destroy()  
    return sbj   

class AppLauncher(Thread):
def init(self):
Thread.init(self)

def start(self):
    if not hasattr(self, 'app'):
        Thread.start(self)
        time.sleep(1)

def launch(self, callable, *arg, **kwargs):
    print 'launching',callable,
    evt = RunEvent(callable=callable, arg=arg)
    wx.PostEvent(self.app.evttarget, evt)   

    print 'wait to show'
    # wait until shown
    while len(self.app.evttarget.GetChildren()) == 0:
        pythoncom.PumpWaitingMessages()
        time.sleep(0.3)
       
    print 'wait to hide'
    # wait until hidden
    while len(self.app.evttarget.GetChildren()) > 0:
        pythoncom.PumpWaitingMessages()
        time.sleep(0.3)
    return self.app.result
       
def run(self):
    print 'applauncher started'
    self.app = App()
    self.app.MainLoop()
    print 'applauncher terminated'

def quit(self):
    evt = RunEvent(callable='End')
    wx.PostEvent(AppLauncher.app.evttarget, evt)  
   
def __call__(self):
    return self

AppLauncher = AppLauncher()

if name == ‘main’:
AppLauncher.start()
AppLauncher.launch(‘hello world’)
AppLauncher.quit()

Christian K wrote:

the following code relies on wx.Frame.GetChildren() to return the list of
all its children. I use this to wait for the event that a child object is
attached to the parent frame. The attachment of the object occurs in the
main wx.App thread and I wait for it from another thread. This worked fine
until at least the latest 2.8 release but with the latest 2.9 release and
any version later it does not work anymore. GetChildren() always
returns an
empty list. Am I doing something wrong or was there an intended change of
anything related to my problem?

You do have some philosophical problems here. You always need to
remember that messages in windows are associated with THREADS. Messages
for a window go to the thread that created the window.

You are launching a secondary thread to host your main message loop.
Then, you return from start and call "launch". Launch then posts an
event to the app's frame, telling it to go create a new window. But
then you call GetChildren from the main thread, and you call
PumpWaitingMessages from the main thread. There won't ever BE any
messages in the main thread, because you don't create any messages from
there.

I'm wondering what children you expect to find here. You are calling
GetChildren on the frame inside the app, but I don't see that you ever
create any children for the frame. The frame is a child to the app, but
you're not asking the app.

It certainly seems to me like this could be organized in a more
traditional manner.

···

--
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

Christian K wrote:

the following code relies on wx.Frame.GetChildren() to return the list of
all its children. I use this to wait for the event that a child object is
attached to the parent frame. The attachment of the object occurs in the
main wx.App thread and I wait for it from another thread. This worked fine
until at least the latest 2.8 release but with the latest 2.9 release and
any version later it does not work anymore. GetChildren() always
returns an
empty list. Am I doing something wrong or was there an intended change of
anything related to my problem?

You do have some philosophical problems here. You always need to
remember that messages in windows are associated with THREADS. Messages
for a window go to the thread that created the window.

I should tell, that in the beginning the launch method did nothing more than calling PostEvent. Doing so, everything that took place in the App object was perfectly detached from the main thread. I hope you agree. Later I needed to get results back from the wxpython thread and is where I messed it up a little bit.

You are launching a secondary thread to host your main message loop.
Then, you return from start and call "launch". Launch then posts an
event to the app's frame, telling it to go create a new window. But
then you call GetChildren from the main thread, and you call
PumpWaitingMessages from the main thread. There won't ever BE any
messages in the main thread, because you don't create any messages from
there.

You are right here, I added it as a quick hack without thinking it too much and was happy that it worked in the first place.

I'm wondering what children you expect to find here. You are calling
GetChildren on the frame inside the app, but I don't see that you ever
create any children for the frame. The frame is a child to the app, but
you're not asking the app.

AppLauncher.launch sends a message to the App object, which reacts by calling its own 'hello_world' method which adds a child (a wx.MessageDialog) to the App's frame. There should nothing be wrong with that, I think. From the main thread I need to know when the dialog is attached and removed again to be able to get a result back from the wxpython thread.

It certainly seems to me like this could be organized in a more
traditional manner.

I am happy to hear any suggestion. The background is that I want to open (and interact) with a wxpython app from another program which has its own main loop (python addin in outlook). This is why I have the wxpython app run in its own thread and eventually have it do something.

And it really worked with wxpython 2.8

Christian

···

Am 03.11.14 15:09, schrieb Tim Roberts:

Why not use wxPython as normal, then communicate with your thread via marshalling? This is what I used to setup calling a thread which does work using a COM object, from a wxPython button handler:

[python-win32] COM and threading

https://mail.python.org/pipermail/python-win32/2008-June/007788.html

(I am assuming your outlook add-in code looks similar to this https://chromium.googlesource.com/chromium/deps/python_26/+/f4f5d43a2599abd4ace5dead9d4d0e63e23055b7/Lib/site-packages/win32com/demos/outlookAddin.py )

···

On Monday, November 3, 2014 11:03:51 AM UTC-8, Christian K. wrote:

I am happy to hear any suggestion. The background is that I want to open
(and interact) with a wxpython app from another program which has its
own main loop (python addin in outlook). This is why I have the wxpython
app run in its own thread and eventually have it do something.

AppLauncher.launch sends a message to the App object, which reacts by
calling its own 'hello_world' method which adds a child (a
wx.MessageDialog) to the App's frame. There should nothing be wrong with
that, I think.

Are you assuming that a dialog is a “child” of the window that owns it? That isn’t usually the case. A child window appears inside and as part of another window.

From the main thread I need to know when the dialog is
attached and removed again to be able to get a result back from the
wxpython thread.

I don’t know what you mean by “attached” and “removed”. Do you mean displayed and hidden? Created and destroyed? Those are things you can test.

I am happy to hear any suggestion. The background is that I want to open
(and interact) with a wxpython app from another program which has its
own main loop (python addin in outlook). This is why I have the wxpython
app run in its own thread and eventually have it do something.

This paragraph still confuses me. If you want your wxPython add-in to launch an external wxPython app, you’d do that with subprocess or os.system. If you want your callback to present message dialogs, you can do that without launching an app.

···

On Nov 3, 2014, at 11:03 AM, Christian K. <ckkart@hoc.net> wrote:

Am 03.11.14 15:09, schrieb Tim Roberts:

--
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

Hi, Christian,

AppLauncher.launch sends a message to the App object, which reacts by
calling its own 'hello_world' method which adds a child (a
wx.MessageDialog) to the App's frame. There should nothing be wrong with
that, I think.

Are you assuming that a dialog is a “child” of the window that owns it? That isn’t usually the case. A child window appears inside and as part of another window.

From the main thread I need to know when the dialog is
attached and removed again to be able to get a result back from the
wxpython thread.

I don’t know what you mean by “attached” and “removed”. Do you mean displayed and hidden? Created and destroyed? Those are things you can test.

I am happy to hear any suggestion. The background is that I want to open
(and interact) with a wxpython app from another program which has its
own main loop (python addin in outlook). This is why I have the wxpython
app run in its own thread and eventually have it do something.

What do you mean by interact?
Is it one-way communication or two-way? If its one-way - which way?
Also do you want to send/receive data or messages/events? If data -
which one: classes or simple int/char/string?
If events - which one - system or user-defined?

Also, if you can provide a simple scenario, it would be great.

Thank you.

···

On Tue, Nov 4, 2014 at 12:07 AM, Tim Roberts <timr@probo.com> wrote:

On Nov 3, 2014, at 11:03 AM, Christian K. <ckkart@hoc.net> wrote:

Am 03.11.14 15:09, schrieb Tim Roberts:

This paragraph still confuses me. If you want your wxPython add-in to launch an external wxPython app, you’d do that with subprocess or os.system. If you want your callback to present message dialogs, you can do that without launching an app.
--
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

--
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.

AppLauncher.launch sends a message to the App object, which reacts by
calling its own 'hello_world' method which adds a child (a
wx.MessageDialog) to the App's frame. There should nothing be wrong with
that, I think.

Are you assuming that a dialog is a “child” of the window that owns it? That isn’t usually the case. A child window appears inside and as part of another window.

I assumed that every window/control that gets a reference to the frame via the 'parent' argument would be a child of the frame, regardless if it is displayed whithin the frame or as standalone frame/dialog. And indeed, the 2.8 versions behave like this.

From the main thread I need to know when the dialog is
attached and removed again to be able to get a result back from the
wxpython thread.

I don’t know what you mean by “attached” and “removed”. Do you mean displayed and hidden? Created and destroyed? Those are things you can test.

I mean wxpython object created with reference to the parent frame and removal/destruction of the same object.

I am happy to hear any suggestion. The background is that I want to open
(and interact) with a wxpython app from another program which has its
own main loop (python addin in outlook). This is why I have the wxpython
app run in its own thread and eventually have it do something.

This paragraph still confuses me. If you want your wxPython add-in to launch an external wxPython app, you’d do that with subprocess or os.system. If you want your callback to present message dialogs, you can do that without launching an app.

If the wxpython mainloop runs in the same thread as the addin, then the host app (outlook) will freeze (not completely though). For that reason I moved the part of the addin which needs user interaction (using wxpython) to its own thread.
Actually I assumed that it will be faster to have a wxpython mainloop permanently running in parallel and have it displayed some frames/dialogs on demand than launching a whole python program again and again via subprocess. On the other hand, in same cases I want to get return values from the python subprocess and even more complicated I need to display progress bars which have to be in sync with operations on the host program (outlook).
The design may still be bad though, thank you for your comments.

Christian

···

Am 04.11.14 02:07, schrieb Tim Roberts:

On Nov 3, 2014, at 11:03 AM, Christian K. <ckkart@hoc.net> wrote:

Am 03.11.14 15:09, schrieb Tim Roberts:

Christian K. wrote:

I assumed that every window/control that gets a reference to the frame
via the 'parent' argument would be a child of the frame, regardless if
it is displayed whithin the frame or as standalone frame/dialog. And
indeed, the 2.8 versions behave like this.

I would have to look at the source to be sure, but these terms are
overloaded. Windows has the concept of a parent/child relationship, and
the concept of ownership. The OK button inside a dialog is a child of
(AND is owned by) the dialog that contains it, but the dialog is not a
child of the application that launched it. It is the owner. wxPython
has its own notion of parent and child that do not necessarily line up
with this concept.

I don’t know what you mean by “attached” and “removed”. Do you mean displayed and hidden? Created and destroyed? Those are things you can test.

I mean wxpython object created with reference to the parent frame and
removal/destruction of the same object.

You're mixing concepts way too freely here. Object creation, window
creation, and owner relationships are all quite separate.

I have attached a version of your code that eliminated the dependency on
parent/child relationships. Instead, the frame window registers with
the launcher when it comes up, and deregisters when it goes away. This
seems to solve the problem quite simply.

This paragraph still confuses me. If you want your wxPython add-in to launch an external wxPython app, you’d do that with subprocess or os.system. If you want your callback to present message dialogs, you can do that without launching an app.

If the wxpython mainloop runs in the same thread as the addin, then the
host app (outlook) will freeze (not completely though). For that reason
I moved the part of the addin which needs user interaction (using
wxpython) to its own thread.

Well, sort of. You are still tying up the calling thread, in
AppLauncher.launch.

Actually I assumed that it will be faster to have a wxpython mainloop
permanently running in parallel and have it displayed some
frames/dialogs on demand than launching a whole python program again and
again via subprocess.

I think you are guilty of premature optimization. The overhead here is
insignificant. The Python interpreter will already be in cache, so the
startup time is quick, especially when you're talking about a UI app
that has to wait for the user anyway.

launcher.py (2.44 KB)

···

--
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.