Search code examples
pythonformattingmodal-dialogwxpython

wxPython - Formatting breaks when I add buttons


So I have a simple modal window that has a few lines of text, an input box, and a couple buttons (save and cancel.) However, the window shows up mostly empty, with the buttons visible but clipped by the edge of the window. But when I comment out the buttons, everything else shows up fine. I can't figure out what's wrong with my buttons, and am hoping some extra eyes will do me some good. Any help is appreciated. Thanks!

    panel = wx.Panel(self)
    vbox = wx.BoxSizer(wx.VERTICAL)
    # Add explanation text
    vbox.Add(wx.StaticText(panel, label="By default, the sensors record data each minute."), flag=wx.LEFT|wx.TOP, border=10)
    vbox.Add(wx.StaticText(panel, label="You can change the recording interval here."), flag=wx.LEFT, border=10)
    vbox.Add(wx.StaticText(panel, label="(Should be between 1 and 60 minutes)"), flag=wx.LEFT|wx.BOTTOM, border=10)
    # Make a horizontal line
    line = wx.StaticLine(panel)
    vbox.Add(line, flag=wx.LEFT|wx.BOTTOM|wx.RIGHT|wx.EXPAND, border=7)
    # Create input
    self.interval_input = wx.SpinCtrl(panel, value=self.interval, min=1, max=60)
    vbox.Add(self.interval_input, 0, wx.ALL|wx.CENTER, 5)
    # Make a horizontal line
    line = wx.StaticLine(panel)
    vbox.Add(line, flag=wx.LEFT|wx.TOP|wx.RIGHT|wx.EXPAND, border=7)
    # Add save and cancel buttons
    button_area = wx.BoxSizer(wx.HORIZONTAL)
    self.save_button = wx.Button(self, label='Save')
    self.save_button.Bind(wx.EVT_BUTTON, self.OnSave)
    button_area.Add(self.save_button, flag=wx.RIGHT, border=5)
    self.cancel_button = wx.Button(self, label='Cancel')
    self.cancel_button.Bind(wx.EVT_BUTTON, self.OnCancel)
    button_area.Add(self.cancel_button)
    vbox.Add(button_area, flag=wx.ALIGN_CENTER|wx.TOP|wx.BOTTOM, border=10)
    # Adjust window size to fit content
    panel.SetSizer(vbox)
    vbox.Fit(self)

Solution

  • This is known as a "parenting" problem. The buttons do not have the same parent as the rest of the widgets, which ends up causing them to get stacked up in a weird way. Change the save and cancel button's parents to panel instead of self and it will work correctly.

    Here's a runnable version:

    import wx
    
    class MyApp(wx.Frame):
    
        def __init__(self):
            wx.Frame.__init__(self, None, title='Test')
    
            panel = wx.Panel(self)
            vbox = wx.BoxSizer(wx.VERTICAL)
            # Add explanation text
            vbox.Add(wx.StaticText(panel, label="By default, the sensors record data each minute."), flag=wx.LEFT|wx.TOP, border=10)
            vbox.Add(wx.StaticText(panel, label="You can change the recording interval here."), flag=wx.LEFT, border=10)
            vbox.Add(wx.StaticText(panel, label="(Should be between 1 and 60 minutes)"), flag=wx.LEFT|wx.BOTTOM, border=10)
            # Make a horizontal line
            line = wx.StaticLine(panel)
            vbox.Add(line, flag=wx.LEFT|wx.BOTTOM|wx.RIGHT|wx.EXPAND, border=7)
            # Create input
            self.interval_input = wx.SpinCtrl(panel, value="", min=1, max=60)
            vbox.Add(self.interval_input, 0, wx.ALL|wx.CENTER, 5)
            # Make a horizontal line
            line = wx.StaticLine(panel)
            vbox.Add(line, flag=wx.LEFT|wx.TOP|wx.RIGHT|wx.EXPAND, border=7)
            # Add save and cancel buttons
            button_area = wx.BoxSizer(wx.HORIZONTAL)
            self.save_button = wx.Button(panel, label='Save')
            self.save_button.Bind(wx.EVT_BUTTON, self.OnSave)
            button_area.Add(self.save_button, flag=wx.RIGHT, border=5)
            self.cancel_button = wx.Button(panel, label='Cancel')
            self.cancel_button.Bind(wx.EVT_BUTTON, self.OnCancel)
            button_area.Add(self.cancel_button)
            vbox.Add(button_area, flag=wx.ALIGN_CENTER|wx.TOP|wx.BOTTOM, border=10)
            # Adjust window size to fit content
            panel.SetSizer(vbox)
            #vbox.Fit(self)
    
            self.Show()
    
        def OnSave(self, event):
            pass
    
        def OnCancel(self, event):
            pass    
    
    if __name__ == '__main__':
        app = wx.App(False)
        frame = MyApp()
        app.MainLoop()