Spacing radio controls inside wx.RadioBox

Hi, I have a question.

I have a dialog, which contains a radio box with four options among other elements.

Although I specify, that radio box should expand, after resizing the window, all radio controls inside radio box remain group to the left, and radio controls are not spaced inside radio box. The desirable behaviour is that radio controls will be distributed evenly inside radio box space. Is it possible to achieve?

The example code is attached (I have tried as well with wx.StaticBox with RadioButtons inside, but was not able to get desired behaviour)

min_example_radio_box.py (3.7 KB)

I don’t know of a way to space the buttons inside a radio box, but I had a go at putting radio buttons inside a StaticBoxSizer which seems to work (on linux at least).

The code below creates two StaticBoxSizers each containing 4 radio buttons. The buttons inside a box sizer act like a radio box in that only one of them can be selected at any instance. The selection in one box sizer does not affect the selection in the other box sizer.

In the top box sizer, the radio buttons themselves are stretched. This means that, if you click on the space in the box sizer it will select the radio button to its left.

In the bottom box sizer, there are stretchable spacers around each radio button, so you have to click on the radio buttons themselves, not the spacers.

See which style you prefer.

Of course you can’t use EVT_RADIOBOX, but will have to bind EVT_RADIOBUTTON for each radio button to an appropriate handler.

import wx

class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        wx.Frame.__init__(self, *args, **kwds)
        self.SetSize((450, 200))
        self.SetTitle("Expanding 'RadioBoxes'")
        self.panel_1 = wx.Panel(self, wx.ID_ANY)
        sizer_1 = wx.BoxSizer(wx.VERTICAL)

        # Top 'radio box' with stretched radio buttons
        self.panel_2 = wx.Panel(self.panel_1, wx.ID_ANY)
        sizer_1.Add(self.panel_2, 0, wx.EXPAND, 0)
        sizer_2 = wx.StaticBoxSizer(wx.StaticBox(self.panel_2, wx.ID_ANY, "Stretched Radio Buttons"), wx.HORIZONTAL)
        self.radio_btn_1 = wx.RadioButton(sizer_2.GetStaticBox(), wx.ID_ANY, "One", style=wx.RB_GROUP)
        sizer_2.Add(self.radio_btn_1, 1, 0, 0)
        self.radio_btn_2 = wx.RadioButton(sizer_2.GetStaticBox(), wx.ID_ANY, "Two")
        sizer_2.Add(self.radio_btn_2, 1, 0, 0)
        self.radio_btn_3 = wx.RadioButton(sizer_2.GetStaticBox(), wx.ID_ANY, "Three")
        sizer_2.Add(self.radio_btn_3, 1, 0, 0)
        self.radio_btn_4 = wx.RadioButton(sizer_2.GetStaticBox(), wx.ID_ANY, "Four")
        sizer_2.Add(self.radio_btn_4, 1, 0, 0)

        # Bottom 'radio box' with expanding spacers
        self.panel_3 = wx.Panel(self.panel_1, wx.ID_ANY)
        sizer_1.Add(self.panel_3, 0, wx.EXPAND | wx.TOP, 25)
        sizer_3 = wx.StaticBoxSizer(wx.StaticBox(self.panel_3, wx.ID_ANY, "Spaced Radio Buttons"), wx.HORIZONTAL)
        sizer_3.Add((20, 20), 1, 0, 0)
        self.radio_btn_5 = wx.RadioButton(sizer_3.GetStaticBox(), wx.ID_ANY, "One", style=wx.RB_GROUP)
        sizer_3.Add(self.radio_btn_5, 0, 0, 0)
        sizer_3.Add((20, 20), 1, 0, 0)
        self.radio_btn_6 = wx.RadioButton(sizer_3.GetStaticBox(), wx.ID_ANY, "Two")
        sizer_3.Add(self.radio_btn_6, 0, 0, 0)
        sizer_3.Add((20, 20), 1, 0, 0)
        self.radio_btn_7 = wx.RadioButton(sizer_3.GetStaticBox(), wx.ID_ANY, "Three")
        sizer_3.Add(self.radio_btn_7, 0, 0, 0)
        sizer_3.Add((20, 20), 1, 0, 0)
        self.radio_btn_8 = wx.RadioButton(sizer_3.GetStaticBox(), wx.ID_ANY, "Four")
        sizer_3.Add(self.radio_btn_8, 0, 0, 0)
        sizer_3.Add((20, 20), 1, 0, 0)

        self.panel_3.SetSizer(sizer_3)
        self.panel_2.SetSizer(sizer_2)
        self.panel_1.SetSizer(sizer_1)
        self.Layout()


