The window of Tkinter just completly freeze with all the widgets when I move the Tkinter window and that's my problem I tested it with another code and it always does the same thing
Is the problem exclusively with tkinter?
just move your tkinter window from left to right you will see that absolutely all the program freeze it's incredible
Someone said to put main in a separated thread but how ? Like without example I don't even know what It means :(
how do you put the threads outside of mainloop() ? What does it mean ? I putted root.mainloop() before the thread1 = threading.Thread(target= lambda : fct(), daemon=True) thread1.start() and it does nothing
from tkinter import *
from tkinter import ttk
import time
import threading
import win32api
import pyautogui
root = Tk()
root.geometry('800x438')
root.resizable(False,False)
root.configure(bg='gray')
label = Label(root, text='Display content', fg='yellow', bg='black', font=('Arial', 13), width=20)
label.place(relx=0.5,rely=0.3)
firstentryvar = StringVar()
secondentryvar = StringVar()
firstentry = Entry(root, textvariable=firstentryvar , justify=CENTER, font = ('Arial', 12))
secondentry = Entry(root, textvariable=secondentryvar, justify=CENTER, font = ('Arial', 12))
def displaycontent(*args):
firstentry.pack()
secondentry.pack()
label.bind('<Button-1>', hidecontent)
def hidecontent(*args):
firstentry.pack_forget()
secondentry.pack_forget()
label.bind('<Button-1>', displaycontent)
label.bind('<Button-1>', displaycontent)
def function1(*args):
count = 0
bool = False
while count < 10:
for i in firstentry.get():
if bool == False:
count +=1
print(i)
bool = True
else:
bool = False
def function2(*args):
while True:
if win32api.GetKeyState(0x45) < 0:
print('you pressed e')
thread1 = threading.Thread(target = lambda : function1(), daemon=True)
thread1.start()
thread2 = threading.Thread(target = lambda : function2(), daemon=True)
thread2.start()
root.mainloop()
the code may not mean much but it's enough to reproduce my example, well you will notice that if you click on the display label and then move the window without entering anything in the entries the window will bug/freeze why?
I played a bit with your code and it seems that problem is with your while loops.
Even though you used threads correctly, using while loops this way makes your program uses all the resources to loop into it. What I means is as you started program, even before you press label to show entry widgets, your loops just iterated thousand of times if not tens of thousands.
However, simply putting a time sleep, you can easily stop this exponential resource consuming. However, you shouldn't use time.sleep with tkinter if you aren't using inside threads. As we are using loops inside threads, there is no problem.
For example:
def function1(*args):
count = 0
bool = False
while count < 10:
time.sleep(0.1)
for i in firstentry.get():
if bool == False:
count +=1
print(i)
bool = True
else:
bool = False
Related
I want to write a program that has only a button, and after pressing that, program will start making 3 labels and then change the color of each one every 1 second only once.
It looks very simple and I wrote the following code :
import tkinter as tk
from time import sleep
def function():
mylist=list()
for i in range(3):
new_label=tk.Label(window,text='* * *',bg='yellow')
new_label.pack()
mylist.append(new_label)
print('First state finished')
sleep(1)
for label in mylist:
label.config(bg='red')
print('one label changed')
sleep(1)
window = tk.Tk()
window.geometry('300x300')
btn=tk.Button(window,text='start',command=function)
btn.pack()
tk.mainloop()
First the app is look like this (that is OK):
Second its look like this (its not OK because its print on the terminal but didn't update the lable) :
Third its look like this (at the end the app must be look like this and its OK) :
But I need to see the changes in the moment and use sleep for that reason.
Thank you All.
I would recommend to use .after(delay, callback) method of the tkinter to set the colour.
Hope this is what you want.
import tkinter as tk
def start():
global mylist
mylist = list()
for i in range(3):
new_label = tk.Label(window, text='* * *', bg='yellow')
new_label.pack()
mylist.append(new_label)
delay = 1000 # delay in seconds
for label in mylist:
# Additional delay so that next color change
# is scheduled after previous label color change
delay += 1000
schedule_color_change(delay, label)
def schedule_color_change(delay, label):
print("schedule color change for:", label)
label.after(delay, set_color, label)
def set_color(label):
print("setting color of:", label)
label.config(bg="red")
window = tk.Tk()
window.geometry('300x300')
btn = tk.Button(window, text='start', command=start)
btn.pack()
tk.mainloop()
Problem
The problem is your sleep(1), because it's a function that suspends the execution of the current thread for a set number of seconds, so it's like there is a stop to the whole script
Solution
The solution is to instantiate Thread with a target function, call start(), and let it start working. So you have to use timer which is included in the threading, then a timer from the threading module (import threading)
Inside the first "for" loop, remove your sleep(1) and write for example Time_Start_Here = threading.Timer (2, function_2) and then of course Time_Start_Here.start() to start.
start_time=threading.Timer(1,function_2)
start_time.start()
Instead you have to remove the second "for" loop and write what's inside ... inside the new function that will be called. Next you need to create the function
def function_2():
for label in mylist:
label.config(bg='red')
label.pack()
print('one label changed')
As Meritor guided me, I followed the after method and wrote the following recursive code without sleep :
import tkinter as tk
def recursive(i, listt):
lbl = listt[i]
if i >= 0:
lbl.config(bg='orange')
i -= 1
lbl.after(500, recursive, i, listt)
def function():
mylist = list()
for i in range(3):
new_label = tk.Label(window, text='* * *', bg='yellow')
new_label.pack()
mylist.append(new_label)
print('all label created')
# 2 is length of list minus 1
recursive(2, mylist)
window = tk.Tk()
window.geometry('300x300')
tk.Button(window, text='start', command=function).pack()
tk.mainloop()
Most likely my code is not optimized because it uses recursive and if you know anything better please tell me
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 new to Tkinter. I was experimenting with this, but while the Tkinter starts just fine, it some times freezes after a while for no reason. Actually, the weird thing that happens, is that while the Tkinter windows do not update the information anymore, the counter keeps printing ON MY WINDOWS DESKTOP LEFT TOP SIDE! (just the black bg box with the white font that shows the counter number) and eventually finds number 777 and ends. But long freezes in between, even without any printing (not on my Windows desktop either)
this is the normal tkinter window
this is the console output
all good here
crazy Windows display here
The simple program here is generating random ints from 1 - 10000 and should 1) if 777 is found it should print the counter and exit 2) if any number between 1-100 is found, it should print the number and counter.
I am trying to learn how to print new content in the same line, and if something happens print more info on another line, while the first (or more) keep updating.
I'm not sure if I exit the program the right way or how exactly I should be using/calling the Tkinter function to update the info on the screen.
Any help or tips most appreciated.
from tkinter import *
import random
import time
tk=Tk()
i = 0
counter = 0
tk.title("Testing...")
tk.geometry('300x200')
canvas_width = 200
canvas_height = 100
canvas = Canvas(tk, width=canvas_width, height=canvas_height, bg='white')
canvas.pack(expand=YES)
def screen(Found):
#canvas.create_text(20, 20, text="Counter:" + str(counter), font="Times 20 italic")
widget = Label(canvas, text="Counter:" + str(counter), fg='white', bg='black')
widget.grid(column=0, row=0)
if Found:
widget = Label(canvas, text="Number " + str(i) +" was found at counter:" + str(counter), fg='white', bg='black')
widget.grid(column=0, row=1)
found = False
loop = True
while loop:
counter += 1
i = random.randint(1, 10000)
if i in range(1, 100):
print("Found i = ", i, " in counter:", counter)
Found = True
else:
Found = False
if i == 777:
loop = False
print("JUST FOUND 777")
print("Counter:", counter)
screen(Found)
tk.update_idletasks()
tk.update()
# tk.mainloop()
As a matter of design, it is usually a better approach to rely on the tkinter mainloop to drive the application. root.after is a great substitute to a while loop. Labels do not need to be recreated; they can use special variables that update the displayed values themselves, and their properties can be reconfigured. Calling update, and update_idletasks is hardly ever needed.
The following is maybe a better approach where one single label is updated from a loop powered by the tkinter mainloop:
(it shows you "how to keep displaying new stuff, without such label issues")
import tkinter as tk
import random
def screen(found, v, c):
if found:
label.configure(bg='red', fg='blue')
print('found')
else:
label.configure(bg='black', fg='white')
update_lbl_txt(v, c)
def update_lbl_txt(v=0, c=0):
txt = f'Number at {v} was found at counter {c}'
lbl_txt.set(txt)
def found_target(t):
return t in range(1, 100) or t == 777
def loop(c=0):
v = random.randint(1, 10000)
screen(found_target(v), v, c)
root.after(100, loop, c+1)
root = tk.Tk()
root.title("Testing...")
root.geometry('300x200')
lbl_txt = tk.StringVar(root)
update_lbl_txt()
label = tk.Label(root, textvariable=lbl_txt, fg='white', bg='black')
label.pack(expand=True, fill=tk.BOTH)
loop()
root.mainloop()
The reason it seems to freeze it at least partly because you have an infinite loop that you never, ever break out of. Most of the time i will not be in the range of 1 to 100, nor will it be 777, but your loop will keep on running and never update the screen.
You're also creating hundreds or thousands of label widgets every second, stacked on top of one another. You will run into performance issues rather quickly.
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 am having a problem where my Tkinter UI becomes completley stuck and non-interactive while a for loop is running. My example code print "Looping" while it is in the loop and there is a "Cancel" button on the UI which is supposed to stop the loop, but since I am unable to click the "Cancel" button the loop can not be stopped. So my question is how can I make my tkinter UI usable while a loop is running. Here is the example code:
from tkinter import*
import time
root = Tk()
i=10
flag = False
def loop():
flag = True
for i in range(100):
if flag == True:
time.sleep(0.5)
print("Looping")
def canc():
flag = False
btn = Button(root, text="Start Loop", command=loop).pack()
cncl = Button(root, text="Cancel", command=canc).pack()
root.mainloop()
I have tried creating a new thread for the loop function but this does not work.
Updated code, the UI is responsive, but nothing happens when cancel is pressed:
from tkinter import*
import threading
import time
root = Tk()
i=10
flag = False
def loop():
flag = True
for i in range(10):
if flag == True:
time.sleep(0.5)
print("Looping")
def run():
threading.Thread(target=loop).start()
def canc():
flag = False
btn = Button(root, text="Start Loop", command=run).pack()
cncl = Button(root, text="Cancel", command=canc).pack()
root.mainloop()
'flag' isnt a global variable so when it is set to False in canc(), the value of the 'flag' local variable in loop() isnt changed and the loop therefore isnt stopped
also root.update() needs to be used to update the GUI
Remedial actions:
from tkinter import*
import threading
import time
root = Tk()
def loop():
global flag
flag = True
for i in range(10):
if flag == True:
root.update()
time.sleep(0.5)
print("Looping")
def canc():
global flag
flag = False
btn = Button(root, text="Start Loop", command=loop).pack()
cncl = Button(root, text="Cancel", command=canc).pack()
root.mainloop()
I found a way around that problem:
I started my time-consuming job inside a thread and I checked if my thread is still running in a while loop, and inside that, I did update my Tkinter root.
here is my code:
def start_axis(input):
print(input)
time.sleep(5)
def axis():
t = threading.Thread(target=start_axis, args=("x"))
t.start()
while t.is_alive():
try:
root.update()
except:
pass
the args part is important so the thread doesn't call the function immediately