Tkinter entry.get() does not update [ Gurobi Python ] - 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()

Related

Why is this not working? tkinter label should change text on button click

Why is this not working? On button click it should add 1.
from tkinter import *
root = Tk()
m = 0
def clicka(m):
m = m + 1
lbl = Label(text=str(m)).place(relx=0.1, rely = 0.2)
exe = Button(text = '↵', bg = 'black', fg = 'red',command=clicka(m), relief='flat').place(relx=0.19, rely = 0.32)
lbl = Label(text=str(m)).place(relx=0.1, rely = 0.2)
root.mainloop()
There are issues in your code:
command=clicka(m) will execute clicka(m) immediately and assign the result of clicka(m) (which is None) to command option. So nothing will be done when the button is clicked later.
passing variable of primitive type will be passed by value, so even it is modified inside the function, the original variable will not be updated.
I would suggest to change m to IntVar and associate it to lbl via textvariable option, then updating m will update lbl at once:
import tkinter as tk
root = tk.Tk()
def clicka():
m.set(m.get()+1)
tk.Button(text='↵', bg='black', fg='red', command=clicka, relief='flat').place(relx=0.19, rely = 0.32)
m = tk.IntVar(value=0)
tk.Label(textvariable=m).place(relx=0.1, rely = 0.2)
root.mainloop()
You could try to set the variable m as global
from tkinter import *
root = Tk()
m = 0
def clicka():
global m
m = m + 1
lbl = Label(text=str(m)).place(relx=0.1, rely = 0.2)
print('Hi')
exe = Button(text = '↵', bg = 'black', fg = 'red',command=clicka, relief='flat').place(relx=0.19, rely = 0.32)
lbl = Label(text=str(m)).place(relx=0.1, rely = 0.2)
root.mainloop()
Indeed like #acw1668 mentioned, you can work with lambda or you can just use global.
This is much shorter.
from tkinter import *
root = Tk()
m = 0
def clicka():
global m
m += 1
lbl["text"] = str(m)
exe = Button(text='↵', bg='black', fg='red', command=clicka, relief='flat')
exe.place(relx=0.19, rely=0.32)
lbl = Label(text=str(m))
lbl.place(relx=0.1, rely=0.2)
root.mainloop()
You could try this :
from tkinter import *
win = Tk()
myvar = IntVar()
def Adder():
k = int(myvar.get())
k = k + 1
lb.insert(1 , k)
win.geometry("750x750")
l1 = Label(win , text = "Number")
l1.grid(row = 0 , column = 0)
e1 = Entry(win , textvariable = myvar)
e1.grid(row = 1 , column = 0)
b = Button(win , command = Adder)
b.grid(row = 2 , column = 0)
lb = Listbox(win , width = 20)
lb.grid(row = 3 , column = 0)
win.mainloop()
well this is the real code!!!
first of all , this question is just about add one number to 1 ;
so our function (you could use lambda function too) could place in the main statements but , how it's work ? in tkinter module , the variables must be define with the Reserved words like StringVar and IntVar. second if you wondering why i put
lb.insert(1,k)
in the function and how it works ? i have to say this "lb" variable, is a global variable in all part of the program, from the beginning to the end, and the Adder function can use it too , besides if you put (lb) variable beneath the
lb.grid(row =3 , column = 0)
lb.insert(1 , Adder())
Adder just calculate the amount of variable but insert method can't show the result (insert method doesn't have this).
I hope it was helpful.

tkinter GUI Text Box is not displaying all results similar to terminal output

