How to see if a widget exists in Tkinter? - python

Now, I know that you can check to see if a window exists by:
x.winfo_exists()
which returns a Boolean. More exactly, I need to check the existence of my buttons, labels, list boxes, sliders etc. Then what?

winfo_exists returns 1 unless you have destroyed the widget, in which case it returns 0. This method can be called on any widget class, not only the Tk root or Toplevels. Alternatively, you can get all the children of a widget with winfo_children:
>>> import Tkinter as tk
>>> root = tk.Tk()
>>> label = tk.Label(root, text="Hello, world")
>>> label.winfo_exists()
1
>>> root.winfo_children()
[<Tkinter.Label instance at 0x0000000002ADC1C8>]
>>> label.destroy()
>>> label.winfo_exists()
0
>>> root.winfo_children()
[]

You can also print the type i.e.. type(label). This can be helpful to provide not only existence, but also find if anything is coming up 'NoneType' without an error. The type() will tell you if you have an instance, or other type that can provide valuable clues as to how close the program is performing or returning items to what you think you are asking! The object.winfo_exists() and object.winfo_children is specific, and will throw an error if the object is not a type 'instance'.

Related

Tkinter StringVar to uppercase

I get the user entry as a StringVar from an Entry box but would like to convert it to uppercase.
I then use the variable in another routine. I have been unable to find the syntax to simply convert 'c6h12' to 'C6H12' when its in a StringVar.
Any help appreciated.
The following snippet does the trick, simply using the StringVar get() method to get the old value, and set() method to update the value after calling upper()
from tkinter import *
root = Tk()
sv = StringVar(value="c6h12")
sv.set(sv.get().upper())
root.mainloop()

What do exclamation marks mean in the representation of an object

I'm writing a GUI application with tkinter and I have different classes that represent different pages of my application. I'm trying to call a variable from one class to another, so I used the repr() function to gain some insight into how to call that object from one class to antother.
I called print(repr(listbox))
my listbox is within a class named SelectionPage and is defined as listbox = tk.Listbox(self)
the representation printed as <tkinter.Listbox object .!selectionpage.!listbox>
I'm new to python and I'm confused as to what these exclamation marks mean. Is this even a good way to debugg?? Sorry if this is a duplicate question, I'm really confused and I couldn't find the answer elsewhere
What do exclamation marks mean in the representation of an object
They don't mean anything. Tkinter developers simply chose to give all of their autogenerated internal widget names a leading exclamation point.
Tkinter is just a python wrapper around a tcl/tk interpreter. In tcl/tk, a widget is represented as a hierarchy of widgets. For example, the root widget is .. A frame in the root might be named .frame. A listbox in that frame might be named .frame.lb. Tcl is very broad in what it will accept - essentially, a widget name can be any character other than "." so using an exclamation point is legal, though uncommon in the tcl/tk world.
When you create a Tkinter widget by instantiating one of its classes, tkinter must create a valid widget name to pass to the tcl interpreter in order to create the actual widget. In older versions of Tkinter it used unique numbers (eg: '.4368693152.4368765368'). In python3, tkinter chose to use more human-friendly names prefixed with an exclamation point.
As far as I know, the exclamation point has no special meaning, though I suppose it might be useful in determining whether a widget name was created by tkinter or through some other means.
I'm trying to call a variable from one class to another, so I used the repr() function to gain some insight into how to call that object from one class to antother.
There are almost certainly better ways to do that than to use the internal name of the widget. However, if you know the internal name of the widget (eg: ".!selectionpage.!listbox") and insist on doing it this way, you can convert the string name to the tkinter widget instance with the universal widget method nametowidget.
import tkinter as tk
root = tk.Tk()
frame = tk.Frame(root)
listbox = tk.Listbox(frame)
listbox_name = str(listbox)
lb = root.nametowidget(listbox_name)
assert lb is listbox
By the way, you can give widgets a name if you don't like the autogenerated name. This works for all widgets except the root widget.
import tkinter as tk
root = tk.Tk()
frame = tk.Frame(root, name="my_frame")
listbox = tk.Listbox(frame, name="my_listbox")
assert str(listbox) == ".my_frame.my_listbox"

Refer to tkinter widget by its instance

