Problem with Tkinter using StringVar(), trace() and Entry() - python

I am defining a StringVar() and giving it a value, then using it as the textvariable of an Entry widget. I have also added a trace to the StringVar so that I can use it as a placeholder checker and make the placeholder a different colour to the typed text.
However, when I run the code the entry widget contains no text, but if I print the value of the StringVar() it gives the correct value. Also, the trace command is never called and when I print the value of the textvariable of the Entry widget, it comes out as PY_VAR#.
Does anyone know why this is not working and how to solve it?
circleVarEmailAddress = StringVar()
circleVarEmailAddress.set("email address")
circletextEmailAddress = Entry(forgottenWindow, textvariable = circleVarEmailAddress,
font = 10, relief = "raised", highlightbackground = "black",
borderwidth = 2, fg = "#808080")
circletextEmailAddress.place(x = 75, y = 100, width = 250, height = 50)
def placeholderEmailAddress(a, b, c):
print("Hey")
for i in range(1, 13):
if("email address"[:i] in circleVarEmailAddress.get() == True and
"email address"[i:] in circleVarEmailAddress.get() == True):
index1 = circleVarEmailAddress.get().index("email address"[:i])
index2 = circleVarEmailAddress.get().index("email address"[i:])
circleVarEmailAddress.set(circleVarEmailAddress.get()[:index1] +
circleVarEmailAddress.get()[index1 + i:index2] +
circleVarEmailAddress.get()[index2 + 13 - i:])
if(circleVarEmailAddress.get() != "email address" and
"email address" in circleVarEmailAddress.get() == True):
circleVarEmailAddress.set(circleVarEmailAddress.get().replace("email address", ""))
if circleVarEmailAddress.get() == "":
circleVarEmailAddress.set("email address")
if circleVarEmailAddress.get() == "email address":
circletextEmailAddress.config(fg = "#808080")
else:
circletextEmailAddress.config(fg = "#000000")
circleVarEmailAddress.trace_add("write", placeholderEmailAddress)

Your code runs fine for me if I add some boilerplate.
I assume from the variable name "forgottenWindow" that this is a new window in your program. The problem you are seeing can happen if you use Tk() to make additional windows. Always use Toplevel to make new windows beyond your first one.
forgottenWindow = Toplevel()

Related

Tkinter entry.get() does not update [ Gurobi Python ]

For simpler demonstration of my problem I'm using a very simple Linear Program.
I want to set the right hand side of one constraint to a certain value that I type into an entry box (Tkinter).
When I click on the button, the variables that are not equal to zero will be shown in the label.
The problem: the initial value of the entry box is 0. When I change it to e.g. 100 I still get the same results. The results don't change whatever I type into the entry box.
When I hardcode the right hand side value of the constraint to any other value, the results change but not if I change it in my entry box.
It seems like whatever I type into my entry box, Python always takes the initial value, which was 0. It doesn't update to what I've typed in
import Tkinter as tk
from gurobipy import *
WIDTH = 330
HEIGHT = 500
root = tk.Tk()
canvas = tk.Canvas(root, height=HEIGHT, width=WIDTH)
canvas.pack()
conslabel = tk.Label(root, text='Constraint value')
conslabel.place(relx=0.1, rely = 0.02)
var = tk.IntVar()
entry = tk.Entry(root, textvariable=var)
entry.place(relx=0.5, rely=0.02, relwidth=0.3)
button = tk.Button(root, text = "yield result", command = lambda: [setEntryValue(), build_model(entryValue), m.optimize(), results(), m.reset()])
button.place(relx = 0.5, rely = 0.675, anchor = tk.CENTER)
outputLabel = tk.Label(root, bg='white')
outputLabel.place(relx = 0.5, rely = 0.85, anchor = tk.CENTER, relheight = 0.2, relwidth = 0.8)
entryValue = 0
def build_model(entryValue):
m = Model("lp1")
m.setParam('OutputFlag', False)
x1 = m.addVar(name="x1")
x2 = m.addVar(name="x2")
m.update()
m.setObjective(5*x1 + 4*x2, GRB.MAXIMIZE)
m.addConstr(6*x1 + 4*x2 <= 24, "constr_1")
m.addConstr(1*x1 + 2*x2 <= 6, "constr_2")
m.addConstr(-1*x1 + 1*x2 <= 1, "constr_3")
m.addConstr(x2 <= entryValue, "constr_4")
def setEntryValue():
global entryValue
entryValue = var.get()
return entryValue
def labeltext():
finalvars = ''
for v in m.getVars():
if v.x != 0:
finalvars += ' ' + str(v.varName)
return finalvars
def results():
for v in m.getVars():
print('%s: %f' % (v.varName, v.x))
print('Obj: %f' % m.objVal)
outputLabel['text'] = labeltext()
root.mainloop()
You are getting the value of the Entry widget about a millisecond after you create it, way before the user has a chance to enter anything. entryValue doesn't get updated when the user enters data.
You need to wait to call entry.get() until the user has a chance to enter data. Typically that means to do it in a callback associated with a button or menu item.
You should create a function for your "yield result" button instead of using lambda. Then, in that function you can set up your data and compute your result.
It should look something like this:
def yield_result():
entryValue = int(entry.get())
m.setObjective(5*x1 + 4*x2, GRB.MAXIMIZE)
m.addConstr(6*x1 + 4*x2 <= 24, "constr_1")
m.addConstr(1*x1 + 2*x2 <= 6, "constr_2")
m.addConstr(-1*x1 + 1*x2 <= 1, "constr_3")
m.addConstr(x2 <= entryValue, "constr_4")
return [m.optimize(), results()]
...
button = tk.Button(root, text = "yield result", command = yield_result)
I think the problem is the fact that you need to assign a textvariable to your Entry:
entry_var = tk.StringVar()
entry = tk.Entry(root, textvar = entry_var)
when you want to get the text in the entry box, you can just use string = entry_var.get()

