Porównaj commity

...

3 Commity

Autor SHA1 Wiadomość Data
peterhinch 1a37b726ea Add FeatherS3 example. 2023-04-30 15:24:10 +01:00
peterhinch eb1f7f1987 Use new FB metods. Requires MP>=V1.20 2023-04-30 12:12:20 +01:00
peterhinch 9227e1c215 MP V1.20 version tested. 2023-04-30 11:37:04 +01:00
5 zmienionych plików z 90 dodań i 89 usunięć

Wyświetl plik

@ -64,6 +64,7 @@ target and a C device driver (unless you can acquire a suitable binary).
# Project status
April 2023: Add limited ePaper support, grid widget, calendar and epaper demos.
Now requires firmware >= V1.20.
July 2022: Add ESP32 touch pad support.
@ -76,8 +77,8 @@ SPIRAM.
February 2022: Supports use with only three buttons devised by Bart Cerneels.
Simplified widget import. Existing users should replace the entire `gui` tree.
Code has been tested on ESP32, ESP32-S2, Pi Pico and Pyboard. This is under
development so check for updates.
Code has been tested on ESP32, ESP32-S2, ESP32-S3, Pi Pico and Pyboard. This is
under development so check for updates.
# 0. Contents
@ -931,7 +932,7 @@ base screen are cancelled.
For finer control, applications can ignore this method and handle cancellation
explicitly in code.
## 4.5 Bound variable
## 4.5 Class variable
* `do_gc = True` By default a coroutine is launched to periodically perform
garbage collection (GC). On most platforms this reduces latency by doing GC

Wyświetl plik

