Not proceeding with code after while loop - python

I'm trying to make a simple digital clock using Tkinter. However, after I use "while True" to update the variable and the label, it doesn't create a window, even though that part is not indented. Here's my code:
from datetime import datetime
from tkinter import *
root = Tk()
root.geometry('400x200')
while True
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
clock = Label(root, text = current_time)
clock.pack()
root.update()
``

I haven't written any python before, however I imagine that while true is always true, therefore you have an infinite loop where your variable now is being constantly updated with the new time, with no chance to break free from the loop

while True:
...
Program is stucked in this part it will keep on executing it.
In order to solve the issue move while True logic inside a thread(use Thread from threading module).
Here's another simple approach we are using clock.after(400, update) which will call after 400 milliseconds and update the label we are using mainloop to ensure that main will not exit till our window is not closed.
from datetime import datetime
from tkinter import *
root = Tk()
root.geometry('400x200')
clock = Label(root)
clock.pack()
def update():
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
print(current_time)
clock.config(text=current_time)
clock.after(400, update)
root.update()
update()
mainloop()

Check here to see how to create a clock with tkinter.
https://www.geeksforgeeks.org/python-create-a-digital-clock-using-tkinter/
Your code will never exit the loop because while will be always looping at a very high speed because the condition is always (True).

Related

Can You Call '.after' Twice in a Function?

I am asking a question in good faith here. I've had a lot of trouble on StackOverflow; I know this is probably Googleable, but I lack the prerequisite knowledge to do so. Please keep that in mind and be kind.
The Overall Goal
I am creating a digital clock which functions normally for five minutes, and then accelerates rapidly for two minutes. Then, it will freeze for a certain amount of time, show the correct time, and the program repeats.
Immediate Goal
I need to call my faketime function from within my time function. When I add the second .after, I get an IndentationError: unindent does not match any outer indentation level. As my indentation appears to fine, I think the issue is elsewhere. Can use you use .after twice in a function? If not, how can I accomplish this? Thank you!
Code
from tkinter import *
from tkinter.ttk import *
# importing strftime function to
# retrieve system's time
from time import strftime
# creating tkinter window
root = Tk()
root.title('Clock')
# This function is used to
# display time on the label
def newtime():
faketime = "Test"
lbl.config(text=faketime)
lbl.after(1000, time)
def time():
string = strftime('%H:%M:%S')
lbl.config(text=string)
lbl.after(1000, time)
lbl.after(300000, newtime())
# Styling the label widget so that clock
# will look more attractive
lbl = Label(root, font=('calibri', 40, 'bold'),
background='black',
foreground='red')
# Placing clock at the centre
# of the tkinter window
lbl.pack(anchor='center')
time()
mainloop()
Line 18 should be lbl.after(1000, newtime) instead of lbl.after(1000, time)
Comment out line 25 #lbl.after(300000, newtime())
HAPPY NEW YEARS!
Output image:

Ongoing event in tkinter

Using tkinter, can I bind an ongoing event to activate while the mainloop still goes off?
Means it executes the same command over and over until the program is closed
you can use root.after to run a function repeatedly like so:
def repeat_task():
# do stuff
root.after(10, repeat_task)
this will constantly do something, and then run itself. the delay time shouldn't be 0 because it may not let tkinter's event loop process other events and will freeze. it will go until the window is closed.
You can also use threading approach:
import tkinter as tk
import threading
def myLoop():
print("Hey I am looping!")
threading.Timer(1, myLoop).start()
window = tk.Tk()
button = tk.Button(window, text = 'Threading', command = lambda: threading.Timer(1, myLoop).start())
button.pack()
window.mainloop()
threading.Timer is an object that will run parallel to your GUI so it won't freeze it.
threading.Timer will fire up every Nth second that you can specify in the constructor.

Can't call function to change stringvar

