Python tkinter create multiple variables - python-3.x

I started to code with tkinter and class methods. I try to code a to-do-list where I can create multiple entries by pressing a button below the entry. And a button next to the entry should change the color of the entry if pressed. The problem now is, that when I create multiple entries, the buttons only change the latest entry. So my question is how do I specify the entry when created?
Sry for obvious mistakes, Im new to coding :p
import tkinter as tk
from tkinter.constants import ANCHOR, CENTER, X
from tkinter import messagebox
class App():
def __init__(self):
self.window = tk.Tk()
self.window.title("To-Do-List")
self.window.geometry("700x700")
self.x_but, self.y_but = 0.05, 0.2
self.x_ent, self.y_ent = 0.05, 0.2
self.x_but2 = 0.3
self.check_var = True
self.start_frame()
self.grid1()
self.window.mainloop()
def start_frame(self):
self.label1 = tk.Label(text="To Do List", font=("", 30))
self.label1.place(relx=0.5, rely=0.05, anchor=CENTER)
def grid1(self):
self.button1 = tk.Button(text="Create", command= self.create_field)
self.button1.place(relx = self.x_but, rely= self.y_but)
def create_field(self):
self.y_but += 0.05
self.button1.place(relx= self.x_but, rely= self.y_but)
self.entry1 = tk.Entry()
self.entry1.place(relx= self.x_ent, rely= self.y_ent)
self.button_check = tk.Button(text="✅", height= 1,command=self.check)
self.button_check.place(relx= self.x_but2, rely=self.y_ent)
self.y_ent += 0.05
def check(self):
if self.check_var:
self.entry1.configure(bg="Green")
self.check_var = False
else:
self.entry1.configure(bg="White")
self.check_var = True
app = App()

You are changing the bg of self.entry1 which keeps overwriting itself each time an entry is created, so when button is clicked, it is always the last entry. The easiest solution is to define a parameter for check and then pass the required entry as an argument to it.
So the method would be:
def check(self,ent):
if self.check_var:
ent.configure(bg="Green")
self.check_var = False
else:
ent.configure(bg="White")
self.check_var = True
...and your button would be:
self.button_check = tk.Button(text="✅", height= 1,command=lambda x=self.entry1: self.check(x))
Also I hope you have a good reason to use place because using grid can make your life much easier, in this case.
For more explanation, read: Tkinter assign button command in loop with lambda

Related

Update variable and calculate inverse

I havea an app where I have two tkEntry widgets. I am trying to enter data into the first and have it populate it's inverse into the second. However sometimes it is easier to input the data into the second and have the first calculated.
For example:
If EntryOne=20 then EntryTwo=0.05.
But if EntryTwo=0.2 then EntryOne=5.
I am have two callbacks that I am using to calc the inverses, but it seems only the first one fires.
Can I have only one callback?
import tkinter as tk
root= tk.Tk()
root.title('XXX')
X_var = tk.DoubleVar()
Y_var = tk.DoubleVar()
def cleardata(Box):
Box.Delete(0,10)
def callback_X():
if not Y_var.get()==0:
X_var.set(1/Y_var.get())
X_Ent['validate']='focusout'
return True
def callback_Y():
if not X_var.get()==0:
Y_var.set(1/X_var.get())
Y_Ent['validate']='focusout'
return True
w=200
h=300
root.geometry(str(w)+"x"+str(h))
canvas = tk.Canvas(root, width = w, height = h)
canvas.pack(fill="both", expand=True)
X_lbl=tk.Label(root, text='X').place(x=68.5-25,y=(h/3))
X_Ent = tk.Entry(root, justify="center", textvariable=X_var, width=10, validate="focusout", validatecommand=None)
X_Ent['validatecommand']=callback_Y
X_Ent.place(x=68.5,y=(h/3))
Y_lbl=tk.Label(root, text='Y').place(x=68.5-25,y=(2*h/3))
Y_Ent = tk.Entry(root, justify="center", textvariable=Y_var, width=10, validate="focusout", validatecommand=None)
Y_Ent['validatecommand']=callback_X
Y_Ent.place(x=68.5,y=(2*h/3))
#def main():
root.mainloop()
Mon
Ok, first of all the code you posted here doesn't work for many reasons, but after fixing this errors I found that the reason your callback functions are not properly working is that the method sent to validatecommand must return True or False. I've done this and worked for me:
import tkinter as tk
root=tk.Tk()
X_var=tk.DoubleVar(value=1)
Y_var=tk.DoubleVar(value=1)
def callback_X():
if not Y_var.get()==0:
X_var.set(1/Y_var.get())
X_entry['validate']='focusout'
return True
def callback_Y():
if not X_var==0:
Y_var.set(1/X_var.get())
Y_entry['validate']='focusout'
return True
X_entry=tk.Entry(root, textvariable=X_var, validate='focusout',validatecommand=None)
Y_entry=tk.Entry(root, textvariable=Y_var, validate='focusout',validatecommand=None)
X_entry['validatecommand']=callback_Y
Y_entry['validatecommand']=callback_X
X_entry.pack()
Y_entry.pack()
root.mainloop()
I think your problem might be that in the first function you defined the "D" in "Def" was capitalized when it shouldn't be.

