Scale widget added. Pass initial test.

ili9341
Peter Hinch 2020-11-03 18:43:24 +00:00
rodzic 5ec4948f91
commit dca99bf247
19 zmienionych plików z 859 dodań i 207 usunięć

Wyświetl plik

@ -0,0 +1,155 @@
# MicroPython SSD1306 OLED driver, I2C and SPI interfaces
from micropython import const
import framebuf
# register definitions
SET_CONTRAST = const(0x81)
SET_ENTIRE_ON = const(0xA4)
SET_NORM_INV = const(0xA6)
SET_DISP = const(0xAE)
SET_MEM_ADDR = const(0x20)
SET_COL_ADDR = const(0x21)
SET_PAGE_ADDR = const(0x22)
SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP = const(0xA0)
SET_MUX_RATIO = const(0xA8)
SET_COM_OUT_DIR = const(0xC0)
SET_DISP_OFFSET = const(0xD3)
SET_COM_PIN_CFG = const(0xDA)
SET_DISP_CLK_DIV = const(0xD5)
SET_PRECHARGE = const(0xD9)
SET_VCOM_DESEL = const(0xDB)
SET_CHARGE_PUMP = const(0x8D)
# Subclassing FrameBuffer provides support for graphics primitives
# http://docs.micropython.org/en/latest/pyboard/library/framebuf.html
class SSD1306(framebuf.FrameBuffer):
def __init__(self, width, height, external_vcc):
self.width = width
self.height = height
self.external_vcc = external_vcc
self.pages = self.height // 8
self.buffer = bytearray(self.pages * self.width)
super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)
self.init_display()
def init_display(self):
for cmd in (
SET_DISP | 0x00, # off
# address setting
SET_MEM_ADDR,
0x00, # horizontal
# resolution and layout
SET_DISP_START_LINE | 0x00,
SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
SET_MUX_RATIO,
self.height - 1,
SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
SET_DISP_OFFSET,
0x00,
SET_COM_PIN_CFG,
0x02 if self.width > 2 * self.height else 0x12,
# timing and driving scheme
SET_DISP_CLK_DIV,
0x80,
SET_PRECHARGE,
0x22 if self.external_vcc else 0xF1,
SET_VCOM_DESEL,
0x30, # 0.83*Vcc
# display
SET_CONTRAST,
0xFF, # maximum
SET_ENTIRE_ON, # output follows RAM contents
SET_NORM_INV, # not inverted
# charge pump
SET_CHARGE_PUMP,
0x10 if self.external_vcc else 0x14,
SET_DISP | 0x01,
): # on
self.write_cmd(cmd)
self.fill(0)
self.show()
def poweroff(self):
self.write_cmd(SET_DISP | 0x00)
def poweron(self):
self.write_cmd(SET_DISP | 0x01)
def contrast(self, contrast):
self.write_cmd(SET_CONTRAST)
self.write_cmd(contrast)
def invert(self, invert):
self.write_cmd(SET_NORM_INV | (invert & 1))
def show(self):
x0 = 0
x1 = self.width - 1
if self.width == 64:
# displays with width of 64 pixels are shifted by 32
x0 += 32
x1 += 32
self.write_cmd(SET_COL_ADDR)
self.write_cmd(x0)
self.write_cmd(x1)
self.write_cmd(SET_PAGE_ADDR)
self.write_cmd(0)
self.write_cmd(self.pages - 1)
self.write_data(self.buffer)
class SSD1306_I2C(SSD1306):
def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False):
self.i2c = i2c
self.addr = addr
self.temp = bytearray(2)
self.write_list = [b"\x40", None] # Co=0, D/C#=1
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.temp[0] = 0x80 # Co=1, D/C#=0
self.temp[1] = cmd
self.i2c.writeto(self.addr, self.temp)
def write_data(self, buf):
self.write_list[1] = buf
self.i2c.writevto(self.addr, self.write_list)
class SSD1306_SPI(SSD1306):
def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
self.rate = 10 * 1024 * 1024
dc.init(dc.OUT, value=0)
res.init(res.OUT, value=0)
cs.init(cs.OUT, value=1)
self.spi = spi
self.dc = dc
self.res = res
self.cs = cs
import time
self.res(1)
time.sleep_ms(1)
self.res(0)
time.sleep_ms(10)
self.res(1)
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs(1)
self.dc(0)
self.cs(0)
self.spi.write(bytearray([cmd]))
self.cs(1)
def write_data(self, buf):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs(1)
self.dc(1)
self.cs(0)
self.spi.write(buf)
self.cs(1)

15
gui/core/colors.py 100644
Wyświetl plik

@ -0,0 +1,15 @@
# colors.py Standard color constants for nano-gui
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2020 Peter Hinch
from drivers.ssd1351.ssd1351 import SSD1351 as SSD
GREEN = SSD.rgb(0, 255, 0)
RED = SSD.rgb(255, 0, 0)
BLUE = SSD.rgb(0, 0, 255)
YELLOW = SSD.rgb(255, 255, 0)
BLACK = 0
WHITE = SSD.rgb(255, 255, 255)
LIGHTGREEN = SSD.rgb(0, 100, 0)

Wyświetl plik