if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame(None)
    frame.Show()
    app.MainLoop()

Hi, Richard, thank you very much

Serhiy

Below is a quick attempt to wrap one of the previous examples as a class derived from wx.Panel. It uses a custom event that the parent can bind to be notified when the selection changes.

import wx

evt_spaced_radiobox_type = wx.NewEventType()
EVT_SPACED_RADIOBOX = wx.PyEventBinder(evt_spaced_radiobox_type, 1)

class SpacedRadioBoxEvent(wx.PyCommandEvent):
    def __init__(self, value=''):
        wx.PyCommandEvent.__init__(self, evt_spaced_radiobox_type, 1)
        self.value = value

    def GetValue(self):
        return self.value


class SpacedRadioBox(wx.Panel):
    def __init__(self, parent, id=wx.ID_ANY, label='', pos=wx.DefaultPosition,
                 size=wx.DefaultSize, choices=None, name="SpacedRadioBox"):
        wx.Panel.__init__(self, parent, id, pos=pos, size=size, name=name)
        if choices is None:
            choices = []
        self.choices = choices
        self.radio_buttons = []

        sizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, label), wx.HORIZONTAL)
        sizer.Add((20, 20), 1, 0, 0)

        for i, choice in enumerate(self.choices):
            if i == 0:
                style = wx.RB_GROUP
            else:
                style = 0
            radio_btn = wx.RadioButton(sizer.GetStaticBox(), wx.ID_ANY, choice, style=style)
            sizer.Add(radio_btn, 0, 0, 0)
            sizer.Add((20, 20), 1, 0, 0)
            self.radio_buttons.append(radio_btn)
            radio_btn.Bind(wx.EVT_RADIOBUTTON, self.OnRadioButton)

        sizer.Add((20, 20), 1, 0, 0)
        self.SetSizer(sizer)

    def GetSelection(self):
        for rb in self.radio_buttons:
            if rb.GetValue():
                return rb.GetLabel()
        return None

    def OnRadioButton(self, event):
        rb = event.GetEventObject()
        label = rb.GetLabel()
        srb_event = SpacedRadioBoxEvent(label)
        srb_event.SetEventObject(self)
        self.GetEventHandler().ProcessEvent(srb_event)


class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        wx.Frame.__init__(self, *args, **kwds)
        self.SetSize((450, 200))
        self.SetTitle("Expanding 'RadioBox'")
        self.main_panel = wx.Panel(self, wx.ID_ANY)
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        self.spaced_radio_box = SpacedRadioBox(self.main_panel, label="Spaced Radio Buttons",
                                               choices=["One", "Two", "Three", "Four"])
        main_sizer.Add(self.spaced_radio_box, 0, wx.EXPAND, 0)
        self.main_panel.SetSizer(main_sizer)
        self.Layout()

        self.spaced_radio_box.Bind(EVT_SPACED_RADIOBOX, self.OnSpacedRadioBox)

        print(f"Initial selection = {self.spaced_radio_box.GetSelection()}")

    def OnSpacedRadioBox(self, event):
        print(event.GetValue())


if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame(None)
    frame.Show()
    app.MainLoop()

Tested using wxPython 4.2.2 gtk3 (phoenix) wxWidgets 3.2.6 + Python 3.12.3 + Linux Mint 22.