Menu Widget¶
Manual page: menu(3tk)
You can use menu widges for a few different things:
- Menu bars are menus that are typically displayed at the top of a window, or top of the screen if you are using e.g. Mac OSX.
- Pop-up menus open when the user e.g. right-clicks something.
Here is an example of a menu bar:
import teek
window = teek.Window()
def hello():
print("hello")
window.config['menu'] = teek.Menu([
teek.MenuItem("File", [
teek.MenuItem("New", hello),
teek.MenuItem("Open", hello),
teek.MenuItem("Save", hello),
teek.MenuItem("Quit", hello),
]),
teek.MenuItem("Edit", [
teek.MenuItem("Cut", hello),
teek.MenuItem("Copy", hello),
teek.MenuItem("Paste", hello),
]),
])
window.geometry(300, 200)
window.on_delete_window.connect(teek.quit)
teek.run()
As you can see, Menu takes one argument, which is a list of
MenuItem objects. This example uses two kinds of menu items; some
menu items just call the hello() function when they are clicked, while the
“File” and “Edit” items display submenus. There are more details about
different kinds of items below.
Here is a pop-up menu example. See bind documentation for more details about the binding stuff.
import teek
window = teek.Window()
def hello():
print("hello")
menu = teek.Menu([
teek.MenuItem("Cut", hello),
teek.MenuItem("Copy", hello),
teek.MenuItem("Paste", hello),
])
def on_right_click(event):
menu.popup(event.rootx, event.rooty)
if teek.windowingsystem() == 'aqua':
# running on Mac OSX, there's no right-click so this must be done a bit
# differently
window.bind('<Button-2>', on_right_click, event=True)
window.bind('<Control-Button-1>', on_right_click, event=True)
else:
window.bind('<Button-3>', on_right_click, event=True)
window.geometry(300, 200)
window.on_delete_window.connect(teek.quit)
teek.run()
I found the Mac OSX specific code from here.
Menu widgets are not Ttk widgets. If you don’t know what that means, you
should go here and learn. The only practical
thing I can think of right now is that menus don’t have a
state attribute.
Creating Menu Items¶
There are a few different ways to create instances of MenuItem. Here
label must be a string.
MenuItem()creates a separator.MenuItem(label, function)creates acommanditem that runsfunction()when it’s clicked. See alsoButton.MenuItem(label, checked_var)creates acheckbuttonmenu item. Thechecked_varmust be aBooleanVar. See alsoCheckbutton.MenuItem(label, string_var, value)creates aradiobuttonmenu item. Use this for letting the user choose one of multiple options. Clicking the item setsvaluetostring_var. Thestring_varmust be aStringVarobject, andvaluemust be a string.MenuItem(label, menu)creates acascademenu item; that is, it displays a submenu with the items ofmenuin it. Themenumust be aMenuwidget.MenuItem(label, item_list)is a handy way to create a newMenuand add it as acascadeitem as explained above.
You can also pass options as keyword arguments in any of the above forms. The
available options are documented as MENU ENTRY OPTIONS in menu(3tk).
For example, instead of this…
MenuItem("Copy", do_the_copy)
…you probably want to do something like this:
MenuItem("Copy", do_the_copy, accelerator='Ctrl+C')
Note that this does not bind anything automatically, so you need to do that yourself if want that Ctrl+C actually does something.
Here is an example that demonstrates most things. See StringVar and
BooleanVar documentation for more info about them.
import teek
def on_click():
print("clicked")
def on_check(is_checked):
print("is it checked now?", is_checked)
def on_choice(choice):
print("chose", repr(choice))
window = teek.Window()
submenu = teek.Menu([
teek.MenuItem("Asd", on_click),
teek.MenuItem("Toot", on_click),
])
check_var = teek.BooleanVar()
check_var.write_trace.connect(on_check)
choice_var = teek.StringVar()
choice_var.write_trace.connect(on_choice)
window.config['menu'] = teek.Menu([
teek.MenuItem("Stuff", [
teek.MenuItem("Click me", on_click),
teek.MenuItem("Check me", check_var),
teek.MenuItem("More stuff", submenu),
teek.MenuItem(), # separator
teek.MenuItem("Choice 1", choice_var, "one"),
teek.MenuItem("Choice 2", choice_var, "two"),
teek.MenuItem("Choice 3", choice_var, "three"),
]),
])
window.geometry(300, 200)
window.on_delete_window.connect(teek.quit)
teek.run()
Reference¶
-
class
teek.Menu(items=(), **kwargs)[source]¶ This is the menu widget.
The
itemsshould be an iterable ofMenuItemobjects, and it’s treated so that this…menu = teek.Menu([ teek.MenuItem("Click me", print), teek.MenuItem("No, click me instead", print), ])
…does the same thing as this:
menu = teek.Menu() menu.append(teek.MenuItem("Click me", print)) menu.append(teek.MenuItem("No, click me instead", print))
Menu widgets behave like lists of menu items, so if you can do something to a list of
MenuItemobjects, you can probably do it directly to aMenuwidget as well.However, menu widgets don’t support slicing, like lists do:
>>> menu = teek.Menu([ ... teek.MenuItem("Click me", print), ... ]) >>> menu.append(teek.MenuItem("No, click me instead", print)) >>> menu <teek.Menu widget: contains 2 items> >>> menu[0] # this works <MenuItem('Click me', <built-in function print>): type='command', added to a menu> >>> for item in menu: # this works ... print(item) ... <MenuItem('Click me', <built-in function print>): type='command', added to a menu> <MenuItem('No, click me instead', <built-in function print>): type='command', added to a menu> >>> menu[:2] # but this doesn't work Traceback (most recent call last): ... TypeError: slicing a Menu widget is not supported >>> list(menu)[:2] # workaround # doctest: +ELLIPSIS [<MenuItem(...): ...>, <MenuItem(...): ...>]
Menuobjects assume that nothing changes the underlying Tk menu widget without theMenuobject. For example:>>> menu = teek.Menu() >>> command = menu.to_tcl() >>> command # doctest: +SKIP '.menu1' >>> # DON'T DO THIS, this is a bad idea >>> teek.tcl_eval(None, '%s add checkbutton -command {puts hello}' % command) >>> len(menu) # the menu widget doesn't know that we added an item 0
If you don’t know what
tcl_eval()does, you don’t need to worry about doing this accidentally.Manual page: menu(3tk)
-
popup(x, y, menu_item=None)[source]¶ Displays the menu on the screen.
x and y are coordinates in pixels, relative to the screen. See tk_popup(3tk) for details. If menu_item is given, its index is passed to tk_popup(3tk).
There are two ways to show popup menus in Tk. This is one of them, and
postis another. I spent a while trying to find something that explains the difference, and the best thing I found is this book. The book usestk_popup, and one of the authors is John Ousterhout, the creator of Tcl and Tk.
-
-
class
teek.MenuItem(*args, **kwargs)[source]¶ Represents an item of a menu. See Creating Menu Items for details about the arguments.
Tk’s manual pages call these things “menu entries” instead of “menu items”, but I called them items to avoid confusing these with
Entry.There are two kinds of
MenuItemobjects:- Menu items that are not in any
Menuwidget because they haven’t been added to a menu yet, or they have been removed from a menu. Trying to do something with these menu items will likely raise aRuntimeError. - Menu items that are currently in a
Menu.
Here’s an example:
>>> item = teek.MenuItem("Click me", print) >>> item.config['label'] = "New text" Traceback (most recent call last): ... RuntimeError: the MenuItem hasn't been added to a Menu yet >>> menu = teek.Menu() >>> menu.append(item) >>> item.config['label'] = "New text" >>> item.config['label'] 'New text'
-
config¶ This attribute is similar to
Widget.config. SeeMENU ENTRY OPTIONSin menu(3tk).The types of the values are the same as for similar widgets. For example, the
'command'of aButtonwidget is aCallbackobject connected to a function passed toButton, and so is the'command'ofteek.MenuItem("Click me", some_function).
- Menu items that are not in any