Sizers API

I'd like to propose a change to the current API for sizers. Since the
current API isn't documented in the wxWindows reference, this change
shouldn't be too dramatic. I'll outline the existing API followed by
my proposed change.

Existing Sizer API

···

==================

Once you create a sizer, you can modify the list of items in a sizer
using add, insert, prepend and remove operations (and the somewhat
related show, hide, and isShown operations). The items in a sizer can
be any combination of windows, spacers, and other sizers. The C++
signatures differ for each of the operations depending on the type of
item involved. Since Python doesn't support overloaded method names,
wxPython provides two ways of dealing with the C++ API: it provides
separate methods for each type, and it detects the type of the first
parameter within the generic call and then calls the appropriate
specific method. Let's look at a concrete example.

The wxWindow API provides for an Add method, with three different
signatures. wxPython provides the following variations::

    def AddSizer(self, sizer, option=0, flag=0, border=0, userData=wx.NULL):

    def AddSpacer(self, width, height, option=0, flag=0, border=0, userData=wx.NULL):

    def AddWindow(self, window, option=0, flag=0, border=0, userData=wx.NULL):

The code for Add does some type inspection and calls the specific
method::

    def Add(self, *args, **kw):
        if type(args[0]) == type(1):
            apply(self.AddSpacer, args, kw)
        elif isinstance(args[0], wxSizerPtr):
            apply(self.AddSizer, args, kw)
        elif isinstance(args[0], wxWindowPtr):
            apply(self.AddWindow, args, kw)
        else:
            raise TypeError, 'Expected int, wxSizer or wxWindow parameter'

Proposed Sizer API

I find the current solution inelegant and verbose. So I'd like to
propose the following change. If you look at the preceding example
you'll see the the various Add* signatures are very similar. The only
real problem is the spacer variation which requires two initial
parameters -- width and height. But if we express these as a tuple,
then all the signatures will have the same number of elements.

So what I would like to see is the elimination of the Add* variations,
leaving a single Add method that looks like this::

    def Add(self, item, option=0, flag=0, border=0, userData=wx.NULL):
        """Add item to sizer. Item is either a window, sizer, or
        (width, height) tuple representing the size of a spacer."""

The code for this Add method would check the type of the first item
and respond accordingly. In order to maintain backward compatibility,
the actual code will still have to allow the old spacer signature to
work, but I don't think we should document it or encourage its use.
New code examples should use a (width, height) tuple instead.

Similar changes should be made to the Insert*, Prepend*, Remove*,
Hide*, IsShown*, SetItemMinSize*, and Show* methods. Each of these
has two or three variations, in addition to the base method, resulting
in an overly large API. I think the variations should be dropped and
the base signature should be modified, if necessary, to allow multiple
object types for some of its parameters.

Issues

None of the wxPython variants are documented in the wxWindows manual,
so no changes are needed there.

As you've no doubt heard me say before, I believe sooner is better
than later and would like to make this change now.

This change only effects code that uses the wxPython variants, and I
wasn't able to find any examples that did. For instance, the examples
on the wiki use Add(...), not AddWindow(...) or AddSizer(...).

Boa doesn't use sizers, so this change won't effect Boa.

I don't know what the other designers do with sizers. Anyone?

Opinions

What do the rest of you think about this proposal?

What issues have I missed?

--
Patrick K. O'Brien
Orbtech http://www.orbtech.com/web/pobrien
-----------------------------------------------
"Your source for Python programming expertise."
-----------------------------------------------

"Patrick K. O'Brien" wrote:

This change only effects code that uses the wxPython variants, and I
wasn't able to find any examples that did. For instance, the examples
on the wiki use Add(...), not AddWindow(...) or AddSizer(...).

That's because they are not documented or used iont he Demo. I wish I
knew they existed, as I would have used them. Personally, I find this
kind of overloading to be more confusing than helpful, so I'd ratyher
keep AddWindow(), AddSizer(), etc, and depricate the Add().

