How do I remove unused space on a resize

One more kick at the cat on sizers if you please. I have an application that consists of one frame with two panels. The left panel contains a nested series of panels and buttons that I want to retain their aspect ratios. The right panel contains a vertical stack of control buttons. I have everything working (spacing, borders, resizing, etc) except for one little annoying detail.

When I resize the app I can’t get rid of extraneous frame. As you can see by the images, one has extra space at the bottom and the other at the right. I will never get extra space on both the right and bottom because the controls will always resize to one edge or the other. My structure was created through wxGlade and looks like

Application
    Frame
        BoxSizer (wx.VERTICAL)
            OuterPanel (wx.EXPAND proportion=1)
                BoxSizer (wx.HORIZONTAL)
                    Panel (wx.EXPAND wx.SHAPED proportion=0)
                        (stuff inside)
                    Panel (wx.ALIGN_RIGHT proportion=0)
                        (stuff inside)

The sizers can maintain the aspect ratio of the things inside it, but they can’t do anything with the parent window if it is resized to something that is not the correct aspect ratio. All it can do is maintain the aspect ratio of the item(s) it is managing, and the parent window will be left with empty space, as you’ve discovered.

There are a couple options that I can think of:

  1. Don’t let your frame be resized. This can be done by removing the resize flags from the style passed to the frame’s constructor, or you can call SetSizeHints to constrain to a size range.

  2. Forcibly resize the frame back to a size conforming to the aspect ratio after they have resized it. You can catch the frame’s EVT_SIZE and then set or reset a timer. When the timer expires, presumably because the user has stopped sizing, then you can reset the frame’s size to conform. Be sure to call event.Layout in the EVT_SIZE handler so the sizer layout will be performed, and also guard against recursion. (Where your SetSize causes another EVT_SIZE which causes you to call SetSize again…) Note that users tend to not like this UX at all.

  3. Leave it as-is. It may feel not so nice to the developers, but users are usually able to deal with sizing beyond the aspect ratio and don’t usually care too much about it. I saw one application that simply painted the area with a different color and a message like “This area intentionally left blank” :smile: Maybe you could fill the area behind the main panels with a wallpaper-like image related to sudoku?

Robin

I found two ways to do what I wanted but it took many hours of experimenting (and much coffee).

First method

class Frame(wx.Frame):    

    def __init__(self):

        super(Frame, self).__init__(None, -1, TITLE)

        self.sudoku   = wx.Panel(self, wx.ID_ANY)
        self.puzzle   = Puzzle(self.sudoku)
        self.controls = Controls(self.sudoku, self.puzzle)

        hsizer = wx.BoxSizer(wx.HORIZONTAL)
        hsizer.Add(self.puzzle,   0, wx.EXPAND | wx.SHAPED, PUZZBORDER)
        hsizer.Add(self.controls, 0, wx.EXPAND | wx.ALIGN_RIGHT, CTRLBORDER)
        self.sudoku.SetSizerAndFit(hsizer)

        vsizer = wx.BoxSizer(wx.VERTICAL)
        vsizer.Add(self.sudoku, 1, wx.EXPAND, 0)
        self.SetSizerAndFit(vsizer)

        self.Bind(wx.EVT_SIZE, self.OnSize)

def OnSize(self, event):
    
    event.Skip()
    self.Layout()

    ss = self.sudoku.GetSize()
    ps = self.puzzle.GetSize()
    cs = self.controls.GetSize()

    ss.Width  = ps.Width + cs.Width
    ss.Height = ps.Height

    self.sudoku.SetMinSize(ss)
    self.Fit()

Second (and more general) method

class Frame(wx.Frame):    

    def __init__(self):

        super(Frame, self).__init__(None, -1, TITLE)

        self.sudoku   = wx.Panel(self, wx.ID_ANY)
        self.puzzle   = Puzzle(self.sudoku)
        self.controls = Controls(self.sudoku, self.puzzle)

        hsizer = wx.BoxSizer(wx.HORIZONTAL)
        hsizer.Add(self.puzzle,   0, wx.EXPAND | wx.SHAPED, PUZZBORDER)
        hsizer.Add(self.controls, 0, wx.EXPAND | wx.ALIGN_RIGHT, CTRLBORDER)
        self.sudoku.SetSizerAndFit(hsizer)

        vsizer = wx.BoxSizer(wx.VERTICAL)
        vsizer.Add(self.sudoku, 1, wx.SHAPED, 0)
        self.SetSizerAndFit(vsizer)

        self.Bind(wx.EVT_SIZE, self.OnSize)

def OnSize(self, event):
    
    event.Skip()
    self.Layout()
    self.sudoku.SetMinSize(self.sudoku.GetSize())
    self.Fit()