5

シリアルポート接続からデータを取得し、そのデータに基づいて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()

それを実行すると、このエラーが発生します:

スレッドThread-2の例外:トレースバック(最後の最後の呼び出し):ファイル "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/threading.py"、行522、__bootstrap_innerself。 run()ファイル "analysis.py"、52行目、run self.lbl1.pack()ファイル "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-tk/Tkinter .py "、1764行目、pack_configure + self._options(cnf、kw))RuntimeError:メインスレッドがメインループにありません

このエラーをグーグルで検索すると、ほとんどの場合、2つの異なるスレッドからウィンドウを操作しようとしている投稿が表示されますが、そうしているとは思いません。何か案は?本当にありがとう!

4

2 に答える 2

7

スレッドから 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()
于 2012-05-13T20:52:02.333 に答える
2

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

afterメイン GUI スレッドでは、 を使用してポーリングをスケジュールすることにより、キューを定期的にチェックするようにポーリングを設定できます。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)

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

于 2012-05-13T21:33:40.440 に答える