Make tkinter label refresh at set time intervals without input - python

I've been trying for the past couple of hours to find a way to refresh a label with information without having to input anything myself.
The program I am trying to write is taking the CPU temp from the Raspberry Pi and displaying it in a window. I need to make that temp input to update every 5 seconds or so, but all attempts to do so have failed.
I tried while loops and found that they do not work inside tkinter, and I can't think how to refresh something constantly without input without one.
I am quite new to Python so I'm sure there is a way and I just haven't come across it yet. Similar questions on here don't quite lead to an answer that applies for me.
Here is my bit of code right now:
import subprocess
from tkinter import *
root = Tk()
root.title('CPU Temp')
cpuLab = Label(root, text = 'CPU Temp:',
font =('Nimbus Mono L',14,),
bg = 'black', fg = 'green').grid(row = 0, column = 0)
cpuTemp = subprocess.check_output(['/opt/vc/bin/vcgencmd', 'measure_temp'])
cpuVar = StringVar()
cpuDisplay = Label(root, textvariable = cpuVar,
font =('Nimbus Mono L',14),
bg = 'black', fg = 'green').grid(row = 0, column = 1)
cpuVar.set(cpuTemp[5:11])
root.mainloop()
This works perfectly for showing the temperature, it just has to be rerun in order to refresh.

Tkinter root windows have a method called after which can be used to schedule a function to be called after a given period of time. So call that function itself like (you will have to create a class first):
def update_label(self):
self.label.configure(cpuTemp)
self.root.after(1000, self.update_label)
This will then reload your label every second.
This may help you: Creating a Timer with tkinter

TKINTER
Refresh, Update, Rerender
This code works for any type of Tkinter widget update
#!/usr/bin/env python3
import sys
import time
from tkinter import *
# Global variables
running = True
# Button action updater
def callback():
if button_1["state"] == "disabled":
button_1["state"] = "normal"
else:
button_1["state"] = "disabled"
root.after(4000, callback)
# Window setup
root = Tk()
root.title("Buttons")
root.geometry("400x300")
# Buttons setup
button_1 = Button(root, text="Learn Python", command=callback)
button_1.pack()
# Software loop
root.mainloop()
Python version used to created this software is: >=3.x

Related

Open window on button click, close current window?

I have been searching for a solution for this issue, but I can't seem to find a viable answer.
I have the following code to open another tkinter script on button click:
# Program to Open on Button Click
def tkinter1():
ret = os.system('python C:\filepath\new_script.py"')
if ret:
# record updated, reload data
treeview_preview()
b1 = Button(master, text="New Window", command=tkinter1)
My problem I am facing is that I want the current window to close on the button click and only keep the new window open.
I know it is possible. I have this instance with many different windows and I seem to be stuck.
The unfortunate thing is that I have an entire different script for different parts of the beta software and the only way I could successfully run all of them is to access them as stated above.
I tried using the if ret: exit() command at the end with the same result. I have windows opening over and over again.
It seems simple, but I haven't been programming tkinter script for too long.(I still have a lot to learn)
All help is appreciated.
You can use master.withdraw(). I
made your code runnable with a few lines. Ok lets say your fixed main code is this:
import tkinter as tk
from tkinter import *
from seccond import *
from multiprocessing import Process
import os
def main():
master = tk.Tk()
# Program to Open on Button Click
def tkinter1():
master.withdraw()
master.destroy()
ret = Process(target=treeview_preview())
ret.start()
#if ret:
# # record updated, reload data
# treeview_preview()
#commented out bc i don't know what it is
b1 = Button(master, text="New Window", command=tkinter1)
b1.pack()
master.mainloop()
if __name__ == '__main__':
main()
the secondary code name seccond.py in the same folder as the main code is called as an external function but also as a separate process.
import tkinter as tk
def treeview_preview():
root = tk.Tk()
S = tk.Scrollbar(root)
T = tk.Text(root, height=4, width=50)
S.pack(side=tk.RIGHT, fill=tk.Y)
T.pack(side=tk.LEFT, fill=tk.Y)
S.config(command=T.yview)
T.config(yscrollcommand=S.set)
quote = """this is the seccond window."""
T.insert(tk.END, quote)
tk.mainloop()
The code will remove the window but not the terminal box as it will use it for the second script.

A countdown timer that runs in the background and interacts with other functions

I've been looking around for this but all the solutions feel extremely complex and even then I haven't been able to get it to work how I want it. I'm making a program that randomly opens an image from a folder, waits 10 seconds, then opens another random image from the same folder, and keeps going.
Simple enough, I was able to do it in a While True loop and making the "wait 10 seconds" part with time.sleep(10). But, I'd like to have a button that simply resets it whenever I feel like it. I'm doing this in tkinter and I have a button that starts it, but when I added a button to reset it, I haven't been able to click it and when I try to, the program crashes. That's because it's in the time.sleep(10) and the whole program stops for 10 seconds. For anyone curious about the code, here it is.
from tkinter import *
import random
import os
from PIL import Image
import time
root = Tk()
def AnmuViewer():
while True:
random_pic = (random.choice(os.listdir("D:/de_clutter/memez/anmu")))
openPic = Image.open('D:/de_clutter/memez/anmu/' + random_pic)
openPic.show()
time.sleep(10)
continue
def Restart():
AnmuViewer()
start_btn = Button(root, text = "Start", command = AnmuViewer)
start_btn.pack()
next_btn = Button(root, text = 'Restart', command = Restart)
next_btn.pack()
root.mainloop()
I know I don't need a "Restart" button as being able to click the "Start" button again would've done the same thing. Either way, that "Start" button itself is also unclickable.
I've looked into threading so I'm thinking of making a function that counts down from 10, and when it reaches 10, the AnmuViewer() starts over again, that way I can click "Start" again whenever and reset the whole code from scratch. But I just haven't been able to get that to work either. Any suggestions?
You can combine start and reset in the same button, but you can also add a reset button that calls the same function (commented code).
That is upon start, the system is reset to its initial state, here with the callback value set to None.
Most times, it is better to use tkinter.mainloop than a custom while loop. Using time.sleep in GUIs is usually a recipe for disaster as it blocks all interactivity during its execution.
I replaced the images with a text label for simplicity, you will have to change that.
import tkinter as tk
import random
def anmu_viewer():
global cb
random_pic = random.choice(images)
lbl.config(text=random_pic)
cb = root.after(10000, anmu_viewer) # keep a reference of the callback
def reset():
global cb
if cb is not None:
root.after_cancel(cb) # cancel the callback
cb = None # reset the reference to the callback to None
anmu_viewer()
images = ['im1', 'im2', 'im3', 'im4', 'im5', 'im6']
root = tk.Tk()
cb = None
lbl = tk.Label(root, text='')
lbl.pack()
start_btn = tk.Button(root, text="Start", command=reset)
start_btn.pack()
# reset_btn = tk.Button(root, text="Reset", command=reset)
# reset_btn.pack()
root.mainloop()
Using while with tkinter will cause some issues with mainloop() and will freeze the issue. You should be using after(ms,func) which does not freeze the GUI. A very simple code can do this, take a look here:
from tkinter import *
from tkinter import filedialog
from PIL import ImageTk, Image
from glob import glob #for the path of the image
import random
root = Tk()
def anmuViewer():
global cb
choice = random.choice(all_img) #random choice of image from the list of img
cur = ImageTk.PhotoImage(file=choice) #make an image object
img_label.config(image=cur) #changing the image on the label
img_label.img = cur #keeping a reference to the image
cb = root.after(10000,anmuViewer) #repeating the function every 10 second
def restart(): #reset func from Roblochons code
global cb
if cb is not None:
root.after_cancel(cb)
cb = None
anmuViewer()
path = filedialog.askdirectory(title='Choose the directory with images') #ask the directory with the image.
png = glob(path+'/*.png') #pick all the png image
jpg = glob(path+'/*.jpg') #pick all the jpg image
all_img = png + jpg #concatenate both the list
img_label = Label(root) #image label, later to be configured
img_label.pack()
start_btn = Button(root, text = "Start", command=anmuViewer)
start_btn.pack(padx=10,pady=10)
next_btn = Button(root, text = 'Restart', command=restart)
next_btn.pack(padx=10,pady=10)
root.mainloop()
I've explained the code with comments so its easier to understand on the go. I've not assumed what path of image you have, so I'm choosing the paths dynamically as you can see. The code looks long, because I simplified most lines of code to understand better.
Anyway you will need to resize the image so that it fits the screen for everyone, because pixels and screen resolution varies from device. Take a look here

How to print time the button got clicked in the Tkinter window and display it in Tkinter window?

I have a Python code that runs query from SQL server and gives back the output whenever the run query button is clicked in Tkinter.
The issue is sometimes the query takes about 10-20 min to finish and I wouldn't know exactly how much time did the query take to complete.
The code below is a sample of printing on screen in Tkinter.
from tkinter import *
def command(d):
print(d)
a = Tk()
b = []
for c in range(0, 5):
x = Button(a, text=c, command=lambda j=c: command(j)))
x.pack()
b.append(x)
a.mainloop()
So I am thinking of adding like a status bar to print the time when the button was clicked and record time again when the process is finished after the display of message box.
You can use datetime module to get the current time and assign a callback for the button to change it on the display like this (datetime.datetime.now() gets the current time, label widget is associated with a variable in a way that when the variable changes, that label changes as well):
import tkinter as tk
import datetime
def command():
global time
time.set(str(datetime.datetime.now()))
root = tk.Tk()
time = tk.StringVar()
time.set('0')
button = tk.Button(root, text='Print time', command=command)
button.pack()
label = tk.Label(root, textvariable=time)
label.pack()
root.mainloop()

Why does my tkinter window only work when created globably?

When I created this module I first made the tkinter window (all of its settings globally) it worked as intended. I could run the module and the window worked, taking the input from the entry field and displaying the welcome or error message. But when I put them into a function, it stopped working correctly, as shown.
How the window looks when created globally, with the button and input working:
https://gyazo.com/ffcb16416b8a971c09bfa60ee9367bbd
How it looks when created inside the function:
https://gyazo.com/c8858a2793befafa41e71d1099f021d3
The error message pops up straight away, then the main window with the entry field but not the button.
Here's the code where I created the window and settings inside a function:
def userSign(userEntry):
userId = userEntry.get()
if userId.isdigit() == True and len(userId) == 4:
welcomeWindow = tkinter.Tk()
welcomeWindow.title("Welcome")
welcomeWindow.geometry("200x50")
welcome = tkinter.Label(master=welcomeWindow, text="Welcome "+userId,font=("Helvetica", 18, "bold"))
welcome.grid()
welcomeWindow.mainloop()
else:
errorWindow = tkinter.Tk()
errorWindow.title("ERROR")
errorWindow.geometry("500x50")
error = tkinter.Label(master=errorWindow, text="ERROR: "+userId +" DOES NOT MEET CRITERIA", font=("Helvetica", 18, "bold"))
error.grid()
userId=""
errorWindow.mainloop()
def show():
window = tkinter.Tk()
window.title("Sign In")
window.geometry("250x100")
signInPrompt = tkinter.Label(master = window, text = "Enter your ID to sign in")
signInPrompt.grid(column=0,row=2)
userEntry = tkinter.Entry(master = window)
userEntry.grid(column=0,row=4)
enterButton = tkinter.Button(master = window, text="Sign in", command=userSign(userEntry))
enterButton.grid(column=0,row=6)
window.mainloop()
How do I get it so that my window works correctly when created inside functions as this module needs to be called by a different, main module.
You are creating two instances of Tk() which is a bad idea. Instead use Toplevel() for additional windows.
When you create variables or widgets inside a function the names are in the local scope and not available outside the function. And whan the function ends they will be garbage colletced.
Also, as #fhdrsdg points out, problems in the button command.

How to make an interactive simple watch in python?

I'm a bit confused with how to create a watch in python
I wrote the follow code:
from Tkinter import *
root = Tk()
tt='vv'
def time(tt):
from time import strftime
tt=strftime('%H:%M:%S')
return tt
Label= Label(root,bd=11,text=time(tt))
Label.pack()
root.mainloop()
How may I make it interactive? now it just shows the time which was at the moment of running program
You can use the after function to schedule a method call.
Here's a little example. Note that I am using a StringVar to easily set the text of the Label.
from Tkinter import *
from time import strftime
root = Tk()
time_var = StringVar()
def set_time():
time_var.set(strftime('%H:%M:%S'))
root.after(1000, set_time)
Label(root, bd=11, textvariable=time_var).pack()
set_time()
root.mainloop()
I call set_time once, and then set_time schedules itself to be called every 1000 ms.

Categories

Resources