Вопрос по python – Динамическое обновление окна Tkinter на основе последовательных данных

5

Я пытаюсь написать программу, которая получает данные от соединения через последовательный порт и автоматически обновляет окно Tkinter в режиме реального времени на основе этих данных.

Я пытался создать отдельный поток для окна, который периодически получает текущие данные из основного потока и обновляет окно, например:

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

Запуск дает мне эту ошибку:

Exception в потоке Thread-2: трассировка (последний вызов был последним): файл "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/threading.py", строка 522, в __bootstrap_inner self .run () Файл "analysis.py", строка 52, в прогоне 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: основной поток не находится в основном цикле

Когда я гуглю эту ошибку, я в основном получаю сообщения, в которых люди пытаются взаимодействовать с окном из двух разных тем, но я не думаю, что делаю это. Любые идеи? Спасибо

Как один поток для получения данных последовательного порта и другой поток для цикла анализа данных? Я сделаю это. user1363445
Вы пробовали запустить часть TK не в потоке? Т.е. просто запустите материал последовательного порта в потоке, и материал TK может остаться в основном процессе. Я подозреваю, что это может сработать ... Nick Craig-Wood

Ваш Ответ

2   ответа
7

новного процесса. Я втирал твой пример во что-то, что демонстрирует принцип

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()
Это было бы лучше, да, но моя цель состояла в том, чтобы показать ОП решение проблемы, а не обучать их механизмам IPC питонов; -) Nick Craig-Wood
ты должен использовать потокобезопасныйQueue объект для связи между потоками вместо использования простой переменной списка. Bryan Oakley
1

ать отдельный поток для опроса последовательного порта. Когда вы читаете данные с последовательного порта, вы можете поместить их в объект Queue.

В главном потоке GUI вы можете настроить опрос для периодической проверки очереди, используяafter чтобы запланировать опрос. Вызовите функцию, которая истощает очередь, а затем вызывает себя с помощьюafter для эффективной эмуляции бесконечного цикла.

Если данные, поступающие с датчика, поступают с довольно низкой скоростью, и вы можете опрашивать последовательный порт без блокировки, вы можете делать все это в основном потоке - вместо того, чтобы выталкивать и извлекать из очереди, ваш основной поток может просто посмотрите, есть ли доступные данные, и прочитайте их, если они есть. Вы можете сделать это, только если можно читать без блокировки, иначе ваш графический интерфейс будет зависать, пока он ожидает данные.

Например, вы можете заставить его работать так:

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, если и только если чтение не будет блокироваться.

Похожие вопросы