Forráskód Böngészése

feat: support row/column/grid layout

wangweimin 4 éve
szülő
commit
78278d367a
1 módosított fájl, 115 hozzáadás és 12 törlés
  1. 115 12
      pywebio/output.py

+ 115 - 12
pywebio/output.py

@@ -2,11 +2,8 @@ r"""输出内容到用户浏览器
 
 本模块提供了一系列函数来输出不同形式的内容到用户浏览器,并支持灵活的输出控制。
 
-输出控制
---------------
-
 输出域Scope
-^^^^^^^^^^^^^^^^^
+--------------
 
 .. autofunction:: set_scope
 .. autofunction:: clear
@@ -16,7 +13,7 @@ r"""输出内容到用户浏览器
 
 
 环境设置
-^^^^^^^^^^^^^^^^^
+--------------
 
 .. autofunction:: set_title
 .. autofunction:: set_output_fixed_height
@@ -41,6 +38,9 @@ r"""输出内容到用户浏览器
 布局与样式
 --------------
 .. autofunction:: style
+.. autofunction:: put_column
+.. autofunction:: put_row
+.. autofunction:: put_grid
 
 """
 import io
@@ -64,7 +64,8 @@ logger = logging.getLogger(__name__)
 __all__ = ['Position', 'set_title', 'set_output_fixed_height', 'set_auto_scroll_bottom', 'remove', 'scroll_to',
            'put_text', 'put_html', 'put_code', 'put_markdown', 'use_scope', 'set_scope', 'clear', 'remove',
            'put_table', 'table_cell_buttons', 'put_buttons', 'put_image', 'put_file', 'PopupSize', 'popup',
-           'close_popup', 'put_widget', 'put_collapse', 'put_link', 'put_scrollable', 'style']
+           'close_popup', 'put_widget', 'put_collapse', 'put_link', 'put_scrollable', 'style', 'put_column',
+           'put_row', 'put_grid', 'column', 'row', 'grid']
 
 
 # popup尺寸
