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

當我谷歌這個錯誤,我主要是在人們試圖從兩個不同的線程與窗口交互的帖子,但我不認爲我這樣做。有任何想法嗎?非常感謝!

+1

你有沒有嘗試在一個線程中運行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顯示解決問題的辦法,而不是教會他們pythons IPC機制;-) –

1

你需要把GUI在主線程,並使用一個單獨的線程來輪詢串行端口。當您從串口讀取數據時,您可以將其推送到Queue對象上。

在主界面線程您可以設置輪詢定期檢查隊列中,通過使用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次每秒,拉一個項目送行的時間。當然,您必須根據您的實際數據條件進行調整。這假設你有一些像has_data這樣的方法,當且僅當讀取不會被阻塞時才能返回True。