I have a label that is updated with status information. What I would like to do is clear its contents after 10 seconds if the status doesn't change. For example I set the label to to "step one" then after 10 seconds it gets cleared "". Now if before the 10 seconds expires I set the label to "Step two" it will update and reset the the 10 second timer.
EDIT:
OK, I never could get this to work right. I made a small app to test that has 3 buttons, two of which just change the text of a label. What I want to happen is the following.
Update the label with the text (Push button 1, or 2)
start a 2 second timer. (after 2 seconds clear the label)
if button 1 or 2 is pressed before 2 seconds is up update label and reset the timer.
what I'm seeing is the timer is holding up things. If I press button 1 the label is updated and the timer starts. But I can't press either buttons again until the timer is finished
import tkinter as tk
class Window(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.master = master
self.myafter = None
# widget can take all window
self.pack(fill= tk.BOTH, expand=1)
# create button, link it to clickExitButton()
startButton = tk.Button(self, text="MSG_1", command=self.clickButton1 )
startButton.place(x=10,y=10,width=50,height=50)
stopButton = tk.Button(self, text="MSG_2", command=self.clickButton2)
stopButton.place(x=100,y=10,width=50,height=50)
exitButton = tk.Button(self, text="EXIT", command=self.clickExitButton)
exitButton.place(x=190,y=10,width=50,height=50)
lblstatus = tk.Label(self, text = "Status:")
lblstatus.place(x=10, y=150)
self.lblStatus = tk.Label(self, text = "READY")
self.lblStatus.place(x=75,y=150)
def clickButton1(self):
if(self.myafter != None):
self.lblStatus.after_cancel(self.myafter)
app.UpdateMessage("Set message 1")
def clickButton2(self):
if(self.myafter != None):
self.lblStatus.after_cancel(self.myafter)
app.UpdateMessage("Set message 2")
def ClearStatus(self):
self.lblStatus["text"] = ""
def UpdateMessage(self,msg= "test"):
self.lblStatus["text"] = msg
self.lblStatus.update()
if(self.myafter == None):
self.myafter = self.lblStatus.after(2000,self.ClearStatus())
def clickExitButton(self):
root.quit()
root = tk.Tk()
app = Window(root)
root.wm_title("Label Clear Test")
root.geometry("300x200")
root.mainloop()
Thanks
PBSnake
Use the threading module for this.
threading.Timer is an object that will run parallel to your GUI so it
won't freeze it.
This code below should do the trick:
import tkinter as tk
import threading
def myLoop():
var.set(var.get() + 1) #Increment the value
threading.Timer(1, myLoop).start()
window = tk.Tk()
var = tk.IntVar()
var.set(1)
button = tk.Button(window, text = 'Threading', command = lambda: threading.Timer(1, myLoop).start())
button.pack()
label = tk.Label(window, textvariable = var)
label.pack()
window.mainloop()
Related
I'm a python newbe and am trying to come to grips with threaded parts in GUI apps I downloaded the following example that names the progress box "tk" this seems to be tied up with self and parent so i guess i need to find a way to make the test string "progress" go into one of the places where these names appear. Being a python newbe i'm not clever enough to know the correct and most compact way to do this given the compact way this entire example is constructed.
"""
An examnple of the use of threading to allow simultaneous operations in a
tkinter gui (which is locked to a single thread)
"""
import threading
import tkinter as tk
from tkinter import ttk
class Progress():
""" threaded progress bar for tkinter gui """
def __init__(self,parent, row, column, columnspan):
self.maximum = 100
self.interval = 10
self.progressbar = ttk.Progressbar(parent, orient=tk.HORIZONTAL,
mode="indeterminate",
maximum=self.maximum)
self.progressbar.grid(row=row, column=column,
columnspan=columnspan, sticky="we")
self.thread = threading.Thread()
self.thread.__init__(self.progressbar.start(self.interval))
self.thread.start()
def pb_stop(self):
""" stops the progress bar """
if not self.thread.is_alive():
VALUE = self.progressbar["value"]
self.progressbar.stop()
self.progressbar["value"] = VALUE
def pb_start(self):
""" starts the progress bar """
if not self.thread.is_alive():
VALUE = self.progressbar["value"]
self.progressbar.configure(mode="indeterminate",
maximum=self.maximum,
value=VALUE)
self.progressbar.start(self.interval)
def pb_clear(self):
""" stops the progress bar """
if not self.thread.is_alive():
self.progressbar.stop()
self.progressbar.configure(mode="determinate", value=0)
def pb_complete(self):
""" stops the progress bar and fills it """
if not self.thread.is_alive():
self.progressbar.stop()
self.progressbar.configure(mode="determinate",
maximum=self.maximum,
value=self.maximum)
def printmsg():
""" prints a message in a seperate thread to tkinter """
print("proof a seperate thread is running")
class AppGUI(tk.Frame):
""" class to define tkinter GUI"""
def __init__(self, parent,):
tk.Frame.__init__(self, master=parent)
prog_bar = Progress(parent, row=0, column=0, columnspan=2)
# Button 1
start_button = ttk.Button(parent, text="start",
command=prog_bar.pb_start)
start_button.grid(row=1, column=0)
# Button 2
stop_button = ttk.Button(parent, text="stop",
command=prog_bar.pb_stop)
stop_button.grid(row=1, column=1)
# Button 3
complete_button = ttk.Button(parent, text="complete",
command=prog_bar.pb_complete)
complete_button.grid(row=2, column=0)
# Button 4
clear_button = ttk.Button(parent, text="clear",
command=prog_bar.pb_clear)
clear_button.grid(row=2, column=1)
# Button 5
test_print_button = ttk.Button(parent, text="thread test",
command=printmsg)
test_print_button.grid(row=3, column=0, columnspan=2, sticky="we")
ROOT = tk.Tk()
APP = AppGUI(ROOT)
ROOT.mainloop()
To change the title bar... just use the title method on the root widget. At the base of your script.
ROOT = tk.Tk()
ROOT.title("Anything you want it to be.")
APP = AppGUI(ROOT)
ROOT.mainloop()
I'm having difficulty comprehending how progress bars work in the context of a loop. My example is as follows:
import time
from tkinter import ttk,Tk, Label, Button,Label,DoubleVar
MAX = 4
root = Tk()
root.title("My Progressbar")
root.geometry('500x500')
theLabel = Label(root, text="Progress")
theLabel.pack()
progress_var = DoubleVar()
progress_var.set(0)
progressbar = ttk.Progressbar(root, variable=progress_var,
length=400,maximum=MAX,mode='determinate')
progressbar.pack()
for i in range(MAX+1):
print(i) #Where I will eventually do all my work. Here I print i and pause .5 sec
time.sleep(.5)
progress_var.set(i)
root.update_idletasks()
# Add quit button
def _quit():
root.quit()
root.destroy()
quit_button = Button(master=root, text="Quit", bg='lightgray',command=_quit)
quit_button.pack()
root.mainloop()
I'm missing something obvious as the bar does not appear until after the loop is complete. Also, is it possible to indicate the % complete somewhere on the bar?
You do the "work" before the flow of execution even has a chance to reach root.mainloop. You'll want to simulate doing the work after starting the mainloop. Maybe you could add a button which, upon being clicked, simulates the work? Try this:
# ...
def do_work():
for i in range(MAX+1):
print(i) #Where I will eventually do all my work. Here I print i and pause .5 sec
time.sleep(.5)
progress_var.set(i)
root.update_idletasks()
# ...
do_work_button = Button(master=root, text="Do Work", command=do_work)
do_work_button.pack()
root.mainloop()
If you only just want to increase the value in the progressbar each 0.5 second.Try this:
import time
from tkinter import ttk,Tk, Label, Button,Label,DoubleVar
MAX = 4
root = Tk()
root.title("My Progressbar")
root.geometry('500x500')
theLabel = Label(root, text="Progress")
theLabel.pack()
progress_var = DoubleVar()
progress_var.set(0)
progressbar = ttk.Progressbar(root, variable=progress_var,
length=400,maximum=MAX,mode='determinate')
progressbar.pack()
def add():
if progress_var.get() < MAX:
print(progress_var.get())
progress_var.set(progress_var.get()+1)
root.after(500, add)
# Add quit button
def _quit():
root.quit()
root.destroy()
quit_button = Button(master=root, text="Quit", bg='lightgray',command=_quit)
quit_button.pack()
root.after(500, add)
root.mainloop()
If it is a specific work, you need to create thread or process.
I have tried all the other posts on this topic but none of them have worked for me...
Here is my code:
from tkinter import *
window=Tk()
window.geometry('600x400')
window.title('hello world')
def wrong():
root=Tk()
text=Text(root)
text.insert(INSERT,"WRONG!, you stupid idiot!!!!")
text.pack()
def right():
root=Tk()
text=Text(root)
text.insert(INSERT,"CORRECT, good job!")
text.pack()
def reset():
hide_widgets()
class UIProgram():
def setupUI(self):
buttonlist=[]
button= Button(window,text='Sanjam',command=wrong).pack()
button2=Button(window,text='Sunny the Bunny',command=wrong).pack()
button3= Button(window, text='Sunjum',command=right).pack()
button4= Button(window, text='bob',command=wrong).pack()
button5= Button(window, text='next',command=reset)
button5.pack()
self.label=Label(window)
self.label.pack()
window.mainloop()
program= UIProgram()
program.setupUI()
I am aware of pack_forget() and tried it, but it keeps giving me an error. Also, is it possible to make a command(like the 'reset' one I have) and use that in the command for a clear screen button. Please help, I am new to Tkinter and don't know much about these things..
Thanks
Example code
I'm tired to describe the same mistakes hundreds of times - some comments in code.
import tkinter as tk
# --- classes ---
class UIProgram():
def __init__(self, master):
self.master = master # use to add elements directly to main window
self.buttons = [] # keep buttons to change text
# frame to group buttons and easily remove all buttons (except `Next`)
self.frame = tk.Frame(self. master)
self.frame.pack()
# group button in frame
button = tk.Button(self.frame, text='Sanjam', command=self.wrong)
button.pack()
self.buttons.append(button)
button = tk.Button(self.frame, text='Sunny the Bunny', command=self.wrong)
button.pack()
self.buttons.append(button)
button = tk.Button(self.frame, text='Sunjum', command=self.right)
button.pack()
self.buttons.append(button)
button = tk.Button(self.frame, text='bob', command=self.wrong)
button.pack()
self.buttons.append(button)
# button outside frame
button_next = tk.Button(self.master, text='Next >>', command=self.reset)
button_next.pack()
self.label = tk.Label(self.frame)
self.label.pack()
def wrong(self):
# create second window with message and closing button
win = tk.Toplevel()
tk.Label(win, text="WRONG!, you stupid idiot!!!!").pack()
tk.Button(win, text='close', command=win.destroy).pack()
def right(self):
# create second window with message and closing button
win = tk.Toplevel()
tk.Label(win, text="CORRECT, good job!").pack()
tk.Button(win, text='close', command=win.destroy).pack()
def reset(self):
# remove frame with all buttons
self.frame.pack_forget()
tk.Label(self.master, text="frame removed").pack()
# or only remove text in labels
#for button in self.buttons:
# button['text'] = '-- place for new text --'
# --- main ---
root = tk.Tk()
root.geometry('600x400')
root.title('hello world')
program = UIProgram(root)
root.mainloop()
BTW: if you do var = Widget(...).pack() then you assign None to var because pack()/grid()/place() return None. You have to do it in two lines
var = Widget(...)
var.pack()
or in one line if you don't need var
Widget(...).pack()
i am trying to achieve this behaviour: When user clicks the button for a shorter time the program runs the first method, which lets say adds one. However, when user keeps the button pressed for a longer time the program runs the second method, which opens a dialog window. The only solution which came to my mind was using time thread.
The problem is that the program is acting really strangely.Sometimes (not always that's also weird) when the dialog pops up my computer kind of freezez(not exactly, the processes are running). I am able to move with my mouse but I am not able to click or write anything with my keyboard.
Here is a code:
from Tkinter import *
import threading
import os
class Dialog(Toplevel):
def __init__(self, parent, title = None):
Toplevel.__init__(self, parent)
self.transient(parent)
if title:
self.title(title)
self.parent = parent
self.result = None
body = Frame(self)
self.initial_focus = self.body(body)
body.pack(padx=5, pady=5)
#self.buttonbox()
self.grab_set()
if not self.initial_focus:
self.initial_focus = self
self.protocol("WM_DELETE_WINDOW", self.cancel)
self.geometry("+%d+%d" % (parent.winfo_rootx()+50,
parent.winfo_rooty()+50))
self.initial_focus.focus_set()
self.wait_window(self)
#
# construction hooks
def body(self, master):
# create dialog body. return widget that should have
# initial focus. this method should be overridden
pass
def buttonbox(self):
# add standard button box. override if you don't want the
# standard buttons
box = Frame(self)
w = Button(box, text="OK", width=10, command=self.ok, default=ACTIVE)
w.pack(side=LEFT, padx=5, pady=5)
w = Button(box, text="Cancel", width=10, command=self.cancel)
w.pack(side=LEFT, padx=5, pady=5)
self.bind("<Return>", self.ok)
self.bind("<Escape>", self.cancel)
box.pack()
#
# standard button semantics
def ok(self, event=None):
if not self.validate():
self.initial_focus.focus_set() # put focus back
return
self.withdraw()
self.update_idletasks()
self.apply()
self.cancel()
def cancel(self, event=None):
# put focus back to the parent window
self.parent.focus_set()
self.destroy()
#
# command hooks
def validate(self):
return 1 # override
def apply(self):
pass # override
class MyDialog(Dialog):
def body(self, master):
master.grid_columnconfigure(0, weight=1)
self.e1 = Entry(master)
self.e1.grid(column=0,row=0,columnspan = 4)
self.buttons = {}
self.buttons['previous'] = Button(master,text =u'/')
self.buttons['previous'].grid(column=0,row =1,columnspan = 2)
self.buttons['next'] = Button(master,text =u'*')
self.buttons['next'].grid(column=2,row =1,columnspan = 2)
self.buttons['minus'] = Button(master,text =u'-')
self.buttons['minus'].grid(column=3,row =2)
self.buttons['plus'] = Button(master,text =u'+')
self.buttons['plus'].grid(column=3,row =3)
self.buttons['enter'] = Button(master,text =u'Enter')
self.buttons['enter'].grid(column=3,row =4,rowspan = 2)
self.buttons['zero'] = Button(master,text =u'0')
self.buttons['zero'].grid(column=0,row =5)
self.buttons['del'] = Button(master,text =u'←')
self.buttons['del'].grid(column=1,row =5)
rows = range(4,1,-1)
columns = range(0,3)
i = 1
for row in rows:
for column in columns:
Button(master,text = i).grid(row= row,column = column)
i+=1
return self.e1 # initial focus
def leftClick():
print 'add one'
def longerClick(): #if the timer ends this will run
d = MyDialog(master) #create dialog
print 'longerClick'
def handleButtonPress(event):
global t
t = threading.Timer(0.8,longerClick) #setTimer
t.start()
print t
def handleButtonRelease(event): #here is a problem probably
global t
t.cancel()
leftClick()
master = Tk()
t = None
master.geometry('{}x{}'.format(300, 300))
button = Button(master, text = 'Stlac')
button.pack()
button.bind('<ButtonPress-1>',handleButtonPress)
button.bind('<ButtonRelease-1>',handleButtonRelease)
mainloop()
So when button is pressed I setup timer. When user releases the button before timer ends, the timer is canceled.When not, longerClick method runs.
If you know where the problem is I would really aprecciate your help. Also if you know a better way how to detect longer button press that would also help me. I have read that it is possible in PYQT although I have to use Tkinter.
I would like for a cant afford label to appear then after a second disappear when i push a button to buy something. It seems like the time.sleep(1) is making it not work properly. This is done on python tkinter.
def buttonpressed():
Label.place(x = 500, y = 500 #where i want the label to appear
time.sleep(1)
Label.place(x = 10000, y = 10000) #moving it away where i wont be able to see it
You can't use sleep because it stops mainloop
You can use root.after(time_in_milliseconds, function_name) to run function
Example
import tkinter as tk
def button_pressed():
# put text
label['text'] = "Hello World!"
# run clear_label after 2000ms (2s)
root.after(2000, clear_label)
def clear_label():
# remove text
label['text'] = ""
root = tk.Tk()
label = tk.Label(root) # empty label for text
label.pack()
button = tk.Button(root, text="Press Button", command=button_pressed)
button.pack()
root.mainloop()
If you have to create and remove label then use label.destroy()
import tkinter as tk
def button_pressed():
label = tk.Label(root, text="Hello World!")
label.pack()
root.after(2000, destroy_widget, label) # label as argument for destroy_widget
def destroy_widget(widget):
widget.destroy()
root = tk.Tk()
button = tk.Button(root, text="Press Button", command=button_pressed)
button.pack()
root.mainloop()
And shorter version without destroy_widget
import tkinter as tk
def button_pressed():
label = tk.Label(root, text="Hello World!")
label.pack()
root.after(2000, label.destroy)
root = tk.Tk()
button = tk.Button(root, text="Press Button", command=button_pressed)
button.pack()
root.mainloop()
Press button many times to see many labels which disappear after 2s.
You can use after() to set up a callback after a specified interval. In the callback function clear the label with pack_forget() (or grid_forget() if you're using grid). This is better than setting the label's text attribute to an empty string because that causes widgets to be resized, which might not be what you want. Here's an example:
import Tkinter as tk
class App():
def __init__(self):
self.root = tk.Tk()
self.label = tk.Label(text='I am a label')
self.label.place(x=0, y=0)
self.label.after(1000, self.clear_label) # 1000ms
self.root.mainloop()
def clear_label(self):
print "clear_label"
self.label.place_forget()
app=App()
Another option is to use self.label.destroy() to destroy the widget, however, pack_forget() allows you to display the label again by calling pack() on the widget again.
# I made it through calling 2 functions:
from tkinter import *
root = Tk()
root.geometry('400x200') # width by height
def one_text():
label['text'] = "Look around"
root.after(1000, another_text)
def another_text():
label['text'] = "and smile"
root.after(1000, one_text)
label= Label(root ,font=('Helvetica', 14), fg='black', bg='white')
label.pack()
one_text()
root.mainloop()