2016-06-07 11 views
3

In tkinter öffnet, würde Ich mag die folgende erstellen:Wie eine kündbare Funktion auszuführen, wenn ein neues Fenster Tkinter

Wenn die Taste foo gedrückt wird, beginnt die Funktion foo sofort ausgeführt wird. Ein neues Fenster öffnet sich mit einem "Abbrechen" -Button, mit dem der Benutzer den Prozess in der Mitte beenden kann (mein tatsächlicher Prozess kann bis zu 30 Minuten dauern). Wenn foo vollständig ausgeführt wird, schließt sich das Fenster selbst und benachrichtigt den Benutzer, dass der Prozess abgeschlossen wurde.

Hier ist Code, mein Spielzeug Problem veranschaulicht:

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() 

Mit diesem Code, wenn die Taste foo gedrückt wird, wird process_window in der Tat gemacht, weil die die Funktion foo läuft, und ich erhalte eine „laufen erfolgreich“ Dialogbox, aber das Fenster erscheint nie! Dies ist ein Problem, da der Benutzer die Möglichkeit haben muss, foo zu beenden. Interessanterweise, wenn ich process_window.launch() lösche, bekomme ich immer noch die "run successful" Nachricht (wie erwartet), aber das process_window erscheint.

Ich habe versucht, den Startbefehl innerhalb der Initialisierung zu setzen, aber das hat das Fenster auch nicht angezeigt.

Ich habe auch versucht, den Prozess vor dem Fenster Erklärung begann wie folgt:

proc.start() 
process_window = ProcessWindow(self, proc) 
proc.join() 

Das Fenster zu zeigen, noch immer nicht, aber der Prozess lief.

Meine Frage ist, wie muss ich diese Teile anordnen, um das ProcessWindow UND den Prozess automatisch zu starten, wenn das ProcessWindow öffnet?

UPDATE (Problemumgehung): Ich bin mir bewusst, dass ich eine Startschaltfläche innerhalb des ProcessWindow erstellen konnte, die die Funktion ausführen würde (und deaktivieren Sie die Schaltfläche, nachdem es einmal gedrückt wurde), aber ich möchte immer noch wissen, wie implementieren, was in der ursprünglichen Frage beschrieben wurde.

+1

Sie machen unsere Hilfe Abschnitt lesen a [Minimal komplette Nachweisbare Beispiel] (http://stackoverflow.com/help/mcve) betrachten kann, ich müsste entweder viel Work-Around-Code implementieren, um dies zu testen, und eine Menge irrelevanten Code herausfiltern, um das Grundproblem zu verstehen. –

+2

@ TadhgMcDonald-Jensen Danke für die Köpfe hoch. In dieser neuen Bearbeitung kann der größte Teil des Codes (main, foo, die TerminateProcess-Ausnahme) ignoriert werden, wurde jedoch als ausführbar angegeben, und alles, was nicht wichtig war, wurde entfernt. –

Antwort

1

Sie suchen vielleicht nach etwas wie folgt aus:

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() 
+0

Das ist genau das, was ich wollte. Vielen Dank! –