With the Help of Tkinter, I am trying to print a single word at a time(with an interval of 2-sec sleep)
I tried the following code, It doesn't work as I want it to do.
My code is printing the whole string stacked upon one another.
After n*len(words) sleep seconds are passed.
I am trying to print ONLY one word at a time(with an interval of 2-sec)
from tkinter import *
from time import sleep
root = Tk()
words = 'Hey there, This is python3'.split()
l = Label(root, text='')
for w in range(len(words)):
sleep(2)
l = Label(root,text = words[w])
#l['text'] = words[w] # this is what I tried
l.pack()
root.mainloop()
I tried above-commented statement, thought this might update but didn't work at all as I expected.
Take a look at this example:
from tkinter import *
root = Tk()
words = 'Hey there, This is python3'.split()
l = Label(root) #creating empty label without text
l.pack()
w = 0 #index number
def call():
global w
if w <= len(words)-1: #if it is in the range of the list
l.config(text=words[w]) #change the text to the corresponding index element
w += 1 #increase index number
root.after(2000,call) #repeat the function after 2 seconds
else:
print('Done') # if out of index range, then dont repeat
call() #call the function initially
root.mainloop()
I've commented the code to understand better.
The method using after() calls the function repeatedly, which may decrease its efficiency. So alternatively you can also use, threading to start a new thread that will not make the GUI freeze while sleep():
from tkinter import *
from time import sleep
import threading #import the library
root = Tk()
words = 'Hey there, This is python3'.split()
l = Label(root) #empty label
l.pack() #pack()
def call():
for w in words: #loop through the list
l.config(text=w) #update label with each word over each iteration
sleep(2) #sleep for 2 seconds
threading.Thread(target=call).start() #create a separate thread to not freeze the GUI
root.mainloop()
Simple answer that uses threading, since time.sleep(2) will make the whole tkinter wait some seconds before actually showing the window.
from tkinter import *
import time
from threading import Thread
root = Tk()
words = 'Hey there, This is python3'.split()
l = Label(root, text='')
l.pack()
def show_words():
for word in words:
time.sleep(2)
l.configure(text=word)
thread = Thread(target = show_words)
thread.start()
root.mainloop()
well you first need to take the mainloop() out of the for loof since it just send the command to run the root again hence only first word is printed . I used a timer and a new screen as well (Note:you can do the same stuff on root as well ) ,I started the timer and and send a command to run the def after a perticular time from the current time.I hope that helped you.
from tkinter import *
import threading
from time import sleep
root = Tk()
words = 'Hey there, This is python3'.split()
l = Label(root, text='')
print(words);
def newWindow():
global newScreen
newScreen=Toplevel()
newScreen.title("Rahul Screen")
newScreen.geometry("300x300+15+15")
newScreen.resizable(0,0)
l=Label(newScreen,text='')
for w in range(len(words)):
print(w);
sleep(2)
l = Label(newScreen,text = words[w])
l.pack()
start_time = threading.Timer(2,newWindow)
start_time.start()
root.mainloop()
Related
I was trying to make a stopwatch in python but every time it stops working beacause of overflow, can someone please fix this??
Code:
import time
from tkinter import *
cur=time.time()
root = Tk()
def functio():
while True:
s = time.time()-cur
l1 = Label(root,text=s)
l1.pack()
l1.destroy()
time.sleep(0.5)
Button(root,text='Start',command=functio).pack()
root.mainloop()
The while loop will block tkinter mainloop from handling pending events, use after() instead.
Also it is better to create the label once outside the function and update it inside the function:
import time
# avoid using wildcard import
import tkinter as tk
cur = time.time()
root = tk.Tk()
def functio():
# update label text
l1['text'] = round(time.time()-cur, 4)
# use after() instead of while loop and time.sleep()
l1.after(500, functio)
tk.Button(root, text='Start', command=functio).pack()
# create the label first
l1 = tk.Label(root)
l1.pack()
root.mainloop()
Note that wildcard import is not recommended.
Flow of execution can never exit your endless while-loop. It will endlessly block the UI, since flow of execution can never return to tkinter's main loop.
You'll want to change your "functio" function to:
def functio():
s = time.time()-cur
l1 = Label(root,text=s)
l1.pack()
l1.destroy()
root.after(500, functio)
That being said, I'm not sure this function makes much sense: You create a widget, and then immediately destroy it?
You'll want to do something like this instead:
import time
from tkinter import *
root = Tk()
def functio():
global timerStarted
global cur
# check if we started the timer already and
# start it if we didn't
if not timerStarted:
cur = time.time()
timerStarted = True
s = round(time.time()-cur, 1)
# config text of the label
l1.config(text=s)
# use after() instead of sleep() like Paul already noted
root.after(500, functio)
timerStarted = False
Button(root, text='Start', command=functio).pack()
l1 = Label(root, text='0')
l1.pack()
root.mainloop()
I am using NI instrument to read data and display it on GUI. Have used tkinter. But could not find a way to update the data using while loop.
import nidaqmx
import time
from tkinter import *
master = Tk()
while True:
with nidaqmx.Task() as task:
task.ai_channels.add_ai_voltage_chan("Dev1/ai0")
print('1 Channel 1 Sample Read: ')
data = task.read()
sensor_value ='%.2f' % data #said sensor value
master.minsize(width=400, height=400)
w = Label(master, text=sensor_value) #shows as text in the window
w.pack() #organizes widgets in blocks before placing them in the parent.
time.sleep(5)
mainloop()
When working with Tkinter we should avoid Threading, while loop with root.update() it is not like we can't use them but not advisable instead use after(delay_ms, callback=None, *args) method provided by Tkinter itself for a reason.
Now to your code, there are few issues in your code.
In while loop you are creating a Label every 5 secs instead create one label and update the value of that label inside the loop with w['text'] = '...' or w.configure(text='...').
Don't put mainloop inside the loop, instead call it in the last line with the instance of the main window in your case master (master.mainloop()).
Same with master.minsize(width=400, height=400), you don't have to tell the master window every 5 sec to set the minimum size to 400x400 it should be called once if not decide to change the minimum size to different geomentry.
Your code should look like this.
import nidaqmx
from tkinter import *
master = Tk()
master.minsize(width=400, height=400)
w = Label(master) #shows as text in the window
w.pack() #organizes widgets in blocks before placing them in the parent.
def run():
with nidaqmx.Task() as task:
task.ai_channels.add_ai_voltage_chan("Dev1/ai0")
print('1 Channel 1 Sample Read: ')
data = task.read()
w['text'] = '%.2f' % data #said sensor value
master.after(5000, run)
run() # run the function once.
master.mainloop()
This should be the right way of doing this and as i couldn't run your code if anything doesn't work, let me know otherwise.
try this code:
import time
from tkinter import *
import nidaqmx
master = Tk()
def test():
while True:
with nidaqmx.Task() as task:
task.ai_channels.add_ai_voltage_chan("Dev1/ai0")
print('1 Channel 1 Sample Read: ')
data = task.read()
sensor_value = '%.2f' % data # said sensor value
w['text'] = sensor_value
master.update()
time.sleep(2)
w = Label(master)
w.pack()
btn = Button(text="Start read from sensor", command=test)
btn.pack()
mainloop()
You'll likely need a second thread to poll the sensor.
import nidaqmx
import time
import threading
from tkinter import *
stop_signal = threading.Event()
def read_loop():
with nidaqmx.Task() as task:
task.ai_channels.add_ai_voltage_chan("Dev1/ai0")
while True:
data = task.read()
label["text"] = "%.2f" % data
# Wait for the signal, or 5 seconds
if stop_signal.wait(timeout=5):
break # If the signal was set, break
# Build window
master = Tk()
master.minsize(width=400, height=400)
label = Label(master, text="")
label.pack()
# Set up & start reading thread
threading.Thread(target=read_loop).start()
try:
# Enter Tk main loop
mainloop()
finally:
# Clean up afterwards
stop_signal.set()
Keep in mind that mainloop() is blocking, so time.sleep() is useless as you won't get back to the task.read() line.
Consider using the Thread module from the threading lib importing it like this:
from threading import Thread
or using a button to refresh the value as someone else suggested you.
Currently, I am working on my course work project and there is a simple function I want to achieve. The program is mainly on tkinter, I want the label shows up for 3 seconds, hide for 7 seconds for one period, and in the next period the text in label should change; while the label changes I am trying to disable an entry box from the first 3 seconds then normalise it.
Countdown and change of label text were alright, but the entry box does not respond at all when it is normalised.
here is my code
def c_time():
from tkinter import *
import time
root=Tk()
en = Entry(root)
en.pack(side=TOP)
en.focus_force()
la = Label(root, text='6666')
la.pack(side=BOTTOM)
li = ['a', 'b','c','d']
for i in li:
la.config(text=i)
root.update()
def la_diappear():
root.after(3000)
la.pack_forget()
root.update()
def la_appear():
root.after(7000)
la.pack()
la_diappear()
la_appear()
root.mainloop()
c_time()
Both root.after and time.sleep methods were tried
and I tried multiprocessing when I reached information about GIL in python:
from multiprocessing import Process
import time
from tkinter import *
def count_down():
global total
total = 5
for i in range(total):
time.sleep(1)
total -= 1
print(total)
def tkwindow():
root=Tk()
en = Entry(root)
en.pack(side=TOP)
en.focus_force()
la = Label(root, text='6666')
la.pack(side=BOTTOM)
li = ['a', 'b','c','d']
for i in li:
la.config(text=i)
root.update()
count_down()
if total == 3:
la.pack_forget()
root.update()
if total == 5:
la.pack()
root.mainloop()
if __name__ == "__main__":
a = Process(target=count_down)
b = Process(target=tkwindow)
b.start()
the code above should be work straight away.
plz reply if any thought related
Thank you very much.
You can wrap the for loop in a function, and thread that function. Then you can also use time.sleep without blocking the main thread.
from tkinter import *
from threading import Thread
import time
root=Tk()
en = Entry(root)
en.pack(side=TOP)
en.focus_force()
la = Label(root, text='6666')
la.pack(side=BOTTOM)
def la_diappear():
la.pack_forget()
def la_appear():
la.pack()
def actions():
li = ['a', 'b','c','d']
for i in li:
la.config(text=i)
time.sleep(3)
la_diappear()
time.sleep(7)
la_appear()
t = Thread(target=actions)
t.start()
root.mainloop()
Everything works but the time does not update and I think it is because the mainloop overrides the while loop. Please help, I have searched for a long time and found nothing.
While loop is below then main code:
def loop1():
Time = time.strftime("%H:%M:%S")
while Time != Alarm:
Time = time.strftime("%H:%M:%S")
alarm = Tk()
label3 = Label(alarm, text=Time)
label3.grid(column=0, row=0)
alarm.mainloop()
#Get new time
Time = time.strftime("%H:%M:%S")
#Change to next second
label3.config(text=Time)
Main code:
#Import libraries
from tkinter import *
import time
Time = time.strftime("%H:%M:%S")
def loop1():
Time = time.strftime("%H:%M:%S")
while Time != Alarm:
Time = time.strftime("%H:%M:%S")
alarm = Tk()
label3 = Label(alarm, text=Time)
label3.grid(column=0, row=0)
alarm.mainloop()
#Get new time
Time = time.strftime("%H:%M:%S")
#Change to next second
label3.config(text=Time)
initalarm = Tk()
label1 = Label(initalarm,text="What time do you want to wake up?")
label2 = Label(initalarm,text="Use this form.\nExample: 06:30:00")
Alarm = Entry()
start = Button(initalarm, text="Set Alarm", command=loop1)
label1.pack()
label2.pack()
Alarm.pack()
start.pack()
mainloop doesn't override anything. It simply will not return until the root window is destroyed. Or more correctly, it won't return until root.quit() is called, which happens automatically when the root window is destroyed.
Don't fight against the framework, use it. GUI programming with Tk is event driven, i.e. you don't control the control flow of your program directly in long running loops. Instead you set up handlers for different events which get called from Tk's main loop when the events occur. Examples of events are button presses or when a certain given time has passed. This can be used to regularly get some handler called by the main loop for updating the display and eventually activating the alarm.
import tkinter as tk
from tkinter.font import nametofont
from tkinter.simpledialog import askstring
from datetime import datetime as DateTime
def update_display(time_label, alarm_time):
now = DateTime.now().strftime('%H:%M:%S')
if now >= alarm_time:
time_label['foreground'] = 'yellow'
time_label['text'] = now
time_label.after(500, update_display, time_label, alarm_time)
def main():
root = tk.Tk()
root.title('Alarm Clock')
font = nametofont('TkTextFont').copy()
font['size'] *= 5
time_label = tk.Label(root, text='--:--:--', font=font)
time_label.pack()
alarm_time = askstring(
'When?', 'What time do you want to wake up?\nExample: 06:30:00'
)
if alarm_time is not None:
update_display(time_label, alarm_time)
root.mainloop()
if __name__ == '__main__':
main()
The code does approximately what was tried with the code in the question if I got it right, but an actual useful alarm clock program needs object oriented programming in Python, if it should not become a hard to understand and follow mess.
Also I would not operate on the times as strings but as objects from the datetime module, or at least as number of seconds since ”epoch” with the time module. The string form is better left to input and output for the user. And the user input should be validated. In the current form it is too easy to enter invalid alarm times by accident.
As per my understanding you are looking for the label to be updated till it reaches alarm time. If that's true than there are two thing wrong with your code
alarm = Tk() should be moved out of the while loop
You should use alarm.update() instead of alarm.mainloop()
Updated loop1 definition:
def loop1():
Time = time.strftime("%H:%M:%S")
alarm = Tk()
while Time != Alarm:
Time = time.strftime("%H:%M:%S")
label3 = Label(alarm, text=Time)
label3.grid(column=0, row=0)
alarm.update()
#Get new time
Time = time.strftime("%H:%M:%S")
#Change to next second
label3.config(text=Time)
Main code:
#Import libraries
from tkinter import *
import time
Time = time.strftime("%H:%M:%S")
def loop1():
Time = time.strftime("%H:%M:%S")
alarm = Tk()
while Time != Alarm:
Time = time.strftime("%H:%M:%S")
label3 = Label(alarm, text=Time)
label3.grid(column=0, row=0)
alarm.update()
#Get new time
Time = time.strftime("%H:%M:%S")
#Change to next second
label3.config(text=Time)
initalarm = Tk()
label1 = Label(initalarm,text="What time do you want to wake up?")
label2 = Label(initalarm,text="Use this form.\nExample: 06:30:00")
Alarm = Entry()
start = Button(initalarm, text="Set Alarm", command=loop1)
label1.pack()
label2.pack()
Alarm.pack()
start.pack()
The way that root.mainloop() or in your case alarm.mainloop() works is that it will make it's own loop that will update the root (or alarm) window. Whenever a command is run (like a root.bind, or a button command) it will pause the mainloopBecause mainloop is looping, doing it's own thing, it will never reach lines past the root.mainloop() or alarm.mainloop() (unless some weird errors occur).A really easy way to fix that is to make your own "mainloop" by having a loop that does your things and then uses root.update() or alarm.update() (which is what is being run over and over by the mainloop command but you can "customize" the loop)
Using this will still allow the binds and button commands to work. Instead of calling alarm.mainloop() before your loop, call alarm.update() inside of your loop
I want to introduce some delay in my code. I am using text.after() to get the delay but my tkinter window opens after that specified delay and the ouput is already printed on it.
Basically I am trying to replicate the functionality of sleep(), that is the lines of code should get executed before the timer starts. Then the timer should introduce 4 secs of delay and then the following lines of code should execute. I want to automate some test equipment and I want the next equipment to turn on after 4 secs of delay after the previous equipment was turned on.
To explain this situation, I will use a very simple code. I want the "Start Timer" to appear first. Then I want the numbers 1,2,3,4 appear on the tkinter GUI after 1 sec interval, like a timer going on from 1 to 4. Then I want "Stop Timer to appear". In this way the execution of the code is delayed by 4sec in between.
Is there a way to do this ?
Here is my updated example code:
from Tkinter import *
root = Tk()
text = Text(root)
text.pack()
def append_number(n):
text.insert(END, str(n) + "\n")
text.see(END)
# add one, and run again in one second
if n > 0:
root.after(1000, append_number, n-1)
# start the auto-running function
text.insert(END, "Start Timer")
append_number(5)
text.insert(END, "Stop Timer")
root.mainloop()
You can use after to cause a function to run in the future. A simple solution is to call that immediately in the loop and schedule all of the updates:
from Tkinter import *
root = Tk()
text = Text(root)
text.pack()
def append_number(n):
text.insert(END, str(n) + "\n")
text.see(END)
for i in range(1, 5, 1):
root.after(i*1000, append_number, i)
root.mainloop()
Another common pattern is to have the function automatically reschedule itself, and then quit after a certain condition. For example:
from Tkinter import *
root = Tk()
text = Text(root)
text.pack()
def append_number(n):
text.insert(END, str(n) + "\n")
text.see(END)
# add one, and run again in one second
if n < 4:
root.after(1000, append_number, n+1)
# start the auto-running function
append_number(1)
root.mainloop()
Change the loop into a function that you can call and don't run a loop inside of it, instead keep track of the values as they change:
from Tkinter import *
root = Tk()
text = Text(root)
text.pack()
def updater(i, j):
if i <= j:
text.insert(END, i)
text.insert(END, "\n")
text.yview_pickplace("end")
i += 1
text.after(1000, updater, *[i, j])
root.after(1000, updater, *[0, 10])
root.mainloop()
This of course is a very general example that you will need to form to fit your own application.