Help with PaintEvents and Displaying Multiple Panels

Hey all,

I’m new to wxPython, so there’s no real functionality in this example - I’m just trying to get my bearings, but I essentially want:

2x Panels, children of the main Frame
Panel 1 paints 3 circles
Panel 2 has 2 Text widgets

However, when I add in the Bind for the painting panel, my second panel disappears. I’m mystified how one is affecting the other. Can anyone point me in the right direction?

class Drawing_Panel(wx.Panel):
    
    def __init__(self, *args, **kwargs):
        wx.Panel.__init__(self, *args, **kwargs)
        
        self.SetBackgroundColour(wx.RED)
        self.SetSize(640,480)

        
        self.Bind(wx.EVT_PAINT, self.OnPaint) #Commenting out this line makes the panels display.
        
    def OnPaint(self, event):

        self.dc = wx.PaintDC(self)

        self.dc.SetPen(wx.Pen('WHITE'))

        self.w, self.h = self.GetClientSize()
        self.circle1 = self.dc.DrawCircle(24, 42, 10)
        self.circle2 = self.dc.DrawCircle(247, 17, 10)
        self.circle3 = self.dc.DrawCircle(75, 117, 10)

class Info_Panel(wx.Panel):
    
    def __init__(self, *args, **kwargs):
        wx.Panel.__init__(self, *args, **kwargs)
        
        self.SetBackgroundColour(wx.WHITE)

        
        self.vertical_sizer = wx.BoxSizer(wx.VERTICAL)
        
        self.st_text_widget1 = wx.StaticText(self, label='This is a Test')
        self.st_text_widget2 = wx.StaticText(self, label='This is also a test')
        
        self.vertical_sizer.Add(self.st_text_widget1, 0, wx.EXPAND)
        self.vertical_sizer.Add(self.st_text_widget2, 0, wx.EXPAND)
        
        self.SetSizer(self.vertical_sizer)
        self.SetSize(640,480)

class Main_Frame(wx.Frame):

    def __init__(self, *args, **kw):
        wx.Frame.__init__(self, *args, **kw)

        self.InitUI()

    def InitUI(self):

        self.SetTitle("Test Window Program")
        self.Centre()

        self.drawing_panel = Drawing_Panel(self)
        self.main_information_panel = Info_Panel(self)
        
        self.main_ui_grid_sizer = wx.GridBagSizer(vgap = 0, hgap = 0)
        self.main_ui_grid_sizer.Add(self.drawing_panel, pos=(0,0), flag=wx.EXPAND)
        self.main_ui_grid_sizer.Add(self.main_information_panel, pos=(0,1), flag=wx.EXPAND)

        self.main_ui_grid_sizer.AddGrowableRow(0, 0)
        self.main_ui_grid_sizer.AddGrowableCol(0, 0)
        self.main_ui_grid_sizer.AddGrowableCol(1, 0)

        self.SetSizer(self.main_ui_grid_sizer)
        
        self.SetSize(640*2,480*2)

def main():

    app = wx.App()
    main_frame = Main_Frame(None)
    main_frame.Show()
    app.MainLoop()


if __name__ == '__main__':
    main()

Any help would be very appreciated. Thanks!

For me, your code seems to work.
Maybe you should specify platforms and versions.

Are you using a GridBagSizer to resemble pixel based placement?
You could make your life easier if you would use the simplest applicable sizers instead. The calls to self.SetSize(640,480) are not required. The sizer fills the available space.

        self.main_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.main_sizer.Add(self.drawing_panel, 1, wx.EXPAND)
        self.main_sizer.Add(self.main_information_panel, 1, wx.EXPAND)
        self.SetSizer(self.main_sizer)

Thanks for the reply! It’s good to hear that it works, even if it’s for someone else. So you see two panels, a red one with three circles, and a white with with two text controls on it? All I can see is red with three circles. The white panel and text controls disappear after the screen paints.

I’m definitely using the more complicating sizer - like I said, there isn’t really a point at the moment, I’m not trying to build something specific. I’m trying to understand how wxPython works… by building a series of windows, capturing events, using different sizers, etc. I wrote a few other test programs with box sizers and other controls, different events - they’re very easy to handle, so the actual point of this code was to explore GridBagSizer and try painting the screen.

Essentially, I’m not a GUI programmer at all, and I’m trying to figure out what all this stuff does by tinkering with it.

For reference, I’m using:

Python 3.8.2
‘4.1.0 gtk3 (phoenix) wxWidgets 3.1.4’
Kubuntu 20.4 LTS

Edit: Meant to add:

Thanks for your code - I tried that as well, and I agree it’s definitely the easier/better way to go.

OK, gtk seems to be more picky here than Windows.

Use dc instead of self.dc (i.e. don’t keep a reference).
Also, call dc.Destroy() at the end of the paint event handler.
Otherwise the PaintDC is still active when the other panel is to be painted.

Alternatively you could use the PaintDC as context manager, but this will work only with recent versions of wxPython:

        with wx.PaintDC(self) as dc:
            dc.SetPen(wx.Pen('WHITE'))
    
            self.w, self.h = self.GetClientSize()
            self.circle1 = dc.DrawCircle(24, 42, 10)
            self.circle2 = dc.DrawCircle(247, 17, 10)
            self.circle3 = dc.DrawCircle(75, 117, 10)

That worked! I definitely would not have thought of that - I do notice that some of the wxPython tutorials that I’ve looked through do not usually keep references, but honestly didn’t think anything of it. Any idea why that behavior occurs?

I’ll try out using the PaintDC as a context manager as well. Thanks for your help!

DCs are expected to be transient objects, you create one, use it, and then throw it away. It’s not an inefficient operation to create them only as needed so there is really no need or benefit to keep them around. And, as you’ve seen, on at least some platforms it’s important that this pattern be followed.