{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 表格\n", "\n", "基于Quasar的[QTable组件](https://quasar.dev/vue-components/table)的表格。\n", "\n", "- `columns`: 列对象列表\n", "- `rows`: 行对象列表\n", "- `row_key`: 包含唯一数据以标识行的列的名称(默认:`\"id\"`)\n", "- `title`: 表格的标题\n", "- `selection`: 选择类型(`\"single\"`或`\"multiple\"`;默认:`None`)\n", "- `pagination`: 与分页对象或每页行数相关的字典(None隐藏分页,0表示\"无限\";默认:`None`)。\n", "- `on_select`: 当选择改变时调用的回调函数\n", "- `on_pagination_change`: 当分页改变时调用的回调函数\n", "\n", "如果选择是`'single'`或`'multiple'`,那么可以访问包含所选行的属性`selected`。" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from nicegui import ui\n", "\n", "columns = [\n", " {'name': 'name', 'label': 'Name', 'field': 'name', 'required': True, 'align': 'left'},\n", " {'name': 'age', 'label': 'Age', 'field': 'age', 'sortable': True},\n", "]\n", "rows = [\n", " {'name': 'Alice', 'age': 18},\n", " {'name': 'Bob', 'age': 21},\n", " {'name': 'Carol'},\n", "]\n", "ui.table(columns=columns, rows=rows, row_key='name')\n", "\n", "# ui.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 具有可展开行的表格\n", "可以使用作用域插槽插入按钮,以切换表格行的展开状态。有关更多信息,请参阅[Quasar文档](https://quasar.dev/vue-components/table#expanding-rows)。" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from nicegui import ui\n", "\n", "columns = [\n", " {'name': 'name', 'label': 'Name', 'field': 'name'},\n", " {'name': 'age', 'label': 'Age', 'field': 'age'},\n", "]\n", "rows = [\n", " {'name': 'Alice', 'age': 18},\n", " {'name': 'Bob', 'age': 21},\n", " {'name': 'Carol'},\n", "]\n", "\n", "table = ui.table(columns=columns, rows=rows, row_key='name').classes('w-72')\n", "table.add_slot('header', r'''\n", " \n", " \n", " \n", " {{ col.label }}\n", " \n", " \n", "''')\n", "table.add_slot('body', r'''\n", " \n", " \n", " \n", " \n", " \n", " {{ col.value }}\n", " \n", " \n", " \n", " \n", "
This is {{ props.row.name }}.
\n", "
\n", "
\n", "''')\n", "\n", "# ui.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 显示和隐藏列\n", "这里是一个示例,展示了如何在表格中显示和隐藏列。" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "from nicegui import ui\n", "from typing import Dict\n", "\n", "columns = [\n", " {'name': 'name', 'label': 'Name', 'field': 'name', 'required': True, 'align': 'left'},\n", " {'name': 'age', 'label': 'Age', 'field': 'age', 'sortable': True},\n", "]\n", "rows = [\n", " {'name': 'Alice', 'age': 18},\n", " {'name': 'Bob', 'age': 21},\n", " {'name': 'Carol'},\n", "]\n", "table = ui.table(columns=columns, rows=rows, row_key='name')\n", "\n", "def toggle(column: Dict, visible: bool) -> None:\n", " column['classes'] = '' if visible else 'hidden'\n", " column['headerClasses'] = '' if visible else 'hidden'\n", " table.update()\n", "\n", "with ui.button(icon='menu'):\n", " with ui.menu(), ui.column().classes('gap-0 p-2'):\n", " for column in columns:\n", " ui.switch(column['label'], value=True, on_change=lambda e,\n", " column=column: toggle(column, e.value))\n", "\n", "# ui.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 带有下拉选择的表格\n", "这里是一个示例,展示了如何在表格中使用下拉选择。从作用域插槽发出重命名事件后,重命名函数会更新表格行。" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from nicegui import events, ui\n", "\n", "columns = [\n", " {'name': 'name', 'label': 'Name', 'field': 'name'},\n", " {'name': 'age', 'label': 'Age', 'field': 'age'},\n", "]\n", "rows = [\n", " {'id': 0, 'name': 'Alice', 'age': 18},\n", " {'id': 1, 'name': 'Bob', 'age': 21},\n", " {'id': 2, 'name': 'Carol'},\n", "]\n", "name_options = ['Alice', 'Bob', 'Carol']\n", "\n", "def rename(e: events.GenericEventArguments) -> None:\n", " for row in rows:\n", " if row['id'] == e.args['id']:\n", " row['name'] = e.args['name']\n", " ui.notify(f'Table.rows is now: {table.rows}')\n", "\n", "table = ui.table(columns=columns, rows=rows).classes('w-full')\n", "table.add_slot('body-cell-name', r'''\n", " \n", " $parent.$emit('rename', props.row)\"\n", " />\n", " \n", "''')\n", "table.on('rename', rename)\n", "\n", "# ui.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 从Pandas DataFrame创建表格\n", "您可以使用`from_pandas`方法从Pandas DataFrame创建一个表格。这个方法接受一个Pandas DataFrame作为输入,并返回一个表格。" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import pandas as pd\n", "from nicegui import ui\n", "\n", "df = pd.DataFrame(data={'col1': [1, 2], 'col2': [3, 4]})\n", "ui.table.from_pandas(df).classes('max-h-40')\n", "\n", "# ui.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 添加行\n", "使用`add_rows(dict)`方法可以轻松添加新行。" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "import os\n", "import random\n", "from nicegui import ui\n", "\n", "def add():\n", " item = os.urandom(10 // 2).hex()\n", " table.add_rows({'id': item, 'count': random.randint(0, 100)})\n", "\n", "ui.button('add', on_click=add)\n", "columns = [\n", " {'name': 'id', 'label': 'ID', 'field': 'id'},\n", " {'name': 'count', 'label': 'Count', 'field': 'count'},\n", "]\n", "table = ui.table(columns=columns, rows=[], row_key='id').classes('w-full')\n", "\n", "# ui.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 自定义排序和格式化\n", "您可以使用:前缀定义动态列属性。这样,您可以定义自定义排序和格式化函数。\n", "\n", "以下示例允许按名称长度对名称列进行排序。年龄列被格式化以显示年龄(以年为单位)。" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from nicegui import ui\n", "\n", "columns = [\n", " {\n", " 'name': 'name',\n", " 'label': 'Name',\n", " 'field': 'name',\n", " 'sortable': True,\n", " ':sort': '(a, b, rowA, rowB) => b.length - a.length',\n", " },\n", " {\n", " 'name': 'age',\n", " 'label': 'Age',\n", " 'field': 'age',\n", " ':format': 'value => value + \" years\"',\n", " },\n", "]\n", "rows = [\n", " {'name': 'Alice', 'age': 18},\n", " {'name': 'Bob', 'age': 21},\n", " {'name': 'Carl', 'age': 42},\n", "]\n", "ui.table(columns=columns, rows=rows, row_key='name')\n", "\n", "# ui.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 切换全屏\n", "您可以使用`toggle_fullscreen()`方法切换表格的全屏模式。" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "from nicegui import ui\n", "\n", "table = ui.table(\n", " columns=[{'name': 'name', 'label': 'Name', 'field': 'name'}],\n", " rows=[{'name': 'Alice'}, {'name': 'Bob'}, {'name': 'Carol'}],\n", ").classes('w-full')\n", "\n", "with table.add_slot('top-left'):\n", " def toggle() -> None:\n", " table.toggle_fullscreen()\n", " button.props('icon=fullscreen_exit' if table.is_fullscreen else 'icon=fullscreen')\n", " button = ui.button('Toggle fullscreen', icon='fullscreen', on_click=toggle).props('flat')\n", "\n", "# ui.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 分页\n", "您可以提供一个整数或一个字典来定义分页。\n", "\n", "该字典可以包含以下键:\n", "\n", "- `rowsPerPage`: 每页的行数。\n", "- `sortBy`: 要排序的列名。\n", "- `descending`: 是否按降序排序。\n", "- `page`: 当前页(从`1`开始)。" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from nicegui import ui\n", "\n", "columns = [\n", " {'name': 'name', 'label': 'Name', 'field': 'name', 'required': True, 'align': 'left'},\n", " {'name': 'age', 'label': 'Age', 'field': 'age', 'sortable': True},\n", "]\n", "rows = [\n", " {'name': 'Elsa', 'age': 18},\n", " {'name': 'Oaken', 'age': 46},\n", " {'name': 'Hans', 'age': 20},\n", " {'name': 'Sven'},\n", " {'name': 'Olaf', 'age': 4},\n", " {'name': 'Anna', 'age': 17},\n", "]\n", "ui.table(columns=columns, rows=rows, pagination=3)\n", "ui.table(columns=columns, rows=rows, pagination={'rowsPerPage': 4, 'sortBy': 'age', 'page': 2})\n", "\n", "# ui.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 处理分页变化\n", "您可以使用`on_pagination_change`参数来处理分页变化。" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from nicegui import ui\n", "\n", "ui.table(\n", " columns=[{'id': 'Name', 'label': 'Name', 'field': 'Name', 'align': 'left'}],\n", " rows=[{'Name': f'Person {i}'} for i in range(100)],\n", " pagination=3,\n", " on_pagination_change=lambda e: ui.notify(e.value),\n", ")\n", "\n", "# ui.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 计算字段\n", "您可以使用函数来计算列的值。该函数将行作为参数。有关更多信息,请参阅[Quasar文档](https://quasar.dev/vue-components/table#defining-the-columns)。" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from nicegui import ui\n", "\n", "columns = [\n", " {'name': 'name', 'label': 'Name', 'field': 'name', 'align': 'left'},\n", " {'name': 'length', 'label': 'Length', ':field': 'row => row.name.length'},\n", "]\n", "rows = [\n", " {'name': 'Alice'},\n", " {'name': 'Bob'},\n", " {'name': 'Christopher'},\n", "]\n", "ui.table(columns=columns, rows=rows, row_key='name')\n", "\n", "# ui.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 条件格式化\n", "您可以使用作用域插槽来有条件地格式化单元格的内容。有关body-cell插槽的更多信息,请[参阅Quasar文档](https://quasar.dev/vue-components/table#example--body-cell-slot)。\n", "\n", "在这个演示中,我们使用`q-badge`以红色显示21岁以下人员的年龄。我们使用`body-cell-age`插槽将`q-badge`插入年龄列中。如果年龄在21岁以下,则q-badge的`\":color\"`属性设置为`\"red\"`,否则设置为`\"green\"`。`\":\"color\"`属性前的冒号表示该值是一个JavaScript表达式。" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from nicegui import ui\n", "\n", "columns = [\n", " {'name': 'name', 'label': 'Name', 'field': 'name'},\n", " {'name': 'age', 'label': 'Age', 'field': 'age'},\n", "]\n", "rows = [\n", " {'name': 'Alice', 'age': 18},\n", " {'name': 'Bob', 'age': 21},\n", " {'name': 'Carol', 'age': 42},\n", "]\n", "table = ui.table(columns=columns, rows=rows, row_key='name')\n", "table.add_slot('body-cell-age', '''\n", " \n", " \n", " {{ props.value }}\n", " \n", " \n", "''')\n", "\n", "# ui.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 表格单元格中的链接\n", "这里是一个演示,展示了如何将链接插入到表格单元格中。我们使用 `body-cell-link` 插槽将 `` 标签插入到链接列中。" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from nicegui import ui\n", "\n", "columns = [\n", " {'name': 'name', 'label': 'Name', 'field': 'name', 'align': 'left'},\n", " {'name': 'link', 'label': 'Link', 'field': 'link', 'align': 'left'},\n", "]\n", "rows = [\n", " {'name': 'Google', 'link': 'https://google.com'},\n", " {'name': 'Facebook', 'link': 'https://facebook.com'},\n", " {'name': 'Twitter', 'link': 'https://twitter.com'},\n", "]\n", "table = ui.table(columns=columns, rows=rows, row_key='name')\n", "table.add_slot('body-cell-link', '''\n", " \n", " {{ props.value }}\n", " \n", "''')\n", "\n", "# ui.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 类似砖石布局的表格\n", "您可以使用`grid`属性将表格显示为类似砖石布局的网格。有关更多信息,请参阅[Quasar文档](https://quasar.dev/vue-components/table#grid-style)。" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from nicegui import ui\n", "\n", "columns = [\n", " {'name': 'name', 'label': 'Name', 'field': 'name'},\n", " {'name': 'age', 'label': 'Age', 'field': 'age'},\n", "]\n", "rows = [\n", " {'name': 'Alice', 'age': 18},\n", " {'name': 'Bob', 'age': 21},\n", " {'name': 'Carol', 'age': 42},\n", "]\n", "table = ui.table(columns=columns, rows=rows, row_key='name').props('grid')\n", "table.add_slot('item', r'''\n", " \n", " \n", " {{ props.row.name }}\n", " \n", " \n", " \n", "
{{ props.row.age }} years
\n", "
\n", "
\n", "''')\n", "\n", "# ui.run()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "py311", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.7" } }, "nbformat": 4, "nbformat_minor": 2 }