I am learning Python w/ Tkinter and I recently learned the difference between the reference and the name/instance of a widget. The reference is the string you assign to a widget (which can be changed later on), but the name seems to be the actual identity of the widget (which is immutable). Essentially it seems as though the reference is a nickname of a widget because it can change overtime and be different depending on who you are talking to, while the actual name of the widget on the widget's drivers license is always the same. Specifically, in this line of code...
sample_frame = Frame(root, name = 'frame1', bg = 'blue', height = 50, width = '50')
"sample frame" is the reference, while 'frame1' is the name.
Unless I specifically assign the string 'frame1' as the name of this frame, python automatically generates a number sequence as its name. In order to view the name of any widget you just have to add...
print(str(sample_frame))
(the output in this example is .frame1)
So in Tkinter if I wanted to place this frame in my GUI i would have to pack/grid/place it in the following line like so...
sample_frame.pack()
But what I would like to do is call the pack method on this frame widget by its name rather than its reference. Something like this...
frame1.pack() #or
.frame1.pack() #because of that period
The problem is that Python claims frame1 was never defined, and .frame1 is invalid syntax. Does anybody know how to do something like this? Thanks.
For broader context I am doing this because I iterated the creation of 21 different frames and placed them in a 3x7 grid. Because of this all 21 frames have an identical reference. BUT, I made sure to make the name of each frame corresponds with its position.
The name= option sets the name of the widget within the Tcl environment that actually implements the GUI - it has no effect on the Python side. The only reason I can think of for doing this is that it might make Tcl error messages somewhat easier to read (the auto-generated widget name that you'd otherwise get is not particularly meaningful).
As always, the proper way to deal with multiple objects created in a loop is to store them in a container of some sort. In your case, it could be a 21 element list, a nested list (widget[row][column]), or perhaps a dict indexed by tuples (widget[row, column]).
While I fully agree with jasonharper's answer that you should keep a proper reference to the widgets and I do not recommend using what I'm about to explain, there actually is a way to achieve what you're asking. There's a widget method called nametowidget(), which returns the widget object when you give it a name. Note that you should call the method on the object (Tk, Toplevel, Frame) that contains the widget you're looking for.
So following your example, this works:
from tkinter import *
root = Tk()
sample_frame = Frame(root, name = 'frame1', bg = 'blue', height = 50, width = '50')
root.nametowidget('frame1').pack()
root.mainloop()
And if you would do the same with a button inside the frame you should do:
sample_button = Button(sample_frame, text='Button', name='button1')
sample_frame.nametowidget('button1').pack()

Pattern matching Tkinter child widgets (winfo_children) to determine type

I'm trying to automatically clear all Entry widgets in a parent widget.
import Tkinter
import re
root=Tkinter.Tk()
E1=Tkinter.Entry(root)
E1.pack()
E2=Tkinter.Entry(root)
E2.pack()
L1=Tkinter.Label(root,text='Label1')
L1.pack()
I'm running into 3 problems
While I can find out the children widget type, I can't seem to be able to use it in a pattern match. Printing out the wlist[0] below is different from the shell output?
Eg:
>> wlist=root.winfo_children()
>> wlist
[<Tkinter.Entry instance at 0x00000000151911C8>,
<Tkinter.Entry instance at 0x00000000151BAD88>,
<Tkinter.Label instance at 0x00000000151B29C8>]
>> wlist[0] # shell output
<Tkinter.Entry instance at 0x00000000151911C8>
>> print wlist[0] # print output here is different vs shell output above
.353964488L
I think due to the differences between the print output & shell output above, my pattern match can't work?
Eg
>> re.search(r'Entry',wlist[0])
<< No output >>
Assuming one is able to determine via pattern match that a child widget is indeed an Entry widget, how would you get the widget object itself to perform a delete method call?
Eg:
## Assuming I have a function to to clear the entry
## How would I pass the object from the pattern match in #2 to this function?
def clear_entry(objEntry):
objEntry.delete(0,Tkinter.END)
The items returned from winfo_children() is a list of widgets. Tkinter widgets have a method to tell you the underlying widget class: winfo_class.
>>> wlist[0].winfo_class()
'Entry'
You can also simply compare the object type, like you can with any other python object:
>>> isinstance(wlist[0], Tkinter.Entry)
True
Since the result of winfo_children is a list of widgets, you can iterate over them and clear out all of the entry widgets like this:
for widget in root.winfo_children():
if isinstance(widget, Tkinter.Entry):
widget.delete(0, "end")
Normally you store them in a list or some container.
entry_list=[E1, E2]
def clear_entry():
for id in entry_list:
id.delete(0,Tkinter.END)
You can check typu using
if type(wlist[0]) == Tkinter.Entry: # True/False
or better
if isinstance(wlist[0], Tkinter.Entry): # True/False
wlist[0] is object so you can do
wlist[0].delete(0,Tkinter.END)
and
clear_entry(wlist[0])
When you try to print object it use str() to convert object to string
print str(wlist[0])
.353964488L
shell use repr() to convert object to string
print repr(wlist[0])
<Tkinter.Entry instance at 0x00000000151911C8>

Python/ttk/tKinter - getting the value of a Checkbox

Following the TkDocs Tutorial (http://www.tkdocs.com/tutorial/widgets.html#checkbutton) I am trying to set up a check box, but I can't follow exactly what I should be doing to 'get' the toggled value.
self.valAStatus = StringVar()
self.checkA = ttk.Checkbutton(self.mainframe, text='A', command = lambda: self.getStatus(self.boxA, "A"),variable=self.valAStatus, onvalue='letter', offvalue='colour')
and
def getStatus(self, boxRef, value):
boxRef.insert(1, value)
What I'm not sure on is how to get the either onvalue or offvalue from the self.checkA object
I'm not sure if I am looking at the StringVar self.valAStatus
(that results in PY_VAR0 and has no attribute onvalue) or if I should be looking at the self.checkA object (that results in .40972728.40972656.40972800.41009024 and has no attribute onvalue).
I've probably missed something in the docs, but if anyone could point out what its doing, so I can get the (on|off)value I'd be obliged..
The answer is self.valAStatus.get() which returns the value associated to that check box (in this case, self.valAStatus).

Categories

Resources