[Twisted-Python] Advice on porting Python application to Twisted

Matt Haggard haggardii at gmail.com
Tue Aug 27 10:31:24 MDT 2013

On Mon, Aug 26, 2013 at 9:06 PM, Matthew Humphrey <mhumphrey at gmail.com> wrote:
> 2) A thread that runs a loop which manages the hardware. It does this with a
> simple state machine composed of a base class and subclasses for all the
> states that the hardware can be in (starting, idle, displaying status on the
> lcd, dispensing a treat, recovering from treat dispense cycle, shutting
> down). After initializing the hardware and the initial state, the thread
> loops continuously calling a "timeTick" event to the current state. WIthin
> the states, the code looks at various hardware status (like whether a button
> is pressed) and decides to take action or to trigger transition to another
> state.

I am not familiar with how you communicate with hardware on a
Raspberry PI.  Can you link to your existing code?  In my brief
reading this morning, I'm guessing you might be using the RPi.GPIO
library?  If so, I'm looking at
https://code.google.com/p/raspberry-gpio-python/wiki/Inputs which
indicates that wait_for_edge() or event_detected() might be useful
(instead of a "continuously called a 'timeTick' event").  But I'm not
sure what thread the callbacks to those functions are called in.

I've also come across
https://www.kernel.org/doc/Documentation/gpio.txt which makes me think
it would be possible to register the GPIO events with the Twisted
reactor (but I've never done that and don't have a Pi to test with).
Perhaps it's time to buy one.

> 3) A thread that runs continuously capturing images from a webcam. This
> thread captures low resolution images continuously, and compares sucessive
> frames to see if there is significant number of different pixels (motion
> detect). If so, it captures a higher resolution image, updates a symbolic
> link to point to the most recent image captured, and deletes any excessive
> files from previous captures. The images captured for the motion detect are
> handled by executing a process and capturing the stdout. The higher res
> images captured by executing a process that writes directly to a file.

Someone with more intelligence than me should answer this, but here's
my attempt:

You can do this without threads since you're just spawning processes
for the image captures.  I'm assuming you only want to capture as many
low res images as you can process. Here's one way to do it:

from twisted.internet import reactor, task, defer
from twisted.internet.utils import getProcessOutput
from twisted.python import log
from twisted.python.filepath import FilePath

class ImageCapturer:

    lowres_args = ('/path/to/lowres_args',)
    highres_args = ('/path/to/highres_args', ['/path/to/outpufile'])

    def __init__(self):
        self._last_image = None

    def getLowResImage(self):
        return getProcessOutput(*self.lowres_args)

    def getHighResImage(self):
        log.msg('getting high res image')
        d = getProcessOutput(*self.highres_args)
        return d

    def start(self):

    def doCycle(self):
        d = self.getLowResImage()
        return d

    def finishCycle(self, _ignored):

    def processLowResImage(self, image):
        if self._last_image:
            if self.isDifferentEnough(self._last_image, image):
                # if it's bad for the lowres script and the highres script
                # to run at the same time, you may want to wait for the high
                # res script to finish (this code doesn't do that).

        self._last_image = image

    def isDifferentEnough(self, image1, image2):
        Compare the two images for differences.  Return True if it warrents
        a high res capture.

        If this is a long-running function, you could send it off to a thread
        with deferToThread.
        return image1 != image2

def example(reactor):
    # for example
    capturer = ImageCapturer()

    # since I don't have an image capture script, I'll just list a directory.
    # low res directory capture :)
    capturer.lowres_args = ('/bin/ls', ['/tmp/'])

    # high res directory capture :)
    capturer.highres_args = ('/bin/ls', ['-al', '/tmp/'])

    # add and remove a file
    tmpfile = FilePath('/tmp/foo')
    reactor.callLater(1, tmpfile.setContent, 'foo')
    reactor.callLater(2, tmpfile.remove)
    return task.deferLater(reactor, 10, lambda:None)

if __name__ == '__main__':
    import sys

More information about the Twisted-Python mailing list