Enable/disable menu items

Hi,

What do folks use to keep there pulldown menu item enable/disable states
correct while an app is being used?

Obviously, I could make sure that at every step of my code I'm invoking
Enable() on the right items with the right value. I fear that will be
complicated.

What I would really prefer is to have the menu item do a callback right
before it displayed itself. The callback returns True/False if the item
is enabled.

Thinking out loud, maybe I can listen for a Paint event or something
similar on the item and make the callback. Here's some pseudo code:

  folderDeleteMenuItem.isEnabledCallback = self.hasSelectedFolder
  ...
  def hasSelectedFolder(self):
     return self.selFolder is not None
  ...
  def onPaintMenuItem(self, event):
    menuItem.object = event.object
    callback = getattr(menuItem, 'isEnabledCallback', None)
    if callback is not None:
       menuItem.Enable(callback)

So there would be only one event handler to make this mechanism work.
There would be as many callbacks as you need. Some menu items might not
need them; other menu items might share the same callback.

I see that Robb Shecter is doing something high level (see 2003-04-08
"Mindwrapper, PythonCard and the wxMenu API request"), but it wasn't
clear to me what kicks off his UpdateUI() method.

Is anyone doing anything like what I describe above?

···

--
Chuck
http://ChuckEsterbrook.com

There was a little discussion a while back about the Sizers API, and
that wasn't the first time.

In working on Vicky's recent problem, I had some more thoughts about why
sizers are so tricky. It breaks down into two problems:

1) The Sizer.Add() call:
  - All those flags that pertain to different things all in one pile.
  - The behavior in the "primary" direction and "secondary" direction
being handled completely differently. Do we even need the Boxsizer?
could there just be a FlexGridSizer, and it could happen to have one row
or one column?

The Add call is more of a newbie problem than a persistent one, although
I always have to refer to the docs to even understand an Add() call I
wrote a little while back. In this case, breaking it into more
parameters, most of them named and optional, would make it a whole lot
more clear. For example:

def Add(self,
        widget, # could be a wxWindow, wxSizer, or (x,y) spacer tuple
        alignment = wxCENTER, # could be wxTOP, wxBOTTOM, wxRIGHT,
wxLEFT
        StretchFactorVertical = 0, # any integer >=0
        StretchFactorHorizontal = 0, # any integer >=0
        Borders = wxALL,# could be wxTOP, wxBOTTOM, wxLEFT, wxRIGHT or
wxALL
        BorderSize = 0, # any integer >= 0
        Flags = None) # other flags, such as wxADJUST_MINSIZE (or should
wxADJUST_MINSIZE be default?)

This has not been well thought out, but I think the idea is clear. It
would make it more verbose to write an Add call, but when you read it
back later, it would be very clear. Plus, most of the parameters are
optional, so in many cases, you wouldn't have to write much.

2) The whole collection of:
       self.SetAutoLayout(true)
       self.SetSizer(box)
       box.Fit(self)
       self.Layout()
       self.Fit()
  - While I can imagine that there are times when the programmer would
need complete control over these processes, most of the time, I just put
all this boiler plate in, and I have no idea when or whether it is all
necessary.

Why are all these calls required? couldn't there be one self.SetSizer()
call, that called all the rest? Or even better, if a window has one
child sizer at the end of it's __init__, those all get called. In that
case, it would mostly do what we all want by default. This would help a
lot to address the quote by Edward K. Ream that I posted a link to
recently:

"""
Tk is clearly the highest level GUI around. The proof of this statement
is direct: everything in Tk relates to _what_ is to appear on the
screen, and nothing in Tk relates to _how_ that is to happen. So
everything I write is essential. Q.E.D.
"""
(from: http://sensei.co.il/python/tk_wx.ui.html)

He's absolutely right when it comes to wxSizers: the basic Add calls
define how you want the controls to look, but all that other stuff is
about how to do it.

I may get a chance to prototype some of my ideas about the Add() call,
but I'm out of my depth when it comes to the simplification of the other
calls. Someone that really gets Sizers would need to help out with that.

Anyway, more kindling on the fire...

-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

I share Chris' opinions on both counts.

Another idea is to change the term "border" to "pad" and provide a
keyword arg like:
  padAll = 16
  padLeft = 4
  padTop = 8
  padHorz = 4 # does left and right

Another idea that I'd like to experiment with is having sizers be
subclassed from wx.Window so there is one containment hierarchy. And
simultaneously doing away with the SetAutoLayout-SetSizer-Fit-Layout
voo doo as a result.

If we're going to get serious about a new way of laying out controls, we
should really list the requirements and some use cases. Otherwise,
we'll invent partial solutions.

So there's more food for thought...

···

On Friday 25 April 2003 04:05 pm, Chris Barker wrote:

I may get a chance to prototype some of my ideas about the Add()
call, but I'm out of my depth when it comes to the simplification of
the other calls. Someone that really gets Sizers would need to help
out with that.

Anyway, more kindling on the fire...

--
Chuck
http://ChuckEsterbrook.com

Chuck Esterbrook <ChuckEsterbrook@yahoo.com> writes:

Hi,

What do folks use to keep there pulldown menu item enable/disable states
correct while an app is being used?

Obviously, I could make sure that at every step of my code I'm invoking
Enable() on the right items with the right value. I fear that will be
complicated.

This is not a particularly elegant way to do it, but for now I'm using
the following in PyCrust/PyAlaMode/etc. (All my Py* programs inherit
from the same base frame class, defined in frame.py.)

I've got a frame that creates a menu. For each menu item, I associate
the same Update UI event handler, like this:

        wx.EVT_UPDATE_UI(self, ID_NEW, self.OnUpdateMenu)
        wx.EVT_UPDATE_UI(self, ID_OPEN, self.OnUpdateMenu)
        wx.EVT_UPDATE_UI(self, ID_REVERT, self.OnUpdateMenu)
        wx.EVT_UPDATE_UI(self, ID_CLOSE, self.OnUpdateMenu)
        wx.EVT_UPDATE_UI(self, ID_SAVE, self.OnUpdateMenu)
        wx.EVT_UPDATE_UI(self, ID_SAVEAS, self.OnUpdateMenu)
        ...

The handler is coded as this:

    def OnUpdateMenu(self, event):
        """Update menu items based on current status and context."""
        win = wx.wxWindow_FindFocus()
        id = event.GetId()
        event.Enable(True)
        try:
            if id == ID_NEW:
                event.Enable(hasattr(self, 'bufferNew'))
            elif id == ID_OPEN:
                event.Enable(hasattr(self, 'bufferOpen'))
            elif id == ID_REVERT:
                event.Enable(hasattr(self, 'bufferRevert') and self.hasBuffer())
            elif id == ID_CLOSE:
                event.Enable(hasattr(self, 'bufferClose') and self.hasBuffer())
            elif id == ID_SAVE:
                event.Enable(hasattr(self, 'bufferSave') and self.bufferHasChanged())
            elif id == ID_SAVEAS:
                event.Enable(hasattr(self, 'bufferSaveAs') and self.hasBuffer())
            elif id == ID_NAMESPACE:
                event.Enable(hasattr(self, 'updateNamespace') and self.hasBuffer())
            elif id == ID_PRINT:
                event.Enable(hasattr(self, 'bufferPrint') and self.hasBuffer())
            elif id == ID_UNDO:
                event.Enable(win.CanUndo())
            elif id == ID_REDO:
                event.Enable(win.CanRedo())
            elif id == ID_CUT:
                event.Enable(win.CanCut())
            elif id == ID_COPY:
                event.Enable(win.CanCopy())
            elif id == ID_COPY_PLUS:
                event.Enable(win.CanCopy() and hasattr(win, 'CopyWithPrompts'))
            elif id == ID_PASTE:
                event.Enable(win.CanPaste())
            elif id == ID_PASTE_PLUS:
                event.Enable(win.CanPaste() and hasattr(win, 'PasteAndRun'))
            elif id == ID_CLEAR:
                event.Enable(win.CanCut())
            elif id == ID_SELECTALL:
                event.Enable(hasattr(win, 'SelectAll'))
            elif id == ID_AUTOCOMP_SHOW:
                event.Check(win.autoComplete)
            elif id == ID_AUTOCOMP_MAGIC:
                event.Check(win.autoCompleteIncludeMagic)
            elif id == ID_AUTOCOMP_SINGLE:
                event.Check(win.autoCompleteIncludeSingle)
            elif id == ID_AUTOCOMP_DOUBLE:
                event.Check(win.autoCompleteIncludeDouble)
            elif id == ID_CALLTIPS_SHOW:
                event.Check(win.autoCallTip)
            elif id == ID_WRAP:
                event.Check(win.GetWrapMode())
            else:
                event.Enable(False)
        except AttributeError:
            # This menu option is not supported in the current context.
            event.Enable(False)

It works, but like I said it isn't a very elegant approach. So I'd be
interested in a better menu API if we can come up with one.

···

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

Chuck Esterbrook <ChuckEsterbrook@yahoo.com> writes:

···

On Friday 25 April 2003 04:05 pm, Chris Barker wrote:
> I may get a chance to prototype some of my ideas about the Add()
> call, but I'm out of my depth when it comes to the simplification of
> the other calls. Someone that really gets Sizers would need to help
> out with that.
>
> Anyway, more kindling on the fire...

I share Chris' opinions on both counts.

Another idea is to change the term "border" to "pad" and provide a
keyword arg like:
  padAll = 16
  padLeft = 4
  padTop = 8
  padHorz = 4 # does left and right

I think "margin" is a better descriptor for what border does. The
difficults with specific suggestions such as this one is that they
would require us to deviate from the wxWindows API, and I doubt Robin
will go for that. However, I don't see why we couldn't create a new
sizer API that translated into the existing API behind the scenes.

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

Chris Barker wrote:

There was a little discussion a while back about the Sizers API, and
that wasn't the first time.

In working on Vicky's recent problem, I had some more thoughts about why
sizers are so tricky. It breaks down into two problems:

1) The Sizer.Add() call:
  - All those flags that pertain to different things all in one pile.

This is to my mind the most confusing thing about sizers: there is no logical reason why the alignment flags and the secondary direction growth flags should be stuffed into the same argument.

  - The behavior in the "primary" direction and "secondary" direction
being handled completely differently. Do we even need the Boxsizer?
could there just be a FlexGridSizer, and it could happen to have one row
or one column?

Here, I disagree. FlexGridSizer doesn't allow for different proportions for growable rows/columns or fixed aspect ratio, so I think a combination BoxSizer and FlexGridSizer would be more confusing than either of the separate classes. Besides, the difference between the directions has always made sense to me.

2) The whole collection of:
       self.SetAutoLayout(true)
       self.SetSizer(box)
       box.Fit(self)
       self.Layout()
       self.Fit()
  - While I can imagine that there are times when the programmer would
need complete control over these processes, most of the time, I just put
all this boiler plate in, and I have no idea when or whether it is all
necessary.

Much of it isn't since 2.3.something, but I always forget which parts are, because I've got lots of code written for 2.2.x where more of them were. I think box.Fit still needs to be a separate call, because it means that the window should be sized to fit the contents, which is different from the default behavior where the contents are sized to fit the window.

Why are all these calls required? couldn't there be one self.SetSizer()
call, that called all the rest? Or even better, if a window has one
child sizer at the end of it's __init__, those all get called. In that
case, it would mostly do what we all want by default. This would help a
lot to address the quote by Edward K. Ream that I posted a link to
recently:

Doing something based on where a statement is in the code is bad practice, even if it is possible, which I doubt. If I add a statement after another statement, I don't expect it to change the effect of the first statement. Also, sizers are children of windows.

David

Chuck Esterbrook wrote:

Chris Barker wrote:
> I may get a chance to prototype some of my ideas about the
> Add() call, but I'm out of my depth when it comes to the
> simplification of the other calls. Someone that really gets
> Sizers would need to help out with that.
>
> Anyway, more kindling on the fire...

I share Chris' opinions on both counts.

Another idea is to change the term "border" to "pad" and
provide a keyword arg like:
  padAll = 16
  padLeft = 4
  padTop = 8
  padHorz = 4 # does left and right

My own preference is "margin".

Another idea that I'd like to experiment with is having sizers
be subclassed from wx.Window so there is one containment
hierarchy. And simultaneously doing away with the
SetAutoLayout-SetSizer-Fit-Layout voo doo as a result.

Exactly! And it does work:

http://mindwrapper.org/prelim/pretutorial.html#controls

Also notice that simply including:

   text = 'Actual'

creates a StaticTextBox inside the panel with a built-in
StaticTextBox sizer.

Just-wishing-it-were-further-along-ly yours,

Donnal Walter, MD
Arkansas Children's Hospital

I've not looked at this before, and have a few questions.

In the docs:
"wxWindows will call your member functions in idle time"
How does a wxEVT_UPDATE_UI event get triggered?

"The same handler can update a menu item and toolbar button, if the identifier is the same."
Can different UI items have the same id? It doesn't confuse wxWindows?

Thanks to anyone who can enlighten me.

Chris.

···

----- Original Message -----
From: Patrick K. O'Brien
To: wxPython-users@lists.wxwindows.org
Sent: Saturday, April 26, 2003 10:20 AM
Subject: Re: [wxPython-users] Enable/disable menu items

Chuck Esterbrook <ChuckEsterbrook@yahoo.com> writes:

Hi,

What do folks use to keep there pulldown menu item enable/disable states
correct while an app is being used?

Obviously, I could make sure that at every step of my code I'm invoking
Enable() on the right items with the right value. I fear that will be
complicated.

This is not a particularly elegant way to do it, but for now I'm using
the following in PyCrust/PyAlaMode/etc. (All my Py* programs inherit
from the same base frame class, defined in frame.py.)

I've got a frame that creates a menu. For each menu item, I associate
the same Update UI event handler, like this:

        wx.EVT_UPDATE_UI(self, ID_NEW, self.OnUpdateMenu)
        wx.EVT_UPDATE_UI(self, ID_OPEN, self.OnUpdateMenu)
        wx.EVT_UPDATE_UI(self, ID_REVERT, self.OnUpdateMenu)
        wx.EVT_UPDATE_UI(self, ID_CLOSE, self.OnUpdateMenu)
        wx.EVT_UPDATE_UI(self, ID_SAVE, self.OnUpdateMenu)
        wx.EVT_UPDATE_UI(self, ID_SAVEAS, self.OnUpdateMenu)
        ...

The handler is coded as this:

    def OnUpdateMenu(self, event):
        """Update menu items based on current status and context."""
        win = wx.wxWindow_FindFocus()
        id = event.GetId()
        event.Enable(True)
        try:
            if id == ID_NEW:
                event.Enable(hasattr(self, 'bufferNew'))
            elif id == ID_OPEN:
                event.Enable(hasattr(self, 'bufferOpen'))
            elif id == ID_REVERT:
                event.Enable(hasattr(self, 'bufferRevert') and self.hasBuffer())
            elif id == ID_CLOSE:
                event.Enable(hasattr(self, 'bufferClose') and self.hasBuffer())
            elif id == ID_SAVE:
                event.Enable(hasattr(self, 'bufferSave') and self.bufferHasChanged())
            elif id == ID_SAVEAS:
                event.Enable(hasattr(self, 'bufferSaveAs') and self.hasBuffer())
            elif id == ID_NAMESPACE:
                event.Enable(hasattr(self, 'updateNamespace') and self.hasBuffer())
            elif id == ID_PRINT:
                event.Enable(hasattr(self, 'bufferPrint') and self.hasBuffer())
            elif id == ID_UNDO:
                event.Enable(win.CanUndo())
            elif id == ID_REDO:
                event.Enable(win.CanRedo())
            elif id == ID_CUT:
                event.Enable(win.CanCut())
            elif id == ID_COPY:
                event.Enable(win.CanCopy())
            elif id == ID_COPY_PLUS:
                event.Enable(win.CanCopy() and hasattr(win, 'CopyWithPrompts'))
            elif id == ID_PASTE:
                event.Enable(win.CanPaste())
            elif id == ID_PASTE_PLUS:
                event.Enable(win.CanPaste() and hasattr(win, 'PasteAndRun'))
            elif id == ID_CLEAR:
                event.Enable(win.CanCut())
            elif id == ID_SELECTALL:
                event.Enable(hasattr(win, 'SelectAll'))
            elif id == ID_AUTOCOMP_SHOW:
                event.Check(win.autoComplete)
            elif id == ID_AUTOCOMP_MAGIC:
                event.Check(win.autoCompleteIncludeMagic)
            elif id == ID_AUTOCOMP_SINGLE:
                event.Check(win.autoCompleteIncludeSingle)
            elif id == ID_AUTOCOMP_DOUBLE:
                event.Check(win.autoCompleteIncludeDouble)
            elif id == ID_CALLTIPS_SHOW:
                event.Check(win.autoCallTip)
            elif id == ID_WRAP:
                event.Check(win.GetWrapMode())
            else:
                event.Enable(False)
        except AttributeError:
            # This menu option is not supported in the current context.
            event.Enable(False)

It works, but like I said it isn't a very elegant approach. So I'd be
interested in a better menu API if we can come up with one.

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

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-users-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-users-help@lists.wxwindows.org

In article <3EA9BF4D.D457EDBD@noaa.gov>,

There was a little discussion a while back about the Sizers API, and
that wasn't the first time.

In working on Vicky's recent problem, I had some more thoughts about why
sizers are so tricky. It breaks down into two problems:

1) The Sizer.Add() call:
- All those flags that pertain to different things all in one pile.
- The behavior in the "primary" direction and "secondary" direction
being handled completely differently. Do we even need the Boxsizer?
could there just be a FlexGridSizer, and it could happen to have one row
or one column?

   I wonder if this isn't chiefly a documentation problem.
The docs I've been using are accurate, but they suffer
(IMHO) from run-on paragraphs and an unhappy mix of
impersonal tech-write with folksy verboseness. I'm grateful
to the original writer -- without whose work I wouldn't have
understood sizers at all -- but I've rewritten part of the
wxBoxSizer description at

http://www.the-wire.com/~mwilson/python/wx41.htm

   Would anybody find this clearer?

        Regards. Mel.

···

"Chris Barker" <Chris.Barker@noaa.gov> wrote:

[snip]

Thanks, that helps me understand. I didn't even know about
EVT_UPDATE_UI, so now I've read up on the docs and list archives for
it.

Patrick, could you do this in place of the repeated calls with each ID?
    wx.EVENT_UPDATE_UI(self, -1, self.onUpdateMenu)

Also, in the event handler, I would love to see if the menu item has a
callback attribute and then simply invoke it. But AFAICT from both docs
and source, an UpdateUIEvent() doesn't have a pointer back to the menu
item, only it's integer ID!

Sidebar rant: Why does wxWindows insist on passing ints all over the
place, instead of just using...(drum roll)...objects!!! Grrr...

Hmmm, maybe in my menu.add() mixin method, I could maintain a dictionary
mapping ints to menu items. Then my single handler becomes:

    def onUpdateMenu(self, event):
        menuItem = self._menuItemForStupidInt[event.int]
        updateUI = getattr(menuItem, 'updateUI', None)
        if updateUI:
            updateUI(event, menuItem)

So now the set up for a single menu item would be:

    menu.add('Revert buffer', callback=self.revertBuffer,
       updateUI=self.updateRevertBuffer)

    def updateRevertBuffer(self, event, item):
        event.enable(hasattr(self, 'bufferRevert') and self.hasBuffer())

Another variation would be to let the add() method figure out the
methods based on the "name":
    menu.add('Revert buffer', owner=self, name='revertBuffer')

Finally, another thought is that the add() mixin could bind the
EVT_UPDATE_UI() which would eliminate the need to map the ints to the
menu items.

Feedback appreciated. I'll try something like this some time soon.

In case you're wondering where menu.add() comes from, I'm currently use
it and have it defined as this:

from wxPython import wx

def add(self, title, helpStr='', itemKind=wx.ITEM_NORMAL, callback=None,
id=None):
    """
    Example:
        menu.add("&About", "Information about this program",
callback=self.onAbout)

    Adds a new menu item with the given parameters, including the event
hookup and callback,
    and returns the new menu item.

    Normally you will not pass the 'id'.
    """
    if id is None:
        id = wx.NewId()

    item = wx.MenuItem(self, id, title, helpStr, itemKind)
    self.appendItem(item)

    if callback:
        receiver = getattr(callback, 'im_self', self) # im_self is the
target of a BoundMethod
        receiver.forEvent('menu', id, callback)

    return item

from wxPython.wx import MenuPtr as Menu
assert not hasattr(Menu, 'add'), Menu.add
Menu.add = add

···

On Friday 25 April 2003 05:50 pm, Patrick K. O'Brien wrote:

Chuck Esterbrook <ChuckEsterbrook@yahoo.com> writes:
> Hi,
>
> What do folks use to keep there pulldown menu item enable/disable
> states correct while an app is being used?
>
> Obviously, I could make sure that at every step of my code I'm
> invoking Enable() on the right items with the right value. I fear
> that will be complicated.

This is not a particularly elegant way to do it, but for now I'm
using the following in PyCrust/PyAlaMode/etc. (All my Py* programs
inherit from the same base frame class, defined in frame.py.)

I've got a frame that creates a menu. For each menu item, I
associate the same Update UI event handler, like this:

        wx.EVT_UPDATE_UI(self, ID_NEW, self.OnUpdateMenu)
        wx.EVT_UPDATE_UI(self, ID_OPEN, self.OnUpdateMenu)
        wx.EVT_UPDATE_UI(self, ID_REVERT, self.OnUpdateMenu)
        wx.EVT_UPDATE_UI(self, ID_CLOSE, self.OnUpdateMenu)
        wx.EVT_UPDATE_UI(self, ID_SAVE, self.OnUpdateMenu)
        wx.EVT_UPDATE_UI(self, ID_SAVEAS, self.OnUpdateMenu)
        ...

The handler is coded as this:

    def OnUpdateMenu(self, event):
        """Update menu items based on current status and context."""
        win = wx.wxWindow_FindFocus()
        id = event.GetId()
        event.Enable(True)
        try:
            if id == ID_NEW:
                event.Enable(hasattr(self, 'bufferNew'))
            elif id == ID_OPEN:
                event.Enable(hasattr(self, 'bufferOpen'))

--
Chuck
http://ChuckEsterbrook.com

Hi Mel,

[Should we, perhaps, move this thread to the wxPython-docs list? It might be a bit off-topic for some people...]

   I wonder if this isn't chiefly a documentation problem.
The docs I've been using are accurate, but they suffer
(IMHO) from run-on paragraphs and an unhappy mix of
impersonal tech-write with folksy verboseness. I'm grateful
to the original writer -- without whose work I wouldn't have
understood sizers at all -- but I've rewritten part of the
wxBoxSizer description at

http://www.the-wire.com/~mwilson/python/wx41.htm

   Would anybody find this clearer?

Very much so! Even after programming with wxPython daily for a couple of years, I still get regularly confused by wxBoxSizer's add() parameters, which can be very frustrating...your clear documentation would make a huge improvement, thanks.

Could I make a couple of suggestions? Firstly, please remove the underlining from all the text -- I'm sure this is a typo in your HTML, but it makes the page unnecessarily hard to read.

Secondly, I'd suggest moving all the stuff about the example to the bottom of the page (immediately before the example program itself), and keep the main body of your description focussed on the behaviour of the wxBoxSizer itself. For example, here are the first few paragraphs from your doc (I've numbered the paragraphs for reference below):

[1] Box sizers lay out window space in rows or columns, and can be nested to handle rows within columns, columns within rows, and so forth.

[2] As an example, we will construct a dialog with a text field at the top and two buttons at the bottom.In our case, we want the text area to grow with the dialog, while the buttons keep their original size. We also want a thin border around all controls and we want the buttons to stay centred as the width of the dialog changes.