That being said, if we're going to keep the "overloaded" Add(), I do
like your change a lot. One thought is to have a "spacer" object, but if
there isn't anything useful to put in it other than two integers, then I
suppose that's a bit silly, and a tuple would be fine. After all, I can
always write code like:

SmallSpacer = (2,2)
...
MySizer.Add(SmallSpacer, ...)

A couple of years ago, I wrote a little treatise on wx-dev about a nicer
API for sizers. Being a Pythonista, and having only a smattering of C++
knowledge, I came up with something that used a lot of keyword
arguments, and really couldn't be implimented in C++, but would have
been pretty nive for Python. Robin expressed interest in including it if
I wrote it, but of course, I never got around to it.

Is there interest in a thicker wrapper around Sizers that will make them
easier to understand, I'll try to dig up that email and we can start the
discussion. The obvious downside is that then wxPython Sizers would have
a different API than wxWindows sizers, and that would have impilcations
for docs and GUI builders.

Should I persue this? Are folks interested?

-Chris

···

--
Christopher Barker, Ph.D.
Oceanographer
                                        
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

<snip>

I find the current solution inelegant and verbose. So I'd like
to propose the following change. If you look at the preceding
example you'll see the the various Add* signatures are very
similar. The only real problem is the spacer variation which
requires two initial parameters -- width and height. But if we
express these as a tuple, then all the signatures will have the
same number of elements.

So what I would like to see is the elimination of the Add*
variations, leaving a single Add method that looks like this:

    def Add(self, item, option=0, flag=0, border=0,
            userData=wx.NULL):
        """Add item to sizer. Item is either a window, sizer, or
        (width, height) tuple representing the size of a
        spacer."""

The code for this Add method would check the type of the first
item and respond accordingly. In order to maintain backward
compatibility, the actual code will still have to allow the old
spacer signature to work, but I don't think we should document
it or encourage its use. New code examples should use a (width,
height) tuple instead.

<snip>

Issues

None of the wxPython variants are documented in the wxWindows
manual, so no changes are needed there.

As you've no doubt heard me say before, I believe sooner is
better than later and would like to make this change now.

This change only effects code that uses the wxPython variants,
and I wasn't able to find any examples that did. For instance,
the examples on the wiki use Add(...), not AddWindow(...) or
AddSizer(...).

Boa doesn't use sizers, so this change won't effect Boa.

I don't know what the other designers do with sizers. Anyone?

I make extensive use of sizers in Mindwrapper, and I consider this
change to be a good thing. It would break my code in one spot, but
the fix would actually SIMPLFY my code substantially.

Opinions

What do the rest of you think about this proposal?

Definitely +1

What issues have I missed?

Can't think of any.
Thanks.

···

--- "Patrick K. O'Brien" <pobrien@orbtech.com> wrote:

=====
Donnal Walter
Arkansas Children's Hospital

Chris Barker <Chris.Barker@noaa.gov> writes:

"Patrick K. O'Brien" wrote:

> This change only effects code that uses the wxPython variants, and I
> wasn't able to find any examples that did. For instance, the examples
> on the wiki use Add(...), not AddWindow(...) or AddSizer(...).

That's because they are not documented or used iont he Demo. I wish I
knew they existed, as I would have used them.

I'm documenting them now. That's how I discovered these variations.

Personally, I find this kind of overloading to be more confusing
than helpful, so I'd ratyher keep AddWindow(), AddSizer(), etc, and
depricate the Add().

Hmmm. Why is that? Python is full of full of functions and methods
that accept objects of multiple types: dir(), str(), list(), len().
And I don't find the differences between AddWindow(), AddSizer(), and
AddSpacer() to be significant. What is significant is the redundancy
in documenting these methods, since they all do basically the same
thing -- add an item to the list managed by the sizer -- but take many
parameters than require a good deal of explaining. So I'm genuinely
curious to know why you would favor the specific methods.

That being said, if we're going to keep the "overloaded" Add(), I do
like your change a lot. One thought is to have a "spacer" object, but if
there isn't anything useful to put in it other than two integers, then I
suppose that's a bit silly, and a tuple would be fine. After all, I can
always write code like:

