i wana delete a text automatically after few seconds of inactivity - python

import tkinter as tk
class App():
def __init__(self):
self.root = tk.Tk()
self.root.config(padx=100, pady=50)
self.root.geometry('800x600')
self.text = tk.Text()
self.entry = tk.Entry()
self.text.place(x=0, y=0)
self.text.after(5000, self.clear_text) # 5000ms
self.root.mainloop()
def clear_text(self):
print ("Le text vient d'etre détruit")
self.text.place_forget()
app = App()
Hello to all,
I am trying to create an Tkinter app where i put some text in a box and if i stop typing for few seconds the text should disappear. I am a beginner, i am stuck!! Thank for giving me an idea!!Yours

Use after and bind Key event to the same method. Use an if statement to check if the event parameter of the method is None, if it is None delete the text.
import tkinter as tk
class App():
def __init__(self):
self.root = tk.Tk()
self.root.config(padx=100, pady=50)
self.root.geometry('800x600')
self.delay = 5000
self.after_id = None
self.text = tk.Text(self.root)
self.text.bind("<Key>", self.clear_text)
self.text.place(x=0, y=0)
self.root.mainloop()
def clear_text(self, event=None):
if not event:
self.text.delete("1.0", "end")
else:
if self.after_id:
self.root.after_cancel(self.after_id)
self.after_id = None
self.after_id = self.root.after(self.delay, self.clear_text)
app = App()

Try this:
import tkinter as tk
from time import perf_counter
class App():
def __init__(self):
self.root = tk.Tk()
self.text = tk.Text(self.root)
self.text.pack()
self.text.bind("<Key>", self.text_entered)
self.text.after(100, self.check_clear_text)
self.time_last_written = perf_counter()
self.root.mainloop()
def text_entered(self, event):
self.time_last_written = perf_counter()
def clear_text(self):
print("Le text vient d'etre détruit")
# Delete all of the text
self.text.delete("0.0", "end")
def check_clear_text(self):
time_diff = perf_counter() - self.time_last_written
print(f"Last key pressed: {time_diff} sec ago.")
if time_diff > 5: # The 5 is in seconds
self.clear_text()
self.text.after(100, self.check_clear_text)
app = App()
It keeps track of the last time you pressed a key and if 5 seconds have passed, it deletes all of the widget's contents using: self.text.delete("0.0", "end")

Related

How to wait for a server response without freezing the tkinter GUI

I am making a tkinter GUI that requests information to a server that takes some time to respond.
I really don't know how to tell tkinter to wait for the response in a clever way so that the window loop doesnt freeze.
What I want to achieve is to make the popup window responsive, and to see the animation of the progressbar.
I don't really know if it helps but I intend to use this GUI on Windows.
Here is my code: (I used time.sleep to simulate sending and recieving from a server)
import tkinter as tk
import tkinter.ttk as ttk
import time
def send_request(data):
# Sends request to server, manages response and returns it
time.sleep(10)
class Window(tk.Toplevel):
def __init__(self, root, *args, **kargs):
super().__init__(root, *args, **kargs)
# Options of the window
self.geometry("500x250")
self.resizable(False, False)
self.grab_set()
# Widgets of the window
self.button = tk.Button(self, text="Send Request", command=self.start_calc)
self.button.pack()
self.bar = ttk.Progressbar(self, orient = "horizontal", mode= "indeterminate")
self.bar.pack(expand=1, fill=tk.X)
def start_calc(self):
# Prepares some data to be send
self.data_to_send = []
# Start bar
self.bar.start()
# Call send request
self.after(10, self.send_request_and_save_results)
def send_request_and_save_results(self):
# Send request with the data_to_send
result = send_request(self.data_to_send)
# Save results
# Close window
self.quit()
self.destroy()
class App:
def __init__(self, root):
self.root = root
self.button = tk.Button(root, text="Open Window", command=self.open_window)
self.button.pack()
def open_window(self):
window = Window(self.root)
window.mainloop()
root = tk.Tk()
root.geometry("600x300")
app = App(root)
root.mainloop()
I came up with this solution:
import tkinter as tk
import tkinter.ttk as ttk
import time
from threading import Thread
def send_request_and_save_results(data, flag):
# This is called in another thread so you shouldn't call any tkinter methods
print("Start sending: ", data)
time.sleep(10)
print("Finished sending")
# Signal that this function is done
flag[0] = True
class Window(tk.Toplevel):
def __init__(self, root, *args, **kargs):
super().__init__(root, *args, **kargs)
# Options of the window
self.geometry("500x250")
self.resizable(False, False)
self.grab_set()
# Widgets of the window
self.button = tk.Button(self, text="Send Request", command=self.start_calc)
self.button.pack()
self.bar = ttk.Progressbar(self, orient="horizontal", mode="indeterminate")
self.bar.pack(expand=1, fill="x")
def start_calc(self):
# Prepares some data to be send
self.data_to_send = [1, 2, 3]
# Start bar
self.bar.start()
# Call send request
self.send_request_and_save_results()
def send_request_and_save_results(self):
# Create a flag that wukk signal if send_request_and_save_results is done
flag = [False]
# Send request with the data_to_send and flag
t1 = Thread(target=send_request_and_save_results,
args=(self.data_to_send, flag))
t1.start()
# A tkinter loop to check if the flag has been set
self.check_flag_close_loop(flag)
def check_flag_close_loop(self, flag):
# if the flag is set, close the window
if flag[0]:
self.close()
# Else call this function again in 100 milliseconds
else:
self.after(100, self.check_flag_close_loop, flag)
def close(self):
# I am pretty sure that one of these is unnecessary but it
# depends on your program
self.quit()
self.destroy()
class App:
def __init__(self, root):
self.root = root
self.button = tk.Button(root, text="Open Window", command=self.open_window)
self.button.pack()
def open_window(self):
window = Window(self.root)
window.mainloop()
root = tk.Tk()
root.geometry("600x300")
app = App(root)
root.mainloop()
Notice how all tkinter calls are in the main thread. This is because sometimes tkinter doesn't play nice with other threads.
All I did was call send_request_and_save_results with a flag that the function sets when it is done. I periodically chech that flag in the check_flag_close_loop method, which is actually a tkinter loop.
The flag is a list with a single bool (the simplest solution). That is because python passes mutable objects by reference and immutable objects by value.
Use threading module to run your code and get the request parallelly
Just call your function like this.
t1 = Thread(target=window.send_request_and_save_results)
t1.start()
Updated Code
import tkinter as tk
import tkinter.ttk as ttk
import time
from threading import Thread
def send_request(data):
# Sends request to server, manages response and returns it
time.sleep(10)
class Window(tk.Toplevel):
def __init__(self, root, *args, **kargs):
super().__init__(root, *args, **kargs)
# Options of the window
self.geometry("500x250")
self.resizable(False, False)
self.grab_set()
# Widgets of the window
self.button = tk.Button(self, text="Send Request", command=self.start_calc)
self.button.pack()
self.bar = ttk.Progressbar(self, orient = "horizontal", mode= "indeterminate")
self.bar.pack(expand=1, fill=tk.X)
def start_calc(self):
# Prepares some data to be send
self.data_to_send = []
# Start bar
self.bar.start()
# Call send request
self.after(10, self.send_request_and_save_results)
def send_request_and_save_results(self):
# Send request with the data_to_send
result = send_request(self.data_to_send)
# Save results
# Close window
self.quit()
self.destroy()
class App:
def __init__(self, root):
self.root = root
self.button = tk.Button(root, text="Open Window", command=self.open_window)
self.button.pack()
def open_window(self):
window = Window(self.root)
t1 = Thread(target=window.send_request_and_save_results)
t1.start()
window.mainloop()
root = tk.Tk()
root.geometry("600x300")
app = App(root)
root.mainloop()
I hope this will help you.
Thanks to #codester_09 suggestion of using threading.Thread I have managed to get it working but I don't really understand why. After many errors this is what I did.
Now I wonder if what I did is safe.
import tkinter as tk
import tkinter.ttk as ttk
import time
from threading import Thread
def send_request_and_save_results(data):
global results
print("Start sending: ", data)
# Sends request to server, manages response and returns it
time.sleep(10)
print("Finished sending")
# Save results
results[0] = "Hello"
results[1] = "Hola"
class Window(tk.Toplevel):
def __init__(self, root, *args, **kargs):
super().__init__(root, *args, **kargs)
# Options of the window
self.geometry("500x250")
self.resizable(False, False)
self.grab_set()
# Widgets of the window
self.button = tk.Button(self, text="Send Request", command=self.start_calc)
self.button.pack()
self.bar = ttk.Progressbar(self, orient = "horizontal", mode= "indeterminate")
self.bar.pack(expand=1, fill=tk.X)
def start_calc(self):
# Prepares some data to be send
self.data_to_send = [1, 2, 3]
# Start bar
self.bar.start()
# Call send request
t1 = Thread(target=self.send_request_and_save_results)
t1.start()
def send_request_and_save_results(self):
# Send request with the data_to_send
send_request_and_save_results(self.data_to_send)
# Close window
self.after(10, self.close)
def close(self):
self.quit()
self.destroy()
class App:
def __init__(self, root):
self.root = root
self.button = tk.Button(root, text="Open Window", command=self.open_window)
self.button.pack()
def open_window(self):
window = Window(self.root)
window.mainloop()
#
root = tk.Tk()
root.geometry("600x300")
app = App(root)
results = [None] * 10
root.mainloop()

update the maximum value of ttk.Progressbar

I am struggling with a seemingly simple problem: I want to be able to update the maximum value of a progress bar in tkinter by manually changing the value of an entry. What happens is that the initial value, 100, does not change. OK, I thought that by invoking set in the method count I would be able to update the maximum value. It didn't work. What is the problem?
import tkinter as tk
from tkinter import ttk
from time import sleep
class Window():
def __init__(self, master):
self.master = master
self.configure()
self.create_widgets()
def configure(self):
self.master.title('Progress bar')
self.master.minsize(height=100, width=500)
def create_widgets(self):
self.progress = tk.DoubleVar()
self.number = tk.StringVar()
self.number.set('100')
self.max = tk.IntVar()
self.max.set(eval(self.number.get()))
b1 = tk.Button(self.master, text='Count!', command=self.count)
b1.pack()
e1 = tk.Entry(self.master, textvariable=self.number, width=5)
e1.pack()
p = ttk.Progressbar(self.master, orient='horizontal', length=200, mode='determinate', variable=self.progress, value=1, maximum=self.max.get())
p.pack()
def count(self):
self.max.set(eval(self.number.get()))
for i in range(eval(self.number.get())):
sleep(0.01)
print(i)
self.progress.set(i)
self.master.update()
def main():
root = tk.Tk()
app = Window(root)
root.mainloop()
main()
You're struggling probably because you've overcomplicated the issue. Why do you use 3 separate Variable classes when you need none? Currently what happens is you create a progress bar with a static maximum of 100, and then you're changing how long to progress in that window by setting the entry's value.
Here's a minimal example that updates the maximum value of progress bar:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
import tkinter.ttk as ttk
except ImportError:
import Tkinter as tk
import ttk
class RestartableProgress(tk.Frame):
def __init__(self, master, *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self.max_input = tk.Entry(self)
self.restart_button = tk.Button(self, text="Restart",
command=self.restart)
self.progressbar = ttk.Progressbar(self)
self.max_input.pack()
self.restart_button.pack()
self.progressbar.pack()
def restart(self):
self.progressbar['value'] = 0
self.progress()
def progress(self):
max_val = self.max_input.get()
if max_val:
self.progressbar['maximum'] = int(max_val)
if self.progressbar['value'] < self.progressbar['maximum']:
self.progressbar['value'] += 1
self.after(10, self.progress)
def main():
root = tk.Tk()
rp = RestartableProgress(root)
rp.pack()
tk.mainloop()
if __name__ == '__main__':
main()

Closing windows of tkinter

I am new to tkinter and would like to do a UI based on the flags shown.
Basically, i would like to close one windows and open another window with the present state or delete the text and show another text with the present state.
class App(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.start()
def callback(self):
self.root.quit()
def run(self):
self.root = tk.Tk()
self.root.protocol("WM_DELETE_WINDOW", self.callback)
label = tk.Label(self.root, text="Start Initialization")
label.pack()
self.root.mainloop()
class QQQ:
def quit(self):
self.delete(1.0,END)
class Appo(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.start()
def callback(self):
self.root.quit()
def run(self):
self.root = tk.Tk()
self.root.protocol("WM_DELETE_WINDOW", self.callback)
label = tk.Label(self.root, text="Initialization ended")
label.pack()
self.root.mainloop()
for i in range(100000):
time.sleep(0.5)
print(i)
if(i==1):
app = App()
time.sleep(1)
qqq=QQQ()
if(i==10):
app=Appo()
If all you want to do is change the text of the label, then use the config method on the label. The App, Appo, and QQQ classes, as well as the for loop can be combined into a single class as:
import Tkinter as tk #Python 2
#import tkinter as tk #Python 3
import threading
import time
class App(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.root = tk.Tk()
self.start()
def callback(self):
self.root.quit()
def run(self):
self.root.protocol("WM_DELETE_WINDOW", self.callback)
label = tk.Label(self.root, text="Initial Text") # You can use text=""
label.pack()
for i in range(100000):
time.sleep(0.5)
print (i)
if i == 1:
label.config(text="Start Initialization")
time.sleep(1)
label.config(text="")
if i == 10:
label.config(text="Initialization ended")
#self.root.mainloop()
app = App()
app.root.mainloop()
It might be better to use the after method of tkinter for time delays instead of time.sleep()

Is it possible to keep the same window for every class in python's tkinter?

I am trying to create a program in tkinter which allows me to open an initial window then to keep it throughout all classes used. For example, if I was to create a button in a window then when I click this button, it would exuecute a method that destroys the widget, and then executes a new class that builds a new screen within the same window, such as text opposed to a button.
from tkinter import *
class Window1:
def __init__(self, master):
self.master = master
self.label = Button(self.master, text = "Example", command = self.load_new)
self.label.pack()
def load_new(self):
self.label.destroy()
## Code to execute next class
class Window2:
def __init__(self, master):
self.master = master
self.label = Label(self.master, text = "Example")
self.label.pack()
def main():
root = Tk()
run = Window1(root)
root.mainloop()
if __name__ == '__main__':
main()
I understand this is less practical, but I am curious. Cheers.
Tk() creates main window and variable root gives you access to this window. You can use root as argument for Window2 and you will have access to main window inside Window2
from tkinter import *
class Window1:
def __init__(self, master):
# keep `root` in `self.master`
self.master = master
self.label = Button(self.master, text="Example", command=self.load_new)
self.label.pack()
def load_new(self):
self.label.destroy()
# use `root` with another class
self.another = Window2(self.master)
class Window2:
def __init__(self, master):
# keep `root` in `self.master`
self.master = master
self.label = Label(self.master, text="Example")
self.label.pack()
root = Tk()
run = Window1(root)
root.mainloop()
--
Probably nobody use another class to create Label in place of Button ;)
--
EDIT: In this example using names Window1 and Windows2 is misleading because there is only one window and two classes which use this window. I would rather use names FirstOwner, SecondOwner
Everything is implemented in one Tk class and in this case there always is only one window.
from tkinter import *
from tkinter import ttk
class MainWindow():
def __init__(self, mainWidget):
self.main_frame = ttk.Frame(mainWidget, width=300, height=150, padding=(0, 0, 0, 0))
self.main_frame.grid(row=0, column=0)
self.some_kind_of_controler = 0
self.main_gui()
def main_gui(self):
root.title('My Window')
self.main_label_1 = ttk.Label(self.main_frame, text='Object_1')
self.main_label_1.grid(row=0, column=0)
self.main_label_2 = ttk.Label(self.main_frame, text='Object_2')
self.main_label_2.grid(row=1, column=0)
self.main_label_3 = ttk.Label(self.main_frame, text='Object_3')
self.main_label_3.grid(row=2, column=0)
self.setings_button = ttk.Button(self.main_frame, text='Setings')
self.setings_button.grid(row=0, column=1)
self.setings_button.bind('<Button-1>', self.setings_gui)
self.gui_elements = [self.main_label_1,
self.main_label_2,
self.main_label_3,
self.setings_button]
def setings_gui(self, event):
self.gui_elements_remove(self.gui_elements)
root.title('Setings')
self.main_label_1 = ttk.Label(self.main_frame, text='Object_1')
self.main_label_1.grid(row=2, column=0)
self.main_menu_button = ttk.Button(self.main_frame, text='Main menu')
self.main_menu_button.grid(row=0, column=1)
self.main_menu_button.bind('<Button-1>', self.back_to_main)
self.some_kind_of_controler = 1
self.gui_elements = [self.main_label_1,
self.main_menu_button]
def back_to_main(self, event):
if self.some_kind_of_controler == 1:
self.gui_elements_remove(self.gui_elements)
else:
pass
self.main_gui()
def gui_elements_remove(self, elements):
for element in elements:
element.destroy()
def main():
global root
root = Tk()
root.geometry('300x150+50+50')
window = MainWindow(root)
root.mainloop()
if __name__ == '__main__':
main()

Execute Button Method

I am trying to execute a Tkinter ttk Button method while the button is pressed, meaning I want the method to keep executing when the button is pressed and stop when I release it, but i can't quite figure it out. Here is the code.
from tkinter import *
from tkinter import ttk
class stuff (object):
def __init__(self, master):
master.title("Grid Master")
master.frame_1 = ttk.Frame(master)
master.frame_1.pack()
master.configure(background = "#FFFFFF")
self.button = ttk.Button(master, text = 'Press', command = self.callback)
self.button.pack()
def callback(self):
print ("Hello world")
def main():
root = Tk()
loop = stuff(root)
root.mainloop()
if __name__ == '__main__':
main()
You can see in the code that the method only prints "Hello world" and I want this function to execute and keep going, printing Hello world until I release the button.
You can use root.after() to repeatedly schedule a job to perform the required task. Note that I have changed the button event to activate when the button is pressed, and to terminate the "after" job when the button is released.
try:
from tkinter import *
from tkinter import ttk
except ImportError:
# Python 2, probably
from Tkinter import *
import ttk
class stuff (object):
def __init__(self, master):
self._master = master
master.title("Grid Master")
master.frame_1 = ttk.Frame(master)
master.frame_1.pack()
master.configure(background = "#FFFFFF")
self.button = ttk.Button(master, text = 'Press')
self.button.bind("<Button-1>", self.button_pressed)
self.button.bind("<ButtonRelease-1>", self.button_released)
self.button.pack()
self.hello_world_frequency = 1 # milliseconds
def hello_world(self):
print ("Hello world")
self._job = self._master.after(self.hello_world_frequency, self.hello_world)
def button_pressed(self, event):
print ("Button down")
self.hello_world()
def button_released(self, event):
print ("Button released")
self._master.after_cancel(self._job)
def main():
root = Tk()
loop = stuff(root)
root.mainloop()
if __name__ == '__main__':
main()
You can make a custom repeating ttk button class that inherits from ttk.Button but adds basic repeating functionality.
Try this. you can use it like
self.button = RepeatButton(master, text='Press', command=self.callback)
and you can set the repeatdelay and repeatinterval arguments, which default to 300 and 100.
class RepeatButton(ttk.Button):
def __init__(self, *args, **kwargs):
self.callback = kwargs.pop('command', None)
self.repeatinterval = kwargs.pop('repeatinterval', 100)
self.repeatdelay = kwargs.pop('repeatdelay', 300)
ttk.Button.__init__(self, *args, **kwargs)
if self.callback:
self.bind('<ButtonPress-1>', self.click)
self.bind('<ButtonRelease-1>', self.release)
def click(self, event=None):
self.callback()
self.after_id = self.after(self.repeatdelay, self.repeat)
def repeat(self):
self.callback()
self.after_id = self.after(self.repeatinterval, self.repeat)
def release(self, event=None):
self.after_cancel(self.after_id)

Categories