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:
|
||||
* `show` Draw the grid lines to the framebuffer.
|
||||
* `__getitem__` This enables an individual `Label`'s `value` method to be
|
||||
retrieved using index notation. The args detailed above enable inividual cells
|
||||
to be updated.
|
||||
* `__getitem__` Return a list containing one or more `Label` instances.
|
||||
* `__setitem__` Assign a value to one or more labels. If multiple labels are
|
||||
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):
|
||||
```python
|
||||
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
|
||||
|
||||
# Populate grid
|
||||
col = 0
|
||||
for y, txt in enumerate("ABCDE"):
|
||||
grid[y + 1, col] = txt
|
||||
row = 0
|
||||
for col in range(1, cols):
|
||||
grid[row, col] = str(col)
|
||||
grid[1:6, 0] = iter("ABCDE") # Label row and col headings
|
||||
grid[0, 1:cols] = (str(x) for x in range(cols))
|
||||
grid[20] = "" # Clear cell 20 by setting its value to ""
|
||||
grid[2, 5] = str(42) # 2d array syntax
|
||||
# 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 extras.date import DateCal
|
||||
|
||||
|
||||
class Calendar:
|
||||
def __init__(
|
||||
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
|
||||
self.grid = Grid(wri, row, col, colwidth, rows, cols, **kwargs)
|
||||
self.grid.show() # Draw grid lines
|
||||
for n, day in enumerate(DateCal.days): # Populate day names
|
||||
self.grid[0, n] = day[:3]
|
||||
self.grid[0, 0:7] = iter([d[:3] for d in DateCal.days]) # 3-char day names
|
||||
self.show()
|
||||
|
||||
def show(self):
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
from gui.core.nanogui import DObject, Writer
|
||||
from gui.core.colors import *
|
||||
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
|
||||
class Grid(DObject):
|
||||
|
@ -33,28 +34,27 @@ class Grid(DObject):
|
|||
r += self.cheight
|
||||
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
|
||||
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:
|
||||
# grid[r, c] = {"text": str(n), "fgcolor" : RED}
|
||||
def __setitem__(self, *args):
|
||||
v = self.cells[self._idx(args[0])].value
|
||||
x = args[1]
|
||||
_ = v(**x) if isinstance(x, dict) else v(x)
|
||||
x = args[1] # Value
|
||||
indices = do_args(args[: -1], self.nrows, self.ncols)
|
||||
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):
|
||||
super().show() # Draw border
|
||||
|
|
Ładowanie…
Reference in New Issue