messagebox after progress bar value is 100% - python-3.x

I made a progressbar but i want my tkinter program to show a messagebox when the value is 100%, can someone help me?
from tkinter import *
import time
from tkinter import ttk
from tkinter import messagebox
def start_bar():
progress_bar.start(100)
progress_bar.after(100,check)
def check():
if progress_bar['value'] == 100:
messagebox.showinfo("Completion Message", "Progress bar is complete.")
root = Tk()
root.geometry("600x400")
root.title("progressbar")
progress_bar = ttk.Progressbar(root,orient=HORIZONTAL,mode="determinate",length=300)
progress_bar.pack(pady=20)
Button(root,text="start", command=start_bar).pack(pady=10)
root.mainloop()
i put my code in so you can see what i tried.

Add another call to progress_bar.after(100, check) inside the check() function so it calls itself periodically and polls the value of the progress bar
def check():
check_poll = progress_bar.after(100, check) # poll the progress bar 10x/sec
if progress_bar['value'] >= 99:
progress_bar.after_cancel(check_poll) # stop polling
progress_bar.stop() # stop incrementing the progress bar
messagebox.showinfo("Completion Message", "Progress bar is complete.")
EDIT - after testing, it looks like the bar never actually hits 100, so you'll want to break at >= 99 instead. Or alternatively, you could use either if not progress_bar['value'] or (perhaps more "correctly/Pythonically") if progress_bar['value'] == 0 which will trigger when the bar loops around again to 0.

Related

Why is Python Skipping Over Tkinter Code?

I'm in Python 3.x using Tkinter to make a button that changes a boolean variable's value from true to false then a if statement to check if that value is false. Here is my code for that:
import tkinter
import time
x = True
top = tkinter.Tk()
def helloCallBack():
x = False
print (x)
B = tkinter.Button(top, text ="Hello", command = helloCallBack)
B.pack()
if x == False:
print ('Hello')
top.mainloop()
This unfortunately didn't work, so I replaced the if statement with a time.sleep(10) and then print (x) so I would have enough time to press the button like so:
import tkinter
import time
x = True
top = tkinter.Tk()
def helloCallBack():
x = False
print (x)
B = tkinter.Button(top, text ="Hello", command = helloCallBack)
B.pack()
time.sleep(10)
print (x)
top.mainloop()
The issue with this is it skips over all of the Tkinter code and goes to the time.sleep(10), prints the value of x then brings everything from Tkinter up after. Why is it doing this?
The reason you see that the code does sleep() and then prints True before the tkinter windows opens is due to how the mainloop() works in tkinter.
sleep() is useful in python however due to tkinter's single threaded nature all sleep() can due is block the mainloop until it has completed sleeping. Tkinter has its own method to work around this issue called after() and you don't need it here but it is very useful for timing things in tkinter.
Tkinter does its updates in the mainloop and you have a sleep() command that occurs before your mainloop as well as a print command before the mainloop. Both of those things must finish execution before the mainloop is reached for the first time thus not allowing tkinter to start until they are complete.
What you want to do is to place this print statement into your function. As well as a few other quality of life clean ups.
See this example:
import tkinter as tk
top = tk.Tk()
x = True
def hello_call_back():
global x
x = False
if not x:
print('Hello')
tk.Button(top, text="Hello", command=hello_call_back).pack()
top.mainloop()
However in this case I think the simplest form of the code should look like this:
import tkinter as tk
top = tk.Tk()
def hello_call_back():
print('Hello')
tk.Button(top, text="Hello", command=hello_call_back).pack()
top.mainloop()
As your current use of x is redundant to the goal.

how to make the python program light, which is written to print the text on screen directly without window?

From last few days, I have been working on a program which one part is to show the text directly on the window screen, and also update/change them as per requirement. I have completed this part with Tkinter module in python.
The problem is whenever I run the program it behaves like a heavy program due to which other processes become slow. Also If I tried to do some other process in parallel, the background of the text becomes black, which is absolutely undesirable.as the shown in image
I also want to show some dynamic symbol like loading but the use of two Tkinter widget make it slower. if possible please make it more modular and light.
from tkinter import *
import win32api, win32con, pywintypes
from time import sleep
f=Tk()
var = StringVar()
var.set(' ')
f =Label(textvariable = var, font=('Cooper','60'), fg='blue', bg='white')
f.master.overrideredirect(True)
f.master.geometry("+900+200")
f.master.lift()
f.master.wm_attributes("-topmost", True)
f.master.wm_attributes("-disabled", True)
f.master.wm_attributes("-transparentcolor", "white")
f.pack()
for i in range(10):
sleep(5) # Need this to slow the changes down
var.set(u'[ A ]' if i%2 else u'[ B ]')
f.update_idletasks()
also, want to ask can we do it without using the Tkinter module.so it becomes more light and modular. and dependency will be less.
Here is a code which makes your code responsive and also don't use your cpu too much.
from tkinter import *
import win32api, win32con, pywintypes
import time
f=Tk()
var = StringVar()
var.set(' ')
f =Label(textvariable = var, font=('Cooper','60'), fg='blue', bg='white',bd=0)
f.master.overrideredirect(True)
f.master.geometry("+900+200")
f.master.lift()
f.master.wm_attributes("-topmost", True)
f.master.wm_attributes("-disabled", True)
f.master.wm_attributes("-transparentcolor", "white")
f.pack()
for i in range(10):
f.update()
t = time.time()
while time.time() - t < 5:
f.update()
var.set(u'[ A ]' if i%2 else u'[ B ]')
f.update_idletasks()
f.update()
Here is an image of task manager. Its taking only 15 MB memory and no cpu:

How to create GUI objects one by one with Tkinter

I like to create an object per second and make the show up one by one. However, the code below wait for 3 seconds and show them all at the same time.
from tkinter import *
import time
def create():
for i in range(3):
r4=Radiobutton(root, text="Option 1"+str(i), value=1)
r4.pack( anchor = W )
time.sleep(1)
root = Tk()
create()
root.mainloop()
Your code, as is, creates a one object per second as you desired it, but this objects need to be shown, and they're shown when code flow reaches the mainloop. Hence, for observer, it looks like there're no objects at all after one second.
Of course, you can use sleep and update, but beware - sleeping leads to unresponsive window, so it's OK option (to be honest - not OK at all), if your application isn't drawn and you're outside of mainloop, but if it's not - prepare for a "frozen" window, because GUI can redraw himself only in mainloop (an event loop, you can reach it with update as well) and sleep blocks this behaviour.
But there's a good alternative, the after method, take a look on it!
And there's a snippet, so you can see the difference:
try:
import tkinter as tk
except ImportError:
import Tkinter as tk
import time
def create_with_sleep():
for _ in range(3):
tk.Radiobutton(frame_for_sleep, text="Sleep Option").pack(anchor='w')
time.sleep(int(time_entry.get()))
root.update()
def create_with_after(times=3):
if times != 0:
tk.Radiobutton(frame_for_after, text="After Option").pack(anchor='w')
times -= 1
root.after(int(time_entry.get()) * 1000, lambda: create_with_after(times))
root = tk.Tk()
test_yard_frame = tk.Frame(root)
frame_for_sleep = tk.Frame(test_yard_frame)
frame_for_after = tk.Frame(test_yard_frame)
test_yard_frame.pack()
frame_for_sleep.pack(side='left')
frame_for_after.pack(side='left')
button_frame = tk.Frame(root)
button_for_sleep = tk.Button(button_frame, text='Create 3 radiobuttons with sleep+update', command=create_with_sleep)
button_for_after = tk.Button(button_frame, text='Create 3 radiobuttons with after', command=create_with_after)
button_frame.pack()
button_for_sleep.pack(side='left')
button_for_after.pack(side='left')
time_label = tk.Label(root, text='Time delay in seconds:')
time_label.pack(fill='x')
time_entry = tk.Entry(root)
time_entry.insert(0, 1)
time_entry.pack(fill='x')
root.mainloop()
With 1 seconds delay there's no much difference, but you can try to increase delay to understand why after option is preferable in general.
You can use update to call a refresh on your objects.
In use, you would have to add the line root.update() in your for loop.

Timed Availability of controls in tkinter GUI

