Skip to content

Display

Display

Game Display is a class that abstracts different S^3 panels into one display

Source code in display/display.py
class Display:
    """Game Display is a class that abstracts different S^3 panels into one display"""

    def __init__(self, board_objects, x_width, y_height):
        """Constructor

        Args:
            board_objects (int[][]): 2d array of seven segment objects oriented how the sign is put together, i.e. [[panel0, panel1],[panel2,panel3]]
            x_width (int): number of digits in the display on the x axis
            y_height (int): number of pixels on the y axis (each vertical digit is split into two pixels)
        """
        self.board_objects = board_objects
        self.x_width = int(x_width)
        self.y_height = int(y_height)
        self.display_buf = [
            [0 for x in range(self.x_width)] for y in range(self.y_height // 2)
        ]
        self.changed_list = []

    def draw_raw(self, x, y, value, push=False):
        """Draw to a specific segment on the screen

        Args:
            x (int): x coordinate
            y (int): y coordinate
            value (int): which leds to turn on for the segment
            push (bool): when true all the recent changes are pushed to the display
        """
        self.display_buf[y][x] = value
        self.changed_list.append((x, y))
        if push:
            self.push()

    def get_raw(self, x, y):
        """Get the value of the segment

        Args:
            x (int): x coordinate
            y (int): y coordinate

        Returns:
            (int): value of the segment
        """
        return self.display_buf[y][x]

    def draw_pixel(self, x, y, value, combine=True, push=False):
        """Draw shape to one pixel location

        Args:
            x (int): x coordinate
            y (int): y coordinate
            value (int): which leds to turn on for the pixel (1 for bottom, 2 for left, 4 for top, 8 for right, add sides together for multiple sides)
            combine (bool): digits are split into two pixels, when drawing to one combine keeps what is already drawn on the other side of the digit
            push (bool): when true all the recent changes are pushed to the display
        """
        half_height = y // 2
        if value == 0 and combine:
            current_value = self.display_buf[half_height][x]
            if y % 2:
                if current_value & 0x62:
                    self.display_buf[half_height][x] = current_value & 0x63
                else:
                    self.display_buf[half_height][x] = current_value & 0x62
            else:
                if current_value & 0x1C:
                    self.display_buf[half_height][x] = current_value & 0x1D
                else:
                    self.display_buf[half_height][x] = current_value & 0x1C
            self.changed_list.append((x, y // 2))
            return

        if y % 2:
            value = (
                (value & 1) << 3
                | (value & 2) << 1
                | (value & 4) >> 2
                | (value & 8) << 1
            )
        else:
            value = (value & 3) | (value & 4) << 4 | (value & 8) << 2
        # if value | self.display_buf[half_height][x] == self.display_buf[half_height][x]:
        #     return
        if combine:
            value = self.display_buf[half_height][x] | value
        self.display_buf[half_height][x] = value
        self.changed_list.append((x, y // 2))
        if push:
            self.push()

    def get_pixel(self, x, y):
        """Get the value already at the pixel

        Args:
            x (int): x coordinate
            y (int): y coordinate

        Returns:
            (int): value of the pixel
        """
        half_height = y // 2
        value = self.display_buf[half_height][x]

        if y % 2:
            half_value = (
                bool(value & 16) << 3
                | bool(value & 1) << 2
                | bool(value & 4) << 1
                | bool(value & 8)
            )
        else:
            half_value = (
                bool(value & 32) << 3
                | bool(value & 64) << 2
                | bool(value & 2) << 1
                | bool(value & 1)
            )

        return half_value

    def push(self):
        """Push all the recent changes to the display"""
        for location in self.changed_list:
            self.board_objects[location[1] // 6][location[0] // 16].raw2(
                location[0] % 16,
                location[1] % 6,
                self.display_buf[location[1]][location[0]],
            )
        for row in self.board_objects:
            for board in row:
                board.flush()
        self.changed_list.clear()

    def clear(self):
        """Clear all the panels on the display"""
        self.display_buf = [
            [0 for x in range(self.x_width)] for y in range(self.y_height // 2)
        ]
        for row in self.board_objects:
            for board in row:
                board.clear()
        self.changed_list.clear()

    def draw_hline(self, start_x, start_y, length, top=True, combine=True, push=False):
        """Draw horizontal line

        Args:
            start_x (int): x coordinate
            start_y (int): y coordinate
            length (int): length of line to draw
            top (int): draw horizontal line on the top or bottom of the pixel
            combine (int): digits are split into two pixels, when drawing to one combine keeps what is already drawn on the other side of the digit
            push (int): when true all the recent changes are pushed to the display
        """
        half_height = start_y // 2
        if start_y % 2:
            for x in range(length):
                if top:
                    if combine:
                        self.display_buf[half_height][x + start_x] = (
                            self.display_buf[half_height][x + start_x] | 0x01
                        )
                    else:
                        self.display_buf[half_height][x + start_x] = 0x01
                else:
                    if combine:
                        self.display_buf[half_height][x + start_x] = (
                            self.display_buf[half_height][x + start_x] | 0x08
                        )
                    else:
                        self.display_buf[half_height][x + start_x] = 0x08
                self.changed_list.append((x + start_x, half_height))
        else:
            for x in range(length):
                if top:
                    if combine:
                        self.display_buf[half_height][x + start_x] = (
                            self.display_buf[half_height][x + start_x] | 0x40
                        )
                    else:
                        self.display_buf[half_height][x + start_x] = 0x40
                else:
                    if combine:
                        self.display_buf[half_height][x + start_x] = (
                            self.display_buf[half_height][x + start_x] | 0x01
                        )
                    else:
                        self.display_buf[half_height][x + start_x] = 0x01
                self.changed_list.append((x + start_x, half_height))
        if push:
            self.push()

    def draw_vline(self, start_x, start_y, length, left=True, combine=True, push=False):
        """Draw vertical line

        Args:
            start_x (int): x coordinate
            start_y (int): y coordinate
            length (int): length of line to draw
            left (int): draw vertial line on the left or right of the pixel
            combine (bool): digits are split into two pixels, when drawing to one combine keeps what is already drawn on the other side of the digit
            push (bool): when true all the recent changes are pushed to the display
        """
        for y in range(length):
            y_loc = start_y + y
            y_half = y_loc // 2
            if y_loc % 2:
                if left:
                    if combine:
                        self.display_buf[y_half][start_x] = (
                            self.display_buf[y_half][start_x] | 0x04
                        )
                    else:
                        self.display_buf[y_half][start_x] = 0x04
                else:
                    if combine:
                        self.display_buf[y_half][start_x] = (
                            self.display_buf[y_half][start_x] | 0x10
                        )
                    else:
                        self.display_buf[y_half][start_x] = 0x10
            else:
                if left:
                    if combine:
                        self.display_buf[y_half][start_x] = (
                            self.display_buf[y_half][start_x] | 0x02
                        )
                    else:
                        self.display_buf[y_half][start_x] = 0x02
                else:
                    if combine:
                        self.display_buf[y_half][start_x] = (
                            self.display_buf[y_half][start_x] | 0x20
                        )
                    else:
                        self.display_buf[y_half][start_x] = 0x20
            self.changed_list.append((start_x, y_half))
        if push:
            self.push()

    def draw_shape_line(
        self, start_x, start_y, end_x, end_y, value, combine=True, push=False
    ):
        """Draw line with given value at each pixel, can be diagonal

        Args:
            start_x (int): starting x coordinate
            start_y (int): starting y coordinate
            end_x (int): ending x coordinate
            end_y (int): ending y coordiante
            value (int): which leds to turn on for the pixel (1 for bottom, 2 for left, 4 for top, 8 for right, add sides together for multiple sides)
            combine (bool): digits are split into two pixels, when drawing to one combine keeps what is already drawn on the other side of the digit
            push (bool): when true all the recent changes are pushed to the display
        """
        if start_x != end_x:
            slope = (end_y - start_y) / (end_x - start_x)
            b = start_y - slope * start_x
            for x in range(min(start_x, end_x), max(start_x, end_x) + 1):
                self.draw_pixel(x, round(b) + round(slope * x), value, combine)
        else:
            for y in range(min(start_y, end_y), max(start_y, end_y) + 1):
                self.draw_pixel(start_x, y, value, combine)
        if push:
            self.push()

    def draw_text(self, x, y, msg, combine=True, push=False):
        """Print a message to the screen, y_height-2 is lowest y value accepted without error

        Args:
            x (int): x coordinate
            y (int): y coordinate
            msg (int): string message to print
            combine (bool): digits are split into two pixels, when drawing to one combine keeps what is already drawn on the other side of the digit
            push (bool): when true all the recent changes are pushed to the display
        """
        if y % 2:
            for pos, char in enumerate(msg):
                value = symbols.get_char2(char)
                # get top and bottom values out of value
                top_value = (value & 0x20) >> 2 | (value & 0x40) >> 4 | (value & 0x02)
                bottom_value = (
                    (value & 0x04) >> 1
                    | (value & 0x08) >> 3
                    | (value & 0x10) >> 1
                    | (value & 0x01) << 2
                )
                self.draw_pixel(x + pos, y, top_value, combine=combine)
                self.draw_pixel(x + pos, y + 1, bottom_value, combine=combine)
        else:
            half_y = y // 2
            for pos, char in enumerate(msg):
                value = symbols.get_char2(char)
                self.display_buf[half_y][x + pos] = value
                self.changed_list.append((x + pos, half_y))
        if push:
            self.push()

    # work in progress
    def fill_box(self, start_x, start_y, x_len, y_len, push=False):
        if push:
            self.push()

__init__(board_objects, x_width, y_height)

Constructor

Parameters:

Name Type Description Default
board_objects int[][]

2d array of seven segment objects oriented how the sign is put together, i.e. [[panel0, panel1],[panel2,panel3]]

required
x_width int

number of digits in the display on the x axis

required
y_height int

number of pixels on the y axis (each vertical digit is split into two pixels)

required
Source code in display/display.py
def __init__(self, board_objects, x_width, y_height):
    """Constructor

    Args:
        board_objects (int[][]): 2d array of seven segment objects oriented how the sign is put together, i.e. [[panel0, panel1],[panel2,panel3]]
        x_width (int): number of digits in the display on the x axis
        y_height (int): number of pixels on the y axis (each vertical digit is split into two pixels)
    """
    self.board_objects = board_objects
    self.x_width = int(x_width)
    self.y_height = int(y_height)
    self.display_buf = [
        [0 for x in range(self.x_width)] for y in range(self.y_height // 2)
    ]
    self.changed_list = []

clear()

Clear all the panels on the display

Source code in display/display.py
def clear(self):
    """Clear all the panels on the display"""
    self.display_buf = [
        [0 for x in range(self.x_width)] for y in range(self.y_height // 2)
    ]
    for row in self.board_objects:
        for board in row:
            board.clear()
    self.changed_list.clear()

draw_hline(start_x, start_y, length, top=True, combine=True, push=False)

Draw horizontal line

Parameters:

Name Type Description Default
start_x int

x coordinate

required
start_y int

y coordinate

required
length int

length of line to draw

required
top int

draw horizontal line on the top or bottom of the pixel

True
combine int

digits are split into two pixels, when drawing to one combine keeps what is already drawn on the other side of the digit

True
push int

when true all the recent changes are pushed to the display

False
Source code in display/display.py
def draw_hline(self, start_x, start_y, length, top=True, combine=True, push=False):
    """Draw horizontal line

    Args:
        start_x (int): x coordinate
        start_y (int): y coordinate
        length (int): length of line to draw
        top (int): draw horizontal line on the top or bottom of the pixel
        combine (int): digits are split into two pixels, when drawing to one combine keeps what is already drawn on the other side of the digit
        push (int): when true all the recent changes are pushed to the display
    """
    half_height = start_y // 2
    if start_y % 2:
        for x in range(length):
            if top:
                if combine:
                    self.display_buf[half_height][x + start_x] = (
                        self.display_buf[half_height][x + start_x] | 0x01
                    )
                else:
                    self.display_buf[half_height][x + start_x] = 0x01
            else:
                if combine:
                    self.display_buf[half_height][x + start_x] = (
                        self.display_buf[half_height][x + start_x] | 0x08
                    )
                else:
                    self.display_buf[half_height][x + start_x] = 0x08
            self.changed_list.append((x + start_x, half_height))
    else:
        for x in range(length):
            if top:
                if combine:
                    self.display_buf[half_height][x + start_x] = (
                        self.display_buf[half_height][x + start_x] | 0x40
                    )
                else:
                    self.display_buf[half_height][x + start_x] = 0x40
            else:
                if combine:
                    self.display_buf[half_height][x + start_x] = (
                        self.display_buf[half_height][x + start_x] | 0x01
                    )
                else:
                    self.display_buf[half_height][x + start_x] = 0x01
            self.changed_list.append((x + start_x, half_height))
    if push:
        self.push()

draw_pixel(x, y, value, combine=True, push=False)

Draw shape to one pixel location

Parameters:

Name Type Description Default
x int

x coordinate

required
y int

y coordinate

required
value int

which leds to turn on for the pixel (1 for bottom, 2 for left, 4 for top, 8 for right, add sides together for multiple sides)

required
combine bool

digits are split into two pixels, when drawing to one combine keeps what is already drawn on the other side of the digit

True
push bool

when true all the recent changes are pushed to the display

False
Source code in display/display.py
def draw_pixel(self, x, y, value, combine=True, push=False):
    """Draw shape to one pixel location

    Args:
        x (int): x coordinate
        y (int): y coordinate
        value (int): which leds to turn on for the pixel (1 for bottom, 2 for left, 4 for top, 8 for right, add sides together for multiple sides)
        combine (bool): digits are split into two pixels, when drawing to one combine keeps what is already drawn on the other side of the digit
        push (bool): when true all the recent changes are pushed to the display
    """
    half_height = y // 2
    if value == 0 and combine:
        current_value = self.display_buf[half_height][x]
        if y % 2:
            if current_value & 0x62:
                self.display_buf[half_height][x] = current_value & 0x63
            else:
                self.display_buf[half_height][x] = current_value & 0x62
        else:
            if current_value & 0x1C:
                self.display_buf[half_height][x] = current_value & 0x1D
            else:
                self.display_buf[half_height][x] = current_value & 0x1C
        self.changed_list.append((x, y // 2))
        return

    if y % 2:
        value = (
            (value & 1) << 3
            | (value & 2) << 1
            | (value & 4) >> 2
            | (value & 8) << 1
        )
    else:
        value = (value & 3) | (value & 4) << 4 | (value & 8) << 2
    # if value | self.display_buf[half_height][x] == self.display_buf[half_height][x]:
    #     return
    if combine:
        value = self.display_buf[half_height][x] | value
    self.display_buf[half_height][x] = value
    self.changed_list.append((x, y // 2))
    if push:
        self.push()

draw_raw(x, y, value, push=False)

Draw to a specific segment on the screen

Parameters:

Name Type Description Default
x int

x coordinate

required
y int

y coordinate

required
value int

which leds to turn on for the segment

required
push bool

when true all the recent changes are pushed to the display

False
Source code in display/display.py
def draw_raw(self, x, y, value, push=False):
    """Draw to a specific segment on the screen

    Args:
        x (int): x coordinate
        y (int): y coordinate
        value (int): which leds to turn on for the segment
        push (bool): when true all the recent changes are pushed to the display
    """
    self.display_buf[y][x] = value
    self.changed_list.append((x, y))
    if push:
        self.push()

draw_shape_line(start_x, start_y, end_x, end_y, value, combine=True, push=False)

Draw line with given value at each pixel, can be diagonal

Parameters:

Name Type Description Default
start_x int

starting x coordinate

required
start_y int

starting y coordinate

required
end_x int

ending x coordinate

required
end_y int

ending y coordiante

required
value int

which leds to turn on for the pixel (1 for bottom, 2 for left, 4 for top, 8 for right, add sides together for multiple sides)

required
combine bool

digits are split into two pixels, when drawing to one combine keeps what is already drawn on the other side of the digit

True
push bool

when true all the recent changes are pushed to the display

False
Source code in display/display.py
def draw_shape_line(
    self, start_x, start_y, end_x, end_y, value, combine=True, push=False
):
    """Draw line with given value at each pixel, can be diagonal

    Args:
        start_x (int): starting x coordinate
        start_y (int): starting y coordinate
        end_x (int): ending x coordinate
        end_y (int): ending y coordiante
        value (int): which leds to turn on for the pixel (1 for bottom, 2 for left, 4 for top, 8 for right, add sides together for multiple sides)
        combine (bool): digits are split into two pixels, when drawing to one combine keeps what is already drawn on the other side of the digit
        push (bool): when true all the recent changes are pushed to the display
    """
    if start_x != end_x:
        slope = (end_y - start_y) / (end_x - start_x)
        b = start_y - slope * start_x
        for x in range(min(start_x, end_x), max(start_x, end_x) + 1):
            self.draw_pixel(x, round(b) + round(slope * x), value, combine)
    else:
        for y in range(min(start_y, end_y), max(start_y, end_y) + 1):
            self.draw_pixel(start_x, y, value, combine)
    if push:
        self.push()

draw_text(x, y, msg, combine=True, push=False)

Print a message to the screen, y_height-2 is lowest y value accepted without error

Parameters:

Name Type Description Default
x int

x coordinate

required
y int

y coordinate

required
msg int

string message to print

required
combine bool

digits are split into two pixels, when drawing to one combine keeps what is already drawn on the other side of the digit

True
push bool

when true all the recent changes are pushed to the display

False
Source code in display/display.py
def draw_text(self, x, y, msg, combine=True, push=False):
    """Print a message to the screen, y_height-2 is lowest y value accepted without error

    Args:
        x (int): x coordinate
        y (int): y coordinate
        msg (int): string message to print
        combine (bool): digits are split into two pixels, when drawing to one combine keeps what is already drawn on the other side of the digit
        push (bool): when true all the recent changes are pushed to the display
    """
    if y % 2:
        for pos, char in enumerate(msg):
            value = symbols.get_char2(char)
            # get top and bottom values out of value
            top_value = (value & 0x20) >> 2 | (value & 0x40) >> 4 | (value & 0x02)
            bottom_value = (
                (value & 0x04) >> 1
                | (value & 0x08) >> 3
                | (value & 0x10) >> 1
                | (value & 0x01) << 2
            )
            self.draw_pixel(x + pos, y, top_value, combine=combine)
            self.draw_pixel(x + pos, y + 1, bottom_value, combine=combine)
    else:
        half_y = y // 2
        for pos, char in enumerate(msg):
            value = symbols.get_char2(char)
            self.display_buf[half_y][x + pos] = value
            self.changed_list.append((x + pos, half_y))
    if push:
        self.push()

draw_vline(start_x, start_y, length, left=True, combine=True, push=False)

Draw vertical line

Parameters:

Name Type Description Default
start_x int

x coordinate

required
start_y int

y coordinate

required
length int

length of line to draw

required
left int

draw vertial line on the left or right of the pixel

True
combine bool

digits are split into two pixels, when drawing to one combine keeps what is already drawn on the other side of the digit

True
push bool

when true all the recent changes are pushed to the display

False
Source code in display/display.py
def draw_vline(self, start_x, start_y, length, left=True, combine=True, push=False):
    """Draw vertical line

    Args:
        start_x (int): x coordinate
        start_y (int): y coordinate
        length (int): length of line to draw
        left (int): draw vertial line on the left or right of the pixel
        combine (bool): digits are split into two pixels, when drawing to one combine keeps what is already drawn on the other side of the digit
        push (bool): when true all the recent changes are pushed to the display
    """
    for y in range(length):
        y_loc = start_y + y
        y_half = y_loc // 2
        if y_loc % 2:
            if left:
                if combine:
                    self.display_buf[y_half][start_x] = (
                        self.display_buf[y_half][start_x] | 0x04
                    )
                else:
                    self.display_buf[y_half][start_x] = 0x04
            else:
                if combine:
                    self.display_buf[y_half][start_x] = (
                        self.display_buf[y_half][start_x] | 0x10
                    )
                else:
                    self.display_buf[y_half][start_x] = 0x10
        else:
            if left:
                if combine:
                    self.display_buf[y_half][start_x] = (
                        self.display_buf[y_half][start_x] | 0x02
                    )
                else:
                    self.display_buf[y_half][start_x] = 0x02
            else:
                if combine:
                    self.display_buf[y_half][start_x] = (
                        self.display_buf[y_half][start_x] | 0x20
                    )
                else:
                    self.display_buf[y_half][start_x] = 0x20
        self.changed_list.append((start_x, y_half))
    if push:
        self.push()

get_pixel(x, y)

Get the value already at the pixel

Parameters:

Name Type Description Default
x int

x coordinate

required
y int

y coordinate

required

Returns:

Type Description
int

value of the pixel

Source code in display/display.py
def get_pixel(self, x, y):
    """Get the value already at the pixel

    Args:
        x (int): x coordinate
        y (int): y coordinate

    Returns:
        (int): value of the pixel
    """
    half_height = y // 2
    value = self.display_buf[half_height][x]

    if y % 2:
        half_value = (
            bool(value & 16) << 3
            | bool(value & 1) << 2
            | bool(value & 4) << 1
            | bool(value & 8)
        )
    else:
        half_value = (
            bool(value & 32) << 3
            | bool(value & 64) << 2
            | bool(value & 2) << 1
            | bool(value & 1)
        )

    return half_value

get_raw(x, y)

Get the value of the segment

Parameters:

Name Type Description Default
x int

x coordinate

required
y int

y coordinate

required

Returns:

Type Description
int

value of the segment

Source code in display/display.py
def get_raw(self, x, y):
    """Get the value of the segment

    Args:
        x (int): x coordinate
        y (int): y coordinate

    Returns:
        (int): value of the segment
    """
    return self.display_buf[y][x]

push()

Push all the recent changes to the display

Source code in display/display.py
def push(self):
    """Push all the recent changes to the display"""
    for location in self.changed_list:
        self.board_objects[location[1] // 6][location[0] // 16].raw2(
            location[0] % 16,
            location[1] % 6,
            self.display_buf[location[1]][location[0]],
        )
    for row in self.board_objects:
        for board in row:
            board.flush()
    self.changed_list.clear()

SegmentDisplay

SegmentDisplay is a class that abstracts the S^3 screen into individually controllable segments. Instead of making each circle of a segment a pixel, this class lets you control each segment as a pixel. This means this display is 2 times wider (there are two horizontal segments) and 3 times higher (there are three vertical segments) than the Display object.

Source code in display/segment_display.py
class SegmentDisplay:
    """
    SegmentDisplay is a class that abstracts the S^3 screen into individually
    controllable segments. Instead of making each circle of a segment a pixel,
    this class lets you control each segment as a pixel. This means this
    display is 2 times wider (there are two horizontal segments) and 3 times
    higher (there are three vertical segments) than the Display object.
    """

    def __init__(self, screen):
        self.screen = screen

        self.screen_width = screen.x_width - 1
        self.screen_height = screen.y_height // 2

        self.width = 2 * self.screen_width
        self.height = 3 * self.screen_height

        self.x_buffer = np.zeros((self.width, self.height))
        self.y_buffer = np.zeros((self.width, self.height))

    def draw(self, push=True):
        """
        Updates the screen with all of the lines drawn using draw_line.

        Args:
            push (bool): when true all the recent changes are pushed to the display
        """
        for x in range(self.screen_width):
            for y in range(self.screen_height):
                state = 0

                if self.x_buffer[x * 2][y * 3]:
                    state += 64  # 64 = TOP
                if self.x_buffer[x * 2][y * 3 + 1]:
                    state += 1  # 1 = CENTER
                if self.x_buffer[x * 2][y * 3 + 2]:
                    state += 8  # 8 = BOTTOM

                if self.y_buffer[x * 2 + 1][y * 3]:
                    state += 32  # 32 = TR
                if self.y_buffer[x * 2 + 1][y * 3 + 1]:
                    state += 16  # 16 = BR
                if self.y_buffer[x * 2][y * 3 + 1]:
                    state += 4  # 4 = BL
                if self.y_buffer[x * 2][y * 3]:
                    state += 2  # 2 = TL

                # Only draw the pixel if there is something to draw
                if state != 0:
                    self.screen.draw_raw(x, y, state)

        if push:
            self.screen.push()

    def undraw(self):
        """
        Clears all segments drawn both from the screen and from the underlying data structure.
        """
        for x in range(self.screen_width):
            for y in range(self.screen_height):
                if self.x_buffer[x * 2][y * 3]:
                    value = self.screen.get_raw(x, y)
                    self.screen.draw_raw(x, y, value - 64)
                    self.x_buffer[x * 2][y * 3] = False

                if self.x_buffer[x * 2][y * 3 + 1]:
                    value = self.screen.get_raw(x, y)
                    self.screen.draw_raw(x, y, value - 1)
                    self.x_buffer[x * 2][y * 3 + 1] = False

                if self.x_buffer[x * 2][y * 3 + 2]:
                    value = self.screen.get_raw(x, y)
                    self.screen.draw_raw(x, y, value - 8)
                    self.x_buffer[x * 2][y * 3 + 2] = False

                if self.y_buffer[x * 2 + 1][y * 3]:
                    value = self.screen.get_raw(x, y)
                    self.screen.draw_raw(x, y, value - 32)
                    self.y_buffer[x * 2 + 1][y * 3] = False

                if self.y_buffer[x * 2 + 1][y * 3 + 1]:
                    value = self.screen.get_raw(x, y)
                    self.screen.draw_raw(x, y, value - 16)
                    self.y_buffer[x * 2 + 1][y * 3 + 1] = False

                if self.y_buffer[x * 2][y * 3 + 1]:
                    value = self.screen.get_raw(x, y)
                    self.screen.draw_raw(x, y, value - 4)
                    self.y_buffer[x * 2][y * 3 + 1] = False

                if self.y_buffer[x * 2][y * 3]:
                    value = self.screen.get_raw(x, y)
                    self.screen.draw_raw(x, y, value - 2)
                    self.y_buffer[x * 2][y * 3] = False

    def draw_line(self, start_x, start_y, end_x, end_y):
        """
        Draws a line from coordinate to another. To display the line, you must
        use the draw function. This function only updates the underlying data
        buffer.

        Args:
            start_x (int): the starting x point
            start_y (int): the starting y point
            end_x (int): the ending x point
            end_y (int): the ending y point
        """
        start_x = self._constrain(start_x, 0, self.width - 1)
        start_y = self._constrain(start_y, 0, self.height - 1)

        end_x = self._constrain(end_x, 0, self.width - 1)
        end_y = self._constrain(end_y, 0, self.height - 1)

        if end_x < start_x:
            start_x, end_x = end_x, start_x
            start_y, end_y = end_y, start_y

        dx = end_x - start_x
        dy = end_y - start_y

        r = 0
        ny = 0
        pny = 0
        nny = 0

        p = dy / dx if dx != 0 else 0
        t = 0

        for i in range(dx + 1):
            r = int(round(t))
            pny = ny
            ny = start_y + r

            if i > 0:  # vertical lines connecting horizontal lines
                for j in range(abs(ny - pny)):
                    if pny > ny:
                        nny = pny - j - 1
                    else:
                        nny = pny + j

                    self.y_buffer[start_x + i][nny] = 1

            if i != dx:
                self.x_buffer[start_x + i][ny] = 1
            t += p

        if dx == 0 and dy != 0:  # in case of no vertical lines
            fs = 0
            fe = int(dy)

            if dy < 0:
                fs = fe
                fe = 0

            for i in range(fs, fe):
                self.y_buffer[start_x][start_y + i] = 1

    def _constrain(self, val, min_val, max_val):
        """A helper function that constrains a value between two values"""
        return min(max_val, max(min_val, val))

draw(push=True)

Updates the screen with all of the lines drawn using draw_line.

Parameters:

Name Type Description Default
push bool

when true all the recent changes are pushed to the display

True
Source code in display/segment_display.py
def draw(self, push=True):
    """
    Updates the screen with all of the lines drawn using draw_line.

    Args:
        push (bool): when true all the recent changes are pushed to the display
    """
    for x in range(self.screen_width):
        for y in range(self.screen_height):
            state = 0

            if self.x_buffer[x * 2][y * 3]:
                state += 64  # 64 = TOP
            if self.x_buffer[x * 2][y * 3 + 1]:
                state += 1  # 1 = CENTER
            if self.x_buffer[x * 2][y * 3 + 2]:
                state += 8  # 8 = BOTTOM

            if self.y_buffer[x * 2 + 1][y * 3]:
                state += 32  # 32 = TR
            if self.y_buffer[x * 2 + 1][y * 3 + 1]:
                state += 16  # 16 = BR
            if self.y_buffer[x * 2][y * 3 + 1]:
                state += 4  # 4 = BL
            if self.y_buffer[x * 2][y * 3]:
                state += 2  # 2 = TL

            # Only draw the pixel if there is something to draw
            if state != 0:
                self.screen.draw_raw(x, y, state)

    if push:
        self.screen.push()

draw_line(start_x, start_y, end_x, end_y)

Draws a line from coordinate to another. To display the line, you must use the draw function. This function only updates the underlying data buffer.

Parameters:

Name Type Description Default
start_x int

the starting x point

required
start_y int

the starting y point

required
end_x int

the ending x point

required
end_y int

the ending y point

required
Source code in display/segment_display.py
def draw_line(self, start_x, start_y, end_x, end_y):
    """
    Draws a line from coordinate to another. To display the line, you must
    use the draw function. This function only updates the underlying data
    buffer.

    Args:
        start_x (int): the starting x point
        start_y (int): the starting y point
        end_x (int): the ending x point
        end_y (int): the ending y point
    """
    start_x = self._constrain(start_x, 0, self.width - 1)
    start_y = self._constrain(start_y, 0, self.height - 1)

    end_x = self._constrain(end_x, 0, self.width - 1)
    end_y = self._constrain(end_y, 0, self.height - 1)

    if end_x < start_x:
        start_x, end_x = end_x, start_x
        start_y, end_y = end_y, start_y

    dx = end_x - start_x
    dy = end_y - start_y

    r = 0
    ny = 0
    pny = 0
    nny = 0

    p = dy / dx if dx != 0 else 0
    t = 0

    for i in range(dx + 1):
        r = int(round(t))
        pny = ny
        ny = start_y + r

        if i > 0:  # vertical lines connecting horizontal lines
            for j in range(abs(ny - pny)):
                if pny > ny:
                    nny = pny - j - 1
                else:
                    nny = pny + j

                self.y_buffer[start_x + i][nny] = 1

        if i != dx:
            self.x_buffer[start_x + i][ny] = 1
        t += p

    if dx == 0 and dy != 0:  # in case of no vertical lines
        fs = 0
        fe = int(dy)

        if dy < 0:
            fs = fe
            fe = 0

        for i in range(fs, fe):
            self.y_buffer[start_x][start_y + i] = 1

undraw()

Clears all segments drawn both from the screen and from the underlying data structure.

Source code in display/segment_display.py
def undraw(self):
    """
    Clears all segments drawn both from the screen and from the underlying data structure.
    """
    for x in range(self.screen_width):
        for y in range(self.screen_height):
            if self.x_buffer[x * 2][y * 3]:
                value = self.screen.get_raw(x, y)
                self.screen.draw_raw(x, y, value - 64)
                self.x_buffer[x * 2][y * 3] = False

            if self.x_buffer[x * 2][y * 3 + 1]:
                value = self.screen.get_raw(x, y)
                self.screen.draw_raw(x, y, value - 1)
                self.x_buffer[x * 2][y * 3 + 1] = False

            if self.x_buffer[x * 2][y * 3 + 2]:
                value = self.screen.get_raw(x, y)
                self.screen.draw_raw(x, y, value - 8)
                self.x_buffer[x * 2][y * 3 + 2] = False

            if self.y_buffer[x * 2 + 1][y * 3]:
                value = self.screen.get_raw(x, y)
                self.screen.draw_raw(x, y, value - 32)
                self.y_buffer[x * 2 + 1][y * 3] = False

            if self.y_buffer[x * 2 + 1][y * 3 + 1]:
                value = self.screen.get_raw(x, y)
                self.screen.draw_raw(x, y, value - 16)
                self.y_buffer[x * 2 + 1][y * 3 + 1] = False

            if self.y_buffer[x * 2][y * 3 + 1]:
                value = self.screen.get_raw(x, y)
                self.screen.draw_raw(x, y, value - 4)
                self.y_buffer[x * 2][y * 3 + 1] = False

            if self.y_buffer[x * 2][y * 3]:
                value = self.screen.get_raw(x, y)
                self.screen.draw_raw(x, y, value - 2)
                self.y_buffer[x * 2][y * 3] = False

SevenSegment

Source code in display/seven_seg.py
class SevenSegment:
    def __init__(
        self,
        num_digits=8,
        num_per_segment=MAX7219_DIGITS,
        baudrate=DEFAULT_BAUDRATE,
        cs_num=0,
        brightness=7,
        clear=True,
        segment_orientation_array=None,
    ):
        """Constructor

        Args:
            num_digits (int): total number of digits in your display
            num_per_segment (int): total number of digits per MAX7219 segment (default 8)
            baudrate (int): rate at which data is transfered (default 9000kHz), excessive rate may result in instability
            cs_num (int): which control select line is being used
            brightness (int): starting brightness of the leds
            clear (bool): clear the screen on initialization
            segment_orientation_array (int[][]): a 2d array of where the MAX7219 segments are located, one indexed 

                Example:
                ```
                [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]]
                ```
                (height is 6 and width is 16) this needs to be populated to use coordinate grid style functions i.e. letter2 (default None)
        """
        self.num_digits = num_digits
        self.num_segments = num_digits // num_per_segment
        self.num_per_segment = num_per_segment
        self.baudrate = baudrate if baudrate < 10000000 else 10000000
        self._buf = [0] * self.num_digits
        self._display_buf = [0] * self.num_digits
        self._spi = spidev.SpiDev()
        self._spi.open(0, cs_num)
        self._spi.max_speed_hz = self.baudrate

        # Setup the display
        self.command(MAX7219_REG_SHUTDOWN, 1)  # 1 enables the display
        self.command(
            MAX7219_REG_DECODEMODE, 0
        )  # 0x01, 0x0F, 0xFF for different Code B modes
        self.command(MAX7219_REG_SCANLIMIT, self.num_per_segment - 1)
        self.command(MAX7219_REG_DISPLAYTEST, 0)
        self.brightness(brightness)

        # Set up cascaded segemtn orientation stuff to enable 2 functions
        self.display = (
            None or segment_orientation_array
        )  # [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]]
        self._display_y_len = len(self.display) if self.display is not None else None

        self._flush_index = []
        if clear:
            self.clear()

    def command(self, register_num, value):
        """
        Sets control registers for each segment in the display

        Args:
            register_num (int): which register to set
            value (int): value to set the register to
        """
        # check register_num is good
        if register_num not in [
            MAX7219_REG_DECODEMODE,
            MAX7219_REG_INTENSITY,
            MAX7219_REG_SCANLIMIT,
            MAX7219_REG_SHUTDOWN,
            MAX7219_REG_DISPLAYTEST,
        ]:
            raise ValueError("register_num is not a correct value")
        # check value is good
        if not isinstance(value, (int)) or (value > 16 or value < 0):
            raise ValueError("value is not a correct value")
        self._write([register_num, value] * self.num_segments)

    def close(self, clear=True, shutdown=True):
        """
        Close the spi connection

        Args:
            clear (bool): clear the display before closing
            shutdown (bool): shutdown the display before closing
        """
        if clear:
            self.clear()
        if shutdown:
            self.command(MAX7219_REG_SHUTDOWN, 0)
        self._spi.close()

    def clear(self, flush=True):
        """
        Clears the buffer, and if specified, flushes the display

        Args:
            flush (bool): flush the display after clearing
        """
        self._buf = [0] * self.num_digits
        if flush:
            self.flush_legacy()

    def brightness(self, value):
        """
        Sets the brightness for all of the segments ranging from 0 - 15

        Args:
            value (int): brightness value to set
        """
        # check value is good
        if not isinstance(value, (int)) or (value > 16 or value < 0):
            raise ValueError("value is not a correct value")
        self.command(MAX7219_REG_INTENSITY, value)

    # Original flush, about 2 times slower than the current flush function, used in clear
    def flush_legacy(self):
        """Cascade the buffer onto the display"""
        for seg in range(self.num_segments):
            for pos in range(self.num_per_segment):
                self._write(
                    ([MAX7219_REG_NOOP, 0] * (self.num_segments - seg))
                    + [
                        pos + MAX7219_REG_DIGIT0,
                        self._buf[pos + (seg * self.num_per_segment)],
                    ]
                    + ([MAX7219_REG_NOOP, 0] * seg)
                )

    def flush(self):
        """Flush all the current changes to the display"""
        for pos in self._flush_index:
            self._write(
                [MAX7219_REG_NOOP, 0]
                * (self.num_segments - 1 - int(pos / self.num_per_segment))
                + [MAX7219_REG_DIGIT0 + pos % self.num_per_segment, self._buf[pos]]
                + [MAX7219_REG_NOOP, 0] * int(pos / self.num_per_segment)
            )
        self._flush_index.clear()

    def raw(self, position, value, flush=False):
        """
        Given raw 0-255 value draw symbol at given postion

        Args:
            position (int): position to draw the symbol
            value (int): value to draw at the position
            flush (bool): flush the display after drawing
        """
        # Check if position is valid
        if (
            not isinstance(position, (int))
            or position < 0
            or position >= self.num_digits
        ):
            raise ValueError("position is not a valid number")
        # Check if char is int between 0 and 255
        if not isinstance(value, (int)) or value < 0 or value > 255:
            raise ValueError("value is either not an int or out of bounds (0-255)")
        self._buf[position] = value
        self._flush_index.append(position)

        if flush:
            self.flush()

    def raw2(self, x, y, value, flush=False):
        """
        Given raw 0-255 value draw symbol at given coordinate

        Args:
            x (int): x coordinate to draw the symbol
        """
        position = self._get_pos(x, y)
        self.raw(position, value, flush)

    def letter(self, position, char, dot=False, flush=False):
        """
        Outputs ascii letter as close as it can, working letters/symbols found in symbols.py

        Args:
            position (int): position to draw the symbol
            char (str): character to draw at the position
            dot (bool): whether or not to draw a dot after the character
            flush (bool): flush the display after drawing
        """
        # Check if position is valid
        if (
            not isinstance(position, (int))
            or position < 0
            or position >= self.num_digits
        ):
            raise ValueError("position is not a valid number")
        value = sy.get_char2(char) | (dot << 7)
        self._buf[position] = value
        self._flush_index.append(position)
        if flush:
            self.flush()

    def letter2(self, x, y, char, dot=False, flush=False):
        """
        Output letter on the display at the coordinates provided if possible

        Args:
            x (int): x coordinate to draw the symbol
            y (int): y coordinate to draw the symbol
            char (str): character to draw at the position
            dot (bool): whether or not to draw a dot after the character
            flush (bool): flush the display after drawing
        """
        # Check to make sure segment array has been initialized
        if self.display is None:
            raise ValueError("segment_orientation_array has not been initialized")
        pos = self._get_pos(x, y)
        self.letter(pos, char, dot, flush)

    def text(self, txt, start_position=0, flush=False):
        """
        Output text on the display at the start position if possible

        Args:
            txt (str): text to draw on the display
            start_position (int): position to start drawing the text
            flush (bool): flush the display after drawing
        """
        # Check if txt is going to overflow buffer
        if start_position + len(txt.replace(".", "")) > self.num_digits:
            raise OverflowError("Message would overflow spi buffer")

        for pos, char in enumerate(txt):
            # Check if current char is a dot and append to previous letter
            if char == "." and pos != 0:  # mutliple dots in a row cause an error
                self.letter(pos + start_position - 1, txt[pos - 1], dot=True)
            else:
                self.letter(start_position + pos, char)

        if flush:
            self.flush()

    def text2(self, x, y, txt, horizontal=True, flush=False):
        """
        Output text on the display at the given x, y - option to display horizontal or vertical text

        Args:
            x (int): x coordinate to draw the symbol
            y (int): y coordinate to draw the symbol
            txt (str): text to draw on the display
            horizontal (bool): whether or not to draw the text horizontally
            flush (bool): flush the display after drawing
        """
        # No initial checks and will let underlying functions do the work
        if horizontal:
            # self.text(txt, self._get_pos(x, y))
            for pos, char in enumerate(txt):
                # Check if current char is a dot and append to previous letter
                if char == "." and pos != 0:  # mutliple dots in a row cause an error
                    self.letter2(x + pos - 1, y, txt[pos - 1], True)
                else:
                    self.letter2(x + pos, y, char)
        else:
            for pos, char in enumerate(txt):
                # Check if current char is a dot and append to previous letter
                if char == "." and pos != 0:  # mutliple dots in a row cause an error
                    self.letter2(x, y + pos - 1, txt[pos - 1], True)
                else:
                    self.letter2(x, y + pos, char)
        if flush:
            self.flush()

    # Write to the SPI file through SPI library
    def _write(self, data):
        self._spi.writebytes(bytes(data))

    # Get position in the buffer for a given x,y coordinate
    def _get_pos(self, x, y):
        # Check y is within bounds
        if not isinstance(y, (int)) or y < 0 or y >= self._display_y_len:
            return ValueError("y value is not a valid number")

        # Check if x is an int
        if not isinstance(x, (int)):
            return ValueError("x value is not an integer")
        x_seg = int(x / self.num_per_segment)

        # check if x is within bounds of y row
        if x_seg >= len(self.display[y]):
            raise ValueError("x value is out of range")

        return (self.display[y][x_seg] - 1) * self.num_per_segment + (
            x % self.num_per_segment
        )

    # Not current in use
    def _check_buf(self):
        indices = []
        for pos in range(len(self._buf)):
            if self._buf[pos] != self._display_buf[pos]:
                indices.append(pos)
        return indices

__init__(num_digits=8, num_per_segment=MAX7219_DIGITS, baudrate=DEFAULT_BAUDRATE, cs_num=0, brightness=7, clear=True, segment_orientation_array=None)

Constructor

Parameters:

Name Type Description Default
num_digits int

total number of digits in your display

8
num_per_segment int

total number of digits per MAX7219 segment (default 8)

MAX7219_DIGITS
baudrate int

rate at which data is transfered (default 9000kHz), excessive rate may result in instability

DEFAULT_BAUDRATE
cs_num int

which control select line is being used

0
brightness int

starting brightness of the leds

7
clear bool

clear the screen on initialization

True
segment_orientation_array int[][]

a 2d array of where the MAX7219 segments are located, one indexed

Example:

[[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]]
(height is 6 and width is 16) this needs to be populated to use coordinate grid style functions i.e. letter2 (default None)

None
Source code in display/seven_seg.py
def __init__(
    self,
    num_digits=8,
    num_per_segment=MAX7219_DIGITS,
    baudrate=DEFAULT_BAUDRATE,
    cs_num=0,
    brightness=7,
    clear=True,
    segment_orientation_array=None,
):
    """Constructor

    Args:
        num_digits (int): total number of digits in your display
        num_per_segment (int): total number of digits per MAX7219 segment (default 8)
        baudrate (int): rate at which data is transfered (default 9000kHz), excessive rate may result in instability
        cs_num (int): which control select line is being used
        brightness (int): starting brightness of the leds
        clear (bool): clear the screen on initialization
        segment_orientation_array (int[][]): a 2d array of where the MAX7219 segments are located, one indexed 

            Example:
            ```
            [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]]
            ```
            (height is 6 and width is 16) this needs to be populated to use coordinate grid style functions i.e. letter2 (default None)
    """
    self.num_digits = num_digits
    self.num_segments = num_digits // num_per_segment
    self.num_per_segment = num_per_segment
    self.baudrate = baudrate if baudrate < 10000000 else 10000000
    self._buf = [0] * self.num_digits
    self._display_buf = [0] * self.num_digits
    self._spi = spidev.SpiDev()
    self._spi.open(0, cs_num)
    self._spi.max_speed_hz = self.baudrate

    # Setup the display
    self.command(MAX7219_REG_SHUTDOWN, 1)  # 1 enables the display
    self.command(
        MAX7219_REG_DECODEMODE, 0
    )  # 0x01, 0x0F, 0xFF for different Code B modes
    self.command(MAX7219_REG_SCANLIMIT, self.num_per_segment - 1)
    self.command(MAX7219_REG_DISPLAYTEST, 0)
    self.brightness(brightness)

    # Set up cascaded segemtn orientation stuff to enable 2 functions
    self.display = (
        None or segment_orientation_array
    )  # [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]]
    self._display_y_len = len(self.display) if self.display is not None else None

    self._flush_index = []
    if clear:
        self.clear()

brightness(value)

Sets the brightness for all of the segments ranging from 0 - 15

Parameters:

Name Type Description Default
value int

brightness value to set

required
Source code in display/seven_seg.py
def brightness(self, value):
    """
    Sets the brightness for all of the segments ranging from 0 - 15

    Args:
        value (int): brightness value to set
    """
    # check value is good
    if not isinstance(value, (int)) or (value > 16 or value < 0):
        raise ValueError("value is not a correct value")
    self.command(MAX7219_REG_INTENSITY, value)

clear(flush=True)

Clears the buffer, and if specified, flushes the display

Parameters:

Name Type Description Default
flush bool

flush the display after clearing

True
Source code in display/seven_seg.py
def clear(self, flush=True):
    """
    Clears the buffer, and if specified, flushes the display

    Args:
        flush (bool): flush the display after clearing
    """
    self._buf = [0] * self.num_digits
    if flush:
        self.flush_legacy()

close(clear=True, shutdown=True)

Close the spi connection

Parameters:

Name Type Description Default
clear bool

clear the display before closing

True
shutdown bool

shutdown the display before closing

True
Source code in display/seven_seg.py
def close(self, clear=True, shutdown=True):
    """
    Close the spi connection

    Args:
        clear (bool): clear the display before closing
        shutdown (bool): shutdown the display before closing
    """
    if clear:
        self.clear()
    if shutdown:
        self.command(MAX7219_REG_SHUTDOWN, 0)
    self._spi.close()

command(register_num, value)

Sets control registers for each segment in the display

Parameters:

Name Type Description Default
register_num int

which register to set

required
value int

value to set the register to

required
Source code in display/seven_seg.py
def command(self, register_num, value):
    """
    Sets control registers for each segment in the display

    Args:
        register_num (int): which register to set
        value (int): value to set the register to
    """
    # check register_num is good
    if register_num not in [
        MAX7219_REG_DECODEMODE,
        MAX7219_REG_INTENSITY,
        MAX7219_REG_SCANLIMIT,
        MAX7219_REG_SHUTDOWN,
        MAX7219_REG_DISPLAYTEST,
    ]:
        raise ValueError("register_num is not a correct value")
    # check value is good
    if not isinstance(value, (int)) or (value > 16 or value < 0):
        raise ValueError("value is not a correct value")
    self._write([register_num, value] * self.num_segments)

flush()

Flush all the current changes to the display

Source code in display/seven_seg.py
def flush(self):
    """Flush all the current changes to the display"""
    for pos in self._flush_index:
        self._write(
            [MAX7219_REG_NOOP, 0]
            * (self.num_segments - 1 - int(pos / self.num_per_segment))
            + [MAX7219_REG_DIGIT0 + pos % self.num_per_segment, self._buf[pos]]
            + [MAX7219_REG_NOOP, 0] * int(pos / self.num_per_segment)
        )
    self._flush_index.clear()

flush_legacy()

Cascade the buffer onto the display

Source code in display/seven_seg.py
def flush_legacy(self):
    """Cascade the buffer onto the display"""
    for seg in range(self.num_segments):
        for pos in range(self.num_per_segment):
            self._write(
                ([MAX7219_REG_NOOP, 0] * (self.num_segments - seg))
                + [
                    pos + MAX7219_REG_DIGIT0,
                    self._buf[pos + (seg * self.num_per_segment)],
                ]
                + ([MAX7219_REG_NOOP, 0] * seg)
            )

letter(position, char, dot=False, flush=False)

Outputs ascii letter as close as it can, working letters/symbols found in symbols.py

Parameters:

Name Type Description Default
position int

position to draw the symbol

required
char str

character to draw at the position

required
dot bool

whether or not to draw a dot after the character

False
flush bool

flush the display after drawing

False
Source code in display/seven_seg.py
def letter(self, position, char, dot=False, flush=False):
    """
    Outputs ascii letter as close as it can, working letters/symbols found in symbols.py

    Args:
        position (int): position to draw the symbol
        char (str): character to draw at the position
        dot (bool): whether or not to draw a dot after the character
        flush (bool): flush the display after drawing
    """
    # Check if position is valid
    if (
        not isinstance(position, (int))
        or position < 0
        or position >= self.num_digits
    ):
        raise ValueError("position is not a valid number")
    value = sy.get_char2(char) | (dot << 7)
    self._buf[position] = value
    self._flush_index.append(position)
    if flush:
        self.flush()

letter2(x, y, char, dot=False, flush=False)

Output letter on the display at the coordinates provided if possible

Parameters:

Name Type Description Default
x int

x coordinate to draw the symbol

required
y int

y coordinate to draw the symbol

required
char str

character to draw at the position

required
dot bool

whether or not to draw a dot after the character

False
flush bool

flush the display after drawing

False
Source code in display/seven_seg.py
def letter2(self, x, y, char, dot=False, flush=False):
    """
    Output letter on the display at the coordinates provided if possible

    Args:
        x (int): x coordinate to draw the symbol
        y (int): y coordinate to draw the symbol
        char (str): character to draw at the position
        dot (bool): whether or not to draw a dot after the character
        flush (bool): flush the display after drawing
    """
    # Check to make sure segment array has been initialized
    if self.display is None:
        raise ValueError("segment_orientation_array has not been initialized")
    pos = self._get_pos(x, y)
    self.letter(pos, char, dot, flush)

raw(position, value, flush=False)

Given raw 0-255 value draw symbol at given postion

Parameters:

Name Type Description Default
position int

position to draw the symbol

required
value int

value to draw at the position

required
flush bool

flush the display after drawing

False
Source code in display/seven_seg.py
def raw(self, position, value, flush=False):
    """
    Given raw 0-255 value draw symbol at given postion

    Args:
        position (int): position to draw the symbol
        value (int): value to draw at the position
        flush (bool): flush the display after drawing
    """
    # Check if position is valid
    if (
        not isinstance(position, (int))
        or position < 0
        or position >= self.num_digits
    ):
        raise ValueError("position is not a valid number")
    # Check if char is int between 0 and 255
    if not isinstance(value, (int)) or value < 0 or value > 255:
        raise ValueError("value is either not an int or out of bounds (0-255)")
    self._buf[position] = value
    self._flush_index.append(position)

    if flush:
        self.flush()

raw2(x, y, value, flush=False)

Given raw 0-255 value draw symbol at given coordinate

Parameters:

Name Type Description Default
x int

x coordinate to draw the symbol

required
Source code in display/seven_seg.py
def raw2(self, x, y, value, flush=False):
    """
    Given raw 0-255 value draw symbol at given coordinate

    Args:
        x (int): x coordinate to draw the symbol
    """
    position = self._get_pos(x, y)
    self.raw(position, value, flush)

text(txt, start_position=0, flush=False)

Output text on the display at the start position if possible

Parameters:

Name Type Description Default
txt str

text to draw on the display

required
start_position int

position to start drawing the text

0
flush bool

flush the display after drawing

False
Source code in display/seven_seg.py
def text(self, txt, start_position=0, flush=False):
    """
    Output text on the display at the start position if possible

    Args:
        txt (str): text to draw on the display
        start_position (int): position to start drawing the text
        flush (bool): flush the display after drawing
    """
    # Check if txt is going to overflow buffer
    if start_position + len(txt.replace(".", "")) > self.num_digits:
        raise OverflowError("Message would overflow spi buffer")

    for pos, char in enumerate(txt):
        # Check if current char is a dot and append to previous letter
        if char == "." and pos != 0:  # mutliple dots in a row cause an error
            self.letter(pos + start_position - 1, txt[pos - 1], dot=True)
        else:
            self.letter(start_position + pos, char)

    if flush:
        self.flush()

text2(x, y, txt, horizontal=True, flush=False)

Output text on the display at the given x, y - option to display horizontal or vertical text

Parameters:

Name Type Description Default
x int

x coordinate to draw the symbol

required
y int

y coordinate to draw the symbol

required
txt str

text to draw on the display

required
horizontal bool

whether or not to draw the text horizontally

True
flush bool

flush the display after drawing

False
Source code in display/seven_seg.py
def text2(self, x, y, txt, horizontal=True, flush=False):
    """
    Output text on the display at the given x, y - option to display horizontal or vertical text

    Args:
        x (int): x coordinate to draw the symbol
        y (int): y coordinate to draw the symbol
        txt (str): text to draw on the display
        horizontal (bool): whether or not to draw the text horizontally
        flush (bool): flush the display after drawing
    """
    # No initial checks and will let underlying functions do the work
    if horizontal:
        # self.text(txt, self._get_pos(x, y))
        for pos, char in enumerate(txt):
            # Check if current char is a dot and append to previous letter
            if char == "." and pos != 0:  # mutliple dots in a row cause an error
                self.letter2(x + pos - 1, y, txt[pos - 1], True)
            else:
                self.letter2(x + pos, y, char)
    else:
        for pos, char in enumerate(txt):
            # Check if current char is a dot and append to previous letter
            if char == "." and pos != 0:  # mutliple dots in a row cause an error
                self.letter2(x, y + pos - 1, txt[pos - 1], True)
            else:
                self.letter2(x, y + pos, char)
    if flush:
        self.flush()

Last update: July 13, 2022
Created: July 13, 2022