Why is my UI element invisible until the user resizes the window?
Question: I have a custom progress bar–like view that basically consists of two sizers
side by side: the one on the left has a red background and the one on the
right has a light gray background. By adjusting the relative sizes of the
sizers I can indicate different percentages. For whatever reason—maybe because
this Window consists only of sizers, with no “substantial” elements—the
progress bar is not visible when its containing Frame first appears. When the
Frame is resized by any amount in any direction, the progress bar suddenly
pops into view, with the correct size and position and everything.

Below is a minimal code sample. It doesn’t quite demonstrate the problem I’m
describing: when run as-is, the progress bar will appear correctly.

If I remove the wx.EXPAND flag from either Add() call in TestWindow.**init** ,
however, then when the window first appears the progress bar will either be
the wrong size or completely invisible. (Resizing the window causes the bar to
become visible and/or take the correct size.) When the run_on_main_thread
decorator is not applied to update_sizes, though, the bar is always displayed
correctly right off the bat, regardless of whether the wx.EXPAND flag was
included!

I’m not sure why my efforts toward thread safety have caused this drawing
issue. Can anyone suggest a way to make the progress bar display correctly
from the beginning, while still ensuring that `update_sizes` will only run on
the main thread? For example, can I trigger an `EVT_SIZE` on the window so
that it will refresh itself without any user interaction?

I’m using Python 2.7.5 and wxPython 2.9.5.0 on OS X 10.9.2.

## Code

    
    
    from functools import wraps
    from math import floor
    import wx
    
    def run_on_main_thread(fn):
        """Decorator. Forces the function to run on the main thread.
    
        Any calls to a function that is wrapped in this decorator will return
        immediately; the return value of such a function is not available.
    
        """
        @wraps(fn)
        def deferred_caller(*args, **kwargs):
            wx.CallAfter(fn, *args, **kwargs)
    
        return deferred_caller
    
    
    class PercentageBar(wx.Window):
        def __init__(self, parent, percentage=0.0):
            wx.Window.__init__(self, parent)
    
            border_color = '#000000'
            active_color = '#cc0000'
            inactive_color = '#d3d7cf'
            height = 24
    
            self.percentage = percentage
    
            self.SetBackgroundColour(border_color)
    
            self.active_rectangle = wx.Panel(self, size=(-1, height))
            self.active_rectangle.SetBackgroundColour(active_color)
    
            self.inactive_rectangle = wx.Panel(self, size=(-1, height))
            self.inactive_rectangle.SetBackgroundColour(inactive_color)
    
            self.sizer = wx.BoxSizer(wx.HORIZONTAL)
            self.SetSizer(self.sizer)
            self.update_sizes()
    
        @run_on_main_thread
        def update_sizes(self):
            self.sizer.Clear(False)
    
            if self.percentage != 0.0 and self.percentage != 1.0:
                active_flags = wx.EXPAND | (wx.ALL & ~wx.RIGHT)
            else:
                active_flags = wx.EXPAND | wx.ALL
    
            self.sizer.Add(self.active_rectangle, floor(1000 * self.percentage),
                    active_flags, 1)
    
            self.sizer.Add(self.inactive_rectangle, floor(1000 * (1.0 - self.percentage)),
                    wx.EXPAND | wx.ALL, 1)
    
            if self.percentage == 0.0:
                self.active_rectangle.Hide()
                self.inactive_rectangle.Show()
            elif self.percentage == 1.0:
                self.active_rectangle.Show()
                self.inactive_rectangle.Hide()
            else:
                self.active_rectangle.Show()
                self.inactive_rectangle.Show()
    
            self.sizer.Layout()
            self.Refresh()
    
    
    class TestWindow(wx.Frame):
        def __init__(self, parent):
            wx.Frame.__init__(self, parent, title='Test')
    
            vertical_sizer = wx.BoxSizer(wx.VERTICAL)
            vertical_sizer.Add((1, 1), 1)
            vertical_sizer.Add(PercentageBar(self, percentage=0.33), 2, wx.EXPAND)
            vertical_sizer.Add((1, 1), 1)
    
            horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)
            horizontal_sizer.Add((1, 1), 1)
            horizontal_sizer.Add(vertical_sizer, 10, wx.EXPAND)
            horizontal_sizer.Add((1, 1), 1)
    
            self.SetSizer(horizontal_sizer)
    
    
    if __name__ == '__main__':
        app = wx.App()
        window = TestWindow(None)
        window.Show()
        app.MainLoop()
    


Answer: Your example is working as expected for me with wxPython 3.0, but your symptom
is a common one. Usually it means that the initial size event is happening
before the sizer has been set, so the default `EVT_SIZE` handler doesn't yet
have a sizer to use for the layout. As soon as the window is resized then the
sizer is used to do the layout and all is as you expected it to be before.

To deal with this problem you just need to trigger the built-in layout
features after the frame and its content have been created. (Usually at the
end of `__init__`) Since the auto-layout is tied to the size events, then you
can use the frame's `SendSizeEvent` method to let it have one. Or you can just
explicily call the `Layout` method yourself.

