buttonalignmentwxpythonsizer

wxPython - Align buttons in an expanding sizer


I'm trying to make a dialog that looks similar to the wx.MessageDialog. There's a section at the bottom that coloured different but in my code, the buttons will not cooperate.

The buttons should be aligned right, and the bottom section should expand to colour it grey. There's probably a simple solution, anyone able to see my error?

This is with the sizer set to expand:

enter image description here

This is with the sizer set not to expand:

enter image description here

Here's a working simplified version of the code which demonstrates the issue. The troublesome code is at the bottom, surrounded with "===":

import wx

class TestDialog(wx.Dialog):
    def __init__(self, parent, msg, title):
        wx.Dialog.__init__(self, parent, id=-1, title=title)
        # outer sizer, this is to allow a spot at the bottom for the buttons
        outerSizer = wx.BoxSizer(wx.VERTICAL)

        # Main sizer for the message dialog
        mainSizer = wx.BoxSizer(wx.HORIZONTAL)

        staticIcon = wx.StaticBitmap(self, bitmap=wx.ArtProvider.GetBitmap(wx.ART_INFORMATION), size=(32,32))
        mainSizer.Add(staticIcon, flag=wx.ALL, border=10)

        # Sizer to hold the message and buttons
        controlsSizer = wx.BoxSizer(wx.VERTICAL)

        # Static text field to show the (error/warning) message on the message dialog
        errorText = wx.StaticText(self, -1, msg, wx.DefaultPosition, wx.DefaultSize, 0)
        errorText.Wrap(600)

        # This is a sizer so we can nest it inside the controls sizer. This allows us to use multiple border flags
        errorTextSizer = wx.BoxSizer(wx.HORIZONTAL)
        errorTextSizer.Add(errorText, flag=wx.TOP, border=15)

        # Add the error text to the controls sizer
        controlsSizer.Add(errorTextSizer, flag=wx.RIGHT, border=10)

        # Outer button panel, to get that slighty greyed look
        outerButtonPanel = wx.Panel(self)
        outerButtonPanelSizer = wx.BoxSizer(wx.HORIZONTAL)
        outerButtonPanel.SetSizer(outerButtonPanelSizer)

        # Button for the "yes" option
        btnYes = wx.Button(outerButtonPanel, label='Yes')
        btnYes.Bind(wx.EVT_BUTTON, self.__yes)
        outerButtonPanelSizer.Add(btnYes, flag=wx.ALIGN_RIGHT | wx.ALL, border=15)

        # Button for the "no" option
        btnNo = wx.Button(outerButtonPanel, label='No')
        btnNo.Bind(wx.EVT_BUTTON, self.__no)
        outerButtonPanelSizer.Add(btnNo, flag=wx.ALIGN_RIGHT | wx.RIGHT | wx.TOP | wx.BOTTOM, border=15)

        outerButtonPanel.SetBackgroundColour(wx.Colour(100, 100, 100)) # find decent colour

        # Add all the sizers to each other, finish up
        mainSizer.Add(controlsSizer)
        outerSizer.Add(mainSizer)

        # ====================================================================
        outerSizer.Add(outerButtonPanel, flag=wx.ALIGN_RIGHT | wx.EXPAND)
        # ====================================================================
        #outerSizer.Add(outerButtonPanel, flag=wx.ALIGN_RIGHT)
        # ====================================================================

        # Done layout
        self.SetSizerAndFit(outerSizer)
        self.CenterOnScreen()

    def __yes(self, evt):
        self.EndModal(wx.ID_YES)

    def __no(self, evt):
        self.EndModal(wx.ID_NO)

if __name__ == '__main__':
    app = wx.App()
    dlg = TestDialog(None, "test test test test test test test test test test test test test test test test", "Test Title")
    val = dlg.ShowModal()
    print "Dialog result: " + str(val == wx.ID_YES)
    app.Exit()

