Re: tkinter puzzler

2005-05-16 Thread Russell E. Owen
In article [EMAIL PROTECTED],
 Paul Rubin http://[EMAIL PROTECTED] wrote:

Martin Franklin [EMAIL PROTECTED] writes:
 I suspect you need to look at the columnconfigure / rowconfigure methods
 of the container (toplevel or frame)

Thanks, columnconfigure turned out to be the answer and Peter Otten's
post showing how to use it was very informative.  For some reason
columnconfigure is not documented in the otherwise excellent tkinter
reference manual from New Mexico Tech.

I'm having sort of a different prob now, which is I want to make a
pulldown menu as commonly seen on web pages.  The NMT reference
suggests using the MenuButton widget, which sort of works, though the
entrycget and entryconfigure methods that are supposedly on the menu
items aren't really there.  The toolkit itself says that MenuButton is
now considered obsolete and Frederik Lundh's manual seems to say
to use Menu somehow instead, but I haven't quite figured out how.

MenuButton is (as far as I know) the recommended way to do this
(and I've been puzzled by F. Lendh's assertion that they are
obsolete). The basics are:

  mbut = Tkinter.Menubutton.(master, text=MyMenu, inicatoron=True)
  mnu = Tkinter.Menu(mbut, tearoff=False)
  mbut[menu] = mnu

where indicatoron=True gives you the this is a pop-up menu indicator 
icon. The circular reference is icky but appears to be necessary (i.e. 
having the menubutton be a parent to the menu and the menu attribute of 
the menubutton point to the menu).

I suggest you buy Grayson's Python and Tkinter Programming
(it has a very useful reference section in the back, though 
unfortunately it is somewhat incomplete). For more serious work you'll 
also want Welch's Practical Programming in Tcl and Tk (lots of good 
details about Tk).

-- Russell
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: tkinter puzzler

2005-05-14 Thread Paul Rubin
Martin Franklin [EMAIL PROTECTED] writes:
 I suspect you need to look at the columnconfigure / rowconfigure methods
 of the container (toplevel or frame)

Thanks, columnconfigure turned out to be the answer and Peter Otten's
post showing how to use it was very informative.  For some reason
columnconfigure is not documented in the otherwise excellent tkinter
reference manual from New Mexico Tech.

I'm having sort of a different prob now, which is I want to make a
pulldown menu as commonly seen on web pages.  The NMT reference
suggests using the MenuButton widget, which sort of works, though the
entrycget and entryconfigure methods that are supposedly on the menu
items aren't really there.  The toolkit itself says that MenuButton is
now considered obsolete and Frederik Lundh's manual seems to say
to use Menu somehow instead, but I haven't quite figured out how.

Is there a preferred and normal way to do this?  One thing I need is
to be able to disable individual menu choices based on stuff happening
in the application, which is why I wanted to use entryconfigure.

Also, I don't see how to get the little horizontal bar marker on the
button (as seen in IDLE) indicating that the button is a pulldown.  A
minute examining the IDLE source code didn't show it either, though I
can probably find it there with more effort.

Thanks as usual.
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: tkinter puzzler

2005-05-12 Thread Martin Franklin
Paul Rubin wrote:
 I have a gui with a bunch of buttons, labels, the usual stuff.  It
 uses the grid manager:
 
