How to get buttons to resize in BoxSizer

Hi. I am trying to create a vertical array of command buttons inside a
scrolled window. If you run the following example you got almost what
Ii want except for one problems - the fifth button is wider than the
other buttons. I deliberately added "-" between several words to
illustrate this. If you remove the "-" then all buttons are the same
size, but now there is blank space between the buttons and the scroll
bar. I admit that I am having some trouble figuring out the sizing
widgets but this should be pretty basic stuff. I am sure it is
something minor.

On a related note - can anyone tell me how to override the default
behaviour for displaying an ampersand in a button? Naturally,
"&String" displays the word "String" with the "S" underlined. Is it
possible to disable this?

import wx

wordlist = "Marley was dead to begin-with-There is no doubt whatever
about that The register of his burial was signed by the clergyman the
clerk the undertaker and the chief mourner".split()

class Frame(wx.Frame):

  def __init__(self):

    wx.Frame.__init__(self,None,-1,"Title",size=wx.Size(300,400))

    scroll = wx.ScrolledWindow(self,-1)
    sizer1 = wx.BoxSizer(wx.HORIZONTAL)

    dy = 0
    x = 0
    y = 0

    for word in wordlist:
      button = wx.Button(scroll,-1,label=word,pos=(x,y))
      w,h = button.GetSizeTuple()
      dy = h + 0
      y = y + dy
      sizer1.Add(button,1,wx.EXPAND)

    scroll.SetScrollbars(0,dy,0,y/dy+1)

    w,h = self.GetSizeTuple()
    self.SetSize((10,400))

    self.Show(1)

···

#######################

app = wx.App(redirect=0)
frame = Frame()
frame.Show()
app.MainLoop()

Of course that one is wider. The 5th string is longer than any other
string. If you want the buttons to be the same size, then set the size
parameter explicitly so they're all the same.

···

On Aug 30, 12:47 pm, Reverend Jim <river...@gmail.com> wrote:

Hi. I am trying to create a vertical array of command buttons inside a
scrolled window. If you run the following example you got almost what
Ii want except for one problems - the fifth button is wider than the
other buttons. I deliberately added "-" between several words to
illustrate this. If you remove the "-" then all buttons are the same
size, but now there is blank space between the buttons and the scroll
bar. I admit that I am having some trouble figuring out the sizing
widgets but this should be pretty basic stuff. I am sure it is
something minor.

-------------------
Mike Driscoll

Blog: http://blog.pythonlibrary.org

Hi. I am trying to create a vertical array of command buttons inside a
scrolled window. If you run the following example you got almost what
Ii want except for one problems - the fifth button is wider than the
other buttons. I deliberately added "-" between several words to
illustrate this. If you remove the "-" then all buttons are the same
size, but now there is blank space between the buttons and the scroll
bar. I admit that I am having some trouble figuring out the sizing
widgets but this should be pretty basic stuff. I am sure it is
something minor.

You did almost everything right, except assign the sizer to the scrolled window. And you used a horizontal sizer but want vertical stacking of buttons.

On a related note - can anyone tell me how to override the default
behaviour for displaying an ampersand in a button? Naturally,
"&String" displays the word "String" with the "S" underlined. Is it
possible to disable this?

Double the &, for example: "&&String"

wordbuttons.py (936 Bytes)

···

On 8/30/10 10:47 AM, Reverend Jim wrote:

--
Robin Dunn
Software Craftsman

Oops...I must still be tired to have missed that...listen to Robin! He
knows!

- Mike

···

On Aug 30, 1:30 pm, Robin Dunn <ro...@alldunn.com> wrote:

On 8/30/10 10:47 AM, Reverend Jim wrote:

> Hi. I am trying to create a vertical array of command buttons inside a
> scrolled window. If you run the following example you got almost what
> Ii want except for one problems - the fifth button is wider than the
> other buttons. I deliberately added "-" between several words to
> illustrate this. If you remove the "-" then all buttons are the same
> size, but now there is blank space between the buttons and the scroll
> bar. I admit that I am having some trouble figuring out the sizing
> widgets but this should be pretty basic stuff. I am sure it is
> something minor.

You did almost everything right, except assign the sizer to the scrolled
window. And you used a horizontal sizer but want vertical stacking of
buttons.

One way to think of how to code sizers is to go from inner to outer,
from control widgets groups (inner) to the frame (outer).

Meaning:
# --------------
First step make windows and widgets
make frame
    make panel 1
        make controls for group 1.1
        make controls for group 1.2
    make panel 2
        make controls for group 2.1
        make controls for group 2.2
# --------------
now make sizers for controls and widgets first
and add the controls to them
        make widget group 1.1 sizer
        add group 1.1 widgets to group sizer 1.1
            sizer1_1.Add(control1, ...)
            sizer1_1.Add(control2, ...)
            sizer1_1.Add(control3, ...) etc.

        make widget group 1.2 sizer
        add group 1.2 widgets to group sizer 1.2
            sizer1_2.Add(control1, ...)
            sizer1_2.Add(control2, ...)
            sizer1_2.Add(control3, ...) etc.

        make widget group 2.1 sizer
        add group 2.1 widgets to group sizer 2.1
             sizer2_1.Add(control1, ...)
             sizer2_1.Add(control2, ...)
             sizer2_1.Add(control3, ...) etc.

        make widget group 2.2 sizer
        add group 2.2 widgets to group sizer 2.2
              sizer2_2.Add(control1, ...)
              sizer2_2.Add(control2, ...)
              sizer2_2.Add(control3, ...) etc.
# -----------------
Next make sizers for the panels and add the widget_group_sizers to the
panel sizers
    make panel one sizer
        add widget-group-1.1 sizer to panel_1 sizer
panel1sizer.Add(sizer1_1, ...)
        add widget-group-1.2 sizer to panel_1 sizer
panel1sizer.Add(sizer1_2, ...)

    make panel 2 sizer
        add widget-group-2.1 sizer to panel_2 sizer
panel2sizer.Add(sizer1_1, ...)
        add widget-group-2.2 sizer to panel_2 sizer
panel2sizer.Add(sizer1_2, ...)
# _____________
Next -assign- (not add) the panel sizers to the panels
(note you do not normally assign sizers to controls - I believe, only
for panels and frames or container windows get this.)
        panel1.SetSizer(sizerpanel1)
        panel2.SetSizer(sizerpanel2)
# _____________
Next optionally make a sizer for the frame and assign the sizer to the
frame.
        frame.SetSizer(framesizer)

If you make just one child panel of the frame the child panel will
fill the frame
If you make just one child control (and no panel) of the frame the
control will fill the frame.
(for example if you add one single multi-line textctrl as the only
child to the frame)
Some OS's have weird visuals if at least one panel is not childed a
frame.

Some things to avoid:
1. Controls get "added" to a sizer. Sizers usually shouldn't
    get assigned to a widget. I have not seen:
    wx.TextCtrl.SetSizer(wx.BoxSizer(wx.HORIZONTAL) )

2. The sizers that have controls or subpanels added to
    them may be added to a panel sizer or frame sizer.
    Then you -assign- the panel/frame sizer to the panel or frame
    like this: panel.SetSizer(mypanel_sizer)

3. The mypanel_sizer should usually only have children added
    to it which belong to the wx object the mypanel_sizer will
    be assigned to.

Things can get weird and broken when you mix up the
parent/child object relationship and the sizer containment
heiarchy. So -add- child objects to a sizer, then -assign- that
sizer to the parent of the child widgets'.

For example:

mainframe() has
   panelA() has
      controlA1
      controlA2
   panelB() has
      controlB1
      controlB2

I believe things can break when you do something like this:
    framesizer = wxboxsizer(wxhorizontal)
    framesizer.add(panelA)
    framesizer.add(panelB)
    framesizer.add(controlA1)
    framesizer.add(controlA2)
    framesizer.add(controlB1)
    framesizer.add(controlB2)

