Reputation: 1549
I'm using tk for a project's GUI. I have some very strange behavior with it, but only as a built executable on windows. Essentially I have a function launches a new process and needs to update some GUI elements after it completes. This works fine on OS X and Windows running interpreted. It works fine as a OS X binary. But as a Windows binary, the code causes a second main window to appear for unknown reason.
The app is launched via:
root = tk.Tk()
root.withdraw()
app = impy()
root.mainloop()
where
class impy(tk.Toplevel):
Then sometime later a user clicks a button which causes this to run:
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)
The callback loop eventually completes via the elif isinstance(obj, Implosion)
clause. Then those functions are all called. Something that they do causes a second Toplevel window to appear which is essentially a clone of the main window. The UI operations are applied to the clone. The __postImplosion__
method is just:
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())
it just has to loop over some check boxes and enable them. I'm pretty baffled since this is only a problem with Windows binaries. Any thoughts?
Update: Some more troubleshooting: The extra main window appears immediately after p.start()
is called. So this seems to be some weird behavior. Why can't I launch a process without an extra Tk window appearing?
Upvotes: 0
Views: 149
Reputation: 1549
OK, as usual, the solution is that I should have RTFM. According to the docs there is a magic function that must be called to fix weird problems with multiprocessing
when frozen on Windows.
if __name__ == "__main__":
freeze_support()
root = tk.Tk()
root.withdraw()
app = impy()
root.mainloop()
Upvotes: 1