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一样宽。