I would like to display some numbers, as fast as possible in Tkinter. The Program, I am trying to do, gets many numbers send and should show those.
Here is an similar environment, where tinter has to change a label very quickly.
from tkinter import *
import time
window = Tk()
lbl13 = Label(window, text="-")
lbl13.grid(column=0, row=0)
x = 0
while 1:
lbl13.config(text = str(x))
time.sleep(2)
x +=1
window.mainloop()
The Tkinter window doesn't even open on my computer. Is that because i have too weak hardware? What could I change that this Program also runs on my Computer. Thank you for every answer!
The infinite while loop will keep the program from getting to the line where you call window.mainloop(). You should call window.update() repeatedly instead of window.mainloop() at the end:
from tkinter import *
import time
window = Tk()
lbl13 = Label(window, text="-")
lbl13.grid(column=0, row=0)
x = 0
while 1:
lbl13.config(text = str(x))
window.update()
x +=1
Using after and a proper mainloop is probably a more more flexible way to achieve what you want; it is also reusable in different contexts, and can be used in an application that does more than trivially increment a number on a label:
maybe something like this:
import tkinter as tk
if __name__ == '__main__':
def increment():
var.set(var.get() + 1)
label.after(1, increment)
window = tk.Tk()
var = tk.IntVar(0)
label = tk.Label(window, textvariable=var)
label.pack()
increment()
window.mainloop()
I have tried to implement a Tkinter progressbar using threading simply to see when a program is running, and to close the progressbar when the program ends.
import tkinter
import ttk
import time
import threading
def task(root):
ft = ttk.Frame()
ft.pack(expand=True, fill=tkinter.BOTH, side=tkinter.TOP)
pb_hD = ttk.Progressbar(ft, orient='horizontal', mode='indeterminate')
pb_hD.pack(expand=True, fill=tkinter.BOTH, side=tkinter.TOP)
pb_hD.start(50)
root.mainloop()
def process_of_unknown_duration(root):
time.sleep(5)
root.destroy()
def pBar():
root = tkinter.Tk()
t1=threading.Thread(target=process_of_unknown_duration, args=(root,))
t1.start()
task(root) # This will block while the mainloop runs
t1.join()
if __name__ == '__main__':
pBar()
#some function
My issue is that once the progressbar starts, the program just hangs and wont do anything else. Any hints?
This is because your call root.mainloop() is blocking the execution of your code. It basically represents the loop for your UI. You might want to look at this answer for a progress bar that is launched by a button.
Are you still interested in this problem? Try to use the update() method for the root object instead of the threading in the simple cases. I offer the following simplified demo-solution with global variables as a start point for subsequent developments.
import tkinter
from tkinter import *
from tkinter import ttk
import time
root1 = Tk()
progrBar1 = None # The main progress bar
win2 = None
def click_but1():
global win2, progrBar2
if win2 is None:
win2 = Toplevel() # Secondary window
win2.title('Secondary')
win2.protocol('WM_DELETE_WINDOW', clickClose) # close the secondary window on [x] pressing
but2 = Button(win2, text='Close', command=clickClose)
but2.pack()
but3 = Button(win2, text='Process', command=clickProcess)
but3.pack()
progrBar2 = ttk.Progressbar(win2, orient = 'horizontal', length = 300, mode = 'determinate')
progrBar2.pack()
if progrBar1:
progrBar1.start(50)
def clickClose():
global win2
progrBar1.stop()
win2.destroy()
win2=None
def clickProcess():
my_func()
def my_func():
global progrBar2
range1, range2 = 20, 40
step1 = 100/range1
for i in range(range1):
for j in range(range2):
time.sleep(0.01)
progrBar2.step(step1)
root1.update() # the "update" method
root1.title('Root') # Main window
progrBar1 = ttk.Progressbar(root1, orient = 'horizontal', mode = 'indeterminate') # The main progress bar
but1 = Button(root1, text = 'Start', command = click_but1)
but1.pack()
progrBar1.pack()
root1.mainloop()
The progress bar in the first (main) window is moving only in the presence of the secondary window. This pogress bar stops and returns to the initial position after closing of the secondary window. The secondary window has it own progress bar for the demo purposes and to show the interaction between windows by means of the update() method.
My little brother is just getting into programming, and for his Science Fair project, he's doing a simulation of a flock of birds in the sky. He's gotten most of his code written, and it works nicely, but the birds need to move every moment.
Tkinter, however, hogs the time for its own event loop, and so his code won't run. Doing root.mainloop() runs, runs, and keeps running, and the only thing it runs is the event handlers.
Is there a way to have his code run alongside the mainloop (without multithreading, it's confusing and this should be kept simple), and if so, what is it?
Right now, he came up with an ugly hack, tying his move() function to <b1-motion>, so that as long as he holds the button down and wiggles the mouse, it works. But there's got to be a better way.
Use the after method on the Tk object:
from tkinter import *
root = Tk()
def task():
print("hello")
root.after(2000, task) # reschedule event in 2 seconds
root.after(2000, task)
root.mainloop()
Here's the declaration and documentation for the after method:
def after(self, ms, func=None, *args):
"""Call function once after given time.
MS specifies the time in milliseconds. FUNC gives the
function which shall be called. Additional parameters
are given as parameters to the function call. Return
identifier to cancel scheduling with after_cancel."""
The solution posted by Bjorn results in a "RuntimeError: Calling Tcl from different appartment" message on my computer (RedHat Enterprise 5, python 2.6.1). Bjorn might not have gotten this message, since, according to one place I checked, mishandling threading with Tkinter is unpredictable and platform-dependent.
The problem seems to be that app.start() counts as a reference to Tk, since app contains Tk elements. I fixed this by replacing app.start() with a self.start() inside __init__. I also made it so that all Tk references are either inside the function that calls mainloop() or are inside functions that are called by the function that calls mainloop() (this is apparently critical to avoid the "different apartment" error).
Finally, I added a protocol handler with a callback, since without this the program exits with an error when the Tk window is closed by the user.
The revised code is as follows:
# Run tkinter code in another thread
import tkinter as tk
import threading
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="Hello World")
label.pack()
self.root.mainloop()
app = App()
print('Now we can continue running code while mainloop runs!')
for i in range(100000):
print(i)
When writing your own loop, as in the simulation (I assume), you need to call the update function which does what the mainloop does: updates the window with your changes, but you do it in your loop.
def task():
# do something
root.update()
while 1:
task()
Another option is to let tkinter execute on a separate thread. One way of doing it is like this:
import Tkinter
import threading
class MyTkApp(threading.Thread):
def __init__(self):
self.root=Tkinter.Tk()
self.s = Tkinter.StringVar()
self.s.set('Foo')
l = Tkinter.Label(self.root,textvariable=self.s)
l.pack()
threading.Thread.__init__(self)
def run(self):
self.root.mainloop()
app = MyTkApp()
app.start()
# Now the app should be running and the value shown on the label
# can be changed by changing the member variable s.
# Like this:
# app.s.set('Bar')
Be careful though, multithreaded programming is hard and it is really easy to shoot your self in the foot. For example you have to be careful when you change member variables of the sample class above so you don't interrupt with the event loop of Tkinter.
This is the first working version of what will be a GPS reader and data presenter. tkinter is a very fragile thing with way too few error messages. It does not put stuff up and does not tell why much of the time. Very difficult coming from a good WYSIWYG form developer. Anyway, this runs a small routine 10 times a second and presents the information on a form. Took a while to make it happen. When I tried a timer value of 0, the form never came up. My head now hurts! 10 or more times per second is good enough for me. I hope it helps someone else. Mike Morrow
import tkinter as tk
import time
def GetDateTime():
# Get current date and time in ISO8601
# https://en.wikipedia.org/wiki/ISO_8601
# https://xkcd.com/1179/
return (time.strftime("%Y%m%d", time.gmtime()),
time.strftime("%H%M%S", time.gmtime()),
time.strftime("%Y%m%d", time.localtime()),
time.strftime("%H%M%S", time.localtime()))
class Application(tk.Frame):
def __init__(self, master):
fontsize = 12
textwidth = 9
tk.Frame.__init__(self, master)
self.pack()
tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
text='Local Time').grid(row=0, column=0)
self.LocalDate = tk.StringVar()
self.LocalDate.set('waiting...')
tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
textvariable=self.LocalDate).grid(row=0, column=1)
tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
text='Local Date').grid(row=1, column=0)
self.LocalTime = tk.StringVar()
self.LocalTime.set('waiting...')
tk.Label(self, font=('Helvetica', fontsize), bg = '#be004e', fg = 'white', width = textwidth,
textvariable=self.LocalTime).grid(row=1, column=1)
tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
text='GMT Time').grid(row=2, column=0)
self.nowGdate = tk.StringVar()
self.nowGdate.set('waiting...')
tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
textvariable=self.nowGdate).grid(row=2, column=1)
tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
text='GMT Date').grid(row=3, column=0)
self.nowGtime = tk.StringVar()
self.nowGtime.set('waiting...')
tk.Label(self, font=('Helvetica', fontsize), bg = '#40CCC0', fg = 'white', width = textwidth,
textvariable=self.nowGtime).grid(row=3, column=1)
tk.Button(self, text='Exit', width = 10, bg = '#FF8080', command=root.destroy).grid(row=4, columnspan=2)
self.gettime()
pass
def gettime(self):
gdt, gtm, ldt, ltm = GetDateTime()
gdt = gdt[0:4] + '/' + gdt[4:6] + '/' + gdt[6:8]
gtm = gtm[0:2] + ':' + gtm[2:4] + ':' + gtm[4:6] + ' Z'
ldt = ldt[0:4] + '/' + ldt[4:6] + '/' + ldt[6:8]
ltm = ltm[0:2] + ':' + ltm[2:4] + ':' + ltm[4:6]
self.nowGtime.set(gdt)
self.nowGdate.set(gtm)
self.LocalTime.set(ldt)
self.LocalDate.set(ltm)
self.after(100, self.gettime)
#print (ltm) # Prove it is running this and the external code, too.
pass
root = tk.Tk()
root.wm_title('Temp Converter')
app = Application(master=root)
w = 200 # width for the Tk root
h = 125 # height for the Tk root
# get display screen width and height
ws = root.winfo_screenwidth() # width of the screen
hs = root.winfo_screenheight() # height of the screen
# calculate x and y coordinates for positioning the Tk root window
#centered
#x = (ws/2) - (w/2)
#y = (hs/2) - (h/2)
#right bottom corner (misfires in Win10 putting it too low. OK in Ubuntu)
x = ws - w
y = hs - h - 35 # -35 fixes it, more or less, for Win10
#set the dimensions of the screen and where it is placed
root.geometry('%dx%d+%d+%d' % (w, h, x, y))
root.mainloop()
What is the easy method to update Tkinter progress bar in a loop?
I need a solution without much mess, so I can easily implement it in my script, since it's already pretty complicated for me.
Let's say the code is:
from Tkinter import *
import ttk
root = Tk()
root.geometry('{}x{}'.format(400, 100))
theLabel = Label(root, text="Sample text to show")
theLabel.pack()
status = Label(root, text="Status bar:", bd=1, relief=SUNKEN, anchor=W)
status.pack(side=BOTTOM, fill=X)
root.mainloop()
def loop_function():
k = 1
while k<30:
### some work to be done
k = k + 1
### here should be progress bar update on the end of the loop
### "Progress: current value of k =" + str(k)
# Begining of a program
loop_function()
Here's a quick example of continuously updating a ttk progressbar. You probably don't want to put sleep in a GUI. This is just to slow the updating down so you can see it change.
from Tkinter import *
import ttk
import time
MAX = 30
root = Tk()
root.geometry('{}x{}'.format(400, 100))
progress_var = DoubleVar() #here you have ints but when calc. %'s usually floats
theLabel = Label(root, text="Sample text to show")
theLabel.pack()
progressbar = ttk.Progressbar(root, variable=progress_var, maximum=MAX)
progressbar.pack(fill=X, expand=1)
def loop_function():
k = 0
while k <= MAX:
### some work to be done
progress_var.set(k)
k += 1
time.sleep(0.02)
root.update_idletasks()
root.after(100, loop_function)
loop_function()
root.mainloop()
If I created Tkinter window with some text that filled the whole window and now wanted to replace the window with a new text, is there a way to refresh the window?
For Example:
a= 100
win= Tk()
win.geometry("500x300")
while a > 0:
if a%2 == 0:
lbl = Label (win, bg = "purple")
lbl.pack()
else:
lbl = Label (win, bg = "blue")
lbl.pack()
a= x-1
The problem with this code is that the Tkinter window does not refresh and just provides the end result instead of showing the windows changing colors.
Thanks for the help!
That is not the way to change UI states, because even if you refreshed the window it would be so quick you won't notice, instead change the state, wait some time and change the state again e.g. here I show how to animate color
from Tkinter import *
index = 0
def changeColor():
global index
if index%2==0:
label.configure(bg = "purple")
else:
label.configure(bg = "blue")
index+=1
label.after(1000, changeColor)
root = Tk()
mainContainer = Frame(root)
label = Label(mainContainer, text="")
label.configure(text="msg will change every sec")
label.pack(side=LEFT, ipadx=5, ipady=5)
mainContainer.pack()
label.after(1000, changeColor)
root.title("Timed event")
root.mainloop()
This Is How I Do To Update Data From Sql Server in tkinter GUI python3
from tkinter import *
import os
window=Tk()
window.geometry('300x300')
def update():
window.destroy()
os.system('test.py')
Button(window,text="Refresh",command=update)
window.mainloop()