@ -1,28 +1,31 @@
# ugui.py Micropython GUI library
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2019-2022 Peter Hinch
# Copyright (c) 2019-2023 Peter Hinch
# Credit to Bart Cerneels for devising and prototyping the 3-button mode
# Also for suggesting abstracting the input device class.
# Now requires firmware >= V1.20
import uasyncio as asyncio
from time import ticks_diff, ticks_ms
import gc
from array import array
import sys
from gui.core.colors import *
from gui.primitives import Pushbutton
if sys.implementation.version < (1, 20, 0):
raise OSError("Firmware V1.20 or later required.")
# Globally available singleton objects
display = None # Singleton instance
ssd = None
_vb = True
gc.collect()
__version__ = (0, 1, 7)
# Null function
dolittle = lambda *_: None
__version__ = (0, 1, 8)
async def _g():
@ -120,6 +123,32 @@ class Input:
# Must be subclassed: subclass provides input device and populates globals
# display and ssd.
class DisplayIP:
# Populate array for clipped rect
@staticmethod
def crect(x, y, w, h):
c = 4 # Clip pixels
return array(
"H",
(
x + c,
y,
x + w - c,
y,
x + w,
y + c,
x + w,
y + h - c,
x + w - c,
y + h,
x + c,
y + h,
x,
y + h - c,
x,
y + c,
),
)
def __init__(self, ipdev):
self.ipdev = ipdev
self.height = ssd.height
@ -146,11 +175,9 @@ class DisplayIP:
# Greying out has only one option given limitation of 4-bit display driver
# It would be possible to do better with RGB565 but would need inverse transformation
# to (r, g, b), scale and re-convert to integer.
def _getcolor(self, color):
def _getcolor(self, color):
# Takes in an integer color, bit size dependent on driver
return (
color_map[GREY_OUT] if self._is_grey and color != color_map[BG] else color
)
return color_map[GREY_OUT] if self._is_grey and color != color_map[BG] else color
def usegrey(self, val): # display.usegrey(True) sets greyed-out
self._is_grey = val
@ -178,77 +205,26 @@ class DisplayIP:
def line(self, x1, y1, x2, y2, color):
ssd.line(x1, y1, x2, y2, self._getcolor(color))
# Private method uses physical color
def _circle(self, x0, y0, r, color): # Single pixel circle
x = -r
y = 0
err = 2 - 2 * r
while x <= 0:
ssd.pixel(x0 - x, y0 + y, color)
ssd.pixel(x0 + x, y0 + y, color)
ssd.pixel(x0 + x, y0 - y, color)
ssd.pixel(x0 - x, y0 - y, color)
e2 = err
if e2 <= y:
y += 1
err += y * 2 + 1
if -x == y and e2 <= x:
e2 = 0
if e2 > x:
x += 1
err += x * 2 + 1
def circle(self, x0, y0, r, color, width=1): # Draw circle (maybe grey)
def circle(self, x0, y0, r, color): # Draw circle (maybe grey)
color = self._getcolor(color)
x0, y0, r = int(x0), int(y0), int(r)
for r in range(r, r - width, -1):
self._circle(x0, y0, r, color)
ssd.ellipse(int(x0), int(y0), int(r), int(r), color)
def fillcircle(self, x0, y0, r, color): # Draw filled circle
color = self._getcolor(color)
x0, y0, r = int(x0), int(y0), int(r)
x = -r
y = 0
err = 2 - 2 * r
while x <= 0:
ssd.line(x0 - x, y0 - y, x0 - x, y0 + y, color)
ssd.line(x0 + x, y0 - y, x0 + x, y0 + y, color)
e2 = err
if e2 <= y:
y += 1
err += y * 2 + 1
if -x == y and e2 <= x:
e2 = 0
if e2 > x:
x += 1
err += x * 2 + 1
ssd.ellipse(int(x0), int(y0), int(r), int(r), color, True)
def clip_rect(self, x, y, w, h, color):
color = self._getcolor(color)
c = 4
ssd.hline(x + c, y, w - 2 * c, color)
ssd.hline(x + c, y + h, w - 2 * c, color)
ssd.vline(x, y + c, h - 2 * c, color)
ssd.vline(x + w - 1, y + c, h - 2 * c, color)
ssd.line(x + c, y, x, y + c, color)
ssd.line(x + w - c - 1, y, x + w - 1, y + c, color)
ssd.line(x, y + h - c - 1, x + c, y + h - 1, color)
ssd.line(x + w - 1, y + h - c - 1, x + w - c - 1, y + h, color)
ssd.poly(0, 0, self.crect(x, y, w, h), self._getcolor(color))
def fill_clip_rect(self, x, y, w, h, color):
color = self._getcolor(color)
c = 4
ssd.fill_rect(x, y + c, w, h - 2 * c, color)
for z in range(c):
l = w - 2 * (c - z) # Line length
ssd.hline(x + c - z, y + z, l, color)
ssd.hline(x + c - z, y + h - z - 1, l, color)
ssd.poly(0, 0, self.crect(x, y, w, h), self._getcolor(color), True)
# Define an input device and populate global ssd and display objects.
class Display(DisplayIP):
def __init__(self, objssd, nxt, sel, prev=None, incr=None, decr=None,
encoder=False, touch=False):
def __init__(
self, objssd, nxt, sel, prev=None, incr=None, decr=None, encoder=False, touch=False
):
global display, ssd
ssd = objssd
if touch:
@ -421,9 +397,7 @@ class Screen:
self.width = ssd.width
self.row = 0
self.col = 0
if (
Screen.current_screen is None and Screen.do_gc
): # Initialising class and task
if Screen.current_screen is None and Screen.do_gc: # Initialising class and task
# Here we create singleton tasks
asyncio.create_task(self._garbage_collect())
Screen.current_screen = self
@ -541,8 +515,8 @@ class Screen:
# Very basic window class. Cuts a rectangular hole in a screen on which
# content may be drawn.
class Window(Screen):
_value = None
# Allow a Window to store an arbitrary object. Retrieval may be
# done by caller, after the Window instance was deleted
@classmethod
@ -659,7 +633,7 @@ class Widget:
self.def_bdcolor = bdcolor
# has_border is True if a border was drawn
self.has_border = False
self.callback = dolittle # Value change callback
self.callback = lambda *_: None # Value change callback
self.args = []
def warning(self):
@ -687,9 +661,7 @@ class Widget:
if hasattr(self, "label"):
self.label.value(text, invert, fgcolor, bgcolor, bdcolor)
else:
raise ValueError(
"Method {}.text does not exist.".format(self.__class__.__name__)
)
raise ValueError("Method {}.text does not exist.".format(self.__class__.__name__))
# Called from subclass prior to populating framebuf with control
def show(self, black=True):
@ -703,9 +675,7 @@ class Widget:
dev = display.usegrey(self._greyed_out)
x = self.col
y = self.row
dev.fill_rect(
x, y, self.width, self.height, color_map[BG] if black else self.bgcolor
)
dev.fill_rect(x, y, self.width, self.height, color_map[BG] if black else self.bgcolor)
return True
# Called by Screen.show(). Draw background and bounding box if required.
@ -808,9 +778,7 @@ class LinearIO(Widget):
):
self.min_delta = min_delta
self.max_delta = max_delta
super().__init__(
writer, row, col, height, width, fgcolor, bgcolor, bdcolor, value, active
)
super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bdcolor, value, active)
self.adjustable = True # Can show adjustable border
self.do_precision = prcolor is not False
if self.do_precision:
@ -825,9 +793,7 @@ class LinearIO(Widget):
# Handle increase and decrease buttons. Redefined by textbox.py, scale_log.py
async def btnhan(self, button, up, d):
maxd = (
self.max_delta if self.precision() else d * 4
) # Why move fast in precision mode?
maxd = self.max_delta if self.precision() else d * 4 # Why move fast in precision mode?
t = ticks_ms()
while button():
await asyncio.sleep_ms(0) # Quit fast on button release

