I'm trying to create a simple tkinter GUI, and have read up online to add a .mainloop() to the end of my code to make the window appear, It is still not appearing.
There is no error message and simply
Process finished with exit code 0
I have attached my code.
Any help is greatly appreciated
def window():
global FPS
global maxFPS
root = Tk()
root.title('Camera Tracker')
root.geometry('500x300')
def quitfunc():
quitm=Tk()
quitm.title('Quit')
quitm.geometry('200x100')
yesbutton=Button(quitm,text='Yes',command=quit)
nobutton =Button(quitm,text='No',command=quitm.destroy)
yesbutton.place(x=50,y=60)
nobutton.place(x=130,y=60)
reassure = Label(quitm,text='Are you sure you want to quit?')
reassure.place(x=17,y=20)
quitm.mainloop()
sbview = Label(root, text=FPS)
sbview.place(y=50, x=50)
def FPScallback(self):
global FPS
FPS = round(sb.get())
if 10 > FPS < 18 or 29 < FPS:
sbview.config(fg='orange')
elif FPS < 10:
sbview.config(fg='red')
else:
sbview.config(fg='green')
sbview.config(text=FPS)
quitbutton = Button(root,command=quitfunc,text='Quit')
quitbutton.pack()
sb = ttk.Scale(root, from_=0, to=maxFPS, command=FPScallback, orient=HORIZONTAL)
sb.place(y=100, x=100)
sb.set(FPS)
root.mainloop()
Thanks A Bunch In Advance
If you create root from inside a function it will not be available when the function exits. In my example I'm creating root as well as some global variables in the global scope.
To ask the user if he wants to exit it's easier to use the standard library messagebox. If you want to catch any attempt to close the application (such as ALT-F4) you should reasearch root.protocol("WM_DELETE_WINDOW", do_exit) which runs the do_exit function when the app wants to exit by any means.
from tkinter import *
from tkinter import ttk
from tkinter import messagebox # Quit dialog
root = Tk() # Create root in the global scope
root.title('Camera Tracker') # so the functions can find it
root.geometry('500x300')
maxFPS = 50 # Initiate variables in the global scope
FPS = 25
sbview = Label(root, text=FPS)
sbview.place(y=50, x=50)
def quitfunc():
result = messagebox.askyesno('Quit', 'Are you sure you want to quit?')
if result:
root.destroy()
def FPScallback(self):
global FPS
FPS = round(sb.get())
if 10 > FPS < 18 or 29 < FPS:
sbview.config(fg='orange')
elif FPS < 10:
sbview.config(fg='red')
else:
sbview.config(fg='green')
sbview.config(text=FPS)
sb = ttk.Scale(root, from_=0, to=maxFPS, command=FPScallback, orient=HORIZONTAL)
sb.place(y=100, x=100)
sb.set(FPS)
quitbutton = Button(root, command=quitfunc, text='Quit')
quitbutton.pack()
root.mainloop()
Related
import tkinter as tk
from tkinter import *
import keyboard
import time
a = 120
root = tk.Tk()
root.state('zoomed')
root.configure(bg='white')
root.resizable(False, False)
a = 30
x = 0
def increase():
global a
a += 10
def ar():
for i in range(50):
labele1.place(x=120, y=a)
root.after(1000, increase)
c1 = Canvas(root, width=700, height=700, bg='gray95')
c1.place(x=450, y=60)
labele1 = tk.Label(c1, text='_______', font='Calibri 20')
startbutton = tk.Button(text='Başla', command=ar)
startbutton.pack()
root.mainloop()
I want to create a moving label which moves per second. Everything looks clear but it doesn't work well. I want to make a car race app but first the walls should move back and it exit the project
The idea to use root.after is correct, but the implementation is wrong. Just because you update the value of a inside a function, tkinter will not automatically update that value with its widget unless you manually ask it to. So what you can do is:
Make the label a canvas object (recommended since you will be making a game and canvas is closer to give you tools you need):
def ar():
global a
a += 10 # Increase the value of a
c1.coords('wall', [120, a]) # Change the coord of the tag 'wall'
root.after(25, ar) # Repeat this function every 25 ms
....
labele1 = tk.Label(c1, text='_______', font='Calibri 20')
c1.create_window(120, a, window=labele1, tags='wall') # Initial coords -> x=120, y=30
Change the location of the label using place_configure:
def ar():
global a
a += 10 # Increase the value of a
labele1.place_configure(y=a)
root.after(25, ar) # Repeat this function every 25 ms
....
labele1 = tk.Label(c1, text='_______', font='Calibri 20')
labele1.place(x=120, y=a)
Plus checking frame size and decreasing functions.
the main problem was a minor bug which you should change in line 37.
import tkinter as tk
from tkinter import *
import keyboard
import time
a = 120
root = tk.Tk()
root.state('zoomed')
root.configure(bg='white')
root.resizable(False, False)
a = 30
x = 0
moving_rate = 10
max_y = 600
min_y = 30
flag_mov_func= 1
def moving_func(): #increase or decrease function
global a
global flag_mov_func
if flag_mov_func :
a += moving_rate
if a + moving_rate == max_y: #simple logic bouncing if it ends
flag_mov_func=0
elif flag_mov_func==0 :
a -= moving_rate
if a - moving_rate == min_y:
flag_mov_func=1
def ar():
moving_func()
labele1.place(x=120, y=a)
root.after(1000, ar)
flag_is_called=1
def button1(): #new update on code to make the button work once.
global flag_is_called
if flag_is_called:
flag_is_called=0
ar()
c1 = Canvas(root, width=700, height=700, bg='gray95')
c1.place(x=450, y=60)
labele1 = tk.Label(c1, text='_______', font='Calibri 20')
startbutton = tk.Button(text='Başla', command=button1)
startbutton.pack()
root.mainloop()
I am trying to add a progress bar to my window until some work is being done. But it is not working properly. I want it to keep moving until the work is done but it just moves rapidly and then stops. Also if I try to minimize or close the progress window it just hangs and stops responding.
Can anyone help me how can I do it properly? Here is my code.
import time
from tkinter import ttk
from tkinter import *
numbers = []
def main():
main_window = Tk()
app = info(main_window)
main_window.mainloop()
class info:
def __init__(self, root):
# start = timer()
self.error_str = ''
self.root1 = root
self.root1.title('LOADING......')
self.root1.geometry("380x200")
self.root1.eval('tk::PlaceWindow . center')
self.root1.resizable(width=False, height=False)
self.root1.configure(background='white')
progress = ttk.Progressbar(self.root1, orient=HORIZONTAL,
length=380, mode='determinate')
progress.place(x=0, y=100)
i = 20
for x in range(1, 50):
numbers.append(x * 2)
print(numbers)
progress['value'] = i
self.root1.update_idletasks()
time.sleep(0.1)
i = i + 40
self.root = root
self.root.title('Second window')
self.root.geometry('1350x800+0+0')
frame1 = Frame(self.root, bg='#7877a5')
frame1.place(x=0, y=0, width=1350, height=150)
title = Label(frame1, text="Second Window", font=("Times New Roman", 40, "bold", "italic"),
bg='#7877a5',
fg='white')
title.place(x=380, y=45)
if __name__ == '__main__':
main()
Generally speaking you shouldn't call time.sleep() in a tkinter application because it interferes with the GUI's mainloop() and will make your program hang or freeze. Use the universal widget method after() instead.
Lastly you need to specify a maximum value for the Progressbar so its indicator scales properly relatively to the values of i you are setting its value to. The default for maximum is only 100, which your code was greatly exceeding in the for x loop.
Here's the code that needs to change in info.__init__(). The two lines changed have # ALL CAPS comments:
progress = ttk.Progressbar(self.root1, orient=HORIZONTAL,
length=380, mode='determinate',
maximum=(48*40)+20) # ADDED ARGUMENT.
progress.place(x=0, y=100)
i = 20
for x in range(1, 50):
numbers.append(x * 2)
print(numbers)
progress['value'] = i
self.root1.update_idletasks()
self.root1.after(100) # Delay in millisecs. # REPLACED TIME.SLEEP() CALL.
i = i + 40
I would like to implement a very simple GUI for my project. I was previously using just Print statements to output some text and data. However, that is not very conveneint and since a person will need to operate a device that I am coding, he needs to be clearly see the instructions that I am going to display on GUI.
my code:
main()
myConnection = mysql.connector.connect( host=hostname, user=username, passwd=password, db=database )
counter = 0
window = tk.Tk()
window.title("GUI")
window.geometry("400x200")
while(1):
# OPERACIJOS KODAI:
# 0 - PILDYMAS
# 1 - KOMPLEKTAVIMAS
# 2 - NETINKAMAS KODAS
tk.Label(window,text = "Scan barcode here:").pack()
entry = tk.Entry(window)
entry.pack()
var = tk.IntVar()
button = tk.Button(window,text="Continue",command = lambda: var.set(1))
button.pack()
print("waiting...")
button.wait_variable(var)
result = entry.get()
print("Entry string=",result)
var.set(0)
operacijos_kodas=Scanning_operation(myConnection,result)
print("operacijos kodas=",operacijos_kodas)
if(operacijos_kodas == 0):
tk.label(window,text = "PILDYMO OPERACIJA:").pack()
pildymo_operacija(myConnection)
elif(operacijos_kodas == 1):
tk.Label(window,text = "PAKAVIMO OPERACIJA:").pack()
insertData_komplektacija(myConnection,"fmb110bbv801.csv");
update_current_operation(myConnection);
picking_operation();
elif(operacijos_kodas == 2):
print("Skenuokite dar karta")
#break
window.mainloop();
Nothing is being displayed. It just opens up an empty GUI window.
First of all, I am unsure where should I call function window.mainloop().
Secondly, since my system runs in an infinite while loop ( the operation starts when a user scans a bar-code, then he completes an operation and the while loop starts over again (waiting for user to scan a bar-code). So I just simply have to display some text and allow user to input data in the text box.
Could someone suggest me whether this GUI is suitable for my needs or I should look for an alternatives?
UPDATE*********************
I have tried to use mainloop:
print ("Using mysql.connector…")
main()
GPIO_SETUP()
myConnection = mysql.connector.connect( host=hostname, user=username, passwd=password, db=database )
counter = 0
window = tk.Tk()
window.resizable(False,False)
window_height = 1000
window_width = 1200
#window.attributes('-fullscreen',True)
#window.config(height=500,width=500)
#can = Canvas(window,bg='red',height=100,width=100)
#can.place(relx=0.5,rely=0.5,anchor='center')
window.title("GUI")
screen_width = window.winfo_screenwidth()
screen_height= window.winfo_screenheight()
x = int((screen_width/ 2) - (window_width / 2))
y = int((screen_height/ 2) - (window_height / 2))
window.geometry("{}x{}+{}+{}".format(window_width,window_height,x,y))
label1=Label(window,text = "SKENUOKITE BARKODA(GUID) ARBA DAIKTO RIVILINI KODA:")
label1.pack()
entry = Entry(window)
entry.pack()
var = tk.IntVar()
button = Button(window,text="Testi operacija",width = 30,command = lambda: var.set(1))
button.pack()
#button2 = Button(window,text="RESTARTUOTI SISTEMA",width = 30,command = restart_devices())
#button2.pack()
print("waiting...")
button.wait_variable(var)
Scanned_serial = entry.get()
print("Entry string=",Scanned_serial)
var.set(0)
label2=Label(window,text = "Vykdoma operacija:")
label2.pack()
window.update()
window.after(1000,Full_operation(Scanned_serial,label2,window))
window.mainloop()
This is my code. As you can see. i call Full_operation function and then window.mainloop()
my Full_operation:
def Full_operation(Scanned_serial,label2,window):
operacijos_kodas=Scanning_operation(myConnection,Scanned_serial)
print("operacijos kodas=",operacijos_kodas)
if(operacijos_kodas == 0):
label2.config(text = "SPAUSKITE MYGTUKA ANT DEZES KURIA NORITE PILDYTI:")#update the label2
window.update()#call update to update the label
pildymo_operacija(myConnection,Scanned_serial,label2,window)
elif(operacijos_kodas == 1):
insertData_komplektacija(myConnection,"fmb110bbv801.csv");
update_current_operation(myConnection);
#label2.config(text = "IMKITE DAIKTUS IS ZALIOS DEZUTES:")#update the label2
picking_operation(myConnection,label2);
elif(operacijos_kodas == 2):
print("Skenuokite dar karta")
label2.config(text = "NUSKENUOTAS NEGALIMAS KODAS:")#update the label2
window.update()#call update to update the label
How can I ensure that everytime I enter FUll_operation function I start from clean GUI again and start another operation.
Now I am able to complete operation once. After that, the GUI is not responsive.
I have added a print statement at the beggining of my full_operation and it does not execute after I complete it once so my mainwindow does not seem to work properly.
You'll need to adapt your code to work with a GUI. You can't introduce infinite loops in to tkinter GUI's without causing all sorts of problems.
Mainloop should only be called once.
I'd suggest that you move all of your scanning/saving operations in to a separate function which you schedule to occur periodically using the tkinter after method.
For example if you call your function scan you would schedule it to occur after 1 second using
root.after(1000, scan)
A more advanced method would be to have your scanning code running on a separate thread.
Also, you are currently trying to create the label each time you go round the while loop rather than just creating and packing them once and updating the text of the labels when you perform the "scanning". You can update the text of a label using the config method, for example
## Create a label
label1 = tk.Label(window,text = "PAKAVIMO OPERACIJA:")
##Pack the label
label1.pack()
## Update the text later
label1.config(text="New Text")
Here is an example of updating tkinter widgets periodically from a function.
import tkinter as tk
import random
def scanning():
num = random.randint(0,100)
entryTemperature.delete(0, tk.END) #Delete the current contents
entryTemperature.insert(0, f"{num} K") #Add new text
root.after(1000, scanning) #Schedule the function to run again in 1000ms (1 second)
root = tk.Tk()
entryTemperature = tk.Entry(root)
entryTemperature.grid(padx=50,pady=50)
root.after(1000, scanning)
root.mainloop()
I am currently coding a little Python script for a colleague of mine which should give the framework for his PhD defense challenge, e.g. little tasks and questions he has to answer. The script itself should guide him through his challenges and give those tasks, introductions, etc.
I am currently using Tkinter for that purpose. Principally, I want to have a canvas/part of the screen, where text and introductions pop up like in the example shown below.
import Tkinter as tk
import time
global_delay =150
counter = 0
delay = global_delay
def display_text():
global num_letters
global global_text
global label
text = global_text[counter]
num_letters = len(text) - 1
old_text = label.cget("text")+'\n'
def display():
global num_letters
global counter
global global_delay
global delay
if delay == 0:
user_text = ''
com_text = ' '
else:
user_text = 'user#hlrdbb4 ~ '
com_text = ''
print_text = old_text + user_text + str(text[1:len(text) - num_letters])+(num_letters+50-len(text))*' ' + com_text
label.config(text=print_text)
label.config(font=("Courier", 30))
num_letters-=1
if num_letters>=0:
label.after(delay, display)
elif counter<len(global_text)-1:
counter += 1
if global_text[counter][0] == 'o':
delay = 0
if global_text[counter][0] == 'i':
delay = global_delay
label.after(global_delay*10, display_text)
display()
root = tk.Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.geometry("%dx%d+0+0" % (w, h))
root.title("PhD")
label = tk.Label(root, fg="green", bg='black', height=h, width=w, anchor=tk.NW)
label.pack()
global_text=['icd E:\dbb\ ','oChange directory to E:\dbb\ ','iget_PhD.exe','oError file not found','iget_PhD.exe','oError file not found','iget_PhD.exe','oExecuting get_PhD.exe','oHere are your introductions...']
display_text()
root.mainloop()
The other side of the GUI should display the corresponding minigames, e.g. a dynamically changing noise plot for which he has to put in some numbers to see a decent signal. As you can see I am currently using the after-method at the moment to display the text, but I can't figure out, how to incorporate such games or how the script could wait for his (keyboard) input to continue.
Could anyone help me here a bit?
You could bind a keyboard input (in this case enter) or a tkinter button, to use the input of an Entry widget. You execute a function with it (in this case callback). You continue your program if you get the desired input.
import tkinter as tk
root = tk.Tk()
e = tk.Entry(root)
e.pack()
def callback(*args):
print (e.get())
e.bind("<Return>",callback)
root.mainloop()
Some good reading material and extra explanation:
Tkinter Confirmation buttons and game GUI (splice(?))
I'm writing a program with Python's tkinter library.
My major problem is that I don't know how to create a timer or a clock like hh:mm:ss.
I need it to update itself (that's what I don't know how to do); when I use time.sleep() in a loop the whole GUI freezes.
Tkinter root windows have a method called after which can be used to schedule a function to be called after a given period of time. If that function itself calls after you've set up an automatically recurring event.
Here is a working example:
# for python 3.x use 'tkinter' rather than 'Tkinter'
import Tkinter as tk
import time
class App():
def __init__(self):
self.root = tk.Tk()
self.label = tk.Label(text="")
self.label.pack()
self.update_clock()
self.root.mainloop()
def update_clock(self):
now = time.strftime("%H:%M:%S")
self.label.configure(text=now)
self.root.after(1000, self.update_clock)
app=App()
Bear in mind that after doesn't guarantee the function will run exactly on time. It only schedules the job to be run after a given amount of time. It the app is busy there may be a delay before it is called since Tkinter is single-threaded. The delay is typically measured in microseconds.
Python3 clock example using the frame.after() rather than the top level application. Also shows updating the label with a StringVar()
#!/usr/bin/env python3
# Display UTC.
# started with https://docs.python.org/3.4/library/tkinter.html#module-tkinter
import tkinter as tk
import time
def current_iso8601():
"""Get current date and time in ISO8601"""
# https://en.wikipedia.org/wiki/ISO_8601
# https://xkcd.com/1179/
return time.strftime("%Y%m%dT%H%M%SZ", time.gmtime())
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.createWidgets()
def createWidgets(self):
self.now = tk.StringVar()
self.time = tk.Label(self, font=('Helvetica', 24))
self.time.pack(side="top")
self.time["textvariable"] = self.now
self.QUIT = tk.Button(self, text="QUIT", fg="red",
command=root.destroy)
self.QUIT.pack(side="bottom")
# initial time display
self.onUpdate()
def onUpdate(self):
# update displayed time
self.now.set(current_iso8601())
# schedule timer to call myself after 1 second
self.after(1000, self.onUpdate)
root = tk.Tk()
app = Application(master=root)
root.mainloop()
from tkinter import *
import time
tk=Tk()
def clock():
t=time.strftime('%I:%M:%S',time.localtime())
if t!='':
label1.config(text=t,font='times 25')
tk.after(100,clock)
label1=Label(tk,justify='center')
label1.pack()
clock()
tk.mainloop()
You should call .after_idle(callback) before the mainloop and .after(ms, callback) at the end of the callback function.
Example:
import tkinter as tk
import time
def refresh_clock():
clock_label.config(
text=time.strftime("%H:%M:%S", time.localtime())
)
root.after(1000, refresh_clock) # <--
root = tk.Tk()
clock_label = tk.Label(root, font="Times 25", justify="center")
clock_label.pack()
root.after_idle(refresh_clock) # <--
root.mainloop()
I have a simple answer to this problem. I created a thread to update the time. In the thread i run a while loop which gets the time and update it. Check the below code and do not forget to mark it as right answer.
from tkinter import *
from tkinter import *
import _thread
import time
def update():
while True:
t=time.strftime('%I:%M:%S',time.localtime())
time_label['text'] = t
win = Tk()
win.geometry('200x200')
time_label = Label(win, text='0:0:0', font=('',15))
time_label.pack()
_thread.start_new_thread(update,())
win.mainloop()
I just created a simple timer using the MVP pattern (however it may be
overkill for that simple project). It has quit, start/pause and a stop button. Time is displayed in HH:MM:SS format. Time counting is implemented using a thread that is running several times a second and the difference between the time the timer has started and the current time.
Source code on github
from tkinter import *
from tkinter import messagebox
root = Tk()
root.geometry("400x400")
root.resizable(0, 0)
root.title("Timer")
seconds = 21
def timer():
global seconds
if seconds > 0:
seconds = seconds - 1
mins = seconds // 60
m = str(mins)
if mins < 10:
m = '0' + str(mins)
se = seconds - (mins * 60)
s = str(se)
if se < 10:
s = '0' + str(se)
time.set(m + ':' + s)
timer_display.config(textvariable=time)
# call this function again in 1,000 milliseconds
root.after(1000, timer)
elif seconds == 0:
messagebox.showinfo('Message', 'Time is completed')
root.quit()
frames = Frame(root, width=500, height=500)
frames.pack()
time = StringVar()
timer_display = Label(root, font=('Trebuchet MS', 30, 'bold'))
timer_display.place(x=145, y=100)
timer() # start the timer
root.mainloop()
You can emulate time.sleep with tksleep and call the function after a given amount of time. This may adds readability to your code, but has its limitations:
def tick():
while True:
clock.configure(text=time.strftime("%H:%M:%S"))
tksleep(0.25) #sleep for 0.25 seconds
root = tk.Tk()
clock = tk.Label(root,text='5')
clock.pack(fill=tk.BOTH,expand=True)
tick()
root.mainloop()