在 Python 3 中,如何使框架(如数据输入表单或状态栏)始终出现在其他 tkinter 框架之上?
How to make a frame (like a data entry form or a status bar) always appear on top of other tkinter frames in Python 3?
所以,我有一个小型 Python 3 项目,它应该有一个 window 由 3 个主要区域组成:
- 工具栏(顶部);
- Table/treeview with database data (center) - 这个 table 应该可以向下增长,带有滚动条,但我仍然不知道如何使它们与 treeview 一起工作;
- 状态栏(底部)。
我也在尝试添加一个快速数据输入表单,该表单应按用户要求显示在状态栏的正上方。我想我已经能够使用 pack() 为此 window 创建主要结构。当用户单击适当的按钮时,数据输入表单也会显示并消失。但是有几件事我不知道如何解决。第一个是当用户调整 window 的大小时,使其变小,状态栏和数据输入表单都会在 table/treeview 下消失。我怎样才能让他们总是在最前面?
这是我的代码:
#!/usr/local/bin/python3
# encoding: utf-8
from tkinter import *
from tkinter import ttk
entryform_visible = 0
class mainWindow:
def __init__(self, master):
self.master = master
master.title("AppTittle")
master.minsize(width=700, height=200)
def show_entryform():
global entryform_visible
if entryform_visible == 1:
hide_entryform()
return
else:
entryform_visible = 1
# Form for data entry (bottom of main window)
status_txt.set("Introducing new data...")
self.bottomframe.pack(side=BOTTOM, fill=X)
def hide_entryform():
global entryform_visible
self.bottomframe.pack_forget()
entryform_visible = 0
status_txt.set("Introduction of data was cancelled.")
def add_register():
hide_entryform()
status_txt.set("New register added!")
# Tool bar (buttons)
self.topframe = ttk.Frame(root, padding="3 3 12 12")
self.topframe.pack(side=TOP, fill=X)
btn_add = ttk.Button(self.topframe, text="Button1").pack(side=LEFT)
btn_add = ttk.Button(self.topframe, text="+", width=2, command=show_entryform).pack(side=RIGHT)
btn_add = ttk.Button(self.topframe, text="Button2").pack(side=RIGHT)
# Data table (center area of window)
self.mainframe = ttk.Frame(root)
self.mainframe.pack(side=TOP, fill=X)
tree = ttk.Treeview(self.mainframe, height=25, selectmode='extended')
tree['columns'] = ('id', 'name', 'descr')
tree.pack(side=TOP, fill=BOTH)
tree.column('#0', anchor=W, minwidth=0, stretch=0, width=0)
tree.column('id', anchor=W, minwidth=30, stretch=0, width=40)
tree.column('name', minwidth=30, stretch=1, width=30)
tree.column('descr', minwidth=100, stretch=1, width=200)
tree.heading('id', text="ID")
tree.heading('name', text="Name")
tree.heading('descr', text="Description")
# Data entry form
self.bottomframe = ttk.Frame(root, padding="3 3 12 12")
ttk.Label(self.bottomframe, text="Field 1").pack(side=LEFT)
text_input_obj = ttk.Entry(self.bottomframe, width=13)
text_input_obj.pack(side=LEFT)
text_input_obj.focus_set()
btn_add = ttk.Button(self.bottomframe, text="Add", command=add_register).pack(side=RIGHT)
# We are going to pack() this later (when the user clicks the '+' button)
# Status bar
self.statusframe = ttk.Frame(root, padding="2 2 2 2")
self.statusframe.pack(side=BOTTOM, fill=X)
status_txt = StringVar()
self.statusbar = ttk.Label(self.statusframe, textvariable=status_txt).pack(side=LEFT)
root = Tk()
appwindow = mainWindow(root)
root.mainloop()
The first one is that when the user resizes the window, making it
smaller, the status bar and the data entry form both disappear under
the table/treeview. How can I make them be always on top?
这与加壳器的工作方式有关。当可用的 space 少于所需的 space 时,它将开始砍掉小部件。它以与小部件的打包方式相反的顺序执行此操作。
在这种情况下,因为底部框架是最后打包的,所以当您将 window 缩小到太小无法容纳所有内容时,首先打包会缩小任何 windows 可能已经扩展到大于它们的默认大小,然后开始从最后一个打包的小部件开始切掉小部件。在这种情况下,self.statusframe
被切碎,因为它是最后一个被打包的。
有两种方法可以解决这个问题。一种是简单地颠倒将所有内容打包到根 window 中的顺序。另一种是最初将树形部件制作得非常小,然后让它长满space。由于 treeview 的自然大小很小,tkinter 会在开始从视图中删除小部件之前缩小它。如果你使用第二种技术,你可以使用 wm_geometry
使 window 在开始时变大(否则树将没有任何 space 可以长成)
我的建议是始终将 pack
和 grid
语句分组。它使这样的更改变得非常容易,并且使您的代码可视化变得更加容易。例如,使用第一种技术,我会将主要区域的打包移动到函数的底部:
# layout the main GUI
self.topframe.pack(side=TOP, fill=X)
self.statusframe.pack(side=BOTTOM, fill=X)
self.mainframe.pack(side=TOP, fill=BOTH, expand=True)
有了这个,当 space 变得紧凑时,就会开始砍树。但是因为这棵树很大,所以在它开始消失之前有很多东西要砍。
当您这样做时,您将在布局中暴露出另一个问题,那就是您没有扩展树视图以在 mainframe
中填充额外的 space。你需要这样打包:
tree.pack(side=TOP, fill=BOTH, expand=True)
这带来了另一个问题。即,您的弹出窗口 bottomframe
可能会隐藏弹出。那是因为 pack 的设计不适用于这种情况。同样,那是因为 pack
将开始以与添加小部件相反的顺序切断小部件。由于您最后添加它,因此它是第一个被砍掉的小部件。如果用户将 window 的大小调整得更小,将其打包在底部可能会导致它永远不会出现。
在这种特定情况下更好的解决方案是不对这个小部件使用 pack。相反,由于您希望它成为 "pop up",您可以使用 place
。 place
非常适合在其他小部件之上弹出小部件。您唯一需要做的另一件事是确保此帧的堆叠顺序高于状态栏,您可以使用 lift
:
def show_entryform():
...
self.bottomframe.place(relx=.5, rely=1, anchor="s", relwidth=1.0)
self.bottomframe.lift()
当然,使用 place_forget
而不是 pack_forget
:
def hide_entryform():
...
self.bottomframe.place_forget()
...
所以,我有一个小型 Python 3 项目,它应该有一个 window 由 3 个主要区域组成:
- 工具栏(顶部);
- Table/treeview with database data (center) - 这个 table 应该可以向下增长,带有滚动条,但我仍然不知道如何使它们与 treeview 一起工作;
- 状态栏(底部)。
我也在尝试添加一个快速数据输入表单,该表单应按用户要求显示在状态栏的正上方。我想我已经能够使用 pack() 为此 window 创建主要结构。当用户单击适当的按钮时,数据输入表单也会显示并消失。但是有几件事我不知道如何解决。第一个是当用户调整 window 的大小时,使其变小,状态栏和数据输入表单都会在 table/treeview 下消失。我怎样才能让他们总是在最前面?
这是我的代码:
#!/usr/local/bin/python3
# encoding: utf-8
from tkinter import *
from tkinter import ttk
entryform_visible = 0
class mainWindow:
def __init__(self, master):
self.master = master
master.title("AppTittle")
master.minsize(width=700, height=200)
def show_entryform():
global entryform_visible
if entryform_visible == 1:
hide_entryform()
return
else:
entryform_visible = 1
# Form for data entry (bottom of main window)
status_txt.set("Introducing new data...")
self.bottomframe.pack(side=BOTTOM, fill=X)
def hide_entryform():
global entryform_visible
self.bottomframe.pack_forget()
entryform_visible = 0
status_txt.set("Introduction of data was cancelled.")
def add_register():
hide_entryform()
status_txt.set("New register added!")
# Tool bar (buttons)
self.topframe = ttk.Frame(root, padding="3 3 12 12")
self.topframe.pack(side=TOP, fill=X)
btn_add = ttk.Button(self.topframe, text="Button1").pack(side=LEFT)
btn_add = ttk.Button(self.topframe, text="+", width=2, command=show_entryform).pack(side=RIGHT)
btn_add = ttk.Button(self.topframe, text="Button2").pack(side=RIGHT)
# Data table (center area of window)
self.mainframe = ttk.Frame(root)
self.mainframe.pack(side=TOP, fill=X)
tree = ttk.Treeview(self.mainframe, height=25, selectmode='extended')
tree['columns'] = ('id', 'name', 'descr')
tree.pack(side=TOP, fill=BOTH)
tree.column('#0', anchor=W, minwidth=0, stretch=0, width=0)
tree.column('id', anchor=W, minwidth=30, stretch=0, width=40)
tree.column('name', minwidth=30, stretch=1, width=30)
tree.column('descr', minwidth=100, stretch=1, width=200)
tree.heading('id', text="ID")
tree.heading('name', text="Name")
tree.heading('descr', text="Description")
# Data entry form
self.bottomframe = ttk.Frame(root, padding="3 3 12 12")
ttk.Label(self.bottomframe, text="Field 1").pack(side=LEFT)
text_input_obj = ttk.Entry(self.bottomframe, width=13)
text_input_obj.pack(side=LEFT)
text_input_obj.focus_set()
btn_add = ttk.Button(self.bottomframe, text="Add", command=add_register).pack(side=RIGHT)
# We are going to pack() this later (when the user clicks the '+' button)
# Status bar
self.statusframe = ttk.Frame(root, padding="2 2 2 2")
self.statusframe.pack(side=BOTTOM, fill=X)
status_txt = StringVar()
self.statusbar = ttk.Label(self.statusframe, textvariable=status_txt).pack(side=LEFT)
root = Tk()
appwindow = mainWindow(root)
root.mainloop()
The first one is that when the user resizes the window, making it smaller, the status bar and the data entry form both disappear under the table/treeview. How can I make them be always on top?
这与加壳器的工作方式有关。当可用的 space 少于所需的 space 时,它将开始砍掉小部件。它以与小部件的打包方式相反的顺序执行此操作。
在这种情况下,因为底部框架是最后打包的,所以当您将 window 缩小到太小无法容纳所有内容时,首先打包会缩小任何 windows 可能已经扩展到大于它们的默认大小,然后开始从最后一个打包的小部件开始切掉小部件。在这种情况下,self.statusframe
被切碎,因为它是最后一个被打包的。
有两种方法可以解决这个问题。一种是简单地颠倒将所有内容打包到根 window 中的顺序。另一种是最初将树形部件制作得非常小,然后让它长满space。由于 treeview 的自然大小很小,tkinter 会在开始从视图中删除小部件之前缩小它。如果你使用第二种技术,你可以使用 wm_geometry
使 window 在开始时变大(否则树将没有任何 space 可以长成)
我的建议是始终将 pack
和 grid
语句分组。它使这样的更改变得非常容易,并且使您的代码可视化变得更加容易。例如,使用第一种技术,我会将主要区域的打包移动到函数的底部:
# layout the main GUI
self.topframe.pack(side=TOP, fill=X)
self.statusframe.pack(side=BOTTOM, fill=X)
self.mainframe.pack(side=TOP, fill=BOTH, expand=True)
有了这个,当 space 变得紧凑时,就会开始砍树。但是因为这棵树很大,所以在它开始消失之前有很多东西要砍。
当您这样做时,您将在布局中暴露出另一个问题,那就是您没有扩展树视图以在 mainframe
中填充额外的 space。你需要这样打包:
tree.pack(side=TOP, fill=BOTH, expand=True)
这带来了另一个问题。即,您的弹出窗口 bottomframe
可能会隐藏弹出。那是因为 pack 的设计不适用于这种情况。同样,那是因为 pack
将开始以与添加小部件相反的顺序切断小部件。由于您最后添加它,因此它是第一个被砍掉的小部件。如果用户将 window 的大小调整得更小,将其打包在底部可能会导致它永远不会出现。
在这种特定情况下更好的解决方案是不对这个小部件使用 pack。相反,由于您希望它成为 "pop up",您可以使用 place
。 place
非常适合在其他小部件之上弹出小部件。您唯一需要做的另一件事是确保此帧的堆叠顺序高于状态栏,您可以使用 lift
:
def show_entryform():
...
self.bottomframe.place(relx=.5, rely=1, anchor="s", relwidth=1.0)
self.bottomframe.lift()
当然,使用 place_forget
而不是 pack_forget
:
def hide_entryform():
...
self.bottomframe.place_forget()
...