@ -177,190 +177,3 @@ class DObject():
self.label.value(text, invert, fgcolor, bgcolor, bdcolor) self.label.value(text, invert, fgcolor, bgcolor, bdcolor)
else: else:
raise ValueError('Attempt to update nonexistent label.') raise ValueError('Attempt to update nonexistent label.')
# text: str display string int save width
class Label(DObject):
def __init__(self, writer, row, col, text, invert=False, fgcolor=None, bgcolor=None, bdcolor=False):
# Determine width of object
if isinstance(text, int):
width = text
text = None
else:
width = writer.stringlen(text)
height = writer.height
super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bdcolor)
if text is not None:
self.value(text, invert)
def value(self, text=None, invert=False, fgcolor=None, bgcolor=None, bdcolor=None):
txt = super().value(text)
# Redraw even if no text supplied: colors may have changed.
self.invert = invert
self.fgcolor = self.def_fgcolor if fgcolor is None else fgcolor
self.bgcolor = self.def_bgcolor if bgcolor is None else bgcolor
if bdcolor is False:
self.def_bdcolor = False
self.bdcolor = self.def_bdcolor if bdcolor is None else bdcolor
self.show()
return txt
def show(self):
txt = super().value()
if txt is None: # No content to draw. Future use.
return
super().show() # Draw or erase border
wri = self.writer
dev = self.device
wri.setcolor(self.fgcolor, self.bgcolor)
Writer.set_textpos(dev, self.row, self.col)
wri.setcolor(self.fgcolor, self.bgcolor)
wri.printstring(txt, self.invert)
wri.setcolor() # Restore defaults
class Meter(DObject):
BAR = 1
LINE = 0
def __init__(self, writer, row, col, *, height=50, width=10,
fgcolor=None, bgcolor=None, ptcolor=None, bdcolor=None,
divisions=5, label=None, style=0, legends=None, value=None):
super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bdcolor)
self.divisions = divisions
if label is not None:
Label(writer, row + height + 3, col, label)
self.style = style
self.legends = legends
self.ptcolor = ptcolor if ptcolor is not None else self.fgcolor
self.value(value)
def value(self, n=None, color=None):
if n is None:
return super().value()
n = super().value(min(1, max(0, n)))
if color is not None:
self.ptcolor = color
self.show()
return n
def show(self):
super().show() # Draw or erase border
val = super().value()
wri = self.writer
dev = self.device
width = self.width
height = self.height
legends = self.legends
x0 = self.col
x1 = self.col + width
y0 = self.row
y1 = self.row + height
if self.divisions > 0:
dy = height / (self.divisions) # Tick marks
for tick in range(self.divisions + 1):
ypos = int(y0 + dy * tick)
dev.hline(x0 + 2, ypos, x1 - x0 - 4, self.fgcolor)
if legends is not None: # Legends
dy = 0 if len(legends) <= 1 else height / (len(legends) -1)
yl = y1 - wri.height / 2 # Start at bottom
for legend in legends:
Label(wri, int(yl), x1 + 4, legend)
yl -= dy
y = int(y1 - val * height) # y position of slider
if self.style == self.LINE:
dev.hline(x0, y, width, self.ptcolor) # Draw pointer
else:
w = width / 2
dev.fill_rect(int(x0 + w - 2), y, 4, y1 - y, self.ptcolor)
class LED(DObject):
def __init__(self, writer, row, col, *, height=12,
fgcolor=None, bgcolor=None, bdcolor=None, label=None):
super().__init__(writer, row, col, height, height, fgcolor, bgcolor, bdcolor)
if label is not None:
self.label = Label(writer, row + height + 3, col, label)
self.radius = self.height // 2
def color(self, c=None):
self.fgcolor = self.bgcolor if c is None else c
self.show()
def show(self):
super().show()
wri = self.writer
dev = self.device
r = self.radius
fillcircle(dev, self.col + r, self.row + r, r, self.fgcolor)
if isinstance(self.bdcolor, int):
circle(dev, self.col + r, self.row + r, r, self.bdcolor)
class Pointer():
def __init__(self, dial):
self.dial = dial
self.val = 0 + 0j
self.color = None
def value(self, v=None, color=None):
self.color = color
if v is not None:
if isinstance(v, complex):
l = cmath.polar(v)[0]
if l > 1:
self.val = v/l
else:
self.val = v
else:
raise ValueError('Pointer value must be complex.')
self.dial.vectors.add(self)
self.dial._set_pend(self.dial) # avoid redrawing for each vector
return self.val
class Dial(DObject):
CLOCK = 0
COMPASS = 1
def __init__(self, writer, row, col, *, height=50,
fgcolor=None, bgcolor=None, bdcolor=False, ticks=4,
label=None, style=0, pip=None):
super().__init__(writer, row, col, height, height, fgcolor, bgcolor, bdcolor)
self.style = style
self.pip = self.fgcolor if pip is None else pip
if label is not None:
self.label = Label(writer, row + height + 3, col, label)
radius = int(height / 2)
self.radius = radius
self.ticks = ticks
self.xorigin = col + radius
self.yorigin = row + radius
self.vectors = set()
def show(self):
super().show()
# cache bound variables
dev = self.device
ticks = self.ticks
radius = self.radius
xo = self.xorigin
yo = self.yorigin
# vectors (complex)
vor = xo + 1j * yo
vtstart = 0.9 * radius + 0j # start of tick
vtick = 0.1 * radius + 0j # tick
vrot = cmath.exp(2j * cmath.pi/ticks) # unit rotation
for _ in range(ticks):
polar(dev, vor + conj(vtstart), vtick, self.fgcolor)
vtick *= vrot
vtstart *= vrot
circle(dev, xo, yo, radius, self.fgcolor)
vshort = 1000 # Length of shortest vector
for v in self.vectors:
color = self.fgcolor if v.color is None else v.color
val = v.value() * radius # val is complex
vshort = min(vshort, cmath.polar(val)[0])
if self.style == Dial.CLOCK:
polar(dev, vor, val, color)
else:
arrow(dev, vor, val, 5, color)
if isinstance(self.pip, int) and vshort > 5:
fillcircle(dev, xo, yo, 2, self.pip)

Wyświetl plik

@ -17,7 +17,9 @@
# Initialise hardware # Initialise hardware
from ssd1351_setup import ssd, height # Create a display instance from ssd1351_setup import ssd, height # Create a display instance
from gui.core.nanogui import Dial, Pointer, refresh, Label from gui.core.nanogui import refresh
from gui.widgets.label import Label
from gui.widgets.dial import Dial, Pointer
refresh(ssd) # Initialise and clear display. refresh(ssd) # Initialise and clear display.
# Now import other modules # Now import other modules

Wyświetl plik

@ -24,7 +24,8 @@ import gc
# Initialise hardware # Initialise hardware
from ssd1351_setup import ssd # Create a display instance from ssd1351_setup import ssd # Create a display instance
from gui.core.nanogui import Dial, Pointer, refresh from gui.core.nanogui import refresh
from gui.widgets.dial import Dial, Pointer
refresh(ssd) # Initialise and clear display. refresh(ssd) # Initialise and clear display.
# Now import other modules # Now import other modules

Wyświetl plik

@ -13,7 +13,10 @@ import uasyncio as asyncio
import pyb import pyb
import uos import uos
from gui.core.writer import CWriter from gui.core.writer import CWriter
from gui.core.nanogui import LED, Meter, refresh from gui.core.nanogui import refresh
from gui.widgets.led import LED
from gui.widgets.meter import Meter
refresh(ssd) refresh(ssd)
# Fonts # Fonts

Wyświetl plik

@ -13,7 +13,10 @@ import uasyncio as asyncio
import pyb import pyb
import uos import uos
from gui.core.writer import CWriter from gui.core.writer import CWriter
from gui.core.nanogui import LED, Meter, refresh from gui.core.nanogui import refresh
from gui.widgets.led import LED
from gui.widgets.meter import Meter
refresh(ssd) refresh(ssd)
# Fonts # Fonts

Wyświetl plik

@ -12,7 +12,11 @@ import cmath
import utime import utime
import uos import uos
from gui.core.writer import Writer, CWriter from gui.core.writer import Writer, CWriter
from gui.core.nanogui import Label, Meter, LED, Dial, Pointer, refresh from gui.core.nanogui import refresh
from gui.widgets.led import LED
from gui.widgets.meter import Meter
from gui.widgets.label import Label
from gui.widgets.dial import Dial, Pointer
# Fonts # Fonts
import gui.fonts.arial10 as arial10 import gui.fonts.arial10 as arial10
@ -149,6 +153,7 @@ def compass(x):
refresh(ssd) refresh(ssd)
print('Color display test is running.') print('Color display test is running.')
print('Test runs to completion.')
clock(70) clock(70)
compass(70) compass(70)
meter() meter()

Wyświetl plik

@ -7,7 +7,11 @@
from ssd1351_setup import ssd # Create a display instance from ssd1351_setup import ssd # Create a display instance
from gui.core.nanogui import Label, Meter, LED, refresh from gui.core.nanogui import refresh
from gui.widgets.led import LED
from gui.widgets.meter import Meter
from gui.widgets.label import Label
refresh(ssd) refresh(ssd)
# Fonts # Fonts
import gui.fonts.arial10 as arial10 import gui.fonts.arial10 as arial10
@ -96,6 +100,7 @@ def vari_fields():
refresh(ssd) refresh(ssd)
print('Color display test is running.') print('Color display test is running.')
print('Test runs to completion.')
meter() meter()
multi_fields(t = 10) multi_fields(t = 10)
vari_fields() vari_fields()

Wyświetl plik

@ -6,19 +6,6 @@
# Released under the MIT License (MIT). See LICENSE. # Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2018-2020 Peter Hinch # Copyright (c) 2018-2020 Peter Hinch
# WIRING (Adafruit pin nos and names)
# Pyb SSD
# 3v3 Vin (10)
# Gnd Gnd (11)
# X1 DC (3 DC)
# X2 CS (5 OC OLEDCS)
# X3 Rst (4 R RESET)
# X6 CLK (2 CL SCK)
# X8 DATA (1 SI MOSI)
height = 96 # 1.27 inch 96*128 (rows*cols) display
# height = 128 # 1.5 inch 128*128 display
from ssd1351_setup import ssd # Create a display instance from ssd1351_setup import ssd # Create a display instance
import cmath import cmath
@ -27,7 +14,10 @@ import utime
import uos import uos
from gui.core.writer import Writer, CWriter from gui.core.writer import Writer, CWriter
from gui.core.fplot import PolarGraph, PolarCurve, CartesianGraph, Curve, TSequence from gui.core.fplot import PolarGraph, PolarCurve, CartesianGraph, Curve, TSequence
from gui.core.nanogui import Label, refresh from gui.core.nanogui import refresh
from gui.widgets.label import Label
refresh(ssd) refresh(ssd)
# Fonts # Fonts
@ -163,6 +153,7 @@ def seq():
refresh(ssd) refresh(ssd)
utime.sleep_ms(100) utime.sleep_ms(100)
print('Test runs to completion.')
seq() seq()
utime.sleep(1.5) utime.sleep(1.5)
liss() liss()

59
gui/demos/scale.py 100644
Wyświetl plik

@ -0,0 +1,59 @@
# scale.py Test/demo of scale widget for nano-gui
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2020 Peter Hinch
# Usage:
# import gui.demos.scale
# Initialise hardware
from ssd1351_setup import ssd # Create a display instance
from gui.core.nanogui import refresh
from gui.core.writer import CWriter
import uasyncio as asyncio
from gui.core.colors import *
import gui.fonts.arial10 as arial10
from gui.widgets.label import Label
from gui.widgets.scale import Scale
# COROUTINES
async def radio(scale):
cv = 88.0 # Current value
val = 108.0 # Target value
while True:
v1, v2 = val, cv
steps = 200
delta = (val - cv) / steps
for _ in range(steps):
cv += delta
# Map user variable to -1.0..+1.0
scale.value(2 * (cv - 88)/(108 - 88) - 1)
await asyncio.sleep_ms(200)
val, cv = v2, v1
async def default(scale, lbl):
cv = -1.0 # Current
val = 1.0
while True:
v1, v2 = val, cv
steps = 400
delta = (val - cv) / steps
for _ in range(steps):
cv += delta
scale.value(cv)
lbl.value('{:4.3f}'.format(cv))
refresh(ssd)
await asyncio.sleep_ms(250)
val, cv = v2, v1
def test():
refresh(ssd) # Initialise and clear display.
CWriter.set_textpos(ssd, 0, 0) # In case previous tests have altered it
wri = CWriter(ssd, arial10, GREEN, BLACK, verbose=False)
wri.set_clip(True, True, False)
lbl = Label(wri, ssd.height - wri.height - 2, 0, 50)
scale = Scale(wri, 5, 5)
asyncio.run(default(scale, lbl))
test()

229
gui/fonts/font10.py 100644
Wyświetl plik

