mirror of
https://github.com/esphome/esphome.git
synced 2025-07-27 21:56:34 +00:00
[lvgl] Make line points templatable (#8502)
This commit is contained in:
parent
1c72fd4674
commit
6240bfff97
@ -375,6 +375,7 @@ async def to_code(configs):
|
|||||||
add_define("LV_COLOR_SCREEN_TRANSP", "1")
|
add_define("LV_COLOR_SCREEN_TRANSP", "1")
|
||||||
for use in helpers.lv_uses:
|
for use in helpers.lv_uses:
|
||||||
add_define(f"LV_USE_{use.upper()}")
|
add_define(f"LV_USE_{use.upper()}")
|
||||||
|
cg.add_define(f"USE_LVGL_{use.upper()}")
|
||||||
lv_conf_h_file = CORE.relative_src_path(LV_CONF_FILENAME)
|
lv_conf_h_file = CORE.relative_src_path(LV_CONF_FILENAME)
|
||||||
write_file_if_changed(lv_conf_h_file, generate_lv_conf_h())
|
write_file_if_changed(lv_conf_h_file, generate_lv_conf_h())
|
||||||
cg.add_build_flag("-DLV_CONF_H=1")
|
cg.add_build_flag("-DLV_CONF_H=1")
|
||||||
|
@ -17,6 +17,7 @@ from .defines import (
|
|||||||
CONF_SHOW_SNOW,
|
CONF_SHOW_SNOW,
|
||||||
PARTS,
|
PARTS,
|
||||||
literal,
|
literal,
|
||||||
|
static_cast,
|
||||||
)
|
)
|
||||||
from .lv_validation import lv_bool, lv_color, lv_image, opacity
|
from .lv_validation import lv_bool, lv_color, lv_image, opacity
|
||||||
from .lvcode import (
|
from .lvcode import (
|
||||||
@ -32,7 +33,6 @@ from .lvcode import (
|
|||||||
lv_expr,
|
lv_expr,
|
||||||
lv_obj,
|
lv_obj,
|
||||||
lvgl_comp,
|
lvgl_comp,
|
||||||
static_cast,
|
|
||||||
)
|
)
|
||||||
from .schemas import DISP_BG_SCHEMA, LIST_ACTION_SCHEMA, LVGL_SCHEMA, base_update_schema
|
from .schemas import DISP_BG_SCHEMA, LIST_ACTION_SCHEMA, LVGL_SCHEMA, base_update_schema
|
||||||
from .types import (
|
from .types import (
|
||||||
|
@ -35,6 +35,10 @@ def literal(arg):
|
|||||||
return arg
|
return arg
|
||||||
|
|
||||||
|
|
||||||
|
def static_cast(type, value):
|
||||||
|
return literal(f"static_cast<{type}>({value})")
|
||||||
|
|
||||||
|
|
||||||
def call_lambda(lamb: LambdaExpression):
|
def call_lambda(lamb: LambdaExpression):
|
||||||
expr = lamb.content.strip()
|
expr = lamb.content.strip()
|
||||||
if expr.startswith("return") and expr.endswith(";"):
|
if expr.startswith("return") and expr.endswith(";"):
|
||||||
|
@ -285,10 +285,6 @@ class LvExpr(MockLv):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def static_cast(type, value):
|
|
||||||
return literal(f"static_cast<{type}>({value})")
|
|
||||||
|
|
||||||
|
|
||||||
# Top level mock for generic lv_ calls to be recorded
|
# Top level mock for generic lv_ calls to be recorded
|
||||||
lv = MockLv("lv_")
|
lv = MockLv("lv_")
|
||||||
# Just generate an expression
|
# Just generate an expression
|
||||||
|
@ -90,6 +90,7 @@ inline void lv_animimg_set_src(lv_obj_t *img, std::vector<image::Image *> images
|
|||||||
// Parent class for things that wrap an LVGL object
|
// Parent class for things that wrap an LVGL object
|
||||||
class LvCompound {
|
class LvCompound {
|
||||||
public:
|
public:
|
||||||
|
virtual ~LvCompound() = default;
|
||||||
virtual void set_obj(lv_obj_t *lv_obj) { this->obj = lv_obj; }
|
virtual void set_obj(lv_obj_t *lv_obj) { this->obj = lv_obj; }
|
||||||
lv_obj_t *obj{};
|
lv_obj_t *obj{};
|
||||||
};
|
};
|
||||||
@ -330,6 +331,19 @@ class LVEncoderListener : public Parented<LvglComponent> {
|
|||||||
};
|
};
|
||||||
#endif // USE_LVGL_KEY_LISTENER
|
#endif // USE_LVGL_KEY_LISTENER
|
||||||
|
|
||||||
|
#ifdef USE_LVGL_LINE
|
||||||
|
class LvLineType : public LvCompound {
|
||||||
|
public:
|
||||||
|
std::vector<lv_point_t> get_points() { return this->points_; }
|
||||||
|
void set_points(std::vector<lv_point_t> points) {
|
||||||
|
this->points_ = std::move(points);
|
||||||
|
lv_line_set_points(this->obj, this->points_.data(), this->points_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<lv_point_t> points_{};
|
||||||
|
};
|
||||||
|
#endif
|
||||||
#if defined(USE_LVGL_DROPDOWN) || defined(LV_USE_ROLLER)
|
#if defined(USE_LVGL_DROPDOWN) || defined(LV_USE_ROLLER)
|
||||||
class LvSelectable : public LvCompound {
|
class LvSelectable : public LvCompound {
|
||||||
public:
|
public:
|
||||||
|
@ -19,7 +19,7 @@ from esphome.core.config import StartupTrigger
|
|||||||
from esphome.schema_extractors import SCHEMA_EXTRACT
|
from esphome.schema_extractors import SCHEMA_EXTRACT
|
||||||
|
|
||||||
from . import defines as df, lv_validation as lvalid
|
from . import defines as df, lv_validation as lvalid
|
||||||
from .defines import CONF_TIME_FORMAT, LV_GRAD_DIR
|
from .defines import CONF_TIME_FORMAT, CONF_X, CONF_Y, LV_GRAD_DIR
|
||||||
from .helpers import add_lv_use, requires_component, validate_printf
|
from .helpers import add_lv_use, requires_component, validate_printf
|
||||||
from .lv_validation import lv_color, lv_font, lv_gradient, lv_image, opacity
|
from .lv_validation import lv_color, lv_font, lv_gradient, lv_image, opacity
|
||||||
from .lvcode import LvglComponent, lv_event_t_ptr
|
from .lvcode import LvglComponent, lv_event_t_ptr
|
||||||
@ -87,6 +87,33 @@ ENCODER_SCHEMA = cv.Schema(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def point_shorthand(value):
|
||||||
|
"""
|
||||||
|
A shorthand for a point in the form of x,y
|
||||||
|
:param value: The value to check
|
||||||
|
:return: The value as a tuple of x,y
|
||||||
|
"""
|
||||||
|
if isinstance(value, str):
|
||||||
|
try:
|
||||||
|
x, y = map(int, value.split(","))
|
||||||
|
return {CONF_X: x, CONF_Y: y}
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
raise cv.Invalid("Invalid point format, should be <x_value>, <y_value>")
|
||||||
|
|
||||||
|
|
||||||
|
POINT_SCHEMA = cv.Any(
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_X): cv.templatable(cv.int_),
|
||||||
|
cv.Required(CONF_Y): cv.templatable(cv.int_),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
point_shorthand,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# All LVGL styles and their validators
|
# All LVGL styles and their validators
|
||||||
STYLE_PROPS = {
|
STYLE_PROPS = {
|
||||||
"align": df.CHILD_ALIGNMENTS.one_of,
|
"align": df.CHILD_ALIGNMENTS.one_of,
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import functools
|
|
||||||
|
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
from esphome.core import Lambda
|
||||||
|
|
||||||
from ..defines import CONF_MAIN
|
from ..defines import CONF_MAIN, CONF_X, CONF_Y, call_lambda
|
||||||
from ..lvcode import lv
|
from ..lvcode import lv_add
|
||||||
from ..types import LvType
|
from ..schemas import POINT_SCHEMA
|
||||||
|
from ..types import LvCompound, LvType
|
||||||
from . import Widget, WidgetType
|
from . import Widget, WidgetType
|
||||||
|
|
||||||
CONF_LINE = "line"
|
CONF_LINE = "line"
|
||||||
@ -15,47 +15,37 @@ CONF_POINT_LIST_ID = "point_list_id"
|
|||||||
lv_point_t = cg.global_ns.struct("lv_point_t")
|
lv_point_t = cg.global_ns.struct("lv_point_t")
|
||||||
|
|
||||||
|
|
||||||
def point_list(il):
|
|
||||||
il = cv.string(il)
|
|
||||||
nl = il.replace(" ", "").split(",")
|
|
||||||
return [int(n) for n in nl]
|
|
||||||
|
|
||||||
|
|
||||||
def cv_point_list(value):
|
|
||||||
if not isinstance(value, list):
|
|
||||||
raise cv.Invalid("List of points required")
|
|
||||||
values = [point_list(v) for v in value]
|
|
||||||
if not functools.reduce(lambda f, v: f and len(v) == 2, values, True):
|
|
||||||
raise cv.Invalid("Points must be a list of x,y integer pairs")
|
|
||||||
return values
|
|
||||||
|
|
||||||
|
|
||||||
LINE_SCHEMA = {
|
LINE_SCHEMA = {
|
||||||
cv.Required(CONF_POINTS): cv_point_list,
|
cv.Required(CONF_POINTS): cv.ensure_list(POINT_SCHEMA),
|
||||||
cv.GenerateID(CONF_POINT_LIST_ID): cv.declare_id(lv_point_t),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LINE_MODIFY_SCHEMA = {
|
|
||||||
cv.Optional(CONF_POINTS): cv_point_list,
|
async def process_coord(coord):
|
||||||
cv.GenerateID(CONF_POINT_LIST_ID): cv.declare_id(lv_point_t),
|
if isinstance(coord, Lambda):
|
||||||
}
|
coord = call_lambda(
|
||||||
|
await cg.process_lambda(coord, (), return_type="lv_coord_t")
|
||||||
|
)
|
||||||
|
if not coord.endswith("()"):
|
||||||
|
coord = f"static_cast<lv_coord_t>({coord})"
|
||||||
|
return cg.RawExpression(coord)
|
||||||
|
return cg.safe_exp(coord)
|
||||||
|
|
||||||
|
|
||||||
class LineType(WidgetType):
|
class LineType(WidgetType):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
CONF_LINE,
|
CONF_LINE,
|
||||||
LvType("lv_line_t"),
|
LvType("LvLineType", parents=(LvCompound,)),
|
||||||
(CONF_MAIN,),
|
(CONF_MAIN,),
|
||||||
LINE_SCHEMA,
|
LINE_SCHEMA,
|
||||||
modify_schema=LINE_MODIFY_SCHEMA,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
async def to_code(self, w: Widget, config):
|
async def to_code(self, w: Widget, config):
|
||||||
"""For a line object, create and add the points"""
|
points = [
|
||||||
if data := config.get(CONF_POINTS):
|
[await process_coord(p[CONF_X]), await process_coord(p[CONF_Y])]
|
||||||
points = cg.static_const_array(config[CONF_POINT_LIST_ID], data)
|
for p in config[CONF_POINTS]
|
||||||
lv.line_set_points(w.obj, points, len(data))
|
]
|
||||||
|
lv_add(w.var.set_points(points))
|
||||||
|
|
||||||
|
|
||||||
line_spec = LineType()
|
line_spec = LineType()
|
||||||
|
@ -614,6 +614,8 @@ lvgl:
|
|||||||
align: center
|
align: center
|
||||||
points:
|
points:
|
||||||
- 5, 5
|
- 5, 5
|
||||||
|
- x: !lambda return random_uint32() % 100;
|
||||||
|
y: !lambda return random_uint32() % 100;
|
||||||
- 70, 70
|
- 70, 70
|
||||||
- 120, 10
|
- 120, 10
|
||||||
- 180, 60
|
- 180, 60
|
||||||
@ -622,6 +624,14 @@ lvgl:
|
|||||||
- lvgl.line.update:
|
- lvgl.line.update:
|
||||||
id: lv_line_id
|
id: lv_line_id
|
||||||
line_color: 0xFFFF
|
line_color: 0xFFFF
|
||||||
|
points:
|
||||||
|
- 5, 5
|
||||||
|
- x: !lambda return random_uint32() % 100;
|
||||||
|
y: !lambda return random_uint32() % 100;
|
||||||
|
- 70, 70
|
||||||
|
- 120, 10
|
||||||
|
- 180, 60
|
||||||
|
- 240, 10
|
||||||
- lvgl.page.next:
|
- lvgl.page.next:
|
||||||
- switch:
|
- switch:
|
||||||
align: right_mid
|
align: right_mid
|
||||||
|
Loading…
x
Reference in New Issue
Block a user