diff --git a/CHANGES.rst b/CHANGES.rst index 799aaf6..c929661 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,8 @@ ChangeLog +------------+------------------------------------------------------------------------+------------+ | Version | Description | Date | +============+========================================================================+============+ +| *Upcoming* | * Change MAX7219's block_orientation to support ±90° angle correction | | ++------------+------------------------------------------------------------------------+------------+ | **0.7.0** | * **BREAKING CHANGE:** Move sevensegment class to | 2017/03/04 | | | ``luma.led_matrix.virtual`` package | | | | * Documentation updates & corrections | | diff --git a/doc/install.rst b/doc/install.rst index f250ac3..a64a00f 100644 --- a/doc/install.rst +++ b/doc/install.rst @@ -126,7 +126,7 @@ matrices:: $ python examples/matrix_demo.py -h usage: matrix_demo.py [-h] [--cascaded CASCADED] - [--block-orientation {horizontal,vertical}] + [--block-orientation {0, 90, -90}] matrix_demo arguments @@ -134,9 +134,9 @@ matrices:: -h, --help show this help message and exit --cascaded CASCADED, -n CASCADED Number of cascaded MAX7219 LED matrices (default: 1) - --block-orientation {horizontal,vertical} + --block-orientation {0, 90, -90} Corrects block orientation when wired vertically - (default: horizontal) + (default: 0) Similarly, there is a basic demo of the capabilities of the :py:class:`luma.led_matrix.virtual.sevensegment` wrapper:: diff --git a/doc/python-usage.rst b/doc/python-usage.rst index 3dea863..b09d99f 100644 --- a/doc/python-usage.rst +++ b/doc/python-usage.rst @@ -179,7 +179,7 @@ below, from luma.led_matrix.device import max7219 from luma.core.legacy.font import proportional, LCD_FONT - serial = spi(port=0, device=0, gpio=noop(), block_orientation="vertical") + serial = spi(port=0, device=0, gpio=noop(), block_orientation=-90) device = max7219(serial, width=32, height=24) with canvas(device) as draw: @@ -202,8 +202,9 @@ out-of-phase such that horizontal scrolling appears as below: .. image:: images/block_reorientation.gif :alt: block alignment -This can be rectified by initializing the :py:class:`~luma.led_matrix.device.max7219` -device with a parameter of :py:attr:`block_orientation="vertical"`: +This can be rectified by initializing the :py:class:`~luma.led_matrix.device.max7219` +device with a parameter of :py:attr:`block_orientation=-90` (or +90, if your device is +aligned the other way): .. code:: python @@ -212,7 +213,7 @@ device with a parameter of :py:attr:`block_orientation="vertical"`: from luma.led_matrix.device import max7219 serial = spi(port=0, device=0, gpio=noop()) - device = max7219(serial, cascaded=4, block_orientation="vertical") + device = max7219(serial, cascaded=4, block_orientation=-90) Every time a display render is subsequenly requested, the underlying image representation is corrected to reverse the 90° phase shift. diff --git a/examples/box_demo.py b/examples/box_demo.py index 4d0ce9e..fad975a 100755 --- a/examples/box_demo.py +++ b/examples/box_demo.py @@ -33,7 +33,7 @@ def demo(w, h, block_orientation, rotate): parser.add_argument('--width', type=int, default=8, help='Width') parser.add_argument('--height', type=int, default=8, help='height') - parser.add_argument('--block-orientation', type=str, default='vertical', choices=['horizontal', 'vertical'], help='Corrects block orientation when wired vertically') + parser.add_argument('--block-orientation', type=int, default=-90, choices=[0, 90, -90], help='Corrects block orientation when wired vertically') parser.add_argument('--rotate', type=int, default=0, choices=[0, 1, 2, 3], help='Rotation factor') args = parser.parse_args() diff --git a/examples/matrix_demo.py b/examples/matrix_demo.py index 7ca0f1f..a01558f 100755 --- a/examples/matrix_demo.py +++ b/examples/matrix_demo.py @@ -111,7 +111,7 @@ def demo(n, block_orientation): formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('--cascaded', '-n', type=int, default=1, help='Number of cascaded MAX7219 LED matrices') - parser.add_argument('--block-orientation', type=str, default='horizontal', choices=['horizontal', 'vertical'], help='Corrects block orientation when wired vertically') + parser.add_argument('--block-orientation', type=str, default=0, choices=[0, 90, -90], help='Corrects block orientation when wired vertically') args = parser.parse_args() diff --git a/luma/led_matrix/device.py b/luma/led_matrix/device.py index 6c2c68c..7158f6e 100644 --- a/luma/led_matrix/device.py +++ b/luma/led_matrix/device.py @@ -30,6 +30,7 @@ from luma.core.serial import noop from luma.core.device import device +from luma.core.util import deprecation import luma.core.error import luma.led_matrix.const @@ -45,7 +46,7 @@ class max7219(device): commands can then be called to affect the brightness and other settings. """ def __init__(self, serial_interface=None, width=8, height=8, cascaded=None, rotate=0, - block_orientation="horizontal", **kwargs): + block_orientation=0, **kwargs): super(max7219, self).__init__(luma.led_matrix.const.max7219, serial_interface) # Derive (override) the width and height if a cascaded param supplied @@ -59,9 +60,27 @@ def __init__(self, serial_interface=None, width=8, height=8, cascaded=None, rota raise luma.core.error.DeviceDisplayModeError( "Unsupported display mode: {0} x {1}".format(width, height)) + assert block_orientation in [0, 90, -90, "horizontal", "vertical"] + if block_orientation == "vertical": + msg = ( + "WARNING! block_orientation=\"vertical\" is now deprecated and " + "should be changed to block_orientation=-90 to acheive the same " + "effect. Use of \"vertical\" will be removed entirely beginning " + "v1.0.0") + deprecation(msg) + self._correction_angle = -90 + elif block_orientation == "horizontal": + msg = ( + "WARNING! block_orientation=\"horizontal\" is now deprecated and " + "should be changed to block_orientation=0 to acheive the same " + "effect. Use of \"horizontal\" will be removed entirely beginning " + "v1.0.0") + deprecation(msg) + self._correction_angle = 0 + else: + self._correction_angle = block_orientation + self.cascaded = cascaded or (width * height) // 64 - assert(block_orientation in ["horizontal", "vertical"]) - self._block_orientation = block_orientation self._offsets = [(y * self._w) + x for y in range(self._h - 8, -8, -8) for x in range(self._w - 8, -8, -8)] @@ -77,18 +96,18 @@ def __init__(self, serial_interface=None, width=8, height=8, cascaded=None, rota def preprocess(self, image): """ - Performs the inherited behviour (if any), and if the LED matrix is - declared to being a common row cathode, each 8x8 block of pixels - is rotated 90° clockwise. + Performs the inherited behviour (if any), and if the LED matrix + orientation is declared to need correction, each 8x8 block of pixels + is rotated 90° clockwise or counter-clockwise. """ image = super(max7219, self).preprocess(image) - if self._block_orientation == "vertical": + if self._correction_angle != 0: image = image.copy() for y in range(0, self._h, 8): for x in range(0, self._w, 8): box = (x, y, x + 8, y + 8) - rotated_block = image.crop(box).rotate(-90) + rotated_block = image.crop(box).rotate(self._correction_angle) image.paste(rotated_block, box) return image diff --git a/luma/led_matrix/legacy.py b/luma/led_matrix/legacy.py index 0b5dcf9..3bea257 100644 --- a/luma/led_matrix/legacy.py +++ b/luma/led_matrix/legacy.py @@ -8,7 +8,8 @@ # trigger DeprecationWarning -deprecation_msg = ("WARNING! 'luma.led_matrix.legacy' is now deprecated and will be removed in " - "v1.0.0. Please use the equivalent functionality from " - "'luma.core.legacy' and 'luma.core.legacy.font'.") +deprecation_msg = ( + "WARNING! 'luma.led_matrix.legacy' is now deprecated and will be removed in " + "v1.0.0. Please use the equivalent functionality from " + "'luma.core.legacy' and 'luma.core.legacy.font'.") deprecation(deprecation_msg) diff --git a/tests/test_max7219.py b/tests/test_max7219.py index 6c2e659..d8fc074 100644 --- a/tests/test_max7219.py +++ b/tests/test_max7219.py @@ -4,6 +4,7 @@ # See LICENSE.rst for details. import pytest +import warnings from luma.led_matrix.device import max7219 from luma.core.render import canvas @@ -121,7 +122,7 @@ def test_display_16x16(): def test_normal_alignment(): - device = max7219(serial, cascaded=2, block_orientation="horizontal") + device = max7219(serial, cascaded=2, block_orientation=0) serial.reset_mock() with canvas(device) as draw: @@ -139,8 +140,8 @@ def test_normal_alignment(): ]) -def test_block_realignment(): - device = max7219(serial, cascaded=2, block_orientation="vertical") +def test_block_realignment_minus90(): + device = max7219(serial, cascaded=2, block_orientation=-90) serial.reset_mock() with canvas(device) as draw: @@ -158,6 +159,32 @@ def test_block_realignment(): ]) +def test_block_realignment_plus90(): + device = max7219(serial, cascaded=2, block_orientation=90) + serial.reset_mock() + + with canvas(device) as draw: + draw.rectangle((0, 0, 15, 3), outline="white") + + serial.data.assert_has_calls([ + call([1, 0xFF, 1, 0xFF]), + call([2, 0x01, 2, 0x80]), + call([3, 0x01, 3, 0x80]), + call([4, 0xFF, 4, 0xFF]), + call([5, 0x00, 5, 0x00]), + call([6, 0x00, 6, 0x00]), + call([7, 0x00, 7, 0x00]), + call([8, 0x00, 8, 0x00]) + ]) + + def test_unknown_block_orientation(): with pytest.raises(AssertionError): max7219(serial, cascaded=2, block_orientation="sausages") + + +def test_deprecated_block_orientation(recwarn): + warnings.simplefilter('always') + max7219(serial, cascaded=2, block_orientation="vertical") + max7219(serial, cascaded=2, block_orientation="horizontal") + assert len(recwarn) == 2