Search code examples
pythonmultithreadingtraitsenthought

Set attribute on a python thread from traits


I am new to python and I am implementing a simple serial adquisition in a thread. I can adquire the data using a class by

class CaptureAngles(threading.Thread, port)
    def __init__(self):
        threading.Thread.__init__(self)
        self.port_name = port
        ...

    def run():
        self.connect(self.port_name)
        ...

However, to better integrate with a graphical interface using the traits library I wrote the code as the following, which is no longer working. I am not able to define the attribute of a thread that is started from traits, what am I doing wrong?

This is the error reported

AttributeError: 'CaptureAngles' object has no attribute 'port_name'

And this the full code:

from threading import Thread
from time import sleep
from enthought.traits.api import *
from enthought.traits.ui.api import View, Item, ButtonEditor
from Queue import Queue

class TextDisplay(HasTraits):
    string =  String()

    view= View( Item('string',show_label=False, springy=True, style='custom' ))


class CaptureAngles(Thread):
    self.port_name = String('COM5') 

    def connect(self, port_name):
        self.port = serial.Serial(
            port = port_name,
            baudrate = 9600,
            )   
        self.display.string='Opening Serial Port...' + self.display.string
        self.port.close()
        self.port.open()

    def run(self):
        self.connect(self.port_name)
        self.display.string = 'Arduino started\n' + self.display.string
        self.port.flushInput()
        self.port.flushOutput()
        self.port.readline() # Discard first package (can be corrupt)
        while not self.wants_abort:
            rcv = self.port.readline() # Read the data and split into words 
            angle = int(rcv)
            self.display.string = '%d angle captured\n' % n_img \
                                                    + self.display.string
            self.close()

    def close(self):
        self.port.close()
        self.display.string='...Serial Port Closed!' + self.display.string


class Arduino(HasTraits):
    start_stop_capture = Button()
    display = Instance(TextDisplay)
    capture_angles = Instance(CaptureAngles)
    capture_angles.port_name = 'COM5'
    view = View(Item('start_stop_capture', show_label=False ))

    def _start_stop_capture_fired(self):
        if self.capture_angles and self.capture_angles.isAlive():
            self.capture_angles.wants_abort = True
        else:
            self.capture_angles = CaptureAngles()
            self.capture_angles.wants_abort = False
            self.capture_angles.display = self.display
            self.capture_angles.start()

class MainWindow(HasTraits):
    display = Instance(TextDisplay, ())

    arduino = Instance(Arduino)

    def _arduino_default(self):
        return Arduino(display=self.display)

    view = View('arduino','display', style="custom", resizable=True)

if __name__ == '__main__':
    MainWindow().configure_traits()

Solution

  • Ok, I got it: I was adding the attribute port_name before creating the instance.

    class Arduino(HasTraits):
        start_stop_capture = Button()
        display = Instance(TextDisplay)
        capture_angles = Instance(CaptureAngles)
        capture_angles.port_name = 'COM5' # <-- wrong: the object is not created yet
        ...
    

    instead of:

    def _start_stop_capture_fired(self):
        if self.capture_angles and self.capture_angles.isAlive():
            self.capture_angles.wants_abort = True
        else:
            self.capture_angles = CaptureAngles()
            self.capture_angles.port_name = 'COM5' # <-- correct
        ...