tkinter不会首先打开TopLevel

时间:2019-12-21 14:24:20

标签: python tkinter

我有一个按钮,命令起作用。此函数内部有2个函数。

  1. 首先是TopLevel,内部带有进度条
  2. 其次是主计算功能

import multiprocessing
import time
import tkinter.ttk as ttk
from tkinter import *

class Sth:
    def __init__(self, main):
        Button(main, text='Start processing!', command=self.go).pack(side=TOP)

    def popup(self):
        t = Toplevel()
        t.wm_attributes('-topmost', 1)
        self.f = Frame(t)
        self.f.pack()
        Label(self.f, text='Please, wait...').pack()

        self.pb = ttk.Progressbar(self.f, mode="determinate")
        self.pb.pack()

    def go(self):
        p1 = multiprocessing.Process(target=self.popup())
        p1.start()
        p1.join()
        p2 = multiprocessing.Process(target=self.process())
        p2.start()
        p2.join()

        # self.popup()
        # self.process()

    def process(self):
        for i in range(5):
            self.pb['value'] += 20
            print('doing sth')
            time.sleep(0.5)



if __name__ == "__main__":
    main = Tk()
    Sth(main)
    main.mainloop()

但是具有顶层和进度条的func,必须在执行之后才打开主calc函数,然后再打开。我做错了什么?

1 个答案:

答案 0 :(得分:0)

首先:所有GUI /小部件(在大多数GUI框架中,在大多数语言中)都必须在主线程(主进程)中运行。

您可以在单独的过程中运行计算,但是ProgressBar必须在主要过程中。您将需要queue才能将数据从计算发送到主流程。并且您将需要tkinter.after(milliseconds, function)来运行将定期运行的功能,以检查队列并更新ProgressBar(并且不会阻止mainloop)。


我在主进程中运行popup,它使用multiprocessing来运行计算(但没有join()会阻塞所有代码)。它还运行update_progressbar,它检查队列中是否有新数据要更新进度条。它使用after()在100毫秒后再次运行自身-这样就不会阻塞mainloop。钙

我使用queue.empty()来检查是否有要获取的数据,因为通常queue.get()会等待数据和块代码,然后mainloop

我不使用p.join(),因为它还会阻塞代码。关闭程序时可以检查它-但我跳过了这一部分。

import multiprocessing
import time
import tkinter.ttk as ttk
#from tkinter import * # not preferred
import tkinter as tk

class Sth:

    def __init__(self, main):
        tk.Button(main, text='Start processing!', command=self.popup).pack(side='top')

    def popup(self):
        self.t = tk.Toplevel()
        self.t.wm_attributes('-topmost', 1)

        self.f = tk.Frame(self.t)
        self.f.pack()

        tk.Label(self.f, text='Please, wait...').pack()

        self.pb = ttk.Progressbar(self.f, mode="determinate")
        self.pb.pack()

        self.queue = multiprocessing.Queue()
        p = multiprocessing.Process(target=self.process, args=(self.queue,))
        p.start()
        #p.join() # don't use in this place becaus it blocks all code

        self.update_progressbar()

    def update_progressbar(self):
        if not self.queue.empty(): # without this `get()` will wait for data and it will block code
            self.pb['value'] += self.queue.get()

        if self.pb['value'] < 100:
            self.t.after(100, self.update_progressbar)

    def process(self, q):
        for i in range(10):
            q.put(10)
            print('doing sth')
            time.sleep(.5)


if __name__ == "__main__":
    main = tk.Tk()
    Sth(main)
    main.mainloop()