2012-05-13 20 views
5

シリアルポート接続からデータを取得し、そのデータに基づいてリアルタイムでTkinterウィンドウを自動的に更新するプログラムを作成しようとしています。シリアルデータに基づいてTkinterウィンドウを動的に更新する

私はこのように、定期的にメインスレッドから現在のデータを取得し、ウィンドウを更新し、ウィンドウのための別のスレッドを作成しようとしました:

serialdata = [] 
data = True 

class SensorThread(threading.Thread): 
    def run(self): 
     serial = serial.Serial('dev/tty.usbmodem1d11', 9600) 
     try: 
      while True: 
       serialdata.append(serial.readline()) 
     except KeyboardInterrupt: 
      serial.close() 
      exit() 

class GuiThread(threading.Thread): 
    def __init__(self): 
     threading.Thread.__init__(self) 
     self.root = Tk() 
     self.lbl = Label(self.root, text="") 

    def run(self): 
     self.lbl(pack) 
     self.lbl.after(1000, self.updateGUI) 
     self.root.mainloop() 

    def updateGUI(self): 
     msg = "Data is True" if data else "Data is False" 
     self.lbl["text"] = msg 
     self.root.update() 
     self.lbl.after(1000, self.updateGUI) 

if __name == "__main__": 
    SensorThread().start() 
    GuiThread().start() 

    try: 
     while True: 
      # A bunch of analysis that sets either data = True or data = False based on serialdata 
    except KeyboardInterrupt: 
     exit() 

が、それは私にこのエラーが発生します実行:

Exception in thread Thread-2: Traceback (most recent call last): File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/threading.py", line 522, in __bootstrap_inner self.run() File "analysis.py", line 52, in run self.lbl1.pack() File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-tk/Tkinter.py", line 1764, in pack_configure + self._options(cnf, kw)) RuntimeError: main thread is not in main loop

私はこのエラーをGoogleに取り上げると、2人の異なるスレッドからウィンドウとやりとりしようとしている投稿がほとんどですが、私はそれをやっているとは思いません。何か案は?本当にありがとう!

+1

スレッドではなくTKパートを実行しようとしましたか?つまり、スレッドでシリアルポートのものを実行するだけで、TKのものはメインプロセスに残ります。私はそれが動作する可能性があると思う... –

+0

シリアルポートのデータを取得するためのスレッドと同様に、データ解析ループのための別のスレッド?私はそのショットを与えるでしょう。 – user1363445

答えて

6

スレッドからTK GUIを実行しないでください - メインプロセスから実行してください。私はあなたの例を原則を示すものに砕きました。

from time import sleep 
import threading 
from Tkinter import * 

serialdata = [] 
data = True 

class SensorThread(threading.Thread): 
    def run(self): 
     try: 
      i = 0 
      while True: 
       serialdata.append("Hello %d" % i) 
       i += 1 
       sleep(1) 
     except KeyboardInterrupt: 
      exit() 

class Gui(object): 
    def __init__(self): 
     self.root = Tk() 
     self.lbl = Label(self.root, text="") 
     self.updateGUI() 
     self.readSensor() 

    def run(self): 
     self.lbl.pack() 
     self.lbl.after(1000, self.updateGUI) 
     self.root.mainloop() 

    def updateGUI(self): 
     msg = "Data is True" if data else "Data is False" 
     self.lbl["text"] = msg 
     self.root.update() 
     self.lbl.after(1000, self.updateGUI) 

    def readSensor(self): 
     self.lbl["text"] = serialdata[-1] 
     self.root.update() 
     self.root.after(527, self.readSensor) 

if __name__ == "__main__": 
    SensorThread().start() 
    Gui().run() 
+1

単純なリスト変数を使用する代わりにスレッド間で通信するには、スレッドセーフ 'Queue'オブジェクトを使用する必要があります。 –

+1

それはより良いはいですが、私の目的は、問題への解決策をOPに表示することでした、彼らにピュータンのIPCメカニズムを教えてくれません;-) –

1

GUIをメインスレッドに置き、別のスレッドを使用してシリアルポートをポーリングする必要があります。シリアルポートからデータを読み取ると、それをQueueオブジェクトにプッシュできます。

メインのGUIスレッドでは、afterを使用して定期的にキューをチェックしてポーリングをスケジュールするようにポーリングを設定できます。待ち行列を排水し、afterでそれ自身を呼び出す関数を呼び出すと、効果的に無限ループをエミュレートできます。

センサーからのデータがかなり遅い場合、ブロッキングせずにシリアルポートをポーリングすることができます。メインスレッドですべてを行うことができます。キューからプッシュしたりプルするのではなく、メインスレッドは、利用可能なデータがあるかどうかを確認し、存在する場合はそれを読み取ることができます。ブロックせずに読み込むことができる場合にのみこれを行うことができます。そうしないと、GUIがデータを待つ間にフリーズします。例えば

、あなたはそれがこのように動作させることができます:

def poll_serial_port(self): 
    if serial.has_data(): 
     data = serial.readline() 
     self.lbl.configure(text=data) 
    self.after(100, self.poll_serial_port) 

上記一度に一つの項目をオフに引っ張って、シリアルポートに毎秒10回をチェックします。もちろん、実際のデータ条件に合わせて調整する必要があります。これは、読み取りがブロックされない場合にのみ、Trueを返すことができるhas_dataのようなメソッドがあることを前提としています。

関連する問題