Search code examples
pythonlayoutwxpythonwxwidgetssizer

In wxPython, how can I use sizers to left- and right-justify statictext beneath a slider?


I'm trying to make a GUI like so:

picture of my intended layout

The big square is a wxGrid, the small ones are wxButtons, and they all act fine. The drawing at left is intended to be a wxSlider, with text labels "slow" and "fast" beneath each end of the slider.

So I lay out a bunch of BoxSizers, like this:

hierarchy of BoxSizers

From outside in:

  • Blue is vertical, and contains a wxGrid and the green BoxSizer
  • Green is horizontal, and contains the orange BoxSizer and two buttons
  • Orange is vertical, and contains a wxSlider and the purple BoxSizer
  • Purple is horizontal, and contains two StaticTexts, with the words "slow" and "fast"

But the closest I can get it to render is this. Notice especially how the slider is tiny, and the slow and fast labels (intended to mark the ends of the slider!) are a mess. my crappy outcome

I've messed with alignments and expands, I've read a bunch of posts from other people complaining about BoxSliders, and I've gotten nowhere. I thought for sure I had it when I read about wx.ST_NO_AUTORESIZE, but that didn't do anything. I even rebuilt my window with wxGlade, and got the same thing, especially with the static text laid out far left.

The one thing I haven't done is specified the size of anything in pixels. It doesn't seem like any layout should require that, because who knows what size screen I'll be running on or what a reasonable number of pixels is. And if I've understood proportions correctly in sizers, I don't have to specify sizes in pixels.

But I'm out of ideas, and I haven't even found a good example, just similar veins of frustration. How do I make my slider take up the full width of the orange boxsizer, and how do I get the slow and fast text to label the ends of the slider?

What I'm running, stripped to the layout essentials (and it's got the slider and labels problem):

import wx
import wx.grid

app = wx.App()

frame = wx.Frame(None, title="MyUnhappyLayout")

blue_sizer = wx.BoxSizer(wx.VERTICAL)

grid = wx.grid.Grid(frame)

blue_sizer.Add(grid, 6, wx.EXPAND, 8)

green_sizer = wx.BoxSizer()
blue_sizer.Add(green_sizer, 1, wx.EXPAND)

button1 = wx.Button(frame)
button2 = wx.Button(frame)

slider = wx.Slider(frame, name="Speed", value=1, minValue=1, maxValue=100)

purple_sizer = wx.BoxSizer()
label_slow = wx.StaticText(frame, label="Slow")
label_fast = wx.StaticText(frame, label="Fast")
purple_sizer.Add(label_slow, wx.ALIGN_LEFT)
purple_sizer.Add(label_fast, wx.ALIGN_RIGHT)

orange_sizer = wx.BoxSizer(wx.VERTICAL)
green_sizer.Add(orange_sizer, 2)
orange_sizer.Add(slider)

orange_sizer.Add(purple_sizer, wx.EXPAND)
green_sizer.Add(button1, 1, wx.EXPAND)
green_sizer.Add(button2, 1, wx.EXPAND)

frame.SetSizerAndFit(blue_sizer)
frame.Show()

app.MainLoop()

Solution

  • There is a "built-in" option for showing min and max labels for a slider: use wxSL_MIN_MAX_LABELS when creating it (unless you are using wxWidgets older than 2.9.1).

    Otherwise, for your specific sizer layout (it might be easier to review if you create each sizer just before using it):

    purple_sizer = wx.BoxSizer()
    purple_sizer.Add(label_slow)
    purple_sizer.AddStretchSpacer()
    purple_sizer.Add(label_fast)
    
    orange_sizer = wx.BoxSizer(wx.VERTICAL)
    # when adding to a sizer, the second argument would be proportion;
    # use SizerFlags to avoid mistakenly skipping an argument
    orange_sizer.Add(slider, wx.SizerFlags().Expand())
    orange_sizer.Add(purple_sizer, wx.SizerFlags().Expand())
    
    green_sizer = wx.BoxSizer()
    green_sizer.Add(orange_sizer, wx.SizerFlags(1)) # no need for proportion=2, 1 should do
    green_sizer.Add(button1) # you probably meant to enlarge the slider, not the buttons
    green_sizer.Add(button2)
    
    blue_sizer = wx.BoxSizer(wx.VERTICAL)
    blue_sizer.Add(grid, wx.SizerFlags(1).Expand().Border(8)) # no need for proportion=6, 1 should do
    blue_sizer.Add(green_sizer, wx.SizerFlags().Expand())