постоянный доступ к потокам в цикле tkinter

Я пытаюсь создать графический интерфейс, который постоянно отображает сигнал, полученный от микропроцессора. Я пытался сделать это, используя только классы, но это не удалось, поскольку был открыт только класс GUI. Теперь я реализовал многопоточность (или, по крайней мере, я так думаю!?), но каждый поток запускается только один раз. что заставляет меня поверить, что я не понимаю, как работает основной цикл в tkinter, поэтому могу ли я переделать свой код так, чтобы потоки стали активными?

import Tkinter
import tkMessageBox as messagebox
from serial import *
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import numpy as np
import time
import threading

go=0

x=[0.0001,0.0002,0.0003]
y=[3600,1000,2000]
stid=time.time()

root = Tkinter.Tk()
root.title("Serial gui")

class SensorThread(threading.Thread):
    def run(self):
        global run
        global x
        global y
        global stid
        print "run er ok"
        if go==1:
            print "go er ok"
            ser = Serial(5, 9600, timeout=1)
            f=ser.read(4)
            ser.close()
            x.append(time.time()-stid)
            y.append(f)


class Foo:
    def __init__(self, master):
        print "foo ok"

        frame = Tkinter.Frame(root)

        self.button_left = Tkinter.Button(frame,text="Start",
                                command=self.start)
        self.button_left.pack(side="left")

        self.button_right = Tkinter.Button(frame,text="Stop",
                                command=self.stop)
        self.button_right.pack(side="right")

        self.button_midt = Tkinter.Button(frame, text='Quit', command=self.terminate)
        self.button_midt.pack(side="bottom")

        fig = Figure()
        ax = fig.add_subplot(111, axisbg='black')

        canvas = FigureCanvasTkAgg(fig,master=master)
        canvas.show()
        canvas.get_tk_widget().pack(side='top', fill='both', expand=1)
        frame.pack()

        line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma
        line1.set_ydata(y)
        fig.canvas.draw()


    def start(self):
        global go
        go=1
        print go

    def stop(self):
        global go
        go=0
        print go

    def terminate(self):
        root.quit()     # stops mainloop
        root.destroy()  # this is necessary on Windows to prevent
                        # Fatal Python Error: PyEval_RestoreThread: NULL tstate

if __name__ == "__main__":
    SensorThread().run()
    Foo(root)
    root.mainloop() 

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

Супер, я изменил следующее в программе,

class SensorThread(threading.Thread):
def run(self):
    global run
    global x
    global y
    global stid
    #print "run er ok"
    if go==1:
        print "go er ok"
        ser = Serial(17, 9600, timeout=1)
        f=ser.read(4)
        ser.close()
        x.append(time.time()-stid)
        y.append(f)
        SensorThread().start()
    else:
        SensorThread().start()

class Foo:
....

if __name__ == "__main__":
SensorThread().start()
Foo(root)
root.mainloop() 

но он по-прежнему не обновляет фигуру, которую рисуют, разве это не должно делать это в классе Foo? Кроме того, теперь, когда я выхожу из скрипта Python, он по-прежнему использует 50% мощности ЦП, вероятно, потому, что поток датчиков теперь работает вечно!?


person AGr    schedule 22.01.2013    source источник
comment
Вы неправильно используете модуль threading; вы должны звонить не .run(), а .start(). Подробнее см. docs.python.org/2/library/threading.html. Информация.   -  person mmgp    schedule 23.01.2013
comment
Спасибо mmgp. Я попытался реализовать то, что я отредактировал, но моя фигура осталась такой же, как инициализированная. Но у меня все еще есть проблема, которую можно увидеть в моем отредактированном вопросе.   -  person AGr    schedule 23.01.2013
comment
Теперь вы также вызываете start внутри метода run :/ Здесь есть пара недоразумений. Например, вы вызываете plot только один раз внутри __init__; почему вы ожидали, что сюжет будет обновляться автоматически, просто потому, что вы изменили списки /после того, как сюжет был выполнен, и никогда не обновляли его снова?   -  person mmgp    schedule 23.01.2013
comment
Если вы можете считать датчик менее чем за пару сотен миллисекунд, потоки совершенно не нужны и добавляют ненужную сложность. Найдите в stackoverflow множество вопросов, связанных с использованием after.   -  person Bryan Oakley    schedule 05.11.2014


Ответы (1)


Посмотрите этот рецепт, он показывает, как это сделать:

http://code.activestate.com/recipes/82965-threads-tkinter-and-asynchronous-io/

person Gonzo    schedule 23.01.2013
comment
Это действительно не связано, другой поток здесь не взаимодействует с элементами графического интерфейса Tk. Это sys.exit(1) действительно не нужно, это всего лишь вопрос установки daemon = True для других созданных потоков. Вот упрощенный ответ (он только длиннее, потому что у него есть куча другого кода для выполнения других операций), использующий очереди для связи между потоками: stackoverflow.com/questions/14379106/. Если все построено с помощью потоков, можно также использовать event_generate и использовать очереди для этой конкретной задачи. - person mmgp; 23.01.2013
comment
Это может быть немного искажено, но это было отправной точкой для меня. Конечно, вы должны добавить фактическую рабочую часть в GuiPart.processIncoming, где в примере есть сообщение для печати. thread1.daemon = Верно, конечно, хороший совет. - person Gonzo; 23.01.2013
comment
Я не имел в виду, что он должен включать код для фактической рабочей части. Я имел в виду, что показанный там код, как вы сказали, искажен, а другой связанный ответ более прямой и обеспечивает тот же метод связи. Кроме того, просто чтобы подкрепить, настоящий вопрос не связан с тем, что дают эти только что связанные ответы. - person mmgp; 23.01.2013