[3] A box sizer is created to work in one orientation, wxHORIZONTAL or wxVERTICAL. Children descended from wxWindow or wxSizer can be put under the control of a sizer using the sizer's Add method. When a box sizer is resized, it resizes the children under its control. Size changes along the sizer's orientation are propagated differently to changes along the other dimension.

[4] We can regard our example dialog as having a top-hierarchy column, controlled by a vertical wxBoxSizer, with the text at the top and a row of buttons at the bottom. The low-hierarchy row, controlled by a horizontal wxBoxSizer, will contain an OK button to the left and a Cancel button to the right. In many cases (particulary dialogs under Unix, and normal frames) the main window will be resizable by the user and this change of size will have to get propagated to its children.

[5] Wnen a box sizer grows or shrinks along its orientation it can distribute the growth unevenly among its children. This is determined by the option parameter given when each child window (or subordinate sizer) is added to the sizer. The growth or shrinkage applied to each child is proprotional to the child's option value, relative to the total of the option values for all children of the sizer. So if a sizer contains two child windows with respective option values of 2 and 3, then 2/5 of a change in size will be applied to the first child, and 3/5 to the second.

I'd suggest simply moving paragraphs 2 and 4 down to a separate "example" section, and keep the rest of the document unchanged. I think your document would read very nicely if you focussed just on the behaviour of the wxBoxSizer, and didn't clutter it up with this example stuff -- which is useful when you're first learning, but much less so when you're using your doc for reference. That's why I suggest moving the example down...

Oh, while I remember, there's one other thing you've missed out:

The full parameters of an Add call to add a window or another sizer are documented in wxSizer. Their effects on a wxBoxSizer are as follows:

  window

    the window or sizer to be added.

Shouldn't you also mention that you can add a "spacer" by passing a size tuple for this parameter?

Anyway, thanks for doing this. I hope it gets included in the main docs at some stage -- it's a huge improvement over what's there at the moment...

Cheers,

  - Erik.

···

At 00:24 27/04/2003, Mel Wilson wrote:

Chris Barker wrote:

There was a little discussion a while back about the Sizers API, and
that wasn't the first time.

In working on Vicky's recent problem, I had some more thoughts about why
sizers are so tricky. It breaks down into two problems:

1) The Sizer.Add() call:
  - All those flags that pertain to different things all in one pile.
  - The behavior in the "primary" direction and "secondary" direction
being handled completely differently. Do we even need the Boxsizer?
could there just be a FlexGridSizer, and it could happen to have one row
or one column?

I have plans for some updates to the Sizer API that make things somewhat simpler, but also are (mostly?) backwards compatible. Things beyond that can perhaps be done in some Python-specific classes, or perhaps a mix-in class if somebody cares to write it...

2) The whole collection of:
       self.SetAutoLayout(true)
       self.SetSizer(box)
       box.Fit(self)
       self.Layout()
       self.Fit()
  - While I can imagine that there are times when the programmer would
need complete control over these processes, most of the time, I just put
all this boiler plate in, and I have no idea when or whether it is all
necessary.

Currently all that is needed is self.SetSizer(box), it will set the autolayout flag internally. If you want the window to be resized to fit the minimum size of the sizer then use Fit, or that can be combined in a single call too: self.SetSizerAndFit(sizer). Layout is normally called in the window's EVT_SIZE, but if there is no event for some reason (the window never gets resized) then you can call Layout yourself as a work around.

···

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

In article <5.1.0.14.1.20030427091214.03c5b150@mail.wave.co.nz>,

Hi Mel,

[Should we, perhaps, move this thread to the wxPython-docs list? It might
be a bit off-topic for some people...]

   I wonder if this isn't chiefly a documentation problem.
[ ... ]

http://www.the-wire.com/~mwilson/python/wx41.htm

   Would anybody find this clearer?

Very much so! [ ... ]
Could I make a couple of suggestions? Firstly, please remove the
underlining from all the text -- I'm sure this is a typo in your HTML, but
it makes the page unnecessarily hard to read.

   This seems to be your browser handling the
<ins...>...</ins> tags that I used to mark my changes.
Obviously these tags have to disapper in the final form.
I've put a warning in the page. The ancient Netscape that I
proofed this on ignored <ins>. Amaya shows me all italics.

Secondly, I'd suggest moving all the stuff about the example to the bottom
of the page (immediately before the example program itself), and keep the
main body of your description focussed on the behaviour of the wxBoxSizer
itself.

   I've done this. Now that I look at it, it is better.

Shouldn't you also mention that you can add a "spacer" by passing a size
tuple for this parameter?

   Also done. Many thanks for your suggestions. I've
subscribed to the wxPython-docs list, and I'll present this
there. In the meantime, I've set up another draft:

     http://www.the-wire.com/~mwilson/python/wx41sans.htm

without the <ins> tags, so that people can read it.

        Regards. Mel.

···

Erik Westra <ewestra@wave.co.nz> wrote:

