Python Tkinter changing label text - python

I know that there are a lot of questions dealing with tkinter but I have looked at a bunch of them and none of them seem to help me.
import tkinter
class Calculator:
def __init__(self):
window = tkinter.Tk()
window.geometry("200x300")
window.title("Calculator")
lbl = tkinter.Label(window, text="placeholder", bg="blue", textvariable="labelText")
lbl.grid(row=0, column=0, columnspan=3)
self.firstNumArray = []
self.secondNumArray = []
self.operation = ""
self.currentNum = "first"
def appendNumber(self, number):
print("Appending Number")
if self.currentNum == "first":
self.firstNumArray.append(number)
print("".join(str(x) for x in self.firstNumArray))
lbl.config(text="".join(str(x) for x in self.firstNumArray))
window.update()
else:
self.secondNumArray.append(number)
for i in range(1,4):
string = "Creating button at ({0},{1})".format(0,i)
print(string)
button = tkinter.Button(text=i, command=lambda: appendNumber(self, i))
button.grid(row=1, column=i-1)
for i in range(1,4):
string = "Creating button at ({0},{1})".format(1,i)
print(string)
button = tkinter.Button(text=i+3, command=lambda: appendNumber(self, i+3))
button.grid(row=2, column=i-1)
for i in range(1,4):
string = "Creating button at ({0},{1})".format(2,i)
print(string)
button = tkinter.Button(text=i+6, command=lambda: appendNumber(self, i+6))
button.grid(row=3, column=i-1)
div = tkinter.Button(text="/")
mult = tkinter.Button(text="*")
add = tkinter.Button(text="+")
sub = tkinter.Button(text="-")
add.grid(row=1, column=3)
sub.grid(row=2, column=3)
mult.grid(row=3, column=3)
div.grid(row=4, column=3)
button = tkinter.Button(text="0")
button.grid(row=4, column=1)
window.mainloop()
calc = Calculator()
When I launch the program the window opens. When I click on a button the text in the label does not change. I have tried using a StringVar as the textvariable and then calling the set() function, but that did not work either. I think it has to do with the scope of the function. I had to place the appendNumber() function inside the __init__() because for some reason self.lbl = tkinter.Label() makes nothing pop up at all.

