README needs updating.

pull/16/head
Peter Hinch 2022-05-21 18:56:04 +01:00
rodzic 447e87bad1
commit 3f7be4ebba
6 zmienionych plików z 245 dodań i 171 usunięć

152
README.md
Wyświetl plik

@ -109,39 +109,42 @@ a workround if it's impossible to upgrade. See
5.1 [Constructor](./README.md#51-constructor) 5.1 [Constructor](./README.md#51-constructor)
5.2 [Class method](./README.md#52-class-method) 5.2 [Class method](./README.md#52-class-method)
5.3 [Popup windows](./README.md#53-popup-windows) 5.3 [Popup windows](./README.md#53-popup-windows)
6. [Label widget](./README.md#6-label-widget) Single line text display. 6. [Widgets](./README.md#6-widgets) Displayable objects.
7. [LED widget](./README.md#7-led-widget) Display Boolean values. 6.1 [Label widget](./README.md#61-label-widget) Single line text display.
8. [Checkbox widget](./README.md#8-checkbox-widget) Enter Boolean values. 6.2 [LED widget](./README.md#62-led-widget) Display Boolean values.
9. [Button and CloseButton widgets](./README.md#9-button-and-closebutton-widgets) Pushbutton emulation. 6.3 [Checkbox widget](./README.md#63-checkbox-widget) Enter Boolean values.
10. [ButtonList object](./README.md#10-buttonlist-object) Pushbuttons with multiple states. 6.4 [Button and CloseButton widgets](./README.md#64-button-and-closebutton-widgets) Pushbutton emulation.
11. [RadioButtons object](./README.md#11-radiobuttons-object) One-of-N pushbuttons. 6.5 [ButtonList object](./README.md#65-buttonlist-object) Pushbuttons with multiple states.
12. [Listbox widget](./README.md#12-listbox-widget) 6.6 [RadioButtons object](./README.md#66-radiobuttons-object) One-of-N pushbuttons.
13. [Dropdown widget](./README.md#13-dropdown-widget) Dropdown lists. 6.7 [Listbox widget](./README.md#67-listbox-widget)
14. [DialogBox class](./README.md#14-dialogbox-class) Pop-up modal dialog boxes. 6.8 [Dropdown widget](./README.md#68-dropdown-widget) Dropdown lists.
15. [Textbox widget](./README.md#15-textbox-widget) Scrolling text display. 6.9 [DialogBox class](./README.md#69-dialogbox-class) Pop-up modal dialog boxes.
16. [Meter widget](./README.md#16-meter-widget) Display floats on an analog meter, with data driven callbacks. 6.10 [Textbox widget](./README.md#610-textbox-widget) Scrolling text display.
16.1 [Region class](./README.md#161-region-class) 6.11 [Meter widget](./README.md#611-meter-widget) Display floats on an analog meter, with data driven callbacks.
17. [Slider and HorizSlider widgets](./README.md#17-slider-and-horizslider-widgets) Linear potentiometer float data entry and display      6.11.1 [Region class](./README.md#161-region-class)
18. [Scale widget](./README.md#18-scale-widget) High precision float entry and display. 6.12 [Slider and HorizSlider widgets](./README.md#612-slider-and-horizslider-widgets) Linear potentiometer float data entry and display
19. [ScaleLog widget](./README.md#19-scalelog-widget) Wide dynamic range float entry and display. 6.13 [Scale widget](./README.md#613-scale-widget) High precision float entry and display.
20. [Dial widget](./README.md#20-dial-widget) Display multiple vectors. 6.14 [ScaleLog widget](./README.md#614-scalelog-widget) Wide dynamic range float entry and display.
21. [Knob widget](./README.md#21-knob-widget) Rotary potentiometer float entry. 6.15 [Dial widget](./README.md#615-dial-widget) Display multiple vectors.
22. [Adjuster widget](./README.md#22-adjuster-widget) Space saving way to enter floats. 6.16 [Knob widget](./README.md#616-knob-widget) Rotary potentiometer float entry.
23. [Menu class](./README.md#23-menu-class) 6.17 [Adjuster widget](./README.md#617-adjuster-widget) Space saving way to enter floats.
24. [Graph plotting](./README.md#24-graph-plotting) Widgets for Cartesian and polar graphs. 6.18 [Menu class](./README.md#618-menu-class)
24.1 [Concepts](./README.md#241-concepts) 6.19 [BitMap widget](./README.md#619-bitmap-widget) Draw bitmaps from files.__
     24.1.1 [Graph classes](./README.md#2411-graph-classes) 6.20 [QRMap widget](./README.md#620-qrmap-widget) Draw QR codes.__
     24.1.2 [Curve classes](./README.md#2412-curve-classes) 7. [Graph plotting](./README.md#7-graph-plotting) Widgets for Cartesian and polar graphs.
     24.1.3 [Coordinates](./README.md#2413-coordinates) 7.1 [Concepts](./README.md#71-concepts)
24.2 [Graph classes](./README.md#242-graph-classes)      7.1.1 [Graph classes](./README.md#711-graph-classes)
     24.2.1 [Class CartesianGraph](./README.md#2421-class-cartesiangraph)      7.1.2 [Curve classes](./README.md#712-curve-classes)
     24.2.2 [Class PolarGraph](./README.md#2422-class-polargraph)      7.1.3 [Coordinates](./README.md#713-coordinates)
24.3 [Curve classes](./README.md#243-curve-classes) 7.2 [Graph classes](./README.md#72-graph-classes)
     24.3.1 [Class Curve](./README.md#2431-class-curve)      7.2.1 [Class CartesianGraph](./README.md#721-class-cartesiangraph)
     24.3.2 [Class PolarCurve](./README.md#2432-class-polarcurve)      7.2.2 [Class PolarGraph](./README.md#722-class-polargraph)
24.4 [Class TSequence](./README.md#244-class-tsequence) Plotting realtime, time sequential data. 7.3 [Curve classes](./README.md#73-curve-classes)
25. [Old firmware](./README.md#25-old-firmware) For users of color displays who can't run current firmware.      7.3.1 [Class Curve](./README.md#731-class-curve)
26. [Realtime applications](./README.md#26-realtime-applications) Accommodating tasks requiring fast RT performance.      7.3.2 [Class PolarCurve](./README.md#732-class-polarcurve)
7.4 [Class TSequence](./README.md#74-class-tsequence) Plotting realtime, time sequential data.
8. [Old firmware](./README.md#8-old-firmware) For users of color displays who can't run current firmware.
9. [Realtime applications](./README.md#9-realtime-applications) Accommodating tasks requiring fast RT performance.
[Appendix 1 Application design](./README.md#appendix-1-application-design) Tab order, button layout, encoder interface, use of graphics primitives [Appendix 1 Application design](./README.md#appendix-1-application-design) Tab order, button layout, encoder interface, use of graphics primitives
# 1. Basic concepts # 1. Basic concepts
@ -960,7 +963,9 @@ constructor and is closed by issuing the `close()` static method.
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 6. Label widget # 6. Widgets
## 6.1 Label widget
```python ```python
from gui.widgets import Label from gui.widgets import Label
@ -1046,7 +1051,7 @@ Screen.change(BaseScreen)
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 7. LED widget ## 6.2 LED widget
```python ```python
from gui.widgets import LED from gui.widgets import LED
@ -1085,7 +1090,7 @@ controlled with `led(True)` or `led(False)`.
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 8. Checkbox widget ## 6.3 Checkbox widget
```python ```python
from gui.widgets import Checkbox from gui.widgets import Checkbox
@ -1126,7 +1131,7 @@ Methods:
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 9. Button and CloseButton widgets ## 6.4 Button and CloseButton widgets
```python ```python
from gui.core.colors import * # Colors and shapes from gui.core.colors import * # Colors and shapes
@ -1203,7 +1208,7 @@ Optional keyword only arguments:
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 10. ButtonList object ## 6.5 ButtonList object
```python ```python
from gui.core.colors import * # Colors and shapes from gui.core.colors import * # Colors and shapes
@ -1262,7 +1267,7 @@ for t in table: # Buttons overlay each other at same location
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 11. RadioButtons object ## 6.6 RadioButtons object
```python ```python
from gui.core.colors import * # Colors and shapes from gui.core.colors import * # Colors and shapes
@ -1310,7 +1315,7 @@ for t in table:
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 12. Listbox widget ## 6.7 Listbox widget
```python ```python
from gui.widgets import Listbox from gui.widgets import Listbox
@ -1419,7 +1424,7 @@ Screen.change(BaseScreen)
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 13. Dropdown widget ## 6.8 Dropdown widget
```python ```python
from gui.widgets import Dropdown from gui.widgets import Dropdown
@ -1534,7 +1539,7 @@ Screen.change(BaseScreen)
``` ```
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 14. DialogBox class ## 6.9 DialogBox class
```python ```python
from gui.widgets import DialogBox from gui.widgets import DialogBox
@ -1588,7 +1593,7 @@ in `gui/demos/screens.py`.
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 15. Textbox widget ## 6.10 Textbox widget
```python ```python
from gui.widgets import Textbox from gui.widgets import Textbox
@ -1655,7 +1660,7 @@ the oldest (topmost) being discarded as required.
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 16. Meter widget ## 6.11 Meter widget
This `passive` widget displays a single floating point value on a vertical This `passive` widget displays a single floating point value on a vertical
linear scale. Optionally it can support data dependent callbacks. linear scale. Optionally it can support data dependent callbacks.
@ -1749,7 +1754,7 @@ behaves similarly for data values between 0.9 and 1.0.
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
## 16.1 Region class ### 6.11.1 Region class
```python ```python
from gui.widgets import Region from gui.widgets import Region
@ -1809,7 +1814,7 @@ callbacks to run as appropriate.
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 17. Slider and HorizSlider widgets ## 6.12 Slider and HorizSlider widgets
```python ```python
from gui.widgets import Slider, HorizSlider from gui.widgets import Slider, HorizSlider
@ -1887,7 +1892,7 @@ around sliders to display all legends.
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 18. Scale widget ## 6.13 Scale widget
```python ```python
from gui.widgets import Scale from gui.widgets import Scale
@ -2024,7 +2029,7 @@ precision. Each visible division on the control represents 10 integer units.
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 19. ScaleLog widget ## 6.14 ScaleLog widget
```python ```python
from gui.widgets import ScaleLog from gui.widgets import ScaleLog
@ -2164,7 +2169,7 @@ def tickcb(f, c):
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 20. Dial widget ## 6.15 Dial widget
```python ```python
from gui.widgets import Dial, Pointer from gui.widgets import Dial, Pointer
@ -2280,7 +2285,7 @@ Screen.change(BaseScreen)
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 21. Knob widget ## 6.16 Knob widget
```python ```python
from gui.widgets import Knob from gui.widgets import Knob
@ -2338,7 +2343,7 @@ value changes. This enables dynamic color change.
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 22. Adjuster widget ## 6.17 Adjuster widget
```python ```python
from gui.widgets import Adjuster from gui.widgets import Adjuster
@ -2404,7 +2409,7 @@ basis. See code comments for further details.
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 23 Menu class ## 6.18 Menu class
```python ```python
from gui.widgets import Menu from gui.widgets import Menu
@ -2512,7 +2517,22 @@ different callback if the application required it.
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 24. Graph Plotting ## 6.19 BitMap Widget
This renders a monochrome bitmap stored in a file to a rectangular region. The
bitmap file format is C source code generated by the Linux `bitmap` editor. The
data may be rendered in any color. Data and colors can be changed at run time.
Not suitable for animation due to blocking time.
Question generator in coroutine.
###### [Contents](./README.md#0-contents)
## 6.20 QRMap Widget
###### [Contents](./README.md#0-contents)
# 7. Graph Plotting
```python ```python
from gui.widgets.graph import PolarGraph, PolarCurve, CartesianGraph, Curve, TSequence from gui.widgets.graph import PolarGraph, PolarCurve, CartesianGraph, Curve, TSequence
@ -2525,7 +2545,7 @@ from gui.widgets.graph import PolarGraph, PolarCurve, CartesianGraph, Curve, TSe
For example code see `gui/demos/plot.py`. For example code see `gui/demos/plot.py`.
## 24.1 Concepts ## 7.1 Concepts
Data for Cartesian graphs constitutes a sequence of x, y pairs, for polar Data for Cartesian graphs constitutes a sequence of x, y pairs, for polar
graphs it is a sequence of complex `z` values. The module supports three graphs it is a sequence of complex `z` values. The module supports three
@ -2535,13 +2555,13 @@ common cases:
3. One or more `y` values arrive gradually. The `X` axis represents time. This 3. One or more `y` values arrive gradually. The `X` axis represents time. This
is a simplifying case of 2. is a simplifying case of 2.
### 24.1.1 Graph classes ### 7.1.1 Graph classes
A user program first instantiates a graph object (`PolarGraph` or A user program first instantiates a graph object (`PolarGraph` or
`CartesianGraph`). This creates an empty graph image upon which one or more `CartesianGraph`). This creates an empty graph image upon which one or more
curves may be plotted. Graphs are passive widgets so cannot accept user input. curves may be plotted. Graphs are passive widgets so cannot accept user input.
### 24.1.2 Curve classes ### 7.1.2 Curve classes
The user program then instantiates one or more curves (`Curve` or The user program then instantiates one or more curves (`Curve` or
`PolarCurve`) as appropriate to the graph. Curves may be assigned colors to `PolarCurve`) as appropriate to the graph. Curves may be assigned colors to
@ -2556,7 +2576,7 @@ Where it is required to plot realtime data as it arrives, this is achieved
via calls to the curve's `point` method. If a prior point exists it causes a via calls to the curve's `point` method. If a prior point exists it causes a
line to be drawn connecting the point to the last one drawn. line to be drawn connecting the point to the last one drawn.
### 24.1.3 Coordinates ### 7.1.3 Coordinates
`PolarGraph` and `CartesianGraph` objects are subclassed from `Widget` and are `PolarGraph` and `CartesianGraph` objects are subclassed from `Widget` and are
positioned accordingly by `row` and `col` with a 2-pixel outside border. The positioned accordingly by `row` and `col` with a 2-pixel outside border. The
@ -2572,9 +2592,9 @@ unit circle but will be clipped to the rectangular graph boundary.
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
## 24.2 Graph classes ## 7.2 Graph classes
### 24.2.1 Class CartesianGraph ### 7.2.1 Class CartesianGraph
Constructor. Constructor.
Mandatory positional arguments: Mandatory positional arguments:
@ -2601,7 +2621,7 @@ Keyword only arguments (all optional):
Method: Method:
* `show` No args. Redraws the empty graph. Used when plotting time sequences. * `show` No args. Redraws the empty graph. Used when plotting time sequences.
### 24.2.2 Class PolarGraph ### 7.2.2 Class PolarGraph
Constructor. Constructor.
Mandatory positional arguments: Mandatory positional arguments:
@ -2626,9 +2646,9 @@ Method:
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
## 24.3 Curve classes ## 7.3 Curve classes
### 24.3.1 Class Curve ### 7.3.1 Class Curve
The Cartesian curve constructor takes the following positional arguments: The Cartesian curve constructor takes the following positional arguments:
@ -2668,7 +2688,7 @@ To plot x values from 1000 to 4000 we would set the `origin` x value to 1000
and the `excursion` x value to 3000. The `excursion` values scale the plotted and the `excursion` x value to 3000. The `excursion` values scale the plotted
values to fit the corresponding axis. values to fit the corresponding axis.
### 24.3.2 Class PolarCurve ### 7.3.2 Class PolarCurve
The constructor takes the following positional arguments: The constructor takes the following positional arguments:
@ -2702,7 +2722,7 @@ Complex points should lie within the unit circle to be drawn within the grid.
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
## 24.4 Class TSequence ## 7.4 Class TSequence
A common task is the acquisition and plotting of real time data against time, A common task is the acquisition and plotting of real time data against time,
such as hourly temperature and air pressure readings. This class facilitates such as hourly temperature and air pressure readings. This class facilitates
@ -2766,7 +2786,7 @@ class TSeq(Screen):
``` ```
###### [Contents](./README.md#0-contents) ###### [Contents](./README.md#0-contents)
# 25. Old firmware # 8. Old firmware
Current firmware is highly recommended. For users of color displays who cannot Current firmware is highly recommended. For users of color displays who cannot
run V1.17 or later it is possible to run under V1.15+. This involves copying run V1.17 or later it is possible to run under V1.15+. This involves copying
@ -2774,7 +2794,7 @@ run V1.17 or later it is possible to run under V1.15+. This involves copying
to `gui/core/writer.py`. This uses Python code to render text if the firmware to `gui/core/writer.py`. This uses Python code to render text if the firmware
or driver are unable to support fast rendering. or driver are unable to support fast rendering.
# 26. Realtime applications # 9. Realtime applications
Screen refresh is performed in a continuous loop with yields to the scheduler. Screen refresh is performed in a continuous loop with yields to the scheduler.
In normal applications this works well, however a significant proportion of In normal applications this works well, however a significant proportion of

Wyświetl plik

@ -1,20 +1,12 @@
# bitmap.py Minimal micro-gui demo. # bitmap.py Display a changing bitmap via the BitMap widget.
# Released under the MIT License (MIT). See LICENSE. # Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2022 Peter Hinch # Copyright (c) 2022 Peter Hinch
# hardware_setup must be imported before other modules because of RAM use. # hardware_setup must be imported before other modules because of RAM use.
import gc
import uasyncio as asyncio
import hardware_setup # Create a display instance import hardware_setup # Create a display instance
from gui.core.ugui import Screen, ssd from gui.core.ugui import Screen, ssd
from gui.widgets import Label, Button, CloseButton, BMG from gui.widgets import Label, Button, CloseButton, BitMap
# Create buffer for bitmapped graphic before fragmentation sets in
scale = 1
qr_ht = 100
qr_wd = 100
qr_buf = BMG.make_buffer(qr_ht, qr_wd)
gc.collect()
from gui.core.writer import CWriter from gui.core.writer import CWriter
import gui.fonts.arial10 as arial10 import gui.fonts.arial10 as arial10
from gui.core.colors import * from gui.core.colors import *
@ -29,19 +21,21 @@ class BaseScreen(Screen):
col = 2 col = 2
row = 2 row = 2
Label(wri, row, col, "Bitmap Demo.") Label(wri, row, col, "Bitmap Demo.")
row = 50 row = 25
self.graphic = BMG(wri, row, col, qr_ht, qr_wd, scale, fgcolor=WHITE, bgcolor=BLACK, buf=qr_buf) self.graphic = BitMap(wri, row, col, 99, 99, fgcolor=WHITE, bgcolor=BLACK)
#Button(wri, row, col, text="URL", callback=my_callback, args=(graphic, qr)) col = 120
asyncio.create_task(self.animate()) Button(wri, row, col, text="Next", callback=self.cb)
CloseButton(wri) # Quit the application CloseButton(wri) # Quit the application
self.image = 0
async def animate(self): def cb(self, _):
while True: self.graphic.value(f"/moon/m{self.image:02d}")
for n in range(13): self.image += 1
fn = f"/moon/m{n}.c" self.image %= 28
#print(fn) if self.image == 3:
await asyncio.sleep_ms(200) self.graphic.color(BLUE)
self.graphic.value(fn) else:
self.graphic.color(WHITE)
def test(): def test():
print("Bitmap demo.") print("Bitmap demo.")

Wyświetl plik

@ -8,12 +8,12 @@ import gc
import hardware_setup # Create a display instance import hardware_setup # Create a display instance
from uQR import QRCode from uQR import QRCode
from gui.core.ugui import Screen, ssd from gui.core.ugui import Screen, ssd
from gui.widgets import Label, Button, CloseButton, BMG from gui.widgets import Label, Button, CloseButton, QRMap
# Create buffer for bitmapped graphic before fragmentation sets in # Create buffer for bitmapped graphic before fragmentation sets in
scale = 3 # Magnification of graphic scale = 3 # Magnification of graphic
qr_ht = scale * 41 qr_ht = scale * 41
qr_wd = scale *41 qr_wd = scale * 41
qr_buf = BMG.make_buffer(qr_ht, qr_wd) qr_buf = QRMap.make_buffer(qr_ht, qr_wd)
gc.collect() gc.collect()
from gui.core.writer import CWriter from gui.core.writer import CWriter
import gui.fonts.arial10 as arial10 import gui.fonts.arial10 as arial10
@ -35,7 +35,7 @@ class BaseScreen(Screen):
row = 2 row = 2
Label(wri, row, col, "QR code Demo.") Label(wri, row, col, "QR code Demo.")
row = 50 row = 50
graphic = BMG(wri, row, col, qr_ht, qr_wd, scale, fgcolor=BLACK, bgcolor=WHITE, buf=qr_buf) graphic = QRMap(wri, row, col, (qr_ht, qr_wd), scale, fgcolor=BLACK, bgcolor=WHITE, buf=qr_buf)
qr = QRCode(version=4) # Gives 41x41 matrix qr = QRCode(version=4) # Gives 41x41 matrix
qr.add_data("uQR rocks!") qr.add_data("uQR rocks!")
graphic.value(qr.get_matrix()) graphic.value(qr.get_matrix())

Wyświetl plik

@ -23,7 +23,8 @@ _attrs = {
"Slider": "sliders", "Slider": "sliders",
"HorizSlider": "sliders", "HorizSlider": "sliders",
"Textbox": "textbox", "Textbox": "textbox",
"BMG": "bitmap", "BitMap": "bitmap",
"QRMap": "qrcode",
} }
# Lazy loader, effectively does: # Lazy loader, effectively does:

Wyświetl plik

@ -1,96 +1,49 @@
# bitmap.py Provides the BMG (bitmapped graphics) class # bitmap.py Provides the BMG (bitmapped graphics) class
# Released under the MIT License (MIT). See LICENSE. # Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2022 Peter Hinch # Copyright (c) 2022 Peter Hinch
import gc
from framebuf import FrameBuffer, MONO_HLSB # Graphics are files created by Linux bitmap utility.
# Widget writes data direct to the FrameBuffer.
# There is no scaling: declared size of the widget must exactly
# match the size of the bitmap.
from gui.core.ugui import Widget from gui.core.ugui import Widget
from gui.core.colors import * from gui.core.colors import *
from gui.core.ugui import ssd from gui.core.ugui import ssd
def rbit8(v):
v = (v & 0x0f) << 4 | (v & 0xf0) >> 4
v = (v & 0x33) << 2 | (v & 0xcc) >> 2
return (v & 0x55) << 1 | (v & 0xaa) >> 1
class BMG(Widget): class BitMap(Widget):
@staticmethod def __init__(self, writer, row, col, height, width, *, fgcolor=None, bgcolor=None, bdcolor=RED):
def make_buffer(height, width): super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bdcolor)
w = (width >> 3) + int(width & 7 > 0)
return bytearray(height * w)
def __init__(self, writer, row, col, height, width, scale=1, *, fgcolor=None, bgcolor=None, bdcolor=RED, buf=None):
super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bdcolor, False)
if buf is None:
buf = BMG.make_buffer(height, width)
self._fb = FrameBuffer(buf, width, height, MONO_HLSB)
self._scale = scale
self._buf = buf
def show(self): def show(self):
if super().show(True): # Draw or erase border if not super().show(True): # Draw or erase border
palette = ssd.palette return
palette.bg(self.bgcolor) if self._value is None:
palette.fg(self.fgcolor) return
ssd.blit(self._fb, self.col, self.row, -1, palette) with open(self._value, "r") as f:
g = self.gen_bytes(f)
bit = 1
wrap = False
for row in range(self.height):
if not wrap:
byte = next(g) # Each row starts on a new byte
bit = 1
for col in range(self.width):
c = self.fgcolor if byte & bit else self.bgcolor
ssd.pixel(self.col + col, self.row + row, c)
wrap = (bit := bit << 1) == 0x100
if wrap:
byte = next(g)
bit = 1
def color(self, fgcolor=None, bgcolor=None): def gen_bytes(self, f): # Yield data bytes from file stream
if fgcolor is not None: f.readline()
self.fgcolor = fgcolor f.readline() # Advance file pointer to data start
if bgcolor is not None:
self.bgcolor = bgcolor
self.draw = True
def value(self, obj):
if isinstance(obj, list): # 2d list of booleans
self._fb.fill(1)
s = self._scale
wd = len(obj[0])
ht = len(obj)
if wd * s > self.width or ht * s > self.height:
print('Object too large for buffer', wd * s, self.width, ht * s, self.height)
else:
print(f"Object is {wd} x {ht}")
for row in range(ht):
for col in range(wd):
v = obj[row][col]
for nc in range(s):
for nr in range(s):
self._fb.pixel(col * s + nc, row * s + nr, v)
elif isinstance(obj, str): # Assume filename
try:
with open(obj, "r") as f:
g = self.handle_stream(f)
n = 0
for x in g:
self._buf[n] = rbit8(x)
n += 1
except OSError:
print(f"Failed to input from {obj}")
self.draw = True
gc.collect()
# TODO graphic must be exactly the right size. Get dims from file in app, pass stream?
def handle_stream(self, f):
m = self._scale
s = f.readline()
elements = s.split(" ")
if elements[1].endswith("width"):
wd = int(elements[2])
else:
raise OSError
s = f.readline()
elements = s.split(" ")
if elements[1].endswith("height"):
ht = int(elements[2])
else:
raise OSError
if wd * m > self.width or ht * m > self.height:
print("Object too large for buffer", wd * m, self.width, ht * m, self.height)
raise OSError
s = f.readline() s = f.readline()
if not s.startswith("static"): if not s.startswith("static"):
raise OSError raise ValueError("Bad file format.")
while s := f.readline(): while s := f.readline():
if (lb := s.find("}")) != -1: if (lb := s.find("}")) != -1:
s = s[:lb] # Strip trailing }; s = s[:lb] # Strip trailing };
@ -98,3 +51,31 @@ class BMG(Widget):
for x in p: for x in p:
if x: if x:
yield int(x, 16) yield int(x, 16)
# Get height/width dimension from file stream.
def _get_dim(self, f, name):
s = f.readline()
elements = s.split(" ")
if not elements[1].endswith(name):
raise ValueError("Bad file format.")
return int(elements[2])
def _validate(self, fn):
if not isinstance(fn, str):
raise ValueError("Value must be a filename.")
with open(fn, "r") as f:
wd = self._get_dim(f, "width")
ht = self._get_dim(f, "height")
if not (wd == self.width and ht == self.height):
raise ValueError(f"Object dimensions {ht}x{wd} do not match widget {self.height}x{self.width}")
def value(self, fn):
self._validate(fn) # Throws on failure
super().value(fn)
def color(self, fgcolor=None, bgcolor=None):
if fgcolor is not None:
self.fgcolor = fgcolor
if bgcolor is not None:
self.bgcolor = bgcolor
self.draw = True

Wyświetl plik

@ -0,0 +1,78 @@
# qrcode.py Provides the QRMap widget to display the output of uQR library.
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2022 Peter Hinch
import gc
from framebuf import FrameBuffer, MONO_HLSB
from gui.core.ugui import Widget
from gui.core.colors import *
from gui.core.ugui import ssd
class QRMap(Widget):
@staticmethod
def make_buffer(height, width): # Given dimensions in pixels
w = (width >> 3) + int(width & 7 > 0)
return bytearray(height * w)
def __init__(self, writer, row, col, image, scale=1, *, fgcolor=None, bgcolor=None, bdcolor=RED, buf=None):
self._scale = scale
self._image = image
try:
height, width = self.dimensions()
except OSError:
print(f"Failed to access {obj}.")
raise
super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bdcolor, False)
if buf is None:
buf = QRMap.make_buffer(height, width)
else:
if len(buf) != ((width >> 3) + int(width & 7 > 0)) * height:
raise OSError("Buffer size does not match width and height.")
self._fb = FrameBuffer(buf, width, height, MONO_HLSB)
if isinstance(image, list):
self.value(image)
def show(self):
if super().show(True): # Draw or erase border
palette = ssd.palette
palette.bg(self.bgcolor)
palette.fg(self.fgcolor)
ssd.blit(self._fb, self.col, self.row, -1, palette)
def color(self, fgcolor=None, bgcolor=None):
if fgcolor is not None:
self.fgcolor = fgcolor
if bgcolor is not None:
self.bgcolor = bgcolor
self.draw = True
def dimensions(self): # Dimensions of current image in pixels
obj = self._image
if isinstance(obj, list): # 2d list of booleans
return len(obj) * self._scale, len(obj[0] * self._scale)
if isinstance(obj, tuple):
return obj
raise OSError
def value(self, obj):
self._image = obj
self._fb.fill(self.bgcolor) # In case tuple was passed or image smaller than buffer
if isinstance(obj, list): # 2d list of booleans
wd, ht = self.dimensions()
s = self._scale
if wd > self.width or ht > self.height:
print('Object too large for buffer', wd, self.width, ht, self.height)
else:
print(f"Object is {wd} x {ht} pixels")
for row in range(ht//s):
for col in range(wd//s):
v = obj[row][col]
for nc in range(s):
for nr in range(s):
self._fb.pixel(col * s + nc, row * s + nr, v)
else:
print(f"Invalid QR code {obj}.")
self.draw = True
gc.collect()