Solution

  • At last, the solution. wx apparently doesn't approve of multiple horizontal box sizers nested together with a panel at the core. More specifically, it doesn't like it when you attempt to align something as well as set it to expand. From what I can tell, you have to nest multiple panels and vertical box sizers (with a horizontal box sizer for the buttons).

    Here's the example above, but working properly. Literally, change

    outerButtonPanelSizer = wx.BoxSizer(wx.VERTICAL)
    

    to

    outerButtonPanelSizer = wx.BoxSizer(wx.HORIZONTAL)
    

    and the whole thing breaks.

    import wx
    
    class TestDialog(wx.Dialog):
        def __init__(self, parent, msg, title):
            wx.Dialog.__init__(self, parent, id=-1, title=title)
            # outer sizer, this is to allow a spot at the bottom for the buttons
            outerSizer = wx.BoxSizer(wx.VERTICAL)
    
            # Main sizer for the message dialog
            mainSizer = wx.BoxSizer(wx.HORIZONTAL)
    
            staticIcon = wx.StaticBitmap(self, bitmap=wx.ArtProvider.GetBitmap(wx.ART_INFORMATION), size=(32,32))
            mainSizer.Add(staticIcon, flag=wx.ALL, border=10)
    
            # Sizer to hold the message and buttons
            controlsSizer = wx.BoxSizer(wx.VERTICAL)
    
            # Static text field to show the (error/warning) message on the message dialog
            errorText = wx.StaticText(self, -1, msg, wx.DefaultPosition, wx.DefaultSize, 0)
            errorText.Wrap(600)
    
            # This is a sizer so we can nest it inside the controls sizer. This allows us to use multiple border flags
            errorTextSizer = wx.BoxSizer(wx.HORIZONTAL)
            errorTextSizer.Add(errorText, flag=wx.TOP, border=15)
    
            # Add the error text to the controls sizer
            controlsSizer.Add(errorTextSizer, flag=wx.RIGHT, border=10)
    
            # Outer button panel, to get that slighty greyed look
            outerButtonPanel = wx.Panel(self)
            outerButtonPanelSizer = wx.BoxSizer(wx.VERTICAL)
            outerButtonPanel.SetSizer(outerButtonPanelSizer)
    
            # inner panel for the buttons (this allows us to right align the buttons
            innerButtonPanel = wx.Panel(outerButtonPanel)
            innerButtonPanelSizer = wx.BoxSizer(wx.HORIZONTAL)
    
            # Button for the "yes" option
            btnYes = wx.Button(innerButtonPanel, label='Yes')
            btnYes.Bind(wx.EVT_BUTTON, self.__yes)
            innerButtonPanelSizer.Add(btnYes, flag=wx.ALL, border=15)
    
            # Button for the "no" option
            btnNo = wx.Button(innerButtonPanel, label='No')
            btnNo.Bind(wx.EVT_BUTTON, self.__no)
            innerButtonPanelSizer.Add(btnNo, flag=wx.RIGHT | wx.TOP | wx.BOTTOM, border=15)
    
            # Add the inner button panel to the outer button panel and align it right
            innerButtonPanel.SetSizer(innerButtonPanelSizer)
            outerButtonPanelSizer.Add(innerButtonPanel, flag=wx.ALIGN_RIGHT)
    
            outerButtonPanel.SetBackgroundColour(wx.Colour(100, 100, 100)) # find decent colour
    
            # Add all the sizers to each other, finish up
            mainSizer.Add(controlsSizer)
            outerSizer.Add(mainSizer)
    
            # ====================================================================
            outerSizer.Add(outerButtonPanel, flag=wx.EXPAND)
    
            # Done layout
            self.SetSizerAndFit(outerSizer)
            self.CenterOnScreen()
    
        def __yes(self, evt):
            self.EndModal(wx.ID_YES)
    
        def __no(self, evt):
            self.EndModal(wx.ID_NO)
    
    if __name__ == '__main__':
        app = wx.App()
        dlg = TestDialog(None, "test test test test test test test test test test test test test test test test", "Test Title")
        val = dlg.ShowModal()
        print "Dialog result: " + str(val == wx.ID_YES)
        app.Exit()