Manual progress bar python - python

Im trying to make a code so that it displays all numbers from 1 to 100 to show as its loading something.
for i in range(101):
self.new = Label(self.label_progress, text=i)
time.sleep(1)
self.new.place(in_= self.label_progress)
if i == 100:
self.new1=Label(self.label_progress, text="the download is complete")
self.new1.place(in_=self.label_progress, x=50)
but it seems like it doesnt want to show each number untill the loop is complete, at the end it just shows a 100. Any suggestions on how to fix it?

tkinter has mainloop() which runs all the time and does many thing - ie. it updates data in widget, redraws widgets, executes your function. When mainloop() executes your function then it can't updates/redraws widgets till your function stop running. You can use root.update() in your function to force tkinter to update/readraw widgets.
Or you can use
root.after(miliseconds, function_name)
to periodically execute function which will update Label . And between executions tkinter will have time to update/redraw widgets.
Examples which use after to update time in Label:
https://github.com/furas/my-python-codes/tree/master/tkinter/timer-using-after
BTW: tkinter has ttk.Progressbar
Example code: https://stackoverflow.com/a/24770800/1832058

Related

Which code does the mainloop() processes infinitely until any event occurs?

To understand my question kindly follow the paragraphs written below:
What code does the mainloop processes infinitely? Like does it read the code of the entire program again and again?
consider the code:
from tkinter import *
window = Tk()
print("lol")
print("Hello World")
window.mainloop()
the output didn't print "Hello World" or "lol" infinite number of times, so the mainloop() doesn't loop the code of the current module.
Now consider this code:
from tkinter import *
print("lol")
window = Tk()
print("Hello World")
while True:
window.update()
Now, even this code executes the same output, so now we can consider the mainloop() loops the code "window.update()" infite number of times, but more efficiently(somehow).
Now the first question arises what does the window.update() function do to update the values in the GUI, does it re-read the code from top to bottom again, or how does the update function update the GUI widget vaules.
The second question is :
I read this article
"Mainloop in Python Tkinter is an infinite loop of the application window which runs forever so that we can see the still screen.
The application window is like a frame that keeps on destroying every microsecond but the main loop keeps on creating a new updated window.
This process of destroying old window screens and creating a new one happens so fast that human eyes don’t even realize it.
Since the process runs infinite time that is why we are able to see the application in front of us and when we close the window then the loop terminates or exits."
Now if this is true then to recreate an updated window the root.mainloop() must read the entire root GUI code again and again entirely or is there another explanation to it.
I have been trying to understand this for the past 6hrs and I have visited every site and I cannot find the solution for the life of me.
Regards,
Rashik
What code does the mainloop processes infinitely? Like does it read the code of the entire program again and again?
No.
Via this function, it calls this C code which has the embedded Tcl interpreter process one event, or wait for Tkinter_busywaitinterval before trying to process another event
Now, even this code executes the same output, so now we can consider the mainloop() loops the code "window.update()" infite number of times, but more efficiently(somehow).
window.update() calls TCL update, which is described to
[...] bring the application “up to date” by entering the event loop repeatedly until all pending events (including idle callbacks) have been processed.
Your infinite loop doesn't have a sleep, so it's spinning your CPU as hard as possible to do practically nothing.
[...] Does it re-read the code from top to bottom again, or how does the update function update the GUI widget vaules.
It certainly doesn't re-read your code. It processes any pending widget updates, which may have happened by running e.g. window.text("...") in e.g. a click callback or an .after() timeout, etc.
I read this article [...]
That article seems wrong and/or at least over-simplifies things.
This simple example clock should clarify how things work:
import time
import tkinter as tk
root = tk.Tk()
text = tk.Label(root)
text.pack()
def tick():
text["text"] = time.ctime() # update `text` widget's content
root.after(1000, tick) # schedule for this function to be called after 1 second
if __name__ == '__main__':
tick() # call the `tick` function once before entering main loop
root.mainloop()

How can I execute multiple tasks with a function when pressing a button in Tkinter?

So, my plan is to create one of those games where a sequence is shown on the screen and you have to click the correct buttons, following the sequence. I'm going to use Tkinter for this project. Here's some explanation: When any buttons is clicked, a function will be executed with a parameter that will tell which button has been clicked, and then the function will check whether it's the correct button or not.
THE PROBLEM: if the user clicked the right button, this function will call a second function to show the next buttons of the sequence. The thing is that it should take some time to display this, and meanwhile the tkinter button remains 'clicked'. I don't want it to happen, also because you cannot execute other tasks, such as leaving the game or restarting the round, while this is happening.
Here I have a simple example to show you the problem, if you couldn't see it yet:
import tkinter as tk
from time import sleep
def func():
print('hi')
func2()
def func2():
print('hi2')
for num in range(3, 11):
sleep(1)
print('hi' + str(num))
wn = tk.Tk()
b1 = tk.Button(wn, command=func)
b1.pack()
wn.mainloop()
In this case, the code should print 'hi' + the numbers from 1 to 10 when the button is pressed. However, the things I've said previously happen.
How can I make this example above work correctly and also my project?
You can either use threads or you can define func3 which runs func1 and func2.

How to show the current window in Tkinter?

Hope you can help me with the following issue.
I'm using the library tkinter and
I have a main_screen main_screen= Tk() which is looping with mainloop()
Inside of the main_screen, there is a little data form where you need to type some necessary data.
And at the end of the window, I have a button to open a second window.
Once I click in that button, report_screen appears (See below in my code)
The new window should appear with the command Toplevel() and print a label that says Starting:
request_start = LabelFrame(report_screen, text="Starting...").pack()
then my program must run a process where it takes around 10 seconds to complete it.
Let's suppose that my process is just this
time.sleep(10)
And finally, run the next line:
request_done = LabelFrame(report_screen, text="Done").pack()
What is my problem?
The problem is that report_screen doesnt appear until the process of 10 sec has finished, and appears with both labels "Starting..." and "Done" at the same time.
I don't want that, I require that report_screen appears with the label "Starting", and then run the process, and when the process finished, add the "Done" label
This is the part of my code where I have this issue
report_screen = Toplevel()
request_start = Label(report_screen, text="Starting...").pack()
time.sleep(10) #example of my process that takes around 10 seconds
request_done = Label(report_screen, text="Done").pack()
Using update() method works to update whatever is in the screen

Problems with update and update_idletasks

I've been learning python for a month now and run into my first brick wall. I have a large art viewer GUI program and at one point want to put an image on screen with a countdown counter-approx every 5 secs. I thought of a code such as the one below The problem is that this uses update and all my reading says that update is bad (starts a new event loop (?)) and that I should use update_idletasks. when I replace update with update_idletasks in the code below the countdown button is not visible until it reaches single figures, update superficially works fine. But also the q bound key calls the subroutine but has no effect
from tkinter import *
import sys
import time
root = Tk()
def q_key(event):
sys.exit()
frame=Frame(root, padx=100, pady=100, bd=10, relief=FLAT)
frame.pack()
button=Button(frame,relief="flat",bg="grey",fg="white",font="-size 18",text="60")
button.pack()
root.bind("q",q_key)
for x in range(30, -1, -5) :
button.configure(text=str(x))
button.update()
print(x)
button.after(5000)
root.mainloop()
In this case you don't need update nor update_idletasks. You also don't need the loop, because tkinter is already running in a loop: mainloop.
Instead, move the body of the loop to a function, and call the function via after. What happens is that you do whatever work you want to do, and then schedule your function to run again after a delay. Since your function exits, tkinter returns to the event loop and is able to process events as normal. When the delay is up, tkinter calls your function and the whole process starts over again.
It looks something like this:
def show(x):
button.configure(text=x)
if x > 0:
button.after(5000, show, x-5)
show(30)

Why does my progress bar work with "print" command and not with tkinter?

I would like to understand why this code:
import time
for i in range(1,11):
print(i)
time.sleep(1)
shows (as it should!) numbers from 1 to 10, each every 1 second, while this code:
from tkinter import *
import time
root = Tk()
for i in range(1,11):
Label(root, text = i).grid(row=0, column=i-1, padx=5, pady =5)
time.sleep(1)
root.mainloop()
waits for 10 seconds, and then displays a window with the 10 numbers (instead of adding them one by one).
I am aware this is a silly question, but I really can't understand! Many Thanks! Alessandro
Most GUI's work differently to what you expect.
They work in an asynchronous way, which means, that you setup your windows and start an event loop.
This event loop will display all widgets, labels, etc, that you set up before calling the event loop and wait for any events (GUI events like mouse or keyboard events, timer events and perhaps network events).
When any event is encountered code associated to that event will be called and this code can request to change the GUI (show or hide elements, change labels or attributes of graphical widgets) However the change to the GUI will only be performed when you give control back to the event loop (when the code handling an event finished)
In your given code you change a label in a for loop with sleep statements, but only after the for loop is finished your main loop is being called and this is the moment, where the final state of your GUI will be displayed.
So what you encounter is a know issue for almost all GUI / asynhronous kind of applications.
You have to rewrite your code such, that you start a timer event, and when the timer event fires a function will set a label and increase the counter by 1. And if the counter is not 11 it will restart another timer
This is because the time.sleep function is before the root.mainloop function.
root.mainloop is what causes the window to appear on-screen and start doing things. Instead, I'd recommend using window.after, as that tells the window to run a function after some time when it's on-screen.
Here's an example of a modification you could make (it's not that good but it works):
from tkinter import *
import time
root = Tk()
progress = 0
end = 10
def update_progress():
global progress
progress += 1
Label(root, text = progress).grid(row=0, column=progress-1, padx=5, pady =5)
if progress < end: root.after(1000,update_progress) # Tell the window to call this function in 1000ms (1 second)
root.after(0,update_progress) # Tell the window to run the update_progress function 0ms after now.
root.mainloop()
I'd recommend looking at gelonida's answer for an explanation of why your original code didn't work, and what you need to keep in mind when programming with GUIs in the future.

Categories