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()
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.
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()
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.
Related
I have a small GUI app in Python that uses Tk. It has several filters --- a text entries (they set filter values) with checkboxes (which set filter on/off). I create filters as a class inhetrited from ttk's Labelframe:
from tkinter.ttk import Labelframe
import tkinter as tk
class FilterWidget(Labelframe):
def __init__(self, parent, label):
Labelframe.__init__(self, parent, text=label)
self.grid()
self._entry = tk.Entry(self)
self._entry.grid(row=0, column=0)
self._checkbox = tk.Checkbutton(self, command=lambda: print(self))
self._checkbox.grid(row=0, column=1)
Then I create several instances of this class and place them in GUI:
root = tk.Tk()
source_filter = FilterWidget(root, "Source")
source_filter.grid(row=0, column=0)
level_filter = FilterWidget(root, "Severity")
level_filter.grid(row=0, column=1)
root.mainloop()
The widgets are created and correctly displayed. However, when one of the checkboxes is clicked and changes state, the other changes state as well!
When diffrent checkboxes are clicked, print command outputs .!filterwidget and .!filterwidget2, so those are separate objects. It seems that they are somehow implicitly syncronized, but I have no idea how did this happend.
So, the question is: how to remove this dependancy and make checkboxes independant of each other?
As the docs mention,
To use a Checkbutton, you must create a Tkinter variable. To inspect
the button state, query the variable.
Here's your code updated to use an IntVar to store the Checkbutton state.
from tkinter.ttk import Labelframe
import tkinter as tk
class FilterWidget(Labelframe):
def __init__(self, parent, label):
Labelframe.__init__(self, parent, text=label)
self._entry = tk.Entry(self)
self._entry.grid(row=0, column=0)
self._var = tk.IntVar()
callback = lambda: print(self._entry.get(), self._var.get())
checkbox = tk.Checkbutton(self, variable=self._var, command=callback)
checkbox.grid(row=0, column=1)
root = tk.Tk()
source_filter = FilterWidget(root, "Source")
source_filter.grid(row=0, column=0)
level_filter = FilterWidget(root, "Severity")
level_filter.grid(row=0, column=1)
root.mainloop()
I'm fairly new to python, and am struggling with a problem with tkinter. I am attempting to create an application which has a main menu, with buttons to open child windows which will contain distinct functions. I can create child windows, but I'm having trouble getting the values entered in Entry widgets in the child window. I wrote a really simplified version (python 3):
from tkinter import (Frame, Label, Entry, StringVar, Toplevel, Tk, Button)
class main(Frame):
def __init__(self):
Frame.__init__(self)
self.master.minsize(width=250, height=250)
self.master.title("main window")
self.grid()
newWindowButton = Button(self,
text="open new window",
command=self.newWindowOpen)
newWindowButton.grid(row=0, column=0)
def newWindowOpen(self):
childWindow = Toplevel()
childWindow.wm_title("child window")
childWindow.itemLabel = Label(childWindow, text="Test Value")
childWindow.itemVar = StringVar()
childWindow.itemEntry = Entry(childWindow,
textvariable=childWindow.itemVar)
childWindow.itemLabel.grid(row=0, column=0)
childWindow.itemEntry.grid(row=0, column=1)
childWindow.submitButton = Button(childWindow,
text="submit",
command=self.submitTest)
childWindow.submitButton.grid(row=1, column=0)
def submitTest(self):
value = self.itemVar.get()
print(value)
root = Tk()
main_menu = main()
main_menu.mainloop()
This won't work, as submitTest is trying to get the value of 'main.itemVar', which of course doesn't exist.
I'm guessing the method submitTest is outside the scope of the childWindow widget, but I'm not sure how to pass the itemVar down appropriately. What's the correct way to do this? Would I be better served creating the childWindow as it's own class, and creating new objects via the main menu?
For background, the child windows will be interacting with a database (sqlite), and all select / update functions will be self-contained in the child windows. The main menu's only function is to open the different parts of the application, it will never need to access the results of a child window directly.
As I understand it is possible in your application to create more that one childwindow. So you can't create one self.itemVar. You can create dict with instance of childwindow in key and instance of itemVar in value. And you can pass instance on childWindow in submitTest. And then you can access to corresponding itemVar
from tkinter import (Frame, Label, Entry, StringVar, Toplevel, Tk, Button)
class main(Frame):
def __init__(self):
Frame.__init__(self)
self.master.minsize(width=250, height=250)
self.master.title("main window")
self.grid()
self.children_dict = dict()
newWindowButton = Button(self,
text="open new window",
command=self.newWindowOpen)
newWindowButton.grid(row=0, column=0)
def newWindowOpen(self):
childWindow = Toplevel()
childWindow.wm_title("child window")
childWindow.itemLabel = Label(childWindow, text="Test Value")
childWindow.itemVar = StringVar()
childWindow.itemEntry = Entry(childWindow,
textvariable=childWindow.itemVar)
self.children_dict[childWindow] = childWindow.itemVar
childWindow.itemLabel.grid(row=0, column=0)
childWindow.itemEntry.grid(row=0, column=1)
childWindow.submitButton = Button(childWindow,
text="submit",
command=lambda: self.submitTest(childWindow))
childWindow.submitButton.grid(row=1, column=0)
def submitTest(self, childWindow):
value = self.children_dict[childWindow].get()
print(value)
root = Tk()
main_menu = main()
main_menu.mainloop()
I'm working on my very first Python GUI and I'm trying to modify this tkinter example, but I simply cannot figure out how to write a callback function for the OK button that will pass on the entered value to the main program.
#!/usr/bin/python
# -*- coding: utf-8 -*-
from Tkinter import Tk, BOTH, StringVar, IntVar
from ttk import Frame, Button, Style, Label, Entry
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Get Value")
self.style = Style()
self.style.theme_use("default")
self.pack(fill=BOTH, expand=1)
valueLabel = Label(self, text="Value: ")
valueLabel.place(x=10, y=10)
value=StringVar(None)
value.set("this is the default value")
valueEntry=Entry(self, textvariable=value)
valueEntry.place(x=70, y=10)
quitButton = Button(self, text="Quit", command=self.quit)
quitButton.place(x=10, y=50)
okButton = Button(self, text="OK", command=self.quit)
okButton.place(x=120, y=50)
def main():
root = Tk()
root.geometry("220x100+300+300")
app = Example(root)
root.mainloop()
if __name__ == '__main__':
main()
I've read a gazillion of tutorials, but none of them explains this clearly. Theoretically, I should be able to get the selected value with value.get(), but I keep getting error messages no matter where I put it. Also, AFAIK, I should be able to define a default value with value.set(), but this doesn't seem to have an effect, since the text box is empty when I run the program.
What is the easiest way to pass on values to the main python program after root.mainloop() terminates? (The actual dialog box contains several entry boxes for entering string and integer values.)
I.e. I want to be able to use something like:
root = Tk()
root.geometry("220x100+300+300")
app = Example(root)
root.mainloop()
print value
print value2
print value3
How do I define default values for entry boxes?
Change every occurrence of the value variable with self.value. This should fix it and the default value will be displayed.
UPDATE
from Tkinter import Tk, BOTH, StringVar, IntVar
from ttk import Frame, Button, Style, Label, Entry
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def showMe(self):
print(self.value.get())
def initUI(self):
self.parent.title("Get Value")
self.style = Style()
self.style.theme_use("default")
self.pack(fill=BOTH, expand=1)
valueLabel = Label(self, text="Value: ")
valueLabel.place(x=10, y=10)
self.value=StringVar(None)
self.value.set("this is the default value")
valueEntry=Entry(self, textvariable=self.value)
valueEntry.place(x=70, y=10)
quitButton = Button(self, text="Quit", command=self.quit)
quitButton.place(x=10, y=50)
okButton = Button(self, text="OK", command=self.showMe)
okButton.place(x=120, y=50)
def main():
root = Tk()
root.geometry("220x100+300+300")
app = Example(root)
root.mainloop()
if __name__ == '__main__':
main()
Both your quitButton and okButton call the self.quit functions. So no mater what value you enter when you press the OK button you are calling the quit function which has its own problems as well outside the scope of your question.
Try to define value as self.value and make the okButton call a function that does: print self.value.get().
I'm trying to make a Tkinter program that displays what you enter but instead I get a bunch of numbers in the IDLE shell.
My program:
from Tkinter import *
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.text_write = Entry(frame)
self.text_write.pack()
self.Show = Button(frame, text='Show', command=self.Show)
self.Show.pack(side=RIGHT)
def Show(self):
Label(self.text_write.get())
root = Tk()
app = App(root)
root.mainloop()
but instead of text being shown. Numbers appear in my IDLE shell such as:
.4334975024
.4334975600
.4334975672
.4334975816
.4334975960
.4334976104
.4334976248
.4334976392
.4334976536
would somebody please explain the problem and how I could fix it.
def Show(self):
Label(self.text_write.get())
If you replace "Label" with "print" it works (as in, prints the value to stdout)
What the code does as-is is create a new label with a string as a parent (when it wants a window as a parent), and then doesn't attach that label to anything.
You probably want to create the Label object and attach it to the window in init, and then update the label's value in Show()?
Try this:
from Tkinter import *
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.text_write = Entry(frame)
self.text_write.pack()
# rename the show button because you gave a member function the same name
self.show_button = Button(frame, text='Show', command=self.Show)
self.show_button.pack(side=RIGHT)
# create label to display entered text
self.display = Label(frame, text='')
self.display.pack(side=BOTTOM)
def Show(self):
# print text to the standard output
print self.text_write.get()
# show text on label
self.display['text'] = self.text_write.get()
root = Tk()
app = App(root)
root.mainloop()
Widget tk.Entry from example_script.py do not save value 'textvariable' field.
example_script.py:
import Tkinter as tk
class App(tk.Frame):
def __init__(self, master, text):
tk.Frame.__init__(self, master)
textVar = tk.StringVar()
textVar.set(text)
entryVar = tk.Entry(self, textvariable=textVar).pack()
self.pack()
def main():
root = tk.Tk()
text = ['text1', 'text2', 'text3']
for i in text:
App(root, i)
root.mainloop()
main_script.py:
import Tkinter import example_script as ex
if __name__ == '__main__':
root = Tkinter.Tk()
Tkinter.Button(root, text='press', command=lambda: ex.main()).pack()
root.mainloop()
If I change row 'entryVar = tk.Entry(self, textvariable=textVar).pack()' to
entryVar = tk.Entry(self)
entryVar.pack()
entryVar.insert(0, text)
field's value is updated. Why?
How will be correct open new window from imported script? Tkinter.Toplevel() is not suitable. Now I use subprocess.Popen.
When you do entryVar = tk.Entry(self).pack(), entryVar will be set to None because that is what pack() returns. When you call pack on a separate line, entryVar gets set to what you think it does.
You cannot create two instances of the Tk class in one program. Tkinter is not designed to work that way.