Changing the show of an entry box numerous times

I would like the contents of this my entry box to be uncovered from the * when the button is clicked, but then recovered when it is clicked again, and so on. Any ideas?
The code i have currently:
password_entry = Entry(root, width = 45, textvariable=user_password, show = "*")
def Show():
password_entry.config(show="")
show_password = Button(canvas, width = 31, height = 17, image = eye_img, bg = "gray33", relief = "flat", command = Show)
This only uncovers the password, and does not recover it when the button is reclicked.
Its seems that you are trying to make a toggle button. Edit your function. Get the value of current show. If it is * then change it to , if it is then change it to *. Your function can be:
def Show():
password_entry["show"] = "*" if password_entry["show"] == "" else ""
Here is a sample code that works like expected:
from tkinter import *
root = Tk()
password_entry = Entry(root, width = 45, show = "*")
password_entry.grid(row=0,column=0)
def Show():
password_entry["show"] = "*" if password_entry["show"] == "" else ""
show_password = Button(root,text="Show Password",bg = "gray33", relief = "flat", command = Show)
show_password.grid(row=0,column=1)
root.mainloop()

Tkinter update label with variable from inside function

I'm using Tkinter to create a window with an entry field and a button. When the button is pressed and a certain condition is not met, I need my_label2 to show a specific text, in this case 'Not Valid'. Otherwise, I need the my_label2 to be blank. I have the variable label_text inside a function that is called by the button press, but I get an error saying that label_text is not defined. Can someone help me out with this?
root = tk.Tk()
def my_function():
valid = #this variable is either true or false
if valid :
label_text = ''
else :
label_text = 'Not Valid'
my_label = tk.Label(root, text = "Enter text: ")
my_label.grid(row = 0, column = 0)
my_entry = tk.Entry(root)
my_entry.grid(row = 0, column = 1)
my_button = tk.Button(root, text = "Submit", command = my_function)
my_button.grid(row = 1, column = 1)
my_label2 = tk.Label(root, textvariable = label_text)
my_label2.grid(row = 2, column = 1)
root.mainloop()
Tkinter Variables are different from normal variables. To create one:
label_text = tk.StringVar()
Then, rather than assigning to the variable, you nee to use the set method:
label_text.set('')
or
label_text.set('Not Valid')
See: http://effbot.org/tkinterbook/variable.htm

tkinter: dynamically create and delete entry