@@ -564,14 +565,12 @@ def put_collapse(title, content, open=False, scope=Scope.Current, position=Outpu
     for item in content:
         assert isinstance(item, (str, Output)), "put_collapse() content must be list of str/put_xxx()"
 
-    tpl = """
-    <details {{#open}}open{{/open}}>
+    tpl = """<details {{#open}}open{{/open}}>
         <summary>{{title}}</summary>
         {{#contents}}
             {{& pywebio_output_parse}}
         {{/contents}}
-    </details>
-    """
+    </details>"""
     return put_widget(tpl, dict(title=title, contents=content, open=open), scope=scope, position=position)
 
 
@@ -654,6 +653,109 @@ def put_widget(template, data, scope=Scope.Current, position=OutputPosition.BOTT
     return Output(spec)
 
 
+@safely_destruct_output_when_exp('content')
+def put_row(content, size=None, scope=Scope.Current, position=OutputPosition.BOTTOM) -> Output:
+    """使用行布局输出内容. 内容在水平方向上排列
+
+    :param list content: 子元素列表, 列表项为 ``put_xxx()`` 调用或者 ``None`` , ``None`` 表示空白列间距
+    :param str size:
+       | 用于指示子元素的宽度, 为空格分割的宽度值列表.
+       | 宽度值需要和 ``content`` 中子元素一一对应( ``None`` 子元素也要对应宽度值).
+       | size 默认给 ``None`` 元素分配10像素宽度,将剩余元素平均分配宽度.
+
+       宽度值可用格式:
+
+        - 像素值: 例如: ``100px``
+        - 百分比: 表示占可用宽度的百分比. 例如: ``33.33%``
+        - ``fr`` 关键字: 表示比例关系, 2fr 表示的宽度为 1fr 的两倍
+        - ``auto`` 关键字: 表示由浏览器自己决定长度
+        - ``minmax()`` : 产生一个长度范围,表示长度就在这个范围之中。它接受两个参数,分别为最小值和最大值。
+          例如: ``minmax(100px, 1fr)`` 表示长度不小于100px,不大于1fr
+
+       可以使用 ``repeat()`` 函数来简化重复的值, 例如: ``repeat(3, 33.33%)`` 、 ``repeat(2, 100px 20px 80px)``
+
+       有时,单元格的大小是固定的,如果希望每一行容纳尽可能多的子元素,可以使用 ``auto-fill`` 关键字表示自动填充.
+       例如: ``repeat(auto-fill, 100px)`` 表示每列宽度100px,然后自动填充,直到一行内不能放置更多的列,多余的子元素将在下一行显示.
+    """
+    return _row_column_layout(content, flow='row', size=size, scope=scope, position=position)
+
+
+@safely_destruct_output_when_exp('content')
+def put_column(content, size=None, scope=Scope.Current, position=OutputPosition.BOTTOM) -> Output:
+    """使用列布局输出内容. 内容在竖直方向上排列
+
+    :param list content: 子元素列表, 列表项为 ``put_xxx()`` 调用或者 ``None`` , ``None`` 表示空白行间距
+    :param str size: 用于指示子元素的高度, 为空格分割的高度值列表. 可用格式参考 `put_column()` 函数的 size 参数.
+    """
+
+    return _row_column_layout(content, flow='column', size=size, scope=scope, position=position)
+
+
+def _row_column_layout(content, flow, size, scope=Scope.Current, position=OutputPosition.BOTTOM) -> Output:
+    if not isinstance(content, (list, tuple, OutputList)):
+        content = [content]
+
+    if not size:
+        size = ' '.join('1fr' if c is not None else '10px' for c in content)
+
+    content = [c if c is not None else put_html('<div></div>') for c in content]
+    for item in content:
+        assert isinstance(item, Output), "put_row() content must be list of put_xxx()"
+
+    size_keymap = dict(row='columns', column='rows')
+    style = 'grid-auto-flow: {flow}; grid-template-{size_key}: {size};'.format(
+        flow=flow, size_key=size_keymap[flow], size=size
+    )
+    tpl = """
+    <div style="display: grid; %s">
+        {{#contents}}
+            {{& pywebio_output_parse}}
+        {{/contents}}
+    </div>""".strip() % style
+    return put_widget(template=tpl, data=dict(contents=content), scope=scope,
+                      position=position)
+
+
+@safely_destruct_output_when_exp('content')
+def put_grid(content, cell_width='auto', cell_height='auto', direction='row', scope=Scope.Current,
+             position=OutputPosition.BOTTOM) -> Output:
+    """使用网格布局输出内容
+
+    :param content: 输出内容. ``put_xxx()`` 调用的二维数组
+    :param str cell_width: 网格元素的宽度. 宽度值格式参考 `put_column()` 函数的 size 参数的注释.
+    :param str cell_height: 网格元素的高度. 高度值格式参考 `put_column()` 函数的 size 参数的注释.
+    :param str direction: 排列方向. 为 ``'row'`` 或 ``'column'`` .
+
+        | ``'row'`` 时表示,content中的每一个子数组代表网格的一行;
+        | ``'column'`` 时表示,content中的每一个子数组代表网格的一列.
+    """
+    assert direction in ('row', 'column'), '"direction" parameter must be "row" or "column"'
+
+    row_cnt, col_cnt = len(content), len(content[0])
+    if direction == 'column':
+        row_cnt, col_cnt = len(content[0]), len(content)
+
+    style = ('grid-auto-flow: {flow};'
+             'grid-template-columns: repeat({col_cnt},{cell_height});'
+             'grid-template-rows: repeat({row_cnt},{cell_width});'
+             ).format(flow=direction, cell_height=cell_height, cell_width=cell_width, col_cnt=col_cnt, row_cnt=row_cnt)
+
+    tpl = """
+    <div style="display: grid; %s">
+        {{#contents}}
+            {{#.}}
+                {{& pywebio_output_parse}}
+            {{/.}}
+        {{/contents}}
+    </div>""".strip() % style
+    return put_widget(template=tpl, data=dict(contents=content), scope=scope, position=position)
+
+
+column = put_column
+row = put_row
+grid = put_grid
+
+
 @safely_destruct_output_when_exp('outputs')
 def style(outputs, css_style) -> Union[Output, OutputList]:
     """自定义输出内容的css样式
@@ -662,8 +764,9 @@ def style(outputs, css_style) -> Union[Output, OutputList]:
     :type outputs: list/put_xxx()
     :param css_style: css样式字符串
     :return: 添加了css样式的输出内容。
-       若 ``outputs`` 为 ``put_xxx()`` 调用,返回值为添加了css样式的输出。
-       若 ``outputs`` 为list,返回值为 ``outputs`` 中每一项都添加了css样式的list。
+
+       | 若 ``outputs`` 为 ``put_xxx()`` 调用,返回值为添加了css样式的输出, 可用于任何接受 ``put_xxx()`` 类调用的地方。
+       | 若 ``outputs`` 为list,返回值为 ``outputs`` 中每一项都添加了css样式的list, 可用于任何接受 ``put_xxx()`` 列表的地方。
 
     :Example: