Wednesday, February 2, 2022

[SOLVED] Python how to use Tkinter GUI without interfering the main code loop

Issue

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.


Solution

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


Answered By - scotty3785
Answer Checked By - Gilberto Lyons (WPSolving Admin)