问题
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