add some code

This commit is contained in:
2025-09-05 13:25:11 +08:00
parent 9ff0a99e7a
commit 3cf1229a85
8911 changed files with 2535396 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
build/
doxygen/
intermediate/
src/_static/built_lv_examples

View File

@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our
project and our community a harassment-free experience for everyone,
regardless of age, body size, disability, ethnicity, gender identity and
expression, level of experience, nationality, personal appearance, race,
religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual
attention or advances
- Trolling, insulting/derogatory comments, and personal or political
attacks
- Public or private harassment
- Publishing others\' private information, such as a physical or
electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in
a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of
acceptable behavior and are expected to take appropriate and fair
corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit,
or reject comments, commits, code, wiki edits, issues, and other
contributions that are not aligned to this Code of Conduct, or to ban
temporarily or permanently any contributor for other behaviors that they
deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public
spaces when an individual is representing the project or its community.
Examples of representing a project or community include using an
official project e-mail address, posting via an official social media
account, or acting as an appointed representative at an online or
offline event. Representation of a project may be further defined and
clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may
be reported by contacting the project team using the [contact
form](https://lvgl.io/about). All complaints will be reviewed and
investigated and will result in a response that is deemed necessary and
appropriate to the circumstances. The project team is obligated to
maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted
separately.
Project maintainers who do not follow or enforce the Code of Conduct in
good faith may face temporary or permanent repercussions as determined
by other members of the project\'s leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor
Covenant](http://contributor-covenant.org), version 1.4, available at
[http://contributor-covenant.org/version/1/4](http://contributor-covenant.org/version/1/4/)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
BUILDDIR = build
SOURCEDIR = intermediatex
# SOURCEDIR can be overridden by LVGL_DOC_BUILD_INTERMEDIATE_DIR
LEN := $(shell printf '%s' '$(LVGL_DOC_BUILD_INTERMEDIATE_DIR)' | wc -c)
ifeq ($(shell test $(LEN) -gt 0; echo $$?),0)
SOURCEDIR = $(LVGL_DOC_BUILD_INTERMEDIATE_DIR)
else
SOURCEDIR = intermediate
endif
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
# It can be supplied from an environment variable 'O' or on make command line.
%: Makefile
$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

@@ -0,0 +1,311 @@
# Documentation
## Building
Building the documentation is easy. Here are the requirements:
* Doxygen
* Python >= 3.10
* C compiler (gcc, msvc, clang, etc...)
Once Python is installed
pip install -r requirements.txt
will install all the prerequisite packages:
* Sphinx
* breathe
* imagesize
* importlib-metadata
* sphinx-rtd-theme
* sphinx-sitemap
* sphinxcontrib-applehelp
* sphinxcontrib-devhelp
* sphinxcontrib-htmlhelp
* sphinxcontrib-jsmath
* sphinxcontrib-qthelp
* sphinxcontrib-serializinghtml
* sphinxcontrib-mermaid==0.9.2
* sphinx-design
* sphinx-rtd-dark-mode
* typing-extensions
* sphinx-reredirects
* dirsync
Now you are ready to build the documentation:
python build.py html
or if you are on a Unix like OS:
python3 build.py html
Intermediate files are prepared in `./docs/intermediate/` and the final documentation will appear in `./docs/build/html/`. (Both of these directories can be overridden using environment variables. See documentation in `build.py` for details.)
If the list of document source files has changed (names or paths):
python build.py clean html
Will remove the old intermediate and build files and regenerate new ones matching the new structure.
To see a list of options available:
python build.py
Read the docstring for `build.py` for detailed documentation on each option.
## For Developers
One of our firm policies is ***EVERYTHING MUST BE DOCUMENTED***.
The below are some rules to follow when updating any of the `.rst` files located in the `./docs/src/` directory tree.
### What to Name Your `.rst` File
The directory structure under the `./docs/src/` directory, and the filenames of the `.rst` files govern the eventual URLs that are generated in the HTML output. These directories are organized so as to reflect the nature of the content. Example: the `.rst` files under `./docs/src/intro` contain introductory material—detailed reference material would not go there, but instead in an appropriate subdirectory of `./docs/src/details/`. It is expected that the content and location of any new documents added would be in alignment with this directory structure, and placed and named according to their content. Additionally, to be linked into the eventual generated documentation, the stem of the new filename would need to appear in at least one (normally *only one*) `.. toctree::` directive, normally in an `index.rst` file in the directory where it will appear in the table of contents (TOC).
Other than that, there are no restrictions on filenames. Previous linking of filenames to generated API links has been removed and replaced by a better scheme. For sake of illustration, let's say you are creating (or enhancing) documentation related to the `lv_scale_t` data type (one of the LVGL Widgets): if you want the doc-build logic to generate appropriate links to LVGL API pages, place an API section at the end of your document (it must be at the end) like this:
```rst
API
***
```
and then, if you want the API-link-generation logic to generate hyperlinks to API pages based on an ***exact, case-sensitive string match*** with specific C symbols, follow it with a reStructuredText comment using this syntax:
```rst
.. API equals: lv_scale_t, lv_scale_create
```
What follows the colon is a comma- or space-separated list of exact C symbols documented somewhere in the `lvgl/src/` directory. If the list is long, it can be wrapped to subsequent lines, though continuation lines must be all indented at the same level. The list ends with the first blank line after this pseudo-directive.
If you instead want the API-link-generation logic to simply include links to code that ***starts with a specific string*** use this syntax instead. The format of the list is the same as for `.. API equals:`:
```rst
.. API startswith: lv_scale, lv_obj_set_style
```
You can also manually link to API pages, in which case the API-link-generation logic will see that you have already added links and will not repeat them.
```rst
:ref:`lv_scale_h`
```
Note that the period before the `h` is replaced with an underscore (`_`). The naming of this reference (`lv_scale_h`) will generate a hyperlink to the documentation extracted by Doxygen from the `lvgl/src/widgets/scale/lv_scale.h` file.
### Text Format
While with `.md` files, it is important to allow paragraphs to flow off to the right with one long line so that when they are formatted as `.html` files, the paragraphs will word-wrap with the width of the browser, this is not true with reStructuredText (`.rst` files). [Sphinx](https://www.sphinx-doc.org/en/master/) and its underlying [docutils parsing engine](https://docutils.sourceforge.io/docs/) conveniently combine grouped text into a proper paragraph with that word-wrapping behavior. This allows the source text documents to be nicely word-wrapped so that they are more readable in text- and code-editors that do not have wide editing windows. So please wrap the text around column 86 or narrower. Wrapping at *exactly* column 86 is not important, but readability and ease of editing is.
### index.rst Files
If you create a new directory you MUST have an `index.rst` file in that directory and that index file needs to be pointed to in the `index.rst` file that is located in the parent directory.
Let's take a look at the `index.rst` file that is located in the `docs/layouts` directory.
```
.. _layouts:
=======
Layouts
=======
.. toctree::
:maxdepth: 2
flex
grid
```
The below explains the parts of this file.
```
.. _layouts: <=== Creates an explicit link target
<=== Empty line -- important!
=======
Layouts <=== Heading seen in documentation
=======
.. toctree:: <=== Table of contents
:maxdepth: 2 <=== Internal use and needs to always be set this way
flex <=== .rst files located in directory with index.rst
grid
```
The first line is for the purposes of providing a uniquely-named **link target** that can be referenced elsewhere in the documentation.
.. _{LINK NAME}:
Note that `{LINK NAME}`:
- **must** be preceded by a single underscore, and
- **must** be followed by at least one blank line for the doc-generation logic to process it correctly.
Replace `{LINK NAME}` with a link name that is unique among all documents under the `./docs/` directory. It can have multiple words if needed to make it unique or when otherwise appropriate for clarity. If multiple words are used, they can be separated with single spaces, hyphens or underscores. Whatever you use, the `{LINK NAME}` string used to reference it must be identical. `{LINK NAME}` strings are not case sensitive.
That unique name is then used to provide a link reference elsewhere in the documentation using one of two formats.
##### When "link text" should be a title or section heading from the target document:
```reStructuredText
:ref:`{LINK NAME}`
```
This in-line markup (interpreted text using the Sphinx-defined custom `:ref:` role) is then replaced with a hyperlink whose "link text" is the name of the section heading just below the **link target**. For this reason, when using this syntax, `{LINK NAME}` must reference **link target**s that are just above a title or section heading.
##### When "link text" should be something else:
```reStructuredText
:ref:`other link text <{LINK NAME}>`
```
This latter syntax enables you to put a **link target** anywhere in an .RST file (not just above a heading) and link to it using this syntax.
Note: This latter syntax was either added or fixed in Sphinx recently. It did not work in Sphinx 7.3.7.
### Section Headings
[Section headings](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#sections) are created by underlining (and optionally overlining) the section title with a punctuation character, at least as long as the text. Example:
```
=================
This Is a Heading
=================
```
reStructuredText does not impose any particular heading levels assigned to certain characters since the structure is determined from the succession of headings. So if you are modifying an existing .RST file, please follow the pattern already in use.
If you are creating a new .RST file, use this convention:
```
=====
Title
=====
Chapter
*******
Section
-------
Sub Section
~~~~~~~~~~~
Sub Sub Section
^^^^^^^^^^^^^^^
Sub Sub Sub Section
'''''''''''''''''''
```
Note that the "underlining" can be longer than the heading title, but if it is shorter, the documentation-generation logic will fail with an error.
For improved readability in the .RST file, place at least 2 blank lines above section headings.
### Code Blocks
* Do not use tab characters in code blocks.
* Each indentation level use 4 spaces.
* Include at least 1 empty line after a code block.
* There must be one empty line between the code block directive and the code.
* `.. code-block::` is the only directive that should be used. Note carefully that unlike the **link target** directive above, this directive has 2 colons. (The only ReST and sphinx directives that are valid with one colon are **link target**s as shown above.) Lone `::`, `:code:` or `.. code:` should not be used.
* If you want to separate code into easier-to-understand sections you can do so with a single empty line.
* For syntax highlighting appropriate to the language in the code block, specify the language after the directive. Some examples are:
- `.. code-block:: c`,
- `.. code-block:: cpp`,
- `.. code-block:: python`,
- `.. code-block:: shell`,
- `.. code-block:: kconfig`,
- `.. code-block:: json`,
- `.. code-block:: yaml`,
- `.. code-block:: csharp` (or "cs"),
- `.. code-block:: vb.net`,
- `.. code-block:: dot` (graphviz),
- `.. code-block:: html`,
- `.. code-block:: css`,
- `.. code-block:: xml`,
- `.. code-block:: make`.
The full set of supported lexers are listed here: https://pygments.org/docs/lexers/ .
### Bulleted Lists
To create a bulleted list, do the following:
- First item description
- If you want to span multiple lines, indent subsequent
lines to align with item text like this.
- If you want to include a code block under a list item,
it must be intended to align with the list item like this:
.. code-block: python
<=== blank line here is important
# this is some code
<=== blank line here is important
- If you want to have nested bulleted lists, indent each
new level to align with its parent list item like this:
<=== blank line here is important
- level 2 item 1: text
- level 2 item 2: text
<=== blank line here is important
- Last list item. Note that the nested list above is preceded
and followed by 1 blank line.
All lists (including nested lists) **must** be preceded and followed with at least 1 blank line. This is mandatory for the documentation-generation logic to process it correctly.
### Referencing API Documentation
If you want to reference portions of the LVGL code from the documentation (in .RST files) there are special directives to do this:
:cpp:func:`lv_init`
:c:macro:`LV_USE_FLEX`
:cpp:type:`lv_event_t`
:cpp:enum:`_lv_event_t`
:cpp:enumerator:`LV_EVENT_ALL`
:cpp:struct:`lv_image_dsc_t`
:cpp:union:`lv_style_value_t`
There is a special directive when wanting to use a more complex expression. For example when showing the arguments passed to a function.
:cpp:expr:`lv_obj_set_layout(widget, LV_LAYOUT_FLEX)`
:cpp:expr:`lv_slider_set_mode(slider, LV_SLIDER_MODE_...)`
Arguments that are expressions (more than one word), or contain non-alphanumeric characters will cause the `:cpp:expr:` interpreted-text to fail. Examples:
:cpp:expr:`lv_obj_set_layout(widget, LV_LAYOUT_FLEX/GRID)` <== arg with > 1 word
:cpp:expr:`lv_obj_set_layout(widget, LV_LAYOUT_*)` <== asterisk
:cpp:expr:`lv_obj_set_layout(*widget, LV_LAYOUT_FLEX)` <== asterisk
:cpp:expr:`lv_obj_set_layout((lv_obj_t *)widget, LV_LAYOUT_FLEX)` <== cast
:cpp:expr:`lv_obj_set_layout(&widget, LV_LAYOUT_FLEX);` <== ampersand & semicolon
:cpp:expr:`lv_obj_set_layout(widget, ...)` <== lone ellipsis
For such examples, simply use reStructuredText literal markup like this:
``lv_obj_set_layout(widget, LV_LAYOUT_FLEX/GRID)``
``lv_obj_set_layout(widget, LV_LAYOUT_*)``
``lv_obj_set_layout(*widget, LV_LAYOUT_FLEX)``
``lv_obj_set_layout((lv_obj_t *)widget, LV_LAYOUT_FLEX)``
``lv_obj_set_layout(&widget, LV_LAYOUT_FLEX);``
``lv_obj_set_layout(widget, ...)``

View File

@@ -0,0 +1,436 @@
<a href="https://github.com/sponsors/lvgl" target="_blank"><img align="left" src="https://lvgl.io/github-assets/sponsor.png" height="32px"></a>
<p align="right"><a href="../README.md">English</a> | <a href="./README_zh.md">中文</a> | <a href="./README_pt_BR.md">Português do Brasil</a> | <b>日本語</b></p>
<p align="center">
  <img src="https://lvgl.io/github-assets/logo-colored.png" width=300px>
</p>
  <h1 align="center">Light and Versatile Graphics Library</h1>
  <br>
<div align="center">
<img src="https://raw.githubusercontent.com/kisvegabor/test/master/smartwatch_demo.gif">
&nbsp;
  <img border="1px" src="https://lvgl.io/github-assets/widgets-demo.gif">
</div>
<br>
<p align="center">
<a href="https://lvgl-io.translate.goog/?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja" title="Homepage of LVGL">Website </a></a> |
<a href="https://docs-lvgl-io.translate.goog/master/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja" title="Detailed documentation with 100+ examples">Docs</a> |
<a href="https://forum.lvgl.io" title="Get help and help others">Forum</a> :gb: |
<a href="https://lvgl-io.translate.goog/demos?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja" title="Demos running in your browser">Demos</a> |
<a href="https://lvgl-io.translate.goog/services?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja" title="Graphics design, UI implementation and consulting">Services</a>
</p>
<br>
## :ledger: Overview
**実績**<br>
LVGL は、フリー&オープンソースの組み込み用グラフィックスライブラリです。
あらゆるMCU、MPU、ディスプレイタイプに対応しており、美しいUI(User Interface)を実現できます。
ARM, STM32, NXP, Espressif, Nuvoton, Arduino, RT-Thread, Zephyr, NuttX, Adafruitなど、業界をリードするベンダーやプロジェクトによりサポートされています。
**機能豊富**<br>
モダンで美しいGUIを作成するための機能をすべて備えています。
30以上の組み込みウィジェット、強力なスタイルシステム、WEB由来のレイアウトマネージャ、多くの言語をサポートする文字グラフィックシステムなどです。
LVGL のシステム要件は、RAM 32KB、Flash 128KB、Cコンパイラ、フレームバッファ、1/10スクリーンサイズのレンダリング用バッファです。
**UIエディタ**<br>
SquareLine Studio は、LVGL用のプロフェッショナルリーズナブルなドラッグドロップ型のUIエディターです。
Windows、Linux、MacOS で動作し、ウェブサイトへの登録なしで試すことができます。
**サービス**<br>
LVGL LLC では、グラフィックデザイン、UI実装、コンサルティングサービスに対する技術サポートが可能です。GUIプロジェクトの開発において何らかのサポートが必要な場合には、お気軽にお問い合わせください。
## :rocket: 特徴
**フリー & 移植可能**
- 外部依存関係がなく、完全に移植可能な Cライブラリ。(C++互換)
- 任意の(RT)OS、任意のMCU・MPU用にコンパイル可能。
- 電子ペーパー、OLEDディスプレイ、TFTディスプレイ、白黒ディスプレイ、モニターに対応。 [Porting Guide](https://docs-lvgl-io.translate.goog/master/details/integration/adding-lvgl-to-your-project/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)
- MITライセンスにより商用利用可能。
- システム要件RAM 32KB、Flash 128KB、フレームバッファ、レンダリング用に1/10以上のスクリーンサイズのバッファ。
- OS、外部メモリ、GPUもサポート。
**ウィジェット、スタイル、レイアウトなど**
- 30以上の組み込み [ウィジェット](https://docs-lvgl-io.translate.goog/master/details/widgets/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja): ボタン、ラベル、スライダー、グラフ、キーボード、メーター、円弧、表など。
- ウィジェットの任意の部分を任意の状態にカスタマイズ可能な豊富なスタイルプロパティを備えた柔軟な [スタイルシステム](https://docs-lvgl-io.translate.goog/master/details/common-widget-features/styles/style.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)。
- [Flexbox](https://docs-lvgl-io.translate.goog/master/details/common-widget-features/layouts/flex.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) および [グリッド](https://docs-lvgl-io.translate.goog/master/details/common-widget-features/layouts/grid.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) 風のレイアウトエンジンにより、ウィジェットのサイズと位置を自動的に設定。
- テキスト表示(UTF-8対応)は、中国語、日本語、韓国語、タイ語、ヒンディー語、アラビア語、ペルシア語をサポート。
- ワードラッピング、カーニング、テキストスクロール、サブピクセルレンダリング、ピンイン-IME中国語入力、テキスト中の絵文字に対応。
- アニメーション、アンチエイリアシング、不透明度、スムーズスクロール、シャドウ、画像変換などをサポートするレンダリングエンジン。
- マウス、タッチパッド、キーパッド、キーボード、外部ボタン、エンコーダ等の [入力デバイス](https://docs-lvgl-io.translate.goog/master/details/integration/adding-lvgl-to-your-project/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) をサポート。
- [マルチディスプレイ](https://docs-lvgl-io.translate.goog/master/details/main-modules/display/overview.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) 対応。
**Binding と Build をサポート**
- [MicroPython Binding](https://blog-lvgl-io.translate.goog/2019-02-20/micropython-bindings?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) が LVGL API を公開。
- カスタムビルドシステムは使用せず、プロジェクトの他のファイルをビルドするときに、LVGLをビルド可能。
- Make と [CMake](https://docs-lvgl-io.translate.goog/master/details/integration/building/cmake.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) が含まれており、すぐ使えるようにサポート。
- [PCのシミュレータで開発したUIコード](https://docs-lvgl-io.translate.goog/master/details/integration/ide/pc-simulator.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) は、そのまま組込み用ハードウェアでも使用可能。
- [Emscripten port](https://github.com/lvgl/lv_web_emscripten) :gb: によりC言語のUIコードをHTMLファイルに変換。
**ドキュメント, ツール, 技術サービス**
- [ドキュメント](https://docs-lvgl-io.translate.goog/master/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)は[100以上の簡単なサンプルプログラム](https://github.com/lvgl/lvgl/tree/master/examples) :gb: 入り 。
- [SquareLine Studio](https://squareline.io/) :gb: - UI開発をスピードアップおよび簡素化するためのプロフェッショナルで使いやすいUIエディターソフトウェア。
- UI開発をよりシンプルかつ迅速にするための、ユーザーインターフェイスの設計、実装、コンサルティングなどの [技術サービス](https://lvgl-io.translate.goog/services?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)。
## :package: パッケージ
LVGL は以下で利用可能です。
- [Arduino library](https://docs-lvgl-io.translate.goog/master/details/integration/framework/arduino.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)
- [PlatformIO package](https://registry.platformio.org/libraries/lvgl/lvgl) :gb:
- [Zephyr library](https://docs-zephyrproject-org.translate.goog/latest/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)
- [ESP32 component](https://docs-lvgl-io.translate.goog/master/details/integration/chip/espressif.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)
- [NXP MCUXpresso component](https://www-nxp-com.translate.goog/design/software/embedded-software/lvgl-open-source-graphics-library:LITTLEVGL-OPEN-SOURCE-GRAPHICS-LIBRARY?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)
- [NuttX library](https://docs-lvgl-io.translate.goog/master/details/integration/os/nuttx.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)
- [RT-Thread RTOS](https://docs-lvgl-io.translate.goog/master/details/integration/os/rt-thread.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)
- NXP MCUXpresso library
- CMSIS-Pack
## :robot: サンプルプログラム
ウィジェット・レイアウト・スタイルのサンプルプログラムを用意しました。
C と MicroPython のコードを選べます。
オンラインの MicroPythonエディタ へのリンクにより、サンプルプログラムの動作確認・編集もできます。
その他のサンプルプログラムは [Examples フォルダ](https://github.com/lvgl/lvgl/tree/master/examples) :gb: を確認してください。
### Button with Click Event
![ラベル付きボタンのLVGLサンプルプログラム](https://github.com/kisvegabor/test/raw/master/readme_example_2.gif)
<details>
<summary>C code</summary>
```c
lv_obj_t * btn = lv_button_create(lv_screen_active());          /*Add a button to the current screen*/
lv_obj_center(btn);                                     /*Set its position*/
lv_obj_set_size(btn, 100, 50);                                  /*Set its size*/
lv_obj_add_event(btn, btn_event_cb, LV_EVENT_CLICKED, NULL); /*Assign a callback to the button*/
lv_obj_t * label = lv_label_create(btn);                        /*Add a label to the button*/
lv_label_set_text(label, "Button");                             /*Set the labels text*/
lv_obj_center(label);                                           /*Align the label to the center*/
...
void btn_event_cb(lv_event_t * e)
{
  printf("Clicked\n");
}
```
</details>
<details>
<summary>MicroPython code | <a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=926bde43ec7af0146c486de470c53f11f167491e" target="_blank">Online Simulator</a> :gb:</summary>
```python
def btn_event_cb(e):
  print("Clicked")
# Create a Button and a Label
btn = lv.btn(lv.screen_active())
btn.center()
btn.set_size(100, 50)
btn.add_event(btn_event_cb, lv.EVENT.CLICKED, None)
label = lv.label(btn)
label.set_text("Button")
label.center()
```
</details>
<br>
### Checkboxes with Layout
![Checkboxes with layout in LVGL](https://github.com/kisvegabor/test/raw/master/readme_example_3.gif)
<details>
<summary>C code</summary>
```c
lv_obj_set_flex_flow(lv_screen_active(), LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(lv_screen_active(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER);
lv_obj_t * cb;
cb = lv_checkbox_create(lv_screen_active());
lv_checkbox_set_text(cb, "Apple");
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
cb = lv_checkbox_create(lv_screen_active());
lv_checkbox_set_text(cb, "Banana");
lv_obj_add_state(cb, LV_STATE_CHECKED);
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
cb = lv_checkbox_create(lv_screen_active());
lv_checkbox_set_text(cb, "Lemon");
lv_obj_add_state(cb, LV_STATE_DISABLED);
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
cb = lv_checkbox_create(lv_screen_active());
lv_obj_add_state(cb, LV_STATE_CHECKED | LV_STATE_DISABLED);
lv_checkbox_set_text(cb, "Melon\nand a new line");
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
```
</details>
<details>
<summary>MicroPython code | <a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=311d37e5f70daf1cb0d2cad24c7f72751b5f1792" target="_blank">Online Simulator</a> :gb:</summary>
```python
def event_handler(e):
code = e.get_code()
obj = e.get_target_obj()
if code == lv.EVENT.VALUE_CHANGED:
txt = obj.get_text()
if obj.get_state() & lv.STATE.CHECKED:
state = "Checked"
else:
state = "Unchecked"
print(txt + ":" + state)
lv.scr_act().set_flex_flow(lv.FLEX_FLOW.COLUMN)
lv.scr_act().set_flex_align(lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.START, lv.FLEX_ALIGN.CENTER)
cb = lv.checkbox(lv.screen_active())
cb.set_text("Apple")
cb.add_event(event_handler, lv.EVENT.ALL, None)
cb = lv.checkbox(lv.screen_active())
cb.set_text("Banana")
cb.add_state(lv.STATE.CHECKED)
cb.add_event(event_handler, lv.EVENT.ALL, None)
cb = lv.checkbox(lv.screen_active())
cb.set_text("Lemon")
cb.add_state(lv.STATE.DISABLED)
cb.add_event(event_handler, lv.EVENT.ALL, None)
cb = lv.checkbox(lv.screen_active())
cb.add_state(lv.STATE.CHECKED | lv.STATE.DISABLED)
cb.set_text("Melon")
cb.add_event(event_handler, lv.EVENT.ALL, None)
```
</details>
<br>
### Styling a Slider
![Styling a slider with LVGL](https://github.com/kisvegabor/test/raw/master/readme_example_4.gif)
<details>
<summary>C code</summary>
```c
lv_obj_t * slider = lv_slider_create(lv_screen_active());
lv_slider_set_value(slider, 70, LV_ANIM_OFF);
lv_obj_set_size(slider, 300, 20);
lv_obj_center(slider);
/*Add local styles to MAIN part (background rectangle)*/
lv_obj_set_style_bg_color(slider, lv_color_hex(0x0F1215), LV_PART_MAIN);
lv_obj_set_style_bg_opa(slider, 255, LV_PART_MAIN);
lv_obj_set_style_border_color(slider, lv_color_hex(0x333943), LV_PART_MAIN);
lv_obj_set_style_border_width(slider, 5, LV_PART_MAIN);
lv_obj_set_style_pad_all(slider, 5, LV_PART_MAIN);
/*Create a reusable style sheet for the INDICATOR part*/
static lv_style_t style_indicator;
lv_style_init(&style_indicator);
lv_style_set_bg_color(&style_indicator, lv_color_hex(0x37B9F5));
lv_style_set_bg_grad_color(&style_indicator, lv_color_hex(0x1464F0));
lv_style_set_bg_grad_dir(&style_indicator, LV_GRAD_DIR_HOR);
lv_style_set_shadow_color(&style_indicator, lv_color_hex(0x37B9F5));
lv_style_set_shadow_width(&style_indicator, 15);
lv_style_set_shadow_spread(&style_indicator, 5);
/*Add the style sheet to the slider's INDICATOR part*/
lv_obj_add_style(slider, &style_indicator, LV_PART_INDICATOR);
/*Add the same style to the KNOB part too and locally overwrite some properties*/
lv_obj_add_style(slider, &style_indicator, LV_PART_KNOB);
lv_obj_set_style_outline_color(slider, lv_color_hex(0x0096FF), LV_PART_KNOB);
lv_obj_set_style_outline_width(slider, 3, LV_PART_KNOB);
lv_obj_set_style_outline_pad(slider, -5, LV_PART_KNOB);
lv_obj_set_style_shadow_spread(slider, 2, LV_PART_KNOB);
```
</details>
<details>
<summary>MicroPython code |
<a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=c431c7b4dfd2cc0dd9c392b74365d5af6ea986f0" target="_blank">Online Simulator</a> :gb:
</summary>
```python
# Create a slider and add the style
slider = lv.slider(lv.screen_active())
slider.set_value(70, lv.ANIM.OFF)
slider.set_size(300, 20)
slider.center()
# Add local styles to MAIN part (background rectangle)
slider.set_style_bg_color(lv.color_hex(0x0F1215), lv.PART.MAIN)
slider.set_style_bg_opa(255, lv.PART.MAIN)
slider.set_style_border_color(lv.color_hex(0x333943), lv.PART.MAIN)
slider.set_style_border_width(5, lv.PART.MAIN)
slider.set_style_pad_all(5, lv.PART.MAIN)
# Create a reusable style sheet for the INDICATOR part
style_indicator = lv.style_t()
style_indicator.init()
style_indicator.set_bg_color(lv.color_hex(0x37B9F5))
style_indicator.set_bg_grad_color(lv.color_hex(0x1464F0))
style_indicator.set_bg_grad_dir(lv.GRAD_DIR.HOR)
style_indicator.set_shadow_color(lv.color_hex(0x37B9F5))
style_indicator.set_shadow_width(15)
style_indicator.set_shadow_spread(5)
# Add the style sheet to the slider's INDICATOR part
slider.add_style(style_indicator, lv.PART.INDICATOR)
slider.add_style(style_indicator, lv.PART.KNOB)
# Add the same style to the KNOB part too and locally overwrite some properties
slider.set_style_outline_color(lv.color_hex(0x0096FF), lv.PART.KNOB)
slider.set_style_outline_width(3, lv.PART.KNOB)
slider.set_style_outline_pad(-5, lv.PART.KNOB)
slider.set_style_shadow_spread(2, lv.PART.KNOB)
```
</details>
<br>
### English, Hebrew (mixed LRT-RTL) and Chinese texts
![English, Hebrew and Chinese texts with LVGL](https://raw.githubusercontent.com/kisvegabor/test/master/readme_example_5.png)
<details>
<summary>C code</summary>
```c
lv_obj_t * ltr_label = lv_label_create(lv_screen_active());
lv_label_set_text(ltr_label, "In modern terminology, a microcontroller is similar to a system on a chip (SoC).");
lv_obj_set_style_text_font(ltr_label, &lv_font_montserrat_16, 0);
lv_obj_set_width(ltr_label, 310);
lv_obj_align(ltr_label, LV_ALIGN_TOP_LEFT, 5, 5);
lv_obj_t * rtl_label = lv_label_create(lv_screen_active());
lv_label_set_text(rtl_label,"מעבד, או בשמו המלא יחידת עיבוד מרכזית (באנגלית: CPU - Central Processing Unit).");
lv_obj_set_style_base_dir(rtl_label, LV_BASE_DIR_RTL, 0);
lv_obj_set_style_text_font(rtl_label, &lv_font_dejavu_16_persian_hebrew, 0);
lv_obj_set_width(rtl_label, 310);
lv_obj_align(rtl_label, LV_ALIGN_LEFT_MID, 5, 0);
lv_obj_t * cz_label = lv_label_create(lv_screen_active());
lv_label_set_text(cz_label,
"嵌入式系统Embedded System\n是一种嵌入机械或电气系统内部、具有专一功能和实时计算性能的计算机系统。");
lv_obj_set_style_text_font(cz_label, &lv_font_source_han_sans_sc_16_cjk, 0);
lv_obj_set_width(cz_label, 310);
lv_obj_align(cz_label, LV_ALIGN_BOTTOM_LEFT, 5, -5);
```
</details>
<details>
<summary>MicroPython code | <a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=18bb38200a64e10ead1aa17a65c977fc18131842" target="_blank">Online Simulator</a> :gb:</summary>
```python
ltr_label = lv.label(lv.screen_active())
ltr_label.set_text("In modern terminology, a microcontroller is similar to a system on a chip (SoC).")
ltr_label.set_style_text_font(lv.font_montserrat_16, 0);
ltr_label.set_width(310)
ltr_label.align(lv.ALIGN.TOP_LEFT, 5, 5)
rtl_label = lv.label(lv.screen_active())
rtl_label.set_text("מעבד, או בשמו המלא יחידת עיבוד מרכזית (באנגלית: CPU - Central Processing Unit).")
rtl_label.set_style_base_dir(lv.BASE_DIR.RTL, 0)
rtl_label.set_style_text_font(lv.font_dejavu_16_persian_hebrew, 0)
rtl_label.set_width(310)
rtl_label.align(lv.ALIGN.LEFT_MID, 5, 0)
font_han_sans_16_cjk = lv.font_load("S:../../assets/font/lv_font_source_han_sans_sc_16_cjk.fnt")
cz_label = lv.label(lv.screen_active())
cz_label.set_style_text_font(font_han_sans_16_cjk, 0)
cz_label.set_text("嵌入式系统Embedded System\n是一种嵌入机械或电气系统内部、具有专一功能和实时计算性能的计算机系统。")
cz_label.set_width(310)
cz_label.align(lv.ALIGN.BOTTOM_LEFT, 5, -5)
```
</details>
## :arrow_forward: はじめに
LVGLを使い始める時は、以下の順に進める事をおすすめします。
**LVGLに触れてみましょう**
1. LVGLの動きを [オンラインデモ](https://lvgl-io.translate.goog/demos?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) で確認しましょう。 (3分間)
2. ドキュメントの [Introduction](https://docs-lvgl-io.translate.goog/master/intro/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) を読みましょう。 (5分間)
3. LVGLの基本に慣れるため [Quick overview](https://docs-lvgl-io.translate.goog/master/intro/getting_started.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) を読みましょう。 (15分間)
**LVGLを使ってみましょう**
4. [シミュレータ](https://docs-lvgl-io.translate.goog/master/details/integration/ide/pc-simulator.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) をセットアップしましょう。 (10 minutes)
5. [サンプルプログラム](https://github.com/lvgl/lvgl/tree/master/examples) :gb: を動かしてみましょう。
6. [移植ガイド](https://docs-lvgl-io.translate.goog/master/details/integration/adding-lvgl-to-your-project/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) を参考に、LVGLを開発ボードに移植してみましょう。すぐ使える形の [プロジェクト](https://github.com/lvgl?q=lv_port_) :gb: も用意してあります。
**より詳しく体験してみましょう**
7. ライブラリの理解を深めるため [Overview](https://docs-lvgl-io.translate.goog/master/intro/getting_started.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) を読みましょう。 (23時間)
8. ウィジェットの機能や使い方の詳細は [Widgets](https://docs-lvgl-io.translate.goog/master/details/widgets/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) でご確認ください。
**助け合いましょう**
9. 質問がある場合は [Forum](http://forum.lvgl.io/) :gb: で質問して下さい。
10. LVGLの改善への協力は大歓迎です。詳細は [Contributing guide](https://docs-lvgl-io.translate.goog/master/contributing/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) をご覧ください。 (15分間)
**さらに理解を深めましょう**
11. [SquareLine Studio](https://squareline.io/) :gb: をダウンロードして試用してみましょう。
12. 技術的サポートが必要であれば、[技術サービス](https://lvgl.io/services) :gb: に問い合わせて下さい。
## :handshake: 技術サービス
[LVGL LLC](https://www.digikey.com/en/design-services-providers/lvgl-kft) は、LVGLライブラリの確かな背景を元に、UI開発のための様々な技術サービスを提供するために設立されました。 UIとグラフィックス業界における15年以上の実績を活かし、UIを次のレベルに引き上げるお手伝いを致します。
- **グラフィックデザイン** 当社のグラフィックデザイナーは、製品とハードウェアのリソースに合わせて美しくモダンなデザインにするエキスパートです。
- **UI実装** お客様または弊社で作成したデザインを元に、UIを実装することも可能です。お客様のハードウェアとLVGLを最大限に活用することをお約束します。
LVGLにない機能やウィジェットは、私たちが実装しますのでご安心ください。
- **コンサルタント&技術サポート** UI開発において、価格と時間を要する作業でのリスクを減らすため、コンサルティングも含めてサポート致します。
- **Board certification** development board または production ready kit を提供している企業に対しては、ボードがLVGLを実行できるようにするためのボード認定を行います。
サンプルは [Demos](https://lvgl-io.translate.goog/demos?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) をご覧ください。
詳しくは [Services page](https://lvgl-io.translate.goog/services?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) をご覧ください。
お問い合わせは [問い合わせフォーム](https://lvgl.io/#contact) :gb: より送信して下さい。
## :star2: 協力
LVGLはオープンプロジェクトであり、協力は大歓迎です。
色々な方法で協力できます。
協力方法の例
- LVGLを使用した作品やプロジェクトの公表
- サンプルプログラムの作成
- ドキュメントの改善
- バグの修正
協力方法の詳細については、ドキュメントの [Contributing section](https://docs-lvgl-io.translate.goog/master/contributing/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) をご覧ください。
すでに 300人以上がLVGLに足跡を残しています。いっしょに活動しましょう! :slightly_smiling_face:
<a href="https://github.com/lvgl/lvgl/graphs/contributors">
<img src="https://contrib.rocks/image?repo=lvgl/lvgl&max=48" />
</a>
... and many others.

View File

@@ -0,0 +1,453 @@
**NOTA IMPORTANTE** A próxima versão principal (v9.0.0) está sendo desenvolvida na branch master.
A última versão estável está disponível na branch [release/v8.3](https://github.com/lvgl/lvgl/tree/release/v8.3).
---
<a href="https://github.com/sponsors/lvgl" target="_blank"><img align="left" src="https://lvgl.io/github-assets/sponsor.png" height="32px"></a>
<p align="right">
<a href="../README.md">English</a> |
<a href="./README_zh.md">中文</a> |
<b>Português do Brasil</b> |
<a href="./README_jp.md">日本語</a>
</p>
<p align="center">
  <img src="https://lvgl.io/github-assets/logo-colored.png" width=300px>
</p>
  <h1 align="center">LVGL - Biblioteca gráfica leve e versátil</h1>
  <br>
<div align="center">
<img src="https://raw.githubusercontent.com/kisvegabor/test/master/smartwatch_demo.gif">
&nbsp;
  <img border="1px" src="https://lvgl.io/github-assets/widgets-demo.gif">
</div>
<br>
<p align="center">
  <a href="https://lvgl.io" title="Página inicial do LVGL">Site</a> |
  <a href="https://docs.lvgl.io/" title="Documentação detalhada com +100 exemplos">Documentação</a> |
  <a href="https://forum.lvgl.io" title="Obtenha ajuda e ajude outras pessoas">Fórum</a> |
  <a href="https://lvgl.io/services" title="Design gráfico, implementações e consultoria de serviços">Serviços</a> |
  <a href="https://lvgl.io/demos" title="Execute demonstrações no seu navegador">Demonstrações</a>
</p>
<br>
## :monocle_face: Visão Geral
**Maduro e popular**
LVGL é a biblioteca gráfica incorporada gratuita e de código aberto mais popular para criar belas interfaces de usuário para qualquer display do tipo MCU, MPU. Ela é suportada por fornecedores e projetos líderes do setor, como ARM, STM32, NXP, Espressif, Nuvoton, Arduino, RT-Thread, Zephyr, NuttX, Adafruit e muitos outros.
**Rico em recursos**
Ela tem todos os recursos para a criação de GUIs modernas e bonitas: mais de 30 widgets integrados, um sistema de design poderoso, gerenciadores de layout inspirados na web e um sistema de tipografia com suporte para vários idiomas. Para integrar o LVGL em sua plataforma, tudo que você precisa é de pelo menos 32kB de RAM e 128kB de Flash, um compilador C, um frame buffer e pelo menos uma tela de tamanho 1/10 para renderização.
**Editor UI profissional**
SquareLine Studio é um editor de interface do usuário de (arrasta e solta) profissional para LVGL. Ele roda em Windows, Linux e MacOS também e você pode experimentá-lo sem se registrar no site.
**Serviços**
Nossa equipe está pronta para ajudá-lo com design gráfico, implementação de UI e serviços de consultoria. Entre em contato conosco se precisar de algum suporte durante o desenvolvimento de seu próximo projeto de GUI.
## :rocket: Recursos
**Gratuito e portátil**
- Uma biblioteca C totalmente portátil (compatível com C++) sem dependências externas.
- Pode ser compilado para qualquer display MCU ou MPU, e qualquer sistema operacional de tempo real (RT-OS).
- Suporta monitores monocromáticos, ePaper, OLED ou TFT. [Guia de portabilidade](https://docs.lvgl.io/master/details/integration/adding-lvgl-to-your-project/index.html)
- Distribuído sob a licença do MIT, para que você também possa usá-lo facilmente em projetos comerciais.
- Precisa de apenas 32 kB de RAM e 128 kB de Flash, um frame buffer e pelo menos uma tela de tamanho 1/10 para renderização.
- Sistemas operacionais, memória externa e GPU são suportados, mas não obrigatórios.
**Widgets, designs, layouts e muito mais**
- Mais de 30 widgets integrados: botão, etiqueta (label), controle deslizante (slider), gráfico (chart), teclado, medidor (meter), tabelas e muito mais.
- Sistema de design flexível com pelo menos 100 propriedades de estilo para personalizar qualquer parte dos widgets.
- Mecanismos de layouts Flexbox e Grid para dimensionar e posicionar automaticamente os widgets de maneira responsiva.
- Os textos são renderizados com codificação UTF-8, suportando sistemas de escrita CJK (chinês, japonês e coreano), tailandês, hindi, árabe e persa.
- Quebra de palavras (word wrapping), espaçamento entre letras (kerning), rolagem de texto (scrolling), renderização subpixel, entrada em chinês Pinyin-IME e emojis.
- Mecanismo de renderização que suporta animações, anti-aliasing, opacidade, rolagem suave (smooth scroll), sombras, transformação de imagens, etc.
- Suporta mouse, touchpad, teclado, botões externos, dispositivos de entrada codificadores (encoders).
- Suporta vários monitores.
**Suporte de vinculação (binding) e compilação de arquivos**
- Exposição da API do LVGL com o [Micropython](https://blog.lvgl.io/2019-02-20/micropython-bindings)
- Nenhum sistema de compilação personalizado é usado. Você pode construir o LVGL enquanto constrói os outros arquivos do seu projeto.
- O suporte para Make e [CMake](https://docs.lvgl.io/master/details/integration/building/cmake.html) já vem incluído.
- [Desenvolva no PC](https://docs.lvgl.io/master/details/integration/ide/pc-simulator.html) e use o mesmo código de interface do usuário em hardwares incorporados (embedded hardware).
- Converta o código C para um arquivo HTML com o [Emscripten port](https://github.com/lvgl/lv_web_emscripten).
**Documentação, ferramentas e serviços**
- Documentação detalhada com [+100 exemplos simples](https://docs.lvgl.io/master/examples.html)
- [SquareLine Studio](https://squareline.io) - Um software editor UI profissional e fácil de usar, para acelerar e simplificar o desenvolvimento da interface do usuário.
- [Serviços](https://lvgl.io/services) como design de UI, implementação e consultoria para tornar o desenvolvimento de UI mais simples e rápido.
## :heart: Patrocinador
Se o LVGL economizou muito tempo e dinheiro ou você apenas se divertiu ao usá-lo, considere Apoiar o desenvolvimento.
**Como e com o que utilizamos os recursos doados?**
Nosso objetivo é fornecer compensação financeira para as pessoas que mais fazem pelo LVGL. Isso significa que não apenas os mantenedores, mas qualquer pessoa que implemente um ótimo recurso deve receber um pagamento com o dinheiro acumulado. Usamos as doações para cobrir nossos custos operacionais, como servidores e serviços relacionados.
**Como doar?**
Usamos o [Open Collective](https://opencollective.com/lvgl), onde você pode enviar facilmente doações únicas ou recorrentes. Você também pode ver todas as nossas despesas de forma transparente.
**Como receber o pagamento de sua contribuição?**
Se alguém implementar ou corrigir um problema rotulado como [Patrocinado](https://github.com/lvgl/lvgl/labels/Sponsored), essa pessoa receberá um pagamento por esse trabalho. Estimamos o tempo necessário, a complexidade e a importância da questão e definimos um preço de acordo. Para entrar, apenas comente sobre um problema patrocinado dizendo "Olá, gostaria de lidar com isso. É assim que estou planejando corrigi-lo/implementá-lo...". Um trabalho é considerado pronto quando é aprovado e mesclado por um mantenedor. Depois disso, você pode enviar uma "despesa" (expense) pela plataforma [opencollective.com](https://opencollective.com/lvgl) e então receberá o pagamento em alguns dias.
**Organizações que apoiam o projeto LVGL**<br>
[![Patrocinadores do LVGL](https://opencollective.com/lvgl/organizations.svg?width=600)](https://opencollective.com/lvgl)
**Pessoas que apoiam o projeto LVGL**<br>
[![Backers of LVGL](https://contrib.rocks/image?repo=lvgl/lvgl&max=48)](https://opencollective.com/lvgl)
## :package: Pacotes
LVGL está disponível para:
- [Arduino library](https://docs.lvgl.io/master/details/integration/framework/arduino.html)
- [PlatformIO package](https://registry.platformio.org/libraries/lvgl/lvgl)
- [Zephyr library](https://docs.zephyrproject.org/latest/kconfig.html#CONFIG_LVGL)
- [ESP32 component](https://docs.lvgl.io/master/details/integration/chip/espressif.html)
- [NXP MCUXpresso component](https://www.nxp.com/design/software/embedded-software/lvgl-open-source-graphics-library:LITTLEVGL-OPEN-SOURCE-GRAPHICS-LIBRARY)
- [NuttX library](https://docs.lvgl.io/master/details/integration/os/nuttx.html)
- [RT-Thread RTOS](https://docs.lvgl.io/master/details/integration/os/rt-thread.html)
- NXP MCUXpresso library
- CMSIS-Pack
## :man_technologist: Exemplos
Veja como criar um botão com um evento de clique em C e MicroPython. Para mais exemplos, veja a pasta [examples](https://github.com/lvgl/lvgl/tree/master/examples).
### Botão com evento de clique
![Botão LVGL com exemplo de rótulo (label)](https://github.com/kisvegabor/test/raw/master/readme_example_2.gif)
<details>
<summary>Código C</summary>
```c
lv_obj_t * btn = lv_button_create(lv_screen_active());          /* Adiciona o botão a tela atual */
lv_obj_center(btn);                                     /* Define a posição do botão */
lv_obj_set_size(btn, 100, 50);                                  /* Define o tamanho do botão */
lv_obj_add_event(btn, btn_event_cb, LV_EVENT_CLICKED, NULL); /* Atribui um retorno de chamada (callback) ao botão */
lv_obj_t * label = lv_label_create(btn);                        /* Adiciona um rótulo (label) */
lv_label_set_text(label, "Botão");                             /* Define um texto para o rótulo (label) */
lv_obj_center(label);                                           /* Alinha o texto no centro do botão */
...
void btn_event_cb(lv_event_t * e)
{
  printf("Clicado\n");
}
```
</details>
<details>
<summary>Código MicroPython | <a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=926bde43ec7af0146c486de470c53f11f167491e" target="_blank">Simulador online</a></summary>
```python
def btn_event_cb(e):
  print("Clicado")
# Cria um botão e um rótulo (label)
btn = lv.btn(lv.screen_active())
btn.center()
btn.set_size(100, 50)
btn.add_event(btn_event_cb, lv.EVENT.CLICKED, None)
label = lv.label(btn)
label.set_text("Botão")
label.center()
```
</details>
<br>
### Caixas de seleção (chackboxes) com layout
![Caixas de seleção (chackboxes) com layout no LVGL](https://github.com/kisvegabor/test/raw/master/readme_example_3.gif)
<details>
<summary>Código em C</summary>
```c
lv_obj_set_flex_flow(lv_screen_active(), LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(lv_screen_active(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER);
lv_obj_t * cb;
cb = lv_checkbox_create(lv_screen_active());
lv_checkbox_set_text(cb, "Maça");
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
cb = lv_checkbox_create(lv_screen_active());
lv_checkbox_set_text(cb, "Banana");
lv_obj_add_state(cb, LV_STATE_CHECKED);
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
cb = lv_checkbox_create(lv_screen_active());
lv_checkbox_set_text(cb, "Limão");
lv_obj_add_state(cb, LV_STATE_DISABLED);
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
cb = lv_checkbox_create(lv_screen_active());
lv_obj_add_state(cb, LV_STATE_CHECKED | LV_STATE_DISABLED);
lv_checkbox_set_text(cb, "Melão\ne uma nova linha");
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
```
</details>
<details>
<summary>Código MicroPython | <a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=311d37e5f70daf1cb0d2cad24c7f72751b5f1792" target="_blank">Online Simulator</a></summary>
```python
def event_handler(e):
code = e.get_code()
obj = e.get_target_obj()
if code == lv.EVENT.VALUE_CHANGED:
txt = obj.get_text()
if obj.get_state() & lv.STATE.CHECKED:
state = "Marcador"
else:
state = "Desmarcado"
print(txt + ":" + state)
lv.scr_act().set_flex_flow(lv.FLEX_FLOW.COLUMN)
lv.scr_act().set_flex_align(lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.START, lv.FLEX_ALIGN.CENTER)
cb = lv.checkbox(lv.screen_active())
cb.set_text("Maça")
cb.add_event(event_handler, lv.EVENT.ALL, None)
cb = lv.checkbox(lv.screen_active())
cb.set_text("Banana")
cb.add_state(lv.STATE.CHECKED)
cb.add_event(event_handler, lv.EVENT.ALL, None)
cb = lv.checkbox(lv.screen_active())
cb.set_text("Limão")
cb.add_state(lv.STATE.DISABLED)
cb.add_event(event_handler, lv.EVENT.ALL, None)
cb = lv.checkbox(lv.screen_active())
cb.add_state(lv.STATE.CHECKED | lv.STATE.DISABLED)
cb.set_text("Melão")
cb.add_event(event_handler, lv.EVENT.ALL, None)
```
</details>
<br>
### Estilizando um controle deslizante (slider)
![Estilizando um controle deslizante (slider) com LVGL](https://github.com/kisvegabor/test/raw/master/readme_example_4.gif)
<details>
<summary>Código C</summary>
```c
lv_obj_t * slider = lv_slider_create(lv_screen_active());
lv_slider_set_value(slider, 70, LV_ANIM_OFF);
lv_obj_set_size(slider, 300, 20);
lv_obj_center(slider);
/* Adiciona estilos locais à parte MAIN (retângulo de fundo) */
lv_obj_set_style_bg_color(slider, lv_color_hex(0x0F1215), LV_PART_MAIN);
lv_obj_set_style_bg_opa(slider, 255, LV_PART_MAIN);
lv_obj_set_style_border_color(slider, lv_color_hex(0x333943), LV_PART_MAIN);
lv_obj_set_style_border_width(slider, 5, LV_PART_MAIN);
lv_obj_set_style_pad_all(slider, 5, LV_PART_MAIN);
/* Crie uma folha de estilo reutilizável para a parte do (INDICADOR) */
static lv_style_t style_indicator;
lv_style_init(&style_indicator);
lv_style_set_bg_color(&style_indicator, lv_color_hex(0x37B9F5));
lv_style_set_bg_grad_color(&style_indicator, lv_color_hex(0x1464F0));
lv_style_set_bg_grad_dir(&style_indicator, LV_GRAD_DIR_HOR);
lv_style_set_shadow_color(&style_indicator, lv_color_hex(0x37B9F5));
lv_style_set_shadow_width(&style_indicator, 15);
lv_style_set_shadow_spread(&style_indicator, 5);
/* Adicione a folha de estilo à parte do INDICATOR do controle deslizante (slider) */
lv_obj_add_style(slider, &style_indicator, LV_PART_INDICATOR);
/* Adicione o mesmo estilo à parte do KNOB e sobrescreva localmente algumas propriedades */
lv_obj_add_style(slider, &style_indicator, LV_PART_KNOB);
lv_obj_set_style_outline_color(slider, lv_color_hex(0x0096FF), LV_PART_KNOB);
lv_obj_set_style_outline_width(slider, 3, LV_PART_KNOB);
lv_obj_set_style_outline_pad(slider, -5, LV_PART_KNOB);
lv_obj_set_style_shadow_spread(slider, 2, LV_PART_KNOB);
```
</details>
<details>
<summary>Código MicroPython |
<a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=c431c7b4dfd2cc0dd9c392b74365d5af6ea986f0" target="_blank">Simulador online</a>
</summary>
```python
# Crie um controle deslizante (slider) e adicione o estilo
slider = lv.slider(lv.screen_active())
slider.set_value(70, lv.ANIM.OFF)
slider.set_size(300, 20)
slider.center()
# Adicione estilos locais à parte MAIN (retângulo de fundo)
slider.set_style_bg_color(lv.color_hex(0x0F1215), lv.PART.MAIN)
slider.set_style_bg_opa(255, lv.PART.MAIN)
slider.set_style_border_color(lv.color_hex(0x333943), lv.PART.MAIN)
slider.set_style_border_width(5, lv.PART.MAIN)
slider.set_style_pad_all(5, lv.PART.MAIN)
# Crie uma folha de estilo reutilizável para a parte do INDICATOR
style_indicator = lv.style_t()
style_indicator.init()
style_indicator.set_bg_color(lv.color_hex(0x37B9F5))
style_indicator.set_bg_grad_color(lv.color_hex(0x1464F0))
style_indicator.set_bg_grad_dir(lv.GRAD_DIR.HOR)
style_indicator.set_shadow_color(lv.color_hex(0x37B9F5))
style_indicator.set_shadow_width(15)
style_indicator.set_shadow_spread(5)
# Adicione a folha de estilo à parte do INDICATOR do controle deslizante (slider)
slider.add_style(style_indicator, lv.PART.INDICATOR)
slider.add_style(style_indicator, lv.PART.KNOB)
# Adicione o mesmo estilo à parte do KNOB e sobrescreva localmente algumas propriedades
slider.set_style_outline_color(lv.color_hex(0x0096FF), lv.PART.KNOB)
slider.set_style_outline_width(3, lv.PART.KNOB)
slider.set_style_outline_pad(-5, lv.PART.KNOB)
slider.set_style_shadow_spread(2, lv.PART.KNOB)
```
</details>
<br>
### Textos em inglês, hebraico (LRT-RTL misto) e chinês
![Textos em inglês, hebraico (LRT-RTL misto) e chinês com LVGL](https://raw.githubusercontent.com/kisvegabor/test/master/readme_example_5.png)
<details>
<summary>Código C</summary>
```c
lv_obj_t * ltr_label = lv_label_create(lv_screen_active());
lv_label_set_text(ltr_label, "In modern terminology, a microcontroller is similar to a system on a chip (SoC).");
lv_obj_set_style_text_font(ltr_label, &lv_font_montserrat_16, 0);
lv_obj_set_width(ltr_label, 310);
lv_obj_align(ltr_label, LV_ALIGN_TOP_LEFT, 5, 5);
lv_obj_t * rtl_label = lv_label_create(lv_screen_active());
lv_label_set_text(rtl_label,"מעבד, או בשמו המלא יחידת עיבוד מרכזית (באנגלית: CPU - Central Processing Unit).");
lv_obj_set_style_base_dir(rtl_label, LV_BASE_DIR_RTL, 0);
lv_obj_set_style_text_font(rtl_label, &lv_font_dejavu_16_persian_hebrew, 0);
lv_obj_set_width(rtl_label, 310);
lv_obj_align(rtl_label, LV_ALIGN_LEFT_MID, 5, 0);
lv_obj_t * cz_label = lv_label_create(lv_screen_active());
lv_label_set_text(cz_label,
"嵌入式系统Embedded System\n是一种嵌入机械或电气系统内部、具有专一功能和实时计算性能的计算机系统。");
lv_obj_set_style_text_font(cz_label, &lv_font_source_han_sans_sc_16_cjk, 0);
lv_obj_set_width(cz_label, 310);
lv_obj_align(cz_label, LV_ALIGN_BOTTOM_LEFT, 5, -5);
```
</details>
<details>
<summary>Código MicroPython | <a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=18bb38200a64e10ead1aa17a65c977fc18131842" target="_blank">Simulador online</a></summary>
```python
ltr_label = lv.label(lv.screen_active())
ltr_label.set_text("In modern terminology, a microcontroller is similar to a system on a chip (SoC).")
ltr_label.set_style_text_font(lv.font_montserrat_16, 0);
ltr_label.set_width(310)
ltr_label.align(lv.ALIGN.TOP_LEFT, 5, 5)
rtl_label = lv.label(lv.screen_active())
rtl_label.set_text("מעבד, או בשמו המלא יחידת עיבוד מרכזית (באנגלית: CPU - Central Processing Unit).")
rtl_label.set_style_base_dir(lv.BASE_DIR.RTL, 0)
rtl_label.set_style_text_font(lv.font_dejavu_16_persian_hebrew, 0)
rtl_label.set_width(310)
rtl_label.align(lv.ALIGN.LEFT_MID, 5, 0)
font_han_sans_16_cjk = lv.font_load("S:../../assets/font/lv_font_source_han_sans_sc_16_cjk.fnt")
cz_label = lv.label(lv.screen_active())
cz_label.set_style_text_font(font_han_sans_16_cjk, 0)
cz_label.set_text("嵌入式系统Embedded System\n是一种嵌入机械或电气系统内部、具有专一功能和实时计算性能的计算机系统。")
cz_label.set_width(310)
cz_label.align(lv.ALIGN.BOTTOM_LEFT, 5, -5)
```
</details>
## :arrow_forward: Começando
Esta lista irá guiá-lo para começar com o LVGL passo a passo.
**Familiarize-se com o LVGL**
1. Confira as [demos on-line](https://lvgl.io/demos) para ver o LVGL em ação (~3 minutos)
2. Leia a página de [introdução](https://docs.lvgl.io/master/intro/index.html) da documentação (~5 minutos)
3. Familiarize-se com o básico na página de [visão geral rápida](https://docs.lvgl.io/master/intro/getting_started.html) (~15 minutos)
**Começando a usar o LVGL**
4. Configure um [simulador](https://docs.lvgl.io/master/details/integration/ide/pc-simulator.html) (~10 minutos)
5. Experimente alguns [exemplos](https://github.com/lvgl/lvgl/tree/master/examples)
6. Porte o LVGL para uma placa. Veja o guia [portando o LVGL](https://docs.lvgl.io/master/details/integration/adding-lvgl-to-your-project/index.html) ou veja um projeto pronto para usar em [projetos](https://github.com/lvgl?q=lv_port_)
**Torne-se um profissional**
7. Leia a página [visão geral](https://docs.lvgl.io/master/intro/getting_started.html) para entender melhor a biblioteca (~2-3 horas)
8. Verifique a documentação dos [widgets](https://docs.lvgl.io/master/details/widgets/index.html) para ver seus recursos e usabilidade
**Obtenha ajuda e ajude outras pessoas**
9. Se você tiver dúvidas, acesse o [Fórum](http://forum.lvgl.io)
10. Leia o guia de [contribuição](https://docs.lvgl.io/master/contributing/index.html) para ver como você pode ajudar a melhorar o LVGL (~15 minutos)
**E mais**
11. Baixe e experimente o editor [SquareLine Studio](https://squareline.io).
12. Entre em contato conosco para [serviços e consultoria](https://lvgl.io/services).
## :handshake: Serviços
A LVGL LLC foi criada para fornecer uma base sólida para a biblioteca LVGL e oferecer vários tipos de serviços para ajudá-lo no desenvolvimento da sua interface do usuário. Com mais de 15 anos de experiência na indústria gráfica e de interface do usuário, podemos ajudá-lo a levar sua interface do usuário para o próximo nível.
- **Design gráfico**: Nossos designers gráficos internos são especialistas em criar belos designs modernos que se adaptam ao seu produto e aos recursos do seu hardware.
- **Implementação da interface do usuário**: Também podemos implementar sua interface do usuário com base no design que você ou nós criamos. Você pode ter certeza de que tiraremos o máximo proveito de seu hardware e do LVGL. Se um recurso ou widget estiver faltando no LVGL, não se preocupe, nós o implementaremos para você.
- **Consultoria e Suporte**: Também podemos apoiá-lo com consultoria para evitar erros que podem te custar caros durante o desenvolvimento da sua interface do usuário.
- **Certificação**: Para empresas que oferecem placas para desenvolvimento ou kits prontos para produção, fazemos certificação que mostram como uma placa pode executar o LVGL.
Confira nossas [demonstrações](https://lvgl.io/demos) como referência. Para obter mais informações, consulte a [página de serviços](https://lvgl.io/services).
[Fale conosco](https://lvgl.io/#contact) e conte como podemos ajudar.
## :star2: Contribuindo
O LVGL é um projeto aberto e sua contribuição é muito bem-vinda. Há muitas maneiras de contribuir, desde simplesmente falando sobre seu projeto, escrevendo exemplos, melhorando a documentação, corrigindo bugs até hospedar seu próprio projeto sob a organização LVGL.
Para obter uma descrição detalhada das oportunidades de contribuição, visite a página de [contribuição](https://docs.lvgl.io/master/contributing/index.html) da documentação.
Mais de 300 pessoas já deixaram sua impressão digital no LVGL. Seja um deles! Veja o seu aqui! :slightly_smiling_face:
<a href="https://github.com/lvgl/lvgl/graphs/contributors">
<img src="https://contrib.rocks/image?repo=lvgl/lvgl&max=48" />
</a>
... e muitos outros.

View File

@@ -0,0 +1,464 @@
<a href="https://github.com/sponsors/lvgl" target="_blank"><img align="left" src="https://lvgl.io/github-assets/sponsor.png" height="32px"></a>
<p align="right">
<a href="../README.md">English</a> | <b>中文</b> | <a href="./README_pt_BR.md">Português do Brasil</a> | <a href="./README_jp.md">日本語</a>
</p>
<br>
<p align="center">
<img src="https://lvgl.io/github-assets/logo-colored.png" width=300px>
</p>
<h1 align="center">
Light and Versatile Graphics Library
</h1>
<h1 align="center">
轻量级通用型图形库
</h1>
<div align="center">
<img src="https://raw.githubusercontent.com/kisvegabor/test/master/smartwatch_demo.gif">
&nbsp;
<img border="1px" src="https://lvgl.io/github-assets/widgets-demo.gif">
</div>
<br>
<p align="center">
<a href="https://lvgl.io" title="Homepage of LVGL">官网 </a> |
<a href="https://docs.lvgl.io/" title="Detailed documentation with 100+ examples">文档</a> |
<a href="https://forum.lvgl.io" title="Get help and help others">论坛</a> |
<a href="https://lvgl.io/demos" title="Demos running in your browser">示例</a> |
<a href="https://lvgl.io/services" title="Graphics design, UI implementation and consulting">服务</a>
</p>
[中文宣传单](./flyers/LVGL-Chinese-Flyer.pdf)
## :ledger: 目录
- [:ledger: 目录](#ledger-目录)
- [:rocket: 概况与总览](#rocket-概况与总览)
- [硬件要求](#硬件要求)
- [:package: 已经支持的平台](#package-已经支持的平台)
- [如何入门](#如何入门)
- [:robot: 例程](#robot-例程)
- [Button with Click Event 按钮与点击事件](#button-with-click-event-按钮与点击事件)
- [Micropython](#micropython)
- [Checkboxes with Layout 带布局的复选框](#checkboxes-with-layout-带布局的复选框)
- [Styling a Slider 设置滑块的样式](#styling-a-slider-设置滑块的样式)
- [English, Hebrew (mixed LRT-RTL) and Chinese texts 英语、希伯来语( 双向文本排版 )和中文](#english-hebrew-mixed-lrt-rtl-and-chinese-texts-英语希伯来语-双向文本排版-和中文)
- [:handshake: 服务](#handshake-服务)
- [:star: 如何向社区贡献](#star-如何向社区贡献)
## :rocket: 概况与总览
**特性**
- 丰富且强大的模块化[图形组件](https://docs.lvgl.io/master/details/widgets/index.html):按钮
(buttons)、图表 (charts)、列表 (lists)、滑动条 (sliders)、图片
(images) 等
- 高级的图形引擎:动画、抗锯齿、透明度、平滑滚动、图层混合等效果
- 支持多种[输入设备](https://docs.lvgl.io/master/details/main-modules/indev.html):触摸屏、键盘、编码器、按键等
- 支持[多显示设备](https://docs.lvgl.io/master/details/main-modules/display/index.html)
- 不依赖特定的硬件平台,可以在任何显示屏上运行
- 配置可裁剪最低资源占用64 kB Flash16 kB RAM
- 基于UTF-8的多语种支持例如中文、日文、韩文、阿拉伯文等
- 可以通过[类CSS](https://docs.lvgl.io/master/details/common-widget-features/styles/style.html)的方式来设计、布局图形界面(例如:[Flexbox](https://docs.lvgl.io/master/details/common-widget-features/layouts/flex.html)、[Grid](https://docs.lvgl.io/master/details/common-widget-features/layouts/grid.html)
- 支持操作系统、外置内存、以及硬件加速LVGL已内建支持STM32 DMA2D、SWM341 DMA2D、NXP PXP和VGLite
- 即便仅有[单缓冲区(frame buffer)](https://docs.lvgl.io/master/details/main-modules/display/index.html)的情况下,也可保证渲染如丝般顺滑
- 全部由C编写完成并支持C++调用
- 支持Micropython编程参见[LVGL API in Micropython](https://blog.lvgl.io/2019-02-20/micropython-bindings)
- 支持[模拟器](https://docs.lvgl.io/master/details/integration/ide/pc-simulator.html) 仿真,可以无硬件依托进行开发
- 丰富详实的[例程](https://github.com/lvgl/lvgl/tree/master/examples)
- 详尽的[文档](http://docs.lvgl.io/) 以及API参考手册可线上查阅或可下载为PDF格式
### 硬件要求
<table>
<tr>
<td><strong>要求</strong></td>
<td><strong>最低要求</strong></td>
<td><strong>建议要求</strong></td>
</tr>
<tr>
<td><strong>架构</strong></td>
<td colspan="2">16、32、64位微控制器或微处理器</td>
</tr>
<tr>
<td><strong>时钟</strong></td>
<td> &gt; 16 MHz</td>
<td> &gt; 48 MHz</td>
</tr>
<tr>
<td><strong>Flash/ROM</strong></td>
<td> &gt; 64 kB</td>
<td> &gt; 180 kB</td>
</tr>
<tr>
<td><strong>Static RAM</strong></td>
<td> &gt; 16 kB</td>
<td> &gt; 48 kB</td>
</tr>
<tr>
<td><strong>Draw buffer</strong></td>
<td> &gt; 1 × <em>hor. res.</em> pixels</td>
<td> &gt; 1/10屏幕大小 </td>
</tr>
<tr>
<td><strong>编译器</strong></td>
<td colspan="2">C99或更新 </td>
</tr>
</table>
*注意:资源占用情况与具体硬件平台、编译器等因素有关,上表中仅给出参考值*
### :package: 已经支持的平台
LVGL本身并不依赖特定的硬件平台任何满足LVGL硬件配置要求的微控制器均可运行LVGL。
如下仅列举其中一部分:
- NXP: Kinetis, LPC, iMX, iMX RT
- STM32F1, STM32F3, STM32F4, STM32F7, STM32L4, STM32L5, STM32H7
- Microchip dsPIC33, PIC24, PIC32MX, PIC32MZ
- [Linux frame buffer](https://blog.lvgl.io/2018-01-03/linux_fb) (/dev/fb)
- [Espressif ESP32](https://github.com/lvgl/lv_port_esp32)
- [Infineon Aurix](https://github.com/lvgl/lv_port_aurix)
- Nordic NRF52 Bluetooth modules
- Quectel modems
- [SYNWIT SWM341](https://www.synwit.cn/)
LVGL也支持
- [Arduino library](https://docs.lvgl.io/master/details/integration/framework/arduino.html)
- [PlatformIO package](https://registry.platformio.org/libraries/lvgl/lvgl)
- [Zephyr library](https://docs.zephyrproject.org/latest/kconfig.html#CONFIG_LVGL)
- [ESP32 component](https://docs.lvgl.io/master/details/integration/chip/espressif.html)
- [NXP MCUXpresso component](https://www.nxp.com/design/software/embedded-software/lvgl-open-source-graphics-library:LITTLEVGL-OPEN-SOURCE-GRAPHICS-LIBRARY)
- [NuttX library](https://docs.lvgl.io/master/details/integration/os/nuttx.html)
- [RT-Thread RTOS](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/packages-manual/lvgl-docs/introduction)
## 如何入门
请按照如下顺序来学习LVGL
1. 使用[网页在线例程](https://lvgl.io/demos) 来体验LVGL3分钟
2. 阅读文档[简介](https://docs.lvgl.io/master/intro/introduction.html)章节来初步了解LVGL5分钟
3. 再来阅读一下文档快速[快速概览](https://docs.lvgl.io/master/intro/getting_started.html)章节来了解LVGL的基本知识15分钟
4. 学习如何使用[模拟器](https://docs.lvgl.io/master/details/integration/ide/pc-simulator.html)来在电脑上仿真LVGL10分钟
5. 试着动手实践一些[例程](https://github.com/lvgl/lvgl/tree/master/examples)
6. 参考[移植指南](https://docs.lvgl.io/master/details/integration/adding-lvgl-to-your-project/index.html)尝试将LVGL移植到一块开发板上LVGL也已经提供了一些移植好的[工程](https://github.com/lvgl?q=lv_port_)
7. 仔细阅读文档[总览](https://docs.lvgl.io/master/details/main-modules/index.html)章节来更加深入的了解和熟悉LVGL2-3小时
8. 浏览文档[组件(Widgets)](https://docs.lvgl.io/master/details/widgets/index.html)章节来了解如何使用它们
9. 如果你有问题可以到LVGL[论坛](http://forum.lvgl.io/)提问
10. 阅读文档[如何向社区贡献](https://docs.lvgl.io/master/contributing/index.html)章节来看看你能帮LVGL社区做些什么以促进LVGL软件质量的不断提高15分钟
## :robot: 例程
更多例程请参见 [examples](https://github.com/lvgl/lvgl/tree/master/examples) 文件夹。
### Button with Click Event 按钮与点击事件
![Button with Click Event](https://raw.githubusercontent.com/kisvegabor/test/master/readme_example_2.gif)
<details>
<summary>C code</summary>
```c
lv_obj_t * btn = lv_button_create(lv_screen_active()); /*Add a button to the current screen*/
lv_obj_center(btn); /*Set its position*/
lv_obj_set_size(btn, 100, 50); /*Set its size*/
lv_obj_add_event(btn, btn_event_cb, LV_EVENT_CLICKED, NULL); /*Assign a callback to the button*/
lv_obj_t * label = lv_label_create(btn); /*Add a label to the button*/
lv_label_set_text(label, "Button"); /*Set the labels text*/
lv_obj_center(label); /*Align the label to the center*/
...
void btn_event_cb(lv_event_t * e)
{
printf("Clicked\n");
}
```
</details>
### Micropython
更多信息请到 [Micropython官网](https://docs.lvgl.io/master/get-started/bindings/micropython.html) 查询.
<details>
<summary>MicroPython code | <a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=926bde43ec7af0146c486de470c53f11f167491e" target="_blank">Online Simulator</a> :gb:</summary>
```python
def btn_event_cb(e):
print("Clicked")
# Create a Button and a Label
btn = lv.btn(lv.screen_active())
btn.center()
btn.set_size(100, 50)
btn.add_event(btn_event_cb, lv.EVENT.CLICKED, None)
label = lv.label(btn)
label.set_text("Button")
label.center()
```
</details>
<br>
### Checkboxes with Layout 带布局的复选框
![Checkboxes with layout in LVGL](https://raw.githubusercontent.com/kisvegabor/test/master/readme_example_3.gif)
<details>
<summary>C code</summary>
```c
lv_obj_set_flex_flow(lv_screen_active(), LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(lv_screen_active(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER);
lv_obj_t * cb;
cb = lv_checkbox_create(lv_screen_active());
lv_checkbox_set_text(cb, "Apple");
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
cb = lv_checkbox_create(lv_screen_active());
lv_checkbox_set_text(cb, "Banana");
lv_obj_add_state(cb, LV_STATE_CHECKED);
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
cb = lv_checkbox_create(lv_screen_active());
lv_checkbox_set_text(cb, "Lemon");
lv_obj_add_state(cb, LV_STATE_DISABLED);
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
cb = lv_checkbox_create(lv_screen_active());
lv_obj_add_state(cb, LV_STATE_CHECKED | LV_STATE_DISABLED);
lv_checkbox_set_text(cb, "Melon\nand a new line");
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
```
</details>
<details>
<summary>MicroPython code | <a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=311d37e5f70daf1cb0d2cad24c7f72751b5f1792" target="_blank">Online Simulator</a> :gb:</summary>
```python
def event_handler(e):
code = e.get_code()
obj = e.get_target_obj()
if code == lv.EVENT.VALUE_CHANGED:
txt = obj.get_text()
if obj.get_state() & lv.STATE.CHECKED:
state = "Checked"
else:
state = "Unchecked"
print(txt + ":" + state)
lv.scr_act().set_flex_flow(lv.FLEX_FLOW.COLUMN)
lv.scr_act().set_flex_align(lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.START, lv.FLEX_ALIGN.CENTER)
cb = lv.checkbox(lv.screen_active())
cb.set_text("Apple")
cb.add_event(event_handler, lv.EVENT.ALL, None)
cb = lv.checkbox(lv.screen_active())
cb.set_text("Banana")
cb.add_state(lv.STATE.CHECKED)
cb.add_event(event_handler, lv.EVENT.ALL, None)
cb = lv.checkbox(lv.screen_active())
cb.set_text("Lemon")
cb.add_state(lv.STATE.DISABLED)
cb.add_event(event_handler, lv.EVENT.ALL, None)
cb = lv.checkbox(lv.screen_active())
cb.add_state(lv.STATE.CHECKED | lv.STATE.DISABLED)
cb.set_text("Melon")
cb.add_event(event_handler, lv.EVENT.ALL, None)
```
</details>
### Styling a Slider 设置滑块的样式
![Styling a slider with LVGL](https://raw.githubusercontent.com/kisvegabor/test/master/readme_example_4.gif)
<details>
<summary>C code</summary>
```c
lv_obj_t * slider = lv_slider_create(lv_screen_active());
lv_slider_set_value(slider, 70, LV_ANIM_OFF);
lv_obj_set_size(slider, 300, 20);
lv_obj_center(slider);
/*Add local styles to MAIN part (background rectangle)*/
lv_obj_set_style_bg_color(slider, lv_color_hex(0x0F1215), LV_PART_MAIN);
lv_obj_set_style_bg_opa(slider, 255, LV_PART_MAIN);
lv_obj_set_style_border_color(slider, lv_color_hex(0x333943), LV_PART_MAIN);
lv_obj_set_style_border_width(slider, 5, LV_PART_MAIN);
lv_obj_set_style_pad_all(slider, 5, LV_PART_MAIN);
/*Create a reusable style sheet for the INDICATOR part*/
static lv_style_t style_indicator;
lv_style_init(&style_indicator);
lv_style_set_bg_color(&style_indicator, lv_color_hex(0x37B9F5));
lv_style_set_bg_grad_color(&style_indicator, lv_color_hex(0x1464F0));
lv_style_set_bg_grad_dir(&style_indicator, LV_GRAD_DIR_HOR);
lv_style_set_shadow_color(&style_indicator, lv_color_hex(0x37B9F5));
lv_style_set_shadow_width(&style_indicator, 15);
lv_style_set_shadow_spread(&style_indicator, 5);
/*Add the style sheet to the slider's INDICATOR part*/
lv_obj_add_style(slider, &style_indicator, LV_PART_INDICATOR);
/*Add the same style to the KNOB part too and locally overwrite some properties*/
lv_obj_add_style(slider, &style_indicator, LV_PART_KNOB);
lv_obj_set_style_outline_color(slider, lv_color_hex(0x0096FF), LV_PART_KNOB);
lv_obj_set_style_outline_width(slider, 3, LV_PART_KNOB);
lv_obj_set_style_outline_pad(slider, -5, LV_PART_KNOB);
lv_obj_set_style_shadow_spread(slider, 2, LV_PART_KNOB);
```
</details>
<details>
<summary>MicroPython code |
<a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=c431c7b4dfd2cc0dd9c392b74365d5af6ea986f0" target="_blank">Online Simulator</a> :gb:
</summary>
```python
# Create a slider and add the style
slider = lv.slider(lv.screen_active())
slider.set_value(70, lv.ANIM.OFF)
slider.set_size(300, 20)
slider.center()
# Add local styles to MAIN part (background rectangle)
slider.set_style_bg_color(lv.color_hex(0x0F1215), lv.PART.MAIN)
slider.set_style_bg_opa(255, lv.PART.MAIN)
slider.set_style_border_color(lv.color_hex(0x333943), lv.PART.MAIN)
slider.set_style_border_width(5, lv.PART.MAIN)
slider.set_style_pad_all(5, lv.PART.MAIN)
# Create a reusable style sheet for the INDICATOR part
style_indicator = lv.style_t()
style_indicator.init()
style_indicator.set_bg_color(lv.color_hex(0x37B9F5))
style_indicator.set_bg_grad_color(lv.color_hex(0x1464F0))
style_indicator.set_bg_grad_dir(lv.GRAD_DIR.HOR)
style_indicator.set_shadow_color(lv.color_hex(0x37B9F5))
style_indicator.set_shadow_width(15)
style_indicator.set_shadow_spread(5)
# Add the style sheet to the slider's INDICATOR part
slider.add_style(style_indicator, lv.PART.INDICATOR)
slider.add_style(style_indicator, lv.PART.KNOB)
# Add the same style to the KNOB part too and locally overwrite some properties
slider.set_style_outline_color(lv.color_hex(0x0096FF), lv.PART.KNOB)
slider.set_style_outline_width(3, lv.PART.KNOB)
slider.set_style_outline_pad(-5, lv.PART.KNOB)
slider.set_style_shadow_spread(2, lv.PART.KNOB)
```
</details>
<br>
### English, Hebrew (mixed LRT-RTL) and Chinese texts 英语、希伯来语( 双向文本排版 )和中文
![English, Hebrew and Chinese texts with LVGL](https://raw.githubusercontent.com/kisvegabor/test/master/readme_example_5.png)
<details>
<summary>C code</summary>
```c
lv_obj_t * ltr_label = lv_label_create(lv_screen_active());
lv_label_set_text(ltr_label, "In modern terminology, a microcontroller is similar to a system on a chip (SoC).");
lv_obj_set_style_text_font(ltr_label, &lv_font_montserrat_16, 0);
lv_obj_set_width(ltr_label, 310);
lv_obj_align(ltr_label, LV_ALIGN_TOP_LEFT, 5, 5);
lv_obj_t * rtl_label = lv_label_create(lv_screen_active());
lv_label_set_text(rtl_label,"מעבד, או בשמו המלא יחידת עיבוד מרכזית (באנגלית: CPU - Central Processing Unit).");
lv_obj_set_style_base_dir(rtl_label, LV_BASE_DIR_RTL, 0);
lv_obj_set_style_text_font(rtl_label, &lv_font_dejavu_16_persian_hebrew, 0);
lv_obj_set_width(rtl_label, 310);
lv_obj_align(rtl_label, LV_ALIGN_LEFT_MID, 5, 0);
lv_obj_t * cz_label = lv_label_create(lv_screen_active());
lv_label_set_text(cz_label,
"嵌入式系统Embedded System\n是一种嵌入机械或电气系统内部、具有专一功能和实时计算性能的计算机系统。");
lv_obj_set_style_text_font(cz_label, &lv_font_source_han_sans_sc_16_cjk, 0);
lv_obj_set_width(cz_label, 310);
lv_obj_align(cz_label, LV_ALIGN_BOTTOM_LEFT, 5, -5);
```
</details>
<details>
<summary>MicroPython code | <a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=18bb38200a64e10ead1aa17a65c977fc18131842" target="_blank">Online Simulator</a> :gb:</summary>
```python
ltr_label = lv.label(lv.screen_active())
ltr_label.set_text("In modern terminology, a microcontroller is similar to a system on a chip (SoC).")
ltr_label.set_style_text_font(lv.font_montserrat_16, 0);
ltr_label.set_width(310)
ltr_label.align(lv.ALIGN.TOP_LEFT, 5, 5)
rtl_label = lv.label(lv.screen_active())
rtl_label.set_text("מעבד, או בשמו המלא יחידת עיבוד מרכזית (באנגלית: CPU - Central Processing Unit).")
rtl_label.set_style_base_dir(lv.BASE_DIR.RTL, 0)
rtl_label.set_style_text_font(lv.font_dejavu_16_persian_hebrew, 0)
rtl_label.set_width(310)
rtl_label.align(lv.ALIGN.LEFT_MID, 5, 0)
font_han_sans_16_cjk = lv.font_load("S:../../assets/font/lv_font_source_han_sans_sc_16_cjk.fnt")
cz_label = lv.label(lv.screen_active())
cz_label.set_style_text_font(font_han_sans_16_cjk, 0)
cz_label.set_text("嵌入式系统Embedded System\n是一种嵌入机械或电气系统内部、具有专一功能和实时计算性能的计算机系统。")
cz_label.set_width(310)
cz_label.align(lv.ALIGN.BOTTOM_LEFT, 5, -5)
```
</details>
## :handshake: 服务
LVGL
责任有限公司成立的目的是为了给用户使用LVGL图形库提供额外的技术支持我们致力于提供以下服务
- 图形设计
- UI设计
- 技术咨询以及技术支持
更多信息请参见 https://lvgl.io/services ,如果有任何问题请随时联系我们。
## :star: 如何向社区贡献
LVGL是一个开源项目非常欢迎您参与到社区贡献当中。您有很多种方式来为提高LVGL贡献您的一份力量包括但不限于
- 介绍你基于LVGL设计的作品或项目
- 写一些例程
- 修改以及完善文档
- 修复bug
请参见文档[如何向社区贡献](https://docs.lvgl.io/master/contributing/index.html)章节来获取更多信息。

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,50 @@
#!/usr/bin/env python3
import os
"""
Please add the translation language you want to add here, while also modifying the variable URL_BASE in _ext/link_roles.py
For example:
LANGUAGE = ':link_to_translation:`zh_CN:[中文]`\t' + \
':link_to_translation:`en:[English]`\t' + \
'\n\n'
URL_BASE = {
"zh_CN": "https://lvgl.100ask.net/",
"en": "https://docs.lvgl.io/"
}
"""
LANGUAGE = ':link_to_translation:`zh_CN:[中文]`\t' + \
'\n\n'
def find_files(dir_path, suffix):
files = []
for root, _, filenames in os.walk(dir_path):
for filename in filenames:
if filename.endswith(suffix):
files.append(os.path.join(root, filename))
return files
def exec(temp_directory):
"""
files = find_files(temp_directory, '.rst')
for rst_file in files:
with open(rst_file, 'r+', encoding='utf-8') as f:
content = f.read()
f.seek(0, 0)
f.write(LANGUAGE + content)
"""
rst_file = os.path.join(temp_directory, 'index.rst')
with open(rst_file, 'r+', encoding='utf-8') as f:
content = f.read()
f.seek(0, 0)
f.write(LANGUAGE + content)

View File

@@ -0,0 +1,118 @@
"""announce.py
Manage logging announcements to `stdout`
It is the designer's intention that:
1. The variable `__file__` be passed as the first argument in
`announce()` and `announce_start()`.
(Unfortunately, there is no way this author knows of yet to
have this module know what Python module is importing it. So
this is a hold-over requirement until that need is fulfilled.)
2. `announce_start()` and `announce_finish()` should be used
in pairs like this:
announce_start(__file__, 'something is running...')
# do something that takes a while here
announce_finish()
3. If this is used in a module that sometimes has a need to
not have anything output to STDOUT, when that is known,
call `announce_set_silent_mode()`. To turn "silent mode"
off, call `announce_set_silent_mode(False)`.
"""
import os
import datetime
__all__ = ('announce', 'announce_colored', 'announce_start', 'announce_finish', 'announce_set_silent_mode')
_announce_start_time: datetime.datetime
_announce_silent_mode: bool = False
_console_color_commands = {
'default' : '\x1b[0m',
'black' : '\x1b[30m',
'red' : '\x1b[31m',
'green' : '\x1b[32m',
'yellow' : '\x1b[33m',
'blue' : '\x1b[34m',
'majenta' : '\x1b[35m',
'cyan' : '\x1b[36m',
'white' : '\x1b[37m',
'bright_black' : '\x1b[90m',
'bright_red' : '\x1b[91m',
'bright_green' : '\x1b[92m',
'bright_yellow' : '\x1b[93m',
'bright_blue' : '\x1b[94m',
'bright_majenta': '\x1b[95m',
'bright_cyan' : '\x1b[96m',
'bright_white' : '\x1b[97m'
}
def _announce(file: str, args: tuple, start: bool, box: bool, box_char: str):
if _announce_silent_mode:
return
_args = []
for arg in args:
# Avoid the single quotes `repr()` puts around strings.
if type(arg) is str:
_args.append(arg)
else:
_args.append(repr(arg))
msg = f'{os.path.basename(file)}: ' + ' '.join(_args)
msg_len = len(msg)
# `start` takes precedence over `box` argument.
if start:
print(msg, end='', flush=True)
else:
if box:
line = box_char * msg_len
print(line)
print(msg)
print(line, flush=True)
else:
print(msg, flush=True)
def announce(file: str, *args, box: bool = False, box_char: str = '*'):
global _announce_start_time
_announce_start_time = None
_announce(file, args, False, box, box_char)
def announce_colored(file: str, clr: str, *args, box: bool = False, box_char: str = '*'):
global _announce_start_time
_announce_start_time = None
if len(args) > 0 and clr in _console_color_commands:
# Tuples are non-mutable so we have to build a new one -- can't insert new elements.
new_args_tuple = (_console_color_commands[clr],) + args + (_console_color_commands['default'],)
_announce(file, new_args_tuple, False, box, box_char)
else:
_announce(file, args, False, box, box_char)
def announce_start(file: str, *args, box: bool = False, box_char: str = '*'):
global _announce_start_time
_announce_start_time = datetime.datetime.now()
_announce(file, args, True, box, box_char)
def announce_finish():
# Just output line ending to terminate output for `announce_start()`.
global _announce_start_time
if _announce_start_time is not None:
if not _announce_silent_mode:
print(' Elapsed: ', datetime.datetime.now() - _announce_start_time, flush=True)
_announce_start_time = None
else:
if not _announce_silent_mode:
print(flush=True)
def announce_set_silent_mode(mode=True):
global _announce_silent_mode
_announce_silent_mode = mode

View File

@@ -0,0 +1,770 @@
"""api_doc_builder.py
Create and provide links to API pages in LVGL doc build.
Uses doxygen_xml.py module to:
- Prep and run Doxygen
- make Doxygen XML output available, and
- make Doxygen-documented symbols from the C code available.
"""
import os
import re
import doxygen_xml
from announce import *
old_html_files = {}
EMIT_WARNINGS = True
rst_section_line_char = '='
# Multi-line match ``API + newline + \*\*\* + whitespace``.
# NB: the ``\s*`` at the end forces the regex to match the whitespace
# at the end including all \r\n's. This will match UP TO:
# - the next non-blank character (could be many blank lines), or
# - to the end of the file, whichever comes first.
_re_api_section_sep = re.compile(r'(?mi)^API *\r?\n^\*\*\*\s*')
# Regex to identify '.. API equals: lv_obj_t, lv_array_t' directives.
_re_api_equals = re.compile(r'(?mi)^\s*\.\.\s+API\s+equals:\s*([\w,\s]+)\r\n\s*')
# Regex to identify '.. API startswith: lv_obj, lv_array' directives.
_re_api_startswith = re.compile(r'(?mi)^\s*\.\.\s+API\s+startswith:\s*([\w,\s]+)\r\n\s*')
# Regex to match comma and whitespace list-item separators on multiple lines.
_re_multi_line_comma_sep = re.compile(r'(?m)[,\s]+')
# Regex to identify editor-added hyperlinks: :ref:`lv_obj_h`
_re_editor_added_hyperlink = re.compile(r'^\s*:ref:`(\w+)`')
# Separator to mark place where this script added hyperlinks.
_auto_gen_sep = '.. Autogenerated'
# List of symbol dictionaries
_defines = {}
_enums = {}
_variables = {}
_namespaces = {}
_structs = {}
_unions = {}
_typedefs = {}
_functions = {}
_symbol_dict_list = [
_defines,
_enums,
_variables,
_namespaces,
_structs,
_unions,
_typedefs,
_functions
]
def old_clean_name(nme):
"""Strip beginning "_lv" and ending "_t"."""
# Handle error:
# AttributeError: 'NoneType' object has no attribute 'startswith'
if nme is None:
return nme
if nme.startswith('_lv_'):
nme = nme[4:]
elif nme.startswith('lv_'):
nme = nme[3:]
if nme.endswith('_t'):
nme = nme[:-2]
return nme
# Definitions:
# - "section" => The name "abc_def" has 2 sections.
# - N = number of sections in `item_name`.
# After removing leading '_lv_', 'lv_' and trailing '_t' from `obj_name`,
# do the remaining first N "sections" of `obj_name` match `item_name`
# (case sensitive)?
def old_is_name_match(item_name, obj_name):
# Handle error:
# AttributeError: 'NoneType' object has no attribute 'split'
if obj_name is None:
return False
sect_count = item_name.count('_') + 1
obj_name = obj_name.split('_')
# Reject (False) if `obj_name` doesn't have as many sections as `item_name`.
if len(obj_name) < sect_count:
return False
obj_name = '_'.join(obj_name[:sect_count])
return item_name == obj_name
def old_get_includes(name1, name2, obj, includes):
name2 = old_clean_name(name2)
if not old_is_name_match(name1, name2):
return
if obj.parent is not None and hasattr(obj.parent, 'header_file'):
header_file = obj.parent.header_file
elif hasattr(obj, 'header_file'):
header_file = obj.header_file
else:
return
if not header_file:
return
if header_file not in old_html_files:
return
includes.add((header_file, old_html_files[header_file]))
def _conditionally_add_hyperlink(obj, genned_link_set: set, exclude_set: set):
"""
Add hyperlink names to `link_set` if:
- not in `exclude_set`, and
- not already in `link_set`.
:param obj: "thing" from dictionary with matching symbol.
These are objects instantiated from classes
in `doxygen_xml` module such as
STRUCT, FUNCTION, DEFINE, ENUMVALUE, etc.
:param genned_link_set: Set in which to accumulate link names
:param exclude_set: Set with link names not to add to `link_set`
:return:
"""
if obj.file_name is not None:
link_name = os.path.basename(obj.file_name).replace('.', '_')
if link_name not in genned_link_set:
if link_name not in exclude_set:
genned_link_set.add(link_name)
def _add_startswith_matches(strings: [str], genned_link_set, editor_link_set):
"""
Add set of hyperlinks to `genned_link_set` that are not already in
`editor_link_set`, for C symbols that start with strings in `strings`.
:param strings: List of strings to match against
:param genned_link_set: Generated link set
:param editor_link_set: Hyperlinks added by editor
:return: n/a
"""
for partial_symbol in strings:
for symbol_dict in _symbol_dict_list:
for key in symbol_dict:
if key is None:
# Dictionary `enums` has a key `None` which contains
# all enum values from all unnamed enums, and each
# enum value has a `file_name` field.
enum_values_list = symbol_dict[None].members
for enum_val in enum_values_list:
if enum_val.name.startswith(partial_symbol):
_conditionally_add_hyperlink(enum_val, genned_link_set, editor_link_set)
else:
if key.startswith(partial_symbol):
obj = symbol_dict[key]
_conditionally_add_hyperlink(obj, genned_link_set, editor_link_set)
def _add_exact_matches(symbols: [str], genned_link_set, editor_link_set):
"""
Add set of hyperlinks to `genned_link_set` that are not already in
`editor_link_set`, for exact C symbol matches.
:param symbols: List of C symbols to match against
:param genned_link_set: Generated link set
:param editor_link_set: Hyperlinks added by editor
:return: n/a
"""
for symbol in symbols:
for symbol_dict in _symbol_dict_list:
if symbol in symbol_dict:
obj = symbol_dict[symbol]
_conditionally_add_hyperlink(obj, genned_link_set, editor_link_set)
def _hyperlink_sort_value(init_value: str):
if init_value.endswith('_h'):
result = init_value[:-2]
else:
result = init_value
return result
def _process_end_of_eligible_doc(b: str, rst_file: str) -> (str, str, int):
"""
Edit end section after API section heading.
3. Initialize:
- links_added_count = 0
- editor_link_set = set()
- genned_link_set = set()
- C = '' # string for generated hyperlinks
4. Remove `_auto_gen_sep` and everything after it:
- new_B = B.split(_auto_gen_sep, 1)[0]
5. With `new_B, add any editor-added hyperlinks to set:
`editor_link_set`.
6. If `_re_api_equals` match present:
- build list of symbols
- compute list of hyperlinks from symbols
- add hyperlinks to `genned_link_set` if not in `editor_link_set`.
7. If `_re_api_startswith` match present:
- build list of symbols
- compute list of hyperlinks from symbols
- add hyperlinks to `genned_link_set` if not in `editor_link_set`.
8. Lacking either of the above custom directives, use the lower-case
stem of the filename and prefix it with "lv_" and try an
"API startswith" search with it.
9. If len(genned_link_set) > 0:
- `C` = _auto_gen_sep + '\n\n' + `genned_link_set`
(with a blank line between each).
10. Return tuple: (new_B, C, links_added_count).
:param b: End of document after API section heading + whitespace.
:return: Tuple: (new_B, C, links_added_count)
"""
# 3. Initialize:
editor_link_set = set()
genned_link_set = set()
c = ''
api_directives_found_count = 0
# 4. Remove `_auto_gen_sep` and everything after it:
new_b = b.split(_auto_gen_sep, 1)[0]
# 5. With `new_B, add any editor-added hyperlinks to set:
# `editor_link_set`.
for line in new_b.splitlines():
match = _re_editor_added_hyperlink.match(line)
if match is not None:
editor_link_set.add(match[1])
# 6. If `_re_api_equals` present:
# - build list of symbols
# - compute list of hyperlinks from symbols
# - add hyperlinks to `genned_link_set` if not in `editor_link_set`.
match = _re_api_equals.search(new_b)
if match is not None:
api_directives_found_count += 1
comma_sep_list = match[1].strip()
symbols = _re_multi_line_comma_sep.split(comma_sep_list)
_add_exact_matches(symbols, genned_link_set, editor_link_set)
# 7. If `_re_api_startswith` present:
# - build list of symbols
# - compute list of hyperlinks from symbols
# - add hyperlinks to `genned_link_set` if not in `editor_link_set`.
match = _re_api_startswith.search(new_b)
if match is not None:
api_directives_found_count += 1
comma_sep_list = match[1].strip()
symbols = _re_multi_line_comma_sep.split(comma_sep_list)
_add_startswith_matches(symbols, genned_link_set, editor_link_set)
# 8. Lacking either of the above custom directives, use the lower-case
# stem of the filename and prefix it with "lv_" and try an
# "API startswith" with it.
if api_directives_found_count == 0:
base = os.path.basename(rst_file)
stem = os.path.splitext(base)[0]
_add_startswith_matches(['lv_' + stem], genned_link_set, editor_link_set)
# 9. If len(genned_link_set) > 0:
# - `C` = _auto_gen_sep + '\n\n' + `genned_link_set`
# (with a blank line between each).
links_added_count = len(genned_link_set)
if links_added_count > 0:
c = _auto_gen_sep + '\n\n'
for link_name in sorted(genned_link_set, key=_hyperlink_sort_value):
c += ':ref:`' + link_name + '`\n\n'
# 10. Return tuple: (new_B, C, links_added_count).
return new_b, c, links_added_count
def _process_one_file(rst_file: str):
"""
Add applicable API hyperlinks to one file.
Eligible
An `.rst` file is eligible if it contains an API section at
its end. This can happen also in `index.rst` files when
they head a single subject for which an API section is
appropriate there and not in the sub-docs. So `index.rst`
files are included, whereas they were not included previously.
Algorithm:
----------
A. Doc editors may have already added a set of hyperlinks of their
own. This routine takes note of and does not duplicate what is
already there.
B. Doc editors may also have added specifications for this routine
that look like this:
.. API equals: lv_obj_t, lv_arc_t, lv_barcode_t,
lv_win_t, lv_list_t,
lv_button_t
.. API startswith: lv_obj, lv_arc, lv_barcode,
lv_win, lv_list,
lv_button
as directives for this routine to build a set of applicable
hyperlinks.
C. Lacking any of the above custom directives, use the lower-case
stem of the filename and prefix it with "lv_" and try an
"API startswith" search with it.
Any hyperlinks added by this routine are prefixed with the
reStructuredText comment defined by the `_auto_gen_sep`
variable, normally:
.. Autogenerated
If `rst_file` is eligible, edit after API section heading such that:
- any editor-added hyperlinks are retained at the top of the list;
- `_auto_gen_sep` (looked for in case a source file ends up having it;
anything after it is replaced);
- applicable hyperlinks added such that they do not repeat those
added by editors of `.rst` file.
Steps to Implement:
-------------------
0. links_added_count = 0
1. Determine if eligible.
- If not, skip to step 12.
- If so, continue.
2. Split doc into 2 parts:
A. beginning through API section heading and subsequent
whitespace including subsequent blank lines;
B. anything after that which may include editor-added hyperlinks.
3-10. new_B, C, links_added_count = _process_end_of_eligible_doc()
11. Write `A` + `new_B` + `C` back to `rst_file`.
12. Return links_added_count.
:param rst_file: Full path to `.rst` file in question.
It may or may not be eligible.
:return: Number of links added.
"""
links_added_count = 0
with open(rst_file, 'rb') as f:
try:
rst_contents = f.read().decode('utf-8')
except UnicodeDecodeError:
announce(__file__, f'Error: UnicodeDecodeError in [{rst_file}].')
raise
eligible_match = _re_api_section_sep.search(rst_contents)
if eligible_match is not None:
# Eligible (API section found).
i = eligible_match.end()
# Split just after the API section heading + whitespace.
a = rst_contents[:i]
b = rst_contents[i:]
new_b, c, links_added_count = _process_end_of_eligible_doc(b, rst_file)
if links_added_count > 0:
rst_contents = a + new_b + c
with open(rst_file, 'wb') as f:
f.write(rst_contents.encode('utf-8'))
return links_added_count
def _build_one_local_dictionary(local_dict, remote_dict):
"""
Remove '_' prefix in symbols beginning with '_lv' to make
symbols like `lv_obj_t` actually connect with the struct
in `lv_obj_private.h`, and not the typedef in `lv_types.h`.
:param local_dict: Local (adjusted) symbol dictionary
:param remote_dict: Dictionary from `doxygen_xml` module
:return: n/a
"""
for symbol in remote_dict:
# Note: symbol `None` is actually a valid symbol in the
# `enums` dictionary, containing all enum-value symbols
# for enums without names.
if symbol is None or not symbol.startswith('_lv'):
loc_symbol = symbol
else:
# Remove '_' prefix.
loc_symbol = symbol[1:]
local_dict[loc_symbol] = remote_dict[symbol]
def _build_local_symbol_dictionaries():
"""
Build "work-around" dictionaries so that a symbol like `lv_obj_t`
actually connects with the struct in `lv_obj_private.h`, and not
the typedef in `lv_types.h`.
:return: n/a
"""
_build_one_local_dictionary(_defines, doxygen_xml.defines)
_build_one_local_dictionary(_enums, doxygen_xml.enums)
_build_one_local_dictionary(_variables, doxygen_xml.variables)
_build_one_local_dictionary(_namespaces, doxygen_xml.namespaces)
_build_one_local_dictionary(_structs, doxygen_xml.structures)
_build_one_local_dictionary(_unions, doxygen_xml.unions)
_build_one_local_dictionary(_typedefs, doxygen_xml.typedefs)
_build_one_local_dictionary(_functions, doxygen_xml.functions)
def _add_hyperlinks_to_eligible_files(intermediate_dir: str,
new_algorithm: bool,
*doc_rel_paths: [str]):
"""
Add applicable hyperlinks to eligible docs found joining
`intermediate_dir` with each relative path in `doc_rel_paths`.
See API-link algorithm documented under `_process_one_file()`.
:param intermediate_dir: Top directory where hyperlinks are to be added.
:param doc_rel_paths: Tuple of relative paths from `intermediate_dir` to
walk to find docs eligible for API hyperlinks.
:return:
"""
if new_algorithm:
# Populate local symbol dictionary set with
# symbols WITHOUT any '_' prefixes.
_build_local_symbol_dictionaries()
# Build `.rst` file list.
file_list = []
for rel_path in doc_rel_paths:
top_dir = os.path.join(intermediate_dir, rel_path)
for dir_bep, sub_dirs, files in os.walk(top_dir, topdown=False):
for file in files:
if file.lower().endswith('.rst'):
file_list.append(os.path.join(dir_bep, file))
total_eligible_doc_count = 0
total_links_added_count = 0
# For each `.rst` file, add appropriate API hyperlinks.
for rst_file in file_list:
links_added_count = _process_one_file(rst_file)
if links_added_count > 0:
total_links_added_count += links_added_count
total_eligible_doc_count += 1
# announce(__file__, f'Eligible doc: [{rst_file}].')
announce(__file__, f'Docs eligible for API hyperlinks: {total_eligible_doc_count:>4}')
announce(__file__, f'API hyperlinks added : {total_links_added_count:>4}')
else:
for folder in doc_rel_paths:
# Fetch a list of '.rst' files excluding 'index.rst'.
rst_files = list(
(os.path.splitext(item)[0], os.path.join(folder, item))
for item in os.listdir(folder)
if item.endswith('.rst') and 'index.rst' not in item
)
# For each .RST file in that directory...
for stem, path in rst_files:
# Start with an empty set.
html_includes = set()
# Build `html_includes` set as a list of tuples containing
# (name, html_file). Example: "draw.rst" has `stem` == 'draw',
# and generates a list of tuples from .H files where matching
# C-code-element names were found. Example:
# {('lv_draw_line', 'draw\\lv_draw_line.html'),
# ('lv_draw_sdl', 'draw\\sdl\\lv_draw_sdl.html'),
# ('lv_draw_sw_blend_to_i1', 'draw\\sw\\blend\\lv_draw_sw_blend_to_i1.html'),
# etc.}
for symbol_dict in (
doxygen_xml.defines,
doxygen_xml.enums,
doxygen_xml.variables,
doxygen_xml.namespaces,
doxygen_xml.structures,
doxygen_xml.unions,
doxygen_xml.typedefs,
doxygen_xml.functions
):
for key, obj in symbol_dict.items():
old_get_includes(stem, key, obj, html_includes)
if html_includes:
# Convert `html_includes` set to a list of strings containing the
# Sphinx hyperlink syntax "link references". Example from above:
# [':ref:`lv_draw_line_h`\n',
# ':ref:`lv_draw_sdl_h`\n',
# ':ref:`lv_draw_sw_blend_to_i1_h`\n',
# etc.]
html_includes = list(
':ref:`{0}_h`\n'.format(inc)
for inc, _ in html_includes
)
# Convert that list to a single string of Sphinx hyperlink
# references with blank lines between them.
# :ref:`lv_draw_line_h`
#
# :ref:`lv_draw_sdl_h`
#
# :ref:`lv_draw_sw_blend_to_i1_h`
#
# etc.
output = ('\n'.join(html_includes)) + '\n'
# Append that string to the source .RST file being processed.
with open(path, 'rb') as f:
try:
data = f.read().decode('utf-8')
except UnicodeDecodeError:
print(path)
raise
data = data.split(_auto_gen_sep, 1)[0]
data += f'{_auto_gen_sep}\n\n'
data += output
with open(path, 'wb') as f:
f.write(data.encode('utf-8'))
def _create_rst_files_for_dir(src_root_dir_len: int,
src_dir_bep: str,
elig_h_files: [str],
elig_sub_dirs: [str],
out_root_dir: str):
"""
- Create `index.rst` file and add its top section.
- For each file in `elig_h_files`:
- Create one `.rst` file.
- Add reference to it in `index.rst`.
- For each subdir in `elig_sub_dirs`:
- add reference "sub_dir_name/index" in `index.rst`.
:param src_root_dir_len: Length of source-root path string, used with `out_root_dir` to build paths
:param src_dir_bep: Directory currently *being processed*
:param elig_h_files: Eligible `.h` files directly contained in `src_dir_bep`
:param elig_sub_dirs: List of sub-dirs that contained eligible `.h` files
:param out_root_dir: Root of output directory, used with to build paths.
:return: n/a
"""
indent = ' '
sub_path = src_dir_bep[src_root_dir_len:]
out_dir = str(os.path.join(out_root_dir, sub_path))
# Ensure dir exists. Multiple dirs MAY have to be created
# since `.rst` files are created in bottom-up sequence.
if not os.path.isdir(out_dir):
os.makedirs(out_dir)
# For top-level directory only... (the last index.rst created,
# since they are created in bottom-up sequence)
if len(sub_path) == 0 and out_dir.endswith(os.sep):
# Trim trailing slash from `out_dir`.
out_dir = out_dir[:-1]
# index.rst
with open(os.path.join(out_dir, 'index.rst'), 'w') as f:
subdir_stem = os.path.split(out_dir)[-1]
section_line = (rst_section_line_char * len(subdir_stem)) + '\n'
f.write(section_line)
f.write(subdir_stem + '\n')
f.write(section_line)
f.write('\n')
f.write('.. toctree::\n :maxdepth: 1\n\n')
# One entry per `.rst` file
for h_file in elig_h_files:
filename = os.path.basename(h_file)
stem = os.path.splitext(filename)[0]
f.write(indent + stem + '\n')
# One entry per eligible subdirectory.
for sub_dir in elig_sub_dirs:
stem = os.path.split(sub_dir)[-1]
f.write(indent + stem + '/index\n')
# One .rst file per h_file
for h_file in elig_h_files:
filename = os.path.basename(h_file)
stem = os.path.splitext(filename)[0]
rst_file = os.path.join(out_dir, stem + '.rst')
html_file = os.path.join(sub_path, stem + '.html')
old_html_files[stem] = html_file
with open(rst_file, 'w') as f:
# Sphinx link target.
f.write(f'.. _{stem}_h:\n\n')
# Doc title.
section_line = (rst_section_line_char * len(filename)) + '\n'
f.write(section_line)
f.write(filename + '\n')
f.write(section_line)
f.write('\n')
# Content for `breathe`.
f.write(f'.. doxygenfile:: {filename}\n')
f.write(' :project: lvgl\n\n')
def _recursively_create_api_rst_files(depth: int,
src_root_len: int,
src_dir_bep: str,
out_root_dir: str) -> int:
"""
Create `.rst` files for the eligible `.h` found in `src_dir_bep` and
recursively for subdirectories below it. ("bep" = being processed.)
Eligible
An `.h` file is eligible if Doxygen generated documentation for it.
The `EXCLUDE_PATTERNS` Doxygen configuration value can cause
Doxygen to skip certain files and directories, in which case,
the `.h` files skipped ARE NOT eligible.
Whether a subdirectory is eligible to be included in an `index.rst`
file depends upon whether any eligible `.h` files were recursively
found within it. And that isn't known until this function finishes
(recursively) processing a directory and returns the number of
eligible `.h` files found. Thus, the steps taken within are:
- Discover all eligible `.h` files directly contained in `src_dir_bep`.
- Recursively do the same for each subdirectory, adding the returned
count of eligible `.h` files to the sum (`elig_h_file_count`).
- If `elig_h_file_count > 0`:
- call _create_rst_files_for_dir() to generate appropriate
`.rst` files for this directory.
- Return `elig_h_file_count`.
Once we have accumulated this information, then we can generate
all the `.rst` files for the current directory without any further
directory-tree walking.
:param depth: Only used for testing/debugging
:param src_root_len: Length of source-root path
:param src_dir_bep: Source directory *being processed*
:param out_root_dir: Output root directory (used to build output paths)
:return: Number of `.h` files encountered (so caller knows
whether that directory recursively held any
eligible `.h` files, to know whether to include
"subdir/index" in caller's local `index.rst` file).
"""
elig_h_files = []
sub_dirs = []
elig_sub_dirs = []
elig_h_file_count = 0
# For each "thing" found in `src_dir_bep`, build lists:
# `elig_sub_dirs` and `elig_h_files`.
# By design change, we are including files with 'private'
# in their names. Reason: advanced users who need to use
# the structs defined within will need the documentation
# in those API pages!
for dir_item in os.listdir(src_dir_bep):
path_bep = os.path.join(src_dir_bep, dir_item)
if os.path.isdir(path_bep):
sub_dirs.append(path_bep) # Add to sub-dir list.
else:
if dir_item.lower().endswith('.h'):
eligible = (dir_item in doxygen_xml.files)
if eligible:
elig_h_files.append(path_bep) # Add to .H file list.
elig_h_file_count += 1
# For each subdir...
for sub_dir in sub_dirs:
subdir_eligible_h_file_count = \
_recursively_create_api_rst_files(depth + 1,
src_root_len,
sub_dir,
out_root_dir)
if subdir_eligible_h_file_count > 0:
elig_sub_dirs.append(sub_dir)
elig_h_file_count += subdir_eligible_h_file_count
if elig_h_file_count > 0:
# Create index.rst plus .RST files for any direct .H files in dir.
_create_rst_files_for_dir(src_root_len,
src_dir_bep,
elig_h_files,
elig_sub_dirs,
out_root_dir)
return elig_h_file_count
def create_api_rst_files(src_root_dir: str, out_root_dir: str):
"""
Create `.rst` files for API pages based on the `.h` files found
in a tree-walk of `a_src_root` and the current contents of the
`doxygen_xml.files` dictionary (used to filter out `.h` files that
Doxygen generated no documentation for). Output the `.rst` files
into `out_root_dir` mirroring the `a_src_root` directory structure.
:param src_root_dir: root source directory to walk
:param out_root_dir: output directory
:return: n/a
"""
src_root_len = len(src_root_dir) + 1
_recursively_create_api_rst_files(0, src_root_len, src_root_dir, out_root_dir)
def build_api_docs(lvgl_src_dir, intermediate_dir, doxyfile_src_file, *doc_rel_paths):
"""
- Prep and run Doxygen, outputting XML.
- Load that XML in a form that can quickly tie C symbols to the
source files they came from.
- Generate API page `.rst` files for source files Doxygen generated
documentation for.
- Add hyperlinks to these API pages for `.rst` files in `*doc_rel_paths`
that are eligible.
:param lvgl_src_dir: Path to LVGL src directory
:param intermediate_dir: Path to intermediate dir being built
:param doxyfile_src_file: Full path to src doxygen configuration file
:param doc_rel_paths: List of relative paths from `intermediate_dir` to
walk to find docs eligible for API hyperlinks.
"""
# ---------------------------------------------------------------------
# - Generate Doxyfile replacing tokens,
# - run Doxygen generating XML, and
# - load the generated XML from Doxygen output.
# ---------------------------------------------------------------------
doxygen_xml.EMIT_WARNINGS = EMIT_WARNINGS
xml_parser = doxygen_xml.DoxygenXml(lvgl_src_dir,
intermediate_dir,
doxyfile_src_file,
silent_mode=False
)
# ---------------------------------------------------------------------
# Generate .RST files for API pages.
# ---------------------------------------------------------------------
announce(__file__, "Generating API documentation .RST files...")
api_out_root_dir = os.path.join(intermediate_dir, 'API')
create_api_rst_files(lvgl_src_dir, api_out_root_dir)
# ---------------------------------------------------------------------
# For each directory entry in `doc_rel_paths` array...
# - add API hyperlinks to .RST files in the directories in passed array.
# ---------------------------------------------------------------------
announce(__file__, "Adding API-page hyperlinks to source docs...")
_add_hyperlinks_to_eligible_files(intermediate_dir,
True,
*doc_rel_paths)

View File

@@ -0,0 +1,755 @@
#!/usr/bin/env python3
""" build.py -- Generate LVGL documentation using Doxygen and Sphinx + Breathe.
Synopsis
--------
- $ python build.py html [ skip_api ] [ fresh_env ]
- $ python build.py latex [ skip_api ] [ fresh_env ]
- $ python build.py intermediate [ skip_api ]
- $ python build.py clean
- $ python build.py clean_intermediate
- $ python build.py clean_html
- $ python build.py clean_latex
Build Arguments and Clean Arguments can be used one at a time
or be freely mixed and combined.
Description
-----------
Copy source files to an intermediate directory and modify them there before
doc generation occurs. If a full rebuild is being done (e.g. after a `clean`)
run Doxygen LVGL's source files to generate intermediate API information
in XML format. Generate API documents for Breathe's consumption. Add API
links to end of some documents. Generate example documents. From there,
Sphinx with Breathe extension uses the resulting set of intermediate files
to generate the desired output.
It is only during this first build that the `skip_api` option has meaning.
After the first build, no further actions is taken regarding API pages since
they are not regenerated after the first build.
The intermediate directory has a fixed location (overridable by
`LVGL_DOC_BUILD_INTERMEDIATE_DIR` environment variable) and by default this
script attempts to rebuild only those documents whose path, name or
modification date has changed since the last build.
The output directory also has a fixed location (overridable by
`LVGL_DOC_BUILD_OUTPUT_DIR` environment variable).
Caution:
The document build meant for end-user consumption should ONLY be done after a
`clean` unless you know that no API documentation and no code examples have changed.
A `sphinx-build` will do a full doc rebuild any time:
- the intermediate directory doesn't exist or is empty (since the new files in
the intermediate directory will have modification times after the generated
HTML or Latex files, even if nothing changed),
- the targeted output directory doesn't exist or is empty, or
- Sphinx determines that a full rebuild is necessary. This happens when:
- intermediate directory (Sphinx's source-file path) has changed,
- any options on the `sphinx-build` command line have changed,
- `conf.py` modification date has changed, or
- `fresh_env` argument is included (runs `sphinx-build` with -E option).
Typical run time:
Full build: 22.5 min
skip_api : 1.9 min (applies to first build only)
Options
-------
help
Print usage note and exit with status 0.
html [ skip_api ] [ fresh_env ]
Build HTML output.
`skip_api` only has effect on first build after a `clean` or `clean_intermediate`.
latex [ skip_api ] [ fresh_env ]
Build Latex/PDF output (on hold pending removal of non-ASCII characters from input files).
`skip_api` only has effect on first build after a `clean` or `clean_intermediate`.
intermediate [ skip_api ]
Generate intermediate directory contents (ready to build output formats).
If they already exist, they are removed and re-generated.
Note: "intermediate" can be abbreviated down to "int".
skip_api (with `html` and/or `latex` and/or `intermediate` options)
Skip API pages and links when intermediate directory contents are being generated
(saving about 91% of build time). Note: they are not thereafter regenerated unless
requested by `intermediate` argument or the intermediate directory does not
exist. This is intended to be used only during doc development to speed up
turn-around time between doc modifications and seeing final results.
fresh_env (with `html` and/or `latex` options)
Run `sphinx-build` with -E command-line argument, which makes it regenerate its
"environment" (memory of what was built previously, forcing a full rebuild).
clean
Remove all generated files.
clean_intermediate
Remove intermediate directory.
Note: "clean_intermediate" can be abbreviated down to "clean_int".
clean_html
Remove HTML output directory.
clean_latex
Remove Latex output directory.
Unrecognized arguments print error message, usage note, and exit with status 1.
Python Package Requirements
---------------------------
The list of Python package requirements are in `requirements.txt`.
Install them by:
$ pip install -r requirements.txt
History
-------
The first version of this file (Apr 2021) discovered the name of
the current branch (e.g. 'master', 'release/v8.4', etc.) to support
different versions of the documentation by establishing the base URL
(used in `conf.py` and in [Edit on GitHub] links), and then ran:
- Doxygen (to generate LVGL API XML), then
- Sphinx
to generate the LVGL document tree. Internally, Sphinx uses `breathe`
(a Sphinx extension) to provide a bridge between Doxygen XML output and
Sphinx documentation. It also supported a command-line option `clean`
to remove generated files before starting (eliminates orphan files,
for docs that have moved or changed).
Since then its duties have grown to include:
- Using environment variables to convey branch names to several more
places where they are used in the docs-generating process (instead
of re-writing writing `conf.py` and a `header.rst` each time docs
were generated). These are documented where they generated below.
- Supporting additional command-line options.
- Generating a `./docs/lv_conf.h` for Doxygen to use (config_builder.py).
- Supporting multiple execution platforms (which then required tokenizing
Doxygen's INPUT path in `Doxyfile` and re-writing portions that used
`sed` to generate input or modify files).
- Adding translation and API links (requiring generating docs in an
intermediate directory so that the links could be programmatically
added to each document before Sphinx was run). Note: translation link
are now done manually since they are only on the main landing page.
- Generating EXAMPLES page + sub-examples where applicable to individual
documents, e.g. to widget-, style-, layout-pages, etc.
- Building PDF via latex (when working).
- Shifting doc-generation paradigm to behave more like `make`.
"""
# ****************************************************************************
# IMPORTANT: If you are getting a PDF-lexer error for an example, check
# for extra lines at the end of the file. Only a single empty line
# is allowed!!! Ask me how long it took me to figure this out.
# -- @kdschlosser
# ****************************************************************************
# Python Library
import sys
import os
import subprocess
import shutil
import dirsync
from datetime import datetime
# LVGL Custom
import example_list
import api_doc_builder
import config_builder
from src.lvgl_version import lvgl_version
from announce import *
# Not Currently Used
# (Code is kept in case we want to re-implement it later.)
# import add_translation
# -------------------------------------------------------------------------
# Configuration
# -------------------------------------------------------------------------
# These are relative paths from the ./docs/ directory.
cfg_project_dir = '..'
cfg_lvgl_src_dir = 'src'
cfg_doc_src_dir = 'src'
cfg_examples_dir = 'examples'
cfg_default_intermediate_dir = 'intermediate'
cfg_default_output_dir = 'build'
cfg_static_dir = '_static'
cfg_downloads_dir = 'downloads'
cfg_lv_conf_filename = 'lv_conf.h'
cfg_lv_version_filename = 'lv_version.h'
cfg_doxyfile_filename = 'Doxyfile'
cfg_top_index_filename = 'index.rst'
cfg_default_branch = 'master'
# Filename generated in `latex_output_dir` and copied to `pdf_output_dir`.
cfg_pdf_filename = 'LVGL.pdf'
def print_usage_note():
"""Print usage note."""
print('Usage:')
print(' $ python build.py [optional_arg ...]')
print()
print(' where `optional_arg` can be any of these:')
print(' html [ skip_api ] [ fresh_env ]')
print(' latex [ skip_api ] [ fresh_env ]')
print(' intermediate [ skip_api ]')
print(' clean')
print(' clean_intermediate')
print(' clean_html')
print(' clean_latex')
print(' help')
def remove_dir(tgt_dir):
"""Remove directory `tgt_dir`."""
if os.path.isdir(tgt_dir):
announce(__file__, f'Removing {tgt_dir}...')
shutil.rmtree(tgt_dir)
else:
announce(__file__, f'{tgt_dir} already removed...')
def cmd(cmd_str, start_dir=None, exit_on_error=True):
"""Run external command and abort build on error."""
saved_dir = None
if start_dir is not None:
saved_dir = os.getcwd()
os.chdir(start_dir)
announce(__file__, f'Running [{cmd_str}] in [{os.getcwd()}]...')
return_code = os.system(cmd_str)
if saved_dir is not None:
os.chdir(saved_dir)
if return_code != 0 and exit_on_error:
announce(__file__, "Exiting build due to previous error.")
sys.exit(1)
def intermediate_dir_contents_exists(dir):
"""Provide answer to question: Can we have reasonable confidence that
the contents of `intermediate_directory` already exists?
"""
result = False
c1 = os.path.isdir(dir)
if c1:
temp_path = os.path.join(dir, 'CHANGELOG.rst')
c2 = os.path.exists(temp_path)
temp_path = os.path.join(dir, '_ext')
c3 = os.path.isdir(temp_path)
temp_path = os.path.join(dir, '_static')
c4 = os.path.isdir(temp_path)
temp_path = os.path.join(dir, 'details')
c5 = os.path.isdir(temp_path)
temp_path = os.path.join(dir, 'intro')
c6 = os.path.isdir(temp_path)
temp_path = os.path.join(dir, 'contributing')
c7 = os.path.isdir(temp_path)
temp_path = os.path.join(dir, cfg_examples_dir)
c8 = os.path.isdir(temp_path)
result = c2 and c3 and c4 and c5 and c6 and c7 and c8
return result
def run(args):
"""Perform doc-build function(s) requested."""
# ---------------------------------------------------------------------
# Set default settings.
# ---------------------------------------------------------------------
build_html = False
build_latex = False
build_intermediate = False
skip_api = False
fresh_sphinx_env = False
clean_all = False
clean_intermediate = False
clean_html = False
clean_latex = False
def print_setting(setting_name, val):
"""Print one setting; used for debugging."""
announce(__file__, f'{setting_name:18} = [{val}]')
def print_settings(and_exit):
"""Print all settings and optionally exit; used for debugging."""
print_setting("build_html", build_html)
print_setting("build_latex", build_latex)
print_setting("build_intermediate", build_intermediate)
print_setting("skip_api", skip_api)
print_setting("fresh_sphinx_env", fresh_sphinx_env)
print_setting("clean_all", clean_all)
print_setting("clean_intermediate", clean_intermediate)
print_setting("clean_html", clean_html)
print_setting("clean_latex", clean_latex)
if and_exit:
exit(0)
# ---------------------------------------------------------------------
# Process args.
# ---------------------------------------------------------------------
# No args means print usage note and exit with status 0.
if len(args) == 0:
print_usage_note()
exit(0)
# Some args are present. Interpret them in loop here.
# Unrecognized arg means print error, print usage note, exit with status 1.
for arg in args:
# We use chained `if-elif-else` instead of `match` for those on Linux
# systems that will not have the required version 3.10 of Python yet.
if arg == 'help':
print_usage_note()
exit(0)
elif arg == "html":
build_html = True
elif arg == "latex":
build_latex = True
elif "intermediate".startswith(arg) and len(arg) >= 3:
# Accept abbreviations.
build_intermediate = True
elif arg == 'skip_api':
skip_api = True
elif arg == 'fresh_env':
fresh_sphinx_env = True
elif arg == "clean":
clean_all = True
clean_intermediate = True
clean_html = True
clean_latex = True
elif arg == "clean_html":
clean_html = True
elif arg == "clean_latex":
clean_latex = True
elif "clean_intermediate".startswith(arg) and len(arg) >= 9:
# Accept abbreviations.
# Needs to be after others so "cl" will not
clean_intermediate = True
else:
print(f'Argument [{arg}] not recognized.')
print()
print_usage_note()
exit(2) # 2 = customary Unix command-line syntax error.
# '-E' option forces Sphinx to rebuild its environment so all docs are
# fully regenerated, even if not changed.
# Note: Sphinx runs in ./docs/, but uses `intermediate_dir` for input.
if fresh_sphinx_env:
announce(__file__, "Force-regenerating all files...")
env_opt = '-E'
else:
env_opt = ''
# ---------------------------------------------------------------------
# Start.
# ---------------------------------------------------------------------
t0 = datetime.now()
# ---------------------------------------------------------------------
# Set up paths.
#
# Variable Suffixes:
# _filename = filename without path
# _path = path leading to a file or directory (absolute or relative)
# _file = path leading to a file (absolute or relative)
# _dir = path leading to a directory (absolute or relative)
# ---------------------------------------------------------------------
base_dir = os.path.abspath(os.path.dirname(__file__))
project_dir = os.path.abspath(os.path.join(base_dir, cfg_project_dir))
examples_dir = os.path.join(project_dir, cfg_examples_dir)
lvgl_src_dir = os.path.join(project_dir, cfg_lvgl_src_dir)
# Establish intermediate directory. The presence of environment variable
# `LVGL_DOC_BUILD_INTERMEDIATE_DIR` overrides default in `cfg_default_intermediate_dir`.
if 'LVGL_DOC_BUILD_INTERMEDIATE_DIR' in os.environ:
intermediate_dir = os.environ['LVGL_DOC_BUILD_INTERMEDIATE_DIR']
else:
intermediate_dir = os.path.join(base_dir, cfg_default_intermediate_dir)
lv_conf_file = os.path.join(intermediate_dir, cfg_lv_conf_filename)
version_dst_file = os.path.join(intermediate_dir, cfg_lv_version_filename)
top_index_file = os.path.join(intermediate_dir, cfg_top_index_filename)
doxyfile_src_file = os.path.join(base_dir, cfg_doxyfile_filename)
doxyfile_dst_file = os.path.join(intermediate_dir, cfg_doxyfile_filename)
pdf_intermediate_dst_dir = os.path.join(intermediate_dir, cfg_static_dir, cfg_downloads_dir)
pdf_intermediate_dst_file = os.path.join(pdf_intermediate_dst_dir, cfg_pdf_filename)
sphinx_path_sep = '/'
pdf_relative_file = cfg_static_dir + sphinx_path_sep + cfg_downloads_dir + sphinx_path_sep + cfg_pdf_filename
pdf_link_ref_str = f'PDF Version: :download:`{cfg_pdf_filename} <{pdf_relative_file}>`'
# Establish build directory. The presence of environment variable
# `LVGL_DOC_BUILD_OUTPUT_DIR` overrides default in `cfg_default_output_dir`.
if 'LVGL_DOC_BUILD_OUTPUT_DIR' in os.environ:
output_dir = os.environ['LVGL_DOC_BUILD_OUTPUT_DIR']
else:
output_dir = os.path.join(base_dir, cfg_default_output_dir)
html_output_dir = os.path.join(output_dir, 'html')
latex_output_dir = os.path.join(output_dir, 'latex')
pdf_output_dir = os.path.join(output_dir, 'pdf')
pdf_src_file = os.path.join(latex_output_dir, cfg_pdf_filename)
pdf_dst_file = os.path.join(pdf_output_dir, cfg_pdf_filename)
version_src_file = os.path.join(project_dir, cfg_lv_version_filename)
# Special stuff for right-aligning PDF download link.
# Note: this needs to be embedded in a <div> tag because the
# Sphinx `:download:` role causes the link to appear in a <p> tag
# and in HTML5, <p> tags cannot be nested!
cfg_right_just_para_text = """.. raw:: html
<div style="text-align: right;">"""
cfg_end_right_just_para_text = """.. raw:: html
</div>"""
# Blank lines are required due to the directives.
cfg_pdf_link_ref_block_str = \
cfg_right_just_para_text + os.linesep \
+ os.linesep \
+ pdf_link_ref_str + os.linesep + \
os.linesep \
+ cfg_end_right_just_para_text + os.linesep \
+ os.linesep
# ---------------------------------------------------------------------
# Change to script directory for consistent run-time environment.
# ---------------------------------------------------------------------
os.chdir(base_dir)
announce(__file__, f'Intermediate dir: [{intermediate_dir}]')
announce(__file__, f'Output dir : [{output_dir}]')
announce(__file__, f'Running from : [{base_dir}]')
# ---------------------------------------------------------------------
# Clean? If so, clean (like `make clean`), but do not exit.
# ---------------------------------------------------------------------
some_cleaning_to_be_done = clean_intermediate or clean_html or clean_latex \
or clean_all or (os.path.isdir(intermediate_dir) and build_intermediate)
if some_cleaning_to_be_done:
announce(__file__, "Cleaning...", box=True)
if clean_intermediate:
remove_dir(intermediate_dir)
if clean_html:
remove_dir(html_output_dir)
if clean_latex:
remove_dir(latex_output_dir)
if clean_all:
remove_dir(output_dir)
if os.path.isdir(intermediate_dir) and build_intermediate:
remove_dir(intermediate_dir)
# ---------------------------------------------------------------------
# Populate LVGL_URLPATH and LVGL_GITCOMMIT environment variables:
# - LVGL_URLPATH <= 'master' or '8.4' '9.2' etc.
# - LVGL_GITCOMMIT <= same (see 03-Oct-2024 note below).
#
# These supply input later in the doc-generation process as follows:
#
# LVGL_URLPATH is used by:
# - `conf.py` to build `html_baseurl` for Sphinx for
# - generated index
# - generated search window
# - establishing canonical page for search engines
# - `link_roles.py` to generate translation links
# - `doxygen_xml.py` to generate links to API pages
#
# LVGL_GITCOMMIT is used by:
# - `conf.py` => html_context['github_version'] for
# Sphinx Read-the-Docs theme to add to [Edit on GitHub] links
# - `conf.py` => repo_commit_hash for generated EXAMPLES pages for:
# - [View on GitHub] buttons (view C code examples)
# - [View on GitHub] buttons (view Python code examples)
# ---------------------------------------------------------------------
# 03-Oct-2024: Gabor requested LVGL_GITCOMMIT be changed to a branch
# name since that will always be current, and it will fix a large
# number of broken links on the docs website, since commits that
# generated docs can sometimes go away. This gets used in:
# - [Edit on GitHub] links in doc pages (via Sphinx theme), and
# - [View on GitHub] links in example pages (via `example_list.py`
# and `lv_example.py`).
# Original code:
# status, br = subprocess.getstatusoutput("git branch --show-current")
# _, gitcommit = subprocess.getstatusoutput("git rev-parse HEAD")
# br = re.sub(r'\* ', '', br)
# 're' was previously used to remove leading '* ' from current branch
# string when we were parsing output from bare `git branch` output.
# This is no longer needed with `--show-current` option now used.
# ---------------------------------------------------------------------
status, branch = subprocess.getstatusoutput("git branch --show-current")
# If above failed (i.e. `branch` not valid), default to 'master'.
if status != 0:
branch = cfg_default_branch
elif branch == cfg_default_branch:
# Expected in most cases. Nothing to change.
pass
else:
# `branch` is valid. Capture release version if in a 'release/' branch.
if branch.startswith('release/'):
branch = branch[8:]
else:
# Default to 'master'.
branch = cfg_default_branch
os.environ['LVGL_URLPATH'] = branch
os.environ['LVGL_GITCOMMIT'] = branch
# ---------------------------------------------------------------------
# Prep `intermediate_dir` to become the `sphinx-build` source dir.
# ---------------------------------------------------------------------
# dirsync `exclude_list` = list of regex patterns to exclude.
intermediate_re = r'^' + cfg_default_intermediate_dir + r'.*'
output_re = r'^' + cfg_default_output_dir + r'.*'
exclude_list = [r'lv_conf\.h', r'^__pycache__.*', intermediate_re, output_re]
if intermediate_dir_contents_exists(intermediate_dir):
# We are just doing an update of the intermediate_dir contents.
announce(__file__, "Updating intermediate directory...", box=True)
exclude_list.append(r'examples.*')
options = {
'verbose': True, # Report files copied.
'create': True, # Create directories if they don't exist.
'twoway': False, # False means data flow only src => tgt.
'purge': False, # False means DO NOT remove orphan files/dirs in tgt dir (preserving examples/ dir).
'exclude': exclude_list
}
# action == 'sync' means copy files even when they do not already exist in tgt dir.
# action == 'update' means DO NOT copy files when they do not already exist in tgt dir.
dirsync.sync(cfg_doc_src_dir, intermediate_dir, 'sync', **options)
dirsync.sync(examples_dir, os.path.join(intermediate_dir, cfg_examples_dir), 'sync', **options)
elif build_intermediate or build_html or build_latex:
# We are having to create the intermediate_dir contents by copying.
announce(__file__, "Building intermediate directory...", box=True)
t1 = datetime.now()
copy_method = 1
# Both of these methods work.
if copy_method == 0:
# --------- Method 0:
ignore_func = shutil.ignore_patterns('tmp*', 'output*')
announce(__file__, 'Copying docs...')
shutil.copytree(cfg_doc_src_dir, intermediate_dir, ignore=ignore_func, dirs_exist_ok=True)
announce(__file__, 'Copying examples...')
shutil.copytree(examples_dir, os.path.join(intermediate_dir, cfg_examples_dir), dirs_exist_ok=True)
else:
# --------- Method 1:
options = {
'create': True, # Create directories if they don't exist.
'exclude': exclude_list
}
# action == 'sync' means copy files even when they do not already exist in tgt dir.
# action == 'update' means DO NOT copy files when they do not already exist in tgt dir.
announce(__file__, 'Copying docs...')
dirsync.sync(cfg_doc_src_dir, intermediate_dir, 'sync', **options)
announce(__file__, 'Copying examples...')
dirsync.sync(examples_dir, os.path.join(intermediate_dir, cfg_examples_dir), 'sync', **options)
# -----------------------------------------------------------------
# Build <intermediate_dir>/lv_conf.h from lv_conf_template.h.
# -----------------------------------------------------------------
config_builder.run(lv_conf_file)
# -----------------------------------------------------------------
# Copy `lv_version.h` into intermediate directory.
# -----------------------------------------------------------------
shutil.copyfile(version_src_file, version_dst_file)
# -----------------------------------------------------------------
# Generate examples pages. Include sub-pages pages that get included
# in individual documents where applicable.
# -----------------------------------------------------------------
announce(__file__, "Generating examples...")
example_list.exec(intermediate_dir)
# -----------------------------------------------------------------
# Add translation links.
# This is being skipped in favor of a manually-placed
# translation link at the top of `./docs/index.rst`.
# -----------------------------------------------------------------
# Original code:
# if True:
# announce(__file__, "Skipping adding translation links.")
# else:
# announce(__file__, "Adding translation links...")
# add_translation.exec(intermediate_dir)
if skip_api:
announce(__file__, "Skipping API generation as requested.")
else:
# -------------------------------------------------------------
# Generate API pages and links thereto.
# -------------------------------------------------------------
announce(__file__, "API page and link processing...")
api_doc_builder.EMIT_WARNINGS = False
# api_doc_builder.run() => doxy_xml_parser.DoxygenXml() now:
# - preps and runs Doxygen generating XML,
# - loads generated XML.
# Then api_doc_builder.run():
# - creates .RST files for API pages, and
# - adds API hyperlinks to .RST files in the directories in passed array.
api_doc_builder.build_api_docs(lvgl_src_dir,
intermediate_dir,
doxyfile_src_file,
'details',
'intro'
)
t2 = datetime.now()
announce(__file__, 'Example/API run time: ' + str(t2 - t1))
# ---------------------------------------------------------------------
# Build PDF
# ---------------------------------------------------------------------
if not build_latex:
announce(__file__, "Skipping Latex build.")
else:
t1 = datetime.now()
announce(__file__, "Building Latex output...", box=True)
# If PDF link is present in top index.rst, remove it so PDF
# does not have a link to itself.
with open(top_index_file, 'rb') as f:
index_data = f.read().decode('utf-8')
if pdf_link_ref_str in index_data:
index_data = index_data.replace(pdf_link_ref_str, '')
with open(top_index_file, 'wb') as f:
f.write(index_data.encode('utf-8'))
src = intermediate_dir
dst = output_dir
cpu = os.cpu_count()
# As of 22-Feb-2025, sadly the -D version=xxx is not working as documented.
# So the version strings applicable to Latex/PDF/man pages/texinfo
# formats are assembled by `conf.py`.
cmd_line = f'sphinx-build -M latex "{src}" "{dst}" -j {cpu}'
cmd(cmd_line)
# Generate PDF.
announce(__file__, "Building PDF...", box=True)
cmd_line = 'latexmk -pdf "LVGL.tex"'
cmd(cmd_line, latex_output_dir, False)
# Move resulting PDF to its output directory.
if not os.path.exists(pdf_output_dir):
os.makedirs(pdf_output_dir)
shutil.move(pdf_src_file, pdf_dst_file)
t2 = datetime.now()
announce(__file__, 'PDF : ' + pdf_dst_file)
announce(__file__, 'Latex gen time: ' + str(t2 - t1))
# ---------------------------------------------------------------------
# Build HTML
# ---------------------------------------------------------------------
if not build_html:
announce(__file__, "Skipping HTML build.")
else:
t1 = datetime.now()
announce(__file__, "Building HTML output...", box=True)
# If PDF is present in build directory, copy it to
# intermediate directory for use by HTML build.
# (Sphinx copies it to its HTML output, so it ends
# up on the webserver where it can be downloaded).
if os.path.isfile(pdf_dst_file):
# Create _static/download/ directory if needed.
if not os.path.exists(pdf_intermediate_dst_dir):
os.makedirs(pdf_intermediate_dst_dir)
shutil.copyfile(pdf_dst_file, pdf_intermediate_dst_file)
# If PDF is present, ensure there is a link to it in the top
# index.rst so HTML build will have it.
# Support both Windows and Linux platforms with `os.linesep`.
if os.path.isfile(pdf_intermediate_dst_file):
with open(top_index_file, 'rb') as f:
index_data = f.read().decode('utf-8')
if pdf_link_ref_str not in index_data:
index_data = cfg_pdf_link_ref_block_str + index_data
with open(top_index_file, 'wb') as f:
f.write(index_data.encode('utf-8'))
# Note: While it can be done (e.g. if one needs to set a stop point
# in Sphinx code for development purposes), it is NOT a good idea to
# run Sphinx from script as
# from sphinx.cmd.build import main as sphinx_build
# sphinx_args = [...]
# sphinx_build(sphinx_args)
# because it takes ~10X longer to run than `sphinx_build` executable,
# literally > 3 hours.
ver = lvgl_version(version_src_file)
src = intermediate_dir
dst = output_dir
cpu = os.cpu_count()
debugging_breathe = 0
if debugging_breathe:
from sphinx.cmd.build import main as sphinx_build
# Don't allow parallel processing while debugging (the '-j' arg is removed).
sphinx_args = ['-M', 'html', f'{src}', f'{dst}', '-D', f'version={ver}']
if len(env_opt) > 0:
sphinx_args.append(f'{env_opt}')
sphinx_build(sphinx_args)
else:
# The -D option correctly replaces (overrides) configuration attribute
# values in the `conf.py` module. Since `conf.py` now correctly
# computes its own `version` value, we don't have to override it here
# with a -D options. If it should need to be used in the future,
# the value after the '=' MUST NOT have quotation marks around it
# or it won't work. Correct usage: f'-D version={ver}' .
cmd_line = f'sphinx-build -M html "{src}" "{dst}" -j {cpu} {env_opt}'
cmd(cmd_line)
t2 = datetime.now()
announce(__file__, 'HTML gen time : ' + str(t2 - t1))
# ---------------------------------------------------------------------
# Indicate results.
# ---------------------------------------------------------------------
t_end = datetime.now()
announce(__file__, 'Total run time: ' + str(t_end - t0))
announce(__file__, 'Done.')
if __name__ == '__main__':
"""Make module importable as well as run-able."""
run(sys.argv[1:])

View File

@@ -0,0 +1,74 @@
"""
Create lv_conf.h in same directory as this file
from ../lv_conf_template.h that has:
1. all its #define LV_USE... 0-or-1 options set to 1
(except for LV_USER_PROFILER),
2. all its #define LV_FONT... 0-or-1 options set to 1,
3. its #if 0 directive set to #if 1.
"""
import os
import sys
import re
base_path = os.path.dirname(__file__)
dst_config = os.path.join(base_path, 'lv_conf.h')
src_config = os.path.abspath(os.path.join(
base_path,
'..',
'lv_conf_template.h'
))
disabled_option_re = re.compile(r'^\s*#define\s+\w+\s+(\b0\b)')
def run(c_path=None):
global dst_config
os.chdir(base_path)
if c_path is not None:
dst_config = c_path
with open(src_config, 'r') as f:
data = f.read()
lines = data.split('\n')
for i, line in enumerate(lines):
if 'LV_USE_PROFILER' in line:
continue
# These 2 fonts have been deprecated in favor of
# LV_FONT_SOURCE_HAN_SANS_SC_14_CJK and
# LV_FONT_SOURCE_HAN_SANS_SC_16_CJK.
if 'LV_FONT_SIMSUN_14_CJK' in line:
continue
if 'LV_FONT_SIMSUN_16_CJK' in line:
continue
if 'LV_USE' in line or ('LV_FONT' in line and '#define' in line):
match = disabled_option_re.search(line)
if match:
# Replace '0' with '1' without altering any other part of line.
# Set `j` to index where '0' was found.
j = match.regs[1][0]
# Surgically insert '1' in place of '0'.
lines[i] = line[:j] + '1' + line[j + 1:]
elif line.startswith('#if 0'):
line = line.replace('#if 0', '#if 1')
lines[i] = line
data = '\n'.join(lines)
with open(dst_config, 'w') as f:
f.write(data)
def cleanup():
if os.path.exists(dst_config):
os.remove(dst_config)
if __name__ == '__main__':
"""Make module importable as well as run-able."""
run()

View File

@@ -0,0 +1,429 @@
"""doxygen_config.py
Python Interface to Doxygen Config Files (Doxyfiles)
Author : "Victor Wheeler"
Copyright: "Copyright (C) 2025 WGA Crystal Research, Inc."
License : "MIT"
Version : "1.0"
This work was inspired by the `doxygen-python-interface` project at
https://github.com/TraceSoftwareInternational/doxygen-python-interface.
On 27-Feb-2025 I was engaged in a production project wherein I wanted
to find a Python module that I could re-use to reliably work with
Doxygen configuration files (Doxyfiles). The best one I found was
`doxygen-python-interface`. Unfortunately, the ``configParser`` from
that project could not be used because it both had important bugs and
design flaws in it (conflicts with legal Doxygen config syntax), and
it appears to have been abandoned after 26-Apr-2018, preventing these
things from being remedied.
So a brand-new module has been created herewith based on sound O-O design
principles and a design that actually works in alignment with Doxygen
configuration syntax.
Usage:
import doxygen_config
...
# 1. Load configuration from Doxyfile.
cfg = doxygen_config.DoxygenConfig()
cfg.load(doxyfile_src_file)
# 2. Get a list of Doxygen option names.
opt_list = cfg.options()
ok_to_proceed = cfg.is_valid_option('PREDEFINED') \
and cfg.is_valid_option('INPUT')
# 3. Update it.
if ok_to_proceed:
temp = cfg.value('PREDEFINED')
temp = temp.replace('<<CONFIG_PATH>>', config_file)
cfg.set('PREDEFINED', temp)
temp = cfg.value('INPUT')
temp = temp.replace('<<SRC>>', f'"{pjt_src_dir}"')
cfg.set('INPUT', temp)
# 4. Save it.
# The original comments and order of config options are preserved.
# The ``bare`` argument discards comments from the output.
cfg.save(cfg_dict, doxyfile_dst_file, bare=True)
Design Differences from `doxygen-python-interface`:
- The DoxygenConfig class represents the actual Doxygen configuration,
in alignment with O-O theory --- it is not just a place to store a
set of functions that never needed to be a class.
- If the user does a default ``save()`` (not requesting a "bare"
version of the Doxygen configuration), the saved Doxyfile
should be a binary match to the original Doxyfile loaded.
Exceptions:
1. Any trailing whitespace in original Doxyfile after the ``=``
on empty options is not preserved.
2. Multi-line lists that had unaligned backslashes after them like this:
EXCLUDE_PATTERNS = */libs/barcode/code* \
*/libs/freetype/ft* \
*/libs/gif/gif* \
*/libs/lodepng/lode* \
*/libs/qrcode/qr* \
*/libs/thorvg/* \
*/libs/tiny_ttf/stb* \
*/libs/tjpgd/tjp* \
*/others/vg_lite_tvg/vg*
will be saved like this:
EXCLUDE_PATTERNS = */libs/barcode/code* \
*/libs/freetype/ft* \
*/libs/gif/gif* \
*/libs/lodepng/lode* \
*/libs/qrcode/qr* \
*/libs/thorvg/* \
*/libs/tiny_ttf/stb* \
*/libs/tjpgd/tjp* \
*/others/vg_lite_tvg/vg*
``doxygen-python-interface`` did not save the comments so an
"edit in place" of a Doxyfile could be catastrophic if the
comments were needed in the source Doxyfile as they often are
in production scenarios.
- The ``save()`` method has an optional ``bare`` argument (default False)
that can be used to save a "bare" version of the Doxyfile options,
discarding the comments from the currently-loaded Doxyfile.
- Input values are preserved exactly as they were found. The
`doxygen-python-interface`'s ``configParser`` class removed
quotation marks from incoming values and added quotation marks
to values containing spaces before storing them again. While
this "sounds nice", it was incompatible with Doxygen for every
type of item that could have a "list" as a value, such as the
PREDEFINED and ABBREVIATE_BRIEF options.
Examples:
PREDEFINED = USE_LIST USE_TABLE USE_CHART
PREDEFINED = DOXYGEN CONFIG_PATH="/path with spaces/to/config.h"
PREDEFINED = DOXYGEN \
CONFIG_PATH="/path with spaces/to/config.h"
These are all valid values for the PREDEFINED option and
MUST NOT have quotes around any of them! Can you imagine the havoc
that would result if a Python module meant to handle Doxygen Doxyfiles
altered Doxygen configuration items like this?
PREDEFINED = "USE_LIST USE_TABLE USE_CHART"
Thus, it is up to the user to know when values he is changing
have space(s) AND ALSO need quotes and take appropriate measures
by adding quotes when needed and not otherwise.
- The storage of the list of Doxygen options is encapsulated
in the instance of the DoxygenConfig class instead of being
returned as a dictionary from the ``load...()`` function.
Its values are readable and writeable via methods. The
end user is not able to add options that were not part
of the original input Doxyfile, nor remove options that were
part of the original input Doxyfile. This gives some level of
control on retaining valid Doxygen options.
It is an error to attempt to set a value with an option name
that does not exist in the configuration. A NameError exception
is raised if it is attempted. Attempting to read the value of
an option name that does not exist returns the value ``None``.
While Doxygen options change from time to time, it is up to the
end user to use ``doxygen -u Doxyfile`` to keep his input
Doxyfile(s) up to date.
Storage:
The actual configuration values are represented in an internal
dictionary not intended to be accessed directly by the typical end
user. The keys are the Doxygen option names and the values are:
- str : single values with possibly embedded spaces
- list: multi-line values with possibly embedded spaces
Quotation marks are neither removed nor added, so it is up to the
user to set values compatible with Doxygen configuration syntax.
This also makes it okay for multi-line values to have more than one
value per line: if it is okay by Doxygen, then it is okay by
the DoxygenConfig class.
If the user sets an option value passing a list, those values
will be represented as a multi-line value in the saved Doxyfile.
The Philosophy of Removing Quotation Marks Is Not Workable for Doxygen:
When one asks, "Is it appropriate to remove the quotation marks?"
What if a value looked like this (2 quoted items in one line),
removing quotation marks would be an error:
"abc def" "ghi jkl"
The ABBREVIATE_BRIEF list could indeed appear like this.
If it were argued that all multi-value items should be formatted as
multi-line lists, then quotation marks theory works, as the
ABBREVIATE_BRIEF option does not require quotation marks around
every value.
However, since Doxygen does not require this, there is still a
strong argument for not tampering with quotation marks at all
when importing values. The strongest reasons are:
- Doxygen can and does accept values like this where the value of
an option can be a list. Doxygen sees this as 2 separate values:
"abc def" "ghi jkl"
- If the end user is going to set values with spaces in them,
it could be made the user's responsibility to know when
there are spaces and thus include quotes when needed.
In the end, the "do not tamper with quotation marks" argument wins
for sake of reliability. So the policy is: quotation marks are
neither removed nor added. It is up to the user to know when they
are needed and add them himself.
"""
import logging
import os
import re
__author__ = "Victor Wheeler"
__copyright__ = "Copyright (C) 2025 WGA Crystal Research, Inc."
__license__ = "MIT"
__version__ = "1.0"
class ParseException(Exception):
"""Exception thrown upon unexpected parsing errors."""
pass
class DoxygenConfig:
"""Doxygen Configurations (from/to Doxyfiles)"""
def __init__(self):
"""Prepare instantiated DoxygenConfig for use."""
# Regexes used during Doxyfile parsing
self._re_single_line_option = re.compile(r'^\s*(\w+)\s*=\s*([^\\]*)\s*$')
self._re_top_of_multiline_option = re.compile(r'^\s*(\w+)\s*=\s*(|.*\S)\s*\\$')
# Doxygen cfg items by option name
self._cfg_items_dict = {}
# Comments by name of option below it.
# Comments at end of file have key 'self._end_key'.
self._cfg_comments_dict = {}
# Key used for comments found after last option in Doxyfile
self._end_key = 'END'
# Configuration to match Doxygen -g output (template Doxyfile)
self._char_count_before_equals = 23
def load(self, doxyfile: str):
"""Load options and comments from `doxyfile`
:param doxyfile: Path to doxyfile
:raise FileNotFoundError: When doxyfile not found
:raise ParseException: When there is a parsing error
"""
if not os.path.exists(doxyfile):
logging.error(f'Doxyfile not found {doxyfile}.')
raise FileNotFoundError(doxyfile)
self._cfg_items_dict.clear()
self._cfg_comments_dict.clear()
# Default encoding: UTF-8.
with open(doxyfile, 'r') as file:
in_multiline_opt = False
multiline_opt_name_bep = None # "bep" = "being processed"
accumulated_other_lines = []
for line in file.readlines():
line = line.strip()
if in_multiline_opt:
# There are 2 ways this list can end:
# 1. the normal way when last item has no trailing `\`, or
# 2. the last item has a trailing `\` and there is a blank-
# or comment-line after it, which should NOT be added
# to the list, but instead signal end-of-list.
if not line.endswith('\\'):
in_multiline_opt = False
val = line.rstrip('\\').strip()
if self._bool_comment_or_blank_line(val):
accumulated_other_lines.append(line)
in_multiline_opt = False
else:
self._cfg_items_dict[multiline_opt_name_bep].append(val)
elif self._bool_comment_or_blank_line(line):
accumulated_other_lines.append(line)
elif self._bool_top_of_multiline_option(line):
multiline_opt_name_bep, val = self._parse_multiline_option(line)
self._cfg_items_dict[multiline_opt_name_bep] = [val]
self._cfg_comments_dict[multiline_opt_name_bep] = accumulated_other_lines
accumulated_other_lines = []
in_multiline_opt = True
elif self._bool_single_line_option(line):
option_name, val = self._parse_single_line_option(line)
self._cfg_items_dict[option_name] = val
self._cfg_comments_dict[option_name] = accumulated_other_lines
accumulated_other_lines = []
# Any comments or blank lines found after last Doxygen option
# are represented in _cfg_comments_dict with key `self._end_key`.
if accumulated_other_lines:
self._cfg_comments_dict[self._end_key] = accumulated_other_lines
accumulated_other_lines.clear()
def save(self, doxyfile: str, bare=False):
"""Save configuration to `doxyfile`.
:param doxyfile: Output path where Doxygen configuration will be
written. Overwrites file if it exists.
:param bare: Do not preserve comments from loaded file.
"""
lines = []
for option_name, val in self._cfg_items_dict.items():
if not bare:
lines.extend(self._cfg_comments_dict[option_name])
if type(val) is list:
# We will be aligning the backslashes after the
# items in the list, so we need to know the longest.
# First value in list:
multi_line_indent = ' ' * (self._char_count_before_equals + 2)
longest_len = len(max(val, key=len))
val_w_len = val[0].ljust(longest_len)
lines.append(f'{option_name:<23}= {val_w_len} \\')
# Next n-2 values in list:
if len(val) > 2:
for temp in val[1:-1]:
val_w_len = temp.ljust(longest_len)
lines.append(f'{multi_line_indent}{val_w_len} \\')
# Last value in list:
lines.append(f'{multi_line_indent}{val[-1]}')
elif type(val) is str:
val_w_len = option_name.ljust(self._char_count_before_equals)
if len(val) == 0:
lines.append(f'{val_w_len}=')
else:
lines.append(f'{val_w_len}= {val}')
if self._end_key in self._cfg_comments_dict:
if not bare:
lines.extend(self._cfg_comments_dict[self._end_key])
# Ensure there is exactly 1 newline at end of file.
lines.append('')
with open(doxyfile, 'w') as file:
file.write('\n'.join(lines))
logging.debug(f'Saved configuration to [{doxyfile}].')
def option_names(self):
"""List of contained Doxygen option names"""
return self._cfg_items_dict.keys()
def is_valid_option(self, option_name: str) -> bool:
"""Is `option_name` a valid option name?"""
return option_name in self._cfg_items_dict
def set(self, option_name: str, val: str or list):
"""Set value of specified option
:param option_name: Name of Doxygen option whose value to fetch
:param val: Value to set
- str = single-line value;
- list = multi-line value.
:raises NameError: When ``name`` is not found.
"""
if option_name in self._cfg_items_dict:
self._cfg_items_dict[option_name] = val
if type(val) is list:
logging.debug(f'Item [{option_name}] set to list.')
else:
logging.debug(f'Item [{option_name}] set to [{val}].')
else:
logging.error(f'Doxyfile option {option_name} not found.')
raise NameError(f'Doxygen option {option_name} not found.')
def value(self, option_name: str) -> str or list:
"""Value of specified option
:param option_name: Name of Doxygen option whose value to fetch
:returns string: single-line value
:returns list: multi-line value
:returns None: When ``option_name`` is not found.
"""
if option_name in self._cfg_items_dict:
result = self._cfg_items_dict[option_name]
logging.debug(f'Item [{option_name}] fetched.')
else:
result = None
logging.debug(f'Item [{option_name}] not found.')
return result
def _parse_multiline_option(self, line) -> (str, str):
"""Extract option name and first line of value of multi-line option.
:param line: line to parse
:return: name and first line of multi-line option
:raise ParseException: When process fail to extract data
"""
matches = self._re_top_of_multiline_option.search(line)
if matches is None or len(matches.groups()) != 2:
logging.error(f'Error extracting first value in multi-line option from [{line}].')
raise ParseException(f'Error extracting first value in multi-line option from [{line}].')
return matches.group(1), matches.group(2)
def _parse_single_line_option(self, line) -> (str, str):
"""Extract option name and value of single line option.
:param line: line to parse
:return: option name and value
:raise ParseException: When process fail to extract data
"""
matches = self._re_single_line_option.search(line)
if matches is None or len(matches.groups()) != 2:
logging.error(f'Error extracting option name and value from [{line}].')
raise ParseException(f'Error extracting option name and value from [{line}].')
return matches.group(1), matches.group(2)
def _bool_single_line_option(self, line: str) -> bool:
return self._re_single_line_option.match(line) is not None
def _bool_comment_or_blank_line(self, line: str) -> bool: # NoQA
return line.startswith("#") or (len(line) == 0)
def _bool_top_of_multiline_option(self, line) -> bool:
return self._re_top_of_multiline_option.match(line) is not None

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,163 @@
#!/usr/bin/env python3
import os
def process_index_rst(path):
# print(path)
with open(path, 'r') as fp:
data = fp.read()
data = data.split('\n')
last_line = ""
title_tmp = ""
for line in data:
line = line.strip()
if not line:
continue
if line.startswith('---'):
title_tmp = last_line.strip()
elif line.startswith('.. lv_example::'):
name = line.replace('.. lv_example::', '').strip()
yield name, title_tmp
last_line = line
h1 = {
"get_started": "Get Started",
"styles": "Styles",
"anim": "Animations",
"event": "Events",
"layouts": "Layouts",
"scroll": "Scrolling",
"widgets": "Widgets"
}
widgets = {
"obj": "Base Widget",
"animimg": "Animation Image",
"arc": "Arc",
"bar": "Bar",
"button": "Button",
"buttonmatrix": "Button Matrix",
"calendar": "Calendar",
"canvas": "Canvas",
"chart": "Chart",
"checkbox": "Checkbox",
"dropdown": "Dropdown",
"image": "Image",
"imagebutton": "Image Button",
"keyboard": "Keyboard",
"label": "Label",
"led": "LED",
"line": "Line",
"list": "List",
"lottie": "Lottie",
"menu": "Menu",
"msgbox": "Message Box",
"roller": "Roller",
"scale":"Scale",
"slider": "Slider",
"span": "Span",
"spinbox": "Spinbox",
"spinner": "Spinner",
"switch": "Switch",
"table": "Table",
"tabview": "Tabview",
"textarea": "Textarea",
"tileview": "Tileview",
"win": "Window",
}
HEADING = '='
CHAPTER = '#'
SECTION = '*'
SUBSECTION = '='
SUBSUBSECTION = '-'
def write_header(h_num, text, f):
text = text.strip()
if h_num == 0:
f.write(header_defs[h_num] * len(text))
f.write('\n')
f.write(text + '\n')
f.write(header_defs[h_num] * len(text))
f.write('\n\n')
# This is the order that Sphinx uses for the headings/titles. 0 is the
# largest and 4 is the smallest. If this order is not kept in the reST files
# Sphinx will complain
header_defs = {
0: HEADING,
1: CHAPTER,
2: SECTION,
3: SUBSECTION,
4: SUBSUBSECTION
}
layouts = {
"flex": "Flex",
"grid": "Grid",
}
def print_item(path, lvl, d, fout):
for k in d:
v = d[k]
if k.startswith(path + "/lv_example_"):
write_header(lvl, v, fout)
fout.write(f".. lv_example:: {k}\n")
fout.write("\n")
def exec(temp_directory):
output_path = os.path.join(temp_directory, 'examples.rst')
paths = ["../examples/", "../demos/"]
fout = open(output_path, "w")
filelist = []
for path in paths:
for root, dirs, files in os.walk(path):
for f in files:
# append the file name to the list
filelist.append(os.path.join(root, f))
filelist = [fi for fi in filelist if fi.endswith("index.rst")]
d_all = {}
# print all the file names
for fn in filelist:
d_all.update(dict(tuple(item for item in process_index_rst(fn))))
# fout.write("```eval_rst\n")
# fout.write(":github_url: |github_link_base|/examples.md\n")
# fout.write("```\n")
# fout.write("\n")
fout.write('.. _examples:\n\n')
write_header(0, 'Examples', fout)
for h in h1:
write_header(1, h1[h], fout)
if h == "widgets":
for w in widgets:
write_header(2, widgets[w], fout)
print_item(h + "/" + w, 3, d_all, fout)
elif h == "layouts":
for l in layouts:
write_header(2, layouts[l], fout)
print_item(h + "/" + l, 3, d_all, fout)
else:
print_item(h, 2, d_all, fout)
fout.write("")

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,77 @@
@ECHO OFF
rem -----------------------------------------------------------------------
rem This file is intended only to be used after the contents of the
rem intermediate directory have been created. Do so by:
rem
rem $ python build.py intermediate [skip_api]
rem
rem This is a modified version of the standard Sphinx `make.bat` file.
rem Changes:
rem - uses these environment variables in the same way `build.py` does
rem when they are set:
rem - LVGL_DOC_BUILD_INTERMEDIATE_DIR
rem - LVGL_DOC_BUILD_OUTPUT_DIR
rem - Cleans up locally-created environment variables at end, so they
rem do not clutter environment variables.
rem -----------------------------------------------------------------------
pushd %~dp0
REM Command file for Sphinx documentation
setlocal ENABLEDELAYEDEXPANSION
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
if "%LVGL_DOC_BUILD_INTERMEDIATE_DIR%" == "" (
set SOURCEDIR=intermediate
) else (
set SOURCEDIR=%LVGL_DOC_BUILD_INTERMEDIATE_DIR%
)
if "%LVGL_DOC_BUILD_OUTPUT_DIR%" == "" (
set BUILDDIR=build
) else (
set BUILDDIR=%LVGL_DOC_BUILD_OUTPUT_DIR%
)
if "%SPHINXOPTS%" == "" (
rem python ./src/lvgl_version.py >_version_temp.txt
rem set /p VER=<_version_temp.txt
rem del _version_temp.txt
for /F %%v in ('python lvgl_version.py') do set VER=%%v
echo VERSION [!VER!]
set SPHINXOPTS=-D version="!VER!" -j 4
set VER=
)
echo SOURCEDIR [%SOURCEDIR%]
echo BUILDDIR [%BUILDDIR%]
echo SPHINXOPTS [%SPHINXOPTS%]
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
if "%1" == "" goto help
echo %SPHINXBUILD% -M %1 "%SOURCEDIR%" "%BUILDDIR%" %SPHINXOPTS% %2 %3 %4 %5 %6 %7 %8 %9
%SPHINXBUILD% -M %1 "%SOURCEDIR%" "%BUILDDIR%" %SPHINXOPTS% %2 %3 %4 %5 %6 %7 %8 %9
goto end
:help
%SPHINXBUILD% -M help "%SOURCEDIR%" "%BUILDDIR%" %SPHINXOPTS% %2 %3 %4 %5 %6 %7 %8 %9
:end
rem Clean up.
popd
set BUILDDIR=
set SOURCEDIR=

View File

@@ -0,0 +1,18 @@
Sphinx
breathe
imagesize
importlib-metadata
sphinx-rtd-theme
sphinx-sitemap
sphinxcontrib-applehelp
sphinxcontrib-devhelp
sphinxcontrib-htmlhelp
sphinxcontrib-jsmath
sphinxcontrib-qthelp
sphinxcontrib-serializinghtml
sphinxcontrib-mermaid==0.9.2
sphinx-design
sphinx-rtd-dark-mode
typing-extensions
sphinx-reredirects
dirsync

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
# based on http://protips.readthedocs.io/link-roles.html
#from __future__ import print_function, unicode_literals
import os
import re
import subprocess
from collections import namedtuple
from docutils import nodes
from sphinx.transforms.post_transforms import SphinxPostTransform
URL_BASE = {
"zh_CN": "https://lvgl.100ask.net/"
}
class translation_link(nodes.Element):
"""Node for "link_to_translation" role."""
# Linking to translation is done at the "writing" stage to avoid issues with the info being cached between builders
def link_to_translation(name, rawtext, text, lineno, inliner, options={}, content=[]):
node = translation_link()
node['expr'] = (rawtext, text, options)
return [node], []
class TranslationLinkNodeTransform(SphinxPostTransform):
# Transform needs to happen early to ensure the new reference node is also transformed
default_priority = 0
def run(self, **kwargs):
# Only output relative links if building HTML
for node in self.document.traverse(translation_link):
if 'html' in self.app.builder.name:
rawtext, text, options = node['expr']
(language, link_text) = text.split(':')
env = self.document.settings.env
docname = env.docname
# doc_path = env.doc2path(docname, False)
if "LVGL_URLPATH" not in os.environ:
os.environ['LVGL_URLPATH'] = 'master'
urlpath = os.getenv('LVGL_URLPATH')+'/'
return_path = URL_BASE.get(language, "") + urlpath
url = '{}.html'.format(os.path.join(return_path, docname))
node.replace_self(nodes.reference(rawtext, link_text, refuri=url, **options))
else:
node.replace_self([])
def setup(app):
# link to the current documentation file in specific language version
app.add_role('link_to_translation', link_to_translation)
app.add_node(translation_link)
app.add_post_transform(TranslationLinkNodeTransform)
return {'parallel_read_safe': True, 'parallel_write_safe': True, 'version': '0.5'}

View File

@@ -0,0 +1,133 @@
import os
from docutils import nodes
from docutils.parsers.rst import Directive, directives
# from docutils.parsers.rst.directives.images import Image
# from sphinx.directives.code import LiteralInclude
def excluded_list(argument):
return argument.split(',')
class LvExample(Directive):
required_arguments = 1
option_spec = {
'excluded_languages': excluded_list,
'language': directives.unchanged,
'description': directives.unchanged
}
def get_example_code_path(self, example_path, language):
base_path = os.path.dirname(__file__)
examples_path = os.path.abspath(os.path.join(base_path, '..', 'examples'))
example_path = os.path.join(examples_path, example_path + '.' + language)
return example_path
def human_language_name(self, language):
if language == 'py':
return 'MicroPython'
elif language == 'c':
return 'C'
else:
return language
def github_path(self, example_path, language):
env = self.state.document.settings.env
return f"https://github.com/lvgl/lvgl/blob/{env.config.repo_commit_hash}/examples/{example_path}.{language}"
def embed_code(self, example_file, example_path, language, buttons={}):
toggle = nodes.container('', literal_block=False, classes=['toggle'])
header = nodes.container('', literal_block=False, classes=['header'])
toggle.append(header)
try:
with open(example_file, 'rb') as f:
contents = f.read().decode('utf-8')
except FileNotFoundError:
print('File Not Found', example_file)
contents = 'Error encountered while trying to open ' + example_file
literal_list = nodes.literal_block(contents, contents)
literal_list['language'] = language
toggle.append(literal_list)
paragraph_node = nodes.raw(text=f"<p>{self.human_language_name(language)} code &nbsp;</p>", format='html')
for text, url in buttons.items():
paragraph_node.append(nodes.raw(text=f"<a class='lv-example-link-button' onclick=\"event.stopPropagation();\" href='{url}'>{text}</a>", format='html'))
header.append(paragraph_node)
return toggle
def run(self):
example_path = self.arguments[0]
example_name = os.path.split(example_path)[1]
excluded_languages = self.options.get('excluded_languages', [])
node_list = []
env = self.state.document.settings.env
iframe_html = ""
c_path = self.get_example_code_path(example_path, 'c')
py_path = self.get_example_code_path(example_path, 'py')
if os.path.exists(c_path):
c_code = self.embed_code(c_path, example_path, 'c', buttons={
'<i class="fa fa-github"></i>&nbsp;View on GitHub': self.github_path(example_path, 'c')
})
else:
c_code = None
if os.path.exists(py_path):
py_code = self.embed_code(py_path, example_path, 'py', buttons={
'<i class="fa fa-github"></i>&nbsp;View on GitHub': self.github_path(example_path, 'py'),
'<i class="fa fa-play"></i>&nbsp;MicroPython Simulator': f"https://sim.lvgl.io/v{env.config.version}/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/{env.config.repo_commit_hash}/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/{env.config.repo_commit_hash}/examples/{example_path}.py"
})
else:
py_code = None
if 'c' not in excluded_languages:
if env.app.tags.has('html'):
iframe_html = f"<div class='lv-example' data-real-src='/{env.config.version}/_static/built_lv_examples/index.html?example={example_name}&w=320&h=240'></div>"
description_html = f"<div class='lv-example-description'>{self.options.get('description', '')}</div>"
layout_node = nodes.raw(text=f"<div class='lv-example-container'>{iframe_html}{description_html}</div>", format='html')
node_list.append(layout_node)
if 'c' not in excluded_languages and c_code is not None:
node_list.append(c_code)
if 'py' not in excluded_languages and py_code is not None:
node_list.append(py_code)
trailing_node = nodes.raw(text=f"<hr/>", format='html')
node_list.append(trailing_node)
return node_list
def setup(app):
app.add_directive("lv_example", LvExample)
# Direct [View on GitHub] links in examples to use current
# branch (stored in LVGL_GITCOMMIT environment variable) instead
# of the current commit hash as was being done previously.
# Default to 'master' if Sphinx is being run outside of `build.py`.
# Resulting example link:
# [https://github.com/lvgl/lvgl/blob/master/examples/anim/lv_example_anim_1.c].
# [https://github.com/lvgl/lvgl/blob/v8.4.0/examples/anim/lv_example_anim_1.c].
# [https://github.com/lvgl/lvgl/blob/v9.2.0/examples/anim/lv_example_anim_1.c].
if 'LVGL_GITCOMMIT' in os.environ:
git_commit = os.environ['LVGL_GITCOMMIT']
else:
git_commit = 'master'
app.add_config_value("repo_commit_hash", git_commit, "env")
# if 'repo_commit_hash' in app.config._options:
# print(f"repo_commit_hash from lv_example.py: [{app.config._options['repo_commit_hash']}]")
# else:
# print("repo_commit_hash not found in [app.config._options] at this time.")
return {
'version': '0.1',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@@ -0,0 +1,287 @@
table, th, td {
border: 1px solid #bbb;
padding: 10px;
}
span.pre {
padding-right:8px;
}
span.pre {
padding-right: 8px;
}
span.pre:first-child {
padding-right: 0px;
}
code.sig-name {
/*margin-left:8px;*/
}
/*-------------------------------------------------------------------------
* `home-img` class is (at this writing) exclusively used for the large
* buttons on the landing page. The rules below provide their hover
* movement behavior.
*-------------------------------------------------------------------------
*/
.home-img {
width: 32%;
transition: transform .3s ease-out;
}
.home-img:hover {
transform: translate(0, -10px);
}
/*-------------------------------------------------------------------------
* All `wy-...` and `rst-content` classes are classes from
* `sphinx_rtd_theme` `layout.html` template.
*-------------------------------------------------------------------------
* `wy-side-nav-search` class adorns the <div> element in the upper left
* corner of each page that contains:
* - the LVGL logo
* - documentation-version selector drop-down (matches LVGL release versions)
* - search TextBox
*/
.wy-side-nav-search {
background-color: #f5f5f5;
}
.wy-side-nav-search > div.version {
color: #333;
display: none; /*replaced by dropdown*/
}
/* `rst-content` class marks the <div> element that contains the whole
* visible part of the right panel where the page content is shown. */
/*Let `code` wrap*/
.rst-content code, .rst-content tt, code {
white-space: normal;
}
.wy-nav-content {
padding: 1.618em 3.236em;
height: 100%;
max-width: 1920px;
margin: auto
}
/*-------------------------------------------------------------------------
* `lv_example` Sections
*-------------------------------------------------------------------------
* The below contains a documented example to show the relationships of
* the rules below to their selected elements within the example itself.
*-------------------------------------------------------------------------
* `lv_example` sections are the output of `./docs/src/_ext/lv_example.py`
* custom Sphinx extension. These are generated when the `.rst` source
* file contains a pattern like this:
*
* A very simple *hello world* label // Sphinx+docutils (.rst parser) generates
* --------------------------------- // the <section> element and title from this.
*
* .. lv_example:: get_started/lv_example_get_started_1 // `lv_example.py` generates the
* :language: c // rest of the <section> content
* // below from this.
*-------------------------------------------------------------------------
* Visible layout consists of a <section> element containing:
* - <h3> element with title
* - <div> element containing live running web-assembly example of LVGL UI
* - <div> element containing [> Show C Code] [G View on GitHub] buttons
* and the C code below them.
*
* <section id="a-very-simple-hello-world-label"> // Contains whole example, buttons and code.
* <h3>A very simple <em>hello world</em> label...</h3> // The title itself.
* <div class="lv-example-container"> // Container for running example.
* <div class="lv-example" data-real-src="..." data-is-loaded="true">
* <iframe src="..."></iframe> // Running web-assembly example.
* </div>
* <div class="lv-example-description"></div> // Whole area to the right of running example.
* </div>
* <div class="toggle docutils container"> // Contains buttons + code;
* <div class="header docutils container" ...> // Contains buttons + code; `open` class is added to this to element "open" the code <div> element.
* <p>C code &nbsp;</p> // [C code] "button"; JS code in `page.html` template arms this "button" with its events when `$(document).ready` event fires.
* <a class="lv-example-link-button" ...> // [View on GitHub] "button"; provides hyperlink to the example code on GitHub.
* <i class="fa fa-github"></i> // "GitHub Cat" icon
* &nbsp;View on GitHub // text of "button"
* </a>
* </div>
* <div class="highlight-c notranslate" ...>
* <div class="highlight">
* <pre>
* ... // Highlighted C code
* </pre>
* </div>
* </div>
* </div>
* <hr>
* </section>
*/
.toggle .header {
display: block;
clear: both;
cursor: pointer;
font-weight: bold;
}
/* Selects <div class="header docutils container" ...> element.
* Places "disclosure triangle" icon + non-breaking space to its left.*/
.toggle .header:before {
font-family: FontAwesome, "Lato", "proxima-nova", "Helvetica Neue", Arial, sans-serif;
content: "\f0da \00a0 Show "; /* \f0da = "closed" disclosure triangle; \f00a0 = non-breaking space */
display: inline-block;
font-size: 1.1em;
}
/* Selects <div class="header docutils container" ...> element when it also contains
* `open` class, which class is toggled by JS code in `page.html` template. */
.toggle .header.open:before {
content: "\f0d7 \00a0 Hide "; /* \f0d7 = "open" disclosure triangle; \f00a0 = non-breaking space */
}
/* Selects the [> C code] "button". */
.header p {
display: inline-block;
font-size: 1.1em;
margin-bottom: 8px;
}
/* Selects web-assembly example <div> and child <iframe> elements. */
.lv-example, .lv-example > iframe {
border: none;
outline: none;
padding: 0;
display: block;
width: 320px;
height: 240px;
flex: none;
position: relative;
}
/* Selects only the <iframe> elements. */
.lv-example > iframe {
position: absolute;
top: 0;
left: 0;
}
/* Selects outer example <div> container elements. */
.lv-example-container {
display: flex;
padding-bottom: 16px;
}
/* Selects <div class="lv-example-description"></div>
* which is the whole area to the right of running example. */
.lv-example-description {
flex: 1 1 auto;
margin-left: 1rem;
}
/* Selects [View on GitHub] "button". */
.lv-example-link-button {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
background-color: #2980b9;
color: white;
margin: 0 4px;
}
/* Selects [View on GitHub] "button" when in `:hover` state. */
.lv-example-link-button:hover {
color: white;
filter: brightness(120%);
}
/* Selects [View on GitHub] "button" when in `:visited` state. */
.lv-example-link-button:visited {
color: white;
}
/*-------------------------------------------------------------------------
* This doesn't select anything at this writing (29-Mar-2025).
*-------------------------------------------------------------------------
*/
dl.cpp.unexpanded dd {
display: none;
}
/*-------------------------------------------------------------------------
* The `lv-api-...` classes do not select anything at this writing (29-Mar-2025).
*-------------------------------------------------------------------------
*/
.lv-api-expansion-button {
padding: 4px;
}
/* There are no elements that these select at this writing (29-Mar-2025). */
.lv-api-expansion-button::before {
font-family: FontAwesome, "Lato", "proxima-nova", "Helvetica Neue", Arial, sans-serif;
display: inline-block;
font-size: 1.1em;
cursor: pointer;
}
/* There are no elements that these select at this writing (29-Mar-2025). */
.unexpanded .lv-api-expansion-button::before {
content: "\f0da \00a0";
}
.expanded .lv-api-expansion-button::before {
content: "\f0d7 \00a0";
}
/* Selects all <div> elements with "body" class.
* There are no elements that this selects at this writing (29-Mar-2025). */
div.body {
min-width: 360px;
max-width: 1920px;
}
/* Selects CPP-expression content <span> elements generated from
* :cpp:expr:`...` Interpreted-Text-Role expressions in the `.rst` source files. */
.cpp-expr {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospace;
color: #e74c3c;
padding: 2px 5px;
border: 1px solid #e1e4e5;
font-size: 75%;
}
/* These replace colors present in `pygments.css` which is used in code highlighting.
* These are too dark to be readlable in DARK mode. They include:
* .highlight .nf -- function names
* .highlight .nl -- code labels
* .descname .n -- API documentation function names
* .highlight .p -- Punctuation
* .highlight -- Plain text in a `.. code-block:: none` block
* The first 2 were created by lightening the `pygments.css` colors without changing their
* angle on the color wheel. The added attribute "conditional" also limits this change to
* DARK MODE only instead of both light and dark modes.
*/
/* Name.Function */
html[data-theme="dark"] .highlight .nf {
color: #ccd285;
}
/* Name.Label */
html[data-theme="dark"] .highlight .nl {
color: #0043e2;
}
/* Name */
html[data-theme="dark"] .descname .n {
color: #0a44de;
}
/* Punctuation */
html[data-theme="dark"] .highlight .p {
color: #5c7c72
}
/* Plain text. */
html[data-theme="dark"] .highlight {
background: #ffffff; color: #bfbfbf
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 973 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 990 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View File

@@ -0,0 +1,57 @@
/* API collapsing */
document.addEventListener('DOMContentLoaded', (event) => {
document.querySelectorAll("dl.cpp").forEach(cppListing => {
const dt = cppListing.querySelector("dt");
let shouldBeExpanded = false;
if(dt.id == document.location.hash.substring(1))
shouldBeExpanded = true;
cppListing.classList.add(shouldBeExpanded ? "expanded" : "unexpanded");
const button = document.createElement("span");
button.classList.add("lv-api-expansion-button");
button.addEventListener("click", () => {
cppListing.classList.toggle("unexpanded");
cppListing.classList.toggle("expanded");
});
dt.insertBefore(button, dt.firstChild);
});
fetch('https://lvgl.io/home-banner.txt') // Replace with your URL
.then(response => {
// Check if the request was successful
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
// Read the response as text
return response.text();
})
.then(data => {
const section = document.querySelector('.wy-nav-content-wrap');
//Add a div
const newDiv = document.createElement('div');
newDiv.style="background-image: linear-gradient(45deg, black, #5e5e5e); color: white; border-bottom: 4px solid #e10010; padding-inline:3em"
section.insertBefore(newDiv, section.firstChild);
//Add a p to the div
const newP = document.createElement('p');
newP.style="padding-block:12px; margin-block:0px;align-content: center;align-items: center;"
newP.innerHTML = data
newDiv.insertBefore(newP, newDiv.firstChild);
const children = newDiv.querySelectorAll('*');
// Iterate over each child
children.forEach(child => {
// Check if the child has an id
if (child.id) {
// Prepend 'docs-' to the id
child.id = 'docs-' + child.id;
}
})
}) .catch(error => {
console.error('Fetch error: ' + error.message);
});
})

View File

@@ -0,0 +1,29 @@
/*https://www.w3schools.com/howto/howto_html_include.asp*/
function includeHTML() {
var z, i, elmnt, file, xhttp;
/*loop through a collection of all HTML elements:*/
z = document.getElementsByTagName("*");
for (i = 0; i < z.length; i++) {
elmnt = z[i];
/*search for elements with a certain attribute:*/
file = elmnt.getAttribute("include-html");
if (file) {
/*make an HTTP request using the attribute value as the file name:*/
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4) {
if (this.status == 200) {elmnt.innerHTML = this.responseText;}
if (this.status == 404) {elmnt.innerHTML = "Page not found.";}
/*remove the attribute, and call this function once more:*/
elmnt.removeAttribute("w3-include-html");
includeHTML();
}
}
xhttp.open("GET", file, true);
xhttp.send();
/*exit the function:*/
return;
}
}
};

View File

@@ -0,0 +1,31 @@
{% extends "!layout.html" %}
{%- block extrahead %}
{{ super() }}
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-78811084-3"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-78811084-3', { 'anonymize_ip': true });
</script>
{% endblock %}
{% block footer %}
{{ super() }}
<div class="footer">This page uses <a href="https://analytics.google.com/">
Google Analytics</a> to collect statistics. You can disable it by blocking
the JavaScript coming from www.google-analytics.com.
<script type="text/javascript">
(function() {
var ga = document.createElement('script');
ga.src = ('https:' == document.location.protocol ?
'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
ga.setAttribute('async', 'true');
document.documentElement.firstChild.appendChild(ga);
})();
</script>
</div>
{% endblock %}

View File

@@ -0,0 +1,82 @@
{% extends "!page.html" %}
{% block footer %}
<style>
.wy-side-nav-search > div[role="search"] {
color: black;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
$(".toggle > *").hide();
$(".toggle .header").show();
$(".toggle .header").click(function() {
$(this).parent().children().not(".header").toggle(400);
$(this).parent().children(".header").toggleClass("open");
})
});
</script>
<script type="text/javascript">
function add_version_selector()
{
return fetch("https://raw.githubusercontent.com/lvgl/docs_compiled/gh-pages/versionlist.txt")
.then(res => res.text())
.then(text => {
const versions = text.split("\n").filter(version => version.trim().length > 0);
let p = document.getElementById("rtd-search-form").parentElement;
p.innerHTML = `
<select name="versions" id="versions" onchange="ver_sel()" style="border-radius:5px; margin-bottom:15px">
${versions.map(version => {
let versionName = "";
if(version == "master") versionName = "master (latest)";
else versionName = "v" + ((version.indexOf(".") != -1) ? version : (version + " (latest minor)"));
return `<option value="${version}">${versionName}</option>`;
})}
</select>` + p.innerHTML;
});
}
function ver_sel()
{
var x = document.getElementById("versions").value;
window.location.href = window.location.protocol + "//" + window.location.host + "/" + x + "/";
}
document.addEventListener('DOMContentLoaded', (event) => {
add_version_selector().then(() => {
var value = window.location.pathname.split('/')[1];
document.getElementById("versions").value = value;
});
})
document.addEventListener('DOMContentLoaded', (event) => {
function onIntersection(entries) {
entries.forEach(entry => {
let currentlyLoaded = entry.target.getAttribute("data-is-loaded") == "true";
let shouldBeLoaded = entry.intersectionRatio > 0;
if(currentlyLoaded != shouldBeLoaded) {
entry.target.setAttribute("data-is-loaded", shouldBeLoaded);
if(shouldBeLoaded) {
let iframe = document.createElement("iframe");
iframe.src = entry.target.getAttribute("data-real-src");
entry.target.appendChild(iframe);
} else {
let iframe = entry.target.querySelector("iframe");
iframe.parentNode.removeChild(iframe);
}
}
});
}
const config = {
rootMargin: '600px 0px',
threshold: 0.01
};
let observer = new IntersectionObserver(onIntersection, config);
document.querySelectorAll(".lv-example").forEach(iframe => {
observer.observe(iframe);
});
});
</script>
{% endblock %}

View File

@@ -0,0 +1,631 @@
# Configuration file for the Sphinx documentation builder.
# Created by sphinx-quickstart on Wed Jun 12 16:38:40 2019.
#
# This file is execfile()'d with the current directory set to its
# containing directory.
#
# All configuration values have a default; some values are included
# below that are commented out and some of these serve to show the
# default. Note that not all possible configuration values are present.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# The major sections below each reflect a major section of that web page,
# and they are ordered in the same sequence so it is clear what config
# items go with what.
#
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath() to make it absolute, as shown here.
import os
import sys
from sphinx.builders.html import StandaloneHTMLBuilder
base_path = os.path.abspath(os.path.dirname(__file__))
# Add path to import link_roles.py and lv_example.py
sys.path.insert(0, os.path.abspath('./_ext'))
# Add path to import lvgl_version.py. Remember this is
# running under `sphinx-build` environment, not `build.py`.
sys.path.insert(0, base_path)
from lvgl_version import lvgl_version #NoQA
# *************************************************************************
# Project Information
# *************************************************************************
project = 'LVGL'
copyright = '2024-%Y, LVGL Kft'
author = 'LVGL Community'
if __name__ == '__main__':
version_src_path = os.path.join(base_path, '../../lv_version.h')
else:
version_src_path = os.path.join(base_path, 'lv_version.h')
version = lvgl_version(version_src_path)
release = version
# Notes about `version` here:
# ---------------------------
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# A short X.Y version is extracted from `lv_version.h` using a cross-platform compatible
# Python function in lvgl_version.py, and passed in on `sphinx-build` command line.
#
# 22-Apr-2025 while the `-D version=...` on the command line works (as long as quotes
# are not placed around the version), having it added after `sphinx-build` has
# executed this script is not soon enough because we need the version in some
# strings below. So we need to get it here from `lv_version.h` in order to do that.
# *************************************************************************
# General Configuration
# *************************************************************************
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or custom extensions.
#
# As of 6-Jan-2025, `link_roles` is being commented out because it is being
# replaced by a manually-installed translation link in ./docs/index.rst.
extensions = [
'sphinx_rtd_theme',
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
'sphinx.ext.todo',
'breathe',
'sphinx_sitemap',
'lv_example',
'sphinx_design',
'sphinx_rtd_dark_mode',
# 'link_roles',
'sphinxcontrib.mermaid',
]
needs_extensions = {
'sphinxcontrib.mermaid': '0.9.2'
}
# If 'SPHINX_REREDIRECTS_STANDDOWN' environment variable exists and
# is set to a value not equal to '0', then do not add 'sphinx_reredirects'
# to extensions. This gives someone testing/editing/debugging documentation
# build the possibility of skipping adding redirects in the local environment
# if desired.
add_redirects = True
if 'SPHINX_REREDIRECTS_STANDDOWN' in os.environ:
if os.environ.get('SPHINX_REREDIRECTS_STANDDOWN') != '0':
print("sphinx_reredirects standing down as requested.")
add_redirects = False
if add_redirects:
extensions.append('sphinx_reredirects')
del add_redirects
# -------------------------------------------------------------------------
# Options for Highlighting
# -------------------------------------------------------------------------
# The default language to highlight source code in. The default is 'python'.
# The value should be a valid Pygments lexer name, see Showing code examples
# for more details.
highlight_language = 'c'
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# -------------------------------------------------------------------------
# Options for Internationalisation
# -------------------------------------------------------------------------
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'en'
# -------------------------------------------------------------------------
# Options for Markup
# -------------------------------------------------------------------------
default_role = 'literal'
# keep_warnings = False # True causes Sphinx warnings to be added to documents.
primary_domain = 'c' # Default: 'py'
# -------------------------------------------------------------------------
# Options for Source Files
# -------------------------------------------------------------------------
# List of glob-style patterns, relative to source directory, that
# match files and directories to ignore when looking for source files.
# These patterns also effect html_static_path and html_extra_path.
exclude_patterns = ['build', 'doxygen', 'Thumbs.db', '.DS_Store',
'README.md', 'README_*', 'lv_examples', 'out_html', 'env', '_ext', 'examples']
# The master toctree document. (Root of TOC tree.)
master_doc = 'index'
# The suffix(es) of source filenames.
# You can specify multiple suffixes as a dictionary per
# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-source_suffix
source_suffix = {'.rst': 'restructuredtext'}
# -------------------------------------------------------------------------
# Options for Smart Quotes
# -------------------------------------------------------------------------
# Enabling smart quotes action to convert `--` to en dashes and `---` to em
# dashes. Converting quotation marks and ellipses is NOT done because the
# default `smartquotes_action` 'qDe' is changed to just 'D' below, which
# accomplishes the dash conversions as desired.
#
# For list of all possible smartquotes_action values, see:
# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-smartquotes_action
smartquotes = True
smartquotes_action = 'D'
smartquotes_excludes = {'builders': ['man', 'text', 'json', 'xml', 'latex']}
# -------------------------------------------------------------------------
# Options for Templating
# -------------------------------------------------------------------------
# A list of paths that contain extra templates (or templates that overwrite
# builtin/theme-specific templates). Relative paths are taken as relative
# to the configuration directory.
templates_path = ['_templates']
# -------------------------------------------------------------------------
# Options for Warning Control
# -------------------------------------------------------------------------
# suppress_warnings = ['app.add_directive', etc.]
# *************************************************************************
# Builder Options
# *************************************************************************
# -------------------------------------------------------------------------
# Options for HTML Builder
# -------------------------------------------------------------------------
# The theme for HTML output. See https://www.sphinx-doc.org/en/master/usage/theming.html
html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a
# theme further. For a list of options available for each theme, see the
# theme documentation.
#
# Various other builders are derived from the HTML output, and also make use
# of these options.
# Note: 'display_version' option is now obsolete in the current (08-Oct-2024)
# version of sphinx-rtd-theme (upgraded for Sphinx v8.x). The removed line
# is preserved by commenting it out in case it is ever needed again.
html_theme_options = {
# 'display_version': True,
'prev_next_buttons_location': 'both',
'style_external_links': False,
# 'vcs_pageview_mode': '',
# 'style_nav_header_background': 'white',
# Toc options
'sticky_navigation': True,
'navigation_depth': 4,
'includehidden': False,
'titles_only': False,
'collapse_navigation': False,
'logo_only': True,
}
# For site map generation
if "LVGL_URLPATH" not in os.environ:
os.environ['LVGL_URLPATH'] = 'master'
_branch = os.getenv('LVGL_URLPATH')
html_baseurl = f"https://docs.lvgl.io/{_branch}/"
#lvgl_github_url = f"https://github.com/lvgl/lvgl/blob/{os.environ['LVGL_GITCOMMIT']}/docs"
#extlinks = {'github_link_base': (github_url + '%s', github_url)}
if "LVGL_GITCOMMIT" not in os.environ:
os.environ['LVGL_GITCOMMIT'] = 'master'
_git_commit_ref = os.getenv('LVGL_GITCOMMIT')
# These keys are used "bare" as template variables in:
# - sphinx_rtd_theme theme template: breadcrumbs.html
# - furo theme template: edit-this-page.html
# - furo theme template: view-this-page.html
html_context = {
'display_github': True,
'github_user': 'lvgl',
'github_repo': 'lvgl',
'github_version': _git_commit_ref,
'conf_py_path': '/docs/src/'
}
html_logo = '_static/images/logo_lvgl.png'
html_favicon = '_static/images/favicon.png'
html_css_files = [
'css/custom.css',
'css/fontawesome.min.css'
]
html_js_files = [
'js/custom.js',
'js/include_html.js'
]
html_static_path = ['_static']
html_last_updated_fmt = '' # Empty string uses default format: '%b %d, %Y'
html_last_updated_use_utc = False # False = use generating system's local date, not GMT.
html_permalinks = True # Default = True, add link anchor for each heading and description environment.
html_sidebars = {
'**': [
'relations.html', # needs 'show_related': True theme option to display
'searchbox.html',
]
}
# html_domain_indices
# html_use_index = True # Default = True
# html_split_index
# html_copy_source = True # Default = True; if True, reStructuredText sources are included in HTML build as _sources/docname.
# html_show_sourcelink = True # Default = True
# html_sourcelink_suffix = '.txt' # Default = '.txt'
# html_use_opensearch = ''
# html_file_suffix = '.html' # Default = '.html'
# html_link_suffix = html_file_suffix
html_show_copyright = True # Default = True; shows copyright notice in footer.
# html_show_search_summary = True # Default = True
# html_show_sphinx = True # Default = True; adds "Created using Sphinx" to footer.
# html_output_encoding = 'utf-8' # Default = 'utf-8'
# html_compact_lists = True # Default = True
# html_secnumber_suffix = '. ' # Default = '. '; set to ' ' to suppress final dot on section numbers.
# html_search_language = language # Default = language; language to be used for generating the HTML full-text search index.
# html_search_options
# Link images that have been resized with a scale option (scale, width, or height)
# to their original full-resolution image. This will not overwrite any link given
# by the target option on the the image directive, if present.
#
# Tip
#
# To disable this feature on a per-image basis, add the no-scaled-link class to the image directive:
#
# .. image:: sphinx.png
# :scale: 50%
# :class: no-scaled-link
# html_scaled_image_link
# html_math_renderer = 'mathjax' # Default = 'mathjax'
# Change image search sequence: image files are searched in the order in which they appear here.
# See https://www.sphinx-doc.org/en/master/usage/builders/index.html#sphinx.builders.html.StandaloneHTMLBuilder.supported_image_types
StandaloneHTMLBuilder.supported_image_types = [
'image/svg+xml',
'image/gif', #prefer gif over png
'image/png',
'image/jpeg'
]
# -------------------------------------------------------------------------
# Options for HTMLHelp Builder
# -------------------------------------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'LVGLdoc'
# -------------------------------------------------------------------------
# Options for LaTeX Builder
# -------------------------------------------------------------------------
latex_engine = 'xelatex'
latex_use_xindy = False
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
'inputenc': '',
'utf8extra': '',
'classoptions': ',openany,oneside',
'babel': '\\usepackage{babel}',
'passoptionstopackages': r'''
\PassOptionsToPackage{bookmarksdepth=5}{hyperref}% depth of pdf bookmarks
''',
'preamble': r'''
\usepackage{fontspec}
\setmonofont{DejaVu Sans Mono}
\usepackage{silence}
\WarningsOff*
''',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'LVGL.tex', 'LVGL v' + version,
author, 'manual'),
]
# -------------------------------------------------------------------------
# Options for Manual Page Builder
# -------------------------------------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'lvgl', 'LVGL v' + version,
[author], 3)
]
# -------------------------------------------------------------------------
# Options for Texinfo Builder
# -------------------------------------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'LVGL', 'LVGL v' + version,
author, 'Contributors of LVGL', 'One line description of project.',
'Miscellaneous'),
]
# *************************************************************************
# Domain Options
# *************************************************************************
# -------------------------------------------------------------------------
# Options for the C Domain
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# Options for the CPP Domain
# -------------------------------------------------------------------------
# *************************************************************************
# Configuration for Sphinx Extensions
# *************************************************************************
# -------------------------------------------------------------------------
# Options for sphinx_rtd_dark_mode extension
# -------------------------------------------------------------------------
default_dark_mode = False
# -------------------------------------------------------------------------
# Options for lv_example extension
# -------------------------------------------------------------------------
repo_commit_hash = _git_commit_ref
# -------------------------------------------------------------------------
# Options for sphinx.ext.todo extension
# -------------------------------------------------------------------------
# If true, `todo` and `todoList` directives produce output, else they produce nothing.
# See https://www.sphinx-doc.org/en/master/usage/extensions/todo.html
todo_include_todos = True
# -------------------------------------------------------------------------
# Options for sphinx_sitemap extension
# -------------------------------------------------------------------------
# See https://sphinx-sitemap.readthedocs.io/en/latest/index.html
sitemap_url_scheme = "{link}"
# -------------------------------------------------------------------------
# Options for breathe extension
# -------------------------------------------------------------------------
# See https://breathe.readthedocs.io/en/latest/
breathe_projects = {
"lvgl": "xml/",
}
breathe_default_project = "lvgl"
# breathe_debug_trace_directives = True
# -------------------------------------------------------------------------
# Options for sphinx_reredirects
# -------------------------------------------------------------------------
# The below generates .HTML page redirects for pages that have been moved.
# Browsers are redirected via `<meta http-equiv="refresh" content="0; url=new_url">`.
redirects = {
"CONTRIBUTING": "contributing/index.html" ,
"get-started/index": "../intro/getting_started.html" ,
"get-started/os/nuttx": "../../details/integration/os/nuttx.html" ,
"get-started/platforms/arduino": "../../details/integration/framework/arduino.html" ,
"get-started/platforms/espressif": "../../details/integration/chip/espressif.html" ,
"get-started/platforms/pc-simulator": "../../details/integration/ide/pc-simulator.html" ,
"get-started/quick-overview": "../intro/getting_started.html" ,
"integration/bindings/api_json": "../../details/integration/bindings/api_json.html" ,
"integration/bindings/cpp": "../../details/integration/bindings/cpp.html" ,
"integration/bindings/index": "../../details/integration/bindings/index.html" ,
"integration/bindings/javascript": "../../details/integration/bindings/javascript.html" ,
"integration/bindings/micropython": "../../details/integration/bindings/micropython.html" ,
"integration/bindings/pikascript": "../../details/integration/bindings/pikascript.html" ,
"integration/building/cmake": "../../details/integration/building/cmake.html" ,
"integration/building/index": "../../details/integration/building/index.html" ,
"integration/building/make": "../../details/integration/building/make.html" ,
"integration/chip/arm": "../../details/integration/chip/arm.html" ,
"integration/chip/espressif": "../../details/integration/chip/espressif.html" ,
"integration/chip/index": "../../details/integration/chip/index.html" ,
"integration/chip/nxp": "../../details/integration/chip/nxp.html" ,
"integration/chip/renesas": "../../details/integration/chip/renesas/index.html" ,
"integration/chip/stm32": "../../details/integration/chip/stm32.html" ,
"integration/driver/X11": "../../details/integration/driver/X11.html" ,
"integration/driver/display/fbdev": "../../../details/integration/driver/display/fbdev.html" ,
"integration/driver/display/gen_mipi": "../../../details/integration/driver/display/gen_mipi.html" ,
"integration/driver/display/ili9341": "../../../details/integration/driver/display/ili9341.html" ,
"integration/driver/display/index": "../../../details/integration/driver/display/index.html" ,
"integration/driver/display/lcd_stm32_guide": "../../../details/integration/driver/display/lcd_stm32_guide.html",
"integration/driver/display/renesas_glcdc": "../../../details/integration/driver/display/renesas_glcdc.html" ,
"integration/driver/display/st7735": "../../../details/integration/driver/display/st7735.html" ,
"integration/driver/display/st7789": "../../../details/integration/driver/display/st7789.html" ,
"integration/driver/display/st7796": "../../../details/integration/driver/display/st7796.html" ,
"integration/driver/display/st_ltdc": "../../../details/integration/driver/display/st_ltdc.html" ,
"integration/driver/index": "../../details/integration/driver/index.html" ,
"integration/driver/libinput": "../../details/integration/driver/libinput.html" ,
"integration/driver/opengles": "../../details/integration/driver/opengles.html" ,
"integration/driver/touchpad/evdev": "../../../details/integration/driver/touchpad/evdev.html" ,
"integration/driver/touchpad/ft6x36": "../../../details/integration/driver/touchpad/ft6x36.html" ,
"integration/driver/touchpad/index": "../../../details/integration/driver/touchpad/index.html" ,
"integration/driver/wayland": "../../details/integration/driver/wayland.html" ,
"integration/driver/windows": "../../details/integration/driver/windows.html" ,
"integration/framework/arduino": "../../details/integration/framework/arduino.html" ,
"integration/framework/index": "../../details/integration/framework/index.html" ,
"integration/framework/platformio": "../../details/integration/framework/platformio.html" ,
"integration/framework/tasmota-berry": "../../details/integration/framework/tasmota-berry.html" ,
"integration/ide/index": "../../details/integration/ide/index.html" ,
"integration/ide/mdk": "../../details/integration/ide/mdk.html" ,
"integration/ide/pc-simulator": "../../details/integration/ide/pc-simulator.html" ,
"integration/index": "../details/integration/index.html" ,
"integration/os/freertos": "../../details/integration/os/freertos.html" ,
"integration/os/index": "../../details/integration/os/index.html" ,
"integration/os/mqx": "../../details/integration/os/mqx.html" ,
"integration/os/nuttx": "../../details/integration/os/nuttx.html" ,
"integration/os/px5": "../../details/integration/os/px5.html" ,
"integration/os/qnx": "../../details/integration/os/qnx.html" ,
"integration/os/rt-thread": "../../details/integration/os/rt-thread.html" ,
"integration/os/yocto/core_components": "../../../details/integration/os/yocto/core_components.html" ,
"integration/os/yocto/index": "../../../details/integration/os/yocto/index.html" ,
"integration/os/yocto/lvgl_recipe": "../../../details/integration/os/yocto/lvgl_recipe.html" ,
"integration/os/yocto/terms_and_variables": "../../../details/integration/os/yocto/terms_and_variables.html" ,
"integration/os/zephyr": "../../details/integration/os/zephyr.html" ,
"layouts/flex": "../details/common-widget-features/layouts/flex.html" ,
"layouts/grid": "../details/common-widget-features/layouts/grid.html" ,
"layouts/index": "../details/common-widget-features/layouts/index.html" ,
"libs/arduino_esp_littlefs": "../details/libs/arduino_esp_littlefs.html" ,
"libs/arduino_sd": "../details/libs/arduino_sd.html" ,
"libs/barcode": "../details/libs/barcode.html" ,
"libs/bmp": "../details/libs/bmp.html" ,
"libs/ffmpeg": "../details/libs/ffmpeg.html" ,
"libs/freetype": "../details/libs/freetype.html" ,
"libs/fs": "../details/libs/fs.html" ,
"libs/gif": "../details/libs/gif.html" ,
"libs/index": "../details/libs/index.html" ,
"libs/lfs": "../details/libs/lfs.html" ,
"libs/libjpeg_turbo": "../details/libs/libjpeg_turbo.html" ,
"libs/libpng": "../details/libs/libpng.html" ,
"libs/lodepng": "../details/libs/lodepng.html" ,
"libs/qrcode": "../details/libs/qrcode.html" ,
"libs/rle": "../details/libs/rle.html" ,
"libs/rlottie": "../details/libs/rlottie.html" ,
"libs/svg": "../details/libs/svg.html" ,
"libs/tiny_ttf": "../details/libs/tiny_ttf.html" ,
"libs/tjpgd": "../details/libs/tjpgd.html" ,
"others/file_explorer": "../details/auxiliary-modules/file_explorer.html" ,
"others/font_manager": "../details/auxiliary-modules/font_manager.html" ,
"others/fragment": "../details/auxiliary-modules/fragment.html" ,
"others/gridnav": "../details/auxiliary-modules/gridnav.html" ,
"others/ime_pinyin": "../details/auxiliary-modules/ime_pinyin.html" ,
"others/imgfont": "../details/auxiliary-modules/imgfont.html" ,
"others/index": "../details/auxiliary-modules/index.html" ,
"others/monkey": "../details/auxiliary-modules/monkey.html" ,
"others/obj_id": "../details/auxiliary-modules/obj_id.html" ,
"others/obj_property": "../details/auxiliary-modules/obj_property.html" ,
"others/observer": "../details/auxiliary-modules/observer.html" ,
"others/snapshot": "../details/auxiliary-modules/snapshot.html" ,
"overview/animations": "../details/main-modules/animation.html" ,
"overview/color": "../details/main-modules/color.html" ,
"overview/coord": "../details/common-widget-features/coord.html" ,
"overview/debugging/gdb_plugin": "../../details/debugging/gdb_plugin.html" ,
"overview/debugging/index": "../../details/debugging/index.html" ,
"overview/debugging/log": "../../details/debugging/log.html" ,
"overview/debugging/profiler": "../../details/debugging/profiler.html" ,
"overview/debugging/vg_lite_tvg": "../../details/debugging/vg_lite_tvg.html" ,
"overview/display": "../details/main-modules/display/index.html" ,
"overview/event": "../details/common-widget-features/event.html" ,
"overview/font": "../details/main-modules/font.html" ,
"overview/fs": "../details/main-modules/fs.html" ,
"overview/image": "../details/main-modules/image.html" ,
"overview/indev": "../details/main-modules/indev.html" ,
"overview/index": "../details/main-modules/index.html" ,
"overview/layer": "../details/common-widget-features/layer.html" ,
"overview/new_widget": "../details/widgets/new_widget.html" ,
"overview/obj": "../details/common-widget-features/obj.html" ,
"overview/renderers/arm2d": "../../details/integration/renderers/arm2d.html" ,
"overview/renderers/index": "../../details/integration/renderers/index.html" ,
"overview/renderers/nema_gfx": "../../details/integration/renderers/nema_gfx.html" ,
"overview/renderers/pxp": "../../details/integration/renderers/nxp_pxp.html" ,
"overview/renderers/sdl": "../../details/integration/renderers/sdl.html" ,
"overview/renderers/stm32_dma2d": "../../details/integration/renderers/stm32_dma2d.html" ,
"overview/renderers/sw": "../../details/integration/renderers/sw.html" ,
"overview/renderers/vg_lite": "../../details/integration/renderers/vg_lite.html" ,
"overview/renderers/vglite": "../../details/integration/renderers/nxp_vglite_gpu.html" ,
"overview/scroll": "../details/common-widget-features/scroll.html" ,
"overview/style": "../details/common-widget-features/styles/styles.html" ,
"overview/style-props": "../details/common-widget-features/styles/style-properties.html" ,
"overview/timer": "../details/main-modules/timer.html" ,
"porting/display": "../details/main-modules/display/index.html" ,
"porting/draw": "../details/main-modules/draw.html" ,
"porting/indev": "../details/main-modules/indev.html" ,
"porting/index": "../details/integration/adding-lvgl-to-your-project/index.html" ,
"porting/os": "../details/integration/adding-lvgl-to-your-project/threading.html",
"porting/project": "../details/integration/adding-lvgl-to-your-project/connecting_lvgl.html",
"porting/sleep": "../details/integration/adding-lvgl-to-your-project/threading.html",
"porting/tick": "../details/integration/adding-lvgl-to-your-project/connecting_lvgl.html",
"porting/timer_handler": "../details/integration/adding-lvgl-to-your-project/timer_handler.html",
"widgets/animimg": "../details/widgets/animimg.html" ,
"widgets/arc": "../details/widgets/arc.html" ,
"widgets/bar": "../details/widgets/bar.html" ,
"widgets/button": "../details/widgets/button.html" ,
"widgets/buttonmatrix": "../details/widgets/buttonmatrix.html" ,
"widgets/calendar": "../details/widgets/calendar.html" ,
"widgets/canvas": "../details/widgets/canvas.html" ,
"widgets/chart": "../details/widgets/chart.html" ,
"widgets/checkbox": "../details/widgets/checkbox.html" ,
"widgets/dropdown": "../details/widgets/dropdown.html" ,
"widgets/image": "../details/widgets/image.html" ,
"widgets/imagebutton": "../details/widgets/imagebutton.html" ,
"widgets/index": "../details/widgets/index.html" ,
"widgets/keyboard": "../details/widgets/keyboard.html" ,
"widgets/label": "../details/widgets/label.html" ,
"widgets/led": "../details/widgets/led.html" ,
"widgets/line": "../details/widgets/line.html" ,
"widgets/list": "../details/widgets/list.html" ,
"widgets/lottie": "../details/widgets/lottie.html" ,
"widgets/menu": "../details/widgets/menu.html" ,
"widgets/msgbox": "../details/widgets/msgbox.html" ,
"widgets/obj": "../details/widgets/base_widget.html" ,
"widgets/roller": "../details/widgets/roller.html" ,
"widgets/scale": "../details/widgets/scale.html" ,
"widgets/slider": "../details/widgets/slider.html" ,
"widgets/span": "../details/widgets/spangroup.html" ,
"widgets/spinbox": "../details/widgets/spinbox.html" ,
"widgets/spinner": "../details/widgets/spinner.html" ,
"widgets/switch": "../details/widgets/switch.html" ,
"widgets/table": "../details/widgets/table.html" ,
"widgets/tabview": "../details/widgets/tabview.html" ,
"widgets/textarea": "../details/widgets/textarea.html" ,
"widgets/tileview": "../details/widgets/tileview.html" ,
"widgets/win": "../details/widgets/win.html" ,
"details/widgets/span": "../../details/widgets/spangroup.html" ,
}
# Example configuration for intersphinx: refer to the Python standard library.
def setup(app):
pass
# app.add_config_value('recommonmark_config', {
# 'enable_eval_rst': True,
# 'enable_auto_toc_tree': 'True',
# }, True)
# app.add_transform(AutoStructify)
# app.add_css_file('css/custom.css')
# app.add_css_file('css/fontawesome.min.css')

View File

@@ -0,0 +1,401 @@
.. _coding-style:
============
Coding Style
============
File Template
*************
Use `misc/lv_templ.c <https://github.com/lvgl/lvgl/blob/master/src/misc/lv_templ.c>`__
and `misc/lv_templ.h <https://github.com/lvgl/lvgl/blob/master/src/misc/lv_templ.h>`__
Naming Conventions
******************
- Words are separated by '\_'
- In variable and function names use only lower case letters
(e.g. *height_tmp*)
- In enums and defines use only upper case letters
(e.g. *MAX_LINE_NUM*)
- Global names (API):
- start with *lv*
- followed by module name: *button*, *label*, *style* etc.
- followed by the action (for functions): *set*, *get*, etc.
- closed with the subject: *name*, *size*, *state* etc.
- Typedefs
- prefer ``typedef struct`` and ``typedef enum`` instead of
``struct name`` and ``enum name``
- always end ``typedef struct`` and ``typedef enum`` type names with
``_t``
- Abbreviations:
- The following abbreviations are used and allowed:
- ``dsc`` descriptor
- ``param`` parameter
- ``indev`` input device
- ``anim`` animation
- ``buf`` buffer
- ``str`` string
- ``min/max`` minimum/maximum
- ``alloc`` allocate
- ``ctrl`` control
- ``pos`` position
- Avoid adding new abbreviations
Coding Guide
************
- Editor:
- Set editor to use 4 spaces for tab indentations (instead of tab characters).
- Exception: the **Kconfig** file and any make files require leading tab characters
on child items.
- Functions:
- Write functions that use the single-responsibility principle.
- Make functions ``static`` when not part of that object's public API (where possible).
- Variables:
- One line, one declaration (BAD: char x, y;).
- Use ``<stdint.h>`` (*uint8_t*, *int32_t* etc).
- Declare variables where needed (not all at function start).
- Use the smallest required scope.
- Variables in a file (outside functions) are always *static*.
- Do not use global variables (use functions to set/get static variables).
Comments
********
Before every function prototype in ``.h`` files, include a Doxygen-formatted comment
like this:
.. code-block:: c
/**
* Brief description. Add a blank line + additional paragraphs when more detail is needed.
* @param parent brief description of argument. Additional detail can appear
* on subsequent lines. List of accepted values:
* - value one
* - value two
* - value three
* @return brief description of return value.
*/
type_name_t * lv_function_name(lv_obj_t * parent);
The normal comment prefix ``/**`` causes the comment to document the code member
*after* the comment. When documenting a code member that is *before* the
comment, such as a struct member, use ``/**<`` like this:
.. code-block:: c
/**
* Brief description of struct
*
* When more detail is needed, add a blank line then the detail.
*/
typedef struct {
char *text; /**< Brief description of this member */
uint16_t length; /**< Brief description of this member */
} lv_example_type_t;
- When commenting code, use block comments like this ``/* Description */``,
not end-of-line comments like this ``// Description``.
- Include a space after the ``/*`` or ``/**<`` and before the ``*/`` for improved readability.
- Write readable code to avoid descriptive comments like: ``x++; /* Add 1 to x */``.
- The code should show clearly what you are doing.
- You should write **why** you did it: ``x++; /* Point to closing '\0' of string */``
- Short "code summaries" of a few lines are accepted: ``/* Calculate new coordinates */``
- In comments use back-quotes (\`...\`) when referring to a code element, such as a variable, type,
or struct name: ``/* Update value of `x_act` */``
- When adding or modifying comments, priorities are (in order of importance):
1. clarity (the ease with which other programmers can understand your intention),
2. readability (the ease with which other programmers can read your comments),
3. brevity (the quality of using few words when speaking or writing).
- Blank lines within comments are desirable when they improve clarity and readability.
- Remember, when you are writing source code, you are not just teaching the computer
what to do, but also teaching other programmers how it works, not only users of the
API, but also future maintainers of your source code. Comments add information
about what you were thinking when the code was written, and **why** you did things
that way---information that cannot be conveyed by the source code alone.
Doxygen Comment Specifics
-------------------------
Doxygen is the first program in a chain that generates the online LVGL API
documentation from the files in the LVGL repository. Doxygen detects files it should
pay attention to by them having a ``@file`` command inside a Doxygen comment. Doxygen
comments begin with a leading ``/**``. It ignores comments that do not have exactly
two ``*``.
The following is an illustration of an API function prototype with documentation
illustrating most of the Doxygen commands used in LVGL.
.. code-block:: c
/**
* Set alignment of Widgets placed in containers with LV_STYLE_FLEX_FLOW style.
*
* The values for the `..._place` arguments come from the `lv_flex_align_t`
* enumeration and have the same meanings as they do for flex containers in CSS.
* @param obj pointer to flex container. It must have
* `LV_STYLE_FLEX_FLOW` style or nothing will happen.
* @param main_place where to place items on main axis (in their track).
* (Any value of `lv_flex_align_t`.)
* @param cross_place where to place item in track on cross axis.
* - `LV_FLEX_ALIGN_START`
* - `LV_FLEX_ALIGN_END`
* - `LV_FLEX_ALIGN_CENTER`
* @param track_cross_place where to place tracks in cross direction.
* (Any value of `lv_flex_align_t`.)
* Example for a title bar layout:
* @code{.c}
* lv_obj_set_flex_align(title_bar, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
* @endcode
* @see
* - https://css-tricks.com/snippets/css/a-guide-to-flexbox/
* - see `lv_obj_set_flex_grow()` for additional information.
*/
void lv_obj_set_flex_align(lv_obj_t * widget, lv_flex_align_t main_place, lv_flex_align_t cross_place,
lv_flex_align_t track_cross_place);
- Always start Doxygen comment with a brief description of the code element it documents.
- When more detail is needed, add a blank line below the brief description and add
additional information that may be needed by LVGL API users, including preconditions
for calling the function. Doxygen needs the blank line to separate "brief" from
"detail" portions of the description.
- Describe function parameters with the ``@param`` command. When a function writes
to the address contained by a pointer parameter, if not already obvious (e.g. when
the parameter name contains the word "out"), include the direction in the command
for clarity:
``@param[out] param_name description``.
- Describe return values with the ``@return`` command.
- Add at least 2 spaces after Doxygen commands for improved readability.
- Use back-quotes (\`...\`) around code elements (variables, type names, function names). For type
names and function names, Doxygen generates a hyperlink to that code member's
documentation (when it exists) with or without the single back-quotes.
- Append empty "()" to function names. Doxygen will not generate a hyperlink to the
function's documentation without this.
- Use proper grammar for clarity. Descriptions of parameters do not need periods
after them unless they are full sentences.
- Align edges of text around lists of parameters for ease of reading.
- Lists (e.g. of accepted parameter values) can be created by using the '-' character.
If the list needs to be numbered, numbers can also be used.
- Place example code in a code block by surrounding it with ``@code{.c}`` and ``@endcode`` commands.
Doxygen doesn't need the ``{.c}`` part, but the downstream software does.
- Refer reader to additional information using the ``@see`` command. Doxygen adds a
"See also" paragraph. The text following the ``@see`` command will be indented.
- If you create a new pair of ``.c`` and ``.h`` files (e.g. for a new driver), ensure
a Doxygen comment like this is at the top of each new file. Doxygen will not parse
the file without the ``@file`` command being present.
.. code-block:: c
/**
* @file filename.c
*
*/
Supported Doxygen Commands
--------------------------
- ``@file``
tells Doxygen to parse this file and also supplies documentation about
the file itself when applicable (everything following it in the same comment).
- ``@param name description``
documents ``name`` as a function parameter, and ``description`` is the text that
follows it until Doxygen encounters a blank line or another Doxygen command.
- ``@return description``
documents the return value until Doxygen encounters a blank line or another Doxygen command.
- ``@code{.c}/@endcode``
surrounds code that should be placed in a code block. While Doxygen knows to use C
color-coding of code blocks in a .C file, the downstream part of the documentation
generation sequence does not, so the ``{.c}`` appendage to the ``@code`` command
is necessary.
- ``@note text``
starts a paragraph where a note can be entered. The note ends with a blank line,
the end of the comment, or another Doxygen command that starts a new section.
If the note contains more than one paragraph, additional paragraphs can be added
by using additional ``@note`` commands. At this writing, ``@par`` commands do not
add additional paragraphs to notes as indicated in the Doxygen documentation.
- ``@see text``
generates a "See also" pagraph in a highlighted section, helpful when additional
information about a topic can be found elsewhere.
API Conventions
***************
To support the auto-generation of bindings, the LVGL C API must
follow some coding conventions:
- Use ``enum``\ s instead of macros. If inevitable to use ``define``\ s
export them with :cpp:expr:`LV_EXPORT_CONST_INT(defined_value)` right after the ``define``.
- In function arguments use ``type name[]`` declaration for array parameters instead of :cpp:expr:`type * name`
- Use typed pointers instead of :cpp:expr:`void *` pointers
- Widget constructor must follow the ``lv_<widget_name>_create(lv_obj_t * parent)`` pattern.
- Widget members function must start with ``lv_<widget_name>`` and should receive :cpp:expr:`lv_obj_t *` as first
argument which is a pointer to Widget object itself.
- ``struct`` APIs should follow the widgets' conventions. That is to receive a pointer to the ``struct`` as the
first argument, and the prefix of the ``struct`` name should be used as the prefix of the
function name as well (e.g. :cpp:expr:`lv_display_set_default(lv_display_t * disp)`)
- Functions and ``struct``\ s which are not part of the public API must begin with underscore in order to mark them as "private".
- Argument must be named in H files as well.
- Do not ``malloc`` into a static or global variables. Instead declare the variable in ``lv_global_t``
structure in ``lv_global.h`` and mark the variable with :cpp:expr:`(LV_GLOBAL_DEFAULT()->variable)` when it's used.
- To register and use callbacks one of the following needs to be followed.
- Pass a pointer to a ``struct`` as the first argument of both the registration function and the callback. That
``struct`` must contain ``void * user_data`` field.
- The last argument of the registration function must be ``void * user_data`` and the same ``user_data``
needs to be passed as the last argument of the callback.
To learn more refer to the documentation of `MicroPython <integration/bindings/micropython>`__.
Formatting
**********
Here is example to show bracket placement and use of white space:
.. code-block:: c
/**
* Set new text for a label. Memory will be allocated by label to store text.
*
* @param label pointer to label Widget
* @param text '\0' terminated character string.
* NULL to refresh with current text.
*/
void lv_label_set_text(lv_obj_t * label, const char * text)
{ /* Main brackets of functions in new line */
if(label == NULL) return; /* No bracket only if command is inline with if statement */
lv_obj_inv(label);
lv_label_ext_t * ext = lv_obj_get_ext(label);
/* Comment before a section */
if(text == ext->txt || text == NULL) { /* Bracket of statements starts on same line */
lv_label_refr_text(label);
return;
}
...
}
You can use **astyle** to format the code. Run ``code-format.py`` from
the ``scripts`` folder.
Includes
********
Various subsystems of LVGL can be enabled or disabled by setting a macro in
``lv_conf.h`` to 1 or 0 respectively. The code files that contain the logic for
those subsystems are often arranged so that there is an ``#if <ENABLING_MACRO_NAME>``
directive near the top of the file, and its matching ``#endif`` is at the end of the
file. If you add or modify such a subsystem in LVGL, whenever possible, the only
``#include`` that should be above those conditional directives should be just enough
to include the enabling/disabling macro. Specifically:
- in the ``.c`` file: the ``#include`` that includes the header with the closest
correspondence to that ``.c`` file
- in the mated ``.h`` file: ``#include "lv_conf_internal.h"``
which, itself includes ``lv_conf.h``. See examples at
`lv_freetype.c <https://github.com/lvgl/lvgl/blob/master/src/libs/freetype/lv_freetype.c>`__,
`lv_freetype_private.h <https://github.com/lvgl/lvgl/blob/master/src/libs/freetype/lv_freetype_private.h>`__ and
`lv_freetype.h <https://github.com/lvgl/lvgl/blob/master/src/libs/freetype/lv_freetype.h>`__.
pre-commit
**********
`pre-commit <https://pre-commit.com/>`__ is a multi-language package
manager for pre-commit hooks. See the `installation
guide <https://pre-commit.com/#installation>`__ to get pre-commit python
package installed into your development machine.
Once you have ``pre-commit`` installed you will need to `set up the git
hook scripts <https://pre-commit.com/#3-install-the-git-hook-scripts>`__
with:
.. code-block:: console
pre-commit install
now ``pre-commit`` will run automatically on ``git commit``!
Hooks
*****
The ``format-source`` local hook (see ``.pre-commit-config.yaml``) runs
**astyle** on all the staged source and header files (that are not
excluded, see ``exclude`` key of the hook configuration) before entering
the commit message, if any file gets formatted by **astyle** you will
need to add the change to the staging area and run ``git commit`` again.
The ``trailing-whitespace`` hook fixes trailing whitespaces on all of
the files.
Skipping hooks
**************
If you want to skip any particular hook you can do so with:
.. code-block:: console
SKIP=name-of-the-hook git commit
Testing hooks
*************
It is not necessary to do a commit to test the hooks, you can test hooks
by adding the files into the staging area and run:
.. code:: console
pre-commit run name-of-the-hook

View File

@@ -0,0 +1,70 @@
.. _contributing_dco:
=======================================
Developer Certification of Origin (DCO)
=======================================
Overview
********
To ensure all licensing criteria are met for every repository of the
LVGL project, we apply a process called DCO (Developer's Certificate of
Origin).
The text of DCO can be read here: https://developercertificate.org/.
By contributing to any repositories of the LVGL project you agree that
your contribution complies with the DCO.
If your contribution fulfills the requirements of the DCO, no further
action is needed. If you are unsure feel free to ask us in a comment,
e.g. in your submitted :ref:`Pull Request <contributing_pull_requests>`.
Accepted Licenses and Copyright Notices
***************************************
To make the DCO easier to digest, here are some practical guides about
specific cases:
Your own work
-------------
The simplest case is when the contribution is solely your own work. In
this case you can just send a Pull Request without worrying about any
licensing issues.
Using code from an online source
--------------------------------
If the code you would like to add is based on an article, post or
comment on a website (e.g. StackOverflow) the license and/or rules of
that site should be followed.
For example in case of StackOverflow a notice like this can be used:
.. code-block::
/* The original version of this code-snippet was published on StackOverflow.
* Post: http://stackoverflow.com/questions/12345
* Author: http://stackoverflow.com/users/12345/username
* The following parts of the snippet were changed:
* - Check this or that
* - Optimize performance here and there
*/
... code snippet here ...
Using MIT-licensed code
-----------------------
As LVGL is MIT licensed, other MIT licensed code can be integrated
without issues. The MIT license requires a copyright notice be added to
the derived work. Any derivative work based on MIT licensed code must
copy the original work's license file or text.
Use GPL-licensed code
---------------------
The GPL license is not compatible with the MIT license. Therefore, LVGL
cannot accept GPL licensed code.

View File

@@ -0,0 +1,14 @@
.. _contributing:
============
Contributing
============
.. toctree::
:maxdepth: 1
introduction
ways_to_contribute
pull_requests
dco
coding_style

View File

@@ -0,0 +1,18 @@
.. _contributing_intro:
============
Introduction
============
Join LVGL's community and leave your footprint in the library!
There are a lot of ways to contribute to LVGL even if you are new to the
library or even new to programming.
It might be scary to make the first step but you have nothing to be
afraid of. A friendly and helpful community is waiting for you. Get to
know like-minded people and make something great together.
So let's find which contribution option fits you the best and helps you
join the development of LVGL!

View File

@@ -0,0 +1,129 @@
.. _contributing_pull_requests:
=============
Pull Requests
=============
Merging new code into the ``lvgl/lvgl`` and other
repositories happens via *Pull Requests* (PR for short). A PR is a
notification like "Hey, I made some updates to your project. Here are
the changes, you can add them if you want." To do this you need a copy
(called fork) of the original project under your account, make some
changes there, and notify the original repository about your updates.
You can see what it looks like on GitHub for LVGL here:
https://github.com/lvgl/lvgl/pulls.
To add your changes you can edit files online on GitHub and send a new
Pull request from there (recommended for small changes) or add the
updates in your favorite editor/IDE and use ``git`` to publish the changes
(recommended for more complex updates).
From GitHub
***********
1. Navigate to the file you want to edit.
2. Click the Edit button in the top right-hand corner.
3. Add your changes to the file.
4. Add a commit message at the bottom of the page.
5. Click the *Propose changes* button.
From Your Local Workstation
***************************
These instructions describe the main ``lvgl`` repository but it works the
same way any remote Git repository.
1. Fork the `lvgl repository <https://github.com/lvgl/lvgl>`__. To do this click the
"Fork" button in the top right corner. It will "copy" the ``lvgl``
repository to your GitHub account (``https://github.com/<YOUR_NAME>?tab=repositories``)
2. Clone your forked repository.
3. Add your changes. You can create a *feature branch* from the ``master`` branch for the updates: ``git checkout -b <new-feature-branch-name>``
4. Commit and push your changes to your forked ``lvgl`` repository.
5. Create a PR on GitHub from the page of your forked ``lvgl`` repository (``https://github.com/<YOUR_NAME>/lvgl``) by
clicking the *"New pull request"* button. Don't forget to select the branch where you added your changes.
6. Set the base branch where you want to merge your update. In the ``lvgl`` repo both fixes
and new features should be directed to the ``master`` branch.
7. Describe what is in the update. Example code is welcome if applicable.
8. If you need to make more changes, just update your forked ``lvgl`` repo with new commits.
They will automatically appear in the PR.
.. _contributing_commit_message_format:
Commit Message Format
*********************
The commit messages format is inspired by `Angular Commit
Format <https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit>`__.
The following structure should be used:
.. code-block:: text
<type>(<scope>): <subject>
<--- blank line
<body>
<--- blank line
<footer>
Possible ``<type>``\ s:
- ``fix`` bugfix in LVGL source code
- ``feat`` new feature
- ``arch`` architectural changes
- ``perf`` changes that affect performance
- ``example`` anything related to examples (including fixes and new examples)
- ``docs`` anything related to documentation (including fixes, formatting, and new pages)
- ``test`` anything related to tests (new and updated tests or CI actions)
- ``chore`` any minor formatting or style changes that would make the changelog noisy
``<scope>`` is the name of the module, file, or subsystem affected by the
commit. It's usually one word and can be chosen freely. For example
``img``, ``layout``, ``txt``, ``anim``. The scope can be omitted.
``<subject>`` contains a short description of the change following these guidelines:
- use the imperative mood: e.g. present tense "change", not "changed" nor "changes";
- don't capitalize the first letter;
- no period (``.``) at the end;
- max 90 characters.
``<body>`` optional and can be used to describe the details of this
change.
``<footer>`` shall contain
- the words "BREAKING CHANGE" if the changes break the API
- reference to the GitHub issue or Pull Request if applicable.
(See `Linking a pull rquest to an issue <https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests#linking-a-pull-request-to-an-issue>`__
for details.)
Some examples:
.. code-block:: text
fix(image): update size when a new source is set
.. code-block:: text
fix(bar): fix memory leak
The animations weren't deleted in the destructor.
Fixes: #1234
.. code-block:: text
feat: add span widget
The span widget allows mixing different font sizes, colors and styles.
It's similar to HTML <span>
.. code-block:: text
docs(porting): fix typo

View File

@@ -0,0 +1,23 @@
.. _contributing_ways:
==================
Ways to Contribute
==================
- **Spread the Word**: Share your LVGL experience with friends or on social media to boost its visibility.
- **Star LVGL** Give us a star on `GitHub <https://github.com/lvgl/lvgl>`__! It helps a lot to make LVGL more appealing for newcomers.
- **Report a Bug**: Open a `GitHub Issue <https://github.com/lvgl/lvgl/issues>`__ if something is not working.
- **Join Our** `Forum <https://forum.lvgl.io/>`__ : Meet fellow developers and discuss questions.
- **Tell Us Your Ideas**: If you feel something is missing from LVGL, we would love to hear about it in a `GitHub Issue <https://github.com/lvgl/lvgl/issues>`__
- **Develop Features**: Help to design or develop a feature. See below.
Mid- and large-scale issues are discussed in `Feature Planning <https://github.com/lvgl/lvgl/issues/new?assignees=&labels=&projects=&template=feat-planning.yml>`__ issues.
An issue can be developed when all the questions in the issue template are answered and there are no objection from any core member.
We are using GitHub labels to show the state and attributes of the issues and Pull requests.
If you are looking for contribution opportunities you can `Filter for these labels <https://github.com/lvgl/lvgl/labels>`__ :
- ``Simple``: Good choice to get started with an LVGL contribution
- ``PR needed``: We investigated the issue but it still needs to be implemented
- ``Review needed``: A Pull request is opened and it needs review/testing

View File

@@ -0,0 +1,226 @@
.. _file_explorer:
=============
File Explorer
=============
``lv_file_explorer`` provides a UI enabling the end user to browse the contents of a
file system. Its main area is called the "Browsing Area" and provides the list of
files contained in the currently-viewed directory.
When enabled, there is also a "Quick-Access" panel on the left, which provides a
convenient way to reach parts of the file system that are frequently accessed.
Available "Quick-Access" destinations are:
- File System,
- HOME,
- Video,
- Pictures,
- Music, and
- Documents.
You specify what paths these lead to during ``lv_file_explorer``\ 's initialization.
``lv_file_explorer`` only provides the file browsing and events caused by user
activity (e.g. clicking a file), but does not provide the actual file operations.
Client code must hook various events and decide what to do when they are emitted
(e.g. a click or double-click on a file). The actions taken might to open the file,
display it, send it to some other part of the application, etc..
``lv_file_explorer`` passes the full path and name of file that was clicked to the
event callback functions. What happens next is up to the application designer.
``lv_file_explorer`` uses the :ref:`lv_table` Widget for the "Browsing Area", and the
:ref:`lv_list` Widget for the "Quick-Access" panel when it is enabled. Thus,
:c:macro:`LV_USE_TABLE` macro must be set to a non-zero value in ``lv_conf.h`` in
order to use ``lv_file_explorer``, and and :c:macro:`LV_USE_LIST` must be set to a
non-zero value to use the "Quick-Access" panel.
.. note::
In order to use File Explorer, :ref:`file_system` has to be set up and
know about all the drive letters you use when passing paths to File System
(described below).
Prerequisites
*************
If you haven't already done so, you will need to learn about the LVGL :ref:`File
System abstraction <file_system>`, since it must be set up and be functional
for File Explorer to work.
.. _file_explorer_usage:
Usage
*****
Set :c:macro:`LV_USE_FILE_EXPLORER` to a non-zero value in ``lv_conf.h``.
First use :cpp:expr:`lv_file_explorer_create(lv_screen_active())` to create a File
Explorer. The default size is the screen size. After that, you can
customize the style like any Widget.
The size of the ``current_path`` buffer is set by :c:macro:`LV_FILE_EXPLORER_PATH_MAX_LEN`
in ``lv_conf.h``.
The object hierarchy of a freshly-created File Explorer looks like this:
- ``File Explorer``: occupies full area of parent Widget, typically a Screen (Flex-Flow COLUMN)
- ``Container``: occupies full area of File Explorer (Flex grow 1)
- ``Quick-Access Panel``:
- ``Device List``: grows to accommodate children
- ``File System``: button
- ``Places List``: grows to accommodate children
- ``HOME``: button
- ``Video``: button
- ``Pictures``: button
- ``Music``: button
- ``Documents``: button
- ``Browser Panel``:
- ``Header``: 14% of ``Browser Panel`` height
- ``Current Path``: label
- ``File Table``: with 1 column, 86% of ``Browser Panel`` height
- Fields:
- ``home_dir`` = NULL
- ``video_dir`` = NULL
- ``pictures_dir`` = NULL
- ``music_dir`` = NULL
- ``docs_dir`` = NULL
- ``fs_dir`` = NULL
- ``current_path`` = [empty buffer]
- ``sel_fn`` (selected file)
- ``sort`` (default :cpp:enumerator:`LV_EXPLORER_SORT_NONE`)
Accessing the Parts
-------------------
This list of functions provides access to the parts shown in diagram above:
- :cpp:expr:`lv_file_explorer_get_selected_file_name(explorer)` (pointer
to NUL-terminated string containing file-path user selected; typically used inside
an :cpp:enumerator:`LV_EVENT_CLICKED` event)
- :cpp:expr:`lv_file_explorer_get_current_path(explorer)` (pointer to ``current_path`` ``char`` buffer)
- :cpp:expr:`lv_file_explorer_get_file_table(explorer)` (pointer to ``File Table`` :ref:`lv_table` Widget)
- :cpp:expr:`lv_file_explorer_get_header(explorer)` (pointer to ``Header`` :ref:`base_widget` Widget)
- :cpp:expr:`lv_file_explorer_get_path_label(explorer)` (pointer to ``Current Path Label`` :ref:`lv_label` Widget)
- :cpp:expr:`lv_file_explorer_get_quick_access_area(explorer)` (pointer to ``Quick-Access Panel`` :ref:`base_widget`)
- :cpp:expr:`lv_file_explorer_get_places_list(explorer)` (pointer to ``Places List`` :ref:`lv_list` Widget)
- :cpp:expr:`lv_file_explorer_get_device_list(explorer)` (pointer to ``Device List`` :ref:`lv_list` Widget)
Quick-Access Panel
------------------
The ``Quick-Access Panel`` behaves like a typical navigation panel and appears on the
left, while the ``Browser Panel`` appears on the right
This panel is optional. If you set :c:macro:`LV_FILE_EXPLORER_QUICK_ACCESS` to ``0``
in ``lv_conf.h``, the ``Quick-Access Panel`` will not be created. This saves only a
little bit of memory.
Soon after the File Explorer is created, you typically use
:cpp:expr:`lv_file_explorer_set_quick_access_path(explorer, LV_EXPLORER_XXX_DIR, "path")`
to set the path that will be navigated to when the buttons in the ``Quick-Access Panel``
are clicked, which is currently a fixed list. The corresponding values you will need
to pass as the 2nd argument are the following:
- :cpp:enumerator:`LV_EXPLORER_HOME_DIR`
- :cpp:enumerator:`LV_EXPLORER_MUSIC_DIR`
- :cpp:enumerator:`LV_EXPLORER_PICTURES_DIR`
- :cpp:enumerator:`LV_EXPLORER_VIDEO_DIR`
- :cpp:enumerator:`LV_EXPLORER_DOCS_DIR`
- :cpp:enumerator:`LV_EXPLORER_FS_DIR`
.. _file_explorer_sort:
Sort
----
You can use
:cpp:expr:`lv_file_explorer_set_sort(explorer, LV_EXPLORER_SORT_XX)` to set
the sorting method.
These are the possible sorting methods:
- :cpp:enumerator:`LV_EXPLORER_SORT_NONE` (default)
- :cpp:enumerator:`LV_EXPLORER_SORT_KIND`
:cpp:expr:`lv_file_explorer_get_sort(explorer)` returns the current sorting method.
.. _file_explorer_events:
Events
******
- :cpp:enumerator:`LV_EVENT_READY` Sent when a directory is opened, which can happen:
- when the File Explorer is initially opened,
- after a user clicks on a ``Quick-Access Panel`` navigation button, and
- after the user clicks on a directory displayed in the ``Browser Panel``.
You can use it to, for example, customize the file sort.
- :cpp:enumerator:`LV_EVENT_VALUE_CHANGED` Sent once when any item (file) in the
``Brwoser Panel``\ 's file list is clicked.
- :cpp:enumerator:`LV_EVENT_CLICKED` Sent twice when an item in the ``Browser Panel``
is clicked: once as a result of the input-device :cpp:enumerator:`LV_EVENT_RELEASED`
event and a second as a result of the input device :cpp:enumerator:`LV_EVENT_CLICKED`
event. This applies to files, directories, and the "< Back" item in the ``Browser Panel``.
In these events you can use :cpp:func:`lv_file_explorer_get_current_path` to get the
current path and :cpp:func:`lv_file_explorer_get_selected_file_name` to get the name
of the currently selected file in the event processing function. For example:
.. code-block:: c
static void file_explorer_event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_VALUE_CHANGED) {
char * cur_path = lv_file_explorer_get_current_path(widget);
char * sel_fn = lv_file_explorer_get_selected_file_name(widget);
LV_LOG_USER("%s%s", cur_path, sel_fn);
}
}
You can also save the obtained **path** and **file** name into an array
through functions such as :cpp:func:`strcpy` and :cpp:func:`strcat` for later use.
.. _file_explorer_example:
Example
*******
.. include:: ../../examples/others/file_explorer/index.rst
.. _file_explorer_api:
API
***

View File

@@ -0,0 +1,137 @@
.. _font_manager:
============
Font Manager
============
The font manager is a secondary encapsulation of the LVGL font engine,
which facilitates the use and management of fonts for applications.
The font management functions currently provided include:
- Font resource reference counting (reduces repeated creation of font resources).
- Font resource concatenation (font fallback).
- Font resource recycling mechanism (buffers recently deleted font resources to
reduce the time overhead of repeated creation).
.. _font_manager_usage:
Usage
*****
Enable FreeType and Font Manager in ``lv_conf.h`` by setting the
:c:macro:`LV_USE_FONT_MANAGER` macros to non-zero
values, and configure :c:macro:`LV_FONT_MANAGER_NAME_MAX_LEN` to set the maximum
length of the font name.
Initialize Font Manager
-----------------------
Use :cpp:func:`lv_font_manager_create` to create a font manager, where the
:cpp:func:`recycle_cache_size` parameter is used to set the number of font recycling
caches, which can improve font creation efficiency.
Use :cpp:func:`lv_font_manager_add_src_static` to add a mapping between font names
and font resources to tell the font manager how to access the font resources.
Note that if the font resource description structure is not statically allocated
(for example, allocated from a local variable), use :cpp:func:`lv_font_manager_add_src` to add the resource.
This function will copy the contents of the structure itself.
Use :cpp:func:`lv_font_manager_remove_src` to remove the font resource mapping.
It should be noted that the ``src`` parameter must strictly correspond to ``class_p``.
``class_p`` will affect the way the font manager interprets src. If an incompatible parameter is passed, the program may fail.
For currently supported font classes, please refer to the example code.
.. code-block:: c
static lv_font_manager_t * g_font_manager = NULL;
void font_manager_init(void)
{
/* Create font manager, with 8 fonts recycling buffers */
g_font_manager = lv_font_manager_create(8);
/* Add font path mapping to font manager */
lv_font_manager_add_src_static(g_font_manager, "Lato-Regular", "./lvgl/examples/libs/freetype/Lato-Regular.ttf", &lv_freetype_font_class);
char path[] = "/path/to/myfont.ttf";
lv_font_manager_add_src(g_font_manager, "MyFont", path, &lv_freetype_font_class);
}
Create Font from Font Manager
-----------------------------
The parameters will be passed to the font creation function of the font backend,
such as :cpp:func:`lv_freetype_font_create` and :cpp:func:`lv_tiny_ttf_create_file`.
The font backend will select the supported parameters by itself and ignore the unsupported parameters.
The ``font_family`` parameter can be filled with the names of multiple fonts
(separated by ``,``) to achieve font concatenation (when the corresponding glyph is
not found in a font file, it will automatically search from the next concatenated
font).
.. code-block:: c
static lv_font_t * g_font = NULL;
/* Create font from font manager */
lv_font_t * g_font = lv_font_manager_create_font(g_font_manager,
"Lato-Regular,MyFont",
LV_FREETYPE_FONT_RENDER_MODE_BITMAP,
24,
LV_FREETYPE_FONT_STYLE_NORMAL,
LV_FONT_KERNING_NONE);
/* Handle error case */
if(g_font == NULL) {
g_font = (lv_font_t *)LV_FONT_DEFAULT;
}
/* Create label with the font */
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_obj_set_style_text_font(label, g_font, 0);
lv_label_set_text(label, "Hello World!");
Delete Font
-----------
Use :cpp:func:`lv_font_manager_delete_font` to delete the font when it is no longer needed.
The font manager will mark the font resource as a recyclable font so that it has the
possibility of being more quickly created next time.
Note that you need to delete any Widgets that used the font first, and then
delete the font to avoid accessing wild pointers.
.. code-block:: c
/* Delete label and font */
lv_obj_delete(label);
lv_font_manager_delete_font(g_font_manager, g_font);
g_font = NULL;
Delete Font Manager
-------------------
Use :cpp:func:`lv_font_manager_delete` to destroy the entire font manager. It should
be noted that before destruction, it is necessary to ensure that the application has
deleted all fonts using :cpp:func:`lv_font_manager_delete_font`. The font manager
will check the reference status of all allocated fonts. If there are still fonts
being referenced, the font manager will fail to be destroyed and the function will return false.
.. _font_manager_example:
Example
*******
.. include:: ../../examples/others/font_manager/index.rst
.. _font_manager_api:
API
***

View File

@@ -0,0 +1,92 @@
.. _fragment:
========
Fragment
========
Fragment is a concept copied from
`Android <https://developer.android.com/guide/fragments>`__.
It represents a reusable portion of your app's UI. A fragment defines
and manages its own layout, has its own lifecycle, and can handle its
own events. Like Android's Fragment that must be hosted by an activity
or another fragment, Fragment in LVGL needs to be hosted by a Widget,
or another fragment. The fragment's view hierarchy becomes part of, or
attaches to, the host's view hierarchy.
Such concept also has some similarities to `UiViewController on
iOS <https://developer.apple.com/documentation/uikit/uiviewcontroller>`__.
Fragment Manager is a manager holding references to fragments attached
to it, and has an internal stack to achieve forward and backwards navigation. You can use
fragment manager to build a navigation stack, or a multi-pane application
easily.
.. _fragment_usage:
Usage
*****
Enable :c:macro:`LV_USE_FRAGMENT` in ``lv_conf.h``.
Create Fragment Class
---------------------
.. code-block:: c
struct sample_fragment_t {
/* IMPORTANT: don't miss this part */
lv_fragment_t base;
/* States, object references and data fields for this fragment */
const char *title;
};
const lv_fragment_class_t sample_cls = {
/* Initialize something needed */
.constructor_cb = sample_fragment_ctor,
/* Create view objects */
.create_obj_cb = sample_fragment_create_obj,
/* IMPORTANT: size of your fragment struct */
.instance_size = sizeof(struct sample_fragment_t),
};
Use ``lv_fragment_manager``
---------------------------
.. code-block:: c
/* Create fragment instance, and Widgets will be added to container */
lv_fragment_manager_t *manager = lv_fragment_manager_create(container, NULL);
/* Replace current fragment with instance of sample_cls, and init_argument is user defined pointer */
lv_fragment_manager_replace(manager, &sample_cls, init_argument);
Fragment Based Navigation
-------------------------
.. code-block:: c
/* Add one instance into manager stack. View object of current fragment will be destroyed,
* but instances created in class constructor will be kept.
*/
lv_fragment_manager_push(manager, &sample_cls, NULL);
/* Remove the top most fragment from the stack, and bring back previous one. */
lv_fragment_manager_pop(manager);
.. _fragment_example:
Example
*******
.. include:: ../../examples/others/fragment/index.rst
.. _fragment_api:
API
***

View File

@@ -0,0 +1,93 @@
.. _gridnav:
===============
Grid navigation
===============
Grid navigation (gridnav for short) is a feature that moves focus among a set
of child Widgets via arrow-key presses.
If the child Widgets are arranged into a grid-like layout then the up, down,
left and right arrows move focus to the nearest sibling in the
respective direction.
It doesn't matter how the children are positioned, as only the current x
and y coordinates are considered. This means that gridnav works with
manually positioned children, as well as :ref:`flex` and
:ref:`grid` layouts.
Gridnav also works if the children are arranged into a single row or
column. This is useful, for example, to simplify navigation among items in a
:ref:`List Widget <lv_list>`.
Gridnav assumes that the Widget to which gridnav is added is part of a
:ref:`group <indev_groups>`. This way, if the Widget with
gridnav has focus, the arrow key presses are automatically forwarded to
the Widget so that gridnav can process the arrow keys.
To move the focus to the next widget of the group use
:cpp:enumerator:`LV_KEY_NEXT` or :cpp:enumerator:`LV_KEY_PREV`.
Optionally you can also use :cpp:func:`lv_group_focus_next`
or :cpp:func:`lv_group_focus_prev` or the ``TAB``
key on keyboard as usual.
If the container is scrollable and the focused child is out of the view,
gridnav will automatically scroll the child into view.
.. _gridnav_usage:
Usage
*****
To add gridnav behavior to any Widget (e.g. one serving as a container for
child Widgets that the end user will navigate among using arrow keys) use
:cpp:expr:`lv_gridnav_add(container, flags)`.
The ``flags`` argument controls the navigation behavior:
- :cpp:enumerator:`LV_GRIDNAV_CTRL_NONE`: Default settings
- :cpp:enumerator:`LV_GRIDNAV_CTRL_ROLLOVER`: If there is no next/previous Widget in a
direction, the focus goes to the Widget in the next/previous row (on
left/right keys) or first/last row (on up/down keys)
- :cpp:enumerator:`LV_GRIDNAV_CTRL_SCROLL_FIRST`: If an arrow is pressed and the focused
Widget can be scrolled in that direction then it will be scrolled instead of
going to the next/previous Widget. If there is no more room for scrolling the
next/previous Widget will receive focus normally.
- :cpp:enumerator:`LV_GRIDNAV_CTRL_HORIZONTAL_MOVE_ONLY`: Only use the left/right keys
for grid navigation. Up/down key events will be sent to the Widget that has focus.
- :cpp:enumerator:`LV_GRIDNAV_CTRL_VERTICAL_MOVE_ONLY`: Only use the up/down keys
for grid navigation. Left/right key events will be sent to the Widget that has focus.
While the above behaviors can be combined by bit-wise OR-ing the above values together,
:cpp:enumerator:`LV_GRIDNAV_CTRL_HORIZONTAL_MOVE_ONLY` and :cpp:enumerator:`LV_GRIDNAV_CTRL_VERTICAL_MOVE_ONLY`
should not be used together.
:cpp:expr:`lv_gridnav_remove(container)` Removes gridnav behavior from a Widget.
Focusable Widgets
*****************
A Widget needs to be clickable or click focusable
(:cpp:enumerator:`LV_OBJ_FLAG_CLICKABLE` or :cpp:enumerator:`LV_OBJ_FLAG_CLICK_FOCUSABLE`) and not
hidden (:cpp:enumerator:`LV_OBJ_FLAG_HIDDEN`) to receive focus via gridnav.
.. _gridnav_example:
Examples
********
.. include:: ../../examples/others/gridnav/index.rst
.. _gridnav_api:
API
***

View File

@@ -0,0 +1,132 @@
.. _ime_pinyin:
==========
Pinyin IME
==========
Pinyin IME provides an API to provide Chinese Pinyin input method (Chinese
input) for a Keyboard Widget, which supports both 26-key and 9-key input modes.
You can think of ``lv_ime_pinyin`` as a Pinyin input method plug-in for
the Keyboard Widget.
Normally, an environment where :ref:`lv_keyboard` can
run can also run ``lv_ime_pinyin``. There are two main influencing
factors: the size of the font file and the size of the dictionary.
.. _ime_pinyin_usage:
Usage
*****
Enable :c:macro:`LV_USE_IME_PINYIN` in ``lv_conf.h``.
First use :cpp:expr:`lv_ime_pinyin_create(lv_screen_active())` to create a Pinyin
input-method plug-in, then use
:cpp:expr:`lv_ime_pinyin_set_keyboard(pinyin_ime, kb)` to add the Keyboard Widget
you created to the Pinyin input method plug-in. You can use
:cpp:expr:`lv_ime_pinyin_set_dict(pinyin_ime, your_dict)` to use a custom
dictionary. If you don't want to use the built-in dictionary,
you can disable :c:macro:`LV_IME_PINYIN_USE_DEFAULT_DICT` in ``lv_conf.h``,
which can save a lot of memory space.
The built-in thesaurus is customized based on the
**LV_FONT_SOURCE_HAN_SANS_SC_16_CJK** font library, which currently has more
than 1,000 of the most common CJK radicals, so it is recommended to use a
custom font and thesaurus.
In the process of using the Pinyin input method plug-in, you can change
the Keyboard and dictionary at any time.
Custom Dictionary
*****************
If you don't want to use the built-in Pinyin dictionary, or if you feel that the
built-in phonetic dictionary consumes too much memory, you can use a custom dictionary.
Customizing the dictionary is very simple.
First, set :c:macro:`LV_IME_PINYIN_USE_DEFAULT_DICT` to ``0`` in ``lv_conf.h``
Then, write a dictionary in the following format.
Dictionary format
-----------------
The arrangement order of each pinyin syllable is very important. If you
need to customize your own thesaurus according to the Hanyu Pinyin
syllable table, you can read
`here <https://baike.baidu.com/item/%E6%B1%89%E8%AF%AD%E6%8B%BC%E9%9F%B3%E9%9F%B3%E8%8A%82/9167981>`__
to learn about the Hanyu Pinyin syllables and the syllable table.
Then, write your own dictionary according to the following format:
.. code-block:: c
lv_100ask_pinyin_dict_t your_pinyin_dict[] = {
{ "a", "啊阿呵吖" },
{ "ai", "埃挨哎唉哀皑蔼矮碍爱隘癌艾" },
{ "an", "按安暗岸俺案鞍氨胺厂广庵揞犴铵桉谙鹌埯黯" },
{ "ang", "昂肮盎仰" },
{ "ao", "凹敖熬翱袄傲奥懊澳" },
{ "ba", "芭捌叭吧笆八疤巴拔跋靶把坝霸罢爸扒耙" },
{ "bai", "白摆佰败拜柏百稗伯" },
/* ...... */
{ "zuo", "昨左佐做作坐座撮琢柞"},
{NULL, NULL}
**The last item** must be ``{null, null}``, or it will not work
properly.
.. _ime_pinyin_apply_new_dictionary:
Applying a new dictionary
-------------------------
After writing a dictionary according to the above dictionary format, you
only need to call this function to set up and use your dictionary:
.. code-block:: c
lv_obj_t * pinyin_ime = lv_100ask_pinyin_ime_create(lv_screen_active());
lv_100ask_pinyin_ime_set_dict(pinyin_ime, your_pinyin_dict);
.. _ime_pinyin_modes:
Modes
*****
lv_ime_pinyin has the following modes:
- :cpp:enumerator:`LV_IME_PINYIN_MODE_K26`: Pinyin 26-key input mode
- :cpp:enumerator:`LV_IME_PINYIN_MODE_K9`: Pinyin 9-key input mode
- :cpp:enumerator:`LV_IME_PINYIN_MODE_K9_NUMBER`: Numeric keypad mode
The Keyboard's ``TEXT``-mode layout contains buttons to change mode.
To set the mode manually, use :cpp:expr:`lv_ime_pinyin_set_mode(pinyin_ime, mode)`.
The default mode is :cpp:enumerator:`LV_IME_PINYIN_MODE_K26`.
.. _ime_pinyin_example:
Example
*******
.. include:: ../../examples/others/ime/index.rst
.. _ime_pinyin_api:
API
***

View File

@@ -0,0 +1,53 @@
.. _lv_imgfont:
==========
Image font
==========
Draw image in **label** or **span** obj with :cpp:type:`lv_imgfont`. This is often used to
display Unicode emoji icons in text.
Supported image formats: determined by enabled LVGL :ref:`image decoders <overview_image_decoder>`.
.. _lv_imgfont_usage:
Usage
*****
Enable :c:macro:`LV_USE_IMGFONT` in ``lv_conf.h``.
To create a new *imgfont*:
.. code-block:: c
static lv_font_t * imgfont;
...
imgfont = lv_imgfont_create(height, path_cb, user_data);
- ``height`` Font size.
- ``path_cb`` A function to get the image path of a character.
Pass ``NULL`` if no image should be shown, but the character itself.
- ``user_data`` Pointer to user data.
To use the *imgfont* in a label, reference it:
:cpp:expr:`lv_obj_set_style_text_font(label, imgfont, LV_PART_MAIN)`
To destroy the *imgfont* that is no longer used, use :cpp:expr:`lv_imgfont_destroy(imgfont)`.
.. _lv_imgfont_example:
Example
*******
.. include:: ../../examples/others/imgfont/index.rst
.. _lv_imgfont_api:
API
***

View File

@@ -0,0 +1,22 @@
.. _auxiliary_modules:
=================
Auxiliary Modules
=================
.. toctree::
:maxdepth: 2
file_explorer
font_manager
fragment
gridnav
ime_pinyin
imgfont
monkey
obj_id
obj_property
observer/index
snapshot
test
xml/index

View File

@@ -0,0 +1,62 @@
.. _monkey:
======
Monkey
======
The Monkey module provides LVGL applications with a simple monkey test. Monkey
Testing is a technique where the user tests the application or system by providing
random inputs and checking the behavior or seeing whether the aplication or system
will crash. This module provides this service as simulated random input to stress
test an LVGL application.
.. _monkey_usage:
Usage
*****
First, enable :c:macro:`LV_USE_MONKEY` in ``lv_conf.h``.
Next, declare a variable (it can be local) of type :c:type:`lv_monkey_config_t` to
define the configuration structure, initialize it using
:cpp:expr:`lv_monkey_config_init(cfg)` then set its ``type`` member to the desired
type of :ref:`input device <indev>`, and set the ``min`` and ``max`` values for its
``period_range`` and ``input_range`` members to set the time ranges (in milliseconds)
and input ranges the Monkey module will use to generate random input at random times.
Next, call :cpp:expr:`lv_monkey_create(cfg)` to create the Monkey. It returns a
pointer to the ``lv_monkey_t`` created.
Finally call :cpp:expr:`lv_monkey_set_enable(monkey, true)` to enable Monkey.
If you want to pause the monkey, call
:cpp:expr:`lv_monkey_set_enable(monkey, false)`. To delete it, call
:cpp:expr:`lv_monkey_delete(monkey)`.
Note that ``input_range`` has different meanings depending on the ``type`` input device:
- :cpp:enumerator:`LV_INDEV_TYPE_POINTER`: No effect, click randomly within the pixels of the screen resolution.
- :cpp:enumerator:`LV_INDEV_TYPE_ENCODER`: The minimum and maximum values of ``enc_diff``.
- :cpp:enumerator:`LV_INDEV_TYPE_BUTTON`: The minimum and maximum values of ``btn_id``.
Use :cpp:func:`lv_monkey_get_indev` to get the input device, and use
:cpp:func:`lv_indev_set_button_points` to map the key ID to the coordinates.
- :cpp:enumerator:`LV_INDEV_TYPE_KEYPAD`: No effect, Send random :ref:`indev_keys`.
.. _monkey_example:
Example
*******
.. include:: ../../examples/others/monkey/index.rst
.. _monkey_api:
API
***

View File

@@ -0,0 +1,197 @@
.. _widget_id:
=========
Widget ID
=========
Widgets can optionally have identifiers added to their functionality if needed for
the application. Exactly how that happens is designed to be flexible, and can morph
with the needs of the application. It can even be a timestamp or other data current
at the time the Widget was created.
.. _widget_id_usage:
Usage
*****
Enable Widget ID functionality by setting :c:macro:`LV_USE_OBJ_ID` to ``1`` in ``lv_conf.h``.
Once enabled, several things change:
- each Widget will now have a ``void *`` field called ``id``;
- these two API functions become available:
- :cpp:expr:`lv_obj_get_id(widget)`,
- :cpp:expr:`lv_obj_find_by_id(widget, id)`;
- several more Widget-ID-related API functions become available if
:c:macro:`LV_USE_OBJ_ID_BUILTIN` is non-zero (more on this below);
- two additional configuration macros both :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` and
:c:macro:`LV_USE_OBJ_ID_BUILTIN` now have meaning.
:c:macro:`LV_OBJ_ID_AUTO_ASSIGN`
--------------------------------
This macro in ``lv_conf.h`` defaults to whatever value :c:macro:`LV_USE_OBJ_ID`
equates to. You can change this if you wish. Either way, if it equates to a
non-zero value, it causes two things to happen:
- :cpp:expr:`lv_obj_assign_id(class_p, widget)` will be called at the end of each
Widget's creation, and
- :cpp:expr:`lv_obj_free_id(widget)` will be called at the end of the sequence when
each Widget is deleted.
Because of this timing, custom versions of these functions can be used according to
the below, and they can even be used like "event hooks" to implement a trace
operation that occurs when each Widget is created and deleted.
:cpp:expr:`lv_obj_assign_id(class_p, widget)`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This function (whether provided by LVGL or by you --- more on this below) is
responsible for assigning a value to the Widget's ``id`` field, and possibly do
other things, depending on the implementation.
:cpp:expr:`lv_obj_free_id(widget)`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This function (whether provided by LVGL or by you --- more on this below) is
responsible for doing the clean-up of any resources allocated by
:cpp:func:`lv_obj_assign_id()`
:c:macro:`LV_USE_OBJ_ID_BUILTIN`
--------------------------------
This macro in ``lv_conf.h`` equates to ``1`` by default. You can change this if you
wish. When it equates to a non-zero value the following function implementations are
provided by LVGL:
- :cpp:expr:`lv_obj_assign_id(class_p, widget)`
- :cpp:expr:`lv_obj_free_id(widget)`
- :cpp:expr:`lv_obj_set_id(widget, id)`
- :cpp:expr:`lv_obj_stringify_id(widget, buf, len)`
- :cpp:expr:`lv_obj_id_compare(id1, id2)`
These supply the default implementation for Widget IDs, namely that for each Widget
created, :cpp:expr:`lv_obj_stringify_id(widget, buf, len)` will produce a unique
string for it. Example: if the following 6 Widgets are created in this sequence:
- Screen
- Label
- Button
- Label
- Label
- Image
the strings produced by :cpp:expr:`lv_obj_stringify_id(widget, buf, len)` would be
- obj1
- label1
- btn1
- label2
- label3
- image1
respectively.
.. _widget_id_custom_generator:
Using a custom ID generator
---------------------------
If you wish, you can provide custom implementations for several Widget-ID related
functions. You do this by first setting :c:macro:`LV_USE_OBJ_ID_BUILTIN` to `0` in
``lv_conf.h``.
You will then need to provide implementations for the following functions (and link
them with LVGL):
.. code-block:: c
const char * lv_obj_stringify_id(lv_obj_t * widget, char * buf, uint32_t len);
int lv_obj_id_compare(const void * id1, const void * id2);
If :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` equates to a non-zero value (or if you otherwise
simply need to use them), you will also need to provide implementations for:
.. code-block:: c
void lv_obj_assign_id(const lv_obj_class_t * class_p, lv_obj_t * widget);
void lv_obj_free_id(lv_obj_t * widget);
If :c:macro:`LV_BUILD_TEST` equates to a non-zero value and you are including LVGL
test code in your compile (or if you otherwise simply need to use them), you
will also need to provide an implementation for:
.. code-block:: c
void lv_obj_set_id(lv_obj_t * widget, void * id);
Examples of implementations of these functions exist in ``lv_obj_id_builtin.c``, but
you are free to use a different design if needed.
:cpp:func:`lv_obj_stringify_id` converts the passed ``widget`` to a string
representation (typically incorporating the ``id`` field) and writes it into the
buffer provided in its ``buf`` argument.
:cpp:func:`lv_obj_id_compare` compares 2 ``void * id`` values and returns ``0`` when
they are considered equal, and non-zero otherwise.
If :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` equates to a non-zero value,
:cpp:func:`lv_obj_assign_id` is called when a Widget is created. It is responsible
for assigning a value to the Widget's ``id`` field. A pointer to the Widget's final
class is passed in its ``class_p`` argument in case it is needed for determining the
value for the ``id`` field, or for other possible needs related to your design for
Widget IDs. Note that this pointer may be different than :cpp:expr:`widget->class_p`
which is the class of the Widget currently being created.
If :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` equates to a non-zero value,
:cpp:func:`lv_obj_free_id` is called when a Widget is deleted. It needs to perform
the clean-up for any resources allocated by :cpp:func:`lv_obj_assign_id`.
Dumping a Widget Tree
---------------------
Regardless of the state of any of the above macros, the function
:cpp:expr:`lv_obj_dump_tree(widget)` provides a "dump" of the Widget Tree for the
specified Widget (that Widget plus all its children recursively) using the
currently-configured method used by the :c:macro:`LV_LOG_USER` macro. If NULL is
passed instead of a pointer to a "root" Widget, the dump will include the Widget Tree
for all :ref:`Screens`, for all :ref:`Displays <display>` in the system.
For :c:macro:`LV_LOG_USER` to produce output, the following needs to be true in
``lv_conf.h``:
- :c:macro:`LV_USE_LOG` must equate to a non-zero value
- :c:macro:`LV_LOG_LEVEL` <= :c:macro:`LV_LOG_LEVEL_USER`
It will recursively walk through all that Widget's children (starting with the Widget
itself) and print the Widget's parent's address, the Widget's address, and if
:c:macro:`LV_USE_OBJ_ID` equates to a non-zero value, will also print the output of
:cpp:func:`lv_obj_stringify_id` for that Widget.
This can be useful in the event of a UI crash. From that log you can examine the
state of the Widget Tree when :cpp:expr:`lv_obj_dump_tree(widget)` was called.
For example, if a pointer to a deleted Widget is stored in a Timer's
:cpp:expr:`timer->user_data` field when the timer event callback is called, attempted
use of that pointer will likely cause a crash because the pointer is not valid any
more. However, a timely dump of the Widget Tree right before that point will show
that the Widget no longer exists.
Find child by ID
----------------
.. warning::
``lv_obj_find_by_id(widget, id)`` is deprecated. To find a widget use ``obj_name``.
:cpp:expr:`lv_obj_find_by_id(widget, id)` will perform a recursive walk through
``widget``\ 's children and return the first child encountered having the given ID.

View File

@@ -0,0 +1,278 @@
.. _widget_property:
=================
Widget Properties
=================
Widget Properties provides a way to greatly reduce the size of the interface between
LVGL and whatever logic layer is just above it, to get and set the most important
properties of Widgets. It's intended use is to:
- simplify (decreasing development time) writing bindings for LVGL in another
language, such as:
- Micropython,
- Lua,
- Python,
- Perl,
- .NET
- make it possible to control the UI (or parts of it, e.g. animation) via external
input, without modifying firmware, such as:
- an external text file (YAML, JSON, XML, custom)
- any external input source (e.g. serial)
While using it consumes more program space and more CPU overhead while setting and
getting Widget properties, it is designed so minimize that additional CPU overhead.
What is a Widget Property?
**************************
A Widget's properties are the combined set of :ref:`styles` plus additional properties
that are unique to each type of Widget, that determine what the Widget looks like and
how it behaves. Examples: size, position, color, are properties of all Widgets
whereas text, long-mode, selection-start, and selection-end, are properties unique to
Label Widgets. A Widget's :ref:`local styles <style_local>` are also valid
properties in this context.
The non-style Widget properties available for a given Widget are implemented at the
top of that Widget's primary ``.c`` file as a ``const`` id-to-function-pointer lookup
array, like this example for the Label Widget:
.. code:: c
#if LV_USE_OBJ_PROPERTY
static const lv_property_ops_t properties[] = {
{
.id = LV_PROPERTY_LABEL_TEXT,
.setter = lv_label_set_text,
.getter = lv_label_get_text,
},
{
.id = LV_PROPERTY_LABEL_LONG_MODE,
.setter = lv_label_set_long_mode,
.getter = lv_label_get_long_mode,
},
{
.id = LV_PROPERTY_LABEL_TEXT_SELECTION_START,
.setter = lv_label_set_text_selection_start,
.getter = lv_label_get_text_selection_start,
},
{
.id = LV_PROPERTY_LABEL_TEXT_SELECTION_END,
.setter = lv_label_set_text_selection_end,
.getter = lv_label_get_text_selection_end,
},
};
#endif
This array is attached to the ``properties`` field of the Widget's class, so all
Widgets of the same type share the same id-to-function-pointer lookup array.
Some properties are read-only. When this is the case, only the ``getter`` field in
the corresponding array element will be initialized with a function pointer.
Example: an object's child-Widget count or scroll position must be controlled via
other types of input, but their values are readable through this API.
.. _widget_property_usage:
Usage
*****
By default, this feature of LVGL is turned off. It can be turned on by configuring
:c:macro:`LV_USE_OBJ_PROPERTY` to ``1`` in ``lv_conf.h``.
The 3 functions that then become available are:
- :cpp:type:`lv_result_t` :cpp:expr:`lv_obj_set_property(widget, lv_property_t * value)`
Sets specified property of Widget.
- :cpp:type:`lv_property_t` :cpp:expr:`lv_obj_get_property(widget, lv_prop_id_t id)`
Reads property value from Widget.
- :cpp:type:`lv_result_t` :cpp:expr:`lv_obj_set_properties(widget, lv_property_t * values, count)`
Sets multiple Widget properties from an array of :cpp:type:`lv_property_t`.
An ``lv_prop_id_t`` is a :ref:`widget_property_id`, whereas an ``lv_property_t`` is a
struct that pairs a :ref:`widget_property_id` with a :ref:`widget_property_value`.
The following is an example of an array that could be used as the ``values`` argument
in :cpp:func:`lv_obj_set_properties`:
.. code-block:: c
lv_property_t values[] = {
{ .id = LV_PROPERTY_IMAGE_SRC, .ptr = &img_demo_widgets_avatar, },
{ .id = LV_PROPERTY_IMAGE_PIVOT, .ptr = &pivot_50, },
{ .id = LV_PROPERTY_IMAGE_SCALE, .num = 128, },
{ .id = LV_PROPERTY_OBJ_FLAG_CLICKABLE, .num = 1, },
{ .id = LV_STYLE_IMAGE_OPA, .num = 128, },
{ .id = LV_STYLE_BG_COLOR, .color = (lv_color_t){.red = 0x11, .green = 0x22, .blue = 0x33}, },
}
Alternately, :cpp:expr:`lv_obj_set_property(widget, value)` could be called using
this array's individual ``value`` elements inside a loop.
.. _widget_property_id:
Property ID
-----------
:cpp:type:`lv_prop_id_t` identifies which property to get/set. It is an enum value
defined in the primary ``.h`` file for the Widget in question. Because the actual
names are "assembled" by a preprocessor string-concatenation macro and are thus
hard to visualize, you can also find the names in the Widget's primary ``.c`` file in
the ``properties[]`` array initializing the ``.id`` fields in the array. For example,
``LV_PROPERTY_LABEL_TEXT`` is one found in ``lv_label.c``, and the properties
available to all Widgets are found near the top of the ``lv_obj.c`` file.
That array is attached to the Widget's class, enabling "getter" and "setter" functions
to be looked up for each type of Widget where Widget properties has been implemented.
(Note: this is done internally so you don't have to.)
If the property you need to set or get using this API is not implemented yet, you can
add your own Widget property ID following same rules and using one of two helper
macros in the ``enum`` in the Widget's primary ``.h`` file. In both cases, the
"assembled" value is a 32-bit value:
- :c:macro:`LV_PROPERTY_ID` (for single values -- see :ref:`Single Values` below)`;
bits ``<31:28>`` contain the property's value type and bits ``<27:0>`` contain the
property ID.
- :c:macro:`LV_PROPERTY_ID2` (for paired values -- see :ref:`Paired Values` below)`;
bits ``<31:28>`` contain the type for the property's 1st value, bits ``<27:24>``
contain the type for the 2nd value, and bits ``<23:0>`` contain the property ID.
Just make sure the ID is unique across all Widgets.
Note that :cpp:type:`lv_style_prop_t` (enumerator values beginning with ``LV_PROPERTY_STYLE_...``)
are also valid property IDs, and can be used to set or get a Widget's style values.
.. _widget_property_value:
Property Value
--------------
:cpp:type:`lv_property_t` is a struct that begins with an ``id`` field whose meaning
is the same as property ID described above, paired with a value, which is a union of
all possible property types including integer, pointer and color. The ``value``
field is also capable of carrying the different value types for styles. It does this
by being a union of all the different types that might be needed. The list of
"union-ed" fields at this writing are:
.. _single values:
Single Values
~~~~~~~~~~~~~
.. code-block:: c
int32_t num; /**< Signed integer number (enums or "normal" numbers) */
uint32_t num_u; /**< Unsigned integer number (opacity, Booleans) */
bool enable; /**< Booleans */
const void * ptr; /**< Constant pointers (font, cone text, etc.) */
lv_color_t color; /**< Colors */
lv_value_precise_t precise; /**< float or int for precise value */
lv_point_t point; /**< Point, contains two int32_t */
struct {
/**
* Note that place struct member `style` at first place is intended.
* `style` shares same memory with `num`, `ptr`, `color`.
* So we set the style value directly without using `prop.style.num`.
*
* E.g.
*
* static const lv_property_t obj_pos_x = {
* .id = LV_PROPERTY_STYLE_X,
* .num = 123,
* .selector = LV_STATE_PRESSED,
* }
*
* instead of:
* static const lv_property_t obj_pos_x = {
* .id = LV_PROPERTY_STYLE_X,
* .style.num = 123, // note this line.
* .selector = LV_STATE_PRESSED,
* }
*/
lv_style_value_t style; /**< Make sure it's the first element in struct. */
uint32_t selector; /**< Style selector, lv_part_t | lv_state_t */
};
.. _paired values:
Paired Values
~~~~~~~~~~~~~
.. code-block:: c
/**
* For some properties like slider range, it contains two simple (4-byte) values
* so we can use `arg1.num` and `arg2.num` to set the argument.
*/
struct {
union {
int32_t num;
uint32_t num_u;
bool enable;
const void * ptr;
lv_color_t color;
lv_value_precise_t precise;
} arg1, arg2;
};
You can find the current :cpp:type:`lv_property_t` struct in the
`lv_obj_property.h <https://github.com/lvgl/lvgl/blob/master/src/core/lv_obj_property.h>`__ file.
Property ID Lookup by Name
--------------------------
Setting configuration macro :c:macro:`LV_USE_OBJ_PROPERTY_NAME` to ``1`` enables the
following functions to look up property IDs by passing property name (a string):
- :cpp:type:`lv_prop_id_t` :cpp:expr:`lv_obj_property_get_id(widget, name)`
Gets property ID by recursively searching for ``name`` in Widget's class hierarchy,
and if still not found, then searches style properties.
- :cpp:type:`lv_prop_id_t` :cpp:expr:`lv_obj_class_property_get_id(class_p, name)`
Gets property ID by doing a non-recursive search for ``name`` directly in Widget
class properties.
- :cpp:type:`lv_prop_id_t` :cpp:expr:`lv_style_property_get_id(name)`
Gets style property ID by name.
The latter two functions are useful when you already know ``name`` is among the
properties of a specific Widget class, or is a style name, since a property name may
exist in both lists. Because of the search sequence in
:cpp:expr:`lv_obj_property_get_id(widget, name)`, if a name does exist in both lists,
then using this function forces the name in the Widget's class hierarchy properties
to have precedence over the style name.
You can tell which names are available by looking in the ``.c`` files in the
``./src/widgets/property/`` directory. Note that to support binary name searches,
these arrays are generated so that they are guaranteed to be in alphabetical order.
If you need to add a property that is not present, it is recommended to add it in the
``enum`` near the top of the Widget's primary ``.h`` file, and re-generate these
lists using ``./scripts/properties.py`` to ensure alphabetical ordering is preserved.
Additional Notes
****************
For the ``lv_property_t * value`` argument of the :cpp:func:`lv_obj_set_property`
function, the language used to call that function (e.g. in a static or
dynamically-loaded library) may need additional code to convert values from their
local data type (e.g. dict, table, etc.) to a C struct before passing it to the
:cpp:func:`lv_obj_set_property` function.
API
***

View File

@@ -0,0 +1,11 @@
.. _observer:
========
Observer
========
.. toctree::
:maxdepth: 2
observer
observer_examples

View File

@@ -0,0 +1,546 @@
.. _observer_how_to_use:
==========
How to Use
==========
.. _observer_overview:
Overview
********
.. _observer pattern: https://en.wikipedia.org/wiki/Observer_pattern
The ``lv_observer`` module is an implemention of the `Observer Pattern`_.
This implementation consists of:
:Subjects: (in global memory or heap) are "logic packages", each containing the
value being "observed" and its type (integer (``int32_t``), a string, a
pointer, an :cpp:type:`lv_color_t`, or a group);
:Observers: (zero or more per Subject, always dynamically-allocated) are always
attached to exactly one Subject, and provide user-defined notifications
each the time Subject's value changes.
A Subject and its Observers can be used in various ways:
1. Simply subscribe to a Subject and get notified when the Subject's value changes.
2. Subscribe to a group Subject (connects a group of Subjects) to get notified when
any of the Subjects' values change in the group.
3. Bind Widgets to Subjects to automatically match the Widget's value with the
Subject (e.g. a Label's text or an Arc's value).
.. _observer_usage:
Usage
*****
Using Observer first requires :c:macro:`LV_USE_OBSERVER` be configured to ``1``.
(It is ``1`` by default, and can be set to ``0`` to save some program space if you
will not be using Observer.)
A typical use case looks like this:
.. code-block:: c
// Any typical global variable
lv_subject_t my_subject;
/*-------
* main.c
*-------*/
extern lv_subject_t my_subject;
void main(void)
{
// Initialize Subject as integer with the default value of 10.
lv_subject_init_int(&my_subject, 10);
some_module_init();
}
/*--------------
* some_module.c
*--------------*/
extern lv_subject_t some_subject;
// Will be called when Subject's value changes
static void some_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
int32_t v = lv_subject_get_int(subject);
do_something(v);
}
void some_module_init(void)
{
// Subscribe to Subject as an Observer.
lv_subject_add_observer(&some_subject, some_observer_cb, NULL);
}
/*--------------
* some_system.c
*--------------*/
extern lv_subject_t some_subject;
void some_event(void)
{
// The below call sets Subject's value to 30 and notifies current Observers.
lv_subject_set_int(&some_subject, 30);
}
.. _observer_subject:
Subject
-------
Subject Initialization
~~~~~~~~~~~~~~~~~~~~~~
Subjects have to be static or global variables, or dynamically-allocated
:cpp:type:`lv_subject_t` objects. Reason: their content must remain valid through
the life of the Subject.
To initialize a Subject use ``lv_subject_init_<type>(&subject, params, init_value)``.
The following initialization functions exist, one for each of the Subject types:
:Integer: void :cpp:expr:`lv_subject_init_int(subject, int_value)`
:String: void :cpp:expr:`lv_subject_init_string(subject, buf, prev_buf, buf_size, initial_string)`
:Pointer: void :cpp:expr:`lv_subject_init_pointer(subject, ptr)`
:Color: void :cpp:expr:`lv_subject_init_color(subject, color)`
:Group: void :cpp:expr:`lv_subject_init_group(group_subject, subject_list[], count)`
Setting a Subject's Value
~~~~~~~~~~~~~~~~~~~~~~~~~
The following functions are used to update a Subject's value:
:Integer: void :cpp:expr:`lv_subject_set_int(subject, int_value)`
:String: void :cpp:expr:`lv_subject_copy_string(subject, buf)`
:Pointer: void :cpp:expr:`lv_subject_set_pointer(subject, ptr)`
:Color: void :cpp:expr:`lv_subject_set_color(subject, color)`
At the end of each of these calls, if the new value differs from the previous value,
a notification is sent to all current Observers.
Getting a Subject's Value
~~~~~~~~~~~~~~~~~~~~~~~~~
The following functions are used to get a Subject's current value:
:Integer: int32_t :cpp:expr:`lv_subject_get_int(subject)`
:String: const char * :cpp:expr:`lv_subject_get_string(subject)`
:Pointer: const void * :cpp:expr:`lv_subject_get_pointer(subject)`
:Color: lv_color_t :cpp:expr:`lv_subject_get_color(subject)`
Getting a Subject's Previous Value
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following functions are used to get a Subject's previous value:
:Integer: int32_t :cpp:expr:`lv_subject_get_previous_int(subject)`
:String: const char * :cpp:expr:`lv_subject_get_previous_string(subject)`
:Pointer: const void * :cpp:expr:`lv_subject_get_previous_pointer(subject)`
:Color: lv_color_t :cpp:expr:`lv_subject_get_previous_color(subject)`
.. _observer_observer:
Observer
--------
Subscribing to a Subject
~~~~~~~~~~~~~~~~~~~~~~~~
The action of subscribing to a Subject:
- dynamically allocates an Observer object,
- attaches it to the Subject,
- performs an initial notification to the Observer (allowing the Observer to
update itself with the Subject's current value), and
- returns a pointer to the newly-created Observer.
Thereafter the Observer will receive a notification each time the Subject's value
changes, as long as that Observer remains attached (subscribed) to that Subject.
Notifications are performed by calling the callback function provided when
subscribing to the Subject.
Simple Subscription
~~~~~~~~~~~~~~~~~~~
To subscribe to a Subject one of the ``lv_subject_add_observer...()`` functions are
used. Alternately, if you want to bind a Subject's value to a Widget's property, one
of the ``lv_<widget_type>_bind_...()`` functions can be used. The former are covered
below. The latter are covered in the :ref:`observer_widget_binding` section.
For the most basic use case, subscribe to a Subject by using the following function:
lv_observer_t * observer = :cpp:expr:`lv_subject_add_observer(&some_subject, some_observer_cb, user_data)`
where the Observer's notification callback should look like this:
.. code-block:: c
static void some_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
...
}
This function returns a pointer to the newly-created Observer.
When using this method of subscribing, it is the responsibility of the user to call
:cpp:expr:`lv_observer_remove(observer)` when the Observer is no longer needed, which
both unsubscribes it from the Subject and deletes it from the LVGL heap.
Subscribing While Associating Observer with a Non-Widget Object
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The function subscribes to a Subject additionally associates the Observer with a
pointer to any type of object, a copy of which is saved in the Observer's ``target``
field. This function should be used when the pointer *does not* point to a Widget.
lv_observer_t * observer = :cpp:expr:`lv_subject_add_observer_with_target(&some_subject, some_observer_cb, some_pointer, user_data)`
A copy of the passed pointer can be retrieved by calling
:cpp:expr:`lv_observer_get_target(observer)`, e.g. inside the callback function.
When using this method of subscribing, it is the responsibility of the user to call
:cpp:expr:`lv_observer_remove(observer)` when the Observer is no longer needed, which
both unsubscribes it from the Subject and deletes it from the LVGL heap.
Subscribing While Associating Observer with a Widget
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The function below assocates a Widget with an Observer while subscribing to a
Subject. A copy of the pointer to that Widget is saved in the Observer's ``target``
field. This works exactly like the above method except that when the Widget is
deleted, the Observer thus created will be automatically unsubscribed from the
Subject and deleted from the LVGL heap. Note this is different from
:ref:`observer_widget_binding`.
lv_observer_t * observer = :cpp:expr:`lv_subject_add_observer_obj(&some_subject, some_observer_cb, widget, user_data)`
Any number of Observers can be created and be associated with a Widget this way.
A copy of the pointer to the Widget can be retrieved by calling
:cpp:expr:`lv_observer_get_target_obj(observer)`, e.g. inside the callback function.
Note that this function returns the stored pointer as a ``lv_obj_t *`` type, as
opposed to the ``void *`` type returned by
:cpp:expr:`lv_observer_get_target_obj(observer)`.
(:cpp:expr:`lv_observer_get_target(observer)` can still be used if you need that
pointer as a ``void *`` type for any reason, but in practice, this would be rare.)
Unsubscribing from a Subject
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When an Observer and its associated events are no longer needed, you can remove it
from memory using any of the methods below, depending on the effect you want to create:
- If Widget needs to be deleted, simply delete the Widget, which will automatically
gracefully remove the Observer (and its events) from the Subject, while deleting
the Widget.
- If Widget does NOT need to be deleted:
- :cpp:expr:`lv_obj_remove_from_subject(widget, subject)` deletes all Observers
associated with ``widget``. ``subject`` can be NULL to unsubscribe the Widget
from all associated Subjects.
- :cpp:expr:`lv_subject_deinit(subject)` gracefully disconnects ``subject`` from
all associated Observers and Widget events. This includes subscriptions made
using any of the :ref:`observer_widget_binding` functions covered below.
- :cpp:expr:`lv_observer_remove(observer)` deletes that specific Observer and
gracefully disconnects it from its ``subject`` and any associated Widgets,
where ``observer`` is the return value any of the above
``lv_subject_add_observer_...()`` functions.
.. _observer_subject_groups:
Subject Groups
--------------
When something in your system relies on more than one value (i.e. it needs to be
notified when any of a SET of two or more values changes), it can be made an
Observer of a Subject Group.
Let us consider an example of an instrument which measures either voltage or current.
To display the measured value on a label, 3 things are required:
1. What is being measured (current or voltage)?
2. What is the measured value?
3. What is the range or unit ("mV", "V", "mA", "A")?
When any of these 3 input values change, the label needs to be updated, and it needs
to know all 3 values to compose its text.
To handle this you can create an array from the addresses of all the Subjects that
are relied upon, and pass that array as a parameter when you initialize a Subject
with GROUP type.
.. code-block:: c
static lv_subject_t * subject_list[3] = {&subject_1, &subject_2, &subject_3};
lv_subject_init_group(&subject_all, subject_list, 3); /* Last argument is number of elements. */
Observers are then added to Subject Groups (e.g. ``subject_all``) in the usual way.
When this is done, a change to the value of any of the Subjects in the group triggers
a notification to all Observers subscribed to the Subject Group (e.g. ``subject_all``).
As an example, the above scenario with Voltage/Current measurement might look like this:
.. code-block:: c
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_subject_t subject_mode; // Voltage or Current
lv_subject_t subject_value; // Measured value
lv_subject_t subject_unit; // The unit
lv_subject_t subject_all; // Subject group that connects the above 3 Subjects
lv_subject_t * subject_list[3] = {&subject_mode, &subject_value, &subject_unit}; // The elements of the group
lv_subject_init_int(&subject_mode, 0); // Let's say 0 is Voltage, 1 is Current
lv_subject_init_int(&subject_value, 0);
lv_subject_init_pointer(&subject_unit, "V");
lv_subject_init_group(&subject_all, subject_list, 3);
lv_subject_add_observer_obj(&subject_all, all_observer_cb, label, NULL);
...
static void all_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
lv_obj_t * label = lv_observer_get_target(observer);
lv_subject_t * subject_mode = lv_subject_get_group_element(subject, 0);
lv_subject_t * subject_value = lv_subject_get_group_element(subject, 1);
lv_subject_t * subject_unit = lv_subject_get_group_element(subject, 2);
int32_t mode = lv_subject_get_int(subject_mode);
int32_t value = lv_subject_get_int(subject_value);
const char * unit = lv_subject_get_pointer(subject_unit);
lv_label_set_text_fmt(label, "%s: %d %s", mode ? "Current" : "Voltage", value, unit);
}
.. _observer_widget_binding:
Widget Binding
--------------
The following methods of subscribing to an integer-type Subject associate the
Observer with ONE of a Widget's properties as thought that property itself were the
Observer. Any of the following Widget properties can be thus bound to an Subject's
integer value:
- flag (or OR-ed combination of flags) from from the ``LV_OBJ_FLAG_...`` enumeration values;
- state (or OR-ed combination of states) from the ``LV_STATE_...`` enumeration values;
- text value for Label Widgets;
- integer value for these Widget types:
- Arc
- Drop-Down
- Roller
- Slider
Any number of Observers can be created for a single Widget, each bound to ONE of
the above properties.
For all of the ``lv_..._bind_...()`` functions covered below, they are similar to
:cpp:expr:`lv_subject_add_observer_obj(&some_subject, some_observer_cb, widget, user_data)`
in that they create an Observer and associates the Widget with it. What is different
is that updates to the Widget's property thus bound are handled internally -- the
user *does not supply callback functions* for any of these subscribing methods -- the
callback methods are supplied by the Observer subsystem.
.. note::
While the examples below show saving a reference to the created Observer objects
for the various ``lv_..._bind_...()`` functions, it is not necessary to do so
unless you need them for some purpose, because the created Observer objects will
be automatically deleted when the Widget is deleted.
Any Widget Type
~~~~~~~~~~~~~~~
Flags
^^^^^
The following methods of subscribing to an integer Subject affect a Widget's flag (or
OR-ed combination of flags). When the subscribing occurs, and each time the
Subject's value is changed thereafter, the Subject's value is compared with the
specified reference value, and the specified flag(s) is (are):
- SET when the Subject's integer value fulfills the indicated condition, and
- CLEARED otherwise.
Here are the functions that carry out this method of subscribing to a Subject. The
``flags`` argument can contain a single, or bit-wise OR-ed combination of any of the
``LV_OBJ_FLAG_...`` enumeration values.
:equal: :cpp:expr:`lv_obj_bind_flag_if_eq(widget, &subject, flags, ref_value)`
:not equal: :cpp:expr:`lv_obj_bind_flag_if_not_eq(widget, &subject, flags, ref_value)`
:greater than: :cpp:expr:`lv_obj_bind_flag_if_gt(widget, &subject, flags, ref_value)`
:greater than or equal: :cpp:expr:`lv_obj_bind_flag_if_ge(widget, &subject, flags, ref_value)`
:less than: :cpp:expr:`lv_obj_bind_flag_if_lt(widget, &subject, flags, ref_value)`
:less than or equal: :cpp:expr:`lv_obj_bind_flag_if_le(widget, &subject, flags, ref_value)`
States
^^^^^^
The following methods of subscribing to an integer Subject affect a Widget's states
(or OR-ed combination of states). When the subscribing occurs, and each time the
Subject's value is changed thereafter, the Subject's value is compared with the
specified reference value, and the specified state(s) is (are):
- SET when the Subject's integer value fulfills the indicated condition, and
- CLEARED otherwise.
Here are the functions that carry out this method of subscribing to a Subject. The
``states`` argument can contain a single, or bit-wise OR-ed combination of any of the
``LV_STATE_...`` enumeration values.
:equal: :cpp:expr:`lv_obj_bind_state_if_eq(widget, &subject, states, ref_value)`
:not equal: :cpp:expr:`lv_obj_bind_state_if_not_eq(widget, &subject, states, ref_value)`
:greater than: :cpp:expr:`lv_obj_bind_state_if_gt(widget, &subject, states, ref_value)`
:greater than or equal: :cpp:expr:`lv_obj_bind_state_if_ge(widget, &subject, states, ref_value)`
:less than: :cpp:expr:`lv_obj_bind_state_if_lt(widget, &subject, states, ref_value)`
:less than or equal: :cpp:expr:`lv_obj_bind_state_if_le(widget, &subject, states, ref_value)`
Checked State
^^^^^^^^^^^^^
The following method of subscribing to an integer Subject affects a Widget's
:cpp:enumerator:`LV_STATE_CHECKED` state. When the subscribing occurs, and each time
the Subject's value is changed thereafter, the Subject's value is compared to a
reference value of ``0``, and the :cpp:enumerator:`LV_STATE_CHECKED` state is:
- CLEARED when the Subject's value is 0, and
- SET when the Subject's integer value is non-zero.
Note that this is a two-way binding (Subject <===> Widget) so direct (or
programmatic) interaction with the Widget that causes its
:cpp:enumerator:`LV_STATE_CHECKED` state to be SET or CLEARED also causes the
Subject's value to be set to ``1`` or ``0`` respectively.
- :cpp:expr:`lv_obj_bind_checked(widget, &subject)`
Label Widgets
~~~~~~~~~~~~~
.. |deg| unicode:: U+000B0 .. DEGREE SIGN
This method of subscribing to an integer Subject affects a Label Widget's
``text``. The Subject can be an STRING, POINTER or INTEGER type.
When the subscribing occurs, and each time the Subject's value is changed thereafter,
the Subject's value is used to update the Label's text as follows:
:string Subject: Subject's string is used to directly update the Label's text.
:pointer Subject: If NULL is passed as the ``format_string`` argument when
subscribing, the Subject's pointer value is assumed to point to a
NUL-terminated string. and is used to directly update the Label's
text. See :ref:`observer_format_string` for other options.
:integer Subject: Subject's integer value is used with the ``format_string`` argument.
See See :ref:`observer_format_string` for details.
Note that this is a one-way binding (Subject ===> Widget).
- :cpp:expr:`lv_label_bind_text(label, &subject, format_string)`
.. _observer_format_string:
The ``format_string`` Argument
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``format_string`` argument is optional and if provided, must contain exactly 1
printf-like format specifier and be one of the following:
:string or pointer Subject: "%s" to format the new pointer value as a string or "%p"
to format the pointer as a pointer (typically the
pointer's address value is spelled out with 4, 8 or 16
hexadecimal characters depending on the platform).
:integer Subject: "%d" format specifier (``"%" PRIdxx`` --- a
cross-platform equivalent where ``xx`` can be ``8``,
``16``, ``32`` or ``64``, depending on the platform).
If NULL is passed for the ``format_string`` argument:
:string or pointer Subject: Updates expect the pointer to point to a NUL-terminated string.
:integer Subject: The Label will display an empty string (i.e. nothing).
**Example:** "%d |deg|\C"
Arc Widgets
~~~~~~~~~~~
This method of subscribing to an integer Subject affects an Arc Widget's integer
value directly. Note that this is a two-way binding (Subject <===> Widget) so an end
user's direct interaction with the Arc Widget updates the Subject's value and vice
versa. (Requires :c:macro:`LV_USE_ARC` to be configured to ``1``.)
- :cpp:expr:`lv_arc_bind_value(arc, &subject)`
Slider Widgets
~~~~~~~~~~~~~~
This method of subscribing to an integer Subject affects a Slider Widget's integer
value directly. Note that this is a two-way binding (Subject <===> Widget) so an end
user's direct interaction with the Slider Widget updates the Subject's value and vice
versa. (Requires :c:macro:`LV_USE_SLIDER` to be configured to ``1``.)
- :cpp:expr:`lv_slider_bind_value(slider, &subject)`
Roller Widgets
~~~~~~~~~~~~~~
This method of subscribing to an integer Subject affects a Roller Widget's integer
value directly. Note that this is a two-way binding (Subject <===> Widget) so an end
user's direct interaction with the Slider Widget updates the Subject's value and vice
versa. (Requires :c:macro:`LV_USE_ROLLER` to be configured to ``1``.)
- :cpp:expr:`lv_roller_bind_value(roller, &subject)`
Drop-Down Widgets
~~~~~~~~~~~~~~~~~
This method of subscribing to an integer Subject affects a Drop-Down Widget's integer
value directly. Note that this is a two-way binding (Subject <===> Widget) so an end
user's direct interaction with the Drop-Down Widget updates the Subject's value and
vice versa. (Requires :c:macro:`LV_USE_DROPDOWN` to be configured to ``1``.)
- :cpp:expr:`lv_dropdown_bind_value(dropdown, &subject)`
.. _observer_api:
API
***

View File

@@ -0,0 +1,8 @@
.. _observer examples:
========
Examples
========
.. include:: ../../../examples/others/observer/index.rst

View File

@@ -0,0 +1,86 @@
.. _snapshot:
========
Snapshot
========
Snapshot provides an API to take a snapshot image for an LVGL Widget together
with its children. The image will look exactly like the Widget on the display.
.. _snapshot_usage:
Usage
*****
Simply call function :cpp:expr:`lv_snapshot_take(widget, color_format)` to generate
the image descriptor which can be used as an Image Widget's image source using
:cpp:func:`lv_image_set_src`.
Note, only following color formats are supported at this time:
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB565`
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB888`
- :cpp:enumerator:`LV_COLOR_FORMAT_XRGB8888`
- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB8888`
Freeing the Image
-----------------
The memory :cpp:func:`lv_snapshot_take` uses is dynamically allocated using
:cpp:func:`lv_draw_buf_create`. Use :cpp:func:`lv_draw_buf_destroy` to free the
memory it allocated.
The snapshot image which is the draw buffer returned by :cpp:func:`lv_snapshot_take`
normally won't be added to the cache because it can be drawn directly. So you don't need
to invalidate the cache by calling :cpp:func:`lv_image_cache_drop` before destroying
the draw buffer.
The below code snippet demonstrates correct use of :cpp:func:`lv_snapshot_take`:
.. code-block:: c
void update_snapshot(lv_obj_t * widget, lv_obj_t * img_snapshot)
{
lv_draw_buf_t* snapshot = (void*)lv_image_get_src(img_snapshot);
if(snapshot) {
lv_draw_buf_destroy(snapshot);
}
snapshot = lv_snapshot_take(widget, LV_COLOR_FORMAT_ARGB8888);
lv_image_set_src(img_snapshot, snapshot);
}
Using an Existing Buffer
------------------------
If the snapshot needs to be updated repeatedly, or if the caller provides the draw
buffer, use :cpp:expr:`lv_snapshot_take_to_draw_buf(widget, color_format, draw_buf)`.
In this case, the caller is responsible for creating and destroying the draw buffer.
If snapshot is generated successfully, the image descriptor is updated,
the image data will be stored to the provided ``draw_buf``, and the function will
return :cpp:enumerator:`LV_RESULT_OK`.
Note that snapshot may fail if the provided buffer is not large enough, which can
happen if the Widget's size changes. It's recommended to use
:cpp:expr:`lv_snapshot_reshape_draw_buf(widget, draw_buf)` to first ensure the buffer
is large enough, and if it fails, destroy the existing draw buffer and call
`lv_snapshot_take` directly.
.. _snapshot_example:
Example
*******
.. include:: ../../examples/others/snapshot/index.rst
.. _snapshot_api:
API
***

View File

@@ -0,0 +1,163 @@
.. _test:
==========
UI Testing
==========
Overview
********
The Test module provides functions to emulate clicks, key presses, encoder turns, time passing, and
compare the UI with reference images.
These functions can be easily used in any test framework (such as Unity, GoogleTest, etc.), and
assertions can be performed to check if, for example:
- A widget's value is different from the expected value after emulating user inputs.
- The values are incorrect after some time has passed.
- The screen's content is different from the reference image.
- Some event functions are not triggered.
- Etc.
Note that it is assumed the tests are performed on a desktop or server environment,
where there are no memory constraints.
Usage
*****
The Test module can be enabled by configuring ``LV_USE_TEST`` to a non-zero value,
and it consists of the following components:
- Helpers
- Display emulation
- Input device emulation
- Screenshot comparison
Helpers
-------
Time
~~~~
To emulate elapsed time, two functions can be used:
1. :cpp:expr:`lv_test_wait(ms)`: Emulates that ``ms`` milliseconds have elapsed, but it also calls ``lv_timer_handler`` after each millisecond.
This is useful to check if events (e.g., long press, long press repeat) and timers were triggered correctly over time.
2. :cpp:expr:`lv_test_fast_forward(ms)`: Jumps ``ms`` milliseconds ahead and calls ``lv_timer_handler`` only once at the end.
:cpp:expr:`lv_refr_now(NULL)` is called at the end of both functions to ensure that animations and
widget coordinates are recalculated.
:cpp:expr:`lv_refr_now(NULL)` can also be called manually to force LVGL to refresh the emulated display.
Memory Usage
~~~~~~~~~~~~
If ``LV_USE_STDLIB_MALLOC`` is set to ``LV_STDLIB_BUILTIN``, memory usage and memory leaks can be monitored.
.. code-block:: c
size_t mem1 = lv_test_get_free_mem();
<create and delete items>
size_t mem2 = lv_test_get_free_mem();
if(mem1 != mem2) fail();
It might make sense to create and delete items in a loop many times and add a small tolerance
to the memory leakage test. This might be needed due to potential memory fragmentation. Empirically,
a tolerance of 32 bytes is recommended.
.. code-block:: c
if(LV_ABS((int64_t)mem2 - (int64_t)mem1) > 32) fail();
Display Emulation
-----------------
By calling :cpp:expr:`lv_test_display_create(hor_res, ver_res)`, a dummy display can be created.
It functions like any other normal display, but its content exists only in memory.
When creating this display, the horizontal and vertical resolutions must be passed. Internally,
a framebuffer will be allocated for this size, and ``XRGB8888`` color format will be set.
The resolution and color format can be changed at any time by calling :cpp:func:`lv_display_set_resolution` and
:cpp:func:`lv_display_set_color_format`.
Input Device Emulation
----------------------
By calling :cpp:func:`lv_test_indev_create_all`, three test input devices will be created:
1. A pointer (for touch or mouse)
2. A keypad
3. An encoder
For example, this is how a scroll gesture can be emulated:
.. code-block:: c
lv_test_mouse_move_to(20, 30);
lv_test_mouse_press();
lv_test_wait(20);
lv_test_mouse_move_by(0, 100);
lv_test_wait(20);
lv_test_mouse_release();
lv_test_wait(20);
It is recommended to add :cpp:func:`lv_test_wait` after user actions to ensure that
the new state and coordinates are read and applied from the input device.
After that, the user can check if the given widget was really scrolled
by getting the Y coordinate of a child.
.. code-block:: c
int32_t y_start = lv_obj_get_y(child);
<scroll emulation>
int32_t y_end = lv_obj_get_y(child);
if(y_start + 100 != y_end) fail();
Please refer to :ref:`lv_test_indev_h` for the list of supported input device emulation functions.
Screenshot Comparison
---------------------
``bool lv_test_screenshot_compare(const char * fn_ref)`` is a useful function
to compare the content of the emulated display with reference PNG images.
This function works in a practical way:
- If the folder(s) referenced in ``fn_ref`` do not exist, they will be created automatically.
- If the reference image is not found, it will be created automatically from the rendered screen.
- If the comparison fails, an ``<image_name>_err.png`` file will be created with the rendered content next to the reference image.
- If the comparison fails, the X and Y coordinates of the first divergent pixel, along with the actual and expected colors, will also be printed.
The reference PNG images should have a **32-bit color format** and match the display size.
The test display's content will be converted to ``XRGB8888`` to simplify comparison with the reference images.
The conversion is supported from the following formats (i.e., the test display should have a color
format in this list):
- :cpp:enumerator:`LV_COLOR_FORMAT_XRGB8888`
- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB8888`
- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB8888_PREMULTIPLIED`
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB888`
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB565`
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB565_SWAPPED`
- :cpp:enumerator:`LV_COLOR_FORMAT_L8`
- :cpp:enumerator:`LV_COLOR_FORMAT_AL88`
- :cpp:enumerator:`LV_COLOR_FORMAT_I1`
To read and decode PNG images and to store the converted rendered image, a few MBs of RAM are dynamically allocated using the standard ``malloc``
(not :cpp:expr:`lv_malloc`).
The screenshot comparison uses `lodepng` which is built-in to LVGL and just needs to be enabled with
``LV_USE_LODEPNG``.
To avoid making the entire Test module dependent on `lodepng`, screenshot comparison can be individually enabled by
``LV_USE_TEST_SCREENSHOT_COMPARE``.
API
***

View File

@@ -0,0 +1,7 @@
.. _xml_animations:
==========
Animations
==========
TODO

View File

@@ -0,0 +1,285 @@
.. _xml_api:
===
API
===
The ``<api>`` tag can be a child of ``<widget>`` and ``<components>`` tags, although
each supports slightly different features.
Properties
**********
Inside ``<prop>`` elements, ``<param>`` elements can be defined to describe the arguments.
For **Widgets**, all properties are optional.
If a property is not set on an instance of a Widget, it simply won't be applied,
and the created Widget's default value for that property will be used (e.g., ``text``
for a label's text).
For **Components**, all properties are mandatory; however, default values can be defined
to be used when a property is not set.
If a property has only one parameter (which is usually the case), a shorthand syntax
can be applied as shown below.
For example:
.. code-block:: xml
<api>
<prop name="range" default="0 100" help="Set the range.">
<param name="range_min" type="int" help="Sets the minimum value."/>
<param name="range_max" type="int" help="Sets the maximum value."/>
</prop>
<prop name="title" type="string" help="The title of the slider"/>
</api>
When a property is used, all parameters are set as a single attribute value. For example:
.. code-block:: xml
<my_slider range="-100 100" title="Room 1"/>
For **Widgets**, each property corresponds to a setter function.
The ``name`` in ``<prop>`` is used to build the name of the setter function like this:
.. code-block:: c
<widget_name>_set_<prop_name>(lv_obj_t * obj, <param1_type> <param1_name>, <param2_type> <param2_name>, ...);
For **Components**, the exported code contains only a single ``create`` function
to which all the properties are passed:
.. code-block:: c
<component_name>_create(lv_obj_t * parent, <param1_type> <param1_name>, <param2_type> <param2_name>, ...);
``<prop>`` elements have an optional ``<postponed>`` boolean attribute.
By default, it is ``false``, but if set to ``true``, the given property will be
applied after all children are created. A practical example is setting the current
tab of a tab view, which cannot be set before the tabs are created. This feature is
not supported yet.
``<enumdef>``
*************
This tag is used only with Widgets. It is used to define new enum types for a given
Widget. It should contain ``<enum>`` elements to define possible options.
Example:
.. code-block:: xml
<!-- my_widget.xml -->
<api>
<enumdef name="my_widget_mode" help="Possible modes">
<enum name="normal" help="Normal mode" value="0x10"/>
<enum name="inverted" help="Inverted mode"/>
</enumdef>
<prop name="mode" help="Set Widget mode">
<param name="mode" type="enum:my_widget_mode"/>
</prop>
</api>
Note that the enum values are not important because:
1. When the code is exported, the enum names will be used, and the compiler generates
its own value for each enumerator symbol.
2. When loaded from XML, the Widget's XML parser should convert the enum names to C
enum fields.
``<element>``
*************
``<element>`` tags also apply only to Widgets. Elements are used to describe
sub-Widgets or internal parts of Widgets. Examples include the list of a dropdown,
the tabs of a tab view, or the data series of a chart.
Elements can have ``<arg>`` and ``<prop>`` definitions. ``<arg>`` elements are
mandatory (default values are supported) as they are used to create the element,
whereas ``<prop>`` elements are optional as they are mapped to setter functions.
An element in a ``<view>`` can be referenced like this: ``<widget_name-element_name>``.
Note that the ``-`` separates two names inside that tag name: the Widget name and the
element name. ``-`` is not allowed in Widget and element names. Only ``_`` can be
used to separate words in tag names.
Example:
.. code-block:: xml
<my_chart-super_series color="0xff0000"/>
An important attribute of elements is ``access``. The possible values are:
- ``add``: Create any number of elements dynamically (e.g., chart series).
- ``get``: Get a pointer to an implicitly created Widget or any data (e.g., list of a Drop-Down List).
- ``set``: Select specific parts of the Widget with indexes (e.g., table cells).
Elements with ``access="add"`` or ``access="get"`` can have a custom data type
defined using ``type="my_data"``. In these cases, no children can be added. If the
``type`` is ``lv_obj``, the element can have children.
It is not yet possible to describe the ``<view>`` of elements in XML; only the API can be defined.
The actual implementation needs to be done in C.
``access="add"``
----------------
The element is explicitly created with an ``add`` function, e.g., ``lv_tabview_add_tab(obj, "Title");``.
``<arg>`` elements defined as direct children of the ``<element>`` are passed to the
``add`` function as arguments.
Example:
.. code-block:: xml
<!-- my_widget.xml -->
<api>
<element name="indicator" type="lv_obj" help="The indicator of my_widget" access="add">
<arg name="color" type="color" help="Help for color"/>
<arg name="max_value" type="int" help="Help for max_value"/>
<prop name="value" help="Set a new value for the indicator">
<param name="value" type="int" help="Help for value"/>
</prop>
</element>
</api>
<view extends="obj">
<button name="btn1"/>
</view>
In a view it can be used like this:
.. code-block:: xml
<!-- complex_widget.xml -->
<view>
<lv_label text="Title"/>
<my_widget width="100px" y="40px">
<my_widget-indicator name="indic1" color="0xff0000" max_value="120" value="30"/>
</my_widget>
</view>
From the API definition the following functions are generated:
.. code-block:: c
lv_obj_t * my_widget_add_indicator(lv_obj_t * parent, lv_color_t color, int32_t max_value);
void my_widget_set_indicator_value(lv_obj_t * obj, int32_t value);
And this is the related C file where the indicator is created:
.. code-block:: c
lv_obj_t * indic1 = my_widget_add_indicator(parent, color, max_value);
lv_my_widget_set_indicator_value(indic1, value);
``access="get"``
----------------
If the element is created internally and implicitly, it can be retrieved with a
function like ``lv_dropdown_get_list(obj);``.
``<arg>`` elements are passed to the ``get`` function as arguments.
Example:
.. code-block:: xml
<!-- my_widget.xml -->
<api>
<element name="control_button" type="lv_obj" help="A control button of my_widget" access="get">
<arg name="index" type="int" help="Zero-based index of the control button"/>
<prop name="title">
<param name="text" type="string"/>
</prop>
</element>
</api>
In a view:
.. code-block:: xml
<!-- complex_widget.xml -->
<view>
<my_widget width="100px">
<my_widget-control_button name="btn1" index="3" title="Hello"/>
</my_widget>
</view>
Generated API:
.. code-block:: c
lv_obj_t * my_widget_get_control_button(lv_obj_t * parent, int32_t index);
void my_widget_set_control_button_title(lv_obj_t * obj, const char * text);
And this is a C file where the control button is retrieved:
.. code-block:: c
lv_obj_t * btn1 = lvmy_widget_get_control_button(parent, index);
my_widget_set_control_button_title(btn1, text);
``access="set"``
----------------
The "set" value is used when elements are created automatically but need to be selected in API calls,
e.g., ``lv_table_set_cell_value(table, row, col, "text");``.
Example:
.. code-block:: xml
<!-- my_widget.xml -->
<api>
<element name="item" type="lv_obj" help="An item on my_widget" access="set">
<arg name="index" type="int" help="The zero-based index of the item"/>
<prop name="icon" help="Set the icon of an item">
<param name="icon_src" type="img_src" help="The image to set as an icon."/>
</prop>
<prop name="color" help="Set the color">
<param name="color" type="color" help="The color to set for the item."/>
</prop>
</element>
</api>
In a view:
.. code-block:: xml
<!-- complex_widget.xml -->
<view>
<my_widget width="100px">
<my_widget-item index="3" icon_src="image1" color="0xff0000"/>
</my_widget>
</view>
This is the generated header file:
.. code-block:: c
void my_widget_set_item_icon(lv_obj_t * parent, int32_t index, const void * icon_src);
void my_widget_set_item_color(lv_obj_t * parent, int32_t index, lv_color_t color);
And this is the related C file where the item properties are set:
.. code-block:: c
my_widget_set_item_icon(parent, index, image1);
my_widget_set_item_color(parent, index, color);

View File

@@ -0,0 +1,189 @@
.. _xml_component_library:
=================
Component Library
=================
Overview
********
.. |nbsp| unicode:: U+000A0 .. NO-BREAK SPACE
:trim:
The collection of Components, Widgets, Screens, Images, Fonts, etc., is called a
Component Library.
A Component Library can be fully self-sufficient, but it can also reference data from
other Component Libraries.
LVGL itself is a Component Library that supplies the built-in Widgets, data types,
etc., so typically Component Libraries use at least the core LVGL data. You can
find the XML files that describe the LVGL Widgets
`here <https://github.com/lvgl/lvgl/tree/master/xmls>`__.
A project always has at least 2 Component Libraries: that of LVGL as mentioned
above, and its own where the Screens, Components, and Widgets of the project are
defined. A project may include additional Component Libraries.
Structure
*********
A typical structure for a Component Library looks like this:
.. code-block:: none
name_of_the_component_library
├── globals.xml
├── components
│ ├── component1.xml
│ ├── component2.xml
│ └── other_folder
│ ├── component3.xml
│ └── component4.xml
├── widgets
│ ├── widget1
│ │ ├── widget1.xml
│ │ ├── widget1.c
│ │ ├── widget1.h
│ │ ├── widget1_gen.c
│ │ ├── widget1_gen.h
│ │ ├── widget1_private_gen.h
│ │ └── widget1_xml_parser.c
│ └── widget2
│ └── ...same as widget1...
├── screens
│ ├── screen1.xml
│ └── screen2.xml
├── fonts
│ ├── font1.ttf
│ └── font2.ttf
└── images
├── image1.png
└── image2.png
Visibility
**********
The content of all ``globals.xml`` files is part of a common global scope, and
any Components, Widgets or Screens defined therein can be used in all .XML files.
Styles, constants, and other data defined in the XML file of Components, Widgets, or Screens
are local to that XML file.
Thus, there are two namespaces:
1. **local namespace** within the given XML file of Components, Widgets and Screens.
2. **global namespace** created from the data in the ``globals.xml`` file from each
Component Library included.
To find names referenced in XML files, the local namespace is checked first, and if
a name is not found there, then the global namespace is checked.
The names of defined Components, Widgets and Screens become part of the global
namespace and must be unique therein. This ensures that each Component has a unique
name.
All data belonging to the LVGL core Component Library is prefixed by ``lv_``
(e.g., ``lv_label``, ``lv_font_default``).
A custom Component can be prefixed with ``watch_``, ``small_``, ``light_``, or
anything else the developer deems appropriate.
LVGL's UI |nbsp| Editor will show an error if there is a name conflict.
``globals.xml``
***************
A single ``globals.xml`` file should be created in the root directory of each
Component Library. The definitions in it do not belong to any specific Widget but
are available throughout the entire UI, Widgets, and all XML files. The valid tags
in it are:
:<config>: Can specify name and help.
:<api>: Used with ``<enumdefs>`` to show possible values for Widget or Component attributes.
:<subjects>: List of :ref:`Subjects <observer_subject>`. Can be considered the API of a Component Library.
:<consts>: Globally available constants.
:<styles>: Globally available styles.
:<fonts>: Globally available fonts.
:<images>: Globally available images.
:<const_variants>: See below.
:<style_variants>: See below.
The ``globals.xml`` must be the only ``globals.xml`` file within the Component
Library's folder tree.
From each ``globals.xml`` file, a ``<config.name>.h`` file is generated, which is
included by all generated header files --- not only in the sub-folders where
``globals.xml`` is created, but in all exported .C and .H files. This ensures that
constants, fonts, and other global data are available for all Widgets and new Widgets.
Variants
--------
``<const_variant>`` can be used by constants to create variants that can be selected at compile time.
This can be used to select a different display size, color scheme, etc.
``<style_variant>`` can be used by styles only, to modify styles at runtime. To
select the current style variant, an integer :ref:`Subject <observer_subject>` (i.e.
a Subject containing an integer value in the Observer Pattern implemented in
``lv_observer.c/.h``) ``<style_variant.name>_variant`` is created. Styles can
subscribe to this, and the style properties can be changed according to the selected
variant's integer value. (See `observer` for details about how to do this.)
All possible variants should be defined in ``globals.xml``.
Example
-------
A ``globals.xml`` file of a Component Library might look like this:
.. code-block:: xml
<globals>
<config name="mylib" help="This is my great Component Library"/>
<const_variants>
<const_variant name="size" help="Select the size">
<case name="small" help="Assets for 320x240 Screen"/>
<case name="large" help="Assets for 1280x768 Screen"/>
</const_variant>
</const_variants>
<style_variants>
<style_variant name="color" help="Select the color of the UI">
<case name="red" help="Select a red theme"/>
<case name="blue" help="Select a blue theme"/>
</style_variant>
</style_variants>
<api>
<enumdef name="mode">
<enum name="slow"/>
<enum name="fast"/>
</enumdef>
</api>
<consts>
<px name="small_unit" value="8"/>
<px name="large_unit" value="16"/>
</consts>
<styles>
<style name="card" bg_color="0xeee" radius="#small_unit" padding="12px"/>
</styles>
<images>
<file name="arrow_left" src="A:/images/arrow_left.png"/>
</images>
<fonts>
<tiny_ttf name="big" src="A:/fonts/arial.ttf" size="28"/>
</fonts>
</globals>

Some files were not shown because too many files have changed in this diff Show More