How to show the current window in Tkinter? - python

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

Related

python tkinter entry widget wait_window in GUI not time.sleep

I have a code snippet. Entry widget creates itself with text, then waits seconds then destroys itself.
entry_var_temporary = tk.StringVar()
entry_var_temporary.set(varsoundTitle_usernameHeroContainer)
entry_shtname_temp=tk.Entry(canvas2,width=30,textvariable=entry_var_temporary)
entry_shtname_temp.pack()
entry_shtname_temp.focus_set()
root.update()
time.sleep(10)
entry_shtname_temp.destroy()
root.update()
I have put 10 seconds to wait, and let user to modify the text, if it wants so. But as I see, time.sleep does not let the entry widget to be modified.
How can I get around this problem?
With wait.window() I realized I can edit text inside widget, but my problem is, it is not compulsory. So, if user doesn't put any text on it, it then after 10 sec needs to be destroyed
This seems to be an extension of a previous question you asked. You only need to add one more line of code to that example in order to automatically delete the entry widget after 10 seconds.
In the following example, notice how I use after to destroy the window in 10 seconds. This call must be done before waiting for the window. This is the example I gave in your previous question, with just that one new statement added:
entry_var = tk.StringVar()
new_sheetname_entryBox=tk.Entry(canvas2,width=30, textvariable=entry_var)
new_sheetname_entryBox.pack()
new_sheetname_entryBox.bind("<Return>", lambda event: new_sheetname_entryBox.destroy())
new_sheetname_entryBox.focus_set()
# wait for the entry widget to be deleted, but automatically
# delete it after 10 seconds if the user doesn't respond.
root.after(10000, new_sheetname_entryBox.destroy)
new_sheetname_entryBox.wait_window()
Use the after method with the destroy method as the callback.
The main reason to use root.after vs time.sleep is the time.sleep stops the thread causing the code to completely stop compared to root.after which is thread-safe because it is implemented in terms of call.
Your widget will destroy itself automatically after a set amount of time.
for example:
import tkinter as tk
root = tk.Tk()
entry_var_temporary = tk.StringVar()
entry = tk.Entry(root, textvariable=entry_var_temporary)
entry.pack()
root.after(10000, entry.destroy) # 10000 milliseconds
root.mainloop()

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()

Updating a TKinter Label during other loops

Currently I'm working on a project of mine involving sensors, and showing that sensory data on a display via TKinter. Everythings written in Python 3.7.3.
The issue im currently handling, is to update the label in the window, while the mainloop is running.
What i mean by this, is that if i execute the script, first the window options get defined, then the update function gets defined with a while true loop. Then its supposed to start the window. Now because of the while true loop it does not reach the window.mainloop() point (obviously, the while loop doesn't break...). My interest was peaked and i tried to put the window.mainloop() function inside the while loop of the update (please don't blame me, i know my script is a spaghetti mess.) I figured out that i could run the whole thing in threads, and so i decided to thread the whole window process, and add queues for the sensor data. Now the while loop was still in the way and didnt work properly, and after a bit of googling i found a code snippet that might help me. After trying to implement it in my script, i got an exception "function init expects 3 arguments, but 4 were given.." (code below) and I'm kinda running out of ideas on this.
Bear in mind that im not raelly a developer, i just need a script that can handle sensor data, dispaly it in a window, and export the current data to a database. So go easy on the blame please.
Current Script:
import time
import board
import adafruit_dht
import threading
import queue
from tkinter import *
dhtDevice = adafruit_dht.DHT22(board.D4, use_pulseio=False)
tempQ = queue.Queue(maxsize=0)
humQ = queue.Queue(maxsize=0)
class windowMain:
def __init__(self):
self.tempC_label = Label(fenster, text="Placeholder TempC")
self.humidity_label = Label(fenster, text="Placeholder Humidity")
self.tempC_label.pack()
self.humidity_label.pack()
self.tempC_label.after(2000, self.labelUpdate)
self.humidity_label.after(2000, self.labelUpdate)
def labelUpdate(self, tempQ, humQ):
self.tempC_label.configure(text= tempQ.get() + "°C")
#this is just to confirm if the function called or not, to see if the label updated or not.
#if the label didnt update, and the function called, there is something wrong with the function
#if the label didnt update, and the function didnt call, there is a problem somwhere else
print("Current Temp: " +tempQ.get() + "°C")
self.label.after(2000, self.labelUpdate)
if __name__ == "__main__":
windowName = Tk()
windowName.title = ("Climatemonitor")
windowMain(windowName)
windowName.mainloop()
try:
windowThread = threading.Thread(target=windowMain, args=(tempQ, humQ, ))
windowThread.start()
except:
print("Unable to start thread")
while True:
try:
temperature_c= dhtDevice.temperature
tempText= temperature_c
tempText= str(tempText)
tempQ.put(tempText)
humidity = dhtDevice.humidity
humidityP = str(humidity)
#this one is just to check if the sensor reads data
print(
"Temp: {:.1f} C Humidity: {}% ".format(
temperature_c, humidity
)
)
time.sleep(2.0)
except RuntimeError as error:
print(error.args[0])
time.sleep(2.0)
continue
except Exception as error:
dhtDevice.exit()
raise error
time.sleep(2.0)
The ultimate goal is to display my sensor data, with a 2 second refresh (the HZ rate of the Sensor), while the sensor continues to read every 2 seconds.
I'd also like to add that this is my first time using Python, since im, again, not really a developer yet.
Thanks a bunch in advance for every critique and help
most simple way of doing this would be using a button to execute a function and then including your while loop in that function,
Using an button gives you an point where you can start running while instead of directly starting it as soon as you run your program
Sample code should be something like this,
import tkinter as t
def execute():
print('hello')
window = t.Tk()
window.title("system")
window.geometry("550x250")
b1 = t.Button(window, text="Start", width=15, command=execute)
b1.grid(row=1, sticky="W", padx=4)
window.mainloop()
As there will be no user interaction, a button can invoked using button.invoke method such as following,
import tkinter as t
def execute():
print('hello')
window = t.Tk()
window.title("system")
window.geometry("550x250")
b1 = t.Button(window, text="Start", width=0, command=execute)
#b1.grid(row=1, sticky="W", padx=4)
b1.invoke()
window.mainloop()
here removing .grid() will cause the button to disapper but can affect your GUI while updating the label value later , also have a look at this ->
Is there a way to press a button without touching it on tkinter / python?
Python tkinter button.invoke method trouble

