StringVar.get() returns a blank value - python-3.x

I've asked this before - but I think I phrased it incorrectly.
What I essentially want is to have a user fill out a tkinter entry box, and have the value saved - somewhere.
I the want to click on a button, and have the user's entered text to manipulate.
I want essentially what is below - although that doesn't work at all - as I can't enter a StringVar there, apparently. I'm really at my wits end here :(
How can I rewrite this, so it will work?
class Driver():
firstname = StringVar()
def __init__(self):
root = Tk()
root.wm_title("Driver")
firstname_label = ttk.Label(root, text="First Name *").grid(row=0, column=0)
firstname_field = ttk.Entry(root, textvariable=self.firstname).grid(row=0, column=1)
ttk.Button(root, text="Send", command=self.submit).grid()
root.mainloop()
def submit(self):
print(self.firstname.get())
I've having lots, and lots of trouble with this. It seems to printing blank values and references to the variable - rather than the value inside it

You cannot use StringVar in this way -- you can't create a StringVar until after you've created the root window. Because you are creating the root window inside the constructor the code will throw an error.
The solution is to move the creation of the StringVar inside your constructor:
class Driver():
def __init__(self):
root = Tk()
root.wm_title("Driver")
self.firstname = StringVar()
firstname_label = ttk.Label(root, text="First Name *").grid(row=0, column=0)
Note that the way you've written the code, firstname_label and firstname_field will always be None, because that is what grid returns. It's always best to separate widget creation from layout.
Also, you don't really need the StringVar under most circumstances (assuming you correctly store a reference to the widget). Just omit it, and when you want the value of the entry widget you can just get it straight from the entry widget:
...
self.firstname_field = Entry(...)
...
print(self.firstname_field.get())
The use of a StringVar is only necessary if you want to share the value between widgets, or you want to put a trace on the variable.

Related

Gather input from multiple tkinter checkboxes created by a for loop

I made an application with tkinter which creates a list of checkboxes for some data. The checkboxes are created dynamically depending on the size of the dataset. I want to know of a way to get the input of each specific checkbox.
Here is my code, which you should be able to run.
from tkinter import *
root = Tk()
height = 21
width = 5
for i in range(1, height):
placeholder_check_gen = Checkbutton(root)
placeholder_check_gen.grid(row=i, column=3, sticky="nsew", pady=1, padx=1)
for i in range(1, height):
placeholder_scope = Checkbutton(root)
placeholder_scope.grid(row=i, column=4, sticky="nsew", pady=1, padx=1)
root.mainloop()
I looked over other answers and some people got away by defining a variable inside the checkbox settings "variable=x" and then calling that variable with a "show():" function that would have "variable.get()" inside. If anyone could please point me in the right direction or how I could proceed here. Thank you and much appreciated.
Normally you need to create an instance of IntVar or StringVar for each checkbutton. You can store those in a list or dictionary and then retrieve the values in the usual way. If you don't create these variables, they will be automatically created for you. In that case you need to save a reference to each checkbutton.
Here's one way to save a reference:
self.general_checkbuttons = {}
for i in range(1, self.height):
cb = Checkbutton(self.new_window)
cb.grid(row=i, column=3, sticky="nsew", pady=1, padx=1)
self.general_checkbuttons[i] = cb
Then, you can iterate over the same range to get the values out. We do that by first asking the widget for the name of its associated variable, and then using tkinter's getvar method to get the value of that variable.
for i in range(1, self.height):
cb = self.general_checkbuttons[i]
varname = cb.cget("variable")
value = self.root.getvar(varname)
print(f"{i}: {value}")

How do I return the input of a tkinter entry box generated by a loop to a list for later use

