├── .github └── workflows │ └── python-package.yml ├── .gitignore ├── LICENSE ├── README-pypi.md ├── README-zh.md ├── README.md ├── doc ├── EVUE产品介绍PDF版.pdf └── images │ ├── QQ.jpg │ ├── brief.png │ ├── designer.png │ ├── dragondjf.jpg │ ├── evue.png │ ├── evue2py.gif │ ├── evue_login.gif │ ├── evue_website.gif │ ├── evuecompiler.png │ └── quicknode.gif ├── evue ├── __init__.py ├── basecomponent.py ├── componentmanager.py ├── databinding.py ├── debounce.py ├── elements │ ├── __init__.py │ ├── base.py │ ├── button.py │ ├── canvas.py │ ├── checkbox.py │ ├── collapsible.py │ ├── column.py │ ├── combobox.py │ ├── counter.py │ ├── dialog.py │ ├── div.py │ ├── fletbaseelement.py │ ├── grid.py │ ├── icon.py │ ├── iconbutton.py │ ├── image.py │ ├── listitem.py │ ├── listview.py │ ├── markdown.py │ ├── progress.py │ ├── qrcode.py │ ├── responsiverow.py │ ├── row.py │ ├── slider.py │ ├── stackview.py │ ├── switch.py │ ├── tab.py │ ├── tabview.py │ ├── text.py │ ├── textarea.py │ ├── textfield.py │ ├── widgets │ │ ├── __init__.py │ │ ├── basecontainer.py │ │ ├── collapsible.py │ │ ├── counter.py │ │ └── evueappbar.py │ └── windowtitlebar.py ├── evueapplication.py ├── fileserver.py ├── globalthis.py ├── iconbrowser.py ├── imagebrowser.py ├── jstimer.py ├── log.py ├── pylock.py ├── pyrect.py ├── router.py ├── sessionobject.py └── util.py ├── examples ├── evue_login │ ├── app.py │ ├── evue_login.evue │ ├── evue_login.py │ ├── evue_register.evue │ ├── evue_register.py │ ├── image │ │ └── website │ │ │ ├── QQ.jpg │ │ │ ├── android.png │ │ │ ├── bolt.png │ │ │ ├── component.png │ │ │ ├── dragondjf.jpg │ │ │ ├── evue_designer.png │ │ │ ├── gitee.png │ │ │ ├── gitee.svg │ │ │ ├── github.png │ │ │ ├── github.svg │ │ │ ├── html.png │ │ │ ├── ios.png │ │ │ ├── iot.png │ │ │ ├── linux.png │ │ │ ├── login.png │ │ │ ├── logo.png │ │ │ ├── mac.png │ │ │ ├── os.png │ │ │ ├── platform.png │ │ │ ├── platform2.png │ │ │ ├── web.png │ │ │ └── windows.png │ └── project.json ├── evue_website │ ├── app.py │ ├── evue_website.evue │ ├── evue_website.py │ ├── image │ │ └── website │ │ │ ├── QQ.jpg │ │ │ ├── android.png │ │ │ ├── bolt.png │ │ │ ├── component.png │ │ │ ├── dragondjf.jpg │ │ │ ├── evue_designer.png │ │ │ ├── gitee.png │ │ │ ├── gitee.svg │ │ │ ├── github.png │ │ │ ├── github.svg │ │ │ ├── html.png │ │ │ ├── ios.png │ │ │ ├── iot.png │ │ │ ├── linux.png │ │ │ ├── login.png │ │ │ ├── logo.png │ │ │ ├── mac.png │ │ │ ├── os.png │ │ │ ├── platform.png │ │ │ ├── platform2.png │ │ │ ├── web.png │ │ │ └── windows.png │ └── project.json ├── evuebrowser.py └── logo │ ├── logo.ico │ └── logo.png ├── pdm.lock ├── pyproject.toml └── setup.py /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: Python package 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | python-version: ["3.8", "3.9", "3.10"] 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v3 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | python -m pip install flake8 pytest 31 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 32 | - name: Lint with flake8 33 | run: | 34 | # stop the build if there are Python syntax errors or undefined names 35 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 36 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 37 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | .vscode/ 132 | .idea/ 133 | .pdm.toml 134 | 135 | examples/*.evue.json 136 | examples/*.evue.q.json 137 | examples/__init__.py 138 | examples/*/*.evue.json 139 | examples/*/__init__.py 140 | examples/*/*.evue.q.json 141 | examples/*/*/__init__.py 142 | examples/*/*/*.evue.q.json -------------------------------------------------------------------------------- /README-pypi.md: -------------------------------------------------------------------------------- 1 | # evue 2 | 3 | > Evue is a high-performance gui framework base an html/css which can run on windows/linux/macos/web/ios/andriod/rtos! Write once, run everywhere! 4 | 5 | ## Github 6 | 7 | [https://github.com/scriptiot/evue](https://github.com/scriptiot/evue) 8 | 9 | ## Installation 10 | Use the package manager [pip](https://github.com/scriptiot/evue) to install evue. 11 | 12 | ```bash 13 | pip install evue 14 | ``` 15 | 16 | or 17 | ```bash 18 | git clone https://github.com/scriptiot/evue.git 19 | cd evue 20 | python setup.py install # also `pip install ." 21 | ``` 22 | 23 | ## Getting started 24 | 25 | + [evue_website](https://github.com/scriptiot/evue/tree/master/examples/evue_website) 26 | 27 | ```python 28 | cd examples 29 | python evuebroswer.py ./evue_website/project.json 30 | or 31 | python evuebroswer.py ./evue_website/app.py 32 | ``` 33 | 34 | + [evue_login](https://github.com/scriptiot/evue/tree/master/examples/evue_login) 35 | 36 | ```python 37 | cd examples 38 | python evuebroswer.py ./evue_login/project.json 39 | or 40 | python evuebroswer.py ./evue_login/app.py 41 | ``` 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /README-zh.md: -------------------------------------------------------------------------------- 1 | # evue 2 | 3 | > Evue是一个高性能的跨平台应用开发框架,可以运行在windows/linux/macos/web/ios/andriod/rtos多种平台,一次开发,多端运行! 4 | 5 | 查看 [English](https://github.com/scriptiot/evue/blob/master/README.md) 英文说明. 6 | 7 | 阅读 [《EVUE 进化蜕变,下一代全平台UI开发利器》](https://www.yuque.com/dragondjf/ltn95z/krmcxd?singleDoc)了解更多介绍 8 | 9 | ## 特性 10 | 11 | ![brief](doc/images/brief.png) 12 | 13 | + Just python as you like 14 | + multi-user for web 15 | + dark/light theme support 16 | + responsive support 17 | + ... 18 | 19 | ## 框架 20 | 21 | + Evue 架构 22 | 23 | > Evue 是一个基于html/css的高性能的gui应用框架,与平台和ui引擎无关 24 | 25 | ![evue](doc/images/evue.png) 26 | 27 | 28 | + Evuecompiler 编译器架构 29 | 30 | > evue编译器的功能主要是将evue文件编译为python/javascript代码; 31 | 32 | ![evuecompiler](doc/images/evuecompiler.png) 33 | 34 | + Evue 全平台运行 35 | + Evue for flutter (windows/linux/macos/web/ios/andriod) 36 | + Evue for lvgl(rtos on mcu like Asr3603/F1C100/F1C200/esp32/stm32/...) 37 | > you can run evue on any platfom as you like! 38 | 39 | + Evue 支持适配任何ui引擎 40 | + Evue for flutter 41 | + Evue for lvgl 42 | + Evue for Qt 43 | + Evue for PySide2 44 | + ... 45 | > you can compile evue to any ui code as you like! 46 | 47 | ## 安装 48 | 使用 [pip](https://github.com/scriptiot/evue)安装evue. 49 | 50 | ```bash 51 | pip install evue 52 | ``` 53 | 54 | or 55 | ```bash 56 | git clone https://github.com/scriptiot/evue.git 57 | cd evue 58 | python setup.py install # also `pip install ." 59 | ``` 60 | 61 | ## 快速开始 62 | 63 | + [evue_website](https://github.com/scriptiot/evue/tree/master/examples/evue_website) 64 | 65 | ```python 66 | cd examples 67 | python evuebroswer.py ./evue_website/project.json 68 | or 69 | python evuebroswer.py ./evue_website/app.py 70 | ``` 71 | 72 | ![evue_website](doc/images/evue_website.gif) 73 | 74 | + [evue_login](https://github.com/scriptiot/evue/tree/master/examples/evue_login) 75 | 76 | ```python 77 | cd examples 78 | python evuebroswer.py ./evue_login/project.json 79 | or 80 | python evuebroswer.py ./evue_login/app.py 81 | ``` 82 | ![evue_login](doc/images/evue_login.gif) 83 | 84 | ## Evue Studio 85 | 86 | > Evue Studio 是一个服务开发者快速创建/编译/发布基于evue的应用的开发者平台。 87 | 88 | ![designer](doc/images/designer.png) 89 | 90 | [下载最新的evue studio](https://gitee.com/scriptiot/evue/releases/download/0.1.6/evuestudio-20221215162924-2c17005.7z) 91 | 92 | + 解压evuestudio-*.7z 93 | + 双击 `evuestudio.exe` 94 | 95 | ## Evue for iot 96 | > Evue for iot 是一个基于evue的商业产品`quicknode`, 轻量级evue解决方案,可以运行在各种mcu上。 97 | 98 | ![quicknode](doc/images/quicknode.gif) 99 | 100 | 更多介绍请阅读 [quicknode产品介绍](doc/EVUE%E4%BA%A7%E5%93%81%E4%BB%8B%E7%BB%8DPDF%E7%89%88.pdf) 101 | 102 | [下载最新的quicknode](https://gitee.com/scriptiot/evue/releases/download/0.1.6/quicknode-qbc-20221215142421-693fbf88.zip) 103 | 104 | + 解压quicknode-qbc-20221215142421-693fbf88.zip 105 | + 双击 `quicknode.bat` or `quicknode_chart.bat` 106 | 107 | [帮助手册](https://www.yuque.com/bytecode/eu1sci/ymto6i) 108 | 109 | ## 编译evue文件->python代码 110 | 111 | ![evue2py](doc/images/evue2py.gif) 112 | 113 | [如何编译evue文件为python代码](https://github.com/scriptiot/evue/wiki/How-to-compile--evue-to--python-code%3F) 114 | 115 | + 登录evue studio 116 | + 切换到`编译`页面 117 | + 添加工程目录到监控列表 118 | + 改变evue文件会自动编译为python代码 119 | 120 | ## 社区讨论 121 | 122 | + [Discussions](https://github.com/scriptiot/evue/discussions) 123 | + [Issues](https://github.com/scriptiot/evue/issues) 124 | 125 | 126 | ## 社区达人招募 127 | 128 | + `无论您是社区技术达人、设计师、产品经理、运营者,欢迎为evue项目贡献自己的一份力量! 您将在贡献者名单上榜上有名!` 129 | + `如果你喜欢,请发送email到【ding465398889@163.com】或者添加微信dragondjf!` 130 | 131 | ## 联系我们 132 | 133 | > 如果需要更多的技术支持或者商务合作, 请发送email/微信/QQ获取更多详细的支持! 134 | 135 | + Email : ding465398889@163.com 136 | + WeChat: dragondjf 137 | >![dragondjf](doc/images/dragondjf.jpg) 138 | + Evue for IOT 139 | >![dragondjf](doc/images/QQ.jpg) 140 | 141 | 142 | ## 致敬 143 | 144 | + [evm](https://github.com/scriptiot/evm) 145 | + [lvgl](https://github.com/lvgl/lvgl) 146 | + [flet](https://github.com/flet-dev/flet) 147 | + [flutter](https://github.com/flutter/flutter) 148 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # evue 2 | 3 | > Evue is a high-performance gui framework base an html/css which can run on windows/linux/macos/web/ios/andriod/rtos! Write once, run everywhere! 4 | 5 | See the [中文文档](https://github.com/scriptiot/evue/blob/master/README-zh.md) for Chinese readme. 6 | 7 | ## Features 8 | 9 | ![brief](https://github.com/scriptiot/evue/blob/master/doc/images/brief.png?raw=true) 10 | 11 | + Just python as you like 12 | + multi-user for web 13 | + dark/light theme support 14 | + responsive support 15 | + ... 16 | 17 | ## Framework 18 | 19 | + Evue architecture 20 | 21 | > Evue is a high-performance gui framework base an html/css which is platform independence and ui engine independence! 22 | 23 | ![evue](https://github.com/scriptiot/evue/blob/master/doc/images/evue.png?raw=true) 24 | 25 | 26 | + Evuecompiler architecture 27 | 28 | > Evuecompiler is a compiler which compile evue file to python/javascript code! 29 | 30 | ![evuecompiler](https://github.com/scriptiot/evue/blob/master/doc/images/evuecompiler.png?raw=true) 31 | 32 | + Evue cross platfom 33 | + Evue for flutter (windows/linux/macos/web/ios/andriod) 34 | + Evue for lvgl(rtos on mcu like Asr3603/F1C100/F1C200/esp32/stm32/...) 35 | > you can run evue on any platfom as you like! 36 | 37 | + Evue for all ui engine 38 | + Evue for flutter 39 | + Evue for lvgl 40 | + Evue for Qt 41 | + Evue for PySide2 42 | + ... 43 | > you can compile evue to any ui code as you like! 44 | 45 | ## Installation 46 | Use the package manager [pip](https://github.com/scriptiot/evue) to install evue. 47 | 48 | ```bash 49 | pip install evue 50 | ``` 51 | 52 | or 53 | ```bash 54 | git clone https://github.com/scriptiot/evue.git 55 | cd evue 56 | python setup.py install # also `pip install ." 57 | ``` 58 | 59 | ## Getting started 60 | 61 | + [evue_website](https://github.com/scriptiot/evue/tree/master/examples/evue_website) 62 | 63 | ```python 64 | cd examples 65 | python evuebrowser.py ./evue_website/project.json 66 | or 67 | python evuebrowser.py ./evue_website/app.py 68 | ``` 69 | 70 | ![evue_website](https://github.com/scriptiot/evue/tree/master/doc/images/evue_website.gif?raw=true) 71 | 72 | + [evue_login](https://github.com/scriptiot/evue/tree/master/examples/evue_login) 73 | 74 | ```python 75 | cd examples 76 | python evuebrowser.py ./evue_login/project.json 77 | or 78 | python evuebrowser.py ./evue_login/app.py 79 | ``` 80 | 81 | ![evue_login](https://github.com/scriptiot/evue/tree/master/doc/images/evue_login.gif?raw=true) 82 | 83 | 84 | ## Evue Studio 85 | 86 | > Evue Studio is a develop platform for evue developer to create/compile/deploy evue project! 87 | 88 | ![designer](https://github.com/scriptiot/evue/blob/master/doc/images/designer.png?raw=true) 89 | 90 | 91 | [download latest evue studio](https://github.com/scriptiot/evue/releases/download/0.1.6.1/evuestudio-20230106211347-65b5a31.7z) 92 | 93 | + uncompress evuestudio-*.7z 94 | + double click `evuestudio.exe` 95 | 96 | ## Evue for iot 97 | > Evue for iot is a commercial product named `quicknode`, a tiny runtime of js engnine which can run on lots of mcu 98 | 99 | ![quicknode](https://github.com/scriptiot/evue/blob/master/doc/images/quicknode.gif?raw=true) 100 | 101 | more info [what is quicknode](doc/EVUE%E4%BA%A7%E5%93%81%E4%BB%8B%E7%BB%8DPDF%E7%89%88.pdf) 102 | 103 | [download quicknode](https://github.com/scriptiot/evue/releases/download/0.1.6/quicknode-qbc-20221215142421-693fbf88.zip) 104 | 105 | + uncompress quicknode-qbc-20221215142421-693fbf88.zip 106 | + double click `quicknode.bat` or `quicknode_chart.bat` 107 | 108 | [document](https://www.yuque.com/bytecode/eu1sci/ymto6i) 109 | 110 | ## how to compile evue to python code 111 | 112 | ![evue2py](https://github.com/scriptiot/evue/blob/master/doc/images/evue2py.gif?raw=true) 113 | 114 | [how to compile evue to python code](https://github.com/scriptiot/evue/wiki/How-to-compile--evue-to--python-code%3F) 115 | 116 | + login in evue studio 117 | + switch to compile page 118 | + add project dir to watch 119 | + evue will be compiled to python code automatically when you change evue file 120 | 121 | ## Community 122 | 123 | + [Discussions](https://github.com/scriptiot/evue/discussions) 124 | + [Issues](https://github.com/scriptiot/evue/issues) 125 | 126 | 127 | ## Contribute to this wonderful project 128 | 129 | + `Welcome to contribute to this wonderful project no matter you are a community technical talent or designer talent, or product manager talent or community operation talent, your name will be one of evue contributors!` 130 | 131 | + `if you like evue, you can send email to [ding465398889@163.com], thanks!` 132 | 133 | 134 | ## Contact 135 | 136 | > If there is a need for cooperation, please send email/wechat/QQ for more support! 137 | 138 | + Email : ding465398889@163.com 139 | + WeChat: dragondjf 140 | > ![dragondjf](https://github.com/scriptiot/evue/blob/master/doc/images/dragondjf.jpg?raw=true) 141 | + Evue for IOT 142 | > ![dragondjf](https://github.com/scriptiot/evue/blob/master/doc/images/QQ.jpg?raw=true) 143 | 144 | ## Salute 145 | 146 | + [evm](https://github.com/scriptiot/evm) 147 | + [lvgl](https://github.com/lvgl/lvgl) 148 | + [flet](https://github.com/flet-dev/flet) 149 | + [flutter](https://github.com/flutter/flutter) 150 | -------------------------------------------------------------------------------- /doc/EVUE产品介绍PDF版.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/doc/EVUE产品介绍PDF版.pdf -------------------------------------------------------------------------------- /doc/images/QQ.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/doc/images/QQ.jpg -------------------------------------------------------------------------------- /doc/images/brief.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/doc/images/brief.png -------------------------------------------------------------------------------- /doc/images/designer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/doc/images/designer.png -------------------------------------------------------------------------------- /doc/images/dragondjf.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/doc/images/dragondjf.jpg -------------------------------------------------------------------------------- /doc/images/evue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/doc/images/evue.png -------------------------------------------------------------------------------- /doc/images/evue2py.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/doc/images/evue2py.gif -------------------------------------------------------------------------------- /doc/images/evue_login.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/doc/images/evue_login.gif -------------------------------------------------------------------------------- /doc/images/evue_website.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/doc/images/evue_website.gif -------------------------------------------------------------------------------- /doc/images/evuecompiler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/doc/images/evuecompiler.png -------------------------------------------------------------------------------- /doc/images/quicknode.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/doc/images/quicknode.gif -------------------------------------------------------------------------------- /evue/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .globalthis import * 3 | from .evueapplication import startApp, EvueApplication 4 | from .sessionobject import SessionObject 5 | from .elements import * 6 | from .jstimer import * 7 | from .router import * 8 | from .basecomponent import * 9 | from .databinding import * 10 | from .componentmanager import componentManager 11 | from .pylock import * 12 | from .debounce import * 13 | from .iconbrowser import IconExplore 14 | from .imagebrowser import ImageExplore 15 | from .fileserver import * 16 | from .util import * 17 | from .log import logger 18 | -------------------------------------------------------------------------------- /evue/basecomponent.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import builtins 3 | from flet import Page 4 | from loguru import logger 5 | from .router import Router 6 | from .globalthis import globalThis 7 | from .databinding import DataBinding 8 | from .sessionobject import SessionObject 9 | 10 | from typing import ( 11 | Any, 12 | Optional, 13 | Dict, 14 | Mapping, 15 | List, 16 | Tuple, 17 | Match, 18 | Callable, 19 | Type, 20 | Sequence, 21 | ) 22 | 23 | class BaseComponent(Dict[str, Any], SessionObject): 24 | 25 | def __init__(self, sessionID=None) -> None: 26 | super().__init__() 27 | self['_data_'] = {} 28 | self['_rootElement_'] = None 29 | self.pageinfo = None 30 | self.sessionID = sessionID 31 | 32 | def __str__(self) -> str: 33 | return "<%s(%s)>" % (self.__class__.__name__, id(self)) 34 | 35 | def getElementById(self, id): 36 | element = self.rootElement.getElementById(id) 37 | if element: 38 | return element 39 | page = self.page 40 | if page: 41 | router:Router = page.router 42 | userid = "%s.%s" % (self.rootElement.uri, id) 43 | element = router.getElementById(userid) 44 | return element 45 | return None 46 | 47 | def getElementByType(self, _type): 48 | els = [] 49 | listElements = self.listElements 50 | if listElements: 51 | for el in listElements: 52 | if el.type == _type: 53 | els.append(el) 54 | return els 55 | 56 | def getElementsByTagName(self, tag): 57 | return self.getElementByType(tag) 58 | 59 | def __hash__(self): 60 | return hash(self.rootElement) 61 | 62 | @property 63 | def eid(self): 64 | ''' 65 | element用户管理使用的id 66 | ''' 67 | return "%s_%d" % (self.node['type'], id(self)) 68 | 69 | @property 70 | def data(self): 71 | return self['_data_'] 72 | 73 | @data.setter 74 | def data(self, value): 75 | self['_data_'] = DataBinding(value, self) 76 | 77 | @property 78 | def rootElement(self): 79 | return self['_rootElement_'] 80 | 81 | @rootElement.setter 82 | def rootElement(self, value): 83 | self['_rootElement_'] = value 84 | 85 | def __getattr__(self, name: str) -> Any: 86 | if name in self['_data_']: 87 | return self['_data_'][name] 88 | else: 89 | return getattr(self['_rootElement_'], name) 90 | 91 | def __setattr__(self, name: str, value: Any) -> None: 92 | if "_data_" in self and name in self["_data_"]: 93 | setattr(self['_data_'], name, value) 94 | else: 95 | super().__setattr__(name, value) 96 | if self.rootElement: 97 | setattr(self.rootElement, name, value) 98 | 99 | def set_binding_value(self, element, attr, key): 100 | if "_data_" in self: 101 | self['_data_'].set_binding_value(element, attr, key) 102 | 103 | @property 104 | def json(self): 105 | attributes = self.rootElement.attributes 106 | attributes['id'] = self.eid 107 | return { 108 | "type": self.node['type'], 109 | "attributes": attributes, 110 | "nodes": [], 111 | "bindings": self.bindings, 112 | "events": self.events, 113 | } 114 | 115 | @classmethod 116 | def defaut_json(cls, name, left=0, top=0): 117 | from .router import require 118 | module = require(name) 119 | json = module.UserElement.defaut_json(left, top) 120 | return json 121 | 122 | def renderRoot(self): 123 | pass 124 | 125 | def onInit(self): 126 | pass 127 | 128 | def onReady(self): 129 | pass 130 | 131 | def onShow(self): 132 | pass 133 | 134 | def onHide(self): 135 | pass 136 | 137 | def onQuit(self): 138 | pass 139 | 140 | def render(self): 141 | pass 142 | 143 | builtins.BaseComponent = BaseComponent 144 | -------------------------------------------------------------------------------- /evue/componentmanager.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .globalthis import require 3 | 4 | class ComponentManager(object): 5 | 6 | def __init__(self) -> None: 7 | self._usercomponents_ = {} 8 | 9 | @property 10 | def userComponents(self): 11 | return self._usercomponents_ 12 | 13 | def registerComponent(self, componentInfo): 14 | name = componentInfo['name'] 15 | path = componentInfo['path'] 16 | module = require(path) 17 | componentInfo['module'] = module 18 | self.userComponents[name] = componentInfo 19 | 20 | def getComponentName(self, name): 21 | elementname = "%sComponent" % name.capitalize() 22 | return elementname 23 | 24 | def getUserComponent(self, name): 25 | componentName = self.getComponentName(name) 26 | import evue 27 | return getattr(evue, componentName) 28 | 29 | componentManager = ComponentManager() 30 | -------------------------------------------------------------------------------- /evue/databinding.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from typing import ( 3 | Any, 4 | Dict 5 | ) 6 | from loguru import logger 7 | 8 | class DataBinding(Dict[str, Any]): 9 | 10 | def __init__(self, data, parent=None) -> None: 11 | super().__init__() 12 | self['_bindings_'] = {} 13 | self['_parent_'] = parent 14 | self.update(data) 15 | 16 | def __getattr__(self, name: str) -> Any: 17 | try: 18 | return self[name] 19 | except KeyError: 20 | raise AttributeError(name) 21 | 22 | def __setattr__(self, name: str, value: Any) -> None: 23 | self[name] = value 24 | 25 | if name in self['_bindings_']: 26 | bindings = self['_bindings_'][name] 27 | for binding in bindings: 28 | element = binding['element'] 29 | attr = binding['attr'] 30 | setattr(element, attr, value) 31 | attrHook = "onPropertyChanged_%s" % binding["key"] 32 | if hasattr(self['_parent_'], attrHook): 33 | getattr(self['_parent_'], attrHook)(value) 34 | 35 | def set_binding_value(self, element, attr, key): 36 | if key not in self['_bindings_']: 37 | self['_bindings_'][key] = [] 38 | 39 | self['_bindings_'][key].append({ 40 | "element": element, 41 | "attr": attr, 42 | "key": key 43 | }) 44 | -------------------------------------------------------------------------------- /evue/debounce.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import functools 3 | from threading import Timer 4 | from typing import Callable 5 | 6 | class debounce: # noqa 7 | def __init__(self, timeout: float = 1): 8 | self.timeout = timeout 9 | self._timer = None 10 | 11 | def __call__(self, func: Callable): 12 | @functools.wraps(func) 13 | def decorator(*args, **kwargs): 14 | if self._timer is not None: 15 | self._timer.cancel() 16 | self._timer = Timer(self.timeout, func, args=args, kwargs=kwargs) 17 | self._timer.start() 18 | 19 | return decorator -------------------------------------------------------------------------------- /evue/elements/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .widgets import * 3 | from .fletbaseelement import * 4 | from .windowtitlebar import WindowtitlebarElement 5 | from .div import DivElement 6 | from .column import ColumnElement 7 | from .stackview import StackViewElement 8 | from .row import RowElement 9 | from .responsiverow import ResponsiveRowElement 10 | from .grid import GridElement 11 | from .collapsible import CollapsibleElement 12 | from .image import ImageElement 13 | from .icon import IconElement 14 | from .iconbutton import IconButtonElement 15 | from .textfield import TextFieldElement 16 | from .textarea import TextareaElement 17 | from .text import TextElement 18 | from .counter import CounterElement 19 | from .button import ButtonElement 20 | from .slider import SliderElement 21 | from .dialog import DialogElement 22 | from .checkbox import CheckboxElement 23 | from .switch import SwitchElement 24 | from .progress import ProgressElement 25 | from .slider import SliderElement 26 | from .combobox import ComboboxElement 27 | from .qrcode import QRCodeElement, qrcode_make_png 28 | from .canvas import CanvasElement 29 | from .listview import ListViewElement 30 | from .listitem import ListItemElement 31 | from .tabview import TabViewElement 32 | from .tab import TabElement 33 | from .markdown import MarkdownElement 34 | 35 | 36 | ElementClass = { 37 | "windowtitlebar": WindowtitlebarElement, 38 | "div": DivElement, 39 | "column": ColumnElement, 40 | "stackview": StackViewElement, 41 | "row": RowElement, 42 | "responsiverow": ResponsiveRowElement, 43 | "grid": GridElement, 44 | "collapsible": CollapsibleElement, 45 | "image": ImageElement, 46 | "icon": IconElement, 47 | "iconbutton": IconButtonElement, 48 | "textfield": TextFieldElement, 49 | "textarea": TextareaElement, 50 | "text": TextElement, 51 | "counter": CounterElement, 52 | "button": ButtonElement, 53 | "slider": SliderElement, 54 | "dialog": DialogElement, 55 | "checkbox": CheckboxElement, 56 | "switch": SwitchElement, 57 | "progress": ProgressElement, 58 | "slider": SliderElement, 59 | "combobox": ComboboxElement, 60 | "qrcode": QRCodeElement, 61 | "canvas": CanvasElement, 62 | "listview": ListViewElement, 63 | "listitem": ListItemElement, 64 | "tabview": TabViewElement, 65 | "tab": TabElement, 66 | "markdown": MarkdownElement 67 | } 68 | 69 | UserElements = {} 70 | 71 | def getCreator(_type): 72 | _type = _type.lower() 73 | if _type in ElementClass: 74 | EClass = ElementClass[_type] 75 | return EClass.__name__ 76 | elif _type in UserElements: 77 | return UserElements[_type] 78 | else: 79 | import evue 80 | return "%s" % evue.componentManager.getComponentName(_type) 81 | 82 | def registerElement(name, _class_): 83 | ElementClass[name] = _class_ 84 | import evue 85 | setattr(evue, _class_.__name__, _class_) 86 | 87 | def registerUserElement(name, classname): 88 | UserElements[name] = classname 89 | -------------------------------------------------------------------------------- /evue/elements/base.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from typing import ( 3 | Any, 4 | Dict 5 | ) 6 | from flet import icons 7 | from PIL import ImageFont 8 | from loguru import logger 9 | import traceback 10 | from ..globalthis import globalThis 11 | from ..sessionobject import SessionObject 12 | 13 | 14 | def getChinese(word:str): 15 | ''' 16 | 判断一个词是否是非英文词,只要包含一个中文,就认为是非英文词汇 17 | :param word: 18 | :return: 19 | ''' 20 | count = 0 21 | ch_str = "" 22 | en_str = "" 23 | for s in word.encode('utf-8').decode('utf-8'): 24 | if u'\u4e00' <= s <= u'\u9fff': 25 | count += 1 26 | ch_str += s 27 | else: 28 | en_str += s 29 | return ch_str, en_str 30 | 31 | 32 | class BaseElement(Dict[str, Any], SessionObject): 33 | 34 | enableUpdate = True 35 | 36 | def __init__(self, sessionID=None) -> None: 37 | super().__init__() 38 | self.sessionID = sessionID 39 | self._font_ = None 40 | self.userData = None 41 | 42 | # public 43 | self['width'] = 0 44 | self['height'] = 0 45 | self['left'] = 0 46 | self['top'] = 0 47 | self['visible'] = True 48 | # div 49 | self['border_width'] = 0 50 | self['border_radius'] = 0 51 | self['border_color'] = "transparent" 52 | self['background_color'] = "white" 53 | # image 54 | self['src'] = "" 55 | # text 56 | self['value'] = "" 57 | self['text-align'] = "center" 58 | self['font-family'] = None 59 | self['size'] = None 60 | self['weight'] = None 61 | self['italic'] = None 62 | self['style'] = None 63 | self['max-lines'] = None 64 | self['overflow'] = None 65 | self['selectable'] = None 66 | self['no-wrap'] = None 67 | self['color'] = None 68 | self['background_color'] = None 69 | self['semantics-label'] = None 70 | self['opacity'] = 0 71 | self['rotate'] = 0 72 | self['scale'] = 1 73 | self['offset'] = 0 74 | 75 | def __getattr__(self, name: str) -> Any: 76 | try: 77 | return self[name] 78 | except KeyError: 79 | raise AttributeError(name) 80 | except: 81 | return None 82 | 83 | def __setattr__(self, name: str, value: Any) -> None: 84 | self[name] = value 85 | _name = name.replace("-", "_") 86 | funcName = "set_%s" % _name 87 | if hasattr(self, funcName): 88 | func = getattr(self, funcName) 89 | func(value) 90 | else: 91 | if hasattr(self, "obj") and self.obj: 92 | setattr(self.obj, _name, value) 93 | try: 94 | if BaseElement.enableUpdate: 95 | self.update() 96 | except: 97 | pass 98 | 99 | @classmethod 100 | def bool(cls, value): 101 | if isinstance(value, str): 102 | if value == "true" or value == "True": 103 | return True 104 | else: 105 | return False 106 | else: 107 | return bool(value) 108 | 109 | @classmethod 110 | def alignment(cls, value): 111 | if value == "left": 112 | return "start" 113 | elif value == "right": 114 | return "end" 115 | else: 116 | return value 117 | 118 | @classmethod 119 | def ficon(cls, value): 120 | if value.startswith("icons."): 121 | name = value[6:] 122 | if hasattr(icons, name): 123 | return getattr(icons, name) 124 | return None 125 | 126 | def measureText(self, value, size=20): 127 | if self.sessionID is None: 128 | return None, None 129 | if self.page and self.page.theme and self.page.theme.font_family: 130 | value = str(value) 131 | try: 132 | if self._font_ is None: 133 | self._font_ = ImageFont.truetype(self.page.theme.font_family, size=20) 134 | if size: 135 | self._font_ = ImageFont.truetype(self.page.theme.font_family, size=size) 136 | ch, en = getChinese(value) 137 | if value == en: 138 | box = self._font_.getbbox(value) 139 | width = box[2] * 1.2 140 | height = box[3] * 1.7 141 | else: 142 | chbox = self._font_.getbbox(ch) 143 | enbox = self._font_.getbbox(en) 144 | width = chbox[2] * 1.7 + enbox[2] * 1.2 145 | height = chbox[3] * 1.7 146 | return width, height 147 | except: 148 | logger.warning(traceback.format_exc()) 149 | return None, None 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /evue/elements/button.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from flet import Text, ElevatedButton, alignment, Draggable, icons 3 | from loguru import logger 4 | from .fletbaseelement import FletBaseElement 5 | from .widgets import BaseContainer 6 | from .text import TextElement 7 | 8 | 9 | class ButtonElement(TextElement): 10 | 11 | def __init__(self, node, parent, draggable=False, sessionID=None): 12 | super().__init__(node, parent, draggable, sessionID=sessionID) 13 | 14 | def create(self, parent, draggable=False): 15 | self._text_ = Text(expand=True) 16 | self._button_ = ElevatedButton(content=self._text_, expand=True) 17 | if draggable: 18 | self._obj_ = BaseContainer(Draggable(content=self._button_), 19 | alignment=alignment.center 20 | ) 21 | self._obj_.content.element = self 22 | else: 23 | self._obj_ = BaseContainer(self._button_, alignment=alignment.center) 24 | 25 | def set_width(self, value): 26 | self._obj_.width = value 27 | self._button_.width = value 28 | 29 | def set_height(self, value): 30 | self._obj_.height = value 31 | self._button_.height = value 32 | 33 | def set_icon(self, value): 34 | self._button_.icon = getattr(icons, value) 35 | 36 | def set_background_color(self, value): 37 | self._button_.bgcolor = value 38 | 39 | def set_value(self, value): 40 | super().set_value(value) 41 | 42 | @classmethod 43 | def defaut_style(cls): 44 | return { 45 | "border-width": 0, 46 | "border-color": "transparent", 47 | "background-color": "#282828", 48 | "color": "white", 49 | "font-size": 20, 50 | "text-align": "center" 51 | } 52 | 53 | @classmethod 54 | def defaut_attributes(cls): 55 | return { 56 | "value": "button", 57 | "style": cls.defaut_style() 58 | } 59 | 60 | @classmethod 61 | def defaut_json(cls, left=0, top=0): 62 | return FletBaseElement.defaut_json(cls, "button", left, top) 63 | -------------------------------------------------------------------------------- /evue/elements/canvas.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .fletbaseelement import FletBaseElement 3 | from .div import DivElement 4 | 5 | 6 | class CanvasElement(DivElement): 7 | 8 | def __init__(self, node, parent, draggable=False, sessionID=None): 9 | super().__init__(node, parent, draggable, sessionID=sessionID) 10 | 11 | @classmethod 12 | def defaut_style(cls): 13 | ret = DivElement.defaut_style() 14 | ret.update({ 15 | "width": 128, 16 | "height": 128, 17 | "background-color": "white", 18 | }) 19 | return ret 20 | 21 | @classmethod 22 | def defaut_json(cls, left=0, top=0): 23 | return FletBaseElement.defaut_json(cls, "canvas", left, top) 24 | -------------------------------------------------------------------------------- /evue/elements/checkbox.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .fletbaseelement import FletBaseElement 3 | from flet import ( 4 | Checkbox, 5 | Row, 6 | Text, 7 | alignment, 8 | Draggable 9 | ) 10 | 11 | from .text import TextElement 12 | from .widgets import BaseContainer 13 | from loguru import logger 14 | 15 | 16 | class CheckboxElement(TextElement): 17 | 18 | def __init__(self, node, parent, draggable=False, sessionID=None): 19 | super().__init__(node, parent, draggable, sessionID=sessionID) 20 | 21 | def create(self, parent, draggable=False): 22 | self._checkbox_ = Checkbox() 23 | self._text_ = Text(expand=True) 24 | self._row_ = Row([self._checkbox_, self._text_], expand=True) 25 | if draggable: 26 | self._obj_ = BaseContainer(Draggable(content=self._row_), 27 | alignment=alignment.center 28 | ) 29 | self._obj_.content.element = self 30 | else: 31 | self._obj_ = BaseContainer(self._row_, alignment=alignment.center) 32 | 33 | def on_change(e): 34 | self.value = e.control.value 35 | logger.warning(self.value) 36 | self._checkbox_.on_change = on_change 37 | 38 | def set_width(self, value): 39 | self._obj_.width = value 40 | 41 | def set_height(self, value): 42 | self._obj_.height = value 43 | 44 | def set_value(self, value): 45 | self._checkbox_.value = FletBaseElement.bool(value) 46 | 47 | def set_text(self, value): 48 | width, height = self.measureText(value, self._text_.size) 49 | if width is not None and width > self.width: 50 | self.width = width + 48 51 | if height is not None and height > self.height: 52 | self.height = height 53 | self._text_.value = value 54 | 55 | def set_disabled(self, value): 56 | self._checkbox_.disabled = FletBaseElement.bool(value) 57 | 58 | def set_checkbox_checked_color(self, value): 59 | self._checkbox_.check_color = value 60 | 61 | def set_checkbox_unchecked_color(self, value): 62 | self._checkbox_.fill_color = value 63 | 64 | def set_onValueChanged(self, func): 65 | def on_change(e): 66 | self.value = e.control.value 67 | logger.warning(self.value) 68 | func(self) 69 | self._checkbox_.on_change = on_change 70 | 71 | @classmethod 72 | def default_events(cls, id): 73 | ret = FletBaseElement.default_events(id) 74 | ret['onValueChanged'] = { 75 | "code": "pass", 76 | "name": "on_%s_valueChanged" % id 77 | } 78 | return ret 79 | 80 | @property 81 | def attributes(self): 82 | attributes = super().attributes 83 | attributes.update({ 84 | "text": self._text_.value, 85 | "value": self._checkbox_.value 86 | }) 87 | return attributes 88 | 89 | @property 90 | def style(self): 91 | ret = super().style 92 | ret.update({ 93 | "checkbox-checked-color": self._checkbox_.check_color, 94 | "checkbox-unchecked-color": self._checkbox_.fill_color 95 | }) 96 | return ret 97 | 98 | def set_attributes(self, node): 99 | super().set_attributes(node) 100 | style = node['attributes']['style'] 101 | if 'checkbox-checked-color' in style: 102 | self.checkbox_checked_color = style['checkbox-checked-color'] 103 | if 'checkbox-unchecked-color' in style: 104 | self.checkbox_unchecked_color = style['checkbox-unchecked-color'] 105 | # attr 106 | attributes = node['attributes'] 107 | self.text = attributes["text"] 108 | self.value = attributes["value"] 109 | 110 | def set_tiny_attributes(self, data): 111 | style = data['style'] 112 | self.font_size = style['font-size'] 113 | self.color = style['color'] 114 | self.text_align = style['text-align'] 115 | if 'checkbox-checked-color' in style: 116 | self.checkbox_checked_color = style['checkbox-checked-color'] 117 | if 'checkbox-unchecked-color' in style: 118 | self.checkbox_unchecked_color = style['checkbox-unchecked-color'] 119 | 120 | # attr 121 | if 'value' in data: 122 | self.value = data["value"] 123 | if 'text' in data: 124 | self.text = data["text"] 125 | 126 | @classmethod 127 | def defaut_style(cls): 128 | return { 129 | "width": 42, 130 | "height": 42, 131 | "border-width": 0, 132 | "border-color": "transparent", 133 | "background-color": "#282828", 134 | "color": "white", 135 | "font-size": 20, 136 | "text-align": "left", 137 | "checkbox-checked-color": "green", 138 | "checkbox-unchecked-color": "red" 139 | } 140 | 141 | @classmethod 142 | def defaut_attributes(cls): 143 | return { 144 | "text": "checkbox", 145 | "value": True, 146 | "style": cls.defaut_style() 147 | } 148 | 149 | @classmethod 150 | def defaut_json(cls, left=0, top=0): 151 | return FletBaseElement.defaut_json(cls, "checkbox", left, top) 152 | -------------------------------------------------------------------------------- /evue/elements/collapsible.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .fletbaseelement import FletBaseElement 3 | from flet import ( 4 | Draggable, 5 | alignment 6 | ) 7 | from .widgets import Collapsible 8 | from .widgets import BaseContainer 9 | from threading import Timer 10 | 11 | 12 | class CollapsibleElement(FletBaseElement): 13 | 14 | def __init__(self, node, parent, draggable=False, sessionID=None): 15 | super().__init__(node, parent, draggable, sessionID=sessionID) 16 | self.create(parent, draggable) 17 | self.setParent(parent) 18 | self._timer = None 19 | 20 | def create(self, parent, draggable=False): 21 | self._collapsible_ = Collapsible() 22 | self._collapsible_.scroll = "adaptive" 23 | self._text_ = self._collapsible_._text_ 24 | self._controls_ = self._collapsible_.content.controls 25 | if draggable: 26 | self._obj_ = BaseContainer( 27 | Draggable(content=self._collapsible_), 28 | alignment = alignment.center 29 | ) 30 | self._obj_.content.element = self 31 | else: 32 | self._obj_ = BaseContainer( 33 | self._collapsible_ 34 | ) 35 | 36 | def toggle(self, timeout=1): 37 | if self._timer is None: 38 | self._timer = Timer(timeout, self._collapsible_.header_click) 39 | if self._timer: 40 | self._timer.start() 41 | else: 42 | self._timer.cancel() 43 | self._timer = None 44 | 45 | @property 46 | def isContainer(self): 47 | return True 48 | 49 | def set_open(self, value): 50 | self._collapsible_.set_open(FletBaseElement.bool(value)) 51 | 52 | def set_width(self, value): 53 | self._obj_.width = value 54 | self._collapsible_.width = value 55 | 56 | def set_height(self, value): 57 | self._obj_.height = value 58 | self._collapsible_.height = value 59 | 60 | def set_title(self, value): 61 | self._text_.value = str(value) 62 | 63 | def set_spacing(self, value): 64 | self._collapsible_.spacingContainer.height = value 65 | 66 | def set_font_size(self, value): 67 | self._text_.size = value 68 | 69 | def set_color(self, value): 70 | self._text_.color = value 71 | 72 | def set_text_align(self, value): 73 | if value == "left": 74 | self._obj_.alignment = alignment.center_left 75 | elif value == "center": 76 | self._obj_.alignment = alignment.center 77 | elif value == "right": 78 | self._obj_.alignment = alignment.center_right 79 | else: 80 | self._obj_.alignment = alignment.center 81 | 82 | if value == "None": 83 | self._text_.text_align = "center" 84 | else: 85 | self._text_.text_align = value 86 | 87 | def set_value(self, value): 88 | self._text_.value = str(value) 89 | 90 | def set_switch(self, value): 91 | if value == "true": 92 | self._collapsible_.set_switch(True) 93 | else: 94 | self._collapsible_.set_switch(False) 95 | -------------------------------------------------------------------------------- /evue/elements/column.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .fletbaseelement import FletBaseElement 3 | from flet import ( 4 | Column, 5 | Draggable, 6 | alignment 7 | ) 8 | from .widgets import BaseContainer 9 | 10 | class ColumnElement(FletBaseElement): 11 | 12 | def __init__(self, node, parent, draggable=False, sessionID=None): 13 | super().__init__(node, parent, draggable, sessionID=sessionID) 14 | self.create(parent, draggable) 15 | self.setParent(parent) 16 | 17 | def create(self, parent, draggable=False): 18 | self._column_ = Column([]) 19 | self._column_.alignment = "start" 20 | self._column_.horizontal_alignment = "start" 21 | # self._column_.scroll = "always" 22 | # self._column_.auto_scroll = True 23 | self._controls_ = self._column_.controls 24 | if draggable: 25 | self._obj_ = BaseContainer( 26 | Draggable(content=self._column_), 27 | alignment = alignment.center 28 | ) 29 | self._obj_.content.element = self 30 | else: 31 | self._obj_ = BaseContainer( 32 | self._column_, 33 | alignment = alignment.top_left 34 | ) 35 | @property 36 | def isContainer(self): 37 | return True 38 | 39 | @property 40 | def isLayout(self): 41 | return True 42 | 43 | def set_width(self, value): 44 | self._obj_.width = value 45 | 46 | def set_height(self, value): 47 | self._obj_.height = value 48 | 49 | def set_scroll(self, value): 50 | self._column_.scroll = value 51 | 52 | def set_auto_scroll(self, value): 53 | self._column_.auto_scroll = value 54 | 55 | def set_alignment(self, value): 56 | self._column_.alignment = FletBaseElement.alignment(value) 57 | 58 | def set_horizontal_alignment(self, value): 59 | self._column_.horizontal_alignment = FletBaseElement.alignment(value) 60 | 61 | def set_wrap(self, value): 62 | self._column_.wrap = FletBaseElement.bool(value) 63 | 64 | def set_spacing(self, value): 65 | self._column_.spacing = value 66 | 67 | def set_attributes(self, node): 68 | super().set_attributes(node) 69 | attributes = node['attributes'] 70 | style = attributes['style'] 71 | # attr 72 | self.scroll = attributes["scroll"] 73 | self.auto_scroll = attributes["auto_scroll"] 74 | self.wrap = attributes["wrap"] 75 | self.spacing = attributes["spacing"] 76 | self.alignment = attributes["alignment"] 77 | self.horizontal_alignment = attributes["horizontal_alignment"] 78 | 79 | def set_tiny_attributes(self, data): 80 | # attr 81 | self.scroll = data["scroll"] 82 | self.auto_scroll = data["auto_scroll"] 83 | self.wrap = data["wrap"] 84 | self.spacing = data["spacing"] 85 | self.alignment = data["alignment"] 86 | self.horizontal_alignment = data["horizontal_alignment"] 87 | 88 | @property 89 | def attributes(self): 90 | attributes = super().attributes 91 | attributes.update({ 92 | "scroll": self._column_.scroll, 93 | "auto_scroll": self._column_.auto_scroll, 94 | "wrap": self._column_.wrap, 95 | "spacing": self._column_.spacing, 96 | "alignment": self._column_.alignment, 97 | "horizontal_alignment": self._column_.horizontal_alignment 98 | }) 99 | return attributes 100 | 101 | @classmethod 102 | def defaut_style(cls): 103 | return { 104 | "left": 0, 105 | "top": 0, 106 | "width": 40, 107 | "height": 200, 108 | "border-width": 0, 109 | "border-radius": 0, 110 | "border-color": "transparent", 111 | "background-color": "white" 112 | } 113 | 114 | @classmethod 115 | def defaut_attributes(cls): 116 | return { 117 | "scroll": "auto", 118 | "auto_scroll": True, 119 | "wrap": True, 120 | "spacing": 5, 121 | "alignment": "start", 122 | "horizontal_alignment": "start", 123 | "style": cls.defaut_style() 124 | } 125 | 126 | @classmethod 127 | def defaut_json(cls, left=0, top=0): 128 | return FletBaseElement.defaut_json(cls, "column", left, top) 129 | -------------------------------------------------------------------------------- /evue/elements/combobox.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .fletbaseelement import FletBaseElement 3 | from flet import ( 4 | Draggable, 5 | Dropdown, 6 | dropdown, 7 | alignment, 8 | ) 9 | 10 | from .widgets import BaseContainer 11 | from loguru import logger 12 | 13 | 14 | class ComboboxElement(FletBaseElement): 15 | 16 | def __init__(self, node, parent, draggable=False, sessionID=None): 17 | super().__init__(node, parent, draggable, sessionID=sessionID) 18 | self.create(parent, draggable) 19 | self.setParent(parent) 20 | 21 | def create(self, parent, draggable=False): 22 | self._dropdown_ = Dropdown(content_padding=5, expand=True) 23 | if draggable: 24 | self._obj_ = BaseContainer(Draggable(content=self._dropdown_), 25 | alignment=alignment.center 26 | ) 27 | self._obj_.content.element = self 28 | else: 29 | self._obj_ = BaseContainer(self._dropdown_, alignment=alignment.center) 30 | 31 | def on_change(e): 32 | self.value = e.control.value 33 | 34 | self._dropdown_.on_change = on_change 35 | 36 | def set_width(self, value): 37 | self._obj_.width = value 38 | 39 | def set_height(self, value): 40 | self._obj_.height = value 41 | 42 | def set_border_width(self, value): 43 | self._obj_.border_width = value 44 | self._dropdown_.border_width = value 45 | 46 | def set_border_radius(self, value): 47 | self._obj_.border_radius = value 48 | self._dropdown_.border_radius = value 49 | 50 | def set_border_color(self, value): 51 | self._obj_.border_color = value 52 | self._dropdown_.border_color = value 53 | 54 | def set_color(self, value): 55 | self._dropdown_.color = value 56 | 57 | def set_font_size(self, value): 58 | if value == "None": 59 | self._dropdown_.text_size = 20 60 | else: 61 | self._dropdown_.text_size = int(value) 62 | 63 | def set_padding(self, value): 64 | self._dropdown_.content_padding = int(value) 65 | 66 | def set_options(self, value): 67 | options = [dropdown.Option(item) for item in value] 68 | self._dropdown_.options = options 69 | 70 | def set_value(self, value): 71 | self._dropdown_.value = value 72 | 73 | def set_disabled(self, value): 74 | self._dropdown_.disabled = FletBaseElement.bool(value) 75 | 76 | def set_onValueChanged(self, func): 77 | def on_change(e): 78 | self.value = e.control.value 79 | func(self) 80 | self._dropdown_.on_change = on_change 81 | 82 | @classmethod 83 | def default_events(cls, id): 84 | ret = FletBaseElement.default_events(id) 85 | ret['onValueChanged'] = { 86 | "code": "pass", 87 | "name": "on_%s_valueChanged" % id 88 | } 89 | return ret 90 | 91 | @property 92 | def attributes(self): 93 | attributes = super().attributes 94 | attributes.update({ 95 | "options": self.options, 96 | "value": self._dropdown_.value 97 | }) 98 | return attributes 99 | 100 | def set_attributes(self, node): 101 | super().set_attributes(node) 102 | style = node["attributes"]['style'] 103 | self.font_size = style['font-size'] 104 | self.padding = style['padding'] 105 | if 'color' in style: 106 | self.color = style['color'] 107 | # attr 108 | attributes = node['attributes'] 109 | self.options = attributes["options"] 110 | self.value = attributes["value"] 111 | 112 | def set_tiny_attributes(self, data): 113 | style = data['style'] 114 | self.font_size = style['font-size'] 115 | self.color = style['color'] 116 | # attr 117 | self.options = data["options"] 118 | self.value = data["value"] 119 | 120 | @classmethod 121 | def defaut_style(cls): 122 | return { 123 | "width": 120, 124 | "height": 42, 125 | "padding": 2, 126 | "border-width": 0, 127 | "border-radius": 5, 128 | "border-color": "transparent", 129 | "background-color": "white", 130 | "font-size": 20, 131 | } 132 | 133 | @classmethod 134 | def defaut_attributes(cls): 135 | return { 136 | "options": ['a', 'b', 'c'], 137 | "value": 'a', 138 | "style": cls.defaut_style() 139 | } 140 | 141 | @classmethod 142 | def defaut_json(cls, left=0, top=0): 143 | return FletBaseElement.defaut_json(cls, "combobox", left, top) 144 | -------------------------------------------------------------------------------- /evue/elements/counter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .fletbaseelement import FletBaseElement 3 | from flet import ( 4 | Draggable, 5 | alignment 6 | ) 7 | from .widgets import Counter 8 | from .widgets import BaseContainer 9 | 10 | 11 | class CounterElement(FletBaseElement): 12 | 13 | def __init__(self, node, parent, draggable=False, sessionID=None): 14 | super().__init__(node, parent, draggable, sessionID=sessionID) 15 | self.create(parent, draggable) 16 | self.setParent(parent) 17 | 18 | def create(self, parent, draggable=False): 19 | self._counter_ = Counter() 20 | self._text_ = self._counter_.text_number 21 | self.minusIconButton = self._counter_.minusIconButton 22 | self.addIconButton = self._counter_.addIconButton 23 | if draggable: 24 | self._obj_ = BaseContainer(Draggable(content=self._counter_), 25 | alignment=alignment.center 26 | ) 27 | self._obj_.content.element = self 28 | else: 29 | self._obj_ = self._counter_ 30 | 31 | def on_change(value): 32 | self.value = int(value) 33 | 34 | self._counter_.on_change = on_change 35 | 36 | def set_width(self, value): 37 | self._counter_.set_width(value) 38 | 39 | def set_border_width(self, value): 40 | self._counter_.set_border_width(value) 41 | 42 | def set_border_radius(self, value): 43 | self._counter_.set_border_radius(value) 44 | 45 | def set_border_color(self, value): 46 | self._counter_.set_border_color(value) 47 | 48 | def set_font_size(self, value): 49 | self._counter_.set_font_size(value) 50 | 51 | def set_color(self, value): 52 | self._counter_.set_color(value) 53 | 54 | def set_text_align(self, value): 55 | if value == "left": 56 | self._obj_.alignment = alignment.center_left 57 | elif value == "center": 58 | self._obj_.alignment = alignment.center 59 | elif value == "right": 60 | self._obj_.alignment = alignment.center_right 61 | else: 62 | self._obj_.alignment = alignment.center 63 | 64 | if value == "None": 65 | self._text_.text_align = "center" 66 | else: 67 | self._text_.text_align = value 68 | 69 | def set_value(self, value): 70 | self._counter_.set_value(int(value)) 71 | 72 | def set_onValueChanged(self, func): 73 | def on_change(value): 74 | self.value = value 75 | func(self) 76 | self._counter_.on_change = on_change 77 | 78 | @classmethod 79 | def default_events(cls, id): 80 | ret = FletBaseElement.default_events(id) 81 | ret['onValueChanged'] = { 82 | "code": "pass", 83 | "name": "on_%s_valueChanged" % id 84 | } 85 | return ret 86 | 87 | @property 88 | def attributes(self): 89 | attributes = super().attributes 90 | attributes.update({ 91 | "value": self.value 92 | }) 93 | return attributes 94 | 95 | @property 96 | def style(self): 97 | style = super().style 98 | style.update({ 99 | "color": self.color, 100 | "font-size": self.font_size, 101 | "text-align": self.text_align 102 | }) 103 | return style 104 | 105 | def set_attributes(self, node): 106 | super().set_attributes(node) 107 | style = node["attributes"]['style'] 108 | self.font_size = style['font-size'] 109 | self.color = style['color'] 110 | self.text_align = style['text-align'] 111 | # attr 112 | attributes = node['attributes'] 113 | self.value = attributes["value"] 114 | 115 | def set_tiny_attributes(self, data): 116 | style = data['style'] 117 | self.font_size = style['font-size'] 118 | self.color = style['color'] 119 | self.text_align = style['text-align'] 120 | # attr 121 | self.value = data["value"] 122 | 123 | @classmethod 124 | def defaut_style(cls): 125 | return { 126 | "width": 190, 127 | "height": 40, 128 | "border-width": 1, 129 | "border-radius": 0, 130 | "border-color": "white", 131 | "background-color": "transparent", 132 | "color": "white", 133 | "font-size": 14, 134 | "text-align": "center" 135 | } 136 | 137 | @classmethod 138 | def defaut_attributes(cls): 139 | return { 140 | "value": 0, 141 | "style": cls.defaut_style() 142 | } 143 | 144 | @classmethod 145 | def defaut_json(cls, left=0, top=0): 146 | return FletBaseElement.defaut_json(cls, "counter", left, top) 147 | -------------------------------------------------------------------------------- /evue/elements/dialog.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .fletbaseelement import FletBaseElement 3 | from flet import ( 4 | Stack, 5 | Text, 6 | AlertDialog, 7 | TextButton 8 | ) 9 | from flet.buttons import * 10 | from ..globalthis import globalThis 11 | from .widgets import BaseContainer 12 | 13 | 14 | class DialogElement(FletBaseElement): 15 | 16 | def __init__(self, node, parent, draggable=False, sessionID=None): 17 | super().__init__(node, parent, draggable, sessionID=sessionID) 18 | self.create(parent, draggable) 19 | self.setParent(parent) 20 | 21 | def create(self, parent, draggable=False): 22 | self._stack_ = Stack([]) 23 | self._obj_ = BaseContainer( 24 | self._stack_, 25 | ) 26 | self._controls_ = self._stack_.controls 27 | self._dialog_ = AlertDialog(content=self._obj_, shape=RoundedRectangleBorder(radius=10)) 28 | self._dialog_.parentElement = self 29 | 30 | def close_dlg(e): 31 | self._dialog_.open = False 32 | self.page.update() 33 | 34 | actions=[ 35 | TextButton("确定", on_click=close_dlg), 36 | TextButton("取消", on_click=close_dlg), 37 | ] 38 | self._dialog_.actions = actions 39 | 40 | @property 41 | def isContainer(self): 42 | return True 43 | 44 | def set_open(self, value): 45 | self._dialog_.open = self.bool(value) 46 | 47 | def set_title(self, value): 48 | self._dialog_.title = Text(value) 49 | 50 | def set_modal(self, value): 51 | self._dialog_.modal = self.bool(value) 52 | 53 | def set_title_padding(self, value): 54 | self._dialog_.title_padding = value 55 | 56 | def set_content_padding(self, value): 57 | self._dialog_.content_padding = value 58 | 59 | def set_actions_alignment(self, value): 60 | self._dialog_.actions_alignment = value 61 | 62 | def set_actions_texts(self, value): 63 | if self._dialog_.actions: 64 | textButton0:TextButton = self._dialog_.actions[0] 65 | textButton0.text = value[0] 66 | textButton1:TextButton = self._dialog_.actions[1] 67 | textButton1.text = value[1] 68 | 69 | def set_action_enter_text(self, value): 70 | if self._dialog_.actions: 71 | textButton:TextButton = self._dialog_.actions[0] 72 | textButton.text = value 73 | 74 | def set_action_cancel_text(self, value): 75 | if self._dialog_.actions: 76 | textButton:TextButton = self._dialog_.actions[1] 77 | textButton.text = value 78 | 79 | 80 | def set_onAccept(self, func): 81 | def action(e): 82 | self._dialog_.open = not func(e) 83 | self.page.update() 84 | if not self._dialog_.open: 85 | self.page.dialog = None 86 | 87 | self._dialog_.actions[0].on_click = action 88 | 89 | def set_onCancel(self, func): 90 | def action(e): 91 | self._dialog_.open = not func(e) 92 | self.page.update() 93 | if not self._dialog_.open: 94 | self.page.dialog = None 95 | self._dialog_.actions[1].on_click = action 96 | 97 | def set_onDismiss(self, func): 98 | def action(e): 99 | self._dialog_.open = not func(e) 100 | self.page.update() 101 | if not self._dialog_.open: 102 | self.page.dialog = None 103 | self._dialog_.on_dismiss = action 104 | 105 | def set_border_radius(self, value): 106 | self._dialog_.shape = RoundedRectangleBorder(radius=value) 107 | 108 | @property 109 | def attributes(self): 110 | attributes = super().attributes 111 | attributes.update({ 112 | "open": self._dialog_.open, 113 | "title": self._dialog_.title, 114 | "modal": self._dialog_.modal, 115 | "title_padding": self._dialog_.title_padding, 116 | "content_padding": self._dialog_.content_padding, 117 | "border-radius": 10 118 | }) 119 | return attributes 120 | -------------------------------------------------------------------------------- /evue/elements/div.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from loguru import logger 4 | from .fletbaseelement import FletBaseElement 5 | from flet import ( 6 | Stack, 7 | Draggable, 8 | alignment, 9 | types 10 | ) 11 | from PIL import Image as PILImage 12 | from .widgets import EvueContainer, BaseContainer 13 | 14 | 15 | 16 | def getSize(src): 17 | if src.endswith(".svg"): 18 | with open(src, "r", encoding="utf-8") as f: 19 | content = f.read() 20 | 21 | def getValue(key): 22 | wIndex = content.index("%s=" % key) 23 | i = wIndex 24 | while content[i:i+4] != "px\" ": 25 | i += 1 26 | value = int(content[wIndex+len(key)+2:i]) 27 | return value 28 | 29 | width = getValue("width") 30 | height = getValue("height") 31 | else: 32 | path = os.path.abspath("%s/%s" % (os.getcwd(), src)) 33 | img = PILImage.open(path) 34 | size = img.size 35 | width = size[0] 36 | height = size[1] 37 | return width, height 38 | 39 | class DivElement(FletBaseElement): 40 | 41 | def __init__(self, node, parent, draggable=False, sessionID=None): 42 | super().__init__(node, parent, draggable, sessionID=sessionID) 43 | self.create(parent, draggable) 44 | self.setParent(parent) 45 | 46 | def create(self, parent, draggable=False): 47 | self._stack_ = Stack([]) 48 | self._div_ = EvueContainer( 49 | self._stack_ 50 | ) 51 | self._controls_ = self._stack_.controls 52 | if draggable: 53 | draggable = Draggable(content=self._div_) 54 | self._obj_ = EvueContainer( 55 | draggable, 56 | alignment = alignment.center 57 | ) 58 | self._div_.element = self 59 | else: 60 | self._obj_ = self._div_ 61 | 62 | @property 63 | def isContainer(self): 64 | return True 65 | 66 | @property 67 | def isLayout(self): 68 | return False 69 | 70 | def set_width(self, value): 71 | self._obj_.width = value 72 | 73 | def set_height(self, value): 74 | self._obj_.height = value 75 | 76 | def set_src(self, value): 77 | if value in self.resources: 78 | self._obj_.image_src_base64 = self.resources[value] 79 | return 80 | 81 | from evue import globalThis 82 | if value.startswith("http"): 83 | self._obj_.image_src = f"%s" % value 84 | else: 85 | if globalThis.isWeb(): 86 | value = "http://%s:%d/%s" % (globalThis.server_ip, globalThis.port, value) 87 | else: 88 | size = getSize(value) 89 | self.width = size[0] 90 | self.height = size[1] 91 | self._obj_.image_src = f"%s" % value 92 | 93 | def set_image_src_base64(self, value): 94 | self._obj_.image_src_base64 = value 95 | 96 | def set_image_repeat(self, value): 97 | self._obj_.image_repeat = value 98 | 99 | def set_image_fit(self, value): 100 | self._obj_.image_fit = value 101 | 102 | def set_image_opacity(self, value): 103 | self._obj_.image_opacity = value 104 | 105 | def set_shape(self, value): 106 | if value == "rectangle": 107 | self._obj_.shape = types.BoxShape.RECTANGLE 108 | else: 109 | self._obj_.shape = types.BoxShape.CIRCLE 110 | 111 | def set_attributes(self, node): 112 | super().set_attributes(node) 113 | 114 | @classmethod 115 | def defaut_json(cls, left=0, top=0): 116 | return FletBaseElement.defaut_json(cls, "div", left, top) 117 | -------------------------------------------------------------------------------- /evue/elements/grid.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .fletbaseelement import FletBaseElement 3 | from flet import ( 4 | Draggable, 5 | GridView, 6 | alignment 7 | ) 8 | from .fletbaseelement import FletBaseElement 9 | from .widgets import BaseContainer 10 | 11 | 12 | class GridElement(FletBaseElement): 13 | 14 | def __init__(self, node, parent, draggable=False, sessionID=None): 15 | super().__init__(node, parent, draggable, sessionID=sessionID) 16 | self.create(parent, draggable) 17 | self.setParent(parent) 18 | 19 | def create(self, parent, draggable=False): 20 | self._grid_ = GridView([], expand=True) 21 | self._controls_ = self._grid_.controls 22 | if draggable: 23 | self._obj_ = BaseContainer( 24 | Draggable(content=self._grid_), 25 | alignment = alignment.center 26 | ) 27 | self._obj_.content.element = self 28 | else: 29 | self._obj_ = BaseContainer( 30 | self._grid_ 31 | ) 32 | 33 | @property 34 | def isContainer(self): 35 | return True 36 | 37 | @property 38 | def isLayout(self): 39 | return True 40 | 41 | def set_width(self, value): 42 | self._obj_.width = value 43 | 44 | def set_height(self, value): 45 | self._obj_.height = value 46 | 47 | def set_horizontal(self, value): 48 | self._grid_.horizontal = FletBaseElement.bool(value) 49 | 50 | def set_runs_count(self, value): 51 | self._grid_.runs_count = value 52 | 53 | def set_max_extent(self, value): 54 | self._grid_.max_extent = value 55 | 56 | def set_child_aspect_ratio(self, value): 57 | self._grid_.child_aspect_ratio = value 58 | 59 | def set_spacing(self, value): 60 | self._grid_.spacing = value 61 | self._grid_.run_spacing = value 62 | 63 | def set_padding(self, value): 64 | self._grid_.padding = value 65 | 66 | def set_attributes(self, node): 67 | super().set_attributes(node) 68 | attributes = node['attributes'] 69 | style = attributes['style'] 70 | # attr 71 | self.horizontal = attributes["horizontal"] 72 | self.runs_count = attributes["runs_count"] 73 | self.max_extent = attributes["max_extent"] 74 | self.spacing = attributes["spacing"] 75 | self.run_spacing = attributes["run_spacing"] 76 | self.child_aspect_ratio = attributes["child_aspect_ratio"] 77 | self.padding = attributes["padding"] 78 | 79 | def set_tiny_attributes(self, data): 80 | # attr 81 | self.horizontal = data["horizontal"] 82 | self.runs_count = data["runs_count"] 83 | self.max_extent = data["max_extent"] 84 | self.spacing = data["spacing"] 85 | self.run_spacing = data["run_spacing"] 86 | self.child_aspect_ratio = data["child_aspect_ratio"] 87 | self.padding = data["padding"] 88 | 89 | @property 90 | def attributes(self): 91 | attributes = super().attributes 92 | attributes.update({ 93 | "horizontal": self._grid_.horizontal, 94 | "runs_count": self._grid_.runs_count, 95 | "max_extent": self._grid_.max_extent, 96 | "spacing": self._grid_.spacing, 97 | "run_spacing": self._grid_.run_spacing, 98 | "child_aspect_ratio": self._grid_.child_aspect_ratio, 99 | "padding": self._grid_.padding 100 | }) 101 | return attributes 102 | 103 | @classmethod 104 | def defaut_style(cls): 105 | return { 106 | "left": 0, 107 | "top": 0, 108 | "width": 200, 109 | "height": 200, 110 | "border-width": 0, 111 | "border-radius": 0, 112 | "border-color": "transparent", 113 | "background-color": "white" 114 | } 115 | 116 | @classmethod 117 | def defaut_attributes(cls): 118 | return { 119 | "horizontal": False, 120 | "runs_count": 5, 121 | "max_extent": 150, 122 | "spacing": 5, 123 | "run_spacing": 5, 124 | "child_aspect_ratio": 1, 125 | "padding": 5, 126 | "style": cls.defaut_style() 127 | } 128 | 129 | @classmethod 130 | def defaut_json(cls, left=0, top=0): 131 | return FletBaseElement.defaut_json(cls, "grid", left, top) 132 | -------------------------------------------------------------------------------- /evue/elements/icon.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .fletbaseelement import FletBaseElement 3 | from flet import ( 4 | Draggable, 5 | alignment, 6 | Icon, 7 | icons 8 | ) 9 | from .widgets import BaseContainer 10 | 11 | 12 | class IconElement(FletBaseElement): 13 | 14 | def __init__(self, node, parent, draggable=False, sessionID=None): 15 | super().__init__(node, parent, draggable, sessionID=sessionID) 16 | self.create(parent, draggable) 17 | self.setParent(parent) 18 | 19 | def create(self, parent, draggable=False): 20 | self._icon_ = Icon() 21 | if draggable: 22 | self._obj_ = BaseContainer( 23 | Draggable(content=self._icon_), 24 | alignment = alignment.center 25 | ) 26 | self._obj_.content.element = self 27 | else: 28 | self._obj_ = BaseContainer( 29 | self._icon_, 30 | alignment = alignment.center 31 | ) 32 | 33 | def onclick(e): 34 | pass 35 | self.onclick = onclick 36 | 37 | def set_width(self, value): 38 | self._obj_.width = value 39 | self._icon_.width = value 40 | 41 | def set_height(self, value): 42 | self._obj_.height = value 43 | self._icon_.height = value 44 | 45 | def set_name(self, value): 46 | if value.startswith("icons."): 47 | name = value[6:] 48 | if hasattr(icons, name): 49 | self._icon_.name = getattr(icons, name) 50 | else: 51 | self._icon_.name = name 52 | 53 | def set_size(self, value): 54 | self._icon_.size = int(value) 55 | 56 | def set_color(self, value): 57 | self._icon_.color = value 58 | 59 | @property 60 | def attributes(self): 61 | attributes = super().attributes 62 | attributes.update({ 63 | "name": self._icon_.name 64 | }) 65 | return attributes 66 | 67 | def set_attributes(self, node): 68 | super().set_attributes(node) 69 | # attr 70 | attributes = node['attributes'] 71 | self.name = attributes["name"] 72 | 73 | 74 | @classmethod 75 | def defaut_style(cls): 76 | return { 77 | "left": 0, 78 | "top": 0, 79 | "border-width": 0, 80 | "border-color": "#282828", 81 | "background-color": "#282828" 82 | } 83 | 84 | @classmethod 85 | def defaut_attributes(cls): 86 | return { 87 | "name": "/image/logo.png", 88 | "size": 32, 89 | "style": cls.defaut_style() 90 | } 91 | 92 | @classmethod 93 | def defaut_json(cls, left=0, top=0): 94 | return FletBaseElement.defaut_json(cls, "image", left, top) 95 | -------------------------------------------------------------------------------- /evue/elements/iconbutton.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from flet import ( 3 | Draggable, 4 | alignment, 5 | IconButton 6 | ) 7 | from .fletbaseelement import FletBaseElement 8 | from .widgets import BaseContainer 9 | 10 | 11 | class IconButtonElement(FletBaseElement): 12 | 13 | def __init__(self, node, parent, draggable=False, sessionID=None): 14 | super().__init__(node, parent, draggable, sessionID=sessionID) 15 | self.create(parent, draggable) 16 | self.setParent(parent) 17 | 18 | def create(self, parent, draggable=False): 19 | self._button_ = IconButton() 20 | if draggable: 21 | self._obj_ = BaseContainer( 22 | Draggable(content=self._button_), 23 | alignment = alignment.center 24 | ) 25 | self._obj_.content.element = self 26 | else: 27 | self._obj_ = BaseContainer( 28 | self._button_, 29 | alignment = alignment.center 30 | ) 31 | 32 | def set_width(self, value): 33 | self._obj_.width = value 34 | 35 | def set_height(self, value): 36 | self._obj_.height = value 37 | 38 | def set_icon(self, value): 39 | from flet import icons 40 | if value.startswith("icons."): 41 | name = value[6:] 42 | if hasattr(icons, name): 43 | self._button_.icon = getattr(icons, name) 44 | else: 45 | self._button_.icon = value 46 | 47 | def set_name(self, value): 48 | self.set_icon(value) 49 | 50 | def set_size(self, value): 51 | self._button_.icon_size = value 52 | 53 | def set_color(self, value): 54 | self._button_.icon_color = value 55 | 56 | def set_tooltip(self, value): 57 | self._button_.tooltip = value 58 | 59 | @property 60 | def attributes(self): 61 | attributes = super().attributes 62 | attributes.update({ 63 | "icon": self.icon, 64 | "size": self._button_.icon_size, 65 | "tooltip": self._button_.tooltip, 66 | "color": self._button_.icon_color 67 | }) 68 | return attributes 69 | 70 | def set_attributes(self, node): 71 | super().set_attributes(node) 72 | # attr 73 | attributes = node['attributes'] 74 | self.icon = attributes["icon"] 75 | self.size = attributes["size"] 76 | self.tooltip = attributes["tooltip"] 77 | self.color = attributes["color"] 78 | 79 | 80 | @classmethod 81 | def defaut_style(cls): 82 | return { 83 | "left": 0, 84 | "top": 0, 85 | "width": 64, 86 | "height": 64, 87 | "border-width": 0, 88 | "border-color": "transparent", 89 | "background-color": "transparent" 90 | } 91 | 92 | @classmethod 93 | def defaut_attributes(cls): 94 | return { 95 | "icon": "icons.INSERT_EMOTICON_SHARP", 96 | "tooltip": "smile", 97 | "size": 48, 98 | "color": "white", 99 | "style": cls.defaut_style() 100 | } 101 | 102 | @classmethod 103 | def defaut_json(cls, left=0, top=0): 104 | return FletBaseElement.defaut_json(cls, "iconbutton", left, top) 105 | -------------------------------------------------------------------------------- /evue/elements/image.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from flet import ( 4 | Draggable, 5 | Image, 6 | Stack, 7 | alignment 8 | ) 9 | from .fletbaseelement import FletBaseElement 10 | from PIL import Image as PILImage 11 | from .widgets import BaseContainer 12 | from loguru import logger 13 | from ..globalthis import globalThis 14 | 15 | 16 | def getSize(src): 17 | if src.endswith(".svg"): 18 | with open(src, "r", encoding="utf-8") as f: 19 | content = f.read() 20 | 21 | def getValue(key): 22 | wIndex = content.index("%s=" % key) 23 | i = wIndex 24 | while content[i:i+4] != "px\" ": 25 | i += 1 26 | value = int(content[wIndex+len(key)+2:i]) 27 | return value 28 | 29 | width = getValue("width") 30 | height = getValue("height") 31 | else: 32 | path = os.path.abspath("%s/%s" % (globalThis.assets_dir, src)) 33 | img = PILImage.open(path) 34 | size = img.size 35 | width = size[0] 36 | height = size[1] 37 | return width, height 38 | 39 | class ImageElement(FletBaseElement): 40 | 41 | def __init__(self, node, parent, draggable=False, sessionID=None): 42 | super().__init__(node, parent, draggable, sessionID=sessionID) 43 | self.create(parent, draggable) 44 | self.setParent(parent) 45 | 46 | def create(self, parent, draggable=False): 47 | self._image_ = Image(gapless_playback=True) 48 | if draggable: 49 | self._obj_ = BaseContainer( 50 | Draggable(content=self._image_), 51 | alignment = alignment.center 52 | ) 53 | self._obj_.content.element = self 54 | else: 55 | self._obj_ = BaseContainer( 56 | self._image_, 57 | alignment = alignment.center 58 | ) 59 | @property 60 | def isContainer(self): 61 | return False 62 | 63 | @property 64 | def isLayout(self): 65 | return False 66 | 67 | def set_width(self, value): 68 | self._obj_.width = value 69 | self._image_.width = value 70 | 71 | def set_height(self, value): 72 | self._obj_.height = value 73 | self._image_.height = value 74 | 75 | def set_tooltip(self, value): 76 | self._image_.tooltip = value 77 | 78 | def set_src(self, value): 79 | if value in self.resources: 80 | logger.warning(value) 81 | self.src_base64 = self.resources[value] 82 | return 83 | 84 | if value.startswith("http"): 85 | self._obj_.image_src = f"%s" % value 86 | else: 87 | if globalThis.isWeb(): 88 | value = "http://%s:%d/%s" % (globalThis.server_ip, globalThis.port, value) 89 | elif "" in value: 90 | self._image_.src = f"%s" % value 91 | else: 92 | try: 93 | size = getSize(value) 94 | self.width = size[0] 95 | self.height = size[1] 96 | except: 97 | pass 98 | self._image_.src = f"%s" % value 99 | self._image_.src_base64 = None 100 | 101 | def set_src_base64(self, value): 102 | self._image_.src_base64 = value 103 | 104 | def set_repeat(self, value): 105 | self._image_.repeat = value 106 | 107 | def set_fit(self, value): 108 | self._image_.fit = value 109 | 110 | def set_image_opacity(self, value): 111 | self._image_.opacity = value 112 | 113 | @property 114 | def attributes(self): 115 | attributes = super().attributes 116 | attributes.update({ 117 | "src": self['src'] 118 | }) 119 | return attributes 120 | 121 | def set_attributes(self, node): 122 | super().set_attributes(node) 123 | # attr 124 | attributes = node['attributes'] 125 | if 'src' in attributes: 126 | self.src = attributes["src"] 127 | 128 | @classmethod 129 | def defaut_style(cls): 130 | style = FletBaseElement.defaut_style() 131 | style.update({ 132 | "background-color": "transparent" 133 | }) 134 | return style 135 | 136 | @classmethod 137 | def defaut_attributes(cls): 138 | return { 139 | "src": "image/logo.png", 140 | "style": cls.defaut_style() 141 | } 142 | 143 | @classmethod 144 | def defaut_json(cls, left=0, top=0): 145 | return FletBaseElement.defaut_json(cls, "image", left, top) 146 | -------------------------------------------------------------------------------- /evue/elements/listitem.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .fletbaseelement import FletBaseElement 3 | from .div import DivElement 4 | 5 | 6 | class ListItemElement(DivElement): 7 | 8 | def __init__(self, node, parent, draggable=False, sessionID=None): 9 | super().__init__(node, parent, draggable, sessionID=sessionID) 10 | 11 | @classmethod 12 | def defaut_style(cls): 13 | return { 14 | "left": 0, 15 | "top": 0, 16 | "width": 150, 17 | "height": 40, 18 | "border-width": 0, 19 | "border-color": "transparent", 20 | "border-radius": 0, 21 | "background-color": "#282828", 22 | } 23 | 24 | @classmethod 25 | def defaut_json(cls, left=0, top=0): 26 | return FletBaseElement.defaut_json(cls, "listitem", left, top) 27 | -------------------------------------------------------------------------------- /evue/elements/listview.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from flet import ( 4 | Draggable, 5 | alignment, 6 | ListView 7 | ) 8 | from .fletbaseelement import FletBaseElement 9 | from .widgets import BaseContainer 10 | from loguru import logger 11 | 12 | 13 | class ListViewElement(FletBaseElement): 14 | 15 | def __init__(self, node, parent, draggable=False, sessionID=None): 16 | super().__init__(node, parent, draggable, sessionID=sessionID) 17 | self.create(parent, draggable) 18 | self.setParent(parent) 19 | 20 | def create(self, parent, draggable=False): 21 | self._listview_ = ListView(expand=True) 22 | self._controls_ = self._listview_.controls 23 | if draggable: 24 | self._obj_ = BaseContainer( 25 | Draggable(content=self._listview_), 26 | alignment = alignment.center 27 | ) 28 | self._obj_.content.element = self 29 | else: 30 | self._obj_ = BaseContainer( 31 | self._listview_ 32 | ) 33 | @property 34 | def isContainer(self): 35 | return True 36 | 37 | @property 38 | def isLayout(self): 39 | return True 40 | 41 | def set_expand(self, value): 42 | self.obj.expend = FletBaseElement.bool(value) 43 | self._listview_.expend = FletBaseElement.bool(value) 44 | 45 | def set_horizontal(self, value): 46 | self._listview_.horizontal = FletBaseElement.bool(value) 47 | 48 | def set_spacing(self, value): 49 | self._listview_.spacing = value 50 | 51 | def set_divider(self, value): 52 | self._listview_.divider_thickness = value 53 | 54 | def set_padding(self, value): 55 | self._listview_.padding = value 56 | 57 | def set_onCurrentIndexChanged(self, value): 58 | self._listview_.currentIndex = value 59 | 60 | def set_attributes(self, node): 61 | super().set_attributes(node) 62 | attributes = node['attributes'] 63 | style = attributes['style'] 64 | # attr 65 | self.expand = attributes["expand"] 66 | self.horizontal = attributes["horizontal"] 67 | self.spacing = attributes["spacing"] 68 | self.divider = attributes["divider"] 69 | self.padding = attributes["padding"] 70 | 71 | def set_tiny_attributes(self, attributes): 72 | # attr 73 | self.expand = attributes["expand"] 74 | self.horizontal = attributes["horizontal"] 75 | self.spacing = attributes["spacing"] 76 | self.divider = attributes["divider"] 77 | self.padding = attributes["padding"] 78 | 79 | @property 80 | def attributes(self): 81 | attributes = super().attributes 82 | attributes.update({ 83 | "expand": self._listview_.expand, 84 | "horizontal": self._listview_.horizontal, 85 | "spacing": self._listview_.spacing, 86 | "divider": self._listview_.divider_thickness, 87 | "padding": self._listview_.padding 88 | }) 89 | return attributes 90 | 91 | @classmethod 92 | def defaut_style(cls): 93 | return { 94 | "left": 0, 95 | "top": 0, 96 | "width": 150, 97 | "height": 400, 98 | "border-width": 0, 99 | "border-radius": 0, 100 | "border-color": "transparent", 101 | "background-color": "white" 102 | } 103 | 104 | @classmethod 105 | def defaut_attributes(cls): 106 | return { 107 | "expand": True, 108 | "horizontal": False, 109 | "spacing": 5, 110 | "divider": 0, 111 | "padding": 5, 112 | "style": cls.defaut_style() 113 | } 114 | 115 | @classmethod 116 | def defaut_json(cls, left=0, top=0): 117 | return FletBaseElement.defaut_json(cls, "listview", left, top) 118 | -------------------------------------------------------------------------------- /evue/elements/markdown.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .fletbaseelement import FletBaseElement 3 | from flet import ( 4 | Draggable, 5 | alignment, 6 | Markdown, 7 | TextStyle 8 | ) 9 | from .widgets import BaseContainer 10 | from loguru import logger 11 | 12 | 13 | class MarkdownElement(FletBaseElement): 14 | 15 | def __init__(self, node, parent, draggable=False, sessionID=None): 16 | super().__init__(node, parent, draggable, sessionID=sessionID) 17 | self.create(parent, draggable) 18 | self.setParent(parent) 19 | 20 | def create(self, parent, draggable=False): 21 | self._markdown_ = Markdown( 22 | selectable=True, 23 | extension_set="gitHubWeb", 24 | code_theme="atom-one-dark", 25 | code_style=TextStyle(font_family="Roboto Mono"), 26 | on_tap_link=lambda e: self.page.launch_url(e.data), 27 | ) 28 | if draggable: 29 | self._obj_ = BaseContainer( 30 | Draggable(content=self._markdown_), 31 | alignment = alignment.center 32 | ) 33 | self._obj_.content.element = self 34 | else: 35 | self._obj_ = BaseContainer( 36 | self._markdown_, 37 | alignment = alignment.top_left, 38 | expand=True 39 | ) 40 | 41 | def set_width(self, value): 42 | self._obj_.width = value 43 | self._markdown_.width = value 44 | 45 | def set_height(self, value): 46 | self._obj_.height = value 47 | self._markdown_.height = value 48 | 49 | def set_content(self, value): 50 | self._markdown_.value = value 51 | 52 | def set_selectable(self, value): 53 | self._markdown_.selectable = FletBaseElement.bool(value) 54 | 55 | def set_code_theme(self, value): 56 | self._markdown_.code_theme = value 57 | 58 | def set_code_style(self, value): 59 | self._markdown_.code_theme = TextStyle(font_family="Roboto Mono") 60 | 61 | def set_extension_set(self, value): 62 | self._markdown_.extension_set = value 63 | 64 | @property 65 | def attributes(self): 66 | attributes = super().attributes 67 | attributes.update({ 68 | "content": self._markdown_.value, 69 | "selectable": self._markdown_.selectable, 70 | "code_theme": self._markdown_.code_theme, 71 | "extension_set": self._markdown_.extension_set 72 | }) 73 | return attributes 74 | 75 | @property 76 | def style(self): 77 | style = super().style 78 | style.update({ 79 | "color": self.color, 80 | "font-size": self.font_size, 81 | "text-align": self.text_align 82 | }) 83 | return style 84 | 85 | def set_attributes(self, node): 86 | super().set_attributes(node) 87 | # attr 88 | attributes = node['attributes'] 89 | self.content = attributes["content"] 90 | self.selectable = attributes["selectable"] 91 | self.code_theme = attributes["code_theme"] 92 | self.extension_set = attributes["extension_set"] 93 | 94 | def set_attributes(self, node): 95 | super().set_attributes(node) 96 | style = node["attributes"]['style'] 97 | self.font_size = style['font-size'] 98 | self.color = style['color'] 99 | self.text_align = style['text-align'] 100 | # attr 101 | attributes = node['attributes'] 102 | self.content = attributes["content"] 103 | self.selectable = attributes["selectable"] 104 | self.code_theme = attributes["code_theme"] 105 | self.extension_set = attributes["extension_set"] 106 | 107 | def set_tiny_attributes(self, attributes): 108 | style = attributes['style'] 109 | self.font_size = style['font-size'] 110 | self.color = style['color'] 111 | self.text_align = style['text-align'] 112 | # attr 113 | self.content = attributes["content"] 114 | self.selectable = attributes["selectable"] 115 | self.code_theme = attributes["code_theme"] 116 | self.extension_set = attributes["extension_set"] 117 | 118 | @classmethod 119 | def defaut_style(cls): 120 | return { 121 | "left": 0, 122 | "top": 0, 123 | "border-width": 0, 124 | "border-color": "#282828", 125 | "background-color": "#282828", 126 | "color": "white", 127 | "font-size": 20, 128 | "text-align": "center" 129 | } 130 | 131 | @classmethod 132 | def defaut_attributes(cls): 133 | return { 134 | "content": "#1111", 135 | "selectable": True, 136 | "code_theme": "atom-one-dark", 137 | "extension_set": "gitHubWeb", 138 | "style": cls.defaut_style() 139 | } 140 | 141 | @classmethod 142 | def defaut_json(cls, left=0, top=0): 143 | return FletBaseElement.defaut_json(cls, "markdown", left, top) 144 | -------------------------------------------------------------------------------- /evue/elements/progress.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from flet import ( 3 | Draggable, 4 | alignment, 5 | border_radius 6 | ) 7 | from .fletbaseelement import FletBaseElement 8 | from .widgets import BaseContainer 9 | 10 | 11 | class ProgressElement(FletBaseElement): 12 | 13 | def __init__(self, node, parent, draggable=False, sessionID=None): 14 | super().__init__(node, parent, draggable, sessionID=sessionID) 15 | self.create(parent, draggable) 16 | self.setParent(parent) 17 | self['percent'] = 0 18 | 19 | def create(self, parent, draggable=False): 20 | self._progressbar_ = BaseContainer() 21 | if draggable: 22 | self._obj_ = BaseContainer(Draggable(content=self._progressbar_), 23 | alignment=alignment.center_left 24 | ) 25 | self._obj_.content.element = self 26 | else: 27 | self._obj_ = BaseContainer(self._progressbar_, alignment=alignment.center_left) 28 | 29 | def set_width(self, value): 30 | self._obj_.width = float(value) 31 | self._progressbar_.width = self._obj_.width * (float(self['percent']) / 100) 32 | 33 | def set_height(self, value): 34 | self._obj_.height = value 35 | self._progressbar_.height = value 36 | 37 | def set_border_radius(self, value): 38 | self.obj.border_radius = border_radius.all(value) 39 | self._progressbar_.border_radius = border_radius.all(value) 40 | 41 | def set_value(self, value): 42 | self.set_percent(value) 43 | 44 | def set_percent(self, value): 45 | if self._obj_.width: 46 | self._progressbar_.width = self._obj_.width * (value / 100) 47 | 48 | def set_background_color(self, value): 49 | self._obj_.bgcolor = value 50 | 51 | def set_progress_indic_color(self, value): 52 | self._progressbar_.bgcolor = value 53 | 54 | @property 55 | def style(self): 56 | ret = super().style 57 | ret.update({ 58 | "progress-indic-color": self._progressbar_.bgcolor 59 | }) 60 | return ret 61 | 62 | @property 63 | def attributes(self): 64 | attributes = super().attributes 65 | attributes.update({ 66 | "percent": self['percent'] 67 | }) 68 | return attributes 69 | 70 | def set_attributes(self, node): 71 | super().set_attributes(node) 72 | attributes = node['attributes'] 73 | style = attributes['style'] 74 | self.progress_indic_color = style['progress-indic-color'] 75 | # attr 76 | self.percent = attributes["percent"] 77 | 78 | def set_tiny_attributes(self, data): 79 | style = data['style'] 80 | self.progress_indic_color = style['progress-indic-color'] 81 | # attr 82 | self.percent = data["percent"] 83 | 84 | @classmethod 85 | def defaut_style(cls): 86 | return { 87 | "width": 200, 88 | "height": 32, 89 | "border-width": 0, 90 | "border-radius": 100, 91 | "border-color": "transparent", 92 | "background-color": "white", 93 | "progress-indic-color": "green", 94 | } 95 | 96 | @classmethod 97 | def defaut_attributes(cls): 98 | return { 99 | "percent": 30, 100 | "style": cls.defaut_style() 101 | } 102 | 103 | @classmethod 104 | def defaut_json(cls, left=0, top=0): 105 | return FletBaseElement.defaut_json(cls, "progress", left, top) 106 | -------------------------------------------------------------------------------- /evue/elements/qrcode.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from .fletbaseelement import FletBaseElement 4 | from .image import ImageElement 5 | from PIL import Image as PILImage 6 | from .widgets import BaseContainer 7 | from ..router import globalThis 8 | from io import BytesIO 9 | import qrcode 10 | import base64 11 | import hashlib 12 | 13 | def qrcode_make(s): 14 | qr = qrcode.make(s) 15 | buffered = BytesIO() 16 | qr.save(buffered, format="JPEG") 17 | s1 = base64.b64encode(buffered.getvalue()) 18 | b64_string = s1.decode('utf-8') 19 | return b64_string 20 | 21 | def qrcode_make_png(s): 22 | md5 = hashlib.md5(s.encode("utf-8")).hexdigest() 23 | path = "image/qrcode_%s.png" % (md5) 24 | if not os.path.exists(path): 25 | qr = qrcode.make(s) 26 | buffered = BytesIO() 27 | qr.save(buffered, format="png") 28 | with open(path, "wb") as f: 29 | f.write(buffered.getvalue()) 30 | return path 31 | 32 | class QRCodeElement(ImageElement): 33 | 34 | def __init__(self, node, parent, draggable=False, sessionID=None): 35 | super().__init__(node, parent, draggable, sessionID=sessionID) 36 | 37 | 38 | def set_value(self, value): 39 | value = str(value) 40 | self._image_.src_base64 = qrcode_make(value) 41 | 42 | def set_src(self, value): 43 | value = str(value) 44 | self.set_value(value) 45 | 46 | def set_src_base64(self, value): 47 | self.set_value(value) 48 | 49 | def set_image_src_base64(self, value): 50 | self.set_value(value) 51 | 52 | @property 53 | def attributes(self): 54 | attributes = super().attributes 55 | attributes.update({ 56 | "value": self.value, 57 | "tooltip": self.tooltip 58 | }) 59 | return attributes 60 | 61 | def set_attributes(self, node): 62 | super().set_attributes(node) 63 | # attr 64 | attributes = node['attributes'] 65 | self.value = attributes["value"] 66 | self.tooltip = attributes["tooltip"] 67 | 68 | def set_tiny_attributes(self, attributes): 69 | self.value = attributes["value"] 70 | self.tooltip = attributes["tooltip"] 71 | 72 | @classmethod 73 | def defaut_style(cls): 74 | return { 75 | "width": 128, 76 | "height": 128, 77 | "background-color": "transparent" 78 | } 79 | 80 | @classmethod 81 | def defaut_attributes(cls): 82 | return { 83 | "value": "https://www.yuque.com/bytecode/eu1sci/bgb7ho", 84 | "tooltip": "EVUE 小程序开发文档 2.0", 85 | "style": cls.defaut_style() 86 | } 87 | 88 | @classmethod 89 | def defaut_json(cls, left=0, top=0): 90 | return FletBaseElement.defaut_json(cls, "qrcode", left, top) 91 | -------------------------------------------------------------------------------- /evue/elements/responsiverow.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from flet import ( 3 | Draggable, 4 | ResponsiveRow, 5 | alignment 6 | ) 7 | from .fletbaseelement import FletBaseElement 8 | from .widgets import BaseContainer 9 | from loguru import logger 10 | 11 | 12 | class ResponsiveRowElement(FletBaseElement): 13 | 14 | def __init__(self, node, parent, draggable=False, sessionID=None): 15 | super().__init__(node, parent, draggable, sessionID=sessionID) 16 | self.create(parent, draggable) 17 | self.setParent(parent) 18 | 19 | def create(self, parent, draggable=False): 20 | self._row_ = ResponsiveRow([], expand=True) 21 | self._controls_ = self._row_.controls 22 | if draggable: 23 | self._obj_ = BaseContainer( 24 | Draggable(content=self._row_), 25 | alignment = alignment.center 26 | ) 27 | self._obj_.content.element = self 28 | else: 29 | self._obj_ = BaseContainer( 30 | self._row_, 31 | alignment = alignment.top_left 32 | ) 33 | @property 34 | def isContainer(self): 35 | return True 36 | 37 | @property 38 | def isLayout(self): 39 | return True 40 | 41 | def set_width(self, value): 42 | self._obj_.width = value 43 | 44 | def set_height(self, value): 45 | self._obj_.height = value 46 | 47 | def set_alignment(self, value): 48 | self._row_.alignment = FletBaseElement.alignment(value) 49 | 50 | def set_spacing(self, value): 51 | self._row_.spacing = int(value) 52 | self._row_.run_spacing = int(value) 53 | 54 | def set_cols(self, value): 55 | if isinstance(value, str): 56 | col = {} 57 | items = value.split("-") 58 | for i in range(0, int(len(items)/2)): 59 | col[items[i * 2]] = int(items[i * 2+1]) 60 | self['col'] = col 61 | else: 62 | self['col'] = col 63 | 64 | def set_attributes(self, node): 65 | super().set_attributes(node) 66 | attributes = node['attributes'] 67 | style = attributes['style'] 68 | # attr 69 | self.spacing = attributes["spacing"] 70 | self.alignment = attributes["alignment"] 71 | self.cols = attributes["cols"] 72 | 73 | def set_tiny_attributes(self, attributes): 74 | # attr 75 | self.spacing = attributes["spacing"] 76 | self.alignment = attributes["alignment"] 77 | self.cols = attributes["cols"] 78 | 79 | @property 80 | def attributes(self): 81 | attributes = super().attributes 82 | attributes.update({ 83 | "spacing": self._row_.spacing, 84 | "alignment": self._row_.alignment, 85 | 'cols': self.cols 86 | }) 87 | return attributes 88 | 89 | @classmethod 90 | def defaut_style(cls): 91 | return { 92 | "left": 0, 93 | "top": 0, 94 | "width": 200, 95 | "height": 40, 96 | "border-width": 0, 97 | "border-radius": 0, 98 | "border-color": "transparent", 99 | "background-color": "white" 100 | } 101 | 102 | @classmethod 103 | def defaut_attributes(cls): 104 | return { 105 | "spacing": 5, 106 | "alignment": "start", 107 | "cols": "sm-6-md-4-xl-3", 108 | "style": cls.defaut_style() 109 | } 110 | 111 | @classmethod 112 | def defaut_json(cls, left=0, top=0): 113 | return FletBaseElement.defaut_json(cls, "responsiverow", left, top) 114 | -------------------------------------------------------------------------------- /evue/elements/row.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from flet import ( 3 | Draggable, 4 | Row, 5 | alignment 6 | ) 7 | from .fletbaseelement import FletBaseElement 8 | from .widgets import BaseContainer 9 | 10 | 11 | class RowElement(FletBaseElement): 12 | 13 | def __init__(self, node, parent, draggable=False, sessionID=None): 14 | super().__init__(node, parent, draggable, sessionID=sessionID) 15 | self.create(parent, draggable) 16 | self.setParent(parent) 17 | 18 | def create(self, parent, draggable=False): 19 | self._row_ = Row([], expand=True) 20 | self._row_.scroll = "auto" 21 | self._controls_ = self._row_.controls 22 | if draggable: 23 | self._obj_ = BaseContainer( 24 | Draggable(content=self._row_), 25 | alignment = alignment.center 26 | ) 27 | self._obj_.content.element = self 28 | else: 29 | self._obj_ = BaseContainer( 30 | self._row_ 31 | ) 32 | @property 33 | def isContainer(self): 34 | return True 35 | 36 | @property 37 | def isLayout(self): 38 | return True 39 | 40 | def set_width(self, value): 41 | self._obj_.width = value 42 | 43 | def set_height(self, value): 44 | self._obj_.height = value 45 | 46 | def set_scroll(self, value): 47 | self._row_.scroll = value 48 | 49 | def set_auto_scroll(self, value): 50 | if value == "None" or value is None: 51 | self._row_.auto_scroll = None 52 | else: 53 | self._row_.auto_scroll = FletBaseElement.bool(value) 54 | 55 | def set_alignment(self, value): 56 | self._row_.alignment = FletBaseElement.alignment(value) 57 | 58 | def set_wrap(self, value): 59 | self._row_.wrap = FletBaseElement.bool(value) 60 | 61 | def set_spacing(self, value): 62 | self._row_.spacing = int(value) 63 | self._row_.run_spacing = int(value) 64 | 65 | def set_attributes(self, node): 66 | super().set_attributes(node) 67 | attributes = node['attributes'] 68 | style = attributes['style'] 69 | # attr 70 | self.scroll = attributes["scroll"] 71 | self.auto_scroll = attributes["auto_scroll"] 72 | self.wrap = attributes["wrap"] 73 | self.spacing = attributes["spacing"] 74 | self.alignment = attributes["alignment"] 75 | 76 | def set_tiny_attributes(self, data): 77 | # attr 78 | self.scroll = data["scroll"] 79 | self.auto_scroll = data["auto_scroll"] 80 | self.wrap = data["wrap"] 81 | self.spacing = data["spacing"] 82 | self.alignment = data["alignment"] 83 | 84 | @property 85 | def attributes(self): 86 | attributes = super().attributes 87 | attributes.update({ 88 | "scroll": self._row_.scroll, 89 | "auto_scroll": self._row_.auto_scroll, 90 | "wrap": self._row_.wrap, 91 | "spacing": self._row_.spacing, 92 | "alignment": self._row_.alignment 93 | }) 94 | return attributes 95 | 96 | @classmethod 97 | def defaut_style(cls): 98 | return { 99 | "left": 0, 100 | "top": 0, 101 | "width": 200, 102 | "height": 40, 103 | "border-width": 0, 104 | "border-radius": 0, 105 | "border-color": "transparent", 106 | "background-color": "white" 107 | } 108 | 109 | @classmethod 110 | def defaut_attributes(cls): 111 | return { 112 | "scroll": "auto", 113 | "auto_scroll": True, 114 | "wrap": True, 115 | "spacing": 5, 116 | "alignment": "start", 117 | "style": cls.defaut_style() 118 | } 119 | 120 | @classmethod 121 | def defaut_json(cls, left=0, top=0): 122 | return FletBaseElement.defaut_json(cls, "row", left, top) 123 | -------------------------------------------------------------------------------- /evue/elements/slider.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from flet import ( 3 | Draggable, 4 | Slider, 5 | alignment 6 | ) 7 | from .fletbaseelement import FletBaseElement 8 | from .widgets import BaseContainer 9 | 10 | 11 | class SliderElement(FletBaseElement): 12 | 13 | def __init__(self, node, parent, draggable=False, sessionID=None): 14 | super().__init__(node, parent, draggable, sessionID=sessionID) 15 | self['min'] = 0 16 | self['max'] = 100 17 | self['step'] = 10 18 | self['label'] = "{value}" 19 | self.create(parent, draggable) 20 | self.setParent(parent) 21 | 22 | def create(self, parent, draggable=False): 23 | self._slider_ = Slider(label="{value}", divisions= 100) 24 | if draggable: 25 | self._obj_ = BaseContainer(Draggable(content=self._slider_), 26 | alignment=alignment.center_left 27 | ) 28 | self._obj_.content.element = self 29 | else: 30 | self._obj_ = BaseContainer(self._slider_, alignment=alignment.center_left) 31 | 32 | def set_width(self, value): 33 | self._obj_.width = value 34 | self._slider_.width = value 35 | 36 | def set_height(self, value): 37 | self._obj_.height = value 38 | self._slider_.height = value 39 | 40 | def set_min(self, value): 41 | self._slider_.min = value 42 | 43 | def set_max(self, value): 44 | self._slider_.max = value 45 | 46 | def set_value(self, value): 47 | self._slider_.value = float(value) 48 | 49 | def set_divisions(self, value): 50 | self._slider_.divisions = value 51 | 52 | def set_label(self, value): 53 | self._slider_.label = "{value}" 54 | 55 | def set_slider_indic_color(self, value): 56 | pass 57 | 58 | def set_slider_knob_color(self, value): 59 | pass 60 | 61 | def set_onValueChanged(self, func): 62 | def on_change(e): 63 | self.value = e.control.value 64 | func(self) 65 | self._slider_.on_change = on_change 66 | 67 | @classmethod 68 | def default_events(cls, id): 69 | ret = FletBaseElement.default_events(id) 70 | ret['onValueChanged'] = { 71 | "code": "pass", 72 | "name": "on_%s_valueChanged" % id 73 | } 74 | return ret 75 | 76 | @property 77 | def style(self): 78 | ret = super().style 79 | ret.update({ 80 | "slider-indic-color": self['slider_indic_color'], 81 | "slider-knob-color": self['slider_knob_color'] 82 | }) 83 | return ret 84 | 85 | @property 86 | def attributes(self): 87 | attributes = super().attributes 88 | attributes.update({ 89 | "min": self._slider_.min, 90 | "max": self._slider_.max, 91 | "value": self._slider_.value, 92 | "divisions": self._slider_.divisions, 93 | "label": self._slider_.label 94 | }) 95 | return attributes 96 | 97 | def set_attributes(self, node): 98 | super().set_attributes(node) 99 | attributes = node['attributes'] 100 | style = attributes['style'] 101 | self.slider_indic_color = style['slider-indic-color'] 102 | self.slider_knob_color = style['slider-knob-color'] 103 | # attr 104 | self.min = attributes["min"] 105 | self.max = attributes["max"] 106 | self.value = attributes["value"] 107 | self.divisions = attributes["divisions"] 108 | self.label = attributes["label"] 109 | 110 | def set_tiny_attributes(self, data): 111 | style = data['style'] 112 | self.slider_indic_color = style['slider-indic-color'] 113 | self.slider_knob_color = style['slider-knob-color'] 114 | # attr 115 | self.percent = data["min"] 116 | self.max = data["max"] 117 | self.value = data["value"] 118 | self.divisions = data["divisions"] 119 | self.label = data["label"] 120 | 121 | @classmethod 122 | def defaut_style(cls): 123 | return { 124 | "width": 200, 125 | "height": 32, 126 | "border-width": 0, 127 | "border-radius": 100, 128 | "border-color": "transparent", 129 | "background-color": "transparent", 130 | "slider-indic-color": "red", 131 | "slider-knob-color": "yellow" 132 | } 133 | 134 | @classmethod 135 | def defaut_attributes(cls): 136 | return { 137 | "min": 0, 138 | "max": 100, 139 | "value": 70, 140 | "divisions": 100, 141 | "label": "{value}", 142 | "style": cls.defaut_style() 143 | } 144 | 145 | @classmethod 146 | def defaut_json(cls, left=0, top=0): 147 | return FletBaseElement.defaut_json(cls, "slider", left, top) 148 | -------------------------------------------------------------------------------- /evue/elements/stackview.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .fletbaseelement import FletBaseElement 3 | from flet import ( 4 | Column, 5 | Draggable, 6 | alignment 7 | ) 8 | from .widgets import BaseContainer 9 | from .column import ColumnElement 10 | from .div import DivElement 11 | from loguru import logger 12 | 13 | class StackViewElement(DivElement): 14 | 15 | def __init__(self, node, parent, draggable=False, sessionID=None): 16 | super().__init__(node, parent, draggable, sessionID=sessionID) 17 | self._currentElement_ = None 18 | 19 | def childAdded(self, element): 20 | currentIndex = self['currentIndex'] 21 | for i, el in enumerate(self.listElements): 22 | if currentIndex == i: 23 | self._currentElement_ = el 24 | self._currentElement_.show() 25 | else: 26 | el.visible = False 27 | 28 | def set_currentIndex(self, value): 29 | lastElement = self._currentElement_ 30 | if lastElement: 31 | lastElement.hide() 32 | for i, el in enumerate(self.listElements): 33 | if value == i: 34 | self._currentElement_ = el 35 | self._currentElement_.show() 36 | 37 | def set_attributes(self, node): 38 | super().set_attributes(node) 39 | attributes = node['attributes'] 40 | self.currentIndex = attributes["currentIndex"] 41 | 42 | def set_tiny_attributes(self, data): 43 | super().set_tiny_attributes(data) 44 | 45 | @property 46 | def attributes(self): 47 | attributes = super().attributes 48 | attributes.update({ 49 | "currentIndex": self.currentIndex 50 | }) 51 | return attributes 52 | 53 | @classmethod 54 | def defaut_attributes(cls): 55 | ret = ColumnElement.defaut_attributes() 56 | ret.update({ 57 | "currentIndex": 0 58 | }) 59 | return ret 60 | 61 | @classmethod 62 | def defaut_json(cls, left=0, top=0): 63 | return FletBaseElement.defaut_json(cls, "stackview", left, top) 64 | -------------------------------------------------------------------------------- /evue/elements/switch.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from flet import ( 3 | Draggable, 4 | Switch, 5 | Row, 6 | Text, 7 | alignment, 8 | ) 9 | from .fletbaseelement import FletBaseElement 10 | from .text import TextElement 11 | from .widgets import BaseContainer 12 | 13 | 14 | class SwitchElement(TextElement): 15 | 16 | def __init__(self, node, parent, draggable=False, sessionID=None): 17 | super().__init__(node, parent, draggable, sessionID=sessionID) 18 | 19 | def create(self, parent, draggable=False): 20 | self._switch_ = Switch() 21 | self._text_ = Text(expand=True) 22 | self._row_ = Row([self._switch_, self._text_], expand=True) 23 | if draggable: 24 | self._obj_ = BaseContainer(Draggable(content=self._row_), 25 | alignment=alignment.center 26 | ) 27 | self._obj_.content.element = self 28 | else: 29 | self._obj_ = BaseContainer(self._row_, alignment=alignment.center) 30 | 31 | def set_width(self, value): 32 | self._obj_.width = value 33 | 34 | def set_height(self, value): 35 | self._obj_.height = value 36 | 37 | def set_text(self, value): 38 | width, height = self.measureText(value, self._text_.size) 39 | if width is not None and width > self.width: 40 | self.width = width + 72 41 | if height is not None and height > self.height: 42 | self.height = height 43 | self._text_.value = value 44 | 45 | def set_value(self, value): 46 | self._switch_.value = FletBaseElement.bool(value) 47 | 48 | def set_disabled(self, value): 49 | self._switch_.disabled = FletBaseElement.bool(value) 50 | 51 | def set_switch_indic_color(self, value): 52 | self._switch_.track_color = value 53 | 54 | def set_switch_knob_color(self, value): 55 | self._switch_.thumb_color = value 56 | 57 | def set_onValueChanged(self, func): 58 | def on_change(e): 59 | self.value = e.control.value 60 | func(self) 61 | self._switch_.on_change = on_change 62 | 63 | @classmethod 64 | def default_events(cls, id): 65 | ret = FletBaseElement.default_events(id) 66 | ret['onValueChanged'] = { 67 | "code": "pass", 68 | "name": "on_%s_valueChanged" % id 69 | } 70 | return ret 71 | 72 | @property 73 | def attributes(self): 74 | attributes = super().attributes 75 | attributes.update({ 76 | "text": self._text_.value, 77 | "value": self._switch_.value 78 | }) 79 | return attributes 80 | 81 | @property 82 | def style(self): 83 | ret = super().style 84 | ret.update({ 85 | "switch-indic-color": self._switch_.track_color, 86 | "switch-knob-color": self._switch_.thumb_color 87 | }) 88 | return ret 89 | 90 | def set_attributes(self, node): 91 | super().set_attributes(node) 92 | style = node['attributes']['style'] 93 | if 'switch-indic-color' in style: 94 | self.switch_indic_color = style['switch-indic-color'] 95 | if 'switch-knob-color' in style: 96 | self.switch_knob_color = style['switch-knob-color'] 97 | # attr 98 | attributes = node['attributes'] 99 | if "text" in attributes: 100 | self.text = attributes["text"] 101 | if "value" in attributes: 102 | self.value = attributes["value"] 103 | 104 | def set_tiny_attributes(self, attributes): 105 | style = attributes['style'] 106 | self.font_size = style['font-size'] 107 | self.color = style['color'] 108 | self.text_align = style['text-align'] 109 | if 'checkbox-checked-color' in style: 110 | self.switch_indic_color = style['switch-indic-color'] 111 | if 'switch-knob-color' in style: 112 | self.switch_knob_color = style['switch-knob-color'] 113 | # attr 114 | if "text" in attributes: 115 | self.text = attributes["text"] 116 | if "value" in attributes: 117 | self.value = attributes["value"] 118 | 119 | @classmethod 120 | def defaut_style(cls): 121 | return { 122 | "width": 64, 123 | "height": 32, 124 | "border-width": 0, 125 | "border-color": "transparent", 126 | "background-color": "#282828", 127 | "color": "white", 128 | "font-size": 20, 129 | "text-align": "left", 130 | "switch-indic-color": "green", 131 | "switch-knob-color": "red" 132 | } 133 | 134 | @classmethod 135 | def defaut_attributes(cls): 136 | return { 137 | "text": "switch", 138 | "value": True, 139 | "style": cls.defaut_style() 140 | } 141 | 142 | @classmethod 143 | def defaut_json(cls, left=0, top=0): 144 | return FletBaseElement.defaut_json(cls, "switch", left, top) 145 | -------------------------------------------------------------------------------- /evue/elements/tab.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from itertools import cycle 3 | from flet import ( 4 | Stack, 5 | Draggable, 6 | alignment, 7 | border, 8 | border_radius, 9 | Tab, 10 | Text 11 | ) 12 | from .fletbaseelement import FletBaseElement 13 | from .widgets import BaseContainer 14 | 15 | 16 | class TabElement(FletBaseElement): 17 | 18 | colors = cycle( 19 | ['#fbb4ae', '#b3cde3', '#ccebc5', '#decbe4', '#fed9a6', '#ffffcc', '#e5d8bd', '#fddaec', '#f2f2f2', 20 | '#b3e2cd', '#fdcdac', '#cbd5e8', '#f4cae4', '#e6f5c9', '#fff2ae', '#f1e2cc', '#cccccc', '#8dd3c7', 21 | '#ffffb3', '#bebada', '#fb8072', '#80b1d3', '#fdb462', '#b3de69', '#fccde5', '#d9d9d9', '#bc80bd', 22 | '#ccebc5', '#ffed6f']) 23 | 24 | def __init__(self, node, parent, draggable=False, sessionID=None): 25 | super().__init__(node, parent, draggable, sessionID=sessionID) 26 | self.create(parent, draggable) 27 | self.setParent(parent) 28 | 29 | def create(self, parent, draggable=False): 30 | self._stack_ = Stack([]) 31 | self._container_ = BaseContainer(self._stack_, expand=True, bgcolor="red", alignment=alignment.center) 32 | self._text_ = Text(color="white", bgcolor="transparent") 33 | self._tab_ = Tab(tab_content=self._text_, content=self._container_) 34 | self._controls_ = self._stack_.controls 35 | if draggable: 36 | self._obj_ = BaseContainer( 37 | Draggable(content=self._tab_), 38 | alignment = alignment.center 39 | ) 40 | self._obj_.content.element = self 41 | else: 42 | self._obj_ = BaseContainer(self._tab_) 43 | 44 | def setParent(self, parent): 45 | if parent and parent.isContainer: 46 | self['parent'] = parent 47 | self['index'] = len(parent._controls_) 48 | parent._controls_.append(self._tab_) 49 | parent.elements[self.eid] = self 50 | parent.currentIndex = self['index'] 51 | 52 | def delete_control(self): 53 | parent = self['parent'] 54 | parent._controls_.remove(self._tab_) 55 | try: 56 | parent.obj.update() 57 | except: 58 | pass 59 | 60 | @property 61 | def isContainer(self): 62 | return True 63 | 64 | def set_left(self, value): 65 | self._obj_.left = value 66 | 67 | def set_top(self, value): 68 | self._obj_.top = value 69 | 70 | def set_width(self, value): 71 | self._obj_.width = value 72 | self._container_.width = value 73 | 74 | def set_height(self, value): 75 | self._obj_.height = value 76 | self._container_.height = value 77 | 78 | def set_background_color(self, value): 79 | self._obj_.bgcolor = value 80 | self._container_.bgcolor = value 81 | 82 | def set_border_width(self, value): 83 | self._obj_.bgcolor = border.all(value, self.border_color) 84 | self._container_.border = border.all(value, self.border_color) 85 | 86 | def set_border_radius(self, value): 87 | self._obj_.border_radius = border_radius.all(value) 88 | self._container_.border_radius = border_radius.all(value) 89 | 90 | def set_border_color(self, value): 91 | self._obj_.border = border.all(self.border_width, value) 92 | self._container_.border = border.all(self.border_width, value) 93 | 94 | def set_expand(self, value): 95 | self._obj_.expend = FletBaseElement.bool(value) 96 | self._tab_.expend = FletBaseElement.bool(value) 97 | 98 | def set_name(self, value): 99 | self._text_.value = value 100 | 101 | def set_font_size(self, value): 102 | if value == "None": 103 | self._text_.size = 20 104 | else: 105 | self._text_.size = value 106 | 107 | def set_color(self, value): 108 | self._text_.color = value 109 | 110 | def set_text_align(self, value): 111 | if value == "left": 112 | self._obj_.alignment = alignment.center_left 113 | elif value == "center": 114 | self._obj_.alignment = alignment.center 115 | elif value == "right": 116 | self._obj_.alignment = alignment.center_right 117 | else: 118 | self._obj_.alignment = alignment.center 119 | 120 | if value == "None": 121 | self._text_.text_align = "center" 122 | else: 123 | self._text_.text_align = value 124 | 125 | def set_attributes(self, node): 126 | super().set_attributes(node) 127 | attributes = node['attributes'] 128 | style = node["attributes"]['style'] 129 | self.font_size = style['font-size'] 130 | self.color = style['color'] 131 | self.text_align = style['text-align'] 132 | # attr 133 | self.expand = attributes["expand"] 134 | self.name = attributes["name"] 135 | 136 | def set_tiny_attributes(self, attributes): 137 | self.font_size = attributes['font-size'] 138 | self.color = attributes['color'] 139 | self.text_align = attributes['text-align'] 140 | # attr 141 | self.expand = attributes["expand"] 142 | self.name = attributes["name"] 143 | 144 | @property 145 | def attributes(self): 146 | attributes = super().attributes 147 | attributes.update({ 148 | "expand": self._tab_.expand, 149 | "name": self._tab_.text, 150 | }) 151 | return attributes 152 | 153 | @property 154 | def style(self): 155 | if self._text_.text_align is None: 156 | self._text_.text_align = "center" 157 | style = super().style 158 | style.update({ 159 | "color": self._text_.color, 160 | "font-size": self._text_.size, 161 | "text-align": self._text_.text_align 162 | }) 163 | return style 164 | 165 | @classmethod 166 | def defaut_style(cls): 167 | color = next(cls.colors) 168 | return { 169 | "left": 0, 170 | "top": 0, 171 | "width": 400, 172 | "height": 400, 173 | "border-width": 0, 174 | "border-radius": 0, 175 | "border-color": "transparent", 176 | "background-color": color, 177 | "color": color, 178 | "font-size": 20, 179 | "text-align": "center" 180 | } 181 | 182 | @classmethod 183 | def defaut_attributes(cls): 184 | return { 185 | "expand": True, 186 | "name": "tab", 187 | "style": cls.defaut_style() 188 | } 189 | 190 | @classmethod 191 | def defaut_json(cls, left=0, top=0): 192 | return FletBaseElement.defaut_json(cls, "tab", left, top) 193 | -------------------------------------------------------------------------------- /evue/elements/tabview.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from flet import ( 3 | Draggable, 4 | alignment, 5 | Tabs 6 | ) 7 | from .fletbaseelement import FletBaseElement 8 | from .widgets import BaseContainer 9 | 10 | 11 | class TabViewElement(FletBaseElement): 12 | 13 | def __init__(self, node, parent, draggable=False, sessionID=None): 14 | super().__init__(node, parent, draggable, sessionID=sessionID) 15 | self.create(parent, draggable) 16 | self.setParent(parent) 17 | 18 | def create(self, parent, draggable=False): 19 | self._tabview_ = Tabs(expand=True) 20 | self._controls_ = self._tabview_.tabs 21 | if draggable: 22 | self._obj_ = BaseContainer( 23 | Draggable(content=self._tabview_), 24 | alignment = alignment.center 25 | ) 26 | self._obj_.content.element = self 27 | else: 28 | self._obj_ = BaseContainer( 29 | self._tabview_ 30 | ) 31 | 32 | def onValueChanged(e): 33 | pass 34 | 35 | self.onValueChanged = onValueChanged 36 | 37 | @property 38 | def isContainer(self): 39 | return True 40 | 41 | @property 42 | def isLayout(self): 43 | return False 44 | 45 | def set_expand(self, value): 46 | self.obj.expend = FletBaseElement.bool(value) 47 | self._tabview_.expend = FletBaseElement.bool(value) 48 | 49 | def set_currentIndex(self, value): 50 | self._tabview_.selected_index = value 51 | 52 | def set_animation_duration(self, value): 53 | self._tabview_.animation_duration = value 54 | 55 | def set_onValueChanged(self, func): 56 | def on_change(e): 57 | index = e.control.selected_index 58 | self.currentIndex = index 59 | func(self) 60 | self._tabview_.on_change = on_change 61 | 62 | 63 | @classmethod 64 | def default_events(cls, id): 65 | ret = FletBaseElement.default_events(id) 66 | ret['onValueChanged'] = { 67 | "code": "pass", 68 | "name": "on_%s_valueChanged" % id 69 | } 70 | return ret 71 | 72 | def set_attributes(self, node): 73 | super().set_attributes(node) 74 | attributes = node['attributes'] 75 | style = attributes['style'] 76 | # attr 77 | self.expand = attributes["expand"] 78 | self.currentIndex = attributes["currentIndex"] 79 | self.animation_duration = attributes["animation_duration"] 80 | 81 | def set_tiny_attributes(self, attributes): 82 | # attr 83 | self.expand = attributes["expand"] 84 | self.currentIndex = attributes["currentIndex"] 85 | self.animation_duration = attributes["animation_duration"] 86 | 87 | @property 88 | def attributes(self): 89 | attributes = super().attributes 90 | attributes.update({ 91 | "expand": self._tabview_.expand, 92 | "currentIndex": self._tabview_.selected_index, 93 | "animation_duration": self._tabview_.animation_duration, 94 | }) 95 | return attributes 96 | 97 | @classmethod 98 | def defaut_style(cls): 99 | return { 100 | "left": 0, 101 | "top": 0, 102 | "width": 400, 103 | "height": 400, 104 | "border-width": 0, 105 | "border-radius": 0, 106 | "border-color": "transparent", 107 | "background-color": "white" 108 | } 109 | 110 | @classmethod 111 | def defaut_attributes(cls): 112 | return { 113 | "expand": True, 114 | "currentIndex": 0, 115 | "animation_duration": 300, 116 | "style": cls.defaut_style() 117 | } 118 | 119 | @classmethod 120 | def defaut_json(cls, left=0, top=0): 121 | return FletBaseElement.defaut_json(cls, "tabview", left, top) 122 | -------------------------------------------------------------------------------- /evue/elements/text.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from flet import ( 3 | Draggable, 4 | Text, 5 | alignment 6 | ) 7 | from .fletbaseelement import FletBaseElement 8 | from .widgets import BaseContainer 9 | from loguru import logger 10 | 11 | 12 | class TextElement(FletBaseElement): 13 | 14 | def __init__(self, node, parent, draggable=False, sessionID=None): 15 | super().__init__(node, parent, draggable, sessionID=sessionID) 16 | self.create(parent, draggable) 17 | self.setParent(parent) 18 | self._font = None 19 | 20 | def create(self, parent, draggable=False): 21 | self._text_ = Text() 22 | if draggable: 23 | self._obj_ = BaseContainer(Draggable(content=self._text_), 24 | alignment=alignment.center 25 | ) 26 | self._obj_.content.element = self 27 | else: 28 | self._obj_ = BaseContainer(self._text_, alignment=alignment.center) 29 | 30 | 31 | def onclick(e): 32 | pass 33 | self.onclick = onclick 34 | 35 | def updateSize(self): 36 | width, height = self.measureText(self.value, self.size) 37 | if width is not None and width > self.width: 38 | self.width = width 39 | if height is not None and height > self.height: 40 | self.height = height 41 | 42 | def set_value(self, value): 43 | self._text_.value = value 44 | if self._text_.expand == True: 45 | self.updateSize() 46 | 47 | def set_font_size(self, value): 48 | if value == "None": 49 | self._text_.size = 20 50 | else: 51 | self._text_.size = value 52 | 53 | def set_font_weight(self, value): 54 | self._text_.weight = value 55 | 56 | def set_weight(self, value): 57 | self._text_.weight = value 58 | 59 | def set_italic(self, value): 60 | self._text_.italic = FletBaseElement.bool(value) 61 | 62 | def set_overflow(self, value): 63 | if value in ["clip", "ellipsis", "fade", "visible"]: 64 | self._text_.overflow = value 65 | else: 66 | self._text_.overflow = None 67 | 68 | def set_selectable(self, value): 69 | self._text_.selectable = FletBaseElement.bool(value) 70 | 71 | def set_no_wrap(self, value): 72 | self._text_.no_wrap = FletBaseElement.bool(value) 73 | 74 | def set_color(self, value): 75 | self._text_.color = value 76 | 77 | def set_text_align(self, value): 78 | if value == "left": 79 | self._obj_.alignment = alignment.center_left 80 | elif value == "center": 81 | self._obj_.alignment = alignment.center 82 | elif value == "right": 83 | self._obj_.alignment = alignment.center_right 84 | else: 85 | self._obj_.alignment = alignment.center 86 | 87 | if value == "None": 88 | self._text_.text_align = "center" 89 | else: 90 | self._text_.text_align = value 91 | 92 | @property 93 | def attributes(self): 94 | attributes = super().attributes 95 | attributes.update({ 96 | "value": self._text_.value 97 | }) 98 | return attributes 99 | 100 | @property 101 | def style(self): 102 | if self._text_.text_align is None: 103 | self._text_.text_align = "center" 104 | style = super().style 105 | style.update({ 106 | "color": self._text_.color, 107 | "font-size": self._text_.size, 108 | "text-align": self._text_.text_align 109 | }) 110 | return style 111 | 112 | def set_attributes(self, node): 113 | super().set_attributes(node) 114 | style = node["attributes"]['style'] 115 | self.font_size = style['font-size'] 116 | self.color = style['color'] 117 | self.text_align = style['text-align'] 118 | # attr 119 | attributes = node['attributes'] 120 | self.value = attributes["value"] 121 | 122 | def set_tiny_attributes(self, data): 123 | style = data['style'] 124 | self.font_size = style['font-size'] 125 | self.color = style['color'] 126 | self.text_align = style['text-align'] 127 | # attr 128 | self.value = data["value"] 129 | 130 | @classmethod 131 | def defaut_style(cls): 132 | return { 133 | "width": 120, 134 | "height": 40, 135 | "border-width": 0, 136 | "border-color": "transparent", 137 | "background-color": "transparent", 138 | "color": "white", 139 | "font-size": 20, 140 | "text-align": "center" 141 | } 142 | 143 | @classmethod 144 | def defaut_attributes(cls): 145 | return { 146 | "value": "evue", 147 | "style": cls.defaut_style() 148 | } 149 | 150 | @classmethod 151 | def defaut_json(cls, left=0, top=0): 152 | return FletBaseElement.defaut_json(cls, "text", left, top) 153 | -------------------------------------------------------------------------------- /evue/elements/textarea.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from flet import ( 3 | alignment 4 | ) 5 | from .fletbaseelement import FletBaseElement 6 | from .textfield import TextFieldElement 7 | 8 | 9 | class TextareaElement(TextFieldElement): 10 | 11 | def __init__(self, node, parent, draggable=False, sessionID=None): 12 | super().__init__(node, parent, draggable, sessionID=sessionID) 13 | self._text_.multiline = True 14 | self._text_.filled = True 15 | self._text_.expand = True 16 | self._obj_.set_padding(5) 17 | self._obj_.alignment = alignment.top_left 18 | 19 | def set_width(self, value): 20 | self._obj_.width = value 21 | self._text_.width = value 22 | 23 | def set_height(self, value): 24 | self._obj_.height = value 25 | self._text_.height = value 26 | 27 | def set_font_size(self, value): 28 | self._text_.text_size = value 29 | 30 | @property 31 | def attributes(self): 32 | attributes = super().attributes 33 | attributes.update({ 34 | "value": self._text_.value 35 | }) 36 | return attributes 37 | 38 | @property 39 | def style(self): 40 | style = super().style 41 | style.update({ 42 | "color": self._text_.color, 43 | "font-size": self._text_.text_size, 44 | "text-align": self._text_.text_align 45 | }) 46 | return style 47 | 48 | @classmethod 49 | def defaut_style(cls): 50 | return { 51 | "width": 240, 52 | "height": 160, 53 | "border-width": 0, 54 | "border-color": "transparent", 55 | "background-color": "#282828", 56 | "color": "white", 57 | "font-size": 16, 58 | "text-align": "left" 59 | } 60 | 61 | @classmethod 62 | def defaut_attributes(cls): 63 | return { 64 | "value": "Evue是面向iot的小程序应用框架, 开箱即用, 超轻量、原生支持MVVM, 丰富的组件和完整支持HTML5 Canvas 2D接口, 纯C开发, 跨平台, 一次适配, 多端运行!", 65 | "style": cls.defaut_style() 66 | } 67 | 68 | @classmethod 69 | def defaut_json(cls, left=0, top=0): 70 | return FletBaseElement.defaut_json(cls, "textarea", left, top) 71 | -------------------------------------------------------------------------------- /evue/elements/textfield.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from sre_parse import expand_template 3 | from flet import ( 4 | Stack, 5 | Draggable, 6 | TextField, 7 | alignment, 8 | Control, 9 | TextStyle 10 | ) 11 | from flet.textfield import KeyboardTypeString 12 | from loguru import logger 13 | from .fletbaseelement import FletBaseElement 14 | from .widgets import BaseContainer 15 | from ..globalthis import globalThis 16 | 17 | 18 | class TextFieldElement(FletBaseElement): 19 | 20 | def __init__(self, node, parent, draggable=False, sessionID=None): 21 | super().__init__(node, parent, draggable, sessionID=sessionID) 22 | self.create(parent, draggable) 23 | self.setParent(parent) 24 | 25 | def create(self, parent, draggable=False): 26 | self._text_ = TextField(content_padding=0) 27 | self._text_.multiline = False 28 | if draggable: 29 | self._obj_ = BaseContainer(Draggable(content=self._text_), 30 | alignment=alignment.center 31 | ) 32 | self._obj_.content.element = self 33 | else: 34 | self._obj_ = BaseContainer(self._text_, alignment=alignment.center) 35 | self._text_.border_width = 0 36 | 37 | def on_change(e): 38 | self.value = e.control.value 39 | 40 | self._text_.on_change = on_change 41 | 42 | def set_width(self, value): 43 | self._obj_.width = value 44 | self._text_.width = value 45 | 46 | def set_height(self, value): 47 | self._obj_.height = value 48 | self._text_.height = value 49 | 50 | def set_border(self, value): 51 | if isinstance(value, str): 52 | self._text_.border = value 53 | else: 54 | return super().set_border(value) 55 | 56 | def set_label(self, value): 57 | self._text_.label = value 58 | 59 | def set_password(self, value): 60 | self._text_.password = FletBaseElement.bool(value) 61 | 62 | def set_can_reveal_password(self, value): 63 | self._text_.can_reveal_password = FletBaseElement.bool(value) 64 | 65 | def set_font_size(self, value): 66 | self._text_.text_size = value 67 | 68 | def set_color(self, value): 69 | self._text_.color = value 70 | 71 | def set_text_align(self, value): 72 | if value == "None": 73 | self._text_.text_align = "center" 74 | else: 75 | self._text_.text_align = value 76 | 77 | def set_value(self, value): 78 | self._text_.value = value 79 | 80 | def set_keyboard_type(self, value:KeyboardTypeString): 81 | self._text_.keyboard_type = value 82 | 83 | def set_multiline(self, value:bool): 84 | self._text_.multiline = value 85 | 86 | def set_min_lines(self, value:int): 87 | self._text_.min_lines = value 88 | 89 | def set_max_lines(self, value:int): 90 | self._text_.max_lines = value 91 | 92 | def set_max_length(self, value:int): 93 | self._text_.max_length = value 94 | 95 | def set_tooltip(self, value): 96 | self._text_.tooltip = value 97 | 98 | def set_read_only(self, value): 99 | self._text_.read_only = FletBaseElement.bool(value) 100 | 101 | def set_icon(self, value): 102 | self._text_.icon = FletBaseElement.ficon(value) 103 | 104 | def set_focused_color(self, value): 105 | self._text_.focused_color = value 106 | 107 | def set_focused_bgcolor(self, value): 108 | self._text_.focused_bgcolor = value 109 | 110 | def set_focused_border_width(self, value): 111 | self._text_.focused_border_width = value 112 | 113 | def set_focused_border_color(self, value): 114 | self._text_.focused_border_color = value 115 | 116 | def set_content_padding(self, value): 117 | self._text_.content_padding = value 118 | 119 | def set_filled(self, value): 120 | self._text_.filled = value 121 | 122 | def set_place_holder(self, value:str): 123 | self._text_.hint_text = value 124 | 125 | def set_hint_text(self, value:str): 126 | self._text_.hint_text = value 127 | 128 | def set_hint_style(self, value:TextStyle): 129 | self._text_.hint_style = value 130 | 131 | def set_helper_text(self, value:str): 132 | self._text_.helper_text = value 133 | 134 | def set_helper_style(self, value:TextStyle): 135 | self._text_.helper_style = value 136 | 137 | def set_counter_text(self, value:str): 138 | self._text_.counter_text = value 139 | 140 | def set_counter_style(self, value:TextStyle): 141 | self._text_.counter_style = value 142 | 143 | def set_error_text(self, value:str): 144 | self._text_.error_text = value 145 | 146 | def set_error_style(self, value:TextStyle): 147 | self._text_.error_style = value 148 | 149 | def set_prefix(self, value:Control): 150 | self._text_.prefix = value 151 | 152 | def set_prefix_icon(self, value): 153 | self._text_.prefix_icon = FletBaseElement.ficon(value) 154 | 155 | def set_prefix_text(self, value): 156 | self._text_.prefix_text = value 157 | 158 | def set_prefix_style(self, value): 159 | self._text_.prefix_style = value 160 | 161 | def set_suffix(self, value:Control): 162 | self._text_.suffix = value 163 | 164 | def set_suffix_icon(self, value): 165 | self._text_.suffix_icon = FletBaseElement.ficon(value) 166 | 167 | def set_suffix_text(self, value): 168 | self._text_.suffix_text = value 169 | 170 | def set_suffix_style(self, value): 171 | self._text_.suffix_style = value 172 | 173 | def set_onValueChanged(self, func): 174 | def on_change(e): 175 | if self.value != e.control.value: 176 | self.value = e.control.value 177 | func(self) 178 | self._text_.on_change = on_change 179 | 180 | def set_attributes(self, node): 181 | super().set_attributes(node) 182 | style = node["attributes"]['style'] 183 | self.font_size = style['font-size'] 184 | self.color = style['color'] 185 | self.text_align = style['text-align'] 186 | # attr 187 | attributes = node['attributes'] 188 | self.value = attributes["value"] 189 | 190 | def set_tiny_attributes(self, data): 191 | style = data['style'] 192 | self.font_size = style['font-size'] 193 | self.color = style['color'] 194 | self.text_align = style['text-align'] 195 | # attr 196 | self.value = data["value"] 197 | 198 | @property 199 | def attributes(self): 200 | attributes = super().attributes 201 | attributes.update({ 202 | "value": self._text_.value 203 | }) 204 | return attributes 205 | 206 | @property 207 | def style(self): 208 | style = super().style 209 | style.update({ 210 | "color": self._text_.color, 211 | "font-size": self._text_.text_size, 212 | "text-align": self._text_.text_align 213 | }) 214 | return style 215 | 216 | @classmethod 217 | def defaut_style(cls): 218 | return { 219 | "width": 200, 220 | "height": 40, 221 | "border-width": 0, 222 | "border-color": "transparent", 223 | "background-color": "#282828", 224 | "color": "white", 225 | "font-size": 14, 226 | "text-align": "center" 227 | } 228 | 229 | @classmethod 230 | def defaut_attributes(cls): 231 | return { 232 | "value": "textfiled", 233 | "style": cls.defaut_style() 234 | } 235 | 236 | @classmethod 237 | def defaut_json(cls, left=0, top=0): 238 | return FletBaseElement.defaut_json(cls, "textfield", left, top) 239 | -------------------------------------------------------------------------------- /evue/elements/widgets/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .basecontainer import BaseContainer, EvueContainer 3 | from .collapsible import Collapsible 4 | from .counter import Counter 5 | from .evueappbar import EvueAppBar 6 | from flet import * -------------------------------------------------------------------------------- /evue/elements/widgets/collapsible.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from asyncio.log import logger 3 | from math import pi 4 | from typing import Optional 5 | 6 | from flet import Column, Container, Icon, Row, Text, icons, padding 7 | from flet.control import Control 8 | from .basecontainer import BaseContainer 9 | 10 | class Collapsible(Column): 11 | def __init__( 12 | self, 13 | title: Optional[str] = "", 14 | content: Optional[Control]= None, 15 | icon: Optional[Control] = None, 16 | spacing: float = 3, 17 | ): 18 | super().__init__() 19 | self.icon = icon 20 | self.title = title 21 | self._text_ = Text(self.title) 22 | self.shevron = Icon( 23 | icons.KEYBOARD_ARROW_DOWN_ROUNDED, 24 | animate_rotation=100, 25 | rotate=0, 26 | color="white" 27 | ) 28 | self.spacingContainer = BaseContainer(height=spacing) 29 | self.content = Column( 30 | [self.spacingContainer], 31 | height=0, 32 | spacing=0, 33 | animate_size=100, 34 | opacity=0, 35 | animate_opacity=100 36 | ) 37 | self.spacing = 0 38 | 39 | self._switch_ = False 40 | 41 | def header_click(self, e=None): 42 | self.content.height = None if self.content.height == 0 else 0 43 | self.content.opacity = 0 if self.content.height == 0 else 1 44 | self.shevron.rotate = pi if self.shevron.rotate == 0 else 0 45 | self.update() 46 | 47 | def set_open(self, value): 48 | if value: 49 | self.content.height = None 50 | self.content.opacity = 0 51 | self.shevron.rotate = pi 52 | else: 53 | self.content.height = 0 54 | self.content.opacity = 1 55 | self.shevron.rotate = 0 56 | self.update() 57 | 58 | def set_switch(self, value): 59 | self._switch_ = value 60 | 61 | def _build(self): 62 | title_row = Row() 63 | if self.icon != None: 64 | title_row.controls.append(self.icon) 65 | title_row.controls.append(self._text_) 66 | self.controls.extend( 67 | [ 68 | BaseContainer( 69 | Row([title_row, self.shevron], alignment="spaceBetween"), 70 | padding=padding.only(left=8, right=8), 71 | height=38, 72 | border_radius=4, 73 | ink=True, 74 | on_click=self.header_click 75 | ), 76 | self.content, 77 | ] 78 | ) 79 | -------------------------------------------------------------------------------- /evue/elements/widgets/counter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from asyncio.log import logger 3 | from math import pi 4 | from typing import Optional 5 | 6 | from flet import IconButton, Container, TextField, Text, Row, border, alignment, border_radius, icons, padding 7 | from flet.control import Control 8 | from .basecontainer import BaseContainer 9 | 10 | class Counter(BaseContainer): 11 | def __init__(self): 12 | super().__init__() 13 | bgcolor = "transparent" 14 | 15 | self.minusIconButton = IconButton(icons.REMOVE, bgcolor=bgcolor, on_click=self.minus_click, icon_size=14) 16 | self.addIconButton = IconButton(icons.ADD, bgcolor=bgcolor, on_click=self.plus_click, icon_size=14) 17 | self.text_number = TextField(value="0", filled=True, bgcolor=bgcolor, text_align="center") 18 | self.text_number.border_width = 0 19 | 20 | self.content = Row([ 21 | self.minusIconButton, 22 | self.text_number, 23 | self.addIconButton 24 | ], alignment="center", spacing=0, run_spacing=0) 25 | 26 | self.alignment = alignment.center 27 | 28 | self._on_change = None 29 | 30 | def on_change(e): 31 | self._on_change(int(e.control.value)) 32 | 33 | def on_submit(e): 34 | logger.warning(e.control.value) 35 | self._on_change(int(e.control.value)) 36 | 37 | self.text_number.on_change = on_change 38 | self.text_number.on_submit = on_submit 39 | 40 | @property 41 | def on_change(self): 42 | return self._on_change 43 | 44 | @on_change.setter 45 | def on_change(self, value): 46 | self._on_change = value 47 | 48 | def minus_click(self, e): 49 | self._on_change(int(self.text_number.value) - 1) 50 | self.update() 51 | 52 | def plus_click(self, e): 53 | self._on_change(int(self.text_number.value) + 1) 54 | self.update() 55 | 56 | def _build(self): 57 | pass 58 | 59 | def set_width(self, value): 60 | self.width = value 61 | self.text_number.width = value - 80 62 | 63 | def set_font_size(self, value): 64 | self.addIconButton.icon_size = value 65 | self.minusIconButton.icon_size = value 66 | self.text_number.text_size = value 67 | self.text_number.height = value + 6 68 | 69 | def set_color(self, value): 70 | self.addIconButton.icon_color = value 71 | self.minusIconButton.icon_color = value 72 | self.text_number.color = value 73 | 74 | def set_value(self, value): 75 | self.text_number.value = value 76 | -------------------------------------------------------------------------------- /evue/elements/widgets/evueappbar.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from loguru import logger 3 | from math import pi 4 | from typing import Optional 5 | from flet import Page, Image, Container, WindowDragArea, Stack, colors, border, margin, padding, border_radius, DragUpdateEvent, AppBar 6 | from ..iconbutton import IconButtonElement 7 | from .basecontainer import EvueContainer 8 | 9 | minimized_svg = ''' 10 | ''' 11 | 12 | class EvueAppBar(AppBar): 13 | def __init__(self, page:Page, *args, **kwargs): 14 | super().__init__(*args, **kwargs) 15 | self.page = page 16 | self.page.window_title_bar_hidden = True 17 | self.page.window_title_bar_buttons_hidden = True 18 | self.sessionID = page.session_id 19 | self.leading_width = 0 20 | self.bgcolor = "transparent" 21 | if "height" in kwargs: 22 | self.height = kwargs['height'] 23 | else: 24 | self.height = 40 25 | 26 | self._stack = Stack([]) 27 | self._controls_ = self._stack.controls 28 | self.title = WindowDragArea(Container(self._stack, expand=True, padding=0, height=self.height, bgcolor="transparent")) 29 | 30 | self.iconbutton = IconButtonElement({"type": "iconbutton"}, parent=None) 31 | self.iconbutton.icon = "icons.CLOSE" 32 | self.iconbuttonContainer = EvueContainer(self.iconbutton.obj) 33 | self.actions = [self.iconbuttonContainer] 34 | 35 | self.iconbuttonContainer.content.on_enter = self.onCloseButtonEnter 36 | self.iconbuttonContainer.content.on_exit = self.onCloseButtonExit 37 | self.iconbutton.onclick = self.onCloseButtonClick 38 | 39 | def onCloseButtonEnter(self, element): 40 | self.iconbutton.background_color = "red" 41 | self.iconbutton.update() 42 | 43 | def onCloseButtonExit(self, el): 44 | self.iconbutton.background_color = "transparent" 45 | self.iconbutton.update() 46 | 47 | def onCloseButtonClick(self, el): 48 | self.page.window_close() 49 | 50 | @property 51 | def height(self): 52 | return self.toolbar_height 53 | 54 | @height.setter 55 | def height(self, value): 56 | self.toolbar_height = value 57 | 58 | def mount(self, uri): 59 | from evue import require 60 | module = require(uri) 61 | component = module.createComponent(sessionID=self.sessionID) 62 | obj = component.rootElement.obj 63 | self._controls_.append(obj) 64 | obj.bgcolor = self.bgcolor 65 | 66 | -------------------------------------------------------------------------------- /evue/elements/windowtitlebar.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from flet import ( 4 | Draggable, 5 | WindowDragArea, 6 | Stack, 7 | alignment 8 | ) 9 | from .fletbaseelement import FletBaseElement 10 | from PIL import Image as PILImage 11 | from .widgets import BaseContainer 12 | from loguru import logger 13 | from ..globalthis import globalThis 14 | 15 | 16 | class WindowtitlebarElement(FletBaseElement): 17 | 18 | def __init__(self, node, parent, draggable=False, sessionID=None): 19 | super().__init__(node, parent, draggable, sessionID=sessionID) 20 | self.create(parent, draggable) 21 | self.setParent(parent) 22 | 23 | def create(self, parent, draggable=False): 24 | self._stack_ = Stack([]) 25 | self._controls_ = self._stack_.controls 26 | self._windowDragArea_ = WindowDragArea(content=BaseContainer(self._stack_)) 27 | self._obj_ = BaseContainer( 28 | self._windowDragArea_, 29 | alignment = alignment.center 30 | ) 31 | 32 | @property 33 | def isContainer(self): 34 | return True 35 | 36 | @property 37 | def isLayout(self): 38 | return False 39 | 40 | @property 41 | def attributes(self): 42 | attributes = super().attributes 43 | return attributes 44 | 45 | def set_attributes(self, node): 46 | super().set_attributes(node) 47 | # attr 48 | attributes = node['attributes'] 49 | 50 | @classmethod 51 | def defaut_style(cls): 52 | style = FletBaseElement.defaut_style() 53 | style.update({ 54 | "background-color": "transparent" 55 | }) 56 | return style 57 | 58 | @classmethod 59 | def defaut_attributes(cls): 60 | return { 61 | "style": cls.defaut_style() 62 | } 63 | 64 | @classmethod 65 | def defaut_json(cls, left=0, top=0): 66 | return FletBaseElement.defaut_json(cls, "windowtitlebar", left, top) 67 | -------------------------------------------------------------------------------- /evue/evueapplication.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import flet 4 | from flet import Page 5 | from flet.flet import * 6 | from flet.flet import _connect_internal 7 | from flet.flet import open_flet_view 8 | from .globalthis import globalThis, loadApp, loadProject 9 | from .fileserver import * 10 | import threading 11 | import signal 12 | from threading import Thread 13 | from loguru import logger 14 | from .router import Router 15 | from pyee import EventEmitter 16 | 17 | os.environ["FLET_WS_MAX_MESSAGE_SIZE"] = "8000000" 18 | def main(page: Page): 19 | globalThis.addSession(page) 20 | page.router = Router(page.session_id) 21 | page.event = EventEmitter() 22 | if hasattr(globalThis, "onCreate"): 23 | if globalThis.onCreate: 24 | if page.route == "/": 25 | globalThis.onCreate(page, globalThis.onAppInit) 26 | else: 27 | globalThis.firstPage.event.emit("on_route_change", page) 28 | else: 29 | logger.error("Please set right [startProject] in start.json!") 30 | 31 | class EvueApplication(object): 32 | 33 | def __init__(self, closeCallback=None) -> None: 34 | self.closeCallback = closeCallback 35 | self.pflet = None 36 | self.connetion = None 37 | 38 | def startFlet(self, 39 | name="", 40 | host=None, 41 | port=0, 42 | target=None, 43 | permissions=None, 44 | view: AppViewer = FLET_APP, 45 | assets_dir=None, 46 | upload_dir=None, 47 | web_renderer="canvaskit", 48 | route_url_strategy="hash", 49 | **kwargs 50 | ): 51 | if target is None: 52 | raise Exception("target argument is not specified") 53 | 54 | conn = _connect_internal( 55 | page_name=name, 56 | host=host, 57 | port=port, 58 | is_app=True, 59 | permissions=permissions, 60 | session_handler=target, 61 | assets_dir=assets_dir, 62 | upload_dir=upload_dir, 63 | web_renderer=web_renderer, 64 | route_url_strategy=route_url_strategy, 65 | ) 66 | 67 | url_prefix = os.getenv("FLET_DISPLAY_URL_PREFIX") 68 | if url_prefix is not None: 69 | print(url_prefix, conn.page_url) 70 | else: 71 | logger.info(f"App URL: {conn.page_url}") 72 | 73 | logger.info("Connected to Flet app and handling user sessions...") 74 | 75 | fvp = None 76 | 77 | if ( 78 | (view == FLET_APP or view == FLET_APP_HIDDEN) 79 | and not is_linux_server() 80 | and url_prefix is None 81 | ): 82 | fvp = open_flet_view(conn.page_url, view == FLET_APP_HIDDEN) 83 | self.pflet = fvp 84 | self.connetion = conn 85 | try: 86 | fvp.wait() 87 | except (Exception) as e: 88 | pass 89 | else: 90 | if view == WEB_BROWSER and url_prefix is None: 91 | open_in_browser(conn.page_url) 92 | 93 | terminate = threading.Event() 94 | 95 | def exit_gracefully(signum, frame): 96 | logger.info("Gracefully terminating Flet app...") 97 | terminate.set() 98 | 99 | signal.signal(signal.SIGINT, exit_gracefully) 100 | signal.signal(signal.SIGTERM, exit_gracefully) 101 | 102 | try: 103 | while True: 104 | if terminate.wait(1): 105 | break 106 | except KeyboardInterrupt: 107 | pass 108 | 109 | self.close() 110 | 111 | def close(self): 112 | logger.info("close app!") 113 | if self.pflet: 114 | self.pflet.terminate() 115 | self.pflet = None 116 | if self.connetion: 117 | self.connetion.close() 118 | self.connetion = None 119 | 120 | if self.pflet is not None and not is_windows(): 121 | try: 122 | logger.info(f"Flet View process {self.pflet.pid}") 123 | os.kill(self.pflet.pid + 1, signal.SIGKILL) 124 | except: 125 | pass 126 | 127 | if self.closeCallback: 128 | self.closeCallback() 129 | 130 | def startApp(path: str, closeCallback=None, threaded=False): 131 | kwargs ={ 132 | "assets_dir": "./", 133 | "view": "desktop", 134 | "web_renderer": "canvas", 135 | "dir": "./" 136 | } 137 | loaded = False 138 | if os.path.exists(path) and path.endswith("app.py"): 139 | app = loadApp(path) 140 | if app: 141 | if hasattr(app, "project"): 142 | kwargs.update(app.project) 143 | loaded = True 144 | 145 | if os.path.exists(path) and path.endswith(".json"): 146 | with open(path, "r", encoding="utf-8") as f: 147 | kwargs.update(json.load(f)) 148 | projectDir = kwargs["dir"] 149 | if loaded == False: 150 | apppy = "%s/app.py" % (projectDir) 151 | if os.path.exists(apppy): 152 | app = loadApp(apppy) 153 | if app: 154 | loaded = True 155 | 156 | if globalThis.project: 157 | loaded = True 158 | 159 | globalThis.rootcwd = os.getcwd() 160 | if 'assets_dir' in kwargs: 161 | assets_dir = os.path.normpath(os.path.abspath(kwargs['assets_dir'])) 162 | else: 163 | assets_dir = globalThis.rootcwd 164 | kwargs['assets_dir'] = assets_dir 165 | globalThis.assets_dir = assets_dir 166 | 167 | if 'host' in kwargs and kwargs['host'] and kwargs['host'] != "0.0.0.0": 168 | host = kwargs['host'] 169 | else: 170 | host = get_host_ip() 171 | kwargs['host'] = host 172 | globalThis.server_ip = host if host not in [None, "", "*"] else "127.0.0.1" 173 | 174 | if 'port' in kwargs and kwargs['port']: 175 | port = kwargs['port'] 176 | globalThis.port = port 177 | else: 178 | port = get_free_tcp_port() 179 | globalThis.port = port 180 | kwargs['port'] = port 181 | 182 | 183 | if "view" in kwargs: 184 | if kwargs["view"] == "desktop": 185 | kwargs['view'] = flet.FLET_APP 186 | elif kwargs["view"] == "web": 187 | kwargs['view']= flet.WEB_BROWSER 188 | if 'web_renderer' in kwargs: 189 | if kwargs['web_renderer'] == "canvas": 190 | kwargs['web_renderer'] = 'canvaskit' 191 | else: 192 | kwargs['web_renderer'] = 'html' 193 | else: 194 | kwargs['web_renderer'] = 'canvaskit' 195 | 196 | globalThis.web_renderer = kwargs['web_renderer'] 197 | 198 | projectDir = kwargs["dir"] 199 | if os.path.exists(projectDir): 200 | t = Thread(target=startFileServer, args=(globalThis.port, globalThis.assets_dir)) 201 | t.daemon = True 202 | t.start() 203 | 204 | if loaded: 205 | sapp = EvueApplication(closeCallback) 206 | globalThis.evueApp = sapp 207 | kwargs['target'] = main 208 | logger.info(kwargs) 209 | if threaded: 210 | t = Thread(target=sapp.startFlet, kwargs=kwargs) 211 | t.daemon = True 212 | t.start() 213 | else: 214 | sapp.startFlet(**kwargs) 215 | else: 216 | logger.error("app loaded failed") 217 | -------------------------------------------------------------------------------- /evue/fileserver.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import socket 4 | import contextlib 5 | from http.server import * 6 | from http.server import test 7 | 8 | class CORSRequestHandler (SimpleHTTPRequestHandler): 9 | def end_headers(self): 10 | self.send_header('Access-Control-Allow-Origin', '*') 11 | SimpleHTTPRequestHandler.end_headers(self) 12 | 13 | def get_host_ip(): 14 | try: 15 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 16 | s.connect(('8.8.8.8', 80)) 17 | ip = s.getsockname()[0] 18 | finally: 19 | s.close() 20 | return ip 21 | 22 | def get_free_tcp_port(): 23 | sock = socket.socket() 24 | sock.bind(("", 0)) 25 | return sock.getsockname()[1] 26 | 27 | def startFileServer(port, directory): 28 | # ensure dual-stack is not disabled; ref #38907 29 | class DualStackServer(ThreadingHTTPServer): 30 | 31 | def server_bind(self): 32 | # suppress exception when protocol is IPv4 33 | with contextlib.suppress(Exception): 34 | self.socket.setsockopt( 35 | socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) 36 | return super().server_bind() 37 | 38 | def finish_request(self, request, client_address): 39 | self.RequestHandlerClass(request, client_address, self, 40 | directory=directory) 41 | 42 | test(HandlerClass=CORSRequestHandler, ServerClass=DualStackServer, port=port, bind="0.0.0.0") 43 | 44 | if __name__ == '__main__': 45 | import argparse 46 | import contextlib 47 | 48 | parser = argparse.ArgumentParser() 49 | parser.add_argument('--cgi', action='store_true', 50 | help='run as CGI server') 51 | parser.add_argument('-b', '--bind', metavar='ADDRESS', 52 | help='bind to this address ' 53 | '(default: all interfaces)') 54 | parser.add_argument('-d', '--directory', default=os.getcwd(), 55 | help='serve this directory ' 56 | '(default: current directory)') 57 | parser.add_argument('-p', '--protocol', metavar='VERSION', 58 | default='HTTP/1.0', 59 | help='conform to this HTTP version ' 60 | '(default: %(default)s)') 61 | parser.add_argument('port', default=8000, type=int, nargs='?', 62 | help='bind to this port ' 63 | '(default: %(default)s)') 64 | args = parser.parse_args() 65 | if args.cgi: 66 | handler_class = CGIHTTPRequestHandler 67 | else: 68 | handler_class = CORSRequestHandler 69 | 70 | # ensure dual-stack is not disabled; ref #38907 71 | class DualStackServer(ThreadingHTTPServer): 72 | 73 | def server_bind(self): 74 | # suppress exception when protocol is IPv4 75 | with contextlib.suppress(Exception): 76 | self.socket.setsockopt( 77 | socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) 78 | return super().server_bind() 79 | 80 | def finish_request(self, request, client_address): 81 | self.RequestHandlerClass(request, client_address, self, 82 | directory=args.directory) 83 | 84 | test( 85 | HandlerClass=handler_class, 86 | ServerClass=DualStackServer, 87 | port=args.port, 88 | bind=args.bind, 89 | protocol=args.protocol, 90 | ) 91 | 92 | -------------------------------------------------------------------------------- /evue/iconbrowser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import re 3 | import functools 4 | from itertools import cycle 5 | from threading import Timer 6 | from collections import defaultdict 7 | from functools import cached_property 8 | from typing import Any, Callable, List 9 | 10 | import pyperclip 11 | import flet 12 | from flet import ( 13 | ListView, 14 | Page, 15 | Text, 16 | UserControl, 17 | Column, 18 | icons, 19 | Container, 20 | Row, 21 | VerticalDivider, 22 | TextButton, 23 | IconButton, 24 | GridView, 25 | ButtonStyle, 26 | SnackBar, 27 | TextField, 28 | Switch 29 | ) 30 | from flet.padding import Padding 31 | from flet.control_event import ControlEvent 32 | 33 | 34 | class debounce: # noqa 35 | def __init__(self, timeout: float = 1): 36 | self.timeout = timeout 37 | self._timer = None 38 | 39 | def __call__(self, func: Callable): 40 | @functools.wraps(func) 41 | def decorator(*args, **kwargs): 42 | if self._timer is not None: 43 | self._timer.cancel() 44 | self._timer = Timer(self.timeout, func, args=args, kwargs=kwargs) 45 | self._timer.start() 46 | 47 | return decorator 48 | 49 | 50 | class Sidebar(UserControl): 51 | def __init__(self, *args, **kwargs): 52 | super().__init__(*args, **kwargs, expand=True) 53 | self.menu = Column( 54 | auto_scroll=True, 55 | width=200, 56 | ) 57 | 58 | def add(self, *item: Any): 59 | self.menu.controls.extend(item) 60 | self.update() 61 | 62 | def build(self): 63 | return self.menu 64 | 65 | 66 | class SearchApp(UserControl): 67 | def __init__(self, app, max_count: int = 200, *args, **kwargs): 68 | super().__init__(*args, **kwargs) 69 | self.app = app 70 | self.max_count = max_count 71 | self.text = TextField(height=35, icon=icons.SEARCH, border_radius=17.5, content_padding=Padding( 72 | 15, 2, 0, 2 73 | ), opacity=0.5, on_change=self.on_search) 74 | self.switch = Switch(label="light", on_change=self.theme_changed) 75 | self.row = Row([ 76 | Row([ 77 | self.text, 78 | TextButton(text="Search", on_click=self.on_search), 79 | ], alignment="center", vertical_alignment="start", expand=True 80 | ), 81 | self.switch 82 | 83 | ], height=50, alignment="center", vertical_alignment="start") 84 | 85 | def theme_changed(self, e): 86 | self.page.theme_mode = "dark" if self.page.theme_mode == "light" else "light" 87 | self.switch.label = "Dark" if self.page.theme_mode == "dark" else "Light" 88 | self.update() 89 | self.page.update() 90 | 91 | def build(self): 92 | return self.row 93 | 94 | @debounce(0.3) 95 | def on_search(self, e: ControlEvent): 96 | icon = [] 97 | if (search := self.text.value) and len(search) >= 2: 98 | count = 0 99 | for values in self.app.icons.values(): 100 | for value in values: 101 | if re.search(search, value, re.IGNORECASE): 102 | icon.append(value) 103 | count += 1 104 | if count >= self.max_count: 105 | self.app.show_icons(icon) 106 | return 107 | self.app.show_icons(icon) 108 | 109 | 110 | class IconExplore(Container): 111 | def __init__(self, page:Page): 112 | super().__init__() 113 | self.page = page 114 | self.__colors = cycle( 115 | ['#fbb4ae', '#b3cde3', '#ccebc5', '#decbe4', '#fed9a6', '#ffffcc', '#e5d8bd', '#fddaec', '#f2f2f2', 116 | '#b3e2cd', '#fdcdac', '#cbd5e8', '#f4cae4', '#e6f5c9', '#fff2ae', '#f1e2cc', '#cccccc', '#8dd3c7', 117 | '#ffffb3', '#bebada', '#fb8072', '#80b1d3', '#fdb462', '#b3de69', '#fccde5', '#d9d9d9', '#bc80bd', 118 | '#ccebc5', '#ffed6f']) 119 | self.container = GridView( 120 | expand=1, 121 | runs_count=5, 122 | max_extent=150, 123 | child_aspect_ratio=1.0, 124 | spacing=5, 125 | run_spacing=5, 126 | ) 127 | 128 | @cached_property 129 | def icons(self): 130 | data = defaultdict(list) 131 | for key in icons.__dict__.keys(): 132 | if key.startswith('_') or not key[0].isupper(): 133 | continue 134 | key_prefix = key.split("_")[0] 135 | data[key_prefix].append(key) 136 | return dict(sorted(data.items(), key=lambda x: (x[0][0], len(x[0])))) 137 | 138 | def color(self): 139 | return next(self.__colors) 140 | 141 | def copy_code(self, e: ControlEvent): 142 | for widget in e.control.content.controls: 143 | if isinstance(widget, Text): 144 | text = "icons.%s" % widget.value 145 | pyperclip.copy(text) 146 | e.page.snack_bar = SnackBar( 147 | Text(f"Copy code: %s to clipboard" % text, 148 | color="white") 149 | ) 150 | e.page.snack_bar.open = True 151 | e.page.update() 152 | 153 | def visible_text(self, e: ControlEvent): 154 | e.control.content.controls[-1].visible = True if e.data == "true" else False 155 | self.page.update() 156 | 157 | def show_icons(self, values: List[str]) -> None: 158 | self.container.controls.clear() 159 | self.page.update() 160 | for index, key in enumerate(values, start=1): 161 | self.container.controls.append( 162 | Container( 163 | content=Column([ 164 | IconButton(icon=getattr(icons, key), icon_size=64), 165 | Text("%s" % key, color="#ffffff", visible=False) 166 | ], 167 | expand=True, 168 | alignment="center", 169 | horizontal_alignment="center", 170 | width=120 171 | ), width=200, border=None, 172 | # bgcolor=self.color(), 173 | on_hover=self.visible_text, 174 | on_click=self.copy_code 175 | ) 176 | ) 177 | if index % 100 == 0: 178 | self.page.update() 179 | self.page.update() 180 | 181 | def _build(self): 182 | def switch(e): 183 | [setattr(v, "style", None) for v in sidebar.controls] 184 | e.control.style = ButtonStyle(bgcolor="#35698f", color="#ffffff") 185 | if values := self.icons.get(e.control.text): 186 | self.show_icons(values) 187 | 188 | sidebar = ListView( 189 | controls=[TextButton(prefix, on_click=switch, left=True) for prefix in self.icons], 190 | width=120 191 | ) 192 | search = SearchApp(self) 193 | 194 | self.content=Column([ 195 | search, 196 | Row([ 197 | sidebar, VerticalDivider(visible=True, width=1, color="#232323"), self.container 198 | ], expand=True) 199 | ], expand=True) 200 | self.expand = True 201 | 202 | def main(page: Page): 203 | dialog = IconExplore(page) 204 | page.theme_mode = "light" 205 | page.expand = True 206 | page.window_center() 207 | page.add(dialog) 208 | page.update() 209 | 210 | if __name__ == '__main__': 211 | flet.app(target=main) 212 | -------------------------------------------------------------------------------- /evue/jstimer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import print_function 3 | import threading 4 | import atexit 5 | 6 | 7 | def test(): 8 | """Test function. Prints "Hello World". Returns None""" 9 | print ("Hello World") 10 | 11 | def noop(): 12 | """Test function. Does nothing.""" 13 | pass 14 | 15 | # Contains a list of active timeouts 16 | timeouts = {} 17 | # Contains a list of active intervals 18 | intervals = {} 19 | 20 | # Internally used counters. 21 | _interval_ctr = 0 22 | _timeout_ctr = 0 23 | 24 | def _new_interval_id(): 25 | """Used internally to return the next available interval ID.""" 26 | global _interval_ctr 27 | _interval_ctr += 1 28 | return _interval_ctr 29 | 30 | def _new_timeout_id(): 31 | """Used internally to return the next available timeout ID.""" 32 | global _timeout_ctr 33 | _timeout_ctr += 1 34 | return _timeout_ctr 35 | 36 | def setTimeout(f, delay, *args, **kwargs): 37 | """Creates a Timer event that runs the function 'f' after 'delay' (in milliseconds) duration. 38 | 39 | f: function to be called 40 | delay: time in milliseconds after which f will be called. 41 | [args,kwargs]: Parameters to be passed to the function 'f' on call. 42 | 43 | Returns: Timeout ID. Used by clearTimeout() to clear the timeout. 44 | """ 45 | t_id = _new_timeout_id() 46 | def f_major(): 47 | f(*args, **kwargs) 48 | try: 49 | del timeouts[t_id] 50 | except KeyError: 51 | pass 52 | t = threading.Timer(1.0*delay/1000,f_major) 53 | timeouts[t_id] = t 54 | t.daemon = True 55 | t.start() 56 | return t_id 57 | 58 | def setInterval(f,delay, *args, **kwargs): 59 | """Creates a Timer event that runs the function 'f' every 'delay' milliseconds till cancelled. 60 | 61 | f: function to be called 62 | delay: time in milliseconds after which f will be repeatedly called. 63 | [args,kwargs]: Parameters to be passed to the function 'f' on call. 64 | 65 | Returns: Interval ID. Used by clearInterval() to clear the interval. 66 | """ 67 | i_id = _new_interval_id() 68 | def f_major(): 69 | f(*args, **kwargs) 70 | t = threading.Timer(1.0*delay/1000,f_major) 71 | intervals[i_id] = t 72 | t.daemon = True 73 | t.start() 74 | t = threading.Timer(1.0*delay/1000,f_major) 75 | intervals[i_id] = t 76 | t.daemon = True 77 | t.start() 78 | return i_id 79 | 80 | def clearInterval(i_id): 81 | """Cancels the Interval event with the specified ID.""" 82 | try: 83 | intervals[i_id].cancel() 84 | del intervals[i_id] 85 | return 0 86 | except KeyError: 87 | return 1 88 | 89 | def clearTimeout(t_id): 90 | """Cancels the Timeout event with the specified ID.""" 91 | try: 92 | timeouts[t_id].cancel() 93 | del timeouts[t_id] 94 | return 0 95 | except KeyError: 96 | return 1 97 | 98 | @atexit.register 99 | def clearAll(): 100 | """Clears all intervals/timeouts. Runs on exit.""" 101 | for i_id in list(intervals): 102 | clearInterval(i_id) 103 | for t_id in list(timeouts): 104 | clearTimeout(t_id) 105 | 106 | if __name__ == "__main__": 107 | print ("""Usage: 108 | 109 | import jstimers 110 | 111 | i_id = jstimers.setInterval(jstimers.test, 2000) 112 | jstimers.clearInterval(i_id) 113 | """) -------------------------------------------------------------------------------- /evue/log.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import logging 3 | from loguru import logger 4 | logging.root.setLevel(logging.WARNING) 5 | -------------------------------------------------------------------------------- /evue/pylock.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import threading 3 | 4 | mutex = threading.Lock() 5 | 6 | def lock(func): 7 | def wrapper(*args, **kwargs): 8 | with mutex: 9 | ret = func(*args, **kwargs) 10 | return ret 11 | return wrapper 12 | -------------------------------------------------------------------------------- /evue/router.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import builtins 3 | from collections import OrderedDict 4 | from flet import Container, Stack, Page 5 | from .globalthis import globalThis, require 6 | from loguru import logger 7 | from .componentmanager import componentManager 8 | from .sessionobject import SessionObject 9 | 10 | 11 | class Router(SessionObject): 12 | 13 | borders = {} 14 | 15 | def __init__(self, sessionID=None) -> None: 16 | logger.warning(sessionID) 17 | super().__init__(sessionID) 18 | self.elements = OrderedDict() 19 | self.paths = ["./"] 20 | self.pageviews = {} 21 | self._currentPageUri = None 22 | self._debug_ = False 23 | 24 | def add(self, uri, id, element): 25 | userid = "%s.%s" % (uri, id) 26 | element.userid = userid 27 | self.elements[userid] = element 28 | 29 | def getElementById(self, id): 30 | if id in self.elements: 31 | return self.elements[id] 32 | return None 33 | 34 | @property 35 | def debug(self): 36 | return self._debug_ 37 | 38 | @property 39 | def currentPageUri(self): 40 | return self._currentPageUri 41 | 42 | @currentPageUri.setter 43 | def currentPageUri(self, value): 44 | if self._currentPageUri != value: 45 | self._currentPageUri = value 46 | self.page.event.emit("page.router.currentPageChanged", value) 47 | 48 | @property 49 | def currentPage(self): 50 | if self.currentPageUri in self.pageviews: 51 | return self.pageviews[self.currentPageUri] 52 | return None 53 | 54 | @property 55 | def currentPageComponent(self): 56 | if self.currentPageUri in self.pageviews: 57 | return self.pageviews[self.currentPageUri].component 58 | return None 59 | 60 | @classmethod 61 | def registerComponents(cls, components): 62 | for c in components: 63 | cls.registerComponent(c) 64 | 65 | @classmethod 66 | def registerComponent(cls, componentInfo): 67 | componentManager.registerComponent(componentInfo) 68 | 69 | def addPaths(self, paths): 70 | self.paths.extend(paths) 71 | 72 | def render(self, pageview): 73 | self.page.add(pageview) 74 | self.page.update() 75 | 76 | def createPage(self, component): 77 | con = Container(content=Stack([component.rootElement.obj]), expand=True) 78 | con.component = component 79 | return con 80 | 81 | def hideAllPages(self): 82 | for uri in self.pageviews: 83 | page = self.pageviews[uri] 84 | page.visible = False 85 | 86 | def mount(self, uri, parent): 87 | component = self.create({'path': uri}, parent) 88 | self.update() 89 | return component 90 | 91 | def create(self, obj, parent=None): 92 | uri = obj['path'] 93 | dirPath = None 94 | if 'dir' in obj: 95 | dirPath = obj['dir'] 96 | module = require(uri, dirPath) 97 | component = module.createComponent(parent=parent, pageinfo=obj, sessionID=self.page.session_id) 98 | if component: 99 | component.pageinfo = obj 100 | return component 101 | 102 | def newEvuePage(self, obj): 103 | uri = obj['path'] 104 | component = self.create(obj) 105 | if component: 106 | pageview = self.createPage(component) 107 | self.pageviews[uri] = pageview 108 | self.render(pageview) 109 | self.currentPageUri = uri 110 | logger.warning("%s.onCreateFinished" % uri) 111 | self.page.event.emit("%s.onCreateFinished" % uri) 112 | 113 | def push(self, obj): 114 | uri = obj['path'] 115 | if uri in self.pageviews: 116 | self.currentPage.visible = False 117 | self.currentPage.component.onHide() 118 | for _uri in self.pageviews: 119 | page = self.pageviews[_uri] 120 | if page: 121 | if _uri != uri: 122 | page.visible = False 123 | else: 124 | page.visible = True 125 | page.component.pageinfo = obj 126 | page.component.onShow() 127 | self.currentPageUri = uri 128 | globalThis.update() 129 | else: 130 | self.hideAllPages() 131 | self.newEvuePage(obj) 132 | 133 | def replace(self, obj): 134 | if self.currentPage in globalThis.page.controls: 135 | globalThis.page.controls.remove(self.currentPage) 136 | self.pageviews.pop(self.currentPageUri) 137 | self._currentPageUri = None 138 | globalThis.update() 139 | 140 | self.hideAllPages() 141 | self.newEvuePage(obj) 142 | 143 | def debugToggle(self): 144 | from .elements import ElementInstances 145 | self._debug_ = not self._debug_ 146 | 147 | def debugPageViewOn(): 148 | for eid in ElementInstances: 149 | element = ElementInstances[eid] 150 | if eid not in self.borders: 151 | self.borders[eid] = { 152 | "border_width" : element.border_width, 153 | "border_radius" : element.border_radius, 154 | "border_color" : element.border_color 155 | } 156 | element.debug_on() 157 | 158 | def debugPageViewOff(): 159 | for eid in ElementInstances: 160 | element = ElementInstances[eid] 161 | element.debug_off() 162 | border = self.borders[eid] 163 | element.border_width = border['border_width'] 164 | element.border_radius = border['border_radius'] 165 | element.border_color = border['border_color'] 166 | 167 | if self._debug_: 168 | debugPageViewOn() 169 | else: 170 | debugPageViewOff() 171 | 172 | builtins.Router = Router 173 | -------------------------------------------------------------------------------- /evue/sessionobject.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from flet import Page 3 | from .globalthis import globalThis 4 | from .util import open_in_browser 5 | from typing import Any 6 | from loguru import logger 7 | from pyee import EventEmitter 8 | 9 | 10 | class SessionObject(object): 11 | 12 | def __init__(self, sessionID) -> None: 13 | self.sessionID = sessionID 14 | 15 | def getPage(self, sessionID)->Page: 16 | return globalThis.getPage(sessionID) 17 | 18 | @property 19 | def page(self)->Page: 20 | return globalThis.getPage(self.sessionID) 21 | 22 | @property 23 | def router(self): 24 | if self.page: 25 | return self.page.router 26 | 27 | @property 28 | def event(self) -> EventEmitter: 29 | if self.page: 30 | return self.page.event 31 | 32 | @property 33 | def isLogin(self): 34 | return self.page.isLogin 35 | 36 | def on(self, event, handler): 37 | self.event.on(event, handler) 38 | 39 | def emit(self,event: str,*args: Any,**kwargs: Any) -> bool: 40 | return self.event.emit(event, *args, **kwargs) 41 | 42 | @property 43 | def localstroage(self): 44 | return self.page.client_storage 45 | 46 | def getElementById(self, id): 47 | return self.router.getElementById(id) 48 | 49 | def add(self, uri, id, element): 50 | return self.router.add(uri, id, element) 51 | 52 | def update(self): 53 | try: 54 | self.page.update() 55 | except: 56 | pass 57 | 58 | def clipboard(self): 59 | globalThis.clipboard(self.sessionID) 60 | 61 | def get_clipboard_data(self): 62 | return globalThis.get_clipboard_data() 63 | 64 | def openDialog(self, uri, dirPath=None, on_accept=None, on_cancle=None, on_dismiss=None): 65 | return globalThis.openDialog(self.sessionID, uri, dirPath=dirPath, on_accept=on_accept, on_cancle=on_cancle, on_dismiss=on_dismiss) 66 | 67 | def getFileDialog(self): 68 | return globalThis.getFileDialog(self.sessionID) 69 | 70 | def getOpenFileName(self, callback, dialog_title="打开文件", initial_directory="", allowed_extensions=[], file_type="any", allow_multiple=False): 71 | return globalThis.getOpenFileName(self.sessionID, callback, dialog_title=dialog_title, initial_directory=initial_directory, allowed_extensions=allowed_extensions, file_type=file_type, allow_multiple=allow_multiple) 72 | 73 | def getOpenFileNames(self, sessionID, callback, dialog_title="打开多个文件", initial_directory="", allowed_extensions=[], file_type="any", allow_multiple=True): 74 | return globalThis.getOpenFileNames(self.sessionID, callback, dialog_title=dialog_title, initial_directory=initial_directory, allowed_extensions=allowed_extensions, file_type=file_type, allow_multiple=allow_multiple) 75 | 76 | def getSaveFileName(self, callback, dialog_title="保存文件", file_name="", initial_directory="", file_type="any", allowed_extensions=[]): 77 | return globalThis.getSaveFileName(self.sessionID, callback, dialog_title=dialog_title, file_name=file_name, initial_directory=initial_directory, file_type=file_type, allowed_extensions=allowed_extensions) 78 | 79 | def getExistingDirectory(self, callback, dialog_title="", initial_directory=""): 80 | return globalThis.getExistingDirectory(self.sessionID, callback, dialog_title=dialog_title, initial_directory=initial_directory) 81 | 82 | def showMessage(self, msg): 83 | return globalThis.showMessage(self.sessionID, msg) 84 | 85 | def updateTitle(self, name): 86 | return globalThis.updateTitle(self.sessionID, name) 87 | 88 | def resetTitle(self): 89 | return globalThis.resetTitle(self.sessionID) 90 | 91 | def isPopupVisible(self): 92 | return globalThis.isMenuVisible(self.sessionID) 93 | 94 | def isMenuVisible(self): 95 | return globalThis.isMenuVisible(self.sessionID) 96 | 97 | def showMenu(self, uri, dirpath=None, x=0, y=0): 98 | return self.showPopup(uri, dirpath=dirpath, x=x, y=y) 99 | 100 | def hideMenu(self): 101 | return self.hidePopup() 102 | 103 | def showPopup(self, uri, dirpath=None, x=0, y=0, isUpdate=True): 104 | return globalThis.showPopup(self.sessionID, uri, dirpath=dirpath, x=x, y=y, isUpdate=isUpdate) 105 | 106 | def hidePopup(self): 107 | return globalThis.hidePopup(self.sessionID) 108 | 109 | def launch_url(self, url): 110 | self.page.launch_url(url) 111 | 112 | def window_destory(self): 113 | self.page.window_destroy() 114 | self.page.update() 115 | 116 | def window_close(self): 117 | self.page.window_close() 118 | self.page.update() 119 | 120 | def window_minimized(self): 121 | self.page.window_minimized = True 122 | self.page.update() 123 | 124 | def window_maximized(self): 125 | self.page.window_maximized = True 126 | self.page.update() 127 | -------------------------------------------------------------------------------- /evue/util.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import math 3 | import os 4 | import platform 5 | import sys 6 | import unicodedata 7 | import webbrowser 8 | 9 | 10 | def is_windows(): 11 | return platform.system() == "Windows" 12 | 13 | 14 | def is_linux(): 15 | return platform.system() == "Linux" 16 | 17 | 18 | def is_linux_server(): 19 | if platform.system() == "Linux": 20 | # check if it's WSL 21 | p = "/proc/version" 22 | if os.path.exists(p): 23 | with open(p, "r") as file: 24 | if "microsoft" in file.read(): 25 | return False # it's WSL, not a server 26 | return os.environ.get("XDG_CURRENT_DESKTOP") is None 27 | return False 28 | 29 | 30 | def is_macos(): 31 | return platform.system() == "Darwin" 32 | 33 | 34 | def get_platform(): 35 | p = platform.system() 36 | if is_windows(): 37 | return "windows" 38 | elif p == "Linux": 39 | return "linux" 40 | elif p == "Darwin": 41 | return "darwin" 42 | else: 43 | raise Exception(f"Unsupported platform: {p}") 44 | 45 | 46 | def get_arch(): 47 | a = platform.machine().lower() 48 | if a == "x86_64" or a == "amd64": 49 | return "amd64" 50 | elif a == "arm64" or a == "aarch64": 51 | return "arm64" 52 | elif a.startswith("arm"): 53 | return "arm_7" 54 | else: 55 | raise Exception(f"Unsupported architecture: {a}") 56 | 57 | 58 | def open_in_browser(url): 59 | webbrowser.open(url) 60 | 61 | 62 | # https://stackoverflow.com/questions/377017/test-if-executable-exists-in-python 63 | def which(program, exclude_exe=None): 64 | import os 65 | 66 | def is_exe(fpath): 67 | return os.path.isfile(fpath) and os.access(fpath, os.X_OK) 68 | 69 | for path in os.environ["PATH"].split(os.pathsep): 70 | exe_file = os.path.join(path, program) 71 | if is_exe(exe_file) and ( 72 | exclude_exe is None 73 | or (exclude_exe is not None and exclude_exe.lower() != exe_file.lower()) 74 | ): 75 | return exe_file 76 | 77 | return None 78 | 79 | 80 | def is_within_directory(directory, target): 81 | 82 | abs_directory = os.path.abspath(directory) 83 | abs_target = os.path.abspath(target) 84 | 85 | prefix = os.path.commonprefix([abs_directory, abs_target]) 86 | 87 | return prefix == abs_directory 88 | 89 | 90 | def safe_tar_extractall(tar, path=".", members=None, *, numeric_owner=False): 91 | 92 | for member in tar.getmembers(): 93 | member_path = os.path.join(path, member.name) 94 | if not is_within_directory(path, member_path): 95 | raise Exception("Attempted Path Traversal in Tar File") 96 | 97 | tar.extractall(path, members, numeric_owner=numeric_owner) 98 | 99 | 100 | def is_localhost_url(url): 101 | return ( 102 | "://localhost/" in url 103 | or "://localhost:" in url 104 | or "://127.0.0.1/" in url 105 | or "://127.0.0.1:" in url 106 | ) 107 | 108 | 109 | def get_current_script_dir(): 110 | pathname = os.path.dirname(sys.argv[0]) 111 | return os.path.abspath(pathname) 112 | 113 | 114 | def slugify(original: str) -> str: 115 | """ 116 | Make a string url friendly. Useful for creating routes for navigation. 117 | 118 | >>> slugify("What's up?") 119 | 'whats-up' 120 | 121 | >>> slugify(" Mitä kuuluu? ") 122 | 'mitä-kuuluu' 123 | """ 124 | slugified = original.strip() 125 | slugified = " ".join(slugified.split()) # Remove extra spaces between words 126 | slugified = slugified.lower() 127 | # Remove unicode punctuation 128 | slugified = "".join( 129 | character 130 | for character in slugified 131 | if not unicodedata.category(character).startswith("P") 132 | ) 133 | slugified = slugified.replace(" ", "-") 134 | 135 | return slugified 136 | 137 | 138 | class Vector(complex): 139 | """ 140 | Simple immutable 2D vector class based on the Python complex number type 141 | 142 | Create and access - coordinates 143 | 144 | >>> v = Vector(1, 2) 145 | >>> v.x, v.y 146 | (1.0, 2.0) 147 | 148 | Create and access - angle and magnitude (length) 149 | 150 | >>> v = Vector.polar(math.pi, 2) 151 | >>> v 152 | Vector(-2.0, 0.0) 153 | >>> v.magnitude # Length of the vector, alias for abs(v) 154 | 2.0 155 | >>> v.radians 156 | 3.141592653589793 157 | >>> v.degrees 158 | 180.0 159 | 160 | Arithmetic operations 161 | 162 | >>> Vector(1, 1) + 2 163 | Vector(3.0, 1.0) 164 | >>> Vector(0.1, 0.1) + Vector(0.2, 0.2) == Vector(0.3, 0.3) # Float tolerance 10 decimals 165 | True 166 | >>> Vector(2, 3) - Vector(1, 1) 167 | Vector(1.0, 2.0) 168 | >>> Vector(1, 1) * 2 169 | Vector(2.0, 2.0) 170 | >>> round(Vector.polar(math.pi / 4, 1), 1) 171 | Vector(0.7, 0.7) 172 | 173 | Get a new vector by adjusting one of the coordinates 174 | >>> v = Vector() 175 | >>> v.with_x(1) 176 | Vector(1.0, 0.0) 177 | >>> v.with_y(2) 178 | Vector(0.0, 2.0) 179 | 180 | Get a new vector by adjusting angle or magnitude 181 | 182 | >>> v = Vector(1, 2) 183 | >>> v = v.with_magnitude(4.47213595499958) # Twice as long 184 | >>> v.x, v.y 185 | (2.0, 4.0) 186 | 187 | >>> v = Vector.polar(math.pi, 2) 188 | >>> v 189 | Vector(-2.0, 0.0) 190 | >>> v.with_radians(0) 191 | Vector(2.0, 0.0) 192 | >>> v.with_degrees(90) 193 | Vector(0.0, 2.0) 194 | """ 195 | 196 | abs_tol = 1e-10 197 | 198 | x = complex.real 199 | y = complex.imag 200 | __add__ = lambda self, other: type(self)(complex.__add__(self, other)) 201 | __sub__ = lambda self, other: type(self)(complex.__sub__(self, other)) 202 | __mul__ = lambda self, other: type(self)(complex.__mul__(self, other)) 203 | __truediv__ = lambda self, other: type(self)(complex.__truediv__(self, other)) 204 | __len__ = lambda self: 2 205 | __round__ = lambda self, ndigits=None: type(self)( 206 | round(self.x, ndigits), round(self.y, ndigits) 207 | ) 208 | 209 | def __eq__(self, other): 210 | return math.isclose(self.x, other.x, abs_tol=self.abs_tol) and math.isclose( 211 | self.y, other.y, abs_tol=self.abs_tol 212 | ) 213 | 214 | def __ne__(self, other): 215 | return not self.__eq__(other) 216 | 217 | def __iter__(self): 218 | return iter([self.x, self.y]) 219 | 220 | def __str__(self): 221 | return str(tuple(self)) 222 | 223 | def __repr__(self): 224 | return f"{type(self).__name__}{str(self)}" 225 | 226 | @classmethod 227 | def polar(cls, radians, magnitude): 228 | return cls( 229 | round(math.cos(radians) * magnitude, 10), 230 | round(math.sin(radians) * magnitude, 10), 231 | ) 232 | 233 | @property 234 | def magnitude(self): 235 | return abs(self) 236 | 237 | @property 238 | def degrees(self): 239 | return math.degrees(self.radians) 240 | 241 | @property 242 | def radians(self): 243 | return math.atan2(self.y, self.x) 244 | 245 | def with_x(self, value): 246 | return type(self)(value, self.y) 247 | 248 | def with_y(self, value): 249 | return type(self)(self.x, value) 250 | 251 | def with_magnitude(self, value): 252 | return self * value / abs(self) 253 | 254 | def with_radians(self, value): 255 | magnitude = abs(self) 256 | return type(self).polar(value, magnitude) 257 | 258 | def with_degrees(self, value): 259 | radians = math.radians(value) 260 | magnitude = abs(self) 261 | return type(self).polar(radians, magnitude) 262 | -------------------------------------------------------------------------------- /examples/evue_login/app.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from evue import EvueApp, globalThis 3 | import os 4 | 5 | def registerComponents(appDir): 6 | pass 7 | 8 | def onCreate(config): 9 | registerComponents(config['appDir']) 10 | 11 | def onDestroy(): 12 | pass 13 | 14 | globalThis.project = {'assets_dir': './', 'host': None, 'port': None, 'view': 'desktop', 'web_renderer': 'canvas', 'dir': './evue_login', 'entry': 'evue_login', 'width': 1240, 'height': 900} 15 | globalThis.project['assets_dir'] = os.path.dirname(__file__) 16 | globalThis.project['root_dir'] = os.path.dirname(os.path.dirname(__file__)) 17 | globalThis.project['dir'] = os.path.dirname(__file__) 18 | globalThis.project['projectJson'] = "%s/project.json" % os.path.dirname(__file__) 19 | 20 | EvueApp({ 21 | 'appDir': os.path.dirname(__file__), 22 | 'onCreate': onCreate, 23 | 'onDestroy': onDestroy, 24 | 'paths': [os.path.dirname(__file__)], 25 | 'uri': globalThis.project['entry'], 26 | }) -------------------------------------------------------------------------------- /examples/evue_login/evue_login.evue: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 6 |
7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 |
24 | 25 |
26 | 27 | 28 | 29 |
30 | 31 | 32 | 33 | 34 |
35 |
36 | 37 |
38 | 39 | 40 | 41 | 42 | 43 |
44 |
45 |
46 |
47 | 48 | 49 | 50 | 86 | 87 | 88 | from loguru import logger 89 | 90 | class UserComponent(object): 91 | 92 | def __init__(self, sessionID=None): 93 | super().__init__(sessionID=sessionID) 94 | 95 | def onInit(self): 96 | logger.warning("evue_login onInit") 97 | 98 | def onReady(self): 99 | logger.warning("evue_login onReady") 100 | 101 | def onShow(self): 102 | logger.warning("evue_login onShow") 103 | logger.warning(self.pageinfo) 104 | 105 | def onHide(self): 106 | logger.warning("========evue_login onHide=========") 107 | 108 | def onQuit(self): 109 | logger.warning("========evue_login onQuit=========") 110 | 111 | def onclick(self, element): 112 | self.router.push({ 113 | "path": "evue_designer" 114 | }) 115 | 116 | def onThemeChanged(self, element): 117 | if element.value: 118 | self.page.theme_mode = "dark" 119 | else: 120 | self.page.theme_mode = "light" 121 | self.page.update() 122 | 123 | def setUserInfo(self, email, password): 124 | self.email = email 125 | self.password = password 126 | 127 | def onLoginRegisterButtonclick(self, element): 128 | self.router.replace({"path": "evue_register", "isRegister": True}) 129 | 130 | def onForgetTextClick(self, element): 131 | self.router.replace({"path": "evue_register", "isRegister": False}) 132 | 133 | def onLoginClick(self, element): 134 | email_element = self.getElementById("emailTextfield") 135 | password_element = self.getElementById("passwordTextfield") 136 | email = email_element.value 137 | password = password_element.value 138 | 139 | if len(email) == 0: 140 | self.showMessage("Email is empty!") 141 | return 142 | 143 | if len(email) == 0: 144 | self.showMessage("Password is empty!") 145 | return 146 | 147 | def push(): 148 | self.router.push({"path": "evue_studio"}) 149 | 150 | self.emit("designer.login", email, password, push) 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /examples/evue_login/image/website/QQ.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/QQ.jpg -------------------------------------------------------------------------------- /examples/evue_login/image/website/android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/android.png -------------------------------------------------------------------------------- /examples/evue_login/image/website/bolt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/bolt.png -------------------------------------------------------------------------------- /examples/evue_login/image/website/component.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/component.png -------------------------------------------------------------------------------- /examples/evue_login/image/website/dragondjf.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/dragondjf.jpg -------------------------------------------------------------------------------- /examples/evue_login/image/website/evue_designer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/evue_designer.png -------------------------------------------------------------------------------- /examples/evue_login/image/website/gitee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/gitee.png -------------------------------------------------------------------------------- /examples/evue_login/image/website/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/github.png -------------------------------------------------------------------------------- /examples/evue_login/image/website/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/evue_login/image/website/html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/html.png -------------------------------------------------------------------------------- /examples/evue_login/image/website/ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/ios.png -------------------------------------------------------------------------------- /examples/evue_login/image/website/iot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/iot.png -------------------------------------------------------------------------------- /examples/evue_login/image/website/linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/linux.png -------------------------------------------------------------------------------- /examples/evue_login/image/website/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/login.png -------------------------------------------------------------------------------- /examples/evue_login/image/website/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/logo.png -------------------------------------------------------------------------------- /examples/evue_login/image/website/mac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/mac.png -------------------------------------------------------------------------------- /examples/evue_login/image/website/os.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/os.png -------------------------------------------------------------------------------- /examples/evue_login/image/website/platform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/platform.png -------------------------------------------------------------------------------- /examples/evue_login/image/website/platform2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/platform2.png -------------------------------------------------------------------------------- /examples/evue_login/image/website/web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/web.png -------------------------------------------------------------------------------- /examples/evue_login/image/website/windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_login/image/website/windows.png -------------------------------------------------------------------------------- /examples/evue_login/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "assets_dir": "./", 3 | "host": null, 4 | "port": null, 5 | "view": "desktop", 6 | "web_renderer": "canvas", 7 | "dir": "./evue_login", 8 | "entry": "evue_login", 9 | "width": 1240, 10 | "height": 900 11 | } -------------------------------------------------------------------------------- /examples/evue_website/app.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from evue import EvueApp, globalThis 3 | import os 4 | 5 | def registerComponents(appDir): 6 | pass 7 | 8 | def onCreate(config): 9 | registerComponents(config['appDir']) 10 | 11 | def onDestroy(): 12 | pass 13 | 14 | globalThis.project = {'assets_dir': './', 'host': None, 'port': None, 'view': 'desktop', 'web_renderer': 'canvas', 'dir': './evue_website', 'entry': 'evue_website'} 15 | globalThis.project['assets_dir'] = os.path.dirname(__file__) 16 | globalThis.project['root_dir'] = os.path.dirname(os.path.dirname(__file__)) 17 | globalThis.project['dir'] = os.path.dirname(__file__) 18 | globalThis.project['projectJson'] = "%s/project.json" % os.path.dirname(__file__) 19 | 20 | EvueApp({ 21 | 'appDir': os.path.dirname(__file__), 22 | 'onCreate': onCreate, 23 | 'onDestroy': onDestroy, 24 | 'paths': [os.path.dirname(__file__)], 25 | 'uri': globalThis.project['entry'], 26 | }) -------------------------------------------------------------------------------- /examples/evue_website/image/website/QQ.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/QQ.jpg -------------------------------------------------------------------------------- /examples/evue_website/image/website/android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/android.png -------------------------------------------------------------------------------- /examples/evue_website/image/website/bolt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/bolt.png -------------------------------------------------------------------------------- /examples/evue_website/image/website/component.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/component.png -------------------------------------------------------------------------------- /examples/evue_website/image/website/dragondjf.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/dragondjf.jpg -------------------------------------------------------------------------------- /examples/evue_website/image/website/evue_designer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/evue_designer.png -------------------------------------------------------------------------------- /examples/evue_website/image/website/gitee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/gitee.png -------------------------------------------------------------------------------- /examples/evue_website/image/website/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/github.png -------------------------------------------------------------------------------- /examples/evue_website/image/website/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/evue_website/image/website/html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/html.png -------------------------------------------------------------------------------- /examples/evue_website/image/website/ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/ios.png -------------------------------------------------------------------------------- /examples/evue_website/image/website/iot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/iot.png -------------------------------------------------------------------------------- /examples/evue_website/image/website/linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/linux.png -------------------------------------------------------------------------------- /examples/evue_website/image/website/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/login.png -------------------------------------------------------------------------------- /examples/evue_website/image/website/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/logo.png -------------------------------------------------------------------------------- /examples/evue_website/image/website/mac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/mac.png -------------------------------------------------------------------------------- /examples/evue_website/image/website/os.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/os.png -------------------------------------------------------------------------------- /examples/evue_website/image/website/platform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/platform.png -------------------------------------------------------------------------------- /examples/evue_website/image/website/platform2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/platform2.png -------------------------------------------------------------------------------- /examples/evue_website/image/website/web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/web.png -------------------------------------------------------------------------------- /examples/evue_website/image/website/windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/evue_website/image/website/windows.png -------------------------------------------------------------------------------- /examples/evue_website/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "assets_dir": "./", 3 | "host": null, 4 | "port": null, 5 | "view": "desktop", 6 | "web_renderer": "canvas", 7 | "dir": "./evue_website", 8 | "entry": "evue_website" 9 | } -------------------------------------------------------------------------------- /examples/evuebrowser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import sys 4 | import time 5 | from loguru import logger 6 | from evue import startApp, globalThis, EvueAppBar, Page 7 | 8 | def on_keyboard_event(e): 9 | page = e.page 10 | if e.key == "F5": 11 | page.router.debugToggle() 12 | elif e.key == "F11": 13 | page.show_semantics_debugger = not page.show_semantics_debugger 14 | page.update() 15 | 16 | def onAppInit(config): 17 | page: Page = config['page'] 18 | page.on_keyboard_event = on_keyboard_event 19 | 20 | if __name__ == '__main__': 21 | globalThis.onAppInit = onAppInit 22 | if len(sys.argv) == 1: 23 | startApp("./project.json") 24 | else: 25 | startApp(sys.argv[1]) 26 | 27 | -------------------------------------------------------------------------------- /examples/logo/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/logo/logo.ico -------------------------------------------------------------------------------- /examples/logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptiot/evue/6f1b57babd1a0d63d64b0963132c664e6c1ab753/examples/logo/logo.png -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.pdm] 2 | 3 | [tool.pdm.scripts] 4 | build = "python setup.py sdist bdist_wheel" 5 | publish = "twine upload dist/* --skip-existing --verbose" 6 | 7 | [tool.pdm.dev-dependencies] 8 | dev = [ 9 | "setuptools>=65.6.3", 10 | "wheel>=0.38.4", 11 | ] 12 | [project] 13 | name = "evue" 14 | version = "0.1.6.1" 15 | description = "Evue is the fastest way to build apps in Python or Javascript on windows/linux/macos/ios/andriod/rtos! Write once, run everywhere! " 16 | authors = [ 17 | {name = "dragondjf", email = "ding465398889@163.com"}, 18 | ] 19 | dependencies = [ 20 | "flet>=0.2.4", 21 | "pyee>=9.0.4", 22 | "loguru>=0.6.0", 23 | "pyperclip>=1.8.2", 24 | "pillow>=9.3.0", 25 | "qrcode>=7.3.1", 26 | ] 27 | requires-python = ">=3.10" 28 | license = {text = "MIT"} 29 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README-pypi.md", "r", encoding="utf8") as fh: 4 | long_description = fh.read() 5 | 6 | setuptools.setup( 7 | name="evue", 8 | version="0.1.6.1", 9 | author="dragondjf", 10 | author_email="ding465398889@163.com", 11 | description="Evue is a high-performance gui framework base an html/css which can run on windows/linux/macos/ios/andriod/rtos! Write once, run everywhere! ", 12 | long_description=long_description, 13 | long_description_content_type="text/markdown", 14 | url="https://github.com/scriptiot/evm", 15 | packages=setuptools.find_packages(), 16 | install_requires=[ 17 | "flet==0.3.2", 18 | "pyee>=9.0.4", 19 | "loguru>=0.6.0", 20 | "pyperclip>=1.8.2", 21 | "pillow>=9.3.0", 22 | "qrcode>=7.3.1", 23 | ], 24 | extras_require={ 25 | 'dev': [] 26 | }, 27 | classifiers=[ 28 | "License :: OSI Approved :: MIT License", 29 | "Operating System :: OS Independent", 30 | 'Programming Language :: Python :: 3' 31 | ], 32 | python_requires='>=3.7', 33 | ) 34 | --------------------------------------------------------------------------------