Tkinter的应用“冻结”,同时不断对内容(多)
轮询管我有两个脚本:Tkinter的应用“冻结”,同时不断对内容(多)
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日,仍然没有解决方案。
您的Connection.poll()
呼叫正在等待并咀嚼CPU。但请注意,Connection对象有一个fileno()
方法;这意味着您可以使用select/poll调用让您的进程在等待它们准备好进入I/O时进入睡眠状态。请注意,tkinter事件循环支持file handlers以允许您在不阻止UI的情况下执行此操作。
这似乎是Unix系统可能的解决方案。不幸的是,我需要一个对Windows来说也很强大的解决方案,因为大多数用户都会在Windows机器上部署它。 Tkinter事件循环文件处理程序说,它们不能在链接文档中的Windows上工作。 – user1318135
在文件处理程序文档中:“此功能在Windows上不可用。” 并在multiprocessing.connection.wait中:“Windows:...请注意,管道句柄和套接字句柄不是可等待的句柄。” – user1318135
这不是在Windows 10中修复的吗?这应该与Linux兼容层一起发布,否则它将无法很好地工作。 –
最简单的方法是从控制台或gui获取输入,然后将结果发送到子程序。当你从控制台请求输入时,如果设置了一些变量,添加一个打开Tkinter的语句,并在那里获取信息。
考虑以客户端 - 服务器方式编写应用程序。
客户端,是Tk应用程序,它可以连接到服务器。服务器只需执行客户端需要的任何东西。 这样,你可以分离处理。 有几种方法可以做到这一点,比如cherrypy,rabbitmq和类似的。
最近,在桌面应用程序中,我使用Electron来连接到cherrypy服务器,并使用Javascript使用Electron的AJAX请求。最后的图标就是启动服务器和客户端。 这允许我有一个更丰富的部件集,因为网络比Tk更强大。
这将允许你在未来可能有一个web应用程序。
HTH
看来,你正在努力实现的行为,而它的运行与功能进行通信。我认为你的问题可以通过使用生成器来解决。通过生成器,您可以从函数中生成多个值,并将值发送到该函数。
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(...)
。
如果您的目标是通过tkinter GUI与命令行应用程序通信,您可能需要查看我的答案[here](https://stackoverflow.com/questions/21811464/how-can-i-embed -a-蟒-解释器框架中有蟒-使用-Tkinter的/ 46545426#46545426)。它使用一个子进程,用单独的线程来获得输出。 – Oli
由于gui_input和input_done中的递归代码可能会冻结 –
由于gui_input和input_done中的递归代码,它可能会冻结。而不是next_one(a_pipe),之后使用将安排一个独立的进程,因此每个函数调用仍在等待从下一个调用返回/退出无限期,即frame.after(100,next_one,a_pipe ) –