Hi i am writing simple typing sped in test in python using tkinter for gui, but i ran into a problem. I want the text in label to change every time the user writes the tenth sentence in entry widget, but when i wrote tenth sentence text in label is changing for example when this word has 5 letters text is changing 5 times. I know why it's hapenning but i don't know how to fix it. thanks.
import tkinter as tk
from tkinter.constants import END
import tkinter.font as font
import random, sys
start = False
# configure tkinter windows
root = tk.Tk()
root.geometry("800x600")
# set window as not resizable
root.resizable(height=False, width=False)
root.title("typing speed test")
# create fonts
myFont = font.Font(size=30)
txtFont = font.Font(size=40)
#this font is needed to entry widget
midFont = font.Font(size=20)
def get_words():
"""
this function open's file and returns
list with content from it
"""
filename = "words.txt"
try:
with open(filename, 'r') as f:
contents = f.read()
#return 10 random words from list
num = random.randrange(0, 990)
contents = contents.split()
return contents[num:num+10]
except FileNotFoundError:
sys.exit("file not founded")
def countdown(count):
"""
this function create timer in tkinter
window
"""
global start
start = True
# change text and font in label
countdown_label['font'] = myFont
countdown_label['text'] = count
if count > 0:
# call countdown again after 1000ms (1s)
root.after(1000, countdown, count-1)
if count <= 10:
# change color of texts
countdown_label["fg"] = "#f00"
if count == 0:
# when count equal 0 disable entry widget
e.config(state='disabled')
#create label
textlabel = tk.Label(root, width=60, height=10, font=midFont, text = "siema")
#add text to label
textlabel.config(text=get_words())
def onValidate(P):
"""
Tkinter validate function
"""
global words
words = P.split()
if P.isalpha() and start == False:
# start counting when user starts
countdown(100)
if len(words) % 10 == 0 :
textlabel.config(text=get_words())
return True
def comapreLists():
"""
This function compare two
lists and return set with
word(s) if it exist in l0
lists
"""
#this list stores words from entry widget
l0 = words
#this list stores words from file
l2 = get_words()
# compare lists
dn = set(l0).intersection(l2)
# return values
return dn
#it's button created only for debug
b = tk.Button(root, text='COMP', command=comapreLists)
#register validate func
vcmd = (root.register(onValidate), '%P')
#create label for timer
countdown_label = tk.Label(root)
#create entry window
e = tk.Entry(validate="key", validatecommand=vcmd,
width=30, font=myFont, state='normal')
#pack elements
countdown_label.pack()
textlabel.pack()
b.pack()
e.place(anchor='center', x=399, y=500)
root.mainloop()
You can use another global variable to store the current number of words entered, then update textLabel only if current number of words not equal to saved one.
last_cnt = 0
def onValidate(P):
"""
Tkinter validate function
"""
global words, last_cnt
if P.isalpha() and start == False:
# start counting when user starts
countdown(100)
words = P.split()
cnt = len(words)
if cnt and (cnt % 10 == 0) and (cnt != last_cnt):
textlabel.config(text=get_words())
last_cnt = cnt
return True
Related
I'm creating a programme using tkinter and python that tests your words per minute.
I've created a loop that starts when you hit the start game button to keep checking the words typed and to highlight words green if inputted correctly.
After a minute I want the loop to end and for labels to show the total words per minute and errors made. When the labels are shown they're stuck in a loop and keep creating labels over and over again.
I've tried a 'running' boolean to end it, I've tried adding a condition to stop the loop if a variable I've added exceeds 60. I've tried separating it and playing with indentation but nothing seems to be working.
Is there a function or something else that can be used to end the loop? I'm struggling to find a solution.
note: I've set the game to end after 10 seconds, not 60, so that it's easier to test out.
from text import text_list
from tkinter import *
window = Tk()
sample_text = (" ".join(text_list))
text_window = Text(window, height=10, width=99, wrap=WORD)
text_window.insert(INSERT, sample_text)
text_window.configure(state=DISABLED)
text_window.pack()
type_input = Entry(window, width=99)
type_input.pack()
def end_game():
global running
running = False
type_input.configure(state=DISABLED)
user_type = type_input.get()
users_input = user_type.split(' ')
WPM = 0
errors = 0
for words in users_input:
if words in text_list:
WPM += 1
else:
errors += 1
show_results(WPM, errors)
def show_results(result, mistakes):
final_wpm = Label(window, text=f"Time's up! Your typing speed is {result} words per minute.")
final_wpm.pack()
final_errors = Label(window, text=f'Total errors made: {mistakes}')
final_errors.pack()
time_spent = 0
def start_game():
global running
global time_spent
running = True
time_spent += 0.5
if running:
user_type = type_input.get()
users_input = user_type.split(' ')
for words in users_input:
if words in text_list:
countVar = StringVar()
pos = text_window.search(words, "1.0", stopindex="end", count=countVar)
end_point = pos + f"+{countVar.get()}c"
text_window.tag_configure("search", background="green")
text_window.tag_add("search", pos, end_point)
else:
continue
if time_spent < 10:
window.after(500, start_game)
window.after(1000 * 10, end_game)
running = False
start_button = Button(window, text='Start Game', command=start_game)
start_button.pack()
window.mainloop()
I am currently working on a hangman game using tkinter in python.
When I click a button of the letter and it is in the word that we are guessing it should show the letter. But when I click the button this problem is popping up:
This example is only with one button. People say that this problem is because of the mainloop(), but i have no idea how to fix it.
from tkinter import *
from tkinter import messagebox
from generate_word import word
#DEFAULT VALUES
score = 0
count = 0
win_count = 2
WINDOW_BG = '#e5404e'
WINDOW_SIZE = '1200x870+300+80'
FONT = ('Arial', 40)
from tkinter import *
from tkinter import messagebox
from generate_word import word
#DEFAULT VALUES
score = 0
count = 0
win_count = 2
WINDOW_BG = '#e5404e'
WINDOW_SIZE = '1200x870+300+80'
FONT = ('Arial', 40)
#this is an example with only one button
buttons = [['b1','a',80,740]]
#Creating window and configurating it
window = Tk()
window.geometry(WINDOW_SIZE)
window.title('Hangman')
window.config(bg = WINDOW_BG)
#generates all of the labels for the word
def gen_labels_word():
label = Label(window, text = " ", bg = WINDOW_BG, font = FONT)
label.pack( padx = 40,pady = (500,100),side = LEFT)
label1 = Label(window, text = word[0], bg = WINDOW_BG, font = FONT)
label1.pack( padx = 41,pady = (500,100),side = LEFT)
x = 21
for var in range(1,len(word)):
exec('label{}=Label(window,text="_",bg=WINDOW_BG,font=FONT)'.format(var))
exec('label{}.pack(padx = {}, pady = (500,100), side=LEFT)'.format(var,x))
x += 1
exec('label{} = Label(window, text = "{}", bg = WINDOW_BG, font = FONT)'.format(len(word),word[-1]))
exec('label{}.pack( padx = {},pady = (500,100),side = LEFT)'.format(len(word), x+1))
# ---------------------------------------------------------------------------------------------------------------------------------------------------
gen_labels_word()
#----------------------------------------------------------------------------------------------------------------------------------------------------
#letters icons(images)
#hangman (images)
hangman = ['h0','h1','h2','h3','h4','h5','h6']
for var in hangman:
exec(f'{var}=PhotoImage(file="{var}.png")')
han = [['label0','h0'],['label1','h1'],['label2','h2'],['label3','h3'],['label4','h4'],['label5','h5'],['label6','h6']]
for p1 in han:
exec('{}=Label(window, bg = WINDOW_BG ,image={})'.format(p1[0],p1[1]))
exec('label0.place(x = 620,y = 0)')
for var in letters:
exec(f'{var}=PhotoImage(file="{var}.png")')
for var in buttons:
exec(f'{var[0]}=Button(window,bd=0,command=lambda: game_brain("{var[0]}","{var[1]}"),bg = WINDOW_BG,font=FONT,image={var[1]})')
exec('{}.place(x={},y={})'.format(var[0],var[2],var[3]))
def game_brain(button, letter):
global count,win_count,score
exec('{}.destroy()'.format(button))
if letter in word:
for i in range(1,len(word)):
if word[i] == letter:
win_count += 1
exec(f'label{i}.config(text="{letter}")')
if win_count == len(word):
score += 1
messagebox.showinfo('GOOD JOB, YOU WON!\n GOODBYE!')
window.destroy()
else:
count += 1
exec('label{}.destroy()'.format(count-1))
exec('label{}.place(x={},y={})'.format(count,620,0))
if count == 6:
messagebox.showinfo('GAME OVER','YOU LOST!\nGOODBYE!')
window.destroy()
def EXIT():
answer = messagebox.askyesno('ALERT','Do you want to exit the game?')
if answer == True:
window.destroy()
e1 = PhotoImage(file = 'exit.png')
ex = Button(window,bd = 0,command = EXIT,bg = WINDOW_BG,font = FONT,image = e1)
ex.place(x=1050,y=20)
#-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
window.mainloop()
Why exec function in python adds '.!' at the beginning of the text?
The exec function isn't doing that. Tkinter by default names all of its widgets with a leading exclamation point. When you print out a widget, it has to be converted to a string. For tkinter widgets, the result of str(some_widget) is the widget's name.
You can see this quite easily without exec:
import tkinter as tk
root = tk.Tk()
label = tk.Label(root)
print(label)
The above will print something like .!label. If you create a second label it will be .!label2, a third will be .!label3 and so on.
On an unrelated note, you shouldn't be using exec to create widgets. It makes the code very hard to understand and debug. If you want to create widgets in a loop, add them to a dictionary or list instead of dynamically creating variables with exec.
For example:
labels = {}
for var in range(1,len(word)):
label = Label(window,text="_",bg=WINDOW_BG,font=FONT)
label.pack(padx=500, pady=100)
labels[var] = label
With that, you can later reference the widgets as labels[1], labels[2], etc.
You should do the same thing with the images, or anything else that you create in a loop and want to keep track of.
So Here i have a function:
def test():
global value
if value ==0:
value=1
return True
else:
value=0
return False
Here is what I come up with:
import tkinter as tk
value=0
while True:
root = tk.Tk()
root.title("my title")
root.geometry('200x150')
root.configure(background='black')
clock_frame = tk.Label(root, font = ('caviar dreams', 130), bg='black')
clock_frame.pack()
def test():
global value
if value ==0:
value=1
return True
else:
value=0
return False
if test() is True:
clock_frame.config(text="HelloWorld",fg="red")
else:
clock_frame.config(text="HelloWorld",fg="white")
root.mainloop()
I want to display the result of this in a Tkinter GUI. I want to change a label while the function is True or False. I want this change to happen every second.
But i don't know how to do it.
Thank you
You can use after() to periodically call test() and update the label:
import tkinter as tk
root = tk.Tk()
root.geometry('+100+100')
root.config(bg='black')
clock = tk.Label(root, text='HelloWorld', font=('caviar dreams', 130), bg='black')
clock.pack()
value = 0
def test():
global value
value = 1 - value
return value == 1
def update():
color = 'red' if test() else 'white'
clock.config(fg=color)
root.after(1000, update) # call update() after 1 second
update() # start the periodic update
root.mainloop()
Use the after method then. The syntax is like this: widget.after(milisecond, action). First you add the time to wait and next you add the action to do.
help this is result recurs like this
Total Account: 13
Total Account: 13
def update():
file = open("list.txt", "r")
line_count = 0
for line in file:
if line != "\n":
line_count += 1
file.close()
Label(kariata,bg="white", text=('Total Account:',(line_count)) , fg="green", font="bold").pack()
kariata.after(1000, update) # call update() after 1 second
update() # start the periodic update
This question already has answers here:
Making python/tkinter label widget update?
(5 answers)
Closed 7 years ago.
I have a python script which I have written for a Raspberry Pi project, the script reads a value from a micrometer every second, and stores this value as a TkInter StringVar (http://effbot.org/tkinterbook/variable.htm)
What I want to happen is that that string is displayed on the UI as a label, and updated on the UI when the value changes.
I chose to use a Tkinter stringvar instead of a standard string because I thought that Tkinter would then automatically update on the UI, but it doesn't seem to work.
Currently the script displays nothing for the label, and does not update when show sets virtual_reading.
From other threads I have read, the Tkinter method update_idletasks() should force the window to update, but I haven't been able to get this to work.
Can anyone give any guidance on where I'm going wrong?
This is my first go at Python so sorry if this is simple.
import datetime
import csv
from tkinter import *
from tkinter import messagebox
import time
import pigpio
import os
os.system("sudo pigpiod")
root = Tk()
winx = 480
winy = 320
CLOCK=21
DATA=20
g_level=0
g_reading=0
g_bits=0
pi = pigpio.pi()
virtual_reading = StringVar()
def go():
global cb1
global cb2
cb1 = pi.callback(DATA, pigpio.EITHER_EDGE, cbf)
cb2 = pi.callback(CLOCK, pigpio.EITHER_EDGE, cbf)
root.after(1000 ,go)
def show(bits, value):
inch = value & (1<<23)
minus = value & (1<<20)
value = value & 0xfffff
if inch:
reading = value / 2000.0
units = "in"
else:
reading = value / 100.0
units = "mm"
if minus:
sign = "-"
else:
sign = ""
global virtual_reading
virtual_reading = StringVar()
virtual_reading.set("{} {:.3f} {}".format(sign, reading, units))
print(virtual_reading.get())
cb2.cancel()
cb1.cancel()
def measure(event=None):
todays_date = datetime.date.today()
try:
get_tool_no = int(tool_no_entry.get())
if get_tool_no <= 0:
messagebox.showerror("Try Again","Please Enter A Number")
else:
with open("thickness records.csv", "a") as thicknessdb:
thicknessdbWriter = csv.writer(thicknessdb, dialect='excel', lineterminator='\r')
thicknessdbWriter.writerow([get_tool_no] + [todays_date] + [virtual_reading.get()])
thicknessdb.close()
except:
messagebox.showerror("Try Again","Please Enter A Number")
tool_no_entry.delete(0, END)
def cbf(g, l, t):
global g_level, g_reading, g_bits
if g == DATA:
if l == 0:
g_level = 1
else:
g_level = 0
elif g == CLOCK:
if l == pigpio.TIMEOUT:
if g_bits > 10:
show(g_bits, g_reading)
g_reading=0
g_bits=0
elif l == 0:
g_reading = g_reading | (g_level<<g_bits)
g_bits += 1
go()
record_button = Button(root,width = 30,
height = 8,
text='Measure',
fg='black',
bg="light grey", command = measure)
tool_no_entry = Entry(root)
reading_display = Label(root, font=("Helvetica", 22), text = virtual_reading.get())
reading_display.place(x = 50, y =80)
root.resizable(width=FALSE, height=FALSE)
root.geometry('%dx%d' % (winx,winy))
root.title("Micrometer Reader V1.0")
record_button.place(x = 340, y = 100, anchor = CENTER)
tool_no_entry.place(x = 120, y = 250, anchor=CENTER)
tool_no_entry.focus_set()
root.bind("<Return>", measure)
root.mainloop()
cb2.cancel()
cb1.cancel()
pi.stop()
You are misunderstanding how StringVars work. For one, you're recreating the StringVar every second, and only the original one is tied to the label. The one you create isn't associated with any widgets so it will never be visible.
The second problem is that you're associating the variable with the label incorrectly. You're doing this:
reading_display = Label(..., text = virtual_reading.get())
... when you should be doing it like this to get the auto-update feature:
reading_display = Label(..., textvariable = virtual_reading)
That being said, you don't need to use a StringVar at all. You can use it, but it's just an extra object you have to manage. You can directly set the displayed string with the configure method anytime you want
text = "{} {:.3f} {}".format(sign, reading, units)
reading_display.configure(text=text)
Note: you do not need to call update or update_idletasks
I need to write a program that first reads email addresses from a csv file and then draws 5 of them. I would like it to display in a desktop application. I don't know how to display the function result in the label field after pressing the button.
import csv
import random
import tkinter as tk
from tkinter import messagebox
with open('test.csv') as csv_plik:
csv_reader = csv.reader(csv_plik, delimiter=',')
numbers = []
for row in csv_reader:
number = row[0]
numbers.append(number)
lenght = len(numbers)
def losowanie():
i = 0
z = 1
while i < 5:
lenght = len(numbers)
y = lenght
c = random.randint(0, y - z)
print(numbers[c])
numbers.remove(numbers[c])
i+=1
main = tk.Tk()
main.geometry('400x400')
w = tk.Button(main, text = 'Losuj', command = main.destroy)
w.place(x=180, y=180)
y = tk.Label(main, text='losowanie')
y.pack()
y.place(x = 180, y = 220)
main.mainloop()
To get 5 random values from list you can use
values = random.choices(numbers, k=5)
next you have to convert list to one string
new_text = ','.join(values)
and finally you can replace text in label
label['text'] = new_text
And you have to assign function to button
command=losowanie
import csv
import random
import tkinter as tk
# --- functions ---
def losowanie():
values = random.choices(numbers, k=5)
new_text = ','.join(values)
label['text'] = new_text
# --- main ---
with open('test.csv') as csv_plik:
csv_reader = csv.reader(csv_plik, delimiter=',')
numbers = []
for row in csv_reader:
number = row[0]
numbers.append(number)
main = tk.Tk()
button = tk.Button(main, text='Losuj', command=losowanie)
button.pack()
label = tk.Label(main, text='losowanie')
label.pack()
main.mainloop()