-1

I have a program that encrypts files and I used multiprocessing to make it faster, but I am having trouble with the tkinter progress bar.

I have implemented it but it completes immediately or lags in between. The progressbar just completes to 100% but the files are still encrypting and i recieve no error.

files contains list of files.

The whole code is here - https://codeshare.io/pq8YxE

Below is the way i have implemented it.

def progbar():
    global pb_lable
    global percent
    global pbar
    global percentlabel
    global pbar_frame
   
    pb_lable = tk.Label(root, text='Progress', font = "Raleway 13 bold")
    pb_lable.grid(row=5, columnspan=2, sticky='w', padx=(35))

    pbar_frame = tk.Frame(root)
    pbar_frame.grid(row=6, columnspan=2)

    pbar = Progressbar(pbar_frame,orient='horizontal',length=500,mode='determinate')
    pbar.grid(row=7,column=0, pady=10, padx=20)

    percent = tk.StringVar()
    percentlabel = tk.Label(root, textvariable=percent, font='Raleway 15')
    percentlabel.grid(row=5,columnspan=2,pady=10, padx=120, sticky='w')


def encryptfn(key, a):
    f = Fernet(key)
    return f.encrypt(a)

def enc(key, process_pool, file):
    task = len(files)
    x = 0
    with open(file,'rb') as original_file:
        original = original_file.read()
    encrypted = process_pool.apply(encryptfn, args=(key, original,))
    with open (file,'wb') as encrypted_file:
        encrypted_file.write(encrypted)
    
    pbar['value']+=100/task
    x = x+1
    percent.set(str(int((x/task)*100))+'%')
    root.update_idletasks()

def encfile():
    password = bytes('asdasd', 'utf-8')
    salt = bytes('zxcasd','utf-8')
    global files
    files = filistbox.get(0,'end')

    if len(files) == 0:
        fierrorbox()
    elif len(password) == 0:
        passerrorbox()
    else:
        file_enc_button['state']='disabled'
        browsefi['state']='disabled'

        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=salt,
            iterations=100,
            backend=default_backend())
        key = base64.urlsafe_b64encode(kdf.derive(password))

        MAX_THREADS = 300
        pool_size = min(MAX_THREADS, cpu_count(), len(files))
        process_pool = Pool(pool_size)
        thread_pool = ThreadPool(min(MAX_THREADS, len(files)))
        worker = partial(enc, key, process_pool)
        thread_pool.map(worker, files)

        root.event_generate("<<encryption_done>>")
        
        file_enc_button['state']='active'
        browsefi['state']='active'

def run_encfile():
    root.bind('<<encryption_done>>', encryption_done)
    Thread(target=encfile).start()

def encryption_done(*args):
    fiencdone()

if __name__ == '__main__':
    root = tk.Tk()

    browsefi = tk.Button(root, text='Browse', command=fibrowse, borderwidth=3)
    browsefi.grid(row=2,column=0,padx=5, pady=5)

    ##  File list  ##
    filist_frame = tk.Frame(root)
    filist_frame.grid(row=3, columnspan=2)

    filistbox = tk.Listbox(filist_frame, width=40, height=10)
    filistbox.grid(row=3,columnspan=2, pady=10)

    ## Button ##
    fibutton_frame = tk.Frame(root)
    fibutton_frame.grid(row=4, columnspan=2)

    file_enc_button = tk.Button(fibutton_frame, text='Encrypt', width=15, command=run_encfile, borderwidth=3)
    file_enc_button.grid(row=4,column=0,padx=10,pady=15)

    progbar()
    percent.set('0%')
    root.mainloop()