gui = Frame()
gui.grid()
gui.Label().grid()  # put some widgets into the gui
...# more widgets
 
 Now at the the very bottom of the gui, I want to add two more buttons,
 let's say stop and go.  I want stop to appear in the gui's lower
 left corner and go to appear in the gui's lower right corner.
 Suppose that up to now, row 16 is the last row in the gui.  Then this
 works:
 
 Button(gui, text=stop).grid(sticky=W)   # starts row 17
 Button(gui, text=go).grid(row=17, column=1, sticky=E)
 
 But I don't really want that hardwired row number and I don't want to
 keep counting rows and adjusting stuff if I stick new rows in the gui.
 So I try the obvious, make one Frame widget containing both new buttons:
 stopgo = Frame(gui)
 Button(stopgo, stop).grid(sticky=W)
 Button(stopgo, go).grid(sticky=E)
 
 and try to stretch it across the bottom row of the gui:
 
 stopgo.grid(sticky=E+W)
 
 However, the buttons keep coming out centered in the gui's bottom row
 pretty much no matter what I do (I've tried all sorts of combinations).
 
 Am I missing something?  I'm not a tkinter whiz and this stuff is
 pretty confusing.  I did come up with an ugly workaround that I'll
 spare you the agony of seeing, but there should be a natural way to do
 this.
 
 Thanks for any advice.

Sorry to say Paul but you may have to show us that ugly code!  I'm no
whiz with the grid manager (I prefer pack!) but seeing your code will
help us help you

I suspect you need to look at the columnconfigure / rowconfigure methods
of the container (toplevel or frame)


Martin













-- 
http://mail.python.org/mailman/listinfo/python-list


Re: tkinter puzzler

2005-05-12 Thread Peter Otten
Paul Rubin wrote:

I think you are missing the columnconfigure()/rowconfigure() methods as
Martin Franklin pointed out. Anyway, here is some code to illustrate the
matter. I've found it helpful to use false colors to see what's going on.
the yellow 'main' frame contains the red 'north' and the blue 'south'
frame.

import Tkinter as tk

root = tk.Tk()
main = tk.Frame(root, bg=yellow)
main.pack(expand=True, fill=both)
main.columnconfigure(0, weight=1)
main.rowconfigure(0, weight=1)

north = tk.Frame(main, bg=red)
north.columnconfigure(1, weight=1)
for row, text in enumerate(alpha beta gamma delta.split()):
label = tk.Label(north, text=text)
label.grid(row=row, column=0, sticky=NW)
entry = tk.Entry(north)
entry.grid(row=row, column=1, sticky=NEW)
button = tk.Button(north, text=text.upper())
button.grid(row=row, column=2, sticky=NW)
north.rowconfigure(row, weight=1)
north.grid(column=0, row=0, sticky=NEW)

south = tk.Frame(main, bg=blue)
stop = tk.Button(south, text=Stop)
go = tk.Button(south, text=Go)
stop.grid(column=0, row=0)
go.grid(column=2, row=0)
# the empty column takes all the extra space
south.columnconfigure(1, weight=1) 
south.grid(column=0, row=1, sticky=NSEW)

root.mainloop()

Peter

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: tkinter puzzler

2005-05-12 Thread Russell E. Owen
In article [EMAIL PROTECTED],
 Paul Rubin http://[EMAIL PROTECTED] wrote:

I have a gui with a bunch of buttons, labels, the usual stuff.  It
uses the grid manager:

   gui = Frame()
   gui.grid()
   gui.Label().grid()  # put some widgets into the gui
   ...# more widgets

Now at the the very bottom of the gui, I want to add two more buttons,
let's say stop and go.  I want stop to appear in the gui's lower
left corner and go to appear in the gui's lower right corner.
Suppose that up to now, row 16 is the last row in the gui.  Then this
works:

Button(gui, text=stop).grid(sticky=W)   # starts row 17
Button(gui, text=go).grid(row=17, column=1, sticky=E)

But I don't really want that hardwired row number and I don't want to
keep counting rows and adjusting stuff if I stick new rows in the gui.

A couple of options here:
- Put the main portion of the gui into one frame and pack or grid the 
button frame below that. That sounds like a natural solution to this 
problem based on the way you describe it. (if you do that, I suggest 
packing the buttons into their frame; although I usually use the gridder 
when in doubt, the packer is often the most natural layout manager for a 
row of buttons).

- Increment as you go:
   row = 0

   wdg.grid(row=row, column=0, ...)
   row += 1

   wdg2.grid(row=row, column=0, ...)
   row += 1

- If you are doing a lot of similar layout, it is worth creating a class 
to do your gridding for you. Each instance grids widgets in a particular 
frame. It keeps  track of the row # for you. For use an existing 
gridder, for instance RO.Wdg.Gridder in the RO package 
http://astro.washington.edu/rowen.


So I try the obvious, make one Frame widget containing both new buttons:
stopgo = Frame(gui)
Button(stopgo, stop).grid(sticky=W)
Button(stopgo, go).grid(sticky=E)

and try to stretch it across the bottom row of the gui:

stopgo.grid(sticky=E+W)

This looks OK to me so I'm not sure what's wrong; I think I'd have to 
see your actual code. I suggest examining the size of the stopgo frame 
by setting its background color.

-- Russell
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Tkinter Puzzler

2005-01-07 Thread Eric Brunel
Tim Daneliuk wrote:
I am trying to initialize a menu in the following manner:
for entry in [(Up, KeyUpDir), (Back, KeyBackDir), (Home, 
KeyHomeDir), (Startdir, KeyStartDir), (Root, KeyRootDir)]:

func = entry[1]
UI.ShortBtn.menu.add_command(label=entry[0], command=lambda: 
func(None))

However, at runtime, each of the menu options binds to the *last* function
named in the list (KeyStartDir).
Explicitly loading each entry on its own line works fine:
UIcommand=lambda:KeyWHATEVERDir(None)
Any ideas why the first form does not fly?
This has nothing to do with Tkinter, but only with the way nested scopes work: 
to put it roughly, your lambda: func(None) only knows about the *name* func, 
which is not actually evaluated until the button is pressed. And when the button 
is pressed, the name func is bound to the last command in the loop.

To do what you want, change your code to:
for entry in (...):
  UI.ShortBtn.menu.add_command(
label=entry[0],
command=lambda func=entry[1]: func(None)
  )
This way, the value for the func parameter is evaluated when the function is 
defined and not when it is called.

HTH
--
- Eric Brunel eric (underscore) brunel (at) despammed (dot) com -
PragmaDev : Real Time Software Development Tools - http://www.pragmadev.com
--
http://mail.python.org/mailman/listinfo/python-list


Re: Tkinter Puzzler

2005-01-07 Thread Alex Martelli
Tim Daneliuk [EMAIL PROTECTED] wrote:

 I am trying to initialize a menu in the following manner:
 
 for entry in [(Up, KeyUpDir), (Back, KeyBackDir), (Home,
 KeyHomeDir), (Startdir, KeyStartDir), (Root, KeyRootDir)]:
 
  func = entry[1]
  UI.ShortBtn.menu.add_command(label=entry[0], command=lambda: func(None))
 
 However, at runtime, each of the menu options binds to the *last* function
 named in the list (KeyStartDir).
 
 Explicitly loading each entry on its own line works fine:
 
 UIcommand=lambda:KeyWHATEVERDir(None)
 
 Any ideas why the first form does not fly?

One word: late binding.  Well, two, pedantically speaking;-).

