How can I implement an `input` method in a Tkinter parent script, with the displayed prompt and return value being sent back to a child script?

二次信任 提交于 2020-01-06 08:04:16

问题


I have two scripts:

Processor_child.py: Its purpose is to perform a number of data analysis and cleaning operations. This must perform the same operations when run alone (without Tkinter_parent.py) as it does when packaged into a GUI with Tkinter_parent.py.

Tkinter_parent.py: Its purpose is to provide a GUI for those who can't use Processor_child directly.

Where I'm struggling is to reproduce the python input function from Processor_child.py in the instance where these two are used together as a GUI. I need to present a prompt to the user (done in code below), transfer that response to the GUI (various options available for this, such as Pipe), and only after that response has been passed have Processor_child resume its operation / retrieve the entry value (how?).

Example code of the issue, with ### comments ### indicating where code needs to be inserted to perform desired functionality:

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

And 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've reviewed a number of related questions on SO, such as Getting a TKinter input stored into a string variable in the next function?: But they do not apply to cases where the input is being waited on before other actions are being performed (as in both cases I've presented here).

Other SO questions like How do I make the program wait for an input using an Entry box in Python GUI? don't work in these instances where the input in embedded within a loop within an executing function in another script; they rely on the 'waiting' being done within the GUI script itself.


回答1:


Note: This only works in rare cases. It is not a true 'solution'... I still face this issue. Fixing the approach below has its own SO question: Tkinter application 'freezes' while continually polling Pipe for contents (multiprocessing)


I've been able to find a pseudo-solution in which I add while loops that poll the Pipe, and upon finding that the Pipe does contain content, request the data in the Pipe. These while loops easily overload the application, and only seem to work in a simplified case. Definitely not the best way.

See amended code:

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

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


来源:https://stackoverflow.com/questions/46778491/how-can-i-implement-an-input-method-in-a-tkinter-parent-script-with-the-displ

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!