I am attempting to create a little clock by changing a tkinter label and using the Time Module. What I can't figure out is how to call the test() function with the while loop because I constantly get an error for timee is not defined or test() is not defined. Is there something I'm missing?
import time
from tkinter import *
def test():
seconds = time.time()
local_time = time.ctime(seconds)
timee.set('The Time Is: ' + local_time)
while 1 > 0:
test()
root = Tk()
timee = StringVar()
l = Label(root, textvariable=timee)
l.pack(side=TOP)
root.mainloop()
If you imagine your program running from top to bottom, you never reach the part where you create the Tk, StringVar and Label object; since it gets stuck in the infinite while loop. The error you get is because when you are calling test, timee is yet to defined.
Simply put, your program can only do one thing at a time. When it is busy running the clock, it cannot create TKinter windows. OTOH, when it is managing a tkinter window (.mainloop function), it cannot update your clock.
You need to get both to play nice with each other. There is a special method for the Tkinter object to tell it that you want something done: after. (See tkinter Widget docs):
import time
from tkinter import *
def test():
seconds = time.time()
local_time = time.ctime(seconds)
timee.set('The Time Is: ' + local_time)
# after updating, schedule next call in 1 second
root.after(1000, test)
root = Tk()
timee = StringVar()
l = Label(root, textvariable=timee)
l.pack(side=TOP)
# Before starting up, instruct Tkinter to run test after 1 second
root.after(1000, test)
root.mainloop()
As you see, there is no explicit call of test() anymore. Instead, in after(), the test function is passed as a variable. You can think of handing after() a list of instructions without already executing them.
Then, when tkinter is running its window (.mainloop() call), it will look at its list of things-to-do and will find the test call that you ordered. At the right time it will call the test function one time; then at the end of the function, the next call is scheduled.
timee is not defined because your function is looking for the local var timee but you want to alter the global variable so you need to indicate it to your function
def test():
global timee
seconds = time.time()
local_time = time.ctime(seconds)
timee.set('The Time Is: ' + local_time)
Tkinter applications are user even-driven which means normal procedural programming techniques often won't work — all processing has to occur while mainloop() is running.
In this case you can use the universal widget method after{} to schedule calls to your test function at regular intervals, like every 1/4 second (250 ms):
import time
from tkinter import *
def test():
seconds = time.time()
local_time = time.ctime(seconds)
timee.set('The Time Is: ' + local_time)
def poll():
test()
root.after(250, poll) # Schedule another call.
root = Tk()
timee = StringVar()
l = Label(root, textvariable=timee)
l.pack(side=TOP)
poll() # Start periodic calls to test()
root.mainloop()

Update the contents of Tkinter window

I'm creating a Python program that displays the time and weather in a Tkinter window. I need to have the time, weather, and anything else constantly updating. Here's my old code:
import time
from Tkinter import *
root = Tk()
while True:
now = time.localtime(time.time()) # Fetch the time
label = time.strftime("%I:%M", now) # Format it nicely
# We'll add weather later once we find a source (urllib maybe?)
w = Label(root, text=label) # Make our Tkinter label
w.pack()
root.mainloop()
I've never done anything with Tkinter before, and it's frustrating that the loop doesn't work. Apparently, Tkinter doesn't let you do anything like loops or anything non-Tkinter while it's running. I thought I could maybe do something with threading.
#!/usr/bin/env python
# Import anything we feel like importing
import threading
import time
# Thread for updating the date and weather
class TimeThread ( threading.Thread ):
def run ( self ):
while True:
now = time.localtime(time.time()) # Get the time
label = time.strftime("%I:%M", now) # Put it in a nice format
global label # Make our label available to the TkinterThread class
time.sleep(6)
label = "Weather is unavailable." # We'll add in weather via urllib later.
time.sleep(6)
# Thread for Tkinter UI
class TkinterThread ( threading.Thread ):
def run ( self ):
from Tkinter import * # Import Tkinter
root = Tk() # Make our root widget
w = Label(root, text=label) # Put our time and weather into a Tkinter label
w.pack() # Pack our Tkinter window
root.mainloop() # Make it go!
# Now that we've defined our threads, we can actually do something interesting.
TimeThread().start() # Start our time thread
while True:
TkinterThread().start() # Start our Tkinter window
TimeThread().start() # Update the time
time.sleep(3) # Wait 3 seconds and update our Tkinter interface
So that doesn't work either. Multiple empty windows appear and they glitch out a ton. I get tons of errors in my debugger too.
Do I need to stop and re-open my window when I update? Can I tell Tkinter to update with something like tkinter.update(root) or something like that?
Is there a workaround or solution, or am I missing something? If you see anything wrong with my code, let me know.
Thanks!
Alex
You can "nest" your after calls:
def update():
now = time.localtime(time.time())
label = time.strftime("%I:%M:%S", now)
w.configure(text=label)
root.after(1000, update)
Now you just have to call after once before the mainloop, and it updates every second from now on.

