0

輪詢管我有兩個腳本: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日,仍然沒有解決方案。

+0

如果您的目標是通過tkinter GUI與命令行應用程序通信,您可能需要查看我的答案[here](https://stackoverflow.com/questions/21811464/how-can-i-embed -a-蟒-解釋器框架中有蟒-使用-Tkinter的/ 46545426#46545426)。它使用一個子進程,用單獨的線程來獲得輸出。 – Oli

+0

由於gui_input和input_done中的遞歸代碼可能會凍結 –

+0

由於gui_input和input_done中的遞歸代碼,它可能會凍結。而不是next_one(a_pipe),之後使用將安排一個獨立的進程,因此每個函數調用仍在等待從下一個調用返回/退出無限期,即frame.after(100,next_one,a_pipe ) –

回答

0

您的Connection.poll()呼叫正在等待並咀嚼CPU。但請注意,Connection對象有一個fileno()方法;這意味着您可以使用select/poll調用讓您的進程在等待它們準備好進入I/O時進入睡眠狀態。請注意,tkinter事件循環支持file handlers以允許您在不阻止UI的情況下執行此操作。

+0

這似乎是Unix系統可能的解決方案。不幸的是,我需要一個對Windows來說也很強大的解決方案,因爲大多數用戶都會在Windows機器上部署它。 Tkinter事件循環文件處理程序說,它們不能在鏈接文檔中的Windows上工作。 – user1318135

+0

在文件處理程序文檔中:「此功能在Windows上不可用。」 並在multiprocessing.connection.wait中:「Windows:...請注意,管道句柄和套接字句柄不是可等待的句柄。」 – user1318135

+0

這不是在Windows 10中修復的嗎?這應該與Linux兼容層一起發佈,否則它將無法很好地工作。 –

0

最簡單的方法是從控制檯或gui獲取輸入,然後將結果發送到子程序。當你從控制檯請求輸入時,如果設置了一些變量,添加一個打開Tkinter的語句,並在那裏獲取信息。

0

考慮以客戶端 - 服務器方式編寫應用程序。

客戶端,是Tk應用程序,它可以連接到服務器。服務器只需執行客戶端需要的任何東西。 這樣,你可以分離處理。 有幾種方法可以做到這一點,比如cherrypy,rabbitmq和類似的。

最近,在桌面應用程序中,我使用Electron來連接到cherrypy服務器,並使用Javascript使用Electron的AJAX請求。最後的圖標就是啓動服務器和客戶端。 這允許我有一個更豐富的部件集,因爲網絡比Tk更強大。

這將允許你在未來可能有一個web應用程序。

HTH

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