Tkinter进度栏如何在模型对话框中正确实现它

问题描述 投票:-1回答:1

我想要一个带有进度条和一些输入,标签和按钮小部件的顶层窗口/对话框。我希望对话框从main_window窗口更新。 main_window完成工作,我需要在对话框中反映出来。我希望主窗口保持活动状态,以便您可以停止该过程。我也希望能够在对话框中停止该过程。

如果不使用多处理和线程处理,我将无法正常工作。看来我正在以错误的方式进行操作,还是我?另外,我还是多处理和线程技术的新手,所以我还是希望我能正确地做到这一点。如果有人知道更好的方法,请告诉我。

下面是我尝试做自己想做的事,它可以工作,但这是正确的方法吗?

import tkinter as tk
import tkinter.ttk as ttk

from time import sleep
from queue import Empty
from threading import Thread
from multiprocessing import Process, Queue


HIDE = -1
STOP = -2
BREAK = -3
PAUSE = -4
RESUME = -5


class App(tk.Tk):
    def __init__(self, **kwargs):
        title = kwargs.pop('title', '')
        theme = kwargs.pop('theme', 'clam')
        geometry = kwargs.pop('geometry', None)
        exit_callback = kwargs.pop('exit_callback', None)
        super().__init__(**kwargs)

        self.title(title)
        self.style = ttk.Style()
        self.style.theme_use(theme)

        if geometry:
            self.geometry(geometry)

        if exit_callback:
            self.protocol('WM_DELETE_WINDOW', exit_callback)


def main_window(out_que, in_que, maximum):
    def worker():
        if app.running:
            return

        app.running = True
        app.finished = False
        for count in range(0, maximum + 1):
            try:
                message = in_que.get_nowait()
                if message:
                    if message == PAUSE:
                        message = in_que.get()

                    if message == BREAK:
                        break
                    elif message == STOP:
                        app.destroy()
            except Empty:
                pass

            sleep(0.1)  # Simulate work.
            out_que.put(count)

        app.running = False
        app.finished = True
        start_btn.config(state=tk.NORMAL)

    def app_stop():
        out_que.put(STOP)
        app.destroy()

    def test_stop():
        if app.running:
            out_que.put(HIDE)
        elif app.finished:
            out_que.put(HIDE)
            in_que.get()

        stop_btn.config(state=tk.DISABLED)
        start_btn.config(state=tk.NORMAL)

    def test_start():
        while not in_que.empty():
            in_que.get()

        stop_btn.config(state=tk.NORMAL)
        start_btn.config(state=tk.DISABLED)

        thread = Thread(target=worker, daemon=True)
        thread.daemon = True
        thread.start()

    app = App(title='Main Window', theme='alt', geometry='350x150', exit_callback=app_stop)
    app.running = False
    app.finished = True
    app.rowconfigure(0, weight=1)
    app.rowconfigure(1, weight=1)
    app.columnconfigure(0, weight=1)

    start_btn = ttk.Button(app, text='Start Test', command=test_start)
    start_btn.grid(padx=10, pady=5, sticky=tk.NSEW)
    stop_btn = ttk.Button(app, text='Stop Test', state=tk.DISABLED, command=test_stop)
    stop_btn.grid(padx=10, pady=5, sticky=tk.NSEW)

    app.mainloop()


def progress_window(in_que, out_que, maximum):
    def hide():
        out_que.put(BREAK)
        pause_btn.config(text='Pause')
        app.withdraw()

    def pause():
        if progress_bar['value'] < progress_bar['maximum']:
            text = pause_btn.cget('text')
            text = 'Resume' if text == 'Pause' else 'Pause'
            pause_btn.config(text=text)
            out_que.put(PAUSE)
        else:
            pause_btn.config(text='Pause')

    def worker():
        while True:
            data = in_que.get()
            print(data)
            if data == HIDE:
                hide()
            elif data == STOP:
                app.destroy()
                out_que.put(STOP)
                break
            elif not data:
                app.deiconify()
                progress_bar["value"] = 0
            else:
                progress_bar["value"] = data
                app.update_idletasks()

    app = App(title='Progress', theme='clam', geometry='350x150', exit_callback=hide)

    app.rowconfigure(0, weight=1)
    app.rowconfigure(1, weight=1)
    app.columnconfigure(0, weight=1)
    app.columnconfigure(1, weight=1)

    progress_bar = ttk.Progressbar(app, orient=tk.HORIZONTAL, mode='determinate')
    progress_bar["maximum"] = maximum
    progress_bar.grid(padx=10, sticky=tk.EW, columnspan=1000)

    pause_btn = ttk.Button(app, text='Pause', command=pause)
    pause_btn.grid()
    cancel_btn = ttk.Button(app, text='Cancel', command=hide)
    cancel_btn.grid(row=1, column=1)

    thread = Thread(target=worker)
    thread.daemon = True
    thread.start()

    app.withdraw()
    app.mainloop()


if __name__ == '__main__':
    jobs = []
    que1 = Queue()
    que2 = Queue()
    process = 50  # The maximum amount of work to process, # items.

    for target in (main_window, progress_window):
        p = Process(target=target, args=(que1, que2, process))
        jobs.append(p)
        p.start()

    for j in jobs:
        j.join()
python tkinter ttk
1个回答
0
投票

老实说,我认为您做得正确。我测试了所有代码,而且似乎可以正常工作。我唯一要说的是将主线程用于UI并仅在必要时创建工作线程。

© www.soinside.com 2019 - 2024. All rights reserved.