Here controlA1 and controlA2 are children of panelA,
but we missmatch the sizer setup by assigning
controlA1 and controlA2 locations to be controlled
by the frame sizer and not controlA1/2's parent
panelA. This may conflict with panelA
layout algorithms for it's children - but not always.

Note there is a missmatch between the
parent/child wx object relationship and the
sizer hiearchy.

A better way to do it:
    panelAsizer = wxboxsizer(wxhorizontal)
    panelAsizer.Add(controlA1)
    panelAsizer.Add(controlA2)
    panelA.SetSizer(panelAsizer)

    panelBsizer = wxboxsizer(wxhorizontal)
    panelBsizer.Add(controlB1)
    panelBsizer.Add(controlB2)
    panelB.SetSizer(panelBsizer)

    framesizer = wxboxsizer(wxhorizontal)
    framesizer.add(panelA)
    framesizer.add(panelB)
    frame.SetSizer(framesizer)

Thus we keep child wx objects controlled by the sizer of its parent wx
object.

Did I get it right?

DevPlayer
We must use Python to build another super mega computer to figure out
the question to the answer 42.

1. Controls get "added" to a sizer. Sizers usually shouldn't
     get assigned to a widget. I have not seen:
     wx.TextCtrl.SetSizer(wx.BoxSizer(wx.HORIZONTAL) )

Correct. You would only want to use sizers with containers that have children. There are exceptions however. For example, you normally would not think of a wx.Control as being a container, but it could in theory have child widgets (say a textctrl and a spin button) and so it could have a sizer to manage their layout. (Although a wx.Panel would work slightly better in this case...)

2. The sizers that have controls or subpanels added to
     them may be added to a panel sizer or frame sizer.
     Then you -assign- the panel/frame sizer to the panel or frame
     like this: panel.SetSizer(mypanel_sizer)

Yes. In other words, a container with children can HAS-A sizer (using the OOP term) to manage the size and layout of the children.

3. The mypanel_sizer should usually only have children added
     to it which belong to the wx object the mypanel_sizer will
     be assigned to.

Or nested sizers, which in turn contain children of the same container, or nested sizers which contain children of the container or nested sizers, which...

A better way to do it:
     panelAsizer = wxboxsizer(wxhorizontal)
     panelAsizer.Add(controlA1)
     panelAsizer.Add(controlA2)
     panelA.SetSizer(panelAsizer)

     panelBsizer = wxboxsizer(wxhorizontal)
     panelBsizer.Add(controlB1)
     panelBsizer.Add(controlB2)
     panelB.SetSizer(panelBsizer)

     framesizer = wxboxsizer(wxhorizontal)
     framesizer.add(panelA)
     framesizer.add(panelB)
     frame.SetSizer(framesizer)

Thus we keep child wx objects controlled by the sizer of its parent wx
object.

Did I get it right?

Yes, but you came around to it by the long path. In a nutshell: A container widget can use a sizer, possibly with nested sizers, to manage the direct children of that container. Any children of the container which are also containers with children need to have their own sizer.

···

On 8/31/10 2:16 PM, DevPlayer wrote:

--
Robin Dunn
Software Craftsman