sorry for what I assume is a noob question. this seems like it should be so simple. I am trying to create a list from an entry box generated by a loop.
I have two lists, one list "myLabelList" that has info such as "job Name, Project name" etc. and one empty list "myEntryLists" to capture the info from the entry.
The problem is when i print(myEntryList) it seems to display info about the entry rather than the input itself. I have a workaround but that's exactly what it is.
sorry if i have formatted this badly, its my first post.
from tkinter import *
root = Tk()
root.title("Job Information Entry")
root.geometry("400x150")
topFrame = Frame(root)
bottomFrame = Frame(root)
topFrame.grid(row=0, column=0)
bottomFrame.grid(row=1, column=0)
myLabelList = ["Enter Job Number", "Enter Project Name", "Enter Job Name", "Enter Drawing Number"]
myEntryList = []
lst = []
# this is where i seem to be having problems
def ok_button_click():
for entry in myEntryList: # this is my workaround
lst.append(entry.get()) # this is my workaround
print(myEntryList) # this is what im getting
print(lst) # this is what i want to print()
x = 0
for i in myLabelList:
myLabel = Label(topFrame, text=i)
myEntry = Entry(topFrame, width=50)
myLabel.grid(row=x, sticky=E)
myEntry.grid(row=x, column=1)
x = x + 1
myEntryList.append(myEntry)
# bottomFrame
okButton = Button(bottomFrame, text="OK", command=ok_button_click)
cancelButton = Button(bottomFrame, text="Cancel")
okButton.grid(row=0, column=0)
cancelButton.grid(row=0, column=1)
root.mainloop()
In this line you insert an Entry widget to myEntryList:
myEntryList.append(myEntry)
When you're trying to print it, it gives you a printable representation of the the Entry widgets, saved in the list.
If you want to get the input itself, just print the lst list.
EDIT:
Entry is an object. In OOP (Object-Oriented Programming), you define classes, which represent an object. Each object has properties and methods. You can read more about OOP here .
Entry widget is an example of an object. It has constructor that creates an instance of that class (Entry()), propeties like 'bg', 'fg' and methods like get().
When you insert to myEntryList an Entry widget, you insert the whole object to the list, and not the input it holds. In order to get the input, you need to use the get() method on the Entry widget.
I hope everything is clear now :)

Naming a widget to auto-destroy-replace it

I'm making a picture gallery that exists independently in several places within the app. When any of the galleries close, the current picture displayed in it is posted to a central graphics edit area. So the pictures were gridding on top of each other and I was planning to add code to delete the one that was already there when I stumbled on some unexpected behavior that seems to make this unnecessary. When I used the name option to give the container a name, instead of gridding a new container and picture on top of the existing one, apparently the old one was destroyed and replaced automatically.
My question is, can I rely on this behavior, or is it some kind of fluke? I can't find any documentation on it. From Fredrick Lundh, one of Tkinter's authors, "If you really need to specify the name of a widget, you can use the name option when you create the widget. One (and most likely the only) reason for this is if you need to interface with code written in Tcl." So I'm worried that I'll be relying on a fluke but so far it's a good fluke because I don't have to figure out how to keep widgets from gridding on top of each other if tkinter is going to auto-destroy the old one for me. It's not that hard but this name option thing is easier.
The example shows this behavior in action. If you remove the name option from the functions that create the widgets, the widgets grid on top of each other. But with the name option used, the widgets replace each other.
import tkinter as tk
def create():
f1 = tk.Frame(root, bg='red', name='exists') #
f1.grid(column=0, row=0)
l1 = tk.Label(f1, bg='yellow', text='This is a very long sentence.')
l1.grid(column=0, row=0)
count_labels()
def replace():
f1 = tk.Frame(root, bg='green', name='exists') #
f1.grid(column=0, row=0)
l1 = tk.Label(f1, bg='tan', text='short sentence')
l1.grid(column=0, row=0)
count_labels()
def count_labels():
root.update_idletasks()
for child in root.winfo_children():
x = child
print(len(x.winfo_children()))
root = tk.Tk()
b1 = tk.Button(root, text='create', command=create)
b1.grid(column=0, row=1)
b2 = tk.Button(root, text='replace', command=replace)
b2.grid(column=1, row=1)
root.mainloop()

Reassigning the 'command' part of a range of tkinter buttons created using a class

Halfway through my program i want to change a range of button instances' "when clicked" commands. The buttons were created using the following class:
class GameBoardButtons:
def __init__(self, name, buttonText, buttonRow, buttonColumn, buttonState, frame, colour):
self.name = name
self.button = Button(frame, text=buttonText, fg=colour,
height=2, width=5, state=buttonState,
command=lambda: shipListAppendFunc(self))
self.button.grid(row=buttonRow, column=buttonColumn)
def GridPosAttackable(self):
self.button.config(command=lambda: whenAttacked(self.name))
And the function that i am calling to adjust the buttons is as follows:
def reconfigureButtons(grid):
children = grid.children
for c in children:
widget = children[c]
if isinstance(widget, (Button)):
widget.configure(command=lambda: whenAttacked(c.name))
Should i be instead calling the function within the class ("GridPosAttackable")? And if so, how would i do that? The current way i have it, all the buttons call the same "self.name", not their unique name.
I want to make it so when "whenAttacked(whatever)" is called, the field/'whatever' part it takes is the buttons unique ID/'self' label.
Many thanks to all forms of help!

what is the difference between a variable and StringVar() of tkinter

Code:
import tkinter as tk
a = "hi"
print(a)
a1 = tk.StringVar()
a1.set("Hi")
print(a1)
Output:
hi ##(Output from first print function)
AttributeError: 'NoneType' object has no attribute '_root' (Output from second print function)
My question:
What is the difference between a and a1 in above code and their use-cases. Why a1 is giving error?
A StringVar() is used to edit a widget's text
For example:
import tkinter as tk
root = tk.Tk()
my_string_var = tk.StringVar()
my_string_var.set('First Time')
tk.Label(root, textvariable=my_string_var).grid()
root.mainloop()
Will have an output with a label saying First Time
NOTE:textvariable has to be used when using string variables
And this code:
import tkinter as tk
def change():
my_string_var.set('Second Time')
root = tk.Tk()
my_string_var = tk.StringVar()
my_string_var.set('First Time')
tk.Label(root, textvariable=my_string_var).grid()
tk.Button(root, text='Change', command=change).grid(row=1)
root.mainloop()
Produces a label saying First Time and a button to very easily change it to Second Time.
A normal variable can't do this, only tkinter's StringVar()
Hopes this answers your questions!
StringVar() is a class from tkinter. It's used so that you can easily monitor changes to tkinter variables if they occur through the example code provided:
def callback(*args):
print "variable changed!"
var = StringVar()
var.trace("w", callback)
var.set("hello")
This code will check if var has been over-written (this mode is defined by the w in var.trace("w", callback).
A string such as "hello" is just a data type, it can be manipulated and read and all sorts, the primary difference is that if the string was assigned to a variable, such as a = 'hello', there is no way of telling if a has changed (i.e if now a = 'hello') unless you do a comparison somewhere which could be messy.
Put it simply: StringVar() allows you to easily track tkinter variables and see if they have been read, overwritten, or if they even exist which you can't easily do with just a typical a = 'hello'
Helpful : http://effbot.org/tkinterbook/variable.htm
Edit : Replaced 'variables' with 'tkinter variables' where appropriate as per #Bryan Oakley's suggestion
Tkinter is a wrapper around an embedded tcl interpreter. StringVar is a class that provides helper functions for directly creating and accessing such variables in that interpreter. As such, it requires that the interpreter exists before you can create an instance. This interpreter is created when you create an instance of Tk. If you try to create an instance of StringVar before you initialize tkinter, you will get the error that is shown in the question.
Once tkinter has been properly initialized and a StringVar instance has been created, it can be treated like any other python object. It has methods to get and set the value that it represents.
At the beginning add
root = tk.Tk()
These Variables are designed for tkinter. and these do not work independently.
Suppose if you are building a GUI calculator, you want to display the values the user inputs in the screen of the calculator. If the user is trying to add 5 + 5, we have to show, "5" "+" "5" in the display. And when the equals button is pressed, we want to display "10". That is the use of StringVar(). It holds the string equivalent of the value the interpreter holds.

Resources