@ -0,0 +1,229 @@
# Code generated by font-to-py.py.
# Font: FreeSans.ttf
version = '0.1'
def height():
return 17
def max_width():
return 17
def hmap():
return True
def reverse():
return False
def monospaced():
return False
_font =\
b'\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
b'\x00\x00\x00\x06\x00\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x80'\
b'\x00\xc0\x00\x00\x00\x00\x06\x00\x00\xf0\xf0\xf0\xa0\x00\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x19'\
b'\x00\x19\x00\x13\x00\x7f\x80\x12\x00\x32\x00\x32\x00\xff\x80\x26'\
b'\x00\x24\x00\x64\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x10'\
b'\x00\x3c\x00\x56\x00\xd3\x00\xd3\x00\xd0\x00\xd0\x00\x3c\x00\x17'\
b'\x00\x13\x00\xd3\x00\xd6\x00\x7c\x00\x10\x00\x00\x00\x00\x00\x00'\
b'\x00\x0f\x00\x00\x00\x78\x20\xcc\x40\xcc\x80\xcc\x80\xc9\x00\x31'\
b'\x00\x02\x78\x04\xcc\x04\xcc\x08\xcc\x08\xcc\x10\x78\x00\x00\x00'\
b'\x00\x00\x00\x00\x00\x0b\x00\x00\x00\x1e\x00\x33\x00\x33\x00\x33'\
b'\x00\x1e\x00\x18\x00\x74\xc0\xe6\xc0\xc3\x80\xc1\x80\xe3\x80\x3c'\
b'\x40\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\xc0\xc0\xc0\x80'\
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x10\x20'\
b'\x20\x60\x40\xc0\xc0\xc0\xc0\xc0\xc0\x40\x60\x20\x30\x10\x00\x06'\
b'\x00\x80\xc0\x40\x60\x20\x30\x30\x30\x30\x30\x30\x20\x60\x40\xc0'\
b'\x80\x00\x07\x00\x20\xa8\x70\x50\x50\x00\x00\x00\x00\x00\x00\x00'\
b'\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
b'\x00\x00\x00\x30\x00\x30\x00\x30\x00\xfc\x00\x30\x00\x30\x00\x30'\
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\x00\xc0\x40\x40\x80\x00\x06\x00\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x04'\
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\x00\x00'\
b'\x00\x00\x05\x00\x08\x08\x10\x10\x10\x20\x20\x20\x40\x40\x40\x80'\
b'\x80\x00\x00\x00\x00\x09\x00\x00\x00\x3c\x00\x66\x00\x42\x00\xc3'\
b'\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\x42\x00\x66\x00\x3c'\
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x10\x00\x30'\
b'\x00\xf0\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30\x00\x30'\
b'\x00\x30\x00\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00'\
b'\x00\x3c\x00\x66\x00\xc3\x00\xc3\x00\x03\x00\x06\x00\x0c\x00\x38'\
b'\x00\x60\x00\x40\x00\xc0\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00'\
b'\x00\x09\x00\x00\x00\x7c\x00\xe7\x00\xc3\x00\x03\x00\x02\x00\x1c'\
b'\x00\x07\x00\x03\x00\x03\x00\xc3\x00\xe6\x00\x3c\x00\x00\x00\x00'\
b'\x00\x00\x00\x00\x00\x09\x00\x00\x00\x0c\x00\x0c\x00\x1c\x00\x2c'\
b'\x00\x2c\x00\x4c\x00\x8c\x00\x8c\x00\xfe\x00\x0c\x00\x0c\x00\x0c'\
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x7e\x00\x40'\
b'\x00\x40\x00\x80\x00\xbc\x00\xe6\x00\x03\x00\x03\x00\x03\x00\xc3'\
b'\x00\x66\x00\x3c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00'\
b'\x00\x3c\x00\x66\x00\x43\x00\xc0\x00\xc0\x00\xfc\x00\xe6\x00\xc3'\
b'\x00\xc3\x00\xc3\x00\x66\x00\x3c\x00\x00\x00\x00\x00\x00\x00\x00'\
b'\x00\x09\x00\x00\x00\xff\x00\x03\x00\x02\x00\x06\x00\x04\x00\x0c'\
b'\x00\x08\x00\x18\x00\x18\x00\x10\x00\x30\x00\x30\x00\x00\x00\x00'\
b'\x00\x00\x00\x00\x00\x09\x00\x00\x00\x3c\x00\x66\x00\xc3\x00\xc3'\
b'\x00\x66\x00\x3c\x00\x66\x00\xc3\x00\xc3\x00\xc3\x00\x66\x00\x3c'\
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x3c\x00\x66'\
b'\x00\xc3\x00\xc3\x00\xc3\x00\x67\x00\x3b\x00\x03\x00\x03\x00\xc2'\
b'\x00\x66\x00\x3c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00'\
b'\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00'\
b'\x04\x00\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\xc0\x40'\
b'\x40\x80\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03'\
b'\x00\x0e\x00\x38\x00\xc0\x00\xe0\x00\x38\x00\x07\x00\x01\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\xff\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\x00\xc0\x00\x70\x00\x1c\x00\x03\x00\x07'\
b'\x00\x1c\x00\xe0\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09'\
b'\x00\x3c\x00\xc7\x00\xc3\x00\x03\x00\x03\x00\x06\x00\x0c\x00\x08'\
b'\x00\x18\x00\x18\x00\x00\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00'\
b'\x00\x00\x00\x11\x00\x07\xe0\x00\x0c\x38\x00\x30\x0c\x00\x20\x06'\
b'\x00\x63\xb7\x00\x4c\x73\x00\xcc\x63\x00\xd8\x63\x00\xd8\x63\x00'\
b'\xd8\x46\x00\xdc\xce\x00\x6f\x78\x00\x30\x00\x00\x18\x00\x00\x0f'\
b'\xe0\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x06\x00\x0e\x00\x0b\x00'\
b'\x1b\x00\x1b\x00\x11\x80\x31\x80\x31\x80\x3f\xc0\x60\xc0\x60\x40'\
b'\x40\x60\xc0\x60\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\xfe\x00'\
b'\xc3\x80\xc1\x80\xc1\x80\xc1\x80\xc3\x00\xfe\x00\xc1\x80\xc0\xc0'\
b'\xc0\xc0\xc0\xc0\xc1\x80\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
b'\x0c\x00\x1f\x80\x30\xc0\x60\x60\x40\x60\xc0\x00\xc0\x00\xc0\x00'\
b'\xc0\x00\xc0\x00\x40\x60\x60\x60\x30\xc0\x1f\x80\x00\x00\x00\x00'\
b'\x00\x00\x00\x00\x0c\x00\xff\x00\xc1\x80\xc0\xc0\xc0\x60\xc0\x60'\
b'\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\xc0\xc1\x80\xff\x00'\
b'\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\xff\x00\xc0\x00\xc0\x00'\
b'\xc0\x00\xc0\x00\xc0\x00\xff\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\
b'\xc0\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\xff\x00'\
b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xfe\x00\xc0\x00\xc0\x00'\
b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
b'\x0d\x00\x0f\xc0\x30\x60\x60\x30\x60\x00\xc0\x00\xc0\x00\xc1\xf0'\
b'\xc0\x30\xc0\x30\x60\x30\x60\x70\x30\xf0\x0f\x10\x00\x00\x00\x00'\
b'\x00\x00\x00\x00\x0c\x00\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\
b'\xc0\xc0\xff\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\
b'\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\xc0\xc0\xc0\xc0\xc0\xc0'\
b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00\x00\x00\x09\x00\x06\x00\x06'\
b'\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\xc6'\
b'\x00\xc6\x00\xc4\x00\x78\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b'\
b'\x00\xc0\xc0\xc1\x80\xc3\x00\xc6\x00\xcc\x00\xd8\x00\xfc\x00\xe6'\
b'\x00\xc6\x00\xc3\x00\xc1\x80\xc1\x80\xc0\xc0\x00\x00\x00\x00\x00'\
b'\x00\x00\x00\x0a\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0'\
b'\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xfe\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\x00\x0e\x00\xe0\x38\xe0\x38\xf0\x78\xf0'\
b'\x78\xd0\x58\xd8\xd8\xd8\xd8\xc8\x98\xcd\x98\xcd\x98\xc5\x18\xc7'\
b'\x18\xc7\x18\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\xe0\x60\xe0'\
b'\x60\xf0\x60\xd0\x60\xd8\x60\xcc\x60\xc4\x60\xc6\x60\xc3\x60\xc3'\
b'\x60\xc1\xe0\xc0\xe0\xc0\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x0d'\
b'\x00\x1f\x80\x30\xc0\x60\x60\xe0\x60\xc0\x30\xc0\x30\xc0\x30\xc0'\
b'\x30\xc0\x30\xe0\x60\x60\x60\x30\xc0\x1f\x80\x00\x00\x00\x00\x00'\
b'\x00\x00\x00\x0b\x00\xff\x00\xc1\x80\xc0\xc0\xc0\xc0\xc0\xc0\xc1'\
b'\x80\xff\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x1f\x80\x30\xc0\x60\x60\xe0'\
b'\x60\xc0\x30\xc0\x30\xc0\x30\xc0\x30\xc0\x30\xe1\x60\x61\xe0\x30'\
b'\xc0\x1f\xe0\x00\x20\x00\x00\x00\x00\x00\x00\x0c\x00\xff\x00\xc1'\
b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc1\x80\xff\x00\xc1\xc0\xc0\xc0\xc0'\
b'\xc0\xc0\xc0\xc0\xc0\xc0\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x0b'\
b'\x00\x3f\x00\x61\x80\xc0\xc0\xc0\x00\xc0\x00\x60\x00\x3e\x00\x07'\
b'\x80\x01\xc0\xc0\xc0\xc0\xc0\x61\x80\x3f\x00\x00\x00\x00\x00\x00'\
b'\x00\x00\x00\x0b\x00\xff\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18'\
b'\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\x00\x0c\x00\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\
b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x61'\
b'\x80\x3f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\xc0\x60\x40'\
b'\x40\x60\xc0\x60\xc0\x20\x80\x31\x80\x31\x80\x11\x00\x1b\x00\x0b'\
b'\x00\x0a\x00\x0e\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10'\
b'\x00\xc1\x83\xc1\x82\x42\x86\x62\xc6\x62\xc6\x62\x44\x24\x44\x24'\
b'\x6c\x34\x2c\x3c\x28\x18\x38\x18\x38\x18\x18\x00\x00\x00\x00\x00'\
b'\x00\x00\x00\x0b\x00\x60\x40\x20\xc0\x31\x80\x19\x00\x1b\x00\x0e'\
b'\x00\x06\x00\x0e\x00\x1b\x00\x11\x80\x31\x80\x60\xc0\x40\x60\x00'\
b'\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x40\x60\x60\x60\x30\xc0\x30'\
b'\xc0\x19\x80\x0d\x00\x0f\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06'\
b'\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\xff\x80\x01'\
b'\x80\x03\x00\x06\x00\x06\x00\x0c\x00\x18\x00\x18\x00\x30\x00\x60'\
b'\x00\x60\x00\xc0\x00\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x05'\
b'\x00\xe0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\
b'\xc0\xe0\x05\x00\x80\x80\x40\x40\x40\x20\x20\x20\x10\x10\x10\x08'\
b'\x08\x00\x00\x00\x00\x05\x00\xe0\x60\x60\x60\x60\x60\x60\x60\x60'\
b'\x60\x60\x60\x60\x60\x60\x60\xe0\x08\x00\x00\x30\x30\x78\x48\x48'\
b'\x84\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\x00\xff\xc0\x00\x00\x00\x00\x00\x00\x04'\
b'\x00\xc0\x40\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
b'\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7c\x00\xc6\x00'\
b'\x06\x00\x06\x00\x7e\x00\xc6\x00\xc6\x00\xce\x00\x77\x00\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\x09\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\
b'\xde\x00\xe3\x00\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xc1\x80\xe3\x00'\
b'\xde\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00'\
b'\x00\x00\x00\x00\x3c\x00\x66\x00\xc3\x00\xc0\x00\xc0\x00\xc0\x00'\
b'\xc3\x00\x66\x00\x3c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00'\
b'\x03\x00\x03\x00\x03\x00\x03\x00\x3b\x00\x67\x00\xc3\x00\xc3\x00'\
b'\xc3\x00\xc3\x00\xc3\x00\x67\x00\x3b\x00\x00\x00\x00\x00\x00\x00'\
b'\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3c\x00\x66\x00'\
b'\xc3\x00\xc3\x00\xff\x00\xc0\x00\xc3\x00\x66\x00\x3c\x00\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\x05\x00\x30\x60\x60\x60\xf0\x60\x60\x60'\
b'\x60\x60\x60\x60\x60\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00'\
b'\x00\x00\x00\x3b\x00\x67\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\xc3'\
b'\x00\x67\x00\x3b\x00\x03\x00\x03\x00\xc6\x00\x7c\x00\x09\x00\xc0'\
b'\x00\xc0\x00\xc0\x00\xc0\x00\xde\x00\xe3\x00\xc3\x00\xc3\x00\xc3'\
b'\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\x00\x00\x00\x00\x00\x00\x00'\
b'\x00\x04\x00\xc0\x00\x00\x00\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\
b'\x00\x00\x00\x00\x04\x00\x60\x00\x00\x00\x60\x60\x60\x60\x60\x60'\
b'\x60\x60\x60\x60\x60\x60\xc0\x09\x00\xc0\x00\xc0\x00\xc0\x00\xc0'\
b'\x00\xc6\x00\xcc\x00\xd8\x00\xf8\x00\xe8\x00\xcc\x00\xc6\x00\xc6'\
b'\x00\xc3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\xc0\xc0\xc0'\
b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00\x00\x00\x0e\x00'\
b'\x00\x00\x00\x00\x00\x00\x00\x00\xdd\xe0\xe7\x30\xc6\x30\xc6\x30'\
b'\xc6\x30\xc6\x30\xc6\x30\xc6\x30\xc6\x30\x00\x00\x00\x00\x00\x00'\
b'\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\x00\xe3\x00'\
b'\xc3\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00'\
b'\x3c\x00\x66\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\x66\x00'\
b'\x3c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x00\x00\x00'\
b'\x00\x00\x00\x00\xde\x00\xe3\x00\xc1\x80\xc1\x80\xc1\x80\xc1\x80'\
b'\xc1\x80\xe3\x00\xde\x00\xc0\x00\xc0\x00\xc0\x00\x00\x00\x0a\x00'\
b'\x00\x00\x00\x00\x00\x00\x00\x00\x3b\x00\x67\x00\xc3\x00\xc3\x00'\
b'\xc3\x00\xc3\x00\xc3\x00\x67\x00\x3b\x00\x03\x00\x03\x00\x03\x00'\
b'\x00\x00\x06\x00\x00\x00\x00\x00\xd8\xe0\xc0\xc0\xc0\xc0\xc0\xc0'\
b'\xc0\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x7c\xc6\xc0\xc0\x70'\
b'\x0e\xc6\xc6\x7c\x00\x00\x00\x00\x05\x00\x00\x00\x60\x60\xf0\x60'\
b'\x60\x60\x60\x60\x60\x60\x70\x00\x00\x00\x00\x09\x00\x00\x00\x00'\
b'\x00\x00\x00\x00\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\xc3\x00\xc3'\
b'\x00\xc3\x00\xc7\x00\x7b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08'\
b'\x00\x00\x00\x00\x00\xc3\x43\x62\x66\x26\x34\x3c\x18\x18\x00\x00'\
b'\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc6\x30\x46\x30'\
b'\x47\x20\x6f\x20\x69\x60\x29\x60\x29\xc0\x39\xc0\x10\xc0\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x42\x66\x34\x18'\
b'\x18\x1c\x24\x66\x43\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\xc3'\
b'\x42\x42\x66\x24\x24\x3c\x18\x18\x18\x10\x30\x60\x08\x00\x00\x00'\
b'\x00\x00\xfe\x0c\x08\x18\x30\x60\x40\xc0\xfe\x00\x00\x00\x00\x06'\
b'\x00\x30\x60\x60\x60\x60\x60\x60\xe0\xc0\xe0\x60\x60\x60\x60\x60'\
b'\x60\x30\x04\x00\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\
b'\xc0\xc0\xc0\xc0\x00\x06\x00\xc0\x60\x60\x60\x60\x60\x60\x70\x30'\
b'\x70\x60\x60\x60\x60\x60\x60\xc0\x09\x00\x00\x00\x00\x00\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\x62\x00\x9e\x00\x00\x00\x00\x00\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
_index =\
b'\x00\x00\x13\x00\x26\x00\x39\x00\x5d\x00\x81\x00\xa5\x00\xc9\x00'\
b'\xdc\x00\xef\x00\x02\x01\x15\x01\x39\x01\x4c\x01\x5f\x01\x72\x01'\
b'\x85\x01\xa9\x01\xcd\x01\xf1\x01\x15\x02\x39\x02\x5d\x02\x81\x02'\
b'\xa5\x02\xc9\x02\xed\x02\x00\x03\x13\x03\x37\x03\x5b\x03\x7f\x03'\
b'\xa3\x03\xd8\x03\xfc\x03\x20\x04\x44\x04\x68\x04\x8c\x04\xb0\x04'\
b'\xd4\x04\xf8\x04\x0b\x05\x2f\x05\x53\x05\x77\x05\x9b\x05\xbf\x05'\
b'\xe3\x05\x07\x06\x2b\x06\x4f\x06\x73\x06\x97\x06\xbb\x06\xdf\x06'\
b'\x03\x07\x27\x07\x4b\x07\x6f\x07\x82\x07\x95\x07\xa8\x07\xbb\x07'\
b'\xdf\x07\xf2\x07\x16\x08\x3a\x08\x5e\x08\x82\x08\xa6\x08\xb9\x08'\
b'\xdd\x08\x01\x09\x14\x09\x27\x09\x4b\x09\x5e\x09\x82\x09\xa6\x09'\
b'\xca\x09\xee\x09\x12\x0a\x25\x0a\x38\x0a\x4b\x0a\x6f\x0a\x82\x0a'\
b'\xa6\x0a\xb9\x0a\xcc\x0a\xdf\x0a\xf2\x0a\x05\x0b\x18\x0b\x3c\x0b'\
_mvfont = memoryview(_font)
def _chr_addr(ordch):
offset = 2 * (ordch - 32)
return int.from_bytes(_index[offset:offset + 2], 'little')
def get_ch(ch):
ordch = ord(ch)
ordch = ordch if ordch >= 32 and ordch <= 126 else ord('?')
offset = _chr_addr(ordch)
width = int.from_bytes(_font[offset:offset + 2], 'little')
next_offs = _chr_addr(ordch +1)
return _mvfont[offset + 2:next_offs], 17, width