How to run a function in the background of tkinter [duplicate]

This question already has an answer here:
Tkinter locks Python when an icon is loaded and tk.mainloop is in a thread
(1 answer)
Closed 7 months ago.
I am new to GUI programming and I want to write a Python program with tkinter. All I want it to do is run a simple function in the background that can be influenced through the GUI.
The function counts from 0 to infinity until a button is pressed. At least that is what I want it to do. But I have no idea how I can run this function in the background, because the mainloop() of tkinter has control all the time. And if I start the function in an endless loop, the mainloop() cannot be executed and the GUI is dead.
I would like to return control back to the mainloop() after each cycle, but how can I get the control back from the mainloop() to the runapp-function without a user-triggered event?
Here is some sample code that kills the GUI:
from Tkinter import *
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.button = Button(frame, text="START", command=self.runapp)
self.button.pack(side=LEFT)
self.hi_there = Button(frame, text="RESTART", command=self.restart)
self.hi_there.pack(side=LEFT)
self.runapp()
def restart(self):
print "Now we are restarting..."
def runapp(self):
counter = 0
while (1):
counter =+ 1
time.sleep(0.1)
Event based programming is conceptually simple. Just imagine that at the end of your program file is a simple infinite loop:
while <we have not been told to exit>:
<pull an event off of the queue>
<process the event>
So, all you need to do to run some small task continually is break it down into bite-sized pieces and place those pieces on the event queue. Each time through the loop the next iteration of your calculation will be performed automatically.
You can place objects on the event queue with the after method. So, create a method that increments the number, then reschedules itself to run a few milliseconds later. It would look something like:
def add_one(self):
self.counter += 1
self.after(1000, self.add_one)
The above will update the counter once a second. When your program initializes you call it once, and from then after it causes itself to be called again and again, etc.
This method only works if you can break your large problem (in your case "count forever") into small steps ("add one"). If you are doing something like a slow database query or huge computation this technique won't necessarily work.
You will find the answer in this other question Tkinter locks python when Icon loaded and tk.mainloop in a thread.
In a nutshell, you need to have two threads, one for tkinter and one for the background task.
Try to understand this example : clock updating in backgroud, and updating GUI ( no need for 2 threads ).
# use Tkinter to show a digital clock
# tested with Python24 vegaseat 10sep2006
from Tkinter import *
import time
root = Tk()
time1 = ''
clock = Label(root, font=('times', 20, 'bold'), bg='green')
clock.pack(fill=BOTH, expand=1)
def tick():
global time1
# get the current local time from the PC
time2 = time.strftime('%H:%M:%S')
# if time string has changed, update it
if time2 != time1:
time1 = time2
clock.config(text=time2)
# calls itself every 200 milliseconds
# to update the time display as needed
# could use >200 ms, but display gets jerky
clock.after(200, tick)
tick()
root.mainloop( )
credits: link to site
I don't have sufficient reputation to comment on Bryan Oakley's answer (which I found to be very effective in my program), so I'll add my experience here. I've found that depending on how long your background function takes to run, and how precise you want the time interval to be, it can be better to put self.after call at the beginning of the recurring function. In Bryan's example, that would look like
def add_one(self):
self.after(1000, self.add_one)
self.counter += 1
Doing it this way ensures that the interval of time is respected exactly, negating any interval drift that might occur if your function takes a long time.
If you don't want to be away from those threads, I would like to give one suggestion for your GUI-
Place the function for your GUI just before the root.mainloop() statement.
Example-
root = tk.Tk()
.
.
graphicsfunction() #function for triggering the graphics or any other background
#function
root.mainloop()
Please up vote if you like.

Categories

Resources