'Accessing child widgets of a window by their names?

Is there any way in Python/tkinter to access child elements referring by their variable names, but from an other function?

For example in VBA, it is possible to directly refer to an element of an other window by its name. For example if I have two windows, UserForm1 and UserForm2 I can change the text value of Label1 of UserForm2 by clicking a button on UserForm1.

Private Sub CommandButton1_Click()
  UserForm2.Label1.Caption = "Changed"
End Sub

In tkinter I have found the winfo_children() to access child elements. Is there any way to access them by their names?

See my sample code below:

import tkinter
from tkinter import *

#----------------------------------------------------------------------
def new(parent_window):
    """"""
    parent_window.withdraw()
    global main_window
    global new_window
    new_window = tkinter.Tk()
    new_window.title("My App - New")

    label1 = tkinter.Label(new_window, text="NEW")
    label1.grid(row=0,column=0,columnspan=3,pady=10,padx=10, sticky="nsw")

    b1 = tkinter.Button(new_window, text="Change It", command=lambda: showdashboard(new_window))
    b1.grid(row=4,column=1,padx=20,pady=10,sticky="nwse")

    b2 = tkinter.Button(new_window, text="Quit", command=lambda: quit())
    b2.grid(row=5,column=1,padx=20,pady=10,sticky="nwse")

#----------------------------------------------------------------------
def dashboard(parent_window):
    """"""
    parent_window.withdraw()
    global main_window
    global dashboard_window
    dashboard_window = tkinter.Tk()
    dashboard_window.title("My App - Dashboard")

    label1 = tkinter.Label(dashboard_window, text="Dashboard")
    label1.grid(row=0,column=0,columnspan=3,pady=10,padx=10, sticky="nsw")

    b1 = tkinter.Button(dashboard_window, text="New", command=lambda: new(dashboard_window))
    b1.grid(row=4,column=1,padx=20,pady=10,sticky="nwse")

#----------------------------------------------------------------------
def showdashboard(parent_window):
    """"""
    parent_window.withdraw()
    dashboard_window.update()
    dashboard_window.deiconify()
    #This way it works <<<<<<<<<<<<<<------!!!!!!!
    byID=dashboard_window.winfo_children()
    byID[0].config(text="change the value")
    #But I am looking for somethin like this <<<<<<<<<<<<<<------????
    dashboard_window.label1.config(text="changed the value")

#----------------------------------------------------------------------
main_window=tkinter.Tk()
main_window.title("MyApp")

label = tkinter.Label(main_window, text="My App")
label.grid(row=0,column=0,pady=10,padx=10,sticky="nwse")

b1 = tkinter.Button(main_window, text="Dashboard", command=lambda:dashboard(main_window))
b1.grid(row=1,column=0,padx=20,pady=10,sticky="nwse")

main_window.mainloop()


Solution 1:[1]

winfo_children() returns an instance of the class associated with the type of widget along with the name that tkinter assigned to the actual tk object.

This means that yes, we can refer to the name of widget, although I'm not sure what advantage this would really give you other than not needing to assign the label to a variable.

from tkinter import *

root = Tk()

Label(root, text="Label1").pack()
label2 = Label(root, name="name", text="Label2")

label2.pack()

print(root.winfo_children())
print(root.nametowidget('.!label'))
print(str(label2))

Button(root, text="Delete label2", command=lambda: root.nametowidget(".name").destroy()).pack()

The above will result in two Label widgets and a Button widget appearing in the window. The first Label is not stored in a variable and yet we can quite happily call it inside the print statement. The second is stored in a variable but you can see that in the command of the Button we don't refer to the variable but the name attribute of the Label.

Bryan Oakley has a fantastic answer here explaining this.

Solution 2:[2]

I'm not sure what you mean by names in:

"Is there any way in Python/tkinter to access child elements referring by their names?"

You can access widgets simply by their object references:

# Procedural
import tkinter as tk


def change():
    object_reference['text'] = "Changed!"

root = tk.Tk()
object_reference = tk.Label(root, text="This is a label for root-window")
object_reference.pack()

another_window = tk.Toplevel(root)
btn_in_other_window = tk.Button(another_window, text="Change")
btn_in_other_window['command'] = change
btn_in_other_window.pack()

root.mainloop()

or if they were to be defined with more of an object-oriented approach, you can make use of the .(dot) notation:

#oop
import tkinter as tk


class App1(tk.Toplevel):
    def __init__(self, master):
        super().__init__()

        self.label = tk.Label(self, text="This is App1's label")
        self.label.pack()


class App2(tk.Toplevel):
    def __init__(self, master):
        super().__init__(master)

        self.button = tk.Button(self, text="This is a App2's button")
        self.button.pack()


def change(label):
    label['text'] = "Changed!"


if __name__ == '__main__':
    root = tk.Tk()
    root.withdraw()

    app1 = App1(root)
    app2 = App2(root)
    app2.button['command'] = lambda label=app1.label: change(label)

    root.mainloop()

Solution 3:[3]

As an alternative, it is possible to get a list of the children widgets of the element window with:

root.update()
lista = list(root.children.values()) 

Then it is possible to refer to the list elements and do whatever with the widget itself. For example to get the width of the first widget do print(lista[0].winfo_width()).

Warning: I am not sure that the list contains the elements in the same order that the widgets appear in the script, although for me it worked in this order. Hopping someone will write in the comments.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ethan Field
Solution 2 Nae
Solution 3