I'm working on a program that will stop users from changing a label after a random amount of time. There are two buttons, start and next, when the user presses start the start button is destroyed but is supposed to come back after a randomly selected amount of time. I tried to have the start button trigger a flag that starts a timer. When the timer reaches a certain value (count_to+1) the flag should go to zero, the start button should reappear, and the label should read end. The flag never seems to switch and the timer never initiates though. Can anyone tell me what I did wrong? and maybe point me towards a solution? Hear is the code:
import sys
from tkinter import *
import random
import time
mGui = Tk()
mGui.geometry('450x450+200+200')
mGui.title('Letters')
stored = ['A', 'b', 'c', 'd']
count_down = [10,20,30,40,50,60]
global count_to
global countFlag
count_to = IntVar()
countFlag = 0
Sec = 0
def run_counter():
count_to = random.choice(count_down)
while countFlag == 1:
Sec+=1
print(sec)
if Sec == count_to+1:
countFlag = 0
newbutton.destroy()
startbutton.grid(row=2,column=1)
phrase.configure(text='End')
return
def change_phrase():
fish = StringVar()
fish = random.choice(stored)
stored.remove(fish)
phrase.configure(text=fish)
#to help with debug
print(countFlag)
print(Sec)
print(count_to)
return
def start_count():
countFlag = True
count_to = random.choice(count_down)
print(countFlag)
startbutton.destroy()
run_counter
return
phrase = Label(mGui,text='Letter',fg='red',bg='blue')
phrase.grid(row=0,column=0, sticky=S,columnspan=2)
startbutton =Button(mGui, text='start',fg='black',bg='green',command=start_count)
startbutton.grid(row=2,column=1)
newbutton = Button(mGui,text='NEXT',fg='black',bg='red',command=change_phrase)
newbutton.grid(row=2,column=0)
#mEntry = Entry(mGui,textvariable=ment)
#mEntry.grid(row=3,column=0)
mGui.mainloop()
Tkinter programming becomes much less messy and confusing once you learn to use classes. Use Tkinter's after() method to call a function every "x" amount of time until the allotted time has elapsed.
import random
import sys
if sys.version_info[0] < 3:
import Tkinter as tk ## Python 2.x
else:
import tkinter as tk ## Python 3.x
class ButtonDisappear():
def __init__(self, root):
self.root=root
self.startbutton=tk.Button(root, text='disappear', fg='black',
bg='green', command=self.disappear)
self.startbutton.grid(row=2,column=1)
self.lab=tk.Label(self.root, text="", bg="lightblue")
def disappear(self):
## remove button
self.startbutton.grid_forget()
## grid label for time
self.lab.grid(row=0, column=0)
## "random" number
self.stop_time=random.choice([1, 2, 3, 4, 5])*1000
self.elapsed=0
self.root.after(100, self.time_it)
def time_it(self):
self.elapsed += 100
## function calls itself until time has finished
if self.elapsed < self.stop_time:
self.lab["text"]="%d of %d" % (self.elapsed, self.stop_time)
self.root.after(100, self.time_it)
## time elapsed so remove label and restore button
else:
self.lab.grid_forget()
self.startbutton.grid(row=2,column=1)
m_gui = tk.Tk()
m_gui.geometry('450x450+200+200')
m_gui.title('Letters')
B=ButtonDisappear(m_gui)
m_gui.mainloop()

tkinter progress bar with file list

I have a loop that read files in python like below:
def Rfile():
for fileName in fileList:
….
How can I add a tkinter progress bar that will be linked to the for loop and the size of the fileList (start before the loop and close after the loop)?.
Thx
This little script should demonstrate how to do that:
import tkinter as tk
from time import sleep
# The truncation will make the progressbar more accurate
# Note however that no progressbar is perfect
from math import trunc
# You will need the ttk module for this
from tkinter import ttk
# Just to demonstrate
fileList = range(10)
# How much to increase by with each iteration
# This formula is in proportion to the length of the progressbar
step = trunc(100/len(fileList))
def MAIN():
"""Put your loop in here"""
for fileName in fileList:
# The sleeping represents a time consuming process
# such as reading a file.
sleep(1)
# Just to demonstrate
print(fileName)
# Update the progressbar
progress.step(step)
progress.update()
root.destroy()
root = tk.Tk()
progress = ttk.Progressbar(root, length=100)
progress.pack()
# Launch the loop once the window is loaded
progress.after(1, MAIN)
root.mainloop()
You can always tweak it to perfectly satisfy your needs.

Resources