tkinter app adding a right click context menu? tkinter app adding a right click context menu? tkinter tkinter

tkinter app adding a right click context menu?


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 traversingthe 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 3class 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.


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 tkfrom tkinter import ttkclass 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:            passroot = tk.Tk()app=Main(root)root.mainloop()

Version 2:

import tkinter as tkfrom tkinter import ttkclass 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 += 1class 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()


from tkinter import *root=Tk()root.geometry("500x400+200+100")class Menu_Entry(Entry):    def __init__(self,perant,*args,**kwargs):        Entry.__init__(self,perant,*args,**kwargs)        self.popup_menu=Menu(self,tearoff=0,background='#1c1b1a',fg='white',                                     activebackground='#534c5c',                             activeforeground='Yellow')        self.popup_menu.add_command(label="Cut                     ",command=self.Cut,                                    accelerator='Ctrl+V')        self.popup_menu.add_command(label="Copy                    ",command=self.Copy,compound=LEFT,                                    accelerator='Ctrl+C')            self.popup_menu.add_command(label="Paste                   ",command=self.Paste,accelerator='Ctrl+V')        self.popup_menu.add_separator()        self.popup_menu.add_command(label="Select all",command=self.select_all,accelerator="Ctrl+A")        self.popup_menu.add_command(label="Delete",command=self.delete_only,accelerator=" Delete")        self.popup_menu.add_command(label="Delete all",command=self.delete_selected,accelerator="Ctrl+D")        self.bind('<Button-3>',self.popup)        self.bind("<Control-d>",self.delete_selected_with_e1)        self.bind('<App>',self.popup)        self.context_menu = Menu(self, tearoff=0)        self.context_menu.add_command(label="Cut")        self.context_menu.add_command(label="Copy")        self.context_menu.add_command(label="Paste")             def popup(self, event):      try:        self.popup_menu.tk_popup(event.x_root, event.y_root, 0)      finally:        self.popup_menu.grab_release()    def Copy(self):      self.event_generate('<<Copy>>')    def Paste(self):      self.event_generate('<<Paste>>')    def Cut(self):      self.event_generate('<<Cut>>')    def delete_selected_with_e1(self,event):      self.select_range(0, END)      self.focus()      self.event_generate("<Delete>")    def delete_selected(self):      self.select_range(0, END)      self.focus()      self.event_generate("<Delete>")    def delete_only(self):      self.event_generate("<BackSpace>")    def select_all(self):      self.select_range(0, END)      self.focus()ent=Menu_Entry(root)ent.pack()root.mainloop()