Wyświetl plik

Wyświetl plik

@ -0,0 +1,78 @@
# dial.py Dial and Pointer classes for nano-gui
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2018-2020 Peter Hinch
import cmath
from gui.core.nanogui import DObject, polar, conj, circle, arrow, fillcircle
from gui.widgets.label import Label
class Pointer():
def __init__(self, dial):
self.dial = dial
self.val = 0 + 0j
self.color = None
def value(self, v=None, color=None):
self.color = color
if v is not None:
if isinstance(v, complex):
l = cmath.polar(v)[0]
if l > 1:
self.val = v/l
else:
self.val = v
else:
raise ValueError('Pointer value must be complex.')
self.dial.vectors.add(self)
self.dial._set_pend(self.dial) # avoid redrawing for each vector
return self.val
class Dial(DObject):
CLOCK = 0
COMPASS = 1
def __init__(self, writer, row, col, *, height=50,
fgcolor=None, bgcolor=None, bdcolor=False, ticks=4,
label=None, style=0, pip=None):
super().__init__(writer, row, col, height, height, fgcolor, bgcolor, bdcolor)
self.style = style
self.pip = self.fgcolor if pip is None else pip
if label is not None:
self.label = Label(writer, row + height + 3, col, label)
radius = int(height / 2)
self.radius = radius
self.ticks = ticks
self.xorigin = col + radius
self.yorigin = row + radius
self.vectors = set()
def show(self):
super().show()
# cache bound variables
dev = self.device
ticks = self.ticks
radius = self.radius
xo = self.xorigin
yo = self.yorigin
# vectors (complex)
vor = xo + 1j * yo
vtstart = 0.9 * radius + 0j # start of tick
vtick = 0.1 * radius + 0j # tick
vrot = cmath.exp(2j * cmath.pi/ticks) # unit rotation
for _ in range(ticks):
polar(dev, vor + conj(vtstart), vtick, self.fgcolor)
vtick *= vrot
vtstart *= vrot
circle(dev, xo, yo, radius, self.fgcolor)
vshort = 1000 # Length of shortest vector
for v in self.vectors:
color = self.fgcolor if v.color is None else v.color
val = v.value() * radius # val is complex
vshort = min(vshort, cmath.polar(val)[0])
if self.style == Dial.CLOCK:
polar(dev, vor, val, color)
else:
arrow(dev, vor, val, 5, color)
if isinstance(self.pip, int) and vshort > 5:
fillcircle(dev, xo, yo, 2, self.pip)

