Tkinter Toplevel - 作为构建的windows exe的奇怪行为

时间:2014-03-20 21:18:30

标签: python windows tkinter

我正在使用tk作为项目的GUI。我有一些非常奇怪的行为,但只作为Windows上构建的可执行文件。本质上我有一个函数启动一个新的进程,并需要在完成后更新一些GUI元素。这适用于OS X和运行解释的Windows。它作为OS X二进制文件工作正常。但作为Windows二进制文件,代码导致第二个主窗口出现原因不明。

该应用程序通过以下方式启动:

root = tk.Tk()
root.withdraw()
app = impy()
root.mainloop()

,其中

class impy(tk.Toplevel):

然后用户点击某个按钮会导致其运行:

dialog = Progress_Dialog()
dialog.set_text('Implosion generation...')
dialog.update_idletasks()

# Use helper function:
parent_conn, child_conn = Pipe()
p = Process(target=ImplosionRunner.run, args=(child_conn,))
self.processes.append(p)

# start the process and send implosion:
p.start()
try:
    parent_conn.send(self.imp)
except:
    raise Exception('Implosion object passed to ImplosionRunner is not pickleable!')

obj = None
# Loop while the process is active:
def callback():
    nonlocal dialog, p, parent_conn
    if dialog.cancelled:
        dialog.withdraw()
        p.terminate()
        return

    # Try to receive from the Pipe:
    if parent_conn.poll():
        # Update the progress, or end otherwise:
        obj = parent_conn.recv()
        if isinstance(obj, Exception):
            from tkinter.messagebox import showerror
            showerror('Error!', 'A problem occurred generating the implosion (class '+self.imp.name()+')\n'+obj.__str__())
            dialog.withdraw()
            p.terminate()
            return
        elif isinstance(obj, float):
            dialog.set(100*obj)
        elif isinstance(obj, Implosion):
            # Pass info back to the main app:
            self.imp = obj
            self.after(10, self.__postImplosion__)
            dialog.withdraw()
            p.terminate()
            return

    self.after(25, callback)

self.after(10, callback)

回调循环最终通过elif isinstance(obj, Implosion)子句完成。然后调用这些函数。他们所做的事情导致出现第二个Toplevel窗口,它实际上是主窗口的克隆。 UI操作应用于克隆。 __postImplosion__方法只是:

for key in self.modControlChecks.keys():
        self.modControlChecks[key].configure(state=tk.NORMAL)

    # Run any modules that were already checked in refresh mode
    for key in self.modControlChecks.keys():
        self.modRedisplay[key] = (self.modControlVars[key].get() == 1)
    for mod in allModules():
        if self.modRedisplay[mod.name()]:
            self.__runModule__(mod.name())

它只需循环一些复选框并启用它们。我非常困惑,因为这只是Windows二进制文件的一个问题。有什么想法吗?

更新:更多问题排查:调用p.start()后会立即显示额外的主窗口。所以这似乎是一些奇怪的行为。为什么我不能在没有额外的Tk窗口的情况下启动进程?

1 个答案:

答案 0 :(得分:1)

好的,像往常一样,解决方案是我应该有RTFM。根据{{​​3}},在Windows上冻结时,必须调用一个魔术函数来修复multiprocessing的奇怪问题。

if __name__ == "__main__":
    freeze_support()
    root = tk.Tk()
    root.withdraw()
    app = impy()
    root.mainloop()