Browse Source

Merge branch 'feat_demo' into dev

wangweimin 5 years ago
parent
commit
3075cc2a6f
9 changed files with 471 additions and 0 deletions
  1. 1 0
      Procfile
  2. 7 0
      demos/__init__.py
  3. 46 0
      demos/__main__.py
  4. 35 0
      demos/bmi.py
  5. 79 0
      demos/chat_room.py
  6. 151 0
      demos/input_usage.py
  7. 147 0
      demos/output_usage.py
  8. 4 0
      docs/demos.rst
  9. 1 0
      docs/index.rst

+ 1 - 0
Procfile

@@ -0,0 +1 @@
+web: python -m demos --port=$PORT

+ 7 - 0
demos/__init__.py

@@ -0,0 +1,7 @@
+r"""示例
+
+.. automodule:: demos.bmi
+.. automodule:: demos.input_usage
+.. automodule:: demos.output_usage
+.. automodule:: demos.chat_room
+"""

+ 46 - 0
demos/__main__.py

@@ -0,0 +1,46 @@
+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 pywebio import STATIC_PATH
+from pywebio.output import put_markdown
+from pywebio.platform.tornado import webio_handler
+from tornado.options import define, options
+
+index_md = r"""# PyWebIO demos
+### Demos list
+
+* [BMI计算](./?pywebio_api=bmi): 根据身高体重计算BMI指数 [源码](https://github.com/wang0618/PyWebIO/blob/master/demos/bmi.py)
+* [聊天室](./?pywebio_api=chat_room): 和当前所有在线的人聊天 [源码](https://github.com/wang0618/PyWebIO/blob/master/demos/chat_room.py)
+* [输入演示](./?pywebio_api=input_usage):  演示PyWebIO输入模块的用法 [源码](https://github.com/wang0618/PyWebIO/blob/master/demos/input_usage.py)
+* [输出演示](./?pywebio_api=output_usage): 演示PyWebIO输出模块的用法 [源码](https://github.com/wang0618/PyWebIO/blob/master/demos/output_usage.py)
+
+### Links
+* PyWebIO Github [github.com/wang0618/PyWebIO](https://github.com/wang0618/PyWebIO)
+* 使用手册和实现文档见 [pywebio.readthedocs.io](https://pywebio.readthedocs.io)
+
+"""
+
+
+def index():
+    put_markdown(index_md)
+
+
+if __name__ == "__main__":
+    define("port", default=5000, help="run on the given port", type=int)
+    tornado.options.parse_command_line()
+
+    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"/(.*)", tornado.web.StaticFileHandler, {"path": STATIC_PATH, 'default_filename': 'index.html'})
+    ])
+    application.listen(port=options.port)
+    tornado.ioloop.IOLoop.current().start()

+ 35 - 0
demos/bmi.py

