Elias
Elias

Reputation: 35

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()

Upvotes: 0

Views: 732

Answers (2)

Rolf of Saxony
Rolf of Saxony

Reputation: 22458

Another example of the sizer concept.
The frame is broken up into component parts, with each part assigned its relevant sizer.
The component sizers are then put within a main (self) sizer, to put it all together.
In this case the components are text, that needs to be centered and a set of buttons to the right, vertically stacked.
Obviously, components and arrangements differ but the concept of breaking the design into parts, which is then assembled for final presentation, remains the same.
Indeed, complicated components often need to have the same principle applied, leaving you with a series of sub-assemblies before the final one, in the main sizer.

import wx

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

        panel = wx.Panel(self) # panel for label
        button_panel = wx.Panel(self) # panel for buttons
        panel.SetBackgroundColour((190,0,0))
        button_panel.SetBackgroundColour((160,0,0))

        label = wx.StaticText(panel, -1, 'PLEASE VERTICALLY CENTER ME ;(')
        btn1 = wx.Button(button_panel,-1, 'Button 1')
        btn2 = wx.Button(button_panel,-1, 'Button 2')
        btn3 = wx.Button(button_panel,-1, 'Button 3')
        btn4 = wx.Button(button_panel,-1, 'Button 4')

        tsizer.AddSpacer(10) 
        tsizer.Add(label, 0, wx.CENTER)
        tsizer.AddSpacer(10) # Ensure gap between text and buttons

        bsizer.Add(btn1)
        bsizer.Add(btn2)
        bsizer.Add(btn3)
        bsizer.Add(btn4, 0, wx.BOTTOM, 35) # With space below

        panel.SetSizer(tsizer)
        button_panel.SetSizer(bsizer)

        mainsizer.Add(panel, proportion=1, flag=wx.EXPAND) # panel to grow when resized
        mainsizer.Add(button_panel, proportion=0, flag=wx.EXPAND) # panel to fill available space

        self.SetSizerAndFit(mainsizer)

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

enter image description here

Upvotes: 1

catalin
catalin

Reputation: 1987

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.

Upvotes: 1

Related Questions