SmallSpacer = (2,2)
...
MySizer.Add(SmallSpacer, ...)

Internally there is a SizerItem class with IsSizer(), IsSpacer(),
IsWindow() methods, but you aren't supposed to create SizerItem
instances directly. They get created when you Add items to the sizer.
I think it would be a bit more Pythonic (as in "explicit is better
than implicit") to create SizerItem instances and then send them to
the Add() method. In that case, the Add() signature would be very
small:

    def Add(self, item):
        """Add SizerItem instance to sizer."""

But this gets us much farther from the wxWindows API. I wouldn't
mind, but others might object.

A couple of years ago, I wrote a little treatise on wx-dev about a nicer
API for sizers. Being a Pythonista, and having only a smattering of C++
knowledge, I came up with something that used a lot of keyword
arguments, and really couldn't be implimented in C++, but would have
been pretty nive for Python. Robin expressed interest in including it if
I wrote it, but of course, I never got around to it.

Is there interest in a thicker wrapper around Sizers that will make them
easier to understand, I'll try to dig up that email and we can start the
discussion. The obvious downside is that then wxPython Sizers would have
a different API than wxWindows sizers, and that would have impilcations
for docs and GUI builders.

Should I persue this? Are folks interested?

I'm definitely interested. I think it's at least worth discussing. I
don't think wxPython programs make enough use of sizers, in part
because they aren't documented sufficiently and explained enough. I'm
working on docs and examples now. But a more Pythonic implementation
might help as well. Perhaps as an alternative on top of, or in
addition to, the implementation that mirrors the wxWindows API.

···

--
Patrick K. O'Brien
Orbtech http://www.orbtech.com/web/pobrien
-----------------------------------------------
"Your source for Python programming expertise."
-----------------------------------------------

...

Issues

None of the wxPython variants are documented in the wxWindows manual,
so no changes are needed there.

As you've no doubt heard me say before, I believe sooner is better
than later and would like to make this change now.

This change only effects code that uses the wxPython variants, and I
wasn't able to find any examples that did. For instance, the examples
on the wiki use Add(...), not AddWindow(...) or AddSizer(...).

Boa doesn't use sizers, so this change won't effect Boa.

I don't know what the other designers do with sizers. Anyone?

Opinions

What do the rest of you think about this proposal?

What issues have I missed?

Am I correct in thinking this would make it easier for Boa to use
sizers? If so, I say go for it! It looks like it woudl make it easier
for me to use them, which also makes me vote for it.

So....

+1

···

On Mon, 2003-03-24 at 10:56, Patrick K. O'Brien wrote:

--
Bill Anderson
RHCE #807302597505773
bill@noreboots.com

Bill Anderson <bill@noreboots.com> writes:

Am I correct in thinking this would make it easier for Boa to use
sizers? If so, I say go for it! It looks like it woudl make it easier
for me to use them, which also makes me vote for it.

I'm not sure. Better ask Riaan. Riaan?

···

--
Patrick K. O'Brien
Orbtech http://www.orbtech.com/web/pobrien
-----------------------------------------------
"Your source for Python programming expertise."
-----------------------------------------------

Patrick K. O'Brien:

Chris Barker:
> A couple of years ago, I wrote a little treatise on wx-dev
> about a nicer API for sizers. Being a Pythonista, and having
> only a smattering of C++ knowledge, I came up with something
> that used a lot of keyword arguments, and really couldn't be
> implimented in C++, but would have been pretty nive for
> Python. Robin expressed interest in including it if I wrote
> it, but of course, I never got around to it.
>
> Is there interest in a thicker wrapper around Sizers that will
> make them easier to understand, I'll try to dig up that email
> and we can start the discussion. The obvious downside is that
> then wxPython Sizers would have a different API than wxWindows
> sizers, and that would have impilcations for docs and GUI
> builders.
>
> Should I persue this? Are folks interested?

I'm definitely interested. I think it's at least worth
discussing. I don't think wxPython programs make enough use
of sizers, in part because they aren't documented sufficiently
and explained enough. I'm working on docs and examples now. But
a more Pythonic implementation might help as well. Perhaps as
an alternative on top of, or in addition to, the implementation
that mirrors the wxWindows API.

Sizers are extremely powerful, but it is common knowledge that they
can be equally confusing. IMHO there are two reasons for this
confusion. The first reason is that the arguments are far from
intuitive and perhaps Chris's API would help with this. (I have
taken a similar approach and implemented three keyword arguments:
resize, margin, and align.)