How to make a variable equal to the input received from the input field in tkinter?

I was wondering how to make this code work.I always get 12 in the console.
from tkinter import *
s = 12
root = Tk()
root.geometry("200x200")
root.title("Program")
e = Entry(root)
e.pack()
def clicked():
e.get = s
print(s)
button = Button(root,command=clicked,text="ok")
button.pack()
root.mainloop()
You have to change the function clicked as follows:
def clicked():
s=e.get()
print(s)
There were two errors in your code:
You were trying to assign the value 12 to a function.
You were not calling the function (using parenthesis).
this line here:
e.get = s
says the method of e named get is equally to s.
Which is nonsense. You want s to be equally to what is returned by e.get.
to have something returned you need to invoke this method first.
So the logical right way to do this is by:
s = e.get()
Note that it is a variable in the enclosed namespaces of your function.
To make it global you need to global the variable.
from tkinter import *
s = 12
root = Tk()
root.geometry("200x200")
root.title("Program")
e = Entry(root)
e.pack()
def clicked():
#global s
s = e.get()
print(s)
button = Button(root,command=clicked,text="ok")
button.pack()
b2 = Button(root, text='print', command=lambda:print(s))
b2.pack()
root.mainloop()
I have used .place() instead of .pack() and placed my same entry on the same position as it was place before, but this time if value changes as you click on button OK.
Use e.insert(0,"Value") to insert any value in a entry.
This worked for me. Also let me know did it worked for you as well?
from tkinter import *
s = 12
root = Tk()
root.geometry("200x200")
root.title("Program")
e = Entry(root)
e.place(relx=0.1, rely = 0.1)
def clicked():
e = Entry(root)
e.insert(0, s)
e.place(relx=0.1, rely = 0.1)
button = Button(root,command=clicked,text="ok")
button.place(relx=0.4, rely = 0.2)
root.mainloop()

How to make a label cycle through preset words or phrases when a button is pressed in tkinter

