GridSizer borders (still) not working as documented

I know we’ve been here before but I had hoped this would have been fixed with a newer release of wxPython. I’ve replaced my original example and replaced it with something much simpler. You had said that you usually avoid using borders with gridsizers, but shouldn’t they still work as documented? Is this a problem with wxPython, or with the underlying wxWidgets?

The following example is taken almost verbatim from wxPython in Action. It demonstrates that the GrizSizer border parameter does not work as described in the documentation. I am using the current versions of both wxPython and Python.



        Demo of possible problem with grid sizer border parameter. What I expect
        to see based on my parameters is a 3x3 grid of BlockWindow objects with
        an outside border of size 10. Instead I get a 3x3 grid with a gap between
        columns 2 & 3, and between rows 2 & 3 (1-relative). The code from this
        example is taken almost verbatim from wxPython in Action. I have made
        minor changes to remove a redundant list (labels), to improve readability,
        and to correct incompatibilities with Python 3.8x. Note that using buttons
        instead of the BlockWindow from the book example produces the same result.

import wx

class BlockWindow(wx.Panel):

    def __init__(self, parent, ID=-1, label="", pos=wx.DefaultPosition, size=(100, 25)):
        wx.Panel.__init__(self, parent, ID, pos, size, wx.RAISED_BORDER, label)

        self.label = label
        self.Bind(wx.EVT_PAINT, self.OnPaint)

    def OnPaint(self, evt):

        sz  = self.GetClientSize()
        dc  = wx.PaintDC(self)
        w,h = dc.GetTextExtent(self.label)
        dc.DrawText(self.label, (sz.width-w)//2, (sz.height-h)//2)

B,T,L,R = wx.BOTTOM, wx.TOP, wx.LEFT, wx.RIGHT

flags = {"one":   T | L,
            "two":   T,
            "three": T | R,
            "four":  L,
            "five":  0,
            "six":   R,
            "seven": B | L,
            "eight": B,
            "nine":  B | R}

class TestFrame(wx.Frame):

    def __init__(self):

        wx.Frame.__init__(self, None, -1, "Sizer Problem Demo")

        sizer = wx.GridSizer(rows=3, cols=3, hgap=0, vgap=0)

        for label,flag in flags.items():
            bw = BlockWindow(self, label=label)
            #bw = wx.Button(self, label=label)  #note - same problem with Buttons
            sizer.Add(bw, 0, flag, 10)


app = wx.App()


Note that FlexGridSizer does not exhibit this odd behaviour.

The key feature of the wx.GridSizer is that all of the “cells” are the same size. So if one item is larger than the others then extra space is added to the other items to make them all the same size again. Take a look at this image, it’s your sample run with the WIT, and having it highlight the sizer:


Look just at “one” and “two” for now. “One” has a top and left border added, and “two” has just the top border. So since “one” is now wider than the others, then that extra width is also added to the cell for item “two”, which shows up on the right side of item “two” because it is aligning to the left by default.

This might make things more obvious. Here is your example again, with only item “one” getting the extra border, (still on the top and left) and all other items being given no border:


OK. It’s not a bug. It’s a feature.

  Seriously, it's good to finally understand why it works the way it

does. I finally have closure. Thanks for taking the time to
explain. I’ll have to look into WIT for future reference. I’m
getting much more familiar with wxPython the more I use it. I’m
slowly converting all my custom vb.Net apps. It’s just so much
nicer working with Python.

  Thanks again.

PS. I just realized what WIT is. I’ve been using it
for a while.