0

J'ai deux scripts:Comment puis-je implémenter une méthode `input` dans un script parent Tkinter, avec l'invite affichée et la valeur de retour renvoyée à un script enfant?

Processor_child.py: Son but est d'effectuer un certain nombre d'analyses de données et les opérations de nettoyage. Cela doit effectuer les mêmes opérations lorsqu'il est exécuté seul (sans Tkinter_parent.py) que lorsqu'il est empaqueté dans une interface graphique avec Tkinter_parent.py.

Tkinter_parent.py: Son but est de fournir une interface graphique pour ceux qui ne peuvent pas utiliser directement Processor_child.

Là où je me bats est de reproduire la fonction python input de Processor_child.py dans le cas où ces deux sont utilisés ensemble comme une interface graphique. Je dois présenter une invite à l'utilisateur (fait dans le code ci-dessous), transférer cette réponse à l'interface graphique (diverses options disponibles pour cela, comme Pipe), et seulement après que cette réponse a été passée, Processor_child reprend son fonctionnement/récupère le valeur d'entrée (comment?).

code Exemple de la question, aveC### commentaires ### indiquant où le code doit être inséré pour effectuer la fonctionnalité souhaitée:

### Processor_child.py ### 
import pandas as pd 

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) 
     review_response = 'Yes' ### INSTEAD SOMEHOW GET RESPONSE FROM Tkinter_parent.py ### 

    if review_response in ['Yes', 'yes']: 
     for v in set(var_names): 
      smart_print(dataset[v].dropna()[:8], a_pipe) 
      if __name__ == "__main__": 
       local_response = input(review_message) 
      else: 
       local_response = None ### INSTEAD SOMEHOW GET RESPONSE FROM Tkinter_parent.py ### 
      if local_response in ['Yes', 'yes']: 
       affirmed.append(v) 

if __name__ == "__main__": 
    var_names = ['var1', 'var2'] 
    df = pd.read_csv('dummy.csv') 
    review_with_user(var_names, df) 

Et 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 

class GUI: 
    def __init__(self, master): 
     self.master = master 

def gui_input(message): 
    def input_done(event=None): 
     ### INSTEAD SOMEHOW send entry.get() back to Processor_child.py ### 
     pass 

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

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

I Nous avons passé en revue un certain nombre de questions connexes sur le SO, telles que Getting a TKinter input stored into a string variable in the next function?: Mais elles ne s'appliquent pas aux cas où l'entrée est attendue avant que d'autres actions soient effectuées (comme dans les deux cas, j'ai présenté ici).

D'autres questions SO comme How do I make the program wait for an input using an Entry box in Python GUI? ne fonctionnent pas dans ces cas où l'entrée est incorporée dans une boucle au sein d'une fonction d'exécution dans un autre script; ils reposent sur l'attente dans le script GUI lui-même.

Répondre

0

Remarque: Cela ne fonctionne que dans de rares cas. Ce n'est pas une véritable «solution» ... Je suis toujours confronté à ce problème. La fixation de l'approche ci-dessous dispose de sa propre question: Tkinter application 'freezes' while continually polling Pipe for contents (multiprocessing)


Je suis en mesure de trouver un pseudo-solution dans laquelle j'ajouter en boucles qui poll le Pipe, et sur la recherche que le Pipe contient du contenu, demander les données dans le Pipe. Ces boucles while surchargent facilement l'application, et ne semblent fonctionner que dans un cas simplifié. Certainement pas le meilleur moyen.

Voir le code modifié:

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

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

Une solution similaire avec multiprocessing.connection.wait() a le même problème, probablement parce que la méthode ne soit pas pris en charge sur Windows (je développe sur une machine Windows, pour les machines Windows). – user1318135