Newly shown frame obscured by top window under Windows XP

Hi all,

I’m developing a medium sized wxPython application, and recently I ran into a problem I don’t know how to solve. I distilled the essential part, and I’m attaching an archive of code demonstrating the problem. Roughly, I do this:

  1. Start the application

  2. Open the main window of the application, set it as a Top window. I specify a None parent when creating the window.

  3. This window has a menu that allows me to open a dialog. I specify the main window as a parent of the dialog.

  4. This dialog has a button that a) closes the dialog, b) opens another frame. I specify the main window to be the parent of the frame.

  5. I end up with the main window obscuring the frame from step 4). However, I’d expect the frame to be above the main window - why should be a newly opened frame below any other window?

However, on Linux (GTK), everything works as I’d expect, after 5) main window is below the frame. On windows XP, though, the main window is above.

This was tested with wxPython2.8-unicode-py27 (2.8.12.1). I load the frames from XRC.

I have attached sources demonstrating the problem. Some small parts of the code are not necessary, but since it is just a distilled fraction of a bigger project with my own framework, I wanted to mimic the behaviour of my application as accurately as possible.

Is there anything I have done wrong? How can I remedy/work around this situation?

Thank you very much,

Kind regards,

Tomas Dvorak

main.py (2.44 KB)

resources.xrc (2.32 KB)

When dialogs close they attempt to make the window that was active when they were opened be the active window again, and with the focus in the same widget if possible. So what is happening is that you are creating and showing your new frame and then the reactivation code in the dialog is causing the original frame to be brought back to the top. If you delay the creation of the second frame long enough for the first one to be activated then your second one will be on top when all is done. I think that using wx.CallAfter may not be enough, but using a timer or wx.CallLater with a delay of 100ms probably will be.

···

On 3/15/12 9:52 AM, Tomáš Dvořák wrote:

Hi all,

I'm developing a medium sized wxPython application, and recently I ran
into a problem I don't know how to solve. I distilled the essential
part, and I'm attaching an archive of code demonstrating the problem.
Roughly, I do this:

1) Start the application
2) Open the main window of the application, set it as a Top window. I
specify a None parent when creating the window.
3) This window has a menu that allows me to open a dialog. I specify the
main window as a parent of the dialog.
4) This dialog has a button that a) closes the dialog, b) opens another
frame. I specify the main window to be the parent of the frame.
5) I end up with the main window obscuring the frame from step 4).
However, I'd expect the frame to be above the main window - why should
be a newly opened frame below any other window?

However, on Linux (GTK), everything works as I'd expect, after 5) main
window is below the frame. On windows XP, though, the main window is above.

This was tested with wxPython2.8-unicode-py27 (2.8.12.1). I load the
frames from XRC.

I have attached sources demonstrating the problem. Some small parts of
the code are not necessary, but since it is just a distilled fraction of
a bigger project with my own framework, I wanted to mimic the behaviour
of my application as accurately as possible.

Is there anything I have done wrong? How can I remedy/work around this
situation?

--
Robin Dunn
Software Craftsman

Tomáš Dvořák wrote:

    I'm developing a medium sized wxPython application, and

recently I ran into a problem I don’t know how to solve. I
distilled the essential part, and I’m attaching an archive of
code demonstrating the problem. Roughly, I do this:

  1. Start the application
    2) Open the main window of the application, set it as a Top

window. I specify a None parent when creating the window.

    3) This window has a menu that allows me to open a dialog. I

specify the main window as a parent of the dialog.

    4) This dialog has a button that a) closes the dialog, b)

opens another frame. I specify the main window to be the parent
of the frame.

    5) I end up with the main window obscuring the frame from

step 4). However, I’d expect the frame to be above the main
window - why should be a newly opened frame below any other
window?

You have some subtle interactions here.  Your new window is being

created during the destruction of the modal dialog. At that point,
because the modal message loop is still running, the parent window
is disabled. You then create the new frame window within that same
handler, while the parent is still disabled. When you return from
this handler and the modal dialog message loop ends, the parent
dialog is going to get re-enabled, and that will cause it to pop to
the top of the window stack.

You can prove this by adding a call to "raw_input()" at the end of

DialogStep1.on_proceed. You will see that the new frame IS, in
fact, on top. It’s only after this function returns and the modal
loop ends that the main window pops back up. Note that the modal
loop thing is, in fact, different on different operating systems,
which is why it works on Linux.

So, you need to trigger a call to raise the frame some time in the

future.

class FrameStep2(TopLevelWindow):

    def load_frame(self, parent):

        self.frame = get_resources().LoadFrame(parent,

“Frame_step2”)

    def run(self):

        self.frame.SetRect((30, 30, 500, 100))

        self.frame.Show()

        self.frame.SetRect((30, 30, 500, 100))

        wx.CallLater(50,self.frame.Raise)
···
-- Tim Roberts, Providenza & Boekelheide, Inc.

timr@probo.com

Robin Dunn wrote:

...If you
delay the creation of the second frame long enough for the first one to
be activated then your second one will be on top when all is done. I
think that using wx.CallAfter may not be enough, but using a timer or
wx.CallLater with a delay of 100ms probably will be.

I see that our identical answers crossed in the ether. I did, in fact,
try CallAfter. As you said, it didn't work. It took me some thinking
to figure out why not. I think the reason is that the message to raise
the parent window is getting sent after the modal message loop exits,
which means it will be on the message queue after the CallAfter message.

This is the second time this issue has come up in the past month or
two. I wonder if there isn't a way to architect this in a cleaner way.
Perhaps the "raise" message should be sent directly to the parent
wndproc, instead of going through the message queue? It may be worth a
little thought.

···

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

Thank you so much! I wouldn’t figure this out without your help.

Tomas Dvorak

Dne čtvrtek, 15. března 2012 18:39:05 UTC+1 Tim Roberts napsal(a):

···

Tomáš Dvořák wrote:

    I'm developing a medium sized wxPython application, and

recently I ran into a problem I don’t know how to solve. I
distilled the essential part, and I’m attaching an archive of
code demonstrating the problem. Roughly, I do this:

  1. Start the application
    2) Open the main window of the application, set it as a Top

window. I specify a None parent when creating the window.

    3) This window has a menu that allows me to open a dialog. I

specify the main window as a parent of the dialog.

    4) This dialog has a button that a) closes the dialog, b)

opens another frame. I specify the main window to be the parent
of the frame.

    5) I end up with the main window obscuring the frame from

step 4). However, I’d expect the frame to be above the main
window - why should be a newly opened frame below any other
window?

You have some subtle interactions here.  Your new window is being

created during the destruction of the modal dialog. At that point,
because the modal message loop is still running, the parent window
is disabled. You then create the new frame window within that same
handler, while the parent is still disabled. When you return from
this handler and the modal dialog message loop ends, the parent
dialog is going to get re-enabled, and that will cause it to pop to
the top of the window stack.

You can prove this by adding a call to "raw_input()" at the end of

DialogStep1.on_proceed. You will see that the new frame IS, in
fact, on top. It’s only after this function returns and the modal
loop ends that the main window pops back up. Note that the modal
loop thing is, in fact, different on different operating systems,
which is why it works on Linux.

So, you need to trigger a call to raise the frame some time in the

future.

class FrameStep2(TopLevelWindow):

    def load_frame(self, parent):

        self.frame = get_resources().LoadFrame(parent,

“Frame_step2”)

    def run(self):

        self.frame.SetRect((30, 30, 500, 100))

        self.frame.Show()

        self.frame.SetRect((30, 30, 500, 100))

        wx.CallLater(50,self.frame.Raise)
-- Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.