There are a few problems with your code.
labelText should, of course, be a StringVar and not a string...
labelText = tkinter.StringVar()
lbl = tkinter.Label(window, bg="blue", textvariable=labelText)
lbl.grid(row=0, column=0, columnspan=3)
Now you can use labelText.set to update the text. Also, no need for self parameter or window.update
def appendNumber(number):
if self.currentNum == "first":
self.firstNumArray.append(number)
labelText.set("".join(str(x) for x in self.firstNumArray))
else:
self.secondNumArray.append(number)
You can put all the buttons in one loop using // (integer (!) division) and % (modulo) operations. Also, be aware that the variable in the lambda is evaluated when the function is called, not when it is declared, i.e. all the lambdas will use the last value of i (9 in this case) -- see e.g. here. As a remedy, use lambda n=i+1: appendNumber(n).
for i in range(9):
btn = tkinter.Button(text=i+1, command=lambda n=i+1: appendNumber(n))
btn.grid(row=i//3+1, column=i%3)
Not really a problem, but since you don't need a reference to those buttons, you can make your code a bit more compact (same for the others):
tkinter.Button(text="/").grid(row=1, column=3)

Related

Retrieve input from Entry widget stored as an instance variable and use it outside of the class

I have difficulties trying to get the input from Entry widget stored as a instance variable, so I can use it as input outside this class:
class CreateGUI:
def __init__(self, master):
self.master = master
self.master.geometry("275x325")
self.master.columnconfigure(0, weight=1)
self.master.columnconfigure(1, weight=2)
self.checkbutton_var1 = IntVar()
self.checkbutton_var2 = IntVar()
self.path = ''
self.type = []
def add_labels(self):
Label(self.master, text="Provide path to file:").grid(column=0, row=0, padx=10, pady=10, sticky="N")
def add_entries(self):
user_input = Entry(self.master)
user_input.grid(column=0, row=1, padx=5, pady=5, ipadx=60)
return user_input
def add_buttons(self, user_input):
checkbutton1 = Checkbutton(self.master, text="test1", variable=self.checkbutton_var1, onvalue=1,offvalue=0,height=2,width=10)
checkbutton1.grid(column=1, row=0)
checkbutton2 = Checkbutton(self.master, text="test2", variable=self.checkbutton_var2, onvalue=1, offvalue=0,height=2, width=10)
checkbutton2.grid(column=1, row=1)
button = Button(self.master, text="push", bg="pink", bd=100, fg="white",
command=lambda: self.retrieve_input(user_input.get(), self.checkbutton_var1.get(), self.checkbutton_var2.get()))
button.grid(column=0, row=3, padx=20, pady=20, sticky="NEWS")
def retrieve_input(self, p, *args):
self.path = p
#print(self.path)
for el in args:
self.type.append(el)
#print(self.type)
def main():
tk = Tk()
app = CreateGUI(tk)
app.add_labels()
user_input = app.add_entries()
app.add_buttons(user_input)
print(app.type)
print(app.path)
tk.mainloop()
When I start the program, write the input and press the button, it does not print anything. There are empty brackets printed the moment the program is initiated. The prints inside the retrieve_input are printing exactly what I need, but I need this inputs outside of the class, because they will be an input to another class.
I tried everything related to this problem, but it is not working and I would really appriciate any kind of help. Thanks!
You are getting the input for the Entry widget right before anyone can have a chance to type in it. As a result, user_input.get() will return an empty string. One thing you can do is make some sort of trigger for calling add_buttons() that the user activates when they are done filling out user_input. Further tweaking after that should make it work.
Please tell me if you have any more trouble.

How to correct the output from an entry box

When using the feeder button, the script for F runs through entirely through to the print before the 'master' box appears, then does not react to the inputs from the 'master' box. This results in the output being 0.0 kW because the input is a long decimals followed by an L, when what I, the user inputs is 8777
I have been roaming the internet for about a day now with no luck finding anything. I am very new to TK but have been trying to learn it.
def F():
master = tk.Tk()
tk.Label(master, text = 'Feeder Number: ').grid(row=0)
entry1 = tk.Entry(master)
entry1.grid(row=0, column=1)
button2 = tk.Button(master,
text=' Confirm',
command=entry1.get())
button2.pack()
button2.grid(row=0, column=2)
fn = entry1.pack()
print fn
feed = filtered['Feeder']==fn
feedfn = filtered[feed]
Cap = feedfn['AC Name Plate Capacity <= 10kw']
Cap = Cap.astype(float)
AcPv = Cap.sum()
print 'The total PV on this feeder is:', AcPv, 'kW'
root = tk.Tk()
frame = tk.Frame(root)
frame.pack()
button = tk.Button(frame,
text='Exit',
fg='red',
command=quit)
button.pack()
button.grid(row=1, column=1)
Fee = tk.Button(frame,
text='Feeder',
command=F)
Fee.pack()
Fee.grid(row=0, column=1)
root.mainloop()
Expected 27.702
Output 0.0
Given that I will not be posting the csv,
entry1/fn should be 8777
currently 'none'
UPDATE
I am now receiving an output of PY_VAR when printing fn, I understand that the code is running all the way through before taking an input. Any recommendations for how to take the input before the filters are run?
def F():
master = tk.Tk()
tk.Label(master, text = 'Feeder Number: ').grid(row=0)
entry1 = tk.Entry(master)
entry1.grid(row=0, column=1)
button2 = tk.Button(master,
text=' Confirm',
command=entry1.get())
button2.grid(row=0, column=2)
fn = tk.IntVar()
print fn
feed = filtered['Feeder']==fn
feedfn = filtered[feed]
Cap = feedfn['AC Name Plate Capacity <= 10kw']
Cap = Cap.astype(float)
AcPv = Cap.sum()
print 'The total PV on this feeder is:', AcPv, 'kW'
For those interested in the final code (Which worked for me):
def F():
master = tk.Tk()
tk.Label(master, text = 'Feeder Number: ').grid(row=0)
entry = tk.Entry(master)
entry.grid(row=0, column=1)
def pint():
data = entry.get()
master.destroy()
feed = filtered['Feeder']==data
feedfn = filtered[feed]
Cap = feedfn['AC Name Plate Capacity <= 10kw']
Cap = Cap.astype(float)
AcPv = Cap.sum()
fdf = tk.Tk()
tk.Label(fdf, text = AcPv).grid(row=0)
button4 = tk.Button(fdf,
text = ' Exit',
fg='red',
command=fdf.destroy)
button4.grid(row=1)
button2 = tk.Button(master,
text=' Confirm',
command = pint)
button2.grid(row=0, column=2)
button3 = tk.Button(master,
text = ' Exit',
fg='red',
command=master.destroy)
button3.grid(row=0, column=3)
master.mainloop()
There a few mistake in your code that lead to the different output you have received.
First, why is your code executing without showing the master box :
Your tkinter need a mainloop() call if you want a persistent window.
master.mainloop()
You did that right with your root, but your master lacks that mainloop. This line is what basically keeping your GUI alive and looping over it for changes until it is destroyed one way or another. You need to add this line after creating your widgets in order to be able to interact with the window. Anything written after this line (but still indented in the definition) will be executed when your window is closed, either manually or with the command :
master.destroy()
Next, although this will yield a working window, you can still interact with your root window while the master window is up, which can lead to problems if you are expecting variable from master. I suggest you read about the Toplevel widget which is made specifically for cases like yours. (http://effbot.org/tkinterbook/toplevel.htm) Alternatively, you could also use tkinter's tkSimpleDialog.askinteger or askfloat functions which seems perfect for your application.
Finally, the entry allows you to write text but how to access the text? you can use Entry1.get() to extract the content of the entry, or as you have started to to in your update, you can assign a tkinter variable to the entry. This variable will be updated as you change write strings or numbers in the entry. To bind the variable to your entry, you must state it in the entry's creation :
fn = tk.StringVar(value = '000')
entry1 = tk.Entry(master, textvariable = fn)
*Note, this will require your fn variable to be initialized before the entry. Also, you can initialize a value for that variable upon creation
the tkinter variable is an object which is why when you print it, you get PY_VAR. (the type of object) To access the value, you need to use the get() method :
print(fn.get())

How do I use a Tkinter Button for two states?

I have the following code, which causes a color/text change when a Tkinter button is clicked. I would like to revert to the original color/text when the button is clicked a second time.
from Tkinter import *
window = Tk()
window.title("Start/Stop Button")
window.geometry('200x100')
def clicked_rf1():
btn_rf1.configure(text="Stop")
lbl_rf1.configure(text=" ON ", bg="green")
btn_rf1 = Button(window, text="Start", command=clicked_rf1)
btn_rf1.grid(column=1, row=1)
lbl_rf1 = Label(window, text=" OFF ", bg="red")
lbl_rf1.grid(column=2, row=1)
window.mainloop()
I want something that behaves a little more like a toggle, but I would like the look of a button.
Help gratefully received.
You will need an if block to choose what to do. You can make another flag variable to keep track of the state, or just use the current Label or Button text:
from Tkinter import *
window = Tk()
window.title("Start/Stop Button")
window.geometry('200x100')
def clicked_rf1():
if btn_rf1['text'] == "Start":
btn_rf1.configure(text="Stop")
lbl_rf1.configure(text=" ON ", bg="green")
else:
btn_rf1.configure(text="Start")
lbl_rf1.configure(text=" OFF ", bg="red")
btn_rf1 = Button(window, text="Start", command=clicked_rf1)
btn_rf1.grid(column=1, row=1)
lbl_rf1 = Label(window, text=" OFF ", bg="red")
lbl_rf1.grid(column=2, row=1)
window.mainloop()
This would be an ideal place to make a custom Button subclass, so you could have many of these in your program:
from Tkinter import *
window = Tk()
window.title("Start/Stop Button")
window.geometry('200x100')
class Christina(Frame):
def __init__(self, master=None, **kwargs):
Frame.__init__(self, master, **kwargs)
self.btn = Button(self, text="Start", command=self.clicked)
self.btn.grid(column=0, row=0)
self.lbl = Label(self, text=" OFF ", bg="red")
self.lbl.grid(column=1, row=0)
def clicked(self):
if self.btn['text'] == "Start":
self.btn.configure(text="Stop")
self.lbl.configure(text=" ON ", bg="green")
else:
self.btn.configure(text="Start")
self.lbl.configure(text=" OFF ", bg="red")
btn1 = Christina(window)
btn1.grid()
btn2 = Christina(window)
btn2.grid()
btn3 = Christina(window)
btn3.grid()
window.mainloop()
If you want a toggle, you can use the checkbutton without an indicator. It has options for the color in the selected and deselected state, and you can tie the value and the label together so that the label changes when you toggle the button.
Like any button, you can tie a command to it. The command can check the value of the variable to determine whether it should do the "on" function or the "off" function.
Here's a simple example:
import Tkinter as tk
def toggle():
if var.get() == "ON":
print("turning on...")
else:
print("turning off...")
root = tk.Tk()
var = tk.StringVar()
toggle = tk.Checkbutton(root, onvalue="ON", offvalue="OFF", width=4,
indicatoron=False,
variable=var, textvariable=var,
selectcolor="green", background="red",
command=toggle)
var.set("OFF")
toggle.pack()
root.mainloop()
Another approach might be to put the "pile of code" to run into different function, collect those in an iterator, and then get the next function from that iterator and execute it:
def bunchofcode():
print("foo")
def somethingelse():
print("bar")
whattodo = iter([bunchofcode, somethingelse])
def clicked_rf1():
try:
next(whattodo)()
except StopIteration:
print("nothing to do")
Or for cyclic behaviour:
from itertools import cycle
whattodo = cycle([bunchofcode, somethingelse])
For a two-state toggle button, you could also use a dict to map the current state to the next. You could also use the button's relief to mark the state.
def clicked_rf1():
transition = {"raised": "sunken", "sunken": "raised"}
btn_rf1["relief"] = transition[btn_rf1["relief"]]

Can't run functions in sequence. Python

So, I'm working on Tkinter and my goal is to when the user presses a button it opens a new window that he can insert data on database and then it populates a table again so it shows the new data inserted. The new window opens fine and data is indeed inserted, but the list is not updated and I don't know why.
Button code:
self.inserir = Button(self.container2, text="Inserir", command=lambda:self.help(tm.FazTela(bd),self.populate()))
Function code that gets the functions as a list and run them:
def help(*functions):
def func(*args, **kwargs):
return_value = None
for function in functions:
return_value = function(*args, **kwargs)
return return_value
return func
If I call the populate function before the function that generates the window it runs nicely but that's not what I want, I want to update after the user has input data.
I don't know if it helps, but here's the code of the window that opens once the button is pressed:
from Tkinter import *
from database import database as db
from database import tratamentos as tr
import tkMessageBox
class TelaMenor():
def __init__(self):
self.root = None
self.OPTIONS = []
self.cor1 = '#D32F2F'
def CloseWindow(self):
self.root.destroy()
self.root = None
def SendToTR(self,nome,valor,tipo,bd):
try:
tr.ProdutosRecieve(nome,valor,tipo,bd)
except:
tkMessageBox.showerror("Erro encontrado", "Digite valores validos!")
finally:
self.CloseWindow()
def FazTela(self,bd):
if(self.root!=None):
self.CloseWindow()
self.FazTela()
else:
self.root=Tk()
# opcoes do droplist
self.OPTIONS = [
"Tipo de produto",
"Doce",
"Salgado",
"Massa",
"Bebida",
"Outro"
]
#fim
# criacao e posicao dos widgets
info = Frame(self.root)
info.grid(sticky=N+S+W+E)
salto1 = Label(info, text=" ")
salto1.grid(row=0, column=0)
nome1 = Label(info, text="Nome:")
nome1['font']=['bold']
nome1.grid(row=1, column=1, sticky=W)
nome2 = Entry(info)
nome2["width"]=40
nome2.grid(row=2, column=1)
salto2 = Label(info, text="")
salto2.grid(row=3, column=0)
valor1 = Label(info, text="Valor:")
valor1['font']=['bold']
valor1.grid(row=4, column=1, sticky=W)
valor2 = Entry(info)
valor2["width"]=40
valor2.grid(row=5, column=1)
salto3 = Label(info, text="")
salto3.grid(row=6, column=0)
variable = StringVar(info)
variable.set(self.OPTIONS[0])
droplist = apply(OptionMenu, (info, variable) + tuple(self.OPTIONS))
droplist.grid(row=7, column=1)
salto4 = Label(info, text="")
salto4.grid(row=8, column=0)
pronto = Button(info, text="Pronto", bg=self.cor1, bd=3,command=lambda: self.SendToTR(nome2.get(),valor2.get(),variable.get(),bd))
pronto['font']=['bold']
pronto['fg']='white'
pronto.grid(row=9, column=1)
salto5 = Label(info, text="")
salto5.grid(row=10, column=1)
espaco1 = Label(info, text=" ")
espaco1.grid(row=10, column=2)
#fim
# barra de "status"
status = Label(info, text="Estado: Normal", bg="white", bd=1, relief=SUNKEN, anchor=W)
status.grid(row= 11, column=0, sticky=S+W+E, columnspan=3)
#fim
# formatacao da janela
self.root.title('Cadastro do Produto')
#root.iconbitmap(r'c:\Python27\DLLs\icon.ico')
self.root.resizable(width=False, height=False)
self.root.geometry('298x276')
self.root.protocol("WM_DELETE_WINDOW",lambda: self.CloseWindow())
self.root.mainloop()
#fim
Sorry, there are some words in portuguese.
This is a good illustration of why you shouldn't use lambda unless absolutely necessary: it makes debugging difficult. I recommend removing the use of lambda, and instead tie the button to a normal function. Doing so will make it easier to insert debugging code.
In this case, your function is running this code:
self.help(tm.FazTela(bd),self.populate())
This is the same as doing this:
a = tm.FazTela(bd)
b = self.populate()
self.help(a,b)
You also have the problem that you are creating more than one root window. In tkinter you must always have exactly one root window. Instead of creating a second instance of Tk, you need to create an instance of Toplevel.
If you want to execute code after the window has been destroyed you can use the function wait_window which will not return until the given window has closed.

Dynamically changing Label variable

What I am trying to do is build a window with a number(default value=1) and
3 buttons underneath it named: "UP","DOWN" and "QUIT". "Up" button is going to increment the number by 1 and the rest of the buttons are clear what they do.
from Tkinter import *
root=Tk()
number=1
Label(root,text=number,height=10,width=7).grid(row=0,pady=10,padx=10,column=10,columnspan=2,sticky=W+E)
def auksisi(number):
number+=1
return number
def meiosi(number):
number = number -1
return number
Label(root, text=auksisi(number),height=10,width=7).grid(row=0,pady=10,padx=10,column=10,columnspan=2,sticky=W+E)
Button(root, text="up",command=lambda: auksisi(number)).grid(row=3,column=2,columnspan=2)
Button(root, text="down",command=lambda: meiosi(number)).grid(row=3,column=3,columnspan=2)
Button(root, text="quit",command=root.destroy).grid(row=3,column=4,columnspan=2)
root.update()
root.mainloop()
What is happening is when I press the buttons nothing changes.Don't worry about the layout I will fix it, I just want the buttons to work.
The grid method returns None, and calling it directly after the creation of your objects, would make your eventual references to be None as well. To change the values of your label, you need a reference to it:
label_reference = Label(root, text=auksisi(number), height=10, width=7)
label_reference.grid(row=0, pady=10, padx=10, column=10, columnspan=2, sticky=W+E)
Now, through label_reference you can change the text using for example the config() method. You can do this in the method that is called when you click your buttons:
def auksisi(number):
number += 1
label_reference.config(text=number)
return number

Categories

Resources