Wyświetl plik

@ -0,0 +1,45 @@
# label.py Label class for nano-gui
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2018-2020 Peter Hinch
from gui.core.nanogui import DObject
from gui.core.writer import Writer
# text: str display string int save width
class Label(DObject):
def __init__(self, writer, row, col, text, invert=False, fgcolor=None, bgcolor=None, bdcolor=False):
# Determine width of object
if isinstance(text, int):
width = text
text = None
else:
width = writer.stringlen(text)
height = writer.height
super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bdcolor)
if text is not None:
self.value(text, invert)
def value(self, text=None, invert=False, fgcolor=None, bgcolor=None, bdcolor=None):
txt = super().value(text)
# Redraw even if no text supplied: colors may have changed.
self.invert = invert
self.fgcolor = self.def_fgcolor if fgcolor is None else fgcolor
self.bgcolor = self.def_bgcolor if bgcolor is None else bgcolor
if bdcolor is False:
self.def_bdcolor = False
self.bdcolor = self.def_bdcolor if bdcolor is None else bdcolor
self.show()
return txt
def show(self):
txt = super().value()
if txt is None: # No content to draw. Future use.
return
super().show() # Draw or erase border
wri = self.writer
dev = self.device
Writer.set_textpos(dev, self.row, self.col)
wri.setcolor(self.fgcolor, self.bgcolor)
wri.printstring(txt, self.invert)
wri.setcolor() # Restore defaults

