diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..21c125c
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,11 @@
+# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries
+#
+# SPDX-License-Identifier: Unlicense
+
+.py text eol=lf
+.rst text eol=lf
+.txt text eol=lf
+.yaml text eol=lf
+.toml text eol=lf
+.license text eol=lf
+.md text eol=lf
diff --git a/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md b/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md
index 71ef8f8..8de294e 100644
--- a/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md
+++ b/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md
@@ -4,7 +4,7 @@
Thank you for contributing! Before you submit a pull request, please read the following.
-Make sure any changes you're submitting are in line with the CircuitPython Design Guide, available here: https://circuitpython.readthedocs.io/en/latest/docs/design_guide.html
+Make sure any changes you're submitting are in line with the CircuitPython Design Guide, available here: https://docs.circuitpython.org/en/latest/docs/design_guide.html
If your changes are to documentation, please verify that the documentation builds locally by following the steps found here: https://adafru.it/build-docs
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ca35544..041a337 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -10,66 +10,5 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- - name: Dump GitHub context
- env:
- GITHUB_CONTEXT: ${{ toJson(github) }}
- run: echo "$GITHUB_CONTEXT"
- - name: Translate Repo Name For Build Tools filename_prefix
- id: repo-name
- run: |
- echo ::set-output name=repo-name::$(
- echo ${{ github.repository }} |
- awk -F '\/' '{ print tolower($2) }' |
- tr '_' '-'
- )
- - name: Set up Python 3.7
- uses: actions/setup-python@v1
- with:
- python-version: 3.7
- - name: Versions
- run: |
- python3 --version
- - name: Checkout Current Repo
- uses: actions/checkout@v1
- with:
- submodules: true
- - name: Checkout tools repo
- uses: actions/checkout@v2
- with:
- repository: adafruit/actions-ci-circuitpython-libs
- path: actions-ci
- - name: Install dependencies
- # (e.g. - apt-get: gettext, etc; pip: circuitpython-build-tools, requirements.txt; etc.)
- run: |
- source actions-ci/install.sh
- - name: Pip install Sphinx, pre-commit
- run: |
- pip install --force-reinstall Sphinx sphinx-rtd-theme pre-commit
- - name: Library version
- run: git describe --dirty --always --tags
- - name: Pre-commit hooks
- run: |
- pre-commit run --all-files
- - name: Build assets
- run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location .
- - name: Archive bundles
- uses: actions/upload-artifact@v2
- with:
- name: bundles
- path: ${{ github.workspace }}/bundles/
- - name: Build docs
- working-directory: docs
- run: sphinx-build -E -W -b html . _build/html
- - name: Check For setup.py
- id: need-pypi
- run: |
- echo ::set-output name=setup-py::$( find . -wholename './setup.py' )
- - name: Build Python package
- if: contains(steps.need-pypi.outputs.setup-py, 'setup.py')
- run: |
- pip install --upgrade setuptools wheel twine readme_renderer testresources
- python setup.py sdist
- python setup.py bdist_wheel --universal
- twine check dist/*
- - name: Setup problem matchers
- uses: adafruit/circuitpython-action-library-ci-problem-matchers@v1
+ - name: Run Build CI workflow
+ uses: adafruit/workflows-circuitpython-libs/build@main
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
deleted file mode 100644
index 6d0015a..0000000
--- a/.github/workflows/release.yml
+++ /dev/null
@@ -1,85 +0,0 @@
-# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
-#
-# SPDX-License-Identifier: MIT
-
-name: Release Actions
-
-on:
- release:
- types: [published]
-
-jobs:
- upload-release-assets:
- runs-on: ubuntu-latest
- steps:
- - name: Dump GitHub context
- env:
- GITHUB_CONTEXT: ${{ toJson(github) }}
- run: echo "$GITHUB_CONTEXT"
- - name: Translate Repo Name For Build Tools filename_prefix
- id: repo-name
- run: |
- echo ::set-output name=repo-name::$(
- echo ${{ github.repository }} |
- awk -F '\/' '{ print tolower($2) }' |
- tr '_' '-'
- )
- - name: Set up Python 3.6
- uses: actions/setup-python@v1
- with:
- python-version: 3.6
- - name: Versions
- run: |
- python3 --version
- - name: Checkout Current Repo
- uses: actions/checkout@v1
- with:
- submodules: true
- - name: Checkout tools repo
- uses: actions/checkout@v2
- with:
- repository: adafruit/actions-ci-circuitpython-libs
- path: actions-ci
- - name: Install deps
- run: |
- source actions-ci/install.sh
- - name: Build assets
- run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location .
- - name: Upload Release Assets
- # the 'official' actions version does not yet support dynamically
- # supplying asset names to upload. @csexton's version chosen based on
- # discussion in the issue below, as its the simplest to implement and
- # allows for selecting files with a pattern.
- # https://github.com/actions/upload-release-asset/issues/4
- #uses: actions/upload-release-asset@v1.0.1
- uses: csexton/release-asset-action@master
- with:
- pattern: "bundles/*"
- github-token: ${{ secrets.GITHUB_TOKEN }}
-
- upload-pypi:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v1
- - name: Check For setup.py
- id: need-pypi
- run: |
- echo ::set-output name=setup-py::$( find . -wholename './setup.py' )
- - name: Set up Python
- if: contains(steps.need-pypi.outputs.setup-py, 'setup.py')
- uses: actions/setup-python@v1
- with:
- python-version: '3.x'
- - name: Install dependencies
- if: contains(steps.need-pypi.outputs.setup-py, 'setup.py')
- run: |
- python -m pip install --upgrade pip
- pip install setuptools wheel twine
- - name: Build and publish
- if: contains(steps.need-pypi.outputs.setup-py, 'setup.py')
- env:
- TWINE_USERNAME: ${{ secrets.pypi_username }}
- TWINE_PASSWORD: ${{ secrets.pypi_password }}
- run: |
- python setup.py sdist
- twine upload dist/*
diff --git a/.github/workflows/release_gh.yml b/.github/workflows/release_gh.yml
new file mode 100644
index 0000000..9acec60
--- /dev/null
+++ b/.github/workflows/release_gh.yml
@@ -0,0 +1,19 @@
+# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
+
+name: GitHub Release Actions
+
+on:
+ release:
+ types: [published]
+
+jobs:
+ upload-release-assets:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Run GitHub Release CI workflow
+ uses: adafruit/workflows-circuitpython-libs/release-gh@main
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ upload-url: ${{ github.event.release.upload_url }}
diff --git a/.github/workflows/release_pypi.yml b/.github/workflows/release_pypi.yml
new file mode 100644
index 0000000..65775b7
--- /dev/null
+++ b/.github/workflows/release_pypi.yml
@@ -0,0 +1,19 @@
+# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
+
+name: PyPI Release Actions
+
+on:
+ release:
+ types: [published]
+
+jobs:
+ upload-release-assets:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Run PyPI Release CI workflow
+ uses: adafruit/workflows-circuitpython-libs/release-pypi@main
+ with:
+ pypi-username: ${{ secrets.pypi_username }}
+ pypi-password: ${{ secrets.pypi_password }}
diff --git a/.gitignore b/.gitignore
index 597d08d..7d6346a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,16 +1,52 @@
-# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
+# SPDX-FileCopyrightText: 2022 Kattni Rembor, written for Adafruit Industries
+# SPDX-FileCopyrightText: 2023 Matt Land
#
-# SPDX-License-Identifier: Unlicense
+# SPDX-License-Identifier: MIT
+# Do not include files and directories created by your personal work environment, such as the IDE
+# you use, except for those already listed here. Pull requests including changes to this file will
+# not be accepted.
+
+# This .gitignore file contains rules for files generated by working with CircuitPython libraries,
+# including building Sphinx, testing with pip, and creating a virual environment, as well as the
+# MacOS and IDE-specific files generated by using MacOS in general, or the PyCharm or VSCode IDEs.
+
+# If you find that there are files being generated on your machine that should not be included in
+# your git commit, you should create a .gitignore_global file on your computer to include the
+# files created by your personal setup. To do so, follow the two steps below.
+
+# First, create a file called .gitignore_global somewhere convenient for you, and add rules for
+# the files you want to exclude from git commits.
+
+# Second, configure Git to use the exclude file for all Git repositories by running the
+# following via commandline, replacing "path/to/your/" with the actual path to your newly created
+# .gitignore_global file:
+# git config --global core.excludesfile path/to/your/.gitignore_global
+
+# CircuitPython-specific files
*.mpy
-.idea
+
+# Python-specific files
__pycache__
-_build
*.pyc
+
+# Sphinx build-specific files
+_build
+
+# This file results from running `pip -e install .` in a local repository
+*.egg-info
+.eggs
+
+# Virtual environment-specific files
.env
-bundles
+.venv
+
+# MacOS-specific files
*.DS_Store
-.eggs
-dist
-**/*.egg-info
-venv
+
+# IDE-specific files
+.idea
+.vscode
+*~
+
+.mypy_cache/
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 43d1385..ff19dde 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,42 +1,21 @@
-# SPDX-FileCopyrightText: 2020 Diego Elio Pettenò
+# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
repos:
-- repo: https://github.com/python/black
- rev: 20.8b1
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.5.0
hooks:
- - id: black
-- repo: https://github.com/fsfe/reuse-tool
- rev: v0.12.1
+ - id: check-yaml
+ - id: end-of-file-fixer
+ - id: trailing-whitespace
+ - repo: https://github.com/astral-sh/ruff-pre-commit
+ rev: v0.3.4
hooks:
- - id: reuse
-- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v2.3.0
+ - id: ruff-format
+ - id: ruff
+ args: ["--fix"]
+ - repo: https://github.com/fsfe/reuse-tool
+ rev: v3.0.1
hooks:
- - id: check-yaml
- - id: end-of-file-fixer
- - id: trailing-whitespace
-- repo: https://github.com/pycqa/pylint
- rev: v2.11.1
- hooks:
- - id: pylint
- name: pylint (library code)
- types: [python]
- args:
- - --disable=consider-using-f-string,duplicate-code
- exclude: "^(docs/|examples/|tests/|setup.py$)"
- - id: pylint
- name: pylint (example code)
- description: Run pylint rules on "examples/*.py" files
- types: [python]
- files: "^examples/"
- args:
- - --disable=missing-docstring,invalid-name,consider-using-f-string,duplicate-code
- - id: pylint
- name: pylint (test code)
- description: Run pylint rules on "tests/*.py" files
- types: [python]
- files: "^tests/"
- args:
- - --disable=missing-docstring,consider-using-f-string,duplicate-code
+ - id: reuse
diff --git a/.pylintrc b/.pylintrc
deleted file mode 100644
index 61d81fb..0000000
--- a/.pylintrc
+++ /dev/null
@@ -1,437 +0,0 @@
-# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
-#
-# SPDX-License-Identifier: Unlicense
-
-[MASTER]
-
-# A comma-separated list of package or module names from where C extensions may
-# be loaded. Extensions are loading into the active Python interpreter and may
-# run arbitrary code
-extension-pkg-whitelist=
-
-# Add files or directories to the blacklist. They should be base names, not
-# paths.
-ignore=CVS
-
-# Add files or directories matching the regex patterns to the blacklist. The
-# regex matches against base names, not paths.
-ignore-patterns=
-
-# Python code to execute, usually for sys.path manipulation such as
-# pygtk.require().
-#init-hook=
-
-# Use multiple processes to speed up Pylint.
-jobs=1
-# jobs=2
-
-# List of plugins (as comma separated values of python modules names) to load,
-# usually to register additional checkers.
-load-plugins=
-
-# Pickle collected data for later comparisons.
-persistent=yes
-
-# Specify a configuration file.
-#rcfile=
-
-# Allow loading of arbitrary C extensions. Extensions are imported into the
-# active Python interpreter and may run arbitrary code.
-unsafe-load-any-extension=no
-
-
-[MESSAGES CONTROL]
-
-# Only show warnings with the listed confidence levels. Leave empty to show
-# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
-confidence=
-
-# Disable the message, report, category or checker with the given id(s). You
-# can either give multiple identifiers separated by comma (,) or put this
-# option multiple times (only on the command line, not in the configuration
-# file where it should appear only once).You can also use "--disable=all" to
-# disable everything first and then reenable specific checks. For example, if
-# you want to run only the similarities checker, you can use "--disable=all
-# --enable=similarities". If you want to run only the classes checker, but have
-# no Warning level messages displayed, use"--disable=all --enable=classes
-# --disable=W"
-# disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call
-disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error,bad-continuation,unspecified-encoding
-
-# Enable the message, report, category or checker with the given id(s). You can
-# either give multiple identifier separated by comma (,) or put this option
-# multiple time (only on the command line, not in the configuration file where
-# it should appear only once). See also the "--disable" option for examples.
-enable=
-
-
-[REPORTS]
-
-# Python expression which should return a note less than 10 (10 is the highest
-# note). You have access to the variables errors warning, statement which
-# respectively contain the number of errors / warnings messages and the total
-# number of statements analyzed. This is used by the global evaluation report
-# (RP0004).
-evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
-
-# Template used to display messages. This is a python new-style format string
-# used to format the message information. See doc for all details
-#msg-template=
-
-# Set the output format. Available formats are text, parseable, colorized, json
-# and msvs (visual studio).You can also give a reporter class, eg
-# mypackage.mymodule.MyReporterClass.
-output-format=text
-
-# Tells whether to display a full report or only the messages
-reports=no
-
-# Activate the evaluation score.
-score=yes
-
-
-[REFACTORING]
-
-# Maximum number of nested blocks for function / method body
-max-nested-blocks=5
-
-
-[LOGGING]
-
-# Logging modules to check that the string format arguments are in logging
-# function parameter format
-logging-modules=logging
-
-
-[SPELLING]
-
-# Spelling dictionary name. Available dictionaries: none. To make it working
-# install python-enchant package.
-spelling-dict=
-
-# List of comma separated words that should not be checked.
-spelling-ignore-words=
-
-# A path to a file that contains private dictionary; one word per line.
-spelling-private-dict-file=
-
-# Tells whether to store unknown words to indicated private dictionary in
-# --spelling-private-dict-file option instead of raising a message.
-spelling-store-unknown-words=no
-
-
-[MISCELLANEOUS]
-
-# List of note tags to take in consideration, separated by a comma.
-# notes=FIXME,XXX,TODO
-notes=FIXME,XXX
-
-
-[TYPECHECK]
-
-# List of decorators that produce context managers, such as
-# contextlib.contextmanager. Add to this list to register other decorators that
-# produce valid context managers.
-contextmanager-decorators=contextlib.contextmanager
-
-# List of members which are set dynamically and missed by pylint inference
-# system, and so shouldn't trigger E1101 when accessed. Python regular
-# expressions are accepted.
-generated-members=
-
-# Tells whether missing members accessed in mixin class should be ignored. A
-# mixin class is detected if its name ends with "mixin" (case insensitive).
-ignore-mixin-members=yes
-
-# This flag controls whether pylint should warn about no-member and similar
-# checks whenever an opaque object is returned when inferring. The inference
-# can return multiple potential results while evaluating a Python object, but
-# some branches might not be evaluated, which results in partial inference. In
-# that case, it might be useful to still emit no-member and other checks for
-# the rest of the inferred objects.
-ignore-on-opaque-inference=yes
-
-# List of class names for which member attributes should not be checked (useful
-# for classes with dynamically set attributes). This supports the use of
-# qualified names.
-ignored-classes=optparse.Values,thread._local,_thread._local
-
-# List of module names for which member attributes should not be checked
-# (useful for modules/projects where namespaces are manipulated during runtime
-# and thus existing member attributes cannot be deduced by static analysis. It
-# supports qualified module names, as well as Unix pattern matching.
-ignored-modules=board
-
-# Show a hint with possible names when a member name was not found. The aspect
-# of finding the hint is based on edit distance.
-missing-member-hint=yes
-
-# The minimum edit distance a name should have in order to be considered a
-# similar match for a missing member name.
-missing-member-hint-distance=1
-
-# The total number of similar names that should be taken in consideration when
-# showing a hint for a missing member.
-missing-member-max-choices=1
-
-
-[VARIABLES]
-
-# List of additional names supposed to be defined in builtins. Remember that
-# you should avoid to define new builtins when possible.
-additional-builtins=
-
-# Tells whether unused global variables should be treated as a violation.
-allow-global-unused-variables=yes
-
-# List of strings which can identify a callback function by name. A callback
-# name must start or end with one of those strings.
-callbacks=cb_,_cb
-
-# A regular expression matching the name of dummy variables (i.e. expectedly
-# not used).
-dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
-
-# Argument names that match this expression will be ignored. Default to name
-# with leading underscore
-ignored-argument-names=_.*|^ignored_|^unused_
-
-# Tells whether we should check for unused import in __init__ files.
-init-import=no
-
-# List of qualified module names which can have objects that can redefine
-# builtins.
-redefining-builtins-modules=six.moves,future.builtins
-
-
-[FORMAT]
-
-# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
-# expected-line-ending-format=
-expected-line-ending-format=LF
-
-# Regexp for a line that is allowed to be longer than the limit.
-ignore-long-lines=^\s*(# )??$
-
-# Number of spaces of indent required inside a hanging or continued line.
-indent-after-paren=4
-
-# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
-# tab).
-indent-string=' '
-
-# Maximum number of characters on a single line.
-max-line-length=100
-
-# Maximum number of lines in a module
-max-module-lines=1000
-
-# List of optional constructs for which whitespace checking is disabled. `dict-
-# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
-# `trailing-comma` allows a space between comma and closing bracket: (a, ).
-# `empty-line` allows space-only lines.
-no-space-check=trailing-comma,dict-separator
-
-# Allow the body of a class to be on the same line as the declaration if body
-# contains single statement.
-single-line-class-stmt=no
-
-# Allow the body of an if to be on the same line as the test if there is no
-# else.
-single-line-if-stmt=no
-
-
-[SIMILARITIES]
-
-# Ignore comments when computing similarities.
-ignore-comments=yes
-
-# Ignore docstrings when computing similarities.
-ignore-docstrings=yes
-
-# Ignore imports when computing similarities.
-ignore-imports=yes
-
-# Minimum lines number of a similarity.
-min-similarity-lines=4
-
-
-[BASIC]
-
-# Naming hint for argument names
-argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
-
-# Regular expression matching correct argument names
-argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
-
-# Naming hint for attribute names
-attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
-
-# Regular expression matching correct attribute names
-attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
-
-# Bad variable names which should always be refused, separated by a comma
-bad-names=foo,bar,baz,toto,tutu,tata
-
-# Naming hint for class attribute names
-class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
-
-# Regular expression matching correct class attribute names
-class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
-
-# Naming hint for class names
-# class-name-hint=[A-Z_][a-zA-Z0-9]+$
-class-name-hint=[A-Z_][a-zA-Z0-9_]+$
-
-# Regular expression matching correct class names
-# class-rgx=[A-Z_][a-zA-Z0-9]+$
-class-rgx=[A-Z_][a-zA-Z0-9_]+$
-
-# Naming hint for constant names
-const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
-
-# Regular expression matching correct constant names
-const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
-
-# Minimum line length for functions/classes that require docstrings, shorter
-# ones are exempt.
-docstring-min-length=-1
-
-# Naming hint for function names
-function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
-
-# Regular expression matching correct function names
-function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
-
-# Good variable names which should always be accepted, separated by a comma
-# good-names=i,j,k,ex,Run,_
-good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_
-
-# Include a hint for the correct naming format with invalid-name
-include-naming-hint=no
-
-# Naming hint for inline iteration names
-inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
-
-# Regular expression matching correct inline iteration names
-inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
-
-# Naming hint for method names
-method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
-
-# Regular expression matching correct method names
-method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
-
-# Naming hint for module names
-module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
-
-# Regular expression matching correct module names
-module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
-
-# Colon-delimited sets of names that determine each other's naming style when
-# the name regexes allow several styles.
-name-group=
-
-# Regular expression which should only match function or class names that do
-# not require a docstring.
-no-docstring-rgx=^_
-
-# List of decorators that produce properties, such as abc.abstractproperty. Add
-# to this list to register other decorators that produce valid properties.
-property-classes=abc.abstractproperty
-
-# Naming hint for variable names
-variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
-
-# Regular expression matching correct variable names
-variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
-
-
-[IMPORTS]
-
-# Allow wildcard imports from modules that define __all__.
-allow-wildcard-with-all=no
-
-# Analyse import fallback blocks. This can be used to support both Python 2 and
-# 3 compatible code, which means that the block might have code that exists
-# only in one or another interpreter, leading to false positives when analysed.
-analyse-fallback-blocks=no
-
-# Deprecated modules which should not be used, separated by a comma
-deprecated-modules=optparse,tkinter.tix
-
-# Create a graph of external dependencies in the given file (report RP0402 must
-# not be disabled)
-ext-import-graph=
-
-# Create a graph of every (i.e. internal and external) dependencies in the
-# given file (report RP0402 must not be disabled)
-import-graph=
-
-# Create a graph of internal dependencies in the given file (report RP0402 must
-# not be disabled)
-int-import-graph=
-
-# Force import order to recognize a module as part of the standard
-# compatibility libraries.
-known-standard-library=
-
-# Force import order to recognize a module as part of a third party library.
-known-third-party=enchant
-
-
-[CLASSES]
-
-# List of method names used to declare (i.e. assign) instance attributes.
-defining-attr-methods=__init__,__new__,setUp
-
-# List of member names, which should be excluded from the protected access
-# warning.
-exclude-protected=_asdict,_fields,_replace,_source,_make
-
-# List of valid names for the first argument in a class method.
-valid-classmethod-first-arg=cls
-
-# List of valid names for the first argument in a metaclass class method.
-valid-metaclass-classmethod-first-arg=mcs
-
-
-[DESIGN]
-
-# Maximum number of arguments for function / method
-max-args=5
-
-# Maximum number of attributes for a class (see R0902).
-# max-attributes=7
-max-attributes=11
-
-# Maximum number of boolean expressions in a if statement
-max-bool-expr=5
-
-# Maximum number of branch for function / method body
-max-branches=12
-
-# Maximum number of locals for function / method body
-max-locals=15
-
-# Maximum number of parents for a class (see R0901).
-max-parents=7
-
-# Maximum number of public methods for a class (see R0904).
-max-public-methods=20
-
-# Maximum number of return / yield for function / method body
-max-returns=6
-
-# Maximum number of statements in function / method body
-max-statements=50
-
-# Minimum number of public methods for a class (see R0903).
-min-public-methods=1
-
-
-[EXCEPTIONS]
-
-# Exceptions that will emit a warning when being caught. Defaults to
-# "Exception"
-overgeneral-exceptions=Exception
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
index 1335112..255dafd 100644
--- a/.readthedocs.yaml
+++ b/.readthedocs.yaml
@@ -8,8 +8,15 @@
# Required
version: 2
+sphinx:
+ configuration: docs/conf.py
+
+build:
+ os: ubuntu-lts-latest
+ tools:
+ python: "3"
+
python:
- version: "3.7"
install:
- requirements: docs/requirements.txt
- requirements: requirements.txt
diff --git a/README.rst b/README.rst
index 463d5b2..63ce6e5 100644
--- a/README.rst
+++ b/README.rst
@@ -2,10 +2,10 @@ Introduction
============
.. image:: https://readthedocs.org/projects/adafruit-circuitpython-rgb_display/badge/?version=latest
- :target: https://circuitpython.readthedocs.io/projects/rgb_display/en/latest/
+ :target: https://docs.circuitpython.org/projects/rgb_display/en/latest/
:alt: Documentation Status
-.. image :: https://img.shields.io/discord/327254708534116352.svg
+.. image:: https://raw.githubusercontent.com/adafruit/Adafruit_CircuitPython_Bundle/main/badges/adafruit_discord.svg
:target: https://adafru.it/discord
:alt: Discord
@@ -13,6 +13,10 @@ Introduction
:target: https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display/actions/
:alt: Build Status
+.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
+ :target: https://github.com/astral-sh/ruff
+ :alt: Code Style: Ruff
+
Port of display drivers from https://github.com/adafruit/micropython-adafruit-rgb-display to Adafruit CircuitPython for use on Adafruit's SAMD21-based and other CircuitPython boards.
.. note:: This driver currently won't work on micropython.org firmware, instead you want the micropython-adafruit-rgb-display driver linked above!
@@ -57,8 +61,8 @@ To install in a virtual environment in your current project:
.. code-block:: shell
mkdir project-name && cd project-name
- python3 -m venv .env
- source .env/bin/activate
+ python3 -m venv .venv
+ source .venv/bin/activate
pip3 install adafruit-circuitpython-rgb-display
Usage Example
@@ -158,7 +162,9 @@ With 1.14" `wiring `_.
+API documentation for this library can be found on `Read the Docs `_.
+
+For information on building library documentation, please check out `this guide `_.
Contributing
============
@@ -166,8 +172,3 @@ Contributing
Contributions are welcome! Please read our `Code of Conduct
`_
before contributing to help this project stay welcoming.
-
-Documentation
-=============
-
-For information on building library documentation, please check out `this guide `_.
diff --git a/adafruit_rgb_display/__init__.py b/adafruit_rgb_display/__init__.py
index 825637e..c69a8da 100644
--- a/adafruit_rgb_display/__init__.py
+++ b/adafruit_rgb_display/__init__.py
@@ -3,4 +3,5 @@
# SPDX-License-Identifier: MIT
"""Auto imports for Adafruit_CircuitPython_RGB_Display"""
+
from adafruit_rgb_display.rgb import color565
diff --git a/adafruit_rgb_display/gc9a01a.py b/adafruit_rgb_display/gc9a01a.py
new file mode 100644
index 0000000..18f5809
--- /dev/null
+++ b/adafruit_rgb_display/gc9a01a.py
@@ -0,0 +1,127 @@
+# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
+"""
+`adafruit_rgb_display.gc9a01a`
+====================================================
+A simple driver for the GC9A01A-based displays.
+
+* Author(s): Liz Clark
+
+"""
+
+import time
+
+import busio
+import digitalio
+from micropython import const
+
+from adafruit_rgb_display.rgb import DisplaySPI
+
+try:
+ from typing import Optional
+except ImportError:
+ pass
+
+__version__ = "0.0.0+auto.0"
+__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display.git"
+
+# Command constants
+_SWRESET = const(0xFE)
+_SLPOUT = const(0x11)
+_NORON = const(0x13)
+_INVOFF = const(0x20)
+_INVON = const(0x21)
+_DISPOFF = const(0x28)
+_DISPON = const(0x29)
+_CASET = const(0x2A)
+_RASET = const(0x2B)
+_RAMWR = const(0x2C)
+_RAMRD = const(0x2E)
+_MADCTL = const(0x36)
+_COLMOD = const(0x3A)
+
+
+class GC9A01A(DisplaySPI):
+ """
+ A simple driver for the GC9A01A-based displays.
+
+ >>> import busio
+ >>> import digitalio
+ >>> import board
+ >>> from adafruit_rgb_display import gc9a01a
+ >>> spi = busio.SPI(clock=board.SCK, MOSI=board.MOSI, MISO=board.MISO)
+ >>> display = gc9a01a.GC9A01A(spi, cs=digitalio.DigitalInOut(board.CE0),
+ ... dc=digitalio.DigitalInOut(board.D25), rst=digitalio.DigitalInOut(board.D27))
+ >>> display.fill(0x7521)
+ >>> display.pixel(64, 64, 0)
+ """
+
+ _COLUMN_SET = _CASET
+ _PAGE_SET = _RASET
+ _RAM_WRITE = _RAMWR
+ _RAM_READ = _RAMRD
+ _INIT = (
+ (_SWRESET, None),
+ (0xEF, None), # Inter Register Enable2
+ (0xB6, b"\x00\x00"), # Display Function Control
+ (_MADCTL, b"\x48"), # Memory Access Control - Set to BGR color filter panel
+ (_COLMOD, b"\x05"), # Interface Pixel Format - 16 bits per pixel
+ (0xC3, b"\x13"), # Power Control 2
+ (0xC4, b"\x13"), # Power Control 3
+ (0xC9, b"\x22"), # Power Control 4
+ (0xF0, b"\x45\x09\x08\x08\x26\x2a"), # SET_GAMMA1
+ (0xF1, b"\x43\x70\x72\x36\x37\x6f"), # SET_GAMMA2
+ (0xF2, b"\x45\x09\x08\x08\x26\x2a"), # SET_GAMMA3
+ (0xF3, b"\x43\x70\x72\x36\x37\x6f"), # SET_GAMMA4
+ (0x66, b"\x3c\x00\xcd\x67\x45\x45\x10\x00\x00\x00"),
+ (0x67, b"\x00\x3c\x00\x00\x00\x01\x54\x10\x32\x98"),
+ (0x74, b"\x10\x85\x80\x00\x00\x4e\x00"),
+ (0x98, b"\x3e\x07"),
+ (0x35, None), # Tearing Effect Line ON
+ (_INVON, None), # Display Inversion ON
+ (_SLPOUT, None), # Sleep Out Mode
+ (_NORON, None), # Normal Display Mode ON
+ (_DISPON, None), # Display ON
+ )
+
+ def __init__(
+ self,
+ spi: busio.SPI,
+ dc: digitalio.DigitalInOut,
+ cs: digitalio.DigitalInOut,
+ rst: Optional[digitalio.DigitalInOut] = None,
+ width: int = 240,
+ height: int = 240,
+ baudrate: int = 24000000,
+ polarity: int = 0,
+ phase: int = 0,
+ *,
+ x_offset: int = 0,
+ y_offset: int = 0,
+ rotation: int = 0,
+ ) -> None:
+ super().__init__(
+ spi,
+ dc,
+ cs,
+ rst,
+ width,
+ height,
+ baudrate=baudrate,
+ polarity=polarity,
+ phase=phase,
+ x_offset=x_offset,
+ y_offset=y_offset,
+ rotation=rotation,
+ )
+
+ def init(self) -> None:
+ """Initialize the display."""
+ if self.rst:
+ self.rst.value = 0
+ time.sleep(0.05)
+ self.rst.value = 1
+ time.sleep(0.05)
+
+ super().init()
diff --git a/adafruit_rgb_display/hx8353.py b/adafruit_rgb_display/hx8353.py
index 2c44124..2c7c8dc 100644
--- a/adafruit_rgb_display/hx8353.py
+++ b/adafruit_rgb_display/hx8353.py
@@ -1,4 +1,5 @@
# SPDX-FileCopyrightText: 2017 Radomir Dopieralski for Adafruit Industries
+# SPDX-FileCopyrightText: 2023 Matt Land
#
# SPDX-License-Identifier: MIT
@@ -8,13 +9,22 @@
A simple driver for the HX8353-based displays.
-* Author(s): Radomir Dopieralski, Michael McWethy
+* Author(s): Radomir Dopieralski, Michael McWethy, Matt Land
"""
+
from micropython import const
+
from adafruit_rgb_display.rgb import DisplaySPI
+try:
+ from typing import Optional
+
+ import busio
+ import digitalio
+except ImportError:
+ pass
-__version__ = "0.0.0-auto.0"
+__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display.git"
_SWRESET = const(0x01)
@@ -58,6 +68,14 @@ class HX8353(DisplaySPI):
_ENCODE_PIXEL = ">H"
_ENCODE_POS = ">HH"
- # pylint: disable-msg=useless-super-delegation, too-many-arguments
- def __init__(self, spi, dc, cs, rst=None, width=128, height=128, rotation=0):
+ def __init__(
+ self,
+ spi: busio.SPI,
+ dc: digitalio.DigitalInOut,
+ cs: digitalio.DigitalInOut,
+ rst: Optional[digitalio.DigitalInOut] = None,
+ width: int = 128,
+ height: int = 128,
+ rotation: int = 0,
+ ) -> None:
super().__init__(spi, dc, cs, rst, width, height, rotation)
diff --git a/adafruit_rgb_display/hx8357.py b/adafruit_rgb_display/hx8357.py
index e0898d9..1a0feea 100755
--- a/adafruit_rgb_display/hx8357.py
+++ b/adafruit_rgb_display/hx8357.py
@@ -1,4 +1,5 @@
# SPDX-FileCopyrightText: 2019 Melissa LeBlanc-Williams for Adafruit Industries
+# SPDX-FileCopyrightText: 2023 Matt Land
#
# SPDX-License-Identifier: MIT
@@ -8,12 +9,22 @@
A simple driver for the HX8357-based displays.
-* Author(s): Melissa LeBlanc-Williams
+* Author(s): Melissa LeBlanc-Williams, Matt Land
"""
+
from micropython import const
+
from adafruit_rgb_display.rgb import DisplaySPI
-__version__ = "0.0.0-auto.0"
+try:
+ from typing import Optional
+
+ import busio
+ import digitalio
+except ImportError:
+ pass
+
+__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display.git"
_SWRESET = const(0x01)
@@ -64,21 +75,21 @@ class HX8357(DisplaySPI):
_RAM_READ = _RAMRD
_INIT = (
(_SWRESET, None),
- (_SETC, b"\xFF\x83\x57"),
+ (_SETC, b"\xff\x83\x57"),
(_SETRGB, b"\x80\x00\x06\x06"), # 0x80 enables SDO pin (0x00 disables)
(_SETCOM, b"\x25"), # -1.52V
(_SETOSC, b"\x68"), # Normal mode 70Hz, Idle mode 55 Hz
(_SETPANEL, b"\x05"), # BGR, Gate direction swapped
- (_SETPWR1, b"\x00\x15\x1C\x1C\x83\xAA"), # Not deep standby BT VSPR VSNR AP
- (_SETSTBA, b"\x50\x50\x01\x3C\x1E\x08"), # OPON normal OPON idle STBA GEN
+ (_SETPWR1, b"\x00\x15\x1c\x1c\x83\xaa"), # Not deep standby BT VSPR VSNR AP
+ (_SETSTBA, b"\x50\x50\x01\x3c\x1e\x08"), # OPON normal OPON idle STBA GEN
(
_SETCYC,
- b"\x02\x40\x00\x2A\x2A\x0D\x78",
+ b"\x02\x40\x00\x2a\x2a\x0d\x78",
), # NW 0x02 RTN DIV DUM DUM GDON GDOFF
(
_SETGAMMA,
- b"\x02\x0A\x11\x1d\x23\x35\x41\x4b\x4b\x42\x3A\x27\x1B\x08\x09\x03\x02"
- b"\x0A\x11\x1d\x23\x35\x41\x4b\x4b\x42\x3A\x27\x1B\x08\x09\x03\x00\x01",
+ b"\x02\x0a\x11\x1d\x23\x35\x41\x4b\x4b\x42\x3a\x27\x1b\x08\x09\x03\x02"
+ b"\x0a\x11\x1d\x23\x35\x41\x4b\x4b\x42\x3a\x27\x1b\x08\x09\x03\x00\x01",
),
(_COLMOD, b"\x55"), # 16 bit
(_MADCTL, b"\xc0"),
@@ -91,19 +102,18 @@ class HX8357(DisplaySPI):
_ENCODE_PIXEL = ">H"
_ENCODE_POS = ">HH"
- # pylint: disable-msg=useless-super-delegation, too-many-arguments
def __init__(
self,
- spi,
- dc,
- cs,
- rst=None,
- width=480,
- height=320,
- baudrate=16000000,
- polarity=0,
- phase=0,
- rotation=0,
+ spi: busio.SPI,
+ dc: digitalio.DigitalInOut,
+ cs: digitalio.DigitalInOut,
+ rst: Optional[digitalio.DigitalInOut] = None,
+ width: int = 480,
+ height: int = 320,
+ baudrate: int = 16000000,
+ polarity: int = 0,
+ phase: int = 0,
+ rotation: int = 0,
):
super().__init__(
spi,
diff --git a/adafruit_rgb_display/ili9341.py b/adafruit_rgb_display/ili9341.py
index b29b3f2..018b87d 100644
--- a/adafruit_rgb_display/ili9341.py
+++ b/adafruit_rgb_display/ili9341.py
@@ -1,4 +1,5 @@
# SPDX-FileCopyrightText: 2017 Radomir Dopieralski for Adafruit Industries
+# SPDX-FileCopyrightText: 2023 Matt Land
#
# SPDX-License-Identifier: MIT
@@ -8,13 +9,22 @@
A simple driver for the ILI9341/ILI9340-based displays.
-* Author(s): Radomir Dopieralski, Michael McWethy
+* Author(s): Radomir Dopieralski, Michael McWethy, Matt Land
"""
+
import struct
from adafruit_rgb_display.rgb import DisplaySPI
-__version__ = "0.0.0-auto.0"
+try:
+ from typing import Optional
+
+ import busio
+ import digitalio
+except ImportError:
+ pass
+
+__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display.git"
@@ -71,19 +81,18 @@ class ILI9341(DisplaySPI):
_ENCODE_POS = ">HH"
_DECODE_PIXEL = ">BBB"
- # pylint: disable-msg=too-many-arguments
def __init__(
self,
- spi,
- dc,
- cs,
- rst=None,
- width=240,
- height=320,
- baudrate=16000000,
- polarity=0,
- phase=0,
- rotation=0,
+ spi: busio.SPI,
+ dc: digitalio.DigitalInOut,
+ cs: digitalio.DigitalInOut,
+ rst: Optional[digitalio.DigitalInOut] = None,
+ width: int = 240,
+ height: int = 320,
+ baudrate: int = 16000000,
+ polarity: int = 0,
+ phase: int = 0,
+ rotation: int = 0,
):
super().__init__(
spi,
@@ -99,9 +108,10 @@ def __init__(
)
self._scroll = 0
- # pylint: enable-msg=too-many-arguments
-
- def scroll(self, dy=None): # pylint: disable-msg=invalid-name
+ def scroll(
+ self,
+ dy: Optional[int] = None,
+ ) -> Optional[int]:
"""Scroll the display by delta y"""
if dy is None:
return self._scroll
diff --git a/adafruit_rgb_display/rgb.py b/adafruit_rgb_display/rgb.py
index 8d92fb8..8cfa5c9 100644
--- a/adafruit_rgb_display/rgb.py
+++ b/adafruit_rgb_display/rgb.py
@@ -1,4 +1,5 @@
# SPDX-FileCopyrightText: 2017 Radomir Dopieralski for Adafruit Industries
+# SPDX-FileCopyrightText: 2023 Matt Land
#
# SPDX-License-Identifier: MIT
@@ -8,12 +9,21 @@
Base class for all RGB Display devices
-* Author(s): Radomir Dopieralski, Michael McWethy
+* Author(s): Radomir Dopieralski, Michael McWethy, Matt Land
"""
import struct
import time
+try:
+ from typing import Any, ByteString, List, Optional, Tuple, Union
+
+ import busio
+ import digitalio
+ from circuitpython_typing.pil import Image
+except ImportError:
+ pass
+
try:
import numpy
except ImportError:
@@ -21,7 +31,7 @@
from adafruit_bus_device import spi_device
-__version__ = "0.0.0-auto.0"
+__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display.git"
# This is the size of the buffer to be used for fill operations, in 16-bit
@@ -36,154 +46,164 @@
pass
-def color565(r, g=0, b=0):
+def color565(
+ r: Union[int, Tuple[int, int, int], List[int]],
+ g: Optional[int] = 0,
+ b: Optional[int] = 0,
+) -> int:
"""Convert red, green and blue values (0-255) into a 16-bit 565 encoding. As
a convenience this is also available in the parent adafruit_rgb_display
package namespace."""
- try:
- r, g, b = r # see if the first var is a tuple/list
- except TypeError:
- pass
- return (r & 0xF8) << 8 | (g & 0xFC) << 3 | b >> 3
+ if isinstance(r, (tuple, list)): # see if the first var is a tuple/list
+ if len(r) >= 3:
+ red, g, b = r[0:3]
+ else:
+ raise ValueError("Not enough values to unpack (expected 3, got %d)" % len(r))
+ else:
+ red = r
+ return (red & 0xF8) << 8 | (g & 0xFC) << 3 | b >> 3
-def image_to_data(image):
+def image_to_data(image: Image) -> Any:
"""Generator function to convert a PIL image to 16-bit 565 RGB bytes."""
# NumPy is much faster at doing this. NumPy code provided by:
# Keith (https://www.blogger.com/profile/02555547344016007163)
data = numpy.array(image.convert("RGB")).astype("uint16")
- color = (
- ((data[:, :, 0] & 0xF8) << 8)
- | ((data[:, :, 1] & 0xFC) << 3)
- | (data[:, :, 2] >> 3)
- )
+ color = ((data[:, :, 0] & 0xF8) << 8) | ((data[:, :, 1] & 0xFC) << 3) | (data[:, :, 2] >> 3)
return numpy.dstack(((color >> 8) & 0xFF, color & 0xFF)).flatten().tolist()
class DummyPin:
"""Can be used in place of a ``DigitalInOut()`` when you don't want to skip it."""
- def deinit(self):
+ def deinit(self) -> None:
"""Dummy DigitalInOut deinit"""
- def switch_to_output(self, *args, **kwargs):
+ def switch_to_output(
+ self, *, value: bool = False, drive_mode: Optional[digitalio.DriveMode] = None
+ ) -> None:
"""Dummy switch_to_output method"""
- def switch_to_input(self, *args, **kwargs):
+ def switch_to_input(self, *, pull: Optional[digitalio.Pull] = None) -> None:
"""Dummy switch_to_input method"""
@property
- def value(self):
+ def value(self) -> bool:
"""Dummy value DigitalInOut property"""
@value.setter
- def value(self, val):
+ def value(self, val: digitalio.DigitalInOut) -> None:
pass
@property
- def direction(self):
+ def direction(self) -> digitalio.Direction:
"""Dummy direction DigitalInOut property"""
@direction.setter
- def direction(self, val):
+ def direction(self, val: digitalio.Direction) -> None:
pass
@property
- def pull(self):
+ def pull(self) -> digitalio.Pull:
"""Dummy pull DigitalInOut property"""
@pull.setter
- def pull(self, val):
+ def pull(self, val: digitalio.Pull) -> None:
pass
-class Display: # pylint: disable-msg=no-member
+class Display:
"""Base class for all RGB display devices
:param width: number of pixels wide
:param height: number of pixels high
"""
- _PAGE_SET = None
- _COLUMN_SET = None
- _RAM_WRITE = None
- _RAM_READ = None
- _X_START = 0 # pylint: disable=invalid-name
- _Y_START = 0 # pylint: disable=invalid-name
- _INIT = ()
+ _PAGE_SET: Optional[int] = None
+ _COLUMN_SET: Optional[int] = None
+ _RAM_WRITE: Optional[int] = None
+ _RAM_READ: Optional[int] = None
+ _X_START = 0
+ _Y_START = 0
+ _INIT: Tuple[Tuple[int, Union[ByteString, None]], ...] = ()
_ENCODE_PIXEL = ">H"
_ENCODE_POS = ">HH"
_DECODE_PIXEL = ">BBB"
- def __init__(self, width, height, rotation):
+ def __init__(self, width: int, height: int, rotation: int) -> None:
self.width = width
self.height = height
- if rotation not in (0, 90, 180, 270):
+ if rotation not in {0, 90, 180, 270}:
raise ValueError("Rotation must be 0/90/180/270")
self._rotation = rotation
self.init()
- def init(self):
+ def write(self, command: Optional[int] = None, data: Optional[ByteString] = None) -> None:
+ """Abstract method"""
+ raise NotImplementedError()
+
+ def read(self, command: Optional[int] = None, count: int = 0) -> ByteString:
+ """Abstract method"""
+ raise NotImplementedError()
+
+ def init(self) -> None:
"""Run the initialization commands."""
for command, data in self._INIT:
self.write(command, data)
- # pylint: disable-msg=invalid-name,too-many-arguments
- def _block(self, x0, y0, x1, y1, data=None):
+ def _block(
+ self, x0: int, y0: int, x1: int, y1: int, data: Optional[ByteString] = None
+ ) -> Optional[ByteString]:
"""Read or write a block of data."""
- self.write(
- self._COLUMN_SET, self._encode_pos(x0 + self._X_START, x1 + self._X_START)
- )
- self.write(
- self._PAGE_SET, self._encode_pos(y0 + self._Y_START, y1 + self._Y_START)
- )
+ self.write(self._COLUMN_SET, self._encode_pos(x0 + self._X_START, x1 + self._X_START))
+ self.write(self._PAGE_SET, self._encode_pos(y0 + self._Y_START, y1 + self._Y_START))
if data is None:
size = struct.calcsize(self._DECODE_PIXEL)
return self.read(self._RAM_READ, (x1 - x0 + 1) * (y1 - y0 + 1) * size)
self.write(self._RAM_WRITE, data)
return None
- # pylint: enable-msg=invalid-name,too-many-arguments
-
- def _encode_pos(self, x, y):
- """Encode a postion into bytes."""
+ def _encode_pos(self, x: int, y: int) -> bytes:
+ """Encode a position into bytes."""
return struct.pack(self._ENCODE_POS, x, y)
- def _encode_pixel(self, color):
+ def _encode_pixel(self, color: Union[int, Tuple]) -> bytes:
"""Encode a pixel color into bytes."""
return struct.pack(self._ENCODE_PIXEL, color)
- def _decode_pixel(self, data):
+ def _decode_pixel(self, data: Union[bytes, Union[bytearray, memoryview]]) -> int:
"""Decode bytes into a pixel color."""
return color565(*struct.unpack(self._DECODE_PIXEL, data))
- def pixel(self, x, y, color=None):
+ def pixel(self, x: int, y: int, color: Optional[Union[int, Tuple]] = None) -> Optional[int]:
"""Read or write a pixel at a given position."""
if color is None:
- return self._decode_pixel(self._block(x, y, x, y))
+ return self._decode_pixel(self._block(x, y, x, y)) # type: ignore[arg-type]
if 0 <= x < self.width and 0 <= y < self.height:
self._block(x, y, x, y, self._encode_pixel(color))
return None
- def image(self, img, rotation=None, x=0, y=0):
+ def image(
+ self,
+ img: Image,
+ rotation: Optional[int] = None,
+ x: int = 0,
+ y: int = 0,
+ ) -> None:
"""Set buffer to value of Python Imaging Library image. The image should
be in 1 bit mode and a size not exceeding the display size when drawn at
the supplied origin."""
if rotation is None:
rotation = self.rotation
- if not img.mode in ("RGB", "RGBA"):
+ if not img.mode in {"RGB", "RGBA"}:
raise ValueError("Image must be in mode RGB or RGBA")
- if rotation not in (0, 90, 180, 270):
+ if rotation not in {0, 90, 180, 270}:
raise ValueError("Rotation must be 0/90/180/270")
if rotation != 0:
img = img.rotate(rotation, expand=True)
imwidth, imheight = img.size
if x + imwidth > self.width or y + imheight > self.height:
- raise ValueError(
- "Image must not exceed dimensions of display ({0}x{1}).".format(
- self.width, self.height
- )
- )
+ raise ValueError(f"Image must not exceed dimensions of display ({self.width}x{self.height}).")
if numpy:
pixels = bytes(image_to_data(img))
else:
@@ -196,8 +216,7 @@ def image(self, img, rotation=None, x=0, y=0):
pixels[2 * (j * imwidth + i) + 1] = pix & 0xFF
self._block(x, y, x + imwidth - 1, y + imheight - 1, pixels)
- # pylint: disable-msg=too-many-arguments
- def fill_rectangle(self, x, y, width, height, color):
+ def fill_rectangle(self, x: int, y: int, width: int, height: int, color: Union[int, Tuple]) -> None:
"""Draw a rectangle at specified position with specified width and
height, and fill it with the specified color."""
x = min(self.width - 1, max(0, x))
@@ -213,28 +232,26 @@ def fill_rectangle(self, x, y, width, height, color):
self.write(None, data)
self.write(None, pixel * rest)
- # pylint: enable-msg=too-many-arguments
-
- def fill(self, color=0):
+ def fill(self, color: Union[int, Tuple] = 0) -> None:
"""Fill the whole display with the specified color."""
self.fill_rectangle(0, 0, self.width, self.height, color)
- def hline(self, x, y, width, color):
+ def hline(self, x: int, y: int, width: int, color: Union[int, Tuple]) -> None:
"""Draw a horizontal line."""
self.fill_rectangle(x, y, width, 1, color)
- def vline(self, x, y, height, color):
+ def vline(self, x: int, y: int, height: int, color: Union[int, Tuple]) -> None:
"""Draw a vertical line."""
self.fill_rectangle(x, y, 1, height, color)
@property
- def rotation(self):
+ def rotation(self) -> int:
"""Set the default rotation"""
return self._rotation
@rotation.setter
- def rotation(self, val):
- if val not in (0, 90, 180, 270):
+ def rotation(self, val: int) -> None:
+ if val not in {0, 90, 180, 270}:
raise ValueError("Rotation must be 0/90/180/270")
self._rotation = val
@@ -242,47 +259,43 @@ def rotation(self, val):
class DisplaySPI(Display):
"""Base class for SPI type devices"""
- # pylint: disable-msg=too-many-arguments
def __init__(
self,
- spi,
- dc,
- cs,
- rst=None,
- width=1,
- height=1,
- baudrate=12000000,
- polarity=0,
- phase=0,
+ spi: busio.SPI,
+ dc: digitalio.DigitalInOut,
+ cs: digitalio.DigitalInOut,
+ rst: Optional[digitalio.DigitalInOut] = None,
+ width: int = 1,
+ height: int = 1,
+ baudrate: int = 12000000,
+ polarity: int = 0,
+ phase: int = 0,
*,
- x_offset=0,
- y_offset=0,
- rotation=0
+ x_offset: int = 0,
+ y_offset: int = 0,
+ rotation: int = 0,
):
- self.spi_device = spi_device.SPIDevice(
- spi, cs, baudrate=baudrate, polarity=polarity, phase=phase
- )
+ self.spi_device = spi_device.SPIDevice(spi, cs, baudrate=baudrate, polarity=polarity, phase=phase)
self.dc_pin = dc
self.rst = rst
self.dc_pin.switch_to_output(value=0)
if self.rst:
self.rst.switch_to_output(value=0)
self.reset()
- self._X_START = x_offset # pylint: disable=invalid-name
- self._Y_START = y_offset # pylint: disable=invalid-name
+ self._X_START = x_offset
+ self._Y_START = y_offset
super().__init__(width, height, rotation)
- # pylint: enable-msg=too-many-arguments
-
- def reset(self):
+ def reset(self) -> None:
"""Reset the device"""
+ if not self.rst:
+ raise RuntimeError("a reset pin was not provided")
self.rst.value = 0
time.sleep(0.050) # 50 milliseconds
self.rst.value = 1
time.sleep(0.050) # 50 milliseconds
- # pylint: disable=no-member
- def write(self, command=None, data=None):
+ def write(self, command: Optional[int] = None, data: Optional[ByteString] = None) -> None:
"""SPI write to the device: commands and data"""
if command is not None:
self.dc_pin.value = 0
@@ -293,7 +306,7 @@ def write(self, command=None, data=None):
with self.spi_device as spi:
spi.write(data)
- def read(self, command=None, count=0):
+ def read(self, command: Optional[int] = None, count: int = 0) -> ByteString:
"""SPI read from device with optional command"""
data = bytearray(count)
self.dc_pin.value = 0
diff --git a/adafruit_rgb_display/s6d02a1.py b/adafruit_rgb_display/s6d02a1.py
index 34fea6c..9382988 100644
--- a/adafruit_rgb_display/s6d02a1.py
+++ b/adafruit_rgb_display/s6d02a1.py
@@ -1,4 +1,5 @@
# SPDX-FileCopyrightText: 2017 Radomir Dopieralski for Adafruit Industries
+# SPDX-FileCopyrightText: 2023 Matt Land
#
# SPDX-License-Identifier: MIT
@@ -8,13 +9,22 @@
A simple driver for the S6D02A1-based displays.
-* Author(s): Radomir Dopieralski, Michael McWethy
+* Author(s): Radomir Dopieralski, Michael McWethy, Matt Land
"""
from micropython import const
+
from adafruit_rgb_display.rgb import DisplaySPI
-__version__ = "0.0.0-auto.0"
+try:
+ from typing import Optional
+
+ import busio
+ import digitalio
+except ImportError:
+ pass
+
+__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display.git"
_SWRESET = const(0x01)
@@ -58,6 +68,22 @@ class S6D02A1(DisplaySPI):
_ENCODE_PIXEL = ">H"
_ENCODE_POS = ">HH"
- # pylint: disable-msg=useless-super-delegation, too-many-arguments
- def __init__(self, spi, dc, cs, rst=None, width=128, height=160, rotation=0):
- super().__init__(spi, dc, cs, rst, width, height, rotation)
+ def __init__(
+ self,
+ spi: busio.SPI,
+ dc: digitalio.DigitalInOut,
+ cs: digitalio.DigitalInOut,
+ rst: Optional[digitalio.DigitalInOut] = None,
+ width: int = 128,
+ height: int = 160,
+ rotation: int = 0,
+ ):
+ super().__init__(
+ spi=spi,
+ dc=dc,
+ cs=cs,
+ rst=rst,
+ width=width,
+ height=height,
+ rotation=rotation,
+ )
diff --git a/adafruit_rgb_display/ssd1331.py b/adafruit_rgb_display/ssd1331.py
index c3f347d..6947c13 100644
--- a/adafruit_rgb_display/ssd1331.py
+++ b/adafruit_rgb_display/ssd1331.py
@@ -1,4 +1,5 @@
# SPDX-FileCopyrightText: 2017 Radomir Dopieralski for Adafruit Industries
+# SPDX-FileCopyrightText: 2023 Matt Land
#
# SPDX-License-Identifier: MIT
@@ -8,13 +9,22 @@
A simple driver for the SSD1331-based displays.
-* Author(s): Radomir Dopieralski, Michael McWethy
+* Author(s): Radomir Dopieralski, Michael McWethy, Matt Land
"""
from micropython import const
+
from adafruit_rgb_display.rgb import DisplaySPI
-__version__ = "0.0.0-auto.0"
+try:
+ from typing import ByteString, Optional
+
+ import busio
+ import digitalio
+except ImportError:
+ pass
+
+__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display.git"
@@ -103,22 +113,20 @@ class SSD1331(DisplaySPI):
_ENCODE_PIXEL = ">H"
_ENCODE_POS = ">BB"
- # pylint: disable-msg=useless-super-delegation, too-many-arguments
- # super required to allow override of default values
def __init__(
self,
- spi,
- dc,
- cs,
- rst=None,
- width=96,
- height=64,
- baudrate=16000000,
- polarity=0,
- phase=0,
+ spi: busio.SPI,
+ dc: digitalio.DigitalInOut,
+ cs: digitalio.DigitalInOut,
+ rst: Optional[digitalio.DigitalInOut] = None,
+ width: int = 96,
+ height: int = 64,
+ baudrate: int = 16000000,
+ polarity: int = 0,
+ phase: int = 0,
*,
- rotation=0
- ):
+ rotation: int = 0,
+ ) -> None:
super().__init__(
spi,
dc,
@@ -132,14 +140,11 @@ def __init__(
rotation=rotation,
)
- # pylint: disable=no-member
- def write(self, command=None, data=None):
+ def write(self, command: Optional[int] = None, data: Optional[ByteString] = None) -> None:
"""write procedure specific to SSD1331"""
self.dc_pin.value = command is None
with self.spi_device as spi:
if command is not None:
spi.write(bytearray([command]))
- print(bytearray([command]))
if data is not None:
spi.write(data)
- print(data)
diff --git a/adafruit_rgb_display/ssd1351.py b/adafruit_rgb_display/ssd1351.py
index 9ae2cb1..e2416a7 100644
--- a/adafruit_rgb_display/ssd1351.py
+++ b/adafruit_rgb_display/ssd1351.py
@@ -1,4 +1,5 @@
# SPDX-FileCopyrightText: 2017 Radomir Dopieralski for Adafruit Industries
+# SPDX-FileCopyrightText: 2023 Matt Land
#
# SPDX-License-Identifier: MIT
@@ -8,12 +9,22 @@
A simple driver for the SSD1351-based displays.
-* Author(s): Radomir Dopieralski, Michael McWethy
+* Author(s): Radomir Dopieralski, Michael McWethy, Matt Land
"""
+
from micropython import const
+
from adafruit_rgb_display.rgb import DisplaySPI
-__version__ = "0.0.0-auto.0"
+try:
+ from typing import Optional
+
+ import busio
+ import digitalio
+except ImportError:
+ pass
+
+__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display.git"
_SETCOLUMN = const(0x15)
@@ -96,25 +107,22 @@ class SSD1351(DisplaySPI):
_ENCODE_PIXEL = ">H"
_ENCODE_POS = ">BB"
- # pylint: disable-msg=useless-super-delegation, too-many-arguments
def __init__(
self,
- spi,
- dc,
- cs,
- rst=None,
- width=128,
- height=128,
- baudrate=16000000,
- polarity=0,
- phase=0,
+ spi: busio.SPI,
+ dc: digitalio.DigitalInOut,
+ cs: digitalio.DigitalInOut,
+ rst: Optional[digitalio.DigitalInOut] = None,
+ width: int = 128,
+ height: int = 128,
+ baudrate: int = 16000000,
+ polarity: int = 0,
+ phase: int = 0,
*,
- x_offset=0,
- y_offset=0,
- rotation=0
+ x_offset: int = 0,
+ y_offset: int = 0,
+ rotation: int = 0,
):
- baudrate = min(baudrate, 16000000) # Limit to Display Max Baudrate
-
super().__init__(
spi,
dc,
@@ -122,7 +130,7 @@ def __init__(
rst,
width,
height,
- baudrate=baudrate,
+ baudrate=min(baudrate, 16000000), # Limit to Display Max Baudrate
polarity=polarity,
phase=phase,
x_offset=x_offset,
diff --git a/adafruit_rgb_display/st7735.py b/adafruit_rgb_display/st7735.py
index b1686bb..e623a5d 100644
--- a/adafruit_rgb_display/st7735.py
+++ b/adafruit_rgb_display/st7735.py
@@ -1,4 +1,5 @@
# SPDX-FileCopyrightText: 2017 Radomir Dopieralski for Adafruit Industries
+# SPDX-FileCopyrightText: 2023 Matt Land
#
# SPDX-License-Identifier: MIT
@@ -8,14 +9,24 @@
A simple driver for the ST7735-based displays.
-* Author(s): Radomir Dopieralski, Michael McWethy
+* Author(s): Radomir Dopieralski, Michael McWethy, Matt Land
"""
+
import struct
from micropython import const
+
from adafruit_rgb_display.rgb import DisplaySPI
-__version__ = "0.0.0-auto.0"
+try:
+ from typing import ByteString, Optional, Tuple, Union
+
+ import busio
+ import digitalio
+except ImportError:
+ pass
+
+__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display.git"
_NOP = const(0x00)
@@ -113,27 +124,26 @@ class ST7735(DisplaySPI):
(_RASET, b"\x00\x02\x00\x81"), # XSTART = 2, XEND = 129
(_NORON, None),
(_DISPON, None),
- )
+ ) # type: Tuple[Tuple[int, Union[ByteString, None]], ...]
_ENCODE_PIXEL = ">H"
_ENCODE_POS = ">HH"
- # pylint: disable-msg=useless-super-delegation, too-many-arguments
def __init__(
self,
- spi,
- dc,
- cs,
- rst=None,
- width=128,
- height=128,
- baudrate=16000000,
- polarity=0,
- phase=0,
+ spi: busio.SPI,
+ dc: digitalio.DigitalInOut,
+ cs: digitalio.DigitalInOut,
+ rst: Optional[digitalio.DigitalInOut] = None,
+ width: int = 128,
+ height: int = 128,
+ baudrate: int = 16000000,
+ polarity: int = 0,
+ phase: int = 0,
*,
- x_offset=0,
- y_offset=0,
- rotation=0
- ):
+ x_offset: int = 0,
+ y_offset: int = 0,
+ rotation: int = 0,
+ ) -> None:
super().__init__(
spi,
dc,
@@ -171,33 +181,34 @@ class ST7735R(ST7735):
(_INVOFF, None),
(
_GMCTRP1,
- b"\x02\x1c\x07\x12\x37\x32\x29\x2d" b"\x29\x25\x2B\x39\x00\x01\x03\x10",
+ b"\x02\x1c\x07\x12\x37\x32\x29\x2d" b"\x29\x25\x2b\x39\x00\x01\x03\x10",
), # Gamma
(
_GMCTRN1,
- b"\x03\x1d\x07\x06\x2E\x2C\x29\x2D" b"\x2E\x2E\x37\x3F\x00\x00\x02\x10",
+ b"\x03\x1d\x07\x06\x2e\x2c\x29\x2d" b"\x2e\x2e\x37\x3f\x00\x00\x02\x10",
),
)
- # pylint: disable-msg=useless-super-delegation, too-many-arguments
def __init__(
self,
- spi,
- dc,
- cs,
- rst=None,
- width=128,
- height=160,
- baudrate=16000000,
- polarity=0,
- phase=0,
+ spi: busio.SPI,
+ dc: digitalio.DigitalInOut,
+ cs: digitalio.DigitalInOut,
+ rst: Optional[digitalio.DigitalInOut] = None,
+ width: int = 128,
+ height: int = 160,
+ baudrate: int = 16000000,
+ polarity: int = 0,
+ phase: int = 0,
*,
- x_offset=0,
- y_offset=0,
- rotation=0,
- bgr=False
- ):
+ x_offset: int = 0,
+ y_offset: int = 0,
+ rotation: int = 0,
+ bgr: bool = False,
+ invert: bool = False,
+ ) -> None:
self._bgr = bgr
+ self._invert = invert
super().__init__(
spi,
dc,
@@ -213,7 +224,7 @@ def __init__(
rotation=rotation,
)
- def init(self):
+ def init(self) -> None:
super().init()
cols = struct.pack(">HH", 0, self.width - 1)
rows = struct.pack(">HH", 0, self.height - 1)
@@ -227,6 +238,8 @@ def init(self):
self.write(command, data)
if self._bgr:
self.write(_MADCTL, b"\xc0")
+ if self._invert:
+ self.write(_INVON, None)
class ST7735S(ST7735):
@@ -264,24 +277,23 @@ class ST7735S(ST7735):
(_DISPON, None),
)
- # pylint: disable-msg=useless-super-delegation, too-many-arguments
def __init__(
self,
- spi,
- dc,
- cs,
- bl,
- rst=None,
- width=128,
- height=160,
- baudrate=16000000,
- polarity=0,
- phase=0,
+ spi: busio.SPI,
+ dc: digitalio.DigitalInOut,
+ cs: digitalio.DigitalInOut,
+ bl: digitalio.DigitalInOut, # Backlight
+ rst: Optional[digitalio.DigitalInOut] = None,
+ width: int = 128,
+ height: int = 160,
+ baudrate: int = 16000000,
+ polarity: int = 0,
+ phase: int = 0,
*,
- x_offset=2,
- y_offset=1,
- rotation=0
- ):
+ x_offset: int = 2,
+ y_offset: int = 1,
+ rotation: int = 0,
+ ) -> None:
self._bl = bl
# Turn on backlight
self._bl.switch_to_output(value=1)
diff --git a/adafruit_rgb_display/st7789.py b/adafruit_rgb_display/st7789.py
index 05133d1..2148789 100644
--- a/adafruit_rgb_display/st7789.py
+++ b/adafruit_rgb_display/st7789.py
@@ -1,4 +1,5 @@
# SPDX-FileCopyrightText: 2019 Melissa LeBlanc-Williams for Adafruit Industries
+# SPDX-FileCopyrightText: 2023 Matt Land
#
# SPDX-License-Identifier: MIT
@@ -8,15 +9,23 @@
A simple driver for the ST7789-based displays.
-* Author(s): Melissa LeBlanc-Williams
+* Author(s): Melissa LeBlanc-Williams, Matt Land
"""
import struct
+import busio
+import digitalio
from micropython import const
+
from adafruit_rgb_display.rgb import DisplaySPI
-__version__ = "0.0.0-auto.0"
+try:
+ from typing import Optional
+except ImportError:
+ pass
+
+__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display.git"
_NOP = const(0x00)
@@ -93,23 +102,22 @@ class ST7789(DisplaySPI):
(_MADCTL, b"\x08"),
)
- # pylint: disable-msg=useless-super-delegation, too-many-arguments
def __init__(
self,
- spi,
- dc,
- cs,
- rst=None,
- width=240,
- height=320,
- baudrate=16000000,
- polarity=0,
- phase=0,
+ spi: busio.SPI,
+ dc: digitalio.DigitalInOut,
+ cs: digitalio.DigitalInOut,
+ rst: Optional[digitalio.DigitalInOut] = None,
+ width: int = 240,
+ height: int = 320,
+ baudrate: int = 16000000,
+ polarity: int = 0,
+ phase: int = 0,
*,
- x_offset=0,
- y_offset=0,
- rotation=0
- ):
+ x_offset: int = 0,
+ y_offset: int = 0,
+ rotation: int = 0,
+ ) -> None:
super().__init__(
spi,
dc,
@@ -125,8 +133,7 @@ def __init__(
rotation=rotation,
)
- def init(self):
-
+ def init(self) -> None:
super().init()
cols = struct.pack(">HH", self._X_START, self.width + self._X_START)
rows = struct.pack(">HH", self._Y_START, self.height + self._Y_START)
diff --git a/docs/api.rst b/docs/api.rst
index f5219f6..0303cb6 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -1,6 +1,9 @@
.. If you created a package, create one automodule per module in the package.
+API Reference
+#############
+
.. automodule:: adafruit_rgb_display.rgb
:members:
diff --git a/docs/conf.py b/docs/conf.py
index c62199c..6ad6c55 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
-
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
#
# SPDX-License-Identifier: MIT
+import datetime
import os
import sys
@@ -16,6 +15,7 @@
# ones.
extensions = [
"sphinx.ext.autodoc",
+ "sphinxcontrib.jquery",
"sphinx.ext.intersphinx",
"sphinx.ext.viewcode",
]
@@ -26,12 +26,12 @@
# autodoc_mock_imports = ["adafruit_bus_device", "micropython"]
intersphinx_mapping = {
- "python": ("https://docs.python.org/3.4", None),
+ "python": ("https://docs.python.org/3", None),
"BusDevice": (
- "https://circuitpython.readthedocs.io/projects/busdevice/en/latest/",
+ "https://docs.circuitpython.org/projects/busdevice/en/latest/",
None,
),
- "CircuitPython": ("https://circuitpython.readthedocs.io/en/latest/", None),
+ "CircuitPython": ("https://docs.circuitpython.org/en/latest/", None),
}
# Add any paths that contain templates here, relative to this directory.
@@ -44,7 +44,10 @@
# General information about the project.
project = "Adafruit RGB_Display Library"
-copyright = "2017 Michale McWethy"
+creation_year = "2017"
+current_year = str(datetime.datetime.now().year)
+year_duration = current_year if current_year == creation_year else creation_year + " - " + current_year
+copyright = year_duration + " Michale McWethy"
author = "Michale McWethy"
# The version info for the project you're documenting, acts as replacement for
@@ -61,7 +64,7 @@
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
-language = None
+language = "en"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
@@ -92,19 +95,9 @@
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
-on_rtd = os.environ.get("READTHEDOCS", None) == "True"
-
-if not on_rtd: # only import and set the theme if we're building docs locally
- try:
- import sphinx_rtd_theme
-
- html_theme = "sphinx_rtd_theme"
- html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."]
- except:
- html_theme = "default"
- html_theme_path = ["."]
-else:
- html_theme_path = ["."]
+import sphinx_rtd_theme
+
+html_theme = "sphinx_rtd_theme"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
diff --git a/docs/index.rst b/docs/index.rst
index 985c9f3..ef52631 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -35,8 +35,9 @@ Table of Contents
.. toctree::
:caption: Other Links
- Download
- CircuitPython Reference Documentation
+ Download from GitHub
+ Download Library Bundle
+ CircuitPython Reference Documentation
CircuitPython Support Forum
Discord Chat
Adafruit Learning System
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 88e6733..979f568 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -2,4 +2,6 @@
#
# SPDX-License-Identifier: Unlicense
-sphinx>=4.0.0
+sphinx
+sphinxcontrib-jquery
+sphinx-rtd-theme
diff --git a/examples/rgb_display_eyespi_beret_animated_gif.py b/examples/rgb_display_eyespi_beret_animated_gif.py
new file mode 100644
index 0000000..92b354a
--- /dev/null
+++ b/examples/rgb_display_eyespi_beret_animated_gif.py
@@ -0,0 +1,218 @@
+# SPDX-FileCopyrightText: 2021 Melissa LeBlanc Williams for Adafruit Industries
+# SPDX-License-Identifier: MIT
+
+"""
+EYESPI Pi Beret GIF Player Demo
+
+Extracts the frames and other parameters from an animated gif
+and then runs the animation on the display.
+
+Save this file as eyespi_beret_gif_player.py to your Raspberry Pi.
+
+Usage:
+python3 eyespi_beret_gif_player.py
+
+This example is for use on Raspberry Pi that are using CPython with
+Adafruit Blinka to support CircuitPython libraries. CircuitPython does
+not support PIL/pillow (python imaging library)!
+
+Author(s): Melissa LeBlanc-Williams for Adafruit Industries
+ Mike Mallett
+"""
+
+import os
+import time
+
+import board
+import digitalio
+import numpy
+from PIL import Image, ImageOps
+
+from adafruit_rgb_display import (
+ hx8357,
+ ili9341,
+ ssd1331,
+ ssd1351,
+ st7735,
+ st7789,
+)
+
+# Button pins for EYESPI Pi Beret
+BUTTON_NEXT = board.D5
+BUTTON_PREVIOUS = board.D6
+
+# CS and DC pins for EYEPSPI Pi Beret:
+cs_pin = digitalio.DigitalInOut(board.CE0)
+dc_pin = digitalio.DigitalInOut(board.D25)
+
+# Reset pin for EYESPI Pi Beret
+reset_pin = digitalio.DigitalInOut(board.D27)
+
+# Backlight pin for Pi Beret
+backlight = digitalio.DigitalInOut(board.D18)
+backlight.switch_to_output()
+backlight.value = True
+
+# Config for display baudrate (default max is 64mhz):
+BAUDRATE = 64000000
+
+# Setup SPI bus using hardware SPI:
+spi = board.SPI()
+
+# fmt: off
+# Create the display.
+disp = ili9341.ILI9341(spi, rotation=90, # 2.2", 2.4", 2.8", 3.2" ILI9341
+# disp = st7789.ST7789(spi, rotation=90, # 2.0" ST7789
+# disp = st7789.ST7789(spi, height=240, y_offset=80, rotation=180, # 1.3", 1.54" ST7789
+# disp = st7789.ST7789(spi, rotation=90, width=135, height=240, x_offset=53, y_offset=40, # 1.14" ST7789
+# disp = st7789.ST7789(spi, rotation=90, width=172, height=320, x_offset=34, # 1.47" ST7789
+# disp = st7789.ST7789(spi, rotation=270, width=170, height=320, x_offset=35, # 1.9" ST7789
+# disp = hx8357.HX8357(spi, rotation=180, # 3.5" HX8357
+# disp = st7735.ST7735R(spi, rotation=90, # 1.8" ST7735R
+# disp = st7735.ST7735R(spi, rotation=270, height=128, x_offset=2, y_offset=3, # 1.44" ST7735R
+# disp = st7735.ST7735R(spi, rotation=90, bgr=True, width=80, # 0.96" MiniTFT Rev A ST7735R
+# disp = st7735.ST7735R(spi, rotation=90, invert=True, width=80, x_offset=26, y_offset=1, # 0.96" MiniTFT Rev B ST7735R # noqa: E501
+# disp = ssd1351.SSD1351(spi, rotation=180, # 1.5" SSD1351
+# disp = ssd1351.SSD1351(spi, height=96, y_offset=32, rotation=180, # 1.27" SSD1351
+# disp = ssd1331.SSD1331(spi, rotation=180, # 0.96" SSD1331
+ cs=cs_pin,
+ dc=dc_pin,
+ rst=reset_pin,
+ baudrate=BAUDRATE,
+ )
+# fmt: on
+
+
+def init_button(pin):
+ button = digitalio.DigitalInOut(pin)
+ button.switch_to_input()
+ button.pull = digitalio.Pull.UP
+ return button
+
+
+class Frame:
+ def __init__(self, duration=0):
+ self.duration = duration
+ self.image = None
+
+
+class AnimatedGif:
+ def __init__(self, display, width=None, height=None, folder=None):
+ self._frame_count = 0
+ self._loop = 0
+ self._index = 0
+ self._duration = 0
+ self._gif_files = []
+ self._frames = []
+
+ if width is not None:
+ self._width = width
+ else:
+ self._width = display.width
+ if height is not None:
+ self._height = height
+ else:
+ self._height = display.height
+ self.display = display
+ self.advance_button = init_button(BUTTON_NEXT)
+ self.back_button = init_button(BUTTON_PREVIOUS)
+ if folder is not None:
+ self.load_files(folder)
+ self.run()
+
+ def advance(self):
+ self._index = (self._index + 1) % len(self._gif_files)
+
+ def back(self):
+ self._index = (self._index - 1 + len(self._gif_files)) % len(self._gif_files)
+
+ def load_files(self, folder):
+ gif_files = [f for f in os.listdir(folder) if f.endswith(".gif")]
+ for gif_file in gif_files:
+ gif_file = os.path.join(folder, gif_file) # noqa: PLW2901, loop var overwrite
+ image = Image.open(gif_file)
+ # Only add animated Gifs
+ if image.is_animated:
+ self._gif_files.append(gif_file)
+
+ print("Found", self._gif_files)
+ if not self._gif_files:
+ print("No Gif files found in current folder")
+ exit() # noqa: PLR1722, use sys.exit
+
+ def preload(self):
+ image = Image.open(self._gif_files[self._index])
+ print(f"Loading {self._gif_files[self._index]}...")
+ if "duration" in image.info:
+ self._duration = image.info["duration"]
+ else:
+ self._duration = 0
+ if "loop" in image.info:
+ self._loop = image.info["loop"]
+ else:
+ self._loop = 1
+ self._frame_count = image.n_frames
+ self._frames.clear()
+ for frame in range(self._frame_count):
+ image.seek(frame)
+ # Create blank image for drawing.
+ # Make sure to create image with mode 'RGB' for full color.
+ frame_object = Frame(duration=self._duration)
+ if "duration" in image.info:
+ frame_object.duration = image.info["duration"]
+ frame_object.image = ImageOps.pad(
+ image.convert("RGB"),
+ (self._width, self._height),
+ method=Image.NEAREST,
+ color=(0, 0, 0),
+ centering=(0.5, 0.5),
+ )
+ self._frames.append(frame_object)
+
+ def play(self):
+ self.preload()
+
+ _prev_advance_btn_val = self.advance_button.value
+ _prev_back_btn_val = self.back_button.value
+ # Check if we have loaded any files first
+ if not self._gif_files:
+ print("There are no Gif Images loaded to Play")
+ return False
+ while True:
+ for frame_object in self._frames:
+ start_time = time.monotonic()
+ self.display.image(frame_object.image)
+ _cur_advance_btn_val = self.advance_button.value
+ _cur_back_btn_val = self.back_button.value
+ if not _cur_advance_btn_val and _prev_advance_btn_val:
+ self.advance()
+ return False
+ if not _cur_back_btn_val and _prev_back_btn_val:
+ self.back()
+ return False
+
+ _prev_back_btn_val = _cur_back_btn_val
+ _prev_advance_btn_val = _cur_advance_btn_val
+ while time.monotonic() < (start_time + frame_object.duration / 1000):
+ pass
+
+ if self._loop == 1:
+ return True
+ if self._loop > 0:
+ self._loop -= 1
+
+ def run(self):
+ while True:
+ auto_advance = self.play()
+ if auto_advance:
+ self.advance()
+
+
+if disp.rotation % 180 == 90:
+ disp_height = disp.width # we swap height/width to rotate it to landscape!
+ disp_width = disp.height
+else:
+ disp_width = disp.width
+ disp_height = disp.height
+
+gif_player = AnimatedGif(disp, width=disp_width, height=disp_height, folder=".")
diff --git a/examples/rgb_display_fbcp.py b/examples/rgb_display_fbcp.py
index 63b210e..6e5f220 100644
--- a/examples/rgb_display_fbcp.py
+++ b/examples/rgb_display_fbcp.py
@@ -1,14 +1,16 @@
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT
-import time
-import os
import fcntl
import mmap
+import os
import struct
-import digitalio
+import time
+
import board
+import digitalio
from PIL import Image, ImageDraw
+
from adafruit_rgb_display import st7789
# definitions from linux/fb.h
@@ -35,7 +37,7 @@
FB_BLANK_POWERDOWN = 4
-class Bitfield: # pylint: disable=too-few-public-methods
+class Bitfield:
def __init__(self, offset, length, msb_right):
self.offset = offset
self.length = length
@@ -44,7 +46,7 @@ def __init__(self, offset, length, msb_right):
# Kind of like a pygame Surface object, or not!
# http://www.pygame.org/docs/ref/surface.html
-class Framebuffer: # pylint: disable=too-many-instance-attributes
+class Framebuffer:
def __init__(self, dev):
self.dev = dev
self.fbfd = os.open(dev, os.O_RDWR)
@@ -52,16 +54,12 @@ def __init__(self, dev):
"8I12I16I4I",
fcntl.ioctl(self.fbfd, FBIOGET_VSCREENINFO, " " * ((8 + 12 + 16 + 4) * 4)),
)
- finfo = struct.unpack(
- "16cL4I3HI", fcntl.ioctl(self.fbfd, FBIOGET_FSCREENINFO, " " * 48)
- )
+ finfo = struct.unpack("16cL4I3HI", fcntl.ioctl(self.fbfd, FBIOGET_FSCREENINFO, " " * 48))
bytes_per_pixel = (vinfo[6] + 7) // 8
screensize = vinfo[0] * vinfo[1] * bytes_per_pixel
- fbp = mmap.mmap(
- self.fbfd, screensize, flags=mmap.MAP_SHARED, prot=mmap.PROT_READ
- )
+ fbp = mmap.mmap(self.fbfd, screensize, flags=mmap.MAP_SHARED, prot=mmap.PROT_READ)
self.fbp = fbp
self.xres = vinfo[0]
@@ -93,7 +91,7 @@ def blank(self, blank):
fcntl.ioctl(self.fbfd, FBIOBLANK, FB_BLANK_POWERDOWN)
else:
fcntl.ioctl(self.fbfd, FBIOBLANK, FB_BLANK_UNBLANK)
- except IOError:
+ except OSError:
pass
def __str__(self):
@@ -122,9 +120,9 @@ def __str__(self):
type_name = type_list[self.type]
return (
- 'mode "%sx%s"\n' % (self.xres, self.yres)
+ 'mode "%sx%s"\n' % (self.xres, self.yres) # noqa: UP031
+ " nonstd %s\n" % self.nonstd
- + " rgba %s/%s,%s/%s,%s/%s,%s/%s\n"
+ + " rgba %s/%s,%s/%s,%s/%s,%s/%s\n" # noqa: UP031
% (
self.red.length,
self.red.offset,
diff --git a/examples/rgb_display_hx8357test.py b/examples/rgb_display_hx8357test.py
index 83036c8..12eca84 100644
--- a/examples/rgb_display_hx8357test.py
+++ b/examples/rgb_display_hx8357test.py
@@ -4,13 +4,14 @@
# Quick test of 3.5" TFT FeatherWing (HX8357) with Feather M0 or M4
# Will fill the TFT black and put a red pixel in the center, wait 2 seconds,
# then fill the screen blue (with no pixel), wait 2 seconds, and repeat.
-import time
import random
-import digitalio
+import time
+
import board
+import digitalio
-from adafruit_rgb_display.rgb import color565
from adafruit_rgb_display import hx8357
+from adafruit_rgb_display.rgb import color565
# Configuration for CS and DC pins (these are TFT FeatherWing defaults):
cs_pin = digitalio.DigitalInOut(board.D9)
@@ -37,8 +38,6 @@
# Pause 2 seconds.
time.sleep(2)
# Clear the screen a random color
- display.fill(
- color565(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
- )
+ display.fill(color565(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))
# Pause 2 seconds.
time.sleep(2)
diff --git a/examples/rgb_display_ili9341test.py b/examples/rgb_display_ili9341test.py
index 38ba796..2fe3f0f 100644
--- a/examples/rgb_display_ili9341test.py
+++ b/examples/rgb_display_ili9341test.py
@@ -4,15 +4,15 @@
# Quick test of TFT FeatherWing (ILI9341) with Feather M0 or M4
# Will fill the TFT black and put a red pixel in the center, wait 2 seconds,
# then fill the screen blue (with no pixel), wait 2 seconds, and repeat.
-import time
import random
+import time
+
+import board
import busio
import digitalio
-import board
-from adafruit_rgb_display.rgb import color565
from adafruit_rgb_display import ili9341
-
+from adafruit_rgb_display.rgb import color565
# Configuratoin for CS and DC pins (these are FeatherWing defaults on M0/M4):
cs_pin = digitalio.DigitalInOut(board.D9)
@@ -39,8 +39,6 @@
# Pause 2 seconds.
time.sleep(2)
# Clear the screen a random color
- display.fill(
- color565(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
- )
+ display.fill(color565(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))
# Pause 2 seconds.
time.sleep(2)
diff --git a/examples/rgb_display_minipitftstats.py b/examples/rgb_display_minipitftstats.py
index 1f625ed..b096c31 100644
--- a/examples/rgb_display_minipitftstats.py
+++ b/examples/rgb_display_minipitftstats.py
@@ -3,13 +3,14 @@
# -*- coding: utf-8 -*-
-import time
import subprocess
-import digitalio
+import time
+
import board
+import digitalio
from PIL import Image, ImageDraw, ImageFont
-from adafruit_rgb_display import st7789
+from adafruit_rgb_display import st7789
# Configuration for CS and DC pins (these are FeatherWing defaults on M0/M4):
cs_pin = digitalio.DigitalInOut(board.CE0)
@@ -81,7 +82,7 @@
MemUsage = subprocess.check_output(cmd, shell=True).decode("utf-8")
cmd = 'df -h | awk \'$NF=="/"{printf "Disk: %d/%d GB %s", $3,$2,$5}\''
Disk = subprocess.check_output(cmd, shell=True).decode("utf-8")
- cmd = "cat /sys/class/thermal/thermal_zone0/temp | awk '{printf \"CPU Temp: %.1f C\", $(NF-0) / 1000}'" # pylint: disable=line-too-long
+ cmd = "cat /sys/class/thermal/thermal_zone0/temp | awk '{printf \"CPU Temp: %.1f C\", $(NF-0) / 1000}'"
Temp = subprocess.check_output(cmd, shell=True).decode("utf-8")
# Write four lines of text.
diff --git a/examples/rgb_display_minipitfttest.py b/examples/rgb_display_minipitfttest.py
index c418229..eeaab5d 100644
--- a/examples/rgb_display_minipitfttest.py
+++ b/examples/rgb_display_minipitfttest.py
@@ -1,11 +1,11 @@
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT
-import digitalio
import board
+import digitalio
-from adafruit_rgb_display.rgb import color565
from adafruit_rgb_display import st7789
+from adafruit_rgb_display.rgb import color565
# Configuration for CS and DC pins for Raspberry Pi
cs_pin = digitalio.DigitalInOut(board.CE0)
diff --git a/examples/rgb_display_pillow_animated_gif.py b/examples/rgb_display_pillow_animated_gif.py
index 4ccf419..b65361c 100644
--- a/examples/rgb_display_pillow_animated_gif.py
+++ b/examples/rgb_display_pillow_animated_gif.py
@@ -13,19 +13,25 @@
not support PIL/pillow (python imaging library)!
Author(s): Melissa LeBlanc-Williams for Adafruit Industries
+ Mike Mallett
"""
+
import os
import time
-import digitalio
+
import board
+import digitalio
+import numpy
from PIL import Image, ImageOps
-import numpy # pylint: disable=unused-import
-from adafruit_rgb_display import ili9341
-from adafruit_rgb_display import st7789 # pylint: disable=unused-import
-from adafruit_rgb_display import hx8357 # pylint: disable=unused-import
-from adafruit_rgb_display import st7735 # pylint: disable=unused-import
-from adafruit_rgb_display import ssd1351 # pylint: disable=unused-import
-from adafruit_rgb_display import ssd1331 # pylint: disable=unused-import
+
+from adafruit_rgb_display import (
+ hx8357,
+ ili9341,
+ ssd1331,
+ ssd1351,
+ st7735,
+ st7789,
+)
# Change to match your display
BUTTON_NEXT = board.D17
@@ -46,16 +52,12 @@ def init_button(pin):
return button
-# pylint: disable=too-few-public-methods
class Frame:
def __init__(self, duration=0):
self.duration = duration
self.image = None
-# pylint: enable=too-few-public-methods
-
-
class AnimatedGif:
def __init__(self, display, width=None, height=None, folder=None):
self._frame_count = 0
@@ -89,6 +91,7 @@ def back(self):
def load_files(self, folder):
gif_files = [f for f in os.listdir(folder) if f.endswith(".gif")]
for gif_file in gif_files:
+ gif_file = os.path.join(folder, gif_file) # noqa: PLW2901, loop var overwrite
image = Image.open(gif_file)
# Only add animated Gifs
if image.is_animated:
@@ -97,11 +100,11 @@ def load_files(self, folder):
print("Found", self._gif_files)
if not self._gif_files:
print("No Gif files found in current folder")
- exit() # pylint: disable=consider-using-sys-exit
+ exit() # noqa: PLR1722, sys.exit
def preload(self):
image = Image.open(self._gif_files[self._index])
- print("Loading {}...".format(self._gif_files[self._index]))
+ print(f"Loading {self._gif_files[self._index]}...")
if "duration" in image.info:
self._duration = image.info["duration"]
else:
@@ -119,7 +122,7 @@ def preload(self):
frame_object = Frame(duration=self._duration)
if "duration" in image.info:
frame_object.duration = image.info["duration"]
- frame_object.image = ImageOps.pad( # pylint: disable=no-member
+ frame_object.image = ImageOps.pad(
image.convert("RGB"),
(self._width, self._height),
method=Image.NEAREST,
@@ -131,6 +134,8 @@ def preload(self):
def play(self):
self.preload()
+ _prev_advance_btn_val = self.advance_button.value
+ _prev_back_btn_val = self.back_button.value
# Check if we have loaded any files first
if not self._gif_files:
print("There are no Gif Images loaded to Play")
@@ -139,12 +144,17 @@ def play(self):
for frame_object in self._frames:
start_time = time.monotonic()
self.display.image(frame_object.image)
- if not self.advance_button.value:
+ _cur_advance_btn_val = self.advance_button.value
+ _cur_back_btn_val = self.back_button.value
+ if not _cur_advance_btn_val and _prev_advance_btn_val:
self.advance()
return False
- if not self.back_button.value:
+ if not _cur_back_btn_val and _prev_back_btn_val:
self.back()
return False
+
+ _prev_back_btn_val = _cur_back_btn_val
+ _prev_advance_btn_val = _cur_advance_btn_val
while time.monotonic() < (start_time + frame_object.duration / 1000):
pass
@@ -166,15 +176,18 @@ def run(self):
# Setup SPI bus using hardware SPI:
spi = board.SPI()
-# pylint: disable=line-too-long
# Create the display:
# disp = st7789.ST7789(spi, rotation=90, # 2.0" ST7789
# disp = st7789.ST7789(spi, height=240, y_offset=80, rotation=180, # 1.3", 1.54" ST7789
# disp = st7789.ST7789(spi, rotation=90, width=135, height=240, x_offset=53, y_offset=40, # 1.14" ST7789
+# disp = st7789.ST7789(spi, rotation=90, width=172, height=320, x_offset=34, # 1.47" ST7789
+# disp = st7789.ST7789(spi, rotation=270, width=170, height=320, x_offset=35, # 1.9" ST7789
# disp = hx8357.HX8357(spi, rotation=180, # 3.5" HX8357
# disp = st7735.ST7735R(spi, rotation=90, # 1.8" ST7735R
# disp = st7735.ST7735R(spi, rotation=270, height=128, x_offset=2, y_offset=3, # 1.44" ST7735R
-# disp = st7735.ST7735R(spi, rotation=90, bgr=True, # 0.96" MiniTFT ST7735R
+# disp = st7735.ST7735R(spi, rotation=90, bgr=True, width=80, # 0.96" MiniTFT Rev A ST7735R
+# disp = st7735.ST7735R(spi, rotation=90, invert=True, width=80, # 0.96" MiniTFT Rev B ST7735R
+# x_offset=26, y_offset=1,
# disp = ssd1351.SSD1351(spi, rotation=180, # 1.5" SSD1351
# disp = ssd1351.SSD1351(spi, height=96, y_offset=32, rotation=180, # 1.27" SSD1351
# disp = ssd1331.SSD1331(spi, rotation=180, # 0.96" SSD1331
@@ -186,7 +199,6 @@ def run(self):
rst=reset_pin,
baudrate=BAUDRATE,
)
-# pylint: enable=line-too-long
if disp.rotation % 180 == 90:
disp_height = disp.width # we swap height/width to rotate it to landscape!
diff --git a/examples/rgb_display_pillow_bonnet_buttons.py b/examples/rgb_display_pillow_bonnet_buttons.py
index 91bb656..cac9d7e 100644
--- a/examples/rgb_display_pillow_bonnet_buttons.py
+++ b/examples/rgb_display_pillow_bonnet_buttons.py
@@ -32,12 +32,14 @@
not support PIL/pillow (python imaging library)!
"""
-import time
import random
+import time
from colorsys import hsv_to_rgb
+
import board
from digitalio import DigitalInOut, Direction
from PIL import Image, ImageDraw, ImageFont
+
from adafruit_rgb_display import st7789
# Create the display
@@ -115,30 +117,22 @@
up_fill = 0
if not button_U.value: # up pressed
up_fill = udlr_fill
- draw.polygon(
- [(40, 40), (60, 4), (80, 40)], outline=udlr_outline, fill=up_fill
- ) # Up
+ draw.polygon([(40, 40), (60, 4), (80, 40)], outline=udlr_outline, fill=up_fill) # Up
down_fill = 0
if not button_D.value: # down pressed
down_fill = udlr_fill
- draw.polygon(
- [(60, 120), (80, 84), (40, 84)], outline=udlr_outline, fill=down_fill
- ) # down
+ draw.polygon([(60, 120), (80, 84), (40, 84)], outline=udlr_outline, fill=down_fill) # down
left_fill = 0
if not button_L.value: # left pressed
left_fill = udlr_fill
- draw.polygon(
- [(0, 60), (36, 42), (36, 81)], outline=udlr_outline, fill=left_fill
- ) # left
+ draw.polygon([(0, 60), (36, 42), (36, 81)], outline=udlr_outline, fill=left_fill) # left
right_fill = 0
if not button_R.value: # right pressed
right_fill = udlr_fill
- draw.polygon(
- [(120, 60), (84, 42), (84, 82)], outline=udlr_outline, fill=right_fill
- ) # right
+ draw.polygon([(120, 60), (84, 42), (84, 82)], outline=udlr_outline, fill=right_fill) # right
center_fill = 0
if not button_C.value: # center pressed
diff --git a/examples/rgb_display_pillow_demo.py b/examples/rgb_display_pillow_demo.py
index f2ce7cf..36f3961 100644
--- a/examples/rgb_display_pillow_demo.py
+++ b/examples/rgb_display_pillow_demo.py
@@ -12,15 +12,18 @@
Author(s): Melissa LeBlanc-Williams for Adafruit Industries
"""
-import digitalio
import board
+import digitalio
from PIL import Image, ImageDraw, ImageFont
-from adafruit_rgb_display import ili9341
-from adafruit_rgb_display import st7789 # pylint: disable=unused-import
-from adafruit_rgb_display import hx8357 # pylint: disable=unused-import
-from adafruit_rgb_display import st7735 # pylint: disable=unused-import
-from adafruit_rgb_display import ssd1351 # pylint: disable=unused-import
-from adafruit_rgb_display import ssd1331 # pylint: disable=unused-import
+
+from adafruit_rgb_display import (
+ hx8357,
+ ili9341,
+ ssd1331,
+ ssd1351,
+ st7735,
+ st7789,
+)
# First define some constants to allow easy resizing of shapes.
BORDER = 20
@@ -37,15 +40,18 @@
# Setup SPI bus using hardware SPI:
spi = board.SPI()
-# pylint: disable=line-too-long
# Create the display:
# disp = st7789.ST7789(spi, rotation=90, # 2.0" ST7789
# disp = st7789.ST7789(spi, height=240, y_offset=80, rotation=180, # 1.3", 1.54" ST7789
# disp = st7789.ST7789(spi, rotation=90, width=135, height=240, x_offset=53, y_offset=40, # 1.14" ST7789
+# disp = st7789.ST7789(spi, rotation=90, width=172, height=320, x_offset=34, # 1.47" ST7789
+# disp = st7789.ST7789(spi, rotation=270, width=170, height=320, x_offset=35, # 1.9" ST7789
# disp = hx8357.HX8357(spi, rotation=180, # 3.5" HX8357
# disp = st7735.ST7735R(spi, rotation=90, # 1.8" ST7735R
# disp = st7735.ST7735R(spi, rotation=270, height=128, x_offset=2, y_offset=3, # 1.44" ST7735R
-# disp = st7735.ST7735R(spi, rotation=90, bgr=True, # 0.96" MiniTFT ST7735R
+# disp = st7735.ST7735R(spi, rotation=90, bgr=True, width=80, # 0.96" MiniTFT Rev A ST7735R
+# disp = st7735.ST7735R(spi, rotation=90, invert=True, width=80, # 0.96" MiniTFT Rev B ST7735R
+# x_offset=26, y_offset=1,
# disp = ssd1351.SSD1351(spi, rotation=180, # 1.5" SSD1351
# disp = ssd1351.SSD1351(spi, height=96, y_offset=32, rotation=180, # 1.27" SSD1351
# disp = ssd1331.SSD1331(spi, rotation=180, # 0.96" SSD1331
@@ -57,7 +63,6 @@
rst=reset_pin,
baudrate=BAUDRATE,
)
-# pylint: enable=line-too-long
# Create blank image for drawing.
# Make sure to create image with mode 'RGB' for full color.
@@ -78,9 +83,7 @@
disp.image(image)
# Draw a smaller inner purple rectangle
-draw.rectangle(
- (BORDER, BORDER, width - BORDER - 1, height - BORDER - 1), fill=(170, 0, 136)
-)
+draw.rectangle((BORDER, BORDER, width - BORDER - 1, height - BORDER - 1), fill=(170, 0, 136))
# Load a TTF Font
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", FONTSIZE)
diff --git a/examples/rgb_display_pillow_image.py b/examples/rgb_display_pillow_image.py
index 2bfff48..c90f5e7 100644
--- a/examples/rgb_display_pillow_image.py
+++ b/examples/rgb_display_pillow_image.py
@@ -11,15 +11,18 @@
Author(s): Melissa LeBlanc-Williams for Adafruit Industries
"""
-import digitalio
import board
+import digitalio
from PIL import Image, ImageDraw
-from adafruit_rgb_display import ili9341
-from adafruit_rgb_display import st7789 # pylint: disable=unused-import
-from adafruit_rgb_display import hx8357 # pylint: disable=unused-import
-from adafruit_rgb_display import st7735 # pylint: disable=unused-import
-from adafruit_rgb_display import ssd1351 # pylint: disable=unused-import
-from adafruit_rgb_display import ssd1331 # pylint: disable=unused-import
+
+from adafruit_rgb_display import (
+ hx8357,
+ ili9341,
+ ssd1331,
+ ssd1351,
+ st7735,
+ st7789,
+)
# Configuration for CS and DC pins (these are PiTFT defaults):
cs_pin = digitalio.DigitalInOut(board.CE0)
@@ -32,15 +35,19 @@
# Setup SPI bus using hardware SPI:
spi = board.SPI()
-# pylint: disable=line-too-long
+
# Create the display:
# disp = st7789.ST7789(spi, rotation=90, # 2.0" ST7789
# disp = st7789.ST7789(spi, height=240, y_offset=80, rotation=180, # 1.3", 1.54" ST7789
# disp = st7789.ST7789(spi, rotation=90, width=135, height=240, x_offset=53, y_offset=40, # 1.14" ST7789
+# disp = st7789.ST7789(spi, rotation=90, width=172, height=320, x_offset=34, # 1.47" ST7789
+# disp = st7789.ST7789(spi, rotation=270, width=170, height=320, x_offset=35, # 1.9" ST7789
# disp = hx8357.HX8357(spi, rotation=180, # 3.5" HX8357
# disp = st7735.ST7735R(spi, rotation=90, # 1.8" ST7735R
# disp = st7735.ST7735R(spi, rotation=270, height=128, x_offset=2, y_offset=3, # 1.44" ST7735R
-# disp = st7735.ST7735R(spi, rotation=90, bgr=True, # 0.96" MiniTFT ST7735R
+# disp = st7735.ST7735R(spi, rotation=90, bgr=True, width=80, # 0.96" MiniTFT Rev A ST7735R
+# disp = st7735.ST7735R(spi, rotation=90, invert=True, width=80, # 0.96" MiniTFT Rev B ST7735R
+# x_offset=26, y_offset=1,
# disp = ssd1351.SSD1351(spi, rotation=180, # 1.5" SSD1351
# disp = ssd1351.SSD1351(spi, height=96, y_offset=32, rotation=180, # 1.27" SSD1351
# disp = ssd1331.SSD1331(spi, rotation=180, # 0.96" SSD1331
@@ -52,7 +59,6 @@
rst=reset_pin,
baudrate=BAUDRATE,
)
-# pylint: enable=line-too-long
# Create blank image for drawing.
# Make sure to create image with mode 'RGB' for full color.
diff --git a/examples/rgb_display_pillow_stats.py b/examples/rgb_display_pillow_stats.py
index e7716c9..673a032 100644
--- a/examples/rgb_display_pillow_stats.py
+++ b/examples/rgb_display_pillow_stats.py
@@ -11,17 +11,21 @@
not support PIL/pillow (python imaging library)!
"""
-import time
import subprocess
-import digitalio
+import time
+
import board
+import digitalio
from PIL import Image, ImageDraw, ImageFont
-from adafruit_rgb_display import ili9341
-from adafruit_rgb_display import st7789 # pylint: disable=unused-import
-from adafruit_rgb_display import hx8357 # pylint: disable=unused-import
-from adafruit_rgb_display import st7735 # pylint: disable=unused-import
-from adafruit_rgb_display import ssd1351 # pylint: disable=unused-import
-from adafruit_rgb_display import ssd1331 # pylint: disable=unused-import
+
+from adafruit_rgb_display import (
+ hx8357,
+ ili9341,
+ ssd1331,
+ ssd1351,
+ st7735,
+ st7789,
+)
# Configuration for CS and DC pins (these are PiTFT defaults):
cs_pin = digitalio.DigitalInOut(board.CE0)
@@ -34,15 +38,18 @@
# Setup SPI bus using hardware SPI:
spi = board.SPI()
-# pylint: disable=line-too-long
# Create the display:
# disp = st7789.ST7789(spi, rotation=90, # 2.0" ST7789
# disp = st7789.ST7789(spi, height=240, y_offset=80, rotation=180, # 1.3", 1.54" ST7789
# disp = st7789.ST7789(spi, rotation=90, width=135, height=240, x_offset=53, y_offset=40, # 1.14" ST7789
+# disp = st7789.ST7789(spi, rotation=90, width=172, height=320, x_offset=34, # 1.47" ST7789
+# disp = st7789.ST7789(spi, rotation=270, width=170, height=320, x_offset=35, # 1.9" ST7789
# disp = hx8357.HX8357(spi, rotation=180, # 3.5" HX8357
# disp = st7735.ST7735R(spi, rotation=90, # 1.8" ST7735R
# disp = st7735.ST7735R(spi, rotation=270, height=128, x_offset=2, y_offset=3, # 1.44" ST7735R
-# disp = st7735.ST7735R(spi, rotation=90, bgr=True, # 0.96" MiniTFT ST7735R
+# disp = st7735.ST7735R(spi, rotation=90, bgr=True, width=80, # 0.96" MiniTFT Rev A ST7735R
+# disp = st7735.ST7735R(spi, rotation=90, invert=True, width=80, # 0.96" MiniTFT Rev B ST7735R
+# x_offset=26, y_offset=1,
# disp = ssd1351.SSD1351(spi, rotation=180, # 1.5" SSD1351
# disp = ssd1351.SSD1351(spi, height=96, y_offset=32, rotation=180, # 1.27" SSD1351
# disp = ssd1331.SSD1331(spi, rotation=180, # 0.96" SSD1331
@@ -54,7 +61,6 @@
rst=reset_pin,
baudrate=BAUDRATE,
)
-# pylint: enable=line-too-long
# Create blank image for drawing.
# Make sure to create image with mode 'RGB' for full color.
@@ -97,7 +103,7 @@
MemUsage = subprocess.check_output(cmd, shell=True).decode("utf-8")
cmd = 'df -h | awk \'$NF=="/"{printf "Disk: %d/%d GB %s", $3,$2,$5}\''
Disk = subprocess.check_output(cmd, shell=True).decode("utf-8")
- cmd = "cat /sys/class/thermal/thermal_zone0/temp | awk '{printf \"CPU Temp: %.1f C\", $(NF-0) / 1000}'" # pylint: disable=line-too-long
+ cmd = "cat /sys/class/thermal/thermal_zone0/temp | awk '{printf \"CPU Temp: %.1f C\", $(NF-0) / 1000}'"
Temp = subprocess.check_output(cmd, shell=True).decode("utf-8")
# Write four lines of text.
diff --git a/examples/rgb_display_simpletest.py b/examples/rgb_display_simpletest.py
index 8f7f7f0..0546972 100644
--- a/examples/rgb_display_simpletest.py
+++ b/examples/rgb_display_simpletest.py
@@ -5,13 +5,14 @@
# This will work even on a device running displayio
# Will fill the TFT black and put a red pixel in the center, wait 2 seconds,
# then fill the screen blue (with no pixel), wait 2 seconds, and repeat.
-import time
import random
-import digitalio
+import time
+
import board
+import digitalio
-from adafruit_rgb_display.rgb import color565
from adafruit_rgb_display import st7789
+from adafruit_rgb_display.rgb import color565
# Configuratoin for CS and DC pins (these are FeatherWing defaults on M0/M4):
cs_pin = digitalio.DigitalInOut(board.D5)
@@ -39,8 +40,6 @@
# Pause 2 seconds.
time.sleep(2)
# Clear the screen a random color
- display.fill(
- color565(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
- )
+ display.fill(color565(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))
# Pause 2 seconds.
time.sleep(2)
diff --git a/mypy.ini b/mypy.ini
new file mode 100644
index 0000000..8bf7236
--- /dev/null
+++ b/mypy.ini
@@ -0,0 +1,22 @@
+# SPDX-FileCopyrightText: 2023 Matt Land
+#
+# SPDX-License-Identifier: Unlicense
+[mypy]
+python_version = 3.7
+disallow_untyped_defs = True
+exclude = (examples|tests|setup.py|docs)
+
+[mypy-digitalio]
+ignore_missing_imports = True
+
+[mypy-busio]
+ignore_missing_imports = True
+
+[mypy-numpy]
+ignore_missing_imports = True
+
+[mypy-adafruit_bus_device]
+ignore_missing_imports = True
+
+[mypy-micropython]
+ignore_missing_imports = True
diff --git a/optional_requirements.txt b/optional_requirements.txt
new file mode 100644
index 0000000..595a870
--- /dev/null
+++ b/optional_requirements.txt
@@ -0,0 +1,8 @@
+# SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries
+# SPDX-FileCopyrightText: 2023 Matt Land
+#
+# SPDX-License-Identifier: Unlicense
+
+#adafruit_bus_device
+#numpy
+#PIL
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..bb09061
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,50 @@
+# SPDX-FileCopyrightText: 2022 Alec Delaney for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
+
+[build-system]
+requires = [
+ "setuptools",
+ "wheel",
+ "setuptools-scm",
+]
+
+[project]
+name = "adafruit-circuitpython-rgb-display"
+description = "CircuitPython library for RGB displays."
+version = "0.0.0+auto.0"
+readme = "README.rst"
+authors = [
+ {name = "Adafruit Industries", email = "circuitpython@adafruit.com"}
+]
+urls = {Homepage = "https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display"}
+keywords = [
+ "adafruit",
+ "rgb",
+ "display",
+ "hx8353",
+ "ili9341",
+ "s6d02A1",
+ "ssd1331",
+ "ssd1351",
+ "st7735hardware",
+ "micropython",
+ "circuitpython",
+]
+license = {text = "MIT"}
+classifiers = [
+ "Intended Audience :: Developers",
+ "Topic :: Software Development :: Libraries",
+ "Topic :: Software Development :: Embedded Systems",
+ "Topic :: System :: Hardware",
+ "License :: OSI Approved :: MIT License",
+ "Programming Language :: Python :: 3",
+]
+dynamic = ["dependencies", "optional-dependencies"]
+
+[tool.setuptools]
+packages = ["adafruit_rgb_display"]
+
+[tool.setuptools.dynamic]
+dependencies = {file = ["requirements.txt"]}
+optional-dependencies = {optional = {file = ["optional_requirements.txt"]}}
diff --git a/requirements.txt b/requirements.txt
index f675e3b..a45c547 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
+# SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
diff --git a/ruff.toml b/ruff.toml
new file mode 100644
index 0000000..6818392
--- /dev/null
+++ b/ruff.toml
@@ -0,0 +1,101 @@
+# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
+#
+# SPDX-License-Identifier: MIT
+
+target-version = "py38"
+line-length = 110
+
+[lint]
+preview = true
+select = ["I", "PL", "UP"]
+
+extend-select = [
+ "D419", # empty-docstring
+ "E501", # line-too-long
+ "W291", # trailing-whitespace
+ "PLC0414", # useless-import-alias
+ "PLC2401", # non-ascii-name
+ "PLC2801", # unnecessary-dunder-call
+ "PLC3002", # unnecessary-direct-lambda-call
+ "E999", # syntax-error
+ "PLE0101", # return-in-init
+ "F706", # return-outside-function
+ "F704", # yield-outside-function
+ "PLE0116", # continue-in-finally
+ "PLE0117", # nonlocal-without-binding
+ "PLE0241", # duplicate-bases
+ "PLE0302", # unexpected-special-method-signature
+ "PLE0604", # invalid-all-object
+ "PLE0605", # invalid-all-format
+ "PLE0643", # potential-index-error
+ "PLE0704", # misplaced-bare-raise
+ "PLE1141", # dict-iter-missing-items
+ "PLE1142", # await-outside-async
+ "PLE1205", # logging-too-many-args
+ "PLE1206", # logging-too-few-args
+ "PLE1307", # bad-string-format-type
+ "PLE1310", # bad-str-strip-call
+ "PLE1507", # invalid-envvar-value
+ "PLE2502", # bidirectional-unicode
+ "PLE2510", # invalid-character-backspace
+ "PLE2512", # invalid-character-sub
+ "PLE2513", # invalid-character-esc
+ "PLE2514", # invalid-character-nul
+ "PLE2515", # invalid-character-zero-width-space
+ "PLR0124", # comparison-with-itself
+ "PLR0202", # no-classmethod-decorator
+ "PLR0203", # no-staticmethod-decorator
+ "UP004", # useless-object-inheritance
+ "PLR0206", # property-with-parameters
+ "PLR0904", # too-many-public-methods
+ "PLR0911", # too-many-return-statements
+ "PLR0912", # too-many-branches
+ "PLR0913", # too-many-arguments
+ "PLR0914", # too-many-locals
+ "PLR0915", # too-many-statements
+ "PLR0916", # too-many-boolean-expressions
+ "PLR1702", # too-many-nested-blocks
+ "PLR1704", # redefined-argument-from-local
+ "PLR1711", # useless-return
+ "C416", # unnecessary-comprehension
+ "PLR1733", # unnecessary-dict-index-lookup
+ "PLR1736", # unnecessary-list-index-lookup
+
+ # ruff reports this rule is unstable
+ #"PLR6301", # no-self-use
+
+ "PLW0108", # unnecessary-lambda
+ "PLW0120", # useless-else-on-loop
+ "PLW0127", # self-assigning-variable
+ "PLW0129", # assert-on-string-literal
+ "B033", # duplicate-value
+ "PLW0131", # named-expr-without-context
+ "PLW0245", # super-without-brackets
+ "PLW0406", # import-self
+ "PLW0602", # global-variable-not-assigned
+ "PLW0603", # global-statement
+ "PLW0604", # global-at-module-level
+
+ # fails on the try: import typing used by libraries
+ #"F401", # unused-import
+
+ "F841", # unused-variable
+ "E722", # bare-except
+ "PLW0711", # binary-op-exception
+ "PLW1501", # bad-open-mode
+ "PLW1508", # invalid-envvar-default
+ "PLW1509", # subprocess-popen-preexec-fn
+ "PLW2101", # useless-with-lock
+ "PLW3301", # nested-min-max
+]
+
+ignore = [
+ "PLR2004", # magic-value-comparison
+ "UP030", # format literals
+ "PLW1514", # unspecified-encoding
+ "PLR0913", # too many arguments
+ "PLR0917", # too many positional arguments
+]
+
+[format]
+line-ending = "lf"
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 5b57862..0000000
--- a/setup.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
-#
-# SPDX-License-Identifier: MIT
-
-"""A setuptools based setup module.
-
-See:
-https://packaging.python.org/en/latest/distributing.html
-https://github.com/pypa/sampleproject
-"""
-
-# Always prefer setuptools over distutils
-from setuptools import setup, find_packages
-
-# To use a consistent encoding
-from codecs import open
-from os import path
-
-here = path.abspath(path.dirname(__file__))
-
-# Get the long description from the README file
-with open(path.join(here, "README.rst"), encoding="utf-8") as f:
- long_description = f.read()
-
-setup(
- name="adafruit-circuitpython-rgb-display",
- use_scm_version=True,
- setup_requires=["setuptools_scm"],
- description="CircuitPython library for RGB displays.",
- long_description=long_description,
- long_description_content_type="text/x-rst",
- # The project's main homepage.
- url="https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display",
- # Author details
- author="Radomir Dopieralski, Michael McWethy",
- author_email="circuitpython@adafruit.com",
- install_requires=["Adafruit-Blinka", "adafruit-circuitpython-busdevice"],
- # Choose your license
- license="MIT",
- # See https://pypi.python.org/pypi?%3Aaction=list_classifiers
- classifiers=[
- "Development Status :: 3 - Alpha",
- "Intended Audience :: Developers",
- "Topic :: Software Development :: Libraries",
- "Topic :: System :: Hardware",
- "License :: OSI Approved :: MIT License",
- "Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.4",
- "Programming Language :: Python :: 3.5",
- ],
- # What does your project relate to?
- keywords="adafruit rgb display hx8353 ili9341 s6d02A1 ssd1331 ssd1351 st7735"
- "hardware micropython circuitpython",
- # You can just specify the packages manually here if your project is
- # simple. Or you can use find_packages().
- packages=["adafruit_rgb_display"],
-)