kopia lustrzana https://github.com/peterhinch/micropython-nano-gui
grid widget: Add addressing options.
rodzic
f6365fc76d
commit
417716dcc8
|
@ -67,10 +67,35 @@ Constructor args:
|
||||||
|
|
||||||
Methods:
|
Methods:
|
||||||
* `show` Draw the grid lines to the framebuffer.
|
* `show` Draw the grid lines to the framebuffer.
|
||||||
* `__getitem__` This enables an individual `Label`'s `value` method to be
|
* `__getitem__` Return a list containing one or more `Label` instances.
|
||||||
retrieved using index notation. The args detailed above enable inividual cells
|
* `__setitem__` Assign a value to one or more labels. If multiple labels are
|
||||||
to be updated.
|
specified and a single text value is passed, all labels will receive that
|
||||||
|
value. If an iterator is passed, consecutive labels will receive values from
|
||||||
|
the iterator. If the iterator runs out of data, the last value will be
|
||||||
|
repeated.
|
||||||
|
|
||||||
|
Addressing:
|
||||||
|
The `Label` instances may be addressed as a 1D array as follows
|
||||||
|
```python
|
||||||
|
grid[20] = str(42)
|
||||||
|
grid[20:25] = iter([str(n) for n in range(20, 25)])
|
||||||
|
```
|
||||||
|
or as a 2D array:
|
||||||
|
```python
|
||||||
|
grid[2, 5] = "A" # Row == 2, col == 5
|
||||||
|
grid[0:7, 3] = "b" # Populate col 3 of rows 0..6
|
||||||
|
grid[1:3, 1:3] = (str(n) for n in range(25)) # Produces
|
||||||
|
# 0 1
|
||||||
|
# 2 3
|
||||||
|
```
|
||||||
|
Columns are populated from left to right, rows from top to bottom. Unused
|
||||||
|
iterator values are ignored. If an iterator runs out of data the last value is
|
||||||
|
repeated, thus
|
||||||
|
```python
|
||||||
|
grid[1:3, 1:3] = (str(n) for n in range(2)) # Produces
|
||||||
|
# 0 1
|
||||||
|
# 1 1
|
||||||
|
```
|
||||||
Sample usage (complete example):
|
Sample usage (complete example):
|
||||||
```python
|
```python
|
||||||
from color_setup import ssd
|
from color_setup import ssd
|
||||||
|
@ -91,12 +116,8 @@ grid = Grid(wri, row, col, colwidth, rows, cols, align=ALIGN_CENTER)
|
||||||
grid.show() # Draw grid lines
|
grid.show() # Draw grid lines
|
||||||
|
|
||||||
# Populate grid
|
# Populate grid
|
||||||
col = 0
|
grid[1:6, 0] = iter("ABCDE") # Label row and col headings
|
||||||
for y, txt in enumerate("ABCDE"):
|
grid[0, 1:cols] = (str(x) for x in range(cols))
|
||||||
grid[y + 1, col] = txt
|
|
||||||
row = 0
|
|
||||||
for col in range(1, cols):
|
|
||||||
grid[row, col] = str(col)
|
|
||||||
grid[20] = "" # Clear cell 20 by setting its value to ""
|
grid[20] = "" # Clear cell 20 by setting its value to ""
|
||||||
grid[2, 5] = str(42) # 2d array syntax
|
grid[2, 5] = str(42) # 2d array syntax
|
||||||
# Dynamic formatting
|
# Dynamic formatting
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
# parse2d.py Parse args for item access dunder methods for a 2D array.
|
||||||
|
|
||||||
|
# Released under the MIT License (MIT). See LICENSE.
|
||||||
|
# Copyright (c) 2023 Peter Hinch
|
||||||
|
|
||||||
|
|
||||||
|
# Called from __getitem__ or __setitem__ args is a 1-tuple. The single item may be an int or a
|
||||||
|
# slice for 1D access. Or it may be a 2-tuple for 2D access. Items in the 2-tuple may be ints
|
||||||
|
# or slices in any combination.
|
||||||
|
# As a generator it returns offsets into the underlying 1D array or list.
|
||||||
|
def do_args(args, nrows, ncols):
|
||||||
|
# Given a slice and a maximum address return start and stop addresses (or None on error)
|
||||||
|
# Step value must be 1, hence does not support start > stop (used with step < 0)
|
||||||
|
def do_slice(sli, nbytes):
|
||||||
|
step = sli.step if sli.step is not None else 1
|
||||||
|
start = sli.start if sli.start is not None else 0
|
||||||
|
stop = sli.stop if sli.stop is not None else nbytes
|
||||||
|
start = min(start if start >= 0 else max(nbytes + start, 0), nbytes)
|
||||||
|
stop = min(stop if stop >= 0 else max(nbytes + stop, 0), nbytes)
|
||||||
|
ok = (start < stop and step > 0) or (start > stop and step < 0)
|
||||||
|
return (start, stop, step) if ok else None # Caller should check
|
||||||
|
|
||||||
|
def ivalid(n, nmax): # Validate an integer arg, handle -ve args
|
||||||
|
n = n if n >= 0 else nmax + n
|
||||||
|
if n < 0 or n > nmax - 1:
|
||||||
|
raise IndexError("Index out of range")
|
||||||
|
return n
|
||||||
|
|
||||||
|
def fail(n):
|
||||||
|
raise IndexError("Invalid index", n)
|
||||||
|
|
||||||
|
ncells = nrows * ncols
|
||||||
|
n = args[0]
|
||||||
|
if isinstance(n, int): # Index into 1D array
|
||||||
|
yield ivalid(n, ncells)
|
||||||
|
elif isinstance(n, slice): # Slice of 1D array
|
||||||
|
cells = do_slice(n, ncells)
|
||||||
|
if cells is not None:
|
||||||
|
for cell in range(*cells):
|
||||||
|
yield cell
|
||||||
|
elif isinstance(n, tuple) or isinstance(n, list): # list allows for old [[]] syntax
|
||||||
|
if len(n) != 2:
|
||||||
|
fail(n)
|
||||||
|
row = n[0] # May be slice
|
||||||
|
if isinstance(row, int):
|
||||||
|
row = ivalid(row, nrows)
|
||||||
|
col = n[1]
|
||||||
|
if isinstance(col, int):
|
||||||
|
col = ivalid(col, ncols)
|
||||||
|
if isinstance(row, int) and isinstance(col, int):
|
||||||
|
yield row * ncols + col
|
||||||
|
elif isinstance(row, slice) and isinstance(col, int):
|
||||||
|
rows = do_slice(row, nrows)
|
||||||
|
if rows is not None:
|
||||||
|
for row in range(*rows):
|
||||||
|
yield row * ncols + col
|
||||||
|
elif isinstance(row, int) and isinstance(col, slice):
|
||||||
|
cols = do_slice(col, ncols)
|
||||||
|
if cols is not None:
|
||||||
|
for col in range(*cols):
|
||||||
|
yield row * ncols + col
|
||||||
|
elif isinstance(row, slice) and isinstance(col, slice):
|
||||||
|
rows = do_slice(row, nrows)
|
||||||
|
cols = do_slice(col, ncols)
|
||||||
|
if cols is not None and rows is not None:
|
||||||
|
for row in range(*rows):
|
||||||
|
for col in range(*cols):
|
||||||
|
yield row * ncols + col
|
||||||
|
else:
|
||||||
|
fail(n)
|
||||||
|
else:
|
||||||
|
fail(n)
|
|
@ -7,7 +7,6 @@ from extras.widgets.grid import Grid
|
||||||
from gui.widgets.label import Label, ALIGN_CENTER
|
from gui.widgets.label import Label, ALIGN_CENTER
|
||||||
from extras.date import DateCal
|
from extras.date import DateCal
|
||||||
|
|
||||||
|
|
||||||
class Calendar:
|
class Calendar:
|
||||||
def __init__(
|
def __init__(
|
||||||
self, wri, row, col, colwidth, fgcolor, bgcolor, today_c, cur_c, sun_c, today_inv=False, cur_inv=False
|
self, wri, row, col, colwidth, fgcolor, bgcolor, today_c, cur_c, sun_c, today_inv=False, cur_inv=False
|
||||||
|
@ -29,8 +28,7 @@ class Calendar:
|
||||||
row += self.lbl.height + 3 # Two border widths
|
row += self.lbl.height + 3 # Two border widths
|
||||||
self.grid = Grid(wri, row, col, colwidth, rows, cols, **kwargs)
|
self.grid = Grid(wri, row, col, colwidth, rows, cols, **kwargs)
|
||||||
self.grid.show() # Draw grid lines
|
self.grid.show() # Draw grid lines
|
||||||
for n, day in enumerate(DateCal.days): # Populate day names
|
self.grid[0, 0:7] = iter([d[:3] for d in DateCal.days]) # 3-char day names
|
||||||
self.grid[0, n] = day[:3]
|
|
||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
from gui.core.nanogui import DObject, Writer
|
from gui.core.nanogui import DObject, Writer
|
||||||
from gui.core.colors import *
|
from gui.core.colors import *
|
||||||
from gui.widgets.label import Label
|
from gui.widgets.label import Label
|
||||||
|
from extras.parse2d import do_args
|
||||||
|
|
||||||
# lwidth may be integer Label width in pixels or a tuple/list of widths
|
# lwidth may be integer Label width in pixels or a tuple/list of widths
|
||||||
class Grid(DObject):
|
class Grid(DObject):
|
||||||
|
@ -33,28 +34,27 @@ class Grid(DObject):
|
||||||
r += self.cheight
|
r += self.cheight
|
||||||
c = col
|
c = col
|
||||||
|
|
||||||
def _idx(self, n):
|
|
||||||
if isinstance(n, tuple) or isinstance(n, list): # list allows old syntax l[[r, c]]
|
|
||||||
if n[0] >= self.nrows:
|
|
||||||
raise ValueError("Grid row index too large")
|
|
||||||
if n[1] >= self.ncols:
|
|
||||||
raise ValueError("Grid col index too large")
|
|
||||||
idx = n[1] + n[0] * self.ncols
|
|
||||||
else:
|
|
||||||
idx = n
|
|
||||||
if idx >= self.ncells:
|
|
||||||
raise ValueError("Grid cell index too large")
|
|
||||||
return idx
|
|
||||||
|
|
||||||
def __getitem__(self, *args): # Return the Label instance
|
def __getitem__(self, *args): # Return the Label instance
|
||||||
return self.cells[self._idx(args[0])]
|
indices = do_args(args, self.nrows, self.ncols)
|
||||||
|
res = []
|
||||||
|
for i in indices:
|
||||||
|
res.append(self.cells[i])
|
||||||
|
return res
|
||||||
|
|
||||||
# allow grid[r, c] = "foo" or kwargs for Label:
|
# allow grid[r, c] = "foo" or kwargs for Label:
|
||||||
# grid[r, c] = {"text": str(n), "fgcolor" : RED}
|
# grid[r, c] = {"text": str(n), "fgcolor" : RED}
|
||||||
def __setitem__(self, *args):
|
def __setitem__(self, *args):
|
||||||
v = self.cells[self._idx(args[0])].value
|
x = args[1] # Value
|
||||||
x = args[1]
|
indices = do_args(args[: -1], self.nrows, self.ncols)
|
||||||
_ = v(**x) if isinstance(x, dict) else v(x)
|
for i in indices:
|
||||||
|
try:
|
||||||
|
z = next(x) # May be a generator
|
||||||
|
except StopIteration:
|
||||||
|
pass # Repeat last value
|
||||||
|
except TypeError:
|
||||||
|
z = x
|
||||||
|
v = self.cells[i].value # method of Label
|
||||||
|
_ = v(**z) if isinstance(x, dict) else v(z)
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
super().show() # Draw border
|
super().show() # Draw border
|
||||||
|
|
Ładowanie…
Reference in New Issue