wxpython

wx python how to place panels in a sizer


I have an app in which I want to use one panel to hold images that I can drag and drop, and one that will hold various controls

I cannot make the image panel display correctly:

import  wx

class MainFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1,
                          'Display panels',
                            size=(900, 700))
        sizer = wx.GridBagSizer()
        image_panel = ImagePanel(self)
        sizer.Add(image_panel, pos=(1, 2))
        #self.SetSizer(sizer)
        self.Centre()
        self.Show()

class ImagePanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.Bind(wx.EVT_PAINT, self.on_paint)

    def on_paint(self, event):
        dc = wx.PaintDC(self)
        dc.Clear()
        self.PrepareDC(dc)
        dc = wx.PaintDC(self)
        dc.SetPen(wx.Pen(wx.BLACK, 4))
        dc.DrawLine(0, 0, 500, 500)

if __name__ == '__main__':
    screen_app = wx.App()
    main_frame = MainFrame()
    screen_app.MainLoop()

If the SetSizer line is commented out it works; if it's not commented, it just appears as a small box in top-left

Can anyone help please?


Solution

  • The key is to have a single panel that is the ONLY child widget of the frame. Then you add your other panels as children of that top-level panel.

    Now, I prefer using wx.BoxSizer over wx.GridBagSizer, although I'm sure you could make it work. Anyway, for this example, I will use the BoxSizer:

    import  wx
    
    class MainFrame(wx.Frame):
        def __init__(self):
            wx.Frame.__init__(self, None, -1,
                              'Display panels',
                                size=(900, 700))
            main_panel = wx.Panel(self)
            
            sizer = wx.BoxSizer(wx.VERTICAL)
            image_panel = ImagePanel(main_panel)
            sizer.Add(image_panel, 1, wx.EXPAND|wx.ALL, 5)
            
            # Add your control panel here
            
            main_panel.SetSizer(sizer)
            self.Centre()
            self.Show()
    
    class ImagePanel(wx.Panel):
        def __init__(self, parent):
            wx.Panel.__init__(self, parent)
            self.Bind(wx.EVT_PAINT, self.on_paint)
    
        def on_paint(self, event):
            dc = wx.PaintDC(self)
            dc.Clear()
            self.PrepareDC(dc)
            dc = wx.PaintDC(self)
            dc.SetPen(wx.Pen(wx.BLACK, 4))
            dc.DrawLine(0, 0, 500, 500)
    
    if __name__ == '__main__':
        screen_app = wx.App()
        main_frame = MainFrame()
        screen_app.MainLoop()
    

    As you can see, when we add the ImagePanel to the sizer, I also pass in the proportion it should be, 1, which means that the ImagePanel should take up 100% of the space. I also pass the wx.EXPAND flag which tells wxPython to expand that widget to fill the space.

    You can do something similar with GridBagSizer, but you will need to make the widget cross cell boundaries, and frankly, I find it a bit of a pain. You are welcome to dig into the documentation though if you really want to use that sizer.