1
votes

I am quite new in python and made a Tkinter application that will execute all python files existing in the directory when pressed the start button. My GUI also has progressbar to see the current progress.

so here is my code

import os
from tkinter import *
from tkinter.ttk import *
from tkinter import messagebox

directory = dir_path = os.path.dirname(os.path.realpath(__file__))

files = os.listdir(directory)


root = Tk()
root.geometry('200x200')
root.maxsize(200,200)
root.minsize(200,200)
root.title('PYTOEXE')

v = 0
def begin():
    global v
    for x in files:
        os.system('pyinstaller '+x)
        v=v+1
        p['value']=v


p = Progressbar(root,length=200,max=len(files))

b = Button(root,text="Start",command=lambda: begin())



p.place(x=0,y=0)
b.place(x=62,y=30)

root.mainloop()

but my problem is, Whenever i press start button, The GUI freezes and codes start getting compiled and when completed, the GUI unfreezes and the Progressbar fills itself full at once...

So i want the GUI not to freeze while processing and show correct progress on the Progressbar.

Example code and Explanation will be better for me.

Thanks for your valuable time...

2

2 Answers

1
votes

This worked.No need to use .after() to check the thread is finished.

import os
from tkinter import *
from tkinter.ttk import *
import threading

def use_pyinstaller(): # this function is to execute pyinstaller command and add value to progressbar.
    v = 0
    for x in files:
        os.system('pyinstaller '+x)
        v+=1
        p['value'] = v

def begin():
    threading.Thread(target=use_pyinstaller).start() # create a non-block thread to start the function.

directory = dir_path = os.path.dirname(os.path.realpath(__file__))
files = os.listdir(directory)
root = Tk()
root.geometry('200x200')
root.maxsize(200,200)
root.minsize(200,200)
root.title('PYTOEXE')

p = Progressbar(root,length=200,max=len(files))
b = Button(root,text="Start",command=begin)

p.place(x=0,y=0)
b.place(x=62,y=30)

root.mainloop()
0
votes

First off, the command argument for the button can just be: command=begin.

GUI toolkits like tkinter are event-driven. They depend on a smooth flow of keyboard and mouse events to work properly. Callbacks (like the command from a button) are called from witin the event loop (root.mainloop). So a callback should only take a short time (say 50 ms) as to not freeze the GUI. You should therefore never run a long-running loop in a callback. You have to program in a different style.

The above link takes you to an article on my website where I compare a simple command-line program with an equivalent GUI program. While that program doesn't use external processes, it illustrates the principle.

The proper way to do this in a GUI, is to start a multiprocessing.Process from the button callback. Then use the root.after method to periodically run a callback that checks if the Process is finished, and then start a new process.