But the second source of confusion is that if one uses nested
windows (panels) and nested sizers to any degree, one ends up with
two entirely different containment hierarchies. The way I have
handled this is to build sizers into my panels, so that adding a
component to the panel automatically adds it to the sizer. No muss,
no fuss.

···

=====
Donnal Walter
Arkansas Children's Hospital

Donnal Walter <donnalcwalter@yahoo.com> writes:

Sizers are extremely powerful, but it is common knowledge that they
can be equally confusing. IMHO there are two reasons for this
confusion. The first reason is that the arguments are far from
intuitive and perhaps Chris's API would help with this. (I have
taken a similar approach and implemented three keyword arguments:
resize, margin, and align.)

Yeah, border is an unfortunate choice of keywords, in that what a
sizer calls a border is really a margin of empty space, not a visible
line, which is what most would think of as a border. Cascading style
sheets have margin (space around an element), border (visible line of
a certain color, style and width) and padding (space between the
element and its border). Worse, the border parameter is really just
the border width. The actual border depends on what type of border is
specified in the flags parameter, itself a mishmash of border and
resize behavior configuration options.

More unfortunate is the option parameter, which only applies to
BoxSizer, but is defined in the Sizer parent class, and isn't easily
ignored because it is the second parameter and comes before the flag
and border parameters. And the wxWindows docs don't call it option,
they call it proportion, which is a bit more descriptive since it
determines the proportion of space allocated to the item in relation
to the space requested by all items in that particular BoxSizer.

I'm starting to think the entire sizer API is ripe for an overhaul.
If ever there was a place to deviate from wxWindows, surely this is a
pretty good one.

But the second source of confusion is that if one uses nested
windows (panels) and nested sizers to any degree, one ends up with
two entirely different containment hierarchies. The way I have
handled this is to build sizers into my panels, so that adding a
component to the panel automatically adds it to the sizer. No muss,
no fuss.

I think this is worth discussing as well. Robin? Any thoughts?

···

--
Patrick K. O'Brien
Orbtech http://www.orbtech.com/web/pobrien
-----------------------------------------------
"Your source for Python programming expertise."
-----------------------------------------------

Patrick K. O'Brien wrote:

More unfortunate is the option parameter, which only applies to
BoxSizer, but is defined in the Sizer parent class, and isn't easily
ignored because it is the second parameter and comes before the flag
and border parameters. And the wxWindows docs don't call it option,
they call it proportion, which is a bit more descriptive since it
determines the proportion of space allocated to the item in relation
to the space requested by all items in that particular BoxSizer.

I'm starting to think the entire sizer API is ripe for an overhaul.
If ever there was a place to deviate from wxWindows, surely this is a
pretty good one.

I've never seen the point of defining Add in wxSizer if it doesn't have the same interface in all subclasses.

Personally, I'd rather see the sizer API fixed in wxWindows as well (with appropriate backward compatibility), but I don't know what the chances of that happening are. By the way, there has been a lot of discussion on the wx-users list lately of suggestions for wxWindows 3 (including a wx namespace with wx::Frame, etc.) so it would probably make sense for some of the principals here to be involved in that discussion as well.

David

"David C. Fox" <davidcfox@post.harvard.edu> writes:

Patrick K. O'Brien wrote:

