Stardand context menu in Python TKinter text widget when mouse right button is pressed
I found a way, thanks to this post. I made some modifications. At the bottom part there's a minimal main
to try it.
from Tkinter import *def rClicker(e): ''' right click context menu for all Tk Entry and Text widgets ''' try: def rClick_Copy(e, apnd=0): e.widget.event_generate('<Control-c>') def rClick_Cut(e): e.widget.event_generate('<Control-x>') def rClick_Paste(e): e.widget.event_generate('<Control-v>') e.widget.focus() nclst=[ (' Cut', lambda e=e: rClick_Cut(e)), (' Copy', lambda e=e: rClick_Copy(e)), (' Paste', lambda e=e: rClick_Paste(e)), ] rmenu = Menu(None, tearoff=0, takefocus=0) for (txt, cmd) in nclst: rmenu.add_command(label=txt, command=cmd) rmenu.tk_popup(e.x_root+40, e.y_root+10,entry="0") except TclError: print ' - rClick menu, something wrong' pass return "break"def rClickbinder(r): try: for b in [ 'Text', 'Entry', 'Listbox', 'Label']: # r.bind_class(b, sequence='<Button-3>', func=rClicker, add='') except TclError: print ' - rClickbinder, something wrong' passif __name__ == '__main__': master = Tk() ent = Entry(master, width=50) ent.pack(anchor="w") #bind context menu to a specific element ent.bind('<Button-3>',rClicker, add='') #or bind it to any Text/Entry/Listbox/Label element #rClickbinder(master) master.mainloop()
Thought I would share my solution, based on several code snippets on stackoverflow. It includes a minimum application to test:
edit: class bindings might not work. Should that be the case use normal bindings and return "break" in the select_all functions.
import Tkinter as tkif 1: # nice widgets import ttkelse: ttk = tkclass EntryPlus(ttk.Entry): def __init__(self, *args, **kwargs): ttk.Entry.__init__(self, *args, **kwargs) _rc_menu_install(self) # overwrite default class binding so we don't need to return "break" self.bind_class("Entry", "<Control-a>", self.event_select_all) self.bind("<Button-3><ButtonRelease-3>", self.show_menu) def event_select_all(self, *args): self.focus_force() self.selection_range(0, tk.END) def show_menu(self, e): self.tk.call("tk_popup", self.menu, e.x_root, e.y_root)class TextPlus(tk.Text): def __init__(self, *args, **kwargs): tk.Text.__init__(self, *args, **kwargs) _rc_menu_install(self) # overwrite default class binding so we don't need to return "break" self.bind_class("Text", "<Control-a>", self.event_select_all) self.bind("<Button-3><ButtonRelease-3>", self.show_menu) def event_select_all(self, *args): self.focus_force() self.tag_add("sel","1.0","end") def show_menu(self, e): self.tk.call("tk_popup", self.menu, e.x_root, e.y_root)def _rc_menu_install(w): w.menu = tk.Menu(w, tearoff=0) w.menu.add_command(label="Cut") w.menu.add_command(label="Copy") w.menu.add_command(label="Paste") w.menu.add_separator() w.menu.add_command(label="Select all") w.menu.entryconfigure("Cut", command=lambda: w.focus_force() or w.event_generate("<<Cut>>")) w.menu.entryconfigure("Copy", command=lambda: w.focus_force() or w.event_generate("<<Copy>>")) w.menu.entryconfigure("Paste", command=lambda: w.focus_force() or w.event_generate("<<Paste>>")) w.menu.entryconfigure("Select all", command=w.event_select_all) if __name__ == "__main__": class SampleApp(tk.Tk): def __init__(self, *args, **kwargs): tk.Tk.__init__(self, *args, **kwargs) self.entry = EntryPlus(self) self.text = TextPlus(self) self.entry.pack() self.text.pack() self.entry.insert(0, "copy paste") self.text.insert(tk.INSERT, "copy paste") app = SampleApp() app.mainloop()
Thanks to Gonzo's code and bluish '<Control-c>',
my textwidget right button worked.I don't have 50 reputations but I had a problem which I faced that the right button will trigger the function regardless of the location of the point clicked.figured that out by:
def show_menu(self, e): if e.widget==self.text: self.tk.call("tk_popup", self.menu, e.x_root, e.y_root)
Now only the right button's popup will be triggered when you click on text widget.