Доступ к основному потоку в сценарии TKinter?

Я думаю, что понимаю, почему я получаю следующую ошибку TclStackFree: incorrect freePtr. Call out of sequence?, но я не знаю, как решить эту проблему.

Резюме моего сценария

Мой сценарий Python, TKinter состоит из трех активных потоков. Основной поток и два дочерних потока. Один из дочерних потоков отвечает за прослушивание сообщений UDP, а другой отвечает за графическое отображение полученных данных (с использованием Matplotlib). Насколько я понимаю, проблема в том, что я пытаюсь получить доступ к графическому интерфейсу из двух дочерних потоков. в то же время. Например, я могу получать сообщения UDP и пытаться отобразить их в графическом интерфейсе, пытаясь отобразить ранее полученные данные. Это не редкость из-за скорости, с которой я получаю пакеты UDP (до 10 000 в секунду).

Код

Два дочерних потока создаются с использованием -

thread.start_new_thread(self.function, ())

Поток прослушивателя UDP

def Listen(self):
    if self.udpClient.IsConnected():
        while True:
            #Listen for messages
            data = self.udpClient.listen(1024)
            #Add the data to the gui
            self.AddNewData(data)
            if self.disconnectFlag:
                break

def AddNewData(self, data):
    self.listbox.insert(END, data)
    receivedData.append(data)

Поток графов

def UpdateGraph(self):
    while True:
        if readyToGraph:
            self.plot.clear()
            self.plot.hexbin(data[0], data[1], bins = "log", extent = self.extent)
            self.canvas.draw()

EDIT: 
if __name__ == "__main__":
    root = TK()
    gui = MainWindow(root, "Receiver")
    root.mainloop()

Как я уже сказал, я думаю, что проблема в том, что иногда поток прослушивателя пытается добавить данные в список в то же время, когда поток графа пытается обновить граф.

Я пытался...

Использование root.after_idle(...) перед попыткой изменить графический интерфейс, но это просто блокирует основной поток. Пример: self.listbox.insert(END, data) -> root.after_idle(self.listbox.insert, END, data) (я также называл root.after_idle перед self.plot.clear(), self.plot.hexbin(...) и self.canvas.draq())

Я также изучил threading.condition и queue.queue, как было предложено здесь, но я не смог найти много информации ни о том, ни о другом. (Поэтому я действительно не пробовал ни одно из решений)

Вопрос

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


person Jordan Carroll    schedule 20.11.2013    source источник
comment
На 90% уверен, что вы должны делать свой график в основном потоке.   -  person tacaswell    schedule 20.11.2013
comment
@tcaswell Позвонив root.after_idle? (Под root я подразумеваю TK, извините, я хотел указать это в вопросе)   -  person Jordan Carroll    schedule 20.11.2013
comment
также не импортируйте pyplot, если вы встраиваете (я не могу сказать, встраиваете ли вы). Также не используйте plot для имен, это технически не конфликтует с plot(), но это делает ваш код действительно трудным для чтения.   -  person tacaswell    schedule 20.11.2013
comment
У меня нет большого опыта работы с tk и потоковой передачей, но с QT вы не можете вызывать какие-либо функции построения графика из рабочего потока. Я предполагаю, что у TK есть какой-то обратный вызов или таймер, чтобы сообщить основному потоку о просмотре данных из потока чтения, когда он будет готов.   -  person tacaswell    schedule 20.11.2013
comment
Если я не беспокоюсь о добавлении данных в список и просто рисую данные, то все работает отлично. Так что это заставляет меня думать, что я могу построить график, не находясь в основном потоке.   -  person Jordan Carroll    schedule 21.11.2013
comment
то, что он не взорвется в одном случае, не означает, что он безопасен, это означает, что один случай не взорвется ;)   -  person tacaswell    schedule 21.11.2013


Ответы (1)


Вот ссылка на то, что обсуждалось в списке рассылки tkinter-discuss: https://mail.python.org/pipermail/tkinter-discuss/2013-November/thread.html

В этом суть:

Hi,

afaik, вызовы after() / after_idle() не являются потокобезопасными!

В моем исследовании, которое привело к коду, предоставленному Андреасом, я обнаружил, что метод event_generate() является единственным потокобезопасным способом вызвать поток tk, не позволяя ему что-либо опрашивать. (Как упоминал Гвидо и видел на других страницах, кажется, что все остальные опрашивают... почему?)

бргдс,

-- Ян

person User    schedule 20.11.2013
comment
Поэтому я не совсем уверен, как это реализовать. Вы знаете, как использовать event_generate? - person Jordan Carroll; 21.11.2013