问题:我想创建一个列表框,它会在label1下面出现并消失,但是当点击label1时它不应该影响label2的位置。怎么办呢?
后台:我已经编写了一个测试代码(见下文)来显示我的问题。在第一次单击鼠标时,会出现列表框但向下推动label2。在第二次鼠标单击时,列表框隐藏但label2保留在新位置并且不返回到原始位置。我尝试过使用.grid_forget()但行为相同。重复鼠标单击只显示并隐藏列表框。
我认为我无法获得所需的窗口小部件行为,因为列表框与其他窗口小部件位于同一平面/窗口上。是这样,我认为顶级窗口是允许平面外窗口小部件存在的另一个窗口小部件?如果是这样,我该怎么做?如何将它放在label1下面?此外,顶级窗口有一个窗口标题栏。我不想要那个。如何删除?得到我想要的东西,我的思维过程是否正确?
#!/usr/bin/python3
# -*- coding: utf-8 -*-
try:
import tkinter as tk # Python 3 tkinter modules
except ImportError:
import Tkinter as tk # Python 2 tkinter modules
class App(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.grid(row=0, column=0, sticky='nsew')
self.toggle = True
self.label1 = tk.Label(self, text = 'Click me to see Names of Famous Folks')
self.lbframe = tk.Frame(self)
self.label2 = tk.Label(self, text = 'The World of Famous Folks! Welcome!')
self.label1.grid(row=0, column=0, sticky='nsew', pady=10)
self.lbframe.grid(row=1, column=0, sticky='nsew')
self.label2.grid(row=2, column=0, sticky='nsew')
nlist1 = ['Peter', 'Scotty', 'Walter', 'Scott', 'Mary']
self.lbframe.list_values = tk.StringVar()
self.lbframe.list_values.set(nlist1)
self.lbframe.list= tk.Listbox(self.lbframe, height=5, width= 10,
listvariable=self.lbframe.list_values)
self.lbframe.list.grid(row=0, column=0, sticky='nsew')
self.lbframe.list.grid_remove()
self.label1.bind("<Button-1>", self.ShowHideListbox)
def ShowHideListbox(self, event):
if self.toggle: # Show
self.toggle=False
self.lbframe.list.grid()
self.lbframe.list.lift(aboveThis=None)
else: # Hide
self.toggle=True
self.lbframe.list.grid_remove()
if __name__ == '__main__':
root = tk.Tk()
app = App(root)
app.mainloop()
答案 0 :(得分:2)
如果要定位窗口小部件以使其不影响任何其他窗口小部件,则应使用place
。关于place
的一个好处是,它允许您相对于其他小部件放置小部件,并相对于其他小部件设置宽度和高度。
例如,要将列表框窗口(包括其包含框架)放置在触发它的窗口小部件的正下方,您可以使用place
,如下所示:
self.lbframe.place(in_=event.widget, x=0, rely=1, relwidth=1.0, anchor="nw")
relwidth
选项表示我们希望包含列表框的框架与条目窗口小部件一样宽。这是可选的,但它说明了place
的控件类型。 in_
参数指定宽度相对于哪个小部件。
由于place
不会影响同一父级的其他子级,因此必须确保父窗口足够大以显示列表。由于list是嵌入式窗口小部件,因此它不会浮动在窗口上方。在您的情况下,由于您的App
实例未填满整个窗口,因此不会发生这种情况。它之所以没有,是因为你忽略了在根窗口中给任何行或列任何权重。
grid
非常擅长它的功能,但有时它却是错误的工具。例如,由于App
是根窗口中唯一的窗口小部件,因此使用pack
意味着您可以使用单个命令来填充窗口。使用grid
至少需要三个(其中两个你忘了打电话)。因此,我建议您使用pack
将App
置于root
内。这不是让place
工作所必需的,它只是让您的代码更容易理解。
我还建议不让App
负责将自己置于其父级。你可以,而且它不会影响place
的工作方式,但它会让你的代码更容易理解,因为你没有一个函数(App.__init__
)负责布局主要窗户及其子女。
考虑到所有这些,以下是基于代码的工作解决方案。注意:如何将值分配给列表框也存在一个错误,但修复它超出了此问题的范围。
#!/usr/bin/python3
# -*- coding: utf-8 -*-
try:
import tkinter as tk # Python 3 tkinter modules
except ImportError:
import Tkinter as tk # Python 2 tkinter modules
class App(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.toggle = True
self.label1 = tk.Label(self, text = 'Click me to see Names of Famous Folks')
self.label2 = tk.Label(self, text = 'The World of Famous Folks! Welcome!')
self.label1.grid(row=0, column=0, sticky='nsew', pady=10)
self.label2.grid(row=2, column=0, sticky='nsew')
nlist1 = ['Peter', 'Scotty', 'Walter', 'Scott', 'Mary']
self.lbframe = tk.Frame(self)
self.lbframe.list_values = tk.StringVar()
self.lbframe.list_values.set(nlist1)
self.lbframe.list= tk.Listbox(self.lbframe, height=5, width= 10,
listvariable=self.lbframe.list_values)
self.lbframe.list.pack(fill="both", expand=True)
self.label1.bind("<Button-1>", self.ShowHideListbox)
def ShowHideListbox(self, event):
if self.toggle: # Show
self.toggle=False
self.lbframe.place(in_=event.widget, x=0, rely=1, relwidth=1.0, anchor="nw")
else: # Hide
self.toggle=True
self.lbframe.place_forget()
if __name__ == '__main__':
root = tk.Tk()
root.geometry("400x200")
app = App(root)
app.pack(fill="both", expand=True)
app.mainloop()