The lambda you're passing as the value for 'command' is a closure: it
knows it will have to look up name 'func' in the environment in which
it's embedded -- but also that it's meant to do that lookup as late as
possible, each time it's called.

If you wanted to do the lookup just once, at the time lambda executes
and created an anonymous function rather than each time said anonymous
function is called, you could have expressed that...:
command=lambda func=func: func(None)
Here, func is a local variable (argument) of the anonymous function, and
its default value is set ONCE, when the anon function is created.

Back to your code, when your anon function is called, it looks up name
'func' in the surrounding environment... and there it finds it bound to
whatever it was RE-bound to the LAST time...


Point to remember: a closure looks up free-variable names in its
surrounding environment *as late as possible*, i.e., when the function
object is called; while default argument values are evaluated *at
function creation time* (when lambda or def executes, not when the
resulting function object gets called).


Alex
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Tkinter Puzzler

2005-01-07 Thread Tim Daneliuk
Tim Daneliuk wrote:
I am trying to initialize a menu in the following manner:
for entry in [(Up, KeyUpDir), (Back, KeyBackDir), (Home, 
KeyHomeDir), (Startdir, KeyStartDir), (Root, KeyRootDir)]:

func = entry[1]
UI.ShortBtn.menu.add_command(label=entry[0], command=lambda: 
func(None))

However, at runtime, each of the menu options binds to the *last* function
named in the list (KeyStartDir).
Explicitly loading each entry on its own line works fine:
UIcommand=lambda:KeyWHATEVERDir(None)
Any ideas why the first form does not fly?
TIA,
 

Tim Daneliuk [EMAIL PROTECTED]
PGP Key: http://www.tundraware.com/PGP/
Thanks All - great responses!
--

Tim Daneliuk [EMAIL PROTECTED]
PGP Key: http://www.tundraware.com/PGP/
--
http://mail.python.org/mailman/listinfo/python-list