I am trying to make a loan amortization table inside tkinter GUI using text box. However, all the loan amortization table is fully displayed in the terminal console as an output.
The tkinter text BOX output display's only the last line: Here is the full code:
import math
import tkinter as tk
from tkinter import *
def calcLoanPayment(p, r, n, t):
if p > 0 and r > 0 and n > 0 and t > 0:
clp = (p *(r/n)*(pow(1 + r/n, n * t)))/(pow(1 + r/n, n * t)-1)
clp = round(clp, 2)
return clp
else:
return -1
def runApp(): #This will bound the function runapp to the calcbtn.
print("running")
p = principalentry.get() #this will get value as a string
try:
p = float(p) #this will make cast it to float variable.
except:
p = -1
principalentry.delete(0, END) #this will clear the entry after calculation
r = interestrateentry.get()
try: #Try will help from crushing the program when wrong value entered
r = float(r)
except:
r = -1
interestrateentry.delete(0, END) #this will clear the entry after calculation
n = compoundintervalentry.get() #this will get value as a string
try:
n = int(n) #this will make cast it to float variable.
except:
n = -1
compoundintervalentry.delete(0, END) #this will clear the entry after calculation
t = durationentry.get()
try: #Try will help from crushing the program when wrong value entered
t = int(t)
except:
t = -1
durationentry.delete(0, END) #this will clear the entry after calculation
clp = calcLoanPayment(p,r,n,t)
print(clp)
paymentinterval = n*t
paymentinterval = int(paymentinterval)
startingBalance=p
endingBalance=p
for i in range(1, paymentinterval+1):
interestcharge =r/n*startingBalance
endingBalance=startingBalance+interestcharge-clp
result ="\t\t"+str(i)+"\t\t" +str(round (startingBalance, 1)
)+"\t\t"+str(round (interestcharge, 1))+"\t\t"+str(round (clp, 1)
)+"\t\t"+str(round (endingBalance, 1))
startingBalance = endingBalance
print(result)
finaloutput = result
output.config(state="normal")
output.delete(0.0, 'end')
output.insert(END, finaloutput)
output.config(state="disabled")
#Frontend maincode Startshere:
#sketch the design of the user interface first.
root = tk.Tk()
root.config(bg="#F8B135")
root.state("zoomed")
#There are 3 steps to build event programming
#Step 1: construct the widgets
#step 2: configure the widget to make it nicer
#srep 3: place or pack the widget in the root window
title = Label(root, text = "Loan Payment Calculator", font = 'bold')
title.config(fg='white', bg="#28348A")
title.pack(fill = BOTH)
principallabel = Label(root, text="Principal: ")
principallabel.config(anchor = W, font = 'bold', bg="#F8B135")
principallabel.place(x=50, y=30)
principalentry = Entry(root)
principalentry.config(bg="#ffffff")
principalentry.place(x=400, y=30)
interestratelabel = Label(root, text="Interest Rate: enter as Decimal (eg.2% as 0.02) ")
interestratelabel.config(anchor = W, font = 'bold', bg="#F8B135")
interestratelabel.place(x=50, y=70)
interestrateentry = Entry(root)
interestrateentry.config(bg="#ffffff")
interestrateentry.place(x=400, y=70)
compoundintervallabel = Label(root, text="Compound Interval: ")
compoundintervallabel.config(anchor = W, font = 'bold', bg="#F8B135")
compoundintervallabel.place(x=50, y=110)
compoundintervalentry = Entry(root)
compoundintervalentry.config(bg="#ffffff")
compoundintervalentry.place(x=400, y=110)
durationlabel = Label(root, text="Duration: ")
durationlabel.config(anchor = W, font = 'bold', bg="#F8B135")
durationlabel.place(x=50, y=150)
durationentry = Entry(root)
durationentry.config(bg="#ffffff")
durationentry.place(x=400, y= 150)
output = Text(root)
output.config(width= 150, height = 100, bg="white", state="disabled", borderwidth=2, relief= "groove", wrap='word')
output.place(x=50, y=230)
calcbtn = Button(root, text= "Calculate", font = 'bold', highlightbackground="#3E4149")
calcbtn.config(fg='#28348A',command=runApp)
calcbtn.place(x=50, y=190)
root. mainloop()
file.close()`
I couldn't figure out How to display the whole output like the terminal did on the tkinter textbox output function. your help is much appreciated.
There are 2 small changes to the code in the function runApp()
output.config(state="normal")
# output.delete(0.0, 'end') # This line deletes the last entry in the text box. Remove this
output.insert(END, finaloutput + '\n') # Add a newline here. Generates the desired 'tabular' output
output.config(state="disabled")

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

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()

Random tkinter window opening on if/else statement

I'm wondering if I got my if else statement wrong or if its a tkinter issue. I want it so that if a 0 is left in any or all boxes, it gives an error message. But after the error message is closed, it opens a random blank window. This is my code. The specific area is the if else statement within the function valueget()
import tkinter as tk
def mainwindow():
mainwindow = tk.Tk()
mainwindow.title('Enter values')
mainwindow.geometry('160x110')
mainwindow.config(bg='#aaf0d1')
tk.Label(mainwindow, text = 'Enter a', font = ('verdana'), bg='#aaf0d1').grid(row=0)
tk.Label(mainwindow, text = 'Enter b', font = ('verdana'), bg='#aaf0d1').grid(row=1)
tk.Label(mainwindow, text = 'Enter c', font = ('verdana'), bg='#aaf0d1').grid(row=2)
getA = tk.IntVar()
aBox = tk.Entry(mainwindow, textvariable = getA, width=3, bg='#aaf0d1')
aBox.grid(row=0, column=1)
aBox.config(highlightbackground='#aaf0d1')
getB = tk.IntVar()
bBox = tk.Entry(mainwindow, textvariable = getB, width=3, bg='#aaf0d1')
bBox.grid(row=1, column=1)
bBox.config(highlightbackground='#aaf0d1')
getC = tk.IntVar()
cBox = tk.Entry(mainwindow, textvariable = getC, width=3, bg='#aaf0d1')
cBox.grid(row=2, column=1)
cBox.config(highlightbackground='#aaf0d1')
button = tk.Button(mainwindow, text='Obtain roots', command = lambda: valueget(), font = ('verdana'), highlightbackground='#aaf0d1')
button.grid(row=4)
button.config(bg='#aaf0d1')
def valueget():
readA = getA.get()
readB = getB.get()
readC = getC.get()
intA = int(readA)
intB = int(readB)
intC = int(readC)
negroot = (readB**2)-(4*readA*readC)
quadformulaplus = (-readB + (pow(negroot,0.5)))/(2*readA) #quad forumla
quadformulaminus = (-readB - (pow(negroot,0.5)))/(2*readA) #quad forumla
messagewindow = tk.Tk()
messagewindow.geometry('290x50')
messagewindow.title('Roots of the equation')
messagewindow.config(bg='#aaf0d1')
if readA == 0 or readB==0 or readC==0 or (readA==0 and readB==0 and readC==0):
errorwindow = tk.messagebox.showerror(message='none').pack()
else:
label = tk.Label(messagewindow, text = f'The roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}', bg='#aaf0d1', font = ('verdana'))
label.grid(row=1)
closebutton = tk.Button(messagewindow, text='Close', command = lambda: messagewindow.destroy(), font = ('verdana'), highlightbackground='#aaf0d1')
closebutton.grid(row=2)
closebutton.config(bg='#aaf0d1')
messagewindow.mainloop()
# print(f'the roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}')
mainwindow.mainloop()
def startup():
startpage = tk.Tk()
startpage.title('Solver')
photo = tk.PhotoImage(file = r"/Users/isa/Desktop/DiffEqns/cover.png") #image load
coverbutton = tk.Button(startpage, image = photo, command = lambda: [startpage.destroy(), mainwindow()])
coverbutton.pack()
coverbutton.configure(highlightbackground='#aaf0d1')
startpage.mainloop()
startup()
Here's a basic idea of what I would do:
import tkinter as tk
from tkinter import messagebox
def mainwindow(root):
# Creates a toplevel window
mainwindow = tk.Toplevel()
mainwindow.protocol("WM_DELETE_WINDOW", root.destroy) # This overrides the "X" being clicked to also destroy the root window.
root.withdraw() # "Hides" the root window, leaving it (and mainloop) running in the background.
mainwindow.title('Enter values')
mainwindow.geometry('160x110')
mainwindow.config(bg='#aaf0d1')
# Since all three of the labels/entries are the same
# we can save space by generating them in a loop
entry_items = ('Enter a', 'Enter b', 'Enter c')
values = []
for x, item in enumerate(entry_items): # Using enumerate and x to assign rows
tk.Label(mainwindow, text = item,
font = ('verdana'), bg='#aaf0d1').grid(row=x) # Row assigned to x.
values.append(tk.StringVar()) # Appended StringVar to list.
tk.Entry(mainwindow,
textvariable = values[-1], # Uses the last value appended to the values list.
highlightbackground='#aaf0d1',
width=3,
bg='#aaf0d1').grid(row=x, column=1) # Row assigned to x.
tk.Button(mainwindow,
text='Obtain roots',
command = lambda vals = values: valueget(vals), # Here the button command is assigned with the values list
font = ('verdana'), bg='#aaf0d1',
highlightbackground='#aaf0d1').grid(row=3) # we know there are 3 items before this.
mainwindow.lift() # This is a method of bringing a window to the front
def valueget(vals):
# This line gets the values from the StringVars, converts them to ints,
# and returns them to their respective variables.
try:
readA, readB, readC = [int(val.get()) for val in vals]
except ValueError:
messagebox.showerror(title="Number Error", message='Values must be numbers')
return
# Here the variables are checked to see if they are 0
# Since each one is being checked if it is 0, there is no need to check if they are all 0.
for val in (readA, readB, readC):
if val == 0:
# If they are 0, shows an error message
messagebox.showerror(title="Zero Error", message='Values must not be zero')
return
# Creates a toplevel to display the results
messagewindow = tk.Toplevel()
messagewindow.title('Roots of the equation')
messagewindow.config(bg='#aaf0d1')
negroot = (readB**2)-(4*readA*readC)
quadformulaplus = (-readB + (pow(negroot,0.5)))/(2*readA) #quad forumla
quadformulaminus = (-readB - (pow(negroot,0.5)))/(2*readA) #quad forumla
tk.Label(messagewindow,
text = f'The roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}',
bg='#aaf0d1',
font = ('verdana')).pack(padx = 5, pady = 2)
tk.Button(messagewindow,
text='Close',
command = messagewindow.destroy, # There is no need for a lambda for this.
font = ('verdana'),
bg = '#aaf0d1',
highlightbackground='#aaf0d1').pack(padx = 5, pady = 2)
# print(f'the roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}')
messagewindow.lift() # This is a method of bringing a window to the front
def startup():
startpage = tk.Tk()
startpage.title('Solver')
# COMMENTED OUT FOR TESTING
#photo = tk.PhotoImage(file = r"/Users/isa/Desktop/DiffEqns/cover.png") #image load
coverbutton = tk.Button(startpage,
# COMMENTED OUT FOR TESTING
#image = photo,
text = "TESTING", # HERE FOR TESTING
highlightbackground='#aaf0d1',
command = lambda root = startpage: mainwindow(root)).pack() # Passes the startpage to the mainwindow function.
startpage.mainloop() # The only mainloop you need.
startup()
I would recommend to improve the readability of the if-else statement for a start.
coefficients = [readA, readB, readC]
if sum(coefficients): # If they all are all zeros this will be False
if min(coefficients): # If any one is zero, this will be False
label = tk.Label(messagewindow, text = f'The roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}', bg='#aaf0d1', font = ('verdana'))
label.grid(row=1)
closebutton = tk.Button(messagewindow, text='Close', command = lambda: messagewindow.destroy(), font = ('verdana'), highlightbackground='#aaf0d1')
closebutton.grid(row=2)
closebutton.config(bg='#aaf0d1')
else:
errorwindow = tk.messagebox.showerror(message='none').pack()
else:
errorwindow = tk.messagebox.showerror(message='none').pack()

Changing text of labels defined in a loop

I'm trying to make a scalable counter.
In the first window you enter how many counters you want.
In the second window there are labels and buttons used to add one to the label.
Here is my code:
from tkinter import *
root = Tk()
def newWindow():
window = Toplevel()
for i in range(int(textbox.get())):
exec("global label"+ str(i))
exec("label" + str(i) + " = Label(window, text = '0')")
exec("label" + str(i) + ".grid(row = 0, column = i)")
exec("global button"+ str(i))
exec("button" + str(i) + " = Button(window, text = 'Add', command = lambda: setText(label" + str(i) + "))")
exec("button" + str(i) + ".grid(row = 1, column = i)")
def setText(label):
label.config(text = str(int(label.cget("text")) + 1))
textbox = Entry(root)
textbox.grid(row = 0)
submitButton = Button(root, text = "Submit", command = newWindow)
submitButton.grid(row = 0, column = 1)
root.mainloop()
However this is the error I get:
name 'label_' is not defined
where _ is i.
Making them global didn't fix this either.
Help please!
If you're using exec in this way, you're doing something very wrong.
The simple solution is to add widgets to a list or dictionary. Though, in this specific case you don't need that because you're never referencing the label anywhere but in the button command.
Here's a working example:
from tkinter import *
root = Tk()
def newWindow():
global labels
window = Toplevel()
labels = {}
for i in range(int(textbox.get())):
label = Label(window, text='0')
button = Button(window, text='Add', command = lambda l=label: setText(l))
label.grid(row=0, column=i)
button.grid(row=1, column=i)
# this allows you to access any label later with something
# like labels[3].configure(...)
labels[i] = label
def setText(label):
label.config(text = str(int(label.cget("text")) + 1))
textbox = Entry(root)
textbox.grid(row = 0)
submitButton = Button(root, text = "Submit", command = newWindow)
submitButton.grid(row = 0, column = 1)
root.mainloop()
If you wanted to make use of labels, you could have your button pass in the index, and let setText get the widget from the dictionary:
def setText(i):
label = labels[i]
label.configure(...)
...
button = Button(..., command=lambda i=i: setText(i))

Categories