At 00:24 27/04/2003, Mel Wilson wrote:

I having tried all that I could think of (I followed the directions and googled) and I have come to the conclusion I need help. I'm not familiar enough with C/C++ compiling & linking & other forms of voodoo.

I'm trying to build wxPython 2.4.0.7 from the source file on FreeBSD 5.0.

1. setenv WXPREF /usr/lib/wxPython
(tcsh)

2. cd wxPythonSrc-2.4.0.7
mkdir build
cd build
../configure --with-gtk \
                     --prefix=$WXPREF \
                     --enable-rpath=$WXPREF/lib \
                     --with-opengl \
                     --enable-geometry \
                     --enable-optimise \
                     --enable-debug_flag \
                     --with-libjpeg=builtin \
                     --with-libpng=builtin \
                     --with-libtiff=builtin \
                     --with-zlib=builtin \
                     --enable-gtk2 \
                    --enable-unicode \

That runs fine.

3. make
<snip>
c++ -c -I./lib/wx/include/gtk2ud-2.4 -I../include -I../src/zlib -I../src/png -I../src/jpeg -I../src/tiff -D_REENTRANT -D_THREAD_SAFE -I/usr/local/include/atk-1.0 -I/usr/local/include/glib-2.0 -I/usr/local/lib/glib-2.0/include -I/usr/X11R6/include/gtk-2.0 -I/usr/X11R6/lib/gtk-2.0/include -I/usr/X11R6/include/pango-1.0 -I/usr/X11R6/include -I/usr/local/include/freetype2 -I/usr/local/include -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES -D__WXGTK__ -D__WXDEBUG__ -O2 -MMD -I/usr/local/include/glib-2.0 -I/usr/local/lib/glib-2.0/include -I/usr/X11R6/include/pango-1.0 -I/usr/local/include/freetype2 -I/usr/local/include -pthread -D_THREAD_SAFE -Wall -fPIC -o cshelp.o ../src/common/cshelp.cpp
c++ -c -I./lib/wx/include/gtk2ud-2.4 -I../include -I../src/zlib -I../src/png -I../src/jpeg -I../src/tiff -D_REENTRANT -D_THREAD_SAFE -I/usr/local/include/atk-1.0 -I/usr/local/include/glib-2.0 -I/usr/local/lib/glib-2.0/include -I/usr/X11R6/include/gtk-2.0 -I/usr/X11R6/lib/gtk-2.0/include -I/usr/X11R6/include/pango-1.0 -I/usr/X11R6/include -I/usr/local/include/freetype2 -I/usr/local/include -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES -D__WXGTK__ -D__WXDEBUG__ -O2 -MMD -I/usr/local/include/glib-2.0 -I/usr/local/lib/glib-2.0/include -I/usr/X11R6/include/pango-1.0 -I/usr/local/include/freetype2 -I/usr/local/include -pthread -D_THREAD_SAFE -Wall -fPIC -o ctrlcmn.o ../src/common/ctrlcmn.cpp
c++ -c -I./lib/wx/include/gtk2ud-2.4 -I../include -I../src/zlib -I../src/png -I../src/jpeg -I../src/tiff -D_REENTRANT -D_THREAD_SAFE -I/usr/local/include/atk-1.0 -I/usr/local/include/glib-2.0 -I/usr/local/lib/glib-2.0/include -I/usr/X11R6/include/gtk-2.0 -I/usr/X11R6/lib/gtk-2.0/include -I/usr/X11R6/include/pango-1.0 -I/usr/X11R6/include -I/usr/local/include/freetype2 -I/usr/local/include -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES -D__WXGTK__ -D__WXDEBUG__ -O2 -MMD -I/usr/local/include/glib-2.0 -I/usr/local/lib/glib-2.0/include -I/usr/X11R6/include/pango-1.0 -I/usr/local/include/freetype2 -I/usr/local/include -pthread -D_THREAD_SAFE -Wall -fPIC -o ctrlsub.o ../src/common/ctrlsub.cpp
c++ -c -I./lib/wx/include/gtk2ud-2.4 -I../include -I../src/zlib -I../src/png -I../src/jpeg -I../src/tiff -D_REENTRANT -D_THREAD_SAFE -I/usr/local/include/atk-1.0 -I/usr/local/include/glib-2.0 -I/usr/local/lib/glib-2.0/include -I/usr/X11R6/include/gtk-2.0 -I/usr/X11R6/lib/gtk-2.0/include -I/usr/X11R6/include/pango-1.0 -I/usr/X11R6/include -I/usr/local/include/freetype2 -I/usr/local/include -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES -D__WXGTK__ -D__WXDEBUG__ -O2 -MMD -I/usr/local/include/glib-2.0 -I/usr/local/lib/glib-2.0/include -I/usr/X11R6/include/pango-1.0 -I/usr/local/include/freetype2 -I/usr/local/include -pthread -D_THREAD_SAFE -Wall -fPIC -o datetime.o ../src/common/datetime.cpp
../src/common/datetime.cpp: In member function `const wxChar*
   wxDateTime::ParseFormat(const wxChar*, const wxChar*, const wxDateTime&)':
../src/common/datetime.cpp:2923: cannot convert `const wxChar*' to `const char*
   ' for argument `1' to `char* strptime(const char*, const char*, tm*)'
../src/common/datetime.cpp:2988: cannot convert `const wxChar*' to `const char*
   ' for argument `1' to `char* strptime(const char*, const char*, tm*)'
*** Error code 1

Stop in /usr/ports/distfiles/wxPython/wxPythonSrc-2.4.0.7/build.

Any suggestions (including a *nix platform that happily compiles & runs most things) are appreciated.

Chris.

...

but I've rewritten part of the
wxBoxSizer description at

http://www.the-wire.com/~mwilson/python/wx41.htm

   Would anybody find this clearer?

YES!

This simple, clear, explanation would have saved me literally hours of
frustration. I haven't taken the time to parse the sample code, but this
explanation is really quite good, IMHO.

Gratefully yours,

John Hopkins

Yes I also do it with a single UpdateUI call, shown below, where the I just hook up to some menu item id. The menu item is enabled/disabled based on flags.

EVT_UPDATE_UI(self, ID_FILE_OPEN, self.OnUpdateUI) # UI-Update

def OnUpdateUI(self, event):
     self.menuFile.Enable(ID_SAVE_UPDATE, Img.imageLoaded)
     .....

I've wondered many times if there is a better (more efficient) way to do it because my OnUpdateUI function is quite long with many flags. Patrick's example is an interesting and compact way to do it but seems like there would be way too many UpdateUI events triggered.

Bob

···

At 12:24 PM 4/26/2003 -0700, you wrote:

On Friday 25 April 2003 05:50 pm, Patrick K. O'Brien wrote:
> Chuck Esterbrook <ChuckEsterbrook@yahoo.com> writes:
> > Hi,
> >
> > What do folks use to keep there pulldown menu item enable/disable
> > states correct while an app is being used?
> >
> > Obviously, I could make sure that at every step of my code I'm
> > invoking Enable() on the right items with the right value. I fear
> > that will be complicated.
>
> This is not a particularly elegant way to do it, but for now I'm
> using the following in PyCrust/PyAlaMode/etc. (All my Py* programs
> inherit from the same base frame class, defined in frame.py.)
>
> I've got a frame that creates a menu. For each menu item, I
> associate the same Update UI event handler, like this:
>
> wx.EVT_UPDATE_UI(self, ID_NEW, self.OnUpdateMenu)
> wx.EVT_UPDATE_UI(self, ID_OPEN, self.OnUpdateMenu)
> wx.EVT_UPDATE_UI(self, ID_REVERT, self.OnUpdateMenu)
> wx.EVT_UPDATE_UI(self, ID_CLOSE, self.OnUpdateMenu)
> wx.EVT_UPDATE_UI(self, ID_SAVE, self.OnUpdateMenu)
> wx.EVT_UPDATE_UI(self, ID_SAVEAS, self.OnUpdateMenu)
> ...
>
> The handler is coded as this:
>
> def OnUpdateMenu(self, event):
> """Update menu items based on current status and context."""
> win = wx.wxWindow_FindFocus()
> id = event.GetId()
> event.Enable(True)
> try:
> if id == ID_NEW:
> event.Enable(hasattr(self, 'bufferNew'))
> elif id == ID_OPEN:
> event.Enable(hasattr(self, 'bufferOpen'))
[snip]

Thanks, that helps me understand. I didn't even know about
EVT_UPDATE_UI, so now I've read up on the docs and list archives for
it.

Patrick, could you do this in place of the repeated calls with each ID?
    wx.EVENT_UPDATE_UI(self, -1, self.onUpdateMenu)

Chris Munchenberg wrote:

I having tried all that I could think of (I followed the directions and googled) and I have come to the conclusion I need help. I'm not familiar enough with C/C++ compiling & linking & other forms of voodoo.

../src/common/datetime.cpp: In member function `const wxChar* wxDateTime::ParseFormat(const wxChar*, const wxChar*, const wxDateTime&)':
../src/common/datetime.cpp:2923: cannot convert `const wxChar*' to `const char*
   ' for argument `1' to `char* strptime(const char*, const char*, tm*)'
../src/common/datetime.cpp:2988: cannot convert `const wxChar*' to `const char*
   ' for argument `1' to `char* strptime(const char*, const char*, tm*)'
*** Error code 1

Stop in /usr/ports/distfiles/wxPython/wxPythonSrc-2.4.0.7/build.

Any suggestions (including a *nix platform that happily compiles & runs most things) are appreciated.

This was also reported a few days ago on Solaris. I've asked about how to fix it on wx-dev but in the mean time you can work around it by not doing a unicode build.

···

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