Below is the file 'software/pixel.py' from this revision. You can also download the file.

#!/usr/bin/python3

import time
import maestro
from PIL import Image
from PIL import ImageSequence
import sys, getopt

default_min = 7232
default_max = 4032

def create_display(s1, s2, s3):
    pixels = [
        [
            [s3,  8, 7232, 4032],
            [s3,  9],
            [s3, 10],
            [s3, 11],
            [s3, 12],
            [s3, 13],
            [s3, 14],
            [s3, 15],
        ], [
            [s3,  0],
            [s3,  1],
            [s3,  2],
            [s3,  3],
            [s3,  4],
            [s3,  5],
            [s3,  6],
            [s3,  7],
        ], [
            [s2, 16],
            [s2, 17],
            [s2, 18],
            [s2, 19],
            [s2, 20],
            [s2, 21],
            [s2, 22],
            [s2, 23],
        ], [
            [s2,  8],
            [s2,  9],
            [s2, 10],
            [s2, 11],
            [s2, 12],
            [s2, 13],
            [s2, 14],
            [s2, 15],
        ], [
            [s2,  0],
            [s2,  1],
            [s2,  2],
            [s2,  3],
            [s2,  4],
            [s2,  5],
            [s2,  6],
            [s2,  7],
        ], [
            [s1, 16],
            [s1, 17],
            [s1, 18],
            [s1, 19],
            [s1, 20],
            [s1, 21],
            [s1, 22],
            [s1, 23],
        ], [
            [s1,  8],
            [s1,  9],
            [s1, 10],
            [s1, 11],
            [s1, 12],
            [s1, 13],
            [s1, 14],
            [s1, 15],
        ], [
            [s1,  0],
            [s1,  1],
            [s1,  2],
            [s1,  3],
            [s1,  4],
            [s1,  5],
            [s1,  6],
            [s1,  7],
        ]
    ]
    return display(pixels)

class pixel:
    def __init__(self, servo, id, min=default_min, max=default_max):
        self.servo = servo
        self.id = id
        self.value = 0
        self.dirty = True
        self.min = min
        self.max = max
        self.output()

    def set_min(self, value):
        self.min = value

    def set_max(self, value):
        self.max = value

    def set(self, value):
        if value < 0:
            value = 0
        if value > 255:
            value = 255
        if self.value != value:
            self.dirty = True
        self.value = value

    def get(self):
        return self.value

    def output(self):
        if self.dirty:
            value = ((self.max-self.min) * self.value / 255) + self.min
            #print('('+repr(self.id)+') '+repr(value))
            if self.servo:
                self.servo.setTarget(self.id, int(round(value)))
            self.dirty = False

    def cal(self):
        self.servo.setTarget(self.id, 1500*4)

class display:
    def __init__(self, params):
        self.p = []
        for param_row in params:
            row = []
            for args in param_row:
                row.append(pixel(*args))
            self.p.append(row)

    def init(self, x, y, pixel):
        self.p[y][x] = pixel

    def set(self, x, y, value):
        self.p[y][x].set(value)

    def draw(self, im):
        for y, row in enumerate(self.p):
            for x, pixel in enumerate(row):
                pixel.set(im.getpixel((x, y)))

    def output(self):
        for row in self.p:
            for pixel in row:
                if pixel != None:
                    pixel.output()

    def cal(self):
        for row in self.p:
            for pixel in row:
                if pixel != None:
                    pixel.cal()

    def print(self):
        for row in self.p:
            s = ""
            for pixel in row:
                if pixel != None:
                    if pixel.get() > 127:
                        s += "X"
                    else:
                        s += " "
            print(s)
        print("")


def slideshow(d, images, delay):
    for image in images:
        show(d, image)
        time.sleep(delay)

def show(d, image):
    global viewmode
    print("Displaying " + repr(image))
    im = Image.open(image)
    print(repr(im.info))
    for frame in ImageSequence.Iterator(im):
        im8 = frame.convert("L")
        print(repr(frame.info))
        if viewmode:
            im8.show()
        else:
            d.draw(im8)
            d.output()
            d.print()
        time.sleep(0.2)

def test(d, value):
    print("Outputting "+repr(value))
    for y in range(0,8):
        for x in range(0,8):
            d.set(x, y, value)
    d.output()

baudrate = 115200

usage = "'pixel.py [-h] [-t] [-v] [-c] [-d delay] <files>'"

def main(argv):
    global viewmode

    delay = 10
    testmode = False
    viewmode = False
    calmode = False
    try:
        opts, args = getopt.getopt(argv, "htvcd:", ["delay="])
    except getopt.GetoptError:
        print(usage)
        sys.exit(2)
    for opt, arg in opts:
        if opt in ('-h', "--help"):
            print(usage)
            sys.exit()
        elif opt in ("-d", "--delay"):
            delay = arg
        elif opt in ("-t", "--test"):
            testmode = True
        elif opt in ("-v", "--view"):
            viewmode = True
        elif opt in ("-c", "--cal"):
            calmode = True
    d = None
    if not viewmode:
        try:
            s1 = maestro.Controller(device=0x0c, baud=baudrate)
            s2 = maestro.Controller(device=0x0d, baud=baudrate)
            s3 = maestro.Controller(device=0x0e, baud=baudrate)
        except:
            s1 = None
            s2 = None
            s3 = None
        d = create_display(s1, s2, s3)
    if calmode:
        d.cal()
    elif testmode:
        test(d, 0)
        time.sleep(1)
        test(d, 255)
        time.sleep(1)
        test(d, 0)
    else:
        slideshow(d, args, delay)

    if not viewmode:
        s3.close
        s2.close
        s1.close


if __name__ == "__main__":
    main(sys.argv[1:])