28
gui/widgets/led.py 100644
Wyświetl plik

@ -0,0 +1,28 @@
# led.py LED class for nano-gui
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2018-2020 Peter Hinch
from gui.core.nanogui import DObject, fillcircle, circle
from gui.widgets.label import Label
class LED(DObject):
def __init__(self, writer, row, col, *, height=12,
fgcolor=None, bgcolor=None, bdcolor=None, label=None):
super().__init__(writer, row, col, height, height, fgcolor, bgcolor, bdcolor)
if label is not None:
self.label = Label(writer, row + height + 3, col, label)
self.radius = self.height // 2
def color(self, c=None):
self.fgcolor = self.bgcolor if c is None else c
self.show()
def show(self):
super().show()
wri = self.writer
dev = self.device
r = self.radius
fillcircle(dev, self.col + r, self.row + r, r, self.fgcolor)
if isinstance(self.bdcolor, int):
circle(dev, self.col + r, self.row + r, r, self.bdcolor)

Wyświetl plik

@ -0,0 +1,63 @@
# meter.py Meter class for nano-gui
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2018-2020 Peter Hinch
from gui.core.nanogui import DObject
from gui.widgets.label import Label
class Meter(DObject):
BAR = 1
LINE = 0
def __init__(self, writer, row, col, *, height=50, width=10,
fgcolor=None, bgcolor=None, ptcolor=None, bdcolor=None,
divisions=5, label=None, style=0, legends=None, value=None):
super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bdcolor)
self.divisions = divisions
if label is not None:
Label(writer, row + height + 3, col, label)
self.style = style
self.legends = legends
self.ptcolor = ptcolor if ptcolor is not None else self.fgcolor
self.value(value)
def value(self, n=None, color=None):
if n is None:
return super().value()
n = super().value(min(1, max(0, n)))
if color is not None:
self.ptcolor = color
self.show()
return n
def show(self):
super().show() # Draw or erase border
val = super().value()
wri = self.writer
dev = self.device
width = self.width
height = self.height
legends = self.legends
x0 = self.col
x1 = self.col + width
y0 = self.row
y1 = self.row + height
if self.divisions > 0:
dy = height / (self.divisions) # Tick marks
for tick in range(self.divisions + 1):
ypos = int(y0 + dy * tick)
dev.hline(x0 + 2, ypos, x1 - x0 - 4, self.fgcolor)
if legends is not None: # Legends
dy = 0 if len(legends) <= 1 else height / (len(legends) -1)
yl = y1 - wri.height / 2 # Start at bottom
for legend in legends:
Label(wri, int(yl), x1 + 4, legend)
yl -= dy
y = int(y1 - val * height) # y position of slider
if self.style == self.LINE:
dev.hline(x0, y, width, self.ptcolor) # Draw pointer
else:
w = width / 2
dev.fill_rect(int(x0 + w - 2), y, 4, y1 - y, self.ptcolor)