> More unfortunate is the option parameter, which only applies to
> BoxSizer, but is defined in the Sizer parent class, and isn't easily
> ignored because it is the second parameter and comes before the flag
> and border parameters. And the wxWindows docs don't call it option,
> they call it proportion, which is a bit more descriptive since it
> determines the proportion of space allocated to the item in relation
> to the space requested by all items in that particular BoxSizer.
> I'm starting to think the entire sizer API is ripe for an overhaul.
> If ever there was a place to deviate from wxWindows, surely this is a
> pretty good one.

I've never seen the point of defining Add in wxSizer if it doesn't
have the same interface in all subclasses.

I think it belongs in Sizer, without the option parameter, which
should be addded in BoxSizer. It's quite possible to implement it
that way in wxPython, in spite of the choices made by wxWindows. In
this particular example I think it would be good to deviate from
wxWindows. Having to specify a option parameter for the Add method of
classes that don't support an option parameter is silly, wasteful, and
confusing.

Personally, I'd rather see the sizer API fixed in wxWindows as well
(with appropriate backward compatibility), but I don't know what the
chances of that happening are. By the way, there has been a lot of
discussion on the wx-users list lately of suggestions for wxWindows 3
(including a wx namespace with wx::Frame, etc.) so it would probably
make sense for some of the principals here to be involved in that
discussion as well.

I agree, but I've only got so much time and energy. Personally, I'd
just as soon fix things in wxPython and let the wxWindows folks catch
up to us. While I'd like the APIs of the two projects to be as close
as possible, in the end I think good design should prevail and is more
important than strict compatibility. But that's just my own personal
opinion.

(Of course, we're not strictly compatible as it is. We call
"proportion," "option," and we introduce a bunch of additional methods
to deal with overloading. Why not take the next step and drop
"option" from everything but BoxSizer? Why not split up the flags
parameter into a more logical grouping?)

···

--
Patrick K. O'Brien
Orbtech http://www.orbtech.com/web/pobrien
-----------------------------------------------
"Your source for Python programming expertise."
-----------------------------------------------

Patrick K. O'Brien:

I'm starting to think the entire sizer API is ripe for an
overhaul. If ever there was a place to deviate from wxWindows,
surely this is a pretty good one.

Donnal Walter:
> But the second source of confusion is that if one uses nested
> windows (panels) and nested sizers to any degree, one ends up
> with two entirely different containment hierarchies. The way
> I have handled this is to build sizers into my panels, so that
> adding a component to the panel automatically adds it to the
> sizer. No muss, no fuss.

I think this is worth discussing as well. Robin? Any thoughts?

While we are tossing things out on the table, how about
wx.StaticBox and wx.StaticBoxSizer? IMHO, these should come for
free: wx.Panel should have a keyword argument (text=None) such that
if a value is given (e.g., text='my title'), the panel should
automatically include the wx.StaticBox and its sizer with an
appropriate sizer nested inside. YMMV

···

=====
Donnal Walter
Arkansas Children's Hospital

Donnal Walter <donnalcwalter@yahoo.com> writes:

Patrick K. O'Brien:
> I'm starting to think the entire sizer API is ripe for an
> overhaul. If ever there was a place to deviate from wxWindows,
> surely this is a pretty good one.
>
> Donnal Walter:
> > But the second source of confusion is that if one uses nested
> > windows (panels) and nested sizers to any degree, one ends up
> > with two entirely different containment hierarchies. The way
> > I have handled this is to build sizers into my panels, so that
> > adding a component to the panel automatically adds it to the
> > sizer. No muss, no fuss.
>
> I think this is worth discussing as well. Robin? Any thoughts?
>

While we are tossing things out on the table, how about
wx.StaticBox and wx.StaticBoxSizer? IMHO, these should come for
free: wx.Panel should have a keyword argument (text=None) such that
if a value is given (e.g., text='my title'), the panel should
automatically include the wx.StaticBox and its sizer with an
appropriate sizer nested inside. YMMV

We could probably implement a StaticPanel that does what you suggest
and make it part of wxPython/lib.

