mirror of
https://github.com/pimoroni/st7735-python.git
synced 2025-01-05 22:40:25 +03:00
Compare commits
22 Commits
v0.0.4-pos
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20d6e8058f | ||
|
|
432a58fea9 | ||
|
|
ed2b0af910 | ||
|
|
1a07965d2d | ||
|
|
c9f6459ee6 | ||
|
|
2b1736c669 | ||
|
|
8e6cee4ef8 | ||
|
|
47fc5e1c78 | ||
|
|
147ccfcc11 | ||
|
|
f6599b1854 | ||
|
|
c0733d678a | ||
|
|
8d4235b9b3 | ||
|
|
89b4406492 | ||
|
|
b1e35beb4b | ||
|
|
f94e1aa318 | ||
|
|
94f8bc2423 | ||
|
|
6719ad8dac | ||
|
|
cf5c2b3df4 | ||
|
|
3b68b77b29 | ||
|
|
99e44092de | ||
|
|
1a70d3ae7e | ||
|
|
1d8d0faf1b |
41
.github/workflows/build.yml
vendored
Normal file
41
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Python ${{ matrix.python }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python: ['3.9', '3.10', '3.11']
|
||||
|
||||
env:
|
||||
RELEASE_FILE: ${{ github.event.repository.name }}-${{ github.event.release.tag_name || github.sha }}-py${{ matrix.python }}
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python ${{ matrix.python }}
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
make dev-deps
|
||||
|
||||
- name: Build Packages
|
||||
run: |
|
||||
make build
|
||||
|
||||
- name: Upload Packages
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.RELEASE_FILE }}
|
||||
path: dist/
|
||||
36
.github/workflows/qa.yml
vendored
Normal file
36
.github/workflows/qa.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: QA
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: linting & spelling
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
TERM: xterm-256color
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python '3,11'
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
make dev-deps
|
||||
|
||||
- name: Run Quality Assurance
|
||||
run: |
|
||||
make qa
|
||||
|
||||
- name: Run Code Checks
|
||||
run: |
|
||||
make check
|
||||
24
.github/workflows/test.yml
vendored
24
.github/workflows/test.yml
vendored
@@ -1,37 +1,41 @@
|
||||
name: Python Tests
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Python ${{ matrix.python }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python: [2.7, 3.6, 3.8]
|
||||
python: ['3.9', '3.10', '3.11']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python ${{ matrix.python }}
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade setuptools tox
|
||||
make dev-deps
|
||||
|
||||
- name: Run Tests
|
||||
working-directory: library
|
||||
run: |
|
||||
tox -e py
|
||||
make pytest
|
||||
|
||||
- name: Coverage
|
||||
if: ${{ matrix.python == '3.9' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
working-directory: library
|
||||
run: |
|
||||
python -m pip install coveralls
|
||||
coveralls --service=github
|
||||
if: ${{ matrix.python == '3.8' }}
|
||||
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
1.0.0
|
||||
-----
|
||||
|
||||
* Rename module from ST7735 to st7735
|
||||
* Port to gpiod/gpiodevice
|
||||
|
||||
0.0.5
|
||||
-----
|
||||
|
||||
* Add support for choosing between BGR/RGB displays
|
||||
* Add methods for display power and sleep control
|
||||
|
||||
0.0.4-post1
|
||||
-----------
|
||||
|
||||
92
Makefile
92
Makefile
@@ -1,70 +1,60 @@
|
||||
LIBRARY_VERSION=$(shell grep version library/setup.cfg | awk -F" = " '{print $$2}')
|
||||
LIBRARY_NAME=$(shell grep name library/setup.cfg | awk -F" = " '{print $$2}')
|
||||
LIBRARY_NAME := $(shell hatch project metadata name 2> /dev/null)
|
||||
LIBRARY_VERSION := $(shell hatch version 2> /dev/null)
|
||||
|
||||
.PHONY: usage install uninstall
|
||||
.PHONY: usage install uninstall check pytest qa build-deps check tag wheel sdist clean dist testdeploy deploy
|
||||
usage:
|
||||
ifdef LIBRARY_NAME
|
||||
@echo "Library: ${LIBRARY_NAME}"
|
||||
@echo "Version: ${LIBRARY_VERSION}\n"
|
||||
else
|
||||
@echo "WARNING: You should 'make dev-deps'\n"
|
||||
endif
|
||||
@echo "Usage: make <target>, where target is one of:\n"
|
||||
@echo "install: install the library locally from source"
|
||||
@echo "uninstall: uninstall the local library"
|
||||
@echo "check: peform basic integrity checks on the codebase"
|
||||
@echo "python-readme: generate library/README.md from README.md + library/CHANGELOG.txt"
|
||||
@echo "python-wheels: build python .whl files for distribution"
|
||||
@echo "python-sdist: build python source distribution"
|
||||
@echo "python-clean: clean python build and dist directories"
|
||||
@echo "python-dist: build all python distribution files"
|
||||
@echo "python-testdeploy: build all and deploy to test PyPi"
|
||||
@echo "tag: tag the repository with the current version"
|
||||
@echo "install: install the library locally from source"
|
||||
@echo "uninstall: uninstall the local library"
|
||||
@echo "dev-deps: install Python dev dependencies"
|
||||
@echo "check: perform basic integrity checks on the codebase"
|
||||
@echo "qa: run linting and package QA"
|
||||
@echo "pytest: run Python test fixtures"
|
||||
@echo "clean: clean Python build and dist directories"
|
||||
@echo "build: build Python distribution files"
|
||||
@echo "testdeploy: build and upload to test PyPi"
|
||||
@echo "deploy: build and upload to PyPi"
|
||||
@echo "tag: tag the repository with the current version\n"
|
||||
|
||||
install:
|
||||
./install.sh
|
||||
./install.sh --unstable
|
||||
|
||||
uninstall:
|
||||
./uninstall.sh
|
||||
|
||||
dev-deps:
|
||||
python3 -m pip install -r requirements-dev.txt
|
||||
sudo apt install dos2unix
|
||||
|
||||
check:
|
||||
@echo "Checking for trailing whitespace"
|
||||
@! grep -IUrn --color "[[:blank:]]$$" --exclude-dir=sphinx --exclude-dir=.tox --exclude-dir=.git --exclude=PKG-INFO
|
||||
@echo "Checking for DOS line-endings"
|
||||
@! grep -IlUrn --color "
|
||||
" --exclude-dir=sphinx --exclude-dir=.tox --exclude-dir=.git --exclude=Makefile
|
||||
@echo "Checking library/CHANGELOG.txt"
|
||||
@cat library/CHANGELOG.txt | grep ^${LIBRARY_VERSION}
|
||||
@echo "Checking library/${LIBRARY_NAME}/__init__.py"
|
||||
@bash check.sh
|
||||
|
||||
qa:
|
||||
tox -e qa
|
||||
|
||||
pytest:
|
||||
tox -e py
|
||||
|
||||
nopost:
|
||||
@bash check.sh --nopost
|
||||
|
||||
tag:
|
||||
git tag -a "v${LIBRARY_VERSION}" -m "Version ${LIBRARY_VERSION}"
|
||||
|
||||
|
||||
build: check
|
||||
@hatch build
|
||||
|
||||
|
||||
clean:
|
||||
-rm -r dist
|
||||
|
||||
|
||||
library/README.md: README.md library/CHANGELOG.txt
|
||||
cp README.md library/README.md
|
||||
printf "\n# Changelog\n" >> library/README.md
|
||||
testdeploy: build
|
||||
twine upload --repository testpypi dist/*
|
||||
|
||||
|
||||
library/LICENSE.txt: LICENSE
|
||||
cp LICENSE library/LICENSE.txt
|
||||
|
||||
python-wheels: python-readme python-license
|
||||
cd library; python3 setup.py bdist_wheel
|
||||
cd library; python setup.py bdist_wheel
|
||||
|
||||
python-sdist: python-readme python-license
|
||||
cd library; python setup.py sdist
|
||||
|
||||
python-clean:
|
||||
-rm -r library/dist
|
||||
-rm -r library/build
|
||||
-rm -r library/*.egg-info
|
||||
|
||||
python-dist: python-clean python-wheels python-sdist
|
||||
ls library/dist
|
||||
|
||||
python-testdeploy: python-dist
|
||||
twine upload --repository-url https://test.pypi.org/legacy/ library/dist/*
|
||||
|
||||
python-deploy: check python-dist
|
||||
deploy: nopost build
|
||||
twine upload dist/*
|
||||
|
||||
32
README.md
32
README.md
@@ -1,50 +1,22 @@
|
||||
# Python ST7735
|
||||
|
||||
[](https://travis-ci.com/pimoroni/st7735-python)
|
||||
[](https://github.com/pimoroni/st7735-python/actions/workflows/test.yml)
|
||||
[](https://coveralls.io/github/pimoroni/st7735-python?branch=master)
|
||||
[](https://pypi.python.org/pypi/st7735)
|
||||
[](https://pypi.python.org/pypi/st7735)
|
||||
|
||||
|
||||
Python library to control an ST7735 TFT LCD display. Allows simple drawing on the display without installing a kernel module.
|
||||
|
||||
Designed specifically to work with a ST7735 based 160x80 pixel TFT SPI display. (Specifically the 0.96" SPI LCD from Pimoroni).
|
||||
|
||||
## Installing
|
||||
|
||||
### Python 2
|
||||
|
||||
Make sure you have the following dependencies:
|
||||
|
||||
````
|
||||
sudo apt update
|
||||
sudo apt install python-rpi.gpio python-spidev python-pip python-pil python-numpy
|
||||
````
|
||||
|
||||
Install this library by running:
|
||||
|
||||
````
|
||||
sudo pip install st7735
|
||||
````
|
||||
|
||||
### Python 3
|
||||
|
||||
Make sure you have the following dependencies:
|
||||
|
||||
````
|
||||
sudo apt update
|
||||
sudo apt install python3-rpi.gpio python3-spidev python3-pip python3-pil python3-numpy
|
||||
````
|
||||
|
||||
Install this library by running:
|
||||
|
||||
````
|
||||
sudo python3 -m pip install st7735
|
||||
pip install st7735
|
||||
````
|
||||
|
||||
See example of usage in the examples folder.
|
||||
|
||||
|
||||
# Licensing & History
|
||||
|
||||
This library is a modification of a modification of code originally written by Tony DiCola for Adafruit Industries, and modified to work with the ST7735 by Clement Skau.
|
||||
|
||||
5
ST7735.py
Normal file
5
ST7735.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from warnings import warn
|
||||
|
||||
from st7735 import * # noqa F403
|
||||
|
||||
warn("Using \"import ST7735\" is deprecated. Please \"import st7735\" (all lowercase)!", DeprecationWarning, stacklevel=2)
|
||||
87
check.sh
Executable file
87
check.sh
Executable file
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script handles some basic QA checks on the source
|
||||
|
||||
NOPOST=$1
|
||||
LIBRARY_NAME=`hatch project metadata name`
|
||||
LIBRARY_VERSION=`hatch version | awk -F "." '{print $1"."$2"."$3}'`
|
||||
POST_VERSION=`hatch version | awk -F "." '{print substr($4,0,length($4))}'`
|
||||
|
||||
success() {
|
||||
echo -e "$(tput setaf 2)$1$(tput sgr0)"
|
||||
}
|
||||
|
||||
inform() {
|
||||
echo -e "$(tput setaf 6)$1$(tput sgr0)"
|
||||
}
|
||||
|
||||
warning() {
|
||||
echo -e "$(tput setaf 1)$1$(tput sgr0)"
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
K="$1"
|
||||
case $K in
|
||||
-p|--nopost)
|
||||
NOPOST=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
if [[ $1 == -* ]]; then
|
||||
printf "Unrecognised option: $1\n";
|
||||
exit 1
|
||||
fi
|
||||
POSITIONAL_ARGS+=("$1")
|
||||
shift
|
||||
esac
|
||||
done
|
||||
|
||||
inform "Checking $LIBRARY_NAME $LIBRARY_VERSION\n"
|
||||
|
||||
inform "Checking for trailing whitespace..."
|
||||
grep -IUrn --color "[[:blank:]]$" --exclude-dir=dist --exclude-dir=.tox --exclude-dir=.git --exclude=PKG-INFO
|
||||
if [[ $? -eq 0 ]]; then
|
||||
warning "Trailing whitespace found!"
|
||||
exit 1
|
||||
else
|
||||
success "No trailing whitespace found."
|
||||
fi
|
||||
printf "\n"
|
||||
|
||||
inform "Checking for DOS line-endings..."
|
||||
grep -lIUrn --color $'\r' --exclude-dir=dist --exclude-dir=.tox --exclude-dir=.git --exclude=Makefile
|
||||
if [[ $? -eq 0 ]]; then
|
||||
warning "DOS line-endings found!"
|
||||
exit 1
|
||||
else
|
||||
success "No DOS line-endings found."
|
||||
fi
|
||||
printf "\n"
|
||||
|
||||
inform "Checking CHANGELOG.md..."
|
||||
cat CHANGELOG.md | grep ^${LIBRARY_VERSION} > /dev/null 2>&1
|
||||
if [[ $? -eq 1 ]]; then
|
||||
warning "Changes missing for version ${LIBRARY_VERSION}! Please update CHANGELOG.md."
|
||||
exit 1
|
||||
else
|
||||
success "Changes found for version ${LIBRARY_VERSION}."
|
||||
fi
|
||||
printf "\n"
|
||||
|
||||
inform "Checking for git tag ${LIBRARY_VERSION}..."
|
||||
git tag -l | grep -E "${LIBRARY_VERSION}$"
|
||||
if [[ $? -eq 1 ]]; then
|
||||
warning "Missing git tag for version ${LIBRARY_VERSION}"
|
||||
fi
|
||||
printf "\n"
|
||||
|
||||
if [[ $NOPOST ]]; then
|
||||
inform "Checking for .postN on library version..."
|
||||
if [[ "$POST_VERSION" != "" ]]; then
|
||||
warning "Found .$POST_VERSION on library version."
|
||||
inform "Please only use these for testpypi releases."
|
||||
exit 1
|
||||
else
|
||||
success "OK"
|
||||
fi
|
||||
fi
|
||||
@@ -18,36 +18,36 @@
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
import time
|
||||
import math
|
||||
import sys
|
||||
import time
|
||||
|
||||
from PIL import Image
|
||||
from PIL import ImageDraw
|
||||
import ST7735 as ST7735
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
SPI_SPEED_MHZ = 10 # Higher speed = higher framerate
|
||||
import st7735
|
||||
|
||||
SPI_SPEED_MHZ = 4 # Higher speed = higher framerate
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
SPI_SPEED_MHZ = int(sys.argv[1])
|
||||
|
||||
print("""
|
||||
print(f"""
|
||||
framerate.py - Test LCD framerate.
|
||||
|
||||
If you're using Breakout Garden, plug the 0.96" LCD (SPI)
|
||||
breakout into the rear slot.
|
||||
|
||||
Running at: {}MHz
|
||||
""".format(SPI_SPEED_MHZ))
|
||||
Running at: {SPI_SPEED_MHZ}MHz
|
||||
""")
|
||||
|
||||
# Create ST7735 LCD display class.
|
||||
disp = ST7735.ST7735(
|
||||
disp = st7735.ST7735(
|
||||
port=0,
|
||||
cs=ST7735.BG_SPI_CS_FRONT, # BG_SPI_CSB_BACK or BG_SPI_CS_FRONT
|
||||
dc=9,
|
||||
backlight=19, # 18 for back BG slot, 19 for front BG slot.
|
||||
cs=st7735.BG_SPI_CS_FRONT, # BG_SPI_CS_BACK or BG_SPI_CS_FRONT. BG_SPI_CS_FRONT (eg: CE1) for Enviro Plus
|
||||
dc="PIN21", # "GPIO9" / "PIN21". "PIN21" for a Pi 5 with Enviro Plus
|
||||
backlight="PIN32", # "PIN18" for back BG slot, "PIN19" for front BG slot. "PIN32" for a Pi 5 with Enviro Plus
|
||||
rotation=90,
|
||||
spi_speed_hz=SPI_SPEED_MHZ * 1000000
|
||||
spi_speed_hz=4000000
|
||||
)
|
||||
|
||||
WIDTH = disp.width
|
||||
@@ -78,7 +78,4 @@ while True:
|
||||
count += 1
|
||||
time_current = time.time() - time_start
|
||||
if count % 120 == 0:
|
||||
print("Time: {:8.3f}, Frames: {:6d}, FPS: {:8.3f}".format(
|
||||
time_current,
|
||||
count,
|
||||
count / time_current))
|
||||
print(f"Time: {time_current:8.3f}, Frames: {count:6d}, FPS: {count / time_current:8.3f}")
|
||||
|
||||
@@ -18,10 +18,12 @@
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
from PIL import Image
|
||||
import ST7735
|
||||
import time
|
||||
import sys
|
||||
import time
|
||||
|
||||
from PIL import Image
|
||||
|
||||
import st7735
|
||||
|
||||
print("""
|
||||
gif.py - Display a gif on the LCD.
|
||||
@@ -33,15 +35,16 @@ breakout into the front slot.
|
||||
if len(sys.argv) > 1:
|
||||
image_file = sys.argv[1]
|
||||
else:
|
||||
print("Usage: {} <filename.gif>".format(sys.argv[0]))
|
||||
print(f"Usage: {sys.argv[0]} <filename.gif>")
|
||||
sys.exit(0)
|
||||
|
||||
# Create TFT LCD display class.
|
||||
disp = ST7735.ST7735(
|
||||
disp = st7735.ST7735(
|
||||
port=0,
|
||||
cs=ST7735.BG_SPI_CS_FRONT, # BG_SPI_CSB_BACK or BG_SPI_CS_FRONT
|
||||
dc=9,
|
||||
backlight=19, # 18 for back BG slot, 19 for front BG slot.
|
||||
cs=st7735.BG_SPI_CS_FRONT, # BG_SPI_CS_BACK or BG_SPI_CS_FRONT. BG_SPI_CS_FRONT (eg: CE1) for Enviro Plus
|
||||
dc="PIN21", # "GPIO9" / "PIN21". "PIN21" for a Pi 5 with Enviro Plus
|
||||
backlight="PIN32", # "PIN18" for back BG slot, "PIN19" for front BG slot. "PIN32" for a Pi 5 with Enviro Plus
|
||||
rotation=90,
|
||||
spi_speed_hz=4000000
|
||||
)
|
||||
|
||||
@@ -52,10 +55,10 @@ width = disp.width
|
||||
height = disp.height
|
||||
|
||||
# Load an image.
|
||||
print('Loading gif: {}...'.format(image_file))
|
||||
print(f"Loading gif: {image_file}...")
|
||||
image = Image.open(image_file)
|
||||
|
||||
print('Drawing gif, press Ctrl+C to exit!')
|
||||
print("Drawing gif, press Ctrl+C to exit!")
|
||||
|
||||
frame = 0
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
import sys
|
||||
|
||||
from PIL import Image
|
||||
import ST7735 as ST7735
|
||||
|
||||
import st7735
|
||||
|
||||
print("""
|
||||
image.py - Display an image on the LCD.
|
||||
@@ -31,17 +32,17 @@ breakout into the rear slot.
|
||||
""")
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: {} <image_file>".format(sys.argv[0]))
|
||||
print(f"Usage: {sys.argv[0]} <image_file>")
|
||||
sys.exit(1)
|
||||
|
||||
image_file = sys.argv[1]
|
||||
|
||||
# Create ST7735 LCD display class.
|
||||
disp = ST7735.ST7735(
|
||||
disp = st7735.ST7735(
|
||||
port=0,
|
||||
cs=ST7735.BG_SPI_CS_FRONT, # BG_SPI_CSB_BACK or BG_SPI_CS_FRONT
|
||||
dc=9,
|
||||
backlight=19, # 18 for back BG slot, 19 for front BG slot.
|
||||
cs=st7735.BG_SPI_CS_FRONT, # BG_SPI_CS_BACK or BG_SPI_CS_FRONT. BG_SPI_CS_FRONT (eg: CE1) for Enviro Plus
|
||||
dc="PIN21", # "GPIO9" / "PIN21". "PIN21" for a Pi 5 with Enviro Plus
|
||||
backlight="PIN32", # "PIN18" for back BG slot, "PIN19" for front BG slot. "PIN32" for a Pi 5 with Enviro Plus
|
||||
rotation=90,
|
||||
spi_speed_hz=4000000
|
||||
)
|
||||
@@ -53,13 +54,13 @@ HEIGHT = disp.height
|
||||
disp.begin()
|
||||
|
||||
# Load an image.
|
||||
print('Loading image: {}...'.format(image_file))
|
||||
print(f"Loading image: {image_file}...")
|
||||
image = Image.open(image_file)
|
||||
|
||||
# Resize the image
|
||||
image = image.resize((WIDTH, HEIGHT))
|
||||
|
||||
# Draw the image on the display hardware.
|
||||
print('Drawing image')
|
||||
print("Drawing image")
|
||||
|
||||
disp.display(image)
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
from PIL import Image
|
||||
from PIL import ImageDraw
|
||||
from PIL import ImageFont
|
||||
import time
|
||||
|
||||
import ST7735
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
import st7735
|
||||
|
||||
MESSAGE = "Hello World! How are you today?"
|
||||
|
||||
# Create ST7735 LCD display class.
|
||||
disp = ST7735.ST7735(
|
||||
disp = st7735.ST7735(
|
||||
port=0,
|
||||
cs=ST7735.BG_SPI_CS_FRONT, # BG_SPI_CSB_BACK or BG_SPI_CS_FRONT
|
||||
dc=9,
|
||||
backlight=19, # 18 for back BG slot, 19 for front BG slot.
|
||||
cs=st7735.BG_SPI_CS_FRONT, # BG_SPI_CS_BACK or BG_SPI_CS_FRONT. BG_SPI_CS_FRONT (eg: CE1) for Enviro Plus
|
||||
dc="PIN21", # "GPIO9" / "PIN21". "PIN21" for a Pi 5 with Enviro Plus
|
||||
backlight="PIN32", # "PIN18" for back BG slot, "PIN19" for front BG slot. "PIN32" for a Pi 5 with Enviro Plus
|
||||
rotation=90,
|
||||
spi_speed_hz=10000000
|
||||
spi_speed_hz=4000000
|
||||
)
|
||||
|
||||
# Initialize display.
|
||||
@@ -31,7 +29,9 @@ draw = ImageDraw.Draw(img)
|
||||
|
||||
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 30)
|
||||
|
||||
size_x, size_y = draw.textsize(MESSAGE, font)
|
||||
x1, y1, x2, y2 = font.getbbox(MESSAGE)
|
||||
size_x = x2 - x1
|
||||
size_y = y2 - y1
|
||||
|
||||
text_x = 160
|
||||
text_y = (80 - size_y) // 2
|
||||
|
||||
@@ -18,26 +18,24 @@
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
from PIL import Image
|
||||
from PIL import ImageDraw
|
||||
from PIL import ImageFont
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
import ST7735
|
||||
import st7735
|
||||
|
||||
print("""
|
||||
shapes.py - Display test shapes on the LCD using PIL.
|
||||
|
||||
If you're using Breakout Garden, plug the 0.96" LCD (SPI)
|
||||
If you"re using Breakout Garden, plug the 0.96" LCD (SPI)
|
||||
breakout into the rear slot.
|
||||
|
||||
""")
|
||||
|
||||
# Create ST7735 LCD display class.
|
||||
disp = ST7735.ST7735(
|
||||
disp = st7735.ST7735(
|
||||
port=0,
|
||||
cs=ST7735.BG_SPI_CS_FRONT, # BG_SPI_CSB_BACK or BG_SPI_CS_FRONT
|
||||
dc=9,
|
||||
backlight=19, # 18 for back BG slot, 19 for front BG slot.
|
||||
cs=st7735.BG_SPI_CS_FRONT, # BG_SPI_CS_BACK or BG_SPI_CS_FRONT. BG_SPI_CS_FRONT (eg: CE1) for Enviro Plus
|
||||
dc="PIN21", # "GPIO9" / "PIN21". "PIN21" for a Pi 5 with Enviro Plus
|
||||
backlight="PIN32", # "PIN18" for back BG slot, "PIN19" for front BG slot. "PIN32" for a Pi 5 with Enviro Plus
|
||||
rotation=90,
|
||||
spi_speed_hz=4000000
|
||||
)
|
||||
@@ -52,7 +50,7 @@ HEIGHT = disp.height
|
||||
# Clear the display to a red background.
|
||||
# Can pass any tuple of red, green, blue values (from 0 to 255 each).
|
||||
# Get a PIL Draw object to start drawing on the display buffer.
|
||||
img = Image.new('RGB', (WIDTH, HEIGHT), color=(255, 0, 0))
|
||||
img = Image.new("RGB", (WIDTH, HEIGHT), color=(255, 0, 0))
|
||||
|
||||
draw = ImageDraw.Draw(img)
|
||||
|
||||
@@ -75,18 +73,19 @@ font = ImageFont.load_default()
|
||||
|
||||
# Alternatively load a TTF font.
|
||||
# Some other nice fonts to try: http://www.dafont.com/bitmap.php
|
||||
# font = ImageFont.truetype('Minecraftia.ttf', 16)
|
||||
# font = ImageFont.truetype("Minecraftia.ttf", 16)
|
||||
|
||||
|
||||
# Define a function to create rotated text. Unfortunately PIL doesn't have good
|
||||
# Define a function to create rotated text. Unfortunately PIL doesn"t have good
|
||||
# native support for rotated fonts, but this function can be used to make a
|
||||
# text image and rotate it so it's easy to paste in the buffer.
|
||||
# text image and rotate it so it"s easy to paste in the buffer.
|
||||
def draw_rotated_text(image, text, position, angle, font, fill=(255, 255, 255)):
|
||||
# Get rendered font width and height.
|
||||
draw = ImageDraw.Draw(image)
|
||||
width, height = draw.textsize(text, font=font)
|
||||
x1, y1, x2, y2 = font.getbbox(text)
|
||||
width = x2 - x1
|
||||
height = y2 - y1
|
||||
# Create a new image with transparent background to store the text.
|
||||
textimage = Image.new('RGBA', (width, height), (0, 0, 0, 0))
|
||||
textimage = Image.new("RGBA", (width, height), (0, 0, 0, 0))
|
||||
# Render the text.
|
||||
textdraw = ImageDraw.Draw(textimage)
|
||||
textdraw.text((0, 0), text, font=font, fill=fill)
|
||||
@@ -97,8 +96,8 @@ def draw_rotated_text(image, text, position, angle, font, fill=(255, 255, 255)):
|
||||
|
||||
|
||||
# Write two lines of white text on the buffer, rotated 90 degrees counter clockwise.
|
||||
draw_rotated_text(img, 'Hello World!', (0, 0), 90, font, fill=(255, 255, 255))
|
||||
draw_rotated_text(img, 'This is a line of text.', (10, HEIGHT - 10), 0, font, fill=(255, 255, 255))
|
||||
draw_rotated_text(img, "Hello World!", (0, 0), 90, font, fill=(255, 255, 255))
|
||||
draw_rotated_text(img, "This is a line of text.", (10, HEIGHT - 10), 0, font, fill=(255, 255, 255))
|
||||
|
||||
# Write buffer to display hardware, must be called to make things visible on the
|
||||
# display!
|
||||
|
||||
227
install.sh
227
install.sh
@@ -1,26 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
CONFIG=/boot/config.txt
|
||||
DATESTAMP=`date "+%Y-%M-%d-%H-%M-%S"`
|
||||
LIBRARY_NAME=`grep -m 1 name pyproject.toml | awk -F" = " '{print substr($2,2,length($2)-2)}'`
|
||||
CONFIG_FILE=config.txt
|
||||
CONFIG_DIR="/boot/firmware"
|
||||
DATESTAMP=`date "+%Y-%m-%d-%H-%M-%S"`
|
||||
CONFIG_BACKUP=false
|
||||
APT_HAS_UPDATED=false
|
||||
USER_HOME=/home/$SUDO_USER
|
||||
RESOURCES_TOP_DIR=$USER_HOME/Pimoroni
|
||||
RESOURCES_TOP_DIR=$HOME/Pimoroni
|
||||
VENV_BASH_SNIPPET=$RESOURCES_TOP_DIR/auto_venv.sh
|
||||
VENV_DIR=$HOME/.virtualenvs/pimoroni
|
||||
WD=`pwd`
|
||||
USAGE="sudo ./install.sh (--unstable)"
|
||||
USAGE="./install.sh (--unstable)"
|
||||
POSITIONAL_ARGS=()
|
||||
FORCE=false
|
||||
UNSTABLE=false
|
||||
PYTHON3=`which python3`
|
||||
PYTHON="python"
|
||||
|
||||
|
||||
user_check() {
|
||||
if [ $(id -u) -ne 0 ]; then
|
||||
printf "Script must be run as root. Try 'sudo ./install.sh'\n"
|
||||
if [ $(id -u) -eq 0 ]; then
|
||||
printf "Script should not be run as root. Try './install.sh'\n"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
confirm() {
|
||||
if [ "$FORCE" == '-y' ]; then
|
||||
if $FORCE; then
|
||||
true
|
||||
else
|
||||
read -r -p "$1 [y/N] " response < /dev/tty
|
||||
@@ -53,16 +57,74 @@ warning() {
|
||||
echo -e "$(tput setaf 1)$1$(tput sgr0)"
|
||||
}
|
||||
|
||||
find_config() {
|
||||
if [ ! -f "$CONFIG_DIR/$CONFIG_FILE" ]; then
|
||||
CONFIG_DIR="/boot"
|
||||
if [ ! -f "$CONFIG_DIR/$CONFIG_FILE"]; then
|
||||
warning "Could not find $CONFIG_FILE!"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if [ -f "/boot/$CONFIG_FILE" ] && [ ! -L "/boot/$CONFIG_FILE" ]; then
|
||||
warning "Oops! It looks like /boot/$CONFIG_FILE is not a link to $CONFIG_DIR/$CONFIG_FILE"
|
||||
warning "You might want to fix this!"
|
||||
fi
|
||||
fi
|
||||
inform "Using $CONFIG_FILE in $CONFIG_DIR"
|
||||
}
|
||||
|
||||
venv_bash_snippet() {
|
||||
inform "Checking for $VENV_BASH_SNIPPET\n"
|
||||
if [ ! -f $VENV_BASH_SNIPPET ]; then
|
||||
inform "Creating $VENV_BASH_SNIPPET\n"
|
||||
cat << EOF > $VENV_BASH_SNIPPET
|
||||
# Add "source $VENV_BASH_SNIPPET" to your ~/.bashrc to activate
|
||||
# the Pimoroni virtual environment automagically!
|
||||
VENV_DIR="$VENV_DIR"
|
||||
if [ ! -f \$VENV_DIR/bin/activate ]; then
|
||||
printf "Creating user Python environment in \$VENV_DIR, please wait...\n"
|
||||
mkdir -p \$VENV_DIR
|
||||
python3 -m venv --system-site-packages \$VENV_DIR
|
||||
fi
|
||||
printf " ↓ ↓ ↓ ↓ Hello, we've activated a Python venv for you. To exit, type \"deactivate\".\n"
|
||||
source \$VENV_DIR/bin/activate
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
|
||||
venv_check() {
|
||||
PYTHON_BIN=`which $PYTHON`
|
||||
if [[ $VIRTUAL_ENV == "" ]] || [[ $PYTHON_BIN != $VIRTUAL_ENV* ]]; then
|
||||
printf "This script should be run in a virtual Python environment.\n"
|
||||
if confirm "Would you like us to create one for you?"; then
|
||||
if [ ! -f $VENV_DIR/bin/activate ]; then
|
||||
inform "Creating virtual Python environment in $VENV_DIR, please wait...\n"
|
||||
mkdir -p $VENV_DIR
|
||||
/usr/bin/python3 -m venv $VENV_DIR --system-site-packages
|
||||
venv_bash_snippet
|
||||
else
|
||||
inform "Found existing virtual Python environment in $VENV_DIR\n"
|
||||
fi
|
||||
inform "Activating virtual Python environment in $VENV_DIR..."
|
||||
inform "source $VENV_DIR/bin/activate\n"
|
||||
source $VENV_DIR/bin/activate
|
||||
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function do_config_backup {
|
||||
if [ ! $CONFIG_BACKUP == true ]; then
|
||||
CONFIG_BACKUP=true
|
||||
FILENAME="config.preinstall-$LIBRARY_NAME-$DATESTAMP.txt"
|
||||
inform "Backing up $CONFIG to /boot/$FILENAME\n"
|
||||
cp $CONFIG /boot/$FILENAME
|
||||
inform "Backing up $CONFIG_DIR/$CONFIG_FILE to $CONFIG_DIR/$FILENAME\n"
|
||||
sudo cp $CONFIG_DIR/$CONFIG_FILE $CONFIG_DIR/$FILENAME
|
||||
mkdir -p $RESOURCES_TOP_DIR/config-backups/
|
||||
cp $CONFIG $RESOURCES_TOP_DIR/config-backups/$FILENAME
|
||||
cp $CONFIG_DIR/$CONFIG_FILE $RESOURCES_TOP_DIR/config-backups/$FILENAME
|
||||
if [ -f "$UNINSTALLER" ]; then
|
||||
echo "cp $RESOURCES_TOP_DIR/config-backups/$FILENAME $CONFIG" >> $UNINSTALLER
|
||||
echo "cp $RESOURCES_TOP_DIR/config-backups/$FILENAME $CONFIG_DIR/$CONFIG_FILE" >> $UNINSTALLER
|
||||
fi
|
||||
fi
|
||||
}
|
||||
@@ -83,16 +145,20 @@ function apt_pkg_install {
|
||||
if ! [ "$PACKAGES" == "" ]; then
|
||||
echo "Installing missing packages: $PACKAGES"
|
||||
if [ ! $APT_HAS_UPDATED ]; then
|
||||
apt update
|
||||
sudo apt update
|
||||
APT_HAS_UPDATED=true
|
||||
fi
|
||||
apt install -y $PACKAGES
|
||||
sudo apt install -y $PACKAGES
|
||||
if [ -f "$UNINSTALLER" ]; then
|
||||
echo "apt uninstall -y $PACKAGES"
|
||||
echo "apt uninstall -y $PACKAGES" >> $UNINSTALLER
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function pip_pkg_install {
|
||||
PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring $PYTHON -m pip install --upgrade "$@"
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
K="$1"
|
||||
case $K in
|
||||
@@ -100,6 +166,15 @@ while [[ $# -gt 0 ]]; do
|
||||
UNSTABLE=true
|
||||
shift
|
||||
;;
|
||||
-f|--force)
|
||||
FORCE=true
|
||||
shift
|
||||
;;
|
||||
-p|--python)
|
||||
PYTHON=$2
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
if [[ $1 == -* ]]; then
|
||||
printf "Unrecognised option: $1\n";
|
||||
@@ -112,31 +187,33 @@ while [[ $# -gt 0 ]]; do
|
||||
done
|
||||
|
||||
user_check
|
||||
venv_check
|
||||
|
||||
apt_pkg_install python-configparser
|
||||
if [ ! -f `which $PYTHON` ]; then
|
||||
printf "Python path $PYTHON not found!\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CONFIG_VARS=`python - <<EOF
|
||||
from configparser import ConfigParser
|
||||
c = ConfigParser()
|
||||
c.read('library/setup.cfg')
|
||||
p = dict(c['pimoroni'])
|
||||
# Convert multi-line config entries into bash arrays
|
||||
for k in p.keys():
|
||||
fmt = '"{}"'
|
||||
if '\n' in p[k]:
|
||||
p[k] = "'\n\t'".join(p[k].split('\n')[1:])
|
||||
fmt = "('{}')"
|
||||
p[k] = fmt.format(p[k])
|
||||
PYTHON_VER=`$PYTHON --version`
|
||||
|
||||
printf "$LIBRARY_NAME Python Library: Installer\n\n"
|
||||
|
||||
inform "Checking Dependencies. Please wait..."
|
||||
|
||||
pip_pkg_install toml
|
||||
|
||||
CONFIG_VARS=`$PYTHON - <<EOF
|
||||
import toml
|
||||
config = toml.load("pyproject.toml")
|
||||
p = dict(config['tool']['pimoroni'])
|
||||
# Convert list config entries into bash arrays
|
||||
for k, v in p.items():
|
||||
v = "'\n\t'".join(v)
|
||||
p[k] = f"('{v}')"
|
||||
print("""
|
||||
LIBRARY_NAME="{name}"
|
||||
LIBRARY_VERSION="{version}"
|
||||
""".format(**c['metadata']))
|
||||
print("""
|
||||
PY3_DEPS={py3deps}
|
||||
PY2_DEPS={py2deps}
|
||||
APT_PACKAGES={apt_packages}
|
||||
SETUP_CMDS={commands}
|
||||
CONFIG_TXT={configtxt}
|
||||
PY3_ONLY={py3only}
|
||||
""".format(**p))
|
||||
EOF`
|
||||
|
||||
@@ -150,6 +227,13 @@ eval $CONFIG_VARS
|
||||
RESOURCES_DIR=$RESOURCES_TOP_DIR/$LIBRARY_NAME
|
||||
UNINSTALLER=$RESOURCES_DIR/uninstall.sh
|
||||
|
||||
RES_DIR_OWNER=`stat -c "%U" $RESOURCES_TOP_DIR`
|
||||
|
||||
if [[ "$RES_DIR_OWNER" == "root" ]]; then
|
||||
warning "\n\nFixing $RESOURCES_TOP_DIR permissions!\n\n"
|
||||
sudo chown -R $USER:$USER $RESOURCES_TOP_DIR
|
||||
fi
|
||||
|
||||
mkdir -p $RESOURCES_DIR
|
||||
|
||||
cat << EOF > $UNINSTALLER
|
||||
@@ -157,57 +241,35 @@ printf "It's recommended you run these steps manually.\n"
|
||||
printf "If you want to run the full script, open it in\n"
|
||||
printf "an editor and remove 'exit 1' from below.\n"
|
||||
exit 1
|
||||
source $VIRTUAL_ENV/bin/activate
|
||||
EOF
|
||||
|
||||
printf "$LIBRARY_NAME $LIBRARY_VERSION Python Library: Installer\n\n"
|
||||
|
||||
if $UNSTABLE; then
|
||||
warning "Installing unstable library from source.\n\n"
|
||||
else
|
||||
printf "Installing stable library from pypi.\n\n"
|
||||
fi
|
||||
|
||||
cd library
|
||||
|
||||
if ! $PY3_ONLY; then
|
||||
printf "Installing for Python 2..\n"
|
||||
apt_pkg_install "${PY2_DEPS[@]}"
|
||||
if $UNSTABLE; then
|
||||
python setup.py install > /dev/null
|
||||
else
|
||||
pip install --upgrade $LIBRARY_NAME
|
||||
fi
|
||||
if [ $? -eq 0 ]; then
|
||||
success "Done!\n"
|
||||
echo "pip uninstall $LIBRARY_NAME" >> $UNINSTALLER
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -f "$PYTHON3" ]; then
|
||||
printf "Installing for Python 3..\n"
|
||||
apt_pkg_install "${PY3_DEPS[@]}"
|
||||
if $UNSTABLE; then
|
||||
python3 setup.py install > /dev/null
|
||||
else
|
||||
pip3 install --upgrade $LIBRARY_NAME
|
||||
fi
|
||||
if [ $? -eq 0 ]; then
|
||||
success "Done!\n"
|
||||
echo "pip3 uninstall $LIBRARY_NAME" >> $UNINSTALLER
|
||||
fi
|
||||
inform "Installing for $PYTHON_VER...\n"
|
||||
apt_pkg_install "${APT_PACKAGES[@]}"
|
||||
if $UNSTABLE; then
|
||||
pip_pkg_install .
|
||||
else
|
||||
if $PY3_ONLY; then
|
||||
warning "Python 3 is required to install this library...\n"
|
||||
exit 1
|
||||
fi
|
||||
pip_pkg_install $LIBRARY_NAME
|
||||
fi
|
||||
if [ $? -eq 0 ]; then
|
||||
success "Done!\n"
|
||||
echo "$PYTHON -m pip uninstall $LIBRARY_NAME" >> $UNINSTALLER
|
||||
fi
|
||||
|
||||
cd $WD
|
||||
|
||||
find_config
|
||||
|
||||
for ((i = 0; i < ${#SETUP_CMDS[@]}; i++)); do
|
||||
CMD="${SETUP_CMDS[$i]}"
|
||||
# Attempt to catch anything that touches /boot/config.txt and trigger a backup
|
||||
if [[ "$CMD" == *"raspi-config"* ]] || [[ "$CMD" == *"$CONFIG"* ]] || [[ "$CMD" == *"\$CONFIG"* ]]; then
|
||||
# Attempt to catch anything that touches config.txt and trigger a backup
|
||||
if [[ "$CMD" == *"raspi-config"* ]] || [[ "$CMD" == *"$CONFIG_DIR/$CONFIG_FILE"* ]] || [[ "$CMD" == *"\$CONFIG_DIR/\$CONFIG_FILE"* ]]; then
|
||||
do_config_backup
|
||||
fi
|
||||
eval $CMD
|
||||
@@ -217,10 +279,10 @@ for ((i = 0; i < ${#CONFIG_TXT[@]}; i++)); do
|
||||
CONFIG_LINE="${CONFIG_TXT[$i]}"
|
||||
if ! [ "$CONFIG_LINE" == "" ]; then
|
||||
do_config_backup
|
||||
inform "Adding $CONFIG_LINE to $CONFIG\n"
|
||||
sed -i "s/^#$CONFIG_LINE/$CONFIG_LINE/" $CONFIG
|
||||
if ! grep -q "^$CONFIG_LINE" $CONFIG; then
|
||||
printf "$CONFIG_LINE\n" >> $CONFIG
|
||||
inform "Adding $CONFIG_LINE to $CONFIG_DIR/$CONFIG_FILE\n"
|
||||
sudo sed -i "s/^#$CONFIG_LINE/$CONFIG_LINE/" $CONFIG_DIR/$CONFIG_FILE
|
||||
if ! grep -q "^$CONFIG_LINE" $CONFIG_DIR/$CONFIG_FILE; then
|
||||
printf "$CONFIG_LINE\n" | sudo tee --append $CONFIG_DIR/$CONFIG_FILE
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@@ -236,13 +298,12 @@ fi
|
||||
|
||||
printf "\n"
|
||||
|
||||
if [ -f "/usr/bin/pydoc" ]; then
|
||||
if confirm "Would you like to generate documentation?"; then
|
||||
pip_pkg_install pdoc
|
||||
printf "Generating documentation.\n"
|
||||
pydoc -w $LIBRARY_NAME > /dev/null
|
||||
if [ -f "$LIBRARY_NAME.html" ]; then
|
||||
cp $LIBRARY_NAME.html $RESOURCES_DIR/docs.html
|
||||
rm -f $LIBRARY_NAME.html
|
||||
inform "Documentation saved to $RESOURCES_DIR/docs.html"
|
||||
$PYTHON -m pdoc $LIBRARY_NAME -o $RESOURCES_DIR/docs > /dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
inform "Documentation saved to $RESOURCES_DIR/docs"
|
||||
success "Done!"
|
||||
else
|
||||
warning "Error: Failed to generate documentation."
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
[run]
|
||||
source = ST7735
|
||||
omit =
|
||||
.tox/*
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Pimoroni Ltd.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,5 +0,0 @@
|
||||
include CHANGELOG.txt
|
||||
include LICENSE.txt
|
||||
include README.md
|
||||
include setup.py
|
||||
recursive-include ST7735 *.py
|
||||
@@ -1,100 +0,0 @@
|
||||
# Python ST7735
|
||||
|
||||
[](https://travis-ci.com/pimoroni/st7735-python)
|
||||
[](https://coveralls.io/github/pimoroni/st7735-python?branch=master)
|
||||
[](https://pypi.python.org/pypi/st7735)
|
||||
[](https://pypi.python.org/pypi/st7735)
|
||||
|
||||
|
||||
Python library to control an ST7735 TFT LCD display. Allows simple drawing on the display without installing a kernel module.
|
||||
|
||||
Designed specifically to work with a ST7735 based 160x80 pixel TFT SPI display. (Specifically the 0.96" SPI LCD from Pimoroni).
|
||||
|
||||
## Installing
|
||||
|
||||
### Python 2
|
||||
|
||||
Make sure you have the following dependencies:
|
||||
|
||||
````
|
||||
sudo apt update
|
||||
sudo apt install python-rpi.gpio python-spidev python-pip python-pil python-numpy
|
||||
````
|
||||
|
||||
Install this library by running:
|
||||
|
||||
````
|
||||
sudo pip install st7735
|
||||
````
|
||||
|
||||
### Python 3
|
||||
|
||||
Make sure you have the following dependencies:
|
||||
|
||||
````
|
||||
sudo apt update
|
||||
sudo apt install python3-rpi.gpio python3-spidev python3-pip python3-pil python3-numpy
|
||||
````
|
||||
|
||||
Install this library by running:
|
||||
|
||||
````
|
||||
sudo python3 -m pip install st7735
|
||||
````
|
||||
|
||||
See example of usage in the examples folder.
|
||||
|
||||
|
||||
# Licensing & History
|
||||
|
||||
This library is a modification of a modification of code originally written by Tony DiCola for Adafruit Industries, and modified to work with the ST7735 by Clement Skau.
|
||||
|
||||
It has been modified by Pimoroni to include support for their 160x80 SPI LCD breakout, and hopefully also generalised enough so that it will support other ST7735-powered displays.
|
||||
|
||||
## Modifications include:
|
||||
|
||||
* PIL/Pillow has been removed from the underlying display driver to separate concerns- you should create your own PIL image and display it using `display(image)`
|
||||
* `width`, `height`, `rotation`, `invert`, `offset_left` and `offset_top` parameters can be passed into `__init__` for alternate displays
|
||||
* `Adafruit_GPIO` has been replaced with `RPi.GPIO` and `spidev` to closely align with our other software (IE: Raspberry Pi only)
|
||||
* Test fixtures have been added to keep this library stable
|
||||
|
||||
Pimoroni invests time and resources forking and modifying this open source code, please support Pimoroni and open-source software by purchasing products from us, too!
|
||||
|
||||
Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!
|
||||
|
||||
Modified from 'Modified from 'Adafruit Python ILI9341' written by Tony DiCola for Adafruit Industries.' written by Clement Skau.
|
||||
|
||||
MIT license, all text above must be included in any redistribution
|
||||
|
||||
# Changelog
|
||||
0.0.4-post1
|
||||
-----------
|
||||
|
||||
* Repackage with Markdown README/setup.cfg
|
||||
* Fix `__version__` to 0.0.4
|
||||
* Update dependencies in README
|
||||
|
||||
0.0.4
|
||||
-----
|
||||
|
||||
* Depend upon spidev==3.4.0 for stability fixes
|
||||
* Switch from manual data chunking to spidev.xfer3()
|
||||
|
||||
|
||||
0.0.3
|
||||
-----
|
||||
|
||||
* Fixed backlight pin
|
||||
* Added `set_backlight`
|
||||
* Added constants BG_SPI_CS_FRONT and BG_SPI_CS_BACK
|
||||
* Added module `__version__`
|
||||
|
||||
0.0.2
|
||||
-----
|
||||
|
||||
* Support for multiple display sizes/orientations
|
||||
|
||||
0.0.1
|
||||
-----
|
||||
|
||||
* Initial Release
|
||||
@@ -1,53 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
[metadata]
|
||||
name = ST7735
|
||||
version = 0.0.4-post1
|
||||
author = Philip Howard
|
||||
author_email = phil@pimoroni.com
|
||||
description = Library to control an ST7735 168x80 TFT LCD display.
|
||||
long_description = file: README.md
|
||||
long_description_content_type = text/markdown
|
||||
keywords = Raspberry Pi
|
||||
url = https://www.pimoroni.com
|
||||
project_urls =
|
||||
GitHub=https://www.github.com/pimoroni/st7735-python
|
||||
license = MIT
|
||||
# This includes the license file(s) in the wheel.
|
||||
# https://wheel.readthedocs.io/en/stable/user_guide.html#including-license-files-in-the-generated-wheel-file
|
||||
license_files = LICENSE.txt
|
||||
classifiers =
|
||||
Development Status :: 4 - Beta
|
||||
Operating System :: POSIX :: Linux
|
||||
License :: OSI Approved :: MIT License
|
||||
Intended Audience :: Developers
|
||||
Programming Language :: Python :: 3
|
||||
Topic :: Software Development
|
||||
Topic :: Software Development :: Libraries
|
||||
Topic :: System :: Hardware
|
||||
|
||||
[options]
|
||||
packages = ST7735
|
||||
install_requires =
|
||||
spidev >= 3.4
|
||||
|
||||
[flake8]
|
||||
exclude =
|
||||
.tox,
|
||||
.eggs,
|
||||
.git,
|
||||
__pycache__,
|
||||
build,
|
||||
dist
|
||||
ignore =
|
||||
E501
|
||||
|
||||
[pimoroni]
|
||||
py3only = false
|
||||
py2deps =
|
||||
python-pil
|
||||
py3deps =
|
||||
python3-pil
|
||||
configtxt =
|
||||
commands =
|
||||
printf "Setting up SPI...\n"
|
||||
raspi-config nonint do_spi 0
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (c) 2016 Pimoroni
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
from setuptools import setup, __version__
|
||||
from pkg_resources import parse_version
|
||||
|
||||
minimum_version = parse_version('30.4.0')
|
||||
|
||||
if parse_version(__version__) < minimum_version:
|
||||
raise RuntimeError("Package setuptools must be at least version {}".format(minimum_version))
|
||||
|
||||
setup()
|
||||
@@ -1,38 +0,0 @@
|
||||
"""Test configuration.
|
||||
These allow the mocking of various Python modules
|
||||
that might otherwise have runtime side-effects.
|
||||
"""
|
||||
import sys
|
||||
import mock
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope='function', autouse=False)
|
||||
def GPIO():
|
||||
"""Mock RPi.GPIO module."""
|
||||
GPIO = mock.MagicMock()
|
||||
# Fudge for Python < 37 (possibly earlier)
|
||||
sys.modules['RPi'] = mock.Mock()
|
||||
sys.modules['RPi'].GPIO = GPIO
|
||||
sys.modules['RPi.GPIO'] = GPIO
|
||||
yield GPIO
|
||||
del sys.modules['RPi']
|
||||
del sys.modules['RPi.GPIO']
|
||||
|
||||
|
||||
@pytest.fixture(scope='function', autouse=False)
|
||||
def spidev():
|
||||
"""Mock spidev module."""
|
||||
spidev = mock.MagicMock()
|
||||
sys.modules['spidev'] = spidev
|
||||
yield spidev
|
||||
del sys.modules['spidev']
|
||||
|
||||
|
||||
@pytest.fixture(scope='function', autouse=False)
|
||||
def numpy():
|
||||
"""Mock numpy module."""
|
||||
numpy = mock.MagicMock()
|
||||
sys.modules['numpy'] = numpy
|
||||
yield numpy
|
||||
del sys.modules['numpy']
|
||||
@@ -1,17 +0,0 @@
|
||||
from tools import force_reimport
|
||||
|
||||
|
||||
def test_128_64_0(GPIO, spidev, numpy):
|
||||
force_reimport('ST7735')
|
||||
import ST7735
|
||||
display = ST7735.ST7735(port=0, cs=0, dc=24, width=128, height=64, rotation=0)
|
||||
assert display.width == 128
|
||||
assert display.height == 64
|
||||
|
||||
|
||||
def test_128_64_90(GPIO, spidev, numpy):
|
||||
force_reimport('ST7735')
|
||||
import ST7735
|
||||
display = ST7735.ST7735(port=0, cs=0, dc=24, width=128, height=64, rotation=90)
|
||||
assert display.width == 64
|
||||
assert display.height == 128
|
||||
@@ -1,25 +0,0 @@
|
||||
import mock
|
||||
from tools import force_reimport
|
||||
|
||||
|
||||
def test_display(GPIO, spidev, numpy):
|
||||
force_reimport('ST7735')
|
||||
import ST7735
|
||||
display = ST7735.ST7735(port=0, cs=0, dc=24)
|
||||
numpy.dstack().flatten().tolist.return_value = [0xff, 0x00, 0xff, 0x00]
|
||||
display.display(mock.MagicMock())
|
||||
|
||||
spidev.SpiDev().xfer3.assert_called_with([0xff, 0x00, 0xff, 0x00])
|
||||
|
||||
|
||||
def test_color565(GPIO, spidev, numpy):
|
||||
force_reimport('ST7735')
|
||||
import ST7735
|
||||
assert ST7735.color565(255, 255, 255) == 0xFFFF
|
||||
|
||||
|
||||
def test_image_to_data(GPIO, spidev, numpy):
|
||||
force_reimport('ST7735')
|
||||
numpy.dstack().flatten().tolist.return_value = []
|
||||
import ST7735
|
||||
assert ST7735.image_to_data(mock.MagicMock()) == []
|
||||
@@ -1,47 +0,0 @@
|
||||
import mock
|
||||
from tools import force_reimport
|
||||
|
||||
|
||||
def test_setup(GPIO, spidev, numpy):
|
||||
force_reimport('ST7735')
|
||||
import ST7735
|
||||
display = ST7735.ST7735(port=0, cs=0, dc=24)
|
||||
del display
|
||||
|
||||
GPIO.output.assert_has_calls([
|
||||
mock.call(24, True),
|
||||
mock.call(24, False)
|
||||
], any_order=True)
|
||||
|
||||
|
||||
def test_setup_no_invert(GPIO, spidev, numpy):
|
||||
force_reimport('ST7735')
|
||||
import ST7735
|
||||
display = ST7735.ST7735(port=0, cs=0, dc=24, invert=False)
|
||||
del display
|
||||
|
||||
|
||||
def test_setup_with_backlight(GPIO, spidev, numpy):
|
||||
force_reimport('ST7735')
|
||||
import ST7735
|
||||
display = ST7735.ST7735(port=0, cs=0, dc=24, backlight=4)
|
||||
GPIO.setup.assert_called_with(4, GPIO.OUT)
|
||||
|
||||
display.set_backlight(GPIO.HIGH)
|
||||
|
||||
GPIO.output.assert_has_calls([
|
||||
mock.call(4, GPIO.LOW),
|
||||
mock.call(4, GPIO.HIGH),
|
||||
# Dozens of falls with True/False here
|
||||
# due to _init() being called and the display
|
||||
# setup setting the command/data pin
|
||||
mock.call(4, GPIO.HIGH)
|
||||
], any_order=True)
|
||||
|
||||
|
||||
def test_setup_with_reset(GPIO, spidev, numpy):
|
||||
force_reimport('ST7735')
|
||||
import ST7735
|
||||
display = ST7735.ST7735(port=0, cs=0, dc=24, rst=4)
|
||||
GPIO.setup.assert_called_with(4, GPIO.OUT)
|
||||
del display
|
||||
@@ -1,23 +0,0 @@
|
||||
import sys
|
||||
|
||||
|
||||
def force_reimport(module):
|
||||
"""Force the module under test to be re-imported.
|
||||
|
||||
Because pytest runs all tests within the same scope (this makes me cry)
|
||||
we have to do some manual housekeeping to avoid tests polluting each other.
|
||||
|
||||
Since conftest.py already does some sys.modules mangling I see no reason not to
|
||||
do the same thing here.
|
||||
"""
|
||||
if "." in module:
|
||||
steps = module.split(".")
|
||||
else:
|
||||
steps = [module]
|
||||
|
||||
for i in range(len(steps)):
|
||||
module = ".".join(steps[0:i + 1])
|
||||
try:
|
||||
del sys.modules[module]
|
||||
except KeyError:
|
||||
pass
|
||||
@@ -1,24 +0,0 @@
|
||||
[tox]
|
||||
envlist = py{27,36,37,39},qa
|
||||
skip_missing_interpreters = True
|
||||
|
||||
[testenv]
|
||||
commands =
|
||||
python setup.py install
|
||||
coverage run -m py.test -v -r wsx
|
||||
coverage report
|
||||
deps =
|
||||
mock
|
||||
pytest>=3.1
|
||||
pytest-cov
|
||||
|
||||
[testenv:qa]
|
||||
commands =
|
||||
check-manifest --ignore tox.ini,tests/*,.coveragerc
|
||||
python setup.py sdist bdist_wheel
|
||||
twine check dist/*
|
||||
flake8 --ignore E501
|
||||
deps =
|
||||
check-manifest
|
||||
flake8
|
||||
twine
|
||||
124
pyproject.toml
Normal file
124
pyproject.toml
Normal file
@@ -0,0 +1,124 @@
|
||||
[build-system]
|
||||
requires = ["hatchling", "hatch-fancy-pypi-readme"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "st7735"
|
||||
dynamic = ["version", "readme"]
|
||||
description = "Library to control an ST7735 168x80 TFT LCD display."
|
||||
license = {file = "LICENSE"}
|
||||
requires-python = ">= 3.7"
|
||||
authors = [
|
||||
{ name = "Philip Howard", email = "phil@pimoroni.com" },
|
||||
]
|
||||
maintainers = [
|
||||
{ name = "Philip Howard", email = "phil@pimoroni.com" },
|
||||
]
|
||||
keywords = [
|
||||
"Pi",
|
||||
"Raspberry",
|
||||
"displays"
|
||||
]
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: POSIX :: Linux",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Topic :: Software Development",
|
||||
"Topic :: Software Development :: Libraries",
|
||||
"Topic :: System :: Hardware",
|
||||
]
|
||||
dependencies = [
|
||||
"spidev>=3.4",
|
||||
"numpy"
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
GitHub = "https://www.github.com/pimoroni/st7735-python"
|
||||
Homepage = "https://www.pimoroni.com"
|
||||
|
||||
[tool.hatch.version]
|
||||
path = "st7735/__init__.py"
|
||||
|
||||
[tool.hatch.build]
|
||||
include = [
|
||||
"st7735",
|
||||
"ST7735.py",
|
||||
"README.md",
|
||||
"CHANGELOG.md",
|
||||
"LICENSE"
|
||||
]
|
||||
|
||||
[tool.hatch.build.targets.sdist]
|
||||
include = [
|
||||
"*"
|
||||
]
|
||||
exclude = [
|
||||
".*",
|
||||
"dist"
|
||||
]
|
||||
|
||||
[tool.hatch.metadata.hooks.fancy-pypi-readme]
|
||||
content-type = "text/markdown"
|
||||
fragments = [
|
||||
{ path = "README.md" },
|
||||
{ text = "\n" },
|
||||
{ path = "CHANGELOG.md" }
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
exclude = [
|
||||
'.tox',
|
||||
'.egg',
|
||||
'.git',
|
||||
'__pycache__',
|
||||
'build',
|
||||
'dist'
|
||||
]
|
||||
line-length = 200
|
||||
|
||||
[tool.codespell]
|
||||
skip = """
|
||||
./.tox,\
|
||||
./.egg,\
|
||||
./.git,\
|
||||
./__pycache__,\
|
||||
./build,\
|
||||
./dist.\
|
||||
"""
|
||||
|
||||
[tool.isort]
|
||||
line_length = 200
|
||||
|
||||
[tool.black]
|
||||
line-length = 200
|
||||
|
||||
[tool.check-manifest]
|
||||
ignore = [
|
||||
'.stickler.yml',
|
||||
'boilerplate.md',
|
||||
'check.sh',
|
||||
'install.sh',
|
||||
'uninstall.sh',
|
||||
'Makefile',
|
||||
'tox.ini',
|
||||
'tests/*',
|
||||
'examples/*',
|
||||
'.coveragerc',
|
||||
'requirements-dev.txt'
|
||||
]
|
||||
|
||||
[tool.pimoroni]
|
||||
apt_packages = []
|
||||
configtxt = []
|
||||
commands = [
|
||||
"printf \"Setting up SPI...\n\"",
|
||||
"sudo raspi-config nonint do_spi 0"
|
||||
]
|
||||
9
requirements-dev.txt
Normal file
9
requirements-dev.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
check-manifest
|
||||
ruff
|
||||
codespell
|
||||
isort
|
||||
twine
|
||||
hatch
|
||||
hatch-fancy-pypi-readme
|
||||
tox
|
||||
pdoc
|
||||
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
pillow>=10.0.0
|
||||
numpy>=1.26.0
|
||||
spidev>=3.4
|
||||
@@ -20,13 +20,16 @@
|
||||
# THE SOFTWARE.
|
||||
import numbers
|
||||
import time
|
||||
|
||||
import gpiod
|
||||
import gpiodevice
|
||||
import numpy as np
|
||||
|
||||
import spidev
|
||||
import RPi.GPIO as GPIO
|
||||
from gpiod.line import Direction, Value
|
||||
|
||||
__version__ = '1.0.0'
|
||||
|
||||
__version__ = '0.0.4'
|
||||
OUTL = gpiod.LineSettings(direction=Direction.OUTPUT, output_value=Value.INACTIVE)
|
||||
|
||||
BG_SPI_CS_BACK = 0
|
||||
BG_SPI_CS_FRONT = 1
|
||||
@@ -119,12 +122,12 @@ class ST7735(object):
|
||||
"""Representation of an ST7735 TFT LCD."""
|
||||
|
||||
def __init__(self, port, cs, dc, backlight=None, rst=None, width=ST7735_TFTWIDTH,
|
||||
height=ST7735_TFTHEIGHT, rotation=90, offset_left=None, offset_top=None, invert=True, spi_speed_hz=4000000):
|
||||
height=ST7735_TFTHEIGHT, rotation=90, offset_left=None, offset_top=None, invert=True, bgr=True, spi_speed_hz=4000000):
|
||||
"""Create an instance of the display using SPI communication.
|
||||
|
||||
Must provide the GPIO pin number for the D/C pin and the SPI driver.
|
||||
Must provide the GPIO pin label for the D/C pin and the SPI driver.
|
||||
|
||||
Can optionally provide the GPIO pin number for the reset pin as the rst parameter.
|
||||
Can optionally provide the GPIO pin label for the reset pin as the rst parameter.
|
||||
|
||||
:param port: SPI port number
|
||||
:param cs: SPI chip-select number (0 or 1 for BCM
|
||||
@@ -140,20 +143,19 @@ class ST7735(object):
|
||||
|
||||
"""
|
||||
|
||||
GPIO.setwarnings(False)
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
|
||||
self._spi = spidev.SpiDev(port, cs)
|
||||
self._spi.mode = 0
|
||||
self._spi.lsbfirst = False
|
||||
self._spi.max_speed_hz = spi_speed_hz
|
||||
|
||||
self._dc = dc
|
||||
self._rst = rst
|
||||
self._width = width
|
||||
self._height = height
|
||||
self._rotation = rotation
|
||||
self._invert = invert
|
||||
self._bgr = bgr
|
||||
|
||||
self._bl = None
|
||||
self._rst = None
|
||||
|
||||
# Default left offset to center display
|
||||
if offset_left is None:
|
||||
@@ -167,24 +169,29 @@ class ST7735(object):
|
||||
|
||||
self._offset_top = offset_top
|
||||
|
||||
gpiodevice.friendly_errors = True
|
||||
|
||||
# Set DC as output.
|
||||
GPIO.setup(dc, GPIO.OUT)
|
||||
self._dc = gpiodevice.get_pin(dc, "st7735-dc", OUTL)
|
||||
|
||||
# Setup backlight as output (if provided).
|
||||
self._backlight = backlight
|
||||
if backlight is not None:
|
||||
GPIO.setup(backlight, GPIO.OUT)
|
||||
GPIO.output(backlight, GPIO.LOW)
|
||||
self._bl = gpiodevice.get_pin(backlight, "st7735-bl", OUTL)
|
||||
self.set_pin(self._bl, False)
|
||||
time.sleep(0.1)
|
||||
GPIO.output(backlight, GPIO.HIGH)
|
||||
self.set_pin(self._bl, True)
|
||||
|
||||
# Setup reset as output (if provided).
|
||||
if rst is not None:
|
||||
GPIO.setup(rst, GPIO.OUT)
|
||||
self._rst = gpiodevice.get_pin(rst, "st7735-rst", OUTL)
|
||||
|
||||
self.reset()
|
||||
self._init()
|
||||
|
||||
def set_pin(self, pin, state):
|
||||
lines, offset = pin
|
||||
lines.set_value(offset, Value.ACTIVE if state else Value.INACTIVE)
|
||||
|
||||
def send(self, data, is_data=True, chunk_size=4096):
|
||||
"""Write a byte or array of bytes to the display. Is_data parameter
|
||||
controls if byte should be interpreted as display data (True) or command
|
||||
@@ -192,7 +199,7 @@ class ST7735(object):
|
||||
single SPI transaction, with a default of 4096.
|
||||
"""
|
||||
# Set DC low for command, high for data.
|
||||
GPIO.output(self._dc, is_data)
|
||||
self.set_pin(self._dc, is_data)
|
||||
# Convert scalar argument to list so either can be passed as parameter.
|
||||
if isinstance(data, numbers.Number):
|
||||
data = [data & 0xFF]
|
||||
@@ -200,8 +207,20 @@ class ST7735(object):
|
||||
|
||||
def set_backlight(self, value):
|
||||
"""Set the backlight on/off."""
|
||||
if self._backlight is not None:
|
||||
GPIO.output(self._backlight, value)
|
||||
if self._bl is not None:
|
||||
self.set_pin(self._bl, value)
|
||||
|
||||
def display_off(self):
|
||||
self.command(ST7735_DISPOFF)
|
||||
|
||||
def display_on(self):
|
||||
self.command(ST7735_DISPON)
|
||||
|
||||
def sleep(self):
|
||||
self.command(ST7735_SLPIN)
|
||||
|
||||
def wake(self):
|
||||
self.command(ST7735_SLPOUT)
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
@@ -222,11 +241,11 @@ class ST7735(object):
|
||||
def reset(self):
|
||||
"""Reset the display, if reset pin is connected."""
|
||||
if self._rst is not None:
|
||||
GPIO.output(self._rst, 1)
|
||||
self.set_pin(self._rst, True)
|
||||
time.sleep(0.500)
|
||||
GPIO.output(self._rst, 0)
|
||||
self.set_pin(self._rst, False)
|
||||
time.sleep(0.500)
|
||||
GPIO.output(self._rst, 1)
|
||||
self.set_pin(self._rst, True)
|
||||
time.sleep(0.500)
|
||||
|
||||
def _init(self):
|
||||
@@ -285,7 +304,10 @@ class ST7735(object):
|
||||
self.command(ST7735_INVOFF) # Don't invert display
|
||||
|
||||
self.command(ST7735_MADCTL) # Memory access control (directions)
|
||||
self.data(0xC8) # row addr/col addr, bottom to top refresh
|
||||
if self._bgr:
|
||||
self.data(0xC8) # row addr/col addr, bottom to top refresh; Set D3 RGB Bit to 1 for format BGR
|
||||
else:
|
||||
self.data(0xC0) # row addr/col addr, bottom to top refresh; Set D3 RGB Bit to 0 for format RGB
|
||||
|
||||
self.command(ST7735_COLMOD) # set color mode
|
||||
self.data(0x05) # 16-bit color
|
||||
@@ -341,7 +363,7 @@ class ST7735(object):
|
||||
self.command(ST7735_NORON) # Normal display on
|
||||
time.sleep(0.10) # 10 ms
|
||||
|
||||
self.command(ST7735_DISPON) # Display on
|
||||
self.display_on()
|
||||
time.sleep(0.100) # 100 ms
|
||||
|
||||
def begin(self):
|
||||
51
tests/conftest.py
Normal file
51
tests/conftest.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""Test configuration.
|
||||
These allow the mocking of various Python modules
|
||||
that might otherwise have runtime side-effects.
|
||||
"""
|
||||
import sys
|
||||
|
||||
import mock
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="function", autouse=False)
|
||||
def st7735():
|
||||
import st7735
|
||||
yield st7735
|
||||
del sys.modules["st7735"]
|
||||
|
||||
|
||||
@pytest.fixture(scope="function", autouse=False)
|
||||
def gpiod():
|
||||
"""Mock gpiod module."""
|
||||
sys.modules["gpiod"] = mock.MagicMock()
|
||||
sys.modules["gpiod.line"] = mock.MagicMock()
|
||||
yield sys.modules["gpiod"]
|
||||
del sys.modules["gpiod"]
|
||||
|
||||
|
||||
@pytest.fixture(scope="function", autouse=False)
|
||||
def gpiodevice():
|
||||
"""Mock gpiodevice module."""
|
||||
sys.modules["gpiodevice"] = mock.MagicMock()
|
||||
sys.modules["gpiodevice"].get_pin.return_value = (mock.Mock(), 0)
|
||||
yield sys.modules["gpiodevice"]
|
||||
del sys.modules["gpiodevice"]
|
||||
|
||||
|
||||
@pytest.fixture(scope="function", autouse=False)
|
||||
def spidev():
|
||||
"""Mock spidev module."""
|
||||
spidev = mock.MagicMock()
|
||||
sys.modules["spidev"] = spidev
|
||||
yield spidev
|
||||
del sys.modules["spidev"]
|
||||
|
||||
|
||||
@pytest.fixture(scope="function", autouse=False)
|
||||
def numpy():
|
||||
"""Mock numpy module."""
|
||||
numpy = mock.MagicMock()
|
||||
sys.modules["numpy"] = numpy
|
||||
yield numpy
|
||||
del sys.modules["numpy"]
|
||||
10
tests/test_dimensions.py
Normal file
10
tests/test_dimensions.py
Normal file
@@ -0,0 +1,10 @@
|
||||
def test_128_64_0(gpiodevice, gpiod, spidev, numpy, st7735):
|
||||
display = st7735.ST7735(port=0, cs=0, dc=24, width=128, height=64, rotation=0)
|
||||
assert display.width == 128
|
||||
assert display.height == 64
|
||||
|
||||
|
||||
def test_128_64_90(gpiodevice, gpiod, spidev, numpy, st7735):
|
||||
display = st7735.ST7735(port=0, cs=0, dc=24, width=128, height=64, rotation=90)
|
||||
assert display.width == 64
|
||||
assert display.height == 128
|
||||
18
tests/test_features.py
Normal file
18
tests/test_features.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import mock
|
||||
|
||||
|
||||
def test_display(gpiodevice, gpiod, spidev, numpy, st7735):
|
||||
display = st7735.ST7735(port=0, cs=0, dc=24)
|
||||
numpy.dstack().flatten().tolist.return_value = [0xff, 0x00, 0xff, 0x00]
|
||||
display.display(mock.MagicMock())
|
||||
|
||||
spidev.SpiDev().xfer3.assert_called_with([0xff, 0x00, 0xff, 0x00])
|
||||
|
||||
|
||||
def test_color565(gpiodevice, gpiod, spidev, numpy, st7735):
|
||||
assert st7735.color565(255, 255, 255) == 0xFFFF
|
||||
|
||||
|
||||
def test_image_to_data(gpiodevice, gpiod, spidev, numpy, st7735):
|
||||
numpy.dstack().flatten().tolist.return_value = []
|
||||
assert st7735.image_to_data(mock.MagicMock()) == []
|
||||
27
tests/test_setup.py
Normal file
27
tests/test_setup.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import mock
|
||||
|
||||
|
||||
def test_setup(gpiodevice, gpiod, spidev, numpy, st7735):
|
||||
_ = st7735.ST7735(port=0, cs=0, dc="GPIO24")
|
||||
|
||||
gpiodevice.get_pin.assert_has_calls([
|
||||
mock.call("GPIO24", "st7735-dc", st7735.OUTL)
|
||||
], any_order=True)
|
||||
|
||||
|
||||
def test_setup_no_invert(gpiodevice, gpiod, spidev, numpy, st7735):
|
||||
_ = st7735.ST7735(port=0, cs=0, dc="GPIO24", invert=False)
|
||||
|
||||
|
||||
def test_setup_with_backlight(gpiodevice, gpiod, spidev, numpy, st7735):
|
||||
display = st7735.ST7735(port=0, cs=0, dc="GPIO24", backlight="GPIO4")
|
||||
|
||||
display.set_backlight(True)
|
||||
|
||||
gpiodevice.get_pin.assert_has_calls([mock.call("GPIO4", "st7735-bl", st7735.OUTL)], any_order=True)
|
||||
|
||||
|
||||
def test_setup_with_reset(gpiodevice, gpiod, spidev, numpy, st7735):
|
||||
_ = st7735.ST7735(port=0, cs=0, dc=24, rst="GPIO4")
|
||||
|
||||
gpiodevice.get_pin.assert_has_calls([mock.call("GPIO4", "st7735-rst", st7735.OUTL)], any_order=True)
|
||||
34
tox.ini
Normal file
34
tox.ini
Normal file
@@ -0,0 +1,34 @@
|
||||
[tox]
|
||||
envlist = py,qa
|
||||
skip_missing_interpreters = True
|
||||
isolated_build = true
|
||||
minversion = 4.0.0
|
||||
|
||||
[testenv]
|
||||
commands =
|
||||
coverage run -m pytest -v -r wsx
|
||||
coverage report
|
||||
deps =
|
||||
mock
|
||||
pytest>=3.1
|
||||
pytest-cov
|
||||
build
|
||||
|
||||
[testenv:qa]
|
||||
commands =
|
||||
check-manifest
|
||||
python -m build --no-isolation
|
||||
python -m twine check dist/*
|
||||
isort --check .
|
||||
ruff .
|
||||
codespell .
|
||||
deps =
|
||||
check-manifest
|
||||
ruff
|
||||
codespell
|
||||
isort
|
||||
twine
|
||||
build
|
||||
hatch
|
||||
hatch-fancy-pypi-readme
|
||||
|
||||
72
uninstall.sh
Executable file
72
uninstall.sh
Executable file
@@ -0,0 +1,72 @@
|
||||
#!/bin/bash
|
||||
|
||||
FORCE=false
|
||||
LIBRARY_NAME=`grep -m 1 name pyproject.toml | awk -F" = " '{print substr($2,2,length($2)-2)}'`
|
||||
RESOURCES_DIR=$HOME/Pimoroni/$LIBRARY_NAME
|
||||
PYTHON="python"
|
||||
|
||||
|
||||
venv_check() {
|
||||
PYTHON_BIN=`which $PYTHON`
|
||||
if [[ $VIRTUAL_ENV == "" ]] || [[ $PYTHON_BIN != $VIRTUAL_ENV* ]]; then
|
||||
printf "This script should be run in a virtual Python environment.\n"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
user_check() {
|
||||
if [ $(id -u) -eq 0 ]; then
|
||||
printf "Script should not be run as root. Try './uninstall.sh'\n"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
confirm() {
|
||||
if $FORCE; then
|
||||
true
|
||||
else
|
||||
read -r -p "$1 [y/N] " response < /dev/tty
|
||||
if [[ $response =~ ^(yes|y|Y)$ ]]; then
|
||||
true
|
||||
else
|
||||
false
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
prompt() {
|
||||
read -r -p "$1 [y/N] " response < /dev/tty
|
||||
if [[ $response =~ ^(yes|y|Y)$ ]]; then
|
||||
true
|
||||
else
|
||||
false
|
||||
fi
|
||||
}
|
||||
|
||||
success() {
|
||||
echo -e "$(tput setaf 2)$1$(tput sgr0)"
|
||||
}
|
||||
|
||||
inform() {
|
||||
echo -e "$(tput setaf 6)$1$(tput sgr0)"
|
||||
}
|
||||
|
||||
warning() {
|
||||
echo -e "$(tput setaf 1)$1$(tput sgr0)"
|
||||
}
|
||||
|
||||
printf "$LIBRARY_NAME Python Library: Uninstaller\n\n"
|
||||
|
||||
user_check
|
||||
venv_check
|
||||
|
||||
printf "Uninstalling for Python 3...\n"
|
||||
$PYTHON -m pip uninstall $LIBRARY_NAME
|
||||
|
||||
if [ -d $RESOURCES_DIR ]; then
|
||||
if confirm "Would you like to delete $RESOURCES_DIR?"; then
|
||||
rm -r $RESOURCES_DIR
|
||||
fi
|
||||
fi
|
||||
|
||||
printf "Done!\n"
|
||||
Reference in New Issue
Block a user