My thanks to all who replied, especially DevPlayer who apparently took
the time to write a mini-thesis on the topic. It was very clear and I
appreciate the time you spent teaching a wx noob. Thirty years of
process control (ie non-GUI) real-time programming just did not give
me the background for this. The learning curve is steep (but I am told
it's worth it).

···

On Aug 31, 10:08 pm, Robin Dunn <ro...@alldunn.com> wrote:

On 8/31/10 2:16 PM, DevPlayer wrote:

> 1. Controls get "added" to a sizer. Sizers usually shouldn't
> get assigned to a widget. I have not seen:
> wx.TextCtrl.SetSizer(wx.BoxSizer(wx.HORIZONTAL) )

Correct. You would only want to use sizers with containers that have
children. There are exceptions however. For example, you normally
would not think of a wx.Control as being a container, but it could in
theory have child widgets (say a textctrl and a spin button) and so it
could have a sizer to manage their layout. (Although a wx.Panel would
work slightly better in this case...)

> 2. The sizers that have controls or subpanels added to
> them may be added to a panel sizer or frame sizer.
> Then you -assign- the panel/frame sizer to the panel or frame
> like this: panel.SetSizer(mypanel_sizer)

Yes. In other words, a container with children can HAS-A sizer (using
the OOP term) to manage the size and layout of the children.

> 3. The mypanel_sizer should usually only have children added
> to it which belong to the wx object the mypanel_sizer will
> be assigned to.

Or nested sizers, which in turn contain children of the same container,
or nested sizers which contain children of the container or nested
sizers, which...

> A better way to do it:
> panelAsizer = wxboxsizer(wxhorizontal)
> panelAsizer.Add(controlA1)
> panelAsizer.Add(controlA2)
> panelA.SetSizer(panelAsizer)

> panelBsizer = wxboxsizer(wxhorizontal)
> panelBsizer.Add(controlB1)
> panelBsizer.Add(controlB2)
> panelB.SetSizer(panelBsizer)

> framesizer = wxboxsizer(wxhorizontal)
> framesizer.add(panelA)
> framesizer.add(panelB)
> frame.SetSizer(framesizer)

> Thus we keep child wx objects controlled by the sizer of its parent wx
> object.

> Did I get it right?

Yes, but you came around to it by the long path. In a nutshell: A
container widget can use a sizer, possibly with nested sizers, to manage
the direct children of that container. Any children of the container
which are also containers with children need to have their own sizer.

--
Robin Dunn
Software Craftsmanhttp://wxPython.org

I hate to appear dense but I still need some clarification on sizers.
Even after reading through DevPlayer's scholarly explanation I am at a
loss as to why the following does not do what I expect. I set the
panel sizers (I thought) to expand, yet neither the buttons nor the
text controls expand to fill the frame.

import wx

"""
This code should create a GUI with two panels stacked vertically.
The top panel should contain a button which fills the entire width
of the frame. The bottom panel should contain pairs of text controls
where the left side is lower case text and the right side is upper
case text. Each text control should take up half the width of the
frame. The text controls should expand equally.
"""

words = "it was the best of times it was the worst of times".split()

app = wx.App(redirect=0)
frame = wx.Frame(None)

#create "top" panel and sizer then add a command button

panel1 = wx.Panel(frame)
sizer1 = wx.BoxSizer(wx.HORIZONTAL)

button = wx.Button(panel1,label = "Rename")
sizer1.Add(button,1,wx.EXPAND)

panel1.SetSizer(sizer1)

#create "bottom" panel and sizer then add text controls

panel2 = wx.Panel(frame)
sizer2 = wx.FlexGridSizer(cols=2)

colours = ((200,200,255,255),(200,255,200,255))

for i,word in enumerate(words):

  text = wx.TextCtrl(panel2,value=word,style=wx.TE_READONLY)
  text.BackgroundColour = colours[i%2]
  sizer2.Add(text,1,wx.EXPAND)

  text = wx.TextCtrl(panel2,value=word.upper(),style=wx.TE_READONLY)
  text.BackgroundColour = colours[i%2]
  sizer2.Add(text,1,wx.EXPAND)

panel2.SetSizer(sizer2)

#create frame sizer to force top & bottom orientation

framesizer = wx.BoxSizer(wx.VERTICAL)
framesizer.Add(panel1,0)
framesizer.Add(panel2,0)

frame.SetSizer(framesizer)
frame.SetAutoLayout(1)
framesizer.Fit(frame)

frame.Show()

app.MainLoop()

From: wxpython-users@googlegroups.com [mailto:wxpython-
users@googlegroups.com] On Behalf Of Reverend Jim
Sent: Thursday, September 02, 2010 10:21 AM
To: wxPython-users
Subject: [wxPython-users] Re: How to get buttons to resize in BoxSizer

I hate to appear dense but I still need some clarification on sizers.
Even after reading through DevPlayer's scholarly explanation I am at a
loss as to why the following does not do what I expect. I set the
panel sizers (I thought) to expand, yet neither the buttons nor the
text controls expand to fill the frame.

import wx

"""
This code should create a GUI with two panels stacked vertically.
The top panel should contain a button which fills the entire width
of the frame. The bottom panel should contain pairs of text controls
where the left side is lower case text and the right side is upper
case text. Each text control should take up half the width of the
frame. The text controls should expand equally.
"""

words = "it was the best of times it was the worst of times".split()

app = wx.App(redirect=0)
frame = wx.Frame(None)

#create "top" panel and sizer then add a command button

panel1 = wx.Panel(frame)
sizer1 = wx.BoxSizer(wx.HORIZONTAL)

button = wx.Button(panel1,label = "Rename")
sizer1.Add(button,1,wx.EXPAND)

panel1.SetSizer(sizer1)

#create "bottom" panel and sizer then add text controls

panel2 = wx.Panel(frame)
sizer2 = wx.FlexGridSizer(cols=2)

colours = ((200,200,255,255),(200,255,200,255))

for i,word in enumerate(words):

  text = wx.TextCtrl(panel2,value=word,style=wx.TE_READONLY)
  text.BackgroundColour = colours[i%2]
  sizer2.Add(text,1,wx.EXPAND)

  text = wx.TextCtrl(panel2,value=word.upper(),style=wx.TE_READONLY)
  text.BackgroundColour = colours[i%2]
  sizer2.Add(text,1,wx.EXPAND)

panel2.SetSizer(sizer2)

#create frame sizer to force top & bottom orientation

framesizer = wx.BoxSizer(wx.VERTICAL)
framesizer.Add(panel1,0)
framesizer.Add(panel2,0)

frame.SetSizer(framesizer)
frame.SetAutoLayout(1)
framesizer.Fit(frame)

frame.Show()

app.MainLoop()

--
To unsubscribe, send email to wxPython-
users+unsubscribe@googlegroups.com
or visit http://groups.google.com/group/wxPython-users?hl=en

[alm>]

I think if you add wx.EXPAND to these lines

framesizer.Add(panel1,0)
framesizer.Add(panel2,0)

as in framesizer.Add(panel2,0,wx.EXPAND) it will work. My experience
is that EXPAND allows the contents of the thing being added to expand, not
the thing itself.

Al

···

-----Original Message-----

Almost there. That allows the button in the top panel to fill the
frame width but it does nothing for the text controls in the lower
panel.

If you add in the WIT (http://wiki.wxpython.org/Widget_Inspection_Tool) you'll see that problem with the button is not that the widget are not expanding to fill the panel, but that the panel are not being sized larger as the frame changes size. So changing this:

   framesizer.Add(panel1,0)

to this:

   framesizer.Add(panel1,0, wx.EXPAND)

takes care of that problem.

The other panel has the same problem, and in addition the FLexGridSizer needs to be told which columns are expandable:

   sizer2.AddGrowableCol(0)
   sizer2.AddGrowableCol(1)

sizertest.py (1.58 KB)

···

On 9/2/10 10:21 AM, Reverend Jim wrote:

I hate to appear dense but I still need some clarification on sizers.
Even after reading through DevPlayer's scholarly explanation I am at a
loss as to why the following does not do what I expect. I set the
panel sizers (I thought) to expand, yet neither the buttons nor the
text controls expand to fill the frame.

--
Robin Dunn
Software Craftsman

The AddGrowableCol was the missing piece. I'll be downloading the
Widget Inspection Tool immediately.

And by download, of course I mean just load it from the already (DOH!)
installed wx files. After so many years of trying new software and
finding out the hard way that "oh, by the way, I forgot to mention
that to make this work you also have to download and install... and
the instructions on how to configure it are impossible to find and if
you do they are written in Swahili BWA HAH HAH HAH", it is difficult
to come to terms with the fact that pretty much everything I need is
already included.