【发布时间】:2012-08-17 23:04:16
【问题描述】:
我有一个 python-tkinter gui 应用程序,我一直在尝试找到一些方法来添加一些功能。我希望有一种方法可以右键单击应用程序列表框区域中的项目并调出上下文菜单。 tkinter 能够做到这一点吗?看看 gtk 或其他一些 gui-toolkit 会更好吗?
【问题讨论】:
标签: python user-interface tkinter contextmenu
我有一个 python-tkinter gui 应用程序,我一直在尝试找到一些方法来添加一些功能。我希望有一种方法可以右键单击应用程序列表框区域中的项目并调出上下文菜单。 tkinter 能够做到这一点吗?看看 gtk 或其他一些 gui-toolkit 会更好吗?
【问题讨论】:
标签: python user-interface tkinter contextmenu
您将创建一个Menu 实例并编写一个调用
的函数
它的post() 或tk_popup() 方法。
tkinter documentation 目前没有关于tk_popup() 的任何信息。
阅读Tk documentation 获取说明或来源:
library/menu.tcl in the Tcl/Tk source:
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)
您将上下文菜单调用函数与通过右键单击相关联:the_widget_clicked_on.bind("<Button-3>", your_function)。
但是,与右键单击相关的数字在每个平台上并不相同。
library/tk.tcl in the Tcl/Tk source:
这是我编写的一个示例,它向列表框添加了上下文菜单:
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()
在an example on effbot中观察到grab_release()的使用。
它对所有系统的影响可能并不相同。
【讨论】:
Tk。
grab_release 对我的影响(Ubuntu、Python 3.6.8、tkinter 8.6)正是我不想要的:它使弹出菜单保持打开状态即使我点击了其他地方。我花了一些时间才发现这就是原因。希望这条评论可以节省其他人的时间:)
为了调整我的需求,我对上面的conext菜单代码做了一些更改,我认为分享一下会很有用:
版本 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()
版本 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()
【讨论】:
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()
【讨论】:
重要提示:
(假设包含坐标的事件参数称为“事件”):除非您使用“event.x_root”和“event.y_root”作为参数,否则调用 tk_popup(...) 时不会发生任何事情或不可见.如果你很明显地使用“event.x”和“event.y”,它不会起作用,即使坐标的名称是“x”和“y”并且没有提到“x_root”和"y_root" 中的任何位置。
至于grab_release(..),在任何地方都没有必要。 “tearoff=0”也不是必需的,将其设置为 1(这是默认值),只需在上下文菜单中添加一个虚线条目。如果单击它,它会分离上下文菜单并使其成为带有窗口装饰器的顶级窗口。 tearoff=0 将隐藏此条目。此外,如果您将菜单的主设置为任何特定的小部件或根目录,或者任何其他内容,都没有关系。
【讨论】:
【讨论】: