python python-3.x tkinter python-multiprocessing tkinter-entry

python - La aplicación Tkinter se "congela" mientras consulta continuamente Pipe para obtener contenido(multiprocesamiento)



python-3.x python-multiprocessing (4)

Su .poll() Connection .poll() está ocupada, esperando y masticando a través de la CPU. Pero tenga en cuenta que los objetos de conexión tienen un método fileno() ; esto significa que puede usar las llamadas select / poll para poner su proceso en reposo mientras espera que estén listos para E / S. Tenga en cuenta que el bucle de eventos tkinter admite manejadores de archivos para que pueda hacer esto sin bloquear la interfaz de usuario.

Tengo dos guiones:

Processor_child.py : Su propósito es realizar varias operaciones de análisis y limpieza de datos. Esto debe realizar las mismas operaciones cuando se ejecuta solo (sin Tkinter_parent.py) que cuando se empaqueta en una GUI con Tkinter_parent.py.

Tkinter_parent.py : Su propósito es proporcionar una GUI para aquellos que no pueden usar Processor_child directamente.

Dentro de Processor_child, hay bucles que piden al usuario información sobre cada iteración. Estas indicaciones deben aparecer en la aplicación Tkinter, aceptar la entrada y enviarla nuevamente a Processor_child.

El siguiente código hace esto, alzando un campo de Entry cada vez que hay datos en el Pipe (agregados por el bucle). Sin embargo, a menudo parece ''congelarse'', quedarse atascado cargando y no progresando a través del código. A veces, funciona perfectamente según lo previsto. (No hay cambios en el código en estas instancias)

¿Cómo puedo resolver esto / hacerlo más estable? He comentado a continuación donde está ocurriendo el ''congelamiento''.

Tkinter_parent.py:

### Tkinter_parent.py ### from tkinter import * from tkinter.filedialog import askopenfilename from tkinter import ttk from multiprocessing import Process, Pipe import pandas as pd import Processor_child import time class GUI: def __init__(self, master): self.master = master def gui_input(message, a_pipe = None): def input_done(event=None): entry.pack_forget() input_label.pack_forget() submit_button.pack_forget() a_pipe.send(entry.get()) next_one(a_pipe) entry = Entry(frame) input_label = ttk.Label(frame, text=message) entry.bind("<Return>", input_done) submit_button = ttk.Button(frame, text="Submit", command=input_done) input_label.pack() entry.pack() submit_button.pack() def file_select(): dataset_path = askopenfilename() if __name__ == ''__main__'': pipe1, pipe2 = Pipe() some_vars = [''a var'', ''another var''] a_df = pd.read_csv(dataset_path) p_review = Process(target=Processor_child.review_with_user, args=(some_vars, a_df, pipe2)) p_review.start() gui_input(pipe1.recv(), pipe1) #time.sleep(1) def next_one(pipe1): while pipe1.poll() != True: ### CAUSES CONSTANT LOADING WITHOUT PROGRESSION time.sleep(0.1) gui_input(pipe1.recv(), pipe1) if __name__ == ''__main__'': root = Tk() my_gui = GUI(root) root.style = ttk.Style() root.style.configure(''my.TButton'') root.style.configure(''my.TLabel'') canvas = Canvas(root) frame = Frame(canvas) frame.place() canvas.pack(side="left", fill="both", expand=True) canvas.create_window((45,50), window=frame, anchor="nw") ttk.Button(frame, text="Select", command=file_select).pack() root.mainloop()

Y processor_child:

### processor_child.py ### import pandas as pd from multiprocessing import * import time def smart_print(message, a_pipe = None): if __name__ == "__main__": print(message) else: a_pipe.send(message) def review_with_user(var_names, dataset, a_pipe = None): affirmed = [] review_message = ''Yes or no?'' if __name__ == "__main__": review_response = input(review_message) else: smart_print(review_message, a_pipe) while a_pipe.poll() != True: time.sleep(0.1) review_response = a_pipe.recv() if review_response in [''Yes'', ''yes'']: for v in dataset.columns: smart_print(dataset[v].dropna(), a_pipe) if __name__ == "__main__": local_response = input(review_message) else: while a_pipe.poll() != True: time.sleep(0.1) local_response = a_pipe.recv() if local_response in [''Yes'', ''yes'']: affirmed.append(v) smart_print(affirmed, a_pipe) if __name__ == "__main__": var_names = [''var1'', ''var2''] df = pd.read_csv(''dummy.csv'') review_with_user(var_names, df)

Esto está relacionado con la pregunta SO más amplia. ¿ Cómo puedo implementar un método de input en una secuencia de comandos principal de Tkinter, con el mensaje visualizado y el valor de retorno enviados a una secuencia de comandos secundaria? , y proviene de una solución publicada, pero no funcional.

A partir del 23 de octubre de 2017, todavía no hay una solución para esto.


Considere escribir su aplicación como cliente-servidor.

El cliente, es la aplicación Tk, que se puede conectar al servidor. el servidor, simplemente ejecuta lo que requiera el cliente. De esta forma, puedes separar el procesamiento. hay varias formas de hacerlo, como cherrypy, rabbitmq y similares.

Recientemente, en aplicaciones de sobremesa, utilicé Electron, para conectarme a un servidor cerebrito, y solicitudes de AJA de Electron usando Javascript. el icono final simplemente inicia ambos, el servidor y el cliente. esto me permite tener un conjunto de widgets más rico, ya que la web es más poderosa que Tk.

Eso te permitirá en un futuro posible tener una aplicación web.

HTH


La forma más sencilla es obtener la entrada, ya sea desde la consola o la interfaz gráfica de usuario, y luego enviar los resultados al programa secundario. Cuando solicite información desde la consola, agregue una declaración que abra Tkinter en su lugar si se establece alguna variable, y que obtenga la información allí.


Parece que el comportamiento que intenta lograr es comunicarse con una función mientras se está ejecutando. Creo que sus problemas podrían resolverse usando generadores. Un generador le permite obtener múltiples valores de una función y enviar valores a esa función.

Aquí hay más información sobre generadores si desea saber cómo funcionan.

No estoy del todo seguro de si este es exactamente el comportamiento que desea de su programa, pero he modificado su código para usar generadores en lugar de multiprocesamiento, y ya no se congela:

Processor_child.py:

### processor_child.py ### import pandas as pd import time def review_with_user(var_names, dataset): affirmed = [] review_message = ''Yes or no?'' review_response = yield review_message if review_response in [''Yes'', ''yes'']: for v in dataset.columns: local_response = yield str(dataset[v].dropna())+"/n"+review_message yield affirmed if __name__ == "__main__": var_names = [''var1'', ''var2''] df = pd.read_csv(''dummy.csv'') gen = review_with_user(var_names, df) # since it is now a generator, you need yo write some code to communicate with it via the console # it doesn''t print to the console or recieve input unless you do this manually while True: try: print(next(gen)) except StopIteration: break print(gen.send(input()))

Tkinter_parent.py:

### Tkinter_parent.py ### from tkinter import * from tkinter.filedialog import askopenfilename from tkinter import ttk import pandas as pd import Processor_child import time class GUI: def __init__(self, master): self.master = master def gui_input(message, p_review): def input_done(event=None): entry.pack_forget() input_label.pack_forget() submit_button.pack_forget() try: p_review.send(entry.get()) next_one(p_review) except StopIteration: # this code is executed when there is no more output from Processor_child.review_with_user return entry = Entry(frame) input_label = ttk.Label(frame, text=message) entry.bind("<Return>", input_done) submit_button = ttk.Button(frame, text="Submit", command=input_done) input_label.pack() entry.pack() submit_button.pack() def file_select(): dataset_path = askopenfilename() if __name__ == ''__main__'': some_vars = [''a var'', ''another var''] a_df = pd.read_csv(dataset_path) p_review = Processor_child.review_with_user(some_vars, a_df) gui_input(next(p_review), p_review) def next_one(p_review): try: gui_input(next(p_review), p_review) except StopIteration: # this code is executed when there is no more output from Processor_child.review_with_user return if __name__ == ''__main__'': root = Tk() my_gui = GUI(root) root.style = ttk.Style() root.style.configure(''my.TButton'') root.style.configure(''my.TLabel'') canvas = Canvas(root) frame = Frame(canvas) frame.place() canvas.pack(side="left", fill="both", expand=True) canvas.create_window((45,50), window=frame, anchor="nw") ttk.Button(frame, text="Select", command=file_select).pack() root.mainloop()

Los generadores emitirán una excepción StopIteration cuando llames a next() y hayan terminado, así que asegúrate de poner las next(p_review) y y p_review.send(...) llamadas dentro de try blocks donde corresponda.