Search code examples
pythonwxpythonwxwidgets

wxpython: StaticText vertically centered text with background


I've read so many questions similar to this but I'm caving in and making my own because nothing is working. Basically, I want to have my wx.StaticText have vertically centered text while resizing with the window. My understanding is that existing solutions don't work because I care about the background of the StaticText, and so I cannot simply vertically center the label itself in a sizer. I also see that it requires messy subclassing to have a transparent-background StaticText, so overlaying it on a panel sounds difficult.

Here is a minimal example (in my project the sizer has several other things in it):

import wx

class MyFrame(wx.Frame):
    def __init__(self):
        super().__init__(None, title='Sample')
        sizer=wx.BoxSizer(wx.VERTICAL)

        label = wx.StaticText(self, -1, 'PLEASE VERTICALLY CENTER ME ;(', style=wx.ALIGN_CENTRE | wx.ST_NO_AUTORESIZE)
        label.SetMinSize((300,300))
        label.SetBackgroundColour((255,0,0))
        sizer.Add(label, 1, wx.EXPAND | wx.ALL, 10)

        self.SetSizerAndFit(sizer)

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

Despite that, it's hard to accept it's not simple to vertically center text. What is the easiest way to have the text in the label be vertically centered?


Solution: catalin's answer gave the concept necessary! Here is a full snippet for anyone else who encounters this problem. I added a button below the StaticText to demonstrate the vertical sizer remaining in control. The vertical sizer could be removed altogether if your application doesn't need it.

import wx

class MyFrame(wx.Frame):
    def __init__(self):
        super().__init__(None, title='Sample')
        sizer=wx.BoxSizer(wx.VERTICAL) # "main" sizer

        panel = wx.Panel(self) # panel just for centering label
        panel.SetBackgroundColour((255,0,0))
        label = wx.StaticText(panel, -1, 'PLEASE VERTICALLY CENTER ME ;(', style=wx.ALIGN_CENTRE | wx.ST_NO_AUTORESIZE)
        hsizer=wx.BoxSizer(wx.HORIZONTAL) # sizer for the panel
        hsizer.Add(label, 1, wx.ALIGN_CENTER_VERTICAL)
        panel.SetSizer(hsizer)
        sizer.Add(panel, 1, wx.EXPAND)

        btn = wx.Button(self, -1, 'Button')
        sizer.Add(btn, 0, wx.EXPAND)

        self.SetSizerAndFit(sizer)


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

Solution

  • In c++ a quick way would look like this:

        wxPanel* p = new wxPanel(this); // you don't need to put it in a sizer if it's the only child
        p->SetBackgroundColour({ 255, 0, 0 }); // this will be inherited by children
        wxStaticText* label = new wxStaticText(p, wxID_ANY, "PLEASE VERTICALLY CENTER ME ;(");
    
        wxSizer* s = new wxBoxSizer(wxHORIZONTAL); // for vertical one, you'd need stretch-spacers, and could not use wxALIGN_CENTER_VERTICAL, see below
        s->Add(label, wxSizerFlags(1).Align(wxALIGN_CENTER_VERTICAL));
        p->SetSizer(s);
    

    Mind that if you want the text to wrap upon resize, wxStaticText will probably not resize correctly, and you might need to replace it with something else.