Tkinter 应用程序“冻结",同时不断轮询管道以获取内容(多处理) [英] Tkinter application 'freezes' while continually polling Pipe for contents (multiprocessing)

查看:28
本文介绍了Tkinter 应用程序“冻结",同时不断轮询管道以获取内容(多处理)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个脚本:

Processor_child.py:它的目的是执行一些数据分析和清理操作.这在单独运行(没有 Tkinter_parent.py)时必须执行与使用 Tkinter_parent.py 打包到 GUI 中时相同的操作.

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:它的目的是为那些不能直接使用 Processor_child 的人提供一个 GUI.

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

在 Processor_child 中,有 for 循环要求用户在每次迭代中输入.这些提示需要出现在 Tkinter 应用程序中,接受输入,并将其发送回 Processor_child.

Within Processor_child, there are for loops that ask the user for input on each iteration. These prompts need to appear in the Tkinter app, accept the input, and send it back to Processor_child.

下面的代码执行此操作,只要 Pipe 中有数据(由循环添加)就会引发 Entry 字段.但是,它似乎经常冻结",卡在加载中并且无法继续执行代码.有时,它会按预期完美运行.(在这些情况下代码没有变化.)

The code below does this, raising an Entry field whenever there's data in the Pipe (added by the loop). However, it often seems to 'freeze', getting stuck loading and not progressing through the code. Sometimes, it runs perfectly as intended. (No changes in the code in these instances.)

我该如何解决这个问题/让它更稳定?我在下面评论了冻结"发生的地方.

How can I resolve this / make it more stable? I've commented below where the 'freeze' is happening.

Tkinter_parent.py:

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:

And 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 问题有关 如何在 Tkinter 父脚本中实现 input 方法,并将显示的提示和返回值发送回子脚本?,以及来自那里发布的但不起作用的解决方案.


This is related to the broader SO question 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?, and comes from a posted, but non-functional, solution there.

截至 2017 年 10 月 23 日,仍然没有解决方案.

As of Oct 23, 2017 there is still not a solution to this.

推荐答案

您试图实现的行为似乎是在函数运行时与其进行通信.我认为您的问题可以通过使用生成器来解决.生成器可让您从一个函数中产生多个值,并将值发送到该函数.

It appears that the behaviour you are trying to achieve is to communicate with a function whilst it's running. I think that your problems could be solved by using generators. A generator lets you yield multiple values from a function, and send values to that function.

这里是关于生成器的更多信息,如果您想知道如何他们工作.

Here is some more information on generators if you want to know how they work.

我不完全确定这是否正是您想要的程序行为,但我修改了您的代码以使用生成器而不是多处理,并且它不再冻结:

I'm not entirely sure if this is exactly the behaviour you want from your program, but I have modified your code to use generators rather than multiprocessing, and it no longer freezes:

Processor_child.py:

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:

### 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 块.

Generators will throw a StopIteration exception when you call next() on them and they have finished, so be sure to put next(p_review) and and p_review.send(...) calls inside try blocks where appropriate.

这篇关于Tkinter 应用程序“冻结",同时不断轮询管道以获取内容(多处理)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