Winfo_y 不 return 正确的值
Winfo_y does not return correct value
我正在尝试在 tkinter 中创建自定义条目小部件。当此条目具有焦点时,它应该在条目小部件的正下方显示一个列表框,其中包含一些选项供用户使用。当它失去焦点时,列表框应该自行删除。
这是我的:
class TestEntry(tk.Entry):
def __init__(self, top):
self.window = top
tk.Entry.__init__(self, self.window)
self.listshown = False
self.bind('<FocusIn>', self.showlist)
self.bind('<FocusOut>', self.hidelist)
def showlist(self, event):
if not self.listshown:
x = self.winfo_x()
y = self.winfo_y()
h = self.winfo_height()
w = self.winfo_width()
# Print bottom left x and y of the entry
print(x, y, h)
self.listbox = tk.Listbox(self.master, highlightbackground='blue', highlightthickness=1)
self.listshown = True
self.listbox.place(x=x, y=y + h, width=w,anchor='nw')
self.listbox.update()
# Print top left x,y of listbox
x = self.listbox.winfo_x()
y = self.listbox.winfo_y()
print(x, y)
def hidelist(self, event):
if self.listshown:
self.listbox.destroy()
self.listshown = False
if __name__ == "__main__":
root = tk.Tk()
root.geometry('500x500')
frame = tk.LabelFrame(root, text='Frame')
frame.pack(padx=30, pady=30, fill='both', expand=True)
TestEntry(frame).pack(side='left', padx=20, pady=20)
tk.Entry(frame).pack(side='left', padx=20, pady=20)
这是我看到的输出:
22 218 19
24 254
如下图所示,列表框没有出现在输入框的左下角,而是出现在更下方。为什么会这样?
编辑:
只有当我将我的小部件放在标签框架中时才会发生此问题,如上面的代码所示。当我将它放在常规框架中时,不会发生这种情况。
所以问题出在标签框架上。我现在修改了我的代码以检查主窗口小部件是否是标签框架。如果它是标签框,那么我从 y 坐标中减去标签框字体的高度。
我使用这个问题的答案来计算字体的高度:()
我也检查它是否是框架,然后减去边框宽度。
更新代码:
class TestEntry(tk.Entry):
def __init__(self, top):
self.window = top
tk.Entry.__init__(self, self.window)
self.listshown = False
self.bind('<FocusIn>', self.showlist)
self.bind('<FocusOut>', self.hidelist)
def showlist(self, event):
if not self.listshown:
x = self.winfo_x()
y = self.winfo_y()
h = self.winfo_height()
w = self.winfo_width()
y = y + h
# If the master widget is a label frame then substract the height of the frame font from y coordinate
if 'labelframe' in self.winfo_parent():
print('master is a label frame')
height_font = tkf.Font(font=self.master['font']).metrics('linespace')
y = y - height_font
# If the master widget is any kind of frame then substract the border width of the frame from x & y
if 'frame' in self.winfo_parent():
print('master is a frame')
width_border = self.master['borderwidth']
x = x - int(width_border)
y = y - int(width_border)
self.listbox = tk.Listbox(self.master, highlightbackground='blue', highlightthickness=1)
self.listshown = True
self.listbox.place(x=x, y=y, width=w, anchor='nw')
self.listbox.update()
def hidelist(self, event):
if self.listshown:
self.listbox.destroy()
self.listshown = False
if __name__ == "__main__":
root = tk.Tk()
root.geometry('800x800')
frame = tk.Frame(root, relief='groove', borderwidth=10)
frame.pack(padx=30, pady=30, fill='both', expand=True)
TestEntry(frame).pack(side='top', padx=40)
label_frame = tk.LabelFrame(root, text="Label Frame", borderwidth='10', font=('Google Sans', 40, 'bold underline'))
label_frame.pack(padx=30, pady=30, fill='both', expand=True)
TestEntry(label_frame).pack(side='top', padx=40)
root.mainloop()
您可以通过使用相对位置来避免所有数学运算。当您使用place
时,您可以指定列表框是相对于条目小部件。
例如:
self.listbox.place(in_=self, relx=0, rely=1.0, bordermode="outside", relwidth=1.0, anchor='nw')
in_
指定相对于哪个小部件
relx
是表示相对x位置的浮点值。 0 == 左边缘,1.0 == 右边缘
rely
是表示相对 y 位置的浮点值。值为 1 表示在小部件的底部
bordermode
表示相对于外边框对待坐标相对于内边框
relwidth
表示让widget和入口widget一样宽。
我正在尝试在 tkinter 中创建自定义条目小部件。当此条目具有焦点时,它应该在条目小部件的正下方显示一个列表框,其中包含一些选项供用户使用。当它失去焦点时,列表框应该自行删除。
这是我的:
class TestEntry(tk.Entry):
def __init__(self, top):
self.window = top
tk.Entry.__init__(self, self.window)
self.listshown = False
self.bind('<FocusIn>', self.showlist)
self.bind('<FocusOut>', self.hidelist)
def showlist(self, event):
if not self.listshown:
x = self.winfo_x()
y = self.winfo_y()
h = self.winfo_height()
w = self.winfo_width()
# Print bottom left x and y of the entry
print(x, y, h)
self.listbox = tk.Listbox(self.master, highlightbackground='blue', highlightthickness=1)
self.listshown = True
self.listbox.place(x=x, y=y + h, width=w,anchor='nw')
self.listbox.update()
# Print top left x,y of listbox
x = self.listbox.winfo_x()
y = self.listbox.winfo_y()
print(x, y)
def hidelist(self, event):
if self.listshown:
self.listbox.destroy()
self.listshown = False
if __name__ == "__main__":
root = tk.Tk()
root.geometry('500x500')
frame = tk.LabelFrame(root, text='Frame')
frame.pack(padx=30, pady=30, fill='both', expand=True)
TestEntry(frame).pack(side='left', padx=20, pady=20)
tk.Entry(frame).pack(side='left', padx=20, pady=20)
这是我看到的输出:
22 218 19
24 254
如下图所示,列表框没有出现在输入框的左下角,而是出现在更下方。为什么会这样?
编辑: 只有当我将我的小部件放在标签框架中时才会发生此问题,如上面的代码所示。当我将它放在常规框架中时,不会发生这种情况。
所以问题出在标签框架上。我现在修改了我的代码以检查主窗口小部件是否是标签框架。如果它是标签框,那么我从 y 坐标中减去标签框字体的高度。
我使用这个问题的答案来计算字体的高度:(
我也检查它是否是框架,然后减去边框宽度。
更新代码:
class TestEntry(tk.Entry):
def __init__(self, top):
self.window = top
tk.Entry.__init__(self, self.window)
self.listshown = False
self.bind('<FocusIn>', self.showlist)
self.bind('<FocusOut>', self.hidelist)
def showlist(self, event):
if not self.listshown:
x = self.winfo_x()
y = self.winfo_y()
h = self.winfo_height()
w = self.winfo_width()
y = y + h
# If the master widget is a label frame then substract the height of the frame font from y coordinate
if 'labelframe' in self.winfo_parent():
print('master is a label frame')
height_font = tkf.Font(font=self.master['font']).metrics('linespace')
y = y - height_font
# If the master widget is any kind of frame then substract the border width of the frame from x & y
if 'frame' in self.winfo_parent():
print('master is a frame')
width_border = self.master['borderwidth']
x = x - int(width_border)
y = y - int(width_border)
self.listbox = tk.Listbox(self.master, highlightbackground='blue', highlightthickness=1)
self.listshown = True
self.listbox.place(x=x, y=y, width=w, anchor='nw')
self.listbox.update()
def hidelist(self, event):
if self.listshown:
self.listbox.destroy()
self.listshown = False
if __name__ == "__main__":
root = tk.Tk()
root.geometry('800x800')
frame = tk.Frame(root, relief='groove', borderwidth=10)
frame.pack(padx=30, pady=30, fill='both', expand=True)
TestEntry(frame).pack(side='top', padx=40)
label_frame = tk.LabelFrame(root, text="Label Frame", borderwidth='10', font=('Google Sans', 40, 'bold underline'))
label_frame.pack(padx=30, pady=30, fill='both', expand=True)
TestEntry(label_frame).pack(side='top', padx=40)
root.mainloop()
您可以通过使用相对位置来避免所有数学运算。当您使用place
时,您可以指定列表框是相对于条目小部件。
例如:
self.listbox.place(in_=self, relx=0, rely=1.0, bordermode="outside", relwidth=1.0, anchor='nw')
in_
指定相对于哪个小部件relx
是表示相对x位置的浮点值。 0 == 左边缘,1.0 == 右边缘rely
是表示相对 y 位置的浮点值。值为 1 表示在小部件的底部bordermode
表示相对于外边框对待坐标相对于内边框relwidth
表示让widget和入口widget一样宽。