Manual progress bar 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

better way to update a Tkinter listbox

Hi
so first of all i made a program that downloads music and displays the percent that has downloaded in a list box.
kind of like this
from Tkinter import *
from urllib2 import *
admin = Tk()
Admin = Tk()
listbox = Listbox(admin, bg="PURPLE")
listbox.pack()
def fores():
chunks = 10000
dat = ''
song = '3 rounds and a sound'
url = 'http://bonton.sweetdarkness.net/music/Blind%20Pilot%20--%203%20Rounds%20and%20A%20Sound.mp3'
down = urlopen(url)
downso = 0
tota = down.info().getheader('Content-Length').strip()
tota = int(tota)
while 1:
a = down.read(chunks)
downso += len(a)
if not a:
break
dat += a
percent = float(downso) / tota
percent = round(percent*100, 1)
listbox.insert(END, percent)
listbox.update()
listbox.delete(0, END)
listbox.insert(END, percent)
listbox.update()
button = Button(Admin, text='Download', command=fores)
button.pack()
button = Button(Admin, text='Download', command=fores)
button.pack()
mainloop()
I wont show you the original program for it is over the limit of the post size.
On my original program if i move the window before i download an mp3 file it downloads less then 3 % and stops and if i then close the window it starts downloading again.
does anyone know why this is or if there is an alternative to displaying the percentage on the Tkinter window?
Please help
and update_idletasks doesent work
The proper widget for displaying a string is a Label. You can change the text at runtime with the configure method:
self.progress = Label(...)
...
self.progress.configure(text="%s%% completed" % percent)
Second, you are creating two root windows - admin and Admin. And strangely, you are putting the listbox in one and the buttons in another. Tk isn't designed to work like that. Third, you need to call the mainloop method of your (single) root window (eg: Admin.mainloop)
Finally, as to your comment that update_idletasks doesn't work -- please define "doesn't work". It will in fact update the display. What it won't do is let you interact with the window while it is running.
I made changes to your code based on the above comments (created only one root, used a Label rather than Listbox, and used update_idletasks and the program ran to completion, downloading the song.
The danger of calling update is this: what if you click the "download" button while you are already downloading? What happens is the next time update is called, that button press will be serviced. In the servicing of that event you'll enter an infinite loop. While that inner infinite loop is running the outer one cannot run. You will have effectively frozen the first download.
The proper solution involves one of (at least) two techniques. One, create a thread to do the downloading, and have it periodically send information back to the main loop so it can update the progress bar. The second is to leverage the already existing infinite loop -- the event loop -- and do your reading of chunks one at a time by placing jobs on the event queue with after.
There are examples on the internet for both approaches.
i use a ttk.Progressbar, all you have to do is associate a variable to it and update that particular variable.
http://docs.python.org/library/ttk.html#progressbar
http://www.tkdocs.com/tutorial/morewidgets.html#progressbar

Categories