So I'm trying to make it cycle through each letter of the alphabet when the button is clicked.
I have tried the method i am showing now.
I have also tried many others and i couldn't get anything to work.
If you do have a solution please try keep it simple i am kinda new too this.
from tkinter import *
win = Tk()
win.title('ab')
a = 0
def changetext():
a = a+1
if a == 1:
lbl.config(text='b')
def changetext():
if a == 2:
lbl.config(text='c')
lbl = Label(win,text='a')
lbl.grid(row=1,column=1)
btn = Button(win,text='u', command =changetext)
btn.grid(row=2,column=1)
win.mainloop()```
In python, variables inside functions are local, which means that if you define a variable a = 0 outside the function, then do a = 1 in the function, the a equals 1 inside the function but it still equals 0 outside. If you want to change the value of a outside the function from inside the function, you need to declare a as a global variable (see code below).
import tkinter as tk # avoid import * to because it leads to naming conflicts
win = tk.Tk()
win.title('ab')
i = 0
letters = "abcdefghijklmnopqrstuvwxyz"
def changetext():
global i # change value of i outside function as well
i += 1
i %= 26 # cycle through the alphabet
lbl.configure(text=letters[i])
lbl = tk.Label(win, text='a')
lbl.grid(row=1, column=1)
btn = tk.Button(win,text='u', command=changetext)
btn.grid(row=2, column=1)
win.mainloop()
You can use itertools.cycle to create a cycle list and then use next() function to get the next item in the cycle list:
import tkinter as tk
from itertools import cycle
words = cycle(['hello', 'world', 'python', 'is', 'awesome'])
root = tk.Tk()
lbl = tk.Label(root, text=next(words), width=20)
lbl.pack()
tk.Button(root, text='Next', command=lambda: lbl.config(text=next(words))).pack()
root.mainloop()
I actually used the first method and adapted it by making the variable global because then it will update it for all the functions making my first method work
from tkinter import *
win = Tk()
win.title('ab')
i = 0
def changetext():
global i
i = i + 1
if i == 1:
lbl.config(text='word 2')
if i == 2:
lbl.config(text='word 1 ')
lbl = Label(win,text='a')
lbl.grid(row=1,column=1)
btn = Button(win,text='u', command =changetext)
btn.grid(row=2,column=1)
win.mainloop()

Returning a value from a tkinter form

I'm using code where I need to ask the user for input, using a tkinter window (I'm not using tkinter in other parts of the code).
My issue is that I simply need to use the tkinter window to return a value upon pressing the OK button on the form, which will also close down the form. The only way I can get it to work so far is by using a global variable. I've searched for other solutions to this but either they don't return a value (they simply print it) or don't allow passing text for the prompt.
Thanks in advance if you can help with this.
from tkinter import *
def input_text(prompt):
def ok():
global ret
ret = entry.get()
master.destroy()
master = Tk()
lbl = Label(master, text=prompt)
lbl.pack()
entry = Entry(master)
entry.pack()
entry.focus_set()
butt = Button(master, text = "OK", width = 10, command = ok)
butt.pack()
mainloop()
print("I am here!")
ret=""
input_text("Enter something")
print("ret is:", ret)
After a good night's sleep I've solved the problem :-)
The solution was to create a class and return the response via an attribute. Here's the code for the archive ... just in case anyone out there has a similar question.
from tkinter import *
class InputForm():
def __init__ (self, prompt):
self.prompt = prompt
self.response = ""
def ok():
self.response = entry.get()
master.destroy()
master = Tk()
lbl = Label(master, text=self.prompt)
lbl.pack()
entry = Entry(master)
entry.pack()
entry.focus_set()
butt = Button(master, text = "OK", width = 10, command = ok)
butt.pack()
mainloop()
abc = InputForm("Enter something").response
print("returned value is:", abc)

Tkinter: Updating Label with New Background Color (Python 3.5.3)

I am designing a simple timer using Tkinter that changes color after a certain amount of time has elasped. I have a base timer program which works well, but now I want to modify it so the background changes color.
I have if statements that trigger on the appropriate intervals and then change the class attribute assigned to the background color, but I can't get the label color to update.
I understand the "makeWidgets" function runs only once and believe this is likely the source of my problem. I've experimented breaking out this function into the main program with mixed success. I am able to get the timer to work, but still cannot get the color to change. I have also tried writing a color change function/s but haven't had any success. I am inexperienced with python, tkinter and full-disclosure, I did not design the bulk of the base timer program.
I would really appreciate any direction/advice on how to get this working. I feel that I am either close, or in need of a complete re-work. Hopefully, the former is the case.
from tkinter import *
import time
class StopWatch(Frame):
global mincount
""" Implements a stop watch frame widget. """
def __init__(self, parent=None, **kw):
Frame.__init__(self, parent, kw)
self.start = 0.0
self.elapsedtime = 0.0
self.running = 0
self.timestr = StringVar()
self.makeWidgets()
self.color = 'green'
def makeWidgets(self): #this function only run once at setup
""" Make the time label. """
self.color='green' #this works
l = Label(self, textvariable=self.timestr, bg=self.color, font=("Helvetica",300), width=12, height=2)
self.setTime(self.elapsedtime)
l.pack(fill=X, expand=YES, pady=2, padx=2)
def update(self):
""" Update the label with elapsed time. """
self.elapsedtime = time.time() - self.start
self.setTime(self.elapsedtime)
self.timer = self.after(50, self.update)
def setTime(self, elap,):
global mincount
""" Set the time string to Minutes:Seconds:Hundreths """
minutes = int(elap/60)
seconds = int(elap - minutes*60.0)
hseconds = int((elap - minutes*60.0 - seconds)*100)
self.timestr.set('%02d:%02d:%02d' % (minutes, seconds, hseconds))
mincount = int(elap)
if mincount>=3:
print("yellow")
self.color='yellow' #has no effect
l.config(bg='yellow') #not in scope
#CHANGE COLOR TO YELLOW - call fx?
if mincount>=5:
print("red")
#CHANGE COLOR TO RED
def Start(self):
""" Start the stopwatch, ignore if running. """
if not self.running:
self.start = time.time() - self.elapsedtime
self.update()
self.running = 1
def Stop(self):
""" Stop the stopwatch, ignore if stopped. """
if self.running:
self.after_cancel(self.timer)
self.elapsedtime = time.time() - self.start
self.setTime(self.elapsedtime)
self.running = 0
def Reset(self):
""" Reset the stopwatch. """
self.start = time.time()
self.elapsedtime = 0.0
self.setTime(self.elapsedtime)
self.color='green'
def main():
root = Tk()
sw = StopWatch(root)
sw.pack(side=TOP)
Button(root, text='Start', command=sw.Start).pack(side=BOTTOM, fill=BOTH)
Button(root, text='Stop', command=sw.Stop).pack(side=BOTTOM, fill=BOTH)
Button(root, text='Reset', command=sw.Reset).pack(side=BOTTOM, fill=BOTH)
Button(root, text='Quit', command=root.quit).pack(side=BOTTOM, fill=BOTH)
current=sw.timestr
root.mainloop()
if __name__ == '__main__':
main()
You can't use l in your setTime function : l is a local variable in makeWidgets
it can't be used in setTime. To fix it you have to make l a variable part of the class in makeWidgets : self.label = ... for exemple. And after that use your new variable self.label in setTime : self.label.config("bg"="yellow")
Something can be improved in your if statement if mincount>=3: because if it's true you change the bg and after that you check if mincount>=5. You should do this:
if mincount>=5:
...
elif mincount >=3:
...

Resources