#!/usr/bin/env python3 import os import re import argparse from collections import defaultdict style_properties_type = { "LV_STYLE_ANIM": "LV_PROPERTY_TYPE_POINTER", "LV_STYLE_ARC_COLOR": "LV_PROPERTY_TYPE_COLOR", "LV_STYLE_ARC_IMAGE_SRC": "LV_PROPERTY_TYPE_IMGSRC", "LV_STYLE_BG_COLOR": "LV_PROPERTY_TYPE_COLOR", "LV_STYLE_BG_GRAD": "LV_PROPERTY_TYPE_POINTER", "LV_STYLE_BG_GRAD_COLOR": "LV_PROPERTY_TYPE_COLOR", "LV_STYLE_BG_IMAGE_RECOLOR": "LV_PROPERTY_TYPE_COLOR", "LV_STYLE_BG_IMAGE_SRC": "LV_PROPERTY_TYPE_IMGSRC", "LV_STYLE_BITMAP_MASK_SRC": "LV_PROPERTY_TYPE_POINTER", "LV_STYLE_BORDER_COLOR": "LV_PROPERTY_TYPE_COLOR", "LV_STYLE_COLOR_FILTER_DSC": "LV_PROPERTY_TYPE_POINTER", "LV_STYLE_GRID_COLUMN_DSC_ARRAY": "LV_PROPERTY_TYPE_POINTER", "LV_STYLE_GRID_ROW_DSC_ARRAY": "LV_PROPERTY_TYPE_POINTER", "LV_STYLE_IMAGE_RECOLOR": "LV_PROPERTY_TYPE_COLOR", "LV_STYLE_LAST_BUILT_IN_PROP": "LV_PROPERTY_TYPE_INVALID", "LV_STYLE_LINE_COLOR": "LV_PROPERTY_TYPE_COLOR", "LV_STYLE_OUTLINE_COLOR": "LV_PROPERTY_TYPE_COLOR", "LV_STYLE_PROP_INV": "LV_PROPERTY_TYPE_INVALID", "LV_STYLE_RECOLOR": "LV_PROPERTY_TYPE_COLOR", "LV_STYLE_SHADOW_COLOR": "LV_PROPERTY_TYPE_COLOR", "LV_STYLE_TEXT_COLOR": "LV_PROPERTY_TYPE_COLOR", "LV_STYLE_TEXT_FONT": "LV_PROPERTY_TYPE_FONT", "LV_STYLE_TEXT_OUTLINE_STROKE_COLOR": "LV_PROPERTY_TYPE_COLOR", "LV_STYLE_TRANSITION": "LV_PROPERTY_TYPE_POINTER", "LV_STYLE_DROP_SHADOW_COLOR": "LV_PROPERTY_TYPE_COLOR", } class Property: def __init__(self, widget, name, type, index, id): self.widget = widget self.name = name self.type = type self.index = index self.id = id def find_headers(directory): if os.path.isfile(directory): yield directory return for root, dirs, files in os.walk(directory): for file in files: if file.endswith('.h'): yield os.path.join(root, file) def read_widget_properties(directory): def match_properties(file_path): pattern = r'^\s*LV_PROPERTY_ID2?\((\w+),\s*(\w+),\s*(\w+)(,\s*(\w+))?,\s*(\d+)\)' with open(file_path, 'r', encoding='utf-8') as file: for line in file.readlines(): match = re.match(pattern, line) if match: id = f"LV_PROPERTY_{match.group(1).upper()}_{match.group(2).upper()}" yield Property( match.group(1).lower(), match.group(2).lower(), match.group(3), match.group(4), id) def match_styles(file_path): pattern_with_value = r'^\s+LV_STYLE_(\w+)\s*=\s*(\d+),' pattern_name_only = r'^\s+LV_STYLE_(\w+)\s*,' last_value = 0 process = False with open(file_path, 'r', encoding='utf-8') as file: for line in file.readlines(): if re.match("enum _lv_style_id_t", line): process = True continue if process and re.match("};", line): return if process == False: continue match = re.match(pattern_with_value, line) name = "" if match: name = match.group(1).upper() last_value = int(match.group(2)) else: match = re.match(pattern_name_only, line) if match: name = match.group(1).upper() last_value += 1 if name: id = f"LV_PROPERTY_STYLE_{name}" yield Property("style", match.group(1).lower(), "style", last_value, id) properties_by_widget = defaultdict(list) for file_path in find_headers(directory): for property in match_properties(file_path): properties_by_widget[property.widget].append(property) for property in match_styles(file_path): properties_by_widget[property.widget].append(property) for widget, properties in properties_by_widget.items(): # sort properties by property name properties.sort(key=lambda x: x.name) properties_by_widget[widget] = properties return properties_by_widget def write_widget_properties(root_folder, properties_by_widget): # Open header file for update. pub_header = os.path.join(root_folder, 'include', 'lvgl', 'core', 'lv_obj_property_names.h') with open(pub_header, "w") as header: header.write(f''' /** * @file lv_obj_property_names.h * GENERATED FILE, DO NOT EDIT IT! */ #ifndef LV_OBJ_PROPERTY_NAMES_H #define LV_OBJ_PROPERTY_NAMES_H #include "../lv_types.h" #if LV_USE_OBJ_PROPERTY && LV_USE_OBJ_PROPERTY_NAME ''') for widget in sorted(properties_by_widget.keys()): properties = properties_by_widget[widget] file_name = f'lv_{widget}_properties.c' output_file = os.path.join(root_folder, 'src', 'widgets', 'property', file_name) count = len(properties) if widget != 'style' and widget != 'obj': guard = f"#if LV_USE_{widget.upper()}" else: guard = None include = "../../lv_public_api.h" with open(output_file, 'w') as f: f.write(f''' /** * GENERATED FILE, DO NOT EDIT IT! * @file {file_name} */ #include "{include}" #if LV_USE_OBJ_PROPERTY && LV_USE_OBJ_PROPERTY_NAME {guard if guard else ""} /** * {widget.capitalize() + ' widget' if widget != 'style' else 'Style'} property names, name must be in order. * Generated code from properties.py */ /* *INDENT-OFF* */ const lv_property_name_t lv_{widget}_property_names[{count}] = {{ ''') for property in properties: name = property.name name_str = '"' + name + '",' f.write(f" {{{name_str :25} {property.id},}},\n") f.write('};\n') if guard: f.write(f"#endif /*LV_USE_{widget.upper()}*/\n\n") f.write("/* *INDENT-ON* */\n") f.write('#endif\n') header.write( f' extern const lv_property_name_t lv_{widget}_property_names[{count}];\n' ) header.write('#endif\n') header.write('#endif\n') def write_style_header(root_folder, properties_by_widget): properties = properties_by_widget['style'] header = os.path.join(root_folder, 'include', 'lvgl', 'core', 'lv_style_properties.h') with open(header, 'w') as f: f.write(f''' /** * GENERATED FILE, DO NOT EDIT IT! * @file lv_style_properties.h */ #ifndef LV_STYLE_PROPERTIES_H #define LV_STYLE_PROPERTIES_H #include "../config/lv_conf_internal.h" #if LV_USE_OBJ_PROPERTY /* *INDENT-OFF* */ enum _lv_property_style_id_t {{ ''') for property in properties: name = property.name id_type = style_properties_type.get(f"LV_STYLE_{name.upper()}", "LV_PROPERTY_TYPE_INT") f.write( f" LV_PROPERTY_ID(STYLE, {name.upper() + ',' :25} {id_type+',' :28} LV_STYLE_{name.upper()}),\n" ) f.write('};\n\n') f.write('#endif\n') f.write('#endif\n') def main(directory): """Generate property names""" property = read_widget_properties(directory) write_widget_properties(directory, property) write_style_header(directory, property) if __name__ == "__main__": parser = argparse.ArgumentParser( description='Search files and filter lines.') parser.add_argument('-d', '--directory', help='Directory to lvgl root path') args = parser.parse_args() # default directory is the lvgl root path of where this script sits if args.directory is None: args.directory = os.path.join(os.path.dirname(__file__), "../") property_folder = os.path.join(args.directory, 'src', 'widgets', 'property') # create output directory if it doesn't exist os.makedirs(property_folder, exist_ok=True) main(args.directory)