├── .gitignore ├── LICENSE ├── README.md ├── index.grid.html ├── index.html ├── index.raw.html └── src ├── apis.js ├── compiler.js ├── data.js ├── gen_apis_js.py ├── lvgl_mp.js ├── main.js ├── template.js ├── utils.js └── wrapper.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # walv 2 | An Online, WYSIWYG GUI designer for LittlevGL. Cross-platform supported(Even Android and IOS). 3 | ## [Try it online!](https://kaiakz.github.io/walv/index.html) 4 | Make sure your browser is the latest version(Chrome, Firefox and Safari). 5 | 6 | ![preview](https://user-images.githubusercontent.com/51747223/75126997-84043300-56f7-11ea-8b54-3a5603cec9b7.gif) 7 | ![style_editor](https://user-images.githubusercontent.com/51747223/75605140-0e46ff80-5b1b-11ea-9260-f0c9ad87dc5a.gif) 8 | 9 | ## Usage 10 | You need a browser firstly(Recommends PC with 1920x1080), and then visit [the github page](https://kaiakz.github.io/walv/index.html) or just run an HTTP server(`python -m http.server`) that serves files from this directory. 11 | ### Create 12 | 1. Click a node of the treeview in the left(as the parent), for example, `screen`. 13 | 2. Choose which widget you want, and then click the `+`. 14 | ### Generat && Export the final code 15 | 1. Click the `Generate` button to generate the source code, you can preview and edit the code in Code Editor. (You need to re-generate after modifying the widget) 16 | 2. Click the `Export` button, you can download the code in Code Editor. 17 | 18 | ## Feature 19 | * WYSIWYG, MVVM(attribute). 20 | * Just a **static** web app, you need to use it with **the latest browser**(FireFox, Chrome...even IOS Safari). 21 | * Drag and drop to control the postion of the widget. 22 | * Choose an object as the parent, and then create children on it. 23 | * Set attribute(postion, size, click, etc). 24 | * Style Editor(initial, now for text only). 25 | * Screenshot. 26 | * Code Preview. 27 | * Highlight(initial). 28 | * TFT_simulator can be customized(size), supports mutiple windows.(To do) 29 | * Animation Editor(planning). 30 | * Save and load project. The tool will save your project automatically, and could restore your work from the last closed window. 31 | * Generate C and MicroPython code: includes GUI and Callback. 32 | 33 | ## Architecture 34 | * A static webpage built with [lv_micropython](https://github.com/littlevgl/lv_micropython)(WASM) and front-end component library, so walv won't send your data to the server. 35 | * WASM part provides a Simulator. 36 | * The front-end component library provides a way to control Simulator: create,delete or modify a widget. Include attribute editor, style editor and animation editor. 37 | * Generate final code by javascript, Use `Blob` to save file. 38 | * Continue your work in last closed window: By `IndexedDB`. 39 | 40 | ## How does it work? 41 | * `lv_micropython` has some JavaScript API: `mp_js_do_str()`(`lv_micropython` will excute the parameter, just like eval() in Python or JavaScript) 42 | * walv wraps some commonly used functions(see Getter & Setter), called `template`. 43 | * walv provides a layer over the `lv_micropython`, it can generate some real functions by `template`, and then send those functions to `lv_micropython` by `mp_js_do_str`. For example, if the user want to change the X of the btn0 to 88 , walv will use the `template` (id.set_x(integer)) to generate the `btn0.set_x(88)`, and then send it to lv_micropython by `mp_js_do_str("btn0.set_x(88)")`. 44 | 45 | ## Alibaba Summer of Code 2019 : [AliOS-Things](https://github.com/alibaba/AliOS-Things/) 46 | ### Old : [lv_gui_designer](https://github.com/kaiakz/lv_gui_designer) 47 | -------------------------------------------------------------------------------- /index.grid.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 37 | 38 | 39 | 40 | 41 | WALV 42 | 43 | 44 | Docs 45 | github@kaiakz 46 | Comming Soon 47 | 48 | 49 | LittlevGL 50 | Forum 51 | AliOS-Things 52 | 53 | 54 | More 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | Generate 65 | Export 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 |
76 | 77 | 78 | 79 | 80 |
81 | 82 | {{ checkedNode.id }} 83 | 84 | 85 | 86 | 87 | 88 |
89 | 90 |
91 | 94 | 95 |
96 | 97 | 98 | 99 | 100 | 101 |
102 | 107 | 108 |
109 |
110 | 111 |
112 |
113 | 114 | 115 | 116 | 117 |
118 | 119 |
120 | REPL Terminal 121 | 122 | 123 | X: {{cursorX}}, Y: {{cursorY}} Size: 480x320 124 |
125 | 126 |
127 |
128 | 129 | 130 | 131 |
132 | 133 | 134 | 135 | Style 136 | Animation 137 | 138 | 139 | 140 |
141 | 142 |
143 | 144 | 145 | 146 | {{ currentWidget.id }} 147 | 148 | 149 | 150 | CB 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | . 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | - 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | DRAG 192 | 193 | 194 | CLICK 195 | 196 | 197 | HIDDEN 198 | 199 | 200 | TOP 201 | 202 | 203 | 204 | 205 |
206 |
207 |
208 |
209 |
210 | 211 | 217 | 218 |
Body
219 | 220 | 221 | 222 |
Text
223 | 224 | 225 | 226 | 227 | font_roboto_16 228 | font_roboto_28 229 | 230 |
231 |
232 |
233 |
234 |
235 |
236 | 237 | 238 |
239 |
240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 37 | 38 | 39 | WALV 40 | 41 | 42 | Docs 43 | github@kaiakz 44 | Comming Soon 45 | 46 | 47 | LittlevGL 48 | Forum 49 | AliOS-Things 50 | 51 | 52 | More 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | Generate 63 | Export 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 |
72 | 73 | 74 | 75 | 76 |
77 | 78 | {{ checkedNode.id }} 79 | 80 | 81 | 82 | 83 | 84 | 85 |
86 | 87 |
88 | 91 | 92 |
93 | 94 | 95 | 96 | 97 | 98 |
99 | 104 | 105 |
106 |
107 | 108 |
109 |
110 | 111 | 112 | 113 | 114 |
115 | 116 |
117 | REPL Terminal 118 | 119 | 120 | X: {{cursorX}}, Y: {{cursorY}} Size: 480x320 121 |
122 | 123 |
124 |
125 | 126 | 127 | 128 |
129 | 130 | 131 | 132 | Style 133 | Animation 134 | 135 | 136 | 137 |
138 | 139 |
140 | 141 | 142 | 143 | {{ currentWidget.id }} 144 | 145 | 146 | 147 | CB 148 | 149 | 150 | 151 | 152 | X: 153 | 154 | 155 | 156 | Y: 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | W: 166 | 167 | 168 | 169 | H: 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | DRAG 180 | 181 | 182 | CLICK 183 | 184 | 185 | 186 | 187 | HIDDEN 188 | 189 | 190 | TOP 191 | 192 | 193 | 194 | 195 |
196 |
197 |
198 |
199 |
200 | 201 | 207 | 208 |
Body
209 | 210 | 211 | 212 |
Text
213 | 214 | 215 | 216 | 217 | font_roboto_16 218 | font_roboto_28 219 | 220 |
221 |
222 | 223 | 224 |
225 |
226 |
227 | 228 |
229 |
230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | -------------------------------------------------------------------------------- /index.raw.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 37 | 38 | 39 | WALV 40 | 41 | 42 | Docs 43 | github@kaiakz 44 | Comming Soon 45 | 46 | 47 | LittlevGL 48 | Forum 49 | AliOS-Things 50 | 51 | 52 | More 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | Generate 63 | Export 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 |
72 | 73 | 74 | 75 | 76 |
77 | 78 | {{ checkedNode.id }} 79 | 80 | 81 | 82 | 83 | 84 | 85 |
86 | 87 |
88 | 91 | 92 |
93 | 94 | 95 | 96 | 97 | 98 |
99 | 104 | 105 |
106 |
107 | 108 |
109 |
110 | 111 | 112 | 113 | 114 |
115 | 116 |
117 | REPL Terminal 118 | 119 | 120 | X: {{cursorX}}, Y: {{cursorY}} 121 |
122 | 123 |
124 |
125 | 126 | 127 | 128 |
129 | 130 | 131 | 132 | Style 133 | Animation 134 | 135 | 136 | 137 |
138 | 139 |
140 | 141 | 142 | {{ currentWidget.id }} 143 | 144 | 145 | 146 | CB 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | . 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | - 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | DRAG 179 | 180 | 181 | CLICK 182 | 183 | 184 | HIDDEN 185 | 186 | 187 | TOP 188 | 189 | 190 | 191 |
192 |

{{ name }}({{ setArgs(body.args) }})

193 |
194 |
195 |
196 | 197 | 203 | 204 |
Body
205 | 206 | 207 | 208 |
Text
209 | 210 | 211 | 212 | 213 | font_roboto_16 214 | font_roboto_28 215 | 216 |
217 |
218 | 219 | 220 |
221 |
222 |
223 | 224 |
225 |
226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | -------------------------------------------------------------------------------- /src/apis.js: -------------------------------------------------------------------------------- 1 | const setter = {null:{}, "cont": {"fit": {"return_type": "NoneType", "args": [{"type": "int", "name": "fit"}], "type": "function", "api": "set_fit"}, "fit4": {"return_type": "NoneType", "args": [{"type": "int", "name": "left"}, {"type": "int", "name": "right"}, {"type": "int", "name": "top"}, {"type": "int", "name": "bottom"}], "type": "function", "api": "set_fit4"}, "fit2": {"return_type": "NoneType", "args": [{"type": "int", "name": "hor"}, {"type": "int", "name": "ver"}], "type": "function", "api": "set_fit2"}, "layout": {"return_type": "NoneType", "args": [{"type": "int", "name": "layout"}], "type": "function", "api": "set_layout"}}, "tileview": {"anim_time": {"return_type": "NoneType", "args": [{"type": "int", "name": "anim_time"}], "type": "function", "api": "set_anim_time"}, "tile_act": {"return_type": "NoneType", "args": [{"type": "int", "name": "x"}, {"type": "int", "name": "y"}, {"type": "int", "name": "anim"}], "type": "function", "api": "set_tile_act"}, "edge_flash": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_edge_flash"}, "valid_positions": {"return_type": "NoneType", "args": [{"type": "mp_arr_to_lv_point_t_____", "name": "valid_pos"}, {"type": "int", "name": "valid_pos_cnt"}], "type": "function", "api": "set_valid_positions"}}, "cb": {"checked": {"return_type": "NoneType", "args": [{"type": "bool", "name": "checked"}], "type": "function", "api": "set_checked"}, "text": {"return_type": "NoneType", "args": [{"type": "str", "name": "txt"}], "type": "function", "api": "set_text"}, "inactive": {"return_type": "NoneType", "args": [], "type": "function", "api": "set_inactive"}, "static_text": {"return_type": "NoneType", "args": [{"type": "str", "name": "txt"}], "type": "function", "api": "set_static_text"}}, "arc": {"angles": {"return_type": "NoneType", "args": [{"type": "int", "name": "start"}, {"type": "int", "name": "end"}], "type": "function", "api": "set_angles"}}, "gauge": {"scale": {"return_type": "NoneType", "args": [{"type": "int", "name": "angle"}, {"type": "int", "name": "line_cnt"}, {"type": "int", "name": "label_cnt"}], "type": "function", "api": "set_scale"}, "range": {"return_type": "NoneType", "args": [{"type": "int", "name": "min"}, {"type": "int", "name": "max"}], "type": "function", "api": "set_range"}, "needle_count": {"return_type": "NoneType", "args": [{"type": "int", "name": "needle_cnt"}, {"type": "mp_arr_to_lv_color_t_____", "name": "colors"}], "type": "function", "api": "set_needle_count"}, "value": {"return_type": "NoneType", "args": [{"type": "int", "name": "needle_id"}, {"type": "int", "name": "value"}], "type": "function", "api": "set_value"}, "critical_value": {"return_type": "NoneType", "args": [{"type": "int", "name": "value"}], "type": "function", "api": "set_critical_value"}}, "table": {"cell_align": {"return_type": "NoneType", "args": [{"type": "int", "name": "row"}, {"type": "int", "name": "col"}, {"type": "int", "name": "align"}], "type": "function", "api": "set_cell_align"}, "cell_value": {"return_type": "NoneType", "args": [{"type": "int", "name": "row"}, {"type": "int", "name": "col"}, {"type": "str", "name": "txt"}], "type": "function", "api": "set_cell_value"}, "col_cnt": {"return_type": "NoneType", "args": [{"type": "int", "name": "col_cnt"}], "type": "function", "api": "set_col_cnt"}, "cell_crop": {"return_type": "NoneType", "args": [{"type": "int", "name": "row"}, {"type": "int", "name": "col"}, {"type": "bool", "name": "crop"}], "type": "function", "api": "set_cell_crop"}, "cell_merge_right": {"return_type": "NoneType", "args": [{"type": "int", "name": "row"}, {"type": "int", "name": "col"}, {"type": "bool", "name": "en"}], "type": "function", "api": "set_cell_merge_right"}, "col_width": {"return_type": "NoneType", "args": [{"type": "int", "name": "col_id"}, {"type": "int", "name": "w"}], "type": "function", "api": "set_col_width"}, "cell_type": {"return_type": "NoneType", "args": [{"type": "int", "name": "row"}, {"type": "int", "name": "col"}, {"type": "int", "name": "type"}], "type": "function", "api": "set_cell_type"}, "row_cnt": {"return_type": "NoneType", "args": [{"type": "int", "name": "row_cnt"}], "type": "function", "api": "set_row_cnt"}}, "calendar": {"highlighted_dates": {"return_type": "NoneType", "args": [{"type": "mp_arr_to_lv_calendar_date_t_____", "name": "highlighted"}, {"type": "int", "name": "date_num"}], "type": "function", "api": "set_highlighted_dates"}, "showed_date": {"return_type": "NoneType", "args": [{"type": "calendar_date_t", "name": "showed"}], "type": "function", "api": "set_showed_date"}, "day_names": {"return_type": "NoneType", "args": [{"type": "pointer", "name": "day_names"}], "type": "function", "api": "set_day_names"}, "month_names": {"return_type": "NoneType", "args": [{"type": "pointer", "name": "month_names"}], "type": "function", "api": "set_month_names"}, "today_date": {"return_type": "NoneType", "args": [{"type": "calendar_date_t", "name": "today"}], "type": "function", "api": "set_today_date"}}, "btn": {"ink_wait_time": {"return_type": "NoneType", "args": [{"type": "int", "name": "time"}], "type": "function", "api": "set_ink_wait_time"}, "fit": {"return_type": "NoneType", "args": [{"type": "int", "name": "fit"}], "type": "function", "api": "set_fit"}, "toggle": {"return_type": "NoneType", "args": [{"type": "bool", "name": "tgl"}], "type": "function", "api": "set_toggle"}, "ink_in_time": {"return_type": "NoneType", "args": [{"type": "int", "name": "time"}], "type": "function", "api": "set_ink_in_time"}, "ink_out_time": {"return_type": "NoneType", "args": [{"type": "int", "name": "time"}], "type": "function", "api": "set_ink_out_time"}, "state": {"return_type": "NoneType", "args": [{"type": "int", "name": "state"}], "type": "function", "api": "set_state"}, "fit4": {"return_type": "NoneType", "args": [{"type": "int", "name": "left"}, {"type": "int", "name": "right"}, {"type": "int", "name": "top"}, {"type": "int", "name": "bottom"}], "type": "function", "api": "set_fit4"}, "fit2": {"return_type": "NoneType", "args": [{"type": "int", "name": "hor"}, {"type": "int", "name": "ver"}], "type": "function", "api": "set_fit2"}, "layout": {"return_type": "NoneType", "args": [{"type": "int", "name": "layout"}], "type": "function", "api": "set_layout"}}, "btnm": {"pressed": {"return_type": "NoneType", "args": [{"type": "int", "name": "id"}], "type": "function", "api": "set_pressed"}, "btn_width": {"return_type": "NoneType", "args": [{"type": "int", "name": "btn_id"}, {"type": "int", "name": "width"}], "type": "function", "api": "set_btn_width"}, "ctrl_map": {"return_type": "NoneType", "args": [{"type": "mp_arr_to_lv_btnm_ctrl_t_____", "name": "ctrl_map"}], "type": "function", "api": "set_ctrl_map"}, "map": {"return_type": "NoneType", "args": [{"type": "mp_arr_to_char_ptr____", "name": "map"}], "type": "function", "api": "set_map"}, "btn_ctrl": {"return_type": "NoneType", "args": [{"type": "int", "name": "btn_id"}, {"type": "int", "name": "ctrl"}], "type": "function", "api": "set_btn_ctrl"}, "one_toggle": {"return_type": "NoneType", "args": [{"type": "bool", "name": "one_toggle"}], "type": "function", "api": "set_one_toggle"}, "recolor": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_recolor"}, "btn_ctrl_all": {"return_type": "NoneType", "args": [{"type": "int", "name": "ctrl"}], "type": "function", "api": "set_btn_ctrl_all"}}, "canvas": {"buffer": {"return_type": "NoneType", "args": [{"type": "pointer", "name": "buf"}, {"type": "int", "name": "w"}, {"type": "int", "name": "h"}, {"type": "int", "name": "cf"}], "type": "function", "api": "set_buffer"}, "palette": {"return_type": "NoneType", "args": [{"type": "int", "name": "id"}, {"type": "color32_t", "name": "c"}], "type": "function", "api": "set_palette"}, "px": {"return_type": "NoneType", "args": [{"type": "int", "name": "x"}, {"type": "int", "name": "y"}, {"type": "color32_t", "name": "c"}], "type": "function", "api": "set_px"}}, "img": {"src": {"return_type": "NoneType", "args": [{"type": "pointer", "name": "src_img"}], "type": "function", "api": "set_src"}, "offx": {"return_type": "NoneType", "args": [{"type": "int", "name": "x"}], "type": "function", "api": "set_offset_x"}, "offy": {"return_type": "NoneType", "args": [{"type": "int", "name": "y"}], "type": "function", "api": "set_offset_y"}, "auto_size": {"return_type": "NoneType", "args": [{"type": "bool", "name": "autosize_en"}], "type": "function", "api": "set_auto_size"}}, "label": {"array_text": {"return_type": "NoneType", "args": [{"type": "str", "name": "array"}, {"type": "int", "name": "size"}], "type": "function", "api": "set_array_text"}, "text_sel_end": {"return_type": "NoneType", "args": [{"type": "int", "name": "index"}], "type": "function", "api": "set_text_sel_end"}, "long_mode": {"return_type": "NoneType", "args": [{"type": "int", "name": "long_mode"}], "type": "function", "api": "set_long_mode"}, "text_sel_start": {"return_type": "NoneType", "args": [{"type": "int", "name": "index"}], "type": "function", "api": "set_text_sel_start"}, "text_fmt": {"return_type": "NoneType", "args": [{"type": "str", "name": "fmt"}], "type": "function", "api": "set_text_fmt"}, "anim_speed": {"return_type": "NoneType", "args": [{"type": "int", "name": "anim_speed"}], "type": "function", "api": "set_anim_speed"}, "body_draw": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_body_draw"}, "recolor": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_recolor"}, "text": {"return_type": "NoneType", "args": [{"type": "str", "name": "text"}], "type": "function", "api": "set_text"}, "align": {"return_type": "NoneType", "args": [{"type": "int", "name": "align"}], "type": "function", "api": "set_align"}, "static_text": {"return_type": "NoneType", "args": [{"type": "str", "name": "text"}], "type": "function", "api": "set_static_text"}}, "sw": {"anim_time": {"return_type": "NoneType", "args": [{"type": "int", "name": "anim_time"}], "type": "function", "api": "set_anim_time"}}, "ddlist": {"anim_time": {"return_type": "NoneType", "args": [{"type": "int", "name": "anim_time"}], "type": "function", "api": "set_anim_time"}, "stay_open": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_stay_open"}, "fix_height": {"return_type": "NoneType", "args": [{"type": "int", "name": "h"}], "type": "function", "api": "set_fix_height"}, "draw_arrow": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_draw_arrow"}, "fix_width": {"return_type": "NoneType", "args": [{"type": "int", "name": "w"}], "type": "function", "api": "set_fix_width"}, "selected": {"return_type": "NoneType", "args": [{"type": "int", "name": "sel_opt"}], "type": "function", "api": "set_selected"}, "sb_mode": {"return_type": "NoneType", "args": [{"type": "int", "name": "mode"}], "type": "function", "api": "set_sb_mode"}, "options": {"return_type": "NoneType", "args": [{"type": "str", "name": "options"}], "type": "function", "api": "set_options"}, "align": {"return_type": "NoneType", "args": [{"type": "int", "name": "align"}], "type": "function", "api": "set_align"}}, "win": {"btn_size": {"return_type": "NoneType", "args": [{"type": "int", "name": "size"}], "type": "function", "api": "set_btn_size"}, "anim_time": {"return_type": "NoneType", "args": [{"type": "int", "name": "anim_time"}], "type": "function", "api": "set_anim_time"}, "content_size": {"return_type": "NoneType", "args": [{"type": "int", "name": "w"}, {"type": "int", "name": "h"}], "type": "function", "api": "set_content_size"}, "layout": {"return_type": "NoneType", "args": [{"type": "int", "name": "layout"}], "type": "function", "api": "set_layout"}, "title": {"return_type": "NoneType", "args": [{"type": "str", "name": "title"}], "type": "function", "api": "set_title"}, "sb_mode": {"return_type": "NoneType", "args": [{"type": "int", "name": "sb_mode"}], "type": "function", "api": "set_sb_mode"}}, "lmeter": {"scale": {"return_type": "NoneType", "args": [{"type": "int", "name": "angle"}, {"type": "int", "name": "line_cnt"}], "type": "function", "api": "set_scale"}, "range": {"return_type": "NoneType", "args": [{"type": "int", "name": "min"}, {"type": "int", "name": "max"}], "type": "function", "api": "set_range"}, "value": {"return_type": "NoneType", "args": [{"type": "int", "name": "value"}], "type": "function", "api": "set_value"}, "angle_offset": {"return_type": "NoneType", "args": [{"type": "int", "name": "angle"}], "type": "function", "api": "set_angle_offset"}}, "ta": {"max_length": {"return_type": "NoneType", "args": [{"type": "int", "name": "num"}], "type": "function", "api": "set_max_length"}, "pwd_show_time": {"return_type": "NoneType", "args": [{"type": "int", "name": "time"}], "type": "function", "api": "set_pwd_show_time"}, "placeholder_text": {"return_type": "NoneType", "args": [{"type": "str", "name": "txt"}], "type": "function", "api": "set_placeholder_text"}, "text_align": {"return_type": "NoneType", "args": [{"type": "int", "name": "align"}], "type": "function", "api": "set_text_align"}, "cursor_pos": {"return_type": "NoneType", "args": [{"type": "int", "name": "pos"}], "type": "function", "api": "set_cursor_pos"}, "text": {"return_type": "NoneType", "args": [{"type": "str", "name": "txt"}], "type": "function", "api": "set_text"}, "sb_mode": {"return_type": "NoneType", "args": [{"type": "int", "name": "mode"}], "type": "function", "api": "set_sb_mode"}, "one_line": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_one_line"}, "cursor_blink_time": {"return_type": "NoneType", "args": [{"type": "int", "name": "time"}], "type": "function", "api": "set_cursor_blink_time"}, "text_sel": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_text_sel"}, "cursor_click_pos": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_cursor_click_pos"}, "scroll_propagation": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_scroll_propagation"}, "accepted_chars": {"return_type": "NoneType", "args": [{"type": "str", "name": "list"}], "type": "function", "api": "set_accepted_chars"}, "pwd_mode": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_pwd_mode"}, "edge_flash": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_edge_flash"}, "insert_replace": {"return_type": "NoneType", "args": [{"type": "str", "name": "txt"}], "type": "function", "api": "set_insert_replace"}, "cursor_type": {"return_type": "NoneType", "args": [{"type": "int", "name": "cur_type"}], "type": "function", "api": "set_cursor_type"}}, "preload": {"spin_time": {"return_type": "NoneType", "args": [{"type": "int", "name": "time"}], "type": "function", "api": "set_spin_time"}, "dir": {"return_type": "NoneType", "args": [{"type": "int", "name": "dir"}], "type": "function", "api": "set_dir"}, "arc_length": {"return_type": "NoneType", "args": [{"type": "int", "name": "deg"}], "type": "function", "api": "set_arc_length"}, "type": {"return_type": "NoneType", "args": [{"type": "int", "name": "type"}], "type": "function", "api": "set_type"}}, "led": {"bright": {"return_type": "NoneType", "args": [{"type": "int", "name": "bright"}], "type": "function", "api": "set_bright"}}, "spinbox": {"range": {"return_type": "NoneType", "args": [{"type": "int", "name": "range_min"}, {"type": "int", "name": "range_max"}], "type": "function", "api": "set_range"}, "value": {"return_type": "NoneType", "args": [{"type": "int", "name": "i"}], "type": "function", "api": "set_value"}, "padding_left": {"return_type": "NoneType", "args": [{"type": "int", "name": "padding"}], "type": "function", "api": "set_padding_left"}, "step": {"return_type": "NoneType", "args": [{"type": "int", "name": "step"}], "type": "function", "api": "set_step"}, "digit_format": {"return_type": "NoneType", "args": [{"type": "int", "name": "digit_count"}, {"type": "int", "name": "separator_position"}], "type": "function", "api": "set_digit_format"}}, "chart": {"margin": {"return_type": "NoneType", "args": [{"type": "int", "name": "margin"}], "type": "function", "api": "set_margin"}, "secondary_y_tick_length": {"return_type": "NoneType", "args": [{"type": "int", "name": "major_tick_len"}, {"type": "int", "name": "minor_tick_len"}], "type": "function", "api": "set_secondary_y_tick_length"}, "next": {"return_type": "NoneType", "args": [{"type": "chart_series_t", "name": "ser"}, {"type": "int", "name": "y"}], "type": "function", "api": "set_next"}, "series_opa": {"return_type": "NoneType", "args": [{"type": "int", "name": "opa"}], "type": "function", "api": "set_series_opa"}, "range": {"return_type": "NoneType", "args": [{"type": "int", "name": "ymin"}, {"type": "int", "name": "ymax"}], "type": "function", "api": "set_range"}, "series_darking": {"return_type": "NoneType", "args": [{"type": "int", "name": "dark_eff"}], "type": "function", "api": "set_series_darking"}, "point_count": {"return_type": "NoneType", "args": [{"type": "int", "name": "point_cnt"}], "type": "function", "api": "set_point_count"}, "points": {"return_type": "NoneType", "args": [{"type": "chart_series_t", "name": "ser"}, {"type": "mp_arr_to_lv_coord_t_____", "name": "y_array"}], "type": "function", "api": "set_points"}, "y_tick_length": {"return_type": "NoneType", "args": [{"type": "int", "name": "major_tick_len"}, {"type": "int", "name": "minor_tick_len"}], "type": "function", "api": "set_y_tick_length"}, "y_tick_texts": {"return_type": "NoneType", "args": [{"type": "str", "name": "list_of_values"}, {"type": "int", "name": "num_tick_marks"}, {"type": "int", "name": "options"}], "type": "function", "api": "set_y_tick_texts"}, "secondary_y_tick_texts": {"return_type": "NoneType", "args": [{"type": "str", "name": "list_of_values"}, {"type": "int", "name": "num_tick_marks"}, {"type": "int", "name": "options"}], "type": "function", "api": "set_secondary_y_tick_texts"}, "x_tick_texts": {"return_type": "NoneType", "args": [{"type": "str", "name": "list_of_values"}, {"type": "int", "name": "num_tick_marks"}, {"type": "int", "name": "options"}], "type": "function", "api": "set_x_tick_texts"}, "type": {"return_type": "NoneType", "args": [{"type": "int", "name": "type"}], "type": "function", "api": "set_type"}, "div_line_count": {"return_type": "NoneType", "args": [{"type": "int", "name": "hdiv"}, {"type": "int", "name": "vdiv"}], "type": "function", "api": "set_div_line_count"}, "series_width": {"return_type": "NoneType", "args": [{"type": "int", "name": "width"}], "type": "function", "api": "set_series_width"}, "x_tick_length": {"return_type": "NoneType", "args": [{"type": "int", "name": "major_tick_len"}, {"type": "int", "name": "minor_tick_len"}], "type": "function", "api": "set_x_tick_length"}, "update_mode": {"return_type": "NoneType", "args": [{"type": "int", "name": "update_mode"}], "type": "function", "api": "set_update_mode"}}, "slider": {"anim_time": {"return_type": "NoneType", "args": [{"type": "int", "name": "anim_time"}], "type": "function", "api": "set_anim_time"}, "knob_in": {"return_type": "NoneType", "args": [{"type": "bool", "name": "in"}], "type": "function", "api": "set_knob_in"}, "range": {"return_type": "NoneType", "args": [{"type": "int", "name": "min"}, {"type": "int", "name": "max"}], "type": "function", "api": "set_range"}, "value": {"return_type": "NoneType", "args": [{"type": "int", "name": "value"}, {"type": "int", "name": "anim"}], "type": "function", "api": "set_value"}, "sym": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_sym"}}, "obj": {}, "line": {"points": {"return_type": "NoneType", "args": [{"type": "mp_arr_to_lv_point_t_____", "name": "point_a"}, {"type": "int", "name": "point_num"}], "type": "function", "api": "set_points"}, "auto_size": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_auto_size"}, "y_invert": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_y_invert"}}, "roller": {"anim_time": {"return_type": "NoneType", "args": [{"type": "int", "name": "anim_time"}], "type": "function", "api": "set_anim_time"}, "fix_width": {"return_type": "NoneType", "args": [{"type": "int", "name": "w"}], "type": "function", "api": "set_fix_width"}, "selected": {"return_type": "NoneType", "args": [{"type": "int", "name": "sel_opt"}, {"type": "int", "name": "anim"}], "type": "function", "api": "set_selected"}, "options": {"return_type": "NoneType", "args": [{"type": "str", "name": "options"}, {"type": "int", "name": "mode"}], "type": "function", "api": "set_options"}, "align": {"return_type": "NoneType", "args": [{"type": "int", "name": "align"}], "type": "function", "api": "set_align"}, "visible_row_count": {"return_type": "NoneType", "args": [{"type": "int", "name": "row_cnt"}], "type": "function", "api": "set_visible_row_count"}}, "kb": {"ctrl_map": {"return_type": "NoneType", "args": [{"type": "mp_arr_to_lv_btnm_ctrl_t_____", "name": "ctrl_map"}], "type": "function", "api": "set_ctrl_map"}, "map": {"return_type": "NoneType", "args": [{"type": "mp_arr_to_char_ptr____", "name": "map"}], "type": "function", "api": "set_map"}, "cursor_manage": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_cursor_manage"}, "mode": {"return_type": "NoneType", "args": [{"type": "int", "name": "mode"}], "type": "function", "api": "set_mode"}, "ta": {"return_type": "NoneType", "args": [{"type": "object", "name": "ta"}], "type": "function", "api": "set_ta"}}, "bar": {"anim_time": {"return_type": "NoneType", "args": [{"type": "int", "name": "anim_time"}], "type": "function", "api": "set_anim_time"}, "range": {"return_type": "NoneType", "args": [{"type": "int", "name": "min"}, {"type": "int", "name": "max"}], "type": "function", "api": "set_range"}, "value": {"return_type": "NoneType", "args": [{"type": "int", "name": "value"}, {"type": "int", "name": "anim"}], "type": "function", "api": "set_value"}, "sym": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_sym"}}, "imgbtn": {"src": {"return_type": "NoneType", "args": [{"type": "int", "name": "state"}, {"type": "pointer", "name": "src"}], "type": "function", "api": "set_src"}, "toggle": {"return_type": "NoneType", "args": [{"type": "bool", "name": "tgl"}], "type": "function", "api": "set_toggle"}, "state": {"return_type": "NoneType", "args": [{"type": "int", "name": "state"}], "type": "function", "api": "set_state"}}, "tabview": {"sliding": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_sliding"}, "anim_time": {"return_type": "NoneType", "args": [{"type": "int", "name": "anim_time"}], "type": "function", "api": "set_anim_time"}, "tab_act": {"return_type": "NoneType", "args": [{"type": "int", "name": "id"}, {"type": "int", "name": "anim"}], "type": "function", "api": "set_tab_act"}, "btns_hidden": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_btns_hidden"}, "btns_pos": {"return_type": "NoneType", "args": [{"type": "int", "name": "btns_pos"}], "type": "function", "api": "set_btns_pos"}}, "list": {"anim_time": {"return_type": "NoneType", "args": [{"type": "int", "name": "anim_time"}], "type": "function", "api": "set_anim_time"}, "scroll_propagation": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_scroll_propagation"}, "single_mode": {"return_type": "NoneType", "args": [{"type": "bool", "name": "mode"}], "type": "function", "api": "set_single_mode"}, "edge_flash": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_edge_flash"}, "btn_selected": {"return_type": "NoneType", "args": [{"type": "object", "name": "btn"}], "type": "function", "api": "set_btn_selected"}, "layout": {"return_type": "NoneType", "args": [{"type": "int", "name": "layout"}], "type": "function", "api": "set_layout"}, "sb_mode": {"return_type": "NoneType", "args": [{"type": "int", "name": "mode"}], "type": "function", "api": "set_sb_mode"}}, "cpicker": {"hue": {"return_value": "bool", "args": [{"type": "int", "name": "hue"}], "type": "function", "api": "set_hue"}, "value": {"return_value": "bool", "args": [{"type": "int", "name": "val"}], "type": "function", "api": "set_value"}, "hsv": {"return_value": "bool", "args": [{"type": "color_hsv_t", "name": "hsv"}], "type": "function", "api": "set_hsv"}, "color": {"return_value": "bool", "args": [{"type": "color32_t", "name": "color"}], "type": "function", "api": "set_color"}, "color_mode": {"return_type": "NoneType", "args": [{"type": "int", "name": "mode"}], "type": "function", "api": "set_color_mode"}, "indic_colored": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_indic_colored"}, "type": {"return_type": "NoneType", "args": [{"type": "int", "name": "type"}], "type": "function", "api": "set_type"}, "saturation": {"return_value": "bool", "args": [{"type": "int", "name": "saturation"}], "type": "function", "api": "set_saturation"}, "color_mode_fixed": {"return_type": "NoneType", "args": [{"type": "bool", "name": "fixed"}], "type": "function", "api": "set_color_mode_fixed"}, "preview": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_preview"}}, "mbox": {"anim_time": {"return_type": "NoneType", "args": [{"type": "int", "name": "anim_time"}], "type": "function", "api": "set_anim_time"}, "recolor": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_recolor"}, "text": {"return_type": "NoneType", "args": [{"type": "str", "name": "txt"}], "type": "function", "api": "set_text"}}, "page": {"scrl_fit2": {"return_type": "NoneType", "args": [{"type": "int", "name": "hor"}, {"type": "int", "name": "ver"}], "type": "function", "api": "set_scrl_fit2"}, "scrl_fit4": {"return_type": "NoneType", "args": [{"type": "int", "name": "left"}, {"type": "int", "name": "right"}, {"type": "int", "name": "top"}, {"type": "int", "name": "bottom"}], "type": "function", "api": "set_scrl_fit4"}, "anim_time": {"return_type": "NoneType", "args": [{"type": "int", "name": "anim_time"}], "type": "function", "api": "set_anim_time"}, "scrl_fit": {"return_type": "NoneType", "args": [{"type": "int", "name": "fit"}], "type": "function", "api": "set_scrl_fit"}, "scrl_layout": {"return_type": "NoneType", "args": [{"type": "int", "name": "layout"}], "type": "function", "api": "set_scrl_layout"}, "scroll_propagation": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_scroll_propagation"}, "scrl_height": {"return_type": "NoneType", "args": [{"type": "int", "name": "h"}], "type": "function", "api": "set_scrl_height"}, "scrl_width": {"return_type": "NoneType", "args": [{"type": "int", "name": "w"}], "type": "function", "api": "set_scrl_width"}, "edge_flash": {"return_type": "NoneType", "args": [{"type": "bool", "name": "en"}], "type": "function", "api": "set_edge_flash"}, "sb_mode": {"return_type": "NoneType", "args": [{"type": "int", "name": "sb_mode"}], "type": "function", "api": "set_sb_mode"}}} -------------------------------------------------------------------------------- /src/compiler.js: -------------------------------------------------------------------------------- 1 | /* generate the C or Python code to a file */ 2 | 3 | function python_generator(info, widget) { 4 | let code = []; 5 | 6 | for (const key in info) { 7 | let id = key; 8 | 9 | let par_id = info[key].parent; 10 | 11 | let type = info[key].type; 12 | 13 | code.push(template_py_create(id, par_id, type)); //code: create, EX: btn0 = lv.btn(scr) 14 | 15 | if (info[id].cb) { 16 | code.push(template_py_cb(id)); 17 | } 18 | 19 | const attributes = info[key].attributes; 20 | for (const attr of attributes) { 21 | let value = widget[id][attr]; 22 | if (value == true) { 23 | value = "True"; 24 | } else if (value == false) { 25 | value = "False"; 26 | } 27 | code.push(template_py_setter_simple(id, attr, value)); 28 | } 29 | } 30 | return code.join("\n"); 31 | } 32 | 33 | 34 | function c_generator(info, widget) { 35 | let body = [], cb = []; 36 | 37 | for (const key in info) { 38 | let id = key; 39 | 40 | let par_id = info[key].parent; 41 | 42 | let type = info[key].type; 43 | 44 | if(info[key].cb) { 45 | cb.push(id); 46 | } 47 | 48 | body.push(template_c_create(id, par_id, type)); //code: create, EX: btn0 = lv.btn(scr) 49 | 50 | const attributes = info[key].attributes; 51 | for (const attr of attributes) { 52 | let value = widget[id][attr]; 53 | 54 | body.push(template_c_setter_simple(id, "obj", attr, value)); 55 | } 56 | } 57 | let cb_s = []; 58 | for (const id of cb) { 59 | cb_s.push(template_c_cb(id)); 60 | } 61 | 62 | return template_c_all(body.join("\n"), cb_s.join("\n")); 63 | } -------------------------------------------------------------------------------- /src/data.js: -------------------------------------------------------------------------------- 1 | ELEMENT.locale(ELEMENT.lang.en) //i18n 2 | 3 | const WidgetsOption = [ 4 | { 5 | value: 'obj', 6 | label: "Object" 7 | }, 8 | { 9 | value: 'form', 10 | label: 'Form', 11 | children: [ 12 | { 13 | value: 'btn', 14 | label: "Button" 15 | }, 16 | { 17 | value: 'label', 18 | label: "Label", 19 | }, 20 | { 21 | value: 'sw', 22 | label: "Switch" 23 | }, 24 | { 25 | value: 'cb', 26 | label: "Checkbox" 27 | }, 28 | { 29 | value: 'ddlist', 30 | label: "Drop-Down List" 31 | }, 32 | { 33 | value: 'roller', 34 | label: "Roller" 35 | }, 36 | { 37 | value: 'slider', 38 | label: "Slider" 39 | }, 40 | ], 41 | }, 42 | { 43 | value: 'data', 44 | label: 'Data', 45 | children: [ 46 | { 47 | value: 'bar', 48 | label: "Bar" 49 | }, 50 | { 51 | value: 'gauge', 52 | label: "Gauge" 53 | }, 54 | { 55 | value: 'led', 56 | label: "LED" 57 | }, 58 | { 59 | value: 'chart', 60 | label: "Chart" 61 | }, 62 | { 63 | value: 'arc', 64 | label: "Arc" 65 | }, 66 | { 67 | value: 'calendar', 68 | label: "Calendar" 69 | }, 70 | { 71 | value: 'lmeter', 72 | label: "Line meter" 73 | }, 74 | { 75 | value: 'preload', 76 | label: "Preloader" 77 | } 78 | ] 79 | }, 80 | { 81 | value: 'layer', 82 | label: 'Layer', 83 | children: [ 84 | { 85 | value: 'page', 86 | label: "Page" 87 | }, 88 | { 89 | value: 'cont', 90 | label: "Container" 91 | }, 92 | { 93 | value: 'win', 94 | label: "Window" 95 | }, 96 | { 97 | value: 'mbox', 98 | label: "Message box" 99 | } 100 | ] 101 | } 102 | 103 | ] 104 | 105 | 106 | //The Python code to Initialize the environment 107 | const EnvInitCode = [ 108 | "import ujson", 109 | "import lvgl as lv", 110 | "lv.init()", 111 | "import SDL", 112 | "SDL.init()", 113 | /* Register SDL display driver. */ 114 | "disp_buf1 = lv.disp_buf_t()", 115 | "buf1_1 = bytes(960*10)", 116 | "lv.disp_buf_init(disp_buf1,buf1_1, None, len(buf1_1)//4)", 117 | "disp_drv = lv.disp_drv_t()", 118 | "lv.disp_drv_init(disp_drv)", 119 | "disp_drv.buffer = disp_buf1", 120 | "disp_drv.flush_cb = SDL.monitor_flush", 121 | "disp_drv.hor_res = 480", 122 | "disp_drv.ver_res = 320", 123 | "lv.disp_drv_register(disp_drv)", 124 | /*Regsiter SDL mouse driver*/ 125 | "indev_drv = lv.indev_drv_t()", 126 | "lv.indev_drv_init(indev_drv)", 127 | "indev_drv.type = lv.INDEV_TYPE.POINTER;", 128 | "indev_drv.read_cb = SDL.mouse_read;", 129 | "lv.indev_drv_register(indev_drv);", 130 | /* Create a screen with a button and a label */ 131 | "screen = lv.obj()", 132 | /* Load the screen */ 133 | "lv.scr_load(screen)", 134 | "baseAttr = dir(lv.obj)" 135 | ]; 136 | 137 | 138 | /* Define special function for python*/ 139 | 140 | // old getobjattr() function: 141 | // "def getobjattr(obj,id,type_s):", 142 | // " d={}", 143 | // " d['id']=id", 144 | // " for i in dir(obj):", 145 | // " if 'get_' in i:", 146 | // " try:", 147 | // " ret = eval(id + '.' + i + '()')", 148 | // " if isinstance(ret, (int,float,str,bool)):", 149 | // " d[i] = ret", 150 | // " except:", 151 | // " pass", 152 | // " for i in ATTR:", 153 | // " d[i]=eval(id+'.'+ATTR[i]+'()')", 154 | // " print('\x06'+ujson.dumps(d)+'\x15')", 155 | 156 | const QueryCode = [ 157 | //Get and send JSON format text 158 | "def query_attr(obj,id,type_s):", 159 | " d={}", 160 | " d['id']=id", 161 | " for i in ATTR['obj']:", 162 | " d[i]=eval(id+'.'+ATTR['obj'][i]+'()')", 163 | " print('\x06'+ujson.dumps(d)+'\x15')", 164 | "def query_xy(obj,id):", 165 | " d={'id':id,'x':obj.get_x(),'y':obj.get_y()}", 166 | " print('\x06'+ujson.dumps(d)+'\x15')", 167 | //Callback: only for the lv.EVENT.DRAG_END 168 | "def walv_callback(obj,id,event):", 169 | " if event == lv.EVENT.DRAG_END:", 170 | " query_xy(obj, id)" 171 | ]; 172 | 173 | 174 | const Getter = { 175 | "obj": { 176 | "x": "get_x", 177 | "y": "get_y", 178 | "width": "get_width", 179 | "height": "get_height", 180 | "drag": "get_drag", 181 | "click": "get_click", 182 | "hidden": "get_hidden", 183 | "top": "get_top", 184 | }, 185 | 186 | "label": { 187 | "label": "get_text", 188 | } 189 | 190 | } 191 | 192 | const Setter = { 193 | "obj": { 194 | "x": "set_x", 195 | "y": "set_y", 196 | "width": "set_width", 197 | "height": "set_height", 198 | "drag": "set_drag", 199 | "click": "set_click", 200 | "hidden": "set_hidden", 201 | "top": "set_top", 202 | }, 203 | "btn": { 204 | "state": "set_state", 205 | "toggle": "set_toggle", 206 | "ink_wait_time": "set_ink_wait_time", 207 | "ink_in_time": "set_ink_in_time", 208 | }, 209 | "label": { 210 | "text": "set_text", 211 | }, 212 | "led": { 213 | 214 | } 215 | } 216 | 217 | -------------------------------------------------------------------------------- /src/gen_apis_js.py: -------------------------------------------------------------------------------- 1 | # Generate the setter table for objects 2 | # https://github.com/littlevgl/lv_gui_builder/issues/1#issuecomment-528845543 3 | # Parse the lv_mpy.json(example: https://raw.githubusercontent.com/littlevgl/lv_binding_micropython/master/gen/lv_mpy_example.json) 4 | 5 | import json 6 | 7 | Setter = {} 8 | path = '../../lv_mpy_example.json' # Path to lv_mpy_example.json 9 | 10 | with open(path) as f: 11 | data = json.load(f) 12 | objs = data['objects'] 13 | for o in objs: 14 | # print(o) 15 | tmp = {} 16 | for fn in objs[o]['members'].keys(): 17 | if fn.startswith('set_') and fn not in objs['obj']['members'].keys(): 18 | name = fn.replace('set_', '') 19 | tmp[name] = objs[o]['members'][fn] 20 | tmp[name]['api'] = fn 21 | del tmp[name]['args'][0] # The first argument is about type 22 | Setter[o] = tmp 23 | # del Setter['obj'] 24 | 25 | # We also need to add 'null:{}' in apis.js 26 | f = open('apis.js', 'w') 27 | f.write("const setter = "); 28 | json.dump(Setter, f) 29 | f.close() 30 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | 2 | var vm = null; 3 | 4 | 5 | window.onload = function() { 6 | vm = new Vue(WALV_MAIN); 7 | 8 | /* Initialize the wasm mpy */ 9 | mpylvInit(vm); 10 | 11 | /* Initialize the ace editor */ 12 | editorInit(vm); 13 | 14 | document.title = "WALV: The Online Designer For LittlevGL"; 15 | 16 | 17 | } 18 | 19 | 20 | 21 | const mpylvInit = (vm) => { 22 | 23 | Module.canvas = document.getElementById("canvas"); 24 | 25 | /* Bind mp_js_stdout */ 26 | mp_js_stdout = document.getElementById('mp_js_stdout'); 27 | mp_js_stdout.value = ""; 28 | 29 | /* Initialize the xtermjs */ 30 | Terminal.applyAddon(fit); 31 | var term = new Terminal({ 32 | cursorBlink: true, 33 | // theme: { 34 | // background: '#fdf6e3' 35 | // } 36 | }); 37 | term.open(document.getElementById("mpy_repl"), true); 38 | term.fit(); 39 | term.write('Welcome To \x1B[1;3;31mWALV\x1B[0m'); 40 | 41 | /*Initialize MicroPython itself*/ 42 | mp_js_init(8 * 1024 * 1024); 43 | 44 | /*Setup printing event handler*/ 45 | mp_js_stdout.addEventListener('print', function(e) { 46 | // console.log(e.data); 47 | term.write(vm.handleOutput(e.data)); 48 | }, false); 49 | 50 | /*Setup key input handler */ 51 | term.on('data', function(key, e) { 52 | for(var i = 0; i < key.length; i++) { 53 | mp_js_process_char(key.charCodeAt(i)); 54 | } 55 | }); 56 | 57 | vm.Term = term; 58 | 59 | /* Run init script */ 60 | mp_js_do_str(EnvInitCode.join('\n')); 61 | 62 | /* Add function querry_attr() & walv_callback() */ 63 | mp_js_do_str(QueryCode.join('\n')); 64 | wrap_equal("ATTR", JSON.stringify(Getter)); //Add ATTR to mpy, ATTR is common getter 65 | 66 | /*Setup lv_task_handler loop*/ 67 | var the_mp_handle_pending = Module.cwrap('mp_handle_pending', null); 68 | function handle_pending() { 69 | the_mp_handle_pending(); 70 | setTimeout(handle_pending, 10); // should call lv_task_handler() 71 | } 72 | 73 | /*Initialize the REPL.*/ 74 | mp_js_init_repl(); 75 | 76 | /*Start the main loop, asynchronously.*/ 77 | handle_pending(); 78 | } 79 | 80 | // Init the ace editor 81 | const editorInit = (vm) => { 82 | let editor = ace.edit("code-editor"); 83 | editor.getSession().setUseWrapMode(true); 84 | editor.setAutoScrollEditorIntoView(true); 85 | editor.setFontSize(15); 86 | editor.resize(); 87 | let c_edit_mode = ace.require("ace/mode/c_cpp").Mode; 88 | let py_edit_mode = ace.require("ace/mode/python").Mode; 89 | editor.session.setMode(new py_edit_mode()); 90 | editor.setOptions({maxLines: "200px" }); 91 | vm.editor = editor; 92 | vm.py_edit_mode = py_edit_mode; 93 | vm.c_edit_mode = c_edit_mode; 94 | } 95 | 96 | 97 | const WALV_MAIN = { 98 | el: "#walv", 99 | 100 | data: { 101 | editor: null, 102 | c_edit_mode: null, 103 | py_edit_mode: null, 104 | is_c_mode: false, //true: c, false: python 105 | Term: null, 106 | 107 | buffer: [], 108 | str_json: "", 109 | mask: false, 110 | currentWidget: {}, // The Attributes 111 | posJSON: {}, 112 | WidgetPool: {}, 113 | InfoPool: {}, 114 | 115 | setter: setter, 116 | currentType: null, 117 | 118 | //Simulator 119 | cursorX: 0, 120 | cursorY: 0, 121 | 122 | //Creator 123 | creator_options: WidgetsOption, 124 | props: {emitPath: false, expandTrigger: 'hover'}, 125 | selectedType: "", 126 | widgetNum: 0, 127 | Count: 0, 128 | 129 | //TreeView 130 | widget_tree: [ 131 | { 132 | label: "screen", 133 | children: [] 134 | }, 135 | // For invisible 136 | { 137 | label: "", 138 | children: [] 139 | } 140 | ], 141 | // Which node in TreeView was checked 142 | checkedNode: { 143 | id: null, 144 | obj: null, 145 | // type: null, // DEPRECATED 146 | }, 147 | 148 | //Terminal 149 | term_visible: true, 150 | 151 | // Style Editor 152 | style_visible: false, 153 | style: { 154 | body: { 155 | main_color: null, 156 | grad_color: null, 157 | }, 158 | text: { 159 | color: "#409EFF", 160 | font: "font_roboto_16", 161 | }, 162 | image: { 163 | 164 | }, 165 | line: { 166 | 167 | }, 168 | } 169 | }, 170 | 171 | 172 | watch: { 173 | //Parse string to JSON 174 | str_json: function() { 175 | try { 176 | 177 | let tmp = JSON.parse(this.str_json); 178 | if(Object.keys(tmp).length == 3) { 179 | this.posJSON = tmp; 180 | 181 | //Update Postion 182 | this.WidgetPool[tmp['id']]['x'] = this.posJSON['x']; 183 | this.WidgetPool[tmp['id']]['y'] = this.posJSON['y']; 184 | 185 | this.changeInfo(tmp['id'], 'x'); 186 | this.changeInfo(tmp['id'], 'y'); 187 | 188 | // Change the Setting to show the widget that was just moved. 189 | this.currentWidget = this.WidgetPool[tmp['id']]; 190 | this.currentType = this.InfoPool[tmp['id']]['type']; 191 | 192 | this.drawRect(this.currentWidget.x, this.currentWidget.y, this.currentWidget.width, this.currentWidget.height); 193 | } else { 194 | this.WidgetPool[tmp['id']] = tmp; 195 | this.currentWidget = this.WidgetPool[tmp['id']]; 196 | } 197 | } catch (error) { 198 | alert(error); 199 | } 200 | }, 201 | 202 | }, 203 | 204 | methods: { 205 | // Handle the information(strats with \x06, end with \x15) 206 | handleOutput: function(text) { 207 | if(text == '\x15') //End: '\x15' 208 | { 209 | this.mask = false; 210 | this.str_json = this.buffer.join(''); 211 | } 212 | if(this.mask) 213 | { 214 | this.buffer.push(text); 215 | text = ""; 216 | } 217 | if(text == '\x06') //Begin: '\x06' 218 | { 219 | this.mask = true; 220 | } 221 | 222 | if(text == '\n') 223 | { 224 | this.buffer.splice(0, this.buffer.length); 225 | } 226 | return text; 227 | }, 228 | 229 | Creator: function() { 230 | if (this.selectedType == "") { 231 | this.$message({ 232 | message: 'Please select a type', 233 | type: 'error' 234 | }); 235 | return; 236 | } else { 237 | let parent_id = this.getCurrentID(); 238 | if (parent_id === null) { 239 | this.$message({ 240 | message: 'You must choose a widget!', 241 | type: 'error' 242 | }); 243 | return; 244 | } 245 | if (parent_id == "") { 246 | this.$message({ 247 | message: 'You created a widget invisible', 248 | type: 'warning' 249 | }); 250 | 251 | } 252 | this.createWidget(this.selectedType, parent_id); 253 | } 254 | }, 255 | 256 | //Parametres are the String type 257 | createWidget: function(type, strPar) { 258 | var id = this.makeID(type); 259 | var par = strPar; 260 | 261 | wrap_create(id, par, type); 262 | 263 | //TODO: BUG 264 | this.appendNode(id); 265 | 266 | //** walv saves the inital info to WidgetPool && InfoPool 267 | 268 | //Store Info that a widget was created from. 269 | this.addInfo(id, par, type); 270 | 271 | // Change currentType, walv will render new input for setters 272 | this.currentType = type; 273 | }, 274 | 275 | // Increase by 1 276 | makeID: function(type) { 277 | let id = type + (this.Count++).toString(16); 278 | this.widgetNum += 1; 279 | return id; 280 | }, 281 | 282 | // Append new node to TreeView 283 | appendNode(widget_name) { 284 | let new_child = { 285 | label: widget_name, 286 | children: [] }; 287 | let node = this.$refs.TreeView.getCurrentNode(); 288 | if (node != null) { 289 | node.children.push(new_child); 290 | } 291 | }, 292 | 293 | // Delete node and its childs(reverse) 294 | deleteNode: function() { 295 | const node = this.checkedNode.obj; 296 | const id = this.checkedNode.id; 297 | 298 | if (id == "screen" || id == "") { 299 | this.$message({ 300 | message: "You can't delete the screen or nothing!", 301 | type: 'error' 302 | }); 303 | return; // Not support delete screen now 304 | } 305 | // delete child 306 | let record = [id]; // Which child was deleted 307 | reverse_del_node(node.data, record); 308 | 309 | // delete itself 310 | const children = node.parent.data.children; 311 | const index = children.findIndex(d => d.label === id); 312 | wrap_delete(id); 313 | children.splice(index, 1); 314 | this.widgetNum -= record.length; 315 | 316 | // Clear this.checkedNode 317 | this.checkedNode.obj = null; 318 | this.checkedNode.id = null; 319 | 320 | // Remove the related info 321 | pool_delete(this.WidgetPool, record); 322 | pool_delete(this.InfoPool, record); 323 | this.currentWidget = this.WidgetPool['screen']; 324 | 325 | this.$message({ 326 | message: 'Delete sucessfully', 327 | type: 'success' 328 | }); 329 | }, 330 | 331 | // When the node is clicked, walv will: change the checkedNode, set new id for label, update the Setting 332 | // https://element.eleme.cn/#/en-US/component/tree 333 | clickNode: function(data, obj, tree_obj) { 334 | this.checkedNode.id = data.label; 335 | this.checkedNode.obj = obj; 336 | 337 | let id = data.label; 338 | if (id == "") {// NOTICE 339 | return; 340 | } 341 | // If WidgetPool doesn't has infomation of the widget 342 | if (this.WidgetPool[id] == undefined) { 343 | let type = "\'obj\'"; 344 | if (id != "screen") { 345 | type = this.InfoPool[id]['type']; 346 | } 347 | wrap_query_attr(id, type); 348 | } 349 | // if (id != 'screen') { 350 | // this.checkedNode.type = this.InfoPool[id]['type']; // TODO 351 | // } DEPRECATED 352 | this.currentWidget = this.WidgetPool[id]; 353 | }, 354 | 355 | // Update the X & Y below the Simulator 356 | cursorXY : function(event) { 357 | this.cursorX = 0 | (event.offsetX / 1.25); 358 | this.cursorY = 0 | (event.offsetY / 1.25); 359 | }, 360 | 361 | // Get the id of recently checked node 362 | getCurrentID: function() { 363 | return this.checkedNode.id; 364 | // node = this.$refs.TreeView.getCurrentNode() 365 | // if (node != null) { 366 | // return node.label; 367 | // } 368 | // return null; 369 | }, 370 | 371 | // Lock the widget, so it can't move anymore 372 | // lock_widget: function() { 373 | // let drag_state = this.currentWidget["get_drag"]; 374 | // if(drag_state == true) { 375 | // drag_state = "True"; 376 | // } else { 377 | // drag_state = "False"; 378 | // } 379 | 380 | // mp_js_do_str(this.currentWidget["id"] + ".set_drag(" + drag_state + ')'); 381 | // }, 382 | 383 | 384 | // Apply change to the widget: number 385 | bindWidgetNumerical: function(attribute) { 386 | 387 | let value = this.currentWidget[attribute]; 388 | 389 | if(value == null) { 390 | value = 0; 391 | } 392 | 393 | let id = this.currentWidget["id"]; 394 | 395 | wrap_simple_setter(id, attribute, value); 396 | 397 | this.changeInfo(id, attribute); 398 | }, 399 | 400 | // Apply change to the widget: boolean 401 | bindWidgetBool: function(attribute) { 402 | 403 | let value = this.currentWidget[attribute]; 404 | 405 | if(value == true) { 406 | value = "True" 407 | } else { 408 | value = "False" 409 | } 410 | 411 | let id = this.currentWidget["id"]; 412 | 413 | wrap_simple_setter(id, attribute, value); 414 | 415 | this.reverseInfo(id, attribute); 416 | }, 417 | 418 | bindWidgetSpecial: function(e, f) { 419 | let id = this.currentWidget["id"]; 420 | let api = f['api']; 421 | let params = e.target.value; 422 | if(params == "") { // If input nothing 423 | return; 424 | } 425 | wrap_setter_str(id, api, params); 426 | }, 427 | 428 | // Add some information for the new widget to InfoPool 429 | addInfo: function(id, par_name, type) { 430 | let info = { 431 | type: type, 432 | parent: par_name, 433 | cb: false, 434 | attributes: [], 435 | }; 436 | this.InfoPool[id] = info; 437 | }, 438 | 439 | // For text or number, save something in InfoPool 440 | changeInfo: function(id, attribute_name) { 441 | let index = this.InfoPool[id].attributes.indexOf(attribute_name); 442 | if (index == -1) { 443 | this.InfoPool[id].attributes.push(attribute_name); 444 | } 445 | }, 446 | 447 | // For boolean only, save something in InfoPool 448 | reverseInfo: function(id, attribute_name) { 449 | let index = this.InfoPool[id].attributes.indexOf(attribute_name); 450 | if (index != -1) { 451 | this.InfoPool[id].attributes.splice(index, 1); 452 | } else { 453 | this.InfoPool[id].attributes.push(attribute_name); 454 | } 455 | }, 456 | 457 | // User enable CallBack template 458 | enableCBInfo: function(id) { 459 | this.InfoPool[id].cb = true; 460 | }, 461 | 462 | refreshTerm: function() { 463 | this.Term.clear(); 464 | this.Term.write("\r\x1b[K>>> "); 465 | }, 466 | 467 | // Take a screenshot for the Simulator 468 | screenshot: function() { 469 | document.getElementById("canvas").toBlob((blob) => { 470 | saveAs(blob, "screenshot.png"); 471 | }); 472 | }, 473 | 474 | // Generate the code and print them to the editor. 475 | generateCode: function() { 476 | let preview_code = ""; 477 | if (this.is_c_mode) { 478 | preview_code = c_generator(this.InfoPool, this.WidgetPool); 479 | } else { 480 | preview_code = python_generator(this.InfoPool, this.WidgetPool); 481 | } 482 | this.editor.setValue(preview_code); 483 | this.$message({ 484 | message: 'Generate code sucessfully', 485 | type: 'success' 486 | }); 487 | }, 488 | 489 | // Export the code in editor as a file. 490 | exportCodeAsFile: function() { 491 | let code = this.editor.getValue(); 492 | this.$message({ 493 | message: 'Export file sucessfully', 494 | type: 'success' 495 | }); 496 | let blob = new Blob([code], {type: "text/plain;charset=utf-8"}); 497 | if (this.is_c_mode) { 498 | saveAs(blob, "lv_gui.h"); 499 | } else { 500 | saveAs(blob, "lv_gui.py"); 501 | } 502 | }, 503 | 504 | // Set the style 505 | makeStyle: function() { 506 | wrap_simple_style(this.currentWidget["id"], this.style); 507 | }, 508 | 509 | //Highlight object 510 | drawRect: (x, y, w, h) => { 511 | let ctx = document.getElementById("canvas").getContext("2d"); 512 | ctx.strokeStyle="red"; 513 | ctx.lineWidth = 2; 514 | ctx.setLineDash([5,5]); 515 | ctx.strokeRect(x, y, w, h); 516 | }, 517 | 518 | setArgs: (args) => { 519 | return setArgvs(args); 520 | } 521 | }, 522 | } 523 | 524 | window.addEventListener('beforeunload', (event) => { 525 | event.preventDefault(); 526 | event.returnValue = ''; 527 | }); -------------------------------------------------------------------------------- /src/template.js: -------------------------------------------------------------------------------- 1 | /* template: generate the related code and return as string */ 2 | 3 | const template_py_create = (id, parent_id, type) => { 4 | return `${id} = lv.${type}(${parent_id})`; 5 | } 6 | 7 | const template_py_setter_number = (id, attr, param) => { 8 | return `${id}.set_${attr}(${param})`; 9 | } 10 | 11 | const template_py_setter_boolean = (id, attr, param) => { 12 | let value = "True"; 13 | if (param == false) { 14 | value = "False"; 15 | } 16 | return `${id}.set_${attr}(${value})`; 17 | } 18 | 19 | const template_py_setter_text = (id, attr, param) => { 20 | return `${id}.set_${attr}("${param}")`; 21 | } 22 | 23 | const template_py_setter_simple = (id, attr, param) => { 24 | return `${id}.set_${attr}(${param})`; 25 | } 26 | 27 | const template_c_create = (id, parent_id, type) => { 28 | return `lv_obj_t * ${id} = lv_${type}_create(${parent_id}, NULL);`; 29 | } 30 | 31 | const template_c_setter_simple = (id, type, attr, param) => { 32 | return `lv_${type}_set_${attr}(${id}, ${param});`; 33 | } 34 | 35 | const template_py_cb = (id) => { 36 | return `${id}.set_event_cb(lambda : ) #Put your code here` 37 | } 38 | 39 | const template_c_cb = (id) => { 40 | return `static void ${id}_event_cb(lv_obj_t * ${id}, lv_event_t event) 41 | { 42 | // Write the event handler here 43 | // if(event == LV_EVENT_) { } 44 | }`; 45 | } 46 | 47 | 48 | const template_c_all = (body, cb) => { 49 | return ` 50 | /** 51 | * @file lv_gui.h 52 | * Generated By WALV 53 | */ 54 | 55 | #ifndef LV_GUI_H 56 | #define LV_GUI_H 57 | 58 | #ifdef __cplusplus 59 | extern "C" { 60 | #endif 61 | 62 | #include "/*Put the path to lvgl.h here*/" 63 | 64 | /********************** 65 | * CALLBACK 66 | **********************/ 67 | ${cb} 68 | /********************** 69 | * GLOBAL FUNCTIONS 70 | **********************/ 71 | ${body} 72 | 73 | #ifdef __cplusplus 74 | } /* extern "C" */ 75 | #endif 76 | 77 | #endif /*LV_GUI_H*/ 78 | ` 79 | } -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | const reverse_del_node = (node, record) => { 2 | let childs = node.children; 3 | for (const iter of childs) { 4 | reverse_del_node(iter, record); 5 | wrap_delete(iter.label); 6 | record.push(iter.label); 7 | } 8 | childs.splice(0, childs.length); 9 | } 10 | 11 | const pool_delete = (pool, list) => { 12 | for (const i of list) { 13 | delete pool[i]; 14 | } 15 | } 16 | 17 | // arguments 18 | const setArgvs = (args) => { 19 | let args_list = []; 20 | for (const i of args) { 21 | args_list.push(i["name"]) 22 | } 23 | return args_list.toString(); 24 | } 25 | 26 | Vue.component('lvgl-setter', { 27 | props: ['id', 'name', 'body'], 28 | data: function() { 29 | return { 30 | args: [], 31 | } 32 | }, 33 | methods: { 34 | checkArgs: function() { 35 | let args_list = []; 36 | for (const arg of this.args) { 37 | if (arg['value'] === "") { 38 | return 39 | } 40 | if (arg['type'] === "str") { 41 | args_list.push(`"${arg['value']}"`); 42 | } else if (arg['type'] === "bool") { 43 | if (arg['value'] === true) { 44 | args_list.push("True"); 45 | } else { 46 | args_list.push("False"); 47 | } 48 | } else { 49 | args_list.push(arg.value); 50 | } 51 | // TODO: We can check each value's type here 52 | } 53 | wrap_setter_str(this.id, this.body.api, args_list.toString()); 54 | }, 55 | }, 56 | // TODO: Rewrite created && beforeUpdate 57 | created() { 58 | for (const arg of this.body.args) { 59 | arg['value'] = ""; // args: [{"name": '', "type": '', "value": ''}] 60 | this.args.push(arg); 61 | } 62 | }, 63 | // Why we need beforeUpdate: https://vuejs.org/v2/guide/list.html#Maintaining-State. If template was rendered by other, Vue won't excute created but beforeUpdate 64 | beforeUpdate() { 65 | this.args.splice(0, this.args.length); 66 | for (const arg of this.body.args) { 67 | arg['value'] = ""; 68 | this.args.push(arg); 69 | } 70 | }, 71 | template: ' {{ name }} ({{arg.name}}: ,)' 72 | }); 73 | 74 | // https://docs.littlevgl.com/en/html/object-types/obj.html 75 | // Vue.component('lvgl-layout', { 76 | // props: ['id', 'x', 'y', 'align', 'obj_ref', 'x_shift', 'y_shift'], // lv_obj_set_x, lv_obj_set_y, lv_obj_align(obj, obj_ref, LV_ALIGN_..., x_shift, y_shift) 77 | // data: function() { 78 | // return { 79 | // x: 0, 80 | // y: 0, 81 | // align: "", 82 | // } 83 | // }, 84 | // template: '' 85 | // }); 86 | 87 | // TODO: Only support align to its parent now 88 | Vue.component('lvgl-align', { 89 | props: ['id', 'ref_id'], 90 | data: function() { 91 | return { 92 | align: '', 93 | ref_obj: '', 94 | x_shift: 0, 95 | y_shift: 0, 96 | options: [ 97 | '', 'OUT_TOP_LEFT', 'OUT_TOP_MID', 'OUT_TOP_RIGHT', '', 0, 98 | 'OUT_LEFT_TOP', 'IN_TOP_LEFT', 'IN_TOP_MID', 'IN_TOP_RIGHT', 'OUT_RIGHT_TOP', 0, 99 | 'OUT_LEFT_MID', 'IN_LEFT_MID', 'CENTER', 'IN_RIGHT_MID', 'OUT_RIGHT_MID', 0, 100 | 'OUT_LEFT_BOTTOM', 'IN_BOTTOM_LEFT', 'IN_BOTTOM_MID', 'IN_BOTTOM_RIGHT', 'OUT_RIGHT_BOTTOM', 0, 101 | '', 'OUT_BOTTOM_LEFT', 'OUT_BOTTOM_MID', 'OUT_BOTTOM_RIGHT', '', 0, 102 | ] 103 | } 104 | }, 105 | watch: { 106 | align: (type) => { 107 | wrap_align(this.id, 'None', type, 0, 0); 108 | } 109 | }, 110 | template: '

{{align}}


' 111 | }); -------------------------------------------------------------------------------- /src/wrapper.js: -------------------------------------------------------------------------------- 1 | /* wrapper: generate the related code, then use `mp_js_do_str` to excute them. */ 2 | 3 | // id = expr 4 | const wrap_equal = (id, expr) => { 5 | mp_js_do_str(`${id}=${expr}`); 6 | } 7 | 8 | const wrap_create = (id, parent_id, type_s) => { 9 | 10 | // id + "=lv." + type_s + "(" + parent_id + ")", 11 | // id + ".set_drag(1)", 12 | // id + ".set_protect(lv.PROTECT.PRESS_LOST)", 13 | // "query_attr(" + id + ",\'" + id + "\',\'" + type_s + "\')", 14 | // id + ".set_event_cb(lambda obj=" + id + ",event=-1,name=\'" + id + "\':walv_callback(obj,name,event))", 15 | 16 | let code = [ 17 | `${id}=lv.${type_s}(${parent_id})`, 18 | `${id}.set_drag(True)`, 19 | `${id}.set_protect(lv.PROTECT.PRESS_LOST)`, 20 | `query_attr(${id},"${id}","${type_s}")`, 21 | `${id}.set_event_cb(lambda obj=${id},event=-1,name="${id}":walv_callback(obj,name,event))`, 22 | ]; 23 | 24 | 25 | const ComplexWidgets = ['ddlist', 'page', 'roller']; 26 | if (ComplexWidgets.indexOf(type_s) != -1) { 27 | code.push(`${id}.get_child(None).set_drag_parent(True)`); 28 | } 29 | mp_js_do_str(code.join('\n')); 30 | } 31 | 32 | const wrap_delete = (id) => { 33 | mp_js_do_str(`${id}.delete()`); 34 | } 35 | 36 | 37 | const wrap_query_attr = (id, type_s) => { 38 | mp_js_do_str(`query_attr(${id},"${id}","${type_s}")`); 39 | } 40 | 41 | const wrap_simple_setter = (id, attr, param) => { 42 | mp_js_do_str(`${id}.set_${attr}(${param})`); 43 | } 44 | 45 | // Convert '#ffffff' to '0xffffff' 46 | const color_convert = (color) => { 47 | return color.replace("#", "0x") 48 | } 49 | 50 | const wrap_setter = (id, type, name, params, database) => { 51 | // params is a list 52 | let api = database[type][name]['api']; // "fit": {"return_type": "NoneType", "args": [{"type": "object", "name": "cont"}, {"type": "int", "name": "fit"}], "type": "function", "api": "set_fit"} 53 | let args = database[type][name]['args']; 54 | let code = `${id}.${api}(${params.toString()})`; 55 | mp_js_do_str(code); 56 | } 57 | 58 | const wrap_align = (id, ref_id, offset_x, offset_y) => { 59 | mp_js_do_str(`${id}.align(${ref_id}, ${offset_x}, ${offset_y})`); 60 | } 61 | 62 | const wrap_setter_str = (id, api, params) => { 63 | // params is a string 64 | let code = `${id}.${api}(${params})`; 65 | mp_js_do_str(code); 66 | } 67 | 68 | const wrap_simple_style = (id, style) => { 69 | let s = style.text; 70 | let code = [ 71 | "s=lv.style_t(lv.style_plain)" 72 | ]; 73 | let c = color_convert(s.color); 74 | code.push(`s.text.font=lv.${s.font}`); 75 | code.push(`s.text.color=lv.color_hex(${c})`); 76 | code.push(`${id}.set_style(lv.label.STYLE.MAIN,s)`); 77 | mp_js_do_str(code.join('\n')); 78 | } --------------------------------------------------------------------------------