gpt4 book ai didi

python - 如何智能地将 DRY 原则应用到 tkinter OptionMenu 中?

转载 作者:行者123 更新时间:2023-11-30 22:06:26 25 4
gpt4 key购买 nike

我有一个简单的 tkinter 应用程序,现在可以正常工作,但代码写得不好。我的主要问题是,似乎每个 OptionMenu 都需要有自己的 tkvar 和自己的 testFunc 才能让应用程序按照我想要的方式运行。我似乎无法在命令部分调用其他变量,这就是为什么我很难合并这段代码。

该应用程序的目的是允许用户选择动物的顺序并立即显示该顺序。希望有人能为我点亮一盏明灯,让这段代码变得更DRY、更智能。

import tkinter as tk
from tkinter import ttk

class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.wm_title("Choose Multiple Animals")
self._frame = None

class AnimalPage(ttk.Frame):
def __init__(self, master, controller):
tk.Frame.__init__(self, master)
self.master = master
self.config(relief='sunken', borderwidth=2)
self.pack(fill = "both", expand = False)
self.grid_rowconfigure(0, weight = 1)
self.grid_columnconfigure(0, weight = 1)

self.animalList = ['Cat', 'Dog', 'Bear']
self.choices = ['None', 'Animal1', 'Animal2', 'Animal3']
self.tkvar1 = tk.StringVar(master)
self.tkvar1.set('None')
self.tkvar2 = tk.StringVar(master)
self.tkvar2.set('None')
self.tkvar3 = tk.StringVar(master)
self.tkvar3.set('None')
self.tkvar4 = tk.StringVar()

self.textLabel1 = ttk.Label(self, text=self.animalList[0])
self.textLabel1.grid(column=0, row = 5, sticky = (tk.W), padx=5, pady=5)
self.popupMenu1 = ttk.OptionMenu(self, self.tkvar1, *self.choices, command=self.testFunc1)
self.popupMenu1.grid(column=1, row = 5, sticky = (tk.W, tk.E), padx=5, pady=5)
self.textLabel2 = ttk.Label(self, text=self.animalList[1])
self.textLabel2.grid(column=0, row = 6, sticky = (tk.W), padx=5, pady=5)
self.popupMenu2 = ttk.OptionMenu(self, self.tkvar2, *self.choices, command=self.testFunc2)
self.popupMenu2.grid(column=1, row = 6, sticky = (tk.W, tk.E), padx=5, pady=5)
self.textLabel3 = ttk.Label(self, text=self.animalList[2])
self.textLabel3.grid(column=0, row = 7, sticky = (tk.W), padx=5, pady=5)
self.popupMenu3 = ttk.OptionMenu(self, self.tkvar3, *self.choices, command=self.testFunc3)
self.popupMenu3.grid(column=1, row = 7, sticky = (tk.W, tk.E), padx=5, pady=5)
self.chosenAnimals = {}

self.textLabel4 = ttk.Label(self, text=self.tkvar4.get())
self.textLabel4.grid(column=0, row = 8, sticky = (tk.W, tk.E), padx=5, pady=5)

def testFunc1(self, value):
self.chosenAnimals.update({value: self.animalList[0]})
self.configure()

def testFunc2(self, value):
self.chosenAnimals.update({value: self.animalList[1]})
self.configure()

def testFunc3(self, value):
self.chosenAnimals.update({value: self.animalList[2]})
self.configure()

def configure(self):
self.printout = ["{} is the {}".format(k, v) for (k,v) in self.chosenAnimals.items()]
self.tkvar4.set(self.printout)
self.textLabel4.config(text = self.tkvar4.get())

if __name__ == "__main__":
app = SampleApp()
newFrame = AnimalPage(app, app)
app.geometry("500x200")
app.mainloop()

最佳答案

使用数组或字典:

import tkinter as tk
from tkinter import ttk

class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.wm_title("Choose Multiple Animals")
self._frame = None

class AnimalPage(ttk.Frame):
def __init__(self, master, controller):
tk.Frame.__init__(self, master)
self.master = master
self.config(relief='sunken', borderwidth=2)
self.pack(fill = "both", expand = False)
self.grid_rowconfigure(0, weight = 1)
self.grid_columnconfigure(0, weight = 1)

self.animalList = ['Cat', 'Dog', 'Bear']
self.choices = ['None', 'Animal1', 'Animal2', 'Animal3']

self.animal_vars = dict()
self.text_labels = dict()
self.popup_menus = dict()
self.chosenAnimals = {}
self.tkvar4 = tk.StringVar()

for i, animal in enumerate(self.animalList):
self.animal_vars[animal] = tk.StringVar(master)
self.animal_vars[animal].set('None')
self.text_labels[animal] = ttk.Label(self, text=animal)
self.text_labels[animal].grid(column=0, row = 5 + i, sticky = (tk.W), padx=5, pady=5)
self.popup_menus[animal] = ttk.OptionMenu(self, self.animal_vars[animal], *self.choices, command=lambda selected, my_animal=animal: self.testFunc(my_animal, selected))
self.popup_menus[animal].grid(column=1, row = 5 + i, sticky = (tk.W, tk.E), padx=5, pady=5)

self.textLabel4 = ttk.Label(self, text=self.tkvar4.get())
self.textLabel4.grid(column=0, row = 8, sticky = (tk.W, tk.E), padx=5, pady=5)

def testFunc(self, animal, selection):
self.chosenAnimals.update({animal: selection})
self.configure()

def configure(self):
self.printout = ["{} is the {}".format(k, v) for (k,v) in self.chosenAnimals.items()]
self.tkvar4.set(self.printout)
self.textLabel4.config(text = self.tkvar4.get())

由于您基本上是通过 animalList 进行迭代来动态创建 LabelOptionMenu,因此您不妨使用 dictlist 来帮助您管理和迭代对象。

设置好 dictlist 后,您现在可以将您创建的 tk 小部件分配/附加到其中并轻松引用。在您的示例中,我个人更喜欢 dict 因为每种动物都有一个有意义的名称,并且更容易调试(查找 self.text_labels['Cat'] 会比self.text_labels[0])

此外,您还可以利用 lambda 来绕过 tk 小部件中 command=... 的限制。这样您就可以将动物名称直接传递回函数,这样您就不需要为每个动物定义它。

顺便说一句,理想情况下我建议您为对象指定更有意义的名称。远离诸如 textLabel4tkvar4 之类的术语,这样更容易理解代码。

重要说明:

为了让 lambda 在循环中工作,您需要将迭代的 animal 作为默认参数,而不是直接在 lambda 内部,一个快速演示:

def func(v):
print(v)

x = list(range(3))
for i in range(len(x)):
x[i] = lambda: func(i)

x[0]
# 2

您可能期望 x[0] 将导致打印 0,但实际上它将是 2,并且它将是x[0:2] 的结果相同。原因是,当 lambda 被赋值时,它在每次迭代中引用 i 对象,而不是它的 [0, 1, 2] 值。因此,自从循环结束后,i = 2,并且您的x函数将始终打印2

但是,如果您将 i 作为 lambda 中的默认参数传递,则将传递 :

x[i] = lambda y=i: func(y)

x[0]()
# 0

结合这个事实,我之所以使用lambda selected, my_animal=animal:...是由于command=...中的OptionMenu 始终将其变量(在本例中为选定的 Animal1、Animal2...)作为函数的第一个参数传递。

希望这能让事情变得更清楚。

关于python - 如何智能地将 DRY 原则应用到 tkinter OptionMenu 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52725403/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com