Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

tkinter app adding a right click context menu?

I have a python-tkinter gui app that I've been trying to find some way to add in some functionality. I was hoping there would be a way to right-click on an item in the app's listbox area and bring up a context menu. Is tkinter able to accomplish this? Would I be better off looking into gtk or some other gui-toolkit?

like image 924
tijko Avatar asked Aug 17 '12 23:08

tijko


People also ask

How do I create a popup menu in Python?

Build A Paint Program With TKinter and Python Menus can be created by initializing the Menu(parent) object along with the menu items. A popup Menu can be created by initializing tk_popup(x_root,y_root, False) which ensures that the menu is visible on the screen.

What does Mainloop () do in Tkinter?

mainloop() tells Python to run the Tkinter event loop. This method listens for events, such as button clicks or keypresses, and blocks any code that comes after it from running until you close the window where you called the method.

What is Tearoff in Tkinter?

A Tearoff permits you to detach menus for the most window making floating menus. If you produce a menu you may see dotted lines at the top after you click a top menu item. To fix this tearoff needs to set to 0 at the time of menu declaration.


2 Answers

You would create a Menu instance and write a function that calls
its post() or tk_popup() method.

The tkinter documentation doesn't currently have any information about tk_popup().
Read the Tk documentation for a description, or the source:

library/menu.tcl in the Tcl/Tk source:

 ::tk_popup -- This procedure pops up a menu and sets things up for traversing the menu and its submenus.  Arguments: menu  - Name of the menu to be popped up. x, y  - Root coordinates at which to pop up the menu.   entry - Index of a menu entry to center over (x,y).           If omitted or specified as {}, then menu's           upper-left corner goes at (x,y).   

tkinter/__init__.py in the Python source:

def tk_popup(self, x, y, entry=""):     """Post the menu at position X,Y with entry ENTRY."""     self.tk.call('tk_popup', self._w, x, y, entry) 

You associate your context menu invoking function with right-click via:
the_widget_clicked_on.bind("<Button-3>", your_function).

However, the number associated with right-click is not the same on every platform.

library/tk.tcl in the Tcl/Tk source:

 On Darwin/Aqua, buttons from left to right are 1,3,2.   On Darwin/X11 with recent XQuartz as the X server, they are 1,2,3;  other X servers may differ. 

Here is an example I wrote that adds a context menu to a Listbox:

import tkinter # Tkinter -> tkinter in Python 3  class FancyListbox(tkinter.Listbox):      def __init__(self, parent, *args, **kwargs):         tkinter.Listbox.__init__(self, parent, *args, **kwargs)          self.popup_menu = tkinter.Menu(self, tearoff=0)         self.popup_menu.add_command(label="Delete",                                     command=self.delete_selected)         self.popup_menu.add_command(label="Select All",                                     command=self.select_all)          self.bind("<Button-3>", self.popup) # Button-2 on Aqua      def popup(self, event):         try:             self.popup_menu.tk_popup(event.x_root, event.y_root, 0)         finally:             self.popup_menu.grab_release()      def delete_selected(self):         for i in self.curselection()[::-1]:             self.delete(i)      def select_all(self):         self.selection_set(0, 'end')   root = tkinter.Tk() flb = FancyListbox(root, selectmode='multiple') for n in range(10):     flb.insert('end', n) flb.pack() root.mainloop() 

The use of grab_release() was observed in an example on effbot.
Its effect might not be the same on all systems.

like image 182
Honest Abe Avatar answered Oct 13 '22 01:10

Honest Abe


I made some changes to the conext menu code above in order to adjust my demand and I think it would be useful to share:

Version 1:

import tkinter as tk from tkinter import ttk  class Main(tk.Frame):     def __init__(self, master):         tk.Frame.__init__(self, master)         master.geometry('500x350')         self.master = master         self.tree = ttk.Treeview(self.master, height=15)         self.tree.pack(fill='x')         self.btn = tk.Button(master, text='click', command=self.clickbtn)         self.btn.pack()         self.aMenu = tk.Menu(master, tearoff=0)         self.aMenu.add_command(label='Delete', command=self.delete)         self.aMenu.add_command(label='Say Hello', command=self.hello)         self.num = 0          # attach popup to treeview widget         self.tree.bind("<Button-3>", self.popup)      def clickbtn(self):         text = 'Hello ' + str(self.num)         self.tree.insert('', 'end', text=text)         self.num += 1      def delete(self):         print(self.tree.focus())         if self.iid:             self.tree.delete(self.iid)      def hello(self):         print ('hello!')      def popup(self, event):         self.iid = self.tree.identify_row(event.y)         if self.iid:             # mouse pointer over item             self.tree.selection_set(self.iid)             self.aMenu.post(event.x_root, event.y_root)                     else:             pass  root = tk.Tk() app=Main(root) root.mainloop() 

Version 2:

import tkinter as tk from tkinter import ttk  class Main(tk.Frame):     def __init__(self, master):         master.geometry('500x350')         self.master = master         tk.Frame.__init__(self, master)         self.tree = ttk.Treeview(self.master, height=15)         self.tree.pack(fill='x')         self.btn = tk.Button(master, text='click', command=self.clickbtn)         self.btn.pack()         self.rclick = RightClick(self.master)         self.num = 0          # attach popup to treeview widget         self.tree.bind('<Button-3>', self.rclick.popup)     def clickbtn(self):         text = 'Hello ' + str(self.num)         self.tree.insert('', 'end', text=text)         self.num += 1  class RightClick:     def __init__(self, master):                 # create a popup menu         self.aMenu = tk.Menu(master, tearoff=0)         self.aMenu.add_command(label='Delete', command=self.delete)         self.aMenu.add_command(label='Say Hello', command=self.hello)          self.tree_item = ''      def delete(self):         if self.tree_item:             app.tree.delete(self.tree_item)      def hello(self):         print ('hello!')      def popup(self, event):         self.aMenu.post(event.x_root, event.y_root)         self.tree_item = app.tree.focus()  root = tk.Tk() app=Main(root) root.mainloop() 
like image 38
bzimor Avatar answered Oct 13 '22 01:10

bzimor