Rename the label in tkinter - python

I work with tkinter. I want to change the name of the labels. By entering the characters in the field and hitting the button, the labels are renamed one by one. That is, the first time I enter the character "Hello", then that character is inserted in the label; It is then removed from the field. This time I have to enter a character for the next label. And so on until the end
(With the help of the for loop).
I did this but it only works for the first label and does not go to the next label):
win=Tk()
size=3
lbls=[]
frms=[]
def func():
for i in range(6,9):
lbls[i].configure(text=name.get())
for i in range(size):
for j in range(size):
frm=Frame(win,bd=2,relief="sunken")
frm.grid(row=i,column=j)
frms.append(frm)
lbl=Label(frm,bg="white",fg="red",width="5")
lbl.grid(row=0,column=0)
lbls.append(lbl)
name=Entry(win)
name.grid(row=4,column=0)
btn=Button(win,text="Start",font=("Arial",14),command=func)
btn.grid(row=3,column=0)
win.mainloop()

You need to use a counter and increase it inside the function. Basically, for loop wont wait for you to type in something and press the button:
counter = 0
def func():
global counter
lbls[counter].configure(text=name.get())
counter += 1 # Increase it by 1
if counter >= len(lbls): # If counter is greater than items in list, then reset
counter = 0
name.delete(0,'end') # Clear the entry
This will keep updating the text each time you press the button.
Some designing tip: Add columnspan for your button and entry
name.grid(row=4,column=0,columnspan=3)
btn.grid(row=3,column=0,columnspan=3)

Related

How to properly update tkinter label element

I'm working on creating a stopwatch that is given two values. It starts with value A and counts down to 0, then changes to value B, counts down to 0, then goes back to value A, counts down to 0 etc until I close the program (I'll probably add a pause button at some point) and overall it's working really well. However, when it updates the label with the new text, It seems to just be making a new text item and putting it as a layer on top of the previous. I can see then when I go from a single double-digit number to a single digit, and the sentence is shortened, the part of the old sentence that isn't covered can still be seen. So I'm hoping I'm just missing something incredibly simple. The newWindow.update() is what I thought would update the window but it does not appear to be doing that. Below is my snippet of code that handles the logic.
def countdown(timer_count,count_type):
counter = timer_count
count_type = count_type
while counter >= 0:
timer = tk.Label(newWindow, text=f"{count_type} for: {counter}")
timer.config(font=("TkDefaultFont",30))
timer.grid(row=0,column=2)
newWindow.update()
time.sleep(1)
counter -= 1
print(counter)
if count_type == "work":
count_type = "rest"
elif count_type == "rest":
count_type = "work"
return count_type
def interval():
counter_type = "work"
while True:
if counter_type == "work":
counter_type = countdown(int(exer_var.get()),counter_type)
elif counter_type == "rest":
counter_type = countdown(int(rest_var.get()),counter_type)
You are creating a new Label widget each time through your while loop instead of changing the text inside the while loop. That is why it is layering one widget on top of the other, so you need to create your widget, then run the while loop, and set the text to change in the timer.config inside the loop. You should also declare the font in the original tk.Label, no need to change that each trip through the loop. For "some_starting value" it would probably be text = counter
timer = tk.Label(newWindow, font=("TkDefaultFont",30), text="some_starting_value")
while counter >= 0:
timer.config(text=f"{count_type} for: {counter}")
timer.grid(row=0,column=2)
Its hard to say from your code where this is taking place, but here is how its usually done:
Make the label outside all functions in the main block.
timer = tk.Label(newWindow,font=("TkDefaultFont",30)) # Adding the font here itself
Then now inside the function just change its value using config:
def countdown(timer_count,count_type):
counter = timer_count
count_type = count_type
while counter >= 0:
timer.config(text=f"{count_type} for: {counter}") # Update the options for the created label.
timer.grid(row=0,column=2)
So now each time function/loop is running, new labels wont be created and overwritten, instead existing label will be configured.
On a side note, using time.sleep() and while loop is not the very best practice, even with update() it will still cause some kind of disturbance to GUI. Instead, re-arrange your code to use after(ms,func) method, which will not freeze the GUI. You can ask a new question on that if you face any trouble, later.

