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 acommand
item that runsfunction()
when it’s clicked. See alsoButton
.MenuItem(label, checked_var)
creates acheckbutton
menu item. Thechecked_var
must be aBooleanVar
. See alsoCheckbutton
.MenuItem(label, string_var, value)
creates aradiobutton
menu item. Use this for letting the user choose one of multiple options. Clicking the item setsvalue
tostring_var
. Thestring_var
must be aStringVar
object, andvalue
must be a string.MenuItem(label, menu)
creates acascade
menu item; that is, it displays a submenu with the items ofmenu
in it. Themenu
must be aMenu
widget.MenuItem(label, item_list)
is a handy way to create a newMenu
and add it as acascade
item 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
items
should be an iterable ofMenuItem
objects, 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
MenuItem
objects, you can probably do it directly to aMenu
widget 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(...): ...>]
Menu
objects assume that nothing changes the underlying Tk menu widget without theMenu
object. 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
post
is 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
MenuItem
objects:- Menu items that are not in any
Menu
widget 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 OPTIONS
in menu(3tk).The types of the values are the same as for similar widgets. For example, the
'command'
of aButton
widget is aCallback
object connected to a function passed toButton
, and so is the'command'
ofteek.MenuItem("Click me", some_function)
.
- Menu items that are not in any