Re: tkinter puzzler
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
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
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
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
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
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
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
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