2012-05-13 9 views
5

Tôi đang cố viết chương trình lấy dữ liệu từ kết nối cổng nối tiếp và tự động cập nhật cửa sổ Tkinter theo thời gian thực dựa trên dữ liệu đó.Tự động cập nhật cửa sổ Tkinter dựa trên dữ liệu nối tiếp

tôi đã cố gắng để tạo ra một chủ đề riêng biệt cho cửa sổ định kỳ được các dữ liệu hiện tại từ các chủ đề chính và cập nhật các cửa sổ, như thế này:

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

Chạy nó mang lại cho tôi lỗi này:

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

Khi tôi google lỗi này, tôi chủ yếu nhận được bài đăng mà mọi người đang cố gắng tương tác với cửa sổ từ hai chủ đề khác nhau, nhưng tôi không nghĩ rằng tôi đang làm điều đó. Bất kỳ ý tưởng? Cám ơn rất nhiều!

+1

Bạn có thử chạy phần TK không nằm trong chuỗi không? Tức là chỉ cần chạy các công cụ cổng nối tiếp trong một chủ đề và các công cụ TK có thể ở lại trong quá trình chính. Tôi nghi ngờ rằng có thể làm việc ... –

+0

Giống như một chủ đề để nhận dữ liệu cổng nối tiếp và một luồng khác cho vòng lặp phân tích dữ liệu? Tôi sẽ cho một phát bắn. – user1363445

Trả lời

6

Không chạy gui TK từ một luồng - hãy chạy nó từ quy trình chính. Tôi đã nghiền ví dụ của bạn thành một cái gì đó thể hiện nguyên tắc

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

bạn nên sử dụng đối tượng 'Queue' an toàn cho chủ đề để giao tiếp giữa các chuỗi thay vì sử dụng biến danh sách đơn giản. –

+1

Điều đó sẽ tốt hơn có, nhưng mục tiêu của tôi là hiển thị OP giải pháp cho vấn đề, không dạy cho họ cơ chế IPC trăn ;-) –

1

Bạn cần đặt GUI trong chủ đề chính và sử dụng một luồng riêng để thăm dò cổng nối tiếp. Khi bạn đọc dữ liệu của cổng nối tiếp, bạn có thể đẩy nó vào một đối tượng Queue.

Trong luồng GUI chính, bạn có thể thiết lập bỏ phiếu để kiểm tra hàng đợi định kỳ, bằng cách sử dụng after để lên lịch bỏ phiếu. Gọi một hàm thoát hàng đợi và sau đó gọi chính nó bằng after để mô phỏng một vòng lặp vô hạn một cách hiệu quả.

Nếu dữ liệu đến từ cảm biến có tốc độ khá chậm và bạn có thể thăm dò cổng nối tiếp mà không bị chặn, bạn có thể thực hiện tất cả trong chuỗi chính - thay vì đẩy và kéo từ hàng đợi, chủ đề chính chỉ có thể xem nếu có sẵn dữ liệu và đọc nó nếu có. Bạn chỉ có thể thực hiện việc này nếu có thể đọc mà không bị chặn, nếu không GUI của bạn sẽ bị đóng băng trong khi chờ dữ liệu.

Ví dụ, bạn có thể làm cho nó hoạt động như thế này:

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

trên sẽ kiểm tra các cổng nối tiếp 10 lần mỗi giây, kéo một mục tắt cùng một lúc. Bạn sẽ phải điều chỉnh điều đó cho các điều kiện dữ liệu thực tế của bạn. Điều này giả định rằng bạn có một số phương thức như has_data có thể trả về True nếu và chỉ khi đọc sẽ không chặn.