如何在Python的tkinter中从链接菜单项传递值?

huangapple go评论122阅读模式
英文:

How to pass values from a linked menu item in python tkinter?

问题

To create a variable to hold and pass the label of the item selected from a drop-down menu in tkinter, you can use the following code:

  1. import tkinter as tk
  2. def selection_changed(event):
  3. selected_label = selection.get()
  4. print(f"Selected label: {selected_label}")
  5. root = tk.Tk()
  6. root.geometry("200x170")
  7. selection = tk.StringVar(root)
  8. selection.set("Select an item") # Set an initial value
  9. options = ["Option 1", "Option 2", "Option 3"]
  10. option_menu = tk.OptionMenu(root, selection, *options)
  11. option_menu.pack()
  12. selection.trace("w", selection_changed)
  13. root.mainloop()

As for sending a second variable into a function in tkinter at the same time, you can modify the selection_changed function to accept both the selected label and your spare variable:

  1. def selection_changed(event):
  2. selected_label = selection.get()
  3. spare_variable = "Your spare variable here" # Replace with your actual spare variable
  4. print(f"Selected label: {selected_label}")
  5. print(f"Spare variable: {spare_variable}")

Then, when you call the selection_changed function, you can pass your spare variable along with the selected label.

英文:

How to create a variable to hold and pass label of the item selected from a drop down menu? While I'm here, may I ask how to send a second (spare) variable into a function in tkinter at the same time. I'm not using classes or "OptionMenu".
Here's what I have...

  1. ## In a tkinter I have this...
  2. import tkinter as tk
  3. from tkinter import Toplevel, Button, Tk, Menu, Frame
  4. root = Tk()
  5. xr, yr = 200, 170
  6. rg = str(xr) + "x" + str(yr) # concatinating
  7. root.geometry(rg)
  8. selection = tk.StringVar(root)
  9. print(f"Before option menu selected, selection.get() = {selection.get()}")
  10. def confirm_stage(*args):
  11. global selection
  12. print(f"selection.get(): {selection.get()}")
  13. print(f"selection: {selection}") # Gives PY_VAR0, How to 'decode' PY_VAR0 ?
  14. # print(f"*selection: {*selection}") # gives and error. Cant use '*' in f-strings
  15. print(f"selection[0]: {selection[0]}") # gives and error: 'StringVar' object is not subscriptable
  16. menubar = Menu(root)
  17. stage_menu_item = Menu(menubar, tearoff=0)
  18. menubar.add_cascade(label="Stage", menu=stage_menu_item) # Shows the caption
  19. stage_menu_item.add_command(label="1", command = confirm_stage)
  20. stage_menu_item.add_command(label="2", command = confirm_stage)
  21. root.config(menu=menubar)
  22. root.mainloop()

The "confirm_stage" appears to offer no argument to the called function. It surely can't be that each single item selected has to point to a new separate and unique function specifically written for each item in the menu, although it would work, the code would be unwieldy.

I've seen references to StringVar() which I don't fully understand, and tried to apply it but no joy, and then how would one pass a useful second accompanying variable?

UPDATE 2: - Stripped down code. I need to get the label of the menu item clicked on.

  1. import tkinter as tk
  2. from tkinter import Toplevel, Button, Tk, Menu, Frame
  3. root = Tk()
  4. root.geometry("200x170")
  5. def donation_fn(selection):
  6. a= clicked_r.get()
  7. print(f"a= {a}")
  8. print(f"selection= {selection}")
  9. b= clicked_m.get()
  10. print(f"b= {b}")
  11. print(f"selection= {selection}")
  12. menubar = Menu(root)
  13. clicked_r = tk.StringVar(root)
  14. clicked_m = tk.StringVar(menubar)
  15. list=['1 donation', '2 donations', '3 donations']
  16. donation = Menu(menubar, tearoff=0)
  17. menubar.add_cascade(label="Donations", menu=donation) # Shows the caption
  18. for _ in list: donation.add_command(label=_, command = lambda: donation_fn(clicked_m.get()))
  19. # port_item.add_command(label=list, command = lambda: None)
  20. root.config(menu=menubar)
  21. root.mainloop()

答案1

得分: 1

  1. 如果你只想将菜单文本传递给回调函数并且不希望在每次调用`.add_command()`时都这样做那么你可以创建一个继承自`tk.Menu`的自定义类并且重写类方法`add_command()`在重写的`add_command()`方法内部当执行时你可以将菜单文本传递给指定的回调函数
  2. ```python
  3. import tkinter as tk
  4. class MyMenu(tk.Menu):
  5. def add_command(self, **kwargs):
  6. callback = kwargs.pop('command', None)
  7. text = kwargs.get('label')
  8. # 将菜单文本传递给指定的回调函数
  9. super().add_command(**kwargs, command=(lambda: callback(text)) if callback else None)
  10. root = tk.Tk()
  11. xr, yr = 200, 170
  12. root.geometry(f"{xr}x{yr}")
  13. def confirm_stage(menu_text):
  14. print(menu_text)
  15. menubar = tk.Menu(root)
  16. # 使用MyMenu而不是tk.Menu
  17. stage_menu_item = MyMenu(menubar, tearoff=0)
  18. menubar.add_cascade(label="Stage", menu=stage_menu_item)
  19. stage_menu_item.add_command(label="1", command=confirm_stage)
  20. stage_menu_item.add_command(label="2", command=confirm_stage)
  21. root.config(menu=menubar)
  22. root.mainloop()
英文:

If you just want to pass the menu text to the callback and don't want to do it in every call of .add_command(), then you can create a custom class inheriting from tk.Menu and override the class method add_command(). Inside the override add_command(), you can pass the menu text to the given callback when it is being executed.

  1. import tkinter as tk
  2. class MyMenu(tk.Menu):
  3. def add_command(self, **kwargs):
  4. callback = kwargs.pop('command', None)
  5. text = kwargs.get('label')
  6. # pass the menu text to the given callback
  7. super().add_command(**kwargs, command=(lambda: callback(text)) if callback else None)
  8. root = tk.Tk()
  9. xr, yr = 200, 170
  10. root.geometry(f"{xr}x{yr}")
  11. def confirm_stage(menu_text):
  12. print(menu_text)
  13. menubar = tk.Menu(root)
  14. # Use MyMenu instead of tk.Menu
  15. stage_menu_item = MyMenu(menubar, tearoff=0)
  16. menubar.add_cascade(label="Stage", menu=stage_menu_item)
  17. stage_menu_item.add_command(label="1", command=confirm_stage)
  18. stage_menu_item.add_command(label="2", command=confirm_stage)
  19. root.config(menu=menubar)
  20. root.mainloop()

答案2

得分: 0

这是您想要的吗?

  1. import tkinter as tk
  2. from tkinter import Toplevel, Button, Tk, Menu, Frame
  3. root = Tk()
  4. xr, yr = 200, 170
  5. rg = str(xr) + "x" + str(yr) # 连接字符串
  6. root.geometry(rg)
  7. def confirm_stage(var):
  8. print(var)
  9. menubar = Menu(root)
  10. stage_menu_item = Menu(menubar, tearoff=0)
  11. menubar.add_cascade(label="Stage", menu=stage_menu_item)
  12. stage_menu_item.add_command(label="1", command=lambda: confirm_stage(1))
  13. stage_menu_item.add_command(label="2", command=lambda: confirm_stage(2))
  14. root.config(menu=menubar)
  15. root.mainloop()

如果需要进一步的帮助,请告诉我。

英文:

Is this what you want?

  1. import tkinter as tk
  2. from tkinter import Toplevel, Button, Tk, Menu, Frame
  3. root = Tk()
  4. xr, yr = 200, 170
  5. rg = str(xr) + "x" + str(yr) # concatinating
  6. root.geometry(rg)
  7. # selection = tk.StringVar()
  8. # print(f"Before option menu selected, selection.get() = {selection.get()}")
  9. # def confirm_stage(*args):
  10. # global selection
  11. # print(f"selection.get(): {selection.get()}")
  12. # print(f"selection: {selection}") # Gives PY_VAR0, How to 'decode' PY_VAR0 ?
  13. # # print(f"*selection: {*selection}") # gives and error. Cant use '*' in f-strings
  14. # print(f"selection[0]: {selection[0]}") # gives and error: 'StringVar' object is not subscriptable
  15. def confirm_stage(var):
  16. print(var)
  17. menubar = Menu(root)
  18. stage_menu_item = Menu(menubar, tearoff=0)
  19. menubar.add_cascade(label="Stage", menu=stage_menu_item) # Shows the caption
  20. # stage_menu_item.add_command(label="1", command = confirm_stage)
  21. # stage_menu_item.add_command(label="2", command = confirm_stage)
  22. stage_menu_item.add_command(label="1", command = lambda: confirm_stage(1))
  23. stage_menu_item.add_command(label="2", command = lambda: confirm_stage(2))
  24. root.config(menu=menubar)
  25. root.mainloop()

答案3

得分: 0

以下是翻译好的部分:

上面提到的“lambda”解决方案,

...
stage_menu_item.add_command(label="1", command = lambda: confirm_stage(1))
...

似乎适用于静态参数,但如果您尝试在循环中使用它,例如:

...
for _x in range(5):
stage_menu_item.add_command(label='Menu'+str(_x), command=lambda: confirm_stage(str(_x)))
...

它不起作用。“_x”始终为“4”,无论您调用哪个菜单项。不知道为什么。

对我而言唯一有效的方法是:

def _set(x:str):
return lambda: confirm_stage(x)

...

for _x in range(5):
stage_menu_item.add_command(label=str(_x), command=_set(str(_x)))

这将产生所需的结果:“0”,“1”,“2”,“3”,“4”

如果有人能告诉我为什么上述简单解决方案(不使用_set函数)不起作用,请继续。

我忘了提到我用tk.Menu替换了MyMenu,因为我不喜欢那种方法。

英文:

The above mentioned "lambda" only solution,

  1. ...
  2. stage_menu_item.add_command(label="1", command = lambda: confirm_stage(1))
  3. ...

seems to work with static parameters, but if you try it with a loop, eg:

  1. ...
  2. for _x in range(5):
  3. stage_menu_item.add_command(label='Menu'+str(_x), command=lambda: confirm_stage(str(_x)))
  4. ...

It doesn't. "_x" is always "4" no matter what menu item you call. Don't know why.

The only way it worked for me was:

  1. def _set(x:str):
  2. return lambda: confirm_stage(x)
  3. ...
  4. for _x in range(5):
  5. stage_menu_item.add_command(label=str(_x), command=_set(str(_x)))

Which renders the wanted result of: "0", "1", "2", "3", "4"

If anyone can enlighten me, why the above easy solution (without _set function) is not working, go ahead.

I forgot to mention I replaced the MyMenu with tk.Menu, since I didn't liked that approach.

huangapple
  • 本文由 发表于 2023年5月13日 23:14:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76243474.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定