用于多处理的 tkinter 进度条

Posted

技术标签:

【中文标题】用于多处理的 tkinter 进度条【英文标题】:tkinter progressbar for multiprocessing 【发布时间】:2021-12-22 23:08:36 【问题描述】:

我有一个加密文件的程序,我使用多处理使其更快,但我在 tkinter 进度条上遇到问题。

我已经实现了它,但它会立即完成或在两者之间滞后。 进度条刚刚完成到 100%,但文件仍在加密中,我没有收到任何错误。

files 包含文件列表。

完整的代码在这里 - https://codeshare.io/pq8YxE

以下是我实现它的方式。

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

【问题讨论】:

您现在已多次发布此问题。你能抽出时间来minimal reproducible example吗?您可能会找到愿意为您编写此代码的人。尽管如此,SO 是用于调试的,而您的问题缺乏细节。预期的行为是什么,它与它有何不同。你有回溯错误吗? 我明白这一点,但我不会通过您的所有代码来进行细微的更改。有很多代码我们不需要知道。您需要一个窗口、一个进度条和一个流程来展示您的问题以及您为使其工作所做的努力。无论如何你最好问问你的老师,但这只是我的意见。 我这里的只是加密代码,整个应用程序很大。我只需要帮助实现进度条,我创建了它的最小版本 - codeshare.io/pq8YxE @Atlas435 你能帮我写代码吗? 这能回答你的问题吗? Python Tkinter multiprocessing progress 【参考方案1】:

问题出现在函数 enc 中,您不断将变量 x 重置为 0。

您需要一个全局变量,例如 tasks_completed,初始化为 0,使用语句 tasks_completed += 1 跟踪已完成并在 enc 内递增的加密次数。从技术上讲,这等效于tasks_completed = tasks_completed + 1,并且不是原子操作,理论上线程可能会在读取tasks_completed 的值后被中断,因此两个线程会将tasks_completed 更新为相同的值。因此,为了完全安全,此更新应在multithreading.Lock 的控制下完成,以确保一次只能有一个线程进行更新。

将函数enc替换为:

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

并在函数encfile中创建一个multiprocessing.Lock实例并将其传递给enc,方法如下:


        lock = Lock() # create lock
        # add additional lock argument to enc:
        worker = partial(enc, key, process_pool, lock)

【讨论】:

getting TypeError: token must be bytes and Exception in thread Thread-1: multiprocessing.pool.RemoteTraceback: img 而且进度条现在没有移动,但加密已完成。 您的异常似乎与进度条无关,而是与加密有关。此外,堆栈跟踪中的行号与您发布的源不对应。至于进度条不动,请根据您的代码查看my code。选择文件后,encryptfn 不加密文件,而是休眠 1 秒,然后返回转换为大写的文件名。我还修改了代码以使用 1 个线程,因此如果您选择 10 个文件,进度条应该每秒移动一次,显示 10%、20%、30% .... 100%.. 所以你可能没有正确复制代码。 是的,我的错误错过了一个逗号。 @Booboo 代码效果很好。感谢一切。你是救生员。再次感谢 @Booboo 你介意帮我here。它与我从这里获取的代码相同。

以上是关于用于多处理的 tkinter 进度条的主要内容,如果未能解决你的问题,请参考以下文章

python的Tkinter中的Windows进度条

多处理:使用 tqdm 显示进度条

Tkinter 之ProgressBar进度条标签

带进度条的Tkinter链接按钮

将文本格式化到 Tkinter 进度条

tkinter 进度条