我在下面的代码基于这个问题:Expandable and contracting frame in Tkinter
from tkinter import *
from tkinter import ttk
class Example:
def __init__(self, root):
list_1 = ['item_1', 'item_2']
list_2 = ['a', 'b', 'c']
for item in list_1 :
frame = Frame(root, width=500, height=22)
frame.pack(side=TOP, anchor=W)
frame.propagate(0)
Label(frame, text=item).pack(side=LEFT, anchor=W)
entry = Entry(frame, width=11)
entry.pack(side=RIGHT, pady=2, anchor=E)
var = IntVar()
var.set(0)
sub_frame = Frame(root, relief="sunken", width=400, height=22, borderwidth=1)
toggle_btn = ttk.Checkbutton(frame, width=2, text='+', command=lambda: self.toggle(var, toggle_btn, sub_frame),
variable=var, style='Toolbutton')
toggle_btn.pack(side=LEFT, anchor=E)
for item in list_2:
Label(sub_frame, text=item).pack(side=TOP)
def toggle(self, show, toggle_button, sub_frame):
if bool(show.get()):
sub_frame.pack(fill="x", expand=1)
sub_frame.propagate(1)
toggle_button.configure(text='-')
else:
sub_frame.forget()
toggle_button.configure(text='+')
def main():
root = Tk()
open = Example(root)
root.mainloop()
if __name__ == '__main__':
main()
我想在这里从list_1创建两个可扩展框架,并用list_2中的标签填充每个框架。然后,这两个框架应该是可扩展和可折叠的,以显示和隐藏list_2中的项目。
第二帧功能正常,但第一帧不起作用,因为它被第二帧覆盖。
所以代码最初给你这个:
如果您点击' +'在item_1旁边,没有任何反应。
如果您点击' +'在item_2旁边,会发生这种情况。
点击' +'在item_1旁边,我希望sub_frame显示在item_1下面(在item_1和item_2之间),就像点击' +' item_2旁边显示了它下面的子框架。
有关如何让两个帧正常工作的任何帮助?我已经阅读了关于存储列表中创建的帧的信息,但是我无法让它为我工作。
非常感谢
答案 0 :(得分:1)
替换:
toggle_btn = ttk.Checkbutton(frame, width=2, text='+', command=lambda: self.toggle(var, toggle_btn, sub_frame),
variable=var, style='Toolbutton')
使用:
toggle_btn = ttk.Checkbutton(frame, width=2, text='+',
variable=var, style='Toolbutton')
toggle_btn['command'] = lambda a1=var, a2=toggle_btn, a3=sub_frame: self.toggle(a1, a2, a3)
有关why的更多信息,请参阅
上述问题的Minimal, Complete, and Verifiable example应最长:
from tkinter import *
class Example:
def __init__(self, root):
list_1 = ['item_1', 'item_2']
list_2 = ['a', 'b', 'c']
for item in list_1:
frame = Frame(root)
frame.pack()
var = IntVar()
sub_frame = Frame(root)
for item in list_2:
Label(sub_frame, text=item).pack()
toggle_btn = Checkbutton(frame,
command=lambda: self.toggle(var, toggle_btn, sub_frame),
variable=var)
toggle_btn.pack()
def toggle(self, show, toggle_button, sub_frame):
if bool(show.get()):
sub_frame.pack()
else:
sub_frame.forget()
def main():
root = Tk()
open = Example(root)
root.mainloop()
if __name__ == '__main__':
main()
如果您不想覆盖,只需使用可迭代类型(例如list
和dict
),而不是多个对象(如框架和标签)的奇异变量。下面的代码会生成一个frames
的字典,然后为每个labels
生成一个frames
的字典:
import tkinter as tk
if __name__ == '__main__':
list_1 = ['item_1', 'item_2']
list_2 = ['a', 'b', 'c']
root = tk.Tk()
frames = dict()
for item in list_1:
frames[item] = tk.Frame(root)
frames[item].pack()
frames[item].labels = dict()
for text in list_2:
frames[item].labels[text] = tk.Label(frames[item], text=text)
frames[item].labels[text].pack()
root.mainloop()
答案 1 :(得分:1)
问题的根源是lambda绑定到变量,但是您希望它在创建lambda时绑定到变量的值。使用lambda时,这是一个非常非常常见的错误。
此网站上有许多与此问题相关的问题和答案。以下是一些:
虽然可以通过修改lambda
的使用来修复您编写的代码,但我建议更好的解决方案是创建一个代表一个可折叠框架的新类。如果这个新类继承自Frame
,则可以像使用其他任何小部件一样使用它。
使用它可以消除使用lambda
的需要,并且可以更容易地将关于可折叠框架的所有内容封装在一个对象中。
例如:
class CustomFrame(tk.Frame):
def __init__(self, parent, label, items):
tk.Frame.__init__(self, parent)
header = tk.Frame(self)
self.sub_frame = tk.Frame(self, relief="sunken",
width=400, height=22, borderwidth=1)
header.pack(side="top", fill="x")
self.sub_frame.pack(side="top", fill="both", expand=True)
self.var = tk.IntVar(value=0)
self.label = tk.Label(header, text=label)
self.toggle_btn = ttk.Checkbutton(header, width=2, text="+",
variable=self.var, style='Toolbutton',
command=self.toggle)
self.entry = tk.Entry(header, width=11)
self.label.pack(side="left")
self.toggle_btn.pack(side="left")
self.entry.pack(side="right", pady=2, anchor="e")
self.sub_frame.pack(side="top", fill="both", expand=True)
for item in items:
tk.Label(self.sub_frame, text=item).pack(side="top")
# this sets the initial state
self.toggle(False)
def toggle(self, show=None):
show = self.var.get() if show is None else show
if show:
self.sub_frame.pack(side="top", fill="x", expand=True)
self.toggle_btn.configure(text='-')
else:
self.sub_frame.forget()
self.toggle_btn.configure(text='+')
这样,您的Example
课程变得微不足道,您可以根据需要创建任意数量的框架:
class Example:
def __init__(self, root):
list_1 = ['item_1', 'item_2']
list_2 = ['a', 'b', 'c']
for item in list_1:
frame = CustomFrame(root, item, list_2)
frame.pack(side="top", fill="x")
注意:上面的代码假定tkinter
和ttk
是这样导入的,与原始代码略有不同:
import tkinter as tk
from tkinter import ttk