2017-06-19 1 views
0

マルチプロセッシングをサポートするTkinterプログラムをビルドしようとしています。私は複数のModbusデバイスから読み込み、出力をGUIに表示する必要があります。Tkinter:Separating Processes

私はこれをプロセスを使ってコマンドラインで正常に実行しましたが、Tkinterでは読み込みを行うたびにGUIがフリーズします。ここで

は私のコードです:私は、スタートボタンを押すと、プロセスが完了するまで

import os 
from multiprocessing import Process 
import threading 
import queue 
import tkinter as tk 
from tkinter import * 
from tkinter import ttk 
import time 
import time as ttt 
import minimalmodbus 
import serial 
minimalmodbus.CLOSE_PORT_AFTER_EACH_CALL = True 


class Application(tk.Frame): 
    def __init__(self, master=None): 
     super().__init__(master) 
     self.gas = minimalmodbus.Instrument('COM3', 1) 
     self.gas.serial.baudrate = 9600 
     self.gas.serial.parity = serial.PARITY_NONE 
     self.gas.serial.bytesize = 8 
     self.gas.serial.stopbits = 1 
     self.gas.serial.timeout = 0.25 
     self.gas.mode = minimalmodbus.MODE_RTU 

     self.pack() 
     self.create_widgets() 

    def create_widgets(self): 
     self.first_gas_labelframe = LabelFrame(self, text="Gas 1", width=100) 
     self.first_gas_labelframe.pack() 

     self.value_label = Label(self.first_gas_labelframe, text="Value") 
     self.value_label.pack() 

     self.unit_label = Label(self.first_gas_labelframe, text="Unit") 
     self.unit_label.pack() 

     self.temp_label = Label(self.first_gas_labelframe, text="Temp") 
     self.temp_label.pack() 


     self.timer_button = tk.Button(self, text='Start', command=self.process) 

     self.quit = tk.Button(self, text="QUIT", fg="red", command=root.destroy) 
     self.quit.pack() 

     self.gas_list = [self.gas] 

    def reader(): 
     self.read = gas_num.read_registers(0,42) 
     self.value_label.config(text=self.read[0]) 
     self.unit_label.config(text=self.read[1]) 
     self.temp_label.config(text=self.read[2]) 

    def process(self): 
     for sen in self.gas_list: 
       self.proc = Process(target=self.reader, args=(sen,)) 
       self.proc.start() 
       self.proc.join() 


if __name__ == '__main__': 
     root = tk.Tk() 
     app = Application() 
     app.mainloop() 

プログラムがフリーズします。プロセスを実行している間にGUIを操作可能にするには、システムを正しく設定するにはどうすればよいですか?

+0

複数のセンサをポーリングするスレッドを使用する例を示します。https://stackoverflow.com/a/44551951/7432(実際のセンサ測定のためだけのスタブと、しかし) –

答えて

1

最も簡単な解決策は、このすべてを別のスレッドに入れることです。 GUIがフリーズする理由は、processメソッドがメインスレッドを消費し、完了するまでTkinterは何も更新しないためです。しかし、これは二回ボタンをクリックするからユーザーを停止しません

self.timer_button = tk.Button(self, text='Start', command=lambda: threading.Thread(target=self.process).start()) 

:ここ

は簡単な例です。これを制御する新しいメソッドを作成することができます。例:

import os 
from multiprocessing import Process 
import threading 
import queue 
import tkinter as tk 
from tkinter import * 
from tkinter import ttk 
import time 
import time as ttt 
import minimalmodbus 
import serial 
minimalmodbus.CLOSE_PORT_AFTER_EACH_CALL = True 
THREAD_LOCK = threading.Lock() 

class Application(tk.Frame): 
    def __init__(self, master=None): 
     super().__init__(master) 
     self.gas = minimalmodbus.Instrument('COM3', 1) 
     self.gas.serial.baudrate = 9600 
     self.gas.serial.parity = serial.PARITY_NONE 
     self.gas.serial.bytesize = 8 
     self.gas.serial.stopbits = 1 
     self.gas.serial.timeout = 0.25 
     self.gas.mode = minimalmodbus.MODE_RTU 

     self.pack() 
     self.create_widgets() 

    def create_widgets(self): 
     self.first_gas_labelframe = LabelFrame(self, text="Gas 1", width=100) 
     self.first_gas_labelframe.pack() 

     self.value_label = Label(self.first_gas_labelframe, text="Value") 
     self.value_label.pack() 

     self.unit_label = Label(self.first_gas_labelframe, text="Unit") 
     self.unit_label.pack() 

     self.temp_label = Label(self.first_gas_labelframe, text="Temp") 
     self.temp_label.pack() 


     self.timer_button = tk.Button(self, text='Start', command=self.start_thread) 

     self.quit = tk.Button(self, text="QUIT", fg="red", command=root.destroy) 
     self.quit.pack() 

     self.gas_list = [self.gas] 

    def check_thread(self): 
     if self.thread.is_alive(): 
      root.after(50, self.check_thread) 
     else: 
      # Thread completed 
      self.timer_button.config(state='normal') 

    def start_thread(self): 
     self.timer_button.config(state='disabled') 
     self.thread = threading.Thread(target=self.process) 
     self.thread.start() 
     root.after(50, self.check_thread) 

    def reader(self): 
     self.read = gas_num.read_registers(0,42) 
     self.value_label.config(text=self.read[0]) 
     self.unit_label.config(text=self.read[1]) 
     self.temp_label.config(text=self.read[2]) 

    def process(self): 
     with THREAD_LOCK: 
      for sen in self.gas_list: 
       self.proc = Process(target=self.reader, args=(sen,)) 
       self.proc.start() 
       self.proc.join() 


if __name__ == '__main__': 
    root = tk.Tk() 
    app = Application() 
    app.mainloop() 

Lockは、2つのスレッドが同時に実行されないようにするためのものです。また、ボタンが無効になっているため、ユーザーは完了するまでクリックできません。 root.afterメソッドを使用すると、実行前に一定期間待機するコールバックを作成できます。

マルチプロセスでは、プロセスを別々のプロセスで実行していますが、一度に1つだけ実行します。一度に多数を実行する場合は、joinコールを別の場所に移動する必要があります。私はプロセスが同時に実行されているどのように多くのわからないが、あなたはこのような何かを行うことができます。この実装では

processes = [] 
for sen in self.gas_list: 
    proc = Process(target=self.reader, args=(sen,)) 
    processes.append(proc) 
    proc.start() 
[x.join() for x in processes] 

は、私はクラス変数としてprocを割り当てる削除しました。これは、アイテムを渡して、self.gas_listをループ6つのプロセスのプールを開始します

私は適切にこのすべてをテストするためのライブラリやデータを持っていないが、それは動作するはず

...

EDIT 〜self.reader。これらが完了すると、秒が経過していないかどうかを確認し、上記のプロセスを再開します。これは永遠に、または例外が発生するまで実行されます。マルチプロセッシングモジュールからPoolをインポートする必要があります。ここで

def process(self): 
    with THREAD_LOCK: 
     pool = Pool(6) 
     while 1: 
      start_time = time.time() 
      pool.map(self.reader, self.gas_list) 
      execution_time = time.time() - start_time 
      if execution_time < 1: 
       time.sleep(1-execution_time) 

+0

グレート答えは、非常に有用!今、私は毎秒プロセスを楽しむ必要があり、6つのプロセスをまとめて行う必要があります。これは達成可能ですか? – GreenSaber

+0

@エヴァン確かに、それはすべきです。しかし、私はより多くの情報が必要です。これは、1秒間に1つのプロセスを開始するという一定のループにありますか?プロセスにはどれくらいの時間がかかりますか? –

+0

私は、1サイクルにつき約42の値を読み取る必要がある6つのデバイスを持っています。各デバイスはそうするために1秒未満を要する(デバイスは非常に速く読み込まれる)、もう一度読み直したい。 – GreenSaber

関連する問題