diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index db94e45..10b414e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -9,7 +9,7 @@ name: CI
jobs:
test:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
strategy:
matrix:
deps:
@@ -32,3 +32,17 @@ jobs:
run: pip install -r requirements-dev.txt
- name: Run tests
run: pytest
+
+ format:
+ runs-on: ubuntu-24.04
+ name: Check code formatting with black
+ steps:
+ - uses: actions/checkout@v3
+ - name: Setup python
+ uses: actions/setup-python@v4
+ with:
+ python-version: 3.12
+ - name: Install black
+ run: pip install black~=25.1
+ - name: Check code formatting
+ run: black --check .
diff --git a/README.rst b/README.rst
index 13d7e1a..af9b92e 100644
--- a/README.rst
+++ b/README.rst
@@ -130,6 +130,10 @@ Coding Guidelines
flake8 --max-line-length=99 --ignore=E126,E127,E128,C901 RPLCD/lcd.py
+Additionally, please reformat your code using `black `_::
+
+ black .
+
About HD44780
=============
diff --git a/RPLCD/__init__.py b/RPLCD/__init__.py
index c79337b..cd57f62 100644
--- a/RPLCD/__init__.py
+++ b/RPLCD/__init__.py
@@ -9,6 +9,9 @@
class CharLCD:
def __new__(cls, *args, **kwargs):
from .gpio import CharLCD as GpioCharLCD
- warnings.warn("Using RPLCD.CharLCD directly is deprecated. " +
- "Use RPLCD.gpio.CharLCD instead!", DeprecationWarning)
+
+ warnings.warn(
+ "Using RPLCD.CharLCD directly is deprecated. Use RPLCD.gpio.CharLCD instead!",
+ DeprecationWarning,
+ )
return GpioCharLCD(*args, **kwargs)
diff --git a/RPLCD/codecs/__init__.py b/RPLCD/codecs/__init__.py
index aa71143..d328e70 100644
--- a/RPLCD/codecs/__init__.py
+++ b/RPLCD/codecs/__init__.py
@@ -12,6 +12,7 @@ class FoundMultiCharMapping(Exception):
"""
Exception to escape nested loops.
"""
+
pass
@@ -25,8 +26,7 @@ def __init__(self, codec):
def encode(self, input_): # type: (str) -> List[int]
result = []
- window_iter = sliding_window(
- input_, self.codec.combined_chars_lookahead)
+ window_iter = sliding_window(input_, self.codec.combined_chars_lookahead)
while True:
try:
window = next(window_iter)
@@ -60,8 +60,7 @@ def encode(self, input_): # type: (str) -> List[int]
continue
# Otherwise, do a regular lookup in the encoding table
- result.append(self.codec.encoding_table.get(
- char, self.codec.replacement_char))
+ result.append(self.codec.encoding_table.get(char, self.codec.replacement_char))
return result
diff --git a/RPLCD/common.py b/RPLCD/common.py
index f11d002..cfb24f8 100644
--- a/RPLCD/common.py
+++ b/RPLCD/common.py
@@ -19,6 +19,7 @@
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
+
import itertools
import time
@@ -72,7 +73,8 @@
RS_DATA = 0x01
-# # # Helper classes # # #
+# # # HELPER CLASSES # # #
+
class Alignment(object):
left = LCD_ENTRYLEFT
@@ -92,6 +94,7 @@ class CursorMode(object):
# # # HELPER FUNCTIONS # # #
+
def msleep(milliseconds):
"""Sleep the specified amount of milliseconds."""
time.sleep(milliseconds / 1000.0)
diff --git a/RPLCD/gpio.py b/RPLCD/gpio.py
index fba9c3b..4e12a19 100644
--- a/RPLCD/gpio.py
+++ b/RPLCD/gpio.py
@@ -19,6 +19,7 @@
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
+
from collections import namedtuple
import RPi.GPIO as GPIO
@@ -35,13 +36,23 @@
class CharLCD(BaseCharLCD):
- def __init__(self, numbering_mode=None, pin_rs=None, pin_rw=None, pin_e=None, pins_data=None,
- pin_backlight=None, backlight_mode='active_low',
- backlight_enabled=True,
- cols=20, rows=4, dotsize=8,
- charmap='A02',
- auto_linebreaks=True,
- compat_mode=False):
+ def __init__(
+ self,
+ numbering_mode=None,
+ pin_rs=None,
+ pin_rw=None,
+ pin_e=None,
+ pins_data=None,
+ pin_backlight=None,
+ backlight_mode='active_low',
+ backlight_enabled=True,
+ cols=20,
+ rows=4,
+ dotsize=8,
+ charmap='A02',
+ auto_linebreaks=True,
+ compat_mode=False,
+ ):
"""
Character LCD controller.
@@ -100,10 +111,12 @@ def __init__(self, numbering_mode=None, pin_rs=None, pin_rw=None, pin_e=None, pi
if numbering_mode == GPIO.BCM or numbering_mode == GPIO.BOARD:
self.numbering_mode = numbering_mode
else:
- raise ValueError('Invalid GPIO numbering mode: numbering_mode=%s, '
- 'must be either GPIO.BOARD or GPIO.BCM.\n'
- 'See https://gist.github.com/dbrgn/77d984a822bfc9fddc844f67016d0f7e '
- 'for more details.' % numbering_mode)
+ raise ValueError(
+ 'Invalid GPIO numbering mode: numbering_mode=%s, '
+ 'must be either GPIO.BOARD or GPIO.BCM.\n'
+ 'See https://gist.github.com/dbrgn/77d984a822bfc9fddc844f67016d0f7e '
+ 'for more details.' % numbering_mode
+ )
if pin_rs is None:
raise ValueError('pin_rs is not defined.')
if pin_e is None:
@@ -118,17 +131,27 @@ def __init__(self, numbering_mode=None, pin_rs=None, pin_rw=None, pin_e=None, pi
else:
raise ValueError('There should be exactly 4 or 8 data pins.')
block2 = pins_data[-4:]
- self.pins = PinConfig(rs=pin_rs, rw=pin_rw, e=pin_e,
- d0=block1[0], d1=block1[1], d2=block1[2], d3=block1[3],
- d4=block2[0], d5=block2[1], d6=block2[2], d7=block2[3],
- backlight=pin_backlight,
- mode=numbering_mode)
+ self.pins = PinConfig(
+ rs=pin_rs,
+ rw=pin_rw,
+ e=pin_e,
+ d0=block1[0],
+ d1=block1[1],
+ d2=block1[2],
+ d3=block1[3],
+ d4=block2[0],
+ d5=block2[1],
+ d6=block2[2],
+ d7=block2[3],
+ backlight=pin_backlight,
+ mode=numbering_mode,
+ )
self.backlight_mode = backlight_mode
# Call superclass
- super(CharLCD, self).__init__(cols, rows, dotsize,
- charmap=charmap,
- auto_linebreaks=auto_linebreaks)
+ super(CharLCD, self).__init__(
+ cols, rows, dotsize, charmap=charmap, auto_linebreaks=auto_linebreaks
+ )
# Set backlight status
if pin_backlight is not None:
@@ -150,9 +173,19 @@ def _init_connection(self):
GPIO.output(self.pins.rw, 0)
def _close_connection(self):
- pins = (self.pins.rs, self.pins.rw, self.pins.e, self.pins.d0, self.pins.d1,
- self.pins.d2, self.pins.d3, self.pins.d4, self.pins.d5, self.pins.d6,
- self.pins.d7)
+ pins = (
+ self.pins.rs,
+ self.pins.rw,
+ self.pins.e,
+ self.pins.d0,
+ self.pins.d1,
+ self.pins.d2,
+ self.pins.d3,
+ self.pins.d4,
+ self.pins.d5,
+ self.pins.d6,
+ self.pins.d7,
+ )
active_pins = [pin for pin in pins if pin is not None]
GPIO.cleanup(active_pins)
@@ -171,11 +204,13 @@ def _set_backlight_enabled(self, value):
if not isinstance(value, bool):
raise ValueError('backlight_enabled must be set to ``True`` or ``False``.')
self._backlight_enabled = value
- GPIO.output(self.pins.backlight,
- value ^ (self.backlight_mode == 'active_low'))
+ GPIO.output(self.pins.backlight, value ^ (self.backlight_mode == 'active_low'))
- backlight_enabled = property(_get_backlight_enabled, _set_backlight_enabled,
- doc='Whether or not to turn on the backlight.')
+ backlight_enabled = property(
+ _get_backlight_enabled,
+ _set_backlight_enabled,
+ doc='Whether or not to turn on the backlight.',
+ )
# Low level commands
@@ -205,11 +240,11 @@ def _send(self, value, mode):
self.last_send_event = now()
def _send_data(self, value):
- """Send data to the display. """
+ """Send data to the display."""
self._send(value, c.RS_DATA)
def _send_instruction(self, value):
- """Send instruction to the display. """
+ """Send instruction to the display."""
self._send(value, c.RS_INSTRUCTION)
def _write4bits(self, value):
diff --git a/RPLCD/i2c.py b/RPLCD/i2c.py
index 0c4a5bf..3f369ef 100644
--- a/RPLCD/i2c.py
+++ b/RPLCD/i2c.py
@@ -19,6 +19,7 @@
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
+
try:
from smbus import SMBus
except ImportError:
@@ -38,7 +39,7 @@
# MCP230XX backlight control
MCP230XX_BACKLIGHT = 0x80
-MCP230XX_NOBACKLIGHT = 0x7f
+MCP230XX_NOBACKLIGHT = 0x7F
# MCP230XX pin bitmasks and datamask
MCP230XX_RS = 0x02
@@ -58,11 +59,19 @@
class CharLCD(BaseCharLCD):
- def __init__(self, i2c_expander, address, expander_params=None, port=1,
- cols=20, rows=4, dotsize=8,
- charmap='A02',
- auto_linebreaks=True,
- backlight_enabled=True):
+ def __init__(
+ self,
+ i2c_expander,
+ address,
+ expander_params=None,
+ port=1,
+ cols=20,
+ rows=4,
+ dotsize=8,
+ charmap='A02',
+ auto_linebreaks=True,
+ backlight_enabled=True,
+ ):
"""
CharLCD via PCF8574 I2C port expander:
@@ -93,7 +102,7 @@ def __init__(self, i2c_expander, address, expander_params=None, port=1,
Pin mapping::
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
- -- | -- | -- | -- | -- | - | -- | -
+ -- | -- | -- | -- | -- | - | -- | -
BL | D7 | D6 | D5 | D4 | E | RS | -
@@ -138,8 +147,10 @@ def __init__(self, i2c_expander, address, expander_params=None, port=1,
# Errorchecking for expander parameters
if expander_params is None:
if self._i2c_expander == 'MCP23017':
- raise ValueError('MCP23017: expander_params[\'gpio_bank\'] is not defined, '
- 'must be either \'A\' or \'B\'')
+ raise ValueError(
+ 'MCP23017: expander_params[\'gpio_bank\'] is not defined, '
+ 'must be either \'A\' or \'B\''
+ )
else:
self._expander_params = {}
else:
@@ -148,8 +159,10 @@ def __init__(self, i2c_expander, address, expander_params=None, port=1,
self._expander_params = {}
self._expander_params['gpio_bank'] = expander_params['gpio_bank']
else:
- raise ValueError('MCP23017: expander_params[\'gpio_bank\'] is \'%s\', '
- 'must be either \'A\' or \'B\'' % expander_params['gpio_bank'])
+ raise ValueError(
+ 'MCP23017: expander_params[\'gpio_bank\'] is \'%s\', '
+ 'must be either \'A\' or \'B\'' % expander_params['gpio_bank']
+ )
# Currently the I2C mode only supports 4 bit communication
self.data_bus_mode = c.LCD_4BITMODE
@@ -161,9 +174,9 @@ def __init__(self, i2c_expander, address, expander_params=None, port=1,
self._backlight = MCP230XX_BACKLIGHT if backlight_enabled else MCP230XX_NOBACKLIGHT
# Call superclass
- super(CharLCD, self).__init__(cols, rows, dotsize,
- charmap=charmap,
- auto_linebreaks=auto_linebreaks)
+ super(CharLCD, self).__init__(
+ cols, rows, dotsize, charmap=charmap, auto_linebreaks=auto_linebreaks
+ )
# Refresh backlight status
self.backlight_enabled = backlight_enabled
@@ -216,8 +229,11 @@ def _set_backlight_enabled(self, value):
self._mcp_data &= MCP230XX_NOBACKLIGHT
self.bus.write_byte_data(self._address, self._mcp_gpio, self._mcp_data)
- backlight_enabled = property(_get_backlight_enabled, _set_backlight_enabled,
- doc='Whether or not to enable the backlight. Either ``True`` or ``False``.')
+ backlight_enabled = property(
+ _get_backlight_enabled,
+ _set_backlight_enabled,
+ doc='Whether or not to enable the backlight. Either ``True`` or ``False``.',
+ )
# Low level commands
@@ -225,8 +241,9 @@ def _send_data(self, value):
if self._i2c_expander == 'PCF8574':
self.bus.write_byte(self._address, (c.RS_DATA | (value & 0xF0)) | self._backlight)
self._pulse_data(c.RS_DATA | (value & 0xF0))
- self.bus.write_byte(self._address, (c.RS_DATA |
- ((value << 4) & 0xF0)) | self._backlight)
+ self.bus.write_byte(
+ self._address, (c.RS_DATA | ((value << 4) & 0xF0)) | self._backlight
+ )
self._pulse_data(c.RS_DATA | ((value << 4) & 0xF0))
elif self._i2c_expander in ['MCP23008', 'MCP23017']:
self._mcp_data |= MCP230XX_RS
@@ -235,11 +252,13 @@ def _send_data(self, value):
def _send_instruction(self, value):
if self._i2c_expander == 'PCF8574':
- self.bus.write_byte(self._address, (c.RS_INSTRUCTION |
- (value & 0xF0)) | self._backlight)
+ self.bus.write_byte(
+ self._address, (c.RS_INSTRUCTION | (value & 0xF0)) | self._backlight
+ )
self._pulse_data(c.RS_INSTRUCTION | (value & 0xF0))
- self.bus.write_byte(self._address, (c.RS_INSTRUCTION |
- ((value << 4) & 0xF0)) | self._backlight)
+ self.bus.write_byte(
+ self._address, (c.RS_INSTRUCTION | ((value << 4) & 0xF0)) | self._backlight
+ )
self._pulse_data(c.RS_INSTRUCTION | ((value << 4) & 0xF0))
elif self._i2c_expander in ['MCP23008', 'MCP23017']:
self._mcp_data &= ~MCP230XX_RS
diff --git a/RPLCD/lcd.py b/RPLCD/lcd.py
index 83f8b95..a58ed2d 100644
--- a/RPLCD/lcd.py
+++ b/RPLCD/lcd.py
@@ -19,6 +19,7 @@
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
+
from collections import namedtuple
from . import codecs
@@ -30,6 +31,7 @@
# # # MAIN # # #
+
class BaseCharLCD(object):
# Init, setup, teardown
@@ -67,7 +69,8 @@ def __init__(self, cols=20, rows=4, dotsize=8, charmap='A02', auto_linebreaks=Tr
pass
else:
raise ValueError(
- 'The ``charmap`` argument must be either ``A00`` or ``A02`` or ``ST0B``')
+ 'The ``charmap`` argument must be either ``A00`` or ``A02`` or ``ST0B``'
+ )
# LCD configuration
self.lcd = LCDConfig(rows=rows, cols=cols, dotsize=dotsize)
@@ -146,8 +149,9 @@ def _get_cursor_pos(self):
def _set_cursor_pos(self, value):
if not hasattr(value, '__getitem__') or len(value) != 2:
raise ValueError('Cursor position should be determined by a 2-tuple.')
- if self.auto_linebreaks and \
- (value[0] not in range(self.lcd.rows) or value[1] not in range(self.lcd.cols)):
+ if self.auto_linebreaks and (
+ value[0] not in range(self.lcd.rows) or value[1] not in range(self.lcd.cols)
+ ):
msg = 'Cursor position {pos!r} invalid on a {lcd.rows}x{lcd.cols} LCD.'
raise ValueError(msg.format(pos=value, lcd=self.lcd))
row_offsets = [0x00, 0x40, self.lcd.cols, 0x40 + self.lcd.cols]
@@ -155,8 +159,9 @@ def _set_cursor_pos(self, value):
self.command(c.LCD_SETDDRAMADDR | row_offsets[value[0]] + value[1])
c.usleep(50)
- cursor_pos = property(_get_cursor_pos, _set_cursor_pos,
- doc='The cursor position as a 2-tuple (row, col).')
+ cursor_pos = property(
+ _get_cursor_pos, _set_cursor_pos, doc='The cursor position as a 2-tuple (row, col).'
+ )
def _get_text_align_mode(self):
if self._text_align_mode == c.Alignment.left:
@@ -176,8 +181,11 @@ def _set_text_align_mode(self, value):
self.command(c.LCD_ENTRYMODESET | self._text_align_mode | self._display_shift_mode)
c.usleep(50)
- text_align_mode = property(_get_text_align_mode, _set_text_align_mode,
- doc='The text alignment (``left`` or ``right``).')
+ text_align_mode = property(
+ _get_text_align_mode,
+ _set_text_align_mode,
+ doc='The text alignment (``left`` or ``right``).',
+ )
def _get_write_shift_mode(self):
if self._display_shift_mode == c.ShiftMode.cursor:
@@ -197,8 +205,11 @@ def _set_write_shift_mode(self, value):
self.command(c.LCD_ENTRYMODESET | self._text_align_mode | self._display_shift_mode)
c.usleep(50)
- write_shift_mode = property(_get_write_shift_mode, _set_write_shift_mode,
- doc='The shift mode when writing (``cursor`` or ``display``).')
+ write_shift_mode = property(
+ _get_write_shift_mode,
+ _set_write_shift_mode,
+ doc='The shift mode when writing (``cursor`` or ``display``).',
+ )
def _get_display_enabled(self):
return self._display_mode == c.LCD_DISPLAYON
@@ -208,8 +219,9 @@ def _set_display_enabled(self, value):
self.command(c.LCD_DISPLAYCONTROL | self._display_mode | self._cursor_mode)
c.usleep(50)
- display_enabled = property(_get_display_enabled, _set_display_enabled,
- doc='Whether or not to display any characters.')
+ display_enabled = property(
+ _get_display_enabled, _set_display_enabled, doc='Whether or not to display any characters.'
+ )
def _get_cursor_mode(self):
if self._cursor_mode == c.CursorMode.hide:
@@ -233,8 +245,11 @@ def _set_cursor_mode(self, value):
self.command(c.LCD_DISPLAYCONTROL | self._display_mode | self._cursor_mode)
c.usleep(50)
- cursor_mode = property(_get_cursor_mode, _set_cursor_mode,
- doc='How the cursor should behave (``hide``, ``line`` or ``blink``).')
+ cursor_mode = property(
+ _get_cursor_mode,
+ _set_cursor_mode,
+ doc='How the cursor should behave (``hide``, ``line`` or ``blink``).',
+ )
# High level commands
@@ -284,8 +299,8 @@ def write_string(self, value):
# linebreak happened recently, and the lookahead matches too,
# ignore this write.
if self.recent_auto_linebreak is True:
- crlf = (char == codecs.CR and lookahead == codecs.LF)
- lfcr = (char == codecs.LF and lookahead == codecs.CR)
+ crlf = char == codecs.CR and lookahead == codecs.LF
+ lfcr = char == codecs.LF and lookahead == codecs.CR
if crlf or lfcr:
ignored = True
continue
diff --git a/RPLCD/pigpio.py b/RPLCD/pigpio.py
index 0127ace..0815c7a 100644
--- a/RPLCD/pigpio.py
+++ b/RPLCD/pigpio.py
@@ -20,6 +20,7 @@
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
+
from collections import namedtuple
import pigpio
@@ -39,16 +40,28 @@
class CharLCD(BaseCharLCD):
- def __init__(self, pi,
- pin_rs=None, pin_rw=None, pin_e=None, pin_e2=None,
- pins_data=None,
- pin_backlight=None, backlight_mode='active_low',
- backlight_pwm=False, backlight_enabled=True,
- pin_contrast=None, contrast_mode='active_high',
- contrast_pwm=None, contrast=0.5,
- cols=20, rows=4, dotsize=8,
- charmap='A02',
- auto_linebreaks=True):
+ def __init__(
+ self,
+ pi,
+ pin_rs=None,
+ pin_rw=None,
+ pin_e=None,
+ pin_e2=None,
+ pins_data=None,
+ pin_backlight=None,
+ backlight_mode='active_low',
+ backlight_pwm=False,
+ backlight_enabled=True,
+ pin_contrast=None,
+ contrast_mode='active_high',
+ contrast_pwm=None,
+ contrast=0.5,
+ cols=20,
+ rows=4,
+ dotsize=8,
+ charmap='A02',
+ auto_linebreaks=True,
+ ):
"""
Character LCD controller.
@@ -135,19 +148,31 @@ def __init__(self, pi,
else:
raise ValueError('There should be exactly 4 or 8 data pins.')
block2 = pins_data[-4:]
- self.pins = PinConfig(rs=pin_rs, rw=pin_rw, e=pin_e, e2=pin_e2,
- d0=block1[0], d1=block1[1], d2=block1[2], d3=block1[3],
- d4=block2[0], d5=block2[1], d6=block2[2], d7=block2[3],
- backlight=pin_backlight, contrast=pin_contrast)
+ self.pins = PinConfig(
+ rs=pin_rs,
+ rw=pin_rw,
+ e=pin_e,
+ e2=pin_e2,
+ d0=block1[0],
+ d1=block1[1],
+ d2=block1[2],
+ d3=block1[3],
+ d4=block2[0],
+ d5=block2[1],
+ d6=block2[2],
+ d7=block2[3],
+ backlight=pin_backlight,
+ contrast=pin_contrast,
+ )
self.backlight_mode = backlight_mode
self.backlight_pwm = backlight_pwm
self.contrast_mode = contrast_mode
self.contrast_pwm = contrast_pwm
# Call superclass
- super(CharLCD, self).__init__(cols, rows, dotsize,
- charmap=charmap,
- auto_linebreaks=auto_linebreaks)
+ super(CharLCD, self).__init__(
+ cols, rows, dotsize, charmap=charmap, auto_linebreaks=auto_linebreaks
+ )
# Set backlight status
if pin_backlight is not None:
@@ -181,41 +206,51 @@ def _init_connection(self):
# pigpio script to pulse the enable flag to process data
enablepulse = [
- 'write {pin.e} 0',
- 'mics 1',
- 'trig {pin.e} 1 1',
- 'mics 100'] # Commands need > 37us to settle
+ 'write {pin.e} 0',
+ 'mics 1',
+ 'trig {pin.e} 1 1',
+ 'mics 100', # Commands need > 37us to settle
+ ]
# pigpio script to write data to the LCD
- piscript = ['write {pin.rs} p0'] # Choose instruction or data mode
+ piscript = ['write {pin.rs} p0'] # Choose instruction or data mode
if self.pins.rw is not None:
# If the RW pin is used, set it to low
piscript.append('write {pin.rw} 0')
if self.data_bus_mode == c.LCD_8BITMODE:
# Script to write 8 bits of data into the data bus.
- piscript.extend( # Write data in 1 chunk of 8 bits
- ['write {pin.d0} p1', # Write 8 bits of data into the data bus
- 'write {pin.d1} p2',
- 'write {pin.d2} p3',
- 'write {pin.d3} p4',
- 'write {pin.d4} p5',
- 'write {pin.d5} p6',
- 'write {pin.d6} p7',
- 'write {pin.d7} p8'])
- piscript.extend(enablepulse) # Process data
+ piscript.extend( # Write data in 1 chunk of 8 bits
+ [
+ 'write {pin.d0} p1', # Write 8 bits of data into the data bus
+ 'write {pin.d1} p2',
+ 'write {pin.d2} p3',
+ 'write {pin.d3} p4',
+ 'write {pin.d4} p5',
+ 'write {pin.d5} p6',
+ 'write {pin.d6} p7',
+ 'write {pin.d7} p8',
+ ]
+ )
+ piscript.extend(enablepulse) # Process data
else:
- piscript.extend( # Write data in 2 chunks of 4 bits
- ['write {pin.d4} p5', # Write 4 bits of data into the data bus
- 'write {pin.d5} p6',
- 'write {pin.d6} p7',
- 'write {pin.d7} p8'])
- piscript.extend(enablepulse) # Process data
+ piscript.extend( # Write data in 2 chunks of 4 bits
+ [
+ 'write {pin.d4} p5', # Write 4 bits of data into the data bus
+ 'write {pin.d5} p6',
+ 'write {pin.d6} p7',
+ 'write {pin.d7} p8',
+ ]
+ )
+ piscript.extend(enablepulse) # Process data
piscript.extend(
- ['write {pin.d4} p1', # Write 4 bits of data into the data bus
- 'write {pin.d5} p2',
- 'write {pin.d6} p3',
- 'write {pin.d7} p4'])
- piscript.extend(enablepulse) # Process data
+ [
+ 'write {pin.d4} p1', # Write 4 bits of data into the data bus
+ 'write {pin.d5} p2',
+ 'write {pin.d6} p3',
+ 'write {pin.d7} p4',
+ ]
+ )
+ piscript.extend(enablepulse) # Process data
# Make one string and insert the pin values
piscript = ' '.join(piscript).format(pin=self.pins)
@@ -246,28 +281,32 @@ def _set_backlight_enabled(self, value):
if self.backlight_pwm:
if not ((0 <= value <= 1) or isinstance(value, bool)):
raise ValueError(
- 'backlight_enabled must be set to a value '
- 'between 0 and 1 or to ``True`` or ``False``, '
- 'if PWM is enabled; got {}'.format(value))
+ 'backlight_enabled must be set to a value '
+ 'between 0 and 1 or to ``True`` or ``False``, '
+ 'if PWM is enabled; got {}'.format(value)
+ )
else:
if not isinstance(value, bool):
raise ValueError(
- 'backlight_enabled must be set to ``True`` or ``False``, '
- 'if PWM is not enabled; got: {}'.format(value))
+ 'backlight_enabled must be set to ``True`` or ``False``, '
+ 'if PWM is not enabled; got: {}'.format(value)
+ )
self._backlight_enabled = value
if self.backlight_pwm:
# Convert perceived brightness (as requested by `value`) to duty
# cycle (see comment above definition of PWM):
- dc = 2**(value / PWM) - 1
+ dc = 2 ** (value / PWM) - 1
if self.backlight_mode == 'active_low':
dc = 255 - dc
self.pi.set_PWM_dutycycle(self.pins.backlight, round(dc))
else:
- self.pi.write(self.pins.backlight,
- value ^ (self.backlight_mode == 'active_low'))
+ self.pi.write(self.pins.backlight, value ^ (self.backlight_mode == 'active_low'))
- backlight_enabled = property(_get_backlight_enabled, _set_backlight_enabled,
- doc='Turn on/off or set the brightness of the backlight.')
+ backlight_enabled = property(
+ _get_backlight_enabled,
+ _set_backlight_enabled,
+ doc='Turn on/off or set the brightness of the backlight.',
+ )
def _get_contrast(self):
# We could probably read the current GPIO output state via sysfs, but
@@ -287,8 +326,7 @@ def _set_contrast(self, value):
dc = 255 - dc
self.pi.set_PWM_dutycycle(self.pins.contrast, round(dc))
- contrast = property(_get_contrast, _set_contrast,
- doc='Set the LCD contrast.')
+ contrast = property(_get_contrast, _set_contrast, doc='Set the LCD contrast.')
# Low level commands
@@ -313,9 +351,9 @@ def _send(self, value, mode):
pigpio.exceptions = True
def _send_data(self, value):
- """Send data to the display. """
+ """Send data to the display."""
self._send(value, c.RS_DATA)
def _send_instruction(self, value):
- """Send instruction to the display. """
+ """Send instruction to the display."""
self._send(value, c.RS_INSTRUCTION)
diff --git a/RPLCD_Tests/entrypoint.py b/RPLCD_Tests/entrypoint.py
index de0095b..52ee1e3 100755
--- a/RPLCD_Tests/entrypoint.py
+++ b/RPLCD_Tests/entrypoint.py
@@ -19,6 +19,7 @@
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
+
import sys
# Import supported tests
@@ -40,7 +41,7 @@ def print_usage(error=None):
print(' testsuite - Tests display formatting, 20x4 and 16x2 displays supported.')
print('')
# Options for i2c mode
- if ((len(sys.argv) > 1) and (sys.argv[1] == 'i2c')):
+ if (len(sys.argv) > 1) and (sys.argv[1] == 'i2c'):
print(' i2c options:')
print('')
print(' expander - Supported I²C port expanders are PCF8574, MCP23008 and MCP23017')
@@ -60,13 +61,17 @@ def print_usage(error=None):
print('')
print('Examples:')
print('')
- print(sys.argv[0] + ' i2c testsuite expander=PCF8574 addr=0x27 port=1 '
- 'cols=20 rows=4 charmap=A00')
- print(sys.argv[0] + ' i2c testsuite expander=MCP23017 addr=0x20 port=1 '
- 'kols=20 rows=4 charmap=A00 gpio_bank=A')
+ print(
+ sys.argv[0] + ' i2c testsuite expander=PCF8574 addr=0x27 port=1 '
+ 'cols=20 rows=4 charmap=A00'
+ )
+ print(
+ sys.argv[0] + ' i2c testsuite expander=MCP23017 addr=0x20 port=1 '
+ 'kols=20 rows=4 charmap=A00 gpio_bank=A'
+ )
# Options for GPIO mode
- elif ((len(sys.argv) > 1) and (sys.argv[1] == 'gpio')):
+ elif (len(sys.argv) > 1) and (sys.argv[1] == 'gpio'):
print(' gpio options:')
print('')
@@ -87,10 +92,12 @@ def print_usage(error=None):
print('')
print('Example:')
print('')
- print(sys.argv[0] + ' gpio testsuite cols=20 rows=4 mode=BCM rs=15 rw=None e=16 '
- 'bl=None data=21,22,23,24 charmap=A00')
+ print(
+ sys.argv[0] + ' gpio testsuite cols=20 rows=4 mode=BCM rs=15 rw=None e=16 '
+ 'bl=None data=21,22,23,24 charmap=A00'
+ )
# Options for PIGPIO mode
- elif ((len(sys.argv) > 1) and (sys.argv[1] == 'pigpio')):
+ elif (len(sys.argv) > 1) and (sys.argv[1] == 'pigpio'):
print(' pigpio options:')
print('')
@@ -118,8 +125,10 @@ def print_usage(error=None):
print('')
print('Example:')
print('')
- print(sys.argv[0] + ' pigpio testsuite cols=20 rows=4 rs=15 rw=None e=16 '
- 'bl=None data=21,22,23,24 charmap=A00')
+ print(
+ sys.argv[0] + ' pigpio testsuite cols=20 rows=4 rs=15 rw=None e=16 '
+ 'bl=None data=21,22,23,24 charmap=A00'
+ )
else:
print(' For info about options run:')
print('')
@@ -133,10 +142,10 @@ def print_usage(error=None):
def options_pop(value, default=no_default):
- ''' Pops value from options with error checking
- value: which option to pop and check.
- default: optional, sets a default if not defined.
- returns: a string corresponding to the option on the command line
+ '''Pops value from options with error checking
+ value: which option to pop and check.
+ default: optional, sets a default if not defined.
+ returns: a string corresponding to the option on the command line
'''
global options
try:
@@ -186,11 +195,20 @@ def run():
address = int(options_pop('addr'), 16)
port = int(options_pop('port', '1'))
try:
- lcd = i2c.CharLCD(i2c_expander, address, port=port, charmap=charmap, cols=cols,
- rows=rows, expander_params=options)
+ lcd = i2c.CharLCD(
+ i2c_expander,
+ address,
+ port=port,
+ charmap=charmap,
+ cols=cols,
+ rows=rows,
+ expander_params=options,
+ )
except IOError:
- print_usage('IOError: Usually caused by the wrong i2c address/port '
- 'or device not connected properly')
+ print_usage(
+ 'IOError: Usually caused by the wrong i2c address/port '
+ 'or device not connected properly'
+ )
elif lcdmode == 'gpio':
import RPi.GPIO as GPIO
@@ -221,8 +239,17 @@ def run():
pins_data = data.split(',')
# Convert data pins to int
pins_data = [int(pin) for pin in pins_data]
- lcd = gpio.CharLCD(pin_rs=rs, pin_rw=rw, pin_e=e, pins_data=pins_data, pin_backlight=bl,
- numbering_mode=numbering_mode, cols=cols, rows=rows, charmap=charmap)
+ lcd = gpio.CharLCD(
+ pin_rs=rs,
+ pin_rw=rw,
+ pin_e=e,
+ pins_data=pins_data,
+ pin_backlight=bl,
+ numbering_mode=numbering_mode,
+ cols=cols,
+ rows=rows,
+ charmap=charmap,
+ )
elif lcdmode == 'pigpio':
from pigpio import pi
@@ -254,12 +281,21 @@ def run():
pins_data = data.split(',')
# Convert data pins to int
pins_data = [int(pin) for pin in pins_data]
- lcd = pigpio.CharLCD(pi,
- pin_rs=rs, pin_rw=rw, pin_e=e, pins_data=pins_data, pin_backlight=bl,
- cols=cols, rows=rows, charmap=charmap)
+ lcd = pigpio.CharLCD(
+ pi,
+ pin_rs=rs,
+ pin_rw=rw,
+ pin_e=e,
+ pins_data=pins_data,
+ pin_backlight=bl,
+ cols=cols,
+ rows=rows,
+ charmap=charmap,
+ )
else:
- print_usage('Connection type %s is not supported. Must be either i2c, gpio or pigpio' %
- lcdmode)
+ print_usage(
+ 'Connection type %s is not supported. Must be either i2c, gpio or pigpio' % lcdmode
+ )
# Run selected test
if test == 'show_charmap':
diff --git a/RPLCD_Tests/testsuite_16x2.py b/RPLCD_Tests/testsuite_16x2.py
index 052ce7b..5bc3a74 100644
--- a/RPLCD_Tests/testsuite_16x2.py
+++ b/RPLCD_Tests/testsuite_16x2.py
@@ -63,8 +63,10 @@ def run(lcd):
input('The string "cursor" should now be on the second row, column 0. ')
lcd.home()
- input('Cursor should now be at initial position. Everything should be shifted '
- 'to the right by 5 characters. ')
+ input(
+ 'Cursor should now be at initial position. Everything should be shifted '
+ 'to the right by 5 characters. '
+ )
lcd.cursor_pos = (1, 15)
lcd.write_string('X')
@@ -103,8 +105,10 @@ def run(lcd):
lcd.cursor_pos = (0, 4)
lcd.write_string('5\n')
lcd.write_string('6')
- input('The numbers 1-6 should now be displayed in a zig zag line starting '
- 'in the top left corner. ')
+ input(
+ 'The numbers 1-6 should now be displayed in a zig zag line starting '
+ 'in the top left corner. '
+ )
lcd.clear()
lcd.write_string('This will wrap around both lines')
diff --git a/RPLCD_Tests/testsuite_20x4.py b/RPLCD_Tests/testsuite_20x4.py
index 5defa28..b67333b 100644
--- a/RPLCD_Tests/testsuite_20x4.py
+++ b/RPLCD_Tests/testsuite_20x4.py
@@ -65,8 +65,10 @@ def run(lcd):
input('The string "cursor" should now be on the third row, column 0. ')
lcd.home()
- input('Cursor should now be at initial position. Everything should be shifted '
- 'to the right by 5 characters. ')
+ input(
+ 'Cursor should now be at initial position. Everything should be shifted '
+ 'to the right by 5 characters. '
+ )
lcd.cursor_pos = (3, 19)
lcd.write_string('X')
@@ -98,8 +100,10 @@ def run(lcd):
lcd.write_string('2\n')
lcd.write_string('3\n')
lcd.write_string('4')
- input('The numbers 1-4 should now be displayed, each line shifted to the right '
- 'by 1 char more than the previous. ')
+ input(
+ 'The numbers 1-4 should now be displayed, each line shifted to the right '
+ 'by 1 char more than the previous. '
+ )
lcd.clear()
lcd.write_string('This is a long string that will wrap across multiple lines!')
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..2a28038
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,9 @@
+[tool.black]
+line-length = 99
+target-version = ["py38", "py39", "py310", "py311", "py312"]
+extend-exclude = '''
+(^/RPLCD/codecs/.*\.py$
+|^/docs/conf.py$
+)
+'''
+skip-string-normalization = true
diff --git a/setup.py b/setup.py
index 6fdacf6..a4c535a 100644
--- a/setup.py
+++ b/setup.py
@@ -2,36 +2,37 @@
from setuptools import setup
-readme = open('README.rst').read()
+readme = open("README.rst").read()
-setup(name='RPLCD',
- version='1.3.1',
- description='A Raspberry Pi LCD library for the widely used Hitachi HD44780 controller.',
- long_description=readme,
- author='Danilo Bargen',
- author_email='mail@dbrgn.ch',
- url='https://github.com/dbrgn/RPLCD',
- license='MIT',
- keywords='raspberry, raspberry pi, lcd, liquid crystal, hitachi, hd44780',
- packages=['RPLCD', 'RPLCD.codecs', 'RPLCD_Tests'],
- entry_points={
- 'console_scripts': ['rplcd-tests=RPLCD_Tests.entrypoint:run'],
- },
- platforms=['any'],
- python_requires='>=3.8',
- classifiers=[
- 'Development Status :: 5 - Production/Stable',
- 'Environment :: Other Environment',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: MIT License',
- 'Operating System :: POSIX',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.8',
- 'Programming Language :: Python :: 3.9',
- 'Programming Language :: Python :: 3.10',
- 'Programming Language :: Python :: 3.11',
- 'Programming Language :: Python :: 3.12',
- 'Topic :: System :: Hardware :: Hardware Drivers',
- 'Topic :: Software Development :: Libraries :: Python Modules',
- ],
- )
+setup(
+ name="RPLCD",
+ version="1.3.1",
+ description="A Raspberry Pi LCD library for the widely used Hitachi HD44780 controller.",
+ long_description=readme,
+ author="Danilo Bargen",
+ author_email="mail@dbrgn.ch",
+ url="https://github.com/dbrgn/RPLCD",
+ license="MIT",
+ keywords="raspberry, raspberry pi, lcd, liquid crystal, hitachi, hd44780",
+ packages=["RPLCD", "RPLCD.codecs", "RPLCD_Tests"],
+ entry_points={
+ "console_scripts": ["rplcd-tests=RPLCD_Tests.entrypoint:run"],
+ },
+ platforms=["any"],
+ python_requires=">=3.8",
+ classifiers=[
+ "Development Status :: 5 - Production/Stable",
+ "Environment :: Other Environment",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: POSIX",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Topic :: System :: Hardware :: Hardware Drivers",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ ],
+)
diff --git a/tests/conftest.py b/tests/conftest.py
index a6eb12e..21d214d 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -16,6 +16,7 @@
@pytest.fixture
def charlcd_kwargs():
import RPi.GPIO as GPIO
+
return {
'numbering_mode': GPIO.BOARD,
'pin_rs': 15,
diff --git a/tests/test_auto_linebreaks.py b/tests/test_auto_linebreaks.py
index cfbf0b0..a4220df 100644
--- a/tests/test_auto_linebreaks.py
+++ b/tests/test_auto_linebreaks.py
@@ -13,6 +13,7 @@ def _func(cols, rows, auto_linebreaks):
mocker.patch.object(lcd, '_send_data')
mocker.patch.object(lcd, '_send_instruction')
return lcd
+
return _func
diff --git a/tests/test_codecs.py b/tests/test_codecs.py
index 758bd5b..7c9840c 100644
--- a/tests/test_codecs.py
+++ b/tests/test_codecs.py
@@ -3,38 +3,57 @@
from RPLCD import codecs
-@pytest.mark.parametrize(['input_', 'result_a00', 'result_a02', 'result_st0b'], [
- # Empty
- ('', [], [], []),
- # Single char, obvious mapping
- (' ', [32], [32], [32]),
- ('a', [97], [97], [97]),
- # Single char, different mapping depending on charmap
- ('α', [224], [144], [223]),
- # Single char, only available on some charmaps
- ('♡', [32], [157], [32]),
- ('❤', [32], [157], [32]),
- ('°', [223], [32], [178]),
- # Multiple 1:1 mapped chars
- ('asdf', [97, 115, 100, 102], [97, 115, 100, 102], [97, 115, 100, 102]),
- # Combined mapping
- ('\u207B\u00B9', [233], [32, 185], [191, 32]),
- ('as\u207B\u00B9df', [97, 115, 233, 100, 102], [97, 115, 32, 185, 100, 102], [97, 115, 191, 32, 100, 102]),
- ('\u207B', [32], [32], [191]),
- ('\u207Ba', [32, 97], [32, 97], [191, 97]),
- # Containing newlines and carriage returns
- ('a\r\nb', [97, codecs.CR, codecs.LF, 98], [97, codecs.CR, codecs.LF, 98], [97, codecs.CR, codecs.LF, 98]),
-])
+@pytest.mark.parametrize(
+ ['input_', 'result_a00', 'result_a02', 'result_st0b'],
+ [
+ # Empty
+ ('', [], [], []),
+ # Single char, obvious mapping
+ (' ', [32], [32], [32]),
+ ('a', [97], [97], [97]),
+ # Single char, different mapping depending on charmap
+ ('α', [224], [144], [223]),
+ # Single char, only available on some charmaps
+ ('♡', [32], [157], [32]),
+ ('❤', [32], [157], [32]),
+ ('°', [223], [32], [178]),
+ # Multiple 1:1 mapped chars
+ ('asdf', [97, 115, 100, 102], [97, 115, 100, 102], [97, 115, 100, 102]),
+ # Combined mapping
+ ('\u207b\u00b9', [233], [32, 185], [191, 32]),
+ (
+ 'as\u207b\u00b9df',
+ [97, 115, 233, 100, 102],
+ [97, 115, 32, 185, 100, 102],
+ [97, 115, 191, 32, 100, 102],
+ ),
+ ('\u207b', [32], [32], [191]),
+ ('\u207ba', [32, 97], [32, 97], [191, 97]),
+ # Containing newlines and carriage returns
+ (
+ 'a\r\nb',
+ [97, codecs.CR, codecs.LF, 98],
+ [97, codecs.CR, codecs.LF, 98],
+ [97, codecs.CR, codecs.LF, 98],
+ ),
+ ],
+)
def test_encode(input_, result_a00, result_a02, result_st0b):
a00 = codecs.A00Codec()
a02 = codecs.A02Codec()
st0b = codecs.ST0BCodec()
- assert a00.encode(input_) == result_a00, \
- 'A00: Input %r encoded to %s' % (input_, a00.encode(input_))
+ assert a00.encode(input_) == result_a00, 'A00: Input %r encoded to %s' % (
+ input_,
+ a00.encode(input_),
+ )
- assert a02.encode(input_) == result_a02, \
- 'A02: Input %r encoded to %s' % (input_, a02.encode(input_))
-
- assert st0b.encode(input_) == result_st0b, \
- 'ST0B: Input %r encoded to %s' % (input_, st0b.encode(input_))
+ assert a02.encode(input_) == result_a02, 'A02: Input %r encoded to %s' % (
+ input_,
+ a02.encode(input_),
+ )
+
+ assert st0b.encode(input_) == result_st0b, 'ST0B: Input %r encoded to %s' % (
+ input_,
+ st0b.encode(input_),
+ )
diff --git a/tests/test_common.py b/tests/test_common.py
index 8971d5c..148ada9 100644
--- a/tests/test_common.py
+++ b/tests/test_common.py
@@ -3,13 +3,16 @@
from RPLCD import common
-@pytest.mark.parametrize(['input_', 'lookahead', 'result'], [
- ('hi', 0, [('h',), ('i',)]),
- ('hi', 1, [('h', 'i'), ('i', ' ')]),
- ('hi', 2, [('h', 'i', ' '), ('i', ' ', ' ')]),
- ('', 0, []),
- ('', 1, []),
- ('', 7, []),
-])
+@pytest.mark.parametrize(
+ ['input_', 'lookahead', 'result'],
+ [
+ ('hi', 0, [('h',), ('i',)]),
+ ('hi', 1, [('h', 'i'), ('i', ' ')]),
+ ('hi', 2, [('h', 'i', ' '), ('i', ' ', ' ')]),
+ ('', 0, []),
+ ('', 1, []),
+ ('', 7, []),
+ ],
+)
def test_window_function(input_, lookahead, result):
assert list(common.sliding_window(input_, lookahead)) == result
diff --git a/tests/test_write.py b/tests/test_write.py
index 695b107..c337f03 100644
--- a/tests/test_write.py
+++ b/tests/test_write.py
@@ -59,10 +59,13 @@ def test_caching(mocker, charlcd_kwargs):
assert instruction_calls[2] == (LCD_SETDDRAMADDR | 5,)
-@pytest.mark.parametrize(['charmap', 'ue'], [
- ('A00', 0b11110101),
- ('A02', 0b11111100),
-])
+@pytest.mark.parametrize(
+ ['charmap', 'ue'],
+ [
+ ('A00', 0b11110101),
+ ('A02', 0b11111100),
+ ],
+)
def test_charmap(mocker, charmap, ue, charlcd_kwargs):
"""
The charmap should be used. The "ü" Umlaut should be encoded correctly.
@@ -81,10 +84,13 @@ def test_charmap(mocker, charmap, ue, charlcd_kwargs):
assert calls[3] == (105,)
-@pytest.mark.parametrize(['rows', 'cols'], [
- (2, 16),
- (4, 20),
-])
+@pytest.mark.parametrize(
+ ['rows', 'cols'],
+ [
+ (2, 16),
+ (4, 20),
+ ],
+)
def test_write_newline(mocker, rows, cols, charlcd_kwargs):
"""
Write text containing CR/LF chars to the display.