(Yet another) sizer question
Frank Millman
frank at chagford.com
Mon Jun 5 07:10:22 PDT 2006
Hi all
I have managed to avoid sizer problems until now. I do not understand them
in depth, but I try to keep my requirements simple, so I am usually able to
get a reasonable layout without too much trouble. Now I have a more complex
situation which I cannot figure out.
As I understand things, you can look at sizers from two points of view.
Assume you have a panel with a number of controls to be laid out.
1. If you supply the external dimensions of the outermost window, the sizer
algorithm calculates how to distribute the controls in the available space,
using the parameters provided.
2. If you do not have a starting dimension, the sizer algorithm can
calculate the minimum dimensions of the outermost window required to lay out
the controls using the parameters provided.
I use sizers primarily for the second purpose. Typically I will set up a
frame and a panel without giving a size parameter. I then populate the panel
with various controls, add them to a sizer, call panel.Layout(), and then
call frame.SetClientSize(panel.GetSize()). It works well enough for most
situations I have come up with so far.
A comment from Robin in a recent post confused me. "panel.Layout checks if
the panel has a sizer and if so it calls the sizer's SetDimension method
passing it the current size of the panel." I do call panel.Layout, but
typically at that point the panel has a default dimension of (20,20). It
still seems to work, though.
There is an implication with my approach. Not all controls can calculate
their minimum size themselves, so I have to supply a size. Some are easy -
for textCtrl's I supply an arbitrary length, with a height of -1. Others are
trickier. For a Grid, I take the number of rows and columns, multiply them
by the default RowHeight and ColHeight, and use the result to call
SetSizeHints(). For a treeCtrl I have not figured out a method, so I just
pass an arbitrary size to SetSizeHints(). I have not used treeCtrl's much
yet, so I don't know if this will prove to be a problem in practice.
Now to my problem. Here is the layout -
Frame
|
- Panel
|
- SplitterWindow (Horizontal)
|
- HtmlWindow
|
- SplitterWindow (Vertical)
|
- Panel (left) - a tree control
|
- Panel (right) - a form with various controls
The first splitter window is used to display some text in an HtmlWindow. The
window may not be big enough to display all the text, so with a splitter the
user can drag the sash down to reveal all of it. I know there is a scroll
bar, but that is not as nice as being able to read all the text at a glance.
The second splitter window is used to display a tree control in one of the
panels. It is not possible to predict how wide or deep the tree might
become, but with a splitter the user can expand the panel to show as much of
the tree as is required.
I can set up sizers for each panel separately and they work fine, but as
soon as I try to put it all together, I cannot get it to display each panel
at its optimum size.
Attached is a sample program. You can vary the display by changing the value
of 'action' near the end. You can display the Html panel, the Tree panel,
the Form panel, the Tree and Form panels togther, or all three panels
combined.
Here are some of my questions -
1. If I run it as is, the Tree panel and Form panel are displayed correctly,
but the Html panel's height is about 3 times normal.
2. If I uncomment self.Layout() and self.SetSizerAndFit(sizer) in
TriPanel(), the Html panel has the correct height, but the tree Panel is
shrunk to its minimum width.
3. The horizontal splitter sash can be dragged near to the bottom of the
window, but cannot be dragged near to the top. I have set the minimum pane
size to 20, but I cannot make the Html panel less than its SetSizeHints
height. However, I have also called SetSizeHints on the tree control, and I
can drag its sash left to the limit of 20, which is much less than its
SetSizeHints width.
4. To set the frame to its correct size, I have to call
SetClientSize(panel.GetBestSize()). In my live app, I call
SetClientSize(panel.GetSize()), but here, GetSize() returns (20,20), even
after calling Layout(). I have not been able to determine what is different
between this and my live app.
All of the above is using 2.6.3.2, on both Linux and MSW (Windows Server
2003).
Sorry for such a long message. Hopefully someone can either give me some
quick answers to sort out my problem, or a pointer to some examples or
reading material that will enable me to understand it better and work it out
from first principles.
Thanks
Frank Millman
-------------- next part --------------
#!/usr/bin/env python
import wx
from wx.html import HtmlWindow
#----------------------------------------------------------------------
class TriPanel(wx.Panel):
def __init__(self,window):
wx.Panel.__init__(self,window,-1)
mainFrame = wx.SplitterWindow(self,-1)
subFrame = wx.SplitterWindow(mainFrame,-1)
textpanel = HtmlPanel(mainFrame)
formpanel = FormPanel(subFrame)
treepanel = TreePanel(subFrame)
mainFrame.SetMinimumPaneSize(20)
mainFrame.SplitHorizontally(textpanel, subFrame)
subFrame.SetMinimumPaneSize(20)
subFrame.SplitVertically(treepanel, formpanel)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(treepanel,1,wx.EXPAND)
sizer.Add(formpanel,1,wx.EXPAND)
subFrame.SetSizer(sizer)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(textpanel,1,wx.EXPAND|wx.LEFT|wx.RIGHT,2)
sizer.Add(subFrame,1,wx.EXPAND)
mainFrame.SetSizer(sizer)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(mainFrame,1,wx.EXPAND|wx.LEFT|wx.RIGHT,2)
self.SetSizer(sizer)
# self.Layout()
# self.SetSizerAndFit(sizer)
#----------------------------------------------------------------------
class BiPanel(wx.Panel):
def __init__(self,window):
wx.Panel.__init__(self,window,-1)
mainFrame = wx.SplitterWindow(self,-1)
formpanel = FormPanel(mainFrame)
treepanel = TreePanel(mainFrame)
mainFrame.SetMinimumPaneSize(20)
mainFrame.SplitVertically(treepanel, formpanel)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(treepanel,1,wx.EXPAND|wx.TOP|wx.BOTTOM,5)
sizer.Add(formpanel,1,wx.EXPAND|wx.TOP|wx.BOTTOM,5)
mainFrame.SetSizer(sizer)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(mainFrame,1,wx.EXPAND|wx.LEFT|wx.RIGHT,2)
self.SetSizer(sizer)
#----------------------------------------------------------------------
class HtmlPanel(HtmlWindow):
def __init__(self,parent):
HtmlWindow.__init__(self,parent,-1)
text = """<html><body><font size=+1>
Use the left-hand pane to maintain the hierarchy.
<br>
Use the right-hand pane to maintain the details.
</font></body></html>
"""
if 'gtk2' in wx.PlatformInfo:
self.SetStandardFonts()
self.SetPage(text)
self.SetSizeHints(*(380,70)) # hardcoded - not ideal
#----------------------------------------------------------------------
class TreePanel(wx.Panel):
def __init__(self,splitter):
wx.Panel.__init__(self,splitter,-1)
tree = wx.TreeCtrl(self,-1,style=wx.TR_HAS_BUTTONS)
tree.SetSizeHints(240,120)
root = tree.AddRoot('Root')
for i in range(1,5):
node = tree.AppendItem(root,'%s' % i)
for j in range(1,5):
leaf = tree.AppendItem(node,'%s.%s' % (i,j))
tree.Expand(root)
tree.SetFocus()
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(tree,1,wx.EXPAND|wx.ALL,5)
self.SetSizer(sizer)
#----------------------------------------------------------------------
class FormPanel(wx.Panel):
def __init__(self,splitter):
wx.Panel.__init__(self,splitter,-1)
gbs = wx.GridBagSizer(8,8)
gbs.SetEmptyCellSize((0,0))
gbs.Add((0,0),(0,0)) # space at top row, left column
gbs.Add(wx.StaticText(self,-1,"Fld 1"),(1,1),flag=wx.ALIGN_CENTRE_VERTICAL)
gbs.Add(wx.TextCtrl(self,-1,"Field 1",size=(120,-1)),(1,2))
gbs.Add(wx.StaticText(self,-1,"Fld 2"),(2,1),flag=wx.ALIGN_CENTRE_VERTICAL)
gbs.Add(wx.TextCtrl(self,-1,"Field 2",size=(120,-1)),(2,2))
gbs.Add(wx.StaticText(self,-1,"Fld 3"),(3,1),flag=wx.ALIGN_CENTRE_VERTICAL)
gbs.Add(wx.TextCtrl(self,-1,"Field 3",size=(120,-1)),(3,2))
gbs.Add(wx.StaticText(self,-1,"Fld 4"),(4,1),flag=wx.ALIGN_CENTRE_VERTICAL)
gbs.Add(wx.TextCtrl(self,-1,"Field 4",size=(120,-1)),(4,2))
gbs.Add(wx.StaticText(self,-1,"Fld 5"),(5,1),flag=wx.ALIGN_CENTRE_VERTICAL)
gbs.Add(wx.TextCtrl(self,-1,"Field 5",size=(120,-1)),(5,2))
gbs.Add((4,4),(3,3)) # space at bottom row, right column
help = wx.StaticText(self,-1,'This is a help message')
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(gbs,0,wx.EXPAND)
sizer.Add((1,10),0,wx.EXPAND)
sizer.Add(help,0,wx.ALL,5)
self.SetSizer(sizer)
#----------------------------------------------------------------------
class MyApp(wx.App):
def OnInit(self):
frame = wx.Frame(None,-1,'Test splitter')
action = 5
if action == 1:
panel = HtmlPanel(frame)
elif action == 2:
panel = TreePanel(frame)
elif action == 3:
panel = FormPanel(frame)
elif action == 4:
panel = BiPanel(frame) # Tree/Form
elif action == 5:
panel = TriPanel(frame) # Html/Tree/Form
frame.SetClientSize(panel.GetBestSize())
frame.CentreOnScreen()
frame.Show(True)
self.SetTopWindow(frame)
return True
#----------------------------------------------------------------------
app = MyApp(0) # Create an instance of the application class
app.MainLoop() # Tell it to start processing events
More information about the wxpython-users
mailing list