diff --git a/.drone.yml b/.drone.yml index b4606536..36cad6e8 100644 --- a/.drone.yml +++ b/.drone.yml @@ -2,27 +2,23 @@ kind: pipeline type: exec name: default -clone: - disable: true - trigger: + branch: + - dev-release event: - - push + - push steps: - - name: clone - commands: - - sleep 300 # wait aliyun repo to sync - - git init - - git remote add aliyun "https://code.aliyun.com/wang0618/pywebio.git" - - git fetch --no-tags --prune --progress --no-recurse-submodules --depth=1 aliyun $DRONE_BRANCH - - git checkout --progress --force -B $DRONE_BRANCH aliyun/$DRONE_BRANCH - - git log -1 - name: deploy demos commands: + - | # https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#pipe-dockerfile-through-stdin + docker build -t pywebio -f- . < - docker run --restart=always --name=pywebio-demos -v $PWD:/app_tmp - --label="traefik.http.services.pywebiodemos.loadbalancer.server.port=80" - -d python:3 bash -c "cp -r /app_tmp /app && cd /app && pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple . && python3 -m demos --port=80" - - sleep 5 # wait container start \ No newline at end of file + - docker run --restart=always --name=pywebio-demos -d pywebio diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index ef7b66f9..36ee4617 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,18 +1,21 @@ --- -name: Bug report -about: Create a report to help us improve -title: '' -labels: bug -assignees: '' +name: Bug report about: Create a bug report to help us improve title: '' +labels: bug assignees: '' --- +Note: For inquiries while using PyWebIO or questions that might be helpful to others, please consider moving +to [Discussions](https://github.com/wang0618/PyWebIO/discussions) for posting. You can +try [PyWebIO QA Bot](https://github.com/pywebio/PyWebIO/discussions/596) to let AI answer the questions you encounter. -**BUG描述** -描述BUG表现以及复现方式。 -如果浏览器控制台有报错以及脚本抛出异常也请将报错信息附上 +**BUG Description** -**环境信息** - - 操作系统: - - 浏览器及版本: - - Python版本: 使用 `python3 --version` 查看 - - PyWebIO版本: 使用 `python3 -c "import pywebio;print(pywebio.__version__)"` 查看 +A clear and concise description of what the bug is and how to reproduce it. + +If the browser console reports an error or the script throws an exception, please also report them. + +**Environment Information** + +- OS and Version: +- Browser and Version: +- Python Version: Use `python3 --version` to view +- PyWebIO Version: Use `python3 -c "import pywebio;print(pywebio.__version__)"` to view diff --git a/.github/ISSUE_TEMPLATE/bug_report_zh.md b/.github/ISSUE_TEMPLATE/bug_report_zh.md new file mode 100644 index 00000000..e9323053 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report_zh.md @@ -0,0 +1,20 @@ +--- +name: Bug报告 +about: 使用中文进行Bug报告 +title: '' +labels: bug, zh +assignees: '' + +--- +注: 对于PyWebIO使用咨询或对于其他人也可能有帮助的问题,请考虑移至 [Discussions](https://github.com/wang0618/PyWebIO/discussions) 进行发帖。 +另,可使用[PyWebIO QA Bot](https://github.com/pywebio/PyWebIO/discussions/596) 来让AI对你遇到的问题进行解答。 + +**BUG描述** +描述BUG表现以及复现方式。 +如果浏览器控制台有报错以及脚本抛出异常也请将报错信息附上 + +**环境信息** + - 操作系统及版本: + - 浏览器及版本: + - Python版本: 使用 `python3 --version` 查看 + - PyWebIO版本: 使用 `python3 -c "import pywebio;print(pywebio.__version__)"` 查看 diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..05714e7e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,11 @@ +--- +name: Feature Request +about: Create a feature request +title: '[Feature Request]' +labels: enhancement +assignees: '' + +--- + +Please move to [Discussions - Feature Request](https://github.com/wang0618/PyWebIO/discussions/categories/feature-request) for posting. + diff --git a/.github/workflows/build_dev.yml b/.github/workflows/build_dev.yml new file mode 100644 index 00000000..7e23d453 --- /dev/null +++ b/.github/workflows/build_dev.yml @@ -0,0 +1,56 @@ +name: build dev version +on: + push: + branches: + - dev + repository_dispatch: + +jobs: + build_dev: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@master + - name: Set up Python 3 + uses: actions/setup-python@v1 + with: + python-version: 3.12 + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: '22' + - name: Build frontend + working-directory: ./webiojs + run: | + npm install + npx gulp + cp dist/pywebio.min.* ../pywebio/html/js + - name: Build doc demos + run: | + pip3 install -e ".[all]" + pip3 install -r requirements.txt + cd docs && CODE_EXPORT_PATH=../demos/doc_demos make clean html + - name: Set dev version + run: python3 tools/build_dev_version.py + - name: Release dev version + run: | + git config --global user.email "$(git log -n 1 --pretty=format:%ae)" + git config --global user.name "${{ github.actor }}" + + # ref: https://stackoverflow.com/questions/8536732/can-i-hold-git-credentials-in-environment-variables + git config --global credential.helper '!f() { sleep 1; echo "username=${{ github.actor }}"; echo "password=${GH_TOKEN}"; }; f' + + git fetch --unshallow origin + git branch -D dev-release || true + git checkout -b dev-release + + rm .gitignore + git add pywebio/__version__.py + git add pywebio/html/js + git add demos/doc_demos + + git commit -m "Build at `date`" + git push -f -u origin dev-release + env: + # This token is provided by Actions, you do not need to create your own token + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 2c40be42..9a4ac56a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -2,29 +2,31 @@ name: Python lint -on: [push, pull_request] +on: [ push, pull_request ] jobs: lint: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.5, 3.6, 3.7, 3.8] + python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v2 + - uses: codespell-project/actions-codespell@master + with: + ignore_words_list: datas + skip: "*.js,*.po,i18n.ts,*.json" - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r requirements.txt + pip install -e ".[all]" - name: Lint with flake8 run: | pip install flake8 - # stop the build if there are Python syntax errors or undefined names - flake8 pywebio --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 pywebio --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics \ No newline at end of file + flake8 --ignore=E126,E127,E128,E131,E226,E402,E731,F401,F403,F405,W291,W292,W293 \ + --max-complexity=32 --max-line-length=525 --show-source --statistics . diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dcb27b4e..7c4083d6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,7 +1,8 @@ -name: Release to PyPi +name: Release +# https://github.community/t/how-to-run-github-actions-workflow-only-for-new-tags/16075/22 on: - create: + push: tags: - v* @@ -14,22 +15,42 @@ jobs: - name: Set up Python 3 uses: actions/setup-python@v1 with: - python-version: 3.7 - - name: Set up Node.js v13.5 - uses: actions/setup-node@v1 + python-version: 3.12 + - name: Set up Node.js + uses: actions/setup-node@v2 with: - node-version: 13.5 + node-version: '22' - name: Build frontend working-directory: ./webiojs run: | npm install - gulp + npx gulp cp dist/pywebio.min.js ../pywebio/html/js - - run: | - pip3 install twine + - name: PyPi Upload + run: | + pip3 install twine setuptools python3 setup.py sdist twine upload --username "__token__" --disable-progress-bar --verbose dist/* env: TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + - name: Set tag + run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + - name: Push asset + run: | + git config --global user.email "wang0.618@qq.com" + git config --global user.name "${{ github.actor }}" + git config --global credential.helper '!f() { sleep 1; echo "username=${{ github.actor }}"; echo "password=${GH_TOKEN}"; }; f' + git clone https://github.com/wang0618/pywebio-assets.git + rm -rf pywebio-assets/* + cp -r pywebio/html/* pywebio-assets + cd pywebio-assets + git add . + git commit -m "Build from https://github.com/wang0618/PyWebIO/commit/$GITHUB_SHA" || true + git tag $RELEASE_VERSION + + git push -u origin --tags + git push -u origin || true + env: + GH_TOKEN: ${{ secrets.ASSET_REPO_TOKEN }} diff --git a/.github/workflows/sync_repo.yml b/.github/workflows/sync_repo.yml deleted file mode 100644 index 722c835c..00000000 --- a/.github/workflows/sync_repo.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Sync with mirror repo -on: push -jobs: - sync: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@master - - name: Set up Python 3.7 - uses: actions/setup-python@v1 - with: - python-version: 3.7 - - name: Set up Node.js v13.5 - uses: actions/setup-node@v1 - with: - node-version: 13.5 - - name: Build frontend - working-directory: ./webiojs - run: | - npm install - DEV=1 gulp - cp dist/pywebio.min.* ../pywebio/html/js - - name: Build doc demos - run: | - pip3 install -e ".[all]" - cd docs && CODE_EXPORT_PATH=../demos/doc_domes make clean text - - name: Push - run: | - git fetch --unshallow origin - git remote add aliyun "https://code.aliyun.com/wang0618/pywebio.git" - git config credential.helper '!f() { sleep 1; echo "username=${ALIYUN_GIT_USER}"; echo "password=${ALIYUN_GIT_PASSWORD}"; }; f' - rm .gitignore - git add pywebio/html/js - git add demos/doc_domes - git config user.email "${ALIYUN_GIT_USER}" - git config user.name "${ALIYUN_GIT_USER}" - git commit --amend --no-edit - git push -f -u aliyun --tags || exit 0 - git push -f -u aliyun || exit 0 - env: - ALIYUN_GIT_USER: ${{ secrets.ALIYUN_GIT_USER }} - ALIYUN_GIT_PASSWORD: ${{ secrets.ALIYUN_GIT_PASSWORD }} \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 78ae873b..f9dfba49 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,41 +6,40 @@ jobs: steps: - name: Checkout uses: actions/checkout@master - - name: Set up Python 3.7 + - name: Set up Python 3 uses: actions/setup-python@v1 with: - python-version: 3.7 - - name: Set up Node.js v13.5 - uses: actions/setup-node@v1 + python-version: 3.12 + - name: Set up Node.js + uses: actions/setup-node@v2 with: - node-version: 13.5 + node-version: '22' - name: Build frontend working-directory: ./webiojs run: | npm install - gulp + npx gulp cp dist/pywebio.min.* ../pywebio/html/js - - name: Install Test JS deps - run: npm install -D @percy/agent - name: Install package - run: pip3 install -e ".[all]" + run: pip3 install ".[all]" - name: Install dev dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt - - name: Install env - run: echo "PERCY_TOKEN=${{ secrets.PERCY_TOKEN }}" >> $GITHUB_ENV + - run: npm install --save-dev @percy/cli - name: Percy Test - uses: percy/exec-action@v0.3.1 - with: - working-directory: ./test - command: "./run_all.sh" + run: npx percy exec -- bash ./run_all.sh + working-directory: ./test + env: + PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }} - name: Upload test output uses: actions/upload-artifact@v1 if: failure() with: name: test output path: test/output - - name: Upload Codecov Report - working-directory: ./test - run: bash <(curl -s https://codecov.io/bash) + - name: Upload test output + uses: codecov/codecov-action@v3 + with: + working-directory: ./test + verbose: true # optional (default = false) diff --git a/.gitignore b/.gitignore index 6ddca755..3f43dac6 100755 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ pywebio/html/js/pywebio.min.* /build /dist /*.egg-info -/docs/_build \ No newline at end of file +/docs/_build +/archive diff --git a/.readthedocs.yml b/.readthedocs.yml index 16d5d9fa..905a6cfa 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -5,6 +5,12 @@ # Required version: 2 +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-lts-latest + tools: + python: "3.9" + # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py @@ -14,11 +20,11 @@ sphinx: # configuration: mkdocs.yml # Optionally build your docs in additional formats such as PDF and ePub -formats: all +formats: + - pdf # Optionally set the version of Python and requirements required to build your docs python: - version: 3.7 install: - requirements: requirements.txt - method: pip diff --git a/MANIFEST.in b/MANIFEST.in index 121ee7ca..43262f9b 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,4 @@ recursive-include demos *.py prune docs -graft pywebio/html \ No newline at end of file +graft pywebio/html +graft pywebio/platform/tpl \ No newline at end of file diff --git a/Procfile b/Procfile deleted file mode 100644 index c32b8756..00000000 --- a/Procfile +++ /dev/null @@ -1 +0,0 @@ -web: python -m demos --port=$PORT \ No newline at end of file diff --git a/README-zh.md b/README-zh.md new file mode 100644 index 00000000..5ff7652e --- /dev/null +++ b/README-zh.md @@ -0,0 +1,155 @@ +

PyWebIO

+

+ Write interactive web app in script way. +

+

+ + Percy visual test + + + Code coverage + + + Jsdelivr hit count + + + Documentation Status + + + Package version + + + Python Version + +
+ + License + +
+ [Document] | [Demos] | [Playground] | [Why PyWebIO?] +

+ +[English](README.md) | [中文](README-zh.md) + +PyWebIO提供了一系列命令式的交互函数来在浏览器上获取用户输入和进行输出,将浏览器变成了一个“富文本终端”,可以用于构建简单的Web应用或基于浏览器的GUI应用。 +PyWebIO还可以方便地整合进现有的Web服务,让你不需要编写HTML和JS代码,就可以构建出具有良好可用性的应用。 + +

+ PyWebIO output demo + PyWebIO input demo +

+ + +功能特性: + +- 使用同步而不是基于回调的方式获取输入,代码编写逻辑更自然 +- 非声明式布局,布局方式简单高效 +- 代码侵入性小,旧脚本代码仅需修改输入输出逻辑便可改造为Web服务 +- 支持整合到现有的Web服务,目前支持与Flask、Django、Tornado、aiohttp、FastAPI框架集成 +- 同时支持基于线程的执行模型和基于协程的执行模型 +- 支持结合第三方库实现数据可视化 + +## Installation + +稳定版安装: + +```bash +pip3 install -U pywebio +``` + +开发版安装: +```bash +pip3 install -U https://github.com/pywebio/PyWebIO/archive/dev-release.zip +``` + +**系统要求**: PyWebIO要求 Python 版本在 3.5.2 及以上 + +## Quickstart + +**Hello, world** + +这是一个使用PyWebIO计算 [BMI指数](https://en.wikipedia.org/wiki/Body_mass_index) 的脚本: + +```python +from pywebio.input import input, FLOAT +from pywebio.output import put_text + +def bmi(): + height = input("请输入你的身高(cm):", type=FLOAT) + weight = input("请输入你的体重(kg):", type=FLOAT) + + BMI = weight / (height / 100) ** 2 + + top_status = [(14.9, '极瘦'), (18.4, '偏瘦'), + (22.9, '正常'), (27.5, '过重'), + (40.0, '肥胖'), (float('inf'), '非常肥胖')] + + for top, status in top_status: + if BMI <= top: + put_text('你的 BMI 值: %.1f,身体状态:%s' % (BMI, status)) + break + +if __name__ == '__main__': + bmi() +``` + + +如果没有使用PyWebIO,这只是一个非常简单的脚本,而通过使用PyWebIO提供的输入输出函数,你可以在浏览器中与代码进行交互 [[demo]](http://pywebio-demos.pywebio.online/bmi): + +

+ + PyWebIO demo + +

+ +**作为Web服务提供** + +上文BMI程序会在计算完毕后立刻退出,可以使用 [`pywebio.start_server()`](https://pywebio.readthedocs.io/zh_CN/latest/platform.html#pywebio.platform.tornado.start_server) 将 `bmi()` 函数作为Web服务提供: + +```python +from pywebio import start_server +from pywebio.input import input, FLOAT +from pywebio.output import put_text + +def bmi(): # bmi() 函数内容不变 + ... + +if __name__ == '__main__': + start_server(bmi, port=80) +``` + +**与现有Web框架整合** + +Tornado应用整合:仅需在现有的Tornado应用中添加一个 `RequestHandler` ,就可以将PyWebIO应用整合进Tornado Web服务中 + +```python +import tornado.ioloop +import tornado.web +from pywebio.platform.tornado import webio_handler + +class MainHandler(tornado.web.RequestHandler): + def get(self): + self.write("Hello, world") + +if __name__ == "__main__": + application = tornado.web.Application([ + (r"/", MainHandler), + (r"/bmi", webio_handler(bmi)), # bmi 即为上文计算BMI指数的函数 + ]) + application.listen(port=80, address='localhost') + tornado.ioloop.IOLoop.current().start() +``` + +在 `http://localhost/bmi` 页面上就可以计算BMI了。 + +与其他Web框架整合请见[文档](https://pywebio.readthedocs.io/zh_CN/latest/advanced.html#integration-with-web-framework) + +## Demos + + - [基本demo](http://pywebio-demos.pywebio.online/) : 包含PyWebIO基本输入输出演示和使用PyWebIO编写的小应用 + - [数据可视化demo](http://pywebio-charts.pywebio.online/) : 使用 bokeh、plotly、pyecharts 等库进行数据可视化 + +## Links + +* 使用手册和实现文档见 [pywebio.readthedocs.io](https://pywebio.readthedocs.io/zh_CN/latest/) +* [PyWebIO Playground](https://play.pywebio.online/): 在线编辑、运行和分享PyWebIO代码 diff --git a/README.md b/README.md index c6f3006c..ae0a1967 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

PyWebIO

+

PyWebIO

Write interactive web app in script way.

@@ -6,8 +6,11 @@ Percy visual test - - Code coverage + + Code coverage + + + Jsdelivr hit count Documentation Status @@ -16,24 +19,19 @@ Package version - Python Version + Python Version
- - Python code quality - - - Javascript code quality - License
- [Document] | [Demos] + [Document] | [Demos] | [Playground] | [Why PyWebIO?]

-PyWebIO提供了一系列命令式的交互函数来在浏览器上获取用户输入和进行输出,将浏览器变成了一个“富文本终端”,可以用于构建简单的Web应用或基于浏览器的GUI应用。 -PyWebIO还可以方便地整合进现有的Web服务,让你不需要编写HTML和JS代码,就可以构建出具有良好可用性的应用。 +[English](README.md) | [中文](README-zh.md) + +PyWebIO provides a series of imperative functions to obtain user input and output on the browser, turning the browser into a "rich text terminal", and can be used to build simple web applications or browser-based GUI applications without the need to have knowledge of HTML and JS. PyWebIO can also be easily integrated into existing Web services. PyWebIO is very suitable for quickly building applications that do not require complex UI.

PyWebIO output demo @@ -41,93 +39,91 @@ PyWebIO还可以方便地整合进现有的Web服务,让你不需要编写HTML

-功能特性: +Features: -- 使用同步而不是基于回调的方式获取输入,代码编写逻辑更自然 -- 非声明式布局,布局方式简单高效 -- 代码侵入性小,旧脚本代码仅需修改输入输出逻辑便可改造为Web服务 -- 支持整合到现有的Web服务,目前支持与Flask、Django、Tornado、aiohttp框架集成 -- 同时支持基于线程的执行模型和基于协程的执行模型 -- 支持结合第三方库实现数据可视化 +- Use synchronization instead of a callback-based method to get input +- Non-declarative layout, simple and efficient +- Less intrusive: old script code can be transformed into a Web application only by modifying the input and output operation +- Support integration into existing web services, currently supports Flask, Django, Tornado, aiohttp, FastAPI framework +- Support for ``asyncio`` and coroutine +- Support data visualization with third-party libraries, e.g., `plotly`, `bokeh`, `pyecharts`. -## Install +## Installation -稳定版安装: +Stable version: ```bash pip3 install -U pywebio ``` -开发版安装: +Development version: ```bash -pip3 install -U --force-reinstall https://code.aliyun.com/wang0618/pywebio/repository/archive.zip +pip3 install -U https://github.com/pywebio/PyWebIO/archive/dev-release.zip ``` -**系统要求**: PyWebIO要求 Python 版本在 3.5.2 及以上 +**Prerequisites**: PyWebIO requires Python 3.5.2 or newer -## Quick start +## Quickstart **Hello, world** -这是一个使用PyWebIO计算 [BMI指数](https://en.wikipedia.org/wiki/Body_mass_index>) 的脚本: +Here is a simple PyWebIO script to calculate the [BMI](https://en.wikipedia.org/wiki/Body_mass_index): ```python from pywebio.input import input, FLOAT from pywebio.output import put_text def bmi(): - height = input("请输入你的身高(cm):", type=FLOAT) - weight = input("请输入你的体重(kg):", type=FLOAT) + height = input("Your Height(cm):", type=FLOAT) + weight = input("Your Weight(kg):", type=FLOAT) BMI = weight / (height / 100) ** 2 - top_status = [(14.9, '极瘦'), (18.4, '偏瘦'), - (22.9, '正常'), (27.5, '过重'), - (40.0, '肥胖'), (float('inf'), '非常肥胖')] + top_status = [(14.9, 'Severely underweight'), (18.4, 'Underweight'), + (22.9, 'Normal'), (27.5, 'Overweight'), + (40.0, 'Moderately obese'), (float('inf'), 'Severely obese')] for top, status in top_status: if BMI <= top: - put_text('你的 BMI 值: %.1f,身体状态:%s' % (BMI, status)) + put_text('Your BMI: %.1f, category: %s' % (BMI, status)) break if __name__ == '__main__': bmi() ``` - -如果没有使用PyWebIO,这只是一个非常简单的脚本,而通过使用PyWebIO提供的输入输出函数,你可以在浏览器中与代码进行交互 [[demo]](http://pywebio-demos.demo.wangweimin.site/?pywebio_api=bmi): +This is just a very simple script if you ignore PyWebIO, but using the input and output functions provided by PyWebIO, you can interact with the code in the browser [[demo]](http://pywebio-demos.pywebio.online/bmi):

- + PyWebIO demo

-**作为Web服务提供** +**Serve as web service** -上文对使用PyWebIO进行改造的程序,运行模式还是脚本,程序计算完毕后立刻退出。可以使用 [`pywebio.start_server()`](https://pywebio.readthedocs.io/zh_CN/latest/platform.html#pywebio.platform.tornado.start_server) 将 `bmi()` 函数作为Web服务提供: +The above BMI program will exit immediately after the calculation, you can use [`pywebio.start_server()`](https://pywebio.readthedocs.io/en/latest/platform.html#pywebio.platform.tornado.start_server) to publish the `bmi()` function as a web application: ```python from pywebio import start_server from pywebio.input import input, FLOAT from pywebio.output import put_text -def bmi(): - ... # bmi() 函数内容不变 +def bmi(): # bmi() keep the same + ... if __name__ == '__main__': start_server(bmi, port=80) ``` -**与现有Web框架整合** +**Integration with web framework** -Tornado应用整合:仅需在现有的Tornado应用中加入加入两个 `RequestHandler` ,就可以将使用PyWebIO编写的函数整合进Tornado应用中 +To integrate a PyWebIO application into Tornado, all you need is to add a `RequestHandler` to the existing Tornado application: ```python import tornado.ioloop import tornado.web from pywebio.platform.tornado import webio_handler -from pywebio import STATIC_PATH class MainHandler(tornado.web.RequestHandler): def get(self): @@ -136,23 +132,22 @@ class MainHandler(tornado.web.RequestHandler): if __name__ == "__main__": application = tornado.web.Application([ (r"/", MainHandler), - (r"/bmi/io", webio_handler(bmi)), # bmi 即为上文计算BMI指数的函数 - (r"/bmi/(.*)", tornado.web.StaticFileHandler, {"path": STATIC_PATH, 'default_filename': 'index.html'}) + (r"/bmi", webio_handler(bmi)), # bmi is the same function as above ]) application.listen(port=80, address='localhost') tornado.ioloop.IOLoop.current().start() ``` -在 `http://localhost/bmi/` 页面上就可以计算BMI了。 +Now, you can open `http://localhost/bmi` for BMI calculation. -与其他Web框架整合请见[文档](https://pywebio.readthedocs.io/zh_CN/latest/guide.html#web) +For integration with other web frameworks, please refer to [document](https://pywebio.readthedocs.io/en/latest/advanced.html#integration-with-web-framework). ## Demos - - [基本demo](http://pywebio-demos.demo.wangweimin.site/) : 包含PyWebIO基本输入输出演示和使用PyWebIO编写的小应用 - - [数据可视化demo](http://pywebio-charts.demo.wangweimin.site/) : 使用 bokeh、plotly、pyecharts 等库进行数据可视化 - -## Document + - [Basic demo](http://pywebio-demos.pywebio.online/) : PyWebIO basic input and output demos and some small applications written using PyWebIO. + - [Data visualization demo](http://pywebio-charts.pywebio.online/) : Data visualization with the third-party libraries, e.g., `plotly`, `bokeh`, `pyecharts`. -使用手册和实现文档见 [https://pywebio.readthedocs.io](https://pywebio.readthedocs.io) +## Links +* Document [pywebio.readthedocs.io](https://pywebio.readthedocs.io) +* [PyWebIO Playground](https://play.pywebio.online/): Edit, Run, Share PyWebIO Code Online \ No newline at end of file diff --git a/demos/__init__.py b/demos/__init__.py index 55158619..e69de29b 100644 --- a/demos/__init__.py +++ b/demos/__init__.py @@ -1,6 +0,0 @@ -r""" -.. automodule:: demos.bmi -.. automodule:: demos.input_usage -.. automodule:: demos.output_usage -.. automodule:: demos.chat_room -""" \ No newline at end of file diff --git a/demos/__main__.py b/demos/__main__.py index e6f627aa..8cc5b292 100644 --- a/demos/__main__.py +++ b/demos/__main__.py @@ -1,83 +1,12 @@ -import tornado.ioloop -import tornado.web - -from demos.bmi import main as bmi -from demos.chat_room import main as chat_room -from demos.input_usage import main as input_usage -from demos.output_usage import main as output_usage -from demos.config import charts_demo_host -from demos.doc_demo import get_app as get_doc_demo_app -from demos.set_env_demo import main as set_env_demo - -from pywebio import STATIC_PATH -from pywebio.output import put_markdown, put_row, put_html, style -from pywebio.platform.tornado import webio_handler -from tornado.options import define, options - -index_md = r"""### 基本demo - - - [BMI计算](./?pywebio_api=bmi): 根据身高体重计算BMI指数 [源码](https://github.com/wang0618/PyWebIO/blob/dev/demos/bmi.py) - - [聊天室](./?pywebio_api=chat_room): 和当前所有在线的人聊天 [源码](https://github.com/wang0618/PyWebIO/blob/dev/demos/chat_room.py) - - [输入演示](./?pywebio_api=input_usage): 演示PyWebIO输入模块的用法 [源码](https://github.com/wang0618/PyWebIO/blob/dev/demos/input_usage.py) - - [输出演示](./?pywebio_api=output_usage): 演示PyWebIO输出模块的用法 [源码](https://github.com/wang0618/PyWebIO/blob/dev/demos/output_usage.py) - - 更多Demo请见[文档](https://pywebio.readthedocs.io)中示例代码的在线Demo - -### 数据可视化demo -PyWebIO还支持使用第三方库进行数据可视化 - - - 使用`bokeh`进行数据可视化 [**demos**]({charts_demo_host}/?app=bokeh) - - 使用`plotly`进行数据可视化 [**demos**]({charts_demo_host}/?app=plotly) - - 使用`pyecharts`创建基于Echarts的图表 [**demos**]({charts_demo_host}/?app=pyecharts) - - 使用`cutecharts.py`创建卡通风格图表 [**demos**]({charts_demo_host}/?app=cutecharts) - -**数据可视化demo截图** - - - bokeh demo - - - - plotly demo - - - - pyecharts demo - - - - cutecharts demo - - -### Links -* PyWebIO Github [github.com/wang0618/PyWebIO](https://github.com/wang0618/PyWebIO) -* 使用手册和实现文档见 [pywebio.readthedocs.io](https://pywebio.readthedocs.io) - -""".format(charts_demo_host=charts_demo_host) - - -def index(): - style(put_row([ - put_markdown('# PyWebIO demos'), - put_html('Star') - ], size='1fr auto'), 'align-items:center') - put_html('') - - put_markdown(index_md) +import argparse +from os import path +from pywebio.platform import path_deploy if __name__ == "__main__": - define("port", default=8080, help="run on the given port", type=int) - tornado.options.parse_command_line() + parser = argparse.ArgumentParser() + parser.add_argument("-p", "--port", default=8080, help="run on the given port", type=int) + args = parser.parse_args() - application = tornado.web.Application([ - (r"/io", webio_handler(index)), - (r"/bmi", webio_handler(bmi)), - (r"/chat_room", webio_handler(chat_room)), - (r"/input_usage", webio_handler(input_usage)), - (r"/output_usage", webio_handler(output_usage)), - (r"/doc_demo", webio_handler(get_doc_demo_app())), - (r"/set_env_demo", webio_handler(set_env_demo)), - (r"/(.*)", tornado.web.StaticFileHandler, {"path": STATIC_PATH, 'default_filename': 'index.html'}) - ]) - application.listen(port=options.port) - tornado.ioloop.IOLoop.current().start() + here_dir = path.dirname(path.abspath(__file__)) + path_deploy(here_dir, port=args.port) diff --git a/demos/bmi.py b/demos/bmi.py index 9ed57e20..df09bd40 100644 --- a/demos/bmi.py +++ b/demos/bmi.py @@ -1,29 +1,47 @@ -""" -BMI指数计算 -^^^^^^^^^^^ - -计算 `BMI指数 `_ 的简单应用 - -:demo_host:`Demo地址 ` `源码 `_ -""" from pywebio import start_server from pywebio.input import * from pywebio.output import * -from pywebio.session import set_env +from pywebio.session import info as session_info + + +def t(eng, chinese): + """return English or Chinese text according to the user's browser language""" + return chinese if 'zh' in session_info.user_language else eng def main(): - set_env(title="BMI Calculation") + """BMI Calculation + + Simple application for calculating Body Mass Index. + 计算BMI指数的简单应用 + """ - put_markdown("""# BMI指数 + put_markdown(t("""# Body Mass Index + + [Body mass index](https://en.wikipedia.org/wiki/Body_mass_index) (BMI) is a measure of body fat based on height and weight that applies to adult men and women. + + BMI Categories: + + | Category | BMI | + | -------------------- | ------------- | + | Severely underweight | BMI<14.9 | + | Underweight | 14.9≤BMI<18.4 | + | Normal | 18.4≤BMI<22.9 | + | Overweight | 22.9≤BMI<27.5 | + | Moderately obese | 27.5≤BMI<40 | + | Severely obese | BMI≥40 | + + ## BMI calculation + The source code of this application is [here](https://github.com/wang0618/PyWebIO/blob/dev/demos/bmi.py) + """, """# BMI指数 [`BMI指数`](https://baike.baidu.com/item/%E4%BD%93%E8%B4%A8%E6%8C%87%E6%95%B0/1455733)(Body Mass Index,BMI),是用体重千克数除以身高米数的平方得出的数字,是国际上常用的衡量人体胖瘦程度以及是否健康的一个标准。 成年人的BMI值处于以下阶段 | 体形分类 | BMI值范围 | - | -------- | --------- | - | 极瘦 | BMI<14.9 | + | ------ | -------- | + | 极瘦 | BMI<14.9 | | 偏瘦 | 14.9≤BMI<18.4 | | 正常 | 18.4≤BMI<22.9 | | 过重 | 22.9≤BMI<27.5 | @@ -33,22 +51,22 @@ def main(): ## BMI指数计算器 本程序的源代码[链接](https://github.com/wang0618/PyWebIO/blob/dev/demos/bmi.py) - """, strip_indent=4) + """)) - info = input_group('计算BMI:', [ - input("请输入你的身高(cm)", name="height", type=FLOAT), - input("请输入你的体重(kg)", name="weight", type=FLOAT), + info = input_group(t('BMI calculation', '计算BMI:'), [ + input(t("Your Height(cm)", "请输入你的身高(cm)"), name="height", type=FLOAT), + input(t("Your Weight(kg)", "请输入你的体重(kg)"), name="weight", type=FLOAT), ]) BMI = info['weight'] / (info['height'] / 100) ** 2 - top_status = [(14.9, '极瘦'), (18.4, '偏瘦'), - (22.9, '正常'), (27.5, '过重'), - (40.0, '肥胖'), (float('inf'), '非常肥胖')] + top_status = [(14.9, t('Severely underweight', '极瘦')), (18.4, t('Underweight', '偏瘦')), + (22.9, t('Normal', '正常')), (27.5, t('Overweight', '过重')), + (40.0, t('Moderately obese', '肥胖')), (float('inf'), t('Severely obese', '非常肥胖'))] for top, status in top_status: if BMI <= top: - put_markdown('你的 BMI 值: `%.1f`,身体状态:`%s`' % (BMI, status)) + put_markdown(t('Your BMI: `%.1f`, Category: `%s`', '你的 BMI 值: `%.1f`,身体状态: `%s`') % (BMI, status)) break diff --git a/demos/bokeh_app.py b/demos/bokeh_app.py index 64d13e2c..d06424a5 100644 --- a/demos/bokeh_app.py +++ b/demos/bokeh_app.py @@ -7,6 +7,7 @@ from pywebio import start_server from pywebio.output import * +from pywebio.session import info as session_info def bkapp(doc): @@ -34,15 +35,24 @@ def callback(attr, old, new): def main(): output_notebook(verbose=False, notebook_type='pywebio') - put_markdown("""# Bokeh Applications in PyWebIO - - [Bokeh Applications](https://docs.bokeh.org/en/latest/docs/user_guide/server.html) 支持向图表的添加按钮、输入框等交互组件,并向组件添加Python回调,从而创建可以与Python代码交互的可视化图表。 - - 在PyWebIO中,你也可以使用 `bokeh.io.show()` 来显示一个Bokeh App,和输出普通图表一样,只需要在会话开始时调用 `bokeh.io.output_notebook(notebook_type='pywebio')` 来设置PyWebIO输出环境。 - - 以下为一个 Bokeh App demo: - - """, lstrip=True) + if 'zh' in session_info.user_language: + put_markdown("""# Bokeh Applications in PyWebIO + [Bokeh Applications](https://docs.bokeh.org/en/latest/docs/user_guide/server.html) 支持向图表的添加按钮、输入框等交互组件,并向组件添加Python回调,从而创建可以与Python代码交互的可视化图表。 + + 在PyWebIO中,你也可以使用 `bokeh.io.show()` 来显示一个Bokeh App,和输出普通图表一样,只需要在会话开始时调用 `bokeh.io.output_notebook(notebook_type='pywebio')` 来设置PyWebIO输出环境。 + + 以下为一个 Bokeh App demo: + """) + else: + put_markdown("""# Bokeh Applications in PyWebIO + [Bokeh Applications](https://docs.bokeh.org/en/latest/docs/user_guide/server.html) can be built by starting the Bokeh server. The purpose of the Bokeh server is to make it easy for Python users to create interactive web applications that can connect front-end UI events to real, running Python code. + + In PyWebIO, you can also use bokeh.io.show() to display a Bokeh App. + + You can use `bokeh.io.output_notebook(notebook_type='pywebio')` in the PyWebIO session to setup Bokeh environment. Then you can use `bokeh.io.show()` to output a boken application. + + This is a demo of Bokeh App: + """) show(bkapp) diff --git a/demos/chat_room.py b/demos/chat_room.py index 349a2e20..afe6e9ac 100644 --- a/demos/chat_room.py +++ b/demos/chat_room.py @@ -1,39 +1,32 @@ -""" -聊天室 -^^^^^^^^^^^ -和当前所有在线的人聊天 - -:demo_host:`Demo地址 ` `源码 `_ - -* 使用基于协程的会话 -* 使用 `run_async() ` 启动后台协程 -""" import asyncio -from pywebio import start_server, run_async +from pywebio import start_server from pywebio.input import * from pywebio.output import * -from pywebio.session import defer_call, set_env, run_js +from pywebio.session import defer_call, info as session_info, run_async -# 最大消息记录保存 MAX_MESSAGES_CNT = 10 ** 4 -chat_msgs = [] # 聊天记录 (name, msg) -online_users = set() # 在线用户 +chat_msgs = [] # The chat message history. The item is (name, message content) +online_users = set() + + +def t(eng, chinese): + """return English or Chinese text according to the user's browser language""" + return chinese if 'zh' in session_info.user_language else eng -async def refresh_msg(my_name, msg_box): - """刷新聊天消息""" +async def refresh_msg(my_name): + """send new message to current session""" global chat_msgs last_idx = len(chat_msgs) while True: await asyncio.sleep(0.5) for m in chat_msgs[last_idx:]: - if m[0] != my_name: # 仅刷新其他人的新信息 - msg_box.append(put_markdown('`%s`: %s' % m)) - run_js('$("#pywebio-scope-msg-container>div").animate({ scrollTop: $("#pywebio-scope-msg-container>div").prop("scrollHeight")}, 1000)') # hack: to scroll bottom + if m[0] != my_name: # only refresh message that not sent by current user + put_markdown('`%s`: %s' % m, sanitize=True, scope='msg-box') - # 清理聊天记录 + # remove expired message if len(chat_msgs) > MAX_MESSAGES_CNT: chat_msgs = chat_msgs[len(chat_msgs) // 2:] @@ -41,43 +34,42 @@ async def refresh_msg(my_name, msg_box): async def main(): - global chat_msgs + """PyWebIO chat room - set_env(title="PyWebIO Chat Room") + You can chat with everyone currently online. + """ + global chat_msgs - put_markdown("##PyWebIO聊天室\n欢迎来到聊天室,你可以和当前所有在线的人聊天。你可以在浏览器的多个标签页中打开本页面来测试聊天效果。" - "本应用使用不到80行代码实现,源代码[链接](https://github.com/wang0618/PyWebIO/blob/dev/demos/chat_room.py)", lstrip=True) + put_markdown(t("## PyWebIO chat room\nWelcome to the chat room, you can chat with all the people currently online. You can open this page in multiple tabs of your browser to simulate a multi-user environment. This application uses less than 90 lines of code, the source code is [here](https://github.com/wang0618/PyWebIO/blob/dev/demos/chat_room.py)", "## PyWebIO聊天室\n欢迎来到聊天室,你可以和当前所有在线的人聊天。你可以在浏览器的多个标签页中打开本页面来测试聊天效果。本应用使用不到90行代码实现,源代码[链接](https://github.com/wang0618/PyWebIO/blob/dev/demos/chat_room.py)")) - msg_box = output() - with use_scope('msg-container'): - style(put_scrollable(msg_box, max_height=300), 'height:300px') - nickname = await input("请输入你的昵称", required=True, validate=lambda n: '昵称已被使用' if n in online_users or n == '📢' else None) + put_scrollable(put_scope('msg-box'), height=300, keep_bottom=True) + nickname = await input(t("Your nickname", "请输入你的昵称"), required=True, validate=lambda n: t('This name is already been used', '昵称已被使用') if n in online_users or n == '📢' else None) online_users.add(nickname) - chat_msgs.append(('📢', '`%s`加入聊天室. 当前在线人数 %s' % (nickname, len(online_users)))) - msg_box.append(put_markdown('`📢`: `%s`加入聊天室. 当前在线人数 %s' % (nickname, len(online_users)))) + chat_msgs.append(('📢', '`%s` joins the room. %s users currently online' % (nickname, len(online_users)))) + put_markdown('`📢`: `%s` join the room. %s users currently online' % (nickname, len(online_users)), sanitize=True, scope='msg-box') @defer_call def on_close(): online_users.remove(nickname) - chat_msgs.append(('📢', '`%s`退出聊天室. 当前在线人数 %s' % (nickname, len(online_users)))) + chat_msgs.append(('📢', '`%s` leaves the room. %s users currently online' % (nickname, len(online_users)))) - refresh_task = run_async(refresh_msg(nickname, msg_box)) + refresh_task = run_async(refresh_msg(nickname)) while True: - data = await input_group('发送消息', [ - input(name='msg', help_text='消息内容支持Markdown 语法', required=True), - actions(name='cmd', buttons=['发送', {'label': '退出', 'type': 'cancel'}]) - ]) + data = await input_group(t('Send message', '发送消息'), [ + input(name='msg', help_text=t('Message content supports inline Markdown syntax', '消息内容支持行内Markdown语法')), + actions(name='cmd', buttons=[t('Send', '发送'), t('Multiline Input', '多行输入'), {'label': t('Exit', '退出'), 'type': 'cancel'}]) + ], validate=lambda d: ('msg', 'Message content cannot be empty') if d['cmd'] == t('Send', '发送') and not d['msg'] else None) if data is None: break - - msg_box.append(put_markdown('`%s`: %s' % (nickname, data['msg']))) - run_js('$("#pywebio-scope-msg-container>div").animate({ scrollTop: $("#pywebio-scope-msg-container>div").prop("scrollHeight")}, 1000)') # hack: to scroll bottom + if data['cmd'] == t('Multiline Input', '多行输入'): + data['msg'] = '\n' + await textarea('Message content', help_text=t('Message content supports Markdown syntax', '消息内容支持Markdown语法')) + put_markdown('`%s`: %s' % (nickname, data['msg']), sanitize=True, scope='msg-box') chat_msgs.append((nickname, data['msg'])) refresh_task.close() - toast("你已经退出聊天室") + toast("You have left the chat room") if __name__ == '__main__': diff --git a/demos/chatgpt.py b/demos/chatgpt.py new file mode 100644 index 00000000..d25c3b6c --- /dev/null +++ b/demos/chatgpt.py @@ -0,0 +1,211 @@ +import json +import time +from typing import Dict, List + +from openai import OpenAI, Stream +from openai.types.chat import ChatCompletionChunk + +import pywebio_battery +from pywebio.input import * +from pywebio.output import * +from pywebio.pin import * +from pywebio.session import set_env, download + + +class ChatGPTStreamResponse: + """ + A wrapper to Stream[ChatCompletionChunk], add a `result()` method to get the final result. + """ + def __init__(self, response: Stream[ChatCompletionChunk]): + self.response = response + self.yielded = [] + self.finish_reason = None + + def __next__(self): + chunk = next(self.response) + self.finish_reason = chunk.choices[0].finish_reason + delta = chunk.choices[0].delta + if delta.content: + self.yielded.append(delta.content) + return delta.content + + def __iter__(self): + return self + + def result(self): + return ''.join(self.yielded) + + +class ChatGPT: + + def __init__(self, messages: List[Dict] = None, model: str = "gpt-3.5-turbo", client: OpenAI = None, **model_kwargs): + """ + Create a chatgpt client + + :param messages: A list of messages comprising the conversation so far. + Each message is a dict with keys "role" and "content". + See: https://platform.openai.com/docs/api-reference/chat/create#chat/create-messages + :param model: The model to use. + :param OpenAI client: The openai client to use. If not provided, a new client will be created. + :param model_kwargs: Other parameters to pass to model, + See https://platform.openai.com/docs/api-reference/chat + """ + self._client = client or OpenAI() + self._messages = list(messages or []) + self.model_kwargs = dict(model=model, **model_kwargs) + + self.pending_stream_reply: ChatGPTStreamResponse = None + self.latest_nonstream_finish_reason = None + + def set_model(self, model: str): + """Set the model to use""" + self.model_kwargs['model'] = model + + def _ask(self, message: str, stream=True, **model_kwargs): + if self.pending_stream_reply: + self._messages.append({"role": "assistant", "content": self.pending_stream_reply.result()}) + self.pending_stream_reply = None + + self._messages.append({"role": "user", "content": message}) + resp = self._client.chat.completions.create( + **self.model_kwargs, + **model_kwargs, + messages=self._messages, + stream=stream, + ) + return resp + + def ask(self, message: str, **model_kwargs) -> str: + """ + Send a message to chatgpt and get the reply in string + + :param message: The message to send + :param model_kwargs: Other parameters to pass to openai.ChatCompletion.create() + :return: The reply from chatgpt + """ + resp = self._ask(message, stream=False, **model_kwargs) + reply = resp['choices'][0] + reply_content = reply['message']['content'] + self._messages.append({"role": "assistant", "content": reply_content}) + self.latest_nonstream_finish_reason = reply['finish_reason'] + + return reply_content + + def ask_stream(self, message: str, **model_kwargs) -> ChatGPTStreamResponse: + """ + Send a message to chatgpt and get the reply in stream + + :param message: The message to send + :param model_kwargs: Other parameters to pass to openai.ChatCompletion.create() + :return: A iterator that yields the reply from chatgpt. + The iterator will be exhausted when the reply is complete. + """ + resp = self._ask(message, stream=True, **model_kwargs) + self.pending_stream_reply = ChatGPTStreamResponse(resp) + return self.pending_stream_reply + + def latest_finish_reason(self) -> str: + """The finish reason for the latest reply of chatgpt. + + The possible values for finish_reason are: + 'stop': API returned complete model output + 'length': Incomplete model output due to max_tokens parameter or token limit + 'content_filter': Omitted content due to a flag from our content filters + 'null': API response still in progress or incomplete + + See: https://platform.openai.com/docs/guides/chat/response-format + """ + if self.pending_stream_reply: + return self.pending_stream_reply.finish_reason + return self.latest_nonstream_finish_reason + + def messages(self) -> List[Dict]: + """Get all messages of the conversation """ + if self.pending_stream_reply: + self._messages.append({"role": "assistant", "content": self.pending_stream_reply.result()}) + self.pending_stream_reply = None + + return self._messages + + +def get_openai_config(): + openai_config = json.loads(pywebio_battery.get_localstorage('openai_config') or '{}') + if not openai_config: + openai_config = input_group('OpenAI API Config', [ + input('API Key', name='api_key', type=TEXT, required=True, + help_text='Get your API key from https://platform.openai.com/account/api-keys'), + input('API Server', name='api_base', type=TEXT, value='https://api.openai.com', required=True), + ]) + openai_config['api_base'] = openai_config['api_base'].removesuffix('/v1').strip('/') + '/v1' + pywebio_battery.set_localstorage('openai_config', json.dumps(openai_config)) + + put_button('Reset OpenAI API Key', reset_openai_config, link_style=True) + return openai_config + + +def reset_openai_config(): + pywebio_battery.set_localstorage('openai_config', json.dumps(None)) + toast("Please refresh the page to take effect") + + +def main(): + """""" + set_env(input_panel_fixed=False, output_animation=False) + put_markdown(""" + # ChatGPT + A ChatGPT client implemented with PyWebIO. [Source Code](https://github.com/pywebio/PyWebIO/blob/dev/demos/chatgpt.py) + TIPS: refresh page to open a new chat. + """) + put_select('model', ['gpt-3.5-turbo', 'gpt-4'], label='Model') + + openai_config = get_openai_config() + client = OpenAI(api_key=openai_config['api_key'], base_url=openai_config['api_base']) + + bot = ChatGPT(client=client, model=pin.model) + pin_on_change('model', lambda v: bot.set_model(v)) + while True: + form = input_group('', [ + input(name='msg', placeholder='Ask ChatGPT'), + actions(name='cmd', buttons=['Send', 'Multi-line Input', 'Save Chat']) + ]) + if form['cmd'] == 'Multi-line Input': + form['msg'] = textarea(value=form['msg']) + elif form['cmd'] == 'Save Chat': + messages = [ + msg['content'] if msg['role'] == 'user' else f"> {msg['content']}" + for msg in bot.messages() + ] + download(f"chatgpt_{time.strftime('%Y%m%d%H%M%S')}.md", + '\n\n'.join(messages).encode('utf8')) + continue + + user_msg = form['msg'] + if not user_msg: + continue + + put_info(put_text(user_msg, inline=True)) + + with use_scope(f'reply-{int(time.time())}'): + put_loading('grow', 'info') + try: + reply_chunks = bot.ask_stream(user_msg) + except Exception as e: + popup('ChatGPT Error', put_error(e)) + continue + finally: + clear() # clear loading + for chunk in reply_chunks: + put_text(chunk, inline=True) + clear() # clear above text + put_markdown(reply_chunks.result()) + + if bot.latest_finish_reason() == 'length': + put_error('Incomplete model output due to max_tokens parameter or token limit.') + elif bot.latest_finish_reason() == 'content_filter': + put_warning("Omitted content due to a flag from OpanAI's content filters.") + + +if __name__ == '__main__': + from pywebio import start_server + + start_server(main, port=8080, debug=True, cdn=False) diff --git a/demos/config.py b/demos/config.py index 0121da41..33d7f187 100644 --- a/demos/config.py +++ b/demos/config.py @@ -1,5 +1,5 @@ -# demos 模块的部署地址 -demo_host = 'http://pywebio-demos.demo.wangweimin.site' +# The deployment address of the demos module +demo_host = 'http://pywebio-demos.pywebio.online' -# https://github.com/wang0618/pywebio-chart-gallery 的部署地址 -charts_demo_host = 'http://pywebio-charts.demo.wangweimin.site' +# The deployment address of https://github.com/wang0618/pywebio-chart-gallery repo +charts_demo_host = 'http://pywebio-charts.pywebio.online' diff --git a/demos/doc_demo.py b/demos/doc_demo.py index 9ddf9e73..281d201a 100644 --- a/demos/doc_demo.py +++ b/demos/doc_demo.py @@ -1,15 +1,45 @@ """ -文档中示例代码在线运行 -^^^^^^^^^^^^^^^^ +Run the example code in the documentation online """ +import base64 + +from functools import partial +from os import path, listdir from pywebio import start_server +from pywebio.platform import config +from pywebio.session import local as session_local, info as session_info +import pywebio_battery + +########################################## +# Pre-import modules for demo +import time # lgtm [py/unused-import] from pywebio.input import * from pywebio.output import * from pywebio.session import * -from os import path, listdir -from functools import partial +from pywebio.pin import * +from pywebio_battery import * + +########################################## here_dir = path.dirname(path.abspath(__file__)) +playground_host = "https://play.pywebio.online" + + +def t(eng, chinese): + """return English or Chinese text according to the user's browser language""" + return chinese if 'zh' in session_info.user_language else eng + + +def playground(code): + pre_import = PRE_IMPORT + battery_apis = pywebio_battery.__all__ + if 'pywebio_battery import' not in code and any(api in code for api in battery_apis): + pre_import += 'from pywebio_battery import *\n' + + code = f"{pre_import}\n{code}" + encode = base64.b64encode(code.encode('utf8')).decode('utf8') + url = f"{playground_host}/#{encode}" + run_js('window.open(url)', url=url) def gen_snippets(code): @@ -21,44 +51,61 @@ def gen_snippets(code): yield p.strip('\n') -def run_code(code, scope, locals): +def run_code(code, scope): with use_scope(scope): try: - exec(code, globals(), locals) + """ + Remember that at module level, globals and locals are the same dictionary. + If exec gets two separate objects as globals and locals, + the code will be executed as if it were embedded in a class definition. + https://docs.python.org/3/library/functions.html#exec + """ + exec(code, session_local.globals) except Exception as e: - toast('代码产生异常:"%s:%s"' % (type(e).__name__, e), color='error') + toast('Exception occurred: "%s:%s"' % (type(e).__name__, e), color='error') -IMPORT_CODE = """from pywebio.input import * +PRE_IMPORT = """from pywebio.input import * from pywebio.output import * from pywebio.session import * +from pywebio.pin import * +from pywebio import start_server +""" + +APP_TPL = f"""{PRE_IMPORT} +def main(): + %s + +start_server(main, port=8080, debug=True) +""" +CLIPBOARD_SETUP = """ +window.writeText = function(text) { + const input = document.createElement('textarea'); + input.style.opacity = 0; + input.style.position = 'absolute'; + input.style.left = '-100000px'; + document.body.appendChild(input); + + input.value = text; + input.select(); + input.setSelectionRange(0, text.length); + document.execCommand('copy'); + document.body.removeChild(input); + return true; +} """ def copytoclipboard(code): + code = APP_TPL % code.replace('\n', '\n ') run_js("writeText(text)", text=code) - toast('已复制') + toast('The code has been copied to the clipboard') def handle_code(code, title): - run_js(""" - window.writeText = function(text) { - const input = document.createElement('INPUT'); - input.style.opacity = 0; - input.style.position = 'absolute'; - input.style.left = '-100000px'; - document.body.appendChild(input); - - input.value = text; - input.select(); - input.setSelectionRange(0, text.length); - document.execCommand('copy'); - document.body.removeChild(input); - return true; - } - """) - locals = {} + run_js(CLIPBOARD_SETUP) + session_local.globals = dict(globals()) if title: put_markdown('## %s' % title) @@ -66,43 +113,40 @@ def handle_code(code, title): with use_scope() as scope: put_code(p, 'python') - put_buttons(['运行', '复制代码'], onclick=[ - partial(run_code, code=p, scope=scope, locals=locals), - partial(copytoclipboard, code=IMPORT_CODE + p) - ]) + put_buttons( + [t('Run', '运行'), + t("Edit", '编辑'), + t("Copy to clipboard", '复制代码')], + onclick=[ + partial(run_code, code=p, scope=scope), + partial(playground, code=p), + partial(copytoclipboard, code=p) + ] + ) put_markdown('----') - hold() - def get_app(): + """PyWebIO demos from document + + Run the demos from the document online. + """ app = {} try: - demos = listdir(path.join(here_dir, 'doc_domes')) + demos = listdir(path.join(here_dir, 'doc_demos')) except Exception: demos = [] - demo_infos = [] for name in demos: - code = open(path.join(here_dir, 'doc_domes', name)).read() + code = open(path.join(here_dir, 'doc_demos', name)).read() title, code = code.split('\n\n', 1) app[name] = partial(handle_code, code=code, title=title) - demo_infos.append([name, title]) - - index_html = "
    " - for name, title in demo_infos: - index_html += '''
  • {name}: {desc}
  • \n'''.format( - name=name, desc=title) - index_html += "
" - - def index(): - put_markdown('# PyWebIO Document Code Example Index') - put_html(index_html) + app[name] = config(title=name, description=title)(app[name]) - app['index'] = index return app +main = get_app() if __name__ == '__main__': - start_server(get_app(), debug=True, port=8080) + start_server(main, debug=True, port=8080) diff --git a/demos/gomoku_game.py b/demos/gomoku_game.py new file mode 100644 index 00000000..0086d825 --- /dev/null +++ b/demos/gomoku_game.py @@ -0,0 +1,94 @@ +import time + +from pywebio import session, start_server +from pywebio.output import * + +goboard_size = 15 +# -1 -> none, 0 -> black, 1 -> white +goboard = [ + [-1] * goboard_size + for _ in range(goboard_size) +] + + +def winner(): # return winner piece, return None if no winner + for x in range(2, goboard_size - 2): + for y in range(2, goboard_size - 2): + # check if (x,y) is the win center + if goboard[x][y] != -1 and any([ + all(goboard[x][y] == goboard[m][n] for m, n in [(x - 2, y), (x - 1, y), (x + 1, y), (x + 2, y)]), + all(goboard[x][y] == goboard[m][n] for m, n in [(x, y - 2), (x, y - 1), (x, y + 1), (x, y + 2)]), + all(goboard[x][y] == goboard[m][n] for m, n in [(x - 2, y - 2), (x - 1, y - 1), (x + 1, y + 1), (x + 2, y + 2)]), + all(goboard[x][y] == goboard[m][n] for m, n in [(x - 2, y + 2), (x - 1, y + 1), (x + 1, y - 1), (x + 2, y - 2)]), + ]): + return ['⚫', '⚪'][goboard[x][y]] + + +session_id = 0 # auto incremented id for each session +current_turn = 0 # 0 for black, 1 for white +player_count = [0, 0] # count of player for two roles + + +def main(): + """Online Shared Gomoku Game + + A web based Gomoku (AKA GoBang, Five in a Row) game made with PyWebIO under 100 lines of Python code.""" + global session_id, current_turn, goboard + if winner(): # The current game is over, reset game + goboard = [[-1] * goboard_size for _ in range(goboard_size)] + current_turn = 0 + + my_turn = session_id % 2 + my_chess = ['⚫', '⚪'][my_turn] + session_id += 1 + player_count[my_turn] += 1 + + @session.defer_call + def player_exit(): + player_count[my_turn] -= 1 + + session.set_env(output_animation=False) + put_html("""""") # Custom styles to make the board more beautiful + + put_markdown(f"""# Online Shared Gomoku Game + All online players are assigned to two groups (black and white) and share this game. You can open this page in multiple tabs of your browser to simulate multiple users. This application uses less than 100 lines of code, the source code is [here](https://github.com/wang0618/PyWebIO/blob/dev/demos/gomoku_game.py) + Currently online player: {player_count[0]} for ⚫, {player_count[1]} for ⚪. Your role is {my_chess}. + """) + + def set_stone(pos): + global current_turn + if current_turn != my_turn: + toast("It's not your turn!!", color='error') + return + x, y = pos + goboard[x][y] = my_turn + current_turn = (current_turn + 1) % 2 + + @use_scope('goboard', clear=True) + def show_goboard(): + table = [ + [ + put_buttons([dict(label=' ', value=(x, y), color='light')], onclick=set_stone) if cell == -1 else [' ⚫', ' ⚪'][cell] + for y, cell in enumerate(row) + ] + for x, row in enumerate(goboard) + ] + put_table(table) + + show_goboard() + while not winner(): + with use_scope('msg', clear=True): + current_turn_copy = current_turn + if current_turn_copy == my_turn: + put_text("It's your turn!") + else: + put_row([put_text("Your opponent's turn, waiting... "), put_loading().style('width:1.5em; height:1.5em')], size='auto 1fr') + while current_turn == current_turn_copy and not session.get_current_session().closed(): # wait for next move + time.sleep(0.2) + show_goboard() + with use_scope('msg', clear=True): + put_text('Game over. The winner is %s!\nRefresh page to start a new round.' % winner()) + + +if __name__ == '__main__': + start_server(main, debug=True, port=8080) diff --git a/demos/index.py b/demos/index.py new file mode 100644 index 00000000..856d9fc4 --- /dev/null +++ b/demos/index.py @@ -0,0 +1,119 @@ +from config import charts_demo_host + +from pywebio.output import put_markdown, put_row, put_html +from pywebio.session import info as session_info + +index_md = r"""### Basic demo +The source code of the demos can be found [here](https://github.com/pywebio/PyWebIO/tree/dev/demos). + + - [BMI calculation](./bmi): Calculating Body Mass Index based on height and weight + - [Online chat room](./chat_room): Chat with everyone currently online (using less than 90 lines of code) + - [Markdown live preview](./markdown_previewer): The online markdown editor with live preview (using less than 40 lines of code) + - [Online Gomoku game](./gomoku_game): An online shared Gomoku game (using less than 100 lines of code) + - [Input demo](./input_usage): Demonstrate the usage of PyWebIO input module + - [Output demo](./output_usage): Demonstrate the usage of PyWebIO output module + - [ChatGPT](./chatgpt): A ChatGPT client implemented with PyWebIO + - [Wordle](./wordle): A wordle-like game implemented with PyWebIO + - [Theme preview](./theme): Demo page with various themes supported by PyWebIO + +### Data visualization demo +PyWebIO supports for data visualization with the third-party libraries. + + - Use `bokeh` for data visualization [**demos**]({charts_demo_host}/?app=bokeh) + - Use `plotly` for data visualization [**demos**]({charts_demo_host}/?app=plotly) + - Use `pyecharts` to create Echarts-based charts in Python [**demos**]({charts_demo_host}/?app=pyecharts) + - Use `pyg2plot` to create G2Plot-based charts in Python [**demos**]({charts_demo_host}/?app=pyg2plot) + - Use `cutecharts.py` to create hand drawing style charts [**demos**]({charts_demo_host}/?app=cutecharts) + +**Screenshots** + + + bokeh demo + + + + plotly demo + + + + pyecharts demo + + + + cutecharts demo + + +### Links +* PyWebIO Github [github.com/wang0618/PyWebIO](https://github.com/wang0618/PyWebIO) +* Document [pywebio.readthedocs.io](https://pywebio.readthedocs.io) +* [PyWebIO Playground](https://play.pywebio.online/): Edit, Run, Share PyWebIO Code Online + +""".format(charts_demo_host=charts_demo_host) + +index_md_zh = r"""### 基本demo + +Demo源码[链接](https://github.com/pywebio/PyWebIO/tree/dev/demos) + + - [BMI计算](./bmi): 根据身高体重计算BMI指数 + - [聊天室](./chat_room): 和当前所有在线的人聊天 (不到90行代码实现) + - [Markdown实时预览](./markdown_previewer): 可以实时预览的在线Markdown编辑器 (不到40行代码实现) + - [在线五子棋游戏](./gomoku_game): 多人协作对战的五子棋游戏 (不到100行代码实现) + - [输入演示](./input_usage): 演示PyWebIO输入模块的用法 + - [输出演示](./output_usage): 演示PyWebIO输出模块的用法 + - [ChatGPT](./chatgpt): 使用PyWebIO编写的ChatGPT客户端 + - [Wordle](./wordle): 使用PyWebIO编写的猜字游戏(wordle) + - [主题预览](./theme): 展示PyWebIO支持的各种主题 + - 更多Demo请见[文档](https://pywebio.readthedocs.io)中示例代码的在线Demo + +### 数据可视化demo +PyWebIO还支持使用第三方库进行数据可视化 + + - 使用`bokeh`进行数据可视化 [**demos**]({charts_demo_host}/?app=bokeh) + - 使用`plotly`进行数据可视化 [**demos**]({charts_demo_host}/?app=plotly) + - 使用`pyecharts`创建基于Echarts的图表 [**demos**]({charts_demo_host}/?app=pyecharts) + - 使用`pyg2plot`创建基于G2Plot的图表 [**demos**]({charts_demo_host}/?app=pyg2plot) + - 使用`cutecharts.py`创建卡通风格图表 [**demos**]({charts_demo_host}/?app=cutecharts) + +**数据可视化demo截图** + + + bokeh demo + + + + plotly demo + + + + pyecharts demo + + + + cutecharts demo + + +### Links +* PyWebIO Github [github.com/wang0618/PyWebIO](https://github.com/wang0618/PyWebIO) +* 使用手册和实现文档见 [pywebio.readthedocs.io](https://pywebio.readthedocs.io/zh_CN/latest/) +* [PyWebIO Playground](https://play.pywebio.online/): 在线编辑、运行和分享PyWebIO代码 + +""".format(charts_demo_host=charts_demo_host) + + +def main(): + """PyWebIO demos + + Basic demo and data visualization demo of PyWebIO. + PyWebIO的基本demo和数据可视化demo + """ + put_row([ + put_markdown('# PyWebIO demos'), + put_html( + 'Star') + ], size='1fr auto').style('align-items:center') + put_html('') + + if 'zh' in session_info.user_language: + put_markdown(index_md_zh) + else: + put_markdown(index_md) diff --git a/demos/input_usage.py b/demos/input_usage.py index 6bd83289..1a06475a 100644 --- a/demos/input_usage.py +++ b/demos/input_usage.py @@ -1,20 +1,41 @@ -""" -输入演示 -^^^^^^^^^^^ -演示PyWebIO支持的各种输入形式 +import json -:demo_host:`Demo地址 ` `源码 `_ -""" from pywebio import start_server from pywebio.input import * from pywebio.output import * -from pywebio.session import set_env +from pywebio.session import set_env, info as session_info + + +def t(eng, chinese): + """return English or Chinese text according to the user's browser language""" + return chinese if 'zh' in session_info.user_language else eng def main(): - set_env(title="PyWebIO输入演示", auto_scroll_bottom=True) + """PyWebIO Input Usage - put_markdown("""# PyWebIO 输入演示 + Demonstrate various input usage supported by PyWebIO. + 演示PyWebIO输入模块的使用 + """ + set_env(auto_scroll_bottom=True) + + put_markdown(t("""# PyWebIO Input Example + + You can get the source code of this demo in [here](https://github.com/wang0618/PyWebIO/blob/dev/demos/input_usage.py) + + This demo only introduces part of the functions of the PyWebIO input module. For the complete features, please refer to the [User Guide](https://pywebio.readthedocs.io/zh_CN/latest/guide.html). + + The input functions are all defined in the `pywebio.input` module and can be imported using `from pywebio.input import *`. + + ### Basic input + Here are some basic types of input. + + #### Text input + ```python + name = input("What's your name?") + ``` + """, + """# PyWebIO 输入演示 在[这里](https://github.com/wang0618/PyWebIO/blob/dev/demos/input_usage.py)可以获取本Demo的源码。 @@ -29,13 +50,34 @@ def main(): ```python name = input("What's your name?") ``` - """, lstrip=True) - put_text("这样一行代码的效果如下:",) + """)) + put_text(t("The results of the above example are as follows:", "这样一行代码的效果如下:")) name = input("What's your name?") put_markdown("`name = %r`" % name) # 其他类型的输入 - put_markdown("""PyWebIO的输入函数是同步的,在表单被提交之前,输入函数不会返回。 + put_markdown(t(""" + PyWebIO’s input functions is blocking and will not return until the form is successfully submitted. + #### Other types of input + Here are some other types of input functions: + ```python + # Password input + password = input("Input password", type=PASSWORD) + + # Drop-down selection + gift = select('Which gift you want?', ['keyboard', 'ipad']) + + # CheckBox + agree = checkbox("User Term", options=['I agree to terms and conditions']) + + # Text Area + text = textarea('Text Area', rows=3, placeholder='Some text') + + # File Upload + img = file_upload("Select a image:", accept="image/*") + ``` + """, """ + PyWebIO的输入函数是同步的,在表单被提交之前,输入函数不会返回。 #### 其他类型的输入: ```python # 密码输入 @@ -53,35 +95,45 @@ def main(): # 文件上传 img = file_upload("Select a image:", accept="image/*") ``` - """, lstrip=True) + """)) password = input("Input password", type=PASSWORD) put_markdown("`password = %r`" % password) gift = select('Which gift you want?', ['keyboard', 'ipad']) put_markdown("`gift = %r`" % gift) - agree = checkbox("用户协议", options=['I agree to terms and conditions']) + agree = checkbox(t("User Term", "用户协议"), options=['I agree to terms and conditions']) put_markdown("`agree = %r`" % agree) text = textarea('Text Area', rows=3, placeholder='Some text') put_markdown("`text = %r`" % text) - img = file_upload("Select a image:", accept="image/*", help_text='可以直接选择"提交"') - put_markdown("`img = %r`" % img) + img = file_upload("Select a image:", accept="image/*", help_text=t('You can just click "Submit" button', '可以直接选择"提交"')) + if img is None: + put_markdown("`img = %r`" % img) + else: + img['content'] = '...' + img.pop('dataurl', None) + put_code(json.dumps(img, indent=4, ensure_ascii=False).replace('"..."', '...'), 'json') # 输入选项 - put_markdown("""#### 输入选项 + put_markdown(t("""#### Parameter of input functions + There are many parameters that can be passed to the input function: + """, """#### 输入选项 输入函数可指定的参数非常丰富: + """)) + put_markdown(""" ```python input('This is label', type=TEXT, placeholder='This is placeholder', help_text='This is help text', required=True, datalist=['candidate1', 'candidate2', 'candidate2']) ``` - """, strip_indent=4) + """) input('This is label', type=TEXT, placeholder='This is placeholder', help_text='This is help text', required=True, datalist=['candidate1', 'candidate2', 'candidate2']) # 校验函数 - put_markdown("""我们可以为输入指定校验函数,校验函数校验通过时返回None,否则返回错误消息: + put_markdown(t("""You can specify a validation function for the input by using `validate` parameter. The validation function should return `None` when the check passes, otherwise an error message will be returned:""", """我们可以为输入指定校验函数,校验函数校验通过时返回`None`,否则返回错误消息:"""), strip_indent=4) + put_markdown(""" ```python - def check_age(p): # 检验函数校验通过时返回None,否则返回错误消息 + def check_age(p): # return None when the check passes, otherwise return the error message if p < 10: return 'Too young!!' if p > 60: @@ -89,7 +141,7 @@ def check_age(p): # 检验函数校验通过时返回None,否则返回错误 age = input("How old are you?", type=NUMBER, validate=check_age) ``` - """, strip_indent=4) + """) def check_age(p): # 检验函数校验通过时返回None,否则返回错误消息 if p < 10: @@ -97,18 +149,19 @@ def check_age(p): # 检验函数校验通过时返回None,否则返回错误 if p > 60: return 'Too old!!' - age = input("How old are you?", type=NUMBER, validate=check_age, help_text='尝试输入一些非法值,比如"8"、"65"') + age = input("How old are you?", type=NUMBER, validate=check_age, help_text=t('Try to input some illegal values, such as "8", "65"', '尝试输入一些非法值,比如"8"、"65"')) put_markdown('`age = %r`' % age) # Codemirror - put_markdown(r"""PyWebIO 的 `textarea()` 输入函数还支持使用 [Codemirror](https://codemirror.net/) 实现代码风格的编辑区,只需使用 `code` 参数传入Codemirror支持的选项即可(最简单的情况是直接传入` code={}` 或 `code=True`): + put_markdown(t("""You can use `code` parameter in `pywebio.input.textarea()` to create a code editing textarea:""", """PyWebIO 的 `textarea()` 输入函数还支持使用 [Codemirror](https://codemirror.net/) 实现代码风格的编辑区,只需使用 `code` 参数传入Codemirror支持的选项即可(最简单的情况是直接传入` code={}` 或 `code=True`):"""), strip_indent=4) + put_markdown(r""" ```python code = textarea('Code Edit', code={ - 'mode': "python", # 编辑区代码语言 - 'theme': 'darcula', # 编辑区darcula主题 + 'mode': "python", # code language + 'theme': 'darcula', # Codemirror theme }, value='import something\n# Write your python code') ``` - """, strip_indent=4) + """) code = textarea('Code Edit', code={ 'mode': "python", # 编辑区代码语言 @@ -118,28 +171,32 @@ def check_age(p): # 检验函数校验通过时返回None,否则返回错误 put_markdown("Your code:\n```python\n%s\n```" % code) # 输入组 - put_markdown(r"""### 输入组 - `input_group()` 接受单项输入组成的列表作为参数,输入组中需要在每一项输入函数中提供 `name` 参数来用于在结果中标识不同输入项。输入组中同样支持设置校验函数,其接受整个表单数据作为参数。 - + put_markdown(t("""### Input Group + `input_group()` accepts a list of single input function call as parameter, and returns a dictionary with the name of the single input function as the key and the input data as the value. + The input group also supports using `validate` parameter to set the validation function, which accepts the entire form data as parameter:""", + """### 输入组 + `input_group()` 接受单项输入组成的列表作为参数,输入组中需要在每一项输入函数中提供 `name` 参数来用于在结果中标识不同输入项。输入组中同样支持设置校验函数,其接受整个表单数据作为参数。检验函数校验通过时返回None,否则返回 `(input name,错误消息)` + """)) + put_markdown(r""" ```python - def check_form(data): # 检验函数校验通过时返回None,否则返回 (input name,错误消息) + def check_form(data): # input group validation: return (input name, error msg) when validation fail if len(data['name']) > 6: - return ('name', '名字太长!') + return ('name', 'Name too long!') if data['age'] <= 0: - return ('age', '年龄不能为负数!') + return ('age', 'Age can not be negative!') data = input_group("Basic info", [ input('Input your name', name='name'), input('Input your age', name='age', type=NUMBER, validate=check_age) ], validate=check_form) ``` - """, strip_indent=4) + """) - def check_form(data): # 检验函数校验通过时返回None,否则返回 (input name,错误消息) + def check_form(data): # input group validation: return (input name, error msg) when validation fail if len(data['name']) > 6: - return ('name', '名字太长!') + return ('name', 'Name too long!') if data['age'] <= 0: - return ('age', '年龄不能为负数!') + return ('age', 'Age can not be negative!') data = input_group("Basic info", [ input('Input your name', name='name'), @@ -148,9 +205,11 @@ def check_form(data): # 检验函数校验通过时返回None,否则返回 (i put_markdown("`data = %r`" % data) - put_markdown("""---- + put_markdown(t("""---- + For more information about input of PyWebIO, please visit PyWebIO [User Guide](https://pywebio.readthedocs.io/zh_CN/latest/guide.html) and [input module documentation](https://pywebio.readthedocs.io/zh_CN/latest/input.html). + """, """---- PyWebIO的输入演示到这里就结束了,更多内容请访问PyWebIO[用户指南](https://pywebio.readthedocs.io/zh_CN/latest/guide.html)和[input模块文档](https://pywebio.readthedocs.io/zh_CN/latest/input.html)。 - """, lstrip=True) + """)) if __name__ == '__main__': diff --git a/demos/markdown_previewer.py b/demos/markdown_previewer.py new file mode 100644 index 00000000..21ed728d --- /dev/null +++ b/demos/markdown_previewer.py @@ -0,0 +1,28 @@ +from pywebio import start_server + +from pywebio.output import * +from pywebio.pin import * +from pywebio.session import set_env, download + + +def main(): + """Markdown Previewer""" + set_env(output_animation=False) + + put_markdown("""# Markdown Live Preview + The online markdown editor with live preview. The source code of this application is [here](https://github.com/wang0618/PyWebIO/blob/dev/demos/markdown_previewer.py). + ## Write your Markdown + """) + put_textarea('md_text', rows=18, code={'mode': 'markdown'}) + + put_buttons(['Download content'], lambda _: download('saved.md', pin.md_text.encode('utf8')), small=True) + + put_markdown('## Preview') + while True: + change_detail = pin_wait_change('md_text') + with use_scope('md', clear=True): + put_markdown(change_detail['value'], sanitize=False) + + +if __name__ == '__main__': + start_server(main, port=8080, debug=True) diff --git a/demos/output_usage.py b/demos/output_usage.py index 1315d5f5..86ef1eae 100644 --- a/demos/output_usage.py +++ b/demos/output_usage.py @@ -1,16 +1,14 @@ -""" -输出演示 -^^^^^^^^^^^ -演示PyWebIO支持的各种输出形式 - -:demo_host:`Demo地址 ` `源码 `_ -""" from pywebio import start_server from pywebio.output import * -from pywebio.session import hold, set_env +from pywebio.session import info as session_info from functools import partial +def t(eng, chinese): + """return English or Chinese text according to the user's browser language""" + return chinese if 'zh' in session_info.user_language else eng + + def code_block(code, strip_indent=4): if strip_indent: lines = ( @@ -26,14 +24,25 @@ def run_code(code, scope): with use_scope() as scope: put_code(code, 'python') - put_buttons([{'label': '运行', 'value': '', 'color': 'success'}], + put_buttons([{'label': t('Run', '运行'), 'value': '', 'color': 'success'}], onclick=[partial(run_code, code=code, scope=scope)], small=True) async def main(): - set_env(title="PyWebIO输出演示") + """PyWebIO Output Usage - put_markdown("""# PyWebIO 输出演示 + Demonstrate various output usage supported by PyWebIO. + 演示PyWebIO输出模块的使用 + """ + put_markdown(t("""# PyWebIO Output demo + + You can get the source code of this demo in [here](https://github.com/wang0618/PyWebIO/blob/dev/demos/output_usage.py) + + This demo only introduces part of the functions of the PyWebIO output module. For the complete features, please refer to the [User Guide](https://pywebio.readthedocs.io/zh_CN/latest/guide.html). + + The output functions are all defined in the `pywebio.output` module and can be imported using `from pywebio.output import *`. + + """, """# PyWebIO 输出演示 在[这里](https://github.com/wang0618/PyWebIO/blob/dev/demos/output_usage.py)可以获取本Demo的源码。 @@ -43,9 +52,25 @@ async def main(): ### 基本输出 PyWebIO提供了一些便捷函数来输出表格、链接等格式: - """, strip_indent=4) + """)) - code_block(r""" + code_block(t(r""" + # Text Output + put_text("Hello world!") + + # Table Output + put_table([ + ['Commodity', 'Price'], + ['Apple', '5.5'], + ['Banana', '7'], + ]) + + # Markdown Output + put_markdown('~~Strikethrough~~') + + # File Output + put_file('hello_word.txt', b'hello word!') + """, r""" # 文本输出 put_text("Hello world!") @@ -61,22 +86,28 @@ async def main(): # 文件输出 put_file('hello_word.txt', b'hello word!') - """) + """)) + + put_markdown(t(r"""For all output functions provided by PyWebIO, please refer to the [document](https://pywebio.readthedocs.io/en/latest/output.html#output-func-list). + + ### Combined Output + The output functions whose name starts with `put_` can be combined with some output functions as part of the final output: - put_markdown(r"""PyWebIO提供的全部输出函数请参考PyWebIO文档 + You can pass `put_xxx()` calls to `put_table()` as cell content: + """, r"""PyWebIO提供的全部输出函数请参考[PyWebIO文档](https://pywebio.readthedocs.io/zh_CN/latest/output.html#output-func-list) ### 组合输出 函数名以 `put_` 开始的输出函数,可以与一些输出函数组合使用,作为最终输出的一部分。 比如`put_table()`支持以`put_xxx()`调用作为单元格内容: - """, strip_indent=4) + """)) code_block(r""" put_table([ ['Type', 'Content'], ['html', put_html('X2')], - ['text', '
'], # 等价于 ['text', put_text('
')] + ['text', '
'], # equal to ['text', put_text('
')] ['buttons', put_buttons(['A', 'B'], onclick=toast)], ['markdown', put_markdown('`Awesome PyWebIO!`')], ['file', put_file('hello.text', b'hello world')], @@ -84,25 +115,32 @@ async def main(): ]) """) - put_markdown(r"""类似地,`popup()`也可以将`put_xxx()`调用作为弹窗内容: - - """, strip_indent=4) + put_markdown(t(r"Similarly, you can pass `put_xxx()` calls to `popup()` as the popup content:", + r"类似地,`popup()`也可以将`put_xxx()`调用作为弹窗内容:")) code_block(r""" popup('Popup title', [ put_html('

Popup Content

'), - 'plain html:
', # 等价于 put_text('plain html:
') + 'plain html:
', # equal to put_text('plain html:
') put_table([['A', 'B'], ['C', 'D']]), put_buttons(['close_popup()'], onclick=lambda _: close_popup()) ]) """) - put_markdown(r"更多接受`put_xxx()`作为参数的输出函数请参考函数文档。") + put_markdown(t(r"For more output functions that accept `put_xxx()` calls as parameters, please refer to the [document](https://pywebio.readthedocs.io/en/latest/output.html#output-func-list).", + r"更多接受`put_xxx()`作为参数的输出函数请参考[函数文档](https://pywebio.readthedocs.io/zh_CN/latest/output.html#output-func-list)。")) - put_markdown(r"""### 事件回调 + put_markdown(t(r"""### Callback + PyWebIO allows you to output some buttons and bind callbacks to them. The provided callback function will be executed when the button is clicked. + + This is an example:%s + The call to `put_table()` will not block. When user clicks a button, the corresponding callback function will be invoked: + """, r"""### 事件回调 PyWebIO允许你输出一些控件,当控件被点击时执行提供的回调函数,就像编写GUI程序一样。 - 下面是一个例子: + 下面是一个例子:%s + `put_table()`的调用不会阻塞。当用户点击了某行中的按钮时,PyWebIO会自动调用相应的回调函数: + """) % """ ```python from functools import partial @@ -116,9 +154,7 @@ def edit_row(choice, row): [3, put_buttons(['edit', 'delete'], onclick=partial(edit_row, row=3))], ]) ``` - `put_table()`的调用不会阻塞。当用户点击了某行中的按钮时,PyWebIO会自动调用相应的回调函数: - - """, strip_indent=4) + """) from functools import partial @@ -134,23 +170,58 @@ def edit_row(choice, row): ]) set_scope('table-callback') - put_markdown(r"""当然,PyWebIO还支持单独的按钮控件: + put_markdown(t("Of course, PyWebIO also supports outputting individual buttons:", "当然,PyWebIO还支持单独的按钮控件:")+r""" ```python def btn_click(btn_val): put_markdown("> You click `%s` button" % btn_val) - put_buttons(['A', 'B', 'C'], onclick=btn_click) + put_buttons(['A', 'B', 'C'], onclick=btn_click) # a group of buttons + + put_button("Click me", onclick=lambda: toast("Clicked")) # single button ``` - """, strip_indent=4) + """) @use_scope('button-callback') def btn_click(btn_val): put_markdown("> You click `%s` button" % btn_val) put_buttons(['A', 'B', 'C'], onclick=btn_click) + put_button("Click me", onclick=lambda: toast("Clicked")) set_scope('button-callback') - put_markdown(r"""### 输出域Scope + put_markdown(t("In fact, all output can be bound to click events, not just buttons. You can call `onclick()` method after the output function (function name like `put_xxx()`) call:", "事实上,不仅是按钮,所有的输出都可以绑定点击事件。你可以在输出函数之后调用 `onclick()` 方法来绑定点击事件:")+r""" + ```python + put_image('some-image.png').onclick(lambda: toast('You click the image')) + + # set onclick in combined output + put_table([ + ['Commodity', 'Price'], + ['Apple', put_text('5.5').onclick(lambda: toast('You click the text'))], + ]) + ``` + """) + + put_image('https://www.python.org/static/img/python-logo.png').onclick(lambda: toast('You click the image')) + # set onclick in combined output + put_table([ + ['Commodity', 'Price'], + ['Apple', put_text('5.5').onclick(lambda: toast('You click the text'))], + ]) + + put_markdown(t("The return value of `onclick()` method is the object itself so it can be used in combined output.", + "`onclick()` 方法的返回值为对象本身,所以可以继续用于组合输出中。")) + + put_markdown(t(r"""### Output Scope + + PyWebIO uses the scope model to give more control to the location of content output. The output area of PyWebIO can be divided into different output domains. The output domain is called Scope in PyWebIO. + + The output domain is a container of output content, and each output domain is arranged vertically, and the output domains can also be nested. + + Each output function (function name like `put_xxx()`) will output its content to a scope, the default is "current scope". "current scope" is determined by the runtime context. The output function can also manually specify the scope to output. The scope name is unique within the session. + + You can use `use_scope()` to open and enter a new output scope, or enter an existing output scope: %s + The above code will generate the following Scope layout: + """, r"""### 输出域Scope PyWebIO使用Scope模型来对内容输出的位置进行灵活地控制,PyWebIO的内容输出区可以划分出不同的输出域,PyWebIO将输出域称作`Scope`。 @@ -158,8 +229,9 @@ def btn_click(btn_val): 每个输出函数(函数名形如 `put_xxx()` )都会将内容输出到一个Scope,默认为”当前Scope”,”当前Scope”由运行时上下文确定,输出函数也可以手动指定输出到的Scope。Scope名在会话内唯一。 - 可以使用 `use_scope()` 开启并进入一个新的输出域,或进入一个已经存在的输出域: - + 可以使用 `use_scope()` 开启并进入一个新的输出域,或进入一个已经存在的输出域: %s + 以上代码将会产生如下Scope布局: + """) % """ ```python with use_scope('A'): put_text('Text in scope A') @@ -170,8 +242,7 @@ def btn_click(btn_val): with use_scope('C'): put_text('Text in scope C') ``` - 以上代码将会产生如下Scope布局: - """, strip_indent=4) + """) with use_scope('A'): put_text('Text in scope A') @@ -187,37 +258,52 @@ def btn_click(btn_val): #pywebio-scope-C {border: 1px solid green;margin-top:2px}
""") - put_markdown(r""" + put_markdown(t(r"""The output function (function name like `put_xxx()`) will output the content to the "current scope" by default, and the "current scope" of the runtime context can be set by `use_scope()`. + + In addition, you can use the `scope` parameter of the output function to specify the destination scope to output: + """, r""" 输出函数(函数名形如 `put_xxx()` )在默认情况下,会将内容输出到”当前Scope”,可以通过 `use_scope()` 设置运行时上下文的”当前Scope”。 此外,也可以通过输出函数的 scope 参数指定输出的目的Scope: - """, strip_indent=4) + """)) put_grid([ - [put_code("put_text('A', scope='A')", 'python'), None, put_buttons(['运行'], [lambda: put_text('A', scope='A')])], - [put_code("put_text('B', scope='B')", 'python'), None, put_buttons(['运行'], [lambda: put_text('B', scope='B')])], - [put_code("put_text('C', scope='C')", 'python'), None, put_buttons(['运行'], [lambda: put_text('C', scope='C')])], + [put_code("put_text('A', scope='A')", 'python'), None, put_buttons([t('Run', '运行')], [lambda: put_text('A', scope='A')])], + [put_code("put_text('B', scope='B')", 'python'), None, put_buttons([t('Run', '运行')], [lambda: put_text('B', scope='B')])], + [put_code("put_text('C', scope='C')", 'python'), None, put_buttons([t('Run', '运行')], [lambda: put_text('C', scope='C')])], ], cell_widths='1fr 10px auto') - put_markdown(r"""输出函数可以使用position参数指定内容在Scope中输出的位置 + put_markdown(t("The output content can be inserted into any positions of the target scope by using the `position` parameter of the output function.", "输出函数可以使用`position`参数指定内容在Scope中输出的位置") + """ ```python put_text(now(), scope='A', position=...) ``` - """, strip_indent=4) + """) import datetime put_buttons([('position=%s' % i, i) for i in [1, 2, 3, -1, -2, -3]], lambda i: put_text(datetime.datetime.now(), position=i, scope='A'), small=True) - put_markdown(r"除了 `use_scope()` , PyWebIO同样提供了以下scope控制函数: ") + put_markdown(t(r"In addition to `use_scope()`, PyWebIO also provides the following scope control functions:", + r"除了 `use_scope()` , PyWebIO同样提供了以下scope控制函数: ")) put_grid([ - [put_code("clear('B') # 清除Scope B中的内容", 'python'), None, put_buttons(['运行'], [lambda: clear('B')])], - [put_code("remove('C') # 移除Scope C", 'python'), None, put_buttons(['运行'], [lambda: remove('C')])], - [put_code("scroll_to('A') # 将页面滚动到Scope A处", 'python'), None, put_buttons(['运行'], [lambda: scroll_to('A')])], + [put_code("clear('B') # Clear content of Scope B", 'python'), None, put_buttons(['运行'], [lambda: clear('B')])], + [put_code("remove('C') # Remove Scope C", 'python'), None, put_buttons(['运行'], [lambda: remove('C')])], + [put_code("scroll_to('A') # Scroll the page to position of Scope A", 'python'), None, put_buttons(['运行'], [lambda: scroll_to('A')])], ], cell_widths='1fr 10px auto') - put_markdown(r"""### 布局 + put_markdown(t(r"""### Layout + + In general, using the various output functions introduced above is enough to output what you want, but these outputs are arranged vertically. If you want to make a more complex layout (such as displaying a code block on the left side of the page and an image on the right), you need to use layout functions. + + The `pywebio.output` module provides 3 layout functions, and you can create complex layouts by combining them: + + - `put_row()` : Use row layout to output content. The content is arranged horizontally + - `put_column()` : Use column layout to output content. The content is arranged vertically + - `put_grid()` : Output content using grid layout + + Here is an example by combining `put_row()` and `put_column()`: + """, r"""### 布局 一般情况下,使用上文介绍的各种输出函数足以完成各种内容的展示,但直接调用输出函数产生的输出之间都是竖直排列的,如果想实现更复杂的布局(比如在页 面左侧显示一个代码块,在右侧显示一个图像),就需要借助布局函数。 `pywebio.output` 模块提供了3个布局函数,通过对他们进行组合可以完成各种复杂的布局: @@ -227,14 +313,14 @@ def btn_click(btn_val): - `put_grid()` : 使用网格布局输出内容 比如,通过通过组合 `put_row()` 和 `put_column()` 实现的布局: - """, strip_indent=4) + """)) code_block(r""" put_row([ put_column([ put_code('A'), put_row([ - put_code('B1'), None, # None 表示输出之间的空白 + put_code('B1'), None, # %s put_code('B2'), None, put_code('B3'), ]), @@ -243,45 +329,33 @@ def btn_click(btn_val): put_code('D'), None, put_code('E') ]) - """) + """ % t('None represents the space between the output', 'None 表示输出之间的空白')) + + put_markdown(t(r"""### Style + If you are familiar with CSS styles, you can use the `style()` method to set a custom style for the output. - put_markdown(r""" - ### 样式 + You can set the CSS style for a single `put_xxx()` output: + """, r"""### 样式 - 如果你熟悉 CSS样式 ,你还可以使用 `style()` 函数给输出设定自定义样式。 + 如果你熟悉 CSS样式 ,你还可以使用 `style()` 方法给输出设定自定义样式。 可以给单个的 `put_xxx()` 输出设定CSS样式,也可以配合组合输出使用: - """, strip_indent=4) + """)) code_block(r""" - style(put_text('Red'), 'color: red') + put_text('Red').style('color: red') put_table([ ['A', 'B'], - ['C', style(put_text('Red'), 'color: red')], + ['C', put_text('Red').style('color: red')], ]) """, strip_indent=4) - put_markdown(r"`style()` 也接受列表作为输入:") - - code_block(r""" - style([ - put_text('Red'), - put_markdown('~~del~~') - ], 'color: red') - - put_collapse('title', style([ - put_text('text'), - put_markdown('~~del~~'), - ], 'margin-left: 20px')) - - """, strip_indent=4) - - put_markdown("""---- + put_markdown(t("""---- + For more information about output of PyWebIO, please visit PyWebIO [User Guide](https://pywebio.readthedocs.io/zh_CN/latest/guide.html) and [output module documentation](https://pywebio.readthedocs.io/zh_CN/latest/output.html). + """, """---- PyWebIO的输出演示到这里就结束了,更多内容请访问PyWebIO[用户指南](https://pywebio.readthedocs.io/zh_CN/latest/guide.html)和[output模块文档](https://pywebio.readthedocs.io/zh_CN/latest/output.html)。 - """, lstrip=True) - - await hold() + """)) if __name__ == '__main__': diff --git a/demos/set_env_demo.py b/demos/set_env_demo.py index c028178f..99cbc502 100644 --- a/demos/set_env_demo.py +++ b/demos/set_env_demo.py @@ -1,7 +1,3 @@ -""" -`pywebio.session.set_env()` demo -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -""" from pywebio import start_server from pywebio.input import * from pywebio.output import * @@ -10,22 +6,31 @@ import asyncio +def t(eng, chinese): + """return English or Chinese text according to the user's browser language""" + return chinese if 'zh' in info.user_language else eng + + async def main(): + """`pywebio.session.set_env()` demo""" + set_scope('time') - put_markdown('> 可用于观察 `output_animation` 项的动画效果') + put_markdown(t('> Can be used to observe the animation effect of the `output_animation` setting', + '> 可用于观察 `output_animation` 项的动画效果')) put_markdown('---') async def bg_task(): while 1: with use_scope('time', clear=True): - put_text('当前时间:', datetime.datetime.now()) + put_text('Current time:', datetime.datetime.now()) await asyncio.sleep(1) run_async(bg_task()) - put_buttons(['输出文本'], [lambda: put_text(datetime.datetime.now())]) - put_markdown('> 可用于观察 `auto_scroll_bottom` 项的自动滚动效果') + put_buttons([t('Output some texts', '输出文本')], [lambda: put_text(datetime.datetime.now())]) + put_markdown(t('> Can be used to observe the automatic scrolling effect of the `auto_scroll_bottom` setting', + '> 可用于观察 `auto_scroll_bottom` 项的自动滚动效果')) put_markdown('---') put_text('Some text.\n' * 10) @@ -38,15 +43,15 @@ async def bg_task(): while 1: curr_state_info = ', '.join('%s=%r' % (k, v) for k, v in state.items()) - key = await actions('选择要更改的会话环境设置项', list(state.keys()), help_text='当前状态:' + curr_state_info) + key = await actions(t('Select the setting item to be changed', '选择要更改的会话环境设置项'), list(state.keys()), help_text='Current state: ' + curr_state_info) if key == 'title': - state['title'] = await input('请输入标题', value=state['title']) + state['title'] = await input('Title', value=state['title']) set_env(title=state['title']) - toast('已将标题设置为%r' % state['title']) + toast('Title is set to %r' % state['title']) elif key in state: state[key] = not (state[key]) set_env(**{key: state[key]}) - toast('已将`%s`设置为%r' % (key, state[key])) + toast('`%s` is set to %r' % (key, state[key])) if __name__ == '__main__': diff --git a/demos/theme.py b/demos/theme.py new file mode 100644 index 00000000..d4eeb779 --- /dev/null +++ b/demos/theme.py @@ -0,0 +1,395 @@ +from functools import partial + +from pywebio import start_server, config +from pywebio.input import * +from pywebio.output import * +from pywebio.pin import * +from pywebio.session import * + + +def pin_widgets(): + put_markdown("# Pin widget") + options = [ + { + "label": "Option one", + "value": 1, + "selected": True, + }, + { + "label": "Option two", + "value": 2, + }, + { + "label": "Disabled option", + "value": 3, + "disabled": True + } + ] + put_input('input', label='Text input', placeholder="Enter email", + help_text="We'll never share your email with anyone else.") + put_input('valid_input', label="Valid input", value="correct value") + put_input('invalid_input', label="Invalid input", value="wrong value") + put_textarea('textarea', label='Textarea', rows=3, maxlength=10, minlength=20, value=None, + placeholder='This is placeholder message', readonly=False) + put_textarea('code', label='Code area', rows=4, code={'mode': 'python'}, + value='import pywebio\npywebio.output.put_text("hello world")') + put_select('select', options=options, label='Select') + put_select('select_multiple', options=options, label='Multiple select', multiple=True, value=None) + put_checkbox('checkbox', options=options, label='Checkbox', inline=False, value=None) + put_checkbox('checkbox_inline', options=options, label='Inline checkbox', inline=True, value=None) + put_radio('radio', options=options, label='Radio', inline=False, value=None) + put_radio('radio_inline', options=options, label='Inline radio', inline=True, value='B') + put_slider('slider', label='Slider') + put_actions('actions', buttons=[ + {'label': 'Submit', 'value': '1'}, + {'label': 'Warning', 'value': '2', 'color': 'warning'}, + {'label': 'Danger', 'value': '3', 'color': 'danger'}, + ], label='Actions') + + pin_update('valid_input', valid_status=True, valid_feedback="Success! You've done it.") + pin_update('invalid_input', valid_status=False, invalid_feedback="Sorry, that username's taken. Try another?") + + +def form(): + options = [ + { + "label": "Option one", + "value": 1, + "selected": True, + }, + { + "label": "Option two", + "value": 2, + }, + { + "label": "Disabled option", + "value": 3, + "disabled": True + } + ] + + input_group('Input group', [ + input('Text', type=TEXT, datalist=['candidate-%s' % i for i in range(10)], name='text', required=True, + help_text='Required'), + input('Number', type=NUMBER, value="42", name='number'), + input('Float', type=FLOAT, name='float'), + input('Password', type=PASSWORD, name='password'), + + textarea('Textarea', rows=3, maxlength=20, name='textarea', + placeholder="The maximum number of characters you can input is 20"), + + textarea('Code', name='code', code={'mode': 'python'}, + value='import pywebio\npywebio.output.put_text("hello world")'), + + select('Multiple select', options, name='select-multiple', multiple=True), + + select('Select', options, name='select'), + + checkbox('Inline checkbox', options, inline=True, name='checkbox-inline'), + + checkbox('Checkbox', options, name='checkbox'), + + radio('Inline radio', options, inline=True, name='radio-inline'), + + radio('Radio', options, inline=False, name='radio'), + + file_upload('File upload', name='file_upload', max_size='10m'), + + actions('Actions', [ + {'label': 'Submit', 'value': 'submit'}, + {'label': 'Disabled', 'disabled': True}, + {'label': 'Reset', 'type': 'reset', 'color': 'warning'}, + {'label': 'Cancel', 'type': 'cancel', 'color': 'danger'}, + ], name='actions'), + ]) + + +def output_widgets(): + ########################################################################################### + put_markdown("# Typography") + put_row([ + put_markdown(""" + ## Heading 2 + ### Heading 3 + #### Heading 4 + ##### Heading 5 + + [PyWebIO](https://github.com/pywebio/PyWebIO) is awesome! + + *This text will be italic* + **This text will be bold** + _You **can** combine them_ + ~~Strikethrough~~ + This is `inline code` + + As Kanye West said: + + > We're living the future so + > the present is our past. + """), + + put_markdown(""" + ### Lists + * Item 1 + * Item 2 + * Item 2a + * Item 2b + + 1. Item 1 + 1. Item 2 + 1. Item 2a + 1. Item 2b + + ### Task Lists + - [x] [links](), **formatting**, and tags supported + - [x] list syntax required (any unordered or ordered list supported) + - [x] this is a complete item + - [ ] this is an incomplete item + """) + ]) + + ########################################################################################### + put_markdown(""" + # Code + ```python + from pywebio import * + + def main(): # PyWebIO application function + name = input.input("what's your name") + output.put_text("hello", name) + + start_server(main, port=8080, debug=True) + ``` + """) + ########################################################################################### + put_markdown('# Image') + with use_scope('image'): + put_image( + "https://opengraph.githubassets.com/6bcea5272d0b5901f48a67d9d05da6c7a7c7c68da32a5327943070ff9c9a3dfb/pywebio/PyWebIO").style(""" + max-height: 250px; + border: 2px solid #fff; + border-radius: 25px; + """) + ########################################################################################### + put_markdown("# Buttons") + # small=None, link_style=False, outline=False, group=False + put_buttons([ + dict(label=i, value=i, color=i) + for i in ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark'] + ], onclick=lambda b: toast(f'Clicked {b} button')) + + put_buttons([ + dict(label=i, value=i, color=i) + for i in ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark'] + ], onclick=lambda b: toast(f'Clicked {b} button'), small=True) + + put_buttons([ + dict(label=i, value=i, color=i) + for i in ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark'] + ], onclick=lambda b: toast(f'Clicked {b} button'), link_style=True) + + put_buttons([ + dict(label=i, value=i, color=i) + for i in ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark'] + ], onclick=lambda b: toast(f'Clicked {b} button'), outline=True) + put_buttons([ + dict(label=i, value=i, color=i) + for i in ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark'] + ], onclick=lambda b: toast(f'Clicked {b} button'), group=True) + ########################################################################################### + put_markdown('# Tables') + put_markdown(""" + First Header | Second Header + ------------ | ------------- + Content from cell 1 | Content from cell 2 + Content in the first column | Content in the second column + """) + + put_table([ + ['Type', 'Content'], + ['text', '
'], + ['html', put_html('X2')], + ['buttons', put_buttons(['A', 'B'], onclick=toast, small=True)], + ['markdown', put_markdown('`awesome PyWebIO!`\n - 1\n - 2\n - 3')], + ['file', put_file('hello.text', b'')], + ['table', put_table([ + ['A', 'B'], + [put_markdown('`C`'), put_markdown('`D`')] + ])] + ]) + ########################################################################################### + put_markdown('# Popup') + + def show_popup(): + popup('Popup title', [ + 'Popup body text goes here.', + put_table([ + ['Type', 'Content'], + ['html', put_html('X2')], + ['text', '
'], + ['buttons', put_buttons(['A', 'B'], onclick=toast)], + ['markdown', put_markdown('`Awesome PyWebIO!`')], + ['file', put_file('hello.text', b'')], + ['table', put_table([['A', 'B'], ['C', 'D']])] + ]), + put_button('Close', onclick=close_popup, outline=True) + ], size=PopupSize.NORMAL) + + put_button("Click me to show a popup", onclick=show_popup) + + ########################################################################################### + put_markdown('# Layout') + put_row([ + put_column([ + put_code('A'), + put_row([ + put_code('B1'), None, + put_code('B2'), None, + put_code('B3'), + ]), + put_code('C'), + ]), None, + put_code('python'), None, + put_code('python\n' * 20).style('max-height:200px;'), + ]) + + ########################################################################################### + put_markdown('# Loading') + put_processbar('processbar', 0.3) + put_text() + put_grid([ + [ + put_loading(shape=shape, color=color) + for color in ('primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark') + ] + for shape in ('border', 'grow') + ], cell_width='50px', cell_height='50px') + ########################################################################################### + put_markdown('# Tabs') + + put_tabs([ + {'title': 'Text', 'content': 'Hello world'}, + {'title': 'Markdown', 'content': put_markdown('~~Strikethrough~~')}, + {'title': 'More content', 'content': [ + put_table([ + ['Commodity', 'Price'], + ['Apple', '5.5'], + ['Banana', '7'], + ]), + put_link('pywebio', 'https://github.com/wang0618/PyWebIO') + ]}, + ]) + ########################################################################################### + put_markdown('# Scrollable') + + put_scrollable("Long text " * 200, height=200) + ########################################################################################### + put_markdown('# Collapse') + put_collapse('Click to expand', [ + 'text', + put_markdown('~~Strikethrough~~'), + put_table([ + ['Commodity', 'Price'], + ['Apple', '5.5'], + ]) + ]) + ########################################################################################### + put_markdown('# Message') + + put_warning( + put_markdown('### Warning!'), + "Best check yo self, you're not looking too good. Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.", + closable=True) + put_success("Well done! You successfully read this important alert message.") + put_info("Heads up! This alert needs your attention, but it's not super important.") + put_error("Oh snap! Change a few things up and try submitting again.") + + +ALL_THEME = ('default', 'dark', 'sketchy', 'minty', 'yeti') + +THEME_SUMMARY = {'default': 'The default theme', 'dark': 'A theme for night', + 'sketchy': 'A hand-drawn look for mockups and mirth', 'minty': 'A fresh feel', + 'yeti': 'A friendly foundation'} + +style = """ +table img:hover { + transition-duration: 400ms; + transform: translateY(-2px); + box-shadow: 0px 2px 9px 2px rgb(0 0 0 / 27%), 0 30px 50px -30px rgb(0 0 0 / 30%) +} +.page-header h1 { + font-size: 3em; +} +#pywebio-scope-image img { + box-shadow: rgb(204 204 204) 3px 3px 13px; +} +.webio-theme-dark #pywebio-scope-image img { + box-shadow: none !important; +} +""" + + +@config(css_style=style) +def page(): + """PyWebIO Theme Preview""" + + theme = eval_js("new URLSearchParams(window.location.search).get('app')") + if theme not in ALL_THEME: + theme = 'default' + + put_html(f""" + + """) + + put_markdown('# Switch Theme') + themes = [ + put_image(f"https://fastly.jsdelivr.net/gh/wang0618/PyWebIO@dev/docs/assets/theme/{name}.png").onclick( + partial(go_app, name=name, new_window=False)) + for name in ALL_THEME if name != theme + ] + if info.user_agent.is_mobile: + put_table([themes[:2], themes[2:]]) + else: + put_table([themes]) + + if theme != 'default': + put_markdown(f""" + ### Usage + Use `pywebio.config()` to apply this theme: + + ```python + @config(theme="{theme}") + def main(): + put_text("hello world") + + start_server(main, port=8080) + ``` + """) + + put_markdown(""" + ### Credits + + The dark theme is modified from ForEvolve's [bootstrap-dark](https://github.com/ForEvolve/bootstrap-dark). + The sketchy, minty and yeti theme are from [bootswatch](https://bootswatch.com/4/). + """) + + set_env(input_panel_min_height=100, input_panel_init_height=190) + output_widgets() + pin_widgets() + form() + + +# bind each theme to the app +main = { + theme: config(theme=theme, title=f"PyWebIO {theme} theme")(page) + for theme in ALL_THEME if theme != 'default' +} +main['index'] = page + +if __name__ == '__main__': + start_server(main, debug=True, port=8080) diff --git a/demos/wordle.py b/demos/wordle.py new file mode 100644 index 00000000..cc9b75f3 --- /dev/null +++ b/demos/wordle.py @@ -0,0 +1,110 @@ +import time +from pywebio import start_server, config +from pywebio.output import * +from pywebio.session import run_js, local as session_local + +TODAY_WORD = 'PYWEBIO' # need to be uppercase + +MAX_TRY = 6 +WORD_LEN = len(TODAY_WORD) + + +CSS = """ +.pywebio {padding-top: 0} .markdown-body table {display:table; width:250px; margin:10px auto;} +.markdown-body table th, .markdown-body table td {font-weight:bold; padding:0; line-height:50px;} +th>div,td>div {width:50px; height:50px}.btn-light {background-color:#d3d6da;} +@media (max-width: 435px) {.btn{padding:0.375rem 0.5rem;}} +@media (max-width: 355px) {.btn{padding:0.375rem 0.4rem;}} +""" + + +# To check if a user's input word is actually a legit word +# We just implement a placeholder function in this example +# If a guess word is UNHAPPY, toast a message +def is_word(s): + return 'UNHAPPY' not in s + + +def on_key_press(char): + if session_local.curr_row >= MAX_TRY or session_local.game_pass: + return + + if char == '◀': + session_local.curr_word = session_local.curr_word[:-1] + return clear(f's-{session_local.curr_row}-{len(session_local.curr_word)}') + + # show the char in grid + with use_scope(f's-{session_local.curr_row}-{len(session_local.curr_word)}', clear=True): + put_text(char) + + session_local.curr_word += char + if len(session_local.curr_word) == WORD_LEN: # submit a word guess + if not is_word(session_local.curr_word): + toast('Not in word list!', color='error') + session_local.curr_word = '' + for i in range(WORD_LEN): + clear(f's-{session_local.curr_row}-{i}') + else: + for idx, c in enumerate(session_local.curr_word): + time.sleep(0.2) + if TODAY_WORD[idx] == c: + session_local.green_chars.add(c) + run_js('$("button:contains(%s)").css({"background-color":"#6aaa64", "color":"white"})' % c) + text_bg = '#6aaa64' + session_local.game_result += '🟩' + elif c in TODAY_WORD: + text_bg = '#c9b458' + session_local.game_result += '🟨' + if c not in session_local.green_chars: + run_js('$("button:contains(%s)").css({"background-color":"#c9b458", "color":"white"})' % c) + else: + text_bg = '#787c7e' + session_local.game_result += '⬜' + run_js('$("button:contains(%s)").css({"background-color":"#787c7e", "color":"white"})' % c) + + with use_scope(f's-{session_local.curr_row}-{idx}', clear=True): + put_text(c).style(f'color:white;background:{text_bg}') + + session_local.game_result += '\n' + if session_local.curr_word == TODAY_WORD: + toast('Genius', color='success') + session_local.game_pass = True + + session_local.curr_row += 1 + session_local.curr_word = '' + + if session_local.game_pass: + message = f'Wordle {session_local.curr_row}/{MAX_TRY}\n' + session_local.game_result + with popup("Game Result", size='small'): + put_text(message).style('text-align: center') + put_button('Share', color='success', onclick=lambda: toast('Copied to clipboard') or run_js("""navigator.clipboard.write([new ClipboardItem({"text/plain":new Blob([text],{type:"text/plain"})})]);""", text=message)).style('text-align: center') + + +@config(title="WORDLE with PyWebIO", description="A wordle-like game implemented with PyWebIO", css_style=CSS) +def main(): + put_markdown( + '# WORDLE \n A pure python implementation of a [Wordle-like game](https://en.wikipedia.org/wiki/Wordle), using PyWebIO library. ' + '[Source code](https://github.com/pywebio/PyWebIO/blob/dev/demos/wordle.py)' + ).style('text-align:center') + + grid = [ + [put_scope(f's-{x}-{y}', content=put_text(' ')) for y in range(WORD_LEN)] + for x in range(MAX_TRY) + ] + put_table(grid).style('text-align: center') + + keyboard = [ + put_buttons([dict(label=c, value=c, color='light') for c in keys], on_key_press, serial_mode=True) + for keys in ['QWERTYUIOP', 'ASDFGHJKL', 'ZXCVBNM◀'] + ] + put_column(keyboard).style('text-align: center') + + session_local.curr_row = 0 + session_local.curr_word = '' + session_local.green_chars = set() + session_local.game_pass = False + session_local.game_result = '' + + +if __name__ == '__main__': + start_server(main, port=8080, cdn=False) diff --git a/docs/FAQ.rst b/docs/FAQ.rst index b9f8b8ee..7767ac1c 100644 --- a/docs/FAQ.rst +++ b/docs/FAQ.rst @@ -1,21 +1,27 @@ -常见问题 +FAQ ========================== .. contents:: :local: -如何让输入框在提交后不消失,并可以持续性地输入 +How to make the input form not disappear after submission, and can continue to receive input? +---------------------------------------------------------------------------------------------- + +You can consider the :doc:`pin <./pin>` module. It achieves persistent input by pinning input widgets to the page. + + +How to output an input widget such as a search bar? ---------------------------------------------------------- -PyWebIO 的设计就是输入表单在成功提交后就销毁,因为 PyWebIO 的输入是阻塞式的,一旦提交表单,输入函数就返回了,此时表单还留在界面上是没有意义的。如果想实现持续性的输入,可以将接收输入以及后续操作放到一个 ``while`` 循环中。 + +You can consider the :doc:`pin <./pin>` module. -如何输出一个诸如搜索栏的输入框 +Why the callback of ``put_buttons()`` does not work? ---------------------------------------------------------- -很遗憾,PyWebIO并不支持将输入框作为一般性的内容输出到页面。因为这样就相当于又回到了基于回调获取输入的方式了,会导致应用开发的复杂性提高,PyWebIO不太推荐过多依赖回调机制,所以对此仅提供了非常少的支持。 -不过也可以使用另一种方式实现近似的效果:只需要在需要显示输入框的地方放置一个button( `put_buttons() ` ),然后在button的回调函数中调用输入函数来获取输入并进行后续操作。 +You might use the old version of PyWebIO, upgrade it to the latest version or see `the old document `_ -为什么 ``put_buttons()`` 的回调不起作用 +Why I cannot download the file using ``put_file()``? ---------------------------------------------------------- -一般情况下,在Server模式下,任务函数一旦返回(或在Script模式下,脚本运行结束),会话就结束了,此时事件回调也将不起作用,可以在任务函数(或脚本)末尾处使用 `pywebio.session.hold()` 函数来将会话保持,这样在用户关闭浏览器页面前,事件回调将一直可用。 参见 :ref:`Server模式与Script模式 ` +The reason is the same as above. \ No newline at end of file diff --git a/docs/_ext/codeblock.py b/docs/_ext/codeblock.py index 23d56a59..6b966653 100644 --- a/docs/_ext/codeblock.py +++ b/docs/_ext/codeblock.py @@ -20,6 +20,9 @@ def md5(str_data): return t.hexdigest() def run(self): + if self.env.app.builder.name == 'gettext': + return super().run() + code_save_path = os.environ.get('CODE_EXPORT_PATH') caption = self.options.get('summary', '') @@ -30,8 +33,11 @@ def run(self): if self.options.get('name', None) is None: # 设置name属性,从而让生成的代码html块具有id属性 self.options.update({'name': 'demo-' + code_id}) + else: + name = self.options.get('name', '') + self.options.update({'name': 'demo-' + name}) - name = self.options.get('name').replace('_','-') + name = self.options.get('name').replace('_', '-') if name in type(self).names: name += '-' + code_id self.options.update({'name': name}) diff --git a/docs/advanced.rst b/docs/advanced.rst new file mode 100644 index 00000000..d40681c5 --- /dev/null +++ b/docs/advanced.rst @@ -0,0 +1,412 @@ +Advanced topic +=============== + +This section will introduce the advanced features of PyWebIO. + + +.. _multiple_app: + +Start multiple applications with start_server() +------------------------------------------------- + +`start_server() ` accepts a function as PyWebIO application. In addition, +`start_server() ` also accepts a list of application function or a dictionary +of it to start multiple applications. You can use `pywebio.session.go_app() ` or +`put_link() ` to jump between application:: + + def task_1(): + put_text('task_1') + put_buttons(['Go task 2'], [lambda: go_app('task_2')]) + + def task_2(): + put_text('task_2') + put_buttons(['Go task 1'], [lambda: go_app('task_1')]) + + def index(): + put_link('Go task 1', app='task_1') # Use `app` parameter to specify the task name + put_link('Go task 2', app='task_2') + + # equal to `start_server({'index': index, 'task_1': task_1, 'task_2': task_2})` + start_server([index, task_1, task_2]) + +When the first parameter of `start_server() ` is a dictionary, whose key is +application name and value is application function. When it is a list, PyWebIO will use function name as application name. + +You can select which application to access through the ``app`` URL parameter +(for example, visit ``http://host:port/?app=foo`` to access the ``foo`` application), +By default, the ``index`` application is opened when no ``app`` URL parameter provided. +When the ``index`` application doesn't exist, PyWebIO will provide a default index application. + + +.. _integration_web_framework: + +Integration with web framework +--------------------------------- + +The PyWebIO application can be integrated into an existing Python Web project, the PyWebIO application and the Web +project share a web framework. PyWebIO currently supports integration with Flask, Tornado, Django, aiohttp and +FastAPI(Starlette) web frameworks. + +The integration methods of those web frameworks are as follows: + +.. tabs:: + + .. tab:: Tornado + + .. only:: latex + + **Tornado** + + Use `pywebio.platform.tornado.webio_handler()` to get the + `WebSocketHandler `_ + class for running PyWebIO applications in Tornado:: + + import tornado.ioloop + import tornado.web + from pywebio.platform.tornado import webio_handler + + class MainHandler(tornado.web.RequestHandler): + def get(self): + self.write("Hello, world") + + if __name__ == "__main__": + application = tornado.web.Application([ + (r"/", MainHandler), + (r"/tool", webio_handler(task_func)), # `task_func` is PyWebIO task function + ]) + application.listen(port=80, address='localhost') + tornado.ioloop.IOLoop.current().start() + + + In above code, we add a routing rule to bind the ``WebSocketHandler`` of the PyWebIO application to the ``/tool`` path. + After starting the Tornado server, you can visit ``http://localhost/tool`` to open the PyWebIO application. + + .. attention:: + + PyWebIO uses the WebSocket protocol to communicate with the browser in Tornado. If your Tornado application + is behind a reverse proxy (such as Nginx), you may need to configure the reverse proxy to support the + WebSocket protocol. :ref:`Here ` is an example of Nginx WebSocket configuration. + + .. tab:: Flask + + .. only:: latex + + **Flask** + + Use `pywebio.platform.flask.webio_view()` to get the view function for running PyWebIO applications in Flask:: + + from pywebio.platform.flask import webio_view + from flask import Flask + + app = Flask(__name__) + + # `task_func` is PyWebIO task function + app.add_url_rule('/tool', 'webio_view', webio_view(task_func), + methods=['GET', 'POST', 'OPTIONS']) # need GET,POST and OPTIONS methods + + app.run(host='localhost', port=80) + + + In above code, we add a routing rule to bind the view function of the PyWebIO application to the ``/tool`` path. + After starting the Flask application, visit ``http://localhost/tool`` to open the PyWebIO application. + + .. tab:: Django + + .. only:: latex + + **Django** + + Use `pywebio.platform.django.webio_view()` to get the view function for running PyWebIO applications in Django:: + + # urls.py + + from django.urls import path + from pywebio.platform.django import webio_view + + # `task_func` is PyWebIO task function + webio_view_func = webio_view(task_func) + + urlpatterns = [ + path(r"tool", webio_view_func), + ] + + + In above code, we add a routing rule to bind the view function of the PyWebIO application to the ``/tool`` path. + After starting the Django server, visit ``http://localhost/tool`` to open the PyWebIO application + + .. tab:: aiohttp + + .. only:: latex + + **aiohttp** + + Use `pywebio.platform.aiohttp.webio_handler()` to get the + `Request Handler `_ coroutine for + running PyWebIO applications in aiohttp:: + + from aiohttp import web + from pywebio.platform.aiohttp import webio_handler + + app = web.Application() + # `task_func` is PyWebIO task function + app.add_routes([web.get('/tool', webio_handler(task_func))]) + + web.run_app(app, host='localhost', port=80) + + After starting the aiohttp server, visit ``http://localhost/tool`` to open the PyWebIO application + + .. attention:: + + PyWebIO uses the WebSocket protocol to communicate with the browser in aiohttp. If your aiohttp server is + behind a reverse proxy (such as Nginx), you may need to configure the reverse proxy to support the WebSocket + protocol. :ref:`Here ` is an example of Nginx WebSocket configuration. + + + .. tab:: FastAPI/Starlette + + .. only:: latex + + **FastAPI/Starlette** + + Use `pywebio.platform.fastapi.webio_routes()` to get the FastAPI/Starlette routes for running PyWebIO applications. + You can mount the routes to your FastAPI/Starlette app. + + FastAPI:: + + from fastapi import FastAPI + from pywebio.platform.fastapi import webio_routes + + app = FastAPI() + + @app.get("/app") + def read_main(): + return {"message": "Hello World from main app"} + + # `task_func` is PyWebIO task function + app.mount("/tool", FastAPI(routes=webio_routes(task_func))) + + Starlette:: + + from starlette.applications import Starlette + from starlette.responses import JSONResponse + from starlette.routing import Route, Mount + from pywebio.platform.fastapi import webio_routes + + async def homepage(request): + return JSONResponse({'hello': 'world'}) + + app = Starlette(routes=[ + Route('/', homepage), + Mount('/tool', routes=webio_routes(task_func)) # `task_func` is PyWebIO task function + ]) + + After starting the server by using ``uvicorn :app`` , visit ``http://localhost:8000/tool/`` to open the PyWebIO application + + See also: `FastAPI doc `_ , `Starlette doc `_ + + .. attention:: + + PyWebIO uses the WebSocket protocol to communicate with the browser in FastAPI/Starlette. If your server is + behind a reverse proxy (such as Nginx), you may need to configure the reverse proxy to support the WebSocket + protocol. :ref:`Here ` is an example of Nginx WebSocket configuration. + + +.. _integration_web_framework_note: + +Notes +^^^^^^^^^^^ +**Deployment in production** + +In your production system, you may want to deploy the web applications with some WSGI/ASGI servers such as uWSGI, Gunicorn, and Uvicorn. +Since PyWebIO applications store session state in memory of process, when you use HTTP-based sessions (Flask and Django) +and spawn multiple workers to handle requests, the request may be dispatched to a process that does not hold the session +to which the request belongs. So you can only start one worker to handle requests when using Flask or Django backend. + +If you still want to use multiple processes to increase concurrency, one way is to use Uvicorn+FastAPI, or you can also +start multiple Tornado/aiohttp processes and add external load balancer (such as HAProxy or nginx) before them. +Those backends use the WebSocket protocol to communicate with the browser in PyWebIO, so there is no the issue as described above. + +**Static resources Hosting** + +By default, the front-end of PyWebIO gets required static resources from CDN. If you want to deploy PyWebIO applications +in an offline environment, you need to host static files by yourself, and set the ``cdn`` parameter of ``webio_view()`` +or ``webio_handler()`` to ``False``. + +When setting ``cdn=False`` , you need to host the static resources in the same directory as the PyWebIO application. +In addition, you can also pass a string to ``cdn`` parameter to directly set the URL of PyWebIO static resources directory. + +The path of the static file of PyWebIO is stored in ``pywebio.STATIC_PATH``, you can use the command +``python3 -c "import pywebio; print(pywebio.STATIC_PATH)"`` to print it out. + +.. note:: + + ``start_server()`` and ``path_deploy()`` also support ``cdn`` parameter, if it is set to ``False``, the static + resource will be hosted in local server automatically, without manual hosting. + + +.. _coroutine_based_session: + +Coroutine-based session +------------------------------- +In most cases, you don’t need the coroutine-based session. All functions or methods in PyWebIO that are only used for +coroutine sessions are specifically noted in the document. + +PyWebIO's session is based on thread by default. Each time a user opens a session connection to the server, PyWebIO will +start a thread to run the task function. In addition to thread-based sessions, PyWebIO also provides coroutine-based sessions. +Coroutine-based sessions accept coroutine functions as task functions. + +The session based on the coroutine is a single-thread model, which means that all sessions run in a single thread. +For IO-bound tasks, coroutines take up fewer resources than threads and have performance comparable to threads. +In addition, the context switching of the coroutine is predictable, which can reduce the need for program synchronization +and locking, and can effectively avoid most critical section problems. + +Using coroutine session +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To use coroutine-based session, you need to use the ``async`` keyword to declare the task function as a coroutine +function, and use the ``await`` syntax to call the PyWebIO input function: + +.. code-block:: python + :emphasize-lines: 5,6 + + from pywebio.input import * + from pywebio.output import * + from pywebio import start_server + + async def say_hello(): + name = await input("what's your name?") + put_text('Hello, %s' % name) + + start_server(say_hello, auto_open_webbrowser=True) + + +In the coroutine task function, you can also use ``await`` to call other coroutines or +(`awaitable objects `_) in the standard +library `asyncio `_: + +.. code-block:: python + :emphasize-lines: 6,10 + + import asyncio + from pywebio import start_server + + async def hello_word(): + put_text('Hello ...') + await asyncio.sleep(1) # await awaitable objects in asyncio + put_text('... World!') + + async def main(): + await hello_word() # await coroutine + put_text('Bye, bye') + + start_server(main, auto_open_webbrowser=True) + +.. attention:: + + In coroutine-based session, all input functions defined in the :doc:`pywebio.input ` module need to use + ``await`` syntax to get the return value. Forgetting to use ``await`` will be a common error when using coroutine-based session. + + Other functions that need to use ``await`` syntax in the coroutine session are: + + * `pywebio.session.run_asyncio_coroutine(coro_obj) ` + * `pywebio.session.eval_js(expression) ` + +.. warning:: + + Although the PyWebIO coroutine session is compatible with the ``awaitable objects`` in the standard library ``asyncio``, + the ``asyncio`` library is not compatible with the ``awaitable objects`` in the PyWebIO coroutine session. + + That is to say, you can't pass PyWebIO ``awaitable objects`` to the ``asyncio`` functions that accept ``awaitable objects``. + For example, the following calls are **not supported** :: + + await asyncio.shield(pywebio.input()) + await asyncio.gather(asyncio.sleep(1), pywebio.session.eval_js('1+1')) + task = asyncio.create_task(pywebio.input()) + +.. _coroutine_based_concurrency: + +Concurrency in coroutine-based sessions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In coroutine-based session, you can start new thread, but you cannot call PyWebIO interactive functions in it +(`register_thread() ` is not available in coroutine session). But you can use +`run_async(coro) ` to execute a coroutine object asynchronously, and PyWebIO interactive +functions can be used in the new coroutine: + +.. code-block:: python + :emphasize-lines: 10 + + from pywebio import start_server + from pywebio.session import run_async + + async def counter(n): + for i in range(n): + put_text(i) + await asyncio.sleep(1) + + async def main(): + run_async(counter(10)) + put_text('Main coroutine function exited.') + + + start_server(main, auto_open_webbrowser=True) + + +`run_async(coro) ` returns a `TaskHandler `, +which can be used to query the running status of the coroutine or close the coroutine. + +Close of session +^^^^^^^^^^^^^^^^^^^ + +Similar to thread-based session, when user close the browser page, the session will be closed. + +After the browser page closed, PyWebIO input function calls that have not yet returned in the current session will +cause `SessionClosedException `, and subsequent calls to PyWebIO interactive +functions will cause `SessionNotFoundException ` or +`SessionClosedException `. + +`defer_call(func) ` also available in coroutine session. + +.. _coroutine_web_integration: + +Integration with Web Framework +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The PyWebIO application that using coroutine-based session can also be integrated to the web framework. + +However, there are some limitations when using coroutine-based sessions to integrate into Flask or Django: + +First, when ``await`` the coroutine objects/awaitable objects in the ``asyncio`` module, you need to use +`run_asyncio_coroutine() ` to wrap the coroutine object. + +Secondly, you need to start a new thread to run the event loop before starting a Flask/Django server. + +Example of coroutine-based session integration into Flask: + +.. code-block:: python + :emphasize-lines: 12,20 + + import asyncio + import threading + from flask import Flask, send_from_directory + from pywebio import STATIC_PATH + from pywebio.output import * + from pywebio.platform.flask import webio_view + from pywebio.platform import run_event_loop + from pywebio.session import run_asyncio_coroutine + + async def hello_word(): + put_text('Hello ...') + await run_asyncio_coroutine(asyncio.sleep(1)) # can't just "await asyncio.sleep(1)" + put_text('... World!') + + app = Flask(__name__) + app.add_url_rule('/hello', 'webio_view', webio_view(hello_word), + methods=['GET', 'POST', 'OPTIONS']) + + # thread to run event loop + threading.Thread(target=run_event_loop, daemon=True).start() + app.run(host='localhost', port=80) + +Finally, coroutine-based session is not available in the script mode. You always need to use ``start_server()`` to +run coroutine task function or integrate it to a web framework. diff --git a/docs/arch.rst b/docs/arch.rst index e4697766..7b6b745e 100644 --- a/docs/arch.rst +++ b/docs/arch.rst @@ -4,5 +4,162 @@ Architecture 概念 ------------ -`Session` 表示浏览器与程序交互产生的一次会话。PyWebIO在会话中运行 ``Task`` ,任务是 +``Session`` 表示浏览器访问PyWebIO应用产生的一次会话。其生命周期从浏览器打开PyWebIO应用开始,到用户关闭浏览器页面或PyWebIO应用逻辑运行结束为止。 + +会话建立后,PyWebIO创建一个线程或协程来执行应用逻辑。这里的线程或协程在PyWebIO中被称为 ``Task`` (执行单元)。 +除了起始的执行单元(由PyWebIO框架启动),应用在会话中也可以自行启动新的执行单元,在新的执行单元中也可以进行输入输出。 + +在浏览器端,相同会话中的不同的执行单元的输入是独立的,共享输出空间,但输出域的栈结构各自独立。 + +若用户正在填写一个执行单元的表单,会话中的其他执行单元也开始向用户请求输入,此时用户正在填写的表单将会被新的表单覆盖, +当用户填写完新表单并提交后,旧表单重新显示,之前在旧表单上的输入也会保留。 + +在基于线程的会话中,会话中的每个执行单元都是一个线程;在基于协程的会话中,会话中的每个执行单元都是一个协程。 + +架构 +------------ + +会话内的每个执行单元使用唯一的task_id进行标识,由于会话内的输入需要区分执行单元,所以每个表单提交时, +除了表单的内容以外,还会携带表单所在的执行单元的task_id,这样,后台会话才可以知道该将表单数据传递给哪个执行单元。 + + +.. image:: /assets/architecture.png + +PyWebIO会话是由事件驱动的,这些事件来自用户在页面上的操作,比如提交表单,点击按钮,这些事件会通过http请求或websocket连接发送到后端框架。 + +后端框架维护有当前在线的Session实例,后端框架在收到用户提交的事件后,回调用相关Session实例的 ``send_client_event()`` 方法将事件发送至会话; + +一个会话内会拥有至少一个执行单元,执行单元在调用PyWebIO的输入函数后会临时挂起,当会话收到用户的输入提交后,会话便将执行单元恢复执行,并提供用户输入的值。 +执行单元内,任何输入输出的调用都会转换成一些命令序列发送给会话. + +当后端框架通过HTTP与用户浏览器通信时,用户浏览器是以轮训的方式获取指令,会话会保存由执行单元生成的、还未发送到浏览器的命令序列,等待下次轮训时由后端框架取走。 + +当后端框架通过WebSocket与用户建立连接时,任何由执行单元发送到会话的命令都会立即发送到后端,并由后端通过WebSocket连接通知用户浏览器。 + +实现 +------------ + +后端与Session的交互 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +后端框架负责从Session会话中获取来自PyWebIO的指令,并发送给用户浏览器;同时后端框架接收用户提交的数据,并发送给相应的会话实例。 + +Session暴露给后端框架的方法仅有 `Session.send_client_event ` 、 `Session.get_task_commands ` 和 `Session.close ` 。 + + +基于HTTP通信的后端的实现逻辑 +""""""""""""""""""""""""""""""""" + +**基于HTTP的前后端通信约定** + +前端按照固定间隔使用GET请求轮训后端接口,在请求中使用 ``webio-session-id`` HTTP头来传递会话ID。 + +会话一开始时,会话ID由后端生成并通过响应中的 ``webio-session-id`` HTTP头返回给前端,后续前端的请求都会在请求头中使用 ``webio-session-id`` 字段传递会话ID。 + +前端产生的事件使用POST请求发送给后端。对于前端的每次轮训和事件提交请求,后端都会返回当前未执行的指令序列作为响应,前端收到响应后会依次执行指令。 + +**代码实现** + +以Flask后端为例,Flask后端与Session的交互都在Flask视图函数中实现,视图函数通过调用 `pywebio.platform.flask.webio_view <./_modules/pywebio/platform/flask.html#webio_view>`_ 获取, +在 `webio_view` 中,先是实例化了一个 `pywebio.platform.httpbased.HttpHandler` ,然后声明了一个内部函数,这个内部函数就是Flask视图函数, +在视图函数内,先是实例化了一个 `pywebio.platform.flask.FlaskHttpContext` 对象,然后通过调用 ``HttpHandler.handle_request(FlaskHttpContext)`` 就获得了视图的响应。 + +这其中,FlaskHttpContext 的基类为 HttpContext ,HttpContext 接口各异的后端框架定义了一个统一的操作接口,用于从当前请求中获取请求相关的数据并设置请求的相应。 FlaskHttpContext 为HttpContext接口的Flask实现。 + +而 HttpHandle 负责维护Session实例并实现HTTP请求与Session之间的交互,HttpHandle 与后端框架相关的交互全都通过 HttpContext 操作。 + +HttpContext的生命周期为一次HTTP请求,HttpHandle的生命周期和整个后端框架的生命周期一致。 + +HttpHandler.handle_request 负责处理前端发送给后端的每一次请求,HttpHandler.handle_request 的处理流程如下: + +1. 检测当前HTTP请求是否满足跨域设置 +2. 根绝当前请求的 webio-session-id 头信息找到相应的Session实例,若不存在 webio-session-id 头则创建新会话并分配webio-session-id +3. 若当前请求为POST事件提交请求,则将提交的数据通过 Session.send_client_event 发送给Session +4. 通过调用 Session.get_task_commands 获取待执行的指令序列,并通过 HttpContext 向后端设置响应数据 + +此外,基于HTTP的会话,用户主动关闭会话时(比如关闭浏览器),后端无法立即感知,所以在HttpHandler.handle_request 中, +还会周期性地检测会话的最后活跃时间,将一段时间内不活跃的会话视为过期,所以在HttpHandler清理过期会话并调用 Session.close 释放会话内的资源。 + + +基于WebSocket通信的后端的实现逻辑 +"""""""""""""""""""""""""""""""""""" +**基于WebSocket的前后端通信约定:** + +浏览器与后端使用一个WebSocket连接来保持一个会话,后端的指令通过JSON序列化之后的消息实时发送给前端,前端用户触发的事件数据也通过JSON序列化之后发送给后端。 + +**代码实现** + +以Tornado后端为例 + +webio_handler用于获取Tornado与前端进行通信的WebSocketHandler子类,其逻辑实现在 _webio_handler 中,由于WebSocket的有状态性, +WebSocketHandler子类的实现比基于HTTP通信的HttpHandler要简单许多,关键部分如下: + +* 在WebSocket连接创建的时候初始化Session实例,并向Session对象注册了 on_task_command和on_session_close 回调,分别在新指令产生时和会话由执行单元关闭时由Session调用, + 用于实现WebSocketHandler向前端实时发送指令 +* 在收到前端浏览器发送来的消息后,WebSocketHandler将收到的数据通过 Session.send_client_event 发送给Session +* 在WebSocket连接关闭时,调用 Session.close 释放会话内的资源。 + +session与执行单元(输入/输出)的交互 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +会话提供给执行单元的关键接口有: + +* get_current_session : 静态方法,获取当前执行单元所属的会话实例 +* get_current_task_id : 静态方法,获取当前执行单元所属的id +* send_task_command : 向会话发送指令 +* next_client_event : 读取来自浏览器的属于当前执行单元的下一个事件 +* register_callback : 向会话注册一个回调 + +同时,会话根据实现方式不同,还分别提供了 register_thread 和 run_async 用于启动新的执行单元。 + + +**回调机制** + +在会话中,为了能够响应用户在界面上的某些事件(比如点击了输出内容中的某个按钮),于是设计了回调机制,可以在执行单元中使用register_callback向当前会话注册回调,然后执行单元会得到一个回调ID, +执行单元再通过相关指令让浏览器输出一些可以触发的控件,并向控件绑定回调ID,当用户触发控件后,前端将带有回调ID的 :ref:`回调事件 ` 发回会话,会话会在专门的执行单元中或启动新执行单元中运行回调。 + +基于线程的会话实现 +"""""""""""""""""""""""""""""""""""" + +在基于线程的会话中,每个执行单元都是一个线程,每个执行单元通过一条消息队列从会话接收来自用户的事件消息,当执行单元所需要的事件用户还没有提交时,执行单元便会挂起。 + +基于线程的会话使用线程ID作为执行单元的ID,在全局使用一个以线程id为key的字典来映射执行单元所属的会话实例,会话内不同执行单元的用户事件消息队列也通过执行单元ID进行索引。 + +使用 register_thread 启动新的执行单元时,也需要为新执行单元注册用户事件消息队列。 + +基于协程的会话实现 +"""""""""""""""""""""""""""""""""""" +在基于协程的会话中,每个执行单元都是一个由协程包装成的任务对象(Task),当会话接收来自用户的事件消息后,便激活相应的任务对象,使得协程恢复运行。 + +由于基于协程的会话是单线程的,所以会话在激活任务对象前是通过将上下文信息保存在全局变量中来实现 get_current_session 和 get_current_task_id 方法,全局的上下文信息包含当前将要执行的会话的实例和执行单元的ID。 + + +Script mode的实现 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Script mode 也是基于线程的,但由于全局仅存在一个会话,所有执行单元必定全部属于这个会话,所以也无需主动调用register_thread(thread)注册线程。 + +当PyWebIO检测到用户代码在后端Server还未启动的情况下就调用了PyWebIO交互函数时,便会启动Script mode: + +1. 在新线程中启动后端Server +2. 启动浏览器打开后端Server运行的地址 +3. 在第一次与用户建立连接时初始化会话 + +script mode的会话类继承了基于线程的会话类,并修改了部分方法: + +* 构造函数 : 仅允许script mode会话类被初始化一次 +* get_current_session : 直接返回全局的会话对象 +* get_current_task_id : 除了返回当前线程id,还会自动将当前线程使用 register_thread 注册到会话中 + +相关对象的文档 +------------------------ + +.. autoclass:: pywebio.platform.httpbased.HttpHandler + :members: + +.. autoclass:: pywebio.platform.flask.FlaskHttpContext + :members: + +.. autoclass:: pywebio.session.base.Session + :members: + diff --git a/docs/assets/architecture.png b/docs/assets/architecture.png new file mode 100644 index 00000000..b9faee43 Binary files /dev/null and b/docs/assets/architecture.png differ diff --git a/docs/assets/demo.gif b/docs/assets/demo.gif index 17815cf1..2d8b0f7c 100644 Binary files a/docs/assets/demo.gif and b/docs/assets/demo.gif differ diff --git a/docs/assets/demo.png b/docs/assets/demo.png index 1cd1414f..b4885c03 100644 Binary files a/docs/assets/demo.png and b/docs/assets/demo.png differ diff --git a/docs/assets/demo_zh.gif b/docs/assets/demo_zh.gif new file mode 100644 index 00000000..17815cf1 Binary files /dev/null and b/docs/assets/demo_zh.gif differ diff --git a/docs/assets/input_1.png b/docs/assets/input_1.png index 058f656d..f800009d 100644 Binary files a/docs/assets/input_1.png and b/docs/assets/input_1.png differ diff --git a/docs/assets/input_2.png b/docs/assets/input_2.png index 7b2db0c6..42d58655 100644 Binary files a/docs/assets/input_2.png and b/docs/assets/input_2.png differ diff --git a/docs/assets/theme/dark.png b/docs/assets/theme/dark.png new file mode 100644 index 00000000..2c55cf6b Binary files /dev/null and b/docs/assets/theme/dark.png differ diff --git a/docs/assets/theme/default.png b/docs/assets/theme/default.png new file mode 100644 index 00000000..591df653 Binary files /dev/null and b/docs/assets/theme/default.png differ diff --git a/docs/assets/theme/minty.png b/docs/assets/theme/minty.png new file mode 100644 index 00000000..b27fa5c2 Binary files /dev/null and b/docs/assets/theme/minty.png differ diff --git a/docs/assets/theme/sketchy.png b/docs/assets/theme/sketchy.png new file mode 100644 index 00000000..fb0a214f Binary files /dev/null and b/docs/assets/theme/sketchy.png differ diff --git a/docs/assets/theme/yeti.png b/docs/assets/theme/yeti.png new file mode 100644 index 00000000..dbfb9b6c Binary files /dev/null and b/docs/assets/theme/yeti.png differ diff --git a/docs/battery.rst b/docs/battery.rst new file mode 100644 index 00000000..37c3e6a9 --- /dev/null +++ b/docs/battery.rst @@ -0,0 +1,2 @@ +.. automodule:: pywebio_battery + :members: \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 12eb762c..6c8d8a5e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -19,8 +19,8 @@ # -- Project information ----------------------------------------------------- project = 'PyWebIO' -copyright = 'WangWeimin' -author = 'WangWeimin' +copyright = 'Weimin Wang' +author = 'Weimin Wang' # -- General configuration --------------------------------------------------- @@ -33,9 +33,13 @@ "sphinx.ext.viewcode", 'sphinx_tabs.tabs', 'sphinx.ext.extlinks', - 'codeblock' + 'codeblock', # defined in docs/_ext/codeblock.py + 'sphinx_toolbox.collapse', ] +# https://github.com/sphinx-doc/sphinx/issues/6316 +toc_object_entries = False + primary_domain = "py" default_role = "py:obj" # intersphinx_mapping = {"python": ("https://docs.python.org/3.6/", None)} @@ -49,7 +53,8 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = 'zh_CN' +language = 'en' +gettext_additional_targets = ["literal-block"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -71,7 +76,9 @@ def setup(app): """Configure Sphinx""" - app.add_stylesheet('pywebio.css') + app.add_css_file('pywebio.css') + # from docutils.parsers.rst.directives.admonitions import Note + # app.add_directive('collapse', Note) # -- Extension configuration ------------------------------------------------- diff --git a/docs/cookbook.rst b/docs/cookbook.rst new file mode 100644 index 00000000..d22b6d2f --- /dev/null +++ b/docs/cookbook.rst @@ -0,0 +1,135 @@ +Cookbook +========================== + +.. seealso:: :doc:`PyWebIO Battery ` + +.. contents:: + :local: + +Interaction related +---------------------------------------------------------------------------------------------- + +Equivalent to "Press any key to continue" +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. exportable-codeblock:: + :name: cookbook-press-anykey-continue + :summary: Press any key to continue + + actions(buttons=["Continue"]) + put_text("Go next") # ..demo-only + + +Output pandas dataframe +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. exportable-codeblock:: + :name: cookbook-pandas-df + :summary: Output pandas dataframe + + import numpy as np + import pandas as pd + + df = pd.DataFrame(np.random.randn(6, 4), columns=list("ABCD")) + put_html(df.to_html(border=0)) + +.. seealso:: `pandas.DataFrame.to_html — pandas documentation `_ + +Output Matplotlib figure +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Instead of using ``matplotlib.pyplot.show()``, to show matplotlib figure in PyWebIO, you need to save the figure to in-memory buffer fist and then output the buffer +via :func:`pywebio.output.put_image`: + +.. exportable-codeblock:: + :name: cookbook-matplotlib + :summary: Output Matplotlib plot + + import matplotlib + import matplotlib.pyplot as plt + import io + import pywebio + + matplotlib.use('agg') # required, use a non-interactive backend + + fig, ax = plt.subplots() # Create a figure containing a single axes. + ax.plot([1, 2, 3, 4], [1, 4, 2, 3]) # Plot some data on the axes. + + buf = io.BytesIO() + fig.savefig(buf) + pywebio.output.put_image(buf.getvalue()) + +The ``matplotlib.use('agg')`` is required so that the server does not try to create (and then destroy) GUI windows +that will never be seen. + +When using Matplotlib in a web server (multiple threads environment), pyplot may cause some conflicts in some cases, +read the following articles for more information: + + * `Multi Threading in Python and Pyplot | by Ranjitha Korrapati | Medium `_ + + * `Embedding in a web application server (Flask) — Matplotlib documentation `_ + + +Add new syntax highlight for code output +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When output code via `put_markdown()` or `put_code()`, PyWebIO provides syntax highlight for some common languages. +If you find your code have no syntax highlight, you can add the syntax highlighter by two following steps: + +1. Go to `prismjs CDN page `_ to get your syntax highlighter link. +2. Use :func:`config(js_file=...) ` to load the syntax highlight module + +:: + + @config(js_file="https://cdn.jsdelivr.net/npm/prismjs@1.23.0/components/prism-diff.min.js") + def main(): + put_code(""" + + AAA + - BBB + CCC + """.strip(), language='diff') + + put_markdown(""" + ```diff + + AAA + - BBB + CCC + ``` + """, lstrip=True) + + + +Web application related +---------------------------------------------------------------------------------------------- + +Add Google AdSense/Analytics code +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When you setup Google AdSense/Analytics, you will get a javascript file and a piece of code that needs to be inserted +into your application page, you can use :func:`pywebio.config()` to inject js file and code to your PyWebIO application:: + + from pywebio import start_server, output, config + + js_file = "https://www.googletagmanager.com/gtag/js?id=G-xxxxxxx" + js_code = """ + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + + gtag('config', 'G-xxxxxxx'); + """ + + @config(js_file=js_file, js_code=js_code) + def main(): + output.put_text("hello world") + + start_server(main, port=8080) + + +Refresh page on connection lost +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Add the following code to the beginning of your PyWebIO application main function:: + + session.run_js('WebIO._state.CurrentSession.on_session_close(()=>{setTimeout(()=>location.reload(), 4000})') + diff --git a/docs/demos.rst b/docs/demos.rst deleted file mode 100644 index dbff9423..00000000 --- a/docs/demos.rst +++ /dev/null @@ -1,12 +0,0 @@ -示例Demos -========== - -基本demo ------------- -使用PyWebIO编写的示例应用 - -.. automodule:: demos - -数据可视化demo ------------------ -PyWebIO支持使用第三方库进行数据可视化,详情见 :ref:`使用PyWebIO进行数据可视化 ` \ No newline at end of file diff --git a/docs/guide.rst b/docs/guide.rst index 38711a5a..5b955ed4 100644 --- a/docs/guide.rst +++ b/docs/guide.rst @@ -1,98 +1,108 @@ User's guide ============ -如果你接触过Web开发,你可能对接下来描述的PyWebIO的用法感到不太习惯,不同于传统Web开发的后端实现接口、前端进行展示交互的模式,在PyWebIO中,所有的逻辑都通过编写Python代码实现。 - -你可以按照编写控制台程序的逻辑编写PyWebIO应用,只不过这里的终端变成了浏览器。通过PyWebIO提供的命令式API, -你可以简单地调用 ``put_text`` 、 ``put_image`` 、 ``put_table`` 等函数输出文本、图片、表格等内容到浏览器,也可以调用 ``input`` 、 ``select`` 、 -``file_upload`` 等函数在浏览器上显示不同表单来接收用户的输入。此外PyWebIO中还提供了点击事件、布局等支持,让你可以使用最少的代码完成与用户的交互, -并尽可能提供良好的用户体验。 - -本篇使用指南从几个方面对PyWebIO的使用进行介绍,覆盖了PyWebIO的绝大部分特性。本文档中大部分示例代码的右上方都有一个Demo链接,点击后可以在线预览代码的运行效果。 - -输入 +If you are familiar with web development, you may not be accustomed to the usage of PyWebIO described below, which is +different from the traditional web development pattern that backend implement api and frontend display content. +In PyWebIO, you only need to write code in Python. + +In fact, the way of writing PyWebIO applications is more like writing a console program, except that the terminal here +becomes a browser. Using the imperative API provided by PyWebIO, you can simply call ``put_text()``, ``put_image()``, +``put_table()`` and other functions to output text, pictures, tables and other content to the browser, or you can call +some functions such as ``input()``, ``select()``, ``file_upload()`` to display different forms on the browser to get +user input. In addition, PyWebIO also provides support for click events, layout, etc. PyWebIO aims to allow you to use +the least code to interact with the user and provide a good user experience as much as possible. + +This user guide introduces you the most of the features of PyWebIO. There is a demo link at the top right of the example +codes in this document, where you can run the example code online and see what happens. Also, the +`PyWebIO Playground `_ is a good place to write, run and share your PyWebIO code online. + +Input ------------ -输入函数都定义在 :doc:`pywebio.input ` 模块中,可以使用 ``from pywebio.input import *`` 引入。 +The input functions are defined in the :doc:`pywebio.input ` module and can be imported using ``from pywebio.input import *``. -调用输入函数会在浏览器上弹出一个输入表单来获取输入。PyWebIO的输入函数是阻塞式的(和Python内置的 `input` 一样),在表单被成功提交之前,输入函数不会返回。 +When calling the input function, an input form will be popped up on the browser. PyWebIO's input functions is blocking +(same as Python's built-in ``input()`` function) and will not return until the form is successfully submitted. -基本输入 -^^^^^^^^^^^ +Basic input +^^^^^^^^^^^^^ -首先是一些基本类型的输入 +Here are some basic types of input. -文本输入: +Text input: .. exportable-codeblock:: :name: text-input - :summary: 文本输入 + :summary: Text input age = input("How old are you?", type=NUMBER) put_text('age = %r' % age) # ..demo-only -这样一行代码的效果为:浏览器会弹出一个文本输入框来获取输入,在用户完成输入将表单提交后,函数返回用户输入的值。 +After running the above code, the browser will pop up a text input field to get the input. After the user completes the +input and submits the form, the function returns the value entered by the user. -下面是一些其他类型的输入函数: +Here are some other types of input functions: .. exportable-codeblock:: :name: basic-input - :summary: 基本输入 + :summary: Basic input - # 密码输入 + # Password input password = input("Input password", type=PASSWORD) put_text('password = %r' % password) # ..demo-only ## ---- - # 下拉选择框 + # Drop-down selection gift = select('Which gift you want?', ['keyboard', 'ipad']) put_text('gift = %r' % gift) # ..demo-only ## ---- - # 勾选选项 - agree = checkbox("用户协议", options=['I agree to terms and conditions']) + # Checkbox + agree = checkbox("User Term", options=['I agree to terms and conditions']) put_text('agree = %r' % agree) # ..demo-only ## ---- - # 单选选项 + # Single choice answer = radio("Choose one", options=['A', 'B', 'C', 'D']) put_text('answer = %r' % answer) # ..demo-only ## ---- - # 多行文本输入 + # Multi-line text input text = textarea('Text Area', rows=3, placeholder='Some text') put_text('text = %r' % text) # ..demo-only ## ---- - # 文件上传 + # File Upload img = file_upload("Select a image:", accept="image/*") if img: # ..demo-only put_image(img['content'], title=img['filename']) # ..demo-only -输入选项 -^^^^^^^^^^^ +Parameter of input functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -输入函数可指定的参数非常丰富(全部参数及含义请见 :doc:`函数文档 ` ): +There are many parameters that can be passed to the input function(for complete parameters, please refer to the +:doc:`function document `): .. exportable-codeblock:: :name: input-args - :summary: 输入参数 + :summary: Parameter of input functions input('This is label', type=TEXT, placeholder='This is placeholder', help_text='This is help text', required=True) -以上代码将在浏览器上显示如下: +The results of the above example are as follows: .. image:: /assets/input_1.png -我们可以为输入指定校验函数,校验函数应在校验通过时返回None,否则返回错误消息: +You can specify a validation function for the input by using ``validate`` parameter. The validation function should +return ``None`` when the check passes, otherwise an error message will be returned: .. exportable-codeblock:: :name: input-valid-func - :summary: 输入指定校验函数 + :summary: Input validate function for - def check_age(p): # 检验函数校验通过时返回None,否则返回错误消息 + def check_age(p): # return None when the check passes, otherwise return the error message if p < 10: return 'Too young!!' if p > 60: @@ -101,39 +111,40 @@ User's guide age = input("How old are you?", type=NUMBER, validate=check_age) put_text('age = %r' % age) # ..demo-only -当用户输入了不合法的值时,页面上的显示如下: +When the user input an illegal value, the input field is displayed as follows: .. image:: /assets/input_2.png - -:func:`pywebio.input.textarea` 还支持使用 `Codemirror `_ 实现代码风格的编辑区,只需使用 ``code`` 参数传入Codemirror支持的选项即可(最简单的情况是直接传入 ``code={}`` 或 ``code=True``): +You can use ``code`` parameter in :func:`pywebio.input.textarea()` to make a code editing textarea. .. exportable-codeblock:: :name: codemirror - :summary: textarea代码编辑 + :summary: Code editing by using textarea code = textarea('Code Edit', code={ - 'mode': "python", # 编辑区代码语言 - 'theme': 'darcula', # 编辑区darcula主题, Visit https://codemirror.net/demo/theme.html#cobalt to get more themes + 'mode': "python", + 'theme': 'darcula', }, value='import something\n# Write your python code') put_code(code, language='python') # ..demo-only -文本框的显示效果为: +The results of the above example are as follows: .. image:: /assets/codemirror_textarea.png -:ref:`这里 ` 列举了一些常用的Codemirror选项,完整的Codemirror选项请见:https://codemirror.net/doc/manual.html#config -输入组 -^^^^^^^ +Input Group +^^^^^^^^^^^^^ + +PyWebIO uses input group to get multiple inputs in a single form. `pywebio.input.input_group()` accepts a list of +single input function call as parameter, and returns a dictionary with the ``name`` of the single input as its key +and the input data as its value: -PyWebIO支持输入组, 返回结果为一个字典。`pywebio.input.input_group()` 接受单项输入组成的列表作为参数, 返回以单项输入函数中的 ``name`` 作为键、以输入数据为值的字典: .. exportable-codeblock:: :name: input-group - :summary: 输入组 + :summary: Input Group - def check_age(p): # 检验函数校验通过时返回None,否则返回错误消息 # ..demo-only + def check_age(p): # ..demo-only if p < 10: # ..demo-only return 'Too young!!' # ..demo-only if p > 60: # ..demo-only @@ -145,23 +156,23 @@ PyWebIO支持输入组, 返回结果为一个字典。`pywebio.input.input_group ]) put_text(data['name'], data['age']) -输入组中同样支持使用 ``validate`` 参数设置校验函数,其接受整个表单数据作为参数: +The input group also supports using ``validate`` parameter to set the validation function, which accepts the entire form data as parameter: .. exportable-codeblock:: - :name: input-group - :summary: 输入组 + :name: input-group-validate + :summary: Input Group validation - def check_age(p): # 检验函数校验通过时返回None,否则返回错误消息 # ..demo-only + def check_age(p): # single input item validation # ..demo-only if p < 10: # ..demo-only return 'Too young!!' # ..demo-only if p > 60: # ..demo-only return 'Too old!!' # ..demo-only # ..demo-only - def check_form(data): # 检验函数校验通过时返回None,否则返回 (input name,错误消息) + def check_form(data): # return (input name, error msg) when validation fail if len(data['name']) > 6: - return ('name', '名字太长!') + return ('name', 'Name too long!') if data['age'] <= 0: - return ('age', '年龄不能为负数!') + return ('age', 'Age can not be negative!') data = input_group("Basic info",[ # ..demo-only input('Input your name', name='name'), # ..demo-only @@ -170,67 +181,95 @@ PyWebIO支持输入组, 返回结果为一个字典。`pywebio.input.input_group put_text(data['name'], data['age']) # ..demo-only .. attention:: - PyWebIO 根据是否在输入函数中传入 ``name`` 参数来判断输入函数是在 `input_group` 中还是被单独调用。 - 所以当单独调用一个输入函数时, **不要** 设置 ``name`` 参数;而在 `input_group` 中调用输入函数时,需 **务必提供** ``name`` 参数 + PyWebIO determines whether the input function is in `input_group()` or is called alone according to whether the + ``name`` parameter is passed. So when calling an input function alone, **do not** set the ``name`` parameter; + when calling the input function in `input_group()`, you **must** provide the ``name`` parameter. -输出 +Output ------------ -输出函数都定义在 :doc:`pywebio.output ` 模块中,可以使用 ``from pywebio.output import *`` 引入。 +The output functions are all defined in the :doc:`pywebio.output ` module and can be imported using +``from pywebio.output import *``. -调用输出函数后,内容会实时输出到浏览器,在应用的生命周期内,可以在任意时刻调用输出函数。 +When output functions is called, the content will be output to the browser in real time. The output functions +can be called at any time during the application lifetime. -基本输出 +Basic Output ^^^^^^^^^^^^^^ -PyWebIO提供了一系列函数来输出表格、链接等格式: +Using output functions, you can output a variety of content, such as text, tables, images and so on: .. exportable-codeblock:: :name: basic-output - :summary: 基本输出 + :summary: Basic Output - # 文本输出 + # Text Output put_text("Hello world!") ## ---- - # 表格输出 + # Table Output put_table([ - ['商品', '价格'], - ['苹果', '5.5'], - ['香蕉', '7'], + ['Commodity', 'Price'], + ['Apple', '5.5'], + ['Banana', '7'], ]) ## ---- - # Markdown输出 - put_markdown('~~删除线~~') + # Image Output + put_image(open('/path/to/some/image.png', 'rb').read()) # local image # ..doc-only + put_image('http://example.com/some-image.png') # internet image # ..doc-only + put_image('https://www.python.org/static/img/python-logo.png') # ..demo-only ## ---- - # 文件输出 + # Markdown Output + put_markdown('~~Strikethrough~~') + ## ---- + + # File Output put_file('hello_word.txt', b'hello word!') ## ---- - # 显示一个弹窗 + # Show a PopUp popup('popup title', 'popup text content') + # Show a notification message + toast('New message 🔔') + + +For all output functions provided by PyWebIO, please refer to the :doc:`pywebio.output ` module. +In addition, PyWebIO also supports data visualization with some third-party libraries, +see :doc:`Third-party library ecology `. + + +.. note:: + + If you use PyWebIO in interactive execution environment of Python shell, IPython or jupyter notebook, + you need call `show()` method explicitly to show output:: + + >>> put_text("Hello world!").show() + >>> put_table([ + ... ['A', 'B'], + ... [put_markdown(...), put_text('C')] + ... ]).show() -PyWebIO提供的全部输出函数见 :doc:`pywebio.output ` 模块。另外,PyWebIO还支持一些第三方库来进行数据可视化,参见 :doc:`第三方库生态 ` 。 .. _combine_output: -组合输出 -^^^^^^^^^^^^^^ -函数名以 ``put_`` 开始的输出函数,可以与一些输出函数组合使用,作为最终输出的一部分: +Combined Output +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The output functions whose name starts with ``put_`` can be combined with some output functions as part of the final output: -`put_table() ` 支持以 ``put_xxx()`` 调用作为单元格内容: +You can pass ``put_xxx()`` calls to `put_table() ` as cell content: .. exportable-codeblock:: :name: putxxx - :summary: 组合输出 + :summary: Combination output put_table([ ['Type', 'Content'], ['html', put_html('X2')], - ['text', '
'], # 等价于 ['text', put_text('
')] + ['text', '
'], # equal to ['text', put_text('
')] ['buttons', put_buttons(['A', 'B'], onclick=...)], # ..doc-only ['buttons', put_buttons(['A', 'B'], onclick=put_text)], # ..demo-only ['markdown', put_markdown('`Awesome PyWebIO!`')], @@ -238,60 +277,67 @@ PyWebIO提供的全部输出函数见 :doc:`pywebio.output ` 模块。 ['table', put_table([['A', 'B'], ['C', 'D']])] ]) -上例显示效果如下: +The results of the above example are as follows: .. image:: /assets/put_table.png -类似地, `popup() ` 也可以将 ``put_xxx()`` 调用作为弹窗内容: +Similarly, you can pass ``put_xxx()`` calls to `popup() ` as the popup content: .. exportable-codeblock:: :name: popup - :summary: 弹窗 + :summary: Popup popup('Popup title', [ put_html('

Popup Content

'), - 'plain html:
', # 等价于 put_text('plain html:
') + 'plain html:
', # Equivalent to: put_text('plain html:
') put_table([['A', 'B'], ['C', 'D']]), - put_buttons(['close_popup()'], onclick=lambda _: close_popup()) + put_button('close_popup()', onclick=close_popup) ]) -其他接受 ``put_xxx()`` 调用作为参数的输出函数还有 `put_collapse() ` 、 `put_scrollable() ` 、`put_row() ` 等, -此外,还可以通过 `put_widget() ` 自定义可接收 ``put_xxx()`` 调用的输出组件,具体用法请参考函数文档。 +In addition, you can use `put_widget() ` to make your own output widgets that can accept ``put_xxx()`` calls. -使用组合输出时,如果想在内容输出后,对其中的 ``put_xxx()`` 子项进行动态修改,可以使用 `output() ` 函数, -`output() ` 就像一个占位符,它可以像 ``put_xxx()`` 一样传入 `put_table` 、 `popup` 、 `put_widget` 等函数中作为输出的一部分, -并且,在输出后,还可以对其中的内容进行修改(比如重置或增加内容): +For a full list of functions that accept ``put_xxx()`` calls as content, see :ref:`Output functions list ` + +**Context Manager** + +Some output functions that accept ``put_xxx()`` calls as content can be used as context manager: .. exportable-codeblock:: - :name: output - :summary: 内容占位符——`output()` + :name: output-context-manager + :summary: Output as context manager - hobby = output(put_text('Coding')) - put_table([ - ['Name', 'Hobbies'], - ['Wang', hobby] # hobby 初始为 Coding - ]) - ## ---- + with put_collapse('This is title'): + for i in range(4): + put_text(i) - hobby.reset(put_text('Movie')) # hobby 被重置为 Movie - ## ---- - hobby.append(put_text('Music'), put_text('Drama')) # 向 hobby 追加 Music, Drama - ## ---- - hobby.insert(0, put_markdown('**Coding**')) # 将 Coding 插入 hobby 顶端 + put_table([ + ['Commodity', 'Price'], + ['Apple', '5.5'], + ['Banana', '7'], + ]) +For a full list of functions that support context manager, see :ref:`Output functions list ` -事件回调 -^^^^^^^^^^^^^^ -从上面可以看出,PyWebIO把交互分成了输入和输出两部分:输入函数为阻塞式调用,会在用户浏览器上显示一个表单,在用户提交表单之前输入函数将不会返回;输出函数将内容实时输出至浏览器。这种交互方式和控制台程序是一致的,因此PyWebIO应用非常适合使用控制台程序的编写逻辑来进行开发。 +.. _callback: + +Click Callback +^^^^^^^^^^^^^^^^ -此外,PyWebIO还支持事件回调:PyWebIO允许你输出一些控件,当控件被点击时执行提供的回调函数。 +As we can see from the above, the interaction of PyWebIO has two parts: input and output. The input function of PyWebIO +is blocking, a form will be displayed on the user's web browser when calling input function, the input function will +not return until the user submits the form. The output function is used to output content to the browser in real time. +The input and output behavior of PyWebIO is consistent with the console program. That's why we say PyWebIO turning the +browser into a "rich text terminal". So you can write PyWebIO applications in script programming way. -下面是一个例子: +In addition, PyWebIO also supports event callbacks: PyWebIO allows you to output some buttons and bind callbacks to them. +The provided callback function will be executed when the button is clicked. + +This is an example: .. exportable-codeblock:: :name: onclick-callback - :summary: 事件回调 + :summary: Event callback from functools import partial @@ -305,60 +351,82 @@ PyWebIO提供的全部输出函数见 :doc:`pywebio.output ` 模块。 [3, put_buttons(['edit', 'delete'], onclick=partial(edit_row, row=3))], ]) -`put_table() ` 的调用不会阻塞。当用户点击了某行中的按钮时,PyWebIO会自动调用相应的回调函数: +The call to `put_table() ` will not block. When user clicks a button, the corresponding +callback function will be invoked: .. image:: /assets/table_onclick.* -当然,PyWebIO还支持单独的按钮控件: +Of course, PyWebIO also supports outputting individual button: .. exportable-codeblock:: :name: put-buttons - :summary: 按钮控件 + :summary: Event callback of button widget def btn_click(btn_val): put_text("You click %s button" % btn_val) - put_buttons(['A', 'B', 'C'], onclick=btn_click) -.. note:: - 在PyWebIO会话(关于会话的概念见下文 :ref:`Server与script模式 ` )结束后,事件回调也将不起作用,你可以在任务函数末尾处使用 :func:`pywebio.session.hold()` 函数来将会话保持,这样在用户关闭浏览器页面前,事件回调将一直可用。 + put_buttons(['A', 'B', 'C'], onclick=btn_click) # a group of buttons + + put_button("Click me", onclick=lambda: toast("Clicked")) # single button -输出域Scope +In fact, all output can be bound to click events, not just buttons. You can call ``onclick()`` method after the output +function (function name like ``put_xxx()``) call: + +.. exportable-codeblock:: + :name: onclick + :summary: Click callback on any output + + put_image('some-image.png').onclick(lambda: toast('You click an image')) # ..doc-only + put_image('https://www.python.org/static/img/python-logo.png').onclick(lambda: toast('You click an image')) # ..demo-only + + # set onclick in combined output + put_table([ + ['Commodity', 'Price'], + ['Apple', put_text('5.5').onclick(lambda: toast('You click the text'))], + ]) + +The return value of ``onclick()`` method is the object itself so it can be used in combined output. + +.. _output_scope: + +Output Scope ^^^^^^^^^^^^^^ -PyWebIO使用Scope模型来对内容输出的位置进行灵活地控制,PyWebIO的内容输出区可以划分出不同的输出域,PyWebIO将输出域称作 `Scope` 。 -输出域为输出内容的容器,各个输出域之间上下排列,输出域也可以进行嵌套。 +PyWebIO uses the scope model to give more control to the location of content output. The output scope is a container +of output content. You can create a scope in somewhere and append content to it. -每个输出函数(函数名形如 `put_xxx()` )都会将内容输出到一个Scope,默认为"当前Scope","当前Scope"由运行时上下文确定,输出函数也可以手动指定输出到的Scope。Scope名在会话内唯一。 +Each output function (function name like ``put_xxx()``) will output its content to a scope, the default is "current scope". +The "current scope" is set by `use_scope() `. .. _use_scope: **use_scope()** -可以使用 `use_scope() ` 开启并进入一个新的输出域,或进入一个已经存在的输出域: +You can use `use_scope() ` to open and enter a new output scope, or enter an existing output scope: .. exportable-codeblock:: :name: use-scope - :summary: 使用`use_scope()`创建或进入输出域 + :summary: use `use_scope()` to open or enter scope - with use_scope('scope1'): # 创建并进入scope 'scope1' - put_text('text1 in scope1') + with use_scope('scope1'): # open and enter a new output: 'scope1' + put_text('text1 in scope1') # output text to scope1 - put_text('text in parent scope of scope1') + put_text('text in parent scope of scope1') # output text to ROOT scope - with use_scope('scope1'): # 进入之前创建的scope 'scope1' - put_text('text2 in scope1') + with use_scope('scope1'): # enter an existing scope: 'scope1' + put_text('text2 in scope1') # output text to scope1 -以上代码将会输出:: +The results of the above code are as follows:: text1 in scope1 text2 in scope1 text in parent scope of scope1 -`use_scope() ` 还可以使用 `clear` 参数将scope中原有的内容清空: +You can use ``clear`` parameter in `use_scope() ` to clear the existing content before entering the scope: .. exportable-codeblock:: - :name: use-scope - :summary: 使用`use_scope()`清空输出域内容 + :name: use-scope-clear + :summary: `use_scope()`'s `clear` parameter with use_scope('scope2'): put_text('create scope2') @@ -366,19 +434,19 @@ PyWebIO使用Scope模型来对内容输出的位置进行灵活地控制,PyWeb put_text('text in parent scope of scope2') ## ---- - with use_scope('scope2', clear=True): # 进入之前创建的scope2,并清空原有内容 + with use_scope('scope2', clear=True): # enter the existing scope and clear the previous content put_text('text in scope2') -以上代码将会输出:: +The results of the above code are as follows:: text in scope2 text in parent scope of scope2 -`use_scope() ` 还可以作为装饰器来使用: +`use_scope() ` can also be used as decorator: .. exportable-codeblock:: :name: use-scope-decorator - :summary: `use_scope()`作为装饰器来使用 + :summary: `use_scope()` as decorator import time # ..demo-only from datetime import datetime @@ -391,16 +459,15 @@ PyWebIO使用Scope模型来对内容输出的位置进行灵活地控制,PyWeb show_time() # ..demo-only time.sleep(1) # ..demo-only -第一次调用 ``show_time`` 时,将会在当前位置创建 ``time`` 输出域并在其中输出当前时间,之后每次调用 ``show_time()`` ,时间都会输出到相同的区域。 +When calling ``show_time()`` for the first time, a ``time`` scope will be created, and the current time will be output +to it. And then every time the ``show_time()`` is called, the new content will replace the previous content. -Scope是可嵌套的,初始条件下,PyWebIO应用只有一个最顶层的 ``ROOT`` Scope。每创建一个新Scope,Scope的嵌套层级便会多加一层,每退出当前Scope,Scope的嵌套层级便会减少一层。 -PyWebIO使用Scope栈来保存运行时的Scope的嵌套层级。 - -例如,如下代码将会创建3个Scope: +Scopes can be nested. At the beginning, PyWebIO applications have only one ``ROOT`` scope. +You can create new scope in a scope. For example, the following code will create 3 scopes: .. exportable-codeblock:: :name: use-scope-nested - :summary: 嵌套Scope + :summary: Nested Scope with use_scope('A'): put_text('Text in scope A') @@ -420,7 +487,7 @@ PyWebIO使用Scope栈来保存运行时的Scope的嵌套层级。 put_buttons([('Put text to %s' % i, i) for i in ('A', 'B', 'C')], lambda s: put_text(s, scope=s)) # ..demo-only -以上代码将会产生如下Scope布局:: +The above code will generate the following scope layout:: ┌─ROOT────────────────────┐ │ │ @@ -436,103 +503,78 @@ PyWebIO使用Scope栈来保存运行时的Scope的嵌套层级。 │ └─────────────────────┘ │ └─────────────────────────┘ -.. _scope_param: - -**输出函数的scope相关参数** +.. _put_scope: -输出函数(函数名形如 ``put_xxx()`` )在默认情况下,会将内容输出到"当前Scope",可以通过 ``use_scope()`` 设置运行时上下文的"当前Scope"。 +**put_scope()** -此外,也可以通过输出函数的 ``scope`` 参数指定输出的目的Scope: +We already know that the scope is a container of output content. So can we use this container as a sub-item +of a output (like, set a cell in table as a container)? Yes, you can use `put_scope() ` to +create a scope explicitly. +The function name starts with ``put_``, which means it can be pass to the functions that accept ``put_xxx()`` calls. .. exportable-codeblock:: - :name: put-xxx-scope - :summary: 输出函数的`scope`参数 + :name: put_scope + :summary: `put_scope()` - with use_scope('scope3'): - put_text('text1 in scope3') # 输出到当前Scope:scope3 - put_text('text in ROOT scope', scope='ROOT') # 输出到ROOT Scope - - put_text('text2 in scope3', scope='scope3') # 输出到scope3 - -以上将会输出:: - - text1 in scope3 - text2 in scope3 - text in ROOT scope - -``scope`` 参数除了直接指定目标Scope名,还可以使用一个整形通过索引Scope栈来确定Scope:0表示最顶层也就是ROOT Scope,-1表示当前Scope,-2表示进入当前Scope前所使用的Scope,...... - -默认条件下,在同一Scope中的输出内容,会根据输出函数的调用顺序从上往下排列,最后调用的输出函数会输出内容到目标Scope的底部。通过输出函数的 ``position`` 参数可以将输出内容插入到目标Scope的其他位置。 - -一个Scope中各次输出的元素具有像数组一样的索引,最前面的编号为0,以此往后递增加一;同样可以使用负数对Scope中的元素进行索引,-1表示最后面的元素,-2表示次后面的元素...... - -``position`` 参数类型为整形, ``position>=0`` 时表示输出内容到目标Scope的第position号元素的前面; ``position<0`` 时表示输出内容到目标Scope第position号元素之后: - -.. exportable-codeblock:: - :name: put-xxx-position - :summary: 输出函数的`position`参数 + put_table([ + ['Name', 'Hobbies'], + ['Tom', put_scope('hobby', content=put_text('Coding'))] # hobby is initialized to coding + ]) - with use_scope('scope1'): - put_text('A') # 输出内容: A ## ---- - with use_scope('scope1'): # ..demo-only - put_text('B', position=0) # 输出内容: B A - ## ---- - with use_scope('scope1'): # ..demo-only - put_text('C', position=-2) # 输出内容: B C A - ## ---- - with use_scope('scope1'): # ..demo-only - put_text('D', position=1) # 输出内容: B D C A - -**输出域控制函数** - -除了 `use_scope()` , PyWebIO同样提供了以下scope控制函数: - -* `set_scope(name) ` : 在当前位置(或指定位置)创建scope -* `clear(scope) ` : 清除scope的内容 -* `remove(scope) ` : 移除scope -* `scroll_to(scope) ` : 将页面滚动到scope处 - + with use_scope('hobby', clear=True): + put_text('Movie') # hobby is reset to Movie -页面环境设置 -^^^^^^^^^^^^^^ + ## ---- + # append Music, Drama to hobby + with use_scope('hobby'): + put_text('Music') + put_text('Drama') -**页面标题** + ## ---- + # insert the Coding into the top of the hobby + put_markdown('**Coding**', scope='hobby', position=0) -调用 `set_env(title=...) ` 可以设置页面标题。 -**自动滚动** +.. caution:: It is not allowed to have two scopes with the same name in the application. -在进行一些持续性的输出时(比如日志输出),有时希望在有新输出后自动将页面滚动到最下方,这时可以调用 `set_env(auto_scroll_bottom=True) ` 来开启自动滚动。 -注意,开启后,只有输出到ROOT Scope才可以触发自动滚动。 +**Scope control** -**输出动画** +In addition to `use_scope() ` and `put_scope() `, +PyWebIO also provides the following scope control functions: -PyWebIO在输出内容时默认会使用淡入的动画效果来显示内容,可使用 `set_env(output_animation=False) ` 来关闭动画。 +* `clear(scope) ` : Clear the contents of the scope +* `remove(scope) ` : Remove scope +* `scroll_to(scope) ` : Scroll the page to the scope -有关不同环境配置的效果可查看 :demo_host:`set_env Demo ` +Also, all output functions (function name like ``put_xxx()``) support a ``scope`` parameter to specify the destination +scope to output, and support a ``position`` parameter to specify the insert position in target scope. +Refer :ref:`output module ` for more information. -布局 +Layout ^^^^^^^^^^^^^^ -一般情况下,使用上文介绍的各种输出函数足以完成各种内容的展示,但直接调用输出函数产生的输出之间都是竖直排列的,如果想实现更复杂的布局(比如在页面左侧显示一个代码块,在右侧显示一个图像),就需要借助布局函数。 -``pywebio.output`` 模块提供了3个布局函数,通过对他们进行组合可以完成各种复杂的布局: +In general, using the output functions introduced above is enough to output what you want, but these outputs are arranged +vertically. If you want to create a more complex layout (such as displaying a code block on the left side of the page +and an image on the right), you need to use layout functions. -* `put_row() ` : 使用行布局输出内容. 内容在水平方向上排列 -* `put_column() ` : 使用列布局输出内容. 内容在竖直方向上排列 -* `put_grid() ` : 使用网格布局输出内容 +The ``pywebio.output`` module provides 3 layout functions, and you can create complex layouts by combining them: -通过组合 ``put_row()`` 和 ``put_column()`` 可以实现灵活布局: +* `put_row() ` : Use row layout to output content. The content is arranged horizontally +* `put_column() ` : Use column layout to output content. The content is arranged vertically +* `put_grid() ` : Output content using grid layout + +Here is an example by combining ``put_row()`` and ``put_column()``: .. exportable-codeblock:: :name: put-row-column - :summary: 布局函数 + :summary: Layout functions put_row([ put_column([ put_code('A'), put_row([ - put_code('B1'), None, # None 表示输出之间的空白 + put_code('B1'), None, # None represents the space between the output put_code('B2'), None, put_code('B3'), ]), @@ -542,103 +584,131 @@ PyWebIO在输出内容时默认会使用淡入的动画效果来显示内容, put_code('E') ]) -显示效果如下: +The results of the above example are as follows: .. image:: /assets/layout.png :align: center -布局函数还支持自定义各部分的尺寸:: +The layout function also supports customizing the size of each part:: + + put_row([put_image(...), put_image(...)], size='40% 60%') # The ratio of the width of two images is 2:3 - put_row([put_image(...), put_image(...)], size='40% 60%') # 左右两图宽度比2:3 +For more information, please refer to the :ref:`layout functions documentation `. -更多布局函数的用法及代码示例请查阅 :ref:`布局函数文档 ` . +.. _style: -样式 +Style ^^^^^^^^^^^^^^ -如果你熟悉 `CSS样式 `_ ,你还可以使用 `style() ` 函数给输出设定自定义样式。 -可以给单个的 ``put_xxx()`` 输出设定CSS样式,也可以配合组合输出使用: +If you are familiar with `CSS `_ styles, +you can use the ``style()`` method of output return to set a custom style for the output. + +You can set the CSS style for a single ``put_xxx()`` output: .. exportable-codeblock:: - :name: style - :summary: 输出样式 + :name: style-demo + :summary: style of output - style(put_text('Red'), 'color: red') + put_text('hello').style('color: red; font-size: 20px') ## ---- - put_table([ - ['A', 'B'], - ['C', style(put_text('Red'), 'color: red')], - ]) + # in combined output + put_row([ + put_text('hello').style('color: red'), + put_markdown('markdown') + ]).style('margin-top: 20px') -``style()`` 也接受列表作为输入,``style()`` 会为列表的每一项都设置CSS样式,返回值可以直接输出,可用于任何接受 ``put_xxx()`` 列表的地方: +The return value of ``style()`` method is the object itself so it can be used in combined output. -.. exportable-codeblock:: - :name: style-list - :summary: 批量设置输出样式 +.. _server_and_script_mode: - style([ - put_text('Red'), - put_markdown('~~del~~') - ], 'color: red') +Run application +---------------- - ## ---- - put_collapse('title', style([ - put_text('text'), - put_markdown('~~del~~'), - ], 'margin-left: 20px')) +In PyWebIO, there are two modes to run PyWebIO applications: running as a script and using +`pywebio.start_server() ` or +`pywebio.platform.path_deploy() ` to run as a web service. +Overview +^^^^^^^^^^^^^^ -.. _server_and_script_mode: +.. _server_mode: + +**Server mode** + +In server mode, PyWebIO will start a web server to continuously provide services. When the user accesses the service +address, PyWebIO will open a new session and run PyWebIO application in it. -Server模式与Script模式 ------------------------------------- +`start_server() ` is the most common way to start a web server to serve given +PyWebIO applications:: -在 :ref:`Hello, world ` 一节中,已经知道,PyWebIO支持在普通的脚本中调用和使用 -`start_server() ` 启动一个Web服务两种模式。 + from pywebio import * -**Server模式** + def main(): # PyWebIO application function + name = input.input("what's your name") + output.put_text("hello", name) -在Server模式下,PyWebIO会启动一个Web服务来持续性地提供服务。需要提供一个任务函数(类似于Web开发中的视图函数),当用户访问服务地址时,PyWebIO会开启一个新会话并运行任务函数。 + start_server(main, port=8080, debug=True) -使用 `start_server() ` 来启动PyWebIO的Server模式, `start_server() ` 除了接收一个函数作为任务函数外, -还支持传入函数列表或字典,从而使一个PyWebIO Server下可以有多个不同功能的服务,服务之间可以通过 `go_app() ` 进行跳转,详细内容见函数文档。 +Now head over to http://127.0.0.1:8080/, and you should see your hello greeting. + +By using ``debug=True`` to enable debug mode, the server will automatically reload if code changes. + +The `start_server() ` provide a remote access support, when enabled +(by passing `remote_access=True` to `start_server()`), you will get a public, shareable address for the current +application, others can access your application in their browser via this address. Because the processing happens +on your device (as long as your device stays on!), you don't have to worry about any dependencies. +Using remote access makes it easy to temporarily share the application with others. + +Another way to deploy PyWebIO application as web service is using `path_deploy() `. +`path_deploy() ` is used to deploy the PyWebIO applications from a directory. +Just define PyWebIO applications in python files under this directory, and you can access them via the path in the URL. +Refer to :ref:`platform module ` for more information. .. attention:: - 注意,在Server模式下,仅能在任务函数上下文中对PyWebIO的交互函数进行调用。比如如下调用是 **不被允许的** :: + Note that in Server mode, all functions from ``pywebio.input``, ``pywebio.output`` and ``pywebio.session`` modules can only be called in + the context of PyWebIO application functions. For example, the following code is **not allowed**:: import pywebio from pywebio.input import input - port = input('Input port number:') # ❌ 在任务函数上下文之外调用了PyWebIO交互函数!! + port = input('Input port number:') # ❌ error pywebio.start_server(my_task_func, port=int(port)) -**Script模式** +**Script mode** -Script模式下,在任何位置都可以调用PyWebIO的交互函数。 +If you never call ``start_server()`` or ``path_deploy()`` in your code, then you are running PyWebIO application as script mode. -如果用户在会话结束之前关闭了浏览器,那么之后会话内对于PyWebIO交互函数的调用将会引发一个 `SessionException ` 异常。 +In script mode, a web browser page will be open automatically when running to the first call to PyWebIO interactive functions, +and all subsequent PyWebIO interactions will take place on this page. When the script exit, the page will be inactive. + +If the user closes the browser before the script exiting, then subsequent calls to PyWebIO's interactive functions +will cause a `SessionException ` exception. .. _thread_in_server_mode: -并发 +Concurrent ^^^^^^^^^^^^^^ -PyWebIO 支持在多线程环境中使用。 +PyWebIO can be used in a multi-threading environment. -**Script模式** +**Script mode** -在 Script模式下,你可以自由地启动线程,并在其中调用PyWebIO的交互函数。当所有非 `Daemon线程 `_ 运行结束后,脚本退出。 +In script mode, you can freely start new thread and call PyWebIO interactive functions in it. +When all `non-daemonic `_ threads finish running, the script exits. -**Server模式** +**Server mode** -Server模式下,如果需要在新创建的线程中使用PyWebIO的交互函数,需要手动调用 `register_thread(thread) ` 对新进程进行注册(这样PyWebIO才能知道新创建的线程属于哪个会话)。 -如果新创建的线程中没有使用到PyWebIO的交互函数,则无需注册。没有使用 `register_thread(thread) ` 注册的线程不受会话管理,其调用PyWebIO的交互函数将会产生 `SessionNotFoundException ` 异常。 -当会话的任务函数和会话内通过 `register_thread(thread) ` 注册的线程都结束运行时,会话关闭。 +In server mode, if you need to use PyWebIO interactive functions in new thread, you need to use +`pywebio.session.register_thread(thread) ` to register the new thread +(so that PyWebIO can know which session the thread belongs to). If the PyWebIO interactive function is not used in +the new thread, no registration is required. Threads that are not registered with +`register_thread(thread) ` calling PyWebIO's interactive functions will cause +`SessionNotFoundException `. -Server模式下多线程的使用示例:: +Example of using multi-threading in Server mode:: def show_time(): while True: @@ -650,9 +720,9 @@ Server模式下多线程的使用示例:: t = threading.Thread(target=show_time) register_thread(t) put_markdown('## Clock') - t.start() # 在后台运行show_time() + t.start() # run `show_time()` in background - # ❌ 没有使用register_thread注册的线程调用PyWebIO交互函数会产生异常 + # ❌ this thread will cause `SessionNotFoundException` threading.Thread(target=show_time).start() put_text('Background task started.') @@ -663,328 +733,83 @@ Server模式下多线程的使用示例:: .. _session_close: -会话的结束 -^^^^^^^^^^^^^^ - -会话还会因为用户的关闭浏览器而结束,这时当前会话内还未返回的PyWebIO输入函数调用将抛出 `SessionClosedException ` 异常,之后对于PyWebIO交互函数的调用将会产生 `SessionNotFoundException ` 或 `SessionClosedException ` 异常。 - -可以使用 `defer_call(func) ` 来设置会话结束时需要调用的函数。无论是因为用户主动关闭页面还是任务结束使得会话关闭,设置的函数都会被执行。 -`defer_call(func) ` 可以用于资源清理等工作。在会话中可以多次调用 `defer_call() ` ,会话结束后将会顺序执行设置的函数。 - - -与Web框架集成 ---------------- - -.. _integration_web_framework: +Close of session +^^^^^^^^^^^^^^^^^ -可以将PyWebIO应用集成到现有的Python Web项目中,PyWebIO应用与Web项目共用一个Web框架。目前支持与Flask、Tornado、Django和aiohttp Web框架的集成。 +When user close the browser page, the session will be closed. After the browser page is closed, PyWebIO input function +calls that have not yet returned in the current session will cause `SessionClosedException `, +and subsequent calls to PyWebIO interactive functions will cause `SessionNotFoundException ` +or `SessionClosedException `. -与Web框架集成需要完成两部分配置:托管PyWebIO前端静态文件;暴露PyWebIO后端接口。这其中需要注意前端页面和后端接口的路径约定, -以及前端静态文件与后端接口分开部署时因为跨域而需要的特别设置。 +In most cases, you don't need to catch those exceptions, because let those exceptions to abort the running is the right way to exit. -集成方法 -^^^^^^^^^^^ +You can use `pywebio.session.defer_call(func) ` to set the function to be called when the +session closes. `defer_call(func) ` can be used for resource cleaning. You can call +`defer_call(func) ` multiple times in the session, and the set functions will be executed +sequentially after the session closes. -不同Web框架的集成方法如下: - -.. tabs:: - - .. tab:: Tornado - - 需要在Tornado应用中引入两个 ``RequestHandler`` , - 一个 ``RequestHandler`` 用来提供静态的前端文件,另一个 ``RequestHandler`` 用来和浏览器进行WebSocket通讯:: - - import tornado.ioloop - import tornado.web - from pywebio.platform.tornado import webio_handler - from pywebio import STATIC_PATH - - class MainHandler(tornado.web.RequestHandler): - def get(self): - self.write("Hello, world") - - if __name__ == "__main__": - application = tornado.web.Application([ - (r"/", MainHandler), - (r"/tool/io", webio_handler(task_func)), # task_func 为使用PyWebIO编写的任务函数 - (r"/tool/(.*)", tornado.web.StaticFileHandler, - {"path": STATIC_PATH, 'default_filename': 'index.html'}) # 前端静态文件托管 - ]) - application.listen(port=80, address='localhost') - tornado.ioloop.IOLoop.current().start() - - 以上代码调用 `webio_handler(task_func) ` 来获得PyWebIO和浏览器进行通讯的Tornado `WebSocketHandler `_ , - 并将其绑定在 ``/tool/io`` 路径下;同时将PyWebIO的静态文件使用 `tornado.web.StaticFileHandler `_ 托管到 ``/tool/(.*)`` 路径下。 - 启动Tornado服务器后,访问 ``http://localhost/tool/`` 即可打开PyWebIO应用 - - .. attention:: - - 当使用Tornado后端时,PyWebIO使用WebSocket协议和浏览器进行通讯,如果你的Tornado应用处在反向代理(比如Nginx)之后, - 可能需要特别配置反向代理来支持WebSocket协议,:ref:`这里 ` 有一个Nginx配置WebSocket的例子。 - - .. tab:: Flask - - 需要添加两个PyWebIO相关的路由:一个用来提供静态的前端文件,另一个用来和浏览器进行Http通讯:: - - from pywebio.platform.flask import webio_view - from pywebio import STATIC_PATH - from flask import Flask, send_from_directory - - app = Flask(__name__) - - # task_func 为使用PyWebIO编写的任务函数 - app.add_url_rule('/io', 'webio_view', webio_view(task_func), - methods=['GET', 'POST', 'OPTIONS']) # 接口需要能接收GET、POST和OPTIONS请求 - - @app.route('/') - @app.route('/') - def serve_static_file(static_file='index.html'): - """前端静态文件托管""" - return send_from_directory(STATIC_PATH, static_file) - - app.run(host='localhost', port=80) - - 以上代码使用 `webio_view(task_func) ` 来获得运行PyWebIO应用的Flask视图 , - 并调用 `Flask.add_url_rule `_ 将其绑定在 ``/io`` 路径下;同时编写视图函数 ``serve_static_file`` 将PyWebIO使用的静态文件托管到 ``/`` 路径下。 - 启动Flask应用后,访问 ``http://localhost/`` 即可打开PyWebIO应用 - - .. tab:: Django - - 在django的路由配置文件 ``urls.py`` 中加入PyWebIO相关的路由即可:: - - # urls.py - - from functools import partial - from django.urls import path - from django.views.static import serve - from pywebio import STATIC_PATH - from pywebio.platform.django import webio_view - - # task_func 为使用PyWebIO编写的任务函数 - webio_view_func = webio_view(task_func) - - urlpatterns = [ - path(r"io", webio_view_func), # http通信接口 - path(r'', partial(serve, path='index.html'), {'document_root': STATIC_PATH}), # 前端index.html文件托管 - path(r'', serve, {'document_root': STATIC_PATH}), # 前端其他文件托管 - ] - - 需要添加3条路由规则,第一条路由规则将PyWebIO应用的视图函数绑定到 ``/io`` 路径下,第二条路由用于提供PyWebIO的前端index.html文件,最后一个路由用于提供PyWebIO的其他静态文件 - - 启动Django应用后,访问 ``http://localhost/`` 即可打开PyWebIO应用 - - .. tab:: aiohttp - - 添加两个PyWebIO相关的路由:一个用来提供静态的前端文件,另一个用来和浏览器进行WebSocket通讯:: - - from aiohttp import web - from pywebio.platform.aiohttp import static_routes, webio_handler - - app = web.Application() - # task_func 为使用PyWebIO编写的任务函数 - app.add_routes([web.get('/io', webio_handler(task_func))]) # http通信接口 - app.add_routes(static_routes('/')) # 前端静态文件托管 - - web.run_app(app, host='localhost', port=8080) - - 启动aiohttp应用后,访问 ``http://localhost/`` 即可打开PyWebIO应用 - - .. attention:: - - 当使用aiohttp后端时,PyWebIO使用WebSocket协议和浏览器进行通讯,如果你的aiohttp应用处在反向代理(比如Nginx)之后, - 可能需要特别配置反向代理来支持WebSocket协议,:ref:`这里 ` 有一个Nginx配置WebSocket的例子。 - -.. _integration_web_framework_note: - -注意事项 -^^^^^^^^^^^ -**PyWebIO静态资源的托管** - -在开发阶段,使用后端框架提供的静态文件服务对于开发和调试都十分方便,上文的与Web框架集成的示例代码也都是使用了后端框架提供的静态文件服务。 -但出于性能考虑,托管静态文件最好的方式是使用 `反向代理 `_ (比如 `nginx `_ ) -或者 `CDN `_ 服务。 - -**前端页面和后端接口的路径约定** - -PyWebIO默认通过当前页面的同级的 ``./io`` API与后端进行通讯。 - -例如你将PyWebIO静态文件托管到 ``/A/B/C/(.*)`` 路径下,那么你需要将PyWebIO API的路由绑定到 ``/A/B/C/io`` 处; -你也可以在PyWebIO应用的地址中添加 ``pywebio_api`` url参数来指定PyWebIO后端API地址, -例如 ``/A/B/C/?pywebio_api=/D/pywebio`` 将PyWebIO后端API地址设置到了 ``/D/pywebio`` 处。 - -``pywebio_api`` 参数可以使用相对地址、绝对地址,也可以指定其他服务器。 - -.. caution:: - - 需要注意 ``pywebio_api`` 参数的格式: - - * 相对地址可以为 ``./xxx/xxx`` 或 ``xxx/xxx`` 的相对地址格式。 - * 绝对地址以 ``/`` 开头,比如 ``/aaa/bbb`` . - * 指定其他服务器需要使用完整格式: ``http://example.com:5000/aaa/io`` 、 ``ws://example.com:8080/bbb/ws_io`` ,或者省略协议字段: ``//example.com:8080/aaa/io`` 。省略协议字段时,PyWebIO根据当前页面的协议确定要使用的协议: 若当前页面为http协议,则后端接口自动选择http或ws协议;若当前页面为https协议,则后端接口自动选择https或wss协议。 - -如果你不想自己托管静态文件,你可以使用PyWebIO的Github Page页面: ``https://wang0618.github.io/PyWebIO/pywebio/html/?pywebio_api=`` ,需要在页面上通过 ``pywebio_api`` 参数传入后端API地址,并且将 ``https://wang0618.github.io`` 加入 ``allowed_origins`` 列表中(见下文"跨域配置"说明)。 - -**跨域配置** - -当后端API与前端页面不在同一host下时,需要在 `webio_handler() ` 或 -`webio_view() ` 中使用 ``allowed_origins`` 或 ``check_origin`` -参数来使后端接口允许前端页面的请求。 - -.. _coroutine_based_session: - -基于协程的会话 ---------------- -此部分内容属于高级特性,您不必使用此部分也可以实现PyWebIO支持的全部功能。PyWebIO中所有仅用于协程会话的函数或方法都在文档中有特别说明。 - -PyWebIO的会话实现默认是基于线程的,用户每打开一个和服务端的会话连接,PyWebIO会启动一个线程来运行任务函数。 -除了基于线程的会话,PyWebIO还提供了基于协程的会话。基于协程的会话接受协程函数作为任务函数。 - -基于协程的会话为单线程模型,所有会话都运行在一个线程内。对于IO密集型的任务,协程比线程占用更少的资源同时又拥有媲美于线程的性能。 -另外,协程的上下文切换具有可预测性,能够减少程序同步与加锁的需要,可以有效避免大多数临界区问题。 - -使用协程会话 -^^^^^^^^^^^^^^^^ - -要使用基于协程的会话,需要使用 ``async`` 关键字将任务函数声明为协程函数,并使用 ``await`` 语法调用PyWebIO输入函数: - -.. code-block:: python - :emphasize-lines: 5,6 - - from pywebio.input import * - from pywebio.output import * - from pywebio import start_server - - async def say_hello(): - name = await input("what's your name?") - put_text('Hello, %s' % name) - - start_server(say_hello, auto_open_webbrowser=True) - -在协程任务函数中,也可以使用 ``await`` 调用其他协程或标准库 `asyncio `_ 中的可等待对象( `awaitable objects `_ ): - -.. code-block:: python - :emphasize-lines: 6,10 - - import asyncio - from pywebio import start_server - - async def hello_word(): - put_text('Hello ...') - await asyncio.sleep(1) # await asyncio 库中的 awaitable objects - put_text('... World!') - - async def main(): - await hello_word() # await 协程 - put_text('Bye, bye') - - start_server(main, auto_open_webbrowser=True) - -.. attention:: - - 在基于协程的会话中, :doc:`pywebio.input ` 模块中的定义输入函数都需要使用 ``await`` 语法来获取返回值, - 忘记使用 ``await`` 将会是在使用基于协程的会话时常出现的错误。 - - 其他在协程会话中也需要使用 ``await`` 语法来进行调用函数有: - - * `pywebio.session.run_asyncio_coroutine(coro_obj) ` - * `pywebio.session.eval_js(expression) ` - * `pywebio.session.hold() ` - -.. warning:: - - 虽然PyWebIO的协程会话兼容标准库 ``asyncio`` 中的 ``awaitable objects`` ,但 ``asyncio`` 库不兼容PyWebIO协程会话中的 ``awaitable objects`` . - - 也就是说,无法将PyWebIO中的 ``awaitable objects`` 传入 ``asyncio`` 中的接受 ``awaitable objects`` 作为参数的函数中,比如如下调用是 **不被支持的** :: - - await asyncio.shield(pywebio.input()) - await asyncio.gather(asyncio.sleep(1), pywebio.session.eval_js('1+1')) - task = asyncio.create_task(pywebio.input()) - -协程会话的并发 -^^^^^^^^^^^^^^^^ - -在基于协程的会话中,你可以启动线程,但是无法在其中调用PyWebIO交互函数( `register_thread() ` 在协程会话中不可用)。 -但你可以使用 `run_async(coro) ` 来异步执行一个协程对象,新协程内可以使用PyWebIO交互函数:: - - from pywebio import start_server - from pywebio.session import run_async - - async def counter(n): - for i in range(n): - put_text(i) - await asyncio.sleep(1) - - async def main(): - run_async(counter(10)) - put_text('Main coroutine function exited.') - - - start_server(main, auto_open_webbrowser=True) - -`run_async(coro) ` 返回一个 `TaskHandle ` ,通过 `TaskHandle ` 可以查询协程运行状态和关闭协程。 - -协程会话的关闭 -^^^^^^^^^^^^^^^^ -与基于线程的会话类似,在基于协程的会话中,当任务函数和在会话内通过 `run_async() ` 运行的协程全部结束后,会话关闭。 +More about PyWebIO +--------------------- +By now, you already get the most important features of PyWebIO and can start to write awesome PyWebIO applications. +However, there are some other useful features we don't cover in the above. Here we just make a briefly explain about them. +When you need them in your application, you can refer to their document. -对于因为用户的关闭浏览器而造成的会话结束,处理逻辑和 :ref:`基于线程的会话 ` 一致: -此时当前会话内还未返回的PyWebIO输入函数调用将抛出 `SessionClosedException ` 异常,之后对于PyWebIO交互函数的调用将会产生 `SessionNotFoundException ` 或 `SessionClosedException ` 异常。 +Also, :doc:`here ` is a cookbook where you can find some useful code snippets for your PyWebIO application. -协程会话也同样支持使用 `defer_call(func) ` 来设置会话结束时需要调用的函数。 +``session`` module +^^^^^^^^^^^^^^^^^^^^ +The :doc:`pywebio.session ` module give you more control to session. -协程会话与Web框架集成 -^^^^^^^^^^^^^^^^^^^^^^^^^ + * Use `set_env() ` to configure the title, page appearance, input panel and so on for current session. -基于协程的会话同样可以与Web框架进行集成,只需要在原来传入任务函数的地方改为传入协程函数即可。 + * The `info ` object provides a lot information about the current session, + such as the user IP address, user language and user browser information. -但当前在使用基于协程的会话集成进Flask或Django时,存在一些限制: + * `local ` is a session-local storage, it used to save data whose values are session specific. -一是协程函数内还无法直接通过 ``await`` 直接等待asyncio库中的协程对象,目前需要使用 `run_asyncio_coroutine() ` 进行包装。 + * `run_js() ` let you execute JavaScript code in user's browser, + and `eval_js() ` let you execute JavaScript expression and get the value of it. -二是,在启动Flask/Django这类基于线程的服务器之前需要启动一个单独的线程来运行事件循环。 +``pin`` module +^^^^^^^^^^^^^^^^^^^^ +As you already know, the input function of PyWebIO is blocking and the input form will be destroyed after successful submission. +In some cases, you may want to make the input form not disappear after submission, and can continue to receive input. +So PyWebIO provides the :doc:`pywebio.pin ` module to achieve persistent input by pinning input widgets to the page. -使用基于协程的会话集成进Flask的示例: +``platform`` module +^^^^^^^^^^^^^^^^^^^^ -.. code-block:: python - :emphasize-lines: 12,25 +The :doc:`pywebio.platform ` module provides support for deploying PyWebIO applications in different ways. - import asyncio - import threading - from flask import Flask, send_from_directory - from pywebio import STATIC_PATH - from pywebio.output import * - from pywebio.platform.flask import webio_view - from pywebio.platform.httpbased import run_event_loop - from pywebio.session import run_asyncio_coroutine +There are two protocols (WebSocket and HTTP) can be used in server to communicates with the browser. The WebSocket is +used by default. If you want to use HTTP protocol, you can choose other ``start_server()`` functions in this module. - async def hello_word(): - put_text('Hello ...') - await run_asyncio_coroutine(asyncio.sleep(1)) # 无法直接 await asyncio.sleep(1) - put_text('... World!') +You might want to set some web page related configuration (such as SEO information, js and css injection) for your PyWebIO application, +`pywebio.config() ` can be helpful. - app = Flask(__name__) - app.add_url_rule('/io', 'webio_view', webio_view(hello_word), - methods=['GET', 'POST', 'OPTIONS']) +Advanced features +^^^^^^^^^^^^^^^^^^^^ - @app.route('/') - @app.route('/') - def serve_static_file(static_file='index.html'): - return send_from_directory(STATIC_PATH, static_file) +The PyWebIO application can be integrated into an existing Python web project, the PyWebIO application and the web +project share a web framework. Refer to :ref:`Advanced Topic: Integration with Web Framework ` +for more information. - # 事件循环线程 - threading.Thread(target=run_event_loop, daemon=True).start() - app.run(host='localhost', port='80') +PyWebIO also provides support for coroutine-based sessions. Refer to :ref:`Advanced Topic: Coroutine-based session ` +for more information. -最后,使用PyWebIO编写的协程函数不支持Script模式,总是需要使用 ``start_server`` 来启动一个服务或者集成进Web框架来调用。 +If you try to bundles your PyWebIO application into a stand-alone executable file, to make users can run the application +without installing a Python interpreter or any modules, you might want to refer to :ref:`Libraries support: Build stand-alone App ` +If you want to make some data visualization in your PyWebIO application, you can't miss :ref:`Libraries support: Data visualization ` Last but not least --------------------- -以上就是PyWebIO的全部功能了,你可以继续阅读接下来的文档,或者立即开始PyWebIO应用的编写了。 +This is basically all features of PyWebIO, you can continue to read the rest of the documents, or start writing your PyWebIO applications now. -最后再提供一条建议,当你在使用PyWebIO遇到设计上的问题时,可以问一下自己:如果在是在终端程序中我会怎么做? -如果你已经有答案了,那么在PyWebIO中一样可以使用这样的方式完成。如果问题依然存在或者觉得解决方案不够好, -你可以考虑使用 `put_buttons() ` 提供的回调机制。 +Finally, please allow me to provide one more suggestion. When you encounter a design problem when using PyWebIO, you can +ask yourself a question: What would I do if it is in a terminal program? +If you already have the answer, it can be done in the same way with PyWebIO. If the problem persists or the solution is +not good enough, you can consider the :ref:`callback mechanism ` or :doc:`pin <./pin>` module. -好了,Have fun with PyWebIO! \ No newline at end of file +OK, Have fun with PyWebIO! diff --git a/docs/index.rst b/docs/index.rst index 33cedc8b..8fa9200a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,99 +1,110 @@ PyWebIO ========== -PyWebIO提供了一系列命令式的交互函数来在浏览器上获取用户输入和进行输出,将浏览器变成了一个“富文本终端”,可以用于构建简单的Web应用或基于浏览器的GUI应用。 -使用PyWebIO,开发者能像编写终端脚本一样(基于input和print进行交互)来编写应用,无需具备HTML和JS的相关知识; -PyWebIO还可以方便地整合进现有的Web服务。非常适合快速构建对UI要求不高的应用。 +PyWebIO provides a diverse set of imperative functions to obtain user input and output content on the browser, +turning the browser into a "rich text terminal", and can be used to build simple web applications or browser-based +GUI applications. Using PyWebIO, developers can write applications just like writing terminal scripts +(interaction based on input and print function), without the need to have knowledge of HTML and JS. +PyWebIO is ideal for quickly building interactive applications that don't require a complicated user interface. - -特性 +Features ------------ -- 使用同步而不是基于回调的方式获取输入,代码编写逻辑更自然 -- 非声明式布局,布局方式简单高效 -- 代码侵入性小,旧脚本代码仅需修改输入输出逻辑便可改造为Web服务 -- 支持整合到现有的Web服务,目前支持与Flask、Django、Tornado、aiohttp框架集成 -- 同时支持基于线程的执行模型和基于协程的执行模型 -- 支持结合第三方库实现数据可视化 +- Use synchronization instead of callback-based method to get input +- Non-declarative layout, simple and efficient +- Less intrusive: old script code can be transformed into a Web service only by modifying the input and output operation +- Support integration into existing web services, currently supports Flask, Django, Tornado, aiohttp and FastAPI(Starlette) framework +- Support for ``asyncio`` and coroutine +- Support data visualization with third-party libraries -Install ------------- +Installation +-------------- -稳定版安装:: +Stable version:: pip3 install -U pywebio -开发版安装:: +Development version:: - pip3 install -U --force-reinstall https://code.aliyun.com/wang0618/pywebio/repository/archive.zip + pip3 install -U https://github.com/pywebio/PyWebIO/archive/dev-release.zip -**系统要求**: PyWebIO要求 Python 版本在 3.5.2 及以上 +**Prerequisites**: PyWebIO requires Python 3.5.2 or newer .. _hello_word: Hello, world -------------- -这是一个使用PyWebIO计算 `BMI指数 `_ 的脚本:: +Here is a simple PyWebIO script to calculate the `BMI `_ :: # A simple script to calculate BMI from pywebio.input import input, FLOAT from pywebio.output import put_text def bmi(): - height = input("请输入你的身高(cm):", type=FLOAT) - weight = input("请输入你的体重(kg):", type=FLOAT) + height = input("Input your height(cm):", type=FLOAT) + weight = input("Input your weight(kg):", type=FLOAT) BMI = weight / (height / 100) ** 2 - top_status = [(14.9, '极瘦'), (18.4, '偏瘦'), - (22.9, '正常'), (27.5, '过重'), - (40.0, '肥胖'), (float('inf'), '非常肥胖')] + top_status = [(16, 'Severely underweight'), (18.5, 'Underweight'), + (25, 'Normal'), (30, 'Overweight'), + (35, 'Moderately obese'), (float('inf'), 'Severely obese')] for top, status in top_status: if BMI <= top: - put_text('你的 BMI 值: %.1f,身体状态:%s' % (BMI, status)) + put_text('Your BMI: %.1f. Category: %s' % (BMI, status)) break if __name__ == '__main__': bmi() -如果没有使用PyWebIO,这只是一个非常简单的脚本,而通过使用PyWebIO提供的输入输出函数,你可以在浏览器中与代码进行交互: +This is just a very simple script if you ignore PyWebIO, but after using the input and output functions provided by PyWebIO, +you can interact with the code in the browser: .. image:: /assets/demo.* :width: 450px :align: center -将上面代码最后一行对 ``bmi()`` 的直接调用改为使用 `pywebio.start_server(bmi, port=80) ` 便可以在80端口提供 ``bmi()`` 服务( :demo_host:`在线Demo ` )。 +In the last line of the above code, changing the function call ``bmi()`` to +`pywebio.start_server(bmi, port=80) ` will start a bmi web service on port 80 +( :demo_host:`online Demo ` ). -将 ``bmi()`` 服务整合到现有的Web框架请参考 :ref:`与Web框架集成 ` +If you want to integrate the ``bmi()`` service into an existing web framework, you can visit +:ref:`Integration with a web framework ` section of this document. Documentation ------------- -这个文档同时也提供 `PDF 和 Epub 格式 `_. +This documentation is also available in `PDF and Epub formats `_. .. toctree:: :maxdepth: 2 - :caption: 使用手册 + :caption: Manual guide input output session platform + pin + advanced libraries_support - demos - misc .. toctree:: :titlesonly: - FAQ + cookbook releases .. toctree:: :maxdepth: 2 - :caption: 实现文档 + :caption: Battery + + battery + +.. toctree:: + :maxdepth: 2 + :caption: Implement Doc spec diff --git a/docs/input.rst b/docs/input.rst index ef9f0501..f540e026 100644 --- a/docs/input.rst +++ b/docs/input.rst @@ -1,4 +1,4 @@ -``pywebio.input`` --- 输入模块 +``pywebio.input`` --- Get input from web browser ==================================================== .. automodule:: pywebio.input diff --git a/docs/libraries_support.rst b/docs/libraries_support.rst index 18f7139b..636cc0d9 100644 --- a/docs/libraries_support.rst +++ b/docs/libraries_support.rst @@ -1,17 +1,52 @@ -第三方库生态 -============== +Libraries support +====================== + +.. _stand_alone_app: + +Build stand-alone App +---------------------- +`PyInstaller `_ bundles a Python application and all its dependencies into a folder or executable. The user can run the packaged app without installing a Python interpreter or any modules. + +You can use PyInstaller to packages PyWebIO application into a stand-alone executable or folder: + +1. Create a pyinstaller spec (specification) file:: + + pyi-makespec app.py + + You need replace ``app.py`` to your PyWebIO application file name. + +2. Only for PyWebIO before v1.8: Edit the spec file, change the ``datas`` parameter of ``Analysis``:: + + from pywebio.utils import pyinstaller_datas + + a = Analysis( + ... + datas=pyinstaller_datas(), + ... + +3. Build the application by passing the spec file to the pyinstaller command:: + + pyinstaller app.spec + + +If you want to create a one-file bundled executable, you need pass ``--onefile`` option in first step. + +For more information, please visit: https://pyinstaller.readthedocs.io/en/stable/spec-files.html + .. _visualization: -数据可视化 -------------- -PyWebIO支持使用第三方库进行数据可视化 +Data visualization +-------------------- +PyWebIO supports for data visualization with the third-party libraries. Bokeh ^^^^^^^^^^^^^^^^^^^^^^ -`Bokeh `_ 是一个支持创建实时交互的数据可视化库。 -在 PyWebIO 会话中调用 ``bokeh.io.output_notebook(notebook_type='pywebio')`` 来设置Bokeh输出到PyWebIO:: +`Bokeh `_ is an interactive visualization library for modern web browsers. It provides elegant, concise construction of versatile graphics, and affords high-performance interactivity over large or streaming datasets. + +You can use ``bokeh.io.output_notebook(notebook_type='pywebio')`` in the PyWebIO session to setup Bokeh environment. +Then you can use ``bokeh.io.show()`` to output a boken chart:: from bokeh.io import output_notebook from bokeh.io import show @@ -21,54 +56,69 @@ Bokeh ... show(fig) -相应demo见 :charts_demo_host:`bokeh demo ` +See related demo on :charts_demo_host:`bokeh demo ` + +In addition to creating ordinary charts, Bokeh can also build the Bokeh applications by starting the `Bokeh server `_. The purpose of the Bokeh server is to make it easy for Python users to create interactive web applications that can connect front-end UI events to real, running Python code. -除了创建普通图表,Bokeh还可以通过启动Bokeh server来显示Bokeh app,Bokeh app支持向图表的添加按钮、输入框等交互组件,并向组件注册Python回调,从而创建可以与Python代码交互的图表。 +In PyWebIO, you can also use ``bokeh.io.show()`` to display a Bokeh App. For the example, see `bokeh_app.py `_. -在PyWebIO中,你也可以使用 ``bokeh.io.show()`` 来显示一个Bokeh App,代码示例见 `bokeh_app.py `_。 +.. note:: Bokeh App currently is only available in the default Tornado backend -.. image:: https://cdn.jsdelivr.net/gh/wang0618/pywebio-chart-gallery@master/assets/bokeh.png +.. image:: https://fastly.jsdelivr.net/gh/wang0618/pywebio-chart-gallery@master/assets/bokeh.png pyecharts ^^^^^^^^^^^^^^^^^^^^^^ -`pyecharts `_ 是一个使用Python创建 `Echarts `_ 可视化图表的库。 -在 PyWebIO 中使用 `put_html() ` 可以输出 pyecharts 库创建的图表:: +`pyecharts `_ is a python plotting library which uses `Echarts `_ as underlying implementation. - # chart 为 pyecharts 的图表实例 +In PyWebIO, you can use the following code to output the pyecharts chart instance:: + + # `chart` is pyecharts chart instance pywebio.output.put_html(chart.render_notebook()) -相应demo见 :charts_demo_host:`pyecharts demo ` +See related demo on :charts_demo_host:`pyecharts demo ` .. only:: not latex - .. image:: https://cdn.jsdelivr.net/gh/wang0618/pywebio-chart-gallery@master/assets/pyecharts.gif + .. image:: https://fastly.jsdelivr.net/gh/wang0618/pywebio-chart-gallery@master/assets/pyecharts.gif plotly ^^^^^^^^^^^^^^^^^^^^^^ -`plotly.py `_ 是一个非常流行的Python数据可视化库,可以生成高质量的交互式图表。 -PyWebIO 支持输出使用 plotly 库创建的图表。使用方式为在PyWebIO会话中调用:: +`plotly.py `_ is an interactive, open-source, and browser-based graphing library for Python. + +In PyWebIO, you can use the following code to output the plotly chart instance:: - # fig 为 plotly 的图表实例 + # `fig` is plotly chart instance html = fig.to_html(include_plotlyjs="require", full_html=False) pywebio.output.put_html(html) -相应demo见 :charts_demo_host:`plotly demo ` +See related demo on :charts_demo_host:`plotly demo ` + +.. image:: https://fastly.jsdelivr.net/gh/wang0618/pywebio-chart-gallery@master/assets/plotly.png + +pyg2plot +^^^^^^^^^^^^^^^^^^^^^^ + +`pyg2plot `_ is a python plotting library which uses `G2Plot `_ as underlying implementation. + +In PyWebIO, you can use the following code to output the pyg2plot chart instance:: + + # `chart` is pyg2plot chart instance + pywebio.output.put_html(chart.render_notebook()) -.. image:: https://cdn.jsdelivr.net/gh/wang0618/pywebio-chart-gallery@master/assets/plotly.png +See related demo on :charts_demo_host:`plotly demo ` cutecharts.py ^^^^^^^^^^^^^^^^^^^^^^ -`cutecharts.py `_ 是一个可以创建具有卡通风格的可视化图表的python库。 -底层使用了 `chart.xkcd `_ Javascript库。 +`cutecharts.py `_ is a hand drawing style charts library for Python which uses `chart.xkcd `_ as underlying implementation. -在 PyWebIO 中使用 `put_html() ` 可以输出 cutecharts.py 库创建的图表:: +In PyWebIO, you can use the following code to output the cutecharts.py chart instance:: - # chart 为 cutecharts 的图表实例 + # `chart` is cutecharts chart instance pywebio.output.put_html(chart.render_notebook()) -相应demo见 :charts_demo_host:`cutecharts demo ` +See related demo on :charts_demo_host:`cutecharts demo ` -.. image:: https://cdn.jsdelivr.net/gh/wang0618/pywebio-chart-gallery@master/assets/cutecharts.png +.. image:: https://fastly.jsdelivr.net/gh/wang0618/pywebio-chart-gallery@master/assets/cutecharts.png diff --git a/docs/locales/fa/LC_MESSAGES/FAQ.po b/docs/locales/fa/LC_MESSAGES/FAQ.po new file mode 100644 index 00000000..f99bd65c --- /dev/null +++ b/docs/locales/fa/LC_MESSAGES/FAQ.po @@ -0,0 +1,71 @@ +# Copyright (C) Weimin Wang +# This file is distributed under the same license as the PyWebIO package. +# +# FIRST AUTHOR , 2022. +# Pikhosh , 2022. +msgid "" +msgstr "" +"Project-Id-Version: PyWebIO 1.5.2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-02-26 16:35+0330\n" +"PO-Revision-Date: 2022-02-26 20:27+0330\n" +"Last-Translator: Pikhosh \n" +"Language-Team: Persian <>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" +"Language: fa\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Lokalize 21.12.2\n" + +#: ../../FAQ.rst:2 +msgid "FAQ" +msgstr "سوالات متداول" + +#: ../../FAQ.rst:8 +msgid "" +"How to make the input form not disappear after submission, and can " +"continue to receive input?" +msgstr "" +"چجوری فرم ورودی رو طوری کنیم که بعد از ارسال ناپدید نشه، و بتونه " +"به دریافت ورودی ادامه بده؟" + +#: ../../FAQ.rst:10 +msgid "" +"You can consider the :doc:`pin <./pin>` module. It achieves persistent " +"input by pinning input widgets to the page." +msgstr "" +"شما می توانید ماژول :doc:`pin <./pin>` را در نظر بگیرید. اون به ورودی " +"مداوم با پین کردن ویجت های ورودی به صفحه دست پیدا می کند." + +#: ../../FAQ.rst:14 +msgid "How to output an input widget such as a search bar?" +msgstr "چجوری یک ویجت ورودی رو به عنوان خروجی نشون بدم مثل یه نوار جست جو؟" + +#: ../../FAQ.rst:16 +msgid "You can consider the :doc:`pin <./pin>` module." +msgstr "شما می توانید ماژول :doc:`pin <./pin>` را در نظر بگیرید." + +#: ../../FAQ.rst:20 +msgid "Why the callback of ``put_buttons()`` does not work?" +msgstr "چرا فراخوانی ``put_buttons()`` کار نمی کنه؟" + +#: ../../FAQ.rst:22 +msgid "" +"You might use the old version of PyWebIO, upgrade it to the latest " +"version or see `the old document " +"`_" +msgstr "" +"شما ممکن است از نسخه قدیمی PyWebIO استفاده کنید، به آخرین " +"نسخه ارتقا اش دهید یا `the old document " +"`_را ببینید" + +#: ../../FAQ.rst:25 +msgid "Why I cannot download the file using ``put_file()``?" +msgstr "چرا من نمی تونم با استفاده از ``put_file()`` فایل رو دانلود کنم؟" + +#: ../../FAQ.rst:27 +msgid "The reason is the same as above." +msgstr "دلیل مشابه بالاست." + diff --git a/docs/locales/fa/LC_MESSAGES/advanced.po b/docs/locales/fa/LC_MESSAGES/advanced.po new file mode 100644 index 00000000..1e10a02c --- /dev/null +++ b/docs/locales/fa/LC_MESSAGES/advanced.po @@ -0,0 +1,664 @@ +# Copyright (C) Weimin Wang +# This file is distributed under the same license as the PyWebIO package. +# +# FIRST AUTHOR , 2022. +# Pikhosh , 2022. +msgid "" +msgstr "" +"Project-Id-Version: PyWebIO 1.5.2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-02-26 16:35+0330\n" +"PO-Revision-Date: 2022-02-28 12:30+0330\n" +"Last-Translator: Pikhosh \n" +"Language-Team: Persian <>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" +"Language: fa\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Lokalize 21.12.2\n" + +#: ../../advanced.rst:2 +msgid "Advanced topic" +msgstr "موضوع پیشرفته" + +#: ../../advanced.rst:4 +msgid "This section will introduce the advanced features of PyWebIO." +msgstr "" + +#: ../../advanced.rst:10 +msgid "Start multiple applications with start_server()" +msgstr "" + +#: ../../advanced.rst:12 +msgid "" +"`start_server() ` accepts a " +"function as PyWebIO application. In addition, `start_server() " +"` also accepts a list of " +"application function or a dictionary of it to start multiple " +"applications. You can use `pywebio.session.go_app() " +"` or `put_link() ` to " +"jump between application::" +msgstr "" + +#: ../../advanced.rst:17 +msgid "" +"def task_1():\n" +" put_text('task_1')\n" +" put_buttons(['Go task 2'], [lambda: go_app('task_2')])\n" +"\n" +"def task_2():\n" +" put_text('task_2')\n" +" put_buttons(['Go task 1'], [lambda: go_app('task_1')])\n" +"\n" +"def index():\n" +" put_link('Go task 1', app='task_1') # Use `app` parameter to specify" +" the task name\n" +" put_link('Go task 2', app='task_2')\n" +"\n" +"# equal to `start_server({'index': index, 'task_1': task_1, 'task_2': " +"task_2})`\n" +"start_server([index, task_1, task_2])" +msgstr "" + +#: ../../advanced.rst:32 +msgid "" +"When the first parameter of `start_server() " +"` is a dictionary, whose key is " +"application name and value is application function. When it is a list, " +"PyWebIO will use function name as application name." +msgstr "" + +#: ../../advanced.rst:35 +msgid "" +"You can select which application to access through the ``app`` URL " +"parameter (for example, visit ``http://host:port/?app=foo`` to access the" +" ``foo`` application), By default, the ``index`` application is opened " +"when no ``app`` URL parameter provided. When the ``index`` application " +"doesn't exist, PyWebIO will provide a default index application." +msgstr "" + +#: ../../advanced.rst:44 +msgid "Integration with web framework" +msgstr "" + +#: ../../advanced.rst:46 +msgid "" +"The PyWebIO application can be integrated into an existing Python Web " +"project, the PyWebIO application and the Web project share a web " +"framework. PyWebIO currently supports integration with Flask, Tornado, " +"Django, aiohttp and FastAPI(Starlette) web frameworks." +msgstr "" + +#: ../../advanced.rst:50 +msgid "The integration methods of those web frameworks are as follows:" +msgstr "" + +#: ../../advanced.rst:54 +msgid "Tornado" +msgstr "" + +#: ../../advanced.rst:58 +msgid "**Tornado**" +msgstr "" + +#: ../../advanced.rst:60 +msgid "" +"Use `pywebio.platform.tornado.webio_handler()` to get the " +"`WebSocketHandler " +"`_" +" class for running PyWebIO applications in Tornado::" +msgstr "" + +#: ../../advanced.rst:64 +msgid "" +"import tornado.ioloop\n" +"import tornado.web\n" +"from pywebio.platform.tornado import webio_handler\n" +"\n" +"class MainHandler(tornado.web.RequestHandler):\n" +" def get(self):\n" +" self.write(\"Hello, world\")\n" +"\n" +"if __name__ == \"__main__\":\n" +" application = tornado.web.Application([\n" +" (r\"/\", MainHandler),\n" +" (r\"/tool\", webio_handler(task_func)), # `task_func` is PyWebIO" +" task function\n" +" ])\n" +" application.listen(port=80, address='localhost')\n" +" tornado.ioloop.IOLoop.current().start()" +msgstr "" + +#: ../../advanced.rst:81 +msgid "" +"In above code, we add a routing rule to bind the ``WebSocketHandler`` of " +"the PyWebIO application to the ``/tool`` path. After starting the Tornado" +" server, you can visit ``http://localhost/tool`` to open the PyWebIO " +"application." +msgstr "" + +#: ../../advanced.rst:86 +msgid "" +"PyWebIO uses the WebSocket protocol to communicate with the browser in " +"Tornado. If your Tornado application is behind a reverse proxy (such as " +"Nginx), you may need to configure the reverse proxy to support the " +"WebSocket protocol. :ref:`Here ` is an example of Nginx " +"WebSocket configuration." +msgstr "" + +#: ../../advanced.rst:90 +msgid "Flask" +msgstr "" + +#: ../../advanced.rst:94 +msgid "**Flask**" +msgstr "" + +#: ../../advanced.rst:96 +msgid "" +"Use `pywebio.platform.flask.webio_view()` to get the view function for " +"running PyWebIO applications in Flask::" +msgstr "" + +#: ../../advanced.rst:98 +msgid "" +"from pywebio.platform.flask import webio_view\n" +"from flask import Flask\n" +"\n" +"app = Flask(__name__)\n" +"\n" +"# `task_func` is PyWebIO task function\n" +"app.add_url_rule('/tool', 'webio_view', webio_view(task_func),\n" +" methods=['GET', 'POST', 'OPTIONS']) # need GET,POST and " +"OPTIONS methods\n" +"\n" +"app.run(host='localhost', port=80)" +msgstr "" + +#: ../../advanced.rst:110 +msgid "" +"In above code, we add a routing rule to bind the view function of the " +"PyWebIO application to the ``/tool`` path. After starting the Flask " +"application, visit ``http://localhost/tool`` to open the PyWebIO " +"application." +msgstr "" + +#: ../../advanced.rst:113 +msgid "Django" +msgstr "" + +#: ../../advanced.rst:117 +msgid "**Django**" +msgstr "" + +#: ../../advanced.rst:119 +msgid "" +"Use `pywebio.platform.django.webio_view()` to get the view function for " +"running PyWebIO applications in Django::" +msgstr "" + +#: ../../advanced.rst:121 +msgid "" +"# urls.py\n" +"\n" +"from django.urls import path\n" +"from pywebio.platform.django import webio_view\n" +"\n" +"# `task_func` is PyWebIO task function\n" +"webio_view_func = webio_view(task_func)\n" +"\n" +"urlpatterns = [\n" +" path(r\"tool\", webio_view_func),\n" +"]" +msgstr "" + +#: ../../advanced.rst:134 +msgid "" +"In above code, we add a routing rule to bind the view function of the " +"PyWebIO application to the ``/tool`` path. After starting the Django " +"server, visit ``http://localhost/tool`` to open the PyWebIO application" +msgstr "" + +#: ../../advanced.rst:137 +msgid "aiohttp" +msgstr "" + +#: ../../advanced.rst:141 +msgid "**aiohttp**" +msgstr "" + +#: ../../advanced.rst:143 +msgid "" +"Use `pywebio.platform.aiohttp.webio_handler()` to get the `Request " +"Handler `_ coroutine for running PyWebIO applications in aiohttp::" +msgstr "" + +#: ../../advanced.rst:147 +msgid "" +"from aiohttp import web\n" +"from pywebio.platform.aiohttp import webio_handler\n" +"\n" +"app = web.Application()\n" +"# `task_func` is PyWebIO task function\n" +"app.add_routes([web.get('/tool', webio_handler(task_func))])\n" +"\n" +"web.run_app(app, host='localhost', port=80)" +msgstr "" + +#: ../../advanced.rst:156 +msgid "" +"After starting the aiohttp server, visit ``http://localhost/tool`` to " +"open the PyWebIO application" +msgstr "" + +#: ../../advanced.rst:160 +msgid "" +"PyWebIO uses the WebSocket protocol to communicate with the browser in " +"aiohttp. If your aiohttp server is behind a reverse proxy (such as " +"Nginx), you may need to configure the reverse proxy to support the " +"WebSocket protocol. :ref:`Here ` is an example of Nginx " +"WebSocket configuration." +msgstr "" + +#: ../../advanced.rst:165 +msgid "FastAPI/Starlette" +msgstr "" + +#: ../../advanced.rst:169 +msgid "**FastAPI/Starlette**" +msgstr "" + +#: ../../advanced.rst:171 +msgid "" +"Use `pywebio.platform.fastapi.webio_routes()` to get the " +"FastAPI/Starlette routes for running PyWebIO applications. You can mount " +"the routes to your FastAPI/Starlette app." +msgstr "" + +#: ../../advanced.rst:174 +msgid "FastAPI::" +msgstr "" + +#: ../../advanced.rst:176 +msgid "" +"from fastapi import FastAPI\n" +"from pywebio.platform.fastapi import webio_routes\n" +"\n" +"app = FastAPI()\n" +"\n" +"@app.get(\"/app\")\n" +"def read_main():\n" +" return {\"message\": \"Hello World from main app\"}\n" +"\n" +"# `task_func` is PyWebIO task function\n" +"app.mount(\"/tool\", FastAPI(routes=webio_routes(task_func)))" +msgstr "" + +#: ../../advanced.rst:188 +msgid "Starlette::" +msgstr "" + +#: ../../advanced.rst:190 +msgid "" +"from starlette.applications import Starlette\n" +"from starlette.responses import JSONResponse\n" +"from starlette.routing import Route, Mount\n" +"from pywebio.platform.fastapi import webio_routes\n" +"\n" +"async def homepage(request):\n" +" return JSONResponse({'hello': 'world'})\n" +"\n" +"app = Starlette(routes=[\n" +" Route('/', homepage),\n" +" Mount('/tool', routes=webio_routes(task_func)) # `task_func` is " +"PyWebIO task function\n" +"])" +msgstr "" + +#: ../../advanced.rst:203 +msgid "" +"After starting the server by using ``uvicorn :app`` , visit " +"``http://localhost:8000/tool/`` to open the PyWebIO application" +msgstr "" + +#: ../../advanced.rst:205 +msgid "" +"See also: `FastAPI doc `_ , `Starlette doc `_" +msgstr "" + +#: ../../advanced.rst:209 +msgid "" +"PyWebIO uses the WebSocket protocol to communicate with the browser in " +"FastAPI/Starlette. If your server is behind a reverse proxy (such as " +"Nginx), you may need to configure the reverse proxy to support the " +"WebSocket protocol. :ref:`Here ` is an example of Nginx " +"WebSocket configuration." +msgstr "" + +#: ../../advanced.rst:217 +msgid "Notes" +msgstr "" + +#: ../../advanced.rst:218 +msgid "**Deployment in production**" +msgstr "" + +#: ../../advanced.rst:220 +msgid "" +"In your production system, you may want to deploy the web applications " +"with some WSGI/ASGI servers such as uWSGI, Gunicorn, and Uvicorn. Since " +"PyWebIO applications store session state in memory of process, when you " +"use HTTP-based sessions (Flask and Django) and spawn multiple workers to " +"handle requests, the request may be dispatched to a process that does not" +" hold the session to which the request belongs. So you can only start one" +" worker to handle requests when using Flask or Django backend." +msgstr "" + +#: ../../advanced.rst:225 +msgid "" +"If you still want to use multiple processes to increase concurrency, one " +"way is to use Uvicorn+FastAPI, or you can also start multiple " +"Tornado/aiohttp processes and add external load balancer (such as HAProxy" +" or nginx) before them. Those backends use the WebSocket protocol to " +"communicate with the browser in PyWebIO, so there is no the issue as " +"described above." +msgstr "" + +#: ../../advanced.rst:229 +msgid "**Static resources Hosting**" +msgstr "" + +#: ../../advanced.rst:231 +msgid "" +"By default, the front-end of PyWebIO gets required static resources from " +"CDN. If you want to deploy PyWebIO applications in an offline " +"environment, you need to host static files by yourself, and set the " +"``cdn`` parameter of ``webio_view()`` or ``webio_handler()`` to " +"``False``." +msgstr "" + +#: ../../advanced.rst:235 +msgid "" +"When setting ``cdn=False`` , you need to host the static resources in the" +" same directory as the PyWebIO application. In addition, you can also " +"pass a string to ``cdn`` parameter to directly set the URL of PyWebIO " +"static resources directory." +msgstr "" + +#: ../../advanced.rst:238 +msgid "" +"The path of the static file of PyWebIO is stored in " +"``pywebio.STATIC_PATH``, you can use the command ``python3 -c \"import " +"pywebio; print(pywebio.STATIC_PATH)\"`` to print it out." +msgstr "" + +#: ../../advanced.rst:243 +msgid "" +"``start_server()`` and ``path_deploy()`` also support ``cdn`` parameter, " +"if it is set to ``False``, the static resource will be hosted in local " +"server automatically, without manual hosting." +msgstr "" + +#: ../../advanced.rst:250 +msgid "Coroutine-based session" +msgstr "" + +#: ../../advanced.rst:251 +msgid "" +"In most cases, you don’t need the coroutine-based session. All functions " +"or methods in PyWebIO that are only used for coroutine sessions are " +"specifically noted in the document." +msgstr "" + +#: ../../advanced.rst:254 +msgid "" +"PyWebIO's session is based on thread by default. Each time a user opens a" +" session connection to the server, PyWebIO will start a thread to run the" +" task function. In addition to thread-based sessions, PyWebIO also " +"provides coroutine-based sessions. Coroutine-based sessions accept " +"coroutine functions as task functions." +msgstr "" + +#: ../../advanced.rst:258 +msgid "" +"The session based on the coroutine is a single-thread model, which means " +"that all sessions run in a single thread. For IO-bound tasks, coroutines " +"take up fewer resources than threads and have performance comparable to " +"threads. In addition, the context switching of the coroutine is " +"predictable, which can reduce the need for program synchronization and " +"locking, and can effectively avoid most critical section problems." +msgstr "" + +#: ../../advanced.rst:264 +msgid "Using coroutine session" +msgstr "" + +#: ../../advanced.rst:266 +msgid "" +"To use coroutine-based session, you need to use the ``async`` keyword to " +"declare the task function as a coroutine function, and use the ``await`` " +"syntax to call the PyWebIO input function:" +msgstr "" + +#: ../../advanced.rst:269 +#, python-format +msgid "" +" from pywebio.input import *\n" +" from pywebio.output import *\n" +" from pywebio import start_server\n" +"\n" +" async def say_hello():\n" +" name = await input(\"what's your name?\")\n" +" put_text('Hello, %s' % name)\n" +"\n" +" start_server(say_hello, auto_open_webbrowser=True)" +msgstr "" + +#: ../../advanced.rst:283 +msgid "" +"In the coroutine task function, you can also use ``await`` to call other " +"coroutines or (`awaitable objects `_) in the standard library " +"`asyncio `_:" +msgstr "" + +#: ../../advanced.rst:287 +msgid "" +" import asyncio\n" +" from pywebio import start_server\n" +"\n" +" async def hello_word():\n" +" put_text('Hello ...')\n" +" await asyncio.sleep(1) # await awaitable objects in asyncio\n" +" put_text('... World!')\n" +"\n" +" async def main():\n" +" await hello_word() # await coroutine\n" +" put_text('Bye, bye')\n" +"\n" +" start_server(main, auto_open_webbrowser=True)" +msgstr "" + +#: ../../advanced.rst:306 +msgid "" +"In coroutine-based session, all input functions defined in the " +":doc:`pywebio.input ` module need to use ``await`` syntax to get " +"the return value. Forgetting to use ``await`` will be a common error when" +" using coroutine-based session." +msgstr "" + +#: ../../advanced.rst:309 +msgid "" +"Other functions that need to use ``await`` syntax in the coroutine " +"session are:" +msgstr "" + +#: ../../advanced.rst:311 +msgid "" +"`pywebio.session.run_asyncio_coroutine(coro_obj) " +"`" +msgstr "" + +#: ../../advanced.rst:312 +msgid "`pywebio.session.eval_js(expression) `" +msgstr "" + +#: ../../advanced.rst:316 +msgid "" +"Although the PyWebIO coroutine session is compatible with the ``awaitable" +" objects`` in the standard library ``asyncio``, the ``asyncio`` library " +"is not compatible with the ``awaitable objects`` in the PyWebIO coroutine" +" session." +msgstr "" + +#: ../../advanced.rst:319 +msgid "" +"That is to say, you can't pass PyWebIO ``awaitable objects`` to the " +"``asyncio`` functions that accept ``awaitable objects``. For example, the" +" following calls are **not supported** ::" +msgstr "" + +#: ../../advanced.rst:322 +msgid "" +"await asyncio.shield(pywebio.input())\n" +"await asyncio.gather(asyncio.sleep(1), pywebio.session.eval_js('1+1'))\n" +"task = asyncio.create_task(pywebio.input())" +msgstr "" + +#: ../../advanced.rst:329 +msgid "Concurrency in coroutine-based sessions" +msgstr "" + +#: ../../advanced.rst:331 +msgid "" +"In coroutine-based session, you can start new thread, but you cannot call" +" PyWebIO interactive functions in it (`register_thread() " +"` is not available in coroutine " +"session). But you can use `run_async(coro) ` " +"to execute a coroutine object asynchronously, and PyWebIO interactive " +"functions can be used in the new coroutine:" +msgstr "" + +#: ../../advanced.rst:336 +msgid "" +" from pywebio import start_server\n" +" from pywebio.session import run_async\n" +"\n" +" async def counter(n):\n" +" for i in range(n):\n" +" put_text(i)\n" +" await asyncio.sleep(1)\n" +"\n" +" async def main():\n" +" run_async(counter(10))\n" +" put_text('Main coroutine function exited.')\n" +"\n" +"\n" +" start_server(main, auto_open_webbrowser=True)" +msgstr "" + +#: ../../advanced.rst:355 +msgid "" +"`run_async(coro) ` returns a `TaskHandler " +"`, which can be used to query" +" the running status of the coroutine or close the coroutine." +msgstr "" + +#: ../../advanced.rst:359 +msgid "Close of session" +msgstr "" + +#: ../../advanced.rst:361 +msgid "" +"Similar to thread-based session, when user close the browser page, the " +"session will be closed." +msgstr "" + +#: ../../advanced.rst:363 +msgid "" +"After the browser page closed, PyWebIO input function calls that have not" +" yet returned in the current session will cause `SessionClosedException " +"`, and subsequent calls to " +"PyWebIO interactive functions will cause `SessionNotFoundException " +"` or `SessionClosedException" +" `." +msgstr "" + +#: ../../advanced.rst:368 +msgid "" +"`defer_call(func) ` also available in " +"coroutine session." +msgstr "" + +#: ../../advanced.rst:373 +msgid "Integration with Web Framework" +msgstr "" + +#: ../../advanced.rst:375 +msgid "" +"The PyWebIO application that using coroutine-based session can also be " +"integrated to the web framework." +msgstr "" + +#: ../../advanced.rst:377 +msgid "" +"However, there are some limitations when using coroutine-based sessions " +"to integrate into Flask or Django:" +msgstr "" + +#: ../../advanced.rst:379 +msgid "" +"First, when ``await`` the coroutine objects/awaitable objects in the " +"``asyncio`` module, you need to use `run_asyncio_coroutine() " +"` to wrap the coroutine object." +msgstr "" + +#: ../../advanced.rst:382 +msgid "" +"Secondly, you need to start a new thread to run the event loop before " +"starting a Flask/Django server." +msgstr "" + +#: ../../advanced.rst:384 +msgid "Example of coroutine-based session integration into Flask:" +msgstr "" + +#: ../../advanced.rst:386 +msgid "" +" import asyncio\n" +" import threading\n" +" from flask import Flask, send_from_directory\n" +" from pywebio import STATIC_PATH\n" +" from pywebio.output import *\n" +" from pywebio.platform.flask import webio_view\n" +" from pywebio.platform import run_event_loop\n" +" from pywebio.session import run_asyncio_coroutine\n" +"\n" +" async def hello_word():\n" +" put_text('Hello ...')\n" +" await run_asyncio_coroutine(asyncio.sleep(1)) # can't just \"await " +"asyncio.sleep(1)\"\n" +" put_text('... World!')\n" +"\n" +" app = Flask(__name__)\n" +" app.add_url_rule('/hello', 'webio_view', webio_view(hello_word),\n" +" methods=['GET', 'POST', 'OPTIONS'])\n" +"\n" +" # thread to run event loop\n" +" threading.Thread(target=run_event_loop, daemon=True).start()\n" +" app.run(host='localhost', port=80)" +msgstr "" + +#: ../../advanced.rst:411 +msgid "" +"Finally, coroutine-based session is not available in the script mode. You" +" always need to use ``start_server()`` to run coroutine task function or " +"integrate it to a web framework." +msgstr "" + diff --git a/docs/locales/fa/LC_MESSAGES/arch.po b/docs/locales/fa/LC_MESSAGES/arch.po new file mode 100644 index 00000000..2100fdb4 --- /dev/null +++ b/docs/locales/fa/LC_MESSAGES/arch.po @@ -0,0 +1,509 @@ +# Copyright (C) Weimin Wang +# This file is distributed under the same license as the PyWebIO package. +# +# FIRST AUTHOR , 2022. +# Pikhosh , 2022. +msgid "" +msgstr "" +"Project-Id-Version: PyWebIO 1.5.2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-02-26 16:35+0330\n" +"PO-Revision-Date: 2022-02-28 14:20+0330\n" +"Last-Translator: Pikhosh \n" +"Language-Team: Persian <>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" +"Language: fa\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Lokalize 21.12.2\n" + +#: ../../arch.rst:2 +msgid "Architecture" +msgstr "" + +#: ../../arch.rst:5 +msgid "概念" +msgstr "" + +#: ../../arch.rst:7 +msgid "``Session`` 表示浏览器与程序交互产生的一次会话。PyWebIO在会话中运行 ``Task`` ,任务是" +msgstr "" + +#: ../../arch.rst:9 +msgid "会话中除了起始的执行单元,也可以并发启动新的执行单元,在新的执行单元中也可以进行输入输出。" +msgstr "" + +#: ../../arch.rst:11 +msgid "在用户端,相同会话中的不同的执行单元的输入是独立的,共享输出空间,但输出域的栈结构各自独立。" +msgstr "" + +#: ../../arch.rst:13 +msgid "" +"若用户正在填写一个执行单元的表单,会话中的其他执行单元也开始向用户请求输入,此时用户正在填写的表单将会隐藏, " +"新的输入表单将会显示给用户,当用户填写完新表单并提交后,旧表单重新显示,之前在旧表单上的输入也会保留。" +msgstr "" + +#: ../../arch.rst:16 +msgid "在基于线程的会话中,会话中的每个执行单元都是一个线程" +msgstr "" + +#: ../../arch.rst:18 +msgid "在基于协程的会话中,会话中的每个执行单元都是一个协程" +msgstr "" + +#: ../../arch.rst:20 +msgid "除了并发执行的执行单元,会话中还有事件回调函数,目前就只有按钮控件可以绑定点击事件的回调函数。" +msgstr "" + +#: ../../arch.rst:23 +msgid "架构" +msgstr "" + +#: ../../arch.rst:25 +msgid "" +"会话内的每个执行单元使用唯一的task_id进行标识,由于会话内的输入需要区分执行单元,所以每个表单提交时, " +"除了表单的内容以外,还会携带表单所在的执行单元的task_id,这样,后台会话才可以知道该将表单数据传递给哪个执行单元。" +msgstr "" + +#: ../../arch.rst:31 +msgid "" +"PyWebIO会话是由事件驱动的,这些事件来自用户在页面上的操作,比如提交表单,点击按钮,这些事件会通过http请求或websocket连接发送到后端框架。" +msgstr "" + +#: ../../arch.rst:33 +msgid "" +"后端框架维护有当前在线的Session实例,后端框架在收到用户提交的事件后,回调用相关Session实例的 " +"``send_client_event()`` 方法将事件发送至会话;" +msgstr "" + +#: ../../arch.rst:35 +msgid "" +"一个会话内会拥有至少一个执行单元,执行单元在调用PyWebIO的输入函数后会临时挂起,当会话收到用户的输入提交后,会话便将执行单元恢复执行,并提供用户输入的" +"值。" +" 执行单元内,任何输入输出的调用都会转换成一些命令序列发送给会话." +msgstr "" + +#: ../../arch.rst:38 +msgid "" +"当后端框架通过HTTP与用户浏览器通信时,用户浏览器是以轮训的方式获取指令,会话会保存由执行单元生成的、还未发送到浏览器的命令序列,等待下次轮训时由后端框架" +"取走。" +msgstr "" + +#: ../../arch.rst:40 +msgid "" +"当后端框架通过WebSocket与用户建立连接时,任何由执行单元发送到会话的命令都会立即发送到后端,并由后端通过WebSocket连接通知用户浏览器。" +msgstr "" + +#: ../../arch.rst:43 +msgid "实现" +msgstr "" + +#: ../../arch.rst:46 +msgid "后端与Session的交互" +msgstr "" + +#: ../../arch.rst:48 +msgid "后端框架负责从Session会话中获取来自PyWebIO的指令,并发送给用户浏览器;同时后端框架接收用户提交的数据,并发送给相应的会话实例。" +msgstr "" + +#: ../../arch.rst:50 +msgid "" +"Session暴露给后端框架的方法仅有 `Session.send_client_event " +"` 、 " +"`Session.get_task_commands " +"` 和 `Session.close " +"` 。" +msgstr "" + +#: ../../arch.rst:54 +msgid "基于HTTP通信的后端的实现逻辑" +msgstr "" + +#: ../../arch.rst:56 +msgid "**基于HTTP的前后端通信约定**" +msgstr "" + +#: ../../arch.rst:58 +msgid "前端按照固定间隔使用GET请求轮训后端接口,在请求中使用 ``webio-session-id`` HTTP头来传递会话ID。" +msgstr "" + +#: ../../arch.rst:60 +msgid "" +"会话一开始时,会话ID由后端生成并通过响应中的 ``webio-session-id`` HTTP头返回给前端,后续前端的请求都会在请求头中使用 " +"``webio-session-id`` 字段传递会话ID。" +msgstr "" + +#: ../../arch.rst:62 +msgid "" +"前端产生的事件使用POST请求发送给后端。对于前端的每次轮训和事件提交请求,后端都会返回当前未执行的指令序列作为响应,前端收到响应后会依次执行指令。" +msgstr "" + +#: ../../arch.rst:64 ../../arch.rst:93 +msgid "**代码实现**" +msgstr "" + +#: ../../arch.rst:66 +msgid "" +"以Flask后端为例,Flask后端与Session的交互都在Flask视图函数中实现,视图函数通过调用 " +"`pywebio.platform.flask.webio_view " +"<./_modules/pywebio/platform/flask.html#webio_view>`_ 获取, 在 `webio_view` " +"中,先是实例化了一个 `pywebio.platform.httpbased.HttpHandler` " +",然后声明了一个内部函数,这个内部函数就是Flask视图函数, 在视图函数内,先是实例化了一个 " +"`pywebio.platform.flask.FlaskHttpContext` 对象,然后通过调用 " +"``HttpHandler.handle_request(FlaskHttpContext)`` 就获得了视图的响应。" +msgstr "" + +#: ../../arch.rst:70 +msgid "" +"这其中,FlaskHttpContext 的基类为 HttpContext ,HttpContext " +"接口各异的后端框架定义了一个统一的操作接口,用于从当前请求中获取请求相关的数据并设置请求的相应。 FlaskHttpContext " +"为HttpContext接口的Flask实现。" +msgstr "" + +#: ../../arch.rst:72 +msgid "" +"而 HttpHandle 负责维护Session实例并实现HTTP请求与Session之间的交互,HttpHandle " +"与后端框架相关的交互全都通过 HttpContext 操作。" +msgstr "" + +#: ../../arch.rst:74 +msgid "HttpContext的生命周期为一次HTTP请求,HttpHandle的生命周期和整个后端框架的生命周期一致。" +msgstr "" + +#: ../../arch.rst:76 +msgid "" +"HttpHandler.handle_request 负责处理前端发送给后端的每一次请求,HttpHandler.handle_request " +"的处理流程如下:" +msgstr "" + +#: ../../arch.rst:78 +msgid "检测当前HTTP请求是否满足跨域设置" +msgstr "" + +#: ../../arch.rst:79 +msgid "" +"根绝当前请求的 webio-session-id 头信息找到相应的Session实例,若不存在 webio-session-id " +"头则创建新会话并分配webio-session-id" +msgstr "" + +#: ../../arch.rst:80 +msgid "若当前请求为POST事件提交请求,则将提交的数据通过 Session.send_client_event 发送给Session" +msgstr "" + +#: ../../arch.rst:81 +msgid "通过调用 Session.get_task_commands 获取待执行的指令序列,并通过 HttpContext 向后端设置响应数据" +msgstr "" + +#: ../../arch.rst:83 +msgid "" +"此外,基于HTTP的会话,用户主动关闭会话时(比如关闭浏览器),后端无法立即感知,所以在HttpHandler.handle_request 中," +" 还会周期性地检测会话的最后活跃时间,将一段时间内不活跃的会话视为过期,所以在HttpHandler清理过期会话并调用 Session.close" +" 释放会话内的资源。" +msgstr "" + +#: ../../arch.rst:88 +msgid "基于WebSocket通信的后端的实现逻辑" +msgstr "" + +#: ../../arch.rst:89 +msgid "**基于WebSocket的前后端通信约定:**" +msgstr "" + +#: ../../arch.rst:91 +msgid "" +"浏览器与后端使用一个WebSocket连接来保持一个会话,后端的指令通过JSON序列化之后的消息实时发送给前端,前端用户触发的事件数据也通过JSON序列化之" +"后发送给后端。" +msgstr "" + +#: ../../arch.rst:95 +msgid "以Tornado后端为例" +msgstr "" + +#: ../../arch.rst:97 +msgid "" +"webio_handler用于获取Tornado与前端进行通信的WebSocketHandler子类,其逻辑实现在 _webio_handler " +"中,由于WebSocket的有状态性, " +"WebSocketHandler子类的实现比基于HTTP通信的HttpHandler要简单许多,关键部分如下:" +msgstr "" + +#: ../../arch.rst:100 +msgid "" +"在WebSocket连接创建的时候初始化Session实例,并向Session对象注册了 " +"on_task_command和on_session_close 回调,分别在新指令产生时和会话由执行单元关闭时由Session调用, " +"用于实现WebSocketHandler向前端实时发送指令" +msgstr "" + +#: ../../arch.rst:102 +msgid "" +"在收到前端浏览器发送来的消息后,WebSocketHandler将收到的数据通过 Session.send_client_event " +"发送给Session" +msgstr "" + +#: ../../arch.rst:103 +msgid "在WebSocket连接关闭时,调用 Session.close 释放会话内的资源。" +msgstr "" + +#: ../../arch.rst:106 +msgid "session与执行单元(输入/输出)的交互" +msgstr "" + +#: ../../arch.rst:108 +msgid "会话提供给执行单元的关键接口有:" +msgstr "" + +#: ../../arch.rst:110 +msgid "get_current_session : 静态方法,获取当前执行单元所属的会话实例" +msgstr "" + +#: ../../arch.rst:111 +msgid "get_current_task_id : 静态方法,获取当前执行单元所属的id" +msgstr "" + +#: ../../arch.rst:112 +msgid "send_task_command : 向会话发送指令" +msgstr "" + +#: ../../arch.rst:113 +msgid "next_client_event : 读取来自浏览器的属于当前执行单元的下一个事件" +msgstr "" + +#: ../../arch.rst:114 +msgid "register_callback : 向会话注册一个回调" +msgstr "" + +#: ../../arch.rst:116 +msgid "同时,会话根据实现方式不同,还分别提供了 register_thread 和 run_async 用于启动新的执行单元。" +msgstr "" + +#: ../../arch.rst:119 +msgid "**回调机制**" +msgstr "" + +#: ../../arch.rst:121 +msgid "" +"在会话中,为了能够响应用户在界面上的某些事件(比如点击了输出内容中的某个按钮),于是设计了回调机制,可以在执行单元中使用register_callback向" +"当前会话注册回调,然后执行单元会得到一个回调ID," +" 执行单元再通过相关指令让浏览器输出一些可以触发的控件,并向控件绑定回调ID,当用户触发控件后,前端将带有回调ID的 :ref:`回调事件 " +"` 发回会话,会话会在专门的执行单元中或启动新执行单元中运行回调。" +msgstr "" + +#: ../../arch.rst:125 +msgid "基于线程的会话实现" +msgstr "" + +#: ../../arch.rst:127 +msgid "" +"在基于线程的会话中,每个执行单元都是一个线程,每个执行单元通过一条消息队列从会话接收来自用户的事件消息,当执行单元所需要的事件用户还没有提交时,执行单元便会" +"挂起。" +msgstr "" + +#: ../../arch.rst:129 +msgid "" +"基于线程的会话使用线程ID作为执行单元的ID,在全局使用一个以线程id为key的字典来映射执行单元所属的会话实例,会话内不同执行单元的用户事件消息队列也通过" +"执行单元ID进行索引。" +msgstr "" + +#: ../../arch.rst:131 +msgid "使用 register_thread 启动新的执行单元时,也需要为新执行单元注册用户事件消息队列。" +msgstr "" + +#: ../../arch.rst:134 +msgid "基于协程的会话实现" +msgstr "" + +#: ../../arch.rst:135 +msgid "" +"在基于协程的会话中,每个执行单元都是一个由协程包装成的任务对象(Task),当会话接收来自用户的事件消息后,便激活相应的任务对象,使得协程恢复运行。" +msgstr "" + +#: ../../arch.rst:137 +msgid "" +"由于基于协程的会话是单线程的,所以会话在激活任务对象前是通过将上下文信息保存在全局变量中来实现 get_current_session 和 " +"get_current_task_id 方法,全局的上下文信息包含当前将要执行的会话的实例和执行单元的ID。" +msgstr "" + +#: ../../arch.rst:141 +msgid "Script mode的实现" +msgstr "" + +#: ../../arch.rst:142 +msgid "" +"Script mode " +"也是基于线程的,但由于全局仅存在一个会话,所有执行单元必定全部属于这个会话,所以也无需主动调用register_thread(thread)注册线程。" +msgstr "" + +#: ../../arch.rst:144 +msgid "当PyWebIO检测到用户代码在后端Server还未启动的情况下就调用了PyWebIO交互函数时,便会启动Script mode:" +msgstr "" + +#: ../../arch.rst:146 +msgid "在新线程中启动后端Server" +msgstr "" + +#: ../../arch.rst:147 +msgid "启动浏览器打开后端Server运行的地址" +msgstr "" + +#: ../../arch.rst:148 +msgid "在第一次与用户建立连接时初始化会话" +msgstr "" + +#: ../../arch.rst:150 +msgid "script mode的会话类继承了基于线程的会话类,并修改了部分方法:" +msgstr "" + +#: ../../arch.rst:152 +msgid "构造函数 : 仅允许script mode会话类被初始化一次" +msgstr "" + +#: ../../arch.rst:153 +msgid "get_current_session : 直接返回全局的会话对象" +msgstr "" + +#: ../../arch.rst:154 +msgid "get_current_task_id : 除了返回当前线程id,还会自动将当前线程使用 register_thread 注册到会话中" +msgstr "" + +#: ../../arch.rst:157 +msgid "相关对象的文档" +msgstr "" + +#: of pywebio.platform.httpbased.HttpHandler:1 +msgid "基于HTTP的后端Handler实现" +msgstr "" + +#: of pywebio.platform.httpbased.HttpHandler:4 +msgid "" +"Don't need a lock when access HttpHandler._webio_sessions, See: " +"https://stackoverflow.com/questions/1312331/using-a-global-dictionary-" +"with-threads-in-python" +msgstr "" + +#: of pywebio.platform.httpbased.HttpHandler.handle_request_context:1 +msgid "called when every http request" +msgstr "" + +#: of pywebio.session.base.Session:1 +msgid "会话对象,由Backend创建" +msgstr "" + +#: of pywebio.session.base.Session:5 +msgid "属性:" +msgstr "" + +#: of pywebio.session.base.Session:4 +msgid "info 表示会话信息的对象 save 会话的数据对象,提供用户在对象上保存一些会话相关数据" +msgstr "" + +#: of pywebio.session.base.Session:20 +msgid "由Task在当前Session上下文中调用:" +msgstr "" + +#: of pywebio.session.base.Session:8 +msgid "get_current_session get_current_task_id" +msgstr "" + +#: of pywebio.session.base.Session:11 +msgid "" +"get_scope_name pop_scope push_scope send_task_command next_client_event " +"on_task_exception register_callback need_keep_alive" +msgstr "" + +#: of pywebio.session.base.Session:20 +msgid "defer_call" +msgstr "" + +#: of pywebio.session.base.Session:25 +msgid "由Backend调用:" +msgstr "" + +#: of pywebio.session.base.Session:23 +msgid "send_client_event get_task_commands close" +msgstr "" + +#: of pywebio.session.base.Session:28 +msgid "Task和Backend都可调用:" +msgstr "" + +#: of pywebio.session.base.Session:28 +msgid "closed" +msgstr "" + +#: of pywebio.session.base.Session:33 +msgid "Session是不同的后端Backend与协程交互的桥梁:" +msgstr "" + +#: of pywebio.session.base.Session:31 +msgid "" +"后端Backend在接收到用户浏览器的数据后,会通过调用 ``send_client_event`` " +"来通知会话,进而由Session驱动协程的运行。 Task内在调用输入输出函数后,会调用 ``send_task_command`` " +"向会话发送输入输出消息指令, Session将其保存并留给后端Backend处理。" +msgstr "" + +#: of pywebio.session.base.Session.get_scope_name:1 +msgid "获取当前任务的scope栈检索scope名" +msgstr "" + +#: of pywebio.session.base.Session.close +#: pywebio.session.base.Session.defer_call +#: pywebio.session.base.Session.get_scope_name +msgid "Parameters" +msgstr "پارامتر ها" + +#: of pywebio.session.base.Session.get_scope_name:3 +msgid "scope栈的索引" +msgstr "" + +#: of pywebio.session.base.Session.get_scope_name +#: pywebio.session.base.Session.pop_scope +msgid "Returns" +msgstr "" + +#: of pywebio.session.base.Session.get_scope_name:4 +msgid "scope名,不存在时返回 None" +msgstr "" + +#: of pywebio.session.base.Session.pop_scope:1 +msgid "弹出当前scope" +msgstr "" + +#: of pywebio.session.base.Session.pop_scope:3 +msgid "当前scope名" +msgstr "" + +#: of pywebio.session.base.Session.push_scope:1 +msgid "进入新scope" +msgstr "" + +#: of pywebio.session.base.Session.next_client_event:1 +msgid "获取来自客户端的下一个事件。阻塞调用,若在等待过程中,会话被用户关闭,则抛出SessionClosedException异常" +msgstr "" + +#: of pywebio.session.base.Session.close:1 +msgid "Close current session" +msgstr "" + +#: of pywebio.session.base.Session.close:3 +msgid "Don't block thread. Used in closing from backend." +msgstr "" + +#: of pywebio.session.base.Session.register_callback:1 +msgid "向Session注册一个回调函数,返回回调id" +msgstr "" + +#: of pywebio.session.base.Session.register_callback:3 +msgid "" +"Session需要保证当收到前端发送的事件消息 ``{event: \"callback\",task_id: 回调id, data:...}``" +" 时, ``callback`` 回调函数被执行, 并传入事件消息中的 ``data`` 字段值作为参数" +msgstr "" + +#: of pywebio.session.base.Session.defer_call:1 +msgid "设置会话结束时调用的函数。可以用于资源清理。 在会话中可以多次调用 `defer_call()` ,会话结束后将会顺序执行设置的函数。" +msgstr "" + +#: of pywebio.session.base.Session.defer_call:4 +msgid "话结束时调用的函数" +msgstr "" + diff --git a/docs/locales/fa/LC_MESSAGES/cookbook.po b/docs/locales/fa/LC_MESSAGES/cookbook.po new file mode 100644 index 00000000..30e4e634 --- /dev/null +++ b/docs/locales/fa/LC_MESSAGES/cookbook.po @@ -0,0 +1,455 @@ +# Copyright (C) Weimin Wang +# This file is distributed under the same license as the PyWebIO package. +# +# FIRST AUTHOR , 2022. +# Pikhosh , 2022. +msgid "" +msgstr "" +"Project-Id-Version: PyWebIO 1.5.2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-02-26 16:35+0330\n" +"PO-Revision-Date: 2022-02-28 12:33+0330\n" +"Last-Translator: Pikhosh \n" +"Language-Team: Persian <>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" +"Language: fa\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Lokalize 21.12.2\n" + +#: ../../cookbook.rst:2 +msgid "Cookbook" +msgstr "دستور های پخت" + +#: ../../cookbook.rst:8 +msgid "Interaction related" +msgstr "" + +#: ../../cookbook.rst:11 +msgid "Equivalent to \"Press any key to continue\"" +msgstr "" + +#: ../../cookbook.rst:13 +msgid "" +"actions(buttons=[\"Continue\"])\n" +"put_text(\"Go next\") # ..demo-only" +msgstr "" + +#: ../../cookbook.rst:22 +msgid "Output pandas dataframe" +msgstr "" + +#: ../../cookbook.rst:24 +msgid "" +"import numpy as np\n" +"import pandas as pd\n" +"\n" +"df = pd.DataFrame(np.random.randn(6, 4), columns=list(\"ABCD\"))\n" +"put_html(df.to_html(border=0))" +msgstr "" + +#: ../../cookbook.rst:34 +msgid "" +"`pandas.DataFrame.to_html — pandas documentation " +"`_" +msgstr "" + +#: ../../cookbook.rst:37 +msgid "Output Matplotlib figure" +msgstr "" + +#: ../../cookbook.rst:39 +msgid "" +"Simply do not call ``matplotlib.pyplot.show``, directly save the figure " +"to in-memory buffer and output the buffer via " +":func:`pywebio.output.put_image`:" +msgstr "" + +#: ../../cookbook.rst:42 +msgid "" +"import matplotlib\n" +"import matplotlib.pyplot as plt\n" +"import io\n" +"import pywebio\n" +"\n" +"matplotlib.use('agg') # required, use a non-interactive backend\n" +"\n" +"fig, ax = plt.subplots() # Create a figure containing a single axes.\n" +"ax.plot([1, 2, 3, 4], [1, 4, 2, 3]) # Plot some data on the axes.\n" +"\n" +"buf = io.BytesIO()\n" +"fig.savefig(buf)\n" +"pywebio.output.put_image(buf.getvalue())" +msgstr "" + +#: ../../cookbook.rst:60 +msgid "" +"The ``matplotlib.use('agg')`` is required so that the server does not try" +" to create (and then destroy) GUI windows that will never be seen." +msgstr "" + +#: ../../cookbook.rst:63 +msgid "" +"When using Matplotlib in a web server (multiple threads environment), " +"pyplot may cause some conflicts in some cases, read the following " +"articles for more information:" +msgstr "" + +#: ../../cookbook.rst:66 +msgid "" +"`Multi Threading in Python and Pyplot | by Ranjitha Korrapati | Medium " +"`_" +msgstr "" + +#: ../../cookbook.rst:68 +msgid "" +"`Embedding in a web application server (Flask) — Matplotlib documentation" +" " +"`_" +msgstr "" + +#: ../../cookbook.rst:72 +msgid "Blocking confirm model" +msgstr "" + +#: ../../cookbook.rst:74 +msgid "" +"The following code uses the lock mechanism to make the button callback " +"function synchronous:" +msgstr "" + +#: ../../cookbook.rst:78 +msgid "" +"import threading\n" +"from pywebio import output\n" +"\n" +"def confirm(title, content=None, timeout=None):\n" +" \"\"\"Show a confirm model.\n" +"\n" +" :param str title: Model title.\n" +" :param list/put_xxx() content: Model content.\n" +" :param None/float timeout: Seconds for operation time out.\n" +" :return: Return `True` when the \"CONFIRM\" button is clicked,\n" +" return `False` when the \"CANCEL\" button is clicked,\n" +" return `None` when a timeout is given and the operation times " +"out.\n" +" \"\"\"\n" +" if not isinstance(content, list):\n" +" content = [content]\n" +"\n" +" event = threading.Event()\n" +" result = None\n" +"\n" +" def onclick(val):\n" +" nonlocal result\n" +" result = val\n" +" event.set()\n" +"\n" +" content.append(output.put_buttons([\n" +" {'label': 'CONFIRM', 'value': True},\n" +" {'label': 'CANCEL', 'value': False, 'color': 'danger'},\n" +" ], onclick=onclick))\n" +" output.popup(title=title, content=content, closable=False)\n" +"\n" +" event.wait(timeout=timeout) # wait the model buttons are clicked\n" +" output.close_popup()\n" +" return result\n" +"\n" +"\n" +"res = confirm('Confirm', 'You have 5 seconds to make s choice', " +"timeout=5)\n" +"output.put_text(\"Your choice is:\", res)" +msgstr "" + +#: ../../cookbook.rst:121 +msgid "Input in the popup" +msgstr "" + +#: ../../cookbook.rst:124 +msgid "" +"In the following code, we define a ``popup_input()`` function, which can " +"be used to get input in popup:" +msgstr "" + +#: ../../cookbook.rst:128 +msgid "" +"def popup_input(pins, names, title='Please fill out the form'):\n" +" \"\"\"Show a form in popup window.\n" +"\n" +" :param list pins: pin output list.\n" +" :param list pins: pin name list.\n" +" :param str title: model title.\n" +" :return: return the form as dict, return None when user cancel the " +"form.\n" +" \"\"\"\n" +" if not isinstance(pins, list):\n" +" pins = [pins]\n" +"\n" +" from pywebio.utils import random_str\n" +" action_name = 'action_' + random_str(10)\n" +"\n" +" pins.append(put_actions(action_name, buttons=[\n" +" {'label': 'Submit', 'value': True},\n" +" {'label': 'Cancel', 'value': False, 'color': 'danger'},\n" +" ]))\n" +" popup(title=title, content=pins, closable=False)\n" +"\n" +" change_info = pin_wait_change(action_name)\n" +" result = None\n" +" if change_info['name'] == action_name and change_info['value']:\n" +" result = {name: pin[name] for name in names}\n" +" close_popup()\n" +" return result\n" +"\n" +"\n" +"from pywebio.pin import put_input\n" +"\n" +"result = popup_input([\n" +" put_input('name', label='Input your name'),\n" +" put_input('age', label='Input your age', type=\"number\")\n" +"], names=['name', 'age'])\n" +"put_text(result)" +msgstr "" + +#: ../../cookbook.rst:168 +msgid "" +"The code uses :doc:`pin module ` to add input widgets to popup " +"window, and uses the lock mechanism to wait the form buttons to be " +"clicked." +msgstr "" + +#: ../../cookbook.rst:173 +msgid "Redirect stdout to PyWebIO application" +msgstr "" + +#: ../../cookbook.rst:176 +msgid "" +"The following code shows how to redirect stdout of python code and " +"subprocess to PyWebIO application:" +msgstr "" + +#: ../../cookbook.rst:180 +msgid "" +"import io\n" +"import time\n" +"import subprocess # ..doc-only\n" +"from contextlib import redirect_stdout\n" +"\n" +"# redirect `print()` to pywebio\n" +"class WebIO(io.IOBase):\n" +" def write(self, content):\n" +" put_text(content, inline=True)\n" +"\n" +"with redirect_stdout(WebIO()):\n" +" for i in range(10):\n" +" print(i, time.time())\n" +" time.sleep(0.2)\n" +"\n" +"## ----\n" +"import subprocess # ..demo-only\n" +"# redirect a subprocess' stdout to pywebio\n" +"process = subprocess.Popen(\"ls -ahl\", shell=True, " +"stdout=subprocess.PIPE, stderr=subprocess.STDOUT)\n" +"while True:\n" +" output = process.stdout.readline()\n" +" if output:\n" +" put_text(output.decode('utf8'), inline=True)\n" +"\n" +" if not output and process.poll() is not None:\n" +" break" +msgstr "" + +#: ../../cookbook.rst:213 +msgid "Add missing syntax highlight for code output" +msgstr "" + +#: ../../cookbook.rst:215 +msgid "" +"When output code via `put_markdown()` or `put_code()`, PyWebIO provides " +"syntax highlight for some common languages. If you find your code have no" +" syntax highlight, you can add the syntax highlighter by two following " +"steps:" +msgstr "" + +#: ../../cookbook.rst:218 +msgid "" +"Go to `prismjs CDN page " +"`_" +" to get your syntax highlighter link." +msgstr "" + +#: ../../cookbook.rst:219 +msgid "" +"Use :func:`config(js_file=...) ` to load the syntax " +"highlight module" +msgstr "" + +#: ../../cookbook.rst:223 +msgid "" +"@config(js_file=\"https://cdn.jsdelivr.net/npm/prismjs@1.23.0/components" +"/prism-diff.min.js\")\n" +"def main():\n" +" put_code(\"\"\"\n" +"+ AAA\n" +"- BBB\n" +"CCC\n" +" \"\"\".strip(), language='diff')\n" +"\n" +" put_markdown(\"\"\"\n" +" ```diff\n" +" + AAA\n" +" - BBB\n" +" CCC\n" +" ```\n" +" \"\"\", lstrip=True)" +msgstr "" + +#: ../../cookbook.rst:242 +msgid "Web application related" +msgstr "" + +#: ../../cookbook.rst:245 +msgid "Get URL parameters of current page" +msgstr "" + +#: ../../cookbook.rst:247 +msgid "" +"You can use URL parameter (known also as \"query strings\" or \"URL query" +" parameters\") to pass information to your web application. In PyWebIO " +"application, you can use the following code to get the URL parameters as " +"a Python dict." +msgstr "" + +#: ../../cookbook.rst:250 +msgid "" +"# `query` is a dict\n" +"query = eval_js(\"Object.fromEntries(new " +"URLSearchParams(window.location.search))\")\n" +"put_text(query)" +msgstr "" + +#: ../../cookbook.rst:260 +msgid "Add Google AdSense/Analytics code" +msgstr "" + +#: ../../cookbook.rst:262 +msgid "" +"When you setup Google AdSense/Analytics, you will get a javascript file " +"and a piece of code that needs to be inserted into your application page," +" you can use :func:`pywebio.config()` to inject js file and code to your " +"PyWebIO application::" +msgstr "" + +#: ../../cookbook.rst:265 +msgid "" +"from pywebio import start_server, output, config\n" +"\n" +"js_file = \"https://www.googletagmanager.com/gtag/js?id=G-xxxxxxx\"\n" +"js_code = \"\"\"\n" +"window.dataLayer = window.dataLayer || [];\n" +"function gtag(){dataLayer.push(arguments);}\n" +"gtag('js', new Date());\n" +"\n" +"gtag('config', 'G-xxxxxxx');\n" +"\"\"\"\n" +"\n" +"@config(js_file=js_file, js_code=js_code)\n" +"def main():\n" +" output.put_text(\"hello world\")\n" +"\n" +"start_server(main, port=8080)" +msgstr "" + +#: ../../cookbook.rst:284 +msgid "Refresh page on connection lost" +msgstr "" + +#: ../../cookbook.rst:286 +msgid "" +"Add the following code to the beginning of your PyWebIO application main " +"function::" +msgstr "" + +#: ../../cookbook.rst:288 +msgid "" +"session.run_js('WebIO._state.CurrentSession.on_session_close(()=" +">{setTimeout(()=>location.reload()," +" 4000})')" +msgstr "" + +#: ../../cookbook.rst:291 +msgid "Cookie and localStorage manipulation" +msgstr "" + +#: ../../cookbook.rst:294 +msgid "" +"You can use `pywebio.session.run_js()` and `pywebio.session.eval_js()` to" +" deal with cookies or localStorage with js." +msgstr "" + +#: ../../cookbook.rst:296 +msgid "``localStorage`` manipulation:" +msgstr "" + +#: ../../cookbook.rst:298 +msgid "" +"set_localstorage = lambda key, value: run_js(\"localStorage.setItem(key, " +"value)\", key=key, value=value)\n" +"get_localstorage = lambda key: eval_js(\"localStorage.getItem(key)\", " +"key=key)\n" +"\n" +"set_localstorage('hello', 'world')\n" +"val = get_localstorage('hello')\n" +"put_text(val)" +msgstr "" + +#: ../../cookbook.rst:310 +msgid "Cookie manipulation:" +msgstr "" + +#: ../../cookbook.rst:314 +msgid "" +"# https://stackoverflow.com/questions/14573223/set-cookie-and-get-cookie-" +"with-javascript\n" +"run_js(\"\"\"\n" +"window.setCookie = function(name,value,days) {\n" +" var expires = \"\";\n" +" if (days) {\n" +" var date = new Date();\n" +" date.setTime(date.getTime() + (days*24*60*60*1000));\n" +" expires = \"; expires=\" + date.toUTCString();\n" +" }\n" +" document.cookie = name + \"=\" + (value || \"\") + expires + \"; " +"path=/\";\n" +"}\n" +"window.getCookie = function(name) {\n" +" var nameEQ = name + \"=\";\n" +" var ca = document.cookie.split(';');\n" +" for(var i=0;i < ca.length;i++) {\n" +" var c = ca[i];\n" +" while (c.charAt(0)==' ') c = c.substring(1,c.length);\n" +" if (c.indexOf(nameEQ) == 0) return " +"c.substring(nameEQ.length,c.length);\n" +" }\n" +" return null;\n" +"}\n" +"\"\"\")\n" +"\n" +"def setcookie(key, value, days=0):\n" +" run_js(\"setCookie(key, value, days)\", key=key, value=value, " +"days=days)\n" +"\n" +"def getcookie(key):\n" +" return eval_js(\"getCookie(key)\", key=key)\n" +"\n" +"setcookie('hello', 'world')\n" +"val = getcookie('hello')\n" +"put_text(val)" +msgstr "" + diff --git a/docs/locales/fa/LC_MESSAGES/exceptions.po b/docs/locales/fa/LC_MESSAGES/exceptions.po new file mode 100644 index 00000000..9f9706f3 --- /dev/null +++ b/docs/locales/fa/LC_MESSAGES/exceptions.po @@ -0,0 +1,43 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) Weimin Wang +# This file is distributed under the same license as the PyWebIO package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PyWebIO 1.5.2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-02-26 16:35+0330\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../exceptions.rst:2 +msgid "``pywebio.exceptions``" +msgstr "" + +#: of pywebio.exceptions:2 +msgid "pywebio.exceptions" +msgstr "" + +#: of pywebio.exceptions:4 +msgid "This module contains the set of PyWebIO's exceptions." +msgstr "" + +#: of pywebio.exceptions.SessionException:1 +msgid "Base class for PyWebIO session related exceptions" +msgstr "" + +#: of pywebio.exceptions.SessionClosedException:1 +msgid "The session has been closed abnormally" +msgstr "" + +#: of pywebio.exceptions.SessionNotFoundException:1 +msgid "Session not found" +msgstr "" + diff --git a/docs/locales/fa/LC_MESSAGES/guide.po b/docs/locales/fa/LC_MESSAGES/guide.po new file mode 100644 index 00000000..78c0ac63 --- /dev/null +++ b/docs/locales/fa/LC_MESSAGES/guide.po @@ -0,0 +1,1380 @@ +# Copyright (C) Weimin Wang +# This file is distributed under the same license as the PyWebIO package. +# +# FIRST AUTHOR , 2022. +# Pikhosh , 2022. +msgid "" +msgstr "" +"Project-Id-Version: PyWebIO 1.5.2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-02-26 16:35+0330\n" +"PO-Revision-Date: 2022-03-14 02:17+0330\n" +"Last-Translator: Pikhosh \n" +"Language-Team: Persian <>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" +"Language: fa\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Lokalize 21.12.2\n" + +#: ../../guide.rst:2 +msgid "User's guide" +msgstr "راهنمای کاربر" + +#: ../../guide.rst:4 +msgid "" +"If you are familiar with web development, you may not be accustomed to " +"the usage of PyWebIO described below, which is different from the " +"traditional web development pattern that backend implement api and " +"frontend display content. In PyWebIO, you only need to write code in " +"Python." +msgstr "" +"اگر شما با توسعه وب آشنایی دارید، ممکن است که به " +"استفاده از PyWebIO که در زیر توضیح داده شده عادت نداشته باشید، که از " +"الگو توسعه وب سنتی که بک اند API را پیاده سازی و " +"فرانت اند محتوا را نمایش می دهد متفاوت است. در PyWebIO، شما فقط نیاز دارید که" +" به " +"پایتون کد بنویسید." + +#: ../../guide.rst:8 +msgid "" +"In fact, the way of writing PyWebIO applications is more like writing a " +"console program, except that the terminal here becomes a browser. Using " +"the imperative API provided by PyWebIO, you can simply call " +"``put_text()``, ``put_image()``, ``put_table()`` and other functions to " +"output text, pictures, tables and other content to the browser, or you " +"can call some functions such as ``input()``, ``select()``, " +"``file_upload()`` to display different forms on the browser to get user " +"input. In addition, PyWebIO also provides support for click events, " +"layout, etc. PyWebIO aims to allow you to use the least code to interact " +"with the user and provide a good user experience as much as possible." +msgstr "" +"در واقع، نحوه نوشتن اپلیکیشن های PyWebIO بیشتر شبیه نوشتن یک " +"برنامه کنسول است، بجز اینکه ترمینال در اینجا تبدیل به یک مرورگر می شود. با" +" استفاده " +"از API دستوری ارائه شده توسط PyWebIO، شما می توانید به راحتی " +"``put_text()``, ``put_image()``, ``put_table()`` و توابع دیگر را برای " +"خروجی دادن متن، تصاویر، جداول و محتوا های دیگر به مرورگر فراخوانی کنید یا شما " +"می توانید برخی توابع مانند ``input()``, ``select()``, " +"``file_upload()`` را برای نمایش فرم های متفاوت روی مرورگر برای دریافت ورودی " +"کاربر فراخوانی کنید. علاوه بر این، PyWebIO همچنین پشتیبانی برای رویداد های" +" کلیک، " +"چیدمان، و... را ارائه می دهد. PyWebIO هدف دارد تا به شما اجازه دهد که از" +" کمترین کد برای تعامل " +"با کاربر استفاده کنید و یک تجربه کاربری خوب را تا حد امکان ارائه دهید." + +#: ../../guide.rst:15 +msgid "" +"This user guide introduces you the most of the features of PyWebIO. There" +" is a demo link at the top right of the example codes in this document, " +"where you can run the example code online and see what happens." +msgstr "" +"این راهنمای کاربر بیشتر ویژگی های PyWebIO را به شما معرفی می کند. یک" +" لینک دمو در بالا سمت راست کد های مثال در این مستندات وجود دارد، " +"جایی که شما می توانید کد مثال را به طور آنلاین اجرا کنید و ببینید که چه" +" اتفاقی می افتد." + +#: ../../guide.rst:19 +msgid "Input" +msgstr "ورودی" + +#: ../../guide.rst:21 +msgid "" +"The input functions are defined in the :doc:`pywebio.input ` " +"module and can be imported using ``from pywebio.input import *``." +msgstr "" + +#: ../../guide.rst:23 +msgid "" +"When calling the input function, an input form will be popped up on the " +"browser. PyWebIO's input functions is blocking (same as Python's built-in" +" ``input()`` function) and will not return until the form is successfully" +" submitted." +msgstr "" + +#: ../../guide.rst:27 +msgid "Basic input" +msgstr "ورودی پایه" + +#: ../../guide.rst:29 +msgid "Here are some basic types of input." +msgstr "در اینجا برخی از انواع پایه ورودی آورده شده." + +#: ../../guide.rst:31 +msgid "Text input:" +msgstr "ورودی متن:" + +#: ../../guide.rst:33 +#, python-format +msgid "" +"age = input(\"How old are you?\", type=NUMBER)\n" +"put_text('age = %r' % age) # ..demo-only" +msgstr "" +"age = input(\"شما چند سال دارید؟\", type=NUMBER)\n" +"put_text('سن = %r' % age) # ..demo-only" + +#: ../../guide.rst:40 +msgid "" +"After running the above code, the browser will pop up a text input field " +"to get the input. After the user completes the input and submits the " +"form, the function returns the value entered by the user." +msgstr "" + +#: ../../guide.rst:43 +msgid "Here are some other types of input functions:" +msgstr "در اینجا برخی از انواع دیگر توابع ورودی آورده شده:" + +#: ../../guide.rst:45 +#, python-format +msgid "" +"# Password input\n" +"password = input(\"Input password\", type=PASSWORD)\n" +"put_text('password = %r' % password) # ..demo-only\n" +"## ----\n" +"\n" +"# Drop-down selection\n" +"gift = select('Which gift you want?', ['keyboard', 'ipad'])\n" +"put_text('gift = %r' % gift) # ..demo-only\n" +"## ----\n" +"\n" +"# Checkbox\n" +"agree = checkbox(\"User Term\", options=['I agree to terms and " +"conditions'])\n" +"put_text('agree = %r' % agree) # ..demo-only\n" +"## ----\n" +"\n" +"# Single choice\n" +"answer = radio(\"Choose one\", options=['A', 'B', 'C', 'D'])\n" +"put_text('answer = %r' % answer) # ..demo-only\n" +"## ----\n" +"\n" +"# Multi-line text input\n" +"text = textarea('Text Area', rows=3, placeholder='Some text')\n" +"put_text('text = %r' % text) # ..demo-only\n" +"## ----\n" +"\n" +"# File Upload\n" +"img = file_upload(\"Select a image:\", accept=\"image/*\")\n" +"if img: # ..demo-only\n" +" put_image(img['content'], title=img['filename']) # ..demo-only" +msgstr "" +"# Password input\n" +"password = input(\"پسورد را وارد کنید\", type=PASSWORD)\n" +"put_text('پسورد = %r' % password) # ..demo-only\n" +"## ----\n" +"\n" +"# Drop-down selection\n" +"gift = select('شما چه هدیه ای را می خواهید؟', ['keyboard', 'ipad'])\n" +"put_text('هدیه = %r' % gift) # ..demo-only\n" +"## ----\n" +"\n" +"# Checkbox\n" +"agree = checkbox(\"شرط کاربر\", options=['من با شرایط و ضوابط موافقت می" +" کنم'])\n" +"put_text('موافقت = %r' % agree) # ..demo-only\n" +"## ----\n" +"\n" +"# Single choice\n" +"answer = radio(\"یکی را انتخاب کنید\", options=['A', 'B', 'C', 'D'])\n" +"put_text('پاسخ = %r' % answer) # ..demo-only\n" +"## ----\n" +"\n" +"# Multi-line text input\n" +"text = textarea('ناحیه متن', rows=3, placeholder='قدری متن')\n" +"put_text('متن = %r' % text) # ..demo-only\n" +"## ----\n" +"\n" +"# File Upload\n" +"img = file_upload(\"یک تصویر انتخاب کنید:\", accept=\"image/*\")\n" +"if img: # ..demo-only\n" +" put_image(img['content'], title=img['filename']) # ..demo-only" + +#: ../../guide.rst:81 +msgid "Parameter of input functions" +msgstr "پارامتر توابع ورودی" + +#: ../../guide.rst:83 +msgid "" +"There are many parameters that can be passed to the input function(for " +"complete parameters, please refer to the :doc:`function document " +"`):" +msgstr "" + +#: ../../guide.rst:86 +msgid "" +"input('This is label', type=TEXT, placeholder='This is placeholder',\n" +" help_text='This is help text', required=True)" +msgstr "" +"input('این لیبل است', type=TEXT, placeholder='این placeholder است',\n" +" help_text='این help text است', required=True)" + +#: ../../guide.rst:93 ../../guide.rst:129 ../../guide.rst:279 +#: ../../guide.rst:586 +msgid "The results of the above example are as follows:" +msgstr "نتایج مثال بالا به شرح زیر است:" + +#: ../../guide.rst:97 +msgid "" +"You can specify a validation function for the input by using ``validate``" +" parameter. The validation function should return ``None`` when the check" +" passes, otherwise an error message will be returned:" +msgstr "" + +#: ../../guide.rst:100 +#, python-format +msgid "" +"def check_age(p): # return None when the check passes, otherwise return " +"the error message\n" +" if p < 10:\n" +" return 'Too young!!'\n" +" if p > 60:\n" +" return 'Too old!!'\n" +"\n" +"age = input(\"How old are you?\", type=NUMBER, validate=check_age)\n" +"put_text('age = %r' % age) # ..demo-only" +msgstr "" +"def check_age(p): # return None when the check passes, otherwise return " +"the error message\n" +" if p < 10:\n" +" return 'خیلی جوان!!'\n" +" if p > 60:\n" +" return 'خیلی پیر!!'\n" +"\n" +"age = input(\"شما چند سال دارید؟\", type=NUMBER, validate=check_age)\n" +"put_text('age = %r' % age) # ..demo-only" + +#: ../../guide.rst:113 +msgid "" +"When the user input an illegal value, the input field is displayed as " +"follows:" +msgstr "" + +#: ../../guide.rst:117 +msgid "" +"You can use ``code`` parameter in :func:`pywebio.input.textarea()` to " +"make a code editing textarea." +msgstr "" + +#: ../../guide.rst:119 +msgid "" +"code = textarea('Code Edit', code={\n" +" 'mode': \"python\",\n" +" 'theme': 'darcula',\n" +"}, value='import something\\n# Write your python code')\n" +"put_code(code, language='python') # ..demo-only" +msgstr "" +"code = textarea('ویرایش کد', code={\n" +" 'mode': \"python\",\n" +" 'theme': 'darcula',\n" +"}, value='import something\\n# کد پایتون خودتان را بنویسید')\n" +"put_code(code, language='python') # ..demo-only" + +#: ../../guide.rst:135 +msgid "Input Group" +msgstr "گروه ورودی" + +#: ../../guide.rst:137 +msgid "" +"PyWebIO uses input group to get multiple inputs in a single form. " +"`pywebio.input.input_group()` accepts a list of single input function " +"call as parameter, and returns a dictionary with the ``name`` of the " +"single input as its key and the input data as its value:" +msgstr "" + +#: ../../guide.rst:142 +msgid "" +"def check_age(p): # ..demo-only\n" +" if p < 10: # ..demo-only\n" +" return 'Too young!!' # ..demo-only\n" +" if p > 60: # ..demo-only\n" +" return 'Too old!!' # ..demo-only\n" +" # ..demo-only\n" +"data = input_group(\"Basic info\",[\n" +" input('Input your name', name='name'),\n" +" input('Input your age', name='age', type=NUMBER, validate=check_age)\n" +"])\n" +"put_text(data['name'], data['age'])" +msgstr "" +"def check_age(p): # ..demo-only\n" +" if p < 10: # ..demo-only\n" +" return 'خیلی جوان!!' # ..demo-only\n" +" if p > 60: # ..demo-only\n" +" return 'خیلی پیر!!' # ..demo-only\n" +" # ..demo-only\n" +"data = input_group(\"اطلاعات پایه\",[\n" +" input('نام خود را وارد کنید', name='name'),\n" +" input('سن خود را وارد کنید', name='age', type=NUMBER, validate=check_age)\n" +"])\n" +"put_text(data['name'], data['age'])" + +#: ../../guide.rst:158 +msgid "" +"The input group also supports using ``validate`` parameter to set the " +"validation function, which accepts the entire form data as parameter:" +msgstr "" + +#: ../../guide.rst:160 +msgid "" +"def check_age(p): # single input item validation # ..demo-only\n" +" if p < 10: # ..demo-only\n" +" return 'Too young!!' # ..demo-only\n" +" if p > 60: # ..demo-only\n" +" return 'Too old!!' # ..demo-only\n" +" # ..demo-only\n" +"def check_form(data): # return (input name, error msg) when validation " +"fail\n" +" if len(data['name']) > 6:\n" +" return ('name', 'Name too long!')\n" +" if data['age'] <= 0:\n" +" return ('age', 'Age can not be negative!')\n" +"\n" +"data = input_group(\"Basic info\",[ # ..demo-only\n" +" input('Input your name', name='name'), # ..demo-only\n" +" input('Input your age', name='age', type=NUMBER, validate=check_age) " +"# ..demo-only\n" +"], validate=check_form) # ..demo-only\n" +"put_text(data['name'], data['age']) # ..demo-only" +msgstr "" +"def check_age(p): # single input item validation # ..demo-only\n" +" if p < 10: # ..demo-only\n" +" return 'خیلی جوان!!' # ..demo-only\n" +" if p > 60: # ..demo-only\n" +" return 'خیلی پیر!!' # ..demo-only\n" +" # ..demo-only\n" +"def check_form(data): # return (input name, error msg) when validation " +"fail\n" +" if len(data['name']) > 6:\n" +" return ('name', 'نام خیلی طولانی است!')\n" +" if data['age'] <= 0:\n" +" return ('age', 'سن نمی تواند منفی باشد!')\n" +"\n" +"data = input_group(\"اطلاعات پایه\",[ # ..demo-only\n" +" input('نام خود را وارد کنید', name='name'), # ..demo-only\n" +" input('سن خود را وارد کنید', name='age', type=NUMBER, validate=check_age) " +"# ..demo-only\n" +"], validate=check_form) # ..demo-only\n" +"put_text(data['name'], data['age']) # ..demo-only" + +#: ../../guide.rst:183 +msgid "" +"PyWebIO determines whether the input function is in `input_group()` or is" +" called alone according to whether the ``name`` parameter is passed. So " +"when calling an input function alone, **do not** set the ``name`` " +"parameter; when calling the input function in `input_group()`, you " +"**must** provide the ``name`` parameter." +msgstr "" + +#: ../../guide.rst:188 +msgid "Output" +msgstr "خروجی" + +#: ../../guide.rst:190 +msgid "" +"The output functions are all defined in the :doc:`pywebio.output " +"` module and can be imported using ``from pywebio.output import " +"*``." +msgstr "" + +#: ../../guide.rst:193 +msgid "" +"When output functions is called, the content will be output to the " +"browser in real time. The output functions can be called at any time " +"during the application lifetime." +msgstr "" + +#: ../../guide.rst:197 +msgid "Basic Output" +msgstr "خروجی پایه" + +#: ../../guide.rst:199 +msgid "" +"Using output functions, you can output a variety of content, such as " +"text, tables, images and so on:" +msgstr "" + +#: ../../guide.rst:201 +msgid "" +"# Text Output\n" +"put_text(\"Hello world!\")\n" +"## ----\n" +"\n" +"# Table Output\n" +"put_table([\n" +" ['Commodity', 'Price'],\n" +" ['Apple', '5.5'],\n" +" ['Banana', '7'],\n" +"])\n" +"## ----\n" +"\n" +"# Image Output\n" +"put_image(open('/path/to/some/image.png', 'rb').read()) # local image # " +"..doc-only\n" +"put_image('http://example.com/some-image.png') # internet image # ..doc-" +"only\n" +"put_image('https://www.python.org/static/img/python-logo.png') # ..demo-" +"only\n" +"## ----\n" +"\n" +"# Markdown Output\n" +"put_markdown('~~Strikethrough~~')\n" +"## ----\n" +"\n" +"# File Output\n" +"put_file('hello_word.txt', b'hello word!')\n" +"## ----\n" +"\n" +"# Show a PopUp\n" +"popup('popup title', 'popup text content')\n" +"\n" +"# Show a notification message\n" +"toast('New message 🔔')" +msgstr "" +"# Text Output\n" +"put_text(\"سلام دنیا!\")\n" +"## ----\n" +"\n" +"# Table Output\n" +"put_table([\n" +" ['کالا', 'قیمت'],\n" +" ['سیب', '5.5'],\n" +" ['موز', '7'],\n" +"])\n" +"## ----\n" +"\n" +"# Image Output\n" +"put_image(open('/path/to/some/image.png', 'rb').read()) # local image # " +"..doc-only\n" +"put_image('http://example.com/some-image.png') # internet image # ..doc-" +"only\n" +"put_image('https://www.python.org/static/img/python-logo.png') # ..demo-" +"only\n" +"## ----\n" +"\n" +"# Markdown Output\n" +"put_markdown('~~Strikethrough~~')\n" +"## ----\n" +"\n" +"# File Output\n" +"put_file('hello_word.txt', b'hello word!')\n" +"## ----\n" +"\n" +"# Show a PopUp\n" +"popup('popup title', 'popup text content')\n" +"\n" +"# Show a notification message\n" +"toast('New message 🔔')" + +#: ../../guide.rst:238 +msgid "" +"For all output functions provided by PyWebIO, please refer to the " +":doc:`pywebio.output ` module. In addition, PyWebIO also " +"supports data visualization with some third-party libraries, see :doc" +":`Third-party library ecology `." +msgstr "" + +#: ../../guide.rst:245 +msgid "" +"If you use PyWebIO in interactive execution environment of Python shell, " +"IPython or jupyter notebook, you need call `show()` method explicitly to " +"show output::" +msgstr "" + +#: ../../guide.rst:248 +msgid "" +">>> put_text(\"Hello world!\").show()\n" +">>> put_table([\n" +"... ['A', 'B'],\n" +"... [put_markdown(...), put_text('C')]\n" +"... ]).show()" +msgstr "" + +#: ../../guide.rst:258 +msgid "Combined Output" +msgstr "خروجی ترکیبی" + +#: ../../guide.rst:260 +msgid "" +"The output functions whose name starts with ``put_`` can be combined with" +" some output functions as part of the final output:" +msgstr "" + +#: ../../guide.rst:262 +msgid "" +"You can pass ``put_xxx()`` calls to `put_table() " +"` as cell content:" +msgstr "" + +#: ../../guide.rst:264 +msgid "" +"put_table([\n" +" ['Type', 'Content'],\n" +" ['html', put_html('X2')],\n" +" ['text', '
'], # equal to ['text', put_text('
')]\n" +" ['buttons', put_buttons(['A', 'B'], onclick=...)], # ..doc-only\n" +" ['buttons', put_buttons(['A', 'B'], onclick=put_text)], # ..demo-" +"only\n" +" ['markdown', put_markdown('`Awesome PyWebIO!`')],\n" +" ['file', put_file('hello.text', b'hello world')],\n" +" ['table', put_table([['A', 'B'], ['C', 'D']])]\n" +"])" +msgstr "" + +#: ../../guide.rst:283 +msgid "" +"Similarly, you can pass ``put_xxx()`` calls to `popup() " +"` as the popup content:" +msgstr "" + +#: ../../guide.rst:285 +msgid "" +"popup('Popup title', [\n" +" put_html('

Popup Content

'),\n" +" 'plain html:
', # Equivalent to: put_text('plain html:
')\n" +" put_table([['A', 'B'], ['C', 'D']]),\n" +" put_button('close_popup()', onclick=close_popup)\n" +"])" +msgstr "" + +#: ../../guide.rst:296 +msgid "" +"In addition, you can use `put_widget() ` to " +"make your own output widgets that can accept ``put_xxx()`` calls." +msgstr "" + +#: ../../guide.rst:298 +msgid "" +"For a full list of functions that accept ``put_xxx()`` calls as content, " +"see :ref:`Output functions list `" +msgstr "" + +#: ../../guide.rst:300 +msgid "**Context Manager**" +msgstr "" + +#: ../../guide.rst:302 +msgid "" +"Some output functions that accept ``put_xxx()`` calls as content can be " +"used as context manager:" +msgstr "" + +#: ../../guide.rst:304 +msgid "" +"with put_collapse('This is title'):\n" +" for i in range(4):\n" +" put_text(i)\n" +"\n" +" put_table([\n" +" ['Commodity', 'Price'],\n" +" ['Apple', '5.5'],\n" +" ['Banana', '7'],\n" +" ])" +msgstr "" + +#: ../../guide.rst:318 +msgid "" +"For a full list of functions that support context manager, see " +":ref:`Output functions list `" +msgstr "" + +#: ../../guide.rst:324 +msgid "Click Callback" +msgstr "فراخوانی کلیک" + +#: ../../guide.rst:326 +msgid "" +"As we can see from the above, the interaction of PyWebIO has two parts: " +"input and output. The input function of PyWebIO is blocking, a form will " +"be displayed on the user's web browser when calling input function, the " +"input function will not return until the user submits the form. The " +"output function is used to output content to the browser in real time. " +"The input and output behavior of PyWebIO is consistent with the console " +"program. That's why we say PyWebIO turning the browser into a \"rich text" +" terminal\". So you can write PyWebIO applications in script programming " +"way." +msgstr "" + +#: ../../guide.rst:332 +msgid "" +"In addition, PyWebIO also supports event callbacks: PyWebIO allows you to" +" output some buttons and bind callbacks to them. The provided callback " +"function will be executed when the button is clicked." +msgstr "" + +#: ../../guide.rst:335 +msgid "This is an example:" +msgstr "این یک مثال است:" + +#: ../../guide.rst:337 +#, python-format +msgid "" +"from functools import partial\n" +"\n" +"def edit_row(choice, row):\n" +" put_text(\"You click %s button ar row %s\" % (choice, row))\n" +"\n" +"put_table([\n" +" ['Idx', 'Actions'],\n" +" [1, put_buttons(['edit', 'delete'], onclick=partial(edit_row, " +"row=1))],\n" +" [2, put_buttons(['edit', 'delete'], onclick=partial(edit_row, " +"row=2))],\n" +" [3, put_buttons(['edit', 'delete'], onclick=partial(edit_row, " +"row=3))],\n" +"])" +msgstr "" + +#: ../../guide.rst:353 +msgid "" +"The call to `put_table() ` will not block. When" +" user clicks a button, the corresponding callback function will be " +"invoked:" +msgstr "" + +#: ../../guide.rst:358 +msgid "Of course, PyWebIO also supports outputting individual button:" +msgstr "" + +#: ../../guide.rst:360 +#, python-format +msgid "" +"def btn_click(btn_val):\n" +" put_text(\"You click %s button\" % btn_val)\n" +"\n" +"put_buttons(['A', 'B', 'C'], onclick=btn_click) # a group of buttons\n" +"\n" +"put_button(\"Click me\", onclick=lambda: toast(\"Clicked\")) # single " +"button" +msgstr "" + +#: ../../guide.rst:371 +msgid "" +"In fact, all output can be bound to click events, not just buttons. You " +"can call ``onclick()`` method after the output function (function name " +"like ``put_xxx()``) call:" +msgstr "" + +#: ../../guide.rst:374 +msgid "" +"put_image('some-image.png').onclick(lambda: toast('You click an image'))" +" # ..doc-only\n" +"put_image('https://www.python.org/static/img/python-" +"logo.png').onclick(lambda: toast('You click an image')) # ..demo-only\n" +"\n" +"# set onclick in combined output\n" +"put_table([\n" +" ['Commodity', 'Price'],\n" +" ['Apple', put_text('5.5').onclick(lambda: toast('You click the " +"text'))],\n" +"])" +msgstr "" + +#: ../../guide.rst:387 +msgid "" +"The return value of ``onclick()`` method is the object itself so it can " +"be used in combined output." +msgstr "" + +#: ../../guide.rst:392 +msgid "Output Scope" +msgstr "محدوده خروجی" + +#: ../../guide.rst:394 +msgid "" +"PyWebIO uses the scope model to give more control to the location of " +"content output. The output scope is a container of output content. You " +"can create a scope in somewhere and append content to it." +msgstr "" + +#: ../../guide.rst:397 +msgid "" +"Each output function (function name like ``put_xxx()``) will output its " +"content to a scope, the default is \"current scope\". The \"current " +"scope\" is set by `use_scope() `." +msgstr "" + +#: ../../guide.rst:402 +msgid "**use_scope()**" +msgstr "" + +#: ../../guide.rst:404 +msgid "" +"You can use `use_scope() ` to open and enter a " +"new output scope, or enter an existing output scope:" +msgstr "" + +#: ../../guide.rst:406 +msgid "" +"with use_scope('scope1'): # open and enter a new output: 'scope1'\n" +" put_text('text1 in scope1') # output text to scope1\n" +"\n" +"put_text('text in parent scope of scope1') # output text to ROOT scope\n" +"\n" +"with use_scope('scope1'): # enter an existing scope: 'scope1'\n" +" put_text('text2 in scope1') # output text to scope1" +msgstr "" + +#: ../../guide.rst:418 ../../guide.rst:439 +msgid "The results of the above code are as follows::" +msgstr "نتایج کد بالا به شرح زیر است::" + +#: ../../guide.rst:420 +msgid "" +"text1 in scope1\n" +"text2 in scope1\n" +"text in parent scope of scope1" +msgstr "" + +#: ../../guide.rst:424 +msgid "" +"You can use ``clear`` parameter in `use_scope() " +"` to clear the existing content before entering" +" the scope:" +msgstr "" + +#: ../../guide.rst:426 +msgid "" +"with use_scope('scope2'):\n" +" put_text('create scope2')\n" +"\n" +"put_text('text in parent scope of scope2')\n" +"## ----\n" +"\n" +"with use_scope('scope2', clear=True): # enter the existing scope and " +"clear the previous content\n" +" put_text('text in scope2')" +msgstr "" + +#: ../../guide.rst:441 +msgid "" +"text in scope2\n" +"text in parent scope of scope2" +msgstr "" + +#: ../../guide.rst:444 +msgid "`use_scope() ` can also be used as decorator:" +msgstr "" + +#: ../../guide.rst:446 +msgid "" +"import time # ..demo-only\n" +"from datetime import datetime\n" +"\n" +"@use_scope('time', clear=True)\n" +"def show_time():\n" +" put_text(datetime.now())\n" +"\n" +"while 1: # ..demo-only\n" +" show_time() # ..demo-only\n" +" time.sleep(1) # ..demo-only" +msgstr "" + +#: ../../guide.rst:461 +msgid "" +"When calling ``show_time()`` for the first time, a ``time`` scope will be" +" created, and the current time will be output to it. And then every time " +"the ``show_time()`` is called, the new content will replace the previous " +"content." +msgstr "" + +#: ../../guide.rst:464 +msgid "" +"Scopes can be nested. At the beginning, PyWebIO applications have only " +"one ``ROOT`` scope. You can create new scope in a scope. For example, the" +" following code will create 3 scopes:" +msgstr "" + +#: ../../guide.rst:467 +#, python-format +msgid "" +"with use_scope('A'):\n" +" put_text('Text in scope A')\n" +"\n" +" with use_scope('B'):\n" +" put_text('Text in scope B')\n" +"\n" +"with use_scope('C'):\n" +" put_text('Text in scope C')\n" +"\n" +"put_html(\"\"\"\"\"\") # ..demo-" +"only\n" +"put_text() # ..demo-" +"only\n" +"put_buttons([('Put text to %s' % i, i) for i in ('A', 'B', 'C')], lambda " +"s: put_text(s, scope=s)) # ..demo-only" +msgstr "" + +#: ../../guide.rst:489 +msgid "The above code will generate the following scope layout::" +msgstr "" + +#: ../../guide.rst:491 +msgid "" +"┌─ROOT────────────────────┐\n" +"│ │\n" +"│ ┌─A───────────────────┐ │\n" +"│ │ Text in scope A │ │\n" +"│ │ ┌─B───────────────┐ │ │\n" +"│ │ │ Text in scope B │ │ │\n" +"│ │ └─────────────────┘ │ │\n" +"│ └─────────────────────┘ │\n" +"│ │\n" +"│ ┌─C───────────────────┐ │\n" +"│ │ Text in scope C │ │\n" +"│ └─────────────────────┘ │\n" +"└─────────────────────────┘" +msgstr "" + +#: ../../guide.rst:507 +msgid "**put_scope()**" +msgstr "" + +#: ../../guide.rst:509 +msgid "" +"We already know that the scope is a container of output content. So can " +"we use this container as a sub-item of a output (like, set a cell in " +"table as a container)? Yes, you can use `put_scope() " +"` to create a scope explicitly. The function " +"name starts with ``put_``, which means it can be pass to the functions " +"that accept ``put_xxx()`` calls." +msgstr "" + +#: ../../guide.rst:514 +msgid "" +"put_table([\n" +" ['Name', 'Hobbies'],\n" +" ['Tom', put_scope('hobby', content=put_text('Coding'))] # hobby is " +"initialized to coding\n" +"])\n" +"\n" +"## ----\n" +"with use_scope('hobby', clear=True):\n" +" put_text('Movie') # hobby is reset to Movie\n" +"\n" +"## ----\n" +"# append Music, Drama to hobby\n" +"with use_scope('hobby'):\n" +" put_text('Music')\n" +" put_text('Drama')\n" +"\n" +"## ----\n" +"# insert the Coding into the top of the hobby\n" +"put_markdown('**Coding**', scope='hobby', position=0)" +msgstr "" + +#: ../../guide.rst:538 +msgid "" +"It is not allowed to have two scopes with the same name in the " +"application." +msgstr "" + +#: ../../guide.rst:540 +msgid "**Scope control**" +msgstr "" + +#: ../../guide.rst:542 +msgid "" +"In addition to `use_scope() ` and `put_scope() " +"`, PyWebIO also provides the following scope " +"control functions:" +msgstr "" + +#: ../../guide.rst:545 +msgid "`clear(scope) ` : Clear the contents of the scope" +msgstr "" + +#: ../../guide.rst:546 +msgid "`remove(scope) ` : Remove scope" +msgstr "" + +#: ../../guide.rst:547 +msgid "" +"`scroll_to(scope) ` : Scroll the page to the " +"scope" +msgstr "" + +#: ../../guide.rst:549 +msgid "" +"Also, all output functions (function name like ``put_xxx()``) support a " +"``scope`` parameter to specify the destination scope to output, and " +"support a ``position`` parameter to specify the insert position in target" +" scope. Refer :ref:`output module ` for more information." +msgstr "" + +#: ../../guide.rst:554 +msgid "Layout" +msgstr "چیدمان" + +#: ../../guide.rst:556 +msgid "" +"In general, using the output functions introduced above is enough to " +"output what you want, but these outputs are arranged vertically. If you " +"want to create a more complex layout (such as displaying a code block on " +"the left side of the page and an image on the right), you need to use " +"layout functions." +msgstr "" + +#: ../../guide.rst:560 +msgid "" +"The ``pywebio.output`` module provides 3 layout functions, and you can " +"create complex layouts by combining them:" +msgstr "" + +#: ../../guide.rst:562 +msgid "" +"`put_row() ` : Use row layout to output content. " +"The content is arranged horizontally" +msgstr "" + +#: ../../guide.rst:563 +msgid "" +"`put_column() ` : Use column layout to output " +"content. The content is arranged vertically" +msgstr "" + +#: ../../guide.rst:564 +msgid "" +"`put_grid() ` : Output content using grid layout" +msgstr "" + +#: ../../guide.rst:566 +msgid "Here is an example by combining ``put_row()`` and ``put_column()``:" +msgstr "" + +#: ../../guide.rst:568 +msgid "" +"put_row([\n" +" put_column([\n" +" put_code('A'),\n" +" put_row([\n" +" put_code('B1'), None, # None represents the space between " +"the output\n" +" put_code('B2'), None,\n" +" put_code('B3'),\n" +" ]),\n" +" put_code('C'),\n" +" ]), None,\n" +" put_code('D'), None,\n" +" put_code('E')\n" +"])" +msgstr "" + +#: ../../guide.rst:591 +msgid "The layout function also supports customizing the size of each part::" +msgstr "" + +#: ../../guide.rst:593 +#, python-format +msgid "" +"put_row([put_image(...), put_image(...)], size='40% 60%') # The ratio of" +" the width of two images is 2:3" +msgstr "" + +#: ../../guide.rst:595 +msgid "" +"For more information, please refer to the :ref:`layout functions " +"documentation `." +msgstr "" + +#: ../../guide.rst:600 +msgid "Style" +msgstr "استایل" + +#: ../../guide.rst:602 +msgid "" +"If you are familiar with `CSS `_ " +"styles, you can use the ``style()`` method of output return to set a " +"custom style for the output." +msgstr "" + +#: ../../guide.rst:605 +msgid "You can set the CSS style for a single ``put_xxx()`` output:" +msgstr "" + +#: ../../guide.rst:607 +msgid "" +"put_text('hello').style('color: red; font-size: 20px')\n" +"\n" +"## ----\n" +"# in combined output\n" +"put_row([\n" +" put_text('hello').style('color: red'),\n" +" put_markdown('markdown')\n" +"]).style('margin-top: 20px')" +msgstr "" + +#: ../../guide.rst:620 +msgid "" +"The return value of ``style()`` method is the object itself so it can be " +"used in combined output." +msgstr "" + +#: ../../guide.rst:625 +msgid "Run application" +msgstr "اجرای اپلیکیشن" + +#: ../../guide.rst:627 +msgid "" +"In PyWebIO, there are two modes to run PyWebIO applications: running as a" +" script and using `pywebio.start_server() " +"` or " +"`pywebio.platform.path_deploy() ` to run as" +" a web service." +msgstr "" + +#: ../../guide.rst:632 +msgid "Overview" +msgstr "بررسی اجمالی" + +#: ../../guide.rst:636 ../../guide.rst:701 +msgid "**Server mode**" +msgstr "**حالت سرور**" + +#: ../../guide.rst:638 +msgid "" +"In server mode, PyWebIO will start a web server to continuously provide " +"services. When the user accesses the service address, PyWebIO will open a" +" new session and run PyWebIO application in it." +msgstr "" + +#: ../../guide.rst:641 +msgid "" +"`start_server() ` is the most " +"common way to start a web server to serve given PyWebIO applications::" +msgstr "" + +#: ../../guide.rst:644 +msgid "" +"from pywebio import *\n" +"\n" +"def main(): # PyWebIO application function\n" +" name = input.input(\"what's your name\")\n" +" output.put_text(\"hello\", name)\n" +"\n" +"start_server(main, port=8080, debug=True)" +msgstr "" + +#: ../../guide.rst:652 +msgid "" +"Now head over to http://127.0.0.1:8080/, and you should see your hello " +"greeting." +msgstr "" + +#: ../../guide.rst:654 +msgid "" +"By using ``debug=True`` to enable debug mode, the server will " +"automatically reload if code changes." +msgstr "" + +#: ../../guide.rst:656 +msgid "" +"The `start_server() ` provide a " +"remote access support, when enabled (by passing `remote_access=True` to " +"`start_server()`), you will get a public, shareable address for the " +"current application, others can access your application in their browser " +"via this address. Because the processing happens on your device (as long " +"as your device stays on!), you don't have to worry about any " +"dependencies. Using remote access makes it easy to temporarily share the " +"application with others." +msgstr "" + +#: ../../guide.rst:662 +msgid "" +"Another way to deploy PyWebIO application as web service is using " +"`path_deploy() `. `path_deploy() " +"` is used to deploy the PyWebIO " +"applications from a directory. Just define PyWebIO applications in python" +" files under this directory, and you can access them via the path in the " +"URL. Refer to :ref:`platform module ` for more information." +msgstr "" + +#: ../../guide.rst:669 +msgid "" +"Note that in Server mode, all functions from ``pywebio.input``, " +"``pywebio.output`` and ``pywebio.session`` modules can only be called in " +"the context of PyWebIO application functions. For example, the following " +"code is **not allowed**::" +msgstr "" + +#: ../../guide.rst:672 +msgid "" +"import pywebio\n" +"from pywebio.input import input\n" +"\n" +"port = input('Input port number:') # ❌ error\n" +"pywebio.start_server(my_task_func, port=int(port))" +msgstr "" + +#: ../../guide.rst:679 ../../guide.rst:696 +msgid "**Script mode**" +msgstr "**حالت اسکریپت**" + +#: ../../guide.rst:681 +msgid "" +"If you never call ``start_server()`` or ``path_deploy()`` in your code, " +"then you are running PyWebIO application as script mode." +msgstr "" + +#: ../../guide.rst:683 +msgid "" +"In script mode, a web browser page will be open automatically when " +"running to the first call to PyWebIO interactive functions, and all " +"subsequent PyWebIO interactions will take place on this page. When the " +"script exit, the page will be inactive." +msgstr "" + +#: ../../guide.rst:686 +msgid "" +"If the user closes the browser before the script exiting, then subsequent" +" calls to PyWebIO's interactive functions will cause a `SessionException " +"` exception." +msgstr "" + +#: ../../guide.rst:692 +msgid "Concurrent" +msgstr "هم زمان" + +#: ../../guide.rst:694 +msgid "PyWebIO can be used in a multi-threading environment." +msgstr "" + +#: ../../guide.rst:698 +msgid "" +"In script mode, you can freely start new thread and call PyWebIO " +"interactive functions in it. When all `non-daemonic " +"`_ " +"threads finish running, the script exits." +msgstr "" + +#: ../../guide.rst:703 +msgid "" +"In server mode, if you need to use PyWebIO interactive functions in new " +"thread, you need to use `pywebio.session.register_thread(thread) " +"` to register the new thread (so that " +"PyWebIO can know which session the thread belongs to). If the PyWebIO " +"interactive function is not used in the new thread, no registration is " +"required. Threads that are not registered with `register_thread(thread) " +"` calling PyWebIO's interactive " +"functions will cause `SessionNotFoundException " +"`." +msgstr "" + +#: ../../guide.rst:710 +msgid "Example of using multi-threading in Server mode::" +msgstr "" + +#: ../../guide.rst:712 +msgid "" +"def show_time():\n" +" while True:\n" +" with use_scope(name='time', clear=True):\n" +" put_text(datetime.datetime.now())\n" +" time.sleep(1)\n" +"\n" +"def app():\n" +" t = threading.Thread(target=show_time)\n" +" register_thread(t)\n" +" put_markdown('## Clock')\n" +" t.start() # run `show_time()` in background\n" +"\n" +" # ❌ this thread will cause `SessionNotFoundException`\n" +" threading.Thread(target=show_time).start()\n" +"\n" +" put_text('Background task started.')\n" +"\n" +"\n" +"start_server(app, port=8080, debug=True)" +msgstr "" + +#: ../../guide.rst:736 +msgid "Close of session" +msgstr "" + +#: ../../guide.rst:738 +msgid "" +"When user close the browser page, the session will be closed. After the " +"browser page is closed, PyWebIO input function calls that have not yet " +"returned in the current session will cause `SessionClosedException " +"`, and subsequent calls to " +"PyWebIO interactive functions will cause `SessionNotFoundException " +"` or `SessionClosedException" +" `." +msgstr "" + +#: ../../guide.rst:743 +msgid "" +"In most cases, you don't need to catch those exceptions, because let " +"those exceptions to abort the running is the right way to exit." +msgstr "" + +#: ../../guide.rst:745 +msgid "" +"You can use `pywebio.session.defer_call(func) " +"` to set the function to be called when the " +"session closes. `defer_call(func) ` can be " +"used for resource cleaning. You can call `defer_call(func) " +"` multiple times in the session, and the set " +"functions will be executed sequentially after the session closes." +msgstr "" + +#: ../../guide.rst:751 +msgid "More about PyWebIO" +msgstr "اطلاعات بیشتر درباره PyWebIO" + +#: ../../guide.rst:752 +msgid "" +"By now, you already get the most important features of PyWebIO and can " +"start to write awesome PyWebIO applications. However, there are some " +"other useful features we don't cover in the above. Here we just make a " +"briefly explain about them. When you need them in your application, you " +"can refer to their document." +msgstr "" + +#: ../../guide.rst:756 +msgid "" +"Also, :doc:`here ` is a cookbook where you can find some " +"useful code snippets for your PyWebIO application." +msgstr "" + +#: ../../guide.rst:759 +msgid "``session`` module" +msgstr "ماژول ``session``" + +#: ../../guide.rst:760 +msgid "" +"The :doc:`pywebio.session ` module give you more control to " +"session." +msgstr "" + +#: ../../guide.rst:762 +msgid "" +"Use `set_env() ` to configure the title, page " +"appearance, input panel and so on for current session." +msgstr "" + +#: ../../guide.rst:764 +msgid "" +"The `info ` object provides a lot information about" +" the current session, such as the user IP address, user language and user" +" browser information." +msgstr "" + +#: ../../guide.rst:767 +msgid "" +"`local ` is a session-local storage, it used to " +"save data whose values are session specific." +msgstr "" + +#: ../../guide.rst:769 +msgid "" +"`run_js() ` let you execute JavaScript code in " +"user's browser, and `eval_js() ` let you execute" +" JavaScript expression and get the value of it." +msgstr "" + +#: ../../guide.rst:773 +msgid "``pin`` module" +msgstr "ماژول ``pin``" + +#: ../../guide.rst:774 +msgid "" +"As you already know, the input function of PyWebIO is blocking and the " +"input form will be destroyed after successful submission. In some cases, " +"you may want to make the input form not disappear after submission, and " +"can continue to receive input. So PyWebIO provides the :doc:`pywebio.pin " +"` module to achieve persistent input by pinning input widgets to " +"the page." +msgstr "" + +#: ../../guide.rst:779 +msgid "``platform`` module" +msgstr "ماژول ``platform``" + +#: ../../guide.rst:781 +msgid "" +"The :doc:`pywebio.platform ` module provides support for " +"deploying PyWebIO applications in different ways." +msgstr "" + +#: ../../guide.rst:783 +msgid "" +"There are two protocols (WebSocket and HTTP) can be used in server to " +"communicates with the browser. The WebSocket is used by default. If you " +"want to use HTTP protocol, you can choose other ``start_server()`` " +"functions in this module." +msgstr "" + +#: ../../guide.rst:786 +msgid "" +"You might want to set some web page related configuration (such as SEO " +"information, js and css injection) for your PyWebIO application, " +"`pywebio.config() ` can be helpful." +msgstr "" + +#: ../../guide.rst:790 +msgid "Advanced features" +msgstr "ویژگی های پیشرفته" + +#: ../../guide.rst:792 +msgid "" +"The PyWebIO application can be integrated into an existing Python web " +"project, the PyWebIO application and the web project share a web " +"framework. Refer to :ref:`Advanced Topic: Integration with Web Framework " +"` for more information." +msgstr "" + +#: ../../guide.rst:796 +msgid "" +"PyWebIO also provides support for coroutine-based sessions. Refer to " +":ref:`Advanced Topic: Coroutine-based session ` " +"for more information." +msgstr "" + +#: ../../guide.rst:799 +msgid "" +"If you try to bundles your PyWebIO application into a stand-alone " +"executable file, to make users can run the application without installing" +" a Python interpreter or any modules, you might want to refer to " +":ref:`Libraries support: Build stand-alone App `" +msgstr "" + +#: ../../guide.rst:802 +msgid "" +"If you want to make some data visualization in your PyWebIO application, " +"you can't miss :ref:`Libraries support: Data visualization " +"`" +msgstr "" + +#: ../../guide.rst:805 +msgid "Last but not least" +msgstr "آخرین اما نه کم اهمیت ترین" + +#: ../../guide.rst:807 +msgid "" +"This is basically all features of PyWebIO, you can continue to read the " +"rest of the documents, or start writing your PyWebIO applications now." +msgstr "" + +#: ../../guide.rst:809 +msgid "" +"Finally, please allow me to provide one more suggestion. When you " +"encounter a design problem when using PyWebIO, you can ask yourself a " +"question: What would I do if it is in a terminal program? If you already " +"have the answer, it can be done in the same way with PyWebIO. If the " +"problem persists or the solution is not good enough, you can consider the" +" :ref:`callback mechanism ` or :doc:`pin <./pin>` module." +msgstr "" + +#: ../../guide.rst:814 +msgid "OK, Have fun with PyWebIO!" +msgstr "خیلی خب، با PyWebIO اوقات خوشی را داشته باشید!" + diff --git a/docs/locales/fa/LC_MESSAGES/index.po b/docs/locales/fa/LC_MESSAGES/index.po new file mode 100644 index 00000000..e49998e1 --- /dev/null +++ b/docs/locales/fa/LC_MESSAGES/index.po @@ -0,0 +1,261 @@ +# Copyright (C) Weimin Wang +# This file is distributed under the same license as the PyWebIO package. +# +# FIRST AUTHOR , 2022. +# Pikhosh , 2022. +msgid "" +msgstr "" +"Project-Id-Version: PyWebIO 1.5.2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-02-26 16:35+0330\n" +"PO-Revision-Date: 2022-03-14 01:55+0330\n" +"Last-Translator: Pikhosh \n" +"Language-Team: English <>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" +"Language: en_US\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Lokalize 21.12.2\n" + +#: ../../index.rst:80 +msgid "Manual" +msgstr "کتابچه راهنما" + +#: ../../index.rst:99 +msgid "Implement Doc" +msgstr "مستندات پیاده سازی" + +#: ../../index.rst:2 +msgid "PyWebIO" +msgstr "" + +#: ../../index.rst:4 +msgid "" +"PyWebIO provides a diverse set of imperative functions to obtain user " +"input and output content on the browser, turning the browser into a " +"\"rich text terminal\", and can be used to build simple web applications " +"or browser-based GUI applications. Using PyWebIO, developers can write " +"applications just like writing terminal scripts (interaction based on " +"input and print function), without the need to have knowledge of HTML and" +" JS. PyWebIO is ideal for quickly building interactive applications that " +"don't require a complicated user interface." +msgstr "" +"PyWebIO مجموعه ای از توابع دستوری را برای به دست آوردن ورودی " +"کاربر و خروجی دادن محتوا روی مرورگر را ارائه می کند، تبدیل مرورگر به یک " +"«ترمینال متن غنی»، و می تواند برای ساخت وب اپلیکیشن های ساده " +"یا اپلیکیشن های دارای رابط کاربری گرافیکی مبتنی بر مرورگر استفاده شود. با" +" استفاده از PyWebIO، توسعه دهندگان می توانند اپلیکیشن ها را درست " +"مثل نوشتن اسکریپت های ترمینال (تعامل بر پایه " +"تابع Input و Print) بنویسند، بدون نیاز به داشتن دانش HTML و" +" جاوا اسکریپت. PyWebIO برای ساخت سریع اپلیکیشن های تعاملی که " +"یک رابط کاربری پیچیده نیاز ندارند ایده آل است." + +#: ../../index.rst:11 +msgid "Features" +msgstr "ویژگی ها" + +#: ../../index.rst:13 +msgid "Use synchronization instead of callback-based method to get input" +msgstr "استفاده از همگام سازی بجای متد مبتنی بر فراخوانی برای دریافت ورودی" + +#: ../../index.rst:14 +msgid "Non-declarative layout, simple and efficient" +msgstr "چیدمان Non-declarative، ساده و کارآمد" + +#: ../../index.rst:15 +msgid "" +"Less intrusive: old script code can be transformed into a Web service " +"only by modifying the input and output operation" +msgstr "" +"کمتر مزاحم: کد اسکریپت قدیم می تواند به یک وب سرویس تبدیل شود فقط با اصلاح" +" عملیات ورودی و خروجی" + +#: ../../index.rst:16 +msgid "" +"Support integration into existing web services, currently supports Flask," +" Django, Tornado, aiohttp and FastAPI(Starlette) framework" +msgstr "" +"پشتیبانی از ادغام با وب سرویس های موجود، در حال حاضر پشتیبانی از Flask," +" Django, Tornado, aiohttp و فریم ورک FastAPI(Starlette)" + +#: ../../index.rst:17 +msgid "Support for ``asyncio`` and coroutine" +msgstr "پشتیبانی برای ``asyncio`` و coroutine" + +#: ../../index.rst:18 +msgid "Support data visualization with third-party libraries" +msgstr "پشتیبانی از بصری سازی داده با کتابخانه های شخص ثالث" + +#: ../../index.rst:21 +msgid "Installation" +msgstr "نصب" + +#: ../../index.rst:23 +msgid "Stable version::" +msgstr "نسخه پایدار::" + +#: ../../index.rst:25 +msgid "pip3 install -U pywebio" +msgstr "" + +#: ../../index.rst:27 +msgid "Development version::" +msgstr "نسخه توسعه::" + +#: ../../index.rst:29 +msgid "" +"pip3 install -U " +"https://code.aliyun.com/wang0618/pywebio/repository/archive.zip" +msgstr "" + +#: ../../index.rst:31 +msgid "**Prerequisites**: PyWebIO requires Python 3.5.2 or newer" +msgstr "**پیش نیاز ها**: PyWebIO به Python 3.5.2 یا جدید تر نیاز دارد" + +#: ../../index.rst:36 +msgid "Hello, world" +msgstr "سلام، دنیا" + +#: ../../index.rst:38 +msgid "" +"Here is a simple PyWebIO script to calculate the `BMI " +"`_ ::" +msgstr "" +"در اینجا یک اسکریپت PyWebIO ساده برای محاسبه `شاخص توده بدنی " +"`_ آورده شده است::" + +#: ../../index.rst:40 +#, python-format +msgid "" +"# A simple script to calculate BMI\n" +"from pywebio.input import input, FLOAT\n" +"from pywebio.output import put_text\n" +"\n" +"def bmi():\n" +" height = input(\"Input your height(cm):\", type=FLOAT)\n" +" weight = input(\"Input your weight(kg):\", type=FLOAT)\n" +"\n" +" BMI = weight / (height / 100) ** 2\n" +"\n" +" top_status = [(16, 'Severely underweight'), (18.5, 'Underweight'),\n" +" (25, 'Normal'), (30, 'Overweight'),\n" +" (35, 'Moderately obese'), (float('inf'), 'Severely " +"obese')]\n" +"\n" +" for top, status in top_status:\n" +" if BMI <= top:\n" +" put_text('Your BMI: %.1f. Category: %s' % (BMI, status))\n" +" break\n" +"\n" +"if __name__ == '__main__':\n" +" bmi()" +msgstr "" +"# یک اسکریپت ساده برای محاسبه شاخص توده بدنی\n" +"from pywebio.input import input, FLOAT\n" +"from pywebio.output import put_text\n" +"\n" +"def bmi():\n" +" height = input(\"قد خود را وارد کنید(سانتی متر):\", type=FLOAT)\n" +" weight = input(\"وزن خود را وارد کنید(کیلوگرم):\", type=FLOAT)\n" +"\n" +" BMI = weight / (height / 100) ** 2\n" +"\n" +" top_status = [(16, 'کمبود وزن شدید'), (18.5, 'کمبود وزن'),\n" +" (25, 'عادی'), (30, 'اضافه وزن'),\n" +" (35, 'چاق متوسط'), (float('inf'), 'چاق " +"شدید')]\n" +"\n" +" for top, status in top_status:\n" +" if BMI <= top:\n" +" put_text('شاخص توده بدنی شما: %.1f. دسته بندی: %s' % (BMI," +" status))\n" +" break\n" +"\n" +"if __name__ == '__main__':\n" +" bmi()" + +#: ../../index.rst:62 +msgid "" +"This is just a very simple script if you ignore PyWebIO, but after using " +"the input and output functions provided by PyWebIO, you can interact with" +" the code in the browser:" +msgstr "" +"این فقط یک اسکریپت خیلی ساده است اگه شما PyWebIO را نادیده بگیرید، اما بعد از" +" استفاده " +"از توابع ورودی و خروجی ارائه شده توسط PyWebIO، شما می توانید با" +" کد در مرورگر تعامل برقرار کنید:" + +#: ../../index.rst:69 +msgid "" +"In the last line of the above code, changing the function call ``bmi()`` " +"to `pywebio.start_server(bmi, port=80) " +"` will start a bmi web service on " +"port 80 ( :demo_host:`online Demo ` )." +msgstr "" +"در آخرین خط کد بالا، تغییر فراخوانی تابع ``bmi()`` " +"به `pywebio.start_server(bmi, port=80) " +"` یک وب سرویس شاخص توده بدنی را روی " +"پورت 80 راه اندازی می کند ( :demo_host:`دمو آنلاین ` )." + +#: ../../index.rst:73 +msgid "" +"If you want to integrate the ``bmi()`` service into an existing web " +"framework, you can visit :ref:`Integration with a web framework " +"` section of this document." +msgstr "" +"اگر شما می خواهید که سرویس ``bmi()`` را با یک وب " +"فریم ورک موجود ادغام کنید، می توانید بخش :ref:`Integration with a web" +" framework " +"` این مستندات را ببینید." + +#: ../../index.rst:77 +msgid "Documentation" +msgstr "مستندات" + +#: ../../index.rst:78 +msgid "" +"This documentation is also available in `PDF and Epub formats " +"`_." +msgstr "" +"این مستندات همچنین در `فرمت های PDF و Epub " +"`_ در دسترس است." + +#: ../../index.rst:106 +msgid "Indices and tables" +msgstr "ایندکس ها و جداول" + +#: ../../index.rst:108 +msgid ":ref:`genindex`" +msgstr "" + +#: ../../index.rst:109 +msgid ":ref:`modindex`" +msgstr "" + +#: ../../index.rst:110 +msgid ":ref:`search`" +msgstr "" + +#: ../../index.rst:114 +msgid "Discussion and support" +msgstr "بحث و پشتیبانی" + +#: ../../index.rst:116 +msgid "" +"Need help when use PyWebIO? Make a new discussion on `Github Discussions " +"`_." +msgstr "" +"هنگام استفاده از PyWebIO نیاز به کمک دارید؟ یک بحث جدید در `Github" +" Discussions " +"`_ بسازید." + +#: ../../index.rst:118 +msgid "" +"Report bugs on the `GitHub issue " +"`_." +msgstr "" +"باگ ها را در `GitHub issue " +"`_ گزارش کنید." + diff --git a/docs/locales/fa/LC_MESSAGES/input.po b/docs/locales/fa/LC_MESSAGES/input.po new file mode 100644 index 00000000..831b2b69 --- /dev/null +++ b/docs/locales/fa/LC_MESSAGES/input.po @@ -0,0 +1,1120 @@ +# Copyright (C) Weimin Wang +# This file is distributed under the same license as the PyWebIO package. +# +# FIRST AUTHOR , 2022. +# Pikhosh , 2022. +msgid "" +msgstr "" +"Project-Id-Version: PyWebIO 1.5.2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-02-26 16:35+0330\n" +"PO-Revision-Date: 2022-02-28 14:19+0330\n" +"Last-Translator: Pikhosh \n" +"Language-Team: Persian <>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" +"Language: fa\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Lokalize 21.12.2\n" + +#: ../../input.rst:2 +msgid "``pywebio.input`` --- Get input from web browser" +msgstr "``pywebio.input`` --- دریافت ورودی از مرورگر وب" + +#: of pywebio.input:1 +msgid "" +"This module provides functions to get all kinds of input of user from the" +" browser" +msgstr "" + +#: of pywebio.input:3 +msgid "" +"There are two ways to use the input functions, one is to call the input " +"function alone to get a single input::" +msgstr "" + +#: of pywebio.input:5 +#, python-format +msgid "" +"name = input(\"What's your name\")\n" +"print(\"Your name is %s\" % name)" +msgstr "" + +#: of pywebio.input:8 +msgid "The other is to use `input_group` to get multiple inputs at once::" +msgstr "" + +#: of pywebio.input:10 +msgid "" +"info = input_group(\"User info\",[\n" +" input('Input your name', name='name'),\n" +" input('Input your age', name='age', type=NUMBER)\n" +"])\n" +"print(info['name'], info['age'])" +msgstr "" + +#: of pywebio.input:16 +msgid "" +"When use `input_group`, you needs to provide the ``name`` parameter in " +"each input function to identify the input items in the result." +msgstr "" + +#: of pywebio.input:20 +msgid "" +"PyWebIO determines whether the input function is in `input_group` or is " +"called alone according to whether the ``name`` parameter is passed. So " +"when calling an input function alone, **do not** set the ``name`` " +"parameter; when calling the input function in `input_group`, you **must**" +" provide the ``name`` parameter." +msgstr "" + +#: of pywebio.input:24 +msgid "" +"By default, the user can submit empty input value. If the user must " +"provide a non-empty input value, you need to pass ``required=True`` to " +"the input function (some input functions do not support the ``required`` " +"parameter)" +msgstr "" + +#: of pywebio.input:27 +msgid "" +"The input functions in this module is blocking, and the input form will " +"be destroyed after successful submission. If you want the form to always " +"be displayed on the page and receive input continuously, you can consider" +" the :doc:`pin <./pin>` module." +msgstr "" + +#: of pywebio.input:32 +msgid "Functions list" +msgstr "لیست توابع" + +#: of pywebio.input:36 +msgid "Function name" +msgstr "" + +#: of pywebio.input:37 +msgid "Description" +msgstr "" + +#: of pywebio.input:39 +msgid "`input `" +msgstr "" + +#: of pywebio.input:40 pywebio.input.input:1 +msgid "Text input" +msgstr "" + +#: of pywebio.input:42 +msgid "`textarea `" +msgstr "" + +#: of pywebio.input:43 +msgid "Multi-line text input" +msgstr "" + +#: of pywebio.input:45 +msgid "`select `" +msgstr "" + +#: of pywebio.input:46 pywebio.input.select:1 +msgid "Drop-down selection" +msgstr "" + +#: of pywebio.input:48 +msgid "`checkbox `" +msgstr "" + +#: of pywebio.input:49 +msgid "Checkbox" +msgstr "" + +#: of pywebio.input:51 +msgid "`radio `" +msgstr "" + +#: of pywebio.input:52 +msgid "Radio" +msgstr "" + +#: of pywebio.input:54 +msgid "`slider `" +msgstr "" + +#: of pywebio.input:55 +msgid "Slider" +msgstr "" + +#: of pywebio.input:57 +msgid "`actions `" +msgstr "" + +#: of pywebio.input:58 pywebio.input.actions:1 +msgid "Actions selection" +msgstr "" + +#: of pywebio.input:60 +msgid "`file_upload `" +msgstr "" + +#: of pywebio.input:61 pywebio.input.file_upload:1 +msgid "File uploading" +msgstr "" + +#: of pywebio.input:63 +msgid "`input_group `" +msgstr "" + +#: of pywebio.input:64 +msgid "Input group" +msgstr "" + +#: of pywebio.input:66 +msgid "`input_update `" +msgstr "" + +#: of pywebio.input:67 +msgid "Update input item" +msgstr "" + +#: of pywebio.input:71 +msgid "Functions doc" +msgstr "مستندات توابع" + +#: of pywebio.input.actions pywebio.input.checkbox pywebio.input.file_upload +#: pywebio.input.input pywebio.input.input_group pywebio.input.input_update +#: pywebio.input.radio pywebio.input.select pywebio.input.slider +#: pywebio.input.textarea +msgid "Parameters" +msgstr "پارامتر ها" + +#: of pywebio.input.input:3 +msgid "Label of input field." +msgstr "" + +#: of pywebio.input.input:4 +msgid "" +"Input type. Currently supported types are:`TEXT` , `NUMBER` , `FLOAT` , " +"`PASSWORD` , `URL` , `DATE` , `TIME` Note that `DATE` and `TIME` type " +"are not supported on some browsers, for details see " +"https://developer.mozilla.org/en-" +"US/docs/Web/HTML/Element/input#Browser_compatibility" +msgstr "" + +#: of pywebio.input.input:4 +msgid "" +"Input type. Currently supported types are:`TEXT` , `NUMBER` , `FLOAT` , " +"`PASSWORD` , `URL` , `DATE` , `TIME`" +msgstr "" + +#: of pywebio.input.input:6 +msgid "" +"Note that `DATE` and `TIME` type are not supported on some browsers, for " +"details see https://developer.mozilla.org/en-" +"US/docs/Web/HTML/Element/input#Browser_compatibility" +msgstr "" + +#: of pywebio.input.input:8 +msgid "" +"Input value validation function. If provided, the validation function " +"will be called when user completes the input field or submits the form. " +"``validate`` receives the input value as a parameter. When the input " +"value is valid, it returns ``None``. When the input value is invalid, it " +"returns an error message string. For example: .. exportable-codeblock::" +" :name: input-valid-func :summary: `input()` validation def " +"check_age(age): if age>30: return 'Too old' " +"elif age<10: return 'Too young' input('Input your age', " +"type=NUMBER, validate=check_age)" +msgstr "" + +#: of pywebio.input.input:8 +msgid "" +"Input value validation function. If provided, the validation function " +"will be called when user completes the input field or submits the form." +msgstr "" + +#: of pywebio.input.input:11 +msgid "" +"``validate`` receives the input value as a parameter. When the input " +"value is valid, it returns ``None``. When the input value is invalid, it " +"returns an error message string. For example:" +msgstr "" + +#: of pywebio.input.input:14 +msgid "" +"def check_age(age):\n" +" if age>30:\n" +" return 'Too old'\n" +" elif age<10:\n" +" return 'Too young'\n" +"input('Input your age', type=NUMBER, validate=check_age)" +msgstr "" + +#: of pywebio.input.input:25 +msgid "" +"A string specifying a name for the input. Used with `input_group()` to " +"identify different input items in the results of the input group. If call" +" the input function alone, this parameter can **not** be set!" +msgstr "" + +#: of pywebio.input.input:27 +msgid "The initial value of the input" +msgstr "" + +#: of pywebio.input.input:29 +msgid "" +"Put a button on the right side of the input field, and user can click the" +" button to set the value for the input. ``label`` is the label of the " +"button, and ``callback`` is the callback function to set the input value " +"when clicked. The callback is invoked with one argument, the " +"``set_value``. ``set_value`` is a callable object, which is invoked with " +"one or two arguments. You can use ``set_value`` to set the value for the " +"input. ``set_value`` can be invoked with one argument: " +"``set_value(value:str)``. The ``value`` parameter is the value to be set " +"for the input. ``set_value`` can be invoked with two arguments: " +"``set_value(value:any, label:str)``. Each arguments are described as " +"follows: * ``value`` : The real value of the input, can be any object. " +"it will not be passed to the user browser. * ``label`` : The text " +"displayed to the user When calling ``set_value`` with two arguments, the" +" input item in web page will become read-only. The usage scenario of " +"``set_value(value:any, label:str)`` is: You need to dynamically generate " +"the value of the input in the callback, and hope that the result " +"displayed to the user is different from the actual submitted data (for " +"example, result displayed to the user can be some user-friendly texts, " +"and the value of the input can be objects that are easier to process) " +"Usage example: .. exportable-codeblock:: :name: input-action " +":summary: `input()` action usage import time def " +"set_now_ts(set_value): set_value(int(time.time())) ts = " +"input('Timestamp', type=NUMBER, action=('Now', set_now_ts)) " +"put_text('Timestamp:', ts) # ..demo-only ## ---- from datetime " +"import date,timedelta def select_date(set_value): with " +"popup('Select Date'): put_buttons(['Today'], onclick=[lambda:" +" set_value(date.today(), 'Today')]) " +"put_buttons(['Yesterday'], onclick=[lambda: set_value(date.today() - " +"timedelta(days=1), 'Yesterday')]) d = input('Date', " +"action=('Select', select_date), readonly=True) put_text(type(d), d) " +"Note: When using :ref:`Coroutine-based session `" +" implementation, the ``callback`` function can be a coroutine function." +msgstr "" + +#: of pywebio.input.input:29 +msgid "" +"Put a button on the right side of the input field, and user can click the" +" button to set the value for the input." +msgstr "" + +#: of pywebio.input.input:31 +msgid "" +"``label`` is the label of the button, and ``callback`` is the callback " +"function to set the input value when clicked." +msgstr "" + +#: of pywebio.input.input:33 +msgid "" +"The callback is invoked with one argument, the ``set_value``. " +"``set_value`` is a callable object, which is invoked with one or two " +"arguments. You can use ``set_value`` to set the value for the input." +msgstr "" + +#: of pywebio.input.input:36 +msgid "" +"``set_value`` can be invoked with one argument: ``set_value(value:str)``." +" The ``value`` parameter is the value to be set for the input." +msgstr "" + +#: of pywebio.input.input:38 +msgid "" +"``set_value`` can be invoked with two arguments: ``set_value(value:any, " +"label:str)``. Each arguments are described as follows:" +msgstr "" + +#: of pywebio.input.input:40 +msgid "" +"``value`` : The real value of the input, can be any object. it will not " +"be passed to the user browser." +msgstr "" + +#: of pywebio.input.input:41 +msgid "``label`` : The text displayed to the user" +msgstr "" + +#: of pywebio.input.input:43 +msgid "" +"When calling ``set_value`` with two arguments, the input item in web page" +" will become read-only." +msgstr "" + +#: of pywebio.input.input:45 +msgid "" +"The usage scenario of ``set_value(value:any, label:str)`` is: You need to" +" dynamically generate the value of the input in the callback, and hope " +"that the result displayed to the user is different from the actual " +"submitted data (for example, result displayed to the user can be some " +"user-friendly texts, and the value of the input can be objects that are " +"easier to process)" +msgstr "" + +#: of pywebio.input.input:50 +msgid "Usage example:" +msgstr "" + +#: of pywebio.input.input:52 +msgid "" +"import time\n" +"def set_now_ts(set_value):\n" +" set_value(int(time.time()))\n" +"\n" +"ts = input('Timestamp', type=NUMBER, action=('Now', set_now_ts))\n" +"put_text('Timestamp:', ts) # ..demo-only\n" +"## ----\n" +"from datetime import date,timedelta\n" +"def select_date(set_value):\n" +" with popup('Select Date'):\n" +" put_buttons(['Today'], onclick=[lambda: set_value(date.today(), " +"'Today')])\n" +" put_buttons(['Yesterday'], onclick=[lambda: " +"set_value(date.today() - timedelta(days=1), 'Yesterday')])\n" +"\n" +"d = input('Date', action=('Select', select_date), readonly=True)\n" +"put_text(type(d), d)" +msgstr "" + +#: of pywebio.input.input:72 +msgid "" +"Note: When using :ref:`Coroutine-based session `" +" implementation, the ``callback`` function can be a coroutine function." +msgstr "" + +#: of pywebio.input.input:75 +msgid "" +"A callback function which will be called when the value of this input " +"field changed. The ``onchange`` callback is invoked with one argument, " +"the current value of input field. A typical usage scenario of " +"``onchange`` is to update other input item by using `input_update()`" +msgstr "" + +#: of pywebio.input.input:75 +msgid "" +"A callback function which will be called when the value of this input " +"field changed." +msgstr "" + +#: of pywebio.input.input:77 +msgid "" +"The ``onchange`` callback is invoked with one argument, the current value" +" of input field. A typical usage scenario of ``onchange`` is to update " +"other input item by using `input_update()`" +msgstr "" + +#: of pywebio.input.input:80 +msgid "" +"A hint to the user of what can be entered in the input. It will appear in" +" the input field when it has no value set." +msgstr "" + +#: of pywebio.input.input:81 +msgid "" +"Whether a value is required for the input to be submittable, default is " +"``False``" +msgstr "" + +#: of pywebio.input.input:82 +msgid "Whether the value is readonly(not editable)" +msgstr "" + +#: of pywebio.input.input:83 +msgid "" +"A list of predefined values to suggest to the user for this input. Can " +"only be used when ``type=TEXT``" +msgstr "" + +#: of pywebio.input.input:84 +msgid "" +"Help text for the input. The text will be displayed below the input field" +" with small font" +msgstr "" + +#: of pywebio.input.input:85 +#, python-format +msgid "" +"Additional html attributes added to the input element. reference: " +"https://developer.mozilla.org/zh-" +"CN/docs/Web/HTML/Element/input#%E5%B1%9E%E6%80%A7" +msgstr "" + +#: of pywebio.input.actions pywebio.input.checkbox pywebio.input.file_upload +#: pywebio.input.input pywebio.input.input_group pywebio.input.radio +#: pywebio.input.select pywebio.input.textarea +msgid "Returns" +msgstr "" + +#: of pywebio.input.input:87 +msgid "The value that user input." +msgstr "" + +#: of pywebio.input.textarea:1 +msgid "Text input area (multi-line text input)" +msgstr "" + +#: of pywebio.input.textarea:3 +msgid "" +"The number of visible text lines for the input area. Scroll bar will be " +"used when content exceeds." +msgstr "" + +#: of pywebio.input.textarea:4 +msgid "" +"The maximum number of characters (UTF-16 code units) that the user can " +"enter. If this value isn't specified, the user can enter an unlimited " +"number of characters." +msgstr "" + +#: of pywebio.input.textarea:6 +msgid "" +"The minimum number of characters (UTF-16 code units) required that the " +"user should enter." +msgstr "" + +#: of pywebio.input.textarea:7 +msgid "" +"Enable a code style editor by providing the `Codemirror " +"`_ options: .. exportable-codeblock:: " +":name: textarea-code :summary: `textarea()` code editor style " +"res = textarea('Text area', code={ 'mode': \"python\", " +"'theme': 'darcula' }) put_code(res, language='python') # ..demo-" +"only You can simply use ``code={}`` or ``code=True`` to enable code " +"style editor. You can use ``Esc`` or ``F11`` to toggle fullscreen of code" +" style textarea. Some commonly used Codemirror options are listed " +":ref:`here `." +msgstr "" + +#: of pywebio.input.textarea:7 +msgid "" +"Enable a code style editor by providing the `Codemirror " +"`_ options:" +msgstr "" + +#: of pywebio.input.textarea:9 +msgid "" +"res = textarea('Text area', code={\n" +" 'mode': \"python\",\n" +" 'theme': 'darcula'\n" +"})\n" +"put_code(res, language='python') # ..demo-only" +msgstr "" + +#: of pywebio.input.textarea:19 +msgid "" +"You can simply use ``code={}`` or ``code=True`` to enable code style " +"editor. You can use ``Esc`` or ``F11`` to toggle fullscreen of code style" +" textarea." +msgstr "" + +#: of pywebio.input.textarea:22 +msgid "" +"Some commonly used Codemirror options are listed :ref:`here " +"`." +msgstr "" + +#: of pywebio.input.actions:39 pywebio.input.checkbox:7 +#: pywebio.input.file_upload:23 pywebio.input.radio:8 pywebio.input.select:29 +#: pywebio.input.slider:8 pywebio.input.textarea:24 +msgid "Those arguments have the same meaning as for `input()`" +msgstr "" + +#: of pywebio.input.textarea:26 +msgid "The string value that user input." +msgstr "" + +#: of pywebio.input.select:3 +msgid "" +"By default, only one option can be selected at a time, you can set " +"``multiple`` parameter to enable multiple selection." +msgstr "" + +#: of pywebio.input.select:5 +msgid "" +"list of options. The available formats of the list items are: * dict::" +" { \"label\":(str) option label, \"value\":(object) " +"option value, \"selected\":(bool, optional) whether the option is" +" initially selected, \"disabled\":(bool, optional) whether the " +"option is initially disabled } * tuple or list: ``(label, value, " +"[selected,] [disabled])`` * single value: label and value of option use " +"the same value Attention: 1. The ``value`` of option can be any JSON " +"serializable object 2. If the ``multiple`` is not ``True``, the list of " +"options can only have one ``selected`` item at most." +msgstr "" + +#: of pywebio.input.select:5 +msgid "list of options. The available formats of the list items are:" +msgstr "" + +#: of pywebio.input.actions:8 pywebio.input.select:7 +msgid "dict::" +msgstr "" + +#: of pywebio.input.select:9 +msgid "" +"{\n" +" \"label\":(str) option label,\n" +" \"value\":(object) option value,\n" +" \"selected\":(bool, optional) whether the option is initially " +"selected,\n" +" \"disabled\":(bool, optional) whether the option is initially " +"disabled\n" +"}" +msgstr "" + +#: of pywebio.input.select:16 +msgid "tuple or list: ``(label, value, [selected,] [disabled])``" +msgstr "" + +#: of pywebio.input.select:17 +msgid "single value: label and value of option use the same value" +msgstr "" + +#: of pywebio.input.select:19 +msgid "Attention:" +msgstr "" + +#: of pywebio.input.select:21 +msgid "The ``value`` of option can be any JSON serializable object" +msgstr "" + +#: of pywebio.input.select:22 +msgid "" +"If the ``multiple`` is not ``True``, the list of options can only have " +"one ``selected`` item at most." +msgstr "" + +#: of pywebio.input.select:24 +msgid "whether multiple options can be selected" +msgstr "" + +#: of pywebio.input.select:25 +msgid "" +"The value of the initial selected item. When ``multiple=True``, ``value``" +" must be a list. You can also set the initial selected option by setting " +"the ``selected`` field in the ``options`` list item." +msgstr "" + +#: of pywebio.input.select:28 +msgid "" +"Whether to select at least one item, only available when ``multiple=True``" +msgstr "" + +#: of pywebio.input.select:30 +msgid "" +"If ``multiple=True``, return a list of the values in the ``options`` " +"selected by the user; otherwise, return the single value selected by the " +"user." +msgstr "" + +#: of pywebio.input.checkbox:1 +msgid "" +"A group of check box that allowing single values to be " +"selected/deselected." +msgstr "" + +#: of pywebio.input.checkbox:3 pywebio.input.radio:3 +msgid "" +"List of options. The format is the same as the ``options`` parameter of " +"the `select()` function" +msgstr "" + +#: of pywebio.input.checkbox:4 pywebio.input.radio:4 +msgid "Whether to display the options on one line. Default is ``False``" +msgstr "" + +#: of pywebio.input.checkbox:5 +msgid "" +"The value list of the initial selected items. You can also set the " +"initial selected option by setting the ``selected`` field in the " +"``options`` list item." +msgstr "" + +#: of pywebio.input.checkbox:8 +msgid "A list of the values in the ``options`` selected by the user" +msgstr "" + +#: of pywebio.input.radio:1 +msgid "A group of radio button. Only a single button can be selected." +msgstr "" + +#: of pywebio.input.radio:5 +msgid "" +"The value of the initial selected items. You can also set the initial " +"selected option by setting the ``selected`` field in the ``options`` list" +" item." +msgstr "" + +#: of pywebio.input.radio:7 +msgid "" +"whether to must select one option. (the user can select nothing option by" +" default)" +msgstr "" + +#: of pywebio.input.radio:9 +msgid "" +"The value of the option selected by the user, if the user does not select" +" any value, return ``None``" +msgstr "" + +#: of pywebio.input.actions:3 +msgid "" +"It is displayed as a group of buttons on the page. After the user clicks " +"the button of it, it will behave differently depending on the type of the" +" button." +msgstr "" + +#: of pywebio.input.actions:6 +msgid "" +"list of buttons. The available formats of the list items are: * dict::" +" { \"label\":(str) button label, " +"\"value\":(object) button value, \"type\":(str, optional) button" +" type, \"disabled\":(bool, optional) whether the button is " +"disabled, \"color\":(str, optional) button color } " +"When ``type='reset'/'cancel'`` or ``disabled=True``, ``value`` can be " +"omitted * tuple or list: ``(label, value, [type], [disabled])`` * " +"single value: label and value of button use the same value The ``value``" +" of button can be any JSON serializable object. ``type`` can be: * " +"``'submit'`` : After clicking the button, the entire form is submitted " +"immediately, and the value of this input item in the final form is the" +" ``value`` of the button that was clicked. ``'submit'`` is the default" +" value of ``type`` * ``'cancel'`` : Cancel form. After clicking the " +"button, the entire form will be submitted immediately, and the form " +"value will return ``None`` * ``'reset'`` : Reset form. After clicking " +"the button, the entire form will be reset, and the input items will " +"become the initial state. Note: After clicking the ``type=reset`` " +"button, the form will not be submitted, and the ``actions()`` call " +"will not return The ``color`` of button can be one of: `primary`, " +"`secondary`, `success`, `danger`, `warning`, `info`, `light`, `dark`." +msgstr "" + +#: of pywebio.input.actions:6 +msgid "list of buttons. The available formats of the list items are:" +msgstr "" + +#: of pywebio.input.actions:10 +msgid "" +"{\n" +" \"label\":(str) button label,\n" +" \"value\":(object) button value,\n" +" \"type\":(str, optional) button type,\n" +" \"disabled\":(bool, optional) whether the button is disabled,\n" +" \"color\":(str, optional) button color\n" +"}" +msgstr "" + +#: of pywebio.input.actions:18 +msgid "" +"When ``type='reset'/'cancel'`` or ``disabled=True``, ``value`` can be " +"omitted" +msgstr "" + +#: of pywebio.input.actions:19 +msgid "tuple or list: ``(label, value, [type], [disabled])``" +msgstr "" + +#: of pywebio.input.actions:20 +msgid "single value: label and value of button use the same value" +msgstr "" + +#: of pywebio.input.actions:22 +msgid "The ``value`` of button can be any JSON serializable object." +msgstr "" + +#: of pywebio.input.actions:24 +msgid "``type`` can be:" +msgstr "" + +#: of pywebio.input.actions:26 +msgid "" +"``'submit'`` : After clicking the button, the entire form is submitted " +"immediately, and the value of this input item in the final form is the " +"``value`` of the button that was clicked. ``'submit'`` is the default " +"value of ``type``" +msgstr "" + +#: of pywebio.input.actions:29 +msgid "" +"``'cancel'`` : Cancel form. After clicking the button, the entire form " +"will be submitted immediately, and the form value will return ``None``" +msgstr "" + +#: of pywebio.input.actions:31 +msgid "" +"``'reset'`` : Reset form. After clicking the button, the entire form will" +" be reset, and the input items will become the initial state. Note: After" +" clicking the ``type=reset`` button, the form will not be submitted, and " +"the ``actions()`` call will not return" +msgstr "" + +#: of pywebio.input.actions:36 +msgid "" +"The ``color`` of button can be one of: `primary`, `secondary`, `success`," +" `danger`, `warning`, `info`, `light`, `dark`." +msgstr "" + +#: of pywebio.input.actions:40 +msgid "" +"If the user clicks the ``type=submit`` button to submit the form, return " +"the value of the button clicked by the user. If the user clicks the " +"``type=cancel`` button or submits the form by other means, ``None`` is " +"returned." +msgstr "" + +#: of pywebio.input.actions:44 +msgid "" +"When ``actions()`` is used as the last input item in `input_group()` and " +"contains a button with ``type='submit'``, the default submit button of " +"the `input_group()` form will be replace with the current ``actions()``" +msgstr "" + +#: of pywebio.input.actions:47 +msgid "**usage scenes of actions() **" +msgstr "" + +#: of pywebio.input.actions:51 +msgid "Perform simple selection operations:" +msgstr "" + +#: of pywebio.input.actions:53 +#, python-format +msgid "" +"confirm = actions('Confirm to delete file?', ['confirm', 'cancel'],\n" +" help_text='Unrecoverable after file deletion')\n" +"if confirm=='confirm': # ..doc-only\n" +" ... # ..doc-only\n" +"put_markdown('You clicked the `%s` button' % confirm) # ..demo-only" +msgstr "" + +#: of pywebio.input.actions:63 +msgid "" +"Compared with other input items, when using `actions()`, the user only " +"needs to click once to complete the submission." +msgstr "" + +#: of pywebio.input.actions:65 +msgid "Replace the default submit button:" +msgstr "" + +#: of pywebio.input.actions:67 +msgid "" +"import json # ..demo-only\n" +" # ..demo-only\n" +"info = input_group('Add user', [\n" +" input('username', type=TEXT, name='username', required=True),\n" +" input('password', type=PASSWORD, name='password', required=True),\n" +" actions('actions', [\n" +" {'label': 'Save', 'value': 'save'},\n" +" {'label': 'Save and add next', 'value': 'save_and_continue'},\n" +" {'label': 'Reset', 'type': 'reset', 'color': 'warning'},\n" +" {'label': 'Cancel', 'type': 'cancel', 'color': 'danger'},\n" +" ], name='action', help_text='actions'),\n" +"])\n" +"put_code('info = ' + json.dumps(info, indent=4))\n" +"if info is not None:\n" +" save_user(info['username'], info['password']) # ..doc-only\n" +" if info['action'] == 'save_and_continue':\n" +" add_next() # ..doc-only\n" +" put_text('Save and add next...') # ..demo-only" +msgstr "" + +#: of pywebio.input.file_upload:3 +msgid "" +"Single value or list, indicating acceptable file types. The available " +"formats of file types are: * A valid case-insensitive filename " +"extension, starting with a period (\".\") character. For example: " +"``.jpg``, ``.pdf``, or ``.doc``. * A valid MIME type string, with no " +"extensions. For examples: ``application/pdf``, ``audio/*``, " +"``video/*``, ``image/*``. For more information, please visit: " +"https://developer.mozilla.org/en-" +"US/docs/Web/HTTP/Basics_of_HTTP/MIME_types" +msgstr "" + +#: of pywebio.input.file_upload:3 +msgid "" +"Single value or list, indicating acceptable file types. The available " +"formats of file types are:" +msgstr "" + +#: of pywebio.input.file_upload:5 +msgid "" +"A valid case-insensitive filename extension, starting with a period " +"(\".\") character. For example: ``.jpg``, ``.pdf``, or ``.doc``." +msgstr "" + +#: of pywebio.input.file_upload:6 +msgid "" +"A valid MIME type string, with no extensions. For examples: " +"``application/pdf``, ``audio/*``, ``video/*``, ``image/*``. For more " +"information, please visit: https://developer.mozilla.org/en-" +"US/docs/Web/HTTP/Basics_of_HTTP/MIME_types" +msgstr "" + +#: of pywebio.input.file_upload:11 +msgid "" +"A hint to the user of what to be uploaded. It will appear in the input " +"field when there is no file selected." +msgstr "" + +#: of pywebio.input.file_upload:12 +msgid "Whether to allow upload multiple files. Default is ``False``." +msgstr "" + +#: of pywebio.input.file_upload:13 +msgid "" +"The maximum size of a single file, exceeding the limit will prohibit " +"uploading. The default is 0, which means there is no limit to the size." +" ``max_size`` can be a integer indicating the number of bytes, or a " +"case-insensitive string ending with `K` / `M` / `G` (representing " +"kilobytes, megabytes, and gigabytes, respectively). E.g: " +"``max_size=500``, ``max_size='40K'``, ``max_size='3M'``" +msgstr "" + +#: of pywebio.input.file_upload:14 +msgid "" +"The maximum size of a single file, exceeding the limit will prohibit " +"uploading." +msgstr "" + +#: of pywebio.input.file_upload:14 +msgid "The default is 0, which means there is no limit to the size." +msgstr "" + +#: of pywebio.input.file_upload:16 +msgid "" +"``max_size`` can be a integer indicating the number of bytes, or a case-" +"insensitive string ending with `K` / `M` / `G` (representing kilobytes, " +"megabytes, and gigabytes, respectively). E.g: ``max_size=500``, " +"``max_size='40K'``, ``max_size='3M'``" +msgstr "" + +#: of pywebio.input.file_upload:20 +msgid "" +"The maximum size of all files. Only available when ``multiple=True``. The" +" default is 0, which means there is no limit to the size. The format is " +"the same as the ``max_size`` parameter" +msgstr "" + +#: of pywebio.input.file_upload:22 +msgid "" +"Indicates whether the user must specify a file for the input. Default is " +"``False``." +msgstr "" + +#: of pywebio.input.file_upload:24 +msgid "" +"When ``multiple=False``, a dict is returned:: { 'filename': file " +"name, 'content':content of the file (in bytes), 'mime_type': " +"MIME type of the file, 'last_modified': Last modified time " +"(timestamp) of the file } If there is no file uploaded, return " +"``None``. When ``multiple=True``, a list is returned. The format of the " +"list item is the same as the return value when ``multiple=False`` above. " +"If the user does not upload a file, an empty list is returned." +msgstr "" + +#: of pywebio.input.file_upload:24 +msgid "When ``multiple=False``, a dict is returned::" +msgstr "" + +#: of pywebio.input.file_upload:26 +msgid "" +"{\n" +" 'filename': file name,\n" +" 'content':content of the file (in bytes),\n" +" 'mime_type': MIME type of the file,\n" +" 'last_modified': Last modified time (timestamp) of the file\n" +"}" +msgstr "" + +#: of pywebio.input.file_upload:33 +msgid "If there is no file uploaded, return ``None``." +msgstr "" + +#: of pywebio.input.file_upload:35 +msgid "" +"When ``multiple=True``, a list is returned. The format of the list item " +"is the same as the return value when ``multiple=False`` above. If the " +"user does not upload a file, an empty list is returned." +msgstr "" + +#: of pywebio.input.file_upload:40 +msgid "" +"If uploading large files, please pay attention to the file upload size " +"limit setting of the web framework. When using :func:`start_server() " +"` or :func:`path_deploy() " +"` to start the PyWebIO application, the " +"maximum file size to be uploaded allowed by the web framework can be set " +"through the ``max_payload_size`` parameter." +msgstr "" + +#: of pywebio.input.file_upload:45 +msgid "" +"# Upload a file and save to server # ..doc-only\n" +"f = input.file_upload(\"Upload a file\") # ..doc-only\n" +"open('asset/'+f['filename'], 'wb').write(f['content']) # ..doc-only\n" +"\n" +"imgs = file_upload(\"Select some pictures:\", accept=\"image/*\", " +"multiple=True)\n" +"for img in imgs:\n" +" put_image(img['content'])" +msgstr "" + +#: of pywebio.input.slider:1 +msgid "Range input." +msgstr "" + +#: of pywebio.input.slider:3 +msgid "The initial value of the slider." +msgstr "" + +#: of pywebio.input.slider:4 +msgid "The minimum permitted value." +msgstr "" + +#: of pywebio.input.slider:5 +msgid "The maximum permitted value." +msgstr "" + +#: of pywebio.input.slider:6 +msgid "" +"The stepping interval. Only available when ``value``, ``min_value`` and " +"``max_value`` are all integer." +msgstr "" + +#: of pywebio.input.slider +msgid "return int/float" +msgstr "" + +#: of pywebio.input.slider:9 +msgid "" +"If one of ``value``, ``min_value`` and ``max_value`` is float, the return" +" value is a float, otherwise an int is returned." +msgstr "" + +#: of pywebio.input.input_group:1 +msgid "Input group. Request a set of inputs from the user at once." +msgstr "" + +#: of pywebio.input.input_group:3 +msgid "Label of input group." +msgstr "" + +#: of pywebio.input.input_group:4 +msgid "" +"Input items. The item of the list is the call to the single input " +"function, and the ``name`` parameter need to be passed in the single " +"input function." +msgstr "" + +#: of pywebio.input.input_group:6 +msgid "" +"validation function for the group. If provided, the validation function " +"will be called when the user submits the form. Function signature: " +"``callback(data) -> (name, error_msg)``. ``validate`` receives the value " +"of the entire group as a parameter. When the form value is valid, it " +"returns ``None``. When an input item's value is invalid, it returns the " +"``name`` value of the item and an error message. For example:" +msgstr "" + +#: of pywebio.input.input_group:6 +msgid "" +"validation function for the group. If provided, the validation function " +"will be called when the user submits the form." +msgstr "" + +#: of pywebio.input.input_group:8 +msgid "" +"Function signature: ``callback(data) -> (name, error_msg)``. ``validate``" +" receives the value of the entire group as a parameter. When the form " +"value is valid, it returns ``None``. When an input item's value is " +"invalid, it returns the ``name`` value of the item and an error message. " +"For example:" +msgstr "" + +#: of pywebio.input.input_group:13 +msgid "" +"def check_form(data):\n" +" if len(data['name']) > 6:\n" +" return ('name', 'Name to long!')\n" +" if data['age'] <= 0:\n" +" return ('age', 'Age cannot be negative!')\n" +"\n" +"data = input_group(\"Basic info\",[\n" +" input('Input your name', name='name'),\n" +" input('Repeat your age', name='age', type=NUMBER)\n" +"], validate=check_form)\n" +"\n" +"put_text(data['name'], data['age'])" +msgstr "" + +#: of pywebio.input.input_group:30 +msgid "" +"Whether the form can be cancelled. Default is ``False``. If " +"``cancelable=True``, a \"Cancel\" button will be displayed at the bottom " +"of the form. Note: If the last input item in the group is `actions()`, " +"``cancelable`` will be ignored." +msgstr "" + +#: of pywebio.input.input_group:30 +msgid "" +"Whether the form can be cancelled. Default is ``False``. If " +"``cancelable=True``, a \"Cancel\" button will be displayed at the bottom " +"of the form." +msgstr "" + +#: of pywebio.input.input_group:33 +msgid "" +"Note: If the last input item in the group is `actions()`, ``cancelable`` " +"will be ignored." +msgstr "" + +#: of pywebio.input.input_group:35 +msgid "" +"If the user cancels the form, return ``None``, otherwise a ``dict`` is " +"returned, whose key is the ``name`` of the input item, and whose value is" +" the value of the input item." +msgstr "" + +#: of pywebio.input.input_update:1 +msgid "" +"Update attributes of input field. This function can only be called in " +"``onchange`` callback of input functions." +msgstr "" + +#: of pywebio.input.input_update:4 +msgid "" +"The ``name`` of the target input item. Optional, default is the name of " +"input field which triggers ``onchange``" +msgstr "" + +#: of pywebio.input.input_update:6 +msgid "" +"The input parameters need to be updated. Note that those parameters can " +"not be updated: ``type``, ``name``, ``validate``, ``action``, ``code``, " +"``onchange``, ``multiple``" +msgstr "" + +#: of pywebio.input.input_update:10 +msgid "An example of implementing dependent input items in an input group:" +msgstr "" + +#: of pywebio.input.input_update:12 +msgid "" +"country2city = {\n" +" 'China': ['Beijing', 'Shanghai', 'Hong Kong'],\n" +" 'USA': ['New York', 'Los Angeles', 'San Francisco'],\n" +"}\n" +"countries = list(country2city.keys())\n" +"location = input_group(\"Select a location\", [\n" +" select('Country', options=countries, name='country',\n" +" onchange=lambda c: input_update('city', " +"options=country2city[c])),\n" +" select('City', options=country2city[countries[0]], name='city'),\n" +"])\n" +"put_text(location) # ..demo-only" +msgstr "" + diff --git a/docs/locales/fa/LC_MESSAGES/libraries_support.po b/docs/locales/fa/LC_MESSAGES/libraries_support.po new file mode 100644 index 00000000..cf18aaf5 --- /dev/null +++ b/docs/locales/fa/LC_MESSAGES/libraries_support.po @@ -0,0 +1,264 @@ +# Copyright (C) Weimin Wang +# This file is distributed under the same license as the PyWebIO package. +# +# FIRST AUTHOR , 2022. +# Pikhosh , 2022. +msgid "" +msgstr "" +"Project-Id-Version: PyWebIO 1.5.2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-02-26 16:35+0330\n" +"PO-Revision-Date: 2022-02-28 14:38+0330\n" +"Last-Translator: Pikhosh \n" +"Language-Team: Persian <>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" +"Language: fa\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Lokalize 21.12.2\n" + +#: ../../libraries_support.rst:2 +msgid "Libraries support" +msgstr "پشتیبانی کتابخانه ها" + +#: ../../libraries_support.rst:7 +msgid "Build stand-alone App" +msgstr "ساخت اپ مستقل" + +#: ../../libraries_support.rst:8 +msgid "" +"`PyInstaller `_ bundles a " +"Python application and all its dependencies into a folder or executable. " +"The user can run the packaged app without installing a Python interpreter" +" or any modules." +msgstr "" + +#: ../../libraries_support.rst:10 +msgid "" +"You can use PyInstaller to packages PyWebIO application into a stand-" +"alone executable or folder:" +msgstr "" + +#: ../../libraries_support.rst:12 +msgid "Create a pyinstaller spec (specification) file::" +msgstr "" + +#: ../../libraries_support.rst:14 +msgid "pyi-makespec app.py" +msgstr "" + +#: ../../libraries_support.rst:16 +msgid "You need replace ``app.py`` to your PyWebIO application file name." +msgstr "" + +#: ../../libraries_support.rst:18 +msgid "Edit the spec file, change the ``datas`` parameter of ``Analysis``::" +msgstr "" + +#: ../../libraries_support.rst:20 +msgid "" +"from pywebio.utils import pyinstaller_datas\n" +"\n" +"a = Analysis(\n" +" ...\n" +" datas=pyinstaller_datas(),\n" +" ..." +msgstr "" + +#: ../../libraries_support.rst:27 +msgid "" +"Build the application by passing the spec file to the pyinstaller " +"command::" +msgstr "" + +#: ../../libraries_support.rst:29 +msgid "pyinstaller app.spec" +msgstr "" + +#: ../../libraries_support.rst:32 +msgid "" +"If you want to create a one-file bundled executable, you need pass " +"``--onefile`` option in first step." +msgstr "" + +#: ../../libraries_support.rst:34 +msgid "" +"For more information, please visit: " +"https://pyinstaller.readthedocs.io/en/stable/spec-files.html" +msgstr "" + +#: ../../libraries_support.rst:40 +msgid "Data visualization" +msgstr "بصری سازی داده" + +#: ../../libraries_support.rst:41 +msgid "PyWebIO supports for data visualization with the third-party libraries." +msgstr "" + +#: ../../libraries_support.rst:44 +msgid "Bokeh" +msgstr "" + +#: ../../libraries_support.rst:46 +msgid "" +"`Bokeh `_ is an interactive visualization" +" library for modern web browsers. It provides elegant, concise " +"construction of versatile graphics, and affords high-performance " +"interactivity over large or streaming datasets." +msgstr "" + +#: ../../libraries_support.rst:48 +msgid "" +"You can use ``bokeh.io.output_notebook(notebook_type='pywebio')`` in the " +"PyWebIO session to setup Bokeh environment. Then you can use " +"``bokeh.io.show()`` to output a boken chart::" +msgstr "" + +#: ../../libraries_support.rst:51 +msgid "" +"from bokeh.io import output_notebook\n" +"from bokeh.io import show\n" +"\n" +"output_notebook(notebook_type='pywebio')\n" +"fig = figure(...)\n" +"...\n" +"show(fig)" +msgstr "" + +#: ../../libraries_support.rst:59 +msgid "See related demo on :charts_demo_host:`bokeh demo `" +msgstr "" + +#: ../../libraries_support.rst:61 +msgid "" +"In addition to creating ordinary charts, Bokeh can also build the Bokeh " +"applications by starting the `Bokeh server " +"`_. The " +"purpose of the Bokeh server is to make it easy for Python users to create" +" interactive web applications that can connect front-end UI events to " +"real, running Python code." +msgstr "" + +#: ../../libraries_support.rst:63 +msgid "" +"In PyWebIO, you can also use ``bokeh.io.show()`` to display a Bokeh App. " +"For the example, see `bokeh_app.py " +"`_." +msgstr "" + +#: ../../libraries_support.rst:65 +msgid "Bokeh App currently is only available in the default Tornado backend" +msgstr "" + +#: ../../libraries_support.rst:70 +msgid "pyecharts" +msgstr "" + +#: ../../libraries_support.rst:72 +msgid "" +"`pyecharts `_ is a python " +"plotting library which uses `Echarts " +"`_ as underlying implementation." +msgstr "" + +#: ../../libraries_support.rst:74 +msgid "" +"In PyWebIO, you can use the following code to output the pyecharts chart " +"instance::" +msgstr "" + +#: ../../libraries_support.rst:76 +msgid "" +"# `chart` is pyecharts chart instance\n" +"pywebio.output.put_html(chart.render_notebook())" +msgstr "" + +#: ../../libraries_support.rst:79 +msgid "" +"See related demo on :charts_demo_host:`pyecharts demo `" +msgstr "" + +#: ../../libraries_support.rst:86 +msgid "plotly" +msgstr "" + +#: ../../libraries_support.rst:88 +msgid "" +"`plotly.py `_ is an interactive, " +"open-source, and browser-based graphing library for Python." +msgstr "" + +#: ../../libraries_support.rst:90 +msgid "" +"In PyWebIO, you can use the following code to output the plotly chart " +"instance::" +msgstr "" + +#: ../../libraries_support.rst:92 +msgid "" +"# `fig` is plotly chart instance\n" +"html = fig.to_html(include_plotlyjs=\"require\", full_html=False)\n" +"pywebio.output.put_html(html)" +msgstr "" + +#: ../../libraries_support.rst:96 +msgid "See related demo on :charts_demo_host:`plotly demo `" +msgstr "" + +#: ../../libraries_support.rst:101 +msgid "pyg2plot" +msgstr "" + +#: ../../libraries_support.rst:103 +msgid "" +"`pyg2plot `_ is a python plotting " +"library which uses `G2Plot `_ as " +"underlying implementation." +msgstr "" + +#: ../../libraries_support.rst:105 +msgid "" +"In PyWebIO, you can use the following code to output the pyg2plot chart " +"instance::" +msgstr "" + +#: ../../libraries_support.rst:107 +msgid "" +"# `chart` is pyg2plot chart instance\n" +"pywebio.output.put_html(chart.render_notebook())" +msgstr "" + +#: ../../libraries_support.rst:110 +msgid "See related demo on :charts_demo_host:`plotly demo `" +msgstr "" + +#: ../../libraries_support.rst:113 +msgid "cutecharts.py" +msgstr "" + +#: ../../libraries_support.rst:115 +msgid "" +"`cutecharts.py `_ is a hand " +"drawing style charts library for Python which uses `chart.xkcd " +"`_ as underlying implementation." +msgstr "" + +#: ../../libraries_support.rst:117 +msgid "" +"In PyWebIO, you can use the following code to output the cutecharts.py " +"chart instance::" +msgstr "" + +#: ../../libraries_support.rst:119 +msgid "" +"# `chart` is cutecharts chart instance\n" +"pywebio.output.put_html(chart.render_notebook())" +msgstr "" + +#: ../../libraries_support.rst:122 +msgid "" +"See related demo on :charts_demo_host:`cutecharts demo `" +msgstr "" + diff --git a/docs/locales/fa/LC_MESSAGES/misc.po b/docs/locales/fa/LC_MESSAGES/misc.po new file mode 100644 index 00000000..f6531d3e --- /dev/null +++ b/docs/locales/fa/LC_MESSAGES/misc.po @@ -0,0 +1,122 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) Weimin Wang +# This file is distributed under the same license as the PyWebIO package. +# FIRST AUTHOR , 2022. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PyWebIO 1.5.2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-02-26 16:35+0330\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" + +#: ../../misc.rst:2 +msgid "Miscellaneous" +msgstr "" + +#: ../../misc.rst:7 +msgid "Commonly used Codemirror options" +msgstr "" + +#: ../../misc.rst:9 +msgid "" +"``mode`` (str): The language of code. For complete list, see " +"https://codemirror.net/mode/index.html" +msgstr "" + +#: ../../misc.rst:10 +msgid "" +"``theme`` (str): The theme to style the editor with. For all available " +"theme, see https://codemirror.net/demo/theme.html" +msgstr "" + +#: ../../misc.rst:11 +msgid "" +"``lineNumbers`` (bool): Whether to show line numbers to the left of the " +"editor." +msgstr "" + +#: ../../misc.rst:12 +msgid "" +"``indentUnit`` (int): How many spaces a block (whatever that means in the" +" edited language) should be indented. The default is 2." +msgstr "" + +#: ../../misc.rst:13 +msgid "``tabSize`` (int): The width of a tab character. Defaults to 4." +msgstr "" + +#: ../../misc.rst:14 +msgid "" +"``lineWrapping`` (bool): Whether CodeMirror should scroll or wrap for " +"long lines. Defaults to false (scroll)." +msgstr "" + +#: ../../misc.rst:16 +msgid "" +"For complete Codemirror options, please visit: " +"https://codemirror.net/doc/manual.html#config" +msgstr "" + +#: ../../misc.rst:21 +msgid "Nginx WebSocket Config Example" +msgstr "" + +#: ../../misc.rst:23 +msgid "" +"Assuming that the PyWebIO application is running at the " +"``localhost:5000`` address, if you want to access your PyWebIO " +"application via ``http://server_ip/some_path/tool``, a sample Nginx " +"configuration is as follows::" +msgstr "" + +#: ../../misc.rst:25 +msgid "" +"map $http_upgrade $connection_upgrade {\n" +" default upgrade;\n" +" '' close;\n" +"}\n" +"\n" +"server {\n" +" listen 80;\n" +"\n" +" location /some_path/ {\n" +" alias /path/to/pywebio/static/dir/;\n" +" }\n" +" location /some_path/tool {\n" +" proxy_read_timeout 300s;\n" +" proxy_send_timeout 300s;\n" +" proxy_http_version 1.1;\n" +" proxy_set_header Host $http_host;\n" +" proxy_set_header Upgrade $http_upgrade;\n" +" proxy_set_header Connection $connection_upgrade;\n" +" proxy_pass http://localhost:5000/;\n" +" }\n" +"}" +msgstr "" + +#: ../../misc.rst:48 +msgid "" +"The above configuration file hosts the static files of PyWebIO on the " +"``/some_path/`` path, and reverse proxy ``/some_path/tool`` to " +"``localhost:5000``." +msgstr "" + +#: ../../misc.rst:50 +msgid "" +"The path of the static file of PyWebIO can be obtained with the command " +"``python3 -c \"import pywebio; print(pywebio.STATIC_PATH)\"``, you can " +"also copy the static file to other directories::" +msgstr "" + +#: ../../misc.rst:52 +msgid "cp -r `python3 -c \"import pywebio; print(pywebio.STATIC_PATH)\"` ~/web" +msgstr "" + diff --git a/docs/locales/fa/LC_MESSAGES/outdate.po b/docs/locales/fa/LC_MESSAGES/outdate.po new file mode 100644 index 00000000..aaf7d608 --- /dev/null +++ b/docs/locales/fa/LC_MESSAGES/outdate.po @@ -0,0 +1,263 @@ +# Copyright (C) Weimin Wang +# This file is distributed under the same license as the PyWebIO package. +# +# FIRST AUTHOR , 2022. +# Pikhosh , 2022. +msgid "" +msgstr "" +"Project-Id-Version: PyWebIO 1.5.2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-02-26 16:35+0330\n" +"PO-Revision-Date: 2022-02-28 14:20+0330\n" +"Last-Translator: Pikhosh \n" +"Language-Team: Persian <>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" +"Language: fa\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Lokalize 21.12.2\n" + +#: ../../outdate.rst:2 +msgid "The outdated functions" +msgstr "" + +#: ../../outdate.rst:4 +msgid "" +"This section shows document of the deprecated functions in case you use " +"the previous version of PyWebIO." +msgstr "" + +#: ../../outdate.rst:6 +msgid "" +"Those functions still work in latest PyWebIO, however they may be removed" +" in future version." +msgstr "" + +#: ../../outdate.rst:9 +msgid "``output`` module" +msgstr "" + +#: of pywebio.output.style:1 +msgid "Customize the css style of output content" +msgstr "" + +#: of pywebio.output.style:3 +msgid "See :ref:`User Guide \"\"\") # ..demo-only\n" +"put_text() # ..demo-only\n" +"put_buttons([('Put text to %s' % i, i) for i in ('A', 'B', 'C')], lambda s: put_text(s, scope=s)) # ..demo-only" +msgstr "" + +#: ../../guide.rst:489 +msgid "The above code will generate the following scope layout::" +msgstr "以上代码将会产生如下Scope布局::" + +#: ../../guide.rst:491 +msgid "" +"┌─ROOT────────────────────┐\n" +"│ │\n" +"│ ┌─A───────────────────┐ │\n" +"│ │ Text in scope A │ │\n" +"│ │ ┌─B───────────────┐ │ │\n" +"│ │ │ Text in scope B │ │ │\n" +"│ │ └─────────────────┘ │ │\n" +"│ └─────────────────────┘ │\n" +"│ │\n" +"│ ┌─C───────────────────┐ │\n" +"│ │ Text in scope C │ │\n" +"│ └─────────────────────┘ │\n" +"└─────────────────────────┘" +msgstr "" + +#: ../../guide.rst:507 +msgid "**put_scope()**" +msgstr "" + +#: ../../guide.rst:509 +msgid "" +"We already know that the scope is a container of output content. So can we use this container as a sub-item of a output (like, set a cell in table " +"as a container)? Yes, you can use `put_scope() ` to create a scope explicitly. The function name starts with ``put_``, " +"which means it can be pass to the functions that accept ``put_xxx()`` calls." +msgstr "" +"我们已经知道scope实际上是输出内容的容器,那么我们能否将scope作为输出的子元素呢(比如将scope作为表格的一个cell),答案是肯定的。你可以使用 " +"`put_scope() ` 来显式创建一个scope,而从它以 ``put_`` 开头的函数名可以看出,它也可以被传递到任何可以接受 ``put_xxx()`` 调" +"用的地方。" + +#: ../../guide.rst:514 +msgid "" +"put_table([\n" +" ['Name', 'Hobbies'],\n" +" ['Tom', put_scope('hobby', content=put_text('Coding'))] # hobby is initialized to coding\n" +"])\n" +"\n" +"## ----\n" +"with use_scope('hobby', clear=True):\n" +" put_text('Movie') # hobby is reset to Movie\n" +"\n" +"## ----\n" +"# append Music, Drama to hobby\n" +"with use_scope('hobby'):\n" +" put_text('Music')\n" +" put_text('Drama')\n" +"\n" +"## ----\n" +"# insert the Coding into the top of the hobby\n" +"put_markdown('**Coding**', scope='hobby', position=0)" +msgstr "" + +#: ../../guide.rst:538 +msgid "It is not allowed to have two scopes with the same name in the application." +msgstr "" + +#: ../../guide.rst:540 +msgid "**Scope control**" +msgstr "**输出域控制函数**" + +#: ../../guide.rst:542 +msgid "" +"In addition to `use_scope() ` and `put_scope() `, PyWebIO also provides the following scope " +"control functions:" +msgstr "除了 `use_scope() ` 和 `put_scope() ` , PyWebIO还提供了以下scope控制函数:" + +#: ../../guide.rst:545 +msgid "`clear(scope) ` : Clear the contents of the scope" +msgstr "`clear(scope) ` : 清除scope的内容" + +#: ../../guide.rst:546 +msgid "`remove(scope) ` : Remove scope" +msgstr "`remove(scope) ` : 移除scope" + +#: ../../guide.rst:547 +msgid "`scroll_to(scope) ` : Scroll the page to the scope" +msgstr "`scroll_to(scope) ` : 将页面滚动到scope处" + +#: ../../guide.rst:549 +msgid "" +"Also, all output functions (function name like ``put_xxx()``) support a ``scope`` parameter to specify the destination scope to output, and support " +"a ``position`` parameter to specify the insert position in target scope. Refer :ref:`output module ` for more information." +msgstr "" +"另外,所有的输出函数还支持使用 ``scope`` 参数来指定输出的目的scope,也可使用 ``position`` 参数来指定在目标scope中输出的位置。更多信息参见 :ref:" +"`output 模块 ` 。" + +#: ../../guide.rst:554 +msgid "Layout" +msgstr "布局" + +#: ../../guide.rst:556 +msgid "" +"In general, using the output functions introduced above is enough to output what you want, but these outputs are arranged vertically. If you want " +"to create a more complex layout (such as displaying a code block on the left side of the page and an image on the right), you need to use layout " +"functions." +msgstr "通常,使用上述输出函数足以完成大部分输出,但是这些输出之间全都是竖直排列的。如果想创建更复杂的布局,需要使用布局函数。" + +#: ../../guide.rst:560 +msgid "The ``pywebio.output`` module provides 3 layout functions, and you can create complex layouts by combining them:" +msgstr "``pywebio.output`` 模块提供了3个布局函数,通过对他们进行组合可以完成各种复杂的布局:" + +#: ../../guide.rst:562 +msgid "`put_row() ` : Use row layout to output content. The content is arranged horizontally" +msgstr "`put_row() ` : 使用行布局输出内容. 内容在水平方向上排列" + +#: ../../guide.rst:563 +msgid "`put_column() ` : Use column layout to output content. The content is arranged vertically" +msgstr "`put_column() ` : 使用列布局输出内容. 内容在竖直方向上排列" + +#: ../../guide.rst:564 +msgid "`put_grid() ` : Output content using grid layout" +msgstr "`put_grid() ` : 使用网格布局输出内容" + +#: ../../guide.rst:566 +msgid "Here is an example by combining ``put_row()`` and ``put_column()``:" +msgstr "通过组合 ``put_row()`` 和 ``put_column()`` 可以实现灵活布局:" + +#: ../../guide.rst:568 +msgid "" +"put_row([\n" +" put_column([\n" +" put_code('A'),\n" +" put_row([\n" +" put_code('B1'), None, # None represents the space between the output\n" +" put_code('B2'), None,\n" +" put_code('B3'),\n" +" ]),\n" +" put_code('C'),\n" +" ]), None,\n" +" put_code('D'), None,\n" +" put_code('E')\n" +"])" +msgstr "" + +#: ../../guide.rst:591 +msgid "The layout function also supports customizing the size of each part::" +msgstr "布局函数还支持自定义各部分的尺寸::" + +#: ../../guide.rst:593 +#, python-format +msgid "put_row([put_image(...), put_image(...)], size='40% 60%') # The ratio of the width of two images is 2:3" +msgstr "put_row([put_image(…), put_image(…)], size='40% 60%') # 左右两图宽度比2:3" + +#: ../../guide.rst:595 +msgid "For more information, please refer to the :ref:`layout functions documentation `." +msgstr "更多布局函数的用法及代码示例请查阅 :ref:`布局函数文档 ` ." + +#: ../../guide.rst:600 +msgid "Style" +msgstr "样式" + +#: ../../guide.rst:602 +msgid "" +"If you are familiar with `CSS `_ styles, you can use the ``style()`` method of output return to set a custom " +"style for the output." +msgstr "" +"如果你熟悉 `CSS样式 `_ ,你还可以在输出函数后调用 ``style()`` 方法给输出设定自定义样式。" + +#: ../../guide.rst:605 +msgid "You can set the CSS style for a single ``put_xxx()`` output:" +msgstr "可以给单个的 ``put_xxx()`` 输出设定CSS样式,也可以配合组合输出使用:" + +#: ../../guide.rst:607 +msgid "" +"put_text('hello').style('color: red; font-size: 20px')\n" +"\n" +"## ----\n" +"# in combined output\n" +"put_row([\n" +" put_text('hello').style('color: red'),\n" +" put_markdown('markdown')\n" +"]).style('margin-top: 20px')" +msgstr "" + +#: ../../guide.rst:620 +msgid "The return value of ``style()`` method is the object itself so it can be used in combined output." +msgstr "``style()`` 方法的返回值为对象本身,所以可以继续用于组合输出中。" + +#: ../../guide.rst:625 +msgid "Run application" +msgstr "" + +#: ../../guide.rst:627 +msgid "" +"In PyWebIO, there are two modes to run PyWebIO applications: running as a script and using `pywebio.start_server() ` or `pywebio.platform.path_deploy() ` to run as a web service." +msgstr "" +"在PyWebIO中,有两种方式用来运行PyWebIO应用:作为脚本运行和使用 `pywebio.start_server() ` 或 `pywebio." +"platform.path_deploy() ` 来作为Web服务运行。" + +#: ../../guide.rst:632 +msgid "Overview" +msgstr "" + +#: ../../guide.rst:636 ../../guide.rst:701 +msgid "**Server mode**" +msgstr "**Server模式**" + +#: ../../guide.rst:638 +msgid "" +"In server mode, PyWebIO will start a web server to continuously provide services. When the user accesses the service address, PyWebIO will open a " +"new session and run PyWebIO application in it." +msgstr "在Server模式下,PyWebIO会启动一个Web服务来持续性地提供服务。当用户访问服务地址时,PyWebIO会开启一个新会话并运行PyWebIO应用。" + +#: ../../guide.rst:641 +msgid "`start_server() ` is the most common way to start a web server to serve given PyWebIO applications::" +msgstr "将PyWebIO应用部署为web服务的最常用方式是使用 `start_server() ` ::" + +#: ../../guide.rst:644 +msgid "" +"from pywebio import *\n" +"\n" +"def main(): # PyWebIO application function\n" +" name = input.input(\"what's your name\")\n" +" output.put_text(\"hello\", name)\n" +"\n" +"start_server(main, port=8080, debug=True)" +msgstr "" + +#: ../../guide.rst:652 +msgid "Now head over to http://127.0.0.1:8080/, and you should see your hello greeting." +msgstr "现在,在 http://127.0.0.1:8080/ 页面就会看到欢迎页面了。" + +#: ../../guide.rst:654 +msgid "By using ``debug=True`` to enable debug mode, the server will automatically reload if code changes." +msgstr "使用 ``debug=True`` 来开启debug模式,这时server会在检测到代码发生更改后进行重启。" + +#: ../../guide.rst:656 +msgid "" +"The `start_server() ` provide a remote access support, when enabled (by passing `remote_access=True` to " +"`start_server()`), you will get a public, shareable address for the current application, others can access your application in their browser via " +"this address. Because the processing happens on your device (as long as your device stays on!), you don't have to worry about any dependencies. " +"Using remote access makes it easy to temporarily share the application with others." +msgstr "" +"`start_server() ` 提供了对远程访问的支持,当开启远程访问后(通过在 `start_server()` 中传入 " +"`remote_access=True` 开启 ),你将会得到一个用于访问当前应用的临时的公网访问地址,其他任何人都可以使用此地址访问你的应用。远程接入可以很方便地将应用" +"临时分享给其他人。" + +#: ../../guide.rst:662 +msgid "" +"Another way to deploy PyWebIO application as web service is using `path_deploy() `. `path_deploy() ` is used to deploy the PyWebIO applications from a directory. Just define PyWebIO applications in python files under this directory, " +"and you can access them via the path in the URL. Refer to :ref:`platform module ` for more information." +msgstr "" +"将PyWebIO应用部署为web服务的另一种方式是使用 `path_deploy() ` 。`path_deploy() ` 可以从" +"一个目录中部署PyWebIO应用,只需要在该目录下的python文件中定义PyWebIO应用,就可以通过URL中的路径来访问这些应用了。" + +#: ../../guide.rst:669 +msgid "" +"Note that in Server mode, all functions from ``pywebio.input``, ``pywebio.output`` and ``pywebio.session`` modules can only be called in the " +"context of PyWebIO application functions. For example, the following code is **not allowed**::" +msgstr "" +"注意,在Server模式下, ``pywebio.input`` 、 ``pywebio.output`` 和 ``pywebio.session`` 模块内的函数仅能在任务函数上下文中进行调用。比如如下调用是 **" +"不被允许的** ::" + +#: ../../guide.rst:672 +msgid "" +"import pywebio\n" +"from pywebio.input import input\n" +"\n" +"port = input('Input port number:') # ❌ error\n" +"pywebio.start_server(my_task_func, port=int(port))" +msgstr "" + +#: ../../guide.rst:679 ../../guide.rst:696 +msgid "**Script mode**" +msgstr "**Script模式**" + +#: ../../guide.rst:681 +msgid "If you never call ``start_server()`` or ``path_deploy()`` in your code, then you are running PyWebIO application as script mode." +msgstr "如果你在代码中没有调用 ``start_server()`` 或 ``path_deploy()`` 函数,那么你就是以脚本模式在运行PyWebIO应用。" + +#: ../../guide.rst:683 +msgid "" +"In script mode, a web browser page will be open automatically when running to the first call to PyWebIO interactive functions, and all subsequent " +"PyWebIO interactions will take place on this page. When the script exit, the page will be inactive." +msgstr "" +"在脚本模式中,当首次运行到对PyWebIO交互函数的调用时,会自动打开浏览器的一个页面,后续的PyWebIO交互都会在这个页面上进行。当脚本运行结束,这个页面也将" +"不再有效。" + +#: ../../guide.rst:686 +msgid "" +"If the user closes the browser before the script exiting, then subsequent calls to PyWebIO's interactive functions will cause a `SessionException " +"` exception." +msgstr "" +"如果用户在脚本结束运行之前关闭了浏览器,那么之后会话内对于PyWebIO交互函数的调用将会引发一个 `SessionException ` 异常。" + +#: ../../guide.rst:692 +msgid "Concurrent" +msgstr "并发" + +#: ../../guide.rst:694 +msgid "PyWebIO can be used in a multi-threading environment." +msgstr "PyWebIO 支持在多线程环境中使用。" + +#: ../../guide.rst:698 +msgid "" +"In script mode, you can freely start new thread and call PyWebIO interactive functions in it. When all `non-daemonic `_ threads finish running, the script exits." +msgstr "" +"在 Script模式下,你可以自由地启动线程,并在其中调用PyWebIO的交互函数。当所有非 `Daemon线程 `_ 运行结束后,脚本退出。" + +#: ../../guide.rst:703 +msgid "" +"In server mode, if you need to use PyWebIO interactive functions in new thread, you need to use `pywebio.session.register_thread(thread) ` to register the new thread (so that PyWebIO can know which session the thread belongs to). If the PyWebIO interactive " +"function is not used in the new thread, no registration is required. Threads that are not registered with `register_thread(thread) ` calling PyWebIO's interactive functions will cause `SessionNotFoundException `." +msgstr "" +"Server模式下,如果需要在新创建的线程中使用PyWebIO的交互函数,需要手动调用 `register_thread(thread) ` 对新进程进行注" +"册(这样PyWebIO才能知道新创建的线程属于哪个会话)。如果新创建的线程中没有使用到PyWebIO的交互函数,则无需注册。没有使用 `register_thread(thread) " +"` 注册的线程不受会话管理,其调用PyWebIO的交互函数将会产生 `SessionNotFoundException ` 异常。" + +#: ../../guide.rst:710 +msgid "Example of using multi-threading in Server mode::" +msgstr "Server模式下多线程的使用示例::" + +#: ../../guide.rst:712 +msgid "" +"def show_time():\n" +" while True:\n" +" with use_scope(name='time', clear=True):\n" +" put_text(datetime.datetime.now())\n" +" time.sleep(1)\n" +"\n" +"def app():\n" +" t = threading.Thread(target=show_time)\n" +" register_thread(t)\n" +" put_markdown('## Clock')\n" +" t.start() # run `show_time()` in background\n" +"\n" +" # ❌ this thread will cause `SessionNotFoundException`\n" +" threading.Thread(target=show_time).start()\n" +"\n" +" put_text('Background task started.')\n" +"\n" +"\n" +"start_server(app, port=8080, debug=True)" +msgstr "" + +#: ../../guide.rst:736 +msgid "Close of session" +msgstr "会话的结束" + +#: ../../guide.rst:738 +msgid "" +"When user close the browser page, the session will be closed. After the browser page is closed, PyWebIO input function calls that have not yet " +"returned in the current session will cause `SessionClosedException `, and subsequent calls to PyWebIO " +"interactive functions will cause `SessionNotFoundException ` or `SessionClosedException `." +msgstr "" +"当用户关闭浏览器页面时,与之相应的会话也将被关闭。会话关闭后,应用中未返回的PyWebIO输入函数的调用将会抛出 `SessionClosedException ` 异常,后续对PyWebIO交互函数的调用将会引发 `SessionNotFoundException ` 或 `SessionClosedException ` 异常。" + +#: ../../guide.rst:743 +msgid "In most cases, you don't need to catch those exceptions, because let those exceptions to abort the running is the right way to exit." +msgstr "大部分情况下,你不需要捕获这些异常,让这些异常来终止代码的执行通常是比较合适的。" + +#: ../../guide.rst:745 +msgid "" +"You can use `pywebio.session.defer_call(func) ` to set the function to be called when the session closes. " +"`defer_call(func) ` can be used for resource cleaning. You can call `defer_call(func) ` " +"multiple times in the session, and the set functions will be executed sequentially after the session closes." +msgstr "" +"可以使用 `pywebio.session.defer_call(func) ` 来设置会话结束时需要调用的函数。无论是因为用户主动关闭页面还是任务结束使得" +"会话关闭,设置的函数都会被执行。`defer_call(func) ` 可以用于资源清理等工作。在会话中可以多次调用 `defer_call() ` ,会话结束后将会顺序执行设置的函数。" + +#: ../../guide.rst:751 +msgid "More about PyWebIO" +msgstr "" + +#: ../../guide.rst:752 +msgid "" +"By now, you already get the most important features of PyWebIO and can start to write awesome PyWebIO applications. However, there are some other " +"useful features we don't cover in the above. Here we just make a briefly explain about them. When you need them in your application, you can refer " +"to their document." +msgstr "" +"目前为止,你已经了解了PyWebIO中最重要的特性,并且可以开始编写PyWebIO应用了。然而,有些功能前面我们并没有覆盖到,这里提供了对剩余特性的一些简短介绍," +"如果你在应用编写过程中需要用到这里的某个特性,你可以查阅对应的详细文档。" + +#: ../../guide.rst:756 +msgid "Also, :doc:`here ` is a cookbook where you can find some useful code snippets for your PyWebIO application." +msgstr "另外,你可以在 :doc:`cookbook ` 页面找到一些对于编写PyWebIO应用很有帮助的代码片段。" + +#: ../../guide.rst:759 +msgid "``session`` module" +msgstr "``session`` 模块" + +#: ../../guide.rst:760 +msgid "The :doc:`pywebio.session ` module give you more control to session." +msgstr ":doc:`pywebio.session ` 模块提供了对会话的更多控制 。" + +#: ../../guide.rst:762 +msgid "Use `set_env() ` to configure the title, page appearance, input panel and so on for current session." +msgstr "使用 `set_env() ` 来为当前会话设置标题、页面外观、输入栏等内容。" + +#: ../../guide.rst:764 +msgid "" +"The `info ` object provides a lot information about the current session, such as the user IP address, user language and user " +"browser information." +msgstr "`info ` 对象提供了关于当前绘画的很多信息,比如用户IP地址、用户语言、用户浏览器信息等。" + +#: ../../guide.rst:767 +msgid "`local ` is a session-local storage, it used to save data whose values are session specific." +msgstr "`local ` 是一个session-local的存储对象, 用于存储会话独立的数据。" + +#: ../../guide.rst:769 +msgid "" +"`run_js() ` let you execute JavaScript code in user's browser, and `eval_js() ` let you execute " +"JavaScript expression and get the value of it." +msgstr "" +"`run_js() ` 让你在用户浏览器中执行JavaScript代码, `eval_js() ` 让你执行并获取JavaScript表达式的值。" + +#: ../../guide.rst:773 +msgid "``pin`` module" +msgstr "``pin`` 模块" + +#: ../../guide.rst:774 +msgid "" +"As you already know, the input function of PyWebIO is blocking and the input form will be destroyed after successful submission. In some cases, you " +"may want to make the input form not disappear after submission, and can continue to receive input. So PyWebIO provides the :doc:`pywebio.pin ` module to achieve persistent input by pinning input widgets to the page." +msgstr "" +"你已经知道,PyWebIO的输入函数是阻塞式的,并且输入表单会在成功提交后消失。在某些时候,你可能想要输入表单一直显示并可以持续性接收用户输入,这时你可以" +"使用 :doc:`pywebio.pin ` 模块。" + +#: ../../guide.rst:779 +msgid "``platform`` module" +msgstr "``platform`` 模块" + +#: ../../guide.rst:781 +msgid "The :doc:`pywebio.platform ` module provides support for deploying PyWebIO applications in different ways." +msgstr ":doc:`pywebio.platform ` 模块提供了将PyWebIO应用以多种方式部署的支持。" + +#: ../../guide.rst:783 +msgid "" +"There are two protocols (WebSocket and HTTP) can be used in server to communicates with the browser. The WebSocket is used by default. If you want " +"to use HTTP protocol, you can choose other ``start_server()`` functions in this module." +msgstr "" +"PyWebIO的服务端与浏览器可以通过两种协议(WebSocket 和 HTTP 协议)进行通信,默认使用WebSocket协议,如果你想使用HTTP协议,你可以选择本模块中的其他 " +"``start_server()`` 函数。" + +#: ../../guide.rst:786 +msgid "" +"You might want to set some web page related configuration (such as SEO information, js and css injection) for your PyWebIO application, `pywebio." +"config() ` can be helpful." +msgstr "如果要为PyWebIO应用设置一些网页相关的配置,可以尝试使用 `pywebio.config() ` 。" + +#: ../../guide.rst:790 +msgid "Advanced features" +msgstr "高级特性" + +#: ../../guide.rst:792 +msgid "" +"The PyWebIO application can be integrated into an existing Python web project, the PyWebIO application and the web project share a web framework. " +"Refer to :ref:`Advanced Topic: Integration with Web Framework ` for more information." +msgstr "" +"可以将PyWebIO应用整合到现存的Python Web项目中,PyWebIO应用和web项目使用一个web框架。详细信息参见 :ref:`Advanced Topic: Integration with Web " +"Framework ` 。" + +#: ../../guide.rst:796 +msgid "" +"PyWebIO also provides support for coroutine-based sessions. Refer to :ref:`Advanced Topic: Coroutine-based session ` for " +"more information." +msgstr "PyWebIO还支持基于协程的会话。具体参见 :ref:`Advanced Topic: Coroutine-based session ` 。" + +#: ../../guide.rst:799 +msgid "" +"If you try to bundles your PyWebIO application into a stand-alone executable file, to make users can run the application without installing a " +"Python interpreter or any modules, you might want to refer to :ref:`Libraries support: Build stand-alone App `" +msgstr "" +"如果你想要将PyWebIO应用打包到一个单独的可执行文件里面,从而使用户可以在没有安装python解释器的情况下运行应用,你可以参考 :ref:`Build stand-alone App " +"`" + +#: ../../guide.rst:802 +msgid "" +"If you want to make some data visualization in your PyWebIO application, you can't miss :ref:`Libraries support: Data visualization `" +msgstr "如果你想在PyWebIO应用中进行一些数据可视化,可以参考 :ref:`Data visualization `" + +#: ../../guide.rst:805 +msgid "Last but not least" +msgstr "" + +#: ../../guide.rst:807 +msgid "This is basically all features of PyWebIO, you can continue to read the rest of the documents, or start writing your PyWebIO applications now." +msgstr "以上基本就是PyWebIO的全部功能了,你可以继续阅读接下来的文档,或者立即开始PyWebIO应用的编写了。" + +#: ../../guide.rst:809 +msgid "" +"Finally, please allow me to provide one more suggestion. When you encounter a design problem when using PyWebIO, you can ask yourself a question: " +"What would I do if it is in a terminal program? If you already have the answer, it can be done in the same way with PyWebIO. If the problem " +"persists or the solution is not good enough, you can consider the :ref:`callback mechanism ` or :doc:`pin <./pin>` module." +msgstr "" +"最后再提供一条建议,当你在使用PyWebIO遇到设计上的问题时,可以问一下自己:如果在是在终端程序中我会怎么做?如果你已经有答案了,那么在PyWebIO中一样可以" +"使用这样的方式完成。如果问题依然存在或者觉得解决方案不够好,你可以考虑使用 :ref:`回调机制 ` 或 :doc:`pin <./pin>` 模块。" + +#: ../../guide.rst:814 +msgid "OK, Have fun with PyWebIO!" +msgstr "" + +#~ msgid "" +#~ "In the :ref:`Hello, world ` section, we already know that PyWebIO supports two modes: running as a script and using `start_server() " +#~ "` to run as a web service." +#~ msgstr "" +#~ "在 :ref:`Hello, world ` 一节中,已经知道,PyWebIO支持在普通的脚本中调用和使用 `start_server() ` 启动一个Web服务两种模式。" + +#~ msgid "" +#~ "Other output functions that accept ``put_xxx()`` calls as parameters are `put_collapse() `, `put_scrollable() " +#~ "`, `put_row() `, etc. In addition, you can use `put_widget() ` " +#~ "to make your own output widgets that can accept ``put_xxx()`` calls. For more information, please refer to corresponding function documentation." +#~ msgstr "" +#~ "其他接受 ``put_xxx()`` 调用作为参数的输出函数还有 `put_collapse() ` 、 `put_scrollable() ` 、`put_row() ` 等,此外,还可以通过 `put_widget() ` 自定义可接收 " +#~ "``put_xxx()`` 调用的输出组件,具体用法请参考函数文档。" + +#~ msgid "" +#~ "In general, using the various output functions introduced above is enough to output what you want, but these outputs are arranged vertically. If " +#~ "you want to make a more complex layout (such as displaying a code block on the left side of the page and an image on the right), you need to use " +#~ "layout functions." +#~ msgstr "" +#~ "一般情况下,使用上文介绍的各种输出函数足以完成各种内容的展示,但直接调用输出函数产生的输出之间都是竖直排列的,如果想实现更复杂的布局(比如在页面" +#~ "左侧显示一个代码块,在右侧显示一个图像),就需要借助布局函数。" + +#~ msgid "Need to add a ``RequestHandler`` to Tornado application::" +#~ msgstr "需要在Tornado应用中引入一个 ``RequestHandler`` ::" + +#~ msgid "One route need to be added to communicate with the browser through HTTP::" +#~ msgstr "需要添加一个PyWebIO相关的路由,用来和浏览器进行Http通讯::" + +#~ msgid "Need to add a route in ``urls.py``::" +#~ msgstr "在django的路由配置文件 ``urls.py`` 中加入PyWebIO相关的路由即可::" + +#~ msgid "One route need to be added to communicate with the browser through WebSocket::" +#~ msgstr "需要添加一个PyWebIO相关的路由,用来和浏览器进行WebSocket通讯::" + +#~ msgid "" +#~ "See also: `FastAPI doc `_ , `Starlette doc `_" +#~ msgstr "" +#~ "参见: `FastAPI doc `_ , `Starlette doc `_" + +#~ msgid "" +#~ "`style() ` also accepts a list of output calls, `style() ` will set the CSS style for each item of " +#~ "the list:" +#~ msgstr "``style()`` 也接受列表作为输入,``style()`` 会为列表的每一项都设置CSS样式,返回值可以直接输出,可用于任何接受 ``put_xxx()`` 列表的地方:" + +#~ msgid "Use `pywebio.platform.tornado.webio_handler()` to get the ``RequestHandler`` class for running PyWebIO applications in Tornado::" +#~ msgstr "使用 `pywebio.platform.tornado.webio_handler()` 来获得在Tornado中运行PyWebIO应用的 ``RequestHandler`` 类::" + +#~ msgid "" +#~ "In above code, we use `webio_handler(task_func) ` to get the Tornado `WebSocketHandler `_ that communicates with the browser, and bind it to the ``/tool`` " +#~ "path. After starting the Tornado server, you can visit ``http://localhost/tool`` to open the PyWebIO application." +#~ msgstr "" +#~ "以上代码调用 `webio_handler(task_func) ` 来获得PyWebIO和浏览器进行通讯的Tornado `WebSocketHandler " +#~ "`_ ,并将其绑定在 ``/tool`` 路由下。启动Tornado服务器后," +#~ "访问 ``http://localhost/tool`` 即可打开PyWebIO应用。" + +#~ msgid "" +#~ "In above code, we use `webio_view(task_func) ` to get the Flask view of the PyWebIO application, and bind it " +#~ "to ``/tool`` path. After starting the Flask application, visit ``http://localhost/tool`` to open the PyWebIO application." +#~ msgstr "" +#~ "以上代码使用 `webio_view(task_func) ` 来获得运行PyWebIO应用的Flask视图 ,并调用 `Flask.add_url_rule `_ 将其绑定在 ``/tool`` 路径下。启动Flask应用后,访问 ``http://localhost/tool`` " +#~ "即可打开PyWebIO应用。" + +#~ msgid "" +#~ "The `start_server() ` provide a remote access support, when enabled (by passing `remote_access=True` to " +#~ "`start_server()`), you can get a temporary public network access address for the current application, others can access your application via " +#~ "this address. Using remote access makes it easy to temporarily share the application with others. This service is powered by `localhost.run " +#~ "`_." +#~ msgstr "" +#~ "`start_server() ` 提供了远程访问的支持,当开启远程访问后(通过在 `start_server()` 中传入 " +#~ "`remote_access=True` 开启 ),你将会得到一个用于访问当前应用的临时的公网访问地址,其他人任何都可以使用此地址访问你的应用。远程接入可以很方便地将" +#~ "应用临时分享给其他人。当前远程接入功能由 `localhost.run `_ 提供。" + +#~ msgid "" +#~ "After the PyWebIO session (see :ref:`Server and script mode ` for more information about session) closed, the event " +#~ "callback will not work. You can call the :func:`pywebio.session.hold()` function at the end of the task function to hold the session, so that " +#~ "the event callback will always be available before the browser page is closed by user." +#~ msgstr "" +#~ "在PyWebIO会话(关于会话的概念见下文 :ref:`Server与script模式 ` )结束后,事件回调也将不起作用,你可以在任务函数末尾处使用 :" +#~ "func:`pywebio.session.hold()` 函数来将会话保持,这样在用户关闭浏览器页面前,事件回调将一直可用。" + +#~ msgid "" +#~ "PyWebIO uses the scope model to give more control to the location of content output. The output area of PyWebIO can be divided into different " +#~ "output domains. The output domain is called Scope in PyWebIO." +#~ msgstr "PyWebIO使用Scope模型来对内容输出的位置进行灵活地控制,PyWebIO的内容输出区可以划分出不同的输出域,PyWebIO将输出域称作 `Scope` 。" + +#~ msgid "" +#~ "The output domain is a container of output content, and each output domain is arranged vertically, and the output domains can also be nested." +#~ msgstr "输出域为输出内容的容器,各个输出域之间上下排列,输出域也可以进行嵌套。" + +#~ msgid "**Scope related parameters of output function**" +#~ msgstr "**输出函数的scope相关参数**" + +#~ msgid "" +#~ "The output function (function name like ``put_xxx()``) will output the content to the \"current scope\" by default, and the \"current scope\" of " +#~ "the runtime context can be set by ``use_scope()``." +#~ msgstr "输出函数(函数名形如 ``put_xxx()`` )在默认情况下,会将内容输出到\"当前Scope\",可以通过 ``use_scope()`` 设置运行时上下文的\"当前Scope\"。" + +#~ msgid "In addition, you can use the ``scope`` parameter of the output function to specify the destination scope to output:" +#~ msgstr "此外,也可以通过输出函数的 ``scope`` 参数指定输出的目的Scope:" + +#~ msgid "" +#~ "In addition to directly specifying the target scope name, the ``scope`` parameter can also accept an integer to determine the scope by indexing " +#~ "the scope stack: 0 means the top level scope(the ROOT Scope), -1 means the current scope, -2 means the scope used before entering the current " +#~ "scope, ..." +#~ msgstr "" +#~ "``scope`` 参数除了直接指定目标Scope名,还可以使用一个整形通过索引Scope栈来确定Scope:0表示最顶层也就是ROOT Scope,-1表示当前Scope,-2表示进入当前" +#~ "Scope前所使用的Scope,……" + +#~ msgid "" +#~ "By default, the content output to the same scope will be arranged from top to bottom according to the calling order of the output function. The " +#~ "output content can be inserted into other positions of the target scope by using the ``position`` parameter of the output function." +#~ msgstr "" +#~ "默认条件下,在同一Scope中的输出内容,会根据输出函数的调用顺序从上往下排列,最后调用的输出函数会输出内容到目标Scope的底部。通过输出函数的 " +#~ "``position`` 参数可以将输出内容插入到目标Scope的其他位置。" + +#~ msgid "" +#~ "Each output item in a scope has an index, the first item's index is 0, and the next item's index is incremented by one. You can also use a " +#~ "negative number to index the items in the scope, -1 means the last item, -2 means the item before the last..." +#~ msgstr "" +#~ "一个Scope中各次输出的元素具有像数组一样的索引,最前面的编号为0,以此往后递增加一;同样可以使用负数对Scope中的元素进行索引,-1表示最后面的元素,-2" +#~ "表示次后面的元素……" + +#~ msgid "" +#~ "The ``position`` parameter of output functions accepts an integer. When ``position>=0``, it means to insert content before the item whose index " +#~ "equal ``position``; when ``position<0``, it means to insert content after the item whose index equal ``position``:" +#~ msgstr "" +#~ "``position`` 参数类型为整形, ``position>=0`` 时表示输出内容到目标Scope的第position号元素的前面; ``position<0`` 时表示输出内容到目标Scope第" +#~ "position号元素之后:" + +#~ msgid "Page environment settings" +#~ msgstr "页面环境设置" + +#~ msgid "**Page Title**" +#~ msgstr "**页面标题**" + +#~ msgid "You can call `set_env(title=...) ` to set the page title。" +#~ msgstr "调用 `set_env(title=…) ` 可以设置页面标题。" + +#~ msgid "**Auto Scroll**" +#~ msgstr "**自动滚动**" + +#~ msgid "" +#~ "When performing some continuous output (such as log output), you may want to scroll the page to the bottom automatically when there is new " +#~ "output. You can call `set_env(auto_scroll_bottom=True) ` to enable automatic scrolling. Note that when enabled, only " +#~ "outputting to ROOT scope can trigger automatic scrolling." +#~ msgstr "" +#~ "在进行一些持续性的输出时(比如日志输出),有时希望在有新输出后自动将页面滚动到最下方,这时可以调用 `set_env(auto_scroll_bottom=True) ` 来开启自动滚动。\n" +#~ "注意,开启后,只有输出到ROOT Scope才可以触发自动滚动。" + +#~ msgid "**Output Animation**" +#~ msgstr "**输出动画**" + +#~ msgid "" +#~ "By default, PyWebIO will use the fade-in animation effect to display the content. You can use `set_env(output_animation=False) ` to turn off the animation." +#~ msgstr "PyWebIO在输出内容时默认会使用淡入的动画效果来显示内容,可使用 `set_env(output_animation=False) ` 来关闭动画。" + +#~ msgid "To view the effects of environment settings, please visit :demo_host:`set_env Demo `" +#~ msgstr "有关不同环境配置的效果可查看 :demo_host:`set_env Demo `" + +#~ msgid "Server mode and Script mode" +#~ msgstr "Server模式与Script模式" + +#~ msgid "" +#~ "Use `start_server() ` to start a web server and serve given PyWebIO applications on it. `start_server() " +#~ "` accepts a function as PyWebIO application. In addition, `start_server() ` also accepts a list of task function or a dictionary of it, so one PyWebIO Server can have multiple services with different " +#~ "functions. You can use `go_app() ` or `put_link() ` to jump between services::" +#~ msgstr "" +#~ "使用 `start_server() ` 启动一个Web Server来将PyWebIO应用作为Web服务运行, `start_server() ` 可以接收一个函数作为PyWebIO应用;\n" +#~ "也支持使用函数列表或字典,从而使一个PyWebIO Server下可以有多个不同功能的服务,服务之间可以通过 `go_app() ` 或 " +#~ "`put_link() ` 进行跳转::" + +#~ msgid "" +#~ "def task_1():\n" +#~ " put_text('task_1')\n" +#~ " put_buttons(['Go task 2'], [lambda: go_app('task_2')])\n" +#~ " hold()\n" +#~ "\n" +#~ "def task_2():\n" +#~ " put_text('task_2')\n" +#~ " put_buttons(['Go task 1'], [lambda: go_app('task_1')])\n" +#~ " hold()\n" +#~ "\n" +#~ "def index():\n" +#~ " put_link('Go task 1', app='task_1') # Use `app` parameter to specify the task name\n" +#~ " put_link('Go task 2', app='task_2')\n" +#~ "\n" +#~ "# equal to `start_server({'index': index, 'task_1': task_1, 'task_2': task_2})`\n" +#~ "start_server([index, task_1, task_2])" +#~ msgstr "" +#~ "def task_1():\n" +#~ " put_text('task_1')\n" +#~ " put_buttons(['Go task 2'], [lambda: go_app('task_2')])\n" +#~ " hold()\n" +#~ "\n" +#~ "def task_2():\n" +#~ " put_text('task_2')\n" +#~ " put_buttons(['Go task 1'], [lambda: go_app('task_1')])\n" +#~ " hold()\n" +#~ "\n" +#~ "def index():\n" +#~ " put_link('Go task 1', app='task_1') # 使用app参数指定任务名\n" +#~ " put_link('Go task 2', app='task_2')\n" +#~ "\n" +#~ "# 等价于 start_server({'index': index, 'task_1': task_1, 'task_2': task_2})\n" +#~ "start_server([index, task_1, task_2])" + +#~ msgid "" +#~ "Use `path_deploy() ` to deploy the PyWebIO applications from a directory. The python file under this directory " +#~ "need contain the ``main`` function to be seen as the PyWebIO application. You can access the application by using the file path as the URL." +#~ msgstr "" +#~ "使用 `path_deploy() ` 可以从一个路径中部署PyWebIO应用。位于该路径下的python文件需要包含名字为 ``main`` 的PyWebIO任" +#~ "务函数才能被视为PyWebIO应用程序。服务端会根据用户访问的URL来确定需要加载的文件并从中读取PyWebIO应用来运行。" + +#~ msgid "For example, given the following folder structure::" +#~ msgstr "例如,给定如下文件结构::" + +#~ msgid "" +#~ "If you use this directory in `path_deploy() `, you can access the PyWebIO application in ``b.py`` by using URL " +#~ "``http://:/A/b``. And if the files have been modified after run `path_deploy() `, you can use " +#~ "``reload`` URL parameter to reload application in the file: ``http://:/A/b?reload``" +#~ msgstr "" +#~ "如果使用以上路径调用 `path_deploy() ` ,你可以通过 URL ``http://:/A/b`` 来访问 ``b.py`` 文件中的PyWebIO" +#~ "应用。当文件在运行 `path_deploy() ` 之后被修改,可以使用 ``reload`` URL参数来重载文件: ``http://:/A/b?" +#~ "reload``" + +#~ msgid "" +#~ "You can also use the command ``pywebio-path-deploy`` to start a server just like using `path_deploy() `. For more " +#~ "information, refer ``pywebio-path-deploy --help``" +#~ msgstr "" +#~ "你还可以使用 ``pywebio-path-deploy`` 命令来启动一个和 `path_deploy() ` 效果一样的server。关于命令的更多信息请查阅命" +#~ "令帮助: ``pywebio-path-deploy --help``" + +#~ msgid "" +#~ "In Server mode, you can use `pywebio.platform.seo()` to set the `SEO `_ information. " +#~ "If ``seo()`` is not used, the `docstring `_ of the task function will be regarded as SEO information " +#~ "by default." +#~ msgstr "" +#~ "在Server模式下,可以使用 `pywebio.platform.seo()` 函数来设置任务函数SEO信息(在被搜索引擎索引时提供的网页信息,包含应用标题和应用简介),如果不使" +#~ "用 ``seo()`` 函数,默认条件下,PyWebIO会将任务函数的函数注释作为SEO信息(应用标题和简介之间使用一个空行分隔)。 " + +#~ msgid "In Script mode, PyWebIO input and output functions can be called anywhere." +#~ msgstr "Script模式下,在任何位置都可以调用PyWebIO的交互函数。" + +#~ msgid "" +#~ "The close of session may also be caused by the user closing the browser page. After the browser page is closed, PyWebIO input function calls " +#~ "that have not yet returned in the current session will cause `SessionClosedException `, and " +#~ "subsequent calls to PyWebIO interactive functions will cause `SessionNotFoundException ` or " +#~ "`SessionClosedException `." +#~ msgstr "" +#~ "会话还会因为用户的关闭浏览器而结束,这时当前会话内还未返回的PyWebIO输入函数调用将抛出 `SessionClosedException ` 异常,之后对于PyWebIO交互函数的调用将会产生 `SessionNotFoundException ` 或 " +#~ "`SessionClosedException ` 异常。" + +#~ msgid "" +#~ "You can use `defer_call(func) ` to set the function to be called when the session closes. Whether it is because the " +#~ "user closes the page or the task finishes to cause session closed, the function set by `defer_call(func) ` will be " +#~ "executed. `defer_call(func) ` can be used for resource cleaning. You can call `defer_call(func) ` multiple times in the session, and the set functions will be executed sequentially after the session closes." +#~ msgstr "" +#~ "可以使用 `defer_call(func) ` 来设置会话结束时需要调用的函数。无论是因为用户主动关闭页面还是任务结束使得会话关闭,设置" +#~ "的函数都会被执行。\n" +#~ "`defer_call(func) ` 可以用于资源清理等工作。在会话中可以多次调用 `defer_call() ` ,会话结" +#~ "束后将会顺序执行设置的函数。" + +#~ msgid "Integration with web framework" +#~ msgstr "与Web框架集成" + +#~ msgid "" +#~ "The PyWebIO application can be integrated into an existing Python Web project, the PyWebIO application and the Web project share a web " +#~ "framework. PyWebIO currently supports integration with Flask, Tornado, Django, aiohttp and FastAPI(Starlette) web frameworks." +#~ msgstr "" +#~ "可以将PyWebIO应用集成到现有的Python Web项目中,PyWebIO应用与Web项目共用一个Web框架。目前支持与Flask、Tornado、Django、aiohttp和" +#~ "FastAPI(Starlette) Web框架的集成。" + +#~ msgid "The integration methods of those web frameworks are as follows:" +#~ msgstr "不同Web框架的集成方法如下: " + +#~ msgid "" +#~ "Use `pywebio.platform.tornado.webio_handler()` to get the `WebSocketHandler `_ class for running PyWebIO applications in Tornado::" +#~ msgstr "" +#~ "使用 `pywebio.platform.tornado.webio_handler()` 来获取在Tornado中运行PyWebIO应用的 `WebSocketHandler `_ 类::" + +#~ msgid "" +#~ "In above code, we add a routing rule to bind the ``WebSocketHandler`` of the PyWebIO application to the ``/tool`` path. After starting the " +#~ "Tornado server, you can visit ``http://localhost/tool`` to open the PyWebIO application." +#~ msgstr "" +#~ "以上代码将 PyWebIO 应用的 ``WebSocketHandler`` 绑定到了 ``/tool`` 路径下。 启动Tornado后,访问 ``http://localhost/tool``即可打开PyWebIO应用。" + +#~ msgid "" +#~ "PyWebIO uses the WebSocket protocol to communicate with the browser in Tornado. If your Tornado application is behind a reverse proxy (such as " +#~ "Nginx), you may need to configure the reverse proxy to support the WebSocket protocol. :ref:`Here ` is an example of Nginx " +#~ "WebSocket configuration." +#~ msgstr "" +#~ "当使用Tornado后端时,PyWebIO使用WebSocket协议和浏览器进行通讯,如果你的Tornado应用处在反向代理(比如Nginx)之后,可能需要特别配置反向代理来支持" +#~ "WebSocket协议,:ref:`这里 ` 有一个Nginx配置WebSocket的例子。" + +#~ msgid "Use `pywebio.platform.flask.webio_view()` to get the view function for running PyWebIO applications in Flask::" +#~ msgstr "使用 `pywebio.platform.flask.webio_view()` 来获取在Flask中运行PyWebIO应用的视图函数::" + +#~ msgid "" +#~ "In above code, we add a routing rule to bind the view function of the PyWebIO application to the ``/tool`` path. After starting the Flask " +#~ "application, visit ``http://localhost/tool`` to open the PyWebIO application." +#~ msgstr "" +#~ "以上代码使用添加了一条路由规则将PyWebIO应用的视图函数绑定到 ``/tool`` 路径下。\n" +#~ "启动Flask应用后,访问 ``http://localhost/tool`` 即可打开PyWebIO应用" + +#~ msgid "Use `pywebio.platform.django.webio_view()` to get the view function for running PyWebIO applications in Django::" +#~ msgstr "使用 `pywebio.platform.django.webio_view()` 来获取在Django中运行PyWebIO应用的视图函数::" + +#~ msgid "" +#~ "In above code, we add a routing rule to bind the view function of the PyWebIO application to the ``/tool`` path. After starting the Django " +#~ "server, visit ``http://localhost/tool`` to open the PyWebIO application" +#~ msgstr "" +#~ "以上代码使用添加了一条路由规则将PyWebIO应用的视图函数绑定到 ``/tool`` 路径下。\n" +#~ "启动Django应用后,访问 ``http://localhost/tool`` 即可打开PyWebIO应用" + +#~ msgid "" +#~ "Use `pywebio.platform.aiohttp.webio_handler()` to get the `Request Handler `_ coroutine for running PyWebIO applications in aiohttp::" +#~ msgstr "" +#~ "使用 `pywebio.platform.aiohttp.webio_handler()` 来获取在aiohttp中运行PyWebIO应用的 `Request Handler `_ 协程::" + +#~ msgid "After starting the aiohttp server, visit ``http://localhost/tool`` to open the PyWebIO application" +#~ msgstr "启动aiohttp应用后,访问 ``http://localhost/tool`` 即可打开PyWebIO应用" + +#~ msgid "" +#~ "PyWebIO uses the WebSocket protocol to communicate with the browser in aiohttp. If your aiohttp server is behind a reverse proxy (such as " +#~ "Nginx), you may need to configure the reverse proxy to support the WebSocket protocol. :ref:`Here ` is an example of Nginx " +#~ "WebSocket configuration." +#~ msgstr "" +#~ "当使用aiohttp后端时,PyWebIO使用WebSocket协议和浏览器进行通讯,如果你的aiohttp应用处在反向代理(比如Nginx)之后,\n" +#~ "可能需要特别配置反向代理来支持WebSocket协议,:ref:`这里 ` 有一个Nginx配置WebSocket的例子。" + +#~ msgid "" +#~ "Use `pywebio.platform.fastapi.webio_routes()` to get the FastAPI/Starlette routes for running PyWebIO applications. You can mount the routes to " +#~ "your FastAPI/Starlette app." +#~ msgstr "" +#~ "使用 `pywebio.platform.fastapi.webio_routes()` 来获取在FastAPI/Starlette中运行PyWebIO应用的路由组件,你可以将其挂载在到FastAPI/Starlette应用中。" + +#~ msgid "After starting the server by using ``uvicorn :app`` , visit ``http://localhost:8000/tool/`` to open the PyWebIO application" +#~ msgstr "使用 ``uvicorn :app` 启动server后,访问 ``http://localhost:8000/tool/`` 即可打开PyWebIO应用" + +#~ msgid "" +#~ "PyWebIO uses the WebSocket protocol to communicate with the browser in FastAPI/Starlette. If your server is behind a reverse proxy (such as " +#~ "Nginx), you may need to configure the reverse proxy to support the WebSocket protocol. :ref:`Here ` is an example of Nginx " +#~ "WebSocket configuration." +#~ msgstr "" +#~ "当使用FastAPI或Starlette后端时,PyWebIO使用WebSocket协议和浏览器进行通讯,如果你的aiohttp应用处在反向代理(比如Nginx)之后,\n" +#~ "可能需要特别配置反向代理来支持WebSocket协议,:ref:`这里 ` 有一个Nginx配置WebSocket的例子。" + +#~ msgid "**Deployment in production**" +#~ msgstr "**生产环境部署**" + +#~ msgid "" +#~ "In your production system, you may want to deploy the web applications with some WSGI/ASGI servers such as uWSGI, Gunicorn, and Uvicorn. Since " +#~ "PyWebIO applications store session state in memory of process, when you use HTTP-based sessions (Flask and Django) and spawn multiple workers to " +#~ "handle requests, the request may be dispatched to a process that does not hold the session to which the request belongs. So you can only start " +#~ "one worker to handle requests when using Flask or Django backend." +#~ msgstr "" +#~ "在生产环境中,你可能会使用一些 WSGI/ASGI 服务器(如 uWSGI、Gunicorn、Uvicorn)部署 Web 应用程序。由于 PyWebIO 应用程序会在进程中存储会话状态,当" +#~ "使用基于 HTTP 的会话(使用Flask 和 Django后端时)并生成多个进程来处理请求时,请求可能会被分发到错误的进程中。因此,在使用基于 HTTP 的会话时,只能" +#~ "启动一个进程来处理请求。" + +#~ msgid "" +#~ "If you still want to use multiple processes to increase concurrency, one way is to use Uvicorn+FastAPI, or you can also start multiple Tornado/" +#~ "aiohttp processes and add external load balancer (such as HAProxy or nginx) before them. Those backends use the WebSocket protocol to " +#~ "communicate with the browser in PyWebIO, so there is no the issue as described above." +#~ msgstr "" +#~ "如果仍然希望使用多进程来提高并发,一种方式是使用 Uvicorn+FastAPI,或者你也可以启动多个Tornado/aiohttp进程,并在它们之前添加外部的负载均衡软件" +#~ "(如 HAProxy 或 nginx)。这些后端使用 WebSocket 协议与浏览器进行通信,所以不存在上述问题。" + +#~ msgid "**Static resources Hosting**" +#~ msgstr "**PyWebIO静态资源的托管**" + +#~ msgid "" +#~ "By default, the front-end of PyWebIO gets required static resources from CDN. If you want to deploy PyWebIO applications in an offline " +#~ "environment, you need to host static files by yourself, and set the ``cdn`` parameter of ``webio_view()`` or ``webio_handler()`` to ``False``." +#~ msgstr "" +#~ "PyWebIO默认使用CDN来获取前端的静态资源,如果要将PyWebIO应用部署到离线环境中,需要自行托管静态文件,\n" +#~ "并将 ``webio_view()`` 或 ``webio_handler()`` 的 ``cdn`` 参数设置为 ``False`` 。" + +#~ msgid "" +#~ "When setting ``cdn=False`` , you need to host the static resources in the same directory as the PyWebIO application. In addition, you can also " +#~ "pass a string to ``cdn`` parameter to directly set the URL of PyWebIO static resources directory." +#~ msgstr "" +#~ "``cdn=False`` 时需要将静态资源托管在和PyWebIO应用同级的目录下。\n" +#~ "同时,也可以通过 ``cdn`` 参数直接设置PyWebIO静态资源的URL目录。" + +#~ msgid "" +#~ "The path of the static file of PyWebIO is stored in ``pywebio.STATIC_PATH``, you can use the command ``python3 -c \"import pywebio; " +#~ "print(pywebio.STATIC_PATH)\"`` to print it out." +#~ msgstr "" +#~ "PyWebIO的静态文件的路径保存在 ``pywebio.STATIC_PATH`` 中,可使用命令 ``python3 -c \"import pywebio; print(pywebio.STATIC_PATH)\"`` 将其打印出来。" + +#~ msgid "" +#~ "``start_server()`` and ``path_deploy()`` also support ``cdn`` parameter, if it is set to ``False``, the static resource will be hosted in local " +#~ "server automatically, without manual hosting." +#~ msgstr "使用 ``start_server()`` 启动的应用,如果将 ``cdn`` 参数设置为 ``False`` ,会自动启动一个本地的静态资源托管服务,无需手动托管。" + +#~ msgid "Coroutine-based session" +#~ msgstr "基于协程的会话" + +#~ msgid "" +#~ "This section will introduce the advanced features of PyWebIO --- coroutine-based session. In most cases, you don’t need it. All functions or " +#~ "methods in PyWebIO that are only used for coroutine sessions are specifically noted in the document." +#~ msgstr "关于协程内容属于高级特性,您不必使用此部分也可以实现PyWebIO支持的全部功能。PyWebIO中所有仅用于协程会话的函数或方法都在文档中有特别说明。" + +#~ msgid "" +#~ "PyWebIO's session is based on thread by default. Each time a user opens a session connection to the server, PyWebIO will start a thread to run " +#~ "the task function. In addition to thread-based sessions, PyWebIO also provides coroutine-based sessions. Coroutine-based sessions accept " +#~ "coroutine functions as task functions." +#~ msgstr "" +#~ "PyWebIO的会话实现默认是基于线程的,用户每打开一个和服务端的会话连接,PyWebIO会启动一个线程来运行任务函数。\n" +#~ "除了基于线程的会话,PyWebIO还提供了基于协程的会话。基于协程的会话接受协程函数作为任务函数。" + +#~ msgid "" +#~ "The session based on the coroutine is a single-thread model, which means that all sessions run in a single thread. For IO-bound tasks, " +#~ "coroutines take up fewer resources than threads and have performance comparable to threads. In addition, the context switching of the coroutine " +#~ "is predictable, which can reduce the need for program synchronization and locking, and can effectively avoid most critical section problems." +#~ msgstr "" +#~ "基于协程的会话为单线程模型,所有会话都运行在一个线程内。对于IO密集型的任务,协程比线程占用更少的资源同时又拥有媲美于线程的性能。\n" +#~ "另外,协程的上下文切换具有可预测性,能够减少程序同步与加锁的需要,可以有效避免大多数临界区问题。" + +#~ msgid "Using coroutine session" +#~ msgstr "使用协程会话" + +#~ msgid "" +#~ "To use coroutine-based session, you need to use the ``async`` keyword to declare the task function as a coroutine function, and use the " +#~ "``await`` syntax to call the PyWebIO input function:" +#~ msgstr "要使用基于协程的会话,需要使用 ``async`` 关键字将任务函数声明为协程函数,并使用 ``await`` 语法调用PyWebIO输入函数:" + +#~ msgid "" +#~ "In the coroutine task function, you can also use ``await`` to call other coroutines or ( `awaitable objects `_ ) in the standard library `asyncio `_:" +#~ msgstr "" +#~ "在协程任务函数中,也可以使用 ``await`` 调用其他协程或标准库 `asyncio `_ 中的可等待对象" +#~ "( `awaitable objects `_ ):" + +#~ msgid "" +#~ "In coroutine-based session, all input functions defined in the :doc:`pywebio.input ` module need to use ``await`` syntax to get the " +#~ "return value. Forgetting to use ``await`` will be a common error when using coroutine-based session." +#~ msgstr "" +#~ "在基于协程的会话中, :doc:`pywebio.input ` 模块中的定义输入函数都需要使用 ``await`` 语法来获取返回值,忘记使用 ``await`` 将会是在使用基于" +#~ "协程的会话时常出现的错误。" + +#~ msgid "Other functions that need to use ``await`` syntax in the coroutine session are:" +#~ msgstr "其他在协程会话中也需要使用 ``await`` 语法来进行调用函数有:" + +#~ msgid "" +#~ "Although the PyWebIO coroutine session is compatible with the ``awaitable objects`` in the standard library ``asyncio``, the ``asyncio`` library " +#~ "is not compatible with the ``awaitable objects`` in the PyWebIO coroutine session." +#~ msgstr "" +#~ "虽然PyWebIO的协程会话兼容标准库 ``asyncio`` 中的 ``awaitable objects`` ,但 ``asyncio`` 库不兼容PyWebIO协程会话中的 ``awaitable objects`` ." + +#~ msgid "" +#~ "That is to say, you can't pass PyWebIO ``awaitable objects`` to the ``asyncio`` functions that accept ``awaitable objects``. For example, the " +#~ "following calls are **not supported** ::" +#~ msgstr "" +#~ "也就是说,无法将PyWebIO中的 ``awaitable objects`` 传入 ``asyncio`` 中的接受 ``awaitable objects`` 作为参数的函数中,比如如下调用是 **不被支持的" +#~ "** ::" + +#~ msgid "Concurrency in coroutine-based sessions" +#~ msgstr "协程会话的并发" + +#~ msgid "" +#~ "In coroutine-based session, you can start new thread, but you cannot call PyWebIO interactive functions in it (`register_thread() ` is not available in coroutine session). But you can use `run_async(coro) ` to execute a " +#~ "coroutine object asynchronously, and PyWebIO interactive functions can be used in the new coroutine:" +#~ msgstr "" +#~ "在基于协程的会话中,你可以启动线程,但是无法在其中调用PyWebIO交互函数( `register_thread() ` 在协程会话中不可" +#~ "用)。\n" +#~ "但你可以使用 `run_async(coro) ` 来异步执行一个协程对象,新协程内可以使用PyWebIO交互函数:" + +#~ msgid "" +#~ "`run_async(coro) ` returns a `TaskHandler `, which can be used to query " +#~ "the running status of the coroutine or close the coroutine." +#~ msgstr "" +#~ "`run_async(coro) ` 返回一个 `TaskHandler ` ,通过该 `TaskHandler ` 可以查询协程运行状态和关闭协程。" + +#~ msgid "" +#~ "Similar to thread-based session, in coroutine-based session, when the task function and the coroutine running through `run_async() ` in the session are all finished, the session is closed." +#~ msgstr "" +#~ "与基于线程的会话类似,在基于协程的会话中,当任务函数和在会话内通过 `run_async() ` 运行的协程全部结束后,会话关闭。" + +#~ msgid "" +#~ "If the close of the session is caused by the user closing the browser, the behavior of PyWebIO is the same as :ref:`Thread-based session " +#~ "`: After the browser page closed, PyWebIO input function calls that have not yet returned in the current session will cause " +#~ "`SessionClosedException `, and subsequent calls to PyWebIO interactive functions will cause " +#~ "`SessionNotFoundException ` or `SessionClosedException `." +#~ msgstr "" +#~ "对于因为用户的关闭浏览器而造成的会话结束,处理逻辑和 :ref:`基于线程的会话 ` 一致:\n" +#~ "此时当前会话内还未返回的PyWebIO输入函数调用将抛出 `SessionClosedException ` 异常,之后对于PyWebIO交互" +#~ "函数的调用将会产生 `SessionNotFoundException ` 或 `SessionClosedException ` 异常。" + +#~ msgid "`defer_call(func) ` also available in coroutine session." +#~ msgstr "协程会话也同样支持使用 `defer_call(func) ` 来设置会话结束时需要调用的函数。" + +#~ msgid "Integration with Web Framework" +#~ msgstr "协程会话与Web框架集成" + +#~ msgid "The PyWebIO application that using coroutine-based session can also be integrated to the web framework." +#~ msgstr "基于协程的会话同样可以与Web框架进行集成,只需要在原来传入任务函数的地方改为传入协程函数即可。" + +#~ msgid "However, there are some limitations when using coroutine-based sessions to integrate into Flask or Django:" +#~ msgstr "但当前在使用基于协程的会话集成进Flask或Django时,存在一些限制:" + +#~ msgid "" +#~ "First, when ``await`` the coroutine objects/awaitable objects in the ``asyncio`` module, you need to use `run_asyncio_coroutine() ` to wrap the coroutine object." +#~ msgstr "" +#~ "一是协程函数内还无法直接通过 ``await`` 直接等待asyncio库中的协程对象,目前需要使用 `run_asyncio_coroutine() ` 进行包装。" + +#~ msgid "Secondly, you need to start a new thread to run the event loop before starting a Flask/Django server." +#~ msgstr "二是,在启动Flask/Django这类基于线程的服务器之前需要启动一个单独的线程来运行事件循环。" + +#~ msgid "Example of coroutine-based session integration into Flask:" +#~ msgstr "使用基于协程的会话集成进Flask的示例:" + +#~ msgid "" +#~ "Finally, coroutine-based session is not available in the script mode. You always need to use ``start_server()`` to run coroutine task function " +#~ "or integrate it to a web framework." +#~ msgstr "最后,使用PyWebIO编写的协程函数不支持Script模式,总是需要使用 ``start_server`` 来启动一个服务或者集成进Web框架来调用。" + +#~ msgid "" +#~ "You can use ``code`` parameter in :func:`pywebio.input.textarea()` to make a code editing textarea. This feature uses `Codemirror `_ as underlying implementation. The ``code`` parameter accept the Codemirror options as a dict." +#~ msgstr "" +#~ ":func:`pywebio.input.textarea` 还支持使用 `Codemirror `_ 实现代码风格的编辑区,只需使用 ``code`` 参数传入Codemirror支持" +#~ "的选项即可(最简单的情况是直接传入 ``code={}`` 或 ``code=True``):" + +#~ msgid "" +#~ ":ref:`Here ` are some commonly used Codemirror options. For complete Codemirror options, please visit: https://codemirror." +#~ "net/doc/manual.html#config" +#~ msgstr ":ref:`这里 ` 列举了一些常用的Codemirror选项,完整的Codemirror选项请见:https://codemirror.net/doc/manual.html#config" + +#~ msgid "" +#~ "Scopes can be nested. At the beginning, PyWebIO applications have only one ``ROOT`` Scope. Each time a new scope is created, the nesting level " +#~ "of the scope will increase by one level, and each time the current scope is exited, the nesting level of the scope will be reduced by one. " +#~ "PyWebIO uses the Scope stack to save the scope nesting level at runtime." +#~ msgstr "" +#~ "Scope是可嵌套的,初始条件下,PyWebIO应用只有一个最顶层的 ``ROOT`` Scope。每创建一个新Scope,Scope的嵌套层级便会多加一层,每退出当前Scope,Scope的" +#~ "嵌套层级便会减少一层。PyWebIO使用Scope栈来保存运行时的Scope的嵌套层级。" + +#~ msgid "For example, the following code will create 3 scopes:" +#~ msgstr "例如,如下代码将会创建3个Scope:" + +#~ msgid "" +#~ "Another way to deploy PyWebIO application as web service is using `path_deploy() `. `path_deploy() ` is used to deploy the PyWebIO applications from a directory. Each python file under this directory defines a PyWebIO " +#~ "application. You can access the application by using the file path as the URL. Refer to :ref:`platform module ` for more information." +#~ msgstr "" +#~ "将PyWebIO应用部署为web服务的另一种方式是使用 `path_deploy() ` 。`path_deploy() ` 可以" +#~ "从一个目录中部署PyWebIO应用,只需要在该目录下的python文件中定义PyWebIO应用,就可以通过URL中的路径来访问这些应用了。" + +#~ msgid "PyWebIO provides a series of functions to output text, tables, images, etc:" +#~ msgstr "PyWebIO提供了一系列函数来输出文本、表格、图像等格式:" + +#~ msgid "**Placeholder**" +#~ msgstr "**占位符**" + +#~ msgid "" +#~ "When using combination output, if you want to dynamically update the ``put_xxx()`` content after it has been output, you can use the `output() " +#~ "` function. `output() ` is like a placeholder, it can be passed in anywhere that ``put_xxx()`` can " +#~ "passed in. And after being output, the content can also be modified:" +#~ msgstr "" +#~ "使用组合输出时,如果想在内容输出后,对其中的 ``put_xxx()`` 子项进行动态修改,可以使用 `output() ` 函数,\n" +#~ "`output() ` 就像一个占位符,它可以像 ``put_xxx()`` 一样传入 `put_table` 、 `popup` 、 `put_widget` 等函数中作为输出的一部" +#~ "分,\n" +#~ "并且,在输出后,还可以对其中的内容进行修改(比如重置或增加内容):" + +#~ msgid "`set_scope(name) ` : Create scope at current location(or specified location)" +#~ msgstr "`set_scope(name) ` : 在当前位置(或指定位置)创建scope" diff --git a/docs/locales/zh_CN/LC_MESSAGES/index.po b/docs/locales/zh_CN/LC_MESSAGES/index.po new file mode 100644 index 00000000..845bfe4c --- /dev/null +++ b/docs/locales/zh_CN/LC_MESSAGES/index.po @@ -0,0 +1,235 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) WangWeimin +# This file is distributed under the same license as the PyWebIO package. +# FIRST AUTHOR , 2021. +# +msgid "" +msgstr "" +"Project-Id-Version: PyWebIO 1.1.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-11-16 23:13+0800\n" +"PO-Revision-Date: 2021-12-02 21:40+0800\n" +"Last-Translator: WangWeimin \n" +"Language: zh_CN\n" +"Language-Team: \n" +"Plural-Forms: nplurals=1; plural=0;\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.8.0\n" +"X-Generator: Poedit 3.0\n" + +#: ../../index.rst:80 +msgid "Manual" +msgstr "使用手册" + +#: ../../index.rst:99 +msgid "Implement Doc" +msgstr "实现文档" + +#: ../../index.rst:2 +msgid "PyWebIO" +msgstr "PyWebIO" + +#: ../../index.rst:4 +msgid "" +"PyWebIO provides a diverse set of imperative functions to obtain user input and " +"output content on the browser, turning the browser into a \"rich text terminal\", " +"and can be used to build simple web applications or browser-based GUI applications. " +"Using PyWebIO, developers can write applications just like writing terminal scripts " +"(interaction based on input and print function), without the need to have knowledge " +"of HTML and JS. PyWebIO is ideal for quickly building interactive applications that " +"don't require a complicated user interface." +msgstr "" +"PyWebIO提供了一系列命令式的交互函数来在浏览器上获取用户输入和进行输出,将浏览器变成" +"了一个“富文本终端”,可以用于构建简单的Web应用或基于浏览器的GUI应用。 使用PyWebIO,开" +"发者能像编写终端脚本一样(基于input和print进行交互)来编写应用,无需具备HTML和JS的相关" +"知识; PyWebIO还可以方便地整合进现有的Web服务。非常适合快速构建对UI要求不高的应用。" + +#: ../../index.rst:11 +msgid "Features" +msgstr "特性" + +#: ../../index.rst:13 +msgid "Use synchronization instead of callback-based method to get input" +msgstr "使用同步而不是基于回调的方式获取输入,代码编写逻辑更自然" + +#: ../../index.rst:14 +msgid "Non-declarative layout, simple and efficient" +msgstr "非声明式布局,布局方式简单高效" + +#: ../../index.rst:15 +msgid "" +"Less intrusive: old script code can be transformed into a Web service only by " +"modifying the input and output operation" +msgstr "代码侵入性小,旧脚本代码仅需修改输入输出逻辑便可改造为Web服务" + +#: ../../index.rst:16 +msgid "" +"Support integration into existing web services, currently supports Flask, Django, " +"Tornado, aiohttp and FastAPI(Starlette) framework" +msgstr "" +"支持整合到现有的Web服务,目前支持与Flask、Django、Tornado、aiohttp、 " +"FastAPI(Starlette)框架集成" + +#: ../../index.rst:17 +msgid "Support for ``asyncio`` and coroutine" +msgstr "同时支持基于线程的执行模型和基于协程的执行模型" + +#: ../../index.rst:18 +msgid "Support data visualization with third-party libraries" +msgstr "支持结合第三方库实现数据可视化" + +#: ../../index.rst:21 +msgid "Installation" +msgstr "" + +#: ../../index.rst:23 +msgid "Stable version::" +msgstr "稳定版::" + +#: ../../index.rst:25 +msgid "pip3 install -U pywebio" +msgstr "" + +#: ../../index.rst:27 +msgid "Development version::" +msgstr "开发版::" + +#: ../../index.rst:29 +msgid "" +"pip3 install -U https://code.aliyun.com/wang0618/pywebio/repository/archive.zip" +msgstr "" + +#: ../../index.rst:31 +msgid "**Prerequisites**: PyWebIO requires Python 3.5.2 or newer" +msgstr "**系统要求**: PyWebIO要求 Python 版本在 3.5.2 及以上" + +#: ../../index.rst:36 +msgid "Hello, world" +msgstr "" + +#: ../../index.rst:38 +msgid "" +"Here is a simple PyWebIO script to calculate the `BMI `_ ::" +msgstr "" +"这是一个使用PyWebIO计算 `BMI指数 `_ " +"的脚本:" + +#: ../../index.rst:40 +#, python-format +msgid "" +"# A simple script to calculate BMI\n" +"from pywebio.input import input, FLOAT\n" +"from pywebio.output import put_text\n" +"\n" +"def bmi():\n" +" height = input(\"Input your height(cm):\", type=FLOAT)\n" +" weight = input(\"Input your weight(kg):\", type=FLOAT)\n" +"\n" +" BMI = weight / (height / 100) ** 2\n" +"\n" +" top_status = [(16, 'Severely underweight'), (18.5, 'Underweight'),\n" +" (25, 'Normal'), (30, 'Overweight'),\n" +" (35, 'Moderately obese'), (float('inf'), 'Severely obese')]\n" +"\n" +" for top, status in top_status:\n" +" if BMI <= top:\n" +" put_text('Your BMI: %.1f. Category: %s' % (BMI, status))\n" +" break\n" +"\n" +"if __name__ == '__main__':\n" +" bmi()" +msgstr "" +"from pywebio.input import input, FLOAT\n" +"from pywebio.output import put_text\n" +"\n" +"def bmi():\n" +" height = input(\"请输入你的身高(cm):\", type=FLOAT)\n" +" weight = input(\"请输入你的体重(kg):\", type=FLOAT)\n" +"\n" +" BMI = weight / (height / 100) ** 2\n" +"\n" +" top_status = [(14.9, '极瘦'), (18.4, '偏瘦'),\n" +" (22.9, '正常'), (27.5, '过重'),\n" +" (40.0, '肥胖'), (float('inf'), '非常肥胖')]\n" +"\n" +" for top, status in top_status:\n" +" if BMI <= top:\n" +" put_text('你的 BMI 值: %.1f,身体状态:%s' % (BMI, status))\n" +" break\n" +"\n" +"if __name__ == '__main__':\n" +" bmi()" + +#: ../../index.rst:62 +msgid "" +"This is just a very simple script if you ignore PyWebIO, but after using the input " +"and output functions provided by PyWebIO, you can interact with the code in the " +"browser:" +msgstr "" +"如果没有使用PyWebIO,这只是一个非常简单的脚本,而通过使用PyWebIO提供的输入输出函数," +"你可以在浏览器中与代码进行交互:" + +#: ../../index.rst:69 +msgid "" +"In the last line of the above code, changing the function call ``bmi()`` to " +"`pywebio.start_server(bmi, port=80) ` will " +"start a bmi web service on port 80 ( :demo_host:`online Demo ` )." +msgstr "" +"将上面代码最后一行对 ``bmi()`` 的直接调用改为使用 `pywebio.start_server(bmi, " +"port=80) ` 便可以在80端口提供 ``bmi()`` 服务" +"( :demo_host:`在线Demo ` )。" + +#: ../../index.rst:73 +msgid "" +"If you want to integrate the ``bmi()`` service into an existing web framework, you " +"can visit :ref:`Integration with a web framework ` " +"section of this document." +msgstr "" +"将 ``bmi()`` 服务整合到现有的Web框架请参考 :ref:`与Web框架集成 " +"` 。" + +#: ../../index.rst:77 +msgid "Documentation" +msgstr "" + +#: ../../index.rst:78 +msgid "" +"This documentation is also available in `PDF and Epub formats `_." +msgstr "" +"这个文档同时也提供 `PDF 和 Epub 格式 `_." + +#: ../../index.rst:106 +msgid "Indices and tables" +msgstr "" + +#: ../../index.rst:108 +msgid ":ref:`genindex`" +msgstr "" + +#: ../../index.rst:109 +msgid ":ref:`modindex`" +msgstr "" + +#: ../../index.rst:110 +msgid ":ref:`search`" +msgstr "" + +#: ../../index.rst:114 +msgid "Discussion and support" +msgstr "" + +#: ../../index.rst:116 +msgid "" +"Need help when use PyWebIO? Make a new discussion on `Github Discussions `_." +msgstr "" + +#: ../../index.rst:118 +msgid "" +"Report bugs on the `GitHub issue `_." +msgstr "" diff --git a/docs/locales/zh_CN/LC_MESSAGES/input.po b/docs/locales/zh_CN/LC_MESSAGES/input.po new file mode 100644 index 00000000..0bd3cfde --- /dev/null +++ b/docs/locales/zh_CN/LC_MESSAGES/input.po @@ -0,0 +1,1138 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) WangWeimin +# This file is distributed under the same license as the PyWebIO package. +# FIRST AUTHOR , 2021. +# +msgid "" +msgstr "" +"Project-Id-Version: PyWebIO 1.1.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-11-16 23:25+0800\n" +"PO-Revision-Date: 2021-11-16 23:26+0800\n" +"Last-Translator: WangWeimin \n" +"Language: zh_CN\n" +"Language-Team: \n" +"Plural-Forms: nplurals=1; plural=0;\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.8.0\n" +"X-Generator: Poedit 3.0\n" + +#: ../../input.rst:2 +msgid "``pywebio.input`` --- Get input from web browser" +msgstr "``pywebio.input`` — 输入模块" + +#: of pywebio.input:1 +msgid "This module provides functions to get all kinds of input of user from the browser" +msgstr "本模块提供了一系列函数来从浏览器接收用户不同的形式的输入" + +#: of pywebio.input:3 +msgid "" +"There are two ways to use the input functions, one is to call the input function alone to get " +"a single input::" +msgstr "输入函数大致分为两类,一类是单项输入::" + +#: of pywebio.input:5 +#, python-format +msgid "" +"name = input(\"What's your name\")\n" +"print(\"Your name is %s\" % name)" +msgstr "" + +#: of pywebio.input:8 +msgid "The other is to use `input_group` to get multiple inputs at once::" +msgstr "另一类是使用 `input_group` 的输入组::" + +#: of pywebio.input:10 +msgid "" +"info = input_group(\"User info\",[\n" +" input('Input your name', name='name'),\n" +" input('Input your age', name='age', type=NUMBER)\n" +"])\n" +"print(info['name'], info['age'])" +msgstr "" + +#: of pywebio.input:16 +msgid "" +"When use `input_group`, you needs to provide the ``name`` parameter in each input function to " +"identify the input items in the result." +msgstr "输入组中需要在每一项输入函数中提供 ``name`` 参数来用于在结果中标识不同输入项." + +#: of pywebio.input:20 +msgid "" +"PyWebIO determines whether the input function is in `input_group` or is called alone " +"according to whether the ``name`` parameter is passed. So when calling an input function " +"alone, **do not** set the ``name`` parameter; when calling the input function in " +"`input_group`, you **must** provide the ``name`` parameter." +msgstr "" +"PyWebIO 根据是否在输入函数中传入 ``name`` 参数来判断输入函数是在 `input_group` 中还是被单独调" +"用。所以当你想要单独调用一个输入函数时,请不要设置 ``name`` 参数;而在 `input_group` 中调用输" +"入函数时,**务必提供** ``name`` 参数。" + +#: of pywebio.input:24 +msgid "" +"By default, the user can submit empty input value. If the user must provide a non-empty input " +"value, you need to pass ``required=True`` to the input function (some input functions do not " +"support the ``required`` parameter)" +msgstr "" +"输入默认可以为空,如果需要用户必须提供值,则需要在输入函数中传入 ``required=True`` (部分输入函" +"数不支持 ``required`` 参数)" + +#: of pywebio.input:27 +msgid "" +"The input functions in this module is blocking, and the input form will be destroyed after " +"successful submission. If you want the form to always be displayed on the page and receive " +"input continuously, you can consider the :doc:`pin <./pin>` module." +msgstr "" +"本模块中的输入函数都是阻塞式的,输入表单会在成功提交后销毁。如果你想让表单可以一直显示在页面上" +"并可以持续性接收输入,你可以考虑使用 :doc:`pin <./pin>` 模块。" + +#: of pywebio.input:32 +msgid "Functions list" +msgstr "函数清单" + +#: of pywebio.input:36 +msgid "Function name" +msgstr "函数" + +#: of pywebio.input:37 +msgid "Description" +msgstr "简介" + +#: of pywebio.input:39 +msgid "`input `" +msgstr "" + +#: of pywebio.input:40 pywebio.input.input:1 +msgid "Text input" +msgstr "文本输入" + +#: of pywebio.input:42 +msgid "`textarea `" +msgstr "" + +#: of pywebio.input:43 +msgid "Multi-line text input" +msgstr "多行文本输入" + +#: of pywebio.input:45 +msgid "`select `" +msgstr "" + +#: of pywebio.input:46 pywebio.input.select:1 +msgid "Drop-down selection" +msgstr "下拉选择框" + +#: of pywebio.input:48 +msgid "`checkbox `" +msgstr "" + +#: of pywebio.input:49 +msgid "Checkbox" +msgstr "勾选选项" + +#: of pywebio.input:51 +msgid "`radio `" +msgstr "" + +#: of pywebio.input:52 +msgid "Radio" +msgstr "单选选项" + +#: of pywebio.input:54 +msgid "`slider `" +msgstr "" + +#: of pywebio.input:55 +msgid "Slider" +msgstr "滑块输入" + +#: of pywebio.input:57 +msgid "`actions `" +msgstr "" + +#: of pywebio.input:58 pywebio.input.actions:1 +msgid "Actions selection" +msgstr "按钮选项" + +#: of pywebio.input:60 +msgid "`file_upload `" +msgstr "" + +#: of pywebio.input:61 pywebio.input.file_upload:1 +msgid "File uploading" +msgstr "文件上传" + +#: of pywebio.input:63 +msgid "`input_group `" +msgstr "" + +#: of pywebio.input:64 +msgid "Input group" +msgstr "输入组" + +#: of pywebio.input:66 +msgid "`input_update `" +msgstr "" + +#: of pywebio.input:67 +msgid "Update input item" +msgstr "更新输入项" + +#: of pywebio.input:71 +msgid "Functions doc" +msgstr "函数文档" + +#: of pywebio.input.actions pywebio.input.checkbox pywebio.input.file_upload pywebio.input.input +#: pywebio.input.input_group pywebio.input.input_update pywebio.input.radio pywebio.input.select +#: pywebio.input.slider pywebio.input.textarea +msgid "Parameters" +msgstr "" + +#: of pywebio.input.input:3 +msgid "Label of input field." +msgstr "输入框标签" + +#: of pywebio.input.input:4 +msgid "" +"Input type. Currently supported types are:`TEXT` , `NUMBER` , `FLOAT` , `PASSWORD` , `URL` , " +"`DATE` , `TIME` Note that `DATE` and `TIME` type are not supported on some browsers, for " +"details see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/" +"input#Browser_compatibility" +msgstr "" + +#: of pywebio.input.input:4 +msgid "" +"Input type. Currently supported types are:`TEXT` , `NUMBER` , `FLOAT` , `PASSWORD` , `URL` , " +"`DATE` , `TIME`" +msgstr "" +"输入类型. 可使用的常量:`TEXT` , `NUMBER` , `FLOAT` , `PASSWORD` , `URL` , `DATE` , `TIME`" + +#: of pywebio.input.input:6 +msgid "" +"Note that `DATE` and `TIME` type are not supported on some browsers, for details see https://" +"developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Browser_compatibility" +msgstr "" +"其中 `DATE` , `TIME` 类型在某些浏览器上不被支持,详情见 https://developer.mozilla.org/en-US/" +"docs/Web/HTML/Element/input#Browser_compatibility" + +#: of pywebio.input.input:8 +msgid "" +"Input value validation function. If provided, the validation function will be called when " +"user completes the input field or submits the form. ``validate`` receives the input value as " +"a parameter. When the input value is valid, it returns ``None``. When the input value is " +"invalid, it returns an error message string. For example: .. exportable-codeblock:: :" +"name: input-valid-func :summary: `input()` validation def check_age(age): if " +"age>30: return 'Too old' elif age<10: return 'Too young' " +"input('Input your age', type=NUMBER, validate=check_age)" +msgstr "" + +#: of pywebio.input.input:8 +msgid "" +"Input value validation function. If provided, the validation function will be called when " +"user completes the input field or submits the form." +msgstr "输入值校验函数。 如果提供,当用户输入完毕或提交表单后校验函数将被调用。" + +#: of pywebio.input.input:11 +msgid "" +"``validate`` receives the input value as a parameter. When the input value is valid, it " +"returns ``None``. When the input value is invalid, it returns an error message string. For " +"example:" +msgstr "" +"``validate`` 接收输入值作为参数,当输入值有效时,返回 ``None`` ,当输入值无效时,返回错误提示" +"字符串. 比如:" + +#: of pywebio.input.input:14 +msgid "" +"def check_age(age):\n" +" if age>30:\n" +" return 'Too old'\n" +" elif age<10:\n" +" return 'Too young'\n" +"input('Input your age', type=NUMBER, validate=check_age)" +msgstr "" + +#: of pywebio.input.input:25 +msgid "" +"A string specifying a name for the input. Used with `input_group()` to identify different " +"input items in the results of the input group. If call the input function alone, this " +"parameter can **not** be set!" +msgstr "" +"输入框的名字。与 `input_group` 配合使用,用于在输入组的结果中标识不同输入项。 **在单个输入" +"中,不可以设置该参数!**" + +#: of pywebio.input.input:27 +msgid "The initial value of the input" +msgstr "输入框的初始值" + +#: of pywebio.input.input:29 +msgid "" +"Put a button on the right side of the input field, and user can click the button to set the " +"value for the input. ``label`` is the label of the button, and ``callback`` is the callback " +"function to set the input value when clicked. The callback is invoked with one argument, the " +"``set_value``. ``set_value`` is a callable object, which is invoked with one or two " +"arguments. You can use ``set_value`` to set the value for the input. ``set_value`` can be " +"invoked with one argument: ``set_value(value:str)``. The ``value`` parameter is the value to " +"be set for the input. ``set_value`` can be invoked with two arguments: ``set_value(value:" +"any, label:str)``. Each arguments are described as follows: * ``value`` : The real value of " +"the input, can be any object. it will not be passed to the user browser. * ``label`` : The " +"text displayed to the user When calling ``set_value`` with two arguments, the input item in " +"web page will become read-only. The usage scenario of ``set_value(value:any, label:str)`` " +"is: You need to dynamically generate the value of the input in the callback, and hope that " +"the result displayed to the user is different from the actual submitted data (for example, " +"result displayed to the user can be some user-friendly texts, and the value of the input can " +"be objects that are easier to process) Usage example: .. exportable-codeblock:: :name: " +"input-action :summary: `input()` action usage import time def " +"set_now_ts(set_value): set_value(int(time.time())) ts = input('Timestamp', " +"type=NUMBER, action=('Now', set_now_ts)) put_text('Timestamp:', ts) # ..demo-only ## " +"---- from datetime import date,timedelta def select_date(set_value): with " +"popup('Select Date'): put_buttons(['Today'], onclick=[lambda: set_value(date." +"today(), 'Today')]) put_buttons(['Yesterday'], onclick=[lambda: set_value(date." +"today() - timedelta(days=1), 'Yesterday')]) d = input('Date', action=('Select', " +"select_date), readonly=True) put_text(type(d), d) Note: When using :ref:`Coroutine-based " +"session ` implementation, the ``callback`` function can be a " +"coroutine function." +msgstr "" + +#: of pywebio.input.input:29 +msgid "" +"Put a button on the right side of the input field, and user can click the button to set the " +"value for the input." +msgstr "在输入框右侧显示一个按钮,用户可通过点击按钮为输入框设置值。" + +#: of pywebio.input.input:31 +msgid "" +"``label`` is the label of the button, and ``callback`` is the callback function to set the " +"input value when clicked." +msgstr "``label`` 为按钮的显示文本, ``callback`` 为按钮点击的回调函数。" + +#: of pywebio.input.input:33 +msgid "" +"The callback is invoked with one argument, the ``set_value``. ``set_value`` is a callable " +"object, which is invoked with one or two arguments. You can use ``set_value`` to set the " +"value for the input." +msgstr "" +"回调函数需要接收一个 ``set_value`` 位置参数, ``set_value`` 是一个可调用对象,接受单参数调用和" +"双参数调用。" + +#: of pywebio.input.input:36 +msgid "" +"``set_value`` can be invoked with one argument: ``set_value(value:str)``. The ``value`` " +"parameter is the value to be set for the input." +msgstr "" +"单参数调用时,签名为 ``set_value(value:str)`` ,调用set_value即可将表单项的值设置为传入的 " +"``value`` 参数。" + +#: of pywebio.input.input:38 +msgid "" +"``set_value`` can be invoked with two arguments: ``set_value(value:any, label:str)``. Each " +"arguments are described as follows:" +msgstr "双参数调用时,签名为 ``set_value(value:any, label:str)`` ,其中:" + +#: of pywebio.input.input:40 +msgid "" +"``value`` : The real value of the input, can be any object. it will not be passed to the user " +"browser." +msgstr "``value`` 参数为最终输入项的返回值,可以为任意Python对象,并不会传递给用户浏览器" + +#: of pywebio.input.input:41 +msgid "``label`` : The text displayed to the user" +msgstr "``label`` 参数用于显示在用户表单项上" + +#: of pywebio.input.input:43 +msgid "" +"When calling ``set_value`` with two arguments, the input item in web page will become read-" +"only." +msgstr "使用双参数调用 ``set_value`` 后,用户表单项会变为只读状态。" + +#: of pywebio.input.input:45 +msgid "" +"The usage scenario of ``set_value(value:any, label:str)`` is: You need to dynamically " +"generate the value of the input in the callback, and hope that the result displayed to the " +"user is different from the actual submitted data (for example, result displayed to the user " +"can be some user-friendly texts, and the value of the input can be objects that are easier to " +"process)" +msgstr "" +"双参数调用的使用场景为:表单项的值通过回调动态生成,同时希望用户表单显示的和实际提交的数据不同" +"(例如表单项上可以显示更人性化的内容,而表单项的值则可以保存更方便被处理的对象)" + +#: of pywebio.input.input:50 +msgid "Usage example:" +msgstr "使用示例" + +#: of pywebio.input.input:52 +msgid "" +"import time\n" +"def set_now_ts(set_value):\n" +" set_value(int(time.time()))\n" +"\n" +"ts = input('Timestamp', type=NUMBER, action=('Now', set_now_ts))\n" +"put_text('Timestamp:', ts) # ..demo-only\n" +"## ----\n" +"from datetime import date,timedelta\n" +"def select_date(set_value):\n" +" with popup('Select Date'):\n" +" put_buttons(['Today'], onclick=[lambda: set_value(date.today(), 'Today')])\n" +" put_buttons(['Yesterday'], onclick=[lambda: set_value(date.today() - " +"timedelta(days=1), 'Yesterday')])\n" +"\n" +"d = input('Date', action=('Select', select_date), readonly=True)\n" +"put_text(type(d), d)" +msgstr "" + +#: of pywebio.input.input:72 +msgid "" +"Note: When using :ref:`Coroutine-based session ` implementation, the " +"``callback`` function can be a coroutine function." +msgstr "" +"Note: 当使用 :ref:`基于协程的会话实现 ` 时,回调函数 ``callback`` 可" +"以为协程函数." + +#: of pywebio.input.input:75 +msgid "" +"A callback function which will be called when the value of this input field changed. The " +"``onchange`` callback is invoked with one argument, the current value of input field. A " +"typical usage scenario of ``onchange`` is to update other input item by using `input_update()`" +msgstr "" + +#: of pywebio.input.input:75 +msgid "A callback function which will be called when the value of this input field changed." +msgstr "当输入项的值发生变化时的回调函数。" + +#: of pywebio.input.input:77 +msgid "" +"The ``onchange`` callback is invoked with one argument, the current value of input field. A " +"typical usage scenario of ``onchange`` is to update other input item by using `input_update()`" +msgstr "" +"``onchange`` 回调函数接收一个参数——输入项改变后的值。 ``onchange`` 的典型用途是配合 " +"`input_update()` 来在一个表单中实现相互依赖的输入。" + +#: of pywebio.input.input:80 +msgid "" +"A hint to the user of what can be entered in the input. It will appear in the input field " +"when it has no value set." +msgstr "输入框的提示内容。提示内容会在输入框未输入值时以浅色字体显示在输入框中" + +#: of pywebio.input.input:81 +msgid "Whether a value is required for the input to be submittable, default is ``False``" +msgstr "当前输入是否为必填项,默认为 ``False``" + +#: of pywebio.input.input:82 +msgid "Whether the value is readonly(not editable)" +msgstr "输入框是否为只读" + +#: of pywebio.input.input:83 +msgid "" +"A list of predefined values to suggest to the user for this input. Can only be used when " +"``type=TEXT``" +msgstr "" +"输入建议内容列表,在页面上的显示效果为下拉候选列表,用户可以忽略建议内容列表而输入其他内容。仅" +"当输入类型 ``type`` 为 `TEXT` 时可用" + +#: of pywebio.input.input:84 +msgid "" +"Help text for the input. The text will be displayed below the input field with small font" +msgstr "输入框的帮助文本。帮助文本会以小号字体显示在输入框下方" + +#: of pywebio.input.input:85 +#, python-format +msgid "" +"Additional html attributes added to the input element. reference: https://developer.mozilla." +"org/zh-CN/docs/Web/HTML/Element/input#%E5%B1%9E%E6%80%A7" +msgstr "" +"在输入框上附加的额外html属性。参考: https://developer.mozilla.org/zh-CN/docs/Web/HTML/" +"Element/input#%E5%B1%9E%E6%80%A7" + +#: of pywebio.input.actions pywebio.input.checkbox pywebio.input.file_upload pywebio.input.input +#: pywebio.input.input_group pywebio.input.radio pywebio.input.select pywebio.input.textarea +msgid "Returns" +msgstr "" + +#: of pywebio.input.input:87 +msgid "The value that user input." +msgstr "用户输入的值" + +#: of pywebio.input.textarea:1 +msgid "Text input area (multi-line text input)" +msgstr "文本输入域(多行文本输入)" + +#: of pywebio.input.textarea:3 +msgid "" +"The number of visible text lines for the input area. Scroll bar will be used when content " +"exceeds." +msgstr "输入框的最多可显示的文本的行数,内容超出时会显示滚动条" + +#: of pywebio.input.textarea:4 +msgid "" +"The maximum number of characters (UTF-16 code units) that the user can enter. If this value " +"isn't specified, the user can enter an unlimited number of characters." +msgstr "最大允许用户输入的字符长度 (Unicode) 。未指定表示无限长度" + +#: of pywebio.input.textarea:6 +msgid "" +"The minimum number of characters (UTF-16 code units) required that the user should enter." +msgstr "最少需要用户输入的字符长度(Unicode)" + +#: of pywebio.input.textarea:7 +msgid "" +"Enable a code style editor by providing the `Codemirror `_ " +"options: .. exportable-codeblock:: :name: textarea-code :summary: `textarea()` code " +"editor style res = textarea('Text area', code={ 'mode': \"python\", " +"'theme': 'darcula' }) put_code(res, language='python') # ..demo-only You can simply " +"use ``code={}`` or ``code=True`` to enable code style editor. You can use ``Esc`` or ``F11`` " +"to toggle fullscreen of code style textarea. Some commonly used Codemirror options are " +"listed :ref:`here `." +msgstr "" + +#: of pywebio.input.textarea:7 +msgid "" +"Enable a code style editor by providing the `Codemirror `_ options:" +msgstr "通过提供 `Codemirror `_ 参数让文本输入域具有代码编辑器样式:" + +#: of pywebio.input.textarea:9 +msgid "" +"res = textarea('Text area', code={\n" +" 'mode': \"python\",\n" +" 'theme': 'darcula'\n" +"})\n" +"put_code(res, language='python') # ..demo-only" +msgstr "" + +#: of pywebio.input.textarea:19 +msgid "" +"You can simply use ``code={}`` or ``code=True`` to enable code style editor. You can use " +"``Esc`` or ``F11`` to toggle fullscreen of code style textarea." +msgstr "" +"可以直接使用 ``code={}`` 或 ``code=True`` 开启代码编辑样式。代码编辑区支持使用 ``Esc`` 或 " +"``F11`` 切换全屏。" + +#: of pywebio.input.textarea:22 +msgid "Some commonly used Codemirror options are listed :ref:`here `." +msgstr ":ref:`这里 ` 列举了一些常用的Codemirror选项" + +#: of pywebio.input.actions:39 pywebio.input.checkbox:7 pywebio.input.file_upload:23 +#: pywebio.input.radio:8 pywebio.input.select:29 pywebio.input.slider:8 +#: pywebio.input.textarea:24 +msgid "Those arguments have the same meaning as for `input()`" +msgstr "与 `input` 输入函数的同名参数含义一致" + +#: of pywebio.input.textarea:26 +msgid "The string value that user input." +msgstr "用户输入的文本" + +#: of pywebio.input.select:3 +msgid "" +"By default, only one option can be selected at a time, you can set ``multiple`` parameter to " +"enable multiple selection." +msgstr "默认单选,可以通过设置 ``multiple`` 参数来允许多选" + +#: of pywebio.input.select:5 +msgid "" +"list of options. The available formats of the list items are: * dict:: { \"label" +"\":(str) option label, \"value\":(object) option value, \"selected\":(bool, " +"optional) whether the option is initially selected, \"disabled\":(bool, optional) " +"whether the option is initially disabled } * tuple or list: ``(label, value, [selected,] " +"[disabled])`` * single value: label and value of option use the same value Attention: 1. " +"The ``value`` of option can be any JSON serializable object 2. If the ``multiple`` is not " +"``True``, the list of options can only have one ``selected`` item at most." +msgstr "" + +#: of pywebio.input.select:5 +msgid "list of options. The available formats of the list items are:" +msgstr "可选项列表。列表项的可用形式有:" + +#: of pywebio.input.actions:8 pywebio.input.select:7 +msgid "dict::" +msgstr "" + +#: of pywebio.input.select:9 +msgid "" +"{\n" +" \"label\":(str) option label,\n" +" \"value\":(object) option value,\n" +" \"selected\":(bool, optional) whether the option is initially selected,\n" +" \"disabled\":(bool, optional) whether the option is initially disabled\n" +"}" +msgstr "" +"{\n" +" \"label\":(str) 选项标签,\n" +" \"value\":(object) 选项值,\n" +" \"selected\":(bool, optional) 是否默认选中,\n" +" \"disabled\":(bool, optional) 是否禁止选中\n" +"}" + +#: of pywebio.input.select:16 +msgid "tuple or list: ``(label, value, [selected,] [disabled])``" +msgstr "" + +#: of pywebio.input.select:17 +msgid "single value: label and value of option use the same value" +msgstr "单值: 此时label和value使用相同的值" + +#: of pywebio.input.select:19 +msgid "Attention:" +msgstr "注意:" + +#: of pywebio.input.select:21 +msgid "The ``value`` of option can be any JSON serializable object" +msgstr "``options`` 中的 ``value`` 可以为任意可JSON序列化对象" + +#: of pywebio.input.select:22 +msgid "" +"If the ``multiple`` is not ``True``, the list of options can only have one ``selected`` item " +"at most." +msgstr "" +"若 ``multiple`` 选项不为 ``True`` 则可选项列表最多仅能有一项的 ``selected`` 为 ``True``。" + +#: of pywebio.input.select:24 +msgid "whether multiple options can be selected" +msgstr "是否可以多选. 默认单选" + +#: of pywebio.input.select:25 +msgid "" +"The value of the initial selected item. When ``multiple=True``, ``value`` must be a list. You " +"can also set the initial selected option by setting the ``selected`` field in the ``options`` " +"list item." +msgstr "" +"下拉选择框初始选中项的值。当 ``multiple=True`` 时, ``value`` 需为list,否则为单个选项的值。\n" +"你也可以通过设置 ``options`` 列表项中的 ``selected`` 字段来设置默认选中选项。\n" +"最终选中项为 ``value`` 参数和 ``options`` 中设置的并集。" + +#: of pywebio.input.select:28 +msgid "Whether to select at least one item, only available when ``multiple=True``" +msgstr "是否至少选择一项,仅在 ``multiple=True`` 时可用" + +#: of pywebio.input.select:30 +msgid "" +"If ``multiple=True``, return a list of the values in the ``options`` selected by the user; " +"otherwise, return the single value selected by the user." +msgstr "" +"如果 ``multiple=True`` 时,返回用户选中的 ``options`` 中的值的列表;否则,返回用户选中的 " +"``options`` 中的值" + +#: of pywebio.input.checkbox:1 +msgid "A group of check box that allowing single values to be selected/deselected." +msgstr "勾选选项。可以多选,也可以不选。" + +#: of pywebio.input.checkbox:3 pywebio.input.radio:3 +msgid "" +"List of options. The format is the same as the ``options`` parameter of the `select()` " +"function" +msgstr "可选项列表。格式与同 `select()` 函数的 ``options`` 参数" + +#: of pywebio.input.checkbox:4 pywebio.input.radio:4 +msgid "Whether to display the options on one line. Default is ``False``" +msgstr "是否将选项显示在一行上。默认每个选项单独占一行" + +#: of pywebio.input.checkbox:5 +msgid "" +"The value list of the initial selected items. You can also set the initial selected option by " +"setting the ``selected`` field in the ``options`` list item." +msgstr "" +"勾选选项初始选中项。为选项值的列表。你也可以通过设置 ``options`` 列表项中的 ``selected`` 字段" +"来设置默认选中选项。" + +#: of pywebio.input.checkbox:8 +msgid "A list of the values in the ``options`` selected by the user" +msgstr "用户选中的 options 中的值的列表。当用户没有勾选任何选项时,返回空列表" + +#: of pywebio.input.radio:1 +msgid "A group of radio button. Only a single button can be selected." +msgstr "单选选项" + +#: of pywebio.input.radio:5 +msgid "" +"The value of the initial selected items. You can also set the initial selected option by " +"setting the ``selected`` field in the ``options`` list item." +msgstr "可选项列表。格式与同 `select()` 函数的 ``options`` 参数" + +#: of pywebio.input.radio:7 +msgid "whether to must select one option. (the user can select nothing option by default)" +msgstr "是否一定要选择一项(默认条件下用户可以不选择任何选项)" + +#: of pywebio.input.radio:9 +msgid "" +"The value of the option selected by the user, if the user does not select any value, return " +"``None``" +msgstr "用户选中的选项的值, 如果用户没有选任何值,返回 ``None``" + +#: of pywebio.input.actions:3 +msgid "" +"It is displayed as a group of buttons on the page. After the user clicks the button of it, it " +"will behave differently depending on the type of the button." +msgstr "在表单上显示为一组按钮,用户点击按钮后依据按钮类型的不同有不同的表现。" + +#: of pywebio.input.actions:6 +msgid "" +"list of buttons. The available formats of the list items are: * dict:: " +"{ \"label\":(str) button label, \"value\":(object) button value, " +"\"type\":(str, optional) button type, \"disabled\":(bool, optional) whether the " +"button is disabled, \"color\":(str, optional) button color } When " +"``type='reset'/'cancel'`` or ``disabled=True``, ``value`` can be omitted * tuple or list: " +"``(label, value, [type], [disabled])`` * single value: label and value of button use the " +"same value The ``value`` of button can be any JSON serializable object. ``type`` can be: " +"* ``'submit'`` : After clicking the button, the entire form is submitted immediately, and " +"the value of this input item in the final form is the ``value`` of the button that was " +"clicked. ``'submit'`` is the default value of ``type`` * ``'cancel'`` : Cancel form. " +"After clicking the button, the entire form will be submitted immediately, and the form " +"value will return ``None`` * ``'reset'`` : Reset form. After clicking the button, the entire " +"form will be reset, and the input items will become the initial state. Note: After " +"clicking the ``type=reset`` button, the form will not be submitted, and the ``actions()`` " +"call will not return The ``color`` of button can be one of: `primary`, `secondary`, " +"`success`, `danger`, `warning`, `info`, `light`, `dark`." +msgstr "" + +#: of pywebio.input.actions:6 +msgid "list of buttons. The available formats of the list items are:" +msgstr "按钮列表。列表项的可用形式有:" + +#: of pywebio.input.actions:10 +msgid "" +"{\n" +" \"label\":(str) button label,\n" +" \"value\":(object) button value,\n" +" \"type\":(str, optional) button type,\n" +" \"disabled\":(bool, optional) whether the button is disabled,\n" +" \"color\":(str, optional) button color\n" +"}" +msgstr "" +"{\n" +" \"label\":(str) 按钮标签,\n" +" \"value\":(object) 按钮值,\n" +" \"type\":(str, optional) 按钮类型,\n" +" \"disabled\":(bool, optional) 是否禁止选择,\n" +" \"color\":(str, optional) 按钮颜色\n" +"\n" +"}" + +#: of pywebio.input.actions:18 +msgid "When ``type='reset'/'cancel'`` or ``disabled=True``, ``value`` can be omitted" +msgstr "若 ``type='reset'/'cancel'`` 或 ``disabled=True`` 可省略 ``value``" + +#: of pywebio.input.actions:19 +msgid "tuple or list: ``(label, value, [type], [disabled])``" +msgstr "" + +#: of pywebio.input.actions:20 +msgid "single value: label and value of button use the same value" +msgstr "单值: 此时label和value使用相同的值" + +#: of pywebio.input.actions:22 +msgid "The ``value`` of button can be any JSON serializable object." +msgstr "其中, ``value`` 可以为任意可JSON序列化的对象。" + +#: of pywebio.input.actions:24 +msgid "``type`` can be:" +msgstr "``type`` 可选值为:" + +#: of pywebio.input.actions:26 +msgid "" +"``'submit'`` : After clicking the button, the entire form is submitted immediately, and the " +"value of this input item in the final form is the ``value`` of the button that was clicked. " +"``'submit'`` is the default value of ``type``" +msgstr "" +"``'submit'`` : 点击按钮后,立即将整个表单提交,最终表单中本项的值为被点击按钮的 ``value`` " +"值。 ``'submit'`` 为 ``type`` 的默认值" + +#: of pywebio.input.actions:29 +msgid "" +"``'cancel'`` : Cancel form. After clicking the button, the entire form will be submitted " +"immediately, and the form value will return ``None``" +msgstr "``'cancel'`` : 取消输入。点击按钮后,立即将整个表单提交,表单值返回 ``None``" + +#: of pywebio.input.actions:31 +msgid "" +"``'reset'`` : Reset form. After clicking the button, the entire form will be reset, and the " +"input items will become the initial state. Note: After clicking the ``type=reset`` button, " +"the form will not be submitted, and the ``actions()`` call will not return" +msgstr "" +"``'reset'`` : 点击按钮后,将整个表单重置,输入项将变为初始状态。\n" +"注意:点击 ``type=reset`` 的按钮后,并不会提交表单, ``actions()`` 调用也不会返回" + +#: of pywebio.input.actions:36 +msgid "" +"The ``color`` of button can be one of: `primary`, `secondary`, `success`, `danger`, " +"`warning`, `info`, `light`, `dark`." +msgstr "" +"按钮的 ``color`` 值可以为: `primary`, `secondary`, `success`, `danger`, `warning`, `info`, " +"`light`, `dark` ." + +#: of pywebio.input.actions:40 +msgid "" +"If the user clicks the ``type=submit`` button to submit the form, return the value of the " +"button clicked by the user. If the user clicks the ``type=cancel`` button or submits the form " +"by other means, ``None`` is returned." +msgstr "" +"若用户点击点击 ``type=submit`` 按钮进行表单提交,返回用户点击的按钮的值;\n" +"若用户点击 ``type=cancel`` 按钮或通过其它方式提交表单,则返回 ``None``" + +#: of pywebio.input.actions:44 +msgid "" +"When ``actions()`` is used as the last input item in `input_group()` and contains a button " +"with ``type='submit'``, the default submit button of the `input_group()` form will be replace " +"with the current ``actions()``" +msgstr "" +"当 ``actions()`` 作为 `input_group()` 中的最后一个输入项、并且含有 ``type='submit'`` 的按钮" +"时,`input_group()` 表单默认的提交按钮会被当前 ``actions()`` 替换" + +#: of pywebio.input.actions:47 +msgid "**usage scenes of actions() **" +msgstr "**actions() 的使用场景**" + +#: of pywebio.input.actions:51 +msgid "Perform simple selection operations:" +msgstr "实现简单的选择操作:" + +#: of pywebio.input.actions:53 +#, python-format +msgid "" +"confirm = actions('Confirm to delete file?', ['confirm', 'cancel'],\n" +" help_text='Unrecoverable after file deletion')\n" +"if confirm=='confirm': # ..doc-only\n" +" ... # ..doc-only\n" +"put_markdown('You clicked the `%s` button' % confirm) # ..demo-only" +msgstr "" +"confirm = actions('确认删除文件?', ['确认', '取消'], help_text='文件删除后不可恢复')\n" +"if confirm=='确认': # ..doc-only\n" +" … # ..doc-only\n" +"put_markdown('点击了`%s`按钮' % confirm) # ..demo-only" + +#: of pywebio.input.actions:63 +msgid "" +"Compared with other input items, when using `actions()`, the user only needs to click once to " +"complete the submission." +msgstr "相比于其他输入项,使用 `actions()` 用户只需要点击一次就可完成提交。" + +#: of pywebio.input.actions:65 +msgid "Replace the default submit button:" +msgstr "替换默认的提交按钮:" + +#: of pywebio.input.actions:67 +msgid "" +"import json # ..demo-only\n" +" # ..demo-only\n" +"info = input_group('Add user', [\n" +" input('username', type=TEXT, name='username', required=True),\n" +" input('password', type=PASSWORD, name='password', required=True),\n" +" actions('actions', [\n" +" {'label': 'Save', 'value': 'save'},\n" +" {'label': 'Save and add next', 'value': 'save_and_continue'},\n" +" {'label': 'Reset', 'type': 'reset', 'color': 'warning'},\n" +" {'label': 'Cancel', 'type': 'cancel', 'color': 'danger'},\n" +" ], name='action', help_text='actions'),\n" +"])\n" +"put_code('info = ' + json.dumps(info, indent=4))\n" +"if info is not None:\n" +" save_user(info['username'], info['password']) # ..doc-only\n" +" if info['action'] == 'save_and_continue':\n" +" add_next() # ..doc-only\n" +" put_text('Save and add next...') # ..demo-only" +msgstr "" + +#: of pywebio.input.file_upload:3 +msgid "" +"Single value or list, indicating acceptable file types. The available formats of file types " +"are: * A valid case-insensitive filename extension, starting with a period (\".\") " +"character. For example: ``.jpg``, ``.pdf``, or ``.doc``. * A valid MIME type string, with no " +"extensions. For examples: ``application/pdf``, ``audio/*``, ``video/*``, ``image/*``. For " +"more information, please visit: https://developer.mozilla.org/en-US/docs/Web/HTTP/" +"Basics_of_HTTP/MIME_types" +msgstr "" + +#: of pywebio.input.file_upload:3 +msgid "" +"Single value or list, indicating acceptable file types. The available formats of file types " +"are:" +msgstr "单值或列表, 表示可接受的文件类型。文件类型的可用形式有:" + +#: of pywebio.input.file_upload:5 +msgid "" +"A valid case-insensitive filename extension, starting with a period (\".\") character. For " +"example: ``.jpg``, ``.pdf``, or ``.doc``." +msgstr "" +"以 ``.`` 字符开始的文件扩展名(例如:``.jpg, .png, .doc``)。\n" +"注意:截至本文档编写之时,微信内置浏览器还不支持这种语法" + +#: of pywebio.input.file_upload:6 +msgid "" +"A valid MIME type string, with no extensions. For examples: ``application/pdf``, ``audio/*``, " +"``video/*``, ``image/*``. For more information, please visit: https://developer.mozilla.org/" +"en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types" +msgstr "" +"一个有效的 MIME 类型。\n" +"例如: ``application/pdf`` 、 ``audio/*`` 表示音频文件、``video/*`` 表示视频文件、``image/*`` " +"表示图片文件。\n" +"参考 https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types" + +#: of pywebio.input.file_upload:11 +msgid "" +"A hint to the user of what to be uploaded. It will appear in the input field when there is no " +"file selected." +msgstr "未上传文件时,文件上传框内显示的文本" + +#: of pywebio.input.file_upload:12 +msgid "Whether to allow upload multiple files. Default is ``False``." +msgstr "是否允许多文件上传,默认关闭" + +#: of pywebio.input.file_upload:13 +msgid "" +"The maximum size of a single file, exceeding the limit will prohibit uploading. The default " +"is 0, which means there is no limit to the size. ``max_size`` can be a integer indicating " +"the number of bytes, or a case-insensitive string ending with `K` / `M` / `G` (representing " +"kilobytes, megabytes, and gigabytes, respectively). E.g: ``max_size=500``, " +"``max_size='40K'``, ``max_size='3M'``" +msgstr "" + +#: of pywebio.input.file_upload:14 +msgid "The maximum size of a single file, exceeding the limit will prohibit uploading." +msgstr "单个文件的最大大小,超过限制将会禁止上传。" + +#: of pywebio.input.file_upload:14 +msgid "The default is 0, which means there is no limit to the size." +msgstr "默认为0,表示不限制上传文件的大小。" + +#: of pywebio.input.file_upload:16 +msgid "" +"``max_size`` can be a integer indicating the number of bytes, or a case-insensitive string " +"ending with `K` / `M` / `G` (representing kilobytes, megabytes, and gigabytes, respectively). " +"E.g: ``max_size=500``, ``max_size='40K'``, ``max_size='3M'``" +msgstr "" +"``max_size`` 值可以为数字表示的字节数,或以 `K` / `M` / `G` 结尾表示的字符串(分别表示 千字节、" +"兆字节、吉字节,大小写不敏感)。例如:\n" +"``max_size=500`` , ``max_size='40K'`` , ``max_size='3M'``" + +#: of pywebio.input.file_upload:20 +msgid "" +"The maximum size of all files. Only available when ``multiple=True``. The default is 0, which " +"means there is no limit to the size. The format is the same as the ``max_size`` parameter" +msgstr "" +"所有文件的最大大小,超过限制将会禁止上传。仅在 ``multiple=True`` 时可用,默认不限制上传文件的" +"大小。 格式同 ``max_size`` 参数" + +#: of pywebio.input.file_upload:22 +msgid "Indicates whether the user must specify a file for the input. Default is ``False``." +msgstr "是否必须要上传文件。默认为 ``False``" + +#: of pywebio.input.file_upload:24 +msgid "" +"When ``multiple=False``, a dict is returned:: { 'filename': file name, " +"'content':content of the file (in bytes), 'mime_type': MIME type of the file, " +"'last_modified': Last modified time (timestamp) of the file } If there is no file uploaded, " +"return ``None``. When ``multiple=True``, a list is returned. The format of the list item is " +"the same as the return value when ``multiple=False`` above. If the user does not upload a " +"file, an empty list is returned." +msgstr "" + +#: of pywebio.input.file_upload:24 +msgid "When ``multiple=False``, a dict is returned::" +msgstr "``multiple=False`` 时(默认),返回dict::" + +#: of pywebio.input.file_upload:26 +msgid "" +"{\n" +" 'filename': file name,\n" +" 'content':content of the file (in bytes),\n" +" 'mime_type': MIME type of the file,\n" +" 'last_modified': Last modified time (timestamp) of the file\n" +"}" +msgstr "" +"{\n" +" 'filename': 文件名, \n" +" 'content':文件二进制数据(bytes object), \n" +" 'mime_type': 文件的MIME类型, \n" +" 'last_modified': 文件上次修改时间(时间戳) \n" +"}" + +#: of pywebio.input.file_upload:33 +msgid "If there is no file uploaded, return ``None``." +msgstr "若用户没有上传文件,返回 ``None`` 。" + +#: of pywebio.input.file_upload:35 +msgid "" +"When ``multiple=True``, a list is returned. The format of the list item is the same as the " +"return value when ``multiple=False`` above. If the user does not upload a file, an empty list " +"is returned." +msgstr "" +"``multiple=True`` 时,返回列表,列表项格式同上文 ``multiple=False`` 时的返回值;若用户没有上传" +"文件,返回空列表。" + +#: of pywebio.input.file_upload:40 +msgid "" +"If uploading large files, please pay attention to the file upload size limit setting of the " +"web framework. When using :func:`start_server() ` or :" +"func:`path_deploy() ` to start the PyWebIO application, the " +"maximum file size to be uploaded allowed by the web framework can be set through the " +"``max_payload_size`` parameter." +msgstr "" +"若上传大文件请留意Web框架的文件上传大小限制设置。在使用 :func:`start_server() ` 或 :func:`path_deploy() ` 启动" +"PyWebIO应用时,\n" +"可通过 `max_payload_size` 参数设置Web框架允许上传的最大文件大小" + +#: of pywebio.input.file_upload:45 +msgid "" +"# Upload a file and save to server # ..doc-only\n" +"f = input.file_upload(\"Upload a file\") # ..doc-only\n" +"open('asset/'+f['filename'], 'wb').write(f['content']) # ..doc-only\n" +"\n" +"imgs = file_upload(\"Select some pictures:\", accept=\"image/*\", multiple=True)\n" +"for img in imgs:\n" +" put_image(img['content'])" +msgstr "" + +#: of pywebio.input.slider:1 +msgid "Range input." +msgstr "滑块输入" + +#: of pywebio.input.slider:3 +msgid "The initial value of the slider." +msgstr "滑块的初始值" + +#: of pywebio.input.slider:4 +msgid "The minimum permitted value." +msgstr "滑块最小允许的值" + +#: of pywebio.input.slider:5 +msgid "The maximum permitted value." +msgstr "滑块最大允许的值" + +#: of pywebio.input.slider:6 +msgid "" +"The stepping interval. Only available when ``value``, ``min_value`` and ``max_value`` are all " +"integer." +msgstr "滑动的步长。仅当 ``value``、 ``min_value`` 和 ``max_value`` 全为int时有效" + +#: of pywebio.input.slider +msgid "return int/float" +msgstr "" + +#: of pywebio.input.slider:9 +msgid "" +"If one of ``value``, ``min_value`` and ``max_value`` is float, the return value is a float, " +"otherwise an int is returned." +msgstr "" +"若 ``value``, ``min_value`` 和 ``max_value`` 中含有float类型,则返回值为float,否则返回值为int" +"类型" + +#: of pywebio.input.input_group:1 +msgid "Input group. Request a set of inputs from the user at once." +msgstr "输入组。向页面上展示一组输入" + +#: of pywebio.input.input_group:3 +msgid "Label of input group." +msgstr "输入组标签" + +#: of pywebio.input.input_group:4 +msgid "" +"Input items. The item of the list is the call to the single input function, and the ``name`` " +"parameter need to be passed in the single input function." +msgstr "输入项列表。列表的内容为对单项输入函数的调用,并在单项输入函数中传入 ``name`` 参数。" + +#: of pywebio.input.input_group:6 +msgid "" +"validation function for the group. If provided, the validation function will be called when " +"the user submits the form. Function signature: ``callback(data) -> (name, error_msg)``. " +"``validate`` receives the value of the entire group as a parameter. When the form value is " +"valid, it returns ``None``. When an input item's value is invalid, it returns the ``name`` " +"value of the item and an error message. For example:" +msgstr "" + +#: of pywebio.input.input_group:6 +msgid "" +"validation function for the group. If provided, the validation function will be called when " +"the user submits the form." +msgstr "输入组校验函数。" + +#: of pywebio.input.input_group:8 +msgid "" +"Function signature: ``callback(data) -> (name, error_msg)``. ``validate`` receives the value " +"of the entire group as a parameter. When the form value is valid, it returns ``None``. When " +"an input item's value is invalid, it returns the ``name`` value of the item and an error " +"message. For example:" +msgstr "" +"函数签名:``callback(data) -> (name, error_msg)``\n" +"``validate`` 接收整个表单的值为参数,当校验表单值有效时,返回 ``None`` ,当某项输入值无效时," +"返回出错输入项的 ``name`` 值和错误提示. 比如:" + +#: of pywebio.input.input_group:13 +msgid "" +"def check_form(data):\n" +" if len(data['name']) > 6:\n" +" return ('name', 'Name to long!')\n" +" if data['age'] <= 0:\n" +" return ('age', 'Age cannot be negative!')\n" +"\n" +"data = input_group(\"Basic info\",[\n" +" input('Input your name', name='name'),\n" +" input('Repeat your age', name='age', type=NUMBER)\n" +"], validate=check_form)\n" +"\n" +"put_text(data['name'], data['age'])" +msgstr "" + +#: of pywebio.input.input_group:30 +msgid "" +"Whether the form can be cancelled. Default is ``False``. If ``cancelable=True``, a \"Cancel\" " +"button will be displayed at the bottom of the form. Note: If the last input item in the " +"group is `actions()`, ``cancelable`` will be ignored." +msgstr "" + +#: of pywebio.input.input_group:30 +msgid "" +"Whether the form can be cancelled. Default is ``False``. If ``cancelable=True``, a \"Cancel\" " +"button will be displayed at the bottom of the form." +msgstr "" +"表单是否可以取消。若 ``cancelable=True`` 则会在表单底部显示一个“取消”按钮,默认为 ``False`` 。" + +#: of pywebio.input.input_group:33 +msgid "" +"Note: If the last input item in the group is `actions()`, ``cancelable`` will be ignored." +msgstr "注意:若 ``inputs`` 中最后一项输入为 `actions()` ,则忽略 ``cancelable``" + +#: of pywebio.input.input_group:35 +msgid "" +"If the user cancels the form, return ``None``, otherwise a ``dict`` is returned, whose key is " +"the ``name`` of the input item, and whose value is the value of the input item." +msgstr "" +"若用户取消表单,返回 ``None`` ,否则返回一个 ``dict`` , 其键为输入项的 ``name`` 值,字典值为输" +"入项的值" + +#: of pywebio.input.input_update:1 +msgid "" +"Update attributes of input field. This function can only be called in ``onchange`` callback " +"of input functions." +msgstr "更新输入项的属性。本函数仅能在输入函数的 ``onchange`` 回调中使用。" + +#: of pywebio.input.input_update:4 +msgid "" +"The ``name`` of the target input item. Optional, default is the name of input field which " +"triggers ``onchange``" +msgstr "目标输入项的 ``name`` 。可选,默认为当前触发 ``onchange`` 回调的输入项" + +#: of pywebio.input.input_update:6 +msgid "" +"The input parameters need to be updated. Note that those parameters can not be updated: " +"``type``, ``name``, ``validate``, ``action``, ``code``, ``onchange``, ``multiple``" +msgstr "" +"需要更新的输入项参数。注意一下参数无法被更新:``type``, ``name``, ``validate``, ``action``, " +"``code``, ``onchange``, ``multiple``" + +#: of pywebio.input.input_update:10 +msgid "An example of implementing dependent input items in an input group:" +msgstr "一个具有依赖关系的输入项的示例:" + +#: of pywebio.input.input_update:12 +msgid "" +"country2city = {\n" +" 'China': ['Beijing', 'Shanghai', 'Hong Kong'],\n" +" 'USA': ['New York', 'Los Angeles', 'San Francisco'],\n" +"}\n" +"countries = list(country2city.keys())\n" +"location = input_group(\"Select a location\", [\n" +" select('Country', options=countries, name='country',\n" +" onchange=lambda c: input_update('city', options=country2city[c])),\n" +" select('City', options=country2city[countries[0]], name='city'),\n" +"])\n" +"put_text(location) # ..demo-only" +msgstr "" diff --git a/docs/locales/zh_CN/LC_MESSAGES/libraries_support.po b/docs/locales/zh_CN/LC_MESSAGES/libraries_support.po new file mode 100644 index 00000000..b4acc6d1 --- /dev/null +++ b/docs/locales/zh_CN/LC_MESSAGES/libraries_support.po @@ -0,0 +1,289 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) WangWeimin +# This file is distributed under the same license as the PyWebIO package. +# FIRST AUTHOR , 2021. +# +msgid "" +msgstr "" +"Project-Id-Version: PyWebIO 1.1.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-03-14 23:25+0800\n" +"PO-Revision-Date: 2021-03-19 22:18+0800\n" +"Last-Translator: WangWeimin \n" +"Language: zh_CN\n" +"Language-Team: \n" +"Plural-Forms: nplurals=1; plural=0;\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.8.0\n" +"X-Generator: Poedit 2.4.2\n" + +#: ../../libraries_support.rst:2 +msgid "Libraries support" +msgstr "第三方库生态" + +#: ../../libraries_support.rst:5 +msgid "Build stand-alone App" +msgstr "构建stand-alone App" + +#: ../../libraries_support.rst:6 +msgid "" +"`PyInstaller `_ bundles a Python " +"application and all its dependencies into a folder or executable. The user can " +"run the packaged app without installing a Python interpreter or any modules." +msgstr "" +"`PyInstaller `_ 用于将一个Python" +"应用及其依赖打包到文件夹或可执行文件中,用户可以在不安装Python解释器以及任何模块" +"的情况下运行打包后的应用程序。" + +#: ../../libraries_support.rst:8 +msgid "" +"You can use PyInstaller to packages PyWebIO application into a stand-alone " +"executable or folder:" +msgstr "可以使用PyInstaller来将PyWebIO应用打包成一个单独的可执行文件或文件夹:" + +#: ../../libraries_support.rst:10 +msgid "Create a pyinstaller spec (specification) file::" +msgstr "创建pyinstaller spec (specification) 文件::" + +#: ../../libraries_support.rst:12 +msgid "pyi-makespec app.py" +msgstr "" + +#: ../../libraries_support.rst:14 +msgid "You need replace ``app.py`` to your PyWebIO application file name." +msgstr "你需要将 ``app.py`` 替换成你PyWebIO应用的文件名。" + +#: ../../libraries_support.rst:16 +msgid "Edit the spec file, change the ``datas`` parameter of ``Analysis``::" +msgstr "编辑生成的spec文件,将其中 ``Analysis`` 的 ``data`` 参数修改为::" + +#: ../../libraries_support.rst:18 +msgid "" +"from pywebio.utils import pyinstaller_datas\n" +"\n" +"a = Analysis(\n" +" ...\n" +" datas=pyinstaller_datas(),\n" +" ..." +msgstr "" + +#: ../../libraries_support.rst:25 +msgid "" +"Build the application by passing the spec file to the pyinstaller command::" +msgstr "使用spec文件来构建可执行文件::" + +#: ../../libraries_support.rst:27 +msgid "pyinstaller app.spec" +msgstr "" + +#: ../../libraries_support.rst:30 +msgid "" +"If you want to create a one-file bundled executable, you need pass ``--" +"onefile`` option in first step." +msgstr "" +"如果你希望生成一个单独的可执行文件而不是文件夹,你需要在第一步时传入 ``--" +"onefile`` 选项。" + +#: ../../libraries_support.rst:32 +msgid "" +"For more information, please visit: https://pyinstaller.readthedocs.io/en/" +"stable/spec-files.html" +msgstr "" +"更多PyInstaller用法请见: https://pyinstaller.readthedocs.io/en/stable/spec-" +"files.html" + +#: ../../libraries_support.rst:38 +msgid "Data visualization" +msgstr "数据可视化" + +#: ../../libraries_support.rst:39 +msgid "PyWebIO supports for data visualization with the third-party libraries." +msgstr "PyWebIO支持使用第三方库进行数据可视化" + +#: ../../libraries_support.rst:42 +msgid "Bokeh" +msgstr "" + +#: ../../libraries_support.rst:44 +msgid "" +"`Bokeh `_ is an interactive visualization " +"library for modern web browsers. It provides elegant, concise construction of " +"versatile graphics, and affords high-performance interactivity over large or " +"streaming datasets." +msgstr "" +"`Bokeh `_ 是一个支持创建实时交互的数据可视化库。" + +#: ../../libraries_support.rst:46 +msgid "" +"You can use ``bokeh.io.output_notebook(notebook_type='pywebio')`` in the " +"PyWebIO session to setup Bokeh environment. Then you can use ``bokeh.io." +"show()`` to output a boken chart::" +msgstr "" +"在 PyWebIO 会话中调用 ``bokeh.io.output_notebook(notebook_type='pywebio')`` 来设" +"置Bokeh输出到PyWebIO::" + +#: ../../libraries_support.rst:49 +msgid "" +"from bokeh.io import output_notebook\n" +"from bokeh.io import show\n" +"\n" +"output_notebook(notebook_type='pywebio')\n" +"fig = figure(...)\n" +"...\n" +"show(fig)" +msgstr "" + +#: ../../libraries_support.rst:57 +msgid "See related demo on :charts_demo_host:`bokeh demo `" +msgstr "相应demo见 :charts_demo_host:`bokeh demo `" + +#: ../../libraries_support.rst:59 +msgid "" +"In addition to creating ordinary charts, Bokeh can also build the Bokeh " +"applications by starting the `Bokeh server `_. The purpose of the Bokeh server is to make it " +"easy for Python users to create interactive web applications that can connect " +"front-end UI events to real, running Python code." +msgstr "" +"除了创建普通图表,Bokeh还可以通过启动Bokeh server来显示Bokeh app,Bokeh app支持" +"向图表的添加按钮、输入框等交互组件,并向组件注册Python回调,从而创建可以与Python" +"代码交互的图表。" + +#: ../../libraries_support.rst:61 +msgid "" +"In PyWebIO, you can also use ``bokeh.io.show()`` to display a Bokeh App. For " +"the example, see `bokeh_app.py `_." +msgstr "" +"在PyWebIO中,你也可以使用 ``bokeh.io.show()`` 来显示一个Bokeh App,代码示例见 " +"`bokeh_app.py `_。" + +#: ../../libraries_support.rst:63 +msgid "Bokeh App currently is only available in the default Tornado backend" +msgstr "Bokeh App当前仅支持默认的Tornado后端" + +#: ../../libraries_support.rst:68 +msgid "pyecharts" +msgstr "" + +#: ../../libraries_support.rst:70 +msgid "" +"`pyecharts `_ is a python plotting " +"library which uses `Echarts `_ as underlying " +"implementation." +msgstr "" +"`pyecharts `_ 是一个使用Python创建 " +"`Echarts `_ 可视化图表的库。" + +#: ../../libraries_support.rst:72 +msgid "" +"In PyWebIO, you can use the following code to output the pyecharts chart " +"instance::" +msgstr "" +"在 PyWebIO 中使用 `put_html() ` 可以输出 pyecharts 库创" +"建的图表::" + +#: ../../libraries_support.rst:74 +msgid "" +"# `chart` is pyecharts chart instance\n" +"pywebio.output.put_html(chart.render_notebook())" +msgstr "" + +#: ../../libraries_support.rst:77 +msgid "See related demo on :charts_demo_host:`pyecharts demo `" +msgstr "相应demo见 :charts_demo_host:`pyecharts demo `" + +#: ../../libraries_support.rst:84 +msgid "plotly" +msgstr "" + +#: ../../libraries_support.rst:86 +msgid "" +"`plotly.py `_ is an interactive, open-" +"source, and browser-based graphing library for Python." +msgstr "" +"`plotly.py `_ 是一个非常流行的Python数据可" +"视化库,可以生成高质量的交互式图表。" + +#: ../../libraries_support.rst:88 +msgid "" +"In PyWebIO, you can use the following code to output the plotly chart instance::" +msgstr "PyWebIO 支持输出使用 plotly 库创建的图表。使用方式为在PyWebIO会话中调用::" + +#: ../../libraries_support.rst:90 +msgid "" +"# `fig` is plotly chart instance\n" +"html = fig.to_html(include_plotlyjs=\"require\", full_html=False)\n" +"pywebio.output.put_html(html)" +msgstr "" + +#: ../../libraries_support.rst:94 +msgid "See related demo on :charts_demo_host:`plotly demo `" +msgstr "相应demo见 :charts_demo_host:`plotly demo `" + +#: ../../libraries_support.rst:99 +msgid "pyg2plot" +msgstr "" + +#: ../../libraries_support.rst:101 +msgid "" +"`pyg2plot `_ is a python plotting library " +"which uses `G2Plot `_ as underlying " +"implementation." +msgstr "" +"`pyg2plot `_ 是一个使用Python创建 `G2Plot " +"`_ 可视化图表的库。" + +#: ../../libraries_support.rst:103 +msgid "" +"In PyWebIO, you can use the following code to output the pyg2plot chart " +"instance::" +msgstr "" +"PyWebIO 支持输出使用 pyg2plot 库创建的图表。使用方式为在PyWebIO会话中调用::" + +#: ../../libraries_support.rst:105 +msgid "" +"# `chart` is pyg2plot chart instance\n" +"pywebio.output.put_html(chart.render_notebook())" +msgstr "" +"# `chart` 为 pyg2plot 图表实例\n" +"pywebio.output.put_html(chart.render_notebook())" + +#: ../../libraries_support.rst:108 +msgid "See related demo on :charts_demo_host:`plotly demo `" +msgstr "相应demo见 :charts_demo_host:`plotly demo `" + +#: ../../libraries_support.rst:111 +msgid "cutecharts.py" +msgstr "" + +#: ../../libraries_support.rst:113 +msgid "" +"`cutecharts.py `_ is a hand " +"drawing style charts library for Python which uses `chart.xkcd `_ as underlying implementation." +msgstr "" +"`cutecharts.py `_ 是一个可以创建具" +"有卡通风格的可视化图表的python库。\n" +"底层使用了 `chart.xkcd `_ Javascript库。" + +#: ../../libraries_support.rst:115 +msgid "" +"In PyWebIO, you can use the following code to output the cutecharts.py chart " +"instance::" +msgstr "" +"在 PyWebIO 中使用 `put_html() ` 可以输出 cutecharts.py " +"库创建的图表::" + +#: ../../libraries_support.rst:117 +msgid "" +"# `chart` is cutecharts chart instance\n" +"pywebio.output.put_html(chart.render_notebook())" +msgstr "" + +#: ../../libraries_support.rst:120 +msgid "See related demo on :charts_demo_host:`cutecharts demo `" +msgstr "相应demo见 :charts_demo_host:`cutecharts demo `" diff --git a/docs/locales/zh_CN/LC_MESSAGES/misc.po b/docs/locales/zh_CN/LC_MESSAGES/misc.po new file mode 100644 index 00000000..8fb7256c --- /dev/null +++ b/docs/locales/zh_CN/LC_MESSAGES/misc.po @@ -0,0 +1,163 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) WangWeimin +# This file is distributed under the same license as the PyWebIO package. +# FIRST AUTHOR , 2021. +# +msgid "" +msgstr "" +"Project-Id-Version: PyWebIO 1.1.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-10-05 00:44+0800\n" +"PO-Revision-Date: 2021-04-14 21:17+0800\n" +"Last-Translator: WangWeimin \n" +"Language: zh_CN\n" +"Language-Team: \n" +"Plural-Forms: nplurals=1; plural=0\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.8.0\n" + +#: ../../misc.rst:2 +msgid "Miscellaneous" +msgstr "其他" + +#: ../../misc.rst:7 +msgid "Commonly used Codemirror options" +msgstr "常用的Codemirror选项" + +#: ../../misc.rst:9 +msgid "" +"``mode`` (str): The language of code. For complete list, see " +"https://codemirror.net/mode/index.html" +msgstr "``mode`` (str): 代码语言。支持的语言有:https://codemirror.net/mode/index.html" + +#: ../../misc.rst:10 +msgid "" +"``theme`` (str): The theme to style the editor with. For all available " +"theme, see https://codemirror.net/demo/theme.html" +msgstr "``theme`` (str): 编辑器主题。可使用的主题:https://codemirror.net/demo/theme.html" + +#: ../../misc.rst:11 +msgid "" +"``lineNumbers`` (bool): Whether to show line numbers to the left of the " +"editor." +msgstr "``lineNumbers`` (bool): 是否显示行号" + +#: ../../misc.rst:12 +msgid "" +"``indentUnit`` (int): How many spaces a block (whatever that means in the " +"edited language) should be indented. The default is 2." +msgstr "``indentUnit`` (int): 缩进使用的空格数" + +#: ../../misc.rst:13 +msgid "``tabSize`` (int): The width of a tab character. Defaults to 4." +msgstr "``tabSize`` (int): 制表符宽度" + +#: ../../misc.rst:14 +msgid "" +"``lineWrapping`` (bool): Whether CodeMirror should scroll or wrap for long " +"lines. Defaults to false (scroll)." +msgstr "``lineWrapping`` (bool): 是否换行以显示长行" + +#: ../../misc.rst:16 +msgid "" +"For complete Codemirror options, please visit: " +"https://codemirror.net/doc/manual.html#config" +msgstr "完整的Codemirror选项请见 https://codemirror.net/doc/manual.html#config" + +#: ../../misc.rst:21 +msgid "Nginx WebSocket Config Example" +msgstr "Nginx WebSocket配置示例" + +#: ../../misc.rst:23 +msgid "" +"Assuming that the PyWebIO application is running at the ``localhost:5000`` " +"address, if you want to access your PyWebIO application via " +"``http://server_ip/some_path/tool``, a sample Nginx configuration is as " +"follows::" +msgstr "" +"假设PyWebIO应用运行在 ``localhost:5000`` 地址,如果你想通过 " +"``http://server_ip/some_path/tool`` 来访问应用,需要的Nginx配置如下::" + +#: ../../misc.rst:25 +msgid "" +"map $http_upgrade $connection_upgrade {\n" +" default upgrade;\n" +" '' close;\n" +"}\n" +"\n" +"server {\n" +" listen 80;\n" +"\n" +" location /some_path/ {\n" +" alias /path/to/pywebio/static/dir/;\n" +" }\n" +" location /some_path/tool {\n" +" proxy_read_timeout 300s;\n" +" proxy_send_timeout 300s;\n" +" proxy_http_version 1.1;\n" +" proxy_set_header Host $http_host;\n" +" proxy_set_header Upgrade $http_upgrade;\n" +" proxy_set_header Connection $connection_upgrade;\n" +" proxy_pass http://localhost:5000/;\n" +" }\n" +"}" +msgstr "" + +#: ../../misc.rst:48 +msgid "" +"The above configuration file hosts the static files of PyWebIO on the " +"``/some_path/`` path, and reverse proxy ``/some_path/tool`` to " +"``localhost:5000``." +msgstr "" +"以上配置文件将PyWebIO的静态文件托管到 ``/some_path/`` 目录下, 并将 ``/some_path/tool`` 反向代理到 " +"``localhost:5000``" + +#: ../../misc.rst:50 +msgid "" +"The path of the static file of PyWebIO can be obtained with the command " +"``python3 -c \"import pywebio; print(pywebio.STATIC_PATH)\"``, you can also " +"copy the static file to other directories::" +msgstr "" +"PyWebIO的静态文件的路径可使用命令 ``python3 -c \"import pywebio; " +"print(pywebio.STATIC_PATH)\"`` 获得,你也可以将静态文件复制到其他目录下::" + +#: ../../misc.rst:52 +msgid "cp -r `python3 -c \"import pywebio; print(pywebio.STATIC_PATH)\"` ~/web" +msgstr "" + +#~ msgid "" +#~ "Assuming that the backend server is " +#~ "running at the ``localhost:5000`` address, " +#~ "and the backend API of PyWebIO is " +#~ "bind to the ``/tool`` path, the " +#~ "configuration of Nginx is as follows::" +#~ msgstr "" +#~ "假设后端服务器运行在 ``localhost:5000`` 地址,并将PyWebIO的后端接口绑定到 " +#~ "``/tool`` 路径上,则通过Nginx访问PyWebIO服务的配置如下::" + +#~ msgid "" +#~ "map $http_upgrade $connection_upgrade {\n" +#~ " default upgrade;\n" +#~ " '' close;\n" +#~ "}\n" +#~ "\n" +#~ "server {\n" +#~ " listen 80;\n" +#~ "\n" +#~ " location /some_path/ {\n" +#~ " alias /path/to/pywebio/static/dir/;\n" +#~ " }\n" +#~ " location /some_path/tool {\n" +#~ " proxy_read_timeout 300s;\n" +#~ " proxy_send_timeout 300s;\n" +#~ " proxy_http_version 1.1;\n" +#~ " proxy_set_header Host $host:$server_port;\n" +#~ " proxy_set_header Upgrade $http_upgrade;\n" +#~ " proxy_set_header Connection $connection_upgrade;\n" +#~ " proxy_pass http://localhost:5000/;\n" +#~ " }\n" +#~ "}" +#~ msgstr "" + diff --git a/docs/locales/zh_CN/LC_MESSAGES/output.po b/docs/locales/zh_CN/LC_MESSAGES/output.po new file mode 100644 index 00000000..75f860cf --- /dev/null +++ b/docs/locales/zh_CN/LC_MESSAGES/output.po @@ -0,0 +1,1907 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) WangWeimin +# This file is distributed under the same license as the PyWebIO package. +# FIRST AUTHOR , 2021. +# +msgid "" +msgstr "" +"Project-Id-Version: PyWebIO 1.1.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-12-02 21:36+0800\n" +"PO-Revision-Date: 2021-12-02 21:52+0800\n" +"Last-Translator: WangWeimin \n" +"Language: zh_CN\n" +"Language-Team: \n" +"Plural-Forms: nplurals=1; plural=0;\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.9.1\n" +"X-Generator: Poedit 3.0\n" + +#: ../../output.rst:2 +msgid "``pywebio.output`` --- Make output to web browser" +msgstr "``pywebio.output`` — 输出模块" + +#: of pywebio.output:1 +msgid "" +"This module provides functions to output all kinds of content to the user's browser, and supply " +"flexible output control." +msgstr "本模块提供了一系列函数来输出不同形式的内容到用户浏览器,并支持灵活的输出控制。" + +#: of pywebio.output:8 +msgid "Functions list" +msgstr "函数清单" + +#: of pywebio.output:12 +msgid "The following table shows the output-related functions provided by PyWebIO." +msgstr "下表为PyWebIO提供的输出相关的函数。" + +#: of pywebio.output:13 +msgid "The functions marked with ``*`` indicate that they accept ``put_xxx`` calls as arguments." +msgstr "其中标记有 ``*`` 的函数表示其支持接收 ``put_xxx`` 调用作为参数。" + +#: of pywebio.output:14 +msgid "The functions marked with ``†`` indicate that they can use as context manager." +msgstr "标记有 ``†`` 的函数表示其支持作为上下文管理器使用。" + +#: of pywebio.output:17 +msgid "**Name**" +msgstr "**函数**" + +#: of pywebio.output:17 +msgid "**Description**" +msgstr "**简介** " + +#: of pywebio.output:19 pywebio.output:87 +msgid "Output Scope" +msgstr "输出域Scope" + +#: of pywebio.output:19 +msgid "`put_scope`" +msgstr "`put_scope`" + +#: of pywebio.output:19 +msgid "Create a new scope" +msgstr "创建一个新的scope. " + +#: of pywebio.output:21 +msgid "`use_scope`:sup:`†`" +msgstr "" + +#: of pywebio.output:21 +msgid "Enter a scope" +msgstr "进入输出域" + +#: of pywebio.output:23 +msgid "`get_scope`" +msgstr "" + +#: of pywebio.output:23 +msgid "Get the current scope name in the runtime scope stack" +msgstr "获取当前正在使用的输出域" + +#: of pywebio.output:25 +msgid "`clear`" +msgstr "" + +#: of pywebio.output:25 +msgid "Clear the content of scope" +msgstr "清空scope内容 " + +#: of pywebio.output:27 +msgid "`remove`" +msgstr "" + +#: of pywebio.output:27 +msgid "Remove the scope" +msgstr "移除Scope" + +#: of pywebio.output:29 +msgid "`scroll_to`" +msgstr "" + +#: of pywebio.output:29 +msgid "Scroll the page to the scope" +msgstr "将页面滚动到 scope Scope处" + +#: of pywebio.output:31 pywebio.output:101 +msgid "Content Outputting" +msgstr "内容输出" + +#: of pywebio.output:31 +msgid "`put_text`" +msgstr "" + +#: of pywebio.output:31 pywebio.output.put_text:1 +msgid "Output plain text" +msgstr "输出文本" + +#: of pywebio.output:33 +msgid "`put_markdown`" +msgstr "" + +#: of pywebio.output:33 pywebio.output.put_markdown:1 +msgid "Output Markdown" +msgstr "输出Markdown" + +#: of pywebio.output +msgid "`put_info`:sup:`*†`" +msgstr "" + +#: of pywebio.output +msgid "`put_success`:sup:`*†`" +msgstr "" + +#: of pywebio.output +msgid "`put_warning`:sup:`*†`" +msgstr "" + +#: of pywebio.output +msgid "`put_error`:sup:`*†`" +msgstr "" + +#: of pywebio.output:35 pywebio.output:164 +msgid "Output Messages." +msgstr "输出通知消息" + +#: of pywebio.output:40 +msgid "`put_html`" +msgstr "" + +#: of pywebio.output:40 +msgid "Output html" +msgstr "输出Html" + +#: of pywebio.output:42 +msgid "`put_link`" +msgstr "" + +#: of pywebio.output:42 +msgid "Output link" +msgstr "输出链接" + +#: of pywebio.output:44 +msgid "`put_processbar`" +msgstr "" + +#: of pywebio.output:44 pywebio.output.put_processbar:1 +msgid "Output a process bar" +msgstr "输出进度条" + +#: of pywebio.output:46 +msgid "`put_loading`:sup:`†`" +msgstr "" + +#: of pywebio.output:46 pywebio.output.put_loading:1 +msgid "Output loading prompt" +msgstr "输出加载提示" + +#: of pywebio.output:48 +msgid "`put_code`" +msgstr "" + +#: of pywebio.output:48 pywebio.output.put_code:1 +msgid "Output code block" +msgstr "输出代码块" + +#: of pywebio.output:50 +msgid "`put_table`:sup:`*`" +msgstr "" + +#: of pywebio.output:50 pywebio.output.put_table:1 +msgid "Output table" +msgstr "输出表格" + +#: of pywebio.output +msgid "`put_button`" +msgstr "" + +#: of pywebio.output +msgid "`put_buttons`" +msgstr "" + +#: of pywebio.output:52 +msgid "Output button and bind click event" +msgstr "输出按钮,并绑定点击事件" + +#: of pywebio.output:55 +msgid "`put_image`" +msgstr "" + +#: of pywebio.output:55 pywebio.output.put_image:1 +msgid "Output image" +msgstr "输出图片" + +#: of pywebio.output:57 +msgid "`put_file`" +msgstr "" + +#: of pywebio.output:57 pywebio.output.put_file:1 +msgid "Output a link to download a file" +msgstr "显示一个文件下载链接" + +#: of pywebio.output:59 +msgid "`put_tabs`:sup:`*`" +msgstr "" + +#: of pywebio.output:59 +msgid "Output tabs" +msgstr "输出横向标签栏Tabs" + +#: of pywebio.output:61 +msgid "`put_collapse`:sup:`*†`" +msgstr "" + +#: of pywebio.output:61 pywebio.output.put_collapse:1 +msgid "Output collapsible content" +msgstr "输出可折叠的内容" + +#: of pywebio.output:63 +msgid "`put_scrollable`:sup:`*†`" +msgstr "" + +#: of pywebio.output +msgid "Output a fixed height content area," +msgstr "固定高度内容输出区域" + +#: of pywebio.output +msgid "scroll bar is displayed when the content" +msgstr "内容超出则显示滚动条" + +#: of pywebio.output +msgid "exceeds the limit" +msgstr "." + +#: of pywebio.output:67 +msgid "`put_widget`:sup:`*`" +msgstr "" + +#: of pywebio.output:67 pywebio.output.put_widget:1 +msgid "Output your own widget" +msgstr "输出自定义的控件" + +#: of pywebio.output:69 pywebio.output:191 +msgid "Other Interactions" +msgstr "其他交互" + +#: of pywebio.output:69 +msgid "`toast`" +msgstr "" + +#: of pywebio.output:69 +msgid "Show a notification message" +msgstr "显示一条通知消息" + +#: of pywebio.output:71 +msgid "`popup`:sup:`*†`" +msgstr "" + +#: of pywebio.output:71 +msgid "Show popup" +msgstr "显示弹窗" + +#: of pywebio.output:73 +msgid "`close_popup`" +msgstr "" + +#: of pywebio.output:73 pywebio.output.close_popup:1 +msgid "Close the current popup window." +msgstr "关闭正在显示的弹窗" + +#: of pywebio.output:75 pywebio.output:199 +msgid "Layout and Style" +msgstr "布局与样式" + +#: of pywebio.output:75 +msgid "`put_row`:sup:`*†`" +msgstr "" + +#: of pywebio.output:75 +msgid "Use row layout to output content" +msgstr "使用行布局输出内容" + +#: of pywebio.output:77 +msgid "`put_column`:sup:`*†`" +msgstr "" + +#: of pywebio.output:77 +msgid "Use column layout to output content" +msgstr "使用列布局输出内容" + +#: of pywebio.output:79 +msgid "`put_grid`:sup:`*`" +msgstr "" + +#: of pywebio.output:79 pywebio.output.put_grid:1 +msgid "Output content using grid layout" +msgstr "使用网格布局输出内容" + +#: of pywebio.output:81 +msgid "`span`" +msgstr "" + +#: of pywebio.output:81 +msgid "Cross-cell content" +msgstr "在 `put_table()` 和 `put_grid()` 中设置内容跨单元格" + +#: of pywebio.output:83 +msgid "`style`:sup:`*`" +msgstr "" + +#: of pywebio.output:83 pywebio.output.style:1 +msgid "Customize the css style of output content" +msgstr "自定义输出内容的css样式" + +#: of pywebio.output:91 +msgid ":ref:`Use Guide: Output Scope `" +msgstr "" + +#: of pywebio.output.put_scope:1 +msgid "Output a scope" +msgstr "输出一个输出域" + +#: of pywebio.output pywebio.output.clear pywebio.output.get_scope pywebio.output.popup +#: pywebio.output.put_button pywebio.output.put_buttons pywebio.output.put_code +#: pywebio.output.put_collapse pywebio.output.put_column pywebio.output.put_file +#: pywebio.output.put_grid pywebio.output.put_html pywebio.output.put_image pywebio.output.put_link +#: pywebio.output.put_loading pywebio.output.put_markdown pywebio.output.put_processbar +#: pywebio.output.put_row pywebio.output.put_scope pywebio.output.put_scrollable +#: pywebio.output.put_table pywebio.output.put_tabs pywebio.output.put_text +#: pywebio.output.put_widget pywebio.output.remove pywebio.output.scroll_to +#: pywebio.output.set_processbar pywebio.output.span pywebio.output.style pywebio.output.toast +#: pywebio.output.use_scope +msgid "Parameters" +msgstr "" + +#: of pywebio.output.put_scope:4 +msgid "The initial content of the scope, can be ``put_xxx()`` or a list of it." +msgstr "输出域里的初始内容,可以为 ``put_xxx()`` 调用或其列表。" + +#: of pywebio.output:169 pywebio.output.put_buttons:46 pywebio.output.put_code:6 +#: pywebio.output.put_collapse:7 pywebio.output.put_column:6 pywebio.output.put_file:8 +#: pywebio.output.put_grid:17 pywebio.output.put_html:5 pywebio.output.put_image:10 +#: pywebio.output.put_link:7 pywebio.output.put_loading:6 pywebio.output.put_markdown:10 +#: pywebio.output.put_processbar:7 pywebio.output.put_row:19 pywebio.output.put_scope:5 +#: pywebio.output.put_scrollable:11 pywebio.output.put_table:13 pywebio.output.put_tabs:5 +#: pywebio.output.put_widget:11 +msgid "Those arguments have the same meaning as for `put_text()`" +msgstr "与 `put_text` 函数的同名参数含义一致" + +#: of pywebio.output.use_scope:1 +msgid "Open or enter a scope. Can be used as context manager and decorator." +msgstr "scope的上下文管理器和装饰器。用于创建一个新的输出域并进入,或进入一个已经存在的输出域。" + +#: of pywebio.output.use_scope:3 +msgid "See :ref:`User manual - use_scope() `" +msgstr "参见 :ref:`用户手册-use_scope() `" + +#: of pywebio.output.use_scope:5 +msgid "" +"Scope name. If it is None, a globally unique scope name is generated. (When used as context " +"manager, the context manager will return the scope name)" +msgstr "" +"scope名. 若为None则生成一个全局唯一的scope名.(以上下文管理器形式的调用时,上下文管理器会返回" +"scope名)" + +#: of pywebio.output.use_scope:7 +msgid "Whether to clear the contents of the scope before entering the scope." +msgstr "在进入scope前是否要清除scope里的内容" + +#: of pywebio.output.use_scope +msgid "Usage" +msgstr "" + +#: of pywebio.output.use_scope:13 +msgid "" +"with use_scope(...) as scope_name:\n" +" put_xxx()\n" +"\n" +"@use_scope(...)\n" +"def app():\n" +" put_xxx()" +msgstr "" + +#: of pywebio.output.get_scope:1 +msgid "Get the scope name of runtime scope stack" +msgstr "获取当前运行时scope栈中的scope名" + +#: of pywebio.output.get_scope:3 +msgid "" +"The index of the runtime scope stack. Default is -1. 0 means the top level scope(the ``ROOT`` " +"Scope), -1 means the current Scope, -2 means the scope used before entering the current " +"scope, ..." +msgstr "" +"当前运行时的scope栈索引。-1表示当前scope,-2表示进入当前scope前的scope,依次类推;0表示 `ROOT` " +"scope" + +#: of pywebio.output.get_scope:3 +msgid "The index of the runtime scope stack. Default is -1." +msgstr "需要获取的scope在scope栈中的索引值。默认返回当前scope名" + +#: of pywebio.output.get_scope:5 +msgid "" +"0 means the top level scope(the ``ROOT`` Scope), -1 means the current Scope, -2 means the scope " +"used before entering the current scope, ..." +msgstr "-1表示当前scope,-2表示进入当前scope前的scope,依次类推;0表示 `ROOT` scope" + +#: of pywebio.output.get_scope pywebio.output.style +msgid "Returns" +msgstr "" + +#: of pywebio.output.get_scope:8 +msgid "Returns the scope name with the index, and returns ``None`` when occurs index error" +msgstr "返回Scope栈中对应索引的scope名,索引错误时返回None" + +#: of pywebio.output.clear:1 +msgid "Clear the content of the specified scope" +msgstr "清空scope内容" + +#: of pywebio.output.clear:3 pywebio.output.remove:3 +msgid "Target scope name. Default is the current scope." +msgstr "目标scope名。默认为当前scope" + +#: of pywebio.output.remove:1 +msgid "Remove the specified scope" +msgstr "移除Scope" + +#: of pywebio.output.scroll_to:1 +msgid "Scroll the page to the specified scope" +msgstr "将页面滚动到 ``scope`` Scope处" + +#: of pywebio.output.scroll_to:3 +msgid "Target scope. Default is the current scope." +msgstr "" + +#: of pywebio.output.scroll_to:4 +msgid "" +"Where to place the scope in the visible area of the page. Available value: * ``'top'`` : Keep " +"the scope at the top of the visible area of the page * ``'middle'`` : Keep the scope at the " +"middle of the visible area of the page * ``'bottom'`` : Keep the scope at the bottom of the " +"visible area of the page" +msgstr "" + +#: of pywebio.output.scroll_to:4 +msgid "Where to place the scope in the visible area of the page. Available value:" +msgstr "将Scope置于屏幕可视区域的位置。可用值:" + +#: of pywebio.output.scroll_to:6 +msgid "``'top'`` : Keep the scope at the top of the visible area of the page" +msgstr "``'top'`` : 滚动页面,让Scope位于屏幕可视区域顶部" + +#: of pywebio.output.scroll_to:7 +msgid "``'middle'`` : Keep the scope at the middle of the visible area of the page" +msgstr "``'middle'`` : 滚动页面,让Scope位于屏幕可视区域中间" + +#: of pywebio.output.scroll_to:8 +msgid "``'bottom'`` : Keep the scope at the bottom of the visible area of the page" +msgstr "``'bottom'`` : 滚动页面,让Scope位于屏幕可视区域底部" + +#: of pywebio.output:105 +msgid "**Scope related parameters of output function**" +msgstr "" + +#: of pywebio.output:107 +msgid "" +"The output function will output the content to the \"current scope\" by default, and the " +"\"current scope\" for the runtime context can be set by `use_scope()`." +msgstr "输出函数默认将内容输出到“当前scope”,“当前scope”可由 `use_scope()` 设置。" + +#: of pywebio.output:110 +msgid "" +"In addition, all output functions support a ``scope`` parameter to specify the destination scope " +"to output:" +msgstr "另外,所有输入函数都支持使用 ``scope`` 参数来指定输出的目的scope:" + +#: of pywebio.output:112 +msgid "" +"with use_scope('scope3'):\n" +" put_text('text1 in scope3') # output to current scope: scope3\n" +" put_text('text in ROOT scope', scope='ROOT') # output to ROOT Scope\n" +"\n" +"put_text('text2 in scope3', scope='scope3') # output to scope3" +msgstr "" + +#: of pywebio.output:122 +msgid "The results of the above code are as follows::" +msgstr "以上代码运行结果如下::" + +#: of pywebio.output:124 +msgid "" +"text1 in scope3\n" +"text2 in scope3\n" +"text in ROOT scope" +msgstr "" + +#: of pywebio.output:128 +msgid "" +"A scope can contain multiple output items, the default behavior of output function is to append " +"its content to target scope. The ``position`` parameter of output function can be used to " +"specify the insert position in target scope." +msgstr "" +"一个scope可以包含多个输出项,输出函数的默认行为是将内容追加到目标scope中。可以使用输出函数的 " +"``position`` 参数来指定输出内容在目标scope中的插入位置。" + +#: of pywebio.output:131 +msgid "" +"Each output item in a scope has an index, the first item's index is 0, and the next item's index " +"is incremented by one. You can also use a negative number to index the items in the scope, -1 " +"means the last item, -2 means the item before the last, ..." +msgstr "" +"一个Scope中各次输出的元素具有像数组一样的索引,最前面的编号为0,以此往后递增加一;同样可以使用负" +"数对Scope中的元素进行索引,-1表示最后面的元素,-2表示次后面的元素……" + +#: of pywebio.output:134 +msgid "" +"The ``position`` parameter of output functions accepts an integer. When ``position>=0``, it " +"means to insert content before the item whose index equal ``position``; when ``position<0``, it " +"means to insert content after the item whose index equal ``position``:" +msgstr "" +"``position`` 参数类型为整形, ``position>=0`` 时表示输出内容到目标Scope的第position号元素的前" +"面; ``position<0`` 时表示输出内容到目标Scope第position号元素之后:" + +#: of pywebio.output:138 +msgid "" +"with use_scope('scope1'):\n" +" put_text('A')\n" +"## ----\n" +"with use_scope('scope1'): # ..demo-only\n" +" put_text('B', position=0) # insert B before A -> B A\n" +"## ----\n" +"with use_scope('scope1'): # ..demo-only\n" +" put_text('C', position=-2) # insert C after B -> B C A\n" +"## ----\n" +"with use_scope('scope1'): # ..demo-only\n" +" put_text('D', position=1) # insert D before C B -> B D C A" +msgstr "" + +#: of pywebio.output:154 +msgid "**Output functions**" +msgstr "" + +#: of pywebio.output.put_text:3 +msgid "" +"Texts need to output. The type can be any object, and the `str()` function will be used for non-" +"string objects." +msgstr "要输出的内容。类型可以为任意对象,对非字符串对象会应用 `str()` 函数作为输出值。" + +#: of pywebio.output.put_text:4 +msgid "The separator between the texts" +msgstr "输出分隔符" + +#: of pywebio.output.put_text:5 +msgid "Use text as an inline element (no line break at the end of the text). Default is ``False``" +msgstr "" +"将文本作为行内元素(连续输出的文本显示在相同的段落中)。默认每次输出的文本都作为一个独立的段落" + +#: of pywebio.output.put_text:6 +msgid "" +"The target scope to output. If the scope does not exist, no operation will be performed. Can " +"specify the scope name or use a integer to index the runtime scope stack." +msgstr "" + +#: of pywebio.output.put_text:6 +msgid "The target scope to output. If the scope does not exist, no operation will be performed." +msgstr "内容输出的目标scope,若scope不存在,则不进行任何输出操作。" + +#: of pywebio.output.put_text:8 +msgid "Can specify the scope name or use a integer to index the runtime scope stack." +msgstr "可以直接指定目标Scope名,或者使用int通过索引Scope栈来确定Scope" + +#: of pywebio.output.put_text:9 +msgid "The position where the content is output in target scope" +msgstr "在scope中输出的位置。" + +#: of pywebio.output.put_text:11 +msgid "" +"For more information about ``scope`` and ``position`` parameter, please refer to :ref:`User " +"Manual `" +msgstr "参数 `scope` 和 `position` 的更多使用说明参见 :ref:`用户手册 `" + +#: of pywebio.output.put_markdown:3 +msgid "Markdown string" +msgstr "Markdown文本" + +#: of pywebio.output.put_markdown:4 +msgid "" +"Whether to remove the leading whitespace in each line of ``mdcontent``. The number of the " +"whitespace to remove will be decided cleverly." +msgstr "是否自动移除 ``mdcontent`` 每一行的前导空白锁进" + +#: of pywebio.output.put_markdown:6 +msgid "" +"Configuration when parsing Markdown. PyWebIO uses `marked `_ library to " +"parse Markdown, the parse options see: https://marked.js.org/using_advanced#options (Only " +"supports members of string and boolean type)" +msgstr "" +"解析Markdown时的配置参数。\n" +"PyWebIO使用 `marked `_ 解析Markdown, 可配置项参见: https://marked.js.org/" +"using_advanced#options (仅支持配置string和boolean类型的项)" + +#: of pywebio.output.put_html:4 pywebio.output.put_markdown:9 +msgid "" +"Whether to use `DOMPurify `_ to filter the content to " +"prevent XSS attacks." +msgstr "是否使用 `DOMPurify `_ 对内容进行过滤来防止XSS攻击。" + +#: of pywebio.output.put_markdown:12 +msgid "" +"When using Python triple quotes syntax to output multi-line Markdown in a function, you can " +"indent the Markdown text to keep a good code format. PyWebIO will cleverly remove the indent for " +"you when show the Markdown::" +msgstr "" +"当使用python三引号语法在函数中输出多行Markdown内容时,你可以缩进Markdown内容来使代码保持美观。" +"PyWebIO会智能地移除Markdown中的缩进::" + +#: of pywebio.output.put_markdown:16 +msgid "" +"# good code format\n" +"def hello():\n" +" put_markdown(r\"\"\" # H1\n" +" This is content.\n" +" \"\"\")" +msgstr "" + +#: of pywebio.output.put_markdown:22 +msgid "Enable `lstrip` by default. Deprecate `strip_indent`." +msgstr "" + +#: of pywebio.output:166 +msgid "" +"Message contents. The item is ``put_xxx()`` call, and any other type will be converted to " +"``put_text(content)``." +msgstr "消息内容. 元素为 ``put_xxx()`` 调用,其他类型会被转换成 ``put_text(content)``" + +#: of pywebio.output:168 +msgid "Whether to show a dismiss button on the right of the message." +msgstr "是否在消息框右侧展示一个关闭按钮。" + +#: of pywebio.output.put_html:1 +msgid "Output HTML content" +msgstr "输出Html内容" + +#: of pywebio.output.put_html:3 +msgid "html string" +msgstr "html字符串" + +#: of pywebio.output.put_link:1 +msgid "Output hyperlinks to other web page or PyWebIO Application page." +msgstr "输出链接到其他网页或PyWebIO App的超链接" + +#: of pywebio.output.put_link:3 +msgid "The label of the link" +msgstr "链接名称" + +#: of pywebio.output.put_link:4 +msgid "Target url" +msgstr "链接到的页面地址" + +#: of pywebio.output.put_link:5 +msgid "Target PyWebIO Application name. See also: :ref:`Server mode `" +msgstr "链接到的PyWebIO应用名。参见 :ref:`Server模式 `" + +#: of pywebio.output.put_link:6 +msgid "Whether to open the link in a new window" +msgstr "是否在新窗口打开链接" + +#: of pywebio.output.put_link:9 +msgid "The ``url`` and ``app`` parameters must specify one but not both" +msgstr "``url`` 和 ``app`` 参数必须指定一个但不可以同时指定" + +#: of pywebio.output.put_processbar:3 +msgid "The name of the progress bar, which is the unique identifier of the progress bar" +msgstr "进度条名称,为进度条的唯一标识" + +#: of pywebio.output.put_processbar:4 +msgid "The initial progress value of the progress bar. The value is between 0 and 1" +msgstr "进度条初始值. 进度条的值在 0 ~ 1 之间" + +#: of pywebio.output.put_processbar:5 pywebio.output.set_processbar:5 +msgid "The label of process bar. The default is the percentage value of the current progress." +msgstr "进度条显示的标签. 默认为当前进度的百分比" + +#: of pywebio.output.put_processbar:6 +msgid "Whether to remove the progress bar after the progress is completed" +msgstr "是否在进度完成后关闭进度条" + +#: of pywebio.output.put_button:10 pywebio.output.put_buttons:20 pywebio.output.put_buttons:63 +#: pywebio.output.put_collapse:9 pywebio.output.put_file:10 pywebio.output.put_image:12 +#: pywebio.output.put_processbar:9 pywebio.output.put_scrollable:13 pywebio.output.put_table:15 +#: pywebio.output.toast:14 +msgid "Example:" +msgstr "" + +#: of pywebio.output.put_processbar:11 +msgid "" +"import time\n" +"\n" +"put_processbar('bar');\n" +"for i in range(1, 11):\n" +" set_processbar('bar', i / 10)\n" +" time.sleep(0.1)" +msgstr "" + +#: of pywebio.output.put_processbar:22 +msgid "use `set_processbar()` to set the progress of progress bar" +msgstr "使用 `set_processbar()` 设置进度条进度" + +#: of pywebio.output.set_processbar:1 +msgid "Set the progress of progress bar" +msgstr "设置进度条进度" + +#: of pywebio.output.set_processbar:3 +msgid "The name of the progress bar" +msgstr "设置进度条进度" + +#: of pywebio.output.set_processbar:4 +msgid "The progress value of the progress bar. The value is between 0 and 1" +msgstr "进度条的值. 范围在 0 ~ 1 之间" + +#: of pywebio.output.set_processbar:7 +msgid "See also: `put_processbar()`" +msgstr "参见 `put_processbar()`" + +#: of pywebio.output.put_loading:3 +msgid "The shape of loading prompt. The available values are: `'border'` (default)、 `'grow'`" +msgstr "加载提示的形状, 可选值: `'border'` (默认,旋转的圆环)、 `'grow'` (大小渐变的圆点)" + +#: of pywebio.output.put_loading:4 +msgid "" +"The color of loading prompt. The available values are: `'primary'` 、 `'secondary'` 、 " +"`'success'` 、 `'danger'` 、 `'warning'` 、`'info'` 、`'light'` 、 `'dark'` (default)" +msgstr "" +"加载提示的颜色, 可选值: `'primary'` 、 `'secondary'` 、 `'success'` 、 `'danger'` 、 " +"`'warning'` 、`'info'` 、`'light'` 、 `'dark'` (默认)" + +#: of pywebio.output.put_loading:8 +msgid "`put_loading()` can be used in 2 ways: direct call and context manager:" +msgstr "`put_loading()` 支持直接调用和上下文管理器:" + +#: of pywebio.output.put_loading:10 +msgid "" +"for shape in ('border', 'grow'):\n" +" for color in ('primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', " +"'dark'):\n" +" put_text(shape, color)\n" +" put_loading(shape=shape, color=color)\n" +"\n" +"## ----\n" +"import time # ..demo-only\n" +"# Use as context manager, the loading prompt will disappear automatically when the context block " +"exits.\n" +"with put_loading():\n" +" time.sleep(3) # Some time-consuming operations\n" +" put_text(\"The answer of the universe is 42\")\n" +"\n" +"## ----\n" +"# using style() to set the size of the loading prompt\n" +"put_loading().style('width:4rem; height:4rem')" +msgstr "" + +#: of pywebio.output.put_code:3 +msgid "code string" +msgstr "代码内容" + +#: of pywebio.output.put_code:4 +msgid "language of code" +msgstr "代码语言" + +#: of pywebio.output.put_code:5 +msgid "" +"The max lines of code can be displayed, no limit by default. The scroll bar will be displayed " +"when the content exceeds." +msgstr "代码块最多可显示的文本行数,默认不限制。内容超出时会使用滚动条。" + +#: of pywebio.output.put_table:3 +msgid "" +"Table data, which can be a two-dimensional list or a list of dict. The table cell can be a " +"string or ``put_xxx()`` call. The cell can use the :func:`span()` to set the cell span." +msgstr "" +"表格数据。列表项可以为 ``list`` 或者 ``dict`` , 单元格的内容可以为字符串或 ``put_xxx`` 类型的输出" +"函数。\n" +"数组项可以使用 :func:`span()` 函数来设定单元格跨度。" + +#: of pywebio.output.put_table:5 +msgid "" +"Table header. When the item of ``tdata`` is of type ``list``, if the ``header`` parameter is " +"omitted, the first item of ``tdata`` will be used as the header. The header item can also use " +"the :func:`span()` function to set the cell span. When ``tdata`` is list of dict, ``header`` is " +"used to specify the order of table headers, which cannot be omitted. In this case, the " +"``header`` can be a list of dict key or a list of ``(