I am using tkinter to create a small GUI for some Python scripts. For this GUI I need a Combobox named combo with three options:
"none" (the user wants to hand in no data)
"constant" (the user wants to hand in only one value)
"adjustable" (the user wants to hand in more than one value)
Depending on the choice done in combo, a different number of entrys should
appear. The first option should show no entry (and delete all "leftover"
entrys), the second one should show only one entry and the third one should show
two entrys. For me it's no problem to create these entrys but I don't know how
to make them disappear. Once they were created they stay until the GUI is
closed.
I tried something like this:
import tkinter as tk
master = tk.Tk()
var1 = tk.StringVar()
var2 = tk.StringVar()
def submit():
if choice.get() == "none": # all entry boxes schould disappear
entry1.destroy()
entry2.destroy()
if choice.get() == "constant": # only 1 entry box schould be visible
entry1 = tk.Entry(master, textvariable = var1)
entry1.grid(column = 0, row = 1)
entry2.destroy()
if choice.get() == "adjustable": # all entry boxes should be visible
entry1 = tk.Entry(master, textvariable = var1)
entry1.grid(column = 0, row = 1)
entry2 = tk.Entry(master, textvariable = var1)
entry2.grid(column = 0, row = 2)
choice = tk.StringVar(value = "none")
combo = ttk.Combobox(master, textvariable = choice, state = "readonly")
combo["values"] = ("none", "constant", "adjustable")
combo.grid(column = 0, row = 0)
action = tk.Button(master, text = "confirm", command = submit)
action.grid(column=1, row=0)
master.mainloop()
But as I said before, once a entry was created it did not disappear anymore. I
also tried entry.grid_forget() insted of entry.destroy() but this also
didn't work.
I also tryed to declare entry1 and entry1 outside of submit() but then I
don't know how to recreate these entrys once they were destroyed.
Thanks to the help of jasonharper I found a solution while working at another script. The code should look something like this:
import tkinter as tk
from tkinter import ttk
master = tk.Tk()
var1 = tk.StringVar()
var2 = tk.StringVar()
def submit():
if choice.get() == "none": # all entry boxes schould disappear
entry1.grid_remove()
entry2.grid_remove()
if choice.get() == "constant": # only 1 entry box schould be visible
entry1.grid(column = 0, row = 1)
entry2.grid_remove()
if choice.get() == "adjustable": # all entry boxes should be visible
entry1.grid(column = 0, row = 1)
entry2.grid(column = 0, row = 2)
choice = tk.StringVar(value = "none")
combo = ttk.Combobox(master, textvariable = choice, state = "readonly")
combo["values"] = ("none", "constant", "adjustable")
combo.grid(column = 0, row = 0)
entry1 = tk.Entry(master, textvariable = var1)
entry2 = tk.Entry(master, textvariable = var2)
action = tk.Button(master, text = "confirm", command = submit)
action.grid(column=1, row=0)
master.mainloop()
Now all entrys are deleted or created when wanted. To delete the text at the entrys You only have to add entry.delete(0,"end").
I hope this will also help others.

Python... Hide widget upon menu option selection - Tkinter

Working with Tkinter, I am trying to make a label disappear and another appear in its place when a specific option is selected with MenuOption(). Can I accomplish this without the need of a "refresh" button?
updated with a sample of my code:
mGui = Tk()
mGui.geometry('570x130+700+200')
mGui.resizable(width = FALSE, height = FALSE)
mGui.title('Title')
mylist = ['henry', 'tom', 'phil']
someValue = StringVar()
mLabel = Label(text = 'name: ').grid(row = 0, column = 0, sticky = E)
someMenu = OptionMenu(mGui, someValue, *mylist)
someMenu.grid(row = 0, column = 1, sticky = W)
someMenu.config(width = 14, anchor = W)
mGui.mainloop()
So, if someMenu.get() == 'tom' i want to hide mLabel...
so i've added the following:
def something():
print someValue.get()
mylist = ['henry', 'tom', 'phil']
someValue = StringVar()
someValue.trace('w', something)
and am getting TypeError: 'NoneType' object is not callable.. hmmmmm
You can put a trace on someValue, which can call a function whenever the value changes. In that function you can do anything you want, including removing widgets.
This website has an example: http://effbot.org/tkinterbook/variable.htm
if someMenu.get == "tom":
buttonName.pack()
else:
buttonName.pack_forget()

Categories