我正在从列表列表中创建一个Frames网格。我有一个Frame类,它有一个绑定到Button-1的弹出菜单。我想使用此弹出菜单更新调用弹出菜单的Frame内的标签。现在只有最后一帧的标签文本正在更新。
我试过玩绑定标签,因为我想也许Button-1只能绑定到最近创建的小部件,但似乎这是一个红色的鲱鱼。我实际上完全不知道从哪里开始。
#!/bin/env python
import tkinter
import tkinter.messagebox
def create_grid():
grid = []
for i in range(0, 3001, 1000):
row = []
for j in range(8):
row.append(i+j)
grid.append(row)
grid.reverse()
return grid
class GridFrame(tkinter.Frame):
def __init__(self, root, index, *args, **kwargs):
tkinter.Frame.__init__(self, root, *args, **kwargs)
self.labeltext = tkinter.StringVar()
self.labeltext.set("+")
self.popup_menu = tkinter.Menu(root, tearoff=0)
self.popup_menu.add_command(label="Set label to 'test'", command=self.update_label)
self.popup_menu.add_command(label="Do nothing", command=print)
self.bind_all("<Button-1>", self.popup)
self.bind_all("<Button-3>", self.reset_label)
self.index_label = tkinter.Label(self, text="{0:04d}".format(index))
self.index_label.pack()
self.framelabel = tkinter.Label(self, textvariable=self.labeltext)
self.framelabel.pack()
def popup(self, event):
try:
self.popup_menu.tk_popup(event.x_root, event.y_root, 0)
finally:
self.popup_menu.grab_release()
def reset_label(self, event):
self.labeltext.set("+")
def update_label(self):
self.labeltext.set("test")
class GridGUI:
def __init__(self, root, grid, *args, **kwargs):
self.root = root
root.title("Grid")
for i, row in enumerate(grid):
for j, index in enumerate(row):
gridframe = GridFrame(root, index)
gridframe.config(borderwidth=3, relief="raised")
gridframe.grid(row=i, column=j, padx=2, pady=2, ipadx=20, ipady=30, sticky="nsew")
def main():
grid = create_grid()
root = tkinter.Tk()
menubar = tkinter.Menu(root)
filemenu = tkinter.Menu(menubar, tearoff=0)
filemenu.add_command(label="Exit", command=root.quit)
menubar.add_cascade(label="File", menu=filemenu)
root.config(menu=menubar)
my_gui = GridGUI(root, grid)
root.mainloop()
main()
我希望弹出菜单中框架中的标签会更新。使用我当前的代码,它似乎只是最近创建的Frame更新中的标签。
你的问题是你正在使用bind_all
,并且每次创建新的GridFrame
时都要重置该绑定。因此,只有与最后一个GridFrame
的绑定才能被tkinter识别。当tkinter检测到绑定时,它会调用最终popup
的GridFrame
方法。
您可以解决此问题,因为事件对象会告诉您单击了哪个窗口小部件。从那里,您可以确定单击了哪个GridFrame
实例。但是,有一个更简单的解决方案。
解决方案是不使用bind_all
,而是单独绑定到每个小部件。当绑定触发时,它将调用与适当对象关联的函数。
创建窗口小部件后,将绑定应用于每个窗口小部件:
class GridFrame(tkinter.Frame):
def __init__(self, root, index, *args, **kwargs):
...
for widget in (self, self.index_label, self.framelabel):
widget.bind("<Button-1>", self.popup)
这样做的另一个好处是,您可以在屏幕上显示任何其他小部件,这些小部件接受按钮单击(按钮,滚动条等),而不会受到全局绑定鼠标单击的影响。
如果你不介意丑陋的黑客,这是一种方法来做到这一点。而不是在每个框架内创建单独的弹出菜单,只在主GUI中创建一个,检查弹出位置,并更改相应的StringVar
。
import tkinter
import tkinter.messagebox
y = []
def create_grid():
grid_list = []
for i in range(0, 3001, 1000):
row = []
for j in range(8):
row.append(i+j)
grid_list.append(row)
grid_list.reverse()
return grid_list
class GridFrame(tkinter.Frame):
def __init__(self, root, index, num, *args, **kwargs):
tkinter.Frame.__init__(self, root, *args, **kwargs,name=f"{num}")
self.labeltext = tkinter.StringVar()
y.append(self.labeltext)
self.labeltext.set("+")
self.index_label = tkinter.Label(self, text="{0:04d}".format(index))
self.index_label.pack()
self.framelabel = tkinter.Label(self, textvariable=self.labeltext)
self.framelabel.pack()
class GridGUI:
def __init__(self, root, grid_list, *args, **kwargs):
root.title("Grid")
num = 0
for i, row in enumerate(grid_list):
for j, index in enumerate(row):
gridframe = GridFrame(root, index, num)
gridframe.config(borderwidth=3, relief="raised")
gridframe.grid(row=i, column=j, padx=2, pady=2, ipadx=20, ipady=30, sticky="nsew")
num+=1
self.popup_menu = tkinter.Menu(root, tearoff=0)
self.popup_menu.add_command(label="Set label to 'test'", command=lambda: self.update_label(self.event))
self.popup_menu.add_command(label="Do nothing", command=lambda: self.reset_label(self.event))
root.bind_all("<Button-1>", self.popup)
root.bind_all("<Button-3>", self.reset_label)
def popup(self, event):
self.event = event
try:
self.popup_menu.tk_popup(event.x_root, event.y_root, 0)
finally:
self.popup_menu.grab_release()
def reset_label(self, event):
result = str(event.widget).split(".")[1]
y[int(result)].set("+")
def update_label(self,event):
result = str(event.widget).split(".")[1]
y[int(result)].set("test")
def main():
grid = create_grid()
root = tkinter.Tk()
menubar = tkinter.Menu(root)
filemenu = tkinter.Menu(menubar, tearoff=0)
filemenu.add_command(label="Exit", command=root.quit)
menubar.add_cascade(label="File", menu=filemenu)
root.config(menu=menubar)
my_gui = GridGUI(root, grid)
root.mainloop()
main()