Python Tkinter Label Delay - python

This maybe a silly question to ask. I have a label in Tkinter GUI and i want it to be updated through time.
Example:
Msglabel=Tkinter.Label(... text="")
Msglabel.Cofigure(text=" EXAMPLE!")
Wait(5sec)
Msglabel.Configure(text=" NEW EXAMPLE!")
I've read about the after() method but im looking for something like Wait.

You'll need to hand over control to Tkinter during your wait period since Tkinter updates the UI in a single threaded loop.
Sleeping between the configure calls will hang the UI.
As you mentioned, after is the method you want. Try something like this:
try:
import Tkinter as tkinter # Python 2
except ImportError:
import tkinter # Python 3
import itertools
class MyApplication(object):
def __init__(self):
# Create and pack widgets
self.root = tkinter.Tk()
self.label = tkinter.Label(self.root)
self.button = tkinter.Button(self.root)
self.label.pack(expand=True)
self.button.pack()
self.label['text'] = 'Initial'
self.button['text'] = 'Update Label'
self.button['command'] = self.wait_update_label
# Configure label values
self.label_values = itertools.cycle(['Hello', 'World'])
def launch(self):
self.root.mainloop()
def wait_update_label(self):
def update_label():
value = next(self.label_values)
self.label['text'] = value
update_period_in_ms = 1500
self.root.after(update_period_in_ms, update_label)
self.label['text'] = 'Waiting...'
if __name__ == '__main__':
app = MyApplication()
app.launch()

Related

trying to Browse processes with gui using tkinter

Im trying to make a program and i need to link two process together. if one of them stoped the other one stops too and for some reason my gui lagging when trying to browse process and check the conditions i made for it.here is the short video from my problem enter link description here.
import tkinter as tk
from tkinter import messagebox
import psutil
import os
class myapp():
def __init__(self):
self.win = tk.Tk()
self.win.title("test")
self.win.geometry('200x100')
self.win.config(bg='black')
self.win.resizable(False,False)
self.ctr = 0
self.tk_var = tk.StringVar()
self.tk_var.set("0")
lab=tk.Label(self.win, textvariable=self.tk_var,
bg='black', fg='#FF0000')
lab.place(x=90, y=45)
btn = tk.Button(self.win,text='test',command='',
bd=0,bg='#7E1600',width=10,
activebackground='#6D0000')
btn.pack(pady=20)
self.upd()
self.test()
self.win.mainloop()
def upd(self):
self.ctr += 1
self.tk_var.set(str(self.ctr))
self.win.after(50,self.upd)
def test(self):
self.service = "notepad.exe" in (i.name() for i in psutil.process_iter())
if self.service != True :
er = messagebox.showerror(title='error', message='notepad.exe has been stopped')
if er == 'ok':
self.win.destroy()
else:
self.win.after(500,self.test)
os.popen('notepad')
myapp()
problem
I'm still working on my English.
import tkinter as tk
from tkinter import messagebox
import psutil
import os
class myapp():
def __init__(self):
self.win = tk.Tk()
self.win.title("test")
self.win.geometry('200x100')
self.win.config(bg='black')
self.win.resizable(False, False)
self.ctr = 0
self.tk_var = tk.StringVar()
self.tk_var.set("0")
lab = tk.Label(self.win, textvariable=self.tk_var,
bg='black', fg='#FF0000')
lab.place(x=90, y=45)
btn = tk.Button(self.win, text='test', command='',
bd=0, bg='#7E1600', width=10,
activebackground='#6D0000')
btn.pack(pady=20)
self.upd()
self.win.mainloop()
def upd(self):
self.ctr += 1
self.tk_var.set(str(self.ctr))
self.win.after(50, self.test)
def test(self):
self.service = "notepad.exe" in (i.name() for i in psutil.process_iter())
if self.service != True:
er = messagebox.showerror(title='error', message='notepad.exe has been stopped')
if er == 'ok':
self.win.destroy()
self.win.after(500, self.upd)
os.popen('notepad')
myapp()
upd->test->upd->test... just It only takes one call upd. No need to call test. Also, the after function call is wrong. See my modified code. It's hard to explain in my short English.
I renamed some functions in my example for better understanding...
I see that your check is too fast, which can cause lag on lower computers, also, keep in mind that when you call the 'check_notepad' function when initializing the class, whenever you click on the 'test' button it adds a new check , that is, another check loop comes into action and this cascades making your app increasingly heavy in memory...
My solution, in addition to some improvements in the code, is just to start the checker once, so as not to create cascades of checks, and from that your app does not lag anymore, if you need to leave it 'on' using a checkbox instead of a button can do more sense...
import os
import tkinter as tk
from tkinter import messagebox
import psutil
class myapp(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# styles
self.geometry('200x100')
# vars
self.timer = 500
# widgets
self.tk_var = tk.IntVar(value=0)
lab = tk.Label(self, textvariable=self.tk_var)
lab.place(x=90, y=45)
self.check_var = tk.BooleanVar()
btn = tk.Checkbutton(self, text='Test', variable=self.check_var)
btn.pack(pady=20)
# Starts
self.update_label()
self.check_notepad()
def update_label(self):
self.tk_var.set(self.tk_var.get() + 1)
self.after(self.timer, self.update_label)
def check_notepad(self):
if self.check_var.get():
if not notepad_is_open():
er = messagebox.showerror(title='error',
message='notepad.exe has been stopped')
if er == 'ok':
self.destroy()
# call back loop
self.after(self.timer, self.check_notepad)
def notepad_is_open():
service = "notepad.exe" in (i.name() for i in psutil.process_iter())
return service
if __name__ == '__main__':
if not notepad_is_open():
os.popen('notepad')
myapp = myapp()
myapp.mainloop()

only last modification to Label being shown in Python - Tkinter . Why?

I am learning GUI development using Tkinter. I want to show multiple messages on the label which I have stored in a string. I used sleep to view the changes.However only the last message string is shown at execution.
from tkinter import *
import time
master = Tk()
def onClick():
for i in range(0,len(list_of_str)):
w.configure(text=list_of_str[i])
time.sleep(5)
list_of_str = ['first','second','third','fourth','fifth']
w = Label(master, text="Hello, world!")
b = Button(master,text='Click me',command = onClick)
w.pack()
b.pack()
mainloop()
I am a noobie. So thanks for helping !
A simple solution to your problem is to use a combination of the try/except method and using after().
In tkinter sleep() will pause the application instead of providing a timer. For tkinter you want to use the after() method to scheduled an event after a set amount of time instead. The after() method is meant for this exact problem and is what you will always use in tkinter for a delayed event.
In my below example I modified your onClick function to take 1 argument and to use that in our after() method to select the next item in the list after 5 seconds. Note that for the after() method time is done in milliseconds so 5000 is 5 seconds.
from tkinter import *
master = Tk()
def onClick(ndex):
try:
w.configure(text=list_of_str[ndex])
master.after(5000, onClick, ndex+1)
except:
print("End of list")
list_of_str = ['first','second','third','fourth','fifth']
w = Label(master, text="Hello, world!")
b = Button(master,text='Click me',command = lambda: onClick(0))
w.pack()
b.pack()
mainloop()
I think you want this:
from tkinter import *
import time
master = Tk()
global i
i = 0
def onClick():
master.after(1, change)
def change():
global i
if i == len(list_of_str):
pass
else:
w.configure(text=list_of_str[i])
i += 1
master.after(1000, onClick)
list_of_str = ['first','second','third','fourth','fifth']
w = Label(master, text="Hello, world!")
b = Button(master,text='Click me',command = onClick)
w.pack()
b.pack()
mainloop()
time.sleep is a no-no in tkinter. I advise you make your gui in a class and it wil be easier.
example with class:
import tkinter as tk
from tkinter import *
class GUI:
def __init__(self, master):
self.list_of_str = ['first','second','third','fourth','fifth']
self.count = 0
self.master = master
self.w = Label(master, text="Hello, world!")
self.w.pack()
self.b = Button(master,text='Click me',command = self.onClick)
self.b.pack()
def onClick(self, event=None):
if self.count == len(self.list_of_str):
pass
else:
self.w.configure(text=self.list_of_str[self.count])
self.count += 1
self.master.after(1000, self.onClick)
def main():
root = tk.Tk()
app = GUI(root)
root.mainloop()
if __name__ == '__main__':
main()

Tkinter: How can I provide text status updates during execution that is in response to a single event?

I have primary.py, the primary file of a Tkinter app that has a user select a file, and then calls a variety of functions contained in other.py.
The functions in other.py take a while to run, so I want to provide users with updates after each one is complete.
My issue is that all of these messages appear all at once, after the completion of step 3. I want them to appear after each step, as I tried to write it. How can I ensure 'Step 1 in Progress' appears before dummy2 is called, and not at the end of all the functions being performed?
from tkinter import *
from tkinter.filedialog import askopenfilename
from tkinter import ttk
from multiprocessing import Process, Pipe
import other.py
class GUI:
def __init__(self, master):
self.master = master
def tkinter_display(the_message):
ttk.Label(frame, text=the_message, style='my.TLabel').pack()
def select():
path = askopenfilename()
tkinter_display('The script is running...')
if __name__ == '__main__':
side1, side2 = Pipe()
tkinter_functions_conn.send(path)
p_import = Process(target=other.dummy, args=(side2,))
p_import.start()
tkinter_display('Step 1 in progress')
tkinter_display(side1.recv())
p_import2 = Process(target=other.dummy2, args=(side2,))
p_import2.start()
tkinter_display('Step 2 in progress')
p_import3 = Process(target=other.dummy3, args=(side2,))
p_import3.start()
tkinter_display('Step 3 in progress')
if __name__ == '__main__':
root = Tk()
my_gui = GUI(root)
# Styles
root.style = ttk.Style()
root.style.configure('my.TButton')
root.style.configure('my.TLabel')
# Display
frame = Frame()
frame.pack()
ttk.Button(frame, text="Select", command=select, style='my.TButton').pack()
root.mainloop()
You can force the GUI to update by changing your tkinter_display method to this:
def tkinter_display(the_message):
ttk.Label(frame, text=the_message, style='my.TLabel').pack()
frame.update()

GUI programming using Tkinter Python

I have a Tkinter GUI having 2 entry fields, 2 buttons ( initialization of these not shown in code). There is one more button (initialized in code) which performs the main task of performing change detection on two images. Also there is a progress bar.
Now, when the task of change detection has been completed, I want to display the 4 images(pre, post, aligned, chng) returned by wave.changedetection() in a separate Tkinter window. I want the new window to come only after changedetection() has completed.(wave.py is my own file, not some module)
Unfortunately, if I try to add code to make new window, Tk.Toplevel() ,after the wave.changedetection() call, nothing happens and the main GUI window becomes unresponsive and has to be killed.
There is no way to know when the new created thread (start_thread)completes it's work, so that I can do Tk.Toplevel() there.
How can I do what I require?
class GUI(Tkinter.Tk):
def __init__(self, parent)
Tkinter.Tk.__init__(self, parent)
self.parent = parent
self.initialize()
def initialize(self):
self.button = Tkinter.Button(text = "Start")
self.button.bind('<Button-1>', self.OnButtonClick)
self.button.pack()
self.int = Tkinter.IntVar()
self.pgbar = Tkinter.ProgressBar(variable = self.int, mode = determinate)
def OnButtonClick(self,event):
#this func has been made since I have more buttons.It may seem redundant here
self.button['command'] = self.start_thread()
self.update_idletasks()
def start_thread(self):
self.int_var.set(1)
q = queue.Queue()
self.secondary_thread = threading.Thread(target = self.change)
self.secondary_thread.start()
self.after(50, self.check_queue, q)
def check_queue(self, q):
while True:
try:
x = wave.q.get_nowait()
except queue.Empty :
self.after(50,self.check_queue,q)
break
else:
self.int_var.set(x)
if x == 6:
self.button3['state'] = 'normal'
break
def change(self):
'''The 6 functions of wave.changedetection() change the value of self.int
due to which progress bar progresses.'''
pre, post, aligned, chng = wave.changedetection(self.entry_1.get(),
self.entry_2.get())
if __name__ == '__main__':
gui = GUI(None)
gui.mainloop()
code to update progress bar taken from here (2nd answer,Honest Abe's answer)
You have to be able to differentiate name spaces, i.e. this is in the main window and this is in the Toplevel. I would suggest that you get the Toplevels working first and then decide if you want to add threading or not. The code below is a simple example of creating Toplevels and shows how to place widgets in a specific name space (window in this case). You may or may not want a separate "create a Toplevel" class if there are functions you want to associate with each Toplevel's namespace. Also there are examples on the web on using Tkinter's "after" to update a progressbar. That is a different question so start another thread if you have questions about the progressbar.
try:
import Tkinter as tk ## Python 2.x
except ImportError:
import tkinter as tk ## Python 3.x
from functools import partial
class OpenToplevels():
""" open and close additional Toplevels with a button
"""
def __init__(self):
self.root = tk.Tk()
self.button_ctr=0
## in the "root" namespace *********************
but=tk.Button(self.root, text="Open a Toplevel",
command=self.open_another)
but.grid(row=0, column=0)
tk.Button(self.root, text="Exit Tkinter", bg="red",
command=self.root.quit).grid(row=1, column=0, sticky="we")
self.root.mainloop()
def close_it(self, id):
## destroy the window in this id's namespace ***********
id.destroy()
## id.withdraw()
## id.iconify()
def open_another(self):
self.button_ctr += 1
id = tk.Toplevel(self.root)
id.title("Toplevel #%d" % (self.button_ctr))
## in the "id for this Toplevel" namespace ***********
tk.Button(id, text="Close Toplevel #%d" % (self.button_ctr),
command=partial(self.close_it, id),
bg="orange", width=20).grid(row=1, column=0)
Ot=OpenToplevels()

Button Command with class in Python

I'm trying to get this right but so far no luck. Would appreciate it if someone can help me with it.
import tkinter
class MyGUI:
def __init__(self):
self.main_window = tkinter.Tk()
self.button1 = tkinter.Button(self.main_window,text='Average',command=self.average)
self.button1.pack()
tkinter.mainloop()
def average(self):
self.mini_window = tkinter.Tk()
self.avg_mess = tkinter.Label(self.mini_window,text='Results:')
self.avg_result_var = tkinter.StringVar()
self.avg_result_display = tkinter.Label(self.mini_window,textvariable=self.avg_result_var)
self.avg_mess.pack()
self.avg_result_display.pack()
self.button2 = tkinter.Button(self.mini_window,text='Calculate',command=self.avg_calc)
self.button2.pack()
def avg_calc(self):
self.avg_result = (100+300+80)/3
self.avg_result_var.set(self.avg_result)
gui = MyGUI()
The problem occurs when the Calculate button is clicked but the avg_result_var does not change its value. And hence the avg.result_display remains blank. I suspect there is something wrong with the function call when the button is pressed. I'm using Python 3.x. Thanks.
You're almost doing it correctly, but there are a couple of problems
First, the result never changes because you use the same numbers each time you do a calculation. The result is always the same so it appears that it is not changing.
The second problem is that you're creating two instances of Tk. Tkinter isn't designed to work like that, and it causes problems such as the one you are observing. If you need additional pop-up windows, use Toplevel rather than Tk.
Here's a modified version of your program, though I've added a random number in the computation so you can see it change each time.
import Tkinter as tkinter
import random
class MyGUI:
def __init__(self):
self.main_window = tkinter.Tk()
self.button1 = tkinter.Button(self.main_window,text='Average',command=self.average)
self.button1.pack()
tkinter.mainloop()
def average(self):
self.mini_window = tkinter.Toplevel()
self.avg_mess = tkinter.Label(self.mini_window,text='Results:')
self.avg_result_var = tkinter.StringVar()
self.avg_result_display = tkinter.Label(self.mini_window,textvariable=self.avg_result_var)
self.avg_mess.pack(fill="both")
self.avg_result_display.pack()
self.button2 = tkinter.Button(self.mini_window,text='Calculate',command=self.avg_calc)
self.button2.pack()
def avg_calc(self):
x = random.randint(100,200)
self.avg_result = (100+300+x)/3
print "result:", self.avg_result
self.avg_result_var.set(self.avg_result)
gui = MyGUI()

Categories