Search code examples
python-3.xuser-interfacewxpythondesktop-application

wxPython: Changing to other panel in the frame hides the first panel, but displays a tiny box at the top left corner instead of the other panel


My app frame contains a default large panel and two sub-panels inside it. When clicking on a particular menu-item, I want this default panel to change to another panel. My code:

This is the main frame shown when the app is opened. It contains one panel, which also contains two more panels:

import wx

class MainFrame(wx.Frame):
    def __init__(self):
        super().__init__(parent=None, title="My Sample App.", size=(1280, 720))

        self.InitMenuBar()
        self.home_panel = HomePanels(self)

    def InitMenuBar(self):
        # Create Menu Bar
        menubar = wx.MenuBar()

        # Create File Menu
        fileMenu = wx.Menu()
        about_item = fileMenu.Append(wx.ID_ABOUT, 'About', 'Show About')
        self.Bind(wx.EVT_MENU, self.on_about, about_item)
        quit = fileMenu.Append(wx.ID_EXIT, '&Quit', 'Close')
        self.Bind(wx.EVT_MENU, self.OnQuit, quit)

        menubar.Append(fileMenu, '&File')

        self.SetMenuBar(menubar)
        self.Center()

    def OnQuit(self, event):
        self.Close()

    def on_about(self, event):
        # Show the About panel and hide the Main panel
        self.about_panel = AboutPanel(self)
        self.home_panel.Hide()
        self.about_panel.Show()

        self.SetAutoLayout(True)
        self.Layout()

if __name__ == '__main__':
    app = wx.App()
    frame = MainFrame()
    frame.Show()
    app.MainLoop()


Below is the panel that's holding two sub-panels in the main frame:

class HomePanels(wx.Panel):
    def __init__(self, parent):
        super().__init__(parent)

        main_sizer = wx.BoxSizer(wx.HORIZONTAL)

        # Left panel-----------------------------------------------------
        left_panel = wx.Panel(self, style=wx.BORDER_SIMPLE, size=(250, 600))
        left_panel.SetBackgroundColour('blue')
        main_sizer.Add(left_panel, 0, wx.EXPAND | wx.ALL, 1)

        # Right panel-----------------------------------------------------
        right_panel = wx.Panel(self, style=wx.BORDER_SIMPLE)
        right_panel.SetBackgroundColour('#f8f8ff')
        main_sizer.Add(right_panel, 1, wx.RIGHT | wx.BOTTOM | wx.EXPAND, 2)

        self.Center()
        self.SetSizer(main_sizer)
        self.SetAutoLayout(True)
        self.Layout()

My AboutPanel.py. This is what I want to replace with the HomePanel panel when the About option in the File menu is clicked on:

class AboutPanel(wx.Panel):
    def __init__(self, parent):
        super().__init__(parent)

        about_main_sizer = wx.BoxSizer(wx.HORIZONTAL)

        abt_text = wx.StaticText(self, label="About Panel", pos=(10, 10))
        about_main_sizer.Add(abt_text, 1, wx.EXPAND, 2)
        
        self.SetSizer(about_main_sizer)

Running this code produces this output: output-whole

And clicking on the About option in File menu produces this output: output-error

I can't figure out the problem! What's causing the About panel to appear like this?


Solution

  • This code does it. The problem you had is that you have not structured your code properly. Panels should belong to the Frame and should be in sizers. Your code was just placing the about panel on the raw frame and so was appearing as a small square in the top left hand corner - A sure sign that it was not in a sizer.

    import wx
    
    
    class MainFrame(wx.Frame):
        def __init__(self):
            super().__init__(parent=None, title="My Sample App.", size=(1280, 720))
    
            self.InitMenuBar()
            self.home_panel = HomePanels(self)
    
            self.sizer = wx.BoxSizer(wx.HORIZONTAL)
            self.sizer.Add(self.home_panel)
            self.SetSizer(self.sizer)
    
            self.Show()
    
        def InitMenuBar(self):
            # Create Menu Bar
            menubar = wx.MenuBar()
    
            # Create File Menu
            fileMenu = wx.Menu()
            about_item = fileMenu.Append(wx.ID_ABOUT, 'About', 'Show About')
            self.Bind(wx.EVT_MENU, self.on_about, about_item)
            quit = fileMenu.Append(wx.ID_EXIT, '&Quit', 'Close')
            self.Bind(wx.EVT_MENU, self.OnQuit, quit)
    
            menubar.Append(fileMenu, '&File')
    
            self.SetMenuBar(menubar)
            self.Center()
    
        def OnQuit(self, event):
            wx.CallAfter(self.Destroy)
    
        def on_about(self, event):
            # Show the About panel and hide the Main panel
            self._clear_sizer(self.sizer)
            self.about_panel = AboutPanel(self)
            self.sizer.Add(self.about_panel)
            self.Layout()
    
        def _clear_sizer(self, sizer):
            """Remove all of the widgets from the sizer."""
            for child in sizer.GetChildren():
                if child.IsSizer():
                    if child.IsSizer():
                        self._clear_sizer(child.GetSizer())
                else:
                    child.GetWindow().Destroy()
            sizer.Clear()
    
    
    class HomePanels(wx.Panel):
        def __init__(self, parent):
            super().__init__(parent)
    
            main_sizer = wx.BoxSizer(wx.HORIZONTAL)
    
            # Left panel-----------------------------------------------------
            left_panel = wx.Panel(self, style=wx.BORDER_SIMPLE, size=(250, 600))
            left_panel.SetBackgroundColour('blue')
            main_sizer.Add(left_panel, 0, wx.EXPAND | wx.ALL, 1)
    
            # Right panel-----------------------------------------------------
            right_panel = wx.Panel(self, style=wx.BORDER_SIMPLE)
            right_panel.SetBackgroundColour('#f8f8ff')
            main_sizer.Add(right_panel, 1, wx.RIGHT | wx.BOTTOM | wx.EXPAND, 2)
    
            self.Center()
            self.SetSizer(main_sizer)
            self.SetAutoLayout(True)
            self.Layout()
    
    class AboutPanel(wx.Panel):
        def __init__(self, parent):
            super().__init__(parent)
    
            about_main_sizer = wx.BoxSizer(wx.HORIZONTAL)
    
            abt_text = wx.StaticText(self, label="About Panel", pos=(10, 10))
            about_main_sizer.Add(abt_text, 1, wx.EXPAND, 2)
    
            self.SetSizer(about_main_sizer)
    
    
    if __name__ == '__main__':
        wx_app = wx.App()
        MainFrame()
        wx_app.MainLoop()
    

    If you really want to display About wouldn't a dialog be better or even wx.adv.AboutDialogInfo?