Wyświetl plik

@ -32,7 +32,7 @@ class BaseScreen(Screen):
display.usegrey(False)
# Coordinates are x, y as per framebuf
# circle method is in Display class only
display.circle(70, 70, 30, RED, 2)
display.circle(70, 70, 30, RED)
# These methods exist in framebuf, so also in SSD and Display
ssd.hline(0, 127, 128, BLUE)
ssd.vline(127, 0, 128, BLUE)

Wyświetl plik

@ -132,6 +132,7 @@ class FooScreen(Screen):
litcolor=RED,
fgcolor=GREEN,
bgcolor=DARKGREEN,
shape=CLIPPED_RECT,
)
col = 2

Wyświetl plik

@ -0,0 +1,33 @@
# ili9341_FeatherS3.py Customise for your hardware config
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2023 Peter Hinch
# As written, supports:
# ili9341 240x320 displays on ESP32-S3 FeatherS3 board.
from machine import Pin, SPI
import gc
from drivers.ili93xx.ili9341 import ILI9341 as SSD
# Create and export an SSD instance
pdc = Pin(17, Pin.OUT, value=0) # Arbitrary pins borrowed from st7735r_esp32.py
prst = Pin(18, Pin.OUT, value=1)
pcs = Pin(14, Pin.OUT, value=1)
gc.collect() # Precaution before instantiating framebuf
spi = SPI(1, 30_000_000, sck=Pin(36), mosi=Pin(35), miso=Pin(37)) # No need to wire MISO.
gc.collect() # Precaution before instantiating framebuf
ssd = SSD(spi, cs=pcs, dc=pdc, rst=prst)
from gui.core.ugui import Display, Screen # Must perform this import after instantiating SSD (see other examples)
gc.collect() # Precaution before instantiating framebuf
# Create and export a Display instance
# Define control buttons
nxt = Pin(8, Pin.IN, Pin.PULL_UP) # Move to next control
sel = Pin(33, Pin.IN, Pin.PULL_UP) # Operate current control
prev = Pin(9, Pin.IN, Pin.PULL_UP) # Move to previous control
increase = Pin(38, Pin.IN, Pin.PULL_UP) # Increase control's value
decrease = Pin(1, Pin.IN, Pin.PULL_UP) # Decrease control's value
display = Display(ssd, nxt, sel, prev, increase, decrease)
Screen.do_gc = False