Can't call function to change stringvar - python

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()

Related

Not proceeding with code after while loop

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).

Tkinter 60 seconds timer

i am trying to do a 60 timer for a project but i am stuck here. My variable temp doesnt update itself on my window everytime i go trough it. Anyhelp would be great.
import tkinter
root=tkinter.Tk()
root.geometry("300x250")
root.title("Time Counter")
Frame=tkinter.Frame(root)
Frame.pack(side=tkinter.TOP)
Label1=tkinter.Label(Frame,text="Timer")
Label1.pack(side=tkinter.LEFT)
def timer(*args,**kwargs):
temp=int(temps.get())
while temp>-1:
temps.set(str(temp))
root.update()
time.sleep(1)
temp-=1
temps=tkinter.StringVar()
temps.set("60")
temps.trace("w",timer)
label=tkinter.Label(Frame,textvariable=temps)
label.pack(side=tkinter.LEFT)
label.after(1000,timer)
root.mainloop()
It works fine if you just remove trace and after and call the function directly:
temps=tkinter.StringVar()
temps.set("60")
label=tkinter.Label(Frame,textvariable=temps)
label.pack(side=tkinter.LEFT)
timer()
Adding the timer() function to the trace of your StringVar leads to the function being called whenever the value of temps changes. This can't work since the function changes the value, itself.

Python: How do you obtain variables from another script which are constantly being updated

I've made a script that uses a while True loop to constantly update a series of variables based on UDP packets that I am constantly recieving. I want to ultimately create a GUI that displays that data and updates the screen constantly, which I plan to do with tkinter (using my_label.after in a function which then calls itself, not sure if this is a good plan).
Here is some testing scripts that I can't get to work properly:
GUI2.py (my test looping script)
import time
var = 0
while True:
var += 1
time.sleep(0.1)
GUI Testing.py (the script that would be accessing those variables)
from GUI2 import *
import time
print('never')
print(var)
time.sleep(1)
The second script never reaches the print('never') line, I think because it gets stuck in the other script's while True loop and never returns.
How should I go about this? I have one script that I want in a constant loop to update my variables to the correct values based on incoming packets, and then another script updating a tkinter window. I went this way as most examples I could find using Tkinter didn't use any sort of while True loops. Could I just put my packet recieving code inside the Tkinter mainloop, and would that effectively act as a while True?
EDIT (added Tkinter loop that I can't get working):
This opens a Tkinter window, but the label stays at 99, then reopens a window when I close it with the new x value (ie. 98, 97, etc). I want the label to update every second.
import tkinter as tk
import time
x = 99
while True:
root = tk.Tk()
label = tk.Label(root, text=x)
label.pack()
x -= 1
time.sleep(1)
root.mainloop()
Below is a sample script to show you how you can update the value in the label widget at a certain time interval. I have provided you the hyperlinks to help you understand tkinter's methods. Best regards.
Key points:
use the textvariable option of the tk.Label widget.
use tkinter's control variable. I have shown you how to set and get it's value.
you can use tkinter's widget method called .after() without having to explicitly use a while-statement and time.sleep() method. Tkinter has it's own event loop that you can use.
writing your tkinter GUI as a class makes it easier to implement what you need.
Example Script:
import tkinter as tk
class App(tk.Frame):
def __init__( self, master, *args, **kw ):
super().__init__( master )
self.master = master
self.create_label()
self.update_label()
def create_label( self ):
self.var = tk.IntVar() # Holds an int; default value 0
self.label = tk.Label(self, textvariable=self.var ) # Use textvariable not text
self.label.pack()
def update_label( self ):
value = self.get_value()
self.var.set( value ) # Set the label widget textvariable value.
self.after(1000, self.update_label) # Call this method after 1000 ms.
def get_value( self ):
'''To simulate calling a function to return a value'''
value = self.var.get() + 1
return value
if __name__ == "__main__":
root = tk.Tk()
root.geometry('100x100+0+24')
app = App( root )
app.pack()
root.mainloop() #This command activates tkinter's event loop
Edit:
As a clarification, this answer shows how to utilize the .after() and .mainloop() methods in GUI Testing.py, i.e. using tkinter event loop and not use two while-loops, to achieve what you wanted to do. This is a way to simplify your GUI script.
For more sophisticated algorithms, e.g. more than one while-loop is involved, you have to look into using threads(note it has its issues) or more recently I found a way of using python's Asyncio approach to do it. The learning curve for these two approaches is a lot steeper. To use the asyncio approach, you can explore modifying my answer to do what you want.
Best solution is to use threads however If you plan to do in simplest possible manner then implement the main loop inside your Tkinter GUI and once you read the packet simply update it on your GUI in same loop. Here is the Updated and working Code.
import tkinter as tk
import time
def setvalue(self, x):
self.label.config(text=x, )
root.update()
time.sleep(1)
def changevalues(self):
x = 99
self.label = tk.Label(root, text=x)
self.label.pack()
while x >0:
x -= 1
setvalue(root,x)
root = tk.Tk()
changevalues(root)
root.mainloop()

Why does time.sleep pause tkinter window before it opens

I'm developing a program for a stadium and time.sleep() pauses the program before the window opens instead of when I want it to. What is the explanation for this behavior?
import Tkinter as tk
import time
import random
root = tk.Tk()
label = tk.Label(root, text="Navigating To Seat")
label.pack(pady=10, padx=10)
rand = random.randint(6, 16)
while rand != 0:
label2 = tk.Label(root, text="Foward: " + str(rand) + "m")
label2.pack()
rand = rand - 1
time.sleep(1)
label2.pack_forget()
root.mainloop()
What time.sleep does is suspend the execution of your program. If you do that 6-16 times, for 1 second each time, before ever calling mainloop(), you're asking it to wait for 6-16 seconds before starting up your GUI.
You probably don't understand how event loop programming works. Reading through some Tkinter tutorials should get the idea across nicely. If you want a less Tkinter-focused explanation and more information about the details of what's happening and the different ways to get around it, see Why your GUI app freezes.
At any rate, I think I can guess what you want to do, even though it isn't clear from your question: You want to start the GUI up, and then, every second, replace the Label. To do that, you have to wait while the GUI is running, not before it starts.
But you can't just call sleep while the GUI is running, either. The GUI can't run while your program is asleep (again, that's what sleep means).
The easiest way out of this is to turn your loop into a sequence of function calls, each of which schedules the next one to run a second later, using the after method. For example:
import Tkinter as tk
import random
root = tk.Tk()
label = tk.Label(root, text="Navigating To Seat")
label.pack(pady=10, padx=10)
rand = random.randint(6, 16)
label2 = None
def add_label():
global rand
global label2
if not rand:
root.quit()
if label2:
label2.pack_forget()
label2 = tk.Label(root, text="Foward: " + str(rand) + "m")
label2.pack()
rand = rand - 1
root.after(1000, add_label)
add_label()
root.mainloop()
When you first call add_label(), it creates the initial label, asks Tkinter to call add_label() again in 1000 milliseconds, and returns. So, a second after you start the loop, it gets called again, which creates the next label and asks Tkinter to call it again a second later. This keeps going until you decrement rand all the way to 0, at which point you call quit instead of after, which ends the main loop, which ends the program.
There are other things you probably want to fix about this program. For example, instead of destroying and creating a new Widget label each time, you can just change its text—or, maybe even more simply, make rand an IntVar connected to the label, so just updating rand automatically changes the text. Also, for anything less trivial than this program, you'd probably want to replace the global variables with something cleaner—most Tkinter tutorials show you how to use a Frame subclass by about the second or third example, which gives you a convenient place to organize both widgets and member variables like rand.

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