Reputation: 309
I am creating a pop-up message that disappears if the user clicks outside of the pop-up's frame. To check if the user clicks outside the frame, the code looks somewhat like this:
import tkinter as tk
def build_popup(self, root):
popup_frame = tk.Frame(root)
popup_frame.grid(row=0, column=0)
# binding to check if click is outside the frame
self.popup_frame_funcid = root.bind_all('<Button-1>', self.delete_popup)
my_canvas = tk.Canvas(popup_frame, width=200, height=200)
my_canvas.grid(row=0, column=0)
def delete_popup(self, root, event):
# if location clicked is not that of a child of the frame destroy popup
if root.winfo_containing(event.x_root, event.y_root) not in popup_frame.winfo_children():
popup_frame.destroy()
root.unbind('<Button-1>', self.popupframe_funcid)
I arrive at a problem however when a widget is added to my_canvas
, for instance an entry, and its parent is declared as my_canvas
. When I click the added widget, popup_frame.winfo_children()
(rather reasonably) does not identify the added widget as a child of popup_frame
and destroys the frame.
Is there a function within tkinter that I can use to check if a widget is a descendant of another widget or am I forced to manually keep track of each widget I add to popup_frame
?
If there is a simpler/alternative way to achieve the same result, I would also be very glad to hear it.
Upvotes: 0
Views: 1513
Reputation: 309
I messed around for a bit and found an alternative solution to the ones already mentioned.
str(my_widget)
returns the string path of my_widget
Hence, you can check if, for instance, my_canvas
is a descendant of popup_frame
by simply checking if my_canvas
's path starts with popup_frame
's path.
In python this is simply:
str(my_canvas).startswith(str(popup_frame))
Upvotes: 1
Reputation: 15226
I use winfo_children()
and winfo_parent()
to ID children and parent widgets/containers. Note that the single .
means root window.
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
frame1 = tk.Frame(self)
btn1 = tk.Button(self)
btn2 = tk.Button(self)
btn3 = tk.Button(frame1)
print('Root children widget are: {}'.format(self.winfo_children()))
print('frame1 children widget is: {}'.format(frame1.winfo_children()))
print('Button 1 parent is: {}'.format(btn1.winfo_parent()))
print('Button 2 parent is: {}'.format(btn2.winfo_parent()))
print('Button 3 parent is: {}'.format(btn3.winfo_parent()))
if __name__ == '__main__':
App().mainloop()
Results:
Upvotes: 0
Reputation: 385970
You can use winfo_parent
to get the parent of a widget. You can then call that on the parent, and the parent's parent, and so on, to get the ancestry of a widget. winfo_parent
returns a string rather than the parent object, but tkinter has a way to convert the name to the widget.
For example, to get the parent widget of a widget named w
, you could do this:
parent = w.nametowidget(w.winfo_parent())
With that, you can work your way up the hierarchy of widgets, stopping when you get to the root window.
Upvotes: 1