How to get the button's new position when placed to another one?

Is there a way to assign an integer value to buttons that are produced from loops? What I want to happen here is to place the button pressed to a certain position when tapped, and when that button is tapped again, it will return to its original position.
I tried using the rooty or rootx method using a dictionary that saves the buttons original coordinates called origvals. Then comparing it to the button's new position when clicked, but it does not work, it instead gives me the same result. Can someone pls tell me of a best way to do this? Ive think of giving each button a 0 value and add that value 1 when it is pressed to make a modulus condition so that it would return when its pressed and go back to the certain position when pressed again.
from tkinter import *
import string
import random
root = Tk()
root.geometry('400x400')
# New Buttons coordinates when clicked
buttons = {}
# X coordinate after being clicked
ax=20
# Original Buttons coordinates; this must be their position once clicked again
origvals = {}
def Enter(Value):
global ax
print(buttons)
print(origvals)
print(buttons[Value].winfo_rooty())
if buttons[Value].winfo_rooty() != origvals[Value]:
buttons[Value].place(x=ax,y=150)
ax+=20
else:
buttons[Value].place(x=origvals[Value][0],y=origvals[Value][1])
print(buttons[Value].winfo_rooty())
print(origvals[Value])
letters = string.ascii_uppercase
# Original x and y position
a=100
b=300
c=100
# Placement of random letter buttons
for i in range(1,13):
x = random.randint(0,25)
if i > 6:
buttons[letters[x]] = Button(root, text = letters[x],command=lambda val=letters[x]:Enter(val))
buttons[letters[x]].place(x=c,y=330)
origvals[letters[x]] = [c,330]
c+=20
else:
buttons[letters[x]] = Button(root, text = letters[x],command=lambda val=letters[x]:Enter(val))
buttons[letters[x]].place(x=a,y=b)
origvals[letters[x]] = [a,b]
a+=20
mainloop()
So you see, its kind of like a word game where you press each letters to form a word, and when one letter is clicked twice, it will go back to its original position.

Changing Value Through Button or Checkbox in Tkinter

My Whole Program is Working in a Loop like this
myWindow = Tk()
myWindow.title("Live Data")
myWindow.geometry("500x600")
def my_mainloop():
statements....
myWindow.after(1000, my_mainloop)
myWindow.after(1000, my_mainloop)
myWindow.mainloop()
I want to implement something(button/checkbox), which is changing a value in variable
like 0 and 1
so i can use this value (0/1) in this infinite loop to code my logic
A Click will change value to 1 and then another click will change value to 0.
Thank you.
my example:
def change_value(*arg):
value = 1
button(myWindow, text, command=change_value)

How to add Combobox every time when i press button in Tkinter?

I wanna build GUI and there is a button which Combobox adds every time the button is pressed. but i cant find out how to figure or code this concept
And, is there any other Widget? or is there other way to build my concept??
please some one help me..
the main concept without using tkinter is Like this shown . i want this concept to be made in Tkinter.
[THIS IS WHAT I TRIED BUT WHENEVER I PRESS THE BUTTON, ALL COMBOBOX ADDED WORKS TOGETHER (which means when i change the list name, all combobox also changes..)]
def Plus_EXT():
button_plus = Button(window3,justify = CENTER,command = Add_EXT, text= "+")
button_plus.grid(row=0,column=0)
def Add_EXT():
global Num
window3.update()
Num += 1
CEList1 = [x for x in os.listdir(cur_dir) if ('CE' in x.upper()) and ('.rpt' in x)]
RPT_EXT_file=ttk.Combobox(window3, values=CEList1,textvariable= EXT_String)
RPT_EXT_file.grid(row=8+Num,column=1)
RPT_EXT_file.set("FILE")
[MAIN CONCEPT WITHOUT USING TKINTER]
CE_Num= int(input("How Many EXTRA Chordae?: "))
User_File = input("Type the New File Name: ")
for Num_Of_EXT in range(CE_Num):
RPT_EXT_file = input("Type the RPT of EXTRA CHORDAE: ") + ".rpt"
EXTRA(User_File,RPT_EXT_file,Num_Of_EXT)
EXTRA_PLT(User_File,Num_Of_EXT)
the output i want is { everytime i press the button a new Combobox is added in the frame with the lists which works separately.}
The issue is that all your Combobox widgets are linked with same textvariable called EXT_String.So when you change the value of one Combobox widget, it updates same value in all of them.
I would suggest to keep a list of such variables and index that list using NUM in your loop to assign the textvariable.
UPDATE:
Just an example based on limited code shared in Add_EXT method:
global var_list
var_list.append(IntVar()) #Or StringVar(), whatever you are using
RPT_EXT_file=ttk.Combobox(window3, values=CEList1,textvariable=var_list[-1])

Variable updating one step behind in list searcher in Tkinter

I'm trying to build a search engine that will check a list and then remove all list items that do not meet the search parameters. I know there is several problems with my program such as it will not add things back to the list when you backspace and in my updating for loop I simply tack on a '*' thinking that it will search for strings only beginning with the current parameters, but I will cross those bridges later.
class StudentFinderWindow(Tkinter.Toplevel):
def __init__(self):
Tkinter.Toplevel.__init__(self) # Create Window
searchResultList = ['student1', 'student2', 'student3'] # Test list.
##### window attributes
self.title('Edit Students') #sets window title.
##### Puts stuff into the window.
# text
editStudentInfoLabel = Tkinter.Label(self,text='Select the student from the list below or search for one in the search box provided')
editStudentInfoLabel.grid(row=0, column=0)
# Entry box
self.searchRepositoryEntry = Tkinter.Entry(self)
self.searchRepositoryEntry.grid(row=1, column=0)
# List box
self.searchResults = Tkinter.Listbox(self)
self.searchResults.grid(row=2, column=0)
This fills the Tkinter Listbox with the original list.
# Search results initial updater.
self.getStudentList()
for student in self.studentList:
self.searchResults.insert(Tkinter.END, student)
##### Event handler
Right here I bind to run the list updater after a key is entered into the search box
self.searchRepositoryEntry.bind('<Key>', self.updateSearch)
This is supposed to run every time a key is pressed. It gets the string that is in the Entry then starts a variable count so I know which index the name is at. After that it run a for loop on the current list supposedly checking to see if it fits the requirement of the parameters and any other letter after it. If it does not match it should delete. The problem is the first time I hit a letter the parameters string is just a blank space and then the next letter the string is the first letter and so on. It is always one step behind. And that is the problem
def updateSearch(self, event):
parameters = self.searchRepositoryEntry.get()
int = 0
currentList = self.searchResults.get(0, Tkinter.END)
for i in currentList:
if not i == parameters + '*':
self.searchResults.delete(int)
int += 1
def getStudentList(self):
global fileDirectory # Gets the directory that all the files are in.
fileList = listdir(fileDirectory)
self.studentList = []
for file in fileList:
self.studentList.append(file[:-4])
I believe I have run into this same problem you describe before, when attempting to make an actively searching ctrl-F feature in one of my programs.
What I found to work is not bind on Key but instead KeyRelease. I'm not entirely sure why this works (probably just a quirk with Tkinter). However, it works.
Snippet's:
The binding
# self.FW.En is an entry widget.
self.FW.En.bind('<KeyRelease>', self.find)
Which would run
def find (self, event):
self.Te.tag_remove('found', '1.0', 'end')
pat = self.FW.En.get()
if len(pat) > 1:
index = '1.0'
while True:
index = self.Te.search(pat, index, nocase=1, stopindex='end')
if not index:
break
lastidex = '%s+%dc' % (index, len(pat))
self.Te.tag_add('found', index, lastidex)
index = lastidex
self.Te.tag_config('found', background='#80ff00')

Categories

Resources