Domagoj
Domagoj

Reputation: 1705

Making top window inaccessible in wx.python

How can I make top frame inaccessible while child frame is open?

import wx

class MyFrame(wx.Frame):
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, title=title, size=(400, 320), style=(wx.MINIMIZE_BOX | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN))

        # Setting up menu
        filemenu = wx.Menu()
        set_m = filemenu.Append(wx.ID_PREFERENCES, 'Settings', 'Settings')
        # filemenu.Append(US)
        filemenu.AppendSeparator()
        exit_m = filemenu.Append(wx.ID_EXIT)

        # Creating the menubar
        menubar = wx.MenuBar()
        menubar.Append(filemenu, 'Menu')
        self.SetMenuBar(menubar)

        self.Bind(wx.EVT_MENU, self.OnSettings, set_m)

    def OnSettings(self, e):
        SetFrame().Show()

class SetFrame(wx.Frame):
    title = 'Settings'
    def __init__(self):
        wx.Frame.__init__(self, wx.GetApp().TopWindow, title=self.title, size=(400, 250), style=(wx.MINIMIZE_BOX | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN))


class MyApp (wx.App):
    def OnInit(self):
        self.frame = MyFrame(None, title='Parent-Frame')
        self.frame.Show()
        self.SetTopWindow(self.frame)
        return True

if __name__ == '__main__':
    app = MyApp(0)
    app.MainLoop()

Upvotes: 1

Views: 1383

Answers (3)

user2682863
user2682863

Reputation: 3218

Nested modal dialogs were causing issues with my app so I ended up doing:

self.GetParent().Disable()
self.Enable()
self.Bind(wx.EVT_CLOSE, self.on_close)

def on_close(self, event):
    event.Skip()
    self.GetParent().Enable()
    self.Destroy()

Disabling the parent also disables all child windows, but they can be re-enabled.

EDIT: Also, adding wx.FRAME_FLOAT_ON_PARENT and wx.FRAME_NO_TASKBAR styles to wx.Frame.__init__ can help replicate the behavior of a modal dialog

Upvotes: 1

Mike Driscoll
Mike Driscoll

Reputation: 33071

You can also make the second frame modal just like a dialog. Here's your code modified to do just that:

import wx

class MyFrame(wx.Frame):
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, title=title, size=(400, 320), style=(wx.MINIMIZE_BOX | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN))

        # Setting up menu
        filemenu = wx.Menu()
        set_m = filemenu.Append(wx.ID_PREFERENCES, 'Settings', 'Settings')
        # filemenu.Append(US)
        filemenu.AppendSeparator()
        exit_m = filemenu.Append(wx.ID_EXIT)

        # Creating the menubar
        menubar = wx.MenuBar()
        menubar.Append(filemenu, 'Menu')
        self.SetMenuBar(menubar)

        self.Bind(wx.EVT_MENU, self.OnSettings, set_m)

    def OnSettings(self, e):
        SetFrame().Show()

class SetFrame(wx.Frame):
    title = 'Settings'
    def __init__(self):
        wx.Frame.__init__(self, wx.GetApp().TopWindow, title=self.title, size=(400, 250), style=(wx.MINIMIZE_BOX | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN))
        self.Bind(wx.EVT_CLOSE, self.onClose)
        self.MakeModal()

    #----------------------------------------------------------------------
    def onClose(self, event):
        """
        Make the frame non-modal as it closes to re-enable other windows
        """
        self.MakeModal(False)
        self.Destroy()

class MyApp (wx.App):
    def OnInit(self):
        self.frame = MyFrame(None, title='Parent-Frame')
        self.frame.Show()
        self.SetTopWindow(self.frame)
        return True

if __name__ == '__main__':
    app = MyApp(0)
    app.MainLoop()

Note that you have to disable the modality when you close the settings frame to re-enable the other windows. See http://wxpython-users.1045709.n5.nabble.com/making-a-frame-modal-td2363708.html for more information.

Upvotes: 1

Yoriz
Yoriz

Reputation: 3625

You could use a modal dialog instead although a modal dialog blocks the rest of the application and you might not want that.

To make your frame act like a modal dialog that doesn't block the application, when you open your child frame disable the parent frame, remembering to enable the parent frame again when the child frame closes.

Also its a good idea to set the style wx.FRAME_FLOAT_ON_PARENT on the child frame to keep it on top of its parent frame and setting it to centre on the parent frame.

I've modified you code below to make the top frame inaccessible while child frame is open.

import wx


class MyFrame(wx.Frame):
    def __init__(self, parent, title):
        style = (wx.MINIMIZE_BOX | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX |
                 wx.CLIP_CHILDREN)
        wx.Frame.__init__(self, parent, title=title, size=(400, 320),
                          style=style)

        # Setting up menu
        filemenu = wx.Menu()
        set_m = filemenu.Append(wx.ID_PREFERENCES, 'Settings', 'Settings')
        # filemenu.Append(US)
        filemenu.AppendSeparator()
        exit_m = filemenu.Append(wx.ID_EXIT)

        # Creating the menubar
        menubar = wx.MenuBar()
        menubar.Append(filemenu, 'Menu')
        self.SetMenuBar(menubar)

        self.Bind(wx.EVT_MENU, self.OnSettings, set_m)

    def OnSettings(self, e):
        SetFrame().Show()


class SetFrame(wx.Frame):
    title = 'Settings'

    def __init__(self):
        style = (wx.MINIMIZE_BOX | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX |
                 wx.CLIP_CHILDREN | wx.FRAME_FLOAT_ON_PARENT)
        wx.Frame.__init__(self, wx.GetApp().TopWindow, title=self.title,
                          size=(400, 250), style=style)

        self.CenterOnParent()
        self.GetParent().Disable()

        self.Bind(wx.EVT_CLOSE, self.onClose)

    def onClose(self, event):
        self.Close()
        event.Skip()

    def Close(self):
        self.GetParent().Enable()
        self.Destroy()


class MyApp (wx.App):
    def OnInit(self):
        self.frame = MyFrame(None, title='Parent-Frame')
        self.frame.Show()
        self.SetTopWindow(self.frame)
        return True

if __name__ == '__main__':
    app = MyApp(0)
    app.MainLoop()

Upvotes: 2

Related Questions