Sanket
  • 45
  • 5
  • You have now posted this Question several times. Could you take the time for a [mre]? You may find someone that is willing to write this code for you. Allthough, SO is for debuggin and your question lack of details. What is the expected behavior and how does it differ from it. Do you have a traceback error? – Thingamabobs Nov 10 '21 at 10:46
  • I understand that, but I wont go through all of your code to make minor changes. There is a lot of code we dont need to know. You need a window a progressbar and a process to demonstrate your issue and what you have tried to make it work. You may better ask your teacher anyway, but thats just my opinion. – Thingamabobs Nov 10 '21 at 10:56
  • what i have here is just code for encryption, the whole app is big. i just need help implementing the progress bar thats y i created a minimal version of it - https://codeshare.io/pq8YxE – Sanket Nov 10 '21 at 10:59
  • @Atlas435 can you help me with the code ? – Sanket Nov 10 '21 at 12:24
  • Does this answer your question? [Python Tkinter multiprocessing progress](https://stackoverflow.com/questions/47670677/python-tkinter-multiprocessing-progress) – Thingamabobs Nov 10 '21 at 12:53
  • @Atlas435 iam having a hard time implementing it in my situation. – Sanket Nov 10 '21 at 14:55

1 Answers1

1

The problem appears with function enc where you keep on resetting variable x back to 0.

You need a global variable, for example tasks_completed, initialized to 0, that keeps track of the number of encryptions that have been completed and gets incremented within enc with the statement tasks_completed += 1. Technically this is equivalent to tasks_completed = tasks_completed + 1 and is not an atomic operation and it is theoretically possible that the thread could be interrupted after reading the value of tasks_completed and therefore two threads would be updating tasks_completed to the same value. So this update, to be completely safe, should be done under control of a multithreading.Lock to ensure only one thread at a time can be doing the updating.

Replace function enc with:

from threading import Lock
tasks_completed = 0

def enc(key, process_pool, lock, file):
    global tasks_completed

    with open(file,'rb') as original_file:
        original = original_file.read()
    encrypted = process_pool.apply(encryptfn, args=(key, original,))
    with open (file,'wb') as encrypted_file:
        encrypted_file.write(encrypted)
    encrypted = process_pool.apply(encryptfn, args=(key, file,))

    with lock:
        tasks_completed += 1
    percentage_completed = int((tasks_completed / len(files)) * 100)

    pbar['value'] = percentage_completed
    percent.set(f'{percentage_completed}%')
    root.update_idletasks()

And create a multiprocessing.Lock instance and pass it to enc in function encfile by modifying the function as follows:


        lock = Lock() # create lock
        # add additional lock argument to enc:
        worker = partial(enc, key, process_pool, lock)
Booboo
  • 38,656
  • 3
  • 37
  • 60
  • getting TypeError: token must be bytes and `Exception in thread Thread-1: multiprocessing.pool.RemoteTraceback:` [img](https://imgur.com/KkEXcFq) Also the progressbar isn't moving now but the encryption is completed. – Sanket Nov 11 '21 at 06:56
  • 1
    Your exception seems to have nothing to do with the progress bar but rather with encryption. Moreover, the line numbers in the stack trace do not correspond to the source you posted. As far as the progress bar not moving, see [my code](https://codeshare.io/1Y1X6z) based on your code. After the files are selected, instead of encrypting the files, `encryptfn` sleeps for 1 second and just returns the name of the file converted to upper case. I have also modified the code just to use 1 thread so if you choose 10 files the progress bar should move once every second showing 10%, 20%, 30% .... 100%.. – Booboo Nov 11 '21 at 11:24
  • So maybe you did not copy the code correctly. – Booboo Nov 11 '21 at 11:24
  • yep, my bad missed a comma. @Booboo the code works great. Thanks for everything. You are a life saver. Thanks again – Sanket Nov 11 '21 at 14:05
  • @Booboo do you mind helping me [here](https://stackoverflow.com/questions/70378894/reading-large-file-uses-100-memory-and-my-whole-pc-frozes). Its the same code that i took from here. – manis Dec 16 '21 at 14:07
  • @Booboo can u help with [this](https://stackoverflow.com/questions/70709895/decryption-takes-more-memory-and-cpu-when-files-are-not-encrypted) – Sanket Jan 17 '22 at 05:51