开发者

Stardand context menu in Python TKinter text widget when mouse right button is pressed

开发者 https://www.devze.com 2023-01-27 06:01 出处:网络
I\'m working on Windows XP, with Python 2.6.x and TKinter. Usin开发者_运维百科g a text widget in the app, but the standard popup menu (cut, copy, paste, delete, select all) is missing. How to make it

I'm working on Windows XP, with Python 2.6.x and TKinter. Usin开发者_运维百科g a text widget in the app, but the standard popup menu (cut, copy, paste, delete, select all) is missing. How to make it appear?


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'
        pass


if __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 tk

if 1:  # nice widgets
    import ttk
else:
    ttk = tk

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


To add function that deletes selected text in tkinter text widget, use:

def delete_highlighted_text():
    text_widget.event_generate("<Delete>")
0

精彩评论

暂无评论...
验证码 换一张
取 消