In tkinter, I would like to create the following:
When the button foo is pressed, the function foo begins running immediately. A new window will pop up with a a "cancel" button that lets the user terminate the process midway through (as my actual process can take up to 30 minutes). If foo runs to completion, the window closes itself and notifies the user that the process completed.
Here is code that demonstrates my toy problem:
from tkinter import ttk, messagebox, Toplevel, Tk
import time
import multiprocessing
def foo():
for i in range(100):
print(i)
time.sleep(0.1)
class TerminatedProcess(Exception):
def __init__(self, str = "Process was terminated"):
self.error_str = str
class ProcessWindow(Toplevel):
def __init__(self, parent, process):
Toplevel.__init__(self, parent)
self.parent = parent
self. process = process
terminate_button = ttk.Button(self, text="Cancel", command=self.cancel)
terminate_button.grid(row=0, column=0)
self.grab_set() # so you can't push submit multiple times
def cancel(self):
self.process.terminate()
self.destroy()
raise TerminatedProcess
def launch(self):
self.process.start()
self.process.join()
self.destroy()
class MainApplication(ttk.Frame):
def __init__(self, parent, *args, **kwargs):
ttk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.button = ttk.Button(self, text = "foo", command = self.callback)
self.button.grid(row = 0, column = 0)
def callback(self):
try:
proc = multiprocessing.Process(target=foo)
process_window = ProcessWindow(self, proc)
process_window.launch()
except TerminatedProcess as e: # raised if foo is cancelled in other window
messagebox.showinfo(title="Cancelled", message=e.error_str)
else:
messagebox.showinfo(message="sucessful run", title="Finished")
def main():
root = Tk()
my_app = MainApplication(root, padding=(4))
my_app.grid(column=0, row=0)
root.mainloop()
if __name__ == '__main__':
main()
With this code, when the button foo is pressed, process_window is indeed made because the the function foo runs, and I get a "run successful" dialogue box, but the window never pops up! This is a problem because the user needs to have the option of terminating foo. Interestingly enough, If I delete process_window.launch(), I will still get the "run successful" message (as expected), but the process_window shows up.
I have tried putting the launch command inside the initialization, but that made the window not show up, either.
I also tried starting the process outside the window declaration as follows:
proc.start()
process_window = ProcessWindow(self, proc)
proc.join()
The window still failed to show up, but the process ran.
My question is, how do I need to arrange these pieces to generate the ProcessWindow AND automatically start the process when the ProcessWindow opens?
UPDATE (workaround): I am aware that I could create a start button inside the ProcessWindow that would run the function (and then disable the button after it had been pressed once), but I would still like to know how to implement what was described in the original question.
Perhaps you are looking for something like this:
from tkinter import ttk, messagebox, Toplevel, Tk
import time
import multiprocessing
def foo():
for i in range(100):
print(i)
time.sleep(0.1)
class ProcessWindow(Toplevel):
def __init__(self, parent, process):
Toplevel.__init__(self, parent)
self.parent = parent
self.process = process
terminate_button = ttk.Button(self, text="Cancel", command=self.cancel)
terminate_button.grid(row=0, column=0)
self.grab_set() # so you can't push submit multiple times
def cancel(self):
self.process.terminate()
self.destroy()
messagebox.showinfo(title="Cancelled", message='Process was terminated')
def launch(self):
self.process.start()
self.after(10, self.isAlive) # Starting the loop to check when the process is going to end
def isAlive(self):
if self.process.is_alive(): # Process still running
self.after(100, self.isAlive) # Going again...
elif self:
# Process finished
messagebox.showinfo(message="sucessful run", title="Finished")
self.destroy()
class MainApplication(ttk.Frame):
def __init__(self, parent, *args, **kwargs):
ttk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.button = ttk.Button(self, text = "foo", command = self.callback)
self.button.grid(row = 0, column = 0)
def callback(self):
proc = multiprocessing.Process(target=foo)
process_window = ProcessWindow(self, proc)
process_window.launch()
def main():
root = Tk()
my_app = MainApplication(root, padding=(4))
my_app.grid(column=0, row=0)
root.mainloop()
if __name__ == '__main__':
main()
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments