Can anyone point me in the direction of a small app that displays 2 panels on a frame, where one panel is small and at the top taking up about 20% of the frame, and the other panel takes up 80%.

I would like it to be possible using wxBoxSizer but if anyone can show me any other way in wx then that's grand.

LarZ

Dani AI

Generated

Short summary and two solid options (with example code) that avoid the resizing surprises seen above. showed a simple BoxSizer approach and pointed out using a zero proportion to stop a pane from taking extra space; tried absolute positioning. If the requirement is a fixed pixel height for the top panel use a splitter; if the top should always be 20% of the frame use a sizer plus a tiny resize handler.

Use a splitter for a fixed top height (keeps the top pane at N pixels while the bottom absorbs extra space):

import wx

class F(wx.Frame):
    def __init__(self):
        super().__init__(None, title="Splitter fixed top", size=(400,300))
        splitter = wx.SplitterWindow(self)
        top = wx.Panel(splitter)
        bottom = wx.Panel(splitter)
        splitter.SplitHorizontally(top, bottom, 80)     # top = 80 px
        splitter.SetSashGravity(0.0)                    # extra space goes to bottom
        splitter.SetMinimumPaneSize(20)
        self.SetSizer(wx.BoxSizer(wx.VERTICAL))
        self.GetSizer().Add(splitter, 1, wx.EXPAND)

For a true 20/80 proportional layout that rescales with the frame, use a vertical BoxSizer and update the top panel minimum height in the frame EVT_SIZE handler:

import wx

class F2(wx.Frame):
    def __init__(self):
        super().__init__(None, title="Percent layout", size=(400,300))
        self.top = wx.Panel(self)
        bottom = wx.Panel(self)
        s = wx.BoxSizer(wx.VERTICAL)
        s.Add(self.top, 0, wx.EXPAND)   # fixed by min size
        s.Add(bottom, 1, wx.EXPAND)
        self.SetSizer(s)
        self.Bind(wx.EVT_SIZE, self.OnSize)

    def OnSize(self, evt):
        h = self.GetClientSize().height
        self.top.SetMinSize((-1, int(h * 0.2)))
        self.Layout()
        evt.Skip()

Notes: SplitterWindow methods like SplitHorizontally, SetSashPosition and SetSashGravity are designed for these behaviors; SetMinimumPaneSize prevents accidental unsplitting. (docs.wxpython.org) Using window SetMinSize and calling Layout lets sizers enforce a pixel height derived from a percentage. (docs.wxpython.org)

Tip: call evt.Skip() in EVT_SIZE handlers, and prefer SetMinSize + Layout over calling SetSize directly so the sizer system stays in control.

Recommended Answers

All 4 Replies

Here is one example:

# put two different size panels into a wxBoxSizer

import wx

class MyFrame(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(250, 70))
        panel = wx.Panel(self, -1)
        panel1 = wx.Panel(panel, -1, size=(50, 30))
        panel1.SetBackgroundColour('blue')
        panel2 = wx.Panel(panel, -1, size=(200, 30))
        panel2.SetBackgroundColour('green')
        box = wx.BoxSizer(wx.VERTICAL)
        box.Add(panel1, 1)
        box.Add(panel2, 1)
        panel.SetSizer(box)
        self.Centre()

class MyApp(wx.App):
     def OnInit(self):
         frame = MyFrame(None, -1, 'wxBoxSizer.py')
         frame.Show(True)
         return True

app = MyApp(0)
app.MainLoop()

Thanks a lot, but the panels are still resizing when the frame gets resized, so they're not of a fixed size.

I don't want each panel taking up half of the overall height of the panel/frame in which it is contained. I want at least the top one to stay the same size at all times....

anyone have any ideas?

yeah i decided to ditch the boxsizer, as it has a mind of its own at the best of times... I'm just throwing in the position of the frames and the size as follows:

from wxPython.wx import *

class APanel(wxPanel):
	def __init__(self, parent,size,pos):
		wxPanel.__init__(self,parent,-1,size = size,pos = pos)

class MainFrame(wxFrame):
	def __init__(self, parent, ID, title):
		wxFrame.__init__(self, parent, ID, title, size = (500,500))
		self.testPanel = self.createTestPanel(self)
		self.argsPanel = self.createArgsPanel(self)

	def createTestPanel(self,parent):
		size = wxSize(250, 100)
		pos = (0,0)
		testPanel = APanel(parent,size,pos)
		testPanel.SetBackgroundColour("GRAY")
		print "returning panel %s" %testPanel
		return testPanel
	
	def createArgsPanel(self,parent):
		size = wxSize(250, 300)
		pos = (0,100)
		argsPanel = APanel(parent,size,pos)
		argsPanel.SetBackgroundColour("BLUE")
		print "returning panel %s" %argsPanel
		return argsPanel

class MyApp(wxApp):
	def OnInit(self):
		frame = MainFrame(NULL, -1, "I hate sizers :D")
		frame.Show(1)
		return true

app = MyApp(0)
app.MainLoop()

Just replace the 1 with a zero in Add() ...

box.Add(panel1, 0)
        box.Add(panel2, 0)
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.