我有两个脚本:
Processor_child.py :其目的是执行大量数据分析和清理操作。当单独运行时(没有Tkinter_parent.py),它必须执行与使用Tkinter_parent.py打包到GUI时相同的操作。
Tkinter_parent.py :其目的是为那些无法直接使用Processor_child的人提供GUI。
在Processor_child中,有for
个循环要求用户在每次迭代时输入。这些提示需要出现在Tkinter应用程序中,接受输入并将其发送回Processor_child。
以下代码执行此操作,只要Entry
中的数据(由循环添加),就会引发Pipe
字段。但是,它似乎经常“冻结”,卡住加载而不是通过代码。有时,它按预期完美运行。 (这些实例中的代码没有变化。)
如何解决此问题/使其更稳定?我在下面评论了“冻结”的位置。正在发生。
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()
和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)
这与更广泛的SO问题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?有关,并且来自一个已发布但无功能的解决方案。
截至2017年10月23日,仍然没有解决方案。
答案 0 :(得分:0)
您的Connection .poll()
电话正忙着等待并咀嚼CPU。但请注意,Connection对象具有fileno()
方法;这意味着您可以使用select / poll调用将进程置于休眠状态,同时等待它们为I / O做好准备。请注意,tkinter事件循环支持file handlers,允许您在不阻止UI的情况下执行此操作。
答案 1 :(得分:0)
最简单的方法是从控制台或gui获取输入,然后将结果发送到子程序。当您从控制台请求输入时,添加一个语句,如果设置了某个变量,则会打开Tkinter,并在那里获取信息。
答案 2 :(得分:0)
考虑以客户端 - 服务器方式编写应用程序。
客户端是Tk应用程序,可以连接到服务器。 服务器,只需执行客户端需要的任何内容。 这样,您可以分离处理。 有几种方法可以做到这一点,比如cherrypy,rabbitmq和类似的。
最近,在台式机应用程序中,我使用Electron连接到一台樱桃服务器,并使用Javascript从Electron请求AJAX。最终的图标只是启动服务器和客户端。 这使得我可以拥有更丰富的小部件集,因为网络比Tk更强大。
这将使您在将来可能拥有一个webapp。
HTH
答案 3 :(得分:0)
您尝试实现的行为似乎是在功能运行时与其进行通信。我认为你的问题可以通过使用发电机来解决。生成器允许您从函数中生成多个值,并将值发送到该函数。
Here是关于生成器的更多信息,如果你想知道它们是如何工作的。
我不完全确定这是否是您希望从程序中获得的行为,但我已修改您的代码以使用生成器而非多处理,并且它不再冻结:
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()
当你在它们上面调用next()
并且它们已经完成时,生成器会抛出一个StopIteration异常,所以一定要在适当的时候将next(p_review)
和p_review.send(...)
调用放在try块中。