Wyświetl plik

@ -0,0 +1,123 @@
# scale.py Extension to nano-gui providing the Scale class
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2020 Peter Hinch
# Usage:
# from gui.widgets.scale import Scale
# NEED print_left, get_stringsize
from gui.core.nanogui import DObject
from gui.core.writer import Writer
from gui.core.colors import BLACK
class Scale(DObject):
def __init__(self, writer, row, col, *,
ticks=200, legendcb=None, tickcb=None,
height=0, width=100, border=2, fgcolor=None, bgcolor=None,
pointercolor=None, fontcolor=None):
if ticks % 2:
raise ValueError('ticks arg must be divisible by 2')
self.ticks = ticks
self.tickcb = tickcb
def lcb(f):
return '{:3.1f}'.format(f)
self.legendcb = legendcb if legendcb is not None else lcb
bgcolor = BLACK if bgcolor is None else bgcolor
text_ht = writer.font.height()
ctrl_ht = 12 # Minimum height for ticks
min_ht = text_ht + 2 * border + 2 # Ht of text, borders and gap between text and ticks
if height < min_ht + ctrl_ht:
height = min_ht + ctrl_ht # min workable height
else:
ctrl_ht = height - min_ht # adjust ticks for greater height
width &= 0xfffe # Make divisible by 2: avoid 1 pixel pointer offset
super().__init__(writer, row, col, height, width, fgcolor, bgcolor, fgcolor)
self.x0 = col + border
self.x1 = col + self.width - border
self.y0 = row + border
self.y1 = row + self.height - border
self.ptrcolor = pointercolor if pointercolor is not None else self.fgcolor
# Define tick dimensions
ytop = self.y0 + text_ht + 2 # Top of scale graphic (2 pixel gap)
ycl = ytop + (self.y1 - ytop) // 2 # Centre line
self.sdl = round(ctrl_ht * 1 / 3) # Length of small tick.
self.sdy0 = ycl - self.sdl // 2
self.mdl = round(ctrl_ht * 2 / 3) # Medium tick
self.mdy0 = ycl - self.mdl // 2
self.ldl = ctrl_ht # Large tick
self.ldy0 = ycl - self.ldl // 2
def show(self):
wri = self.writer
dev = self.device
x0: int = self.x0 # Internal rectangle occupied by scale and text
x1: int = self.x1
y0: int = self.y0
y1: int = self.y1
dev.fill_rect(x0, y0, x1 - x0, y1 - y0, self.bgcolor)
super().show()
# Scale is drawn using ints. Each division is 10 units.
val: int = self._value # 0..ticks*10
# iv increments for each tick. Its value modulo N determines tick length
iv: int # val / 10 at a tick position
d: int # val % 10: offset relative to a tick position
fx: int # X offset of current tick in value units
if val >= 100: # Whole LHS of scale will be drawn
iv, d = divmod(val - 100, 10) # Initial value
fx = 10 - d
iv += 1
else: # Scale will scroll right
iv = 0
fx = 100 - val
# Window shows 20 divisions, each of which corresponds to 10 units of value.
# So pixels per unit value == win_width/200
win_width: int = x1 - x0
ticks: int = self.ticks # Total # of ticks visible and hidden
while True:
x: int = x0 + (fx * win_width) // 200 # Current X position
ys: int # Start Y position for tick
yl: int # tick length
if x > x1 or iv > ticks: # Out of space or data (scroll left)
break
if not iv % 10:
txt = self.legendcb(self._fvalue(iv * 10))
tlen = wri.stringlen(txt)
Writer.set_textpos(dev, y0, min(x, x1 - tlen))
wri.setcolor(self.fgcolor, self.bgcolor)
wri.printstring(txt)
wri.setcolor()
ys = self.ldy0 # Large tick
yl = self.ldl
elif not iv % 5:
ys = self.mdy0
yl = self.mdl
else:
ys = self.sdy0
yl = self.sdl
if self.tickcb is None:
color = self.fgcolor
else:
color = self.tickcb(self._fvalue(iv * 10), self.fgcolor)
dev.vline(x, ys, yl, color) # Draw tick
fx += 10
iv += 1
dev.vline(x0 + (x1 - x0) // 2, y0, y1 - y0, self.ptrcolor) # Draw pointer
def _to_int(self, v):
return round((v + 1.0) * self.ticks * 5) # 0..self.ticks*10
def _fvalue(self, v=None):
return v / (5 * self.ticks) - 1.0
def value(self, val=None): # User method to get or set value
if val is not None:
val = min(max(val, - 1.0), 1.0)
v = self._to_int(val)
if v != self._value:
self._value = v
self.show()
return self._fvalue(self._value)

34
ssd1351_setup.py 100644
Wyświetl plik

@ -0,0 +1,34 @@
# ssd1351_setup.py Customise for your hardware config
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2020 Peter Hinch
# Demo of initialisation procedure designed to minimise risk of memory fail
# when instantiating the frame buffer. The aim is to do this as early as
# possible before importing other modules.
# WIRING (Adafruit pin nos and names)
# Pyb SSD
# 3v3 Vin (10)
# Gnd Gnd (11)
# Y1 DC (3 DC)
# Y2 CS (5 OC OLEDCS)
# Y3 Rst (4 R RESET)
# Y6 CLK (2 CL SCK)
# Y8 DATA (1 SI MOSI)
height = 96 # 1.27 inch 96*128 (rows*cols) display
import machine
import gc
from drivers.ssd1351.ssd1351 import SSD1351 as SSD
height = 96 # 1.27 inch 96*128 (rows*cols) display
# height = 128 # 1.5 inch 128*128 display
pdc = machine.Pin('Y1', machine.Pin.OUT_PP, value=0)
pcs = machine.Pin('Y2', machine.Pin.OUT_PP, value=1)
prst = machine.Pin('Y3', machine.Pin.OUT_PP, value=1)
spi = machine.SPI(2)
gc.collect() # Precaution before instantiating framebuf
ssd = SSD(spi, pcs, pdc, prst, height) # Create a display instance