In fact, we could put all of these sizer suggestions into wxPython/lib
and leave the existing sizers compatible with wxWindows for those
folks who want/need compatible versions. (Though there is something
to be said for having only one way to do sizers in wxPython, so I'm
only +0 on that last suggestion.)

···

--
Patrick K. O'Brien
Orbtech http://www.orbtech.com/web/pobrien
-----------------------------------------------
"Your source for Python programming expertise."
-----------------------------------------------

Patrick K. O'Brien wrote:

And the wxWindows docs don't call it option,
they call it proportion, which is a bit more descriptive since it
determines the proportion of space allocated to the item in relation
to the space requested by all items in that particular BoxSizer.

This was just recently changed in the docs. I almost changed it in the wxPython wrappers too but then I remembed the pain of changing the wxNotebook.AddPage parameter names and decided to wait a bit...

If there is to be an API change then there is no reason to not change the parameter names as well.

I'm starting to think the entire sizer API is ripe for an overhaul.
If ever there was a place to deviate from wxWindows, surely this is a
pretty good one.

I'm -1 on doing it in 2.4 since the API of the 2.4 branch should stay as stable as possible... OTOH, I suppose it could be started in 2.4 as long as there was backwards compatibility aliases which would be simple to do.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Patrick K. O'Brien wrote:

Issues

...

This change only effects code that uses the wxPython variants, and I
wasn't able to find any examples that did. For instance, the examples
on the wiki use Add(...), not AddWindow(...) or AddSizer(...).

Boa doesn't use sizers, so this change won't effect Boa.

I don't know what the other designers do with sizers. Anyone?

wxDesigner outputs code using AddWindow, AddSizer and AddSpacer, but a bit of backwards compatibility code will take care of it. It's been a long time since I looked at the code generated by wxGlade so I don't remember about it. Anybody know?

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Patrick K. O'Brien wrote:

What do the rest of you think about this proposal?

I agree with the proposal. I agree with your assertion that changes like
this are better done as we think of them instead of later.

What issues have I missed?

Sounds like you're preserving backwards-compatibility, which is a plus.
Seems like a wx.Size should be just as acceptable as a tuple.

Chris Barker wrote:

That's because they are not documented or used iont he Demo. I wish I
knew they existed, as I would have used them. Personally, I find this
kind of overloading to be more confusing than helpful, so I'd ratyher
keep AddWindow(), AddSizer(), etc, and depricate the Add().

I think the overloading *was* confusing when the width and height were
in there directly. Using Add() always felt awkward. But with Patrick's
proposal, the signature is essentially the same: you're adding one
thing.

Should I persue this? Are folks interested?

I'm always interested in API improvs. I'm even cooking up a few of my
own. :slight_smile:

I also found Will Sadkin's comments on what makes sizers confusing to be
quite cogent.

Patrick K. O'Brien wrote:

   def Add(self, item):
       """Add SizerItem instance to sizer."""
But this gets us much farther from the wxWindows API. I wouldn't
mind, but others might object.

I object. :slight_smile: This feels inconvenient and artificial. It's not even
clear to me from the outside why a sizer needs to wrap things in a
sizer item anyway. Windows, sizers and tuples are already different
types with their own methods and such. But if the implementor of sizers
needed to this, then let it be a private implementation thing.

Patrick K. O'Brien wrote:
[snip]

I'm starting to think the entire sizer API is ripe for an overhaul.
If ever there was a place to deviate from wxWindows, surely this is a
pretty good one.

I agree again. It wasn't until you described the sizer odditities that I
finally understood why using sizers always feels like voo doo.

Patrick K. O'Brien wrote:

While I'd like the APIs of the two projects to be as close
as possible, in the end I think good design should prevail and is
more important than strict compatibility. But that's just my own
personal opinion.

I just wanted to second that.

···

--
Chuck
http://ChuckEsterbrook.com

It's been a
long time since I looked at the code generated by wxGlade so I don't
remember about it. Anybody know?

I do :slight_smile: wxGlade uses only the overloaded versions (Add and Insert), both internally and in the generated code, so as long as these are kept, there are no problems.

Alberto