From b7399044920d7f44cd07cdbba335dcd4bebf810e Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Tue, 20 Feb 2024 23:07:35 -0300 Subject: [PATCH 01/54] Initial project template --- tools/config_editor/.gitignore | 2 + tools/config_editor/README.md | 0 tools/config_editor/config_editor/__init__.py | 0 tools/config_editor/config_editor/main.py | 78 +++++ .../config_editor/pyinstaller.py | 13 + tools/config_editor/config_editor/style.tcss | 26 ++ tools/config_editor/poetry.lock | 319 ++++++++++++++++++ tools/config_editor/pyproject.toml | 19 ++ tools/config_editor/tests/__init__.py | 0 9 files changed, 457 insertions(+) create mode 100644 tools/config_editor/.gitignore create mode 100644 tools/config_editor/README.md create mode 100644 tools/config_editor/config_editor/__init__.py create mode 100644 tools/config_editor/config_editor/main.py create mode 100644 tools/config_editor/config_editor/pyinstaller.py create mode 100644 tools/config_editor/config_editor/style.tcss create mode 100644 tools/config_editor/poetry.lock create mode 100644 tools/config_editor/pyproject.toml create mode 100644 tools/config_editor/tests/__init__.py diff --git a/tools/config_editor/.gitignore b/tools/config_editor/.gitignore new file mode 100644 index 000000000..808386fd8 --- /dev/null +++ b/tools/config_editor/.gitignore @@ -0,0 +1,2 @@ +.venv/ +__pycache__/ \ No newline at end of file diff --git a/tools/config_editor/README.md b/tools/config_editor/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/tools/config_editor/config_editor/__init__.py b/tools/config_editor/config_editor/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tools/config_editor/config_editor/main.py b/tools/config_editor/config_editor/main.py new file mode 100644 index 000000000..27923ce34 --- /dev/null +++ b/tools/config_editor/config_editor/main.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python + +""" +Arduino-esp32 Libraries Configuration Editor +""" + +import sys + +from rich.syntax import Syntax +from rich.traceback import Traceback + +from textual.app import App, ComposeResult +from textual.containers import Container, VerticalScroll +from textual.reactive import var +from textual.widgets import DirectoryTree, Footer, Header, Static + + +class ConfigEditor(App): + """Textual config editor app.""" + + CSS_PATH = "style.tcss" + BINDINGS = [ + ("f", "toggle_files", "Toggle Files"), + ("q", "quit", "Quit"), + ] + + show_tree = var(True) + + def watch_show_tree(self, show_tree: bool) -> None: + """Called when show_tree is modified.""" + self.set_class(show_tree, "-show-tree") + + def compose(self) -> ComposeResult: + """Compose our UI.""" + path = "./" if len(sys.argv) < 2 else sys.argv[1] + yield Header() + with Container(): + yield DirectoryTree(path, id="tree-view") + with VerticalScroll(id="code-view"): + yield Static(id="code", expand=True) + yield Footer() + + def on_mount(self) -> None: + self.query_one(DirectoryTree).focus() + + def on_directory_tree_file_selected( + self, event: DirectoryTree.FileSelected + ) -> None: + """Called when the user click a file in the directory tree.""" + event.stop() + code_view = self.query_one("#code", Static) + try: + syntax = Syntax.from_path( + str(event.path), + line_numbers=True, + word_wrap=False, + indent_guides=True, + theme="github-dark", + ) + except Exception: + code_view.update(Traceback(theme="github-dark", width=None)) + self.sub_title = "ERROR" + else: + code_view.update(syntax) + self.query_one("#code-view").scroll_home(animate=False) + self.sub_title = str(event.path) + + def action_toggle_files(self) -> None: + """Called in response to key binding.""" + self.show_tree = not self.show_tree + + +def main() -> None: + """Run the app.""" + ConfigEditor().run() + +if __name__ == "__main__": + main() diff --git a/tools/config_editor/config_editor/pyinstaller.py b/tools/config_editor/config_editor/pyinstaller.py new file mode 100644 index 000000000..034ad4188 --- /dev/null +++ b/tools/config_editor/config_editor/pyinstaller.py @@ -0,0 +1,13 @@ +import PyInstaller.__main__ +from pathlib import Path + +main_folder = Path(__file__).parent.absolute() +path_to_main = str(main_folder / "main.py") + +def install(): + PyInstaller.__main__.run([ + path_to_main, + '--onefile', + '--windowed', + # other pyinstaller options... + ]) diff --git a/tools/config_editor/config_editor/style.tcss b/tools/config_editor/config_editor/style.tcss new file mode 100644 index 000000000..40d1a6312 --- /dev/null +++ b/tools/config_editor/config_editor/style.tcss @@ -0,0 +1,26 @@ +Screen { + background: $surface-darken-1; +} + +#tree-view { + display: none; + scrollbar-gutter: stable; + overflow: auto; + width: auto; + height: 100%; + dock: left; +} + +ConfigEditor.-show-tree #tree-view { + display: block; + max-width: 50%; +} + +#code-view { + overflow: auto scroll; + min-width: 100%; +} + +#code { + width: auto; +} diff --git a/tools/config_editor/poetry.lock b/tools/config_editor/poetry.lock new file mode 100644 index 000000000..4a6b17a91 --- /dev/null +++ b/tools/config_editor/poetry.lock @@ -0,0 +1,319 @@ +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. + +[[package]] +name = "altgraph" +version = "0.17.4" +description = "Python graph (network) package" +optional = false +python-versions = "*" +files = [ + {file = "altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff"}, + {file = "altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406"}, +] + +[[package]] +name = "importlib-metadata" +version = "7.0.1" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, + {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] + +[[package]] +name = "linkify-it-py" +version = "2.0.3" +description = "Links recognition library with FULL unicode support." +optional = false +python-versions = ">=3.7" +files = [ + {file = "linkify-it-py-2.0.3.tar.gz", hash = "sha256:68cda27e162e9215c17d786649d1da0021a451bdc436ef9e0fa0ba5234b9b048"}, + {file = "linkify_it_py-2.0.3-py3-none-any.whl", hash = "sha256:6bcbc417b0ac14323382aef5c5192c0075bf8a9d6b41820a2b66371eac6b6d79"}, +] + +[package.dependencies] +uc-micro-py = "*" + +[package.extras] +benchmark = ["pytest", "pytest-benchmark"] +dev = ["black", "flake8", "isort", "pre-commit", "pyproject-flake8"] +doc = ["myst-parser", "sphinx", "sphinx-book-theme"] +test = ["coverage", "pytest", "pytest-cov"] + +[[package]] +name = "macholib" +version = "1.16.3" +description = "Mach-O header analysis and editing" +optional = false +python-versions = "*" +files = [ + {file = "macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c"}, + {file = "macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30"}, +] + +[package.dependencies] +altgraph = ">=0.17" + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +linkify-it-py = {version = ">=1,<3", optional = true, markers = "extra == \"linkify\""} +mdit-py-plugins = {version = "*", optional = true, markers = "extra == \"plugins\""} +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "mdit-py-plugins" +version = "0.4.0" +description = "Collection of plugins for markdown-it-py" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"}, + {file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"}, +] + +[package.dependencies] +markdown-it-py = ">=1.0.0,<4.0.0" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["myst-parser", "sphinx-book-theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "packaging" +version = "23.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, +] + +[[package]] +name = "pefile" +version = "2023.2.7" +description = "Python PE parsing module" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6"}, + {file = "pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc"}, +] + +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pyinstaller" +version = "6.4.0" +description = "PyInstaller bundles a Python application and all its dependencies into a single package." +optional = false +python-versions = "<3.13,>=3.8" +files = [ + {file = "pyinstaller-6.4.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:a2e63fa71784f290bbf79b31b60a27c45b17a18b8c7f910757f9474e0c12c95d"}, + {file = "pyinstaller-6.4.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:3127724d1841f785a9916d7b4cfd9595f359925e9ce7d137a16db8c29ca8453b"}, + {file = "pyinstaller-6.4.0-py3-none-manylinux2014_i686.whl", hash = "sha256:a37f83850cb150ad1e00fe92acecc4d39b8e10162a1850a5836a05fcb2daa870"}, + {file = "pyinstaller-6.4.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:28b98fa3c74602bdc4c5a7698e907f31e714cc40a13f6358082bcbc74ddab35c"}, + {file = "pyinstaller-6.4.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:3ae62cf8858ec4dc54df6fa03d29bc78297e3c87caf532887eae8c3893be0789"}, + {file = "pyinstaller-6.4.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e3e1e6922a4260dcacf6f5655b0ca857451e05ac502d01642935d0f2873ad3c7"}, + {file = "pyinstaller-6.4.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:78fb66ca753ef8becdf059eaa1e764d384cacb8c2ec76800126f8c9ef6d19a50"}, + {file = "pyinstaller-6.4.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:e07cff584600647af7dc279dd04c60cd1b4b1b41947b0753f8fcf1969300a583"}, + {file = "pyinstaller-6.4.0-py3-none-win32.whl", hash = "sha256:c7bc0fbea8a9010484cfa7d3856416003af73271f03ca3da4bc0eaf14680ad17"}, + {file = "pyinstaller-6.4.0-py3-none-win_amd64.whl", hash = "sha256:ec8a08c983e3febb0247893cd9bd59f55b6767a1f649cb41a0a129b8f04ff2cb"}, + {file = "pyinstaller-6.4.0-py3-none-win_arm64.whl", hash = "sha256:11e6da6a6e441379352ee460a8880f2633dac91dac0f5a9eeff5d449d459b046"}, + {file = "pyinstaller-6.4.0.tar.gz", hash = "sha256:1bf608ed947b58614711275a7ff169289b32560dc97ec748ebd5fa8bdec80649"}, +] + +[package.dependencies] +altgraph = "*" +importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} +macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""} +packaging = ">=22.0" +pefile = {version = ">=2022.5.30", markers = "sys_platform == \"win32\""} +pyinstaller-hooks-contrib = ">=2024.0" +pywin32-ctypes = {version = ">=0.2.1", markers = "sys_platform == \"win32\""} +setuptools = ">=42.0.0" + +[package.extras] +completion = ["argcomplete"] +hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"] + +[[package]] +name = "pyinstaller-hooks-contrib" +version = "2024.1" +description = "Community maintained hooks for PyInstaller" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyinstaller-hooks-contrib-2024.1.tar.gz", hash = "sha256:51a51ea9e1ae6bd5ffa7ec45eba7579624bf4f2472ff56dba0edc186f6ed46a6"}, + {file = "pyinstaller_hooks_contrib-2024.1-py2.py3-none-any.whl", hash = "sha256:131494f9cfce190aaa66ed82e82c78b2723d1720ce64d012fbaf938f4ab01d35"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} +packaging = ">=22.0" +setuptools = ">=42.0.0" + +[[package]] +name = "pywin32-ctypes" +version = "0.2.2" +description = "A (partial) reimplementation of pywin32 using ctypes/cffi" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60"}, + {file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"}, +] + +[[package]] +name = "rich" +version = "13.7.0" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, + {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "setuptools" +version = "69.1.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.1.0-py3-none-any.whl", hash = "sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6"}, + {file = "setuptools-69.1.0.tar.gz", hash = "sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "textual" +version = "0.52.1" +description = "Modern Text User Interface framework" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "textual-0.52.1-py3-none-any.whl", hash = "sha256:960a19df2319482918b4a58736d9552cdc1ab65d170ba0bc15273ce0e1922b7a"}, + {file = "textual-0.52.1.tar.gz", hash = "sha256:4232e5c2b423ed7c63baaeb6030355e14e1de1b9df096c9655b68a1e60e4de5f"}, +] + +[package.dependencies] +markdown-it-py = {version = ">=2.1.0", extras = ["linkify", "plugins"]} +rich = ">=13.3.3" +typing-extensions = ">=4.4.0,<5.0.0" + +[package.extras] +syntax = ["tree-sitter (>=0.20.1,<0.21.0)", "tree_sitter_languages (>=1.7.0)"] + +[[package]] +name = "typing-extensions" +version = "4.9.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, + {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, +] + +[[package]] +name = "uc-micro-py" +version = "1.0.3" +description = "Micro subset of unicode data files for linkify-it-py projects." +optional = false +python-versions = ">=3.7" +files = [ + {file = "uc-micro-py-1.0.3.tar.gz", hash = "sha256:d321b92cff673ec58027c04015fcaa8bb1e005478643ff4a500882eaab88c48a"}, + {file = "uc_micro_py-1.0.3-py3-none-any.whl", hash = "sha256:db1dffff340817673d7b466ec86114a9dc0e9d4d9b5ba229d9d60e5c12600cd5"}, +] + +[package.extras] +test = ["coverage", "pytest", "pytest-cov"] + +[[package]] +name = "zipp" +version = "3.17.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, + {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] + +[metadata] +lock-version = "2.0" +python-versions = "~3.8" +content-hash = "811e6bafea3f3565affcadfecaffccf7254d05cea3d8350eb72087a114da5fcf" diff --git a/tools/config_editor/pyproject.toml b/tools/config_editor/pyproject.toml new file mode 100644 index 000000000..fc6e85597 --- /dev/null +++ b/tools/config_editor/pyproject.toml @@ -0,0 +1,19 @@ +[tool.poetry] +name = "config_editor" +version = "0.1.0" +description = "Configuration Editor" +authors = ["Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com>"] +readme = "README.md" + +[tool.poetry.dependencies] +python = "~3.8" +textual = "^0.52.1" +pyinstaller = "^6.4.0" + +[tool.poetry.scripts] +build = "config_editor.pyinstaller:install" +app = "config_editor.main:main" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/tools/config_editor/tests/__init__.py b/tools/config_editor/tests/__init__.py new file mode 100644 index 000000000..e69de29bb From 352e85ae2aa0e50689a0d18501e7e124304241ec Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Tue, 20 Feb 2024 23:09:24 -0300 Subject: [PATCH 02/54] Fix EOF --- tools/config_editor/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/config_editor/.gitignore b/tools/config_editor/.gitignore index 808386fd8..a230a78ae 100644 --- a/tools/config_editor/.gitignore +++ b/tools/config_editor/.gitignore @@ -1,2 +1,2 @@ .venv/ -__pycache__/ \ No newline at end of file +__pycache__/ From e2c051633cf9cab047176e1615c873c752542ff9 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Wed, 21 Feb 2024 10:12:48 -0300 Subject: [PATCH 03/54] Simplify --- tools/config_editor/README.md | 0 .../main.py => config_editor.py} | 0 tools/config_editor/config_editor/__init__.py | 0 .../config_editor/pyinstaller.py | 13 - tools/config_editor/poetry.lock | 319 ------------------ tools/config_editor/pyproject.toml | 19 -- .../{config_editor => }/style.tcss | 0 tools/config_editor/tests/__init__.py | 0 8 files changed, 351 deletions(-) delete mode 100644 tools/config_editor/README.md rename tools/config_editor/{config_editor/main.py => config_editor.py} (100%) delete mode 100644 tools/config_editor/config_editor/__init__.py delete mode 100644 tools/config_editor/config_editor/pyinstaller.py delete mode 100644 tools/config_editor/poetry.lock delete mode 100644 tools/config_editor/pyproject.toml rename tools/config_editor/{config_editor => }/style.tcss (100%) delete mode 100644 tools/config_editor/tests/__init__.py diff --git a/tools/config_editor/README.md b/tools/config_editor/README.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/tools/config_editor/config_editor/main.py b/tools/config_editor/config_editor.py similarity index 100% rename from tools/config_editor/config_editor/main.py rename to tools/config_editor/config_editor.py diff --git a/tools/config_editor/config_editor/__init__.py b/tools/config_editor/config_editor/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tools/config_editor/config_editor/pyinstaller.py b/tools/config_editor/config_editor/pyinstaller.py deleted file mode 100644 index 034ad4188..000000000 --- a/tools/config_editor/config_editor/pyinstaller.py +++ /dev/null @@ -1,13 +0,0 @@ -import PyInstaller.__main__ -from pathlib import Path - -main_folder = Path(__file__).parent.absolute() -path_to_main = str(main_folder / "main.py") - -def install(): - PyInstaller.__main__.run([ - path_to_main, - '--onefile', - '--windowed', - # other pyinstaller options... - ]) diff --git a/tools/config_editor/poetry.lock b/tools/config_editor/poetry.lock deleted file mode 100644 index 4a6b17a91..000000000 --- a/tools/config_editor/poetry.lock +++ /dev/null @@ -1,319 +0,0 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. - -[[package]] -name = "altgraph" -version = "0.17.4" -description = "Python graph (network) package" -optional = false -python-versions = "*" -files = [ - {file = "altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff"}, - {file = "altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406"}, -] - -[[package]] -name = "importlib-metadata" -version = "7.0.1" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, - {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, -] - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] - -[[package]] -name = "linkify-it-py" -version = "2.0.3" -description = "Links recognition library with FULL unicode support." -optional = false -python-versions = ">=3.7" -files = [ - {file = "linkify-it-py-2.0.3.tar.gz", hash = "sha256:68cda27e162e9215c17d786649d1da0021a451bdc436ef9e0fa0ba5234b9b048"}, - {file = "linkify_it_py-2.0.3-py3-none-any.whl", hash = "sha256:6bcbc417b0ac14323382aef5c5192c0075bf8a9d6b41820a2b66371eac6b6d79"}, -] - -[package.dependencies] -uc-micro-py = "*" - -[package.extras] -benchmark = ["pytest", "pytest-benchmark"] -dev = ["black", "flake8", "isort", "pre-commit", "pyproject-flake8"] -doc = ["myst-parser", "sphinx", "sphinx-book-theme"] -test = ["coverage", "pytest", "pytest-cov"] - -[[package]] -name = "macholib" -version = "1.16.3" -description = "Mach-O header analysis and editing" -optional = false -python-versions = "*" -files = [ - {file = "macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c"}, - {file = "macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30"}, -] - -[package.dependencies] -altgraph = ">=0.17" - -[[package]] -name = "markdown-it-py" -version = "3.0.0" -description = "Python port of markdown-it. Markdown parsing, done right!" -optional = false -python-versions = ">=3.8" -files = [ - {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, - {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, -] - -[package.dependencies] -linkify-it-py = {version = ">=1,<3", optional = true, markers = "extra == \"linkify\""} -mdit-py-plugins = {version = "*", optional = true, markers = "extra == \"plugins\""} -mdurl = ">=0.1,<1.0" - -[package.extras] -benchmarking = ["psutil", "pytest", "pytest-benchmark"] -code-style = ["pre-commit (>=3.0,<4.0)"] -compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] -linkify = ["linkify-it-py (>=1,<3)"] -plugins = ["mdit-py-plugins"] -profiling = ["gprof2dot"] -rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] - -[[package]] -name = "mdit-py-plugins" -version = "0.4.0" -description = "Collection of plugins for markdown-it-py" -optional = false -python-versions = ">=3.8" -files = [ - {file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"}, - {file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"}, -] - -[package.dependencies] -markdown-it-py = ">=1.0.0,<4.0.0" - -[package.extras] -code-style = ["pre-commit"] -rtd = ["myst-parser", "sphinx-book-theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] - -[[package]] -name = "mdurl" -version = "0.1.2" -description = "Markdown URL utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, - {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, -] - -[[package]] -name = "packaging" -version = "23.2" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, -] - -[[package]] -name = "pefile" -version = "2023.2.7" -description = "Python PE parsing module" -optional = false -python-versions = ">=3.6.0" -files = [ - {file = "pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6"}, - {file = "pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc"}, -] - -[[package]] -name = "pygments" -version = "2.17.2" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.7" -files = [ - {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, - {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, -] - -[package.extras] -plugins = ["importlib-metadata"] -windows-terminal = ["colorama (>=0.4.6)"] - -[[package]] -name = "pyinstaller" -version = "6.4.0" -description = "PyInstaller bundles a Python application and all its dependencies into a single package." -optional = false -python-versions = "<3.13,>=3.8" -files = [ - {file = "pyinstaller-6.4.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:a2e63fa71784f290bbf79b31b60a27c45b17a18b8c7f910757f9474e0c12c95d"}, - {file = "pyinstaller-6.4.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:3127724d1841f785a9916d7b4cfd9595f359925e9ce7d137a16db8c29ca8453b"}, - {file = "pyinstaller-6.4.0-py3-none-manylinux2014_i686.whl", hash = "sha256:a37f83850cb150ad1e00fe92acecc4d39b8e10162a1850a5836a05fcb2daa870"}, - {file = "pyinstaller-6.4.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:28b98fa3c74602bdc4c5a7698e907f31e714cc40a13f6358082bcbc74ddab35c"}, - {file = "pyinstaller-6.4.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:3ae62cf8858ec4dc54df6fa03d29bc78297e3c87caf532887eae8c3893be0789"}, - {file = "pyinstaller-6.4.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e3e1e6922a4260dcacf6f5655b0ca857451e05ac502d01642935d0f2873ad3c7"}, - {file = "pyinstaller-6.4.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:78fb66ca753ef8becdf059eaa1e764d384cacb8c2ec76800126f8c9ef6d19a50"}, - {file = "pyinstaller-6.4.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:e07cff584600647af7dc279dd04c60cd1b4b1b41947b0753f8fcf1969300a583"}, - {file = "pyinstaller-6.4.0-py3-none-win32.whl", hash = "sha256:c7bc0fbea8a9010484cfa7d3856416003af73271f03ca3da4bc0eaf14680ad17"}, - {file = "pyinstaller-6.4.0-py3-none-win_amd64.whl", hash = "sha256:ec8a08c983e3febb0247893cd9bd59f55b6767a1f649cb41a0a129b8f04ff2cb"}, - {file = "pyinstaller-6.4.0-py3-none-win_arm64.whl", hash = "sha256:11e6da6a6e441379352ee460a8880f2633dac91dac0f5a9eeff5d449d459b046"}, - {file = "pyinstaller-6.4.0.tar.gz", hash = "sha256:1bf608ed947b58614711275a7ff169289b32560dc97ec748ebd5fa8bdec80649"}, -] - -[package.dependencies] -altgraph = "*" -importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} -macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""} -packaging = ">=22.0" -pefile = {version = ">=2022.5.30", markers = "sys_platform == \"win32\""} -pyinstaller-hooks-contrib = ">=2024.0" -pywin32-ctypes = {version = ">=0.2.1", markers = "sys_platform == \"win32\""} -setuptools = ">=42.0.0" - -[package.extras] -completion = ["argcomplete"] -hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"] - -[[package]] -name = "pyinstaller-hooks-contrib" -version = "2024.1" -description = "Community maintained hooks for PyInstaller" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pyinstaller-hooks-contrib-2024.1.tar.gz", hash = "sha256:51a51ea9e1ae6bd5ffa7ec45eba7579624bf4f2472ff56dba0edc186f6ed46a6"}, - {file = "pyinstaller_hooks_contrib-2024.1-py2.py3-none-any.whl", hash = "sha256:131494f9cfce190aaa66ed82e82c78b2723d1720ce64d012fbaf938f4ab01d35"}, -] - -[package.dependencies] -importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} -packaging = ">=22.0" -setuptools = ">=42.0.0" - -[[package]] -name = "pywin32-ctypes" -version = "0.2.2" -description = "A (partial) reimplementation of pywin32 using ctypes/cffi" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60"}, - {file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"}, -] - -[[package]] -name = "rich" -version = "13.7.0" -description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, - {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, -] - -[package.dependencies] -markdown-it-py = ">=2.2.0" -pygments = ">=2.13.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} - -[package.extras] -jupyter = ["ipywidgets (>=7.5.1,<9)"] - -[[package]] -name = "setuptools" -version = "69.1.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "setuptools-69.1.0-py3-none-any.whl", hash = "sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6"}, - {file = "setuptools-69.1.0.tar.gz", hash = "sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "textual" -version = "0.52.1" -description = "Modern Text User Interface framework" -optional = false -python-versions = ">=3.8,<4.0" -files = [ - {file = "textual-0.52.1-py3-none-any.whl", hash = "sha256:960a19df2319482918b4a58736d9552cdc1ab65d170ba0bc15273ce0e1922b7a"}, - {file = "textual-0.52.1.tar.gz", hash = "sha256:4232e5c2b423ed7c63baaeb6030355e14e1de1b9df096c9655b68a1e60e4de5f"}, -] - -[package.dependencies] -markdown-it-py = {version = ">=2.1.0", extras = ["linkify", "plugins"]} -rich = ">=13.3.3" -typing-extensions = ">=4.4.0,<5.0.0" - -[package.extras] -syntax = ["tree-sitter (>=0.20.1,<0.21.0)", "tree_sitter_languages (>=1.7.0)"] - -[[package]] -name = "typing-extensions" -version = "4.9.0" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, - {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, -] - -[[package]] -name = "uc-micro-py" -version = "1.0.3" -description = "Micro subset of unicode data files for linkify-it-py projects." -optional = false -python-versions = ">=3.7" -files = [ - {file = "uc-micro-py-1.0.3.tar.gz", hash = "sha256:d321b92cff673ec58027c04015fcaa8bb1e005478643ff4a500882eaab88c48a"}, - {file = "uc_micro_py-1.0.3-py3-none-any.whl", hash = "sha256:db1dffff340817673d7b466ec86114a9dc0e9d4d9b5ba229d9d60e5c12600cd5"}, -] - -[package.extras] -test = ["coverage", "pytest", "pytest-cov"] - -[[package]] -name = "zipp" -version = "3.17.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] - -[metadata] -lock-version = "2.0" -python-versions = "~3.8" -content-hash = "811e6bafea3f3565affcadfecaffccf7254d05cea3d8350eb72087a114da5fcf" diff --git a/tools/config_editor/pyproject.toml b/tools/config_editor/pyproject.toml deleted file mode 100644 index fc6e85597..000000000 --- a/tools/config_editor/pyproject.toml +++ /dev/null @@ -1,19 +0,0 @@ -[tool.poetry] -name = "config_editor" -version = "0.1.0" -description = "Configuration Editor" -authors = ["Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com>"] -readme = "README.md" - -[tool.poetry.dependencies] -python = "~3.8" -textual = "^0.52.1" -pyinstaller = "^6.4.0" - -[tool.poetry.scripts] -build = "config_editor.pyinstaller:install" -app = "config_editor.main:main" - -[build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" diff --git a/tools/config_editor/config_editor/style.tcss b/tools/config_editor/style.tcss similarity index 100% rename from tools/config_editor/config_editor/style.tcss rename to tools/config_editor/style.tcss diff --git a/tools/config_editor/tests/__init__.py b/tools/config_editor/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 From ed73ad9a04197419762caade621ae8883c7c6618 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Wed, 28 Feb 2024 18:03:21 -0300 Subject: [PATCH 04/54] Test --- tools/config_editor/app.py | 86 +++++++++++++++++++ tools/config_editor/compile.py | 18 ++++ .../{config_editor.py => editor.py} | 33 ++----- tools/config_editor/style.tcss | 70 ++++++++++++++- tools/config_editor/targets.py | 56 ++++++++++++ 5 files changed, 234 insertions(+), 29 deletions(-) create mode 100644 tools/config_editor/app.py create mode 100644 tools/config_editor/compile.py rename tools/config_editor/{config_editor.py => editor.py} (77%) create mode 100644 tools/config_editor/targets.py diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py new file mode 100644 index 000000000..bbdd7a88f --- /dev/null +++ b/tools/config_editor/app.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python + +""" +Arduino Static Libraries Configuration Editor +""" + +from rich.syntax import Syntax +from rich.traceback import Traceback +from rich.console import RenderableType + +from textual.app import App, ComposeResult +from textual.containers import Container +from textual.widgets import Button, Footer, Header, RichLog, Label + +from targets import TargetsScreen +from editor import EditorScreen +from compile import CompileScreen + +target_dict = {} + +class ConfigEditorApp(App): + """Textual config editor app.""" + + ENABLE_COMMAND_PALETTE = False + CSS_PATH = "style.tcss" + SCREENS = { + "targets": TargetsScreen(), + "compile": CompileScreen(), + "editor": EditorScreen(), + } + BINDINGS = [ + ("c", "push_screen('compile')", "Compile"), + ("t", "push_screen('targets', update_targets)", "Change Targets"), + ("o", "push_screen('editor')", "Change Options"), + ("l", "app.toggle_class('RichLog', '-hidden')", "Log"), + ("q", "quit", "Quit"), + ] + + def log_print(self, renderable: RenderableType) -> None: + self.query_one(RichLog).write(renderable) + + def update_targets(self, targets: dict) -> None: + """Update the targets dictionary.""" + self.log_print("Updating targets") + self.log_print(targets) + if targets: + target_dict = targets + + def on_button_pressed(self, event: Button.Pressed) -> None: + """Event handler called when a button is pressed.""" + if event.button.id == "compile-button": + self.log_print("Compile button pressed") + self.push_screen('compile') + elif event.button.id == "targets-button": + self.log_print("Targets button pressed") + self.push_screen('targets', self.update_targets) + elif event.button.id == "options-button": + self.log_print("Options button pressed") + self.push_screen('editor') + elif event.button.id == "quit-button": + self.log_print("Quit button pressed") + quit() + + def compose(self) -> ComposeResult: + """Compose our UI.""" + yield Header() + with Container(id="main-menu-container"): + yield Label("ESP32 Arduino Static Libraries Configuration Editor", id="main-menu-title") + yield Button("Compile", id="compile-button", classes="main-menu-button") + yield Button("Select Targets", id="targets-button", classes="main-menu-button") + yield Button("Change Configuration Options", id="options-button", classes="main-menu-button") + yield Button("Quit", id="quit-button", classes="main-menu-button") + yield RichLog(classes="-hidden", wrap=False, highlight=True, markup=True) + yield Footer() + + def on_mount(self) -> None: + self.title = "Configurator" + self.sub_title = "Main Menu" + self.log_print("[b green]Welcome to the ESP32 Arduino Static Libraries Configuration Editor!") + +def main() -> None: + """Run the app.""" + ConfigEditorApp().run() + +if __name__ == "__main__": + main() diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py new file mode 100644 index 000000000..70d230819 --- /dev/null +++ b/tools/config_editor/compile.py @@ -0,0 +1,18 @@ +from textual.app import ComposeResult +from textual.containers import Container +from textual.screen import Screen +from textual.widgets import Footer, Header, Static, RichLog + +class CompileScreen(Screen): + + def compose(self) -> ComposeResult: + """Compose our UI.""" + yield Header() + with Container(): + yield Static("Compile", id="compile-title") + yield Footer() + + def on_mount(self) -> None: + pass + + diff --git a/tools/config_editor/config_editor.py b/tools/config_editor/editor.py similarity index 77% rename from tools/config_editor/config_editor.py rename to tools/config_editor/editor.py index 27923ce34..15e928dec 100644 --- a/tools/config_editor/config_editor.py +++ b/tools/config_editor/editor.py @@ -1,29 +1,12 @@ -#!/usr/bin/env python - -""" -Arduino-esp32 Libraries Configuration Editor -""" - import sys -from rich.syntax import Syntax -from rich.traceback import Traceback - -from textual.app import App, ComposeResult +from textual.app import ComposeResult from textual.containers import Container, VerticalScroll +from textual.screen import Screen from textual.reactive import var -from textual.widgets import DirectoryTree, Footer, Header, Static - - -class ConfigEditor(App): - """Textual config editor app.""" - - CSS_PATH = "style.tcss" - BINDINGS = [ - ("f", "toggle_files", "Toggle Files"), - ("q", "quit", "Quit"), - ] +from textual.widgets import DirectoryTree, Footer, Header, Static, RichLog +class EditorScreen(Screen): show_tree = var(True) def watch_show_tree(self, show_tree: bool) -> None: @@ -41,6 +24,7 @@ def compose(self) -> ComposeResult: yield Footer() def on_mount(self) -> None: + self.sub_title = "Select a file" self.query_one(DirectoryTree).focus() def on_directory_tree_file_selected( @@ -69,10 +53,3 @@ def action_toggle_files(self) -> None: """Called in response to key binding.""" self.show_tree = not self.show_tree - -def main() -> None: - """Run the app.""" - ConfigEditor().run() - -if __name__ == "__main__": - main() diff --git a/tools/config_editor/style.tcss b/tools/config_editor/style.tcss index 40d1a6312..f0b0fd343 100644 --- a/tools/config_editor/style.tcss +++ b/tools/config_editor/style.tcss @@ -11,7 +11,7 @@ Screen { dock: left; } -ConfigEditor.-show-tree #tree-view { +ConfigEditorApp.-show-tree #tree-view { display: block; max-width: 50%; } @@ -24,3 +24,71 @@ ConfigEditor.-show-tree #tree-view { #code { width: auto; } + +RichLog { + background: $surface; + color: $text; + height: 50vh; + dock: bottom; + layer: notes; + border-top: hkey $primary; + offset-y: 0; + padding: 0 1 1 1; +} + +RichLog:focus { + offset: 0 0 !important; +} + +RichLog.-hidden { + offset-y: 100%; +} + +Button.main-menu-button { + min-width: 100%; + max-width: 0.5fr; +} + +#main-menu-container { + align: center middle; + width: 1fr; +} + +#target-selection-container { + align: center middle; + width: 1fr; +} + +#target-scroll-container { + align: center middle; + max-width: 0.4fr; + min-width: 100%; +} + +#target-button-container { + width: 100%; + max-height: 20%; + min-height: 5; + align: center middle; +} + +.target-button { + margin: 1; + min-width: 100%; + max-width: 0.2fr; + align: center middle; +} + +.target-checkbox { + align: center middle; + width: 100%; + background: rgb(50, 50, 50); +} + +#main-menu-title { + text-align: center; + margin-bottom: 4; + text-style: bold; + color: green; + width: 0.5fr; +} diff --git a/tools/config_editor/targets.py b/tools/config_editor/targets.py new file mode 100644 index 000000000..5c4e04563 --- /dev/null +++ b/tools/config_editor/targets.py @@ -0,0 +1,56 @@ +from textual.app import ComposeResult +from textual.containers import VerticalScroll, Container, Horizontal +from textual.screen import Screen +from textual.widgets import Footer, Header, Checkbox, Button + +temp_target_dict = { + "esp32": True, + "esp32s2": True, + "esp32s3": True, + "esp32c2": True, + "esp32c3": True, + "esp32c6": True, + "esp32h2": True +} + +class TargetsScreen(Screen[dict]): + + def on_button_pressed(self, event: Button.Pressed) -> None: + """Event handler called when a button is pressed.""" + if event.button.id == "save-button": + for checkbox in self.query("Checkbox"): + target_text = checkbox.id[:-9] + temp_target_dict[target_text] = checkbox.value + self.dismiss(temp_target_dict) + elif event.button.id == "select-all-button": + for checkbox in self.query("Checkbox"): + checkbox.value = True + elif event.button.id == "select-none-button": + for checkbox in self.query("Checkbox"): + checkbox.value = False + elif event.button.id == "cancel-button": + self.dismiss({}) + + def compose(self) -> ComposeResult: + yield Header() + with Container(id="target-selection-container"): + with VerticalScroll(id="target-scroll-container"): + yield Checkbox("ESP32", temp_target_dict["esp32"], classes="target-checkbox", id="esp32-checkbox") + yield Checkbox("ESP32-S2", temp_target_dict["esp32s2"], classes="target-checkbox", id="esp32s2-checkbox") + yield Checkbox("ESP32-S3", temp_target_dict["esp32s3"], classes="target-checkbox", id="esp32s3-checkbox") + yield Checkbox("ESP32-C2 (ESP8684)", temp_target_dict["esp32c2"], classes="target-checkbox", id="esp32c2-checkbox") + yield Checkbox("ESP32-C3", temp_target_dict["esp32c3"], classes="target-checkbox", id="esp32c3-checkbox") + yield Checkbox("ESP32-C6", temp_target_dict["esp32c6"], classes="target-checkbox", id="esp32c6-checkbox") + yield Checkbox("ESP32-H2", temp_target_dict["esp32h2"], classes="target-checkbox", id="esp32h2-checkbox") + with Horizontal(id="target-button-container"): + yield Button("Save", id="save-button", classes="target-button") + yield Button("Select All", id="select-all-button", classes="target-button") + yield Button("Select None", id="select-none-button", classes="target-button") + yield Button("Cancel", id="cancel-button", classes="target-button") + yield Footer() + + def on_mount(self) -> None: + self.sub_title = "Target Selection" + self.query_one("#esp32-checkbox", Checkbox).focus() + + From bc48e6bfb175111b4e4659e8b1bdb4e0ae5c1bdb Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Wed, 28 Feb 2024 23:14:58 -0300 Subject: [PATCH 05/54] Fix --- tools/config_editor/app.py | 8 -------- tools/config_editor/compile.py | 1 - tools/config_editor/editor.py | 1 - tools/config_editor/targets.py | 1 - 4 files changed, 11 deletions(-) mode change 100644 => 100755 tools/config_editor/app.py diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py old mode 100644 new mode 100755 index bbdd7a88f..cb61ba379 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -28,13 +28,6 @@ class ConfigEditorApp(App): "compile": CompileScreen(), "editor": EditorScreen(), } - BINDINGS = [ - ("c", "push_screen('compile')", "Compile"), - ("t", "push_screen('targets', update_targets)", "Change Targets"), - ("o", "push_screen('editor')", "Change Options"), - ("l", "app.toggle_class('RichLog', '-hidden')", "Log"), - ("q", "quit", "Quit"), - ] def log_print(self, renderable: RenderableType) -> None: self.query_one(RichLog).write(renderable) @@ -71,7 +64,6 @@ def compose(self) -> ComposeResult: yield Button("Change Configuration Options", id="options-button", classes="main-menu-button") yield Button("Quit", id="quit-button", classes="main-menu-button") yield RichLog(classes="-hidden", wrap=False, highlight=True, markup=True) - yield Footer() def on_mount(self) -> None: self.title = "Configurator" diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py index 70d230819..e92c46dcc 100644 --- a/tools/config_editor/compile.py +++ b/tools/config_editor/compile.py @@ -10,7 +10,6 @@ def compose(self) -> ComposeResult: yield Header() with Container(): yield Static("Compile", id="compile-title") - yield Footer() def on_mount(self) -> None: pass diff --git a/tools/config_editor/editor.py b/tools/config_editor/editor.py index 15e928dec..361c5d0bd 100644 --- a/tools/config_editor/editor.py +++ b/tools/config_editor/editor.py @@ -21,7 +21,6 @@ def compose(self) -> ComposeResult: yield DirectoryTree(path, id="tree-view") with VerticalScroll(id="code-view"): yield Static(id="code", expand=True) - yield Footer() def on_mount(self) -> None: self.sub_title = "Select a file" diff --git a/tools/config_editor/targets.py b/tools/config_editor/targets.py index 5c4e04563..c6467991b 100644 --- a/tools/config_editor/targets.py +++ b/tools/config_editor/targets.py @@ -47,7 +47,6 @@ def compose(self) -> ComposeResult: yield Button("Select All", id="select-all-button", classes="target-button") yield Button("Select None", id="select-none-button", classes="target-button") yield Button("Cancel", id="cancel-button", classes="target-button") - yield Footer() def on_mount(self) -> None: self.sub_title = "Target Selection" From ca29343d4cc89f69f8c4e2076e3590fd83c2b6da Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Thu, 29 Feb 2024 16:47:14 -0300 Subject: [PATCH 06/54] Improve Screens --- tools/config_editor/app.py | 19 ++++++---- tools/config_editor/editor.py | 64 ++++++++++++++++++++-------------- tools/config_editor/style.tcss | 23 +++++++++--- tools/config_editor/targets.py | 46 ++++++++++++------------ 4 files changed, 91 insertions(+), 61 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index cb61ba379..7758c5d20 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -4,8 +4,6 @@ Arduino Static Libraries Configuration Editor """ -from rich.syntax import Syntax -from rich.traceback import Traceback from rich.console import RenderableType from textual.app import App, ComposeResult @@ -16,11 +14,19 @@ from editor import EditorScreen from compile import CompileScreen -target_dict = {} - class ConfigEditorApp(App): """Textual config editor app.""" + target_dict = { + "esp32": True, + "esp32s2": True, + "esp32s3": True, + "esp32c2": True, + "esp32c3": True, + "esp32c6": True, + "esp32h2": True + } + ENABLE_COMMAND_PALETTE = False CSS_PATH = "style.tcss" SCREENS = { @@ -37,7 +43,7 @@ def update_targets(self, targets: dict) -> None: self.log_print("Updating targets") self.log_print(targets) if targets: - target_dict = targets + self.target_dict = targets def on_button_pressed(self, event: Button.Pressed) -> None: """Event handler called when a button is pressed.""" @@ -59,7 +65,7 @@ def compose(self) -> ComposeResult: yield Header() with Container(id="main-menu-container"): yield Label("ESP32 Arduino Static Libraries Configuration Editor", id="main-menu-title") - yield Button("Compile", id="compile-button", classes="main-menu-button") + yield Button("Compile Static Libraries", id="compile-button", classes="main-menu-button") yield Button("Select Targets", id="targets-button", classes="main-menu-button") yield Button("Change Configuration Options", id="options-button", classes="main-menu-button") yield Button("Quit", id="quit-button", classes="main-menu-button") @@ -76,3 +82,4 @@ def main() -> None: if __name__ == "__main__": main() + diff --git a/tools/config_editor/editor.py b/tools/config_editor/editor.py index 361c5d0bd..3366b9ee4 100644 --- a/tools/config_editor/editor.py +++ b/tools/config_editor/editor.py @@ -1,54 +1,64 @@ -import sys - from textual.app import ComposeResult -from textual.containers import Container, VerticalScroll +from textual.containers import Container, VerticalScroll, Horizontal from textual.screen import Screen from textual.reactive import var -from textual.widgets import DirectoryTree, Footer, Header, Static, RichLog +from textual.widgets import DirectoryTree, Footer, Header, TextArea, RichLog, Button class EditorScreen(Screen): - show_tree = var(True) - - def watch_show_tree(self, show_tree: bool) -> None: - """Called when show_tree is modified.""" - self.set_class(show_tree, "-show-tree") + current_file = "" def compose(self) -> ComposeResult: """Compose our UI.""" - path = "./" if len(sys.argv) < 2 else sys.argv[1] + path = "./configs/" yield Header() with Container(): yield DirectoryTree(path, id="tree-view") with VerticalScroll(id="code-view"): - yield Static(id="code", expand=True) + yield TextArea.code_editor("", id="code") + with Horizontal(id="editor-buttons-container"): + yield Button("Save", id="save-editor-button", classes="editor-button") + yield Button("Cancel", id="cancel-editor-button", classes="editor-button") def on_mount(self) -> None: self.sub_title = "Select a file" self.query_one(DirectoryTree).focus() + self.query_one(TextArea).clear() + self.curent_file = "" + + def on_button_pressed(self, event: Button.Pressed) -> None: + """Event handler called when a button is pressed.""" + code_view = self.query_one("#code", TextArea) + if event.button.id == "save-editor-button" and self.curent_file != "": + current_text = code_view.text + try: + file = open(self.curent_file, "w") + file.write(current_text) + file.close() + except Exception: + self.sub_title = "ERROR" + else: + self.sub_title = self.curent_file + self.on_mount() + self.dismiss() + elif event.button.id == "cancel-editor-button": + self.on_mount() + self.dismiss() def on_directory_tree_file_selected( self, event: DirectoryTree.FileSelected ) -> None: """Called when the user click a file in the directory tree.""" event.stop() - code_view = self.query_one("#code", Static) + code_view = self.query_one("#code", TextArea) + code_view.clear() + self.curent_file = str(event.path) try: - syntax = Syntax.from_path( - str(event.path), - line_numbers=True, - word_wrap=False, - indent_guides=True, - theme="github-dark", - ) + file = open(self.curent_file, "r") + file_content = file.read() + file.close() except Exception: - code_view.update(Traceback(theme="github-dark", width=None)) self.sub_title = "ERROR" else: - code_view.update(syntax) - self.query_one("#code-view").scroll_home(animate=False) - self.sub_title = str(event.path) - - def action_toggle_files(self) -> None: - """Called in response to key binding.""" - self.show_tree = not self.show_tree + code_view.insert(file_content) + self.sub_title = self.curent_file diff --git a/tools/config_editor/style.tcss b/tools/config_editor/style.tcss index f0b0fd343..96a496c3d 100644 --- a/tools/config_editor/style.tcss +++ b/tools/config_editor/style.tcss @@ -9,9 +9,6 @@ Screen { width: auto; height: 100%; dock: left; -} - -ConfigEditorApp.-show-tree #tree-view { display: block; max-width: 50%; } @@ -22,7 +19,7 @@ ConfigEditorApp.-show-tree #tree-view { } #code { - width: auto; + width: 100%; } RichLog { @@ -92,3 +89,21 @@ Button.main-menu-button { color: green; width: 0.5fr; } + +.editor-button { + width: 20%; +} + +#save-editor-button { + dock: left; + margin: 1; +} + +#cancel-editor-button { + dock: right; + margin: 1 3; +} + +#editor-buttons-container { + height: 5; +} diff --git a/tools/config_editor/targets.py b/tools/config_editor/targets.py index c6467991b..b65384046 100644 --- a/tools/config_editor/targets.py +++ b/tools/config_editor/targets.py @@ -3,53 +3,51 @@ from textual.screen import Screen from textual.widgets import Footer, Header, Checkbox, Button -temp_target_dict = { - "esp32": True, - "esp32s2": True, - "esp32s3": True, - "esp32c2": True, - "esp32c3": True, - "esp32c6": True, - "esp32h2": True -} - class TargetsScreen(Screen[dict]): + temp_target_dict = { + "esp32": True, + "esp32s2": True, + "esp32s3": True, + "esp32c2": True, + "esp32c3": True, + "esp32c6": True, + "esp32h2": True + } def on_button_pressed(self, event: Button.Pressed) -> None: """Event handler called when a button is pressed.""" - if event.button.id == "save-button": + if event.button.id == "save-target-button": for checkbox in self.query("Checkbox"): target_text = checkbox.id[:-9] - temp_target_dict[target_text] = checkbox.value - self.dismiss(temp_target_dict) + self.temp_target_dict[target_text] = checkbox.value + self.dismiss(self.temp_target_dict) elif event.button.id == "select-all-button": for checkbox in self.query("Checkbox"): checkbox.value = True elif event.button.id == "select-none-button": for checkbox in self.query("Checkbox"): checkbox.value = False - elif event.button.id == "cancel-button": + elif event.button.id == "cancel-target-button": self.dismiss({}) def compose(self) -> ComposeResult: yield Header() with Container(id="target-selection-container"): with VerticalScroll(id="target-scroll-container"): - yield Checkbox("ESP32", temp_target_dict["esp32"], classes="target-checkbox", id="esp32-checkbox") - yield Checkbox("ESP32-S2", temp_target_dict["esp32s2"], classes="target-checkbox", id="esp32s2-checkbox") - yield Checkbox("ESP32-S3", temp_target_dict["esp32s3"], classes="target-checkbox", id="esp32s3-checkbox") - yield Checkbox("ESP32-C2 (ESP8684)", temp_target_dict["esp32c2"], classes="target-checkbox", id="esp32c2-checkbox") - yield Checkbox("ESP32-C3", temp_target_dict["esp32c3"], classes="target-checkbox", id="esp32c3-checkbox") - yield Checkbox("ESP32-C6", temp_target_dict["esp32c6"], classes="target-checkbox", id="esp32c6-checkbox") - yield Checkbox("ESP32-H2", temp_target_dict["esp32h2"], classes="target-checkbox", id="esp32h2-checkbox") + yield Checkbox("ESP32", self.temp_target_dict["esp32"], classes="target-checkbox", id="esp32-checkbox") + yield Checkbox("ESP32-S2", self.temp_target_dict["esp32s2"], classes="target-checkbox", id="esp32s2-checkbox") + yield Checkbox("ESP32-S3", self.temp_target_dict["esp32s3"], classes="target-checkbox", id="esp32s3-checkbox") + yield Checkbox("ESP32-C2 (ESP8684)", self.temp_target_dict["esp32c2"], classes="target-checkbox", id="esp32c2-checkbox") + yield Checkbox("ESP32-C3", self.temp_target_dict["esp32c3"], classes="target-checkbox", id="esp32c3-checkbox") + yield Checkbox("ESP32-C6", self.temp_target_dict["esp32c6"], classes="target-checkbox", id="esp32c6-checkbox") + yield Checkbox("ESP32-H2", self.temp_target_dict["esp32h2"], classes="target-checkbox", id="esp32h2-checkbox") with Horizontal(id="target-button-container"): - yield Button("Save", id="save-button", classes="target-button") + yield Button("Save", id="save-target-button", classes="target-button") yield Button("Select All", id="select-all-button", classes="target-button") yield Button("Select None", id="select-none-button", classes="target-button") - yield Button("Cancel", id="cancel-button", classes="target-button") + yield Button("Cancel", id="cancel-target-button", classes="target-button") def on_mount(self) -> None: self.sub_title = "Target Selection" self.query_one("#esp32-checkbox", Checkbox).focus() - From 4cf89ba1fa2e49e2e6d19fa78446d673e73288c9 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Thu, 29 Feb 2024 16:47:26 -0300 Subject: [PATCH 07/54] Add placeholder Readme --- tools/config_editor/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 tools/config_editor/README.md diff --git a/tools/config_editor/README.md b/tools/config_editor/README.md new file mode 100644 index 000000000..61ccd77e6 --- /dev/null +++ b/tools/config_editor/README.md @@ -0,0 +1 @@ +# Configuration Editor From dc1539a324dfaffcafee737d965d74686ca2a771 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Thu, 29 Feb 2024 16:50:20 -0300 Subject: [PATCH 08/54] Remove unused imports --- tools/config_editor/app.py | 2 +- tools/config_editor/editor.py | 3 +-- tools/config_editor/targets.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index 7758c5d20..b00454c09 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -8,7 +8,7 @@ from textual.app import App, ComposeResult from textual.containers import Container -from textual.widgets import Button, Footer, Header, RichLog, Label +from textual.widgets import Button, Header, RichLog, Label from targets import TargetsScreen from editor import EditorScreen diff --git a/tools/config_editor/editor.py b/tools/config_editor/editor.py index 3366b9ee4..ef63a940b 100644 --- a/tools/config_editor/editor.py +++ b/tools/config_editor/editor.py @@ -1,8 +1,7 @@ from textual.app import ComposeResult from textual.containers import Container, VerticalScroll, Horizontal from textual.screen import Screen -from textual.reactive import var -from textual.widgets import DirectoryTree, Footer, Header, TextArea, RichLog, Button +from textual.widgets import DirectoryTree, Header, TextArea, Button class EditorScreen(Screen): current_file = "" diff --git a/tools/config_editor/targets.py b/tools/config_editor/targets.py index b65384046..c98fb10cc 100644 --- a/tools/config_editor/targets.py +++ b/tools/config_editor/targets.py @@ -1,7 +1,7 @@ from textual.app import ComposeResult from textual.containers import VerticalScroll, Container, Horizontal from textual.screen import Screen -from textual.widgets import Footer, Header, Checkbox, Button +from textual.widgets import Header, Checkbox, Button class TargetsScreen(Screen[dict]): temp_target_dict = { From c142e5b4e2e6f660e5d96a5b3cb302f2d6596e69 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 1 Mar 2024 11:10:15 -0300 Subject: [PATCH 09/54] Fix CWD --- tools/config_editor/app.py | 5 +++++ tools/config_editor/editor.py | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index b00454c09..a662a0e05 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -4,6 +4,8 @@ Arduino Static Libraries Configuration Editor """ +import os + from rich.console import RenderableType from textual.app import App, ComposeResult @@ -17,6 +19,9 @@ class ConfigEditorApp(App): """Textual config editor app.""" + root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) + os.chdir(root_path) + target_dict = { "esp32": True, "esp32s2": True, diff --git a/tools/config_editor/editor.py b/tools/config_editor/editor.py index ef63a940b..c22d593e9 100644 --- a/tools/config_editor/editor.py +++ b/tools/config_editor/editor.py @@ -1,3 +1,5 @@ +import os + from textual.app import ComposeResult from textual.containers import Container, VerticalScroll, Horizontal from textual.screen import Screen @@ -8,7 +10,7 @@ class EditorScreen(Screen): def compose(self) -> ComposeResult: """Compose our UI.""" - path = "./configs/" + path = os.path.join(self.app.root_path, 'configs') yield Header() with Container(): yield DirectoryTree(path, id="tree-view") From 6921aeebe8ec142cd29ae0bffe36445d8ec916bc Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 1 Mar 2024 13:16:11 -0300 Subject: [PATCH 10/54] Fix target selection --- tools/config_editor/app.py | 2 +- tools/config_editor/targets.py | 39 ++++++++++++++++++++-------------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index a662a0e05..6017a2c0a 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -48,7 +48,7 @@ def update_targets(self, targets: dict) -> None: self.log_print("Updating targets") self.log_print(targets) if targets: - self.target_dict = targets + self.target_dict = dict(targets) def on_button_pressed(self, event: Button.Pressed) -> None: """Event handler called when a button is pressed.""" diff --git a/tools/config_editor/targets.py b/tools/config_editor/targets.py index c98fb10cc..f31ae9220 100644 --- a/tools/config_editor/targets.py +++ b/tools/config_editor/targets.py @@ -1,18 +1,15 @@ +from textual import on from textual.app import ComposeResult from textual.containers import VerticalScroll, Container, Horizontal from textual.screen import Screen +from textual.events import ScreenResume from textual.widgets import Header, Checkbox, Button class TargetsScreen(Screen[dict]): - temp_target_dict = { - "esp32": True, - "esp32s2": True, - "esp32s3": True, - "esp32c2": True, - "esp32c3": True, - "esp32c6": True, - "esp32h2": True - } + temp_target_dict = {} + + def update_targets(self) -> None: + self.temp_target_dict = dict(self.app.target_dict) def on_button_pressed(self, event: Button.Pressed) -> None: """Event handler called when a button is pressed.""" @@ -31,16 +28,17 @@ def on_button_pressed(self, event: Button.Pressed) -> None: self.dismiss({}) def compose(self) -> ComposeResult: + self.update_targets() yield Header() with Container(id="target-selection-container"): with VerticalScroll(id="target-scroll-container"): - yield Checkbox("ESP32", self.temp_target_dict["esp32"], classes="target-checkbox", id="esp32-checkbox") - yield Checkbox("ESP32-S2", self.temp_target_dict["esp32s2"], classes="target-checkbox", id="esp32s2-checkbox") - yield Checkbox("ESP32-S3", self.temp_target_dict["esp32s3"], classes="target-checkbox", id="esp32s3-checkbox") - yield Checkbox("ESP32-C2 (ESP8684)", self.temp_target_dict["esp32c2"], classes="target-checkbox", id="esp32c2-checkbox") - yield Checkbox("ESP32-C3", self.temp_target_dict["esp32c3"], classes="target-checkbox", id="esp32c3-checkbox") - yield Checkbox("ESP32-C6", self.temp_target_dict["esp32c6"], classes="target-checkbox", id="esp32c6-checkbox") - yield Checkbox("ESP32-H2", self.temp_target_dict["esp32h2"], classes="target-checkbox", id="esp32h2-checkbox") + yield Checkbox("ESP32", True, classes="target-checkbox", id="esp32-checkbox") + yield Checkbox("ESP32-S2", True, classes="target-checkbox", id="esp32s2-checkbox") + yield Checkbox("ESP32-S3", True, classes="target-checkbox", id="esp32s3-checkbox") + yield Checkbox("ESP32-C2 (ESP8684)", True, classes="target-checkbox", id="esp32c2-checkbox") + yield Checkbox("ESP32-C3", True, classes="target-checkbox", id="esp32c3-checkbox") + yield Checkbox("ESP32-C6", True, classes="target-checkbox", id="esp32c6-checkbox") + yield Checkbox("ESP32-H2", True, classes="target-checkbox", id="esp32h2-checkbox") with Horizontal(id="target-button-container"): yield Button("Save", id="save-target-button", classes="target-button") yield Button("Select All", id="select-all-button", classes="target-button") @@ -51,3 +49,12 @@ def on_mount(self) -> None: self.sub_title = "Target Selection" self.query_one("#esp32-checkbox", Checkbox).focus() + @on(ScreenResume) + def on_resume(self) -> None: + print("Targets screen resumed") + self.update_targets() + print(self.temp_target_dict) + for checkbox in self.query("Checkbox"): + target_text = checkbox.id[:-9] + checkbox.value = self.temp_target_dict[target_text] + From 21092e45401898aa350604624b5a0f9bc8ea0964 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 1 Mar 2024 15:27:46 -0300 Subject: [PATCH 11/54] Misc improvements --- tools/config_editor/app.py | 13 ++++++++----- tools/config_editor/targets.py | 5 ++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index 6017a2c0a..3d19c8f15 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -6,11 +6,14 @@ import os -from rich.console import RenderableType - -from textual.app import App, ComposeResult -from textual.containers import Container -from textual.widgets import Button, Header, RichLog, Label +try: + from rich.console import RenderableType + from textual.app import App, ComposeResult + from textual.containers import Container + from textual.widgets import Button, Header, RichLog, Label +except ImportError: + print("Please install the 'textual-dev' package before running this script.") + quit() from targets import TargetsScreen from editor import EditorScreen diff --git a/tools/config_editor/targets.py b/tools/config_editor/targets.py index f31ae9220..6c3ca9660 100644 --- a/tools/config_editor/targets.py +++ b/tools/config_editor/targets.py @@ -10,6 +10,8 @@ class TargetsScreen(Screen[dict]): def update_targets(self) -> None: self.temp_target_dict = dict(self.app.target_dict) + print("Targets updated:") + print(self.temp_target_dict) def on_button_pressed(self, event: Button.Pressed) -> None: """Event handler called when a button is pressed.""" @@ -28,7 +30,6 @@ def on_button_pressed(self, event: Button.Pressed) -> None: self.dismiss({}) def compose(self) -> ComposeResult: - self.update_targets() yield Header() with Container(id="target-selection-container"): with VerticalScroll(id="target-scroll-container"): @@ -53,8 +54,6 @@ def on_mount(self) -> None: def on_resume(self) -> None: print("Targets screen resumed") self.update_targets() - print(self.temp_target_dict) for checkbox in self.query("Checkbox"): target_text = checkbox.id[:-9] checkbox.value = self.temp_target_dict[target_text] - From 1e986a76e070e1f679937159a4b8746bd4ea8d47 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 1 Mar 2024 16:07:29 -0300 Subject: [PATCH 12/54] WIP compile screen --- tools/config_editor/compile.py | 87 +++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 6 deletions(-) diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py index e92c46dcc..6ef8c3384 100644 --- a/tools/config_editor/compile.py +++ b/tools/config_editor/compile.py @@ -1,17 +1,92 @@ +import sys +import subprocess + +from rich.console import RenderableType + +from textual import on from textual.app import ComposeResult +from textual.events import ScreenResume from textual.containers import Container from textual.screen import Screen -from textual.widgets import Footer, Header, Static, RichLog +from textual.widgets import Header, Static, RichLog, ProgressBar, LoadingIndicator, Button class CompileScreen(Screen): + """Compile screen.""" + + child_process = None + + def print_output(self, renderable: RenderableType) -> None: + self.query_one(RichLog).write(renderable) + + def compile_libs(self) -> None: + print("Starting compilation process") + + arduino_path = "" + + if len(sys.argv) > 1: + #Custom Arduino path + arduino_path = sys.argv[1] + else: + #Default Arduino path + arduino_path = "/arduino-esp32" + + label = self.query_one("#compile-title", Static) + progress_bar = self.query_one(ProgressBar) + target_list = [k for k, v in self.app.target_dict.items() if v == True] + self.child_process = None + + if target_list: + print("Targets selected:") + print(target_list) + # At least one target selected + progress_bar.update(total=len(target_list), progress=0) + for target in target_list: + print("Compiling for " + target.upper()) + label.update("Compiling for " + target.upper()) + self.print_output("\n======== Compiling for " + target.upper() + " ========\n") + #self.child_process = subprocess.Popen(["build.sh", "-c", arduino_path, "-t", target], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True) + self.child_process = subprocess.Popen(["./build.sh", "--help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + while True: + output = self.child_process.stdout.readline() + if output == '' and self.child_process.poll() is not None: + break + if output: + self.print_output(output.strip()) # Update RichLog widget with subprocess output + self.child_process.stdout.close() + if self.child_process.returncode != 0: + print("Compilation failed for " + target.upper()) + print("Return code: " + str(self.child_process.returncode)) + label.update("Compilation failed for " + target.upper()) + break + progress_bar.advance(1) + else: + # No targets selected + print("No targets selected") + label.update("No targets selected") + progress_bar.update(total=1, progress=1) + + def on_button_pressed(self, event: Button.Pressed) -> None: + """Event handler called when a button is pressed.""" + if self.child_process: + print("Terminating child process") + self.child_process.terminate() + self.child_process.wait() + self.dismiss() def compose(self) -> ComposeResult: """Compose our UI.""" yield Header() + with Container(id="compile-container"): + yield Static("Compiling for ...", id="compile-title") + yield ProgressBar(id="compile-progress", show_eta=True) + yield Button("Back", id="compile-back-button", classes="compile-button") with Container(): - yield Static("Compile", id="compile-title") - - def on_mount(self) -> None: - pass - + yield RichLog(markup=True) + @on(ScreenResume) + def on_resume(self) -> None: + print("Compile screen resumed") + log = self.query_one(RichLog) + log.clear() + log.focus() + self.compile_libs() From b21e885142c75f79530874a88588b816183cd29c Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 1 Mar 2024 16:56:25 -0300 Subject: [PATCH 13/54] Update --- tools/config_editor/app.py | 32 ++++++------------ tools/config_editor/compile.py | 50 ++++++++++----------------- tools/config_editor/style.tcss | 7 ++-- tools/config_editor/targets.py | 62 ++++++++++++++++------------------ 4 files changed, 61 insertions(+), 90 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index 3d19c8f15..8d3cd713e 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -25,15 +25,7 @@ class ConfigEditorApp(App): root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) os.chdir(root_path) - target_dict = { - "esp32": True, - "esp32s2": True, - "esp32s3": True, - "esp32c2": True, - "esp32c3": True, - "esp32c6": True, - "esp32h2": True - } + target_str = "all" ENABLE_COMMAND_PALETTE = False CSS_PATH = "style.tcss" @@ -43,29 +35,26 @@ class ConfigEditorApp(App): "editor": EditorScreen(), } - def log_print(self, renderable: RenderableType) -> None: - self.query_one(RichLog).write(renderable) - - def update_targets(self, targets: dict) -> None: + def update_targets(self, targets: str) -> None: """Update the targets dictionary.""" - self.log_print("Updating targets") - self.log_print(targets) + print("Updating targets in app") + print(targets) if targets: - self.target_dict = dict(targets) + self.target_str = str(targets) def on_button_pressed(self, event: Button.Pressed) -> None: """Event handler called when a button is pressed.""" if event.button.id == "compile-button": - self.log_print("Compile button pressed") + print("Compile button pressed") self.push_screen('compile') elif event.button.id == "targets-button": - self.log_print("Targets button pressed") + print("Targets button pressed") self.push_screen('targets', self.update_targets) elif event.button.id == "options-button": - self.log_print("Options button pressed") + print("Options button pressed") self.push_screen('editor') elif event.button.id == "quit-button": - self.log_print("Quit button pressed") + print("Quit button pressed") quit() def compose(self) -> ComposeResult: @@ -77,12 +66,11 @@ def compose(self) -> ComposeResult: yield Button("Select Targets", id="targets-button", classes="main-menu-button") yield Button("Change Configuration Options", id="options-button", classes="main-menu-button") yield Button("Quit", id="quit-button", classes="main-menu-button") - yield RichLog(classes="-hidden", wrap=False, highlight=True, markup=True) def on_mount(self) -> None: self.title = "Configurator" self.sub_title = "Main Menu" - self.log_print("[b green]Welcome to the ESP32 Arduino Static Libraries Configuration Editor!") + print("App mounted") def main() -> None: """Run the app.""" diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py index 6ef8c3384..84b45f74b 100644 --- a/tools/config_editor/compile.py +++ b/tools/config_editor/compile.py @@ -31,39 +31,26 @@ def compile_libs(self) -> None: arduino_path = "/arduino-esp32" label = self.query_one("#compile-title", Static) - progress_bar = self.query_one(ProgressBar) - target_list = [k for k, v in self.app.target_dict.items() if v == True] self.child_process = None + target = self.app.target_str - if target_list: - print("Targets selected:") - print(target_list) - # At least one target selected - progress_bar.update(total=len(target_list), progress=0) - for target in target_list: - print("Compiling for " + target.upper()) - label.update("Compiling for " + target.upper()) - self.print_output("\n======== Compiling for " + target.upper() + " ========\n") - #self.child_process = subprocess.Popen(["build.sh", "-c", arduino_path, "-t", target], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True) - self.child_process = subprocess.Popen(["./build.sh", "--help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - while True: - output = self.child_process.stdout.readline() - if output == '' and self.child_process.poll() is not None: - break - if output: - self.print_output(output.strip()) # Update RichLog widget with subprocess output - self.child_process.stdout.close() - if self.child_process.returncode != 0: - print("Compilation failed for " + target.upper()) - print("Return code: " + str(self.child_process.returncode)) - label.update("Compilation failed for " + target.upper()) - break - progress_bar.advance(1) - else: - # No targets selected - print("No targets selected") - label.update("No targets selected") - progress_bar.update(total=1, progress=1) + print("Compiling for " + target.upper()) + command = ["./build.sh", "-c", arduino_path, "-t", target, "--help"] + label.update("Compiling for " + target.upper()) + self.print_output("======== Compiling for " + target.upper() + " ========") + self.print_output("Running: " + " ".join(command) + "\n") + self.child_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + while True: + output = self.child_process.stdout.readline() + if output == '' and self.child_process.poll() is not None: + break + if output: + self.print_output(output.strip()) # Update RichLog widget with subprocess output + self.child_process.stdout.close() + if self.child_process.returncode != 0: + print("Compilation failed for " + target.upper()) + print("Return code: " + str(self.child_process.returncode)) + label.update("Compilation failed for " + target.upper()) def on_button_pressed(self, event: Button.Pressed) -> None: """Event handler called when a button is pressed.""" @@ -78,7 +65,6 @@ def compose(self) -> ComposeResult: yield Header() with Container(id="compile-container"): yield Static("Compiling for ...", id="compile-title") - yield ProgressBar(id="compile-progress", show_eta=True) yield Button("Back", id="compile-back-button", classes="compile-button") with Container(): yield RichLog(markup=True) diff --git a/tools/config_editor/style.tcss b/tools/config_editor/style.tcss index 96a496c3d..9e74b4bea 100644 --- a/tools/config_editor/style.tcss +++ b/tools/config_editor/style.tcss @@ -26,11 +26,11 @@ RichLog { background: $surface; color: $text; height: 50vh; - dock: bottom; layer: notes; border-top: hkey $primary; offset-y: 0; padding: 0 1 1 1; + margin: 2; } RichLog:focus { @@ -51,14 +51,13 @@ Button.main-menu-button { width: 1fr; } -#target-selection-container { +#target-radioset { align: center middle; - width: 1fr; + width: 0.4fr; } #target-scroll-container { align: center middle; - max-width: 0.4fr; min-width: 100%; } diff --git a/tools/config_editor/targets.py b/tools/config_editor/targets.py index 6c3ca9660..d6c52a5e2 100644 --- a/tools/config_editor/targets.py +++ b/tools/config_editor/targets.py @@ -3,57 +3,55 @@ from textual.containers import VerticalScroll, Container, Horizontal from textual.screen import Screen from textual.events import ScreenResume -from textual.widgets import Header, Checkbox, Button +from textual.widgets import Header, RadioButton, Button, RadioSet -class TargetsScreen(Screen[dict]): - temp_target_dict = {} +class TargetsScreen(Screen[str]): + temp_target_str = "" def update_targets(self) -> None: - self.temp_target_dict = dict(self.app.target_dict) - print("Targets updated:") - print(self.temp_target_dict) + self.temp_target_str = str(self.app.target_str) + print("Targets updated in screen") + print(self.temp_target_str) def on_button_pressed(self, event: Button.Pressed) -> None: """Event handler called when a button is pressed.""" if event.button.id == "save-target-button": - for checkbox in self.query("Checkbox"): - target_text = checkbox.id[:-9] - self.temp_target_dict[target_text] = checkbox.value - self.dismiss(self.temp_target_dict) - elif event.button.id == "select-all-button": - for checkbox in self.query("Checkbox"): - checkbox.value = True - elif event.button.id == "select-none-button": - for checkbox in self.query("Checkbox"): - checkbox.value = False + for radiobutton in self.query("RadioButton"): + target_text = radiobutton.id[:-12] # Remove "-radiobutton" from the id + print(target_text + " " + str(radiobutton.value)) + if radiobutton.value: + self.temp_target_str = target_text + self.dismiss(self.temp_target_str) elif event.button.id == "cancel-target-button": - self.dismiss({}) + self.dismiss("") def compose(self) -> ComposeResult: yield Header() - with Container(id="target-selection-container"): - with VerticalScroll(id="target-scroll-container"): - yield Checkbox("ESP32", True, classes="target-checkbox", id="esp32-checkbox") - yield Checkbox("ESP32-S2", True, classes="target-checkbox", id="esp32s2-checkbox") - yield Checkbox("ESP32-S3", True, classes="target-checkbox", id="esp32s3-checkbox") - yield Checkbox("ESP32-C2 (ESP8684)", True, classes="target-checkbox", id="esp32c2-checkbox") - yield Checkbox("ESP32-C3", True, classes="target-checkbox", id="esp32c3-checkbox") - yield Checkbox("ESP32-C6", True, classes="target-checkbox", id="esp32c6-checkbox") - yield Checkbox("ESP32-H2", True, classes="target-checkbox", id="esp32h2-checkbox") + with VerticalScroll(id="target-scroll-container"): + with RadioSet(id="target-radioset"): + yield RadioButton("All", value=True, classes="target-radiobutton", id="all-radiobutton") + yield RadioButton("ESP32", classes="target-radiobutton", id="esp32-radiobutton") + yield RadioButton("ESP32-S2", classes="target-radiobutton", id="esp32s2-radiobutton") + yield RadioButton("ESP32-S3", classes="target-radiobutton", id="esp32s3-radiobutton") + yield RadioButton("ESP32-C2 (ESP8684)", classes="target-radiobutton", id="esp32c2-radiobutton") + yield RadioButton("ESP32-C3", classes="target-radiobutton", id="esp32c3-radiobutton") + yield RadioButton("ESP32-C6", classes="target-radiobutton", id="esp32c6-radiobutton") + yield RadioButton("ESP32-H2", classes="target-radiobutton", id="esp32h2-radiobutton") with Horizontal(id="target-button-container"): yield Button("Save", id="save-target-button", classes="target-button") - yield Button("Select All", id="select-all-button", classes="target-button") - yield Button("Select None", id="select-none-button", classes="target-button") yield Button("Cancel", id="cancel-target-button", classes="target-button") def on_mount(self) -> None: self.sub_title = "Target Selection" - self.query_one("#esp32-checkbox", Checkbox).focus() + self.query_one("#all-radiobutton", RadioButton).focus() @on(ScreenResume) def on_resume(self) -> None: print("Targets screen resumed") self.update_targets() - for checkbox in self.query("Checkbox"): - target_text = checkbox.id[:-9] - checkbox.value = self.temp_target_dict[target_text] + for radiobutton in self.query("RadioButton"): + target_text = radiobutton.id[:-12] # Remove "-radiobutton" from the id + if target_text == self.temp_target_str: + radiobutton.value = True + else: + radiobutton.value = False From f4701bb520582fa2f5eb31176d294cba7d9b33da Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 1 Mar 2024 17:03:47 -0300 Subject: [PATCH 14/54] Fix command --- tools/config_editor/compile.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py index 84b45f74b..f79458ab9 100644 --- a/tools/config_editor/compile.py +++ b/tools/config_editor/compile.py @@ -35,7 +35,11 @@ def compile_libs(self) -> None: target = self.app.target_str print("Compiling for " + target.upper()) - command = ["./build.sh", "-c", arduino_path, "-t", target, "--help"] + if target == "all": + command = ["./build.sh", "-c", arduino_path] + else: + command = ["./build.sh", "-c", arduino_path, "-t", target] + command.append("--help") label.update("Compiling for " + target.upper()) self.print_output("======== Compiling for " + target.upper() + " ========") self.print_output("Running: " + " ".join(command) + "\n") From 430bb18ed52a281aa6584de5302629eb572b75a0 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 1 Mar 2024 17:34:25 -0300 Subject: [PATCH 15/54] Finish compile screen --- tools/config_editor/compile.py | 12 ++--- tools/config_editor/style.tcss | 94 ++++++++++++++++++---------------- 2 files changed, 55 insertions(+), 51 deletions(-) diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py index f79458ab9..2224508a2 100644 --- a/tools/config_editor/compile.py +++ b/tools/config_editor/compile.py @@ -8,7 +8,7 @@ from textual.events import ScreenResume from textual.containers import Container from textual.screen import Screen -from textual.widgets import Header, Static, RichLog, ProgressBar, LoadingIndicator, Button +from textual.widgets import Header, Static, RichLog, Button class CompileScreen(Screen): """Compile screen.""" @@ -39,7 +39,7 @@ def compile_libs(self) -> None: command = ["./build.sh", "-c", arduino_path] else: command = ["./build.sh", "-c", arduino_path, "-t", target] - command.append("--help") + #command.append("--help") # For testing without compiling label.update("Compiling for " + target.upper()) self.print_output("======== Compiling for " + target.upper() + " ========") self.print_output("Running: " + " ".join(command) + "\n") @@ -67,11 +67,11 @@ def on_button_pressed(self, event: Button.Pressed) -> None: def compose(self) -> ComposeResult: """Compose our UI.""" yield Header() - with Container(id="compile-container"): + with Container(id="compile-log-container"): + yield RichLog(markup=True, id="compile-log") + with Container(id="compile-status-container"): yield Static("Compiling for ...", id="compile-title") - yield Button("Back", id="compile-back-button", classes="compile-button") - with Container(): - yield RichLog(markup=True) + yield Button("Back", id="compile-back-button") @on(ScreenResume) def on_resume(self) -> None: diff --git a/tools/config_editor/style.tcss b/tools/config_editor/style.tcss index 9e74b4bea..db4c643fb 100644 --- a/tools/config_editor/style.tcss +++ b/tools/config_editor/style.tcss @@ -1,56 +1,53 @@ +# General + Screen { background: $surface-darken-1; } -#tree-view { - display: none; - scrollbar-gutter: stable; - overflow: auto; - width: auto; - height: 100%; - dock: left; - display: block; - max-width: 50%; -} +# Main Screen -#code-view { - overflow: auto scroll; - min-width: 100%; +Button.main-menu-button { + min-width: 100%; + max-width: 0.4fr; } -#code { - width: 100%; +#main-menu-container { + align: center middle; + width: 1fr; } -RichLog { - background: $surface; - color: $text; - height: 50vh; - layer: notes; - border-top: hkey $primary; - offset-y: 0; - padding: 0 1 1 1; - margin: 2; +#main-menu-title { + text-align: center; + margin-bottom: 4; + text-style: bold; + color: green; + width: 0.4fr; } -RichLog:focus { - offset: 0 0 !important; +# Compile Screen + +#compile-status-container { + layout: horizontal; + padding: 0 2; + height: 4; } -RichLog.-hidden { - offset-y: 100%; +#compile-title { + dock: left; } -Button.main-menu-button { - min-width: 100%; - max-width: 0.5fr; +#compile-back-button { + dock: right; } -#main-menu-container { - align: center middle; - width: 1fr; +#compile-log { + background: $surface; + padding: 0 1 1 1; + margin: 1 2; } +# Targets Screen + #target-radioset { align: center middle; width: 0.4fr; @@ -58,7 +55,6 @@ Button.main-menu-button { #target-scroll-container { align: center middle; - min-width: 100%; } #target-button-container { @@ -75,18 +71,26 @@ Button.main-menu-button { align: center middle; } -.target-checkbox { - align: center middle; - width: 100%; - background: rgb(50, 50, 50); +# Editor Screen + +#tree-view { + display: none; + scrollbar-gutter: stable; + overflow: auto; + width: auto; + height: 100%; + dock: left; + display: block; + max-width: 50%; } -#main-menu-title { - text-align: center; - margin-bottom: 4; - text-style: bold; - color: green; - width: 0.5fr; +#code-view { + overflow: auto scroll; + min-width: 100%; +} + +#code { + width: 100%; } .editor-button { From 2416cdf84f37eae93356ed6ed712f17839da389c Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 1 Mar 2024 19:48:52 -0300 Subject: [PATCH 16/54] Added comments and improvements --- tools/config_editor/app.py | 36 +++++++++----- tools/config_editor/compile.py | 86 ++++++++++++++++++++-------------- tools/config_editor/editor.py | 60 ++++++++++++++---------- tools/config_editor/targets.py | 37 +++++++++------ 4 files changed, 134 insertions(+), 85 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index 8d3cd713e..a2dbe04f0 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -2,15 +2,23 @@ """ Arduino Static Libraries Configuration Editor + +This is a simple application to configure the static libraries for the ESP32 Arduino core. +It allows the user to select the targets to compile, change the configuration options and compile the libraries. + +The application is built using the 'textual' library, which is a Python library for building text-based user interfaces. + +The first argument to the script is the path to the Arduino core. If no argument is provided, it is assumed that the +Arduino core is located in the default path `/arduino-esp32` (docker image default). + """ import os try: - from rich.console import RenderableType from textual.app import App, ComposeResult from textual.containers import Container - from textual.widgets import Button, Header, RichLog, Label + from textual.widgets import Button, Header, Label except ImportError: print("Please install the 'textual-dev' package before running this script.") quit() @@ -20,11 +28,14 @@ from compile import CompileScreen class ConfigEditorApp(App): - """Textual config editor app.""" + # Main application class - root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) + # Change to the root directory of the app to the root of the project + script_path = os.path.abspath(os.path.dirname(__file__)) + root_path = os.path.abspath(os.path.join(script_path, '..', '..')) os.chdir(root_path) + # Target is set to "all" by default target_str = "all" ENABLE_COMMAND_PALETTE = False @@ -36,14 +47,13 @@ class ConfigEditorApp(App): } def update_targets(self, targets: str) -> None: - """Update the targets dictionary.""" - print("Updating targets in app") - print(targets) + # Callback function to update the targets after returning from the targets screen + print("Updating targets in app, received: " + targets) if targets: self.target_str = str(targets) def on_button_pressed(self, event: Button.Pressed) -> None: - """Event handler called when a button is pressed.""" + # Event handler called when a button is pressed if event.button.id == "compile-button": print("Compile button pressed") self.push_screen('compile') @@ -58,7 +68,7 @@ def on_button_pressed(self, event: Button.Pressed) -> None: quit() def compose(self) -> ComposeResult: - """Compose our UI.""" + # Compose main menu yield Header() with Container(id="main-menu-container"): yield Label("ESP32 Arduino Static Libraries Configuration Editor", id="main-menu-title") @@ -68,14 +78,16 @@ def compose(self) -> ComposeResult: yield Button("Quit", id="quit-button", classes="main-menu-button") def on_mount(self) -> None: + # Event handler called when the app is mounted for the first time self.title = "Configurator" self.sub_title = "Main Menu" - print("App mounted") + print("App started") + def main() -> None: - """Run the app.""" + # Main function to run the app ConfigEditorApp().run() if __name__ == "__main__": + # If this script is run directly, start the app main() - diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py index 2224508a2..aee2ba73b 100644 --- a/tools/config_editor/compile.py +++ b/tools/config_editor/compile.py @@ -1,5 +1,6 @@ import sys import subprocess +import os from rich.console import RenderableType @@ -11,72 +12,89 @@ from textual.widgets import Header, Static, RichLog, Button class CompileScreen(Screen): - """Compile screen.""" + # Compile screen + # Child process running the libraries compilation child_process = None def print_output(self, renderable: RenderableType) -> None: + # Print output to the RichLog widget self.query_one(RichLog).write(renderable) def compile_libs(self) -> None: - print("Starting compilation process") + # Compile the libraries + # Get the Arduino path from the command line arguments or use the default path arduino_path = "" - if len(sys.argv) > 1: - #Custom Arduino path arduino_path = sys.argv[1] else: - #Default Arduino path arduino_path = "/arduino-esp32" label = self.query_one("#compile-title", Static) self.child_process = None target = self.app.target_str - print("Compiling for " + target.upper()) - if target == "all": - command = ["./build.sh", "-c", arduino_path] + if os.path.exists(arduino_path): + print("Starting compilation process. Using Arduino path: " + arduino_path) + + print("Compiling for " + target.upper()) + if target == "all": + command = ["./build.sh", "-c", arduino_path] + else: + command = ["./build.sh", "-c", arduino_path, "-t", target] + #command.append("--help") # For testing without compiling + + label.update("Compiling for " + target.upper()) + self.print_output("======== Compiling for " + target.upper() + " ========") + self.print_output("Running: " + " ".join(command) + "\n") + print("Running: " + " ".join(command)) + self.child_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + while True: + output = self.child_process.stdout.readline() + if output == '' and self.child_process.poll() is not None: + print("Child process finished") + break + if output: + self.print_output(output.strip()) # Update RichLog widget with subprocess output + self.child_process.stdout.close() else: - command = ["./build.sh", "-c", arduino_path, "-t", target] - #command.append("--help") # For testing without compiling - label.update("Compiling for " + target.upper()) - self.print_output("======== Compiling for " + target.upper() + " ========") - self.print_output("Running: " + " ".join(command) + "\n") - self.child_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - while True: - output = self.child_process.stdout.readline() - if output == '' and self.child_process.poll() is not None: - break - if output: - self.print_output(output.strip()) # Update RichLog widget with subprocess output - self.child_process.stdout.close() - if self.child_process.returncode != 0: - print("Compilation failed for " + target.upper()) - print("Return code: " + str(self.child_process.returncode)) + print("Arduino path does not exist: " + arduino_path) + self.print_output("Arduino path does not exist: " + arduino_path) + + if not self.child_process: + print("Compilation failed for " + target.upper() + ". Invalid path to Arduino core.") + label.update("Compilation failed for " + target.upper() + ". Invalid path to Arduino core.") + elif self.child_process.returncode != 0: + print("Compilation failed for " + target.upper() + ". Return code: " + str(self.child_process.returncode)) label.update("Compilation failed for " + target.upper()) + else: + print("Compilation successful for " + target.upper()) + label.update("Compilation successful for " + target.upper()) def on_button_pressed(self, event: Button.Pressed) -> None: - """Event handler called when a button is pressed.""" + # Event handler called when a button is pressed if self.child_process: + # Terminate the child process if it is running print("Terminating child process") self.child_process.terminate() self.child_process.wait() self.dismiss() + @on(ScreenResume) + def on_resume(self) -> None: + # Event handler called every time the screen is activated + print("Compile screen resumed. Clearing logs and starting compilation process") + log = self.query_one(RichLog) + log.clear() + log.focus() + self.compile_libs() + def compose(self) -> ComposeResult: - """Compose our UI.""" + # Compose the compilation screen yield Header() with Container(id="compile-log-container"): yield RichLog(markup=True, id="compile-log") with Container(id="compile-status-container"): yield Static("Compiling for ...", id="compile-title") yield Button("Back", id="compile-back-button") - - @on(ScreenResume) - def on_resume(self) -> None: - print("Compile screen resumed") - log = self.query_one(RichLog) - log.clear() - log.focus() - self.compile_libs() diff --git a/tools/config_editor/editor.py b/tools/config_editor/editor.py index c22d593e9..5b9bf5a12 100644 --- a/tools/config_editor/editor.py +++ b/tools/config_editor/editor.py @@ -1,65 +1,75 @@ import os +from textual import on from textual.app import ComposeResult from textual.containers import Container, VerticalScroll, Horizontal from textual.screen import Screen +from textual.events import ScreenResume from textual.widgets import DirectoryTree, Header, TextArea, Button class EditorScreen(Screen): - current_file = "" - - def compose(self) -> ComposeResult: - """Compose our UI.""" - path = os.path.join(self.app.root_path, 'configs') - yield Header() - with Container(): - yield DirectoryTree(path, id="tree-view") - with VerticalScroll(id="code-view"): - yield TextArea.code_editor("", id="code") - with Horizontal(id="editor-buttons-container"): - yield Button("Save", id="save-editor-button", classes="editor-button") - yield Button("Cancel", id="cancel-editor-button", classes="editor-button") + # Configuration file editor screen - def on_mount(self) -> None: - self.sub_title = "Select a file" - self.query_one(DirectoryTree).focus() - self.query_one(TextArea).clear() - self.curent_file = "" + # Current file being edited + current_file = "" def on_button_pressed(self, event: Button.Pressed) -> None: - """Event handler called when a button is pressed.""" + # Event handler called when a button is pressed code_view = self.query_one("#code", TextArea) if event.button.id == "save-editor-button" and self.curent_file != "": current_text = code_view.text try: + print("Save button pressed. Trying to save file: " + self.curent_file) file = open(self.curent_file, "w") file.write(current_text) file.close() except Exception: + print("Error saving file: " + self.curent_file) self.sub_title = "ERROR" else: + print("File saved: " + self.curent_file) self.sub_title = self.curent_file - self.on_mount() self.dismiss() elif event.button.id == "cancel-editor-button": - self.on_mount() + print("Cancel button pressed") self.dismiss() - def on_directory_tree_file_selected( - self, event: DirectoryTree.FileSelected - ) -> None: - """Called when the user click a file in the directory tree.""" + def on_directory_tree_file_selected(self, event: DirectoryTree.FileSelected) -> None: + # Called when the user click a file in the directory tree event.stop() code_view = self.query_one("#code", TextArea) code_view.clear() self.curent_file = str(event.path) try: + print("Opening file: " + self.curent_file) file = open(self.curent_file, "r") file_content = file.read() file.close() except Exception: + print("Error opening file: " + self.curent_file) self.sub_title = "ERROR" else: + print("File opened: " + self.curent_file) code_view.insert(file_content) self.sub_title = self.curent_file + @on(ScreenResume) + def on_resume(self) -> None: + # Event handler called every time the screen is activated + print("Editor screen resumed. Clearing code view") + self.sub_title = "Select a file" + self.query_one(DirectoryTree).focus() + self.query_one(TextArea).clear() + self.curent_file = "" + + def compose(self) -> ComposeResult: + # Compose editor screen + path = os.path.join(self.app.root_path, 'configs') + yield Header() + with Container(): + yield DirectoryTree(path, id="tree-view") + with VerticalScroll(id="code-view"): + yield TextArea.code_editor("", id="code") + with Horizontal(id="editor-buttons-container"): + yield Button("Save", id="save-editor-button", classes="editor-button") + yield Button("Cancel", id="cancel-editor-button", classes="editor-button") diff --git a/tools/config_editor/targets.py b/tools/config_editor/targets.py index d6c52a5e2..bcc830bef 100644 --- a/tools/config_editor/targets.py +++ b/tools/config_editor/targets.py @@ -6,26 +6,44 @@ from textual.widgets import Header, RadioButton, Button, RadioSet class TargetsScreen(Screen[str]): + # Target selection screen + + # Temporary target string temp_target_str = "" def update_targets(self) -> None: + # Update the targets in the screen self.temp_target_str = str(self.app.target_str) - print("Targets updated in screen") - print(self.temp_target_str) + print("Target updated in screen: " + self.temp_target_str) def on_button_pressed(self, event: Button.Pressed) -> None: - """Event handler called when a button is pressed.""" + # Event handler called when a button is pressed if event.button.id == "save-target-button": for radiobutton in self.query("RadioButton"): target_text = radiobutton.id[:-12] # Remove "-radiobutton" from the id print(target_text + " " + str(radiobutton.value)) if radiobutton.value: self.temp_target_str = target_text + print("Save button pressed. Selected target: " + self.temp_target_str) self.dismiss(self.temp_target_str) elif event.button.id == "cancel-target-button": + print("Cancel button pressed") self.dismiss("") + @on(ScreenResume) + def on_resume(self) -> None: + # Event handler called every time the screen is activated + self.update_targets() + print("Targets screen resumed. Loaded target: " + self.temp_target_str) + for radiobutton in self.query("RadioButton"): + target_text = radiobutton.id[:-12] # Remove "-radiobutton" from the id + if target_text == self.temp_target_str: + radiobutton.value = True + else: + radiobutton.value = False + def compose(self) -> ComposeResult: + # Compose the target selection screen yield Header() with VerticalScroll(id="target-scroll-container"): with RadioSet(id="target-radioset"): @@ -42,16 +60,7 @@ def compose(self) -> ComposeResult: yield Button("Cancel", id="cancel-target-button", classes="target-button") def on_mount(self) -> None: + # Event handler called when the screen is mounted for the first time self.sub_title = "Target Selection" self.query_one("#all-radiobutton", RadioButton).focus() - - @on(ScreenResume) - def on_resume(self) -> None: - print("Targets screen resumed") - self.update_targets() - for radiobutton in self.query("RadioButton"): - target_text = radiobutton.id[:-12] # Remove "-radiobutton" from the id - if target_text == self.temp_target_str: - radiobutton.value = True - else: - radiobutton.value = False + print("Targets screen mounted") From 6e9dd8f8c0aec5672039c549a8de7fcaf5c9f08e Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 1 Mar 2024 21:11:31 -0300 Subject: [PATCH 17/54] Run on task --- tools/config_editor/compile.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py index aee2ba73b..11805fef6 100644 --- a/tools/config_editor/compile.py +++ b/tools/config_editor/compile.py @@ -4,7 +4,7 @@ from rich.console import RenderableType -from textual import on +from textual import on, work from textual.app import ComposeResult from textual.events import ScreenResume from textual.containers import Container @@ -21,7 +21,8 @@ def print_output(self, renderable: RenderableType) -> None: # Print output to the RichLog widget self.query_one(RichLog).write(renderable) - def compile_libs(self) -> None: + @work(exclusive=True) + async def compile_libs(self) -> None: # Compile the libraries # Get the Arduino path from the command line arguments or use the default path @@ -49,9 +50,8 @@ def compile_libs(self) -> None: self.print_output("======== Compiling for " + target.upper() + " ========") self.print_output("Running: " + " ".join(command) + "\n") print("Running: " + " ".join(command)) - self.child_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - while True: - output = self.child_process.stdout.readline() + self.child_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True) + for output in self.child_process.stdout: if output == '' and self.child_process.poll() is not None: print("Child process finished") break @@ -67,6 +67,8 @@ def compile_libs(self) -> None: label.update("Compilation failed for " + target.upper() + ". Invalid path to Arduino core.") elif self.child_process.returncode != 0: print("Compilation failed for " + target.upper() + ". Return code: " + str(self.child_process.returncode)) + self.print_output("Compilation failed for " + target.upper() + ". Return code: " + str(self.child_process.returncode)) + self.print_output("Error: " + self.child_process.stderr.read()) label.update("Compilation failed for " + target.upper()) else: print("Compilation successful for " + target.upper()) From 88da64d1b032fbc801727af99f78cf2ceeb7e6ca Mon Sep 17 00:00:00 2001 From: me-no-dev Date: Sat, 2 Mar 2024 03:01:06 +0200 Subject: [PATCH 18/54] Make style a bit more Arduino-esque --- tools/config_editor/style.tcss | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/config_editor/style.tcss b/tools/config_editor/style.tcss index db4c643fb..da1341207 100644 --- a/tools/config_editor/style.tcss +++ b/tools/config_editor/style.tcss @@ -4,6 +4,13 @@ Screen { background: $surface-darken-1; } +Button { + margin-bottom: 1; + background: rgb(73,158,165); + border: rgb(73,158,165); + text-style: bold; +} + # Main Screen Button.main-menu-button { @@ -20,7 +27,7 @@ Button.main-menu-button { text-align: center; margin-bottom: 4; text-style: bold; - color: green; + color: lightgray; width: 0.4fr; } From c53b7eb3934a68708f63a653529f994aefc84550 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Wed, 6 Mar 2024 19:56:10 -0300 Subject: [PATCH 19/54] Add CLI arguments --- tools/config_editor/app.py | 103 +++++++++++++++++++++++++++++---- tools/config_editor/compile.py | 2 +- tools/config_editor/targets.py | 2 +- 3 files changed, 93 insertions(+), 14 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index a2dbe04f0..d543f2e81 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -6,21 +6,25 @@ This is a simple application to configure the static libraries for the ESP32 Arduino core. It allows the user to select the targets to compile, change the configuration options and compile the libraries. -The application is built using the 'textual' library, which is a Python library for building text-based user interfaces. +The application is built using the "textual" library, which is a Python library for building text-based user interfaces. The first argument to the script is the path to the Arduino core. If no argument is provided, it is assumed that the Arduino core is located in the default path `/arduino-esp32` (docker image default). """ +import argparse import os +import platform + +from pathlib import Path try: from textual.app import App, ComposeResult from textual.containers import Container from textual.widgets import Button, Header, Label except ImportError: - print("Please install the 'textual-dev' package before running this script.") + print("Please install the \"textual-dev\" package before running this script.") quit() from targets import TargetsScreen @@ -32,11 +36,16 @@ class ConfigEditorApp(App): # Change to the root directory of the app to the root of the project script_path = os.path.abspath(os.path.dirname(__file__)) - root_path = os.path.abspath(os.path.join(script_path, '..', '..')) + root_path = os.path.abspath(os.path.join(script_path, "..", "..")) os.chdir(root_path) - # Target is set to "all" by default - target_str = "all" + # Options + option_target = None + option_arduino_path = None + option_arduino_branch = None + option_idf_branch = None + option_idf_commit = None + option_debug_level = None ENABLE_COMMAND_PALETTE = False CSS_PATH = "style.tcss" @@ -50,19 +59,19 @@ def update_targets(self, targets: str) -> None: # Callback function to update the targets after returning from the targets screen print("Updating targets in app, received: " + targets) if targets: - self.target_str = str(targets) + self.option_target = str(targets) def on_button_pressed(self, event: Button.Pressed) -> None: # Event handler called when a button is pressed if event.button.id == "compile-button": print("Compile button pressed") - self.push_screen('compile') + self.push_screen("compile") elif event.button.id == "targets-button": print("Targets button pressed") - self.push_screen('targets', self.update_targets) + self.push_screen("targets", self.update_targets) elif event.button.id == "options-button": print("Options button pressed") - self.push_screen('editor') + self.push_screen("editor") elif event.button.id == "quit-button": print("Quit button pressed") quit() @@ -81,12 +90,82 @@ def on_mount(self) -> None: # Event handler called when the app is mounted for the first time self.title = "Configurator" self.sub_title = "Main Menu" - print("App started") - + print("App started. Initial Options:") + print("Target: " + str(self.option_target)) + print("Arduino Path: " + str(self.option_arduino_path)) + print("Arduino Branch: " + str(self.option_arduino_branch)) + print("IDF Branch: " + str(self.option_idf_branch)) + print("IDF Commit: " + str(self.option_idf_commit)) + print("IDF Debug Level: " + str(self.option_debug_level)) + +def arduino_default_path(): + sys_name = platform.system() + home = str(Path.home()) + if sys_name == "Linux": + return os.path.join(home, "Arduino", "hardware", "espressif", "esp32") + else: # Windows and MacOS + return os.path.join(home, "Documents", "Arduino", "hardware", "espressif", "esp32") def main() -> None: + app = ConfigEditorApp() + + parser = argparse.ArgumentParser(description="Configure and compile the ESP32 Arduino static libraries") + + target_choices = ("all", "esp32", "esp32s2", "esp32s3", "esp32c2", "esp32c3", "esp32c6", "esp32h2") + parser.add_argument("-t", "--target", + metavar="", + type=str, + default="all", + choices=target_choices, + required=False, + help="Target to be compiled. Choose from: " + ", ".join(target_choices)) + + parser.add_argument("-c", "--arduino-path", + metavar="", + type=str, + default=arduino_default_path(), + required=False, + help="Path to arduino-esp32 directory") + + parser.add_argument("-A", "--arduino-branch", + metavar="", + type=str, + required=False, + help="Branch of the arduino-esp32 repository to be used") + + parser.add_argument("-I", "--idf-branch", + metavar="", + type=str, + required=False, + help="Branch of the ESP-IDF repository to be used") + + parser.add_argument("-i", "--idf-commit", + metavar="", + type=str, + required=False, + help="Commit of the ESP-IDF repository to be used") + + debug_level_choices = ("default", "none", "error", "warning", "info", "debug", "verbose") + parser.add_argument("-D", "--debug-level", + metavar="", + type=str, + default="default", + choices=debug_level_choices, + required=False, + help="Debug level to be set to ESP-IDF. Choose from: " + ", ".join(debug_level_choices)) + + args = parser.parse_args() + + # Set the options in the app + app.option_target = args.target + app.option_arduino_path = args.arduino_path + app.option_arduino_branch = args.arduino_branch + app.option_idf_branch = args.idf_branch + app.option_idf_commit = args.idf_commit + app.option_debug_level = args.debug_level + # Main function to run the app - ConfigEditorApp().run() + app.run() if __name__ == "__main__": # If this script is run directly, start the app diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py index 11805fef6..ab915b91c 100644 --- a/tools/config_editor/compile.py +++ b/tools/config_editor/compile.py @@ -34,7 +34,7 @@ async def compile_libs(self) -> None: label = self.query_one("#compile-title", Static) self.child_process = None - target = self.app.target_str + target = self.app.option_target if os.path.exists(arduino_path): print("Starting compilation process. Using Arduino path: " + arduino_path) diff --git a/tools/config_editor/targets.py b/tools/config_editor/targets.py index bcc830bef..d4702584b 100644 --- a/tools/config_editor/targets.py +++ b/tools/config_editor/targets.py @@ -13,7 +13,7 @@ class TargetsScreen(Screen[str]): def update_targets(self) -> None: # Update the targets in the screen - self.temp_target_str = str(self.app.target_str) + self.temp_target_str = str(self.app.option_target) print("Target updated in screen: " + self.temp_target_str) def on_button_pressed(self, event: Button.Pressed) -> None: From 683e548593ff60b2fbb721f32f2c8841593a35fa Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Wed, 6 Mar 2024 20:05:26 -0300 Subject: [PATCH 20/54] Small improvements --- tools/config_editor/app.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index d543f2e81..72c403939 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -35,11 +35,14 @@ class ConfigEditorApp(App): # Main application class # Change to the root directory of the app to the root of the project - script_path = os.path.abspath(os.path.dirname(__file__)) - root_path = os.path.abspath(os.path.join(script_path, "..", "..")) - os.chdir(root_path) + SCRIPT_PATH = os.path.abspath(os.path.dirname(__file__)) + ROOT_PATH = os.path.abspath(os.path.join(SCRIPT_PATH, "..", "..")) + os.chdir(ROOT_PATH) - # Options + # Set the application options + option_enable_copy = True + + # Options to be set by the command line arguments option_target = None option_arduino_path = None option_arduino_branch = None @@ -130,18 +133,21 @@ def main() -> None: parser.add_argument("-A", "--arduino-branch", metavar="", type=str, + default="", required=False, help="Branch of the arduino-esp32 repository to be used") parser.add_argument("-I", "--idf-branch", metavar="", type=str, + default="", required=False, help="Branch of the ESP-IDF repository to be used") parser.add_argument("-i", "--idf-commit", metavar="", type=str, + default="", required=False, help="Commit of the ESP-IDF repository to be used") From e9fbf63f82e238d4a98b2601f8a8d42316e0738c Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Wed, 6 Mar 2024 20:38:51 -0300 Subject: [PATCH 21/54] Rename --- tools/config_editor/app.py | 50 ++++++++----------- tools/config_editor/compile.py | 2 +- .../config_editor/{targets.py => settings.py} | 11 ++-- 3 files changed, 29 insertions(+), 34 deletions(-) rename tools/config_editor/{targets.py => settings.py} (93%) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index 72c403939..e876daf27 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -27,7 +27,7 @@ print("Please install the \"textual-dev\" package before running this script.") quit() -from targets import TargetsScreen +from settings import SettingsScreen from editor import EditorScreen from compile import CompileScreen @@ -40,30 +40,24 @@ class ConfigEditorApp(App): os.chdir(ROOT_PATH) # Set the application options - option_enable_copy = True + setting_enable_copy = True # Options to be set by the command line arguments - option_target = None - option_arduino_path = None - option_arduino_branch = None - option_idf_branch = None - option_idf_commit = None - option_debug_level = None + setting_target = None + setting_arduino_path = None + setting_arduino_branch = None + setting_idf_branch = None + setting_idf_commit = None + setting_debug_level = None ENABLE_COMMAND_PALETTE = False CSS_PATH = "style.tcss" SCREENS = { - "targets": TargetsScreen(), + "settings": SettingsScreen(), "compile": CompileScreen(), "editor": EditorScreen(), } - def update_targets(self, targets: str) -> None: - # Callback function to update the targets after returning from the targets screen - print("Updating targets in app, received: " + targets) - if targets: - self.option_target = str(targets) - def on_button_pressed(self, event: Button.Pressed) -> None: # Event handler called when a button is pressed if event.button.id == "compile-button": @@ -71,7 +65,7 @@ def on_button_pressed(self, event: Button.Pressed) -> None: self.push_screen("compile") elif event.button.id == "targets-button": print("Targets button pressed") - self.push_screen("targets", self.update_targets) + self.push_screen("settings") elif event.button.id == "options-button": print("Options button pressed") self.push_screen("editor") @@ -94,12 +88,12 @@ def on_mount(self) -> None: self.title = "Configurator" self.sub_title = "Main Menu" print("App started. Initial Options:") - print("Target: " + str(self.option_target)) - print("Arduino Path: " + str(self.option_arduino_path)) - print("Arduino Branch: " + str(self.option_arduino_branch)) - print("IDF Branch: " + str(self.option_idf_branch)) - print("IDF Commit: " + str(self.option_idf_commit)) - print("IDF Debug Level: " + str(self.option_debug_level)) + print("Target: " + str(self.setting_target)) + print("Arduino Path: " + str(self.setting_arduino_path)) + print("Arduino Branch: " + str(self.setting_arduino_branch)) + print("IDF Branch: " + str(self.setting_idf_branch)) + print("IDF Commit: " + str(self.setting_idf_commit)) + print("IDF Debug Level: " + str(self.setting_debug_level)) def arduino_default_path(): sys_name = platform.system() @@ -163,12 +157,12 @@ def main() -> None: args = parser.parse_args() # Set the options in the app - app.option_target = args.target - app.option_arduino_path = args.arduino_path - app.option_arduino_branch = args.arduino_branch - app.option_idf_branch = args.idf_branch - app.option_idf_commit = args.idf_commit - app.option_debug_level = args.debug_level + app.setting_target = args.target + app.setting_arduino_path = args.arduino_path + app.setting_arduino_branch = args.arduino_branch + app.setting_idf_branch = args.idf_branch + app.setting_idf_commit = args.idf_commit + app.setting_debug_level = args.debug_level # Main function to run the app app.run() diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py index ab915b91c..7870086d6 100644 --- a/tools/config_editor/compile.py +++ b/tools/config_editor/compile.py @@ -34,7 +34,7 @@ async def compile_libs(self) -> None: label = self.query_one("#compile-title", Static) self.child_process = None - target = self.app.option_target + target = self.app.setting_target if os.path.exists(arduino_path): print("Starting compilation process. Using Arduino path: " + arduino_path) diff --git a/tools/config_editor/targets.py b/tools/config_editor/settings.py similarity index 93% rename from tools/config_editor/targets.py rename to tools/config_editor/settings.py index d4702584b..1826da941 100644 --- a/tools/config_editor/targets.py +++ b/tools/config_editor/settings.py @@ -5,15 +5,15 @@ from textual.events import ScreenResume from textual.widgets import Header, RadioButton, Button, RadioSet -class TargetsScreen(Screen[str]): - # Target selection screen +class SettingsScreen(Screen): + # Settings screen # Temporary target string temp_target_str = "" def update_targets(self) -> None: # Update the targets in the screen - self.temp_target_str = str(self.app.option_target) + self.temp_target_str = str(self.app.setting_target) print("Target updated in screen: " + self.temp_target_str) def on_button_pressed(self, event: Button.Pressed) -> None: @@ -25,10 +25,11 @@ def on_button_pressed(self, event: Button.Pressed) -> None: if radiobutton.value: self.temp_target_str = target_text print("Save button pressed. Selected target: " + self.temp_target_str) - self.dismiss(self.temp_target_str) + self.app.setting_target = str(self.temp_target_str) + self.dismiss() elif event.button.id == "cancel-target-button": print("Cancel button pressed") - self.dismiss("") + self.dismiss() @on(ScreenResume) def on_resume(self) -> None: From 0d6d21a347b6bd312b1ad5a73dd13018274b6926 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Thu, 7 Mar 2024 21:55:47 -0300 Subject: [PATCH 22/54] Add custom widgets --- tools/config_editor/app.py | 25 ++++++----- tools/config_editor/editor.py | 2 +- tools/config_editor/settings.py | 79 +++++++++++++++------------------ tools/config_editor/style.tcss | 18 +++++--- tools/config_editor/widgets.py | 65 +++++++++++++++++++++++++++ 5 files changed, 127 insertions(+), 62 deletions(-) create mode 100644 tools/config_editor/widgets.py diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index e876daf27..9916be60f 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -43,12 +43,12 @@ class ConfigEditorApp(App): setting_enable_copy = True # Options to be set by the command line arguments - setting_target = None - setting_arduino_path = None - setting_arduino_branch = None - setting_idf_branch = None - setting_idf_commit = None - setting_debug_level = None + setting_target = "" + setting_arduino_path = "" + setting_arduino_branch = "" + setting_idf_branch = "" + setting_idf_commit = "" + setting_debug_level = "" ENABLE_COMMAND_PALETTE = False CSS_PATH = "style.tcss" @@ -63,11 +63,11 @@ def on_button_pressed(self, event: Button.Pressed) -> None: if event.button.id == "compile-button": print("Compile button pressed") self.push_screen("compile") - elif event.button.id == "targets-button": - print("Targets button pressed") + elif event.button.id == "settings-button": + print("Settings button pressed") self.push_screen("settings") - elif event.button.id == "options-button": - print("Options button pressed") + elif event.button.id == "editor-button": + print("Editor button pressed") self.push_screen("editor") elif event.button.id == "quit-button": print("Quit button pressed") @@ -79,8 +79,8 @@ def compose(self) -> ComposeResult: with Container(id="main-menu-container"): yield Label("ESP32 Arduino Static Libraries Configuration Editor", id="main-menu-title") yield Button("Compile Static Libraries", id="compile-button", classes="main-menu-button") - yield Button("Select Targets", id="targets-button", classes="main-menu-button") - yield Button("Change Configuration Options", id="options-button", classes="main-menu-button") + yield Button("Change sdkconfig Flags", id="editor-button", classes="main-menu-button") + yield Button("Settings", id="settings-button", classes="main-menu-button") yield Button("Quit", id="quit-button", classes="main-menu-button") def on_mount(self) -> None: @@ -88,6 +88,7 @@ def on_mount(self) -> None: self.title = "Configurator" self.sub_title = "Main Menu" print("App started. Initial Options:") + print("Enable Copy: " + str(self.setting_enable_copy)) print("Target: " + str(self.setting_target)) print("Arduino Path: " + str(self.setting_arduino_path)) print("Arduino Branch: " + str(self.setting_arduino_branch)) diff --git a/tools/config_editor/editor.py b/tools/config_editor/editor.py index 5b9bf5a12..5b9f3917e 100644 --- a/tools/config_editor/editor.py +++ b/tools/config_editor/editor.py @@ -64,7 +64,7 @@ def on_resume(self) -> None: def compose(self) -> ComposeResult: # Compose editor screen - path = os.path.join(self.app.root_path, 'configs') + path = os.path.join(self.app.ROOT_PATH, 'configs') yield Header() with Container(): yield DirectoryTree(path, id="tree-view") diff --git a/tools/config_editor/settings.py b/tools/config_editor/settings.py index 1826da941..c0fa0a1a7 100644 --- a/tools/config_editor/settings.py +++ b/tools/config_editor/settings.py @@ -3,65 +3,60 @@ from textual.containers import VerticalScroll, Container, Horizontal from textual.screen import Screen from textual.events import ScreenResume -from textual.widgets import Header, RadioButton, Button, RadioSet +from textual.widgets import Header, Button, Switch + +from widgets import LabelledInput, LabelledSelect class SettingsScreen(Screen): # Settings screen - # Temporary target string - temp_target_str = "" - - def update_targets(self) -> None: - # Update the targets in the screen - self.temp_target_str = str(self.app.setting_target) - print("Target updated in screen: " + self.temp_target_str) - def on_button_pressed(self, event: Button.Pressed) -> None: # Event handler called when a button is pressed - if event.button.id == "save-target-button": - for radiobutton in self.query("RadioButton"): - target_text = radiobutton.id[:-12] # Remove "-radiobutton" from the id - print(target_text + " " + str(radiobutton.value)) - if radiobutton.value: - self.temp_target_str = target_text - print("Save button pressed. Selected target: " + self.temp_target_str) - self.app.setting_target = str(self.temp_target_str) + if event.button.id == "save-settings-button": + print("Save button pressed") + + # Update the target setting + self.app.setting_target = self.query_one("#target-select", LabelledSelect).value + print("Target setting updated: " + self.app.setting_target) + self.dismiss() - elif event.button.id == "cancel-target-button": + elif event.button.id == "cancel-settings-button": print("Cancel button pressed") self.dismiss() @on(ScreenResume) def on_resume(self) -> None: # Event handler called every time the screen is activated - self.update_targets() - print("Targets screen resumed. Loaded target: " + self.temp_target_str) - for radiobutton in self.query("RadioButton"): - target_text = radiobutton.id[:-12] # Remove "-radiobutton" from the id - if target_text == self.temp_target_str: - radiobutton.value = True - else: - radiobutton.value = False + print("Settings screen resumed. Updating settings.") + + # Update Target selection + self.query_one("#target-select", LabelledSelect).value = self.app.setting_target def compose(self) -> ComposeResult: # Compose the target selection screen yield Header() - with VerticalScroll(id="target-scroll-container"): - with RadioSet(id="target-radioset"): - yield RadioButton("All", value=True, classes="target-radiobutton", id="all-radiobutton") - yield RadioButton("ESP32", classes="target-radiobutton", id="esp32-radiobutton") - yield RadioButton("ESP32-S2", classes="target-radiobutton", id="esp32s2-radiobutton") - yield RadioButton("ESP32-S3", classes="target-radiobutton", id="esp32s3-radiobutton") - yield RadioButton("ESP32-C2 (ESP8684)", classes="target-radiobutton", id="esp32c2-radiobutton") - yield RadioButton("ESP32-C3", classes="target-radiobutton", id="esp32c3-radiobutton") - yield RadioButton("ESP32-C6", classes="target-radiobutton", id="esp32c6-radiobutton") - yield RadioButton("ESP32-H2", classes="target-radiobutton", id="esp32h2-radiobutton") - with Horizontal(id="target-button-container"): - yield Button("Save", id="save-target-button", classes="target-button") - yield Button("Cancel", id="cancel-target-button", classes="target-button") + with VerticalScroll(id="settings-scroll-container"): + target_options = [ + ("All", "all"), + ("ESP32", "esp32"), + ("ESP32-S2", "esp32s2"), + ("ESP32-S3", "esp32s3"), + ("ESP32-C2 (ESP8684)", "esp32c2"), + ("ESP32-C3", "esp32c3"), + ("ESP32-C6", "esp32c6"), + ("ESP32-H2", "esp32h2") + ] + yield LabelledSelect("Compilation Target", target_options, id="target-select", classes="settings-select", allow_blank=False) + with Horizontal(id="settings-enable-copy-container"): + yield Switch(id="enable-copy-switch", classes="settings-switch", value=self.app.setting_enable_copy) + yield LabelledInput("Arduino-esp32 Path", placeholder="Path to your arduino-esp32 installation", value=self.app.setting_arduino_path) + yield LabelledInput("Arduino-esp32 Branch", placeholder="Leave empty to use default", value=self.app.setting_arduino_branch) + + with Horizontal(id="settings-button-container"): + yield Button("Save", id="save-settings-button", classes="settings-button") + yield Button("Cancel", id="cancel-settings-button", classes="settings-button") def on_mount(self) -> None: # Event handler called when the screen is mounted for the first time - self.sub_title = "Target Selection" - self.query_one("#all-radiobutton", RadioButton).focus() - print("Targets screen mounted") + self.sub_title = "Settings" + print("Settings screen mounted") diff --git a/tools/config_editor/style.tcss b/tools/config_editor/style.tcss index da1341207..599989ddd 100644 --- a/tools/config_editor/style.tcss +++ b/tools/config_editor/style.tcss @@ -14,8 +14,8 @@ Button { # Main Screen Button.main-menu-button { - min-width: 100%; - max-width: 0.4fr; + min-width: 100%; + max-width: 0.4fr; } #main-menu-container { @@ -53,31 +53,35 @@ Button.main-menu-button { margin: 1 2; } -# Targets Screen +# Settings Screen -#target-radioset { +#settings-radioset { align: center middle; width: 0.4fr; } -#target-scroll-container { +#settings-scroll-container { align: center middle; } -#target-button-container { +#settings-button-container { width: 100%; max-height: 20%; min-height: 5; align: center middle; } -.target-button { +.settings-button { margin: 1; min-width: 100%; max-width: 0.2fr; align: center middle; } +.settings-labelled-input { + margin: 1; +} + # Editor Screen #tree-view { diff --git a/tools/config_editor/widgets.py b/tools/config_editor/widgets.py new file mode 100644 index 000000000..ce0c79586 --- /dev/null +++ b/tools/config_editor/widgets.py @@ -0,0 +1,65 @@ +from textual.widget import Widget + +from textual.widgets import Input, Label, Select + +class LabelledInput(Widget): + DEFAULT_CSS = """ + LabelledInput { + width: 1fr; + height: 4; + } + LabelledInput Label { + padding-left: 1; + } + """ + + def __init__(self, + label, + *, + placeholder="", + value="", + name=None, + id=None, + classes=None, + disabled=False): + super().__init__(name=name, id=id, classes=classes, disabled=disabled) + self.label = label + self.placeholder = placeholder + self.value = value + + def compose(self): + yield Label(f"{self.label}:") + yield Input(placeholder=self.placeholder, value=self.value) + + +class LabelledSelect(Widget): + DEFAULT_CSS = """ + LabelledSelect { + height: 4; + } + LabelledSelect Label { + padding-left: 1; + } + """ + + def __init__(self, + label, + options, + *, + prompt="Select", + allow_blank=True, + value=Select.BLANK, + name=None, + id=None, + classes=None, + disabled=False): + super().__init__(name=name, id=id, classes=classes, disabled=disabled) + self.label = label + self.options = options + self.value = value + self.prompt = prompt + self.allow_blank = allow_blank + + def compose(self): + yield Label(f"{self.label}:") + yield Select(options=self.options, value=self.value, prompt=self.prompt, allow_blank=self.allow_blank) From bb13cc0d80b6ba3e22afb0d94c7c084df4215b8e Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 8 Mar 2024 18:25:30 -0300 Subject: [PATCH 23/54] Fix widgets --- tools/config_editor/compile.py | 4 +-- tools/config_editor/settings.py | 4 +-- tools/config_editor/style.tcss | 1 + tools/config_editor/widgets.py | 53 +++++++++++++++++++++++++-------- 4 files changed, 46 insertions(+), 16 deletions(-) diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py index 7870086d6..da29d2b51 100644 --- a/tools/config_editor/compile.py +++ b/tools/config_editor/compile.py @@ -21,8 +21,8 @@ def print_output(self, renderable: RenderableType) -> None: # Print output to the RichLog widget self.query_one(RichLog).write(renderable) - @work(exclusive=True) - async def compile_libs(self) -> None: + @work(name="compliation_worker", group="compilation", exclusive=True, thread=True) + def compile_libs(self) -> None: # Compile the libraries # Get the Arduino path from the command line arguments or use the default path diff --git a/tools/config_editor/settings.py b/tools/config_editor/settings.py index c0fa0a1a7..3595e49e4 100644 --- a/tools/config_editor/settings.py +++ b/tools/config_editor/settings.py @@ -16,7 +16,7 @@ def on_button_pressed(self, event: Button.Pressed) -> None: print("Save button pressed") # Update the target setting - self.app.setting_target = self.query_one("#target-select", LabelledSelect).value + self.app.setting_target = self.query_one("#target-select", LabelledSelect).get_select_value() print("Target setting updated: " + self.app.setting_target) self.dismiss() @@ -30,7 +30,7 @@ def on_resume(self) -> None: print("Settings screen resumed. Updating settings.") # Update Target selection - self.query_one("#target-select", LabelledSelect).value = self.app.setting_target + self.query_one("#target-select", LabelledSelect).set_select_value(self.app.setting_target) def compose(self) -> ComposeResult: # Compose the target selection screen diff --git a/tools/config_editor/style.tcss b/tools/config_editor/style.tcss index 599989ddd..bd4abc349 100644 --- a/tools/config_editor/style.tcss +++ b/tools/config_editor/style.tcss @@ -62,6 +62,7 @@ Button.main-menu-button { #settings-scroll-container { align: center middle; + padding: 1; } #settings-button-container { diff --git a/tools/config_editor/widgets.py b/tools/config_editor/widgets.py index ce0c79586..12f949535 100644 --- a/tools/config_editor/widgets.py +++ b/tools/config_editor/widgets.py @@ -13,6 +13,15 @@ class LabelledInput(Widget): } """ + label_widget: Label + input_widget: Input + + def set_input_value(self, value): + self.input_widget.value = value + + def get_input_value(self): + return self.input_widget.value + def __init__(self, label, *, @@ -23,13 +32,15 @@ def __init__(self, classes=None, disabled=False): super().__init__(name=name, id=id, classes=classes, disabled=disabled) - self.label = label - self.placeholder = placeholder - self.value = value + self.__label = label + self.__placeholder = placeholder + self.__init_value = value def compose(self): - yield Label(f"{self.label}:") - yield Input(placeholder=self.placeholder, value=self.value) + self.label_widget = Label(f"{self.__label}:") + self.input_widget = Input(placeholder=self.__placeholder, value=self.__init_value) + yield self.label_widget + yield self.input_widget class LabelledSelect(Widget): @@ -42,6 +53,22 @@ class LabelledSelect(Widget): } """ + label_widget: Label + select_widget: Select + + def set_select_options(self, options): + self.__options = options + self.select_widget.options = options + + def get_select_options(self): + return self.__options + + def set_select_value(self, value): + self.select_widget.value = value + + def get_select_value(self): + return self.select_widget.value + def __init__(self, label, options, @@ -54,12 +81,14 @@ def __init__(self, classes=None, disabled=False): super().__init__(name=name, id=id, classes=classes, disabled=disabled) - self.label = label - self.options = options - self.value = value - self.prompt = prompt - self.allow_blank = allow_blank + self.__label = label + self.__options = options + self.__init_value = value + self.__prompt = prompt + self.__allow_blank = allow_blank def compose(self): - yield Label(f"{self.label}:") - yield Select(options=self.options, value=self.value, prompt=self.prompt, allow_blank=self.allow_blank) + self.label_widget = Label(f"{self.__label}:") + self.select_widget = Select(options=self.__options, value=self.__init_value, prompt=self.__prompt, allow_blank=self.__allow_blank) + yield self.label_widget + yield self.select_widget From b118528ab22f8d1cc42635fbb135212b535c4423 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 8 Mar 2024 20:06:08 -0300 Subject: [PATCH 24/54] Add remaining settings --- tools/config_editor/app.py | 4 +- tools/config_editor/settings.py | 77 ++++++++++++++++++++++++++++----- tools/config_editor/style.tcss | 9 ---- tools/config_editor/widgets.py | 3 +- 4 files changed, 71 insertions(+), 22 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index 9916be60f..8b212857c 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -21,7 +21,7 @@ try: from textual.app import App, ComposeResult - from textual.containers import Container + from textual.containers import VerticalScroll from textual.widgets import Button, Header, Label except ImportError: print("Please install the \"textual-dev\" package before running this script.") @@ -76,7 +76,7 @@ def on_button_pressed(self, event: Button.Pressed) -> None: def compose(self) -> ComposeResult: # Compose main menu yield Header() - with Container(id="main-menu-container"): + with VerticalScroll(id="main-menu-container"): yield Label("ESP32 Arduino Static Libraries Configuration Editor", id="main-menu-title") yield Button("Compile Static Libraries", id="compile-button", classes="main-menu-button") yield Button("Change sdkconfig Flags", id="editor-button", classes="main-menu-button") diff --git a/tools/config_editor/settings.py b/tools/config_editor/settings.py index 3595e49e4..ff27f26a4 100644 --- a/tools/config_editor/settings.py +++ b/tools/config_editor/settings.py @@ -3,22 +3,48 @@ from textual.containers import VerticalScroll, Container, Horizontal from textual.screen import Screen from textual.events import ScreenResume -from textual.widgets import Header, Button, Switch +from textual.widgets import Header, Button, Switch, Label from widgets import LabelledInput, LabelledSelect class SettingsScreen(Screen): # Settings screen + target_select: LabelledSelect + enable_copy_switch: Switch + arduino_path_input: LabelledInput + arduino_branch_input: LabelledInput + idf_branch_input: LabelledInput + idf_commit_input: LabelledInput + idf_debug_select: LabelledSelect + def on_button_pressed(self, event: Button.Pressed) -> None: # Event handler called when a button is pressed if event.button.id == "save-settings-button": print("Save button pressed") - # Update the target setting - self.app.setting_target = self.query_one("#target-select", LabelledSelect).get_select_value() + self.app.setting_target = self.target_select.get_select_value() print("Target setting updated: " + self.app.setting_target) + self.app.setting_enable_copy = self.enable_copy_switch.value + print("Enable copy setting updated: " + str(self.app.setting_enable_copy)) + + if self.enable_copy_switch.value: + self.app.setting_arduino_path = self.arduino_path_input.get_input_value() + print("Arduino path setting updated: " + self.app.setting_arduino_path) + + self.app.setting_arduino_branch = self.arduino_branch_input.get_input_value() + print("Arduino branch setting updated: " + self.app.setting_arduino_branch) + + self.app.setting_idf_branch = self.idf_branch_input.get_input_value() + print("IDF branch setting updated: " + self.app.setting_idf_branch) + + self.app.setting_idf_commit = self.idf_commit_input.get_input_value() + print("IDF commit setting updated: " + self.app.setting_idf_commit) + + self.app.setting_debug_level = self.idf_debug_select.get_select_value() + print("Debug level setting updated: " + self.app.setting_debug_level) + self.dismiss() elif event.button.id == "cancel-settings-button": print("Cancel button pressed") @@ -28,9 +54,13 @@ def on_button_pressed(self, event: Button.Pressed) -> None: def on_resume(self) -> None: # Event handler called every time the screen is activated print("Settings screen resumed. Updating settings.") - - # Update Target selection - self.query_one("#target-select", LabelledSelect).set_select_value(self.app.setting_target) + self.target_select.set_select_value(self.app.setting_target) + self.enable_copy_switch.value = self.app.setting_enable_copy + self.arduino_path_input.set_input_value(self.app.setting_arduino_path) + self.arduino_branch_input.set_input_value(self.app.setting_arduino_branch) + self.idf_branch_input.set_input_value(self.app.setting_idf_branch) + self.idf_commit_input.set_input_value(self.app.setting_idf_commit) + self.idf_debug_select.set_select_value(self.app.setting_debug_level) def compose(self) -> ComposeResult: # Compose the target selection screen @@ -46,11 +76,38 @@ def compose(self) -> ComposeResult: ("ESP32-C6", "esp32c6"), ("ESP32-H2", "esp32h2") ] - yield LabelledSelect("Compilation Target", target_options, id="target-select", classes="settings-select", allow_blank=False) + self.target_select = LabelledSelect("Compilation Target", target_options, allow_blank=False, id="target-select") + yield self.target_select + with Horizontal(id="settings-enable-copy-container"): - yield Switch(id="enable-copy-switch", classes="settings-switch", value=self.app.setting_enable_copy) - yield LabelledInput("Arduino-esp32 Path", placeholder="Path to your arduino-esp32 installation", value=self.app.setting_arduino_path) - yield LabelledInput("Arduino-esp32 Branch", placeholder="Leave empty to use default", value=self.app.setting_arduino_branch) + yield Label("Copy to arduino-esp32 after compilation", id="enable-copy-label") + + self.enable_copy_switch = Switch(value=self.app.setting_enable_copy, id="enable-copy-switch") + yield self.enable_copy_switch + + self.arduino_path_input = LabelledInput("Arduino-esp32 Path", placeholder="Path to your arduino-esp32 installation", value=self.app.setting_arduino_path, id="arduino-path-input") + yield self.arduino_path_input + + self.arduino_branch_input = LabelledInput("Arduino-esp32 Branch", placeholder="Leave empty to use default", value=self.app.setting_arduino_branch, id="arduino-branch-input") + yield self.arduino_branch_input + + self.idf_branch_input = LabelledInput("ESP-IDF Branch", placeholder="Leave empty to use default", value=self.app.setting_idf_branch, id="idf-branch-input") + yield self.idf_branch_input + + self.idf_commit_input = LabelledInput("ESP-IDF Commit", placeholder="Leave empty to use default", value=self.app.setting_idf_commit, id="idf-commit-input") + yield self.idf_commit_input + + debug_options = [ + ("Default", "default"), + ("None", "none"), + ("Error", "error"), + ("Warning", "warning"), + ("Info", "info"), + ("Debug", "debug"), + ("Verbose", "verbose") + ] + self.idf_debug_select = LabelledSelect("ESP-IDF Debug Level", debug_options, allow_blank=False, id="idf-debug-select") + yield self.idf_debug_select with Horizontal(id="settings-button-container"): yield Button("Save", id="save-settings-button", classes="settings-button") diff --git a/tools/config_editor/style.tcss b/tools/config_editor/style.tcss index bd4abc349..89363410e 100644 --- a/tools/config_editor/style.tcss +++ b/tools/config_editor/style.tcss @@ -55,11 +55,6 @@ Button.main-menu-button { # Settings Screen -#settings-radioset { - align: center middle; - width: 0.4fr; -} - #settings-scroll-container { align: center middle; padding: 1; @@ -79,10 +74,6 @@ Button.main-menu-button { align: center middle; } -.settings-labelled-input { - margin: 1; -} - # Editor Screen #tree-view { diff --git a/tools/config_editor/widgets.py b/tools/config_editor/widgets.py index 12f949535..afec3297f 100644 --- a/tools/config_editor/widgets.py +++ b/tools/config_editor/widgets.py @@ -5,8 +5,8 @@ class LabelledInput(Widget): DEFAULT_CSS = """ LabelledInput { - width: 1fr; height: 4; + margin-bottom: 1; } LabelledInput Label { padding-left: 1; @@ -47,6 +47,7 @@ class LabelledSelect(Widget): DEFAULT_CSS = """ LabelledSelect { height: 4; + margin-bottom: 1; } LabelledSelect Label { padding-left: 1; From 9f4becdfcab69cc0853818341a85745c1cb6d91b Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 8 Mar 2024 20:20:36 -0300 Subject: [PATCH 25/54] Improve style --- tools/config_editor/settings.py | 6 +++--- tools/config_editor/style.tcss | 13 ++++++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/tools/config_editor/settings.py b/tools/config_editor/settings.py index ff27f26a4..9cbc160f2 100644 --- a/tools/config_editor/settings.py +++ b/tools/config_editor/settings.py @@ -79,12 +79,12 @@ def compose(self) -> ComposeResult: self.target_select = LabelledSelect("Compilation Target", target_options, allow_blank=False, id="target-select") yield self.target_select - with Horizontal(id="settings-enable-copy-container"): - yield Label("Copy to arduino-esp32 after compilation", id="enable-copy-label") - + with Horizontal(classes="settings-switch-container"): self.enable_copy_switch = Switch(value=self.app.setting_enable_copy, id="enable-copy-switch") yield self.enable_copy_switch + yield Label("Copy to arduino-esp32 after compilation") + self.arduino_path_input = LabelledInput("Arduino-esp32 Path", placeholder="Path to your arduino-esp32 installation", value=self.app.setting_arduino_path, id="arduino-path-input") yield self.arduino_path_input diff --git a/tools/config_editor/style.tcss b/tools/config_editor/style.tcss index 89363410e..c312d5e87 100644 --- a/tools/config_editor/style.tcss +++ b/tools/config_editor/style.tcss @@ -56,7 +56,6 @@ Button.main-menu-button { # Settings Screen #settings-scroll-container { - align: center middle; padding: 1; } @@ -74,6 +73,18 @@ Button.main-menu-button { align: center middle; } +.settings-switch-container { + height: 4; +} + +.settings-switch-container Switch { + margin-right: 2; +} + +.settings-switch-container Label { + margin-top: 1; +} + # Editor Screen #tree-view { From 6a5319ae9510af42adb1a1d8186c415be8b72654 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 8 Mar 2024 20:43:38 -0300 Subject: [PATCH 26/54] Improve compilation screen --- tools/config_editor/compile.py | 67 ++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py index da29d2b51..c41ba6090 100644 --- a/tools/config_editor/compile.py +++ b/tools/config_editor/compile.py @@ -25,46 +25,49 @@ def print_output(self, renderable: RenderableType) -> None: def compile_libs(self) -> None: # Compile the libraries - # Get the Arduino path from the command line arguments or use the default path - arduino_path = "" - if len(sys.argv) > 1: - arduino_path = sys.argv[1] - else: - arduino_path = "/arduino-esp32" - label = self.query_one("#compile-title", Static) self.child_process = None target = self.app.setting_target - if os.path.exists(arduino_path): - print("Starting compilation process. Using Arduino path: " + arduino_path) + print("Compiling for " + target.upper()) + label.update("Compiling for " + target.upper()) + self.print_output("======== Compiling for " + target.upper() + " ========") - print("Compiling for " + target.upper()) - if target == "all": - command = ["./build.sh", "-c", arduino_path] + command = ["./build.sh", "-t", target, "-D", self.app.setting_debug_level] + #command.append("--help") # For testing without compiling + + if self.app.setting_enable_copy: + if os.path.isdir(self.app.setting_arduino_path): + command.extend(["-c", self.app.setting_arduino_path]) else: - command = ["./build.sh", "-c", arduino_path, "-t", target] - #command.append("--help") # For testing without compiling - - label.update("Compiling for " + target.upper()) - self.print_output("======== Compiling for " + target.upper() + " ========") - self.print_output("Running: " + " ".join(command) + "\n") - print("Running: " + " ".join(command)) - self.child_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True) - for output in self.child_process.stdout: - if output == '' and self.child_process.poll() is not None: - print("Child process finished") - break - if output: - self.print_output(output.strip()) # Update RichLog widget with subprocess output - self.child_process.stdout.close() - else: - print("Arduino path does not exist: " + arduino_path) - self.print_output("Arduino path does not exist: " + arduino_path) + print("Invalid path to Arduino core: " + self.app.setting_arduino_path) + self.print_output("Invalid path to Arduino core: " + self.app.setting_arduino_path) + label.update("Invalid path to Arduino core") + return + + if self.app.setting_arduino_branch: + command.extend(["-A", self.app.setting_arduino_branch]) + + if self.app.setting_idf_branch: + command.extend(["-I", self.app.setting_idf_branch]) + + if self.app.setting_idf_commit: + command.extend(["-i", self.app.setting_idf_commit]) + + self.print_output("Running: " + " ".join(command) + "\n") + print("Running: " + " ".join(command)) + self.child_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True) + for output in self.child_process.stdout: + if output == '' and self.child_process.poll() is not None: + print("Child process finished") + break + if output: + self.print_output(output.strip()) # Update RichLog widget with subprocess output + self.child_process.stdout.close() if not self.child_process: - print("Compilation failed for " + target.upper() + ". Invalid path to Arduino core.") - label.update("Compilation failed for " + target.upper() + ". Invalid path to Arduino core.") + print("Compilation failed for " + target.upper() + "Child process failed to start") + label.update("Compilation failed for " + target.upper() + "Child process failed to start") elif self.child_process.returncode != 0: print("Compilation failed for " + target.upper() + ". Return code: " + str(self.child_process.returncode)) self.print_output("Compilation failed for " + target.upper() + ". Return code: " + str(self.child_process.returncode)) From 54f10827db06e38b6d3d7b4ca975c0f54e34109c Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 8 Mar 2024 20:54:58 -0300 Subject: [PATCH 27/54] Handle exception --- tools/config_editor/compile.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py index c41ba6090..1f6630048 100644 --- a/tools/config_editor/compile.py +++ b/tools/config_editor/compile.py @@ -57,18 +57,25 @@ def compile_libs(self) -> None: self.print_output("Running: " + " ".join(command) + "\n") print("Running: " + " ".join(command)) self.child_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True) - for output in self.child_process.stdout: - if output == '' and self.child_process.poll() is not None: - print("Child process finished") - break - if output: - self.print_output(output.strip()) # Update RichLog widget with subprocess output - self.child_process.stdout.close() + try: + for output in self.child_process.stdout: + if output == '' and self.child_process.poll() is not None: + break + if output: + self.print_output(output.strip()) # Update RichLog widget with subprocess output + self.child_process.stdout.close() + except Exception as e: + print("Error reading child process output: " + str(e)) + print("Process might have terminated") if not self.child_process: print("Compilation failed for " + target.upper() + "Child process failed to start") label.update("Compilation failed for " + target.upper() + "Child process failed to start") - elif self.child_process.returncode != 0: + return + else: + self.child_process.wait() + + if self.child_process.returncode != 0: print("Compilation failed for " + target.upper() + ". Return code: " + str(self.child_process.returncode)) self.print_output("Compilation failed for " + target.upper() + ". Return code: " + str(self.child_process.returncode)) self.print_output("Error: " + self.child_process.stderr.read()) From 2d8e4624a59d889369df24100d1d62355ae29879 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 8 Mar 2024 21:07:36 -0300 Subject: [PATCH 28/54] Add no copy argument --- tools/config_editor/app.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index 8b212857c..596664ca9 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -118,12 +118,19 @@ def main() -> None: required=False, help="Target to be compiled. Choose from: " + ", ".join(target_choices)) + parser.add_argument("--no-copy", + type=bool, + action=argparse.BooleanOptionalAction, + default=False, + required=False, + help="Disable copying the compiled libraries to arduino-esp32") + parser.add_argument("-c", "--arduino-path", metavar="", type=str, default=arduino_default_path(), required=False, - help="Path to arduino-esp32 directory") + help="Path to arduino-esp32 directory. Default: " + arduino_default_path()) parser.add_argument("-A", "--arduino-branch", metavar="", @@ -159,6 +166,7 @@ def main() -> None: # Set the options in the app app.setting_target = args.target + app.setting_enable_copy = not args.no_copy app.setting_arduino_path = args.arduino_path app.setting_arduino_branch = args.arduino_branch app.setting_idf_branch = args.idf_branch From f649a80f704df7df863a994c5003c1d42dc4d038 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 8 Mar 2024 21:29:57 -0300 Subject: [PATCH 29/54] Replace "arch" with "uname -m" --- tools/config.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/config.sh b/tools/config.sh index 74de3b8c5..936a9c50c 100755 --- a/tools/config.sh +++ b/tools/config.sh @@ -58,7 +58,7 @@ if [ -d "$IDF_PATH" ]; then fi function get_os(){ - OSBITS=`arch` + OSBITS=`uname -m` if [[ "$OSTYPE" == "linux"* ]]; then if [[ "$OSBITS" == "i686" ]]; then echo "linux32" From 458126531e249779b88daa21df885b2c442fcffc Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 8 Mar 2024 22:22:21 -0300 Subject: [PATCH 30/54] Fix copy argument --- tools/config_editor/app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index 596664ca9..33eb1ae42 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -118,10 +118,10 @@ def main() -> None: required=False, help="Target to be compiled. Choose from: " + ", ".join(target_choices)) - parser.add_argument("--no-copy", + parser.add_argument("--copy", type=bool, action=argparse.BooleanOptionalAction, - default=False, + default=True, required=False, help="Disable copying the compiled libraries to arduino-esp32") @@ -166,7 +166,7 @@ def main() -> None: # Set the options in the app app.setting_target = args.target - app.setting_enable_copy = not args.no_copy + app.setting_enable_copy = args.copy app.setting_arduino_path = args.arduino_path app.setting_arduino_branch = args.arduino_branch app.setting_idf_branch = args.idf_branch From e7358449d4af1002cc43dda8aeb1428348228f7a Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 8 Mar 2024 22:28:17 -0300 Subject: [PATCH 31/54] Fix arg message --- tools/config_editor/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index 33eb1ae42..44855e224 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -123,7 +123,7 @@ def main() -> None: action=argparse.BooleanOptionalAction, default=True, required=False, - help="Disable copying the compiled libraries to arduino-esp32") + help="Enable/disable copying the compiled libraries to arduino-esp32. Enabled by default") parser.add_argument("-c", "--arduino-path", metavar="", From e2de23a3a097715f52bca66ded3ede7342066f20 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 8 Mar 2024 22:59:16 -0300 Subject: [PATCH 32/54] Fix chdir --- tools/config_editor/app.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index 44855e224..926f4bb23 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -34,10 +34,9 @@ class ConfigEditorApp(App): # Main application class - # Change to the root directory of the app to the root of the project + # Set the root and script paths SCRIPT_PATH = os.path.abspath(os.path.dirname(__file__)) ROOT_PATH = os.path.abspath(os.path.join(SCRIPT_PATH, "..", "..")) - os.chdir(ROOT_PATH) # Set the application options setting_enable_copy = True @@ -88,8 +87,10 @@ def on_mount(self) -> None: self.title = "Configurator" self.sub_title = "Main Menu" print("App started. Initial Options:") - print("Enable Copy: " + str(self.setting_enable_copy)) + print("Root path: " + self.ROOT_PATH) + print("Script path: " + self.SCRIPT_PATH) print("Target: " + str(self.setting_target)) + print("Enable Copy: " + str(self.setting_enable_copy)) print("Arduino Path: " + str(self.setting_arduino_path)) print("Arduino Branch: " + str(self.setting_arduino_branch)) print("IDF Branch: " + str(self.setting_idf_branch)) @@ -167,12 +168,15 @@ def main() -> None: # Set the options in the app app.setting_target = args.target app.setting_enable_copy = args.copy - app.setting_arduino_path = args.arduino_path + app.setting_arduino_path = os.path.abspath(args.arduino_path) app.setting_arduino_branch = args.arduino_branch app.setting_idf_branch = args.idf_branch app.setting_idf_commit = args.idf_commit app.setting_debug_level = args.debug_level + # Change to the root directory of the app to the root of the project + os.chdir(app.ROOT_PATH) + # Main function to run the app app.run() From b1e368193841d2f91d8fd42155adad4c2400f97c Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Sat, 9 Mar 2024 00:39:50 -0300 Subject: [PATCH 33/54] Improve header --- tools/config_editor/app.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index 926f4bb23..3bb3ec3e7 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -8,8 +8,16 @@ The application is built using the "textual" library, which is a Python library for building text-based user interfaces. -The first argument to the script is the path to the Arduino core. If no argument is provided, it is assumed that the -Arduino core is located in the default path `/arduino-esp32` (docker image default). +Note that this application still needs the requirements from esp32-arduino-lib-builder to be installed. + +Command line arguments: + -t, --target Target to be compiled. Choose from: all, esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2 + --copy, --no-copy Enable/disable copying the compiled libraries to arduino-esp32. Enabled by default + -c, --arduino-path Path to arduino-esp32 directory. Default: OS dependent + -A, --arduino-branch Branch of the arduino-esp32 repository to be used + -I, --idf-branch Branch of the ESP-IDF repository to be used + -i, --idf-commit Commit of the ESP-IDF repository to be used + -D, --debug-level Debug level to be set to ESP-IDF. Choose from: default, none, error, warning, info, debug, verbose """ From fd5c2fcbebf094f7388da001a1f56d4959e4accc Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Sat, 9 Mar 2024 01:11:25 -0300 Subject: [PATCH 34/54] Properly kill subprocess --- tools/config_editor/compile.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py index 1f6630048..56a94a800 100644 --- a/tools/config_editor/compile.py +++ b/tools/config_editor/compile.py @@ -87,10 +87,11 @@ def compile_libs(self) -> None: def on_button_pressed(self, event: Button.Pressed) -> None: # Event handler called when a button is pressed if self.child_process: - # Terminate the child process if it is running + # Kill the child process if it is running print("Terminating child process") - self.child_process.terminate() + self.child_process.kill() self.child_process.wait() + self.child_process = None self.dismiss() @on(ScreenResume) From cdd170d8519aed478bbfeacf2778c11e7dc34d56 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Sat, 9 Mar 2024 18:55:50 -0300 Subject: [PATCH 35/54] Fix compilation issues --- tools/config_editor/compile.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py index 56a94a800..14bb97c22 100644 --- a/tools/config_editor/compile.py +++ b/tools/config_editor/compile.py @@ -34,7 +34,7 @@ def compile_libs(self) -> None: self.print_output("======== Compiling for " + target.upper() + " ========") command = ["./build.sh", "-t", target, "-D", self.app.setting_debug_level] - #command.append("--help") # For testing without compiling + #command.append("--help") # For testing output without compiling if self.app.setting_enable_copy: if os.path.isdir(self.app.setting_arduino_path): @@ -56,7 +56,7 @@ def compile_libs(self) -> None: self.print_output("Running: " + " ".join(command) + "\n") print("Running: " + " ".join(command)) - self.child_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True) + self.child_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) try: for output in self.child_process.stdout: if output == '' and self.child_process.poll() is not None: @@ -86,12 +86,17 @@ def compile_libs(self) -> None: def on_button_pressed(self, event: Button.Pressed) -> None: # Event handler called when a button is pressed + self.workers.cancel_all() if self.child_process: - # Kill the child process if it is running + # Terminate the child process if it is running print("Terminating child process") - self.child_process.kill() + self.child_process.terminate() + try: + self.child_process.stdout.close() + self.child_process.stderr.close() + except: + pass self.child_process.wait() - self.child_process = None self.dismiss() @on(ScreenResume) From 8662df076d35f5822af328ff334afae29a700871 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Tue, 12 Mar 2024 10:27:32 -0300 Subject: [PATCH 36/54] Hide setting depending on switch state --- tools/config_editor/settings.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/config_editor/settings.py b/tools/config_editor/settings.py index 9cbc160f2..ea59b7286 100644 --- a/tools/config_editor/settings.py +++ b/tools/config_editor/settings.py @@ -56,12 +56,24 @@ def on_resume(self) -> None: print("Settings screen resumed. Updating settings.") self.target_select.set_select_value(self.app.setting_target) self.enable_copy_switch.value = self.app.setting_enable_copy + if self.app.setting_enable_copy: + self.arduino_path_input.visible = True + else: + self.arduino_path_input.visible = False self.arduino_path_input.set_input_value(self.app.setting_arduino_path) self.arduino_branch_input.set_input_value(self.app.setting_arduino_branch) self.idf_branch_input.set_input_value(self.app.setting_idf_branch) self.idf_commit_input.set_input_value(self.app.setting_idf_commit) self.idf_debug_select.set_select_value(self.app.setting_debug_level) + def on_switch_changed(self, event: Switch.Changed) -> None: + # Event handler called when a switch is changed + if event.switch.id == "enable-copy-switch": + if event.switch.value: + self.arduino_path_input.visible = True + else: + self.arduino_path_input.visible = False + def compose(self) -> ComposeResult: # Compose the target selection screen yield Header() From 9d32f2649742639103e71e7c0b5d15379e19a4be Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Tue, 12 Mar 2024 10:33:24 -0300 Subject: [PATCH 37/54] QoL improvements --- tools/config_editor/app.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index 3bb3ec3e7..ad94d17ef 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -6,6 +6,8 @@ This is a simple application to configure the static libraries for the ESP32 Arduino core. It allows the user to select the targets to compile, change the configuration options and compile the libraries. +Requires Python 3.9 or later. + The application is built using the "textual" library, which is a Python library for building text-based user interfaces. Note that this application still needs the requirements from esp32-arduino-lib-builder to be installed. @@ -114,6 +116,9 @@ def arduino_default_path(): return os.path.join(home, "Documents", "Arduino", "hardware", "espressif", "esp32") def main() -> None: + # Set the PYTHONUNBUFFERED environment variable to "1" to disable the output buffering + os.environ['PYTHONUNBUFFERED'] = "1" + app = ConfigEditorApp() parser = argparse.ArgumentParser(description="Configure and compile the ESP32 Arduino static libraries") From 8caa77e033a75d02f5d8f05baf73c82935a433b8 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:00:37 -0300 Subject: [PATCH 38/54] Improve style --- tools/config_editor/compile.py | 68 ++++++++++++++++++++++++--------- tools/config_editor/style.tcss | 70 ++++++++++++++++++++++++++++++++-- 2 files changed, 117 insertions(+), 21 deletions(-) diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py index 14bb97c22..9d8453325 100644 --- a/tools/config_editor/compile.py +++ b/tools/config_editor/compile.py @@ -17,31 +17,55 @@ class CompileScreen(Screen): # Child process running the libraries compilation child_process = None - def print_output(self, renderable: RenderableType) -> None: + log_widget: RichLog + button_widget: Button + + def print_output(self, renderable: RenderableType, style=None) -> None: # Print output to the RichLog widget - self.query_one(RichLog).write(renderable) + if style is None: + self.log_widget.write(renderable) + else: + # Check the available styles at https://rich.readthedocs.io/en/stable/style.html + self.log_widget.write("[" + str(style) + "]" + renderable) + + def print_error(self, error: str) -> None: + # Print error to the RichLog widget + self.log_widget.write("[b bright_red]" + error) + self.button_widget.add_class("-error") + #print("Error: " + error) # For debugging + + def print_success(self, message: str) -> None: + # Print success message to the RichLog widget + self.log_widget.write("[b bright_green]" + message) + self.button_widget.add_class("-success") + #print("Success: " + message) # For debugging + + def print_info(self, message: str) -> None: + # Print info message to the RichLog widget + self.log_widget.write("[b bright_cyan]" + message) + #print("Info: " + message) # For debugging @work(name="compliation_worker", group="compilation", exclusive=True, thread=True) def compile_libs(self) -> None: # Compile the libraries + print("Starting compilation process") label = self.query_one("#compile-title", Static) self.child_process = None target = self.app.setting_target - print("Compiling for " + target.upper()) label.update("Compiling for " + target.upper()) - self.print_output("======== Compiling for " + target.upper() + " ========") + self.print_info("======== Compiling for " + target.upper() + " ========") command = ["./build.sh", "-t", target, "-D", self.app.setting_debug_level] + #command.append("--help") # For testing output without compiling if self.app.setting_enable_copy: if os.path.isdir(self.app.setting_arduino_path): command.extend(["-c", self.app.setting_arduino_path]) else: - print("Invalid path to Arduino core: " + self.app.setting_arduino_path) - self.print_output("Invalid path to Arduino core: " + self.app.setting_arduino_path) + self.print_error("Invalid path to Arduino core: " + self.app.setting_arduino_path) label.update("Invalid path to Arduino core") return @@ -54,8 +78,7 @@ def compile_libs(self) -> None: if self.app.setting_idf_commit: command.extend(["-i", self.app.setting_idf_commit]) - self.print_output("Running: " + " ".join(command) + "\n") - print("Running: " + " ".join(command)) + self.print_info("Running: " + " ".join(command) + "\n") self.child_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) try: for output in self.child_process.stdout: @@ -69,19 +92,25 @@ def compile_libs(self) -> None: print("Process might have terminated") if not self.child_process: - print("Compilation failed for " + target.upper() + "Child process failed to start") + self.print_error("Compilation failed for " + target.upper() + "Child process failed to start") label.update("Compilation failed for " + target.upper() + "Child process failed to start") return else: self.child_process.wait() if self.child_process.returncode != 0: - print("Compilation failed for " + target.upper() + ". Return code: " + str(self.child_process.returncode)) - self.print_output("Compilation failed for " + target.upper() + ". Return code: " + str(self.child_process.returncode)) - self.print_output("Error: " + self.child_process.stderr.read()) + self.print_error("Compilation failed for " + target.upper() + ". Return code: " + str(self.child_process.returncode)) + self.print_error("Errors:") + try: + for error in self.child_process.stderr: + if error: + self.print_error(error.strip()) + self.child_process.stderr.close() + except Exception as e: + print("Error reading child process errors: " + str(e)) label.update("Compilation failed for " + target.upper()) else: - print("Compilation successful for " + target.upper()) + self.print_success("Compilation successful for " + target.upper()) label.update("Compilation successful for " + target.upper()) def on_button_pressed(self, event: Button.Pressed) -> None: @@ -103,16 +132,19 @@ def on_button_pressed(self, event: Button.Pressed) -> None: def on_resume(self) -> None: # Event handler called every time the screen is activated print("Compile screen resumed. Clearing logs and starting compilation process") - log = self.query_one(RichLog) - log.clear() - log.focus() + self.button_widget.remove_class("-error") + self.button_widget.remove_class("-success") + self.log_widget.clear() + self.log_widget.focus() self.compile_libs() def compose(self) -> ComposeResult: # Compose the compilation screen yield Header() with Container(id="compile-log-container"): - yield RichLog(markup=True, id="compile-log") + self.log_widget = RichLog(markup=True, id="compile-log") + yield self.log_widget with Container(id="compile-status-container"): yield Static("Compiling for ...", id="compile-title") - yield Button("Back", id="compile-back-button") + self.button_widget = Button("Back", id="compile-back-button") + yield self.button_widget diff --git a/tools/config_editor/style.tcss b/tools/config_editor/style.tcss index c312d5e87..b51b5806c 100644 --- a/tools/config_editor/style.tcss +++ b/tools/config_editor/style.tcss @@ -5,15 +5,79 @@ Screen { } Button { - margin-bottom: 1; - background: rgb(73,158,165); - border: rgb(73,158,165); + width: auto; + min-width: 16; + height: auto; + color: $text; + border: none; + background: #038c8c; + border-top: tall #026868; + border-bottom: tall #6ab8b8; + text-align: center; + content-align: center middle; text-style: bold; + + &:focus { + text-style: bold reverse; + } + &:hover { + border-top: tall #014444; + border-bottom: tall #3d8080; + background: #025b5b; + color: $text; + } + &.-active { + background: #025b5b; + border-bottom: tall #3d8080; + border-top: tall #014444; + tint: $background 30%; + } + + &.-success { + background: $success; + color: $text; + border-top: tall $success-lighten-2; + border-bottom: tall $success-darken-3; + + &:hover { + background: $success-darken-2; + color: $text; + border-top: tall $success; + } + + &.-active { + background: $success; + border-bottom: tall $success-lighten-2; + border-top: tall $success-darken-2; + } + } + + &.-error { + background: $error; + color: $text; + border-top: tall $error-lighten-2; + border-bottom: tall $error-darken-3; + + &:hover { + background: $error-darken-1; + color: $text; + border-top: tall $error; + } + + &.-active { + background: $error; + border-bottom: tall $error-lighten-2; + border-top: tall $error-darken-2; + } + + } + } # Main Screen Button.main-menu-button { + margin-bottom: 1; min-width: 100%; max-width: 0.4fr; } From 8364e755bc96bfad910656282d5d4f5a104f7e35 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:38:07 -0300 Subject: [PATCH 39/54] Add python version check --- tools/config_editor/app.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index ad94d17ef..45ee113c2 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -26,6 +26,7 @@ import argparse import os import platform +import sys from pathlib import Path @@ -35,7 +36,7 @@ from textual.widgets import Button, Header, Label except ImportError: print("Please install the \"textual-dev\" package before running this script.") - quit() + quit(1) from settings import SettingsScreen from editor import EditorScreen @@ -96,6 +97,7 @@ def on_mount(self) -> None: # Event handler called when the app is mounted for the first time self.title = "Configurator" self.sub_title = "Main Menu" + print("Using Python version: " + sys.version) print("App started. Initial Options:") print("Root path: " + self.ROOT_PATH) print("Script path: " + self.SCRIPT_PATH) @@ -119,6 +121,11 @@ def main() -> None: # Set the PYTHONUNBUFFERED environment variable to "1" to disable the output buffering os.environ['PYTHONUNBUFFERED'] = "1" + # Check Python version + if sys.version_info < (3, 9): + print("This script requires Python 3.9 or later") + quit(1) + app = ConfigEditorApp() parser = argparse.ArgumentParser(description="Configure and compile the ESP32 Arduino static libraries") From d2a0f10e1ae097cb953645bfacae4a9c03f92da9 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Mon, 18 Mar 2024 10:51:21 -0300 Subject: [PATCH 40/54] Separate main menu screen --- tools/config_editor/app.py | 82 +++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index 45ee113c2..00ded39cb 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -33,6 +33,7 @@ try: from textual.app import App, ComposeResult from textual.containers import VerticalScroll + from textual.screen import Screen from textual.widgets import Button, Header, Label except ImportError: print("Please install the \"textual-dev\" package before running this script.") @@ -42,43 +43,20 @@ from editor import EditorScreen from compile import CompileScreen -class ConfigEditorApp(App): - # Main application class - - # Set the root and script paths - SCRIPT_PATH = os.path.abspath(os.path.dirname(__file__)) - ROOT_PATH = os.path.abspath(os.path.join(SCRIPT_PATH, "..", "..")) - - # Set the application options - setting_enable_copy = True - - # Options to be set by the command line arguments - setting_target = "" - setting_arduino_path = "" - setting_arduino_branch = "" - setting_idf_branch = "" - setting_idf_commit = "" - setting_debug_level = "" - - ENABLE_COMMAND_PALETTE = False - CSS_PATH = "style.tcss" - SCREENS = { - "settings": SettingsScreen(), - "compile": CompileScreen(), - "editor": EditorScreen(), - } +class MainScreen(Screen): + # Main screen class def on_button_pressed(self, event: Button.Pressed) -> None: # Event handler called when a button is pressed if event.button.id == "compile-button": print("Compile button pressed") - self.push_screen("compile") + self.app.push_screen("compile") elif event.button.id == "settings-button": print("Settings button pressed") - self.push_screen("settings") + self.app.push_screen("settings") elif event.button.id == "editor-button": print("Editor button pressed") - self.push_screen("editor") + self.app.push_screen("editor") elif event.button.id == "quit-button": print("Quit button pressed") quit() @@ -99,15 +77,45 @@ def on_mount(self) -> None: self.sub_title = "Main Menu" print("Using Python version: " + sys.version) print("App started. Initial Options:") - print("Root path: " + self.ROOT_PATH) - print("Script path: " + self.SCRIPT_PATH) - print("Target: " + str(self.setting_target)) - print("Enable Copy: " + str(self.setting_enable_copy)) - print("Arduino Path: " + str(self.setting_arduino_path)) - print("Arduino Branch: " + str(self.setting_arduino_branch)) - print("IDF Branch: " + str(self.setting_idf_branch)) - print("IDF Commit: " + str(self.setting_idf_commit)) - print("IDF Debug Level: " + str(self.setting_debug_level)) + print("Root path: " + self.app.ROOT_PATH) + print("Script path: " + self.app.SCRIPT_PATH) + print("Target: " + str(self.app.setting_target)) + print("Enable Copy: " + str(self.app.setting_enable_copy)) + print("Arduino Path: " + str(self.app.setting_arduino_path)) + print("Arduino Branch: " + str(self.app.setting_arduino_branch)) + print("IDF Branch: " + str(self.app.setting_idf_branch)) + print("IDF Commit: " + str(self.app.setting_idf_commit)) + print("IDF Debug Level: " + str(self.app.setting_debug_level)) + +class ConfigEditorApp(App): + # Main application class + + # Set the root and script paths + SCRIPT_PATH = os.path.abspath(os.path.dirname(__file__)) + ROOT_PATH = os.path.abspath(os.path.join(SCRIPT_PATH, "..", "..")) + + # Set the application options + setting_enable_copy = True + + # Options to be set by the command line arguments + setting_target = "" + setting_arduino_path = "" + setting_arduino_branch = "" + setting_idf_branch = "" + setting_idf_commit = "" + setting_debug_level = "" + + ENABLE_COMMAND_PALETTE = False + CSS_PATH = "style.tcss" + SCREENS = { + "main": MainScreen(), + "settings": SettingsScreen(), + "compile": CompileScreen(), + "editor": EditorScreen(), + } + + def on_mount(self) -> None: + self.push_screen("main") def arduino_default_path(): sys_name = platform.system() From e90707e3d355ad1dabb3e0943e567c0e2d9c77f1 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Mon, 18 Mar 2024 11:05:54 -0300 Subject: [PATCH 41/54] Fix quit and improve logs --- tools/config_editor/app.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index 00ded39cb..f2ea6eaf4 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -59,7 +59,7 @@ def on_button_pressed(self, event: Button.Pressed) -> None: self.app.push_screen("editor") elif event.button.id == "quit-button": print("Quit button pressed") - quit() + self.app.exit() def compose(self) -> ComposeResult: # Compose main menu @@ -75,17 +75,7 @@ def on_mount(self) -> None: # Event handler called when the app is mounted for the first time self.title = "Configurator" self.sub_title = "Main Menu" - print("Using Python version: " + sys.version) - print("App started. Initial Options:") - print("Root path: " + self.app.ROOT_PATH) - print("Script path: " + self.app.SCRIPT_PATH) - print("Target: " + str(self.app.setting_target)) - print("Enable Copy: " + str(self.app.setting_enable_copy)) - print("Arduino Path: " + str(self.app.setting_arduino_path)) - print("Arduino Branch: " + str(self.app.setting_arduino_branch)) - print("IDF Branch: " + str(self.app.setting_idf_branch)) - print("IDF Commit: " + str(self.app.setting_idf_commit)) - print("IDF Debug Level: " + str(self.app.setting_debug_level)) + print("Main screen mounted.") class ConfigEditorApp(App): # Main application class @@ -115,6 +105,17 @@ class ConfigEditorApp(App): } def on_mount(self) -> None: + print("Application mounted. Initial options:") + print("Python version: " + sys.version) + print("Root path: " + self.ROOT_PATH) + print("Script path: " + self.SCRIPT_PATH) + print("Target: " + str(self.setting_target)) + print("Enable Copy: " + str(self.setting_enable_copy)) + print("Arduino Path: " + str(self.setting_arduino_path)) + print("Arduino Branch: " + str(self.setting_arduino_branch)) + print("IDF Branch: " + str(self.setting_idf_branch)) + print("IDF Commit: " + str(self.setting_idf_commit)) + print("IDF Debug Level: " + str(self.setting_debug_level)) self.push_screen("main") def arduino_default_path(): From 7eef90cb95f633005358178f62e32a8999689716 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Mon, 18 Mar 2024 11:08:34 -0300 Subject: [PATCH 42/54] Propagate exit code --- tools/config_editor/app.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index f2ea6eaf4..f0b547a39 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -209,6 +209,9 @@ def main() -> None: # Main function to run the app app.run() + # Propagate the exit code from the app + sys.exit(app.return_code or 0) + if __name__ == "__main__": # If this script is run directly, start the app main() From febe5fac15f3c5a29eec66c5e726a6eccd56ab7e Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Mon, 18 Mar 2024 11:12:00 -0300 Subject: [PATCH 43/54] Rename quit to exit for consistency --- tools/config_editor/app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index f0b547a39..cd0805553 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -37,7 +37,7 @@ from textual.widgets import Button, Header, Label except ImportError: print("Please install the \"textual-dev\" package before running this script.") - quit(1) + exit(1) from settings import SettingsScreen from editor import EditorScreen @@ -133,7 +133,7 @@ def main() -> None: # Check Python version if sys.version_info < (3, 9): print("This script requires Python 3.9 or later") - quit(1) + exit(1) app = ConfigEditorApp() @@ -210,7 +210,7 @@ def main() -> None: app.run() # Propagate the exit code from the app - sys.exit(app.return_code or 0) + exit(app.return_code or 0) if __name__ == "__main__": # If this script is run directly, start the app From 9fbb3f89f89137950b237fbcb0d8c09f5200b98e Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Mon, 18 Mar 2024 16:49:10 -0300 Subject: [PATCH 44/54] Add readme --- tools/config_editor/README.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/tools/config_editor/README.md b/tools/config_editor/README.md index 61ccd77e6..11bb890e7 100644 --- a/tools/config_editor/README.md +++ b/tools/config_editor/README.md @@ -1 +1,24 @@ -# Configuration Editor +# Arduino Static Libraries Configuration Editor + +This is a simple application to configure the static libraries for the ESP32 Arduino core. +It allows the user to select the targets to compile, change the configuration options and compile the libraries. + +## Requirements + - Python 3.9 or later + - The "textual" library (install it using `pip install textual`) + - The requirements from esp32-arduino-lib-builder + +### WSL +If you are using WSL, it is recommended to use the Windows Terminal to visualize the application. Otherwise, the application layout and colors might not be displayed correctly. +The Windows Terminal can be installed from the Microsoft Store. + +## Usage + +Command line arguments: + -t, --target Target to be compiled. Choose from: all, esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2 + --copy, --no-copy Enable/disable copying the compiled libraries to arduino-esp32. Enabled by default + -c, --arduino-path Path to arduino-esp32 directory. Default: OS dependent + -A, --arduino-branch Branch of the arduino-esp32 repository to be used + -I, --idf-branch Branch of the ESP-IDF repository to be used + -i, --idf-commit Commit of the ESP-IDF repository to be used + -D, --debug-level Debug level to be set to ESP-IDF. Choose from: default, none, error, warning, info, debug, verbose From 8358f5796dac1125230b153172761fd710dd7b59 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Mon, 18 Mar 2024 16:50:31 -0300 Subject: [PATCH 45/54] Improve Readme --- tools/config_editor/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/config_editor/README.md b/tools/config_editor/README.md index 11bb890e7..8fde7e86c 100644 --- a/tools/config_editor/README.md +++ b/tools/config_editor/README.md @@ -14,6 +14,8 @@ The Windows Terminal can be installed from the Microsoft Store. ## Usage +These command line arguments can be used to pre-configure the application: + Command line arguments: -t, --target Target to be compiled. Choose from: all, esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2 --copy, --no-copy Enable/disable copying the compiled libraries to arduino-esp32. Enabled by default From 7ae42f44b6c58acfca0ae5632ff73969c6d57542 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Mon, 18 Mar 2024 16:53:37 -0300 Subject: [PATCH 46/54] Add keybindings --- tools/config_editor/app.py | 16 ++++++++-- tools/config_editor/compile.py | 36 ++++++++++++++-------- tools/config_editor/editor.py | 41 ++++++++++++++++--------- tools/config_editor/settings.py | 54 +++++++++++++++++++-------------- tools/config_editor/style.tcss | 6 ++-- 5 files changed, 95 insertions(+), 58 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index cd0805553..c2dd18d1a 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -32,11 +32,12 @@ try: from textual.app import App, ComposeResult + from textual.binding import Binding from textual.containers import VerticalScroll from textual.screen import Screen - from textual.widgets import Button, Header, Label + from textual.widgets import Button, Header, Label, Footer except ImportError: - print("Please install the \"textual-dev\" package before running this script.") + print("Please install the \"textual\" package before running this script.") exit(1) from settings import SettingsScreen @@ -46,6 +47,14 @@ class MainScreen(Screen): # Main screen class + # Set the key bindings + BINDINGS = [ + Binding("c", "app.push_screen('compile')", "Compile"), + Binding("e", "app.push_screen('editor')", "Editor"), + Binding("s", "app.push_screen('settings')", "Settings"), + Binding("q", "app.quit", "Quit"), + ] + def on_button_pressed(self, event: Button.Pressed) -> None: # Event handler called when a button is pressed if event.button.id == "compile-button": @@ -67,9 +76,10 @@ def compose(self) -> ComposeResult: with VerticalScroll(id="main-menu-container"): yield Label("ESP32 Arduino Static Libraries Configuration Editor", id="main-menu-title") yield Button("Compile Static Libraries", id="compile-button", classes="main-menu-button") - yield Button("Change sdkconfig Flags", id="editor-button", classes="main-menu-button") + yield Button("Sdkconfig Editor", id="editor-button", classes="main-menu-button") yield Button("Settings", id="settings-button", classes="main-menu-button") yield Button("Quit", id="quit-button", classes="main-menu-button") + yield Footer() def on_mount(self) -> None: # Event handler called when the app is mounted for the first time diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py index 9d8453325..3eefea62e 100644 --- a/tools/config_editor/compile.py +++ b/tools/config_editor/compile.py @@ -6,20 +6,40 @@ from textual import on, work from textual.app import ComposeResult +from textual.binding import Binding from textual.events import ScreenResume from textual.containers import Container from textual.screen import Screen -from textual.widgets import Header, Static, RichLog, Button +from textual.widgets import Header, Static, RichLog, Button, Footer class CompileScreen(Screen): # Compile screen + # Set the key bindings + BINDINGS = [ + Binding("escape", "back", "Back") + ] + # Child process running the libraries compilation child_process = None log_widget: RichLog button_widget: Button + def action_back(self) -> None: + self.workers.cancel_all() + if self.child_process: + # Terminate the child process if it is running + print("Terminating child process") + self.child_process.terminate() + try: + self.child_process.stdout.close() + self.child_process.stderr.close() + except: + pass + self.child_process.wait() + self.dismiss() + def print_output(self, renderable: RenderableType, style=None) -> None: # Print output to the RichLog widget if style is None: @@ -115,18 +135,7 @@ def compile_libs(self) -> None: def on_button_pressed(self, event: Button.Pressed) -> None: # Event handler called when a button is pressed - self.workers.cancel_all() - if self.child_process: - # Terminate the child process if it is running - print("Terminating child process") - self.child_process.terminate() - try: - self.child_process.stdout.close() - self.child_process.stderr.close() - except: - pass - self.child_process.wait() - self.dismiss() + self.action_back() @on(ScreenResume) def on_resume(self) -> None: @@ -148,3 +157,4 @@ def compose(self) -> ComposeResult: yield Static("Compiling for ...", id="compile-title") self.button_widget = Button("Back", id="compile-back-button") yield self.button_widget + yield Footer() diff --git a/tools/config_editor/editor.py b/tools/config_editor/editor.py index 5b9f3917e..87217f49d 100644 --- a/tools/config_editor/editor.py +++ b/tools/config_editor/editor.py @@ -2,34 +2,44 @@ from textual import on from textual.app import ComposeResult +from textual.binding import Binding from textual.containers import Container, VerticalScroll, Horizontal from textual.screen import Screen from textual.events import ScreenResume -from textual.widgets import DirectoryTree, Header, TextArea, Button +from textual.widgets import DirectoryTree, Header, TextArea, Button, Footer class EditorScreen(Screen): # Configuration file editor screen + # Set the key bindings + BINDINGS = [ + Binding("ctrl+s", "save", "Save", priority=True), + Binding("escape", "app.pop_screen", "Discard") + ] + # Current file being edited current_file = "" + def action_save(self) -> None: + code_view = self.query_one("#code", TextArea) + current_text = code_view.text + try: + file = open(self.curent_file, "w") + file.write(current_text) + file.close() + except Exception: + print("Error saving file: " + self.curent_file) + self.sub_title = "ERROR" + else: + print("File saved: " + self.curent_file) + self.sub_title = self.curent_file + self.dismiss() + def on_button_pressed(self, event: Button.Pressed) -> None: # Event handler called when a button is pressed - code_view = self.query_one("#code", TextArea) if event.button.id == "save-editor-button" and self.curent_file != "": - current_text = code_view.text - try: - print("Save button pressed. Trying to save file: " + self.curent_file) - file = open(self.curent_file, "w") - file.write(current_text) - file.close() - except Exception: - print("Error saving file: " + self.curent_file) - self.sub_title = "ERROR" - else: - print("File saved: " + self.curent_file) - self.sub_title = self.curent_file - self.dismiss() + print("Save button pressed. Trying to save file: " + self.curent_file) + self.action_save() elif event.button.id == "cancel-editor-button": print("Cancel button pressed") self.dismiss() @@ -73,3 +83,4 @@ def compose(self) -> ComposeResult: with Horizontal(id="editor-buttons-container"): yield Button("Save", id="save-editor-button", classes="editor-button") yield Button("Cancel", id="cancel-editor-button", classes="editor-button") + yield Footer() diff --git a/tools/config_editor/settings.py b/tools/config_editor/settings.py index ea59b7286..4eb577792 100644 --- a/tools/config_editor/settings.py +++ b/tools/config_editor/settings.py @@ -1,15 +1,22 @@ from textual import on from textual.app import ComposeResult +from textual.binding import Binding from textual.containers import VerticalScroll, Container, Horizontal from textual.screen import Screen from textual.events import ScreenResume -from textual.widgets import Header, Button, Switch, Label +from textual.widgets import Header, Button, Switch, Label, Footer from widgets import LabelledInput, LabelledSelect class SettingsScreen(Screen): # Settings screen + # Set the key bindings + BINDINGS = [ + Binding("s", "save", "Save"), + Binding("escape", "app.pop_screen", "Discard") + ] + target_select: LabelledSelect enable_copy_switch: Switch arduino_path_input: LabelledInput @@ -18,37 +25,37 @@ class SettingsScreen(Screen): idf_commit_input: LabelledInput idf_debug_select: LabelledSelect - def on_button_pressed(self, event: Button.Pressed) -> None: - # Event handler called when a button is pressed - if event.button.id == "save-settings-button": - print("Save button pressed") - - self.app.setting_target = self.target_select.get_select_value() - print("Target setting updated: " + self.app.setting_target) + def action_save(self) -> None: + self.app.setting_target = self.target_select.get_select_value() + print("Target setting updated: " + self.app.setting_target) - self.app.setting_enable_copy = self.enable_copy_switch.value - print("Enable copy setting updated: " + str(self.app.setting_enable_copy)) + self.app.setting_enable_copy = self.enable_copy_switch.value + print("Enable copy setting updated: " + str(self.app.setting_enable_copy)) - if self.enable_copy_switch.value: - self.app.setting_arduino_path = self.arduino_path_input.get_input_value() - print("Arduino path setting updated: " + self.app.setting_arduino_path) + if self.enable_copy_switch.value: + self.app.setting_arduino_path = self.arduino_path_input.get_input_value() + print("Arduino path setting updated: " + self.app.setting_arduino_path) - self.app.setting_arduino_branch = self.arduino_branch_input.get_input_value() - print("Arduino branch setting updated: " + self.app.setting_arduino_branch) + self.app.setting_arduino_branch = self.arduino_branch_input.get_input_value() + print("Arduino branch setting updated: " + self.app.setting_arduino_branch) - self.app.setting_idf_branch = self.idf_branch_input.get_input_value() - print("IDF branch setting updated: " + self.app.setting_idf_branch) + self.app.setting_idf_branch = self.idf_branch_input.get_input_value() + print("IDF branch setting updated: " + self.app.setting_idf_branch) - self.app.setting_idf_commit = self.idf_commit_input.get_input_value() - print("IDF commit setting updated: " + self.app.setting_idf_commit) + self.app.setting_idf_commit = self.idf_commit_input.get_input_value() + print("IDF commit setting updated: " + self.app.setting_idf_commit) - self.app.setting_debug_level = self.idf_debug_select.get_select_value() - print("Debug level setting updated: " + self.app.setting_debug_level) + self.app.setting_debug_level = self.idf_debug_select.get_select_value() + print("Debug level setting updated: " + self.app.setting_debug_level) - self.dismiss() + def on_button_pressed(self, event: Button.Pressed) -> None: + # Event handler called when a button is pressed + if event.button.id == "save-settings-button": + print("Save button pressed") + self.action_save() elif event.button.id == "cancel-settings-button": print("Cancel button pressed") - self.dismiss() + self.dismiss() @on(ScreenResume) def on_resume(self) -> None: @@ -124,6 +131,7 @@ def compose(self) -> ComposeResult: with Horizontal(id="settings-button-container"): yield Button("Save", id="save-settings-button", classes="settings-button") yield Button("Cancel", id="cancel-settings-button", classes="settings-button") + yield Footer() def on_mount(self) -> None: # Event handler called when the screen is mounted for the first time diff --git a/tools/config_editor/style.tcss b/tools/config_editor/style.tcss index b51b5806c..d846473d7 100644 --- a/tools/config_editor/style.tcss +++ b/tools/config_editor/style.tcss @@ -69,14 +69,12 @@ Button { border-bottom: tall $error-lighten-2; border-top: tall $error-darken-2; } - } - } # Main Screen -Button.main-menu-button { +.main-menu-button { margin-bottom: 1; min-width: 100%; max-width: 0.4fr; @@ -91,7 +89,7 @@ Button.main-menu-button { text-align: center; margin-bottom: 4; text-style: bold; - color: lightgray; + color: auto; width: 0.4fr; } From d58881f6ade0af7e27d4e5bfe3ffc3eb885170be Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Mon, 18 Mar 2024 16:58:00 -0300 Subject: [PATCH 47/54] Improve readme --- tools/config_editor/README.md | 10 +++++----- tools/config_editor/app.py | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tools/config_editor/README.md b/tools/config_editor/README.md index 8fde7e86c..75e6143f6 100644 --- a/tools/config_editor/README.md +++ b/tools/config_editor/README.md @@ -17,10 +17,10 @@ The Windows Terminal can be installed from the Microsoft Store. These command line arguments can be used to pre-configure the application: Command line arguments: - -t, --target Target to be compiled. Choose from: all, esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2 + -t, --target Target to be compiled. Choose from: all, esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2. Default: all --copy, --no-copy Enable/disable copying the compiled libraries to arduino-esp32. Enabled by default -c, --arduino-path Path to arduino-esp32 directory. Default: OS dependent - -A, --arduino-branch Branch of the arduino-esp32 repository to be used - -I, --idf-branch Branch of the ESP-IDF repository to be used - -i, --idf-commit Commit of the ESP-IDF repository to be used - -D, --debug-level Debug level to be set to ESP-IDF. Choose from: default, none, error, warning, info, debug, verbose + -A, --arduino-branch Branch of the arduino-esp32 repository to be used. Default: set by the build script + -I, --idf-branch Branch of the ESP-IDF repository to be used. Default: set by the build script + -i, --idf-commit Commit of the ESP-IDF repository to be used. Default: set by the build script + -D, --debug-level Debug level to be set to ESP-IDF. Choose from: default, none, error, warning, info, debug, verbose. Default: default diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index c2dd18d1a..1844d2c5c 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -13,13 +13,13 @@ Note that this application still needs the requirements from esp32-arduino-lib-builder to be installed. Command line arguments: - -t, --target Target to be compiled. Choose from: all, esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2 + -t, --target Target to be compiled. Choose from: all, esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2. Default: all --copy, --no-copy Enable/disable copying the compiled libraries to arduino-esp32. Enabled by default -c, --arduino-path Path to arduino-esp32 directory. Default: OS dependent - -A, --arduino-branch Branch of the arduino-esp32 repository to be used - -I, --idf-branch Branch of the ESP-IDF repository to be used - -i, --idf-commit Commit of the ESP-IDF repository to be used - -D, --debug-level Debug level to be set to ESP-IDF. Choose from: default, none, error, warning, info, debug, verbose + -A, --arduino-branch Branch of the arduino-esp32 repository to be used. Default: set by the build script + -I, --idf-branch Branch of the ESP-IDF repository to be used. Default: set by the build script + -i, --idf-commit Commit of the ESP-IDF repository to be used. Default: set by the build script + -D, --debug-level Debug level to be set to ESP-IDF. Choose from: default, none, error, warning, info, debug, verbose. Default: default """ From c184c896948120f4fee7f70f1790d5bf7960e5c9 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Mon, 18 Mar 2024 21:59:51 -0300 Subject: [PATCH 48/54] Revert "Merge branch 'bugfix/arch_cmd' into feature/config_ui" This reverts commit 31dbd36336e48c75a4f6752646402a3538de579c, reversing changes made to 2d8e4624a59d889369df24100d1d62355ae29879. --- tools/config.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/config.sh b/tools/config.sh index 936a9c50c..74de3b8c5 100755 --- a/tools/config.sh +++ b/tools/config.sh @@ -58,7 +58,7 @@ if [ -d "$IDF_PATH" ]; then fi function get_os(){ - OSBITS=`uname -m` + OSBITS=`arch` if [[ "$OSTYPE" == "linux"* ]]; then if [[ "$OSBITS" == "i686" ]]; then echo "linux32" From 6bdcb04251633e31262cd5c00f1226dbffee5f0f Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 5 Apr 2024 12:16:17 -0300 Subject: [PATCH 49/54] Target list based on JSON and support for any combination of targets --- tools/config_editor/app.py | 42 ++++++++++++++++++++++++++++----- tools/config_editor/compile.py | 23 ++++++++++-------- tools/config_editor/settings.py | 42 ++++++++++++++++++++------------- tools/config_editor/style.tcss | 14 +++++++++++ 4 files changed, 89 insertions(+), 32 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index 1844d2c5c..2075b7463 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -24,6 +24,7 @@ """ import argparse +import json import os import platform import sys @@ -95,6 +96,7 @@ class ConfigEditorApp(App): ROOT_PATH = os.path.abspath(os.path.join(SCRIPT_PATH, "..", "..")) # Set the application options + supported_targets = [] setting_enable_copy = True # Options to be set by the command line arguments @@ -119,7 +121,8 @@ def on_mount(self) -> None: print("Python version: " + sys.version) print("Root path: " + self.ROOT_PATH) print("Script path: " + self.SCRIPT_PATH) - print("Target: " + str(self.setting_target)) + print("Supported Targets: " + ", ".join(self.supported_targets)) + print("Default targets: " + self.setting_target) print("Enable Copy: " + str(self.setting_enable_copy)) print("Arduino Path: " + str(self.setting_arduino_path)) print("Arduino Branch: " + str(self.setting_arduino_branch)) @@ -136,6 +139,9 @@ def arduino_default_path(): else: # Windows and MacOS return os.path.join(home, "Documents", "Arduino", "hardware", "espressif", "esp32") +def check_arduino_path(): + return os.path.isdir(arduino_default_path()) + def main() -> None: # Set the PYTHONUNBUFFERED environment variable to "1" to disable the output buffering os.environ['PYTHONUNBUFFERED'] = "1" @@ -147,21 +153,39 @@ def main() -> None: app = ConfigEditorApp() + target_choices = [] + + # Parse build JSON file + build_json_path = os.path.join(app.ROOT_PATH, "configs", "builds.json") + if os.path.isfile(build_json_path): + with open(build_json_path, "r") as build_json_file: + build_json = json.load(build_json_file) + for target in build_json["targets"]: + try: + default = False if target["skip"] else True + except: + default = True + target_choices.append((target["target"], default)) + else: + print("Error: configs/builds.json file not found.") + exit(1) + + target_choices.sort(key=lambda x: x[0]) + parser = argparse.ArgumentParser(description="Configure and compile the ESP32 Arduino static libraries") - target_choices = ("all", "esp32", "esp32s2", "esp32s3", "esp32c2", "esp32c3", "esp32c6", "esp32h2") parser.add_argument("-t", "--target", metavar="", type=str, - default="all", - choices=target_choices, + default="default", required=False, - help="Target to be compiled. Choose from: " + ", ".join(target_choices)) + help="Comma separated list of targets to be compiled. Choose from: " + ", ".join([x[0] for x in target_choices]) + + ". Default: All except " + ", ".join([x[0] for x in target_choices if not x[1]])) parser.add_argument("--copy", type=bool, action=argparse.BooleanOptionalAction, - default=True, + default=True if check_arduino_path() else False, required=False, help="Enable/disable copying the compiled libraries to arduino-esp32. Enabled by default") @@ -205,6 +229,12 @@ def main() -> None: args = parser.parse_args() # Set the options in the app + if args.target.strip() == "default": + args.target = ",".join([x[0] for x in target_choices if x[1]]) + elif args.target.strip() == "all": + args.target = ",".join([x[0] for x in target_choices]) + + app.supported_targets = [x[0] for x in target_choices] app.setting_target = args.target app.setting_enable_copy = args.copy app.setting_arduino_path = os.path.abspath(args.arduino_path) diff --git a/tools/config_editor/compile.py b/tools/config_editor/compile.py index 3eefea62e..3cfb056b7 100644 --- a/tools/config_editor/compile.py +++ b/tools/config_editor/compile.py @@ -72,12 +72,15 @@ def compile_libs(self) -> None: label = self.query_one("#compile-title", Static) self.child_process = None - target = self.app.setting_target + if self.app.setting_target == ",".join(self.app.supported_targets): + target = "all targets" + else: + target = self.app.setting_target.replace(",", ", ").upper() - label.update("Compiling for " + target.upper()) - self.print_info("======== Compiling for " + target.upper() + " ========") + label.update("Compiling for " + target) + self.print_info("======== Compiling for " + target + " ========") - command = ["./build.sh", "-t", target, "-D", self.app.setting_debug_level] + command = ["./build.sh", "-t", self.app.setting_target, "-D", self.app.setting_debug_level] #command.append("--help") # For testing output without compiling @@ -112,14 +115,14 @@ def compile_libs(self) -> None: print("Process might have terminated") if not self.child_process: - self.print_error("Compilation failed for " + target.upper() + "Child process failed to start") - label.update("Compilation failed for " + target.upper() + "Child process failed to start") + self.print_error("Compilation failed for " + target + "Child process failed to start") + label.update("Compilation failed for " + target + "Child process failed to start") return else: self.child_process.wait() if self.child_process.returncode != 0: - self.print_error("Compilation failed for " + target.upper() + ". Return code: " + str(self.child_process.returncode)) + self.print_error("Compilation failed for " + target + ". Return code: " + str(self.child_process.returncode)) self.print_error("Errors:") try: for error in self.child_process.stderr: @@ -128,10 +131,10 @@ def compile_libs(self) -> None: self.child_process.stderr.close() except Exception as e: print("Error reading child process errors: " + str(e)) - label.update("Compilation failed for " + target.upper()) + label.update("Compilation failed for " + target) else: - self.print_success("Compilation successful for " + target.upper()) - label.update("Compilation successful for " + target.upper()) + self.print_success("Compilation successful for " + target) + label.update("Compilation successful for " + target) def on_button_pressed(self, event: Button.Pressed) -> None: # Event handler called when a button is pressed diff --git a/tools/config_editor/settings.py b/tools/config_editor/settings.py index 4eb577792..c92358374 100644 --- a/tools/config_editor/settings.py +++ b/tools/config_editor/settings.py @@ -1,10 +1,12 @@ +import math + from textual import on from textual.app import ComposeResult from textual.binding import Binding from textual.containers import VerticalScroll, Container, Horizontal from textual.screen import Screen from textual.events import ScreenResume -from textual.widgets import Header, Button, Switch, Label, Footer +from textual.widgets import Header, Button, Switch, Label, Footer, Checkbox from widgets import LabelledInput, LabelledSelect @@ -17,7 +19,6 @@ class SettingsScreen(Screen): Binding("escape", "app.pop_screen", "Discard") ] - target_select: LabelledSelect enable_copy_switch: Switch arduino_path_input: LabelledInput arduino_branch_input: LabelledInput @@ -26,7 +27,13 @@ class SettingsScreen(Screen): idf_debug_select: LabelledSelect def action_save(self) -> None: - self.app.setting_target = self.target_select.get_select_value() + checkboxes = self.query(Checkbox) + self.app.setting_target = "" + for checkbox in checkboxes: + if checkbox.value: + if self.app.setting_target: + self.app.setting_target += "," + self.app.setting_target += checkbox.id.replace("-checkbox", "") print("Target setting updated: " + self.app.setting_target) self.app.setting_enable_copy = self.enable_copy_switch.value @@ -61,7 +68,12 @@ def on_button_pressed(self, event: Button.Pressed) -> None: def on_resume(self) -> None: # Event handler called every time the screen is activated print("Settings screen resumed. Updating settings.") - self.target_select.set_select_value(self.app.setting_target) + targets = self.app.setting_target.split(",") + checkboxes = self.query(Checkbox) + for checkbox in checkboxes: + checkbox.value = False + if checkbox.id.replace("-checkbox", "") in targets: + checkbox.value = True self.enable_copy_switch.value = self.app.setting_enable_copy if self.app.setting_enable_copy: self.arduino_path_input.visible = True @@ -85,18 +97,11 @@ def compose(self) -> ComposeResult: # Compose the target selection screen yield Header() with VerticalScroll(id="settings-scroll-container"): - target_options = [ - ("All", "all"), - ("ESP32", "esp32"), - ("ESP32-S2", "esp32s2"), - ("ESP32-S3", "esp32s3"), - ("ESP32-C2 (ESP8684)", "esp32c2"), - ("ESP32-C3", "esp32c3"), - ("ESP32-C6", "esp32c6"), - ("ESP32-H2", "esp32h2") - ] - self.target_select = LabelledSelect("Compilation Target", target_options, allow_blank=False, id="target-select") - yield self.target_select + + yield Label("Compilation Targets", id="settings-target-label") + with Container(id="settings-target-container"): + for target in self.app.supported_targets: + yield Checkbox(target.upper(), id=target + "-checkbox") with Horizontal(classes="settings-switch-container"): self.enable_copy_switch = Switch(value=self.app.setting_enable_copy, id="enable-copy-switch") @@ -136,4 +141,9 @@ def compose(self) -> ComposeResult: def on_mount(self) -> None: # Event handler called when the screen is mounted for the first time self.sub_title = "Settings" + target_container = self.query_one("#settings-target-container") + # Height needs to be 3 for each row of targets + 1 + height_value = str(int(math.ceil(len(self.app.supported_targets) / int(target_container.styles.grid_size_columns)) * 3 + 1)) + print("Target container height: " + height_value) + target_container.styles.height = height_value print("Settings screen mounted") diff --git a/tools/config_editor/style.tcss b/tools/config_editor/style.tcss index d846473d7..4359e58e0 100644 --- a/tools/config_editor/style.tcss +++ b/tools/config_editor/style.tcss @@ -128,6 +128,20 @@ Button { align: center middle; } +#settings-target-label { + margin-left: 1; +} + +#settings-target-container { + layout: grid; + grid-size: 4; +} + +#settings-target-container Checkbox { + width: 100%; + margin-right: -1; +} + .settings-button { margin: 1; min-width: 100%; From f0702ef1330dc75f301628faf619e8de649af7e9 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 5 Apr 2024 12:43:37 -0300 Subject: [PATCH 50/54] Improve documentation --- README.md | 23 +++++++++++++++++++++++ tools/config_editor/app.py | 6 ++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8133c0d7a..c7739dc93 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,29 @@ git clone https://github.com/espressif/esp32-arduino-lib-builder cd esp32-arduino-lib-builder ./build.sh ``` + +### Using the User Interface + +You can more easily build the libraries using the user interface found in the `tools/config_editor/` folder. +It is a Python script that allows you to select and edit the options for the libraries you want to build. +The script has mouse support and can also be pre-configured using the same command line arguments as the `build.sh` script. +To use it, follow these steps: + +1. Make sure you have the required dependencies installed: + - Python 3.9 or later + - The [Textual](https://github.com/textualize/textual/) library + - All the dependencies listed in the previous section + +2. Execute the script `tools/config_editor/app.py` from any folder. It will automatically detect the path to the root of the repository. + +3. Configure the compilation and ESP-IDF options as desired. + +4. Click on the "Compile Static Libraries" button to start the compilation process. + +5. The script will show the compilation output in a new screen. Note that the compilation process can take many hours, depending on the number of libraries selected and the options chosen. + +6. If the compilation is successful and the option to copy the libraries to the Arduino Core folder is enabled, it will already be available for use in the Arduino IDE. Otherwise, you can find the compiled libraries in the `esp32-arduino-libs` folder alongside this repository. + ### Documentation For more information about how to use the Library builder, please refer to this [Documentation page](https://docs.espressif.com/projects/arduino-esp32/en/latest/lib_builder.html?highlight=lib%20builder) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index 2075b7463..a1607e48c 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -13,13 +13,15 @@ Note that this application still needs the requirements from esp32-arduino-lib-builder to be installed. Command line arguments: - -t, --target Target to be compiled. Choose from: all, esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2. Default: all + -t, --target Comma separated list of targets to be compiled. + Choose from: all, esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2. Default: all except esp32c2 --copy, --no-copy Enable/disable copying the compiled libraries to arduino-esp32. Enabled by default -c, --arduino-path Path to arduino-esp32 directory. Default: OS dependent -A, --arduino-branch Branch of the arduino-esp32 repository to be used. Default: set by the build script -I, --idf-branch Branch of the ESP-IDF repository to be used. Default: set by the build script -i, --idf-commit Commit of the ESP-IDF repository to be used. Default: set by the build script - -D, --debug-level Debug level to be set to ESP-IDF. Choose from: default, none, error, warning, info, debug, verbose. Default: default + -D, --debug-level Debug level to be set to ESP-IDF. + Choose from: default, none, error, warning, info, debug, verbose. Default: default """ From 6e5ea45ca1235e470958361e2a212c321f1d8252 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 5 Apr 2024 13:12:26 -0300 Subject: [PATCH 51/54] Add note to the documentation --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c7739dc93..03a6788f9 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ To use it, follow these steps: 5. The script will show the compilation output in a new screen. Note that the compilation process can take many hours, depending on the number of libraries selected and the options chosen. 6. If the compilation is successful and the option to copy the libraries to the Arduino Core folder is enabled, it will already be available for use in the Arduino IDE. Otherwise, you can find the compiled libraries in the `esp32-arduino-libs` folder alongside this repository. + - Note that the copy operation doesn't currently support the core downloaded from the Arduino IDE Boards Manager, only the manual installation from the [`arduino-esp32`](https://github.com/espressif/arduino-esp32) repository. ### Documentation From b252b7cb493c974d3f036121574f2cdb4462dc66 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 5 Apr 2024 13:28:11 -0300 Subject: [PATCH 52/54] Fix copy after compilation --- tools/config_editor/app.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index a1607e48c..018610c4e 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -141,8 +141,8 @@ def arduino_default_path(): else: # Windows and MacOS return os.path.join(home, "Documents", "Arduino", "hardware", "espressif", "esp32") -def check_arduino_path(): - return os.path.isdir(arduino_default_path()) +def check_arduino_path(path): + return os.path.isdir(path) def main() -> None: # Set the PYTHONUNBUFFERED environment variable to "1" to disable the output buffering @@ -187,7 +187,7 @@ def main() -> None: parser.add_argument("--copy", type=bool, action=argparse.BooleanOptionalAction, - default=True if check_arduino_path() else False, + default=True, required=False, help="Enable/disable copying the compiled libraries to arduino-esp32. Enabled by default") @@ -238,7 +238,16 @@ def main() -> None: app.supported_targets = [x[0] for x in target_choices] app.setting_target = args.target - app.setting_enable_copy = args.copy + + if args.copy: + if check_arduino_path(args.arduino_path): + app.setting_enable_copy = True + else: + print("Invalid path to Arduino core: " + os.path.abspath(args.arduino_path)) + exit(1) + else: + app.setting_enable_copy = False + app.setting_arduino_path = os.path.abspath(args.arduino_path) app.setting_arduino_branch = args.arduino_branch app.setting_idf_branch = args.idf_branch From 967d8873ea66a8b748d622c171fd2a98d706cff1 Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 5 Apr 2024 13:54:59 -0300 Subject: [PATCH 53/54] Fix bugs --- tools/config_editor/app.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tools/config_editor/app.py b/tools/config_editor/app.py index 018610c4e..8a7b2ce24 100755 --- a/tools/config_editor/app.py +++ b/tools/config_editor/app.py @@ -13,7 +13,7 @@ Note that this application still needs the requirements from esp32-arduino-lib-builder to be installed. Command line arguments: - -t, --target Comma separated list of targets to be compiled. + -t, --target Comma-separated list of targets to be compiled. Choose from: all, esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2. Default: all except esp32c2 --copy, --no-copy Enable/disable copying the compiled libraries to arduino-esp32. Enabled by default -c, --arduino-path Path to arduino-esp32 directory. Default: OS dependent @@ -155,6 +155,7 @@ def main() -> None: app = ConfigEditorApp() + # List of tuples for the target choices containing the target name and if it is enabled by default target_choices = [] # Parse build JSON file @@ -181,7 +182,7 @@ def main() -> None: type=str, default="default", required=False, - help="Comma separated list of targets to be compiled. Choose from: " + ", ".join([x[0] for x in target_choices]) + help="Comma-separated list of targets to be compiled. Choose from: " + ", ".join([x[0] for x in target_choices]) + ". Default: All except " + ", ".join([x[0] for x in target_choices if not x[1]])) parser.add_argument("--copy", @@ -237,11 +238,20 @@ def main() -> None: args.target = ",".join([x[0] for x in target_choices]) app.supported_targets = [x[0] for x in target_choices] + + for target in args.target.split(","): + if target not in app.supported_targets: + print("Invalid target: " + target) + exit(1) + app.setting_target = args.target if args.copy: if check_arduino_path(args.arduino_path): app.setting_enable_copy = True + elif args.arduino_path == arduino_default_path(): + print("Warning: Default Arduino path not found. Disabling copy to Arduino.") + app.setting_enable_copy = False else: print("Invalid path to Arduino core: " + os.path.abspath(args.arduino_path)) exit(1) From d82696bc99b7108805c9bfa89683af3bb8c8cf4b Mon Sep 17 00:00:00 2001 From: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com> Date: Fri, 5 Apr 2024 13:55:27 -0300 Subject: [PATCH 54/54] Improve documentation --- README.md | 2 ++ tools/config_editor/README.md | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 03a6788f9..501c62887 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ cd esp32-arduino-lib-builder You can more easily build the libraries using the user interface found in the `tools/config_editor/` folder. It is a Python script that allows you to select and edit the options for the libraries you want to build. The script has mouse support and can also be pre-configured using the same command line arguments as the `build.sh` script. +For more information and troubleshooting, please refer to the [UI README](tools/config_editor/README.md). + To use it, follow these steps: 1. Make sure you have the required dependencies installed: diff --git a/tools/config_editor/README.md b/tools/config_editor/README.md index 75e6143f6..c0ba0bc40 100644 --- a/tools/config_editor/README.md +++ b/tools/config_editor/README.md @@ -2,25 +2,37 @@ This is a simple application to configure the static libraries for the ESP32 Arduino core. It allows the user to select the targets to compile, change the configuration options and compile the libraries. +It has mouse support and can be pre-configured using command line arguments. ## Requirements - Python 3.9 or later - The "textual" library (install it using `pip install textual`) - The requirements from esp32-arduino-lib-builder +## Troubleshooting + +In some cases, the UI might not look as expected. This can happen due to the terminal emulator not supporting the required features. + ### WSL + If you are using WSL, it is recommended to use the Windows Terminal to visualize the application. Otherwise, the application layout and colors might not be displayed correctly. The Windows Terminal can be installed from the Microsoft Store. +### MacOS + +If you are using MacOS and the application looks weird, check [this guide from Textual](https://textual.textualize.io/FAQ/#why-doesnt-textual-look-good-on-macos) to fix it. + ## Usage These command line arguments can be used to pre-configure the application: Command line arguments: - -t, --target Target to be compiled. Choose from: all, esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2. Default: all + -t, --target Comma-separated list of targets to be compiled. + Choose from: all, esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c6, esp32h2. Default: all except esp32c2 --copy, --no-copy Enable/disable copying the compiled libraries to arduino-esp32. Enabled by default -c, --arduino-path Path to arduino-esp32 directory. Default: OS dependent -A, --arduino-branch Branch of the arduino-esp32 repository to be used. Default: set by the build script -I, --idf-branch Branch of the ESP-IDF repository to be used. Default: set by the build script -i, --idf-commit Commit of the ESP-IDF repository to be used. Default: set by the build script - -D, --debug-level Debug level to be set to ESP-IDF. Choose from: default, none, error, warning, info, debug, verbose. Default: default + -D, --debug-level Debug level to be set to ESP-IDF. + Choose from: default, none, error, warning, info, debug, verbose. Default: default