0

I am trying to make a GUI that continually plot a signal received from a micro processor. I have tried to make this happen by use of classes only, but that failed since only the GUI class was oppend. Now i have implemented threading (or at least I think I have!?) but each thread is only run once. which make me believe that I don't understand how the mainloop in tkinter works, so can I remake my code in away that the threads become active?

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

I hope some of you can help my make this code become a program that realtime updates a plot.

Super I have changed the following in the program,

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

but it still doesn't update the figure that are plottet, shouldn't it do this in the Foo class? Also now when I exit or quite the python script it still uses 50% of CPU power probably because the Sensor thread now runs for ever!?

dsolimano
  • 8,870
  • 3
  • 48
  • 63
AGr
  • 1
  • 2
  • You are misusing the `threading` module; you are not supposed to call `.run()`, but `.start()`. See http://docs.python.org/2/library/threading.html for more information. – mmgp Jan 23 '13 at 01:22
  • Thank you mmgp I have tried to implement the things I have edited but my figure stay as initialized. But i still have some problem which can be seen in my edited question. – AGr Jan 23 '13 at 11:12
  • Now you calling `start` inside the `run` method too :/ There is a couple of misunderstandings there. For example, you call `plot` only once inside `__init__`; why did you expect the plot to be updated automatically simply because you changed the lists /after/ the plot has been performed, and never updated it again ? – mmgp Jan 23 '13 at 12:39
  • If you can read the sensor in under a couple hundred milliseconds, threads are completely unnecessary and add unneeded complexity. Search stackoverflow for a myriad of questions related to the use of `after`. – Bryan Oakley Nov 05 '14 at 20:52

1 Answers1

0

Check out this recipe, it shows how to do it:

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

Gonzo
  • 2,023
  • 3
  • 21
  • 30
  • That is really unrelated, the other thread here is not interacting with Tk GUI elements. That `sys.exit(1)` is really unneeded, it is only a matter of setting `daemon = True` for the other created threads . Here is a simplified answer (it is only longer because it has a bunch of other code for performing other operations) using queues to communicate between threads: http://stackoverflow.com/questions/14379106/mutli-threading-python-with-tkinter/14381671#14381671. If everything is built with threads, one can also use `event_generate` and void using queues for this specific task. – mmgp Jan 23 '13 at 14:48
  • It might be a little twisted but it was a starting point for me. Of course you have to add the actual work part in GuiPart.processIncoming where there is "print msg" in the example. "thread1.daemon = True" sure is a good hint. – Gonzo Jan 23 '13 at 15:01
  • I didn't mean it was supposed to include code for the actual work part. What I meant is the code shown there is, like you said, "twisted" and the other linked answer is more direct and provides the same communication method. Also, just to reinforce, the present question is unrelated to what these just linked answers are providing. – mmgp Jan 23 '13 at 15:57