@@ -0,0 +1,35 @@
+"""
+BMI指数计算
+^^^^^^^^^^^
+
+计算 `BMI指数 <https://en.wikipedia.org/wiki/Body_mass_index>`_ 的简单应用
+
+`Demo地址 <https://pywebio.herokuapp.com/?pywebio_api=bmi>`_  `源码 <https://github.com/wang0618/PyWebIO/blob/master/demos/bmi.py>`_
+"""
+from pywebio import start_server
+from pywebio.input import *
+from pywebio.output import *
+
+
+def main():
+    set_output_fixed_height(True)
+
+    info = input_group('请输入', [
+        input("请输入你的身高(cm)", name="height", type=FLOAT),
+        input("请输入你的体重(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'), '非常肥胖')]
+
+    for top, status in top_status:
+        if BMI <= top:
+            put_markdown('你的 BMI 值: `%.1f`,身体状态:`%s`' % (BMI, status))
+            break
+
+
+if __name__ == '__main__':
+    start_server(main, debug=True, port=8080)

+ 79 - 0
demos/chat_room.py

@@ -0,0 +1,79 @@
+"""
+聊天室
+^^^^^^^^^^^
+和当前所有在线的人聊天
+
+`Demo地址 <https://pywebio.herokuapp.com/?pywebio_api=chat_room>`_  `源码 <https://github.com/wang0618/PyWebIO/blob/master/demos/chat_room.py>`_
+
+* 使用基于协程的会话
+* 使用 `run_async() <pywebio.session.run_async>` 启动后台协程
+"""
+import asyncio
+
+from pywebio import start_server, run_async
+from pywebio.input import *
+from pywebio.output import *
+
+# 最大消息记录保存
+MAX_MESSAGES_CNT = 10 ** 4
+
+chat_msgs = []  # 聊天记录 (name, msg)
+online_users = set()  # 在线用户 todo 无法统计主动关闭浏览器的用户退出
+
+
+async def refresh_msg(my_name):
+    """刷新聊天消息"""
+    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:  # 仅刷新其他人的新信息
+                put_markdown('`%s`: %s' % m)
+
+        # 清理聊天记录
+        if len(chat_msgs) > MAX_MESSAGES_CNT:
+            chat_msgs = chat_msgs[len(chat_msgs) // 2:]
+
+        last_idx = len(chat_msgs)
+
+
+async def main():
+    global chat_msgs
+
+    set_output_fixed_height(True)
+    set_title("PyWebIO Chat Room")
+    put_markdown("""欢迎来到聊天室,你可以和当前所有在线的人聊天\n
+    本应用使用不到80行代码实现,源代码[链接](https://github.com/wang0618/PyWebIO/blob/master/demos/chat_room.py)""", lstrip=True)
+
+    nickname = await input("请输入你的昵称", required=True,
+                           valid_func=lambda n: '昵称已被使用' if n in online_users or n == '📢' else None)
+
+    online_users.add(nickname)
+    chat_msgs.append(('📢', '`%s`加入聊天室. 当前在线人数 %s' % (nickname, len(online_users))))
+    put_markdown('`📢`: `%s`加入聊天室. 当前在线人数 %s' % (nickname, len(online_users)))
+
+    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'}])
+        ])
+        if data is None:
+            break
+
+        put_markdown('`%s`: %s' % (nickname, data['msg']))
+        chat_msgs.append((nickname, data['msg']))
+
+    online_users.remove(nickname)
+    refresh_task.close()
+    chat_msgs.append(('📢', '`%s`退出聊天室. 当前在线人数 %s' % (nickname, len(online_users))))
+    put_text("你已经退出聊天室")
+
+    if not online_users:
+        chat_msgs = []
+
+
+if __name__ == '__main__':
+    start_server(main, debug=True, port=8080)

+ 151 - 0
demos/input_usage.py

@@ -0,0 +1,151 @@
+"""
+输入演示
+^^^^^^^^^^^
+演示PyWebIO支持的各种输入形式
+
+`Demo地址 <https://pywebio.herokuapp.com/?pywebio_api=input_usage>`_  `源码 <https://github.com/wang0618/PyWebIO/blob/master/demos/input_usage.py>`_
+"""
+from pywebio import start_server
+from pywebio.input import *
+from pywebio.output import *
+
+
+def main():
+    set_auto_scroll_bottom(False)
+    set_title("PyWebIO输入演示")
+
+    put_markdown("""# PyWebIO 输入演示
+    
+    在[这里](https://github.com/wang0618/PyWebIO/blob/master/demos/input_usage.py)可以获取本Demo的源码。
+    
+    PyWebIO的输入函数都定义在 `pywebio.input` 模块中,可以使用 `from pywebio.input import *` 引入。
+
+    ### 基本输入
+    首先是一些基本类型的输入
+
+    #### 文本输入
+    ```python
+    name = input("What's your name?")
+    ```
+    """, lstrip=True)
+    put_text("这样一行代码的效果如下:", anchor='input-1')
+    name = input("What's your name?")
+    put_markdown("`name = %r`" % name, anchor='input-1')
+
+    # 其他类型的输入
+    put_markdown("""PyWebIO的输入函数是同步的,在表单被提交之前,输入函数不会返回。
+    #### 其他类型的输入:
+    ```python
+    # 密码输入
+    password = input("Input password", type=PASSWORD)
+    
+    # 下拉选择框
+    gift = select('Which gift you want?', ['keyboard', 'ipad'])
+    
+    # CheckBox
+    agree = checkbox("用户协议", options=['I agree to terms and conditions'])
+    
+    # Text Area
+    text = textarea('Text Area', rows=3, placeholder='Some text')
+    
+    # 文件上传
+    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'])
+    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)
+
+    # 输入选项
+    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,否则返回错误消息:
+    ```python
+    def check_age(p):  # 检验函数校验通过时返回None,否则返回错误消息
+        if p < 10:
+            return 'Too young!!'
+        if p > 60:
+            return 'Too old!!'
+
+    age = input("How old are you?", type=NUMBER, valid_func=check_age)
+    ```
+    """, strip_indent=4)
+
+    def check_age(p):  # 检验函数校验通过时返回None,否则返回错误消息
+        if p < 10:
+            return 'Too young!!'
+        if p > 60:
+            return 'Too old!!'
+
+    age = input("How old are you?", type=NUMBER, valid_func=check_age, help_text='尝试输入一些非法值,比如"8"、"65"')
+    put_markdown('`age = %r`' % age)
+
+    # Codemirror
+    put_markdown(r"""PyWebIO 的 `textarea()` 输入函数还支持使用 [Codemirror](https://codemirror.net/) 实现代码风格的编辑区,只需使用 `code` 参数传入Codemirror支持的选项即可(最简单的情况是直接传入` code={}` 或 `code=True`):
+    ```python
+    code = textarea('Code Edit', code={
+        'mode': "python",  # 编辑区代码语言
+        'theme': 'darcula',  # 编辑区darcula主题
+    }, value='import something\n# Write your python code')
+    ```
+        """, strip_indent=4)
+
+    code = textarea('Code Edit', code={
+        'mode': "python",  # 编辑区代码语言
+        'theme': 'darcula',  # 编辑区darcula主题, Visit https://codemirror.net/demo/theme.html#cobalt to get more themes
+    }, value='import something\n# Write your python code')
+
+    put_markdown("Your code:\n```python\n%s\n```" % code)
+
+    # 输入组
+    put_markdown(r"""### 输入组
+    `input_group()` 接受单项输入组成的列表作为参数,输入组中需要在每一项输入函数中提供 `name` 参数来用于在结果中标识不同输入项。输入组中同样支持设置校验函数,其接受整个表单数据作为参数。
+
+    ```python
+    def check_form(data):  # 检验函数校验通过时返回None,否则返回 (input name,错误消息)
+        if len(data['name']) > 6:
+            return ('name', '名字太长!')
+        if data['age'] <= 0:
+            return ('age', '年龄不能为负数!')
+
+    data = input_group("Basic info", [
+        input('Input your name', name='name'),
+        input('Input your age', name='age', type=NUMBER, valid_func=check_age)
+    ], valid_func=check_form)
+    ```
+    """, strip_indent=4)
+
+    def check_form(data):  # 检验函数校验通过时返回None,否则返回 (input name,错误消息)
+        if len(data['name']) > 6:
+            return ('name', '名字太长!')
+        if data['age'] <= 0:
+            return ('age', '年龄不能为负数!')
+
+    data = input_group("Basic info", [
+        input('Input your name', name='name'),
+        input('Input your age', name='age', type=NUMBER, valid_func=check_age)
+    ], valid_func=check_form)
+
+    put_markdown("`data = %r`" % data)
+
+
+if __name__ == '__main__':
+    start_server(main, debug=True, port=8080)

+ 147 - 0
demos/output_usage.py

@@ -0,0 +1,147 @@
+"""
+输出演示
+^^^^^^^^^^^
+演示PyWebIO支持的各种输出形式
+
+`Demo地址 <https://pywebio.herokuapp.com/?pywebio_api=output_usage>`_  `源码 <https://github.com/wang0618/PyWebIO/blob/master/demos/output_usage.py>`_
+"""
+from pywebio import start_server
+from pywebio.input import *
+from pywebio.output import *
+
+
+def main():
+    set_auto_scroll_bottom(False)
+    set_title("PyWebIO输出演示")
+
+    put_markdown("""# PyWebIO 输入演示
+    
+    在[这里](https://github.com/wang0618/PyWebIO/blob/master/demos/input_usage.py)可以获取本Demo的源码。
+    
+    PyWebIO的输出函数都定义在 `pywebio.output` 模块中,可以使用 `from pywebio.output import *` 引入。
+
+    ### 基本输出
+    PyWebIO提供了一些便捷函数来输出表格、链接等格式:
+    ```python
+    # 文本输出
+    put_text("Hello world!")
+
+    # 表格输出
+    put_table([
+        ['商品', '价格'],
+        ['苹果', '5.5'],
+        ['香蕉', '7'],
+    ])
+
+    # Markdown输出
+    put_markdown('~~删除线~~')
+
+    # 文件输出
+    put_file('hello_word.txt', b'hello word!')
+    ```
+    
+    PyWebIO提供的全部输出函数请参考PyWebIO文档
+    """, strip_indent=4)
+    # 文本输出
+    put_text("Hello world!")
+    # 表格输出
+    put_table([
+        ['商品', '价格'],
+        ['苹果', '5.5'],
+        ['香蕉', '7'],
+    ])
+    # Markdown输出
+    put_markdown('~~删除线~~')
+    # 文件输出
+    put_file('hello_word.txt', b'hello word!')
+
+    put_markdown(r"""### 输出事件回调
+    PyWebIO允许你输出一些控件,当控件被点击时执行提供的回调函数,就像编写GUI程序一样。
+    
+    下面是一个例子:
+    ```python
+    from functools import partial
+
+    def edit_row(choice, row):
+        put_markdown("> You click`%s` button ar row `%s`" % (choice, row))
+
+    put_table([
+        ['Idx', 'Actions'],
+        [1, table_cell_buttons(['edit', 'delete'], onclick=partial(edit_row, row=1))],
+        [2, table_cell_buttons(['edit', 'delete'], onclick=partial(edit_row, row=2))],
+        [3, table_cell_buttons(['edit', 'delete'], onclick=partial(edit_row, row=3))],
+    ])
+    ```
+    """, strip_indent=4)
+
+    from functools import partial
+
+    def edit_row(choice, row):
+        put_markdown("> You click `%s` button ar row `%s`" % (choice, row), anchor='table-callback')
+
+    put_table([
+        ['Idx', 'Actions'],
+        [1, table_cell_buttons(['edit', 'delete'], onclick=partial(edit_row, row=1))],
+        [2, table_cell_buttons(['edit', 'delete'], onclick=partial(edit_row, row=2))],
+        [3, table_cell_buttons(['edit', 'delete'], onclick=partial(edit_row, row=3))],
+    ])
+    set_anchor('table-callback')
+
+    put_markdown(r"""当然,PyWebIO还支持单独的按钮控件:
+    ```python
+    def btn_click(btn_val):
+        put_markdown("> You click `%s` button" % btn_val)
+
+    put_buttons(['A', 'B', 'C'], onclick=btn_click)
+    ```
+    """, strip_indent=4)
+
+    def btn_click(btn_val):
+        put_markdown("> You click `%s` button" % btn_val, anchor='button-callback')
+
+    put_buttons(['A', 'B', 'C'], onclick=btn_click)
+    set_anchor('button-callback')
+
+    put_markdown(r"""### 锚点
+    就像在控制台输出文本一样,PyWebIO默认在页面的末尾输出各种内容,你可以使用锚点来改变这一行为。
+
+    你可以调用 `set_anchor(name)` 对当前输出位置进行标记。
+    
+    你可以在任何输出函数中使用 `before` 参数将内容插入到指定的锚点之前,也可以使用 `after` 参数将内容插入到指定的锚点之后。
+    
+    在输出函数中使用 `anchor` 参数为当前的输出内容标记锚点,若锚点已经存在,则将锚点处的内容替换为当前内容。
+    
+    以下代码展示了在输出函数中使用锚点:
+    ```python
+    set_anchor('top')
+    put_text('A')
+    put_text('B', anchor='b')
+    put_text('C', after='top')
+    put_text('D', before='b')
+    ```
+    以上代码将输出:
+    
+        C
+        A
+        D
+        B
+
+    """, strip_indent=4)
+
+    put_markdown(r"""### 页面环境设置
+    #### 输出区外观
+    PyWebIO支持两种外观:输出区固定高度/可变高度。 可以通过调用 `set_output_fixed_height(True)` 来开启输出区固定高度。
+    
+    #### 设置页面标题
+    
+    调用 `set_title(title)` 可以设置页面标题。
+    
+    #### 自动滚动
+    
+    在不指定锚点进行输出时,PyWebIO默认在输出完毕后自动将页面滚动到页面最下方;在调用输入函数时,也会将页面滚动到表单处。 通过调用 `set_auto_scroll_bottom(False)` 来关闭自动滚动。
+
+    """, strip_indent=4)
+
+
+if __name__ == '__main__':
+    start_server(main, debug=True, port=8080)

+ 4 - 0
docs/demos.rst

@@ -0,0 +1,4 @@
+Demos
+==========
+
+.. automodule:: demos

+ 1 - 0
docs/index.rst

@@ -80,6 +80,7 @@ Documentation
    session
    session
    platform
    platform
    misc
    misc
+   demos
 
 
 .. toctree::
 .. toctree::
    :maxdepth: 2
    :maxdepth: 2