2012-05-13 3 views
5

직렬 포트 연결에서 데이터를 가져 와서 해당 데이터를 기반으로 실시간으로 Tkinter 창을 자동으로 업데이트하는 프로그램을 작성하려고합니다.직렬 데이터를 기반으로 Tkinter 창을 동적으로 업데이트

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

: 그것은 나를이 오류를 제공 실행

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

:

나는 정기적으로 다음과 같이 창을 메인 스레드에서 현재 데이터를 가져 와서 업데이트 창에 대한 별도의 스레드를 만들려고

이 오류를 Google로 검색 할 때 대부분 사람들이 두 개의 다른 스레드에서 창과 상호 작용하려고하는 게시물을 가져 오지만 실제로 그렇게하지는 않는다고 생각합니다. 어떤 아이디어? 정말 고마워!

+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를 주 스레드에 넣고 별도의 스레드를 사용하여 직렬 포트를 폴링해야한다. 직렬 포트에서 데이터를 읽을 때 큐 객체로 푸시 할 수 있습니다.

주 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 번을 확인합니다. 물론 실제 데이터 조건에 맞게 조정해야합니다. 여기에는 has_data과 같은 메서드가 있다고 가정합니다.이 메서드는 읽기가 차단되지 않는 경우에만 True를 반환 할 수 있습니다.

관련 문제