Updating tkinter label text - python

I made a little script that takes some values from a web page and displays them as tkinter labels. The values are updated every 5 minutes. How can I make the labels update every time the values change?
url = 'https://myurl.something'
def check():
sleep(300)
value1 = str(requests.get(url))
root = Tk()
label1 = label(root, text=value1)
label1.grid()
while True:
check()
root.mainloop()
I know that the sleep loop could never work, but that's how I did this before adding a GUI

Here is some basic code to get you started. I took out the requests stuff, as it is not really relevant to your problem.
The key things are
The StringVar, which allows you to update your text.
The use of after, to shift your periodic task away from the gui
thread. No need for sleep.
code:
from tkinter import Tk, Label, StringVar
def check(val):
# do your request.get here
sv.set("dummy text %d" % val)
root.after(1000, lambda: check(val + 1))
root = Tk()
sv = StringVar()
sv.set("waiting...")
label1 = Label(root, textvariable=sv)
label1.grid()
root.after(1000, lambda: check(1))
root.mainloop()
Given this code, hopefully you can fit your http request back in and it will do what you want.

Related

tkinter wont refresh widget

I want to write a program that has only a button, and after pressing that, program will start making 3 labels and then change the color of each one every 1 second only once.
It looks very simple and I wrote the following code :
import tkinter as tk
from time import sleep
def function():
mylist=list()
for i in range(3):
new_label=tk.Label(window,text='* * *',bg='yellow')
new_label.pack()
mylist.append(new_label)
print('First state finished')
sleep(1)
for label in mylist:
label.config(bg='red')
print('one label changed')
sleep(1)
window = tk.Tk()
window.geometry('300x300')
btn=tk.Button(window,text='start',command=function)
btn.pack()
tk.mainloop()
First the app is look like this (that is OK):
Second its look like this (its not OK because its print on the terminal but didn't update the lable) :
Third its look like this (at the end the app must be look like this and its OK) :
But I need to see the changes in the moment and use sleep for that reason.
Thank you All.
I would recommend to use .after(delay, callback) method of the tkinter to set the colour.
Hope this is what you want.
import tkinter as tk
def start():
global mylist
mylist = list()
for i in range(3):
new_label = tk.Label(window, text='* * *', bg='yellow')
new_label.pack()
mylist.append(new_label)
delay = 1000 # delay in seconds
for label in mylist:
# Additional delay so that next color change
# is scheduled after previous label color change
delay += 1000
schedule_color_change(delay, label)
def schedule_color_change(delay, label):
print("schedule color change for:", label)
label.after(delay, set_color, label)
def set_color(label):
print("setting color of:", label)
label.config(bg="red")
window = tk.Tk()
window.geometry('300x300')
btn = tk.Button(window, text='start', command=start)
btn.pack()
tk.mainloop()
Problem
The problem is your sleep(1), because it's a function that suspends the execution of the current thread for a set number of seconds, so it's like there is a stop to the whole script
Solution
The solution is to instantiate Thread with a target function, call start(), and let it start working. So you have to use timer which is included in the threading, then a timer from the threading module (import threading)
Inside the first "for" loop, remove your sleep(1) and write for example Time_Start_Here = threading.Timer (2, function_2) and then of course Time_Start_Here.start() to start.
start_time=threading.Timer(1,function_2)
start_time.start()
Instead you have to remove the second "for" loop and write what's inside ... inside the new function that will be called. Next you need to create the function
def function_2():
for label in mylist:
label.config(bg='red')
label.pack()
print('one label changed')
As Meritor guided me, I followed the after method and wrote the following recursive code without sleep :
import tkinter as tk
def recursive(i, listt):
lbl = listt[i]
if i >= 0:
lbl.config(bg='orange')
i -= 1
lbl.after(500, recursive, i, listt)
def function():
mylist = list()
for i in range(3):
new_label = tk.Label(window, text='* * *', bg='yellow')
new_label.pack()
mylist.append(new_label)
print('all label created')
# 2 is length of list minus 1
recursive(2, mylist)
window = tk.Tk()
window.geometry('300x300')
tk.Button(window, text='start', command=function).pack()
tk.mainloop()
Most likely my code is not optimized because it uses recursive and if you know anything better please tell me

How to add a delay between 2 text messages being displayed in Tkinter Python?

So my aim is to use a single function to show a text message upon a button click. Then there should be a delay and then another text message should be displayed.
The game is a dice game that should show 'Rolling...' upon a button click. And then after a while, it should display a random number.
I tried both .sleep() and .after() and both of them resulted in my program not showing the before delay text. Here's my code:
# Imports
import tkinter as tk
from random import randrange
import time
# Global variables
# SIDES is a constant
SIDES = 12
# Functions
def func():
display["text"] = "Rolling..."
window.after(2000)
display["text"] = str(randrange(SIDES) + 1)
# Main program loop
window = tk.Tk()
display = tk.Label(window, text="Press the button \nto roll the dice.", width=20, height=3)
button = tk.Button(window, text="Roll", command=func)
display.pack()
button.pack(pady=10)
window.mainloop()
Any help would be much appreciated!
Try:
window.after(2000, lambda: display.config(text=randrange(SIDES) + 1))
instead of the:
window.after(2000)
display["text"] = str(randrange(SIDES) + 1)
The problem is that when you sleep in the function, the tkinter main loop is interrupted and the screen isn't updated. (window.after() is just a gloified sleep here). The correct solution is to pass a callback to after, which will make it immediately return and call the callback later:
def func():
display["text"] = "Rolling..."
window.after(2000, lambda: display.__setitem__("text", str(randrange(SIDES) + 1)))
(Note that the call to __setitem__ is a direct one-liner lambda translation. This is not good design.)

How can I display the current time left in a timer in a label?

I made a timer for part of a project I am making. I have the timer made, but I would like for the time left in the timer to be printed on to a label. ALso, if this wasn't expected, I would like for the time to be on the label then after a second it deletes itself and places the new time remaining(I do not want it to keep printing the time on a new line one after another).
One post I found that was pretty much what I wanted to do, but it did not work for me and I had to change some functions and add some new ones. I am not sure why this didn't work, but I would prefer it to be different because it has a preset time of 10 seconds and I would like for it to be the users choice. The link: Making a countdown timer with Python and Tkinter?
class Application(Frame):
def createWidgets(self):
# More code here
self.timeLeftLabel = Label(root, text='Time Left: ')
self.timeLeftLabel.pack()
def timeLeft(t):
time.sleep(1)
print(t)
def countdownInGUI():
countdown = Label(root, text=entryInt)
countdown.pack()
entryInt = IntVar()
t = Entry(root, textvariable=entryInt)
t.bind('<Return>', get)
t.pack(pady=5)
I am hoping that the time left will show in the label called countdown, but instead nothing shows up until the timer ends then it says "PY_VAR0" on a new line for each second (So its on 3 lines for 3 seconds, 4 lines for seconds, etc..)
In your func countdownInGUI, you created your Label widget by Label(root, text=entryInt), so tkinter will try to convert what you passed to a string. What you should do is to set entryInt as a textvariable instead.
On the other hand, you don't really need to set a textvariable for your Entry widget - you can retrieve the content directly by calling Entry.get().
Here is how everything could work base on your code:
import tkinter as tk
class Application(tk.Frame):
def __init__(self,master=None,**kwargs):
super().__init__(master,**kwargs)
self.master = master
self.timeLeftLabel = tk.Label(master, text='Time Left: ')
self.timeLeftLabel.pack()
self.entryInt = tk.StringVar()
self.countdown = tk.Label(master, textvariable=self.entryInt)
self.countdown.pack()
self.t = tk.Entry(master)
self.t.bind('<Return>', self.start_countdown)
self.t.pack(pady=5)
def start_countdown(self,event=None):
if self.t.get().isdigit():
self.time_left(int(self.t.get()))
def time_left(self, t):
self.entryInt.set(t)
t-=1
if t>=0:
self.master.after(1000,self.time_left, t)
else:
self.entryInt.set("Boom!")
root = tk.Tk()
frame = Application(root)
frame.pack()
root.mainloop()

Fast changing labels in Tkinter?

I would like to display some numbers, as fast as possible in Tkinter. The Program, I am trying to do, gets many numbers send and should show those.
Here is an similar environment, where tinter has to change a label very quickly.
from tkinter import *
import time
window = Tk()
lbl13 = Label(window, text="-")
lbl13.grid(column=0, row=0)
x = 0
while 1:
lbl13.config(text = str(x))
time.sleep(2)
x +=1
window.mainloop()
The Tkinter window doesn't even open on my computer. Is that because i have too weak hardware? What could I change that this Program also runs on my Computer. Thank you for every answer!
The infinite while loop will keep the program from getting to the line where you call window.mainloop(). You should call window.update() repeatedly instead of window.mainloop() at the end:
from tkinter import *
import time
window = Tk()
lbl13 = Label(window, text="-")
lbl13.grid(column=0, row=0)
x = 0
while 1:
lbl13.config(text = str(x))
window.update()
x +=1
Using after and a proper mainloop is probably a more more flexible way to achieve what you want; it is also reusable in different contexts, and can be used in an application that does more than trivially increment a number on a label:
maybe something like this:
import tkinter as tk
if __name__ == '__main__':
def increment():
var.set(var.get() + 1)
label.after(1, increment)
window = tk.Tk()
var = tk.IntVar(0)
label = tk.Label(window, textvariable=var)
label.pack()
increment()
window.mainloop()

How to make randomly pop some gui without stopping the script #python 2.7

I'm relatively new to python, so please bear with me.
My question has two aspects:
First, I'm trying to make a GUI that randomly pops different sentences to the user, every time in a new frame.
Second, I want the user to be able to close the GUI without stopping the script, like if it was running in the background.
here is my code:
import Tkinter as tk
import random
number = random.randint(0,13)
sentence = {contains 13 sentences
}
window = tk.Tk()
window.output = tk.Label(text = sentence[number])
window.title("I Always See You")
window.geometry("300x150")
def next():
rand= random.randint(0, 13)
window2 = tk.Tk()
window2.output = tk.Label(text = sentence[rand])
window2.output.pack(side="top", fill="x", expand=True)
window2.title("I Always See You")
window2.geometry("300x150")
window.output.pack(side="top", fill="x", expand=True)
choice()
window.after(1000, next)
window.mainloop()
My problem: when my second frame pops, there isn't any text showing, and if it does have something popping, it appears in the first frame.
also, how can you insert a random float in .after() ?
Thank you so much for your help!
cheers
You do not see the text in the second window because Tkinter cannot handle two main windows. You need to use the Toplevel class for the others. In addition, you haven't specified the parent of the label in next, so it will probably be packed inside window instead of window2.
In addition, you need 14 sentences because randint, unlike randrange includes both end points.
To set a random time in after, just use randint because it expects an integer number of ms.
To achieve what you want I suggest you to create a main window that will be withdraw (it will run in the background). Then popup Toplevels with random sentences. You need to call again after inside the next function if you want the windows to keep poping up. To prevent the after to be cancelled if the user closes the Toplevel, I called the after method from the withdrawn main window:
import Tkinter as tk
import random
number = random.randint(0,13)
sentence = {i: str(i) for i in range(14)}
def next():
rand=random.randint(0, 13)
window2 = tk.Toplevel(root)
window2.output = tk.Label(window2, text=sentence[rand])
window2.output.pack(side="top", fill="x", expand=True)
window2.title("I Always See You")
window2.geometry("300x150")
tps = random.randint(1000, 10000)
root.after(tps, next)
root = tk.Tk()
root.withdraw() # hide root window
window = tk.Toplevel(root)
window.output = tk.Label(window, text=sentence[number])
window.title("I Always See You")
window.geometry("300x150")
window.output.pack(side="top", fill="x", expand=True)
tps = random.randint(1000, 10000)
root.after(tps, next)
root.mainloop()

Categories