Winfo_y does not return correct value
You can avoid all of the math by using relative placement. When you use place
, you can specify that the listbox is relative to the entry widget.
For example:
self.listbox.place(in_=self, relx=0, rely=1.0, bordermode="outside", relwidth=1.0, anchor='nw')
in_
specifies which widget to be relative torelx
is a floating point value representing the relative x position. 0 == left edge, 1.0 == right edgerely
is a floating point value represeenting the relative y position. A value of 1 means at the bottom of the widgetbordermode
means to treat the coordinates relative to the outside border than inside the borderrelwidth
means to make the widget as wide as the entry widget.
So the issue was with the label frame. I have now modified my code to check if the master widget is a label frame. If it is a label frame then I subtract the height of the font of the label frame from the y coordinate.
I calculate the height of the font using the answer to this question: (Measure the height of a string in Tkinter Python?)
I also check if it is a frame and then subtract the border width.
Updated code:
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 = Falseif __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()