dialogwxpythonmodeless

wxPython: Why does modeless, non modal dialog stay on top of parent window?


I want to open a dialog modeless with Show(). After opening I can click on the parent window so the dialog opened successfully modeless. The problem I have is that the modeless dialog always stays on top of the parent window. I want the parent to get on top when I click on it.

It doesn't matter wether I give the dialog a parent window or not. I test for the wx.STAY_ON_TOP style which is False for the dialog.

With simple code below (copied and modified from https://www.tutorialspoint.com/wxpython/wx_dialog_class.htm) I'm having the same problem.

How can I get a modeless dialog that can get behind the parent window?

import wx

# -----------------------------------------------------------------------------------
class MyDialog(wx.Dialog):
    def __init__(self, parent, title):
        super(MyDialog, self).__init__(parent, title=title, size=(250, 150))
        panel = wx.Panel(self)
        self.btn = wx.Button(panel, wx.ID_OK, label="ok", size=(50, 20), pos=(75, 50))

        style = self.GetWindowStyle()
        if style & wx.STAY_ON_TOP:
            print('STAY_ON_TOP = True')
        else:
            print('STAY_ON_TOP = False')

# -----------------------------------------------------------------------------------
class Mywin(wx.Frame):

    def __init__(self, parent, title):
        super(Mywin, self).__init__(parent, title=title, size=(300, 300))
        self.InitUI()

    def InitUI(self):
        panel = wx.Panel(self)
        btn1 = wx.Button(panel, label="Modal Dialog", pos=(20, 10))
        btn2 = wx.Button(panel, label="Modeless Dialog Parent", pos=(20, 40))
        btn3 = wx.Button(panel, label="Modeless Dialog Parentless", pos=(20, 70))

        btn1.Bind(wx.EVT_BUTTON, self.OnModal)
        btn2.Bind(wx.EVT_BUTTON, self.OnModelessParent)
        btn3.Bind(wx.EVT_BUTTON, self.OnModelessParentless)
        self.Show(True)

    def OnModal(self, event):
        MyDialog(self, "Dialog").ShowModal()

    def OnModelessParent(self, event):
        dlg = MyDialog(self, "Dialog").Show()

    def OnModelessParentless(self, event):
        dlg = MyDialog(None, "Dialog").Show()

# -----------------------------------------------------------------------------------
ex = wx.App()
Mywin(None, 'Modal / Modeless')
ex.MainLoop()

Solution

  • Adding the style wx.DIALOG_NO_PARENT to your dialog's constructor allows the dialogs to float behind the frame. Ex:

    import wx
    
    
    # -----------------------------------------------------------------------------------
    class MyDialog(wx.Dialog):
        def __init__(self, parent, title):
            super(MyDialog, self).__init__(parent, title=title, size=(250, 150),
                                           style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT)
            panel = wx.Panel(self)
            self.btn = wx.Button(panel, wx.ID_OK, label="ok", size=(50, 20), pos=(75, 50))
    
            style = self.GetWindowStyle()
            if style & wx.STAY_ON_TOP:
                print('STAY_ON_TOP = True')
            else:
                print('STAY_ON_TOP = False')
    
    
    # -----------------------------------------------------------------------------------
    class Mywin(wx.Frame):
    
        def __init__(self, parent, title):
            super(Mywin, self).__init__(parent, title=title, size=(300, 300))
            self.InitUI()
    
        def InitUI(self):
            panel = wx.Panel(self)
            btn1 = wx.Button(panel, label="Modal Dialog", pos=(20, 10))
            btn2 = wx.Button(panel, label="Modeless Dialog Parent", pos=(20, 40))
            btn3 = wx.Button(panel, label="Modeless Dialog Parentless", pos=(20, 70))
    
            btn1.Bind(wx.EVT_BUTTON, self.OnModal)
            btn2.Bind(wx.EVT_BUTTON, self.OnModelessParent)
            btn3.Bind(wx.EVT_BUTTON, self.OnModelessParentless)
            self.Show(True)
    
        def OnModal(self, event):
            MyDialog(self, "Dialog").ShowModal()
    
        def OnModelessParent(self, event):
            MyDialog(self, "Dialog").Show()
    
        def OnModelessParentless(self, event):
            MyDialog(None, "Dialog").Show()
    
    
    # -----------------------------------------------------------------------------------
    ex = wx.App()
    Mywin(None, 'Modal / Modeless')
    ex.MainLoop()
    

    I removed the variable assignment dlg = on your 2 modeless event handlers because Show() returns True not the dialog instance.

    It should also be noted that dialogs aren't automatically destroyed and you must call Destroy() manually to release their memory when you're done with them.