├── .gitattributes
├── .github
├── codecov.yml
└── workflows
│ ├── comment-pr.yml
│ ├── continuous-integration.yml
│ ├── publish-book.yml
│ └── publish.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .pylintrc
├── LICENSE
├── README.md
├── apps
├── dash
│ ├── 1_display_only.py
│ ├── 2_selected_rows.py
│ ├── 3_update_table.py
│ └── assets
│ │ └── style.css
├── marimo
│ ├── html.py
│ └── widget.py
├── shiny
│ ├── README.md
│ ├── itable_widget
│ │ ├── app-core.py
│ │ ├── app.py
│ │ ├── requirements.txt
│ │ └── rsconnect-python
│ │ │ └── itable_widget.json
│ └── itables_DT
│ │ ├── app-core.py
│ │ ├── app.py
│ │ ├── requirements.txt
│ │ └── rsconnect-python
│ │ └── itables_DT.json
└── streamlit
│ ├── itables_app.py
│ └── requirements.txt
├── binder
├── labconfig
│ └── default_setting_overrides.json
├── postBuild
└── requirements.txt
├── docs
├── _config.yml
├── _toc.yml
├── apps
│ ├── dash.md
│ ├── html.md
│ ├── marimo.md
│ ├── notebook.md
│ ├── quarto.md
│ ├── shiny.md
│ ├── streamlit.md
│ └── widget.md
├── changelog.md
├── contributing.md
├── css.md
├── custom_extensions.md
├── dark_mode.md
├── developing.md
├── df_example.png
├── downsampling.md
├── formatting.md
├── images
│ ├── code.png
│ ├── colab.png
│ ├── html.png
│ ├── lab.png
│ ├── notebook.png
│ └── pycharm.png
├── options
│ ├── allow_html.md
│ ├── buttons.md
│ ├── caption.md
│ ├── classes.md
│ ├── column_defs.md
│ ├── column_filters.md
│ ├── colvis.md
│ ├── fixed_columns.md
│ ├── footer.md
│ ├── keys.md
│ ├── layout.md
│ ├── length_menu.md
│ ├── options.md
│ ├── order.md
│ ├── paging.md
│ ├── row_group.md
│ ├── scroll_x.md
│ ├── scroll_y.md
│ ├── search.md
│ ├── search_builder.md
│ ├── search_panes.md
│ ├── select.md
│ ├── show_index.md
│ ├── state_save.md
│ ├── style.md
│ └── text_in_header_can_be_selected.md
├── pandas_dataframes.md
├── pandas_style.md
├── polars_dataframes.md
├── py
│ ├── README.md
│ ├── apps
│ │ ├── dash.py
│ │ ├── html.py
│ │ ├── marimo.py
│ │ ├── notebook.py
│ │ └── widget.py
│ ├── contributing.py
│ ├── css.py
│ ├── custom_extensions.py
│ ├── dark_mode.py
│ ├── downsampling.py
│ ├── formatting.py
│ ├── options
│ │ ├── allow_html.py
│ │ ├── buttons.py
│ │ ├── caption.py
│ │ ├── classes.py
│ │ ├── column_defs.py
│ │ ├── column_filters.py
│ │ ├── colvis.py
│ │ ├── fixed_columns.py
│ │ ├── footer.py
│ │ ├── keys.py
│ │ ├── layout.py
│ │ ├── length_menu.py
│ │ ├── options.py
│ │ ├── order.py
│ │ ├── paging.py
│ │ ├── row_group.py
│ │ ├── scroll_x.py
│ │ ├── scroll_y.py
│ │ ├── search.py
│ │ ├── search_builder.py
│ │ ├── search_panes.py
│ │ ├── select.py
│ │ ├── show_index.py
│ │ ├── state_save.py
│ │ ├── style.py
│ │ └── text_in_header_can_be_selected.py
│ ├── pandas_dataframes.py
│ ├── pandas_style.py
│ ├── polars_dataframes.py
│ ├── quick_start.py
│ └── troubleshooting.py
├── quarto
│ ├── quarto_html.qmd
│ └── quarto_revealjs.qmd
├── quick_start.md
├── references.md
├── show_df.png
├── supported_editors.md
└── troubleshooting.md
├── environment.yml
├── packages
├── dt_for_itables
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ └── src
│ │ ├── index.css
│ │ ├── index.d.ts
│ │ └── index.js
├── itables_anywidget
│ ├── README.md
│ ├── js
│ │ ├── widget.css
│ │ └── widget.ts
│ ├── package-lock.json
│ ├── package.json
│ └── tsconfig.json
├── itables_for_dash
│ ├── .babelrc
│ ├── package-lock.json
│ ├── package.json
│ ├── src
│ │ └── lib
│ │ │ ├── LazyLoader.js
│ │ │ ├── components
│ │ │ └── ITable.react.js
│ │ │ ├── fragments
│ │ │ └── ITable.react.js
│ │ │ └── index.js
│ └── webpack.config.js
├── itables_for_streamlit
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ └── index.tsx
│ └── tsconfig.json
├── package-lock.json
└── package.json
├── pyproject.toml
├── src
├── itables
│ ├── __init__.py
│ ├── dash.py
│ ├── datatables_format.py
│ ├── downsample.py
│ ├── html
│ │ ├── datatables_template.html
│ │ └── init_datatables.html
│ ├── interactive.py
│ ├── javascript.py
│ ├── logo
│ │ ├── loading.svg
│ │ ├── logo.svg
│ │ ├── text.svg
│ │ ├── wide.svg
│ │ └── with_text.svg
│ ├── options.py
│ ├── py.typed
│ ├── sample_dfs.py
│ ├── samples
│ │ ├── countries.csv
│ │ ├── indicators.csv
│ │ └── population.csv
│ ├── shiny.py
│ ├── streamlit.py
│ ├── typing.py
│ ├── utils.py
│ ├── version.py
│ └── widget
│ │ └── __init__.py
└── itables_for_dash
│ ├── ITable.py
│ ├── __init__.py
│ ├── properties.py
│ └── py.typed
└── tests
├── __init__.py
├── conftest.py
├── test_changelog.py
├── test_connected_notebook_is_small.py
├── test_datatables_format.py
├── test_documentation_notebooks_run.py
├── test_downsample.py
├── test_extension_arguments.py
├── test_html_in_table_header.py
├── test_init.py
├── test_itables_for_dash.py
├── test_javascript.py
├── test_keys_to_be_evaluated.py
├── test_marimo.py
├── test_notebook.ipynb
├── test_pandas_style.py
├── test_polars.py
├── test_sample_dfs.py
├── test_shiny.py
├── test_streamlit.py
├── test_typing.py
├── test_update_samples.py
└── test_widget.py
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.ipynb linguist-documentation
2 |
--------------------------------------------------------------------------------
/.github/codecov.yml:
--------------------------------------------------------------------------------
1 | codecov:
2 | token: cc72f10a-bed8-4475-a793-62b028610aa3
3 | notify:
4 | wait_for_ci: true
5 |
6 | coverage:
7 | status:
8 | project:
9 | default: false
10 | tests:
11 | paths:
12 | - "tests/"
13 | target: 99%
14 | source:
15 | paths:
16 | - "src/"
17 | target: 93%
18 | threshold: 0.002
19 | patch:
20 | default:
21 | target: 80%
22 |
--------------------------------------------------------------------------------
/.github/workflows/comment-pr.yml:
--------------------------------------------------------------------------------
1 | name: Comment PR
2 |
3 | on:
4 | pull_request_target:
5 |
6 | permissions:
7 | pull-requests: write
8 |
9 | jobs:
10 | comment-pr:
11 | runs-on: ubuntu-latest
12 | name: Comment PR
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v4
16 |
17 | - name: Comment PR
18 | uses: thollander/actions-comment-pull-request@v2
19 | with:
20 | message: |
21 | Thank you for making this pull request.
22 |
23 | Did you know? You can try it on Binder: [](https://mybinder.org/v2/gh/${{ github.event.pull_request.head.repo.full_name }}/${{ github.event.pull_request.head.ref }}?urlpath=lab/tree/docs/quick_start.md).
24 |
25 | Also, the version of ITables developed in this PR can be installed with `pip`:
26 | ```
27 | pip install git+${{ github.event.pull_request.head.repo.clone_url }}@${{ github.event.pull_request.head.ref }}
28 | ```
29 | (this requires `nodejs`, see more at [Developing ITables](https://mwouts.github.io/itables/developing.html))
30 | comment_tag: binder_link
31 |
--------------------------------------------------------------------------------
/.github/workflows/publish-book.yml:
--------------------------------------------------------------------------------
1 | # Only run this when the main branch changes
2 | on:
3 | push:
4 | branches:
5 | - main
6 | # If your git repository has the Jupyter Book within some-subfolder next to
7 | # unrelated files, you can make this run only if a file within that specific
8 | # folder has been modified.
9 | #
10 | # paths:
11 | # - docs/**
12 |
13 | # This job installs dependencies, builds the book, and pushes it to `gh-pages`
14 | jobs:
15 | deploy-book:
16 | # Grant GITHUB_TOKEN the permissions required to make a Pages deployment
17 | permissions:
18 | pages: write # to deploy to Pages
19 | id-token: write # to verify the deployment originates from an appropriate source
20 |
21 | # Deploy to the github-pages environment
22 | environment:
23 | name: github-pages
24 | url: ${{ steps.deployment.outputs.page_url }}
25 |
26 | runs-on: ubuntu-latest
27 | steps:
28 | - uses: actions/checkout@v4
29 |
30 | - name: Set up Node
31 | uses: actions/setup-node@v4
32 |
33 | - name: Set up Python 3.11
34 | uses: actions/setup-python@v5
35 | with:
36 | python-version: 3.11
37 |
38 | - name: Install dependencies
39 | run: |
40 | pip install jupyter-book sphinxext-rediraffe
41 | pip install matplotlib # Pandas style
42 | pip install .[all]
43 |
44 | - name: Create a kernel
45 | run: |
46 | pip install ipykernel
47 | python -m ipykernel install --name itables --user
48 |
49 | - name: Install Quarto
50 | uses: quarto-dev/quarto-actions/setup@v2
51 |
52 | - name: Render the quarto examples
53 | run: |
54 | for qmd_file in `ls docs/quarto/*.qmd`; do quarto render ${qmd_file}; done
55 |
56 | # Build the book
57 | - name: Build the book
58 | run: |
59 | jupyter-book build docs
60 |
61 | # Upload the book's HTML as an artifact
62 | - name: Upload artifact
63 | uses: actions/upload-pages-artifact@v3
64 | with:
65 | path: "docs/_build/html"
66 |
67 | # Deploy the book's HTML to GitHub Pages
68 | - name: Deploy to GitHub Pages
69 | id: deployment
70 | uses: actions/deploy-pages@v4
71 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish to PyPI
2 | on:
3 | push:
4 | tags:
5 | - "v[0-9]+.[0-9]+.[0-9]+*"
6 |
7 | jobs:
8 | publish:
9 | runs-on: ubuntu-latest
10 | environment:
11 | name: pypi
12 | url: https://pypi.org/p/itables
13 | permissions:
14 | id-token: write
15 | steps:
16 | - name: Checkout source
17 | uses: actions/checkout@v4
18 | - name: Set up Node
19 | uses: actions/setup-node@v4
20 | - name: Set up Python
21 | uses: actions/setup-python@v5
22 | with:
23 | python-version: 3.11
24 | - name: Build package
25 | run: |
26 | python -m pip install hatch
27 | python -m hatch build
28 | - name: Publish
29 | uses: pypa/gh-action-pypi-publish@release/v1
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # PyCharm & VS Code
2 | .idea
3 | .vscode
4 |
5 | # Python
6 | *.egg-info
7 | *.pyc
8 | build
9 | dist
10 |
11 | # Notebooks
12 | *.ipynb
13 | .ipynb_checkpoints
14 | !tests/test_notebook.ipynb
15 |
16 | # Jupyter Book
17 | _build
18 |
19 | # Quarto
20 | .jupyter_cache
21 | docs/quarto/*.html
22 | docs/quarto/*_files/
23 |
24 | # Node packages
25 | node_modules
26 |
27 | # DataTables bundle
28 | dt_bundle.js
29 | dt_bundle.css
30 |
31 | # Dash component
32 | src/itables_for_dash/*.js*
33 | src/itables_for_dash/_imports_.py
34 |
35 | # Streamlit component
36 | src/itables/itables_for_streamlit
37 |
38 | # Jupyter Widget
39 | src/itables/widget/static
40 |
41 | # On-going research
42 | research
43 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | # Install pre-commit hooks via
2 | # pre-commit install
3 |
4 | exclude: ^src/itables_for_dash/ITable.py$ # auto-generated
5 | repos:
6 |
7 | - repo: https://github.com/pre-commit/pre-commit-hooks
8 | rev: v5.0.0
9 | hooks:
10 | - id: check-json
11 | - id: check-yaml
12 | - id: end-of-file-fixer
13 | - id: trailing-whitespace
14 |
15 | - repo: https://github.com/timothycrosley/isort
16 | rev: 6.0.1
17 | hooks:
18 | - id: isort
19 | args: ["--profile", "black", "--filter-files"]
20 |
21 | - repo: https://github.com/psf/black
22 | rev: 25.1.0
23 | hooks:
24 | - id: black
25 |
26 | - repo: https://github.com/astral-sh/ruff-pre-commit
27 | rev: v0.11.7
28 | hooks:
29 | - id: ruff
30 | args: ["--fix", "--show-fixes"]
31 |
32 | - repo: https://github.com/mwouts/jupytext
33 | rev: v1.17.1
34 | hooks:
35 | - id: jupytext
36 | files: "^docs/"
37 | args: ["--sync"]
38 |
--------------------------------------------------------------------------------
/.pylintrc:
--------------------------------------------------------------------------------
1 | [MASTER]
2 |
3 | max-line-length=127
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019-2025 Marc Wouts
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/apps/dash/1_display_only.py:
--------------------------------------------------------------------------------
1 | from dash import Dash, html
2 |
3 | from itables.dash import ITable
4 | from itables.sample_dfs import get_countries
5 |
6 | app = Dash(__name__)
7 |
8 | df = get_countries(html=False)
9 |
10 | app.layout = html.Div(
11 | [
12 | html.H1("ITables in a Dash application"),
13 | ITable(id="my_dataframe", df=df, caption="A DataFrame displayed with ITables"),
14 | ]
15 | )
16 |
17 | if __name__ == "__main__":
18 | app.run(debug=True)
19 |
--------------------------------------------------------------------------------
/apps/dash/2_selected_rows.py:
--------------------------------------------------------------------------------
1 | from dash import Dash, Input, Output, callback, html
2 |
3 | from itables.dash import ITable
4 | from itables.sample_dfs import get_countries
5 |
6 | app = Dash(__name__)
7 |
8 | df = get_countries(html=False)
9 |
10 | app.layout = html.Div(
11 | [
12 | html.H1("ITables in a Dash application"),
13 | ITable(
14 | id="my_dataframe",
15 | df=df,
16 | caption="A DataFrame displayed with ITables",
17 | select=True,
18 | ),
19 | html.Div(id="output"),
20 | ]
21 | )
22 |
23 |
24 | @callback(
25 | Output("output", "children"),
26 | Input("my_dataframe", "selected_rows"),
27 | )
28 | def show_selection(selected_rows):
29 | return f"Selected rows: {selected_rows}"
30 |
31 |
32 | if __name__ == "__main__":
33 | app.run(debug=True)
34 |
--------------------------------------------------------------------------------
/apps/dash/assets/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Arial, sans-serif;
3 | margin: 0;
4 | padding: 0;
5 | background-color: #f5f5f5;
6 | }
7 | .container {
8 | display: flex;
9 | min-height: 100vh;
10 | }
11 | .sidebar {
12 | width: 250px;
13 | background-color: #f0f0f0;
14 | padding: 20px;
15 | box-shadow: 2px 0 5px rgba(0,0,0,0.1);
16 | }
17 | .main-content {
18 | flex: 1;
19 | padding: 20px;
20 | }
21 |
--------------------------------------------------------------------------------
/apps/marimo/html.py:
--------------------------------------------------------------------------------
1 | import marimo
2 |
3 | __generated_with = "0.13.6"
4 | app = marimo.App()
5 |
6 |
7 | @app.cell(hide_code=True)
8 | def _(mo):
9 | mo.md(
10 | r"""
11 | # Using the ITable HTML representation in Marimo
12 |
13 | The `show` method of ITables does not work in Marimo. We recommend
14 | that you use the `ITable` widget instead.
15 |
16 | However you can also use the `to_html_datatable` method in combination
17 | with `mo.iframe` to display a table in Marimo. You need to pass
18 | `connected=True` as the HTML snippet is not able to access the `init_notebook_mode` cell.
19 | """
20 | )
21 | return
22 |
23 |
24 | @app.cell
25 | def _():
26 | import marimo as mo
27 |
28 | from itables import to_html_datatable
29 | from itables.sample_dfs import get_dict_of_test_dfs
30 |
31 | df = get_dict_of_test_dfs()["int_float_str"]
32 |
33 | html = to_html_datatable(df, selected_rows=[0, 2, 5], select=True, connected=True)
34 | mo.iframe(html)
35 |
36 |
37 | if __name__ == "__main__":
38 | app.run()
39 |
--------------------------------------------------------------------------------
/apps/shiny/README.md:
--------------------------------------------------------------------------------
1 | # ITables in Shiny for Python: a Demo
2 |
3 | This directory contains two example Python for Shiny applications that render
4 | Pandas DataFrames using [ITables](https://mwouts.github.io/itables).
5 |
6 | ## The ITable Widget
7 |
8 | This is the recommended way to use ITable in a Shiny application.
9 |
10 | See the source code at [`app.py`](itable_widget/app.py) (Shiny Express) or [`app-core.py`](itable_widget/app-core.py) (Shiny Core).
11 |
12 | Install the requirements with
13 | ```
14 | pip install -r itable_widget/requirements.txt
15 | ```
16 | and launch the app with
17 | ```
18 | shiny run itable_widget/app-express.py # or app-core.py
19 | ```
20 |
21 | Or access the app online at https://itables.shinyapps.io/itable_widget/
22 |
23 | ## Using DT
24 |
25 | This is an alternative way to use ITable in a Shiny application.
26 |
27 | See the source code at [`app.py`](itables_DT/app.py) (Shiny Express) or [`app-core.py`](itables_DT/app-core.py) (Shiny Core).
28 |
29 | Install the requirements with
30 | ```
31 | pip install -r itables_DT/requirements.txt
32 | ```
33 | and launch the app with
34 | ```
35 | shiny run itables_DT/app-express.py # or app-core.py
36 | ```
37 |
38 | Or access the app online at https://itables.shinyapps.io/itables_DT/
39 |
--------------------------------------------------------------------------------
/apps/shiny/itable_widget/app-core.py:
--------------------------------------------------------------------------------
1 | from shiny import App, reactive, render, ui
2 | from shinywidgets import output_widget, reactive_read, render_widget
3 |
4 | from itables.sample_dfs import get_dict_of_test_dfs
5 | from itables.widget import ITable
6 |
7 | dfs = get_dict_of_test_dfs()
8 |
9 | app_ui = ui.page_sidebar(
10 | ui.sidebar(
11 | ui.input_select(
12 | "table_selector",
13 | "Table",
14 | choices=list(dfs.keys()),
15 | selected="int_float_str",
16 | )
17 | ),
18 | output_widget("my_table"),
19 | ui.markdown("Selected rows"),
20 | ui.output_code("selected_rows"),
21 | title="Using the ITable Widget in a Shiny App",
22 | fillable=True,
23 | )
24 |
25 |
26 | def server(input, output, session):
27 | @render_widget
28 | def my_table():
29 | """
30 | This function creates the "my_table" widget. While we could
31 | pass the df or the other arguments here, it's nicer
32 | to use a "reactive.effect" to update the widget
33 | """
34 | return ITable(caption="A table rendered with ITable", select=True)
35 |
36 | @reactive.effect
37 | def _():
38 | """
39 | This "reactive.effect" uses the "update" method of the ITable widget
40 | to update the widget with the new inputs.
41 | """
42 | # Get the new inputs
43 | df = dfs[input.table_selector()]
44 | selected_rows = list(range(0, len(df), 3))
45 |
46 | # Update the widget
47 | assert isinstance(
48 | my_table.widget, ITable
49 | ) # otherwise pyright complains it might be None
50 | my_table.widget.update(df, selected_rows=selected_rows)
51 |
52 | ui.markdown("Selected rows")
53 |
54 | @render.code
55 | def selected_rows():
56 | """
57 | Here we read the "selected_rows" attribute of the ITable widget
58 | """
59 | assert isinstance(
60 | my_table.widget, ITable
61 | ) # otherwise pyright complains it might be None
62 | return str(reactive_read(my_table.widget, "selected_rows"))
63 |
64 |
65 | app = App(app_ui, server)
66 |
--------------------------------------------------------------------------------
/apps/shiny/itable_widget/app.py:
--------------------------------------------------------------------------------
1 | from shiny import reactive
2 | from shiny.express import input, render, ui
3 | from shinywidgets import reactive_read, render_widget
4 |
5 | from itables.sample_dfs import get_dict_of_test_dfs
6 | from itables.widget import ITable
7 |
8 | dfs = get_dict_of_test_dfs()
9 |
10 | ui.page_opts(title="Using the ITable Widget in a Shiny App", fillable=True)
11 |
12 | with ui.card():
13 | with ui.layout_sidebar():
14 | with ui.sidebar():
15 | ui.input_select(
16 | "table_selector",
17 | "Table",
18 | choices=list(dfs.keys()),
19 | selected="int_float_str",
20 | )
21 |
22 | @render_widget
23 | def my_table():
24 | """
25 | This function creates the "my_table" widget. While we could
26 | pass the df or the other arguments here, it's nicer
27 | to use a "reactive.effect" to update the widget
28 | """
29 | return ITable(caption="A table rendered with ITable", select=True)
30 |
31 | @reactive.effect
32 | def _():
33 | """
34 | This "reactive.effect" uses the "update" method of the ITable widget
35 | to update the widget with the new inputs.
36 | """
37 | # Get the new inputs
38 | df = dfs[input.table_selector()]
39 | selected_rows = list(range(0, len(df), 3))
40 |
41 | # Update the widget
42 | assert isinstance(
43 | my_table.widget, ITable
44 | ) # otherwise pyright complains it might be None
45 | my_table.widget.update(df, selected_rows=selected_rows)
46 |
47 | ui.markdown("Selected rows")
48 |
49 | @render.code
50 | def selected_rows():
51 | """
52 | Here we read the "selected_rows" attribute of the ITable widget
53 | """
54 | assert isinstance(
55 | my_table.widget, ITable
56 | ) # otherwise pyright complains it might be None
57 | return str(reactive_read(my_table.widget, "selected_rows"))
58 |
--------------------------------------------------------------------------------
/apps/shiny/itable_widget/requirements.txt:
--------------------------------------------------------------------------------
1 | itables>=2.2
2 | shiny
3 | shinywidgets
4 | anywidget
5 |
--------------------------------------------------------------------------------
/apps/shiny/itable_widget/rsconnect-python/itable_widget.json:
--------------------------------------------------------------------------------
1 | {
2 | "https://api.shinyapps.io": {
3 | "server_url": "https://api.shinyapps.io",
4 | "filename": "/home/marc/GitHub/itables/apps/shiny/itable_widget",
5 | "app_url": "https://itables.shinyapps.io/itable_widget/",
6 | "app_id": 12865888,
7 | "app_guid": null,
8 | "title": "itable_widget",
9 | "app_mode": "python-shiny",
10 | "app_store_version": 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/apps/shiny/itables_DT/app-core.py:
--------------------------------------------------------------------------------
1 | from shiny import App, render, ui
2 |
3 | from itables.sample_dfs import get_dict_of_test_dfs
4 | from itables.shiny import DT, init_itables
5 |
6 | dfs = get_dict_of_test_dfs()
7 |
8 | app_ui = ui.page_sidebar(
9 | ui.sidebar(
10 | ui.input_select(
11 | "table_selector",
12 | "Table",
13 | choices=list(dfs.keys()),
14 | selected="int_float_str",
15 | )
16 | ),
17 | ui.HTML(init_itables()),
18 | ui.output_ui("my_table"),
19 | ui.markdown("Selected rows"),
20 | ui.output_code("selected_rows"),
21 | title="Using DT in a Shiny App",
22 | fillable=True,
23 | )
24 |
25 |
26 | def server(input, output, session):
27 | @render.ui
28 | def my_table():
29 | """
30 | This function renders the table using "DT".
31 | """
32 | df = dfs[input.table_selector()]
33 |
34 | return ui.HTML(DT(df))
35 |
36 |
37 | app = App(app_ui, server)
38 |
--------------------------------------------------------------------------------
/apps/shiny/itables_DT/app.py:
--------------------------------------------------------------------------------
1 | from shiny.express import input, render, ui
2 |
3 | from itables.sample_dfs import get_dict_of_test_dfs
4 | from itables.shiny import DT, init_itables
5 |
6 | dfs = get_dict_of_test_dfs()
7 |
8 | ui.page_opts(title="Using DT in a Shiny App", fillable=True)
9 |
10 | with ui.card():
11 | with ui.layout_sidebar():
12 | with ui.sidebar():
13 | ui.input_select(
14 | "table_selector",
15 | "Table",
16 | choices=list(dfs.keys()),
17 | selected="int_float_str",
18 | )
19 |
20 | ui.HTML(init_itables())
21 |
22 | @render.ui
23 | def my_table():
24 | """
25 | This function renders the table using "DT".
26 | """
27 | df = dfs[input.table_selector()]
28 | return ui.HTML(DT(df, caption="A table rendered with ITable"))
29 |
--------------------------------------------------------------------------------
/apps/shiny/itables_DT/requirements.txt:
--------------------------------------------------------------------------------
1 | itables>=2.2
2 | shiny
3 |
--------------------------------------------------------------------------------
/apps/shiny/itables_DT/rsconnect-python/itables_DT.json:
--------------------------------------------------------------------------------
1 | {
2 | "https://api.shinyapps.io": {
3 | "server_url": "https://api.shinyapps.io",
4 | "filename": "/home/marc/GitHub/itables/apps/shiny/itables_DT",
5 | "app_url": "https://itables.shinyapps.io/itables_dt/",
6 | "app_id": 12865873,
7 | "app_guid": null,
8 | "title": "itables_DT",
9 | "app_mode": "python-shiny",
10 | "app_store_version": 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/apps/streamlit/requirements.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwouts/itables/8fa250a684a6433314d2a984ce375bc9d8b07429/apps/streamlit/requirements.txt
--------------------------------------------------------------------------------
/binder/labconfig/default_setting_overrides.json:
--------------------------------------------------------------------------------
1 | {
2 | "@jupyterlab/docmanager-extension:plugin": {
3 | "defaultViewers": {
4 | "markdown": "Jupytext Notebook",
5 | "myst": "Jupytext Notebook",
6 | "r-markdown": "Jupytext Notebook",
7 | "quarto": "Jupytext Notebook",
8 | "julia": "Jupytext Notebook",
9 | "python": "Jupytext Notebook",
10 | "r": "Jupytext Notebook"
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/binder/postBuild:
--------------------------------------------------------------------------------
1 | # Stop everything if one command fails
2 | set -e
3 |
4 | # Install from sources
5 | pip install .[all]
6 |
7 | # Open .md notebooks with the Notebook editor
8 | mkdir -p ${HOME}/.jupyter/labconfig
9 | cp binder/labconfig/* ${HOME}/.jupyter/labconfig
10 |
11 | # Install a kernel called itables
12 | python -m ipykernel install --name itables --user
13 |
14 | # Use that kernel for the documentation notebooks
15 | jupytext docs/*.md --set-kernel itables
16 | jupytext README.md --set-kernel itables
17 |
--------------------------------------------------------------------------------
/binder/requirements.txt:
--------------------------------------------------------------------------------
1 | jupyterlab>=4.0.0a16
2 | jupytext
3 |
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | title: Interactive Tables
2 | author: Marc Wouts
3 | copyright: "2019-2025"
4 | logo: "../src/itables/logo/logo.svg"
5 | execute:
6 | execute_notebooks: force
7 | sphinx:
8 | config:
9 | html_extra_path: ['quarto']
10 | rediraffe_branch: main
11 | rediraffe_redirects:
12 | advanced_parameters.md: options/options.md
13 | extensions.md: options/buttons.md
14 | select.md: options/select.md
15 | custom_css.md: css.md
16 | dash.md: apps/dash.md
17 | html_export.md: apps/html.md
18 | shiny.md: apps/shiny.md
19 | quarto.md: apps/quarto.md
20 | streamlit.md: apps/streamlit.md
21 | widget.md: apps/widget.md
22 | sample_dataframes.md: pandas_dataframes.md
23 | extra_extensions:
24 | - sphinxext.rediraffe
25 | repository:
26 | url: https://github.com/mwouts/itables
27 | path_to_book: docs
28 | branch: main
29 | html:
30 | use_edit_page_button: true
31 | use_repository_button: true
32 | use_issues_button: true
33 | launch_buttons:
34 | notebook_interface: "jupyterlab"
35 | binderhub_url: "https://mybinder.org"
36 |
--------------------------------------------------------------------------------
/docs/_toc.yml:
--------------------------------------------------------------------------------
1 | format: jb-book
2 | root: quick_start
3 | parts:
4 | - caption: Using ITables
5 | chapters:
6 | - file: apps/notebook
7 | - file: apps/html
8 | - file: apps/widget
9 | - file: apps/marimo
10 | - file: apps/dash
11 | - file: apps/streamlit
12 | - file: apps/shiny
13 | - file: apps/quarto
14 | - caption: Options
15 | chapters:
16 | - file: options/options
17 | - file: options/classes
18 | - file: options/style
19 | - file: options/caption
20 | - file: options/layout
21 | - file: options/column_defs
22 | - file: options/search
23 | - file: options/length_menu
24 | - file: options/paging
25 | - file: options/scroll_y
26 | - file: options/scroll_x
27 | - file: options/footer
28 | - file: options/column_filters
29 | - file: options/order
30 | - file: options/state_save
31 | - file: options/select
32 | - file: options/buttons
33 | - file: options/colvis
34 | - file: options/search_panes
35 | - file: options/search_builder
36 | - file: options/fixed_columns
37 | - file: options/row_group
38 | - file: options/keys
39 | - file: options/allow_html
40 | - file: options/show_index
41 | - file: options/text_in_header_can_be_selected
42 | - caption: More about ITables
43 | chapters:
44 | - file: downsampling
45 | - file: formatting
46 | - file: dark_mode
47 | - file: css
48 | - file: custom_extensions
49 | - file: references
50 | - file: supported_editors
51 | - caption: Contributing to ITables
52 | chapters:
53 | - file: contributing
54 | - file: developing
55 | - file: troubleshooting
56 | - file: changelog
57 | - caption: Examples
58 | chapters:
59 | - file: pandas_dataframes
60 | - file: pandas_style
61 | - file: polars_dataframes
62 |
--------------------------------------------------------------------------------
/docs/apps/dash.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Dash
16 |
17 | If you wish to display a DataFrame which content is fixed (not reacting to the other controls in the application), you just need to import `ITable` from `itables.dash` and add it to your layout like here:
18 |
19 | ```{include} ../../apps/dash/1_display_only.py
20 | :code: python
21 | ```
22 |
23 | ## Selected rows
24 |
25 | Listening to the selected rows is simply done by adding `select=True` to the `ITable` call, and then implementing a callback on `Input("my_dataframe", "selected_rows")`.
26 |
27 | ```{include} ../../apps/dash/2_selected_rows.py
28 | :code: python
29 | ```
30 |
31 | ## Updating the DataFrame
32 |
33 | The `ITable` component has many properties. These properties (table content, selected rows etc) need to be updated in a consistent way. Therefore we recommend that you list the outputs with `ITableOutputs("my_dataframe")` in your callback, and update them with `updated_itable_outputs` which takes the same arguments as `show`, e.g. `df`, `caption`, `selected_rows`, etc, like in the below (extracted from this [example app](https://github.com/mwouts/itables/tree/main/apps/dash/3_update_table.py)):
34 |
35 | ```python
36 | from itables.dash import ITable, ITableOutputs, updated_itable_outputs
37 |
38 | # (...)
39 |
40 | @callback(
41 | ITableOutputs("my_dataframe"),
42 | [
43 | Input("checklist", "value"),
44 | Input("caption", "value"),
45 | State("my_dataframe", "selected_rows"),
46 | State("my_dataframe", "dt_args"),
47 | ],
48 | )
49 | def update_table(checklist, caption, selected_rows, dt_args):
50 | if checklist is None:
51 | checklist = []
52 |
53 | kwargs = {}
54 |
55 | # When df=None and when the dt_args don't change, the table is not updated
56 | if callback_context.triggered_id == "checklist":
57 | kwargs["df"] = get_countries(html="HTML" in checklist)
58 |
59 | kwargs["select"] = "Select" in checklist
60 | if "Buttons" in checklist:
61 | kwargs["buttons"] = ["copyHtml5", "csvHtml5", "excelHtml5"]
62 |
63 | return updated_itable_outputs(
64 | caption=caption, selected_rows=selected_rows, current_dt_args=dt_args, **kwargs
65 | )
66 | ```
67 |
--------------------------------------------------------------------------------
/docs/apps/html.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | ```{code-cell} ipython3
16 | :tags: [remove-cell]
17 |
18 | # pyright: reportUnknownVariableType=false
19 | ```
20 |
21 | # HTML export
22 |
23 | To get the HTML representation of a Pandas DataFrame `df` as an interactive [DataTable](https://datatables.net/), you can use `to_html_datatable` as below:
24 |
25 | ```{code-cell} ipython3
26 | from IPython.display import HTML, display
27 |
28 | import itables
29 |
30 | df = itables.sample_dfs.get_countries(html=False)
31 | html = itables.to_html_datatable(df.head(3), display_logo_when_loading=False)
32 | ```
33 |
34 | You can then save the `html` variable to a text file (note: if you're writing an HTML application, you could consider using [Shiny](shiny.md) or [Streamlit](streamlit.md) instead), or print it:
35 |
36 | ```{code-cell} ipython3
37 | :tags: [scroll-output]
38 |
39 | print(html)
40 | ```
41 |
42 | or display it, like `show` does:
43 |
44 | ```{code-cell} ipython3
45 | display(HTML(html))
46 | ```
47 |
48 | ~~~{admonition} The `connected` argument
49 | :class: tip
50 | The `to_html_datatable` function has a `connected` argument which defaults to what you set in `init_notebook_mode` (it's `True` if you didn't call it).
51 |
52 | - With `connected=True` you get an autonomous HTML fragment that loads `dt_for_itables` from the Internet
53 | - With `connected=False`, the HTML snippet works only after you add the output of `generate_init_offline_itables_html()` to your HTML document
54 | ~~~
55 |
--------------------------------------------------------------------------------
/docs/apps/marimo.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Marimo
16 |
17 | ```{warning}
18 | The `init_notebook_mode` and the `show` function do not work in Marimo. This is because they both use `IPython.display` to display the HTML representation of the table, which is not a good fit for Marimo.
19 | ```
20 |
21 | In Marimo the recommended way to use ITable is through the `ITable` [widget](widget.md):
22 |
23 | ```{code-cell} ipython3
24 | import pandas as pd
25 |
26 | from itables.widget import ITable
27 |
28 | df = pd.DataFrame({"x": [2, 1, 3]})
29 |
30 | ITable(df)
31 | ```
32 |
33 | A Sample Marimo application is available at [`apps/marimo/widget.py`](https://github.com/mwouts/itables/tree/main/apps/marimo/widget.py).
34 |
35 |
36 | ## Using HTML
37 |
38 | You can also use `to_html_datatable` in combination with `mo.iframe` like in this example - but, as there is no `init_notebook_mode` cell, you will have to use the connected mode:
39 |
40 | ```python
41 | import marimo as mo
42 |
43 | from itables import to_html_datatable
44 |
45 | html = to_html_datatable(df, connected=True)
46 | mo.iframe(html)
47 | ```
48 |
49 | A Sample Marimo application that uses `to_html_datatable` is available at [`apps/marimo/html.py`](https://github.com/mwouts/itables/tree/main/apps/marimo/html.py).
50 |
--------------------------------------------------------------------------------
/docs/apps/notebook.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Notebook Mode
16 |
17 | Activate ITables in a Jupyter environment for all your tables with `init_notebook_mode`:
18 |
19 | ```{code-cell} ipython3
20 | import itables
21 |
22 | itables.init_notebook_mode()
23 | ```
24 |
25 | You can go back to the standard HTML representation of Pandas DataFrames with `init_notebook_mode(all_interactive=False)`.
26 |
27 | Note that the `init_connected_mode` function also activates ITable's offline mode, unless you call it with a `connected=False` argument.
28 |
29 |
30 | ## Offline mode
31 |
32 | By default `init_connected_mode` configures ITables to work offline (except in Colab). No internet connection is required as the JavaScript code is embedded into the notebook itself when you execute `init_notebook_mode`.
33 |
34 | In some contexts (Jupyter Book, Google Colab, etc...) you might
35 | prefer to load the libraries dynamically from the internet.
36 | To do so, add the argument `connected=True` when you
37 | execute `init_notebook_mode`. This will also make your notebook lighter by
38 | about [700kB](https://github.com/mwouts/itables/blob/main/tests/test_connected_notebook_is_small.py). Note that, in Google Colab, `connected=True` is the only working option.
39 |
40 | ## Show
41 |
42 | If you prefer to render only certain tables using `itables`, or want to set additional options, use `show`:
43 |
44 | ```{code-cell} ipython3
45 | df = itables.sample_dfs.get_countries(html=False)
46 |
47 | itables.show(
48 | df,
49 | caption="A DataFrame rendered with ITables",
50 | lengthMenu=[2, 5, 10, 25, 50, 100, 250],
51 | )
52 | ```
53 |
54 | ## HTML
55 |
56 | The `show` function simply displays the HTML snippet for the table, which is obtained with `to_html_datatable`. See more in the section on [HTML export](html.md).
57 |
--------------------------------------------------------------------------------
/docs/apps/quarto.md:
--------------------------------------------------------------------------------
1 | # Quarto
2 |
3 | ITables works well with the `revealjs` and `html` outputs formats of [Quarto](https://quarto.org).
4 |
5 | ## HTML Presentations
6 |
7 | See this revealjs presentation,
8 | or download the source file and render it with `quarto render`:
9 |
10 |
11 |
12 | ## HTML reports
13 |
14 | See this HTML report,
15 | or download the source file and render it with `quarto render`:
16 |
17 |
18 |
--------------------------------------------------------------------------------
/docs/apps/streamlit.md:
--------------------------------------------------------------------------------
1 | # Streamlit
2 |
3 | To display Python DataFrames as interactive DataTables in Streamlit applications, use
4 |
5 | ```
6 | from itables.streamlit import interactive_table
7 | ```
8 |
9 | We have a sample application available at https://itables.streamlit.app (source code [here](https://github.com/mwouts/demo_itables_in_streamlit/blob/main/itables_app.py))
10 |
11 | ```{div} full-width
12 |
14 | ```
15 |
16 | ## Selected rows
17 |
18 | Use the `selected_rows: list[int]` argument from `interactive_table` to
19 | select rows when the table is first displayed. Add `select=True` to let the user modify the selection. Then, the `interactive_table` component returns a dict, with a key `"selected_rows"` that points to the updated selection.
20 |
21 | ## Using HTML
22 |
23 | Before ITables v2.4.0, the streamlit component was missing a few options compared to the HTML implementation. A possible fallback was to use `to_html_datatable` in combination with Streamlit's `html` function - but you should not need that anymore with the latest version of ITables.
24 |
25 | Please note that:
26 | - you will have to specify the table height manually,
27 | - an internet connection is required when using `to_html_datatable`,
28 | - the app/table might take longer to display.
29 |
30 | A sample application is available at https://to-html-datatable.streamlit.app (source code [here](https://github.com/mwouts/to_html_datatable_in_streamlit/blob/main/itables_app.py))
31 |
32 | ```{div} full-width
33 |
35 | ```
36 |
--------------------------------------------------------------------------------
/docs/contributing.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | ```{code-cell} ipython3
16 | :tags: [remove-cell]
17 |
18 | # pyright: reportUnknownVariableType=false
19 | ```
20 |
21 | # Contributing
22 |
23 | Thanks for considering making a contribution to ITables. There are
24 | many ways you can help!
25 |
26 | ## Report an issue
27 |
28 | If you see an issue, a possible improvement, or if you can't find
29 | the answer to your question, then you are very welcome to create
30 | an issue on this project. Please provide enough detail so that
31 | we can reproduce the issue.
32 |
33 | ## Improve the documentation
34 |
35 | If you would like to add a new example,
36 | or improve the documentation, feel free to make a pull request!
37 |
38 | You can render the documentation locally - see the section on
39 | [Jupyter Book](developing.md#jupyter-book) in the developer guide.
40 |
41 | ## Give credit to ITables
42 |
43 | It's always great to see new stars coming to ITables!
44 |
45 |
46 | If you wanted to share a link to ITables and DataTables (no obligation whatsoever), you could use something like this:
47 |
48 | ```{code-cell} ipython3
49 | from IPython.display import HTML, display
50 |
51 | display(
52 | HTML(
53 | """
54 | Tables displayed with ITables,
55 | a Python wrapper for DataTables
56 | """
57 | )
58 | )
59 | ```
60 |
61 | ## Support DataTables
62 |
63 | Allan Jardine, the main developer of DataTables, has done a fantastic work on [DataTables](https://datatables.net/).
64 |
65 | If you enjoy his library, you could become a
66 | [supporter](https://datatables.net/supporters/) -
67 | contributions range from 9 to 99$/year before VAT.
68 | Or you could take a subscription for DataTable's [Editor](https://editor.datatables.net)
69 | that ITables might support in the future (please subscribe to [#243](https://github.com/mwouts/itables/issues/243) for updates).
70 |
71 | ## Develop a new feature
72 |
73 | It is generally a good idea to get in touch with us first - e.g.
74 | open an issue and let us know what you'd like to do.
75 |
76 | But you can also simply clone the project and test your ideas.
77 | A guide on how to set up a development environment, and how to
78 | run some tests, is available [here](developing.md).
79 |
--------------------------------------------------------------------------------
/docs/css.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | ```{code-cell} ipython3
16 | :tags: [remove-cell]
17 |
18 | # ruff: noqa: E402
19 | # pyright: reportUnknownVariableType=false
20 | ```
21 |
22 | # CSS
23 |
24 | You can use CSS to alter how tables are rendered.
25 |
26 | For instance, we change the
27 | [font size](https://developer.mozilla.org/en-US/docs/Web/CSS/font-size)
28 | for all the tables in the document with this code:
29 |
30 | ```{code-cell} ipython3
31 | from IPython.display import HTML, display
32 |
33 | css = """
34 | .dt-container {
35 | font-size: small;
36 | }
37 | """
38 | display(HTML(f"" ""))
39 | ```
40 |
41 | This is helpful for instance in the context of
42 | [Quarto presentations](apps/quarto.md).
43 |
44 | With this over CSS, we change _every datatable_ table header
45 | in the notebook to bold/italic.
46 |
47 | ```{code-cell} ipython3
48 | css = """
49 | .dataTable th {
50 | font-weight: bolder;
51 | font-style: italic;
52 | }
53 | """
54 | display(HTML(f"" ""))
55 | ```
56 |
57 | You might also want to alter the style of specific tables only.
58 | To do this, add a new class to the target tables, as
59 | in the example below:
60 |
61 | ```{code-cell} ipython3
62 | class_specific_css = ".table_with_monospace_font { font-family: courier, monospace }"
63 | display(HTML(f"" ""))
64 | ```
65 |
66 | ```{code-cell} ipython3
67 | :tags: [full-width]
68 |
69 | import itables
70 |
71 | itables.init_notebook_mode()
72 |
73 | df = itables.sample_dfs.get_countries(html=False)
74 |
75 | itables.show(df, classes="display nowrap table_with_monospace_font")
76 | ```
77 |
--------------------------------------------------------------------------------
/docs/dark_mode.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Dark Themes
16 |
17 | When a notebook or application is rendered using a dark theme, DataTable requires that a `dark`
18 | class be added to the HTML document. This can be done with the following Javascript snippet:
19 | ```javascript
20 | document.documentElement.classList.add('dark');
21 | ```
22 |
23 | When ITables is used in a notebook, this is handled by
24 | `init_notebook_mode` which displays the [`init_datatables.html`](https://github.com/mwouts/itables/blob/main/src/itables/html/init_datatables.html) snippet.
25 |
26 | Please open a PR if you see how to further improve the
27 | support of light vs dark themes, and e.g. set the `dark`
28 | class dynamically when the theme is changed.
29 |
--------------------------------------------------------------------------------
/docs/developing.md:
--------------------------------------------------------------------------------
1 | # Developing ITables
2 |
3 | In this page you will find instructions on how to
4 | create a development environment and how
5 | to test your changes.
6 |
7 | ## How to create a development environment
8 |
9 | Create a conda environment with
10 | ```shell
11 | mamba env create --file environment.yml
12 | ```
13 | (use `conda` if you don't have `mamba`) or update it with
14 | ```shell
15 | mamba env update --file environment.yml
16 | ```
17 |
18 | Then, activate that environment with
19 | ```shell
20 | conda activate itables
21 | ```
22 |
23 | Install the pre-commit hooks with
24 | ```shell
25 | pre-commit install
26 | ```
27 |
28 | and finally, install the development version of `itables` with
29 | ```shell
30 | pip install -e .
31 | ```
32 |
33 | ## How to run the Python test suite
34 |
35 | The Python code can be tested with just
36 |
37 | ```shell
38 | pytest
39 | ```
40 |
41 | Running the `pytest` test suite is not enough to guaranty that a change won't
42 | break `itables`. You also need to test that the tables are well rendered in the
43 | different contexts like Jupyter Book, Jupyter Lab, VS Code.
44 |
45 | ## Jupyter Book
46 |
47 | The `itables` documentation uses [Jupyter Book](https://jupyterbook.org/).
48 |
49 | To build the documentation locally,
50 | you need to create a Jupyter kernel named `itables` with
51 | ```shell
52 | python -m ipykernel install --name itables --user
53 | ```
54 | Then you can build the documentation with
55 | ```
56 | jupyter book build docs
57 | ```
58 |
59 | This will give you a link to a local version of the documentation.
60 |
61 | If you make any significant change then you should go through
62 | the updated documentation and make sure all the examples
63 | still work properly.
64 |
65 | ## Jupyter Lab
66 |
67 | In the `itables` conda environment, you can start Jupyter with
68 | ```
69 | jupyter lab
70 | ```
71 |
72 | You should test at least this code snippet:
73 | ```python
74 | import itables
75 |
76 | # try both connected=False (the default) and connected=True
77 | itables.init_notebook_mode(connected=False)
78 |
79 | itables.sample_dfs.get_countries()
80 | ```
81 |
82 | You can do this using for instance the notebook at `tests/test_notebook.ipynb`.
83 |
84 | Note that you can also open the documentation in Jupyter:
85 | go to the `docs` folder and open any `.md` file _as a notebook_ (using a right click).
86 |
87 | ## Other notebook editors
88 |
89 | If you change anything related to the Javascript/HTML code,
90 | you should test the [supported editors](supported_editors.md)
91 | in both the connected and offline mode.
92 |
93 | For the online editors like [Google Colab](https://colab.research.google.com/),
94 | you might have to install your development version there with e.g.
95 | ```
96 | !pip uninstall itables -y
97 | !pip install git+https://github.com/mwouts/itables.git@branch
98 | ```
99 |
--------------------------------------------------------------------------------
/docs/df_example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwouts/itables/8fa250a684a6433314d2a984ce375bc9d8b07429/docs/df_example.png
--------------------------------------------------------------------------------
/docs/downsampling.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | ```{code-cell} ipython3
16 | :tags: [remove-cell]
17 |
18 | # pyright: reportUnusedExpression=false
19 | ```
20 |
21 | # Downsampling
22 |
23 | When an interactive table is displayed by `itables`, the table data is embedded in the notebook output. As we don't want your notebook to become super heavy just because you displayed a large table, we have a downsampling mechanism in place.
24 |
25 | When the data in a table is larger than `maxBytes`, which is equal to 64KB by default, `itables` will display only a subset of the table - one that fits into `maxBytes`, and display a warning that points to the `itables` documentation.
26 |
27 | If you wish, you can increase the value of `maxBytes` or even deactivate the limit (with `maxBytes=0`). Similarly, you can set a limit on the number of rows (`maxRows`, defaults to 0) or columns (`maxColumns`, defaults to `200`).
28 |
29 | ```{code-cell} ipython3
30 | import itables
31 |
32 | itables.init_notebook_mode()
33 | ```
34 |
35 | ```{code-cell} ipython3
36 | itables.options.lengthMenu = [2, 5, 10, 20, 50, 100, 200, 500]
37 | itables.options.maxBytes = "8KB"
38 |
39 | df = itables.sample_dfs.get_indicators()
40 | itables.downsample.as_nbytes(itables.options.maxBytes), itables.downsample.nbytes(df)
41 | ```
42 |
43 | ```{code-cell} ipython3
44 | :tags: [full-width]
45 |
46 | df
47 | ```
48 |
49 | To show the table in full, we can modify the value of `maxBytes` either locally:
50 |
51 | ```{code-cell} ipython3
52 | :tags: [full-width]
53 |
54 | itables.show(df, maxBytes=0)
55 | ```
56 |
57 | or globally:
58 |
59 | ```{code-cell} ipython3
60 | :tags: [full-width]
61 |
62 | itables.options.maxBytes = "1MB"
63 | df
64 | ```
65 |
--------------------------------------------------------------------------------
/docs/images/code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwouts/itables/8fa250a684a6433314d2a984ce375bc9d8b07429/docs/images/code.png
--------------------------------------------------------------------------------
/docs/images/colab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwouts/itables/8fa250a684a6433314d2a984ce375bc9d8b07429/docs/images/colab.png
--------------------------------------------------------------------------------
/docs/images/html.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwouts/itables/8fa250a684a6433314d2a984ce375bc9d8b07429/docs/images/html.png
--------------------------------------------------------------------------------
/docs/images/lab.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwouts/itables/8fa250a684a6433314d2a984ce375bc9d8b07429/docs/images/lab.png
--------------------------------------------------------------------------------
/docs/images/notebook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwouts/itables/8fa250a684a6433314d2a984ce375bc9d8b07429/docs/images/notebook.png
--------------------------------------------------------------------------------
/docs/images/pycharm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwouts/itables/8fa250a684a6433314d2a984ce375bc9d8b07429/docs/images/pycharm.png
--------------------------------------------------------------------------------
/docs/options/buttons.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Buttons
16 |
17 | The DataTables [buttons](https://datatables.net/extensions/buttons/) let you copy the table data, or export it as CSV or Excel files.
18 |
19 | To display the buttons, you need to pass a `buttons` argument to the `show` function:
20 |
21 | ```{code-cell} ipython3
22 | import itables
23 |
24 | itables.init_notebook_mode()
25 |
26 | df = itables.sample_dfs.get_countries(html=False)
27 |
28 | itables.show(df, buttons=["pageLength", "copyHtml5", "csvHtml5", "excelHtml5"])
29 | ```
30 |
31 | You can also specify a [`layout`](layout) modifier that will decide
32 | the location of the buttons (the default is `layout={"topStart": "buttons"}`). And if you want to keep the pagination control too, you can add `"pageLength"` to the list of buttons - as done above.
33 |
34 | As always, it is possible to set default values for these parameters by setting these on `itables.options`. For instance, set
35 | ```python
36 | itables.options.buttons = ["copyHtml5", "csvHtml5", "excelHtml5"]
37 | ```
38 | to get the buttons for all your tables.
39 |
40 |
41 | By default, the exported file name is the name of the HTML page. To change it, set a
42 | [`title` option](https://datatables.net/extensions/buttons/examples/html5/filename.html) on the buttons, like
43 | here:
44 |
45 | ```{code-cell} ipython3
46 | :tags: [full-width]
47 |
48 | itables.show(
49 | df,
50 | buttons=[
51 | "pageLength",
52 | {"extend": "csvHtml5", "title": "download_filename"},
53 | {"extend": "excelHtml5", "title": "download_filename"},
54 | ],
55 | )
56 | ```
57 |
58 | ```{tip}
59 | Only the filtered or selected rows are exported to CSV/Excel. To filter the rows you can use the simple search box, the [SearchPanes](search_panes) and [SearchBuilder](search_builder) options, or the [select](select.md) extension.
60 | ```
61 |
62 | ```{warning}
63 | At the moment, the CSV and Excel buttons don't work well with large tables in some browsers.
64 | Please subscribe to [#251](https://github.com/mwouts/itables/issues/251) if you wish to receive updates on this.
65 | ```
66 |
67 | ```{warning}
68 | The PDF button is not included in ITables' DataTable bundle. This is because the required PDF libraries have a large footprint on the bundle size. Still, you can add it to your custom bundle, see our page on how to bundle [custom extensions](../custom_extensions.md).
69 | ```
70 |
--------------------------------------------------------------------------------
/docs/options/caption.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Caption
16 |
17 | ```{code-cell} ipython3
18 | :tags: [full-width]
19 |
20 | import itables
21 |
22 | itables.init_notebook_mode()
23 |
24 | df = itables.sample_dfs.get_countries(html=False)
25 | itables.show(df, "Countries from the World Bank Database")
26 | ```
27 |
28 | The caption appears at the bottom of the table by default: this is governed by `caption-side:bottom`
29 | in the [`style` option](style) (but for some reason this is not effective in Jupyter Book 🤔).
30 |
--------------------------------------------------------------------------------
/docs/options/classes.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Classes
16 |
17 | Select how your table looks like with the `classes` argument (defaults to `"display nowrap"`) of the `show` function, or by changing `itables.options.classes`.
18 |
19 | Add `"compact"` if you want a denser table:
20 |
21 | ```{code-cell} ipython3
22 | :tags: [full-width]
23 |
24 | import itables
25 |
26 | itables.init_notebook_mode()
27 |
28 | df = itables.sample_dfs.get_countries(html=False)
29 |
30 | itables.show(df, classes="display nowrap compact")
31 | ```
32 |
33 | Remove `"nowrap"` if you want the cell content to be wrapped:
34 |
35 | ```{code-cell} ipython3
36 | :tags: [full-width]
37 |
38 | itables.show(df, classes="display")
39 | ```
40 |
41 | [More options](https://datatables.net/manual/styling/classes#Table-classes) like `"cell-border"` are available:
42 |
43 | ```{code-cell} ipython3
44 | :tags: [full-width]
45 |
46 | itables.show(df, classes="display nowrap cell-border")
47 | ```
48 |
--------------------------------------------------------------------------------
/docs/options/column_defs.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | ```{code-cell} ipython3
16 | :tags: [remove-cell]
17 |
18 | # pyright: reportUnusedExpression=false
19 | ```
20 |
21 | # Column Definitions
22 |
23 | The [`columnDefs.width`](https://datatables.net/reference/option/columns.width) argument let you adjust the column widths.
24 |
25 | Note that the default value of `style`, or of `autoWidth` (defaults to `True`), might override custom column widths,
26 | so you might have to change their values as in the examples below.
27 |
28 | You can set a fixed width for all the columns with `"targets": "_all"`:
29 |
30 | ```{code-cell} ipython3
31 | :tags: [full-width]
32 |
33 | import itables
34 |
35 | itables.init_notebook_mode()
36 |
37 | df = itables.sample_dfs.get_countries(html=False)
38 |
39 | itables.show(
40 | df,
41 | columnDefs=[{"width": "120px", "targets": "_all"}],
42 | style="width:1200px",
43 | autoWidth=False,
44 | )
45 | ```
46 |
47 | You can also adjust the width of selected columns only:
48 |
49 | ```{code-cell} ipython3
50 | :tags: [full-width]
51 |
52 | itables.show(
53 | df,
54 | columnDefs=[{"width": "30%", "targets": [2, 3]}],
55 | style="width:100%;margin:auto",
56 | )
57 | ```
58 |
59 | If you wish you can also set a value for `columnDefs` permanently in `itables.options` as demonstrated in the cell alignment example below.
60 |
61 | You can use the DataTables [cell classes](https://datatables.net/manual/styling/classes#Cell-classes) like `dt-left`, `dt-center`, `dt-right` etc. to set the cell alignment. Specify it for one table by using the `columnDefs` argument of `show`
62 |
63 | ```{code-cell} ipython3
64 | :tags: [full-width]
65 |
66 | itables.show(df, columnDefs=[{"className": "dt-center", "targets": "_all"}])
67 | ```
68 |
69 | or globally by setting `itables.options.columnDefs`:
70 |
71 | ```{code-cell} ipython3
72 | :tags: [full-width]
73 |
74 | itables.options.columnDefs = [{"className": "dt-center", "targets": "_all"}]
75 |
76 | df
77 | ```
78 |
--------------------------------------------------------------------------------
/docs/options/column_filters.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Column Filters
16 |
17 | Use `column_filters = "header"` or `"footer"` if you wish to display individual column filters
18 | (remove the global search box with a [`layout`](layout) modifier if desired).
19 |
20 | ```{code-cell} ipython3
21 | import pandas as pd
22 |
23 | import itables
24 |
25 | itables.init_notebook_mode()
26 |
27 | alpha_numeric_df = pd.DataFrame(
28 | [["one", 1.5], ["two", 2.3]], columns=["string", "numeric"]
29 | )
30 |
31 | itables.show(alpha_numeric_df, column_filters="header", layout={"topEnd": None})
32 | ```
33 |
34 | As always you can set activate column filters by default with e.g.
35 |
36 | ```{code-cell} ipython3
37 | itables.options.column_filters = "footer"
38 | ```
39 |
40 | Column filters also work on dataframes with multiindex columns:
41 |
42 | ```{code-cell} ipython3
43 | itables.sample_dfs.get_dict_of_test_dfs()["multiindex"]
44 | ```
45 |
46 | ```{code-cell} ipython3
47 | :tags: [remove-cell]
48 |
49 | # Revert back to the default to avoid interactions with the tests
50 | itables.options.column_filters = False
51 | ```
52 |
--------------------------------------------------------------------------------
/docs/options/colvis.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Column Visibility
16 |
17 | The [column visibility](https://datatables.net/extensions/buttons/examples/column_visibility/index.html) buttons of DataTables let you select which columns are visible.
18 |
19 | ```{code-cell} ipython3
20 | :tags: [full-width]
21 |
22 | import itables
23 |
24 | itables.init_notebook_mode()
25 |
26 | df = itables.sample_dfs.get_countries(html=False)
27 |
28 | itables.show(
29 | # column visibility works best with a flat header
30 | df.reset_index(),
31 | buttons=["columnsToggle"],
32 | )
33 | ```
34 |
35 | ```{tip}
36 | The column visibility button is available under many forms.
37 |
38 | Check-out `buttons=["colvis"]` for a [single](https://datatables.net/extensions/buttons/examples/column_visibility/simple.html) button.
39 |
40 | Extend the `colvis` button with the [collection layout](https://datatables.net/extensions/buttons/examples/column_visibility/layout.html).
41 |
42 | As always, when porting examples from DataTables to ITables, you will
43 | have to convert the JavaScript notation (left) to Python (right) as in the below:
44 | ::::{grid}
45 |
46 | :::{grid-item}
47 | :outline:
48 | :columns: 6
49 | ~~~javascript
50 | buttons: [
51 | {
52 | extend: 'colvis',
53 | collectionLayout: 'fixed columns',
54 | popoverTitle: 'Column visibility control'
55 | }
56 | ]
57 | ~~~
58 | :::
59 | :::{grid-item}
60 | :outline:
61 | :columns: 6
62 | ~~~python
63 | buttons = [
64 | {
65 | "extend": "colvis",
66 | "collectionLayout": "fixed columns",
67 | "popoverTitle": "Column visibility control"
68 | }
69 | ]
70 | ~~~
71 | :::
72 |
73 | ::::
74 |
75 | ```
76 |
--------------------------------------------------------------------------------
/docs/options/fixed_columns.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # FixedColumns
16 |
17 | [FixedColumn](https://datatables.net/extensions/fixedcolumns/) is an extension
18 | that let you fix some columns as you scroll horizontally.
19 |
20 | ```{code-cell} ipython3
21 | import string
22 |
23 | import numpy as np
24 | import pandas as pd
25 |
26 | import itables
27 |
28 | itables.init_notebook_mode()
29 |
30 | wide_df = pd.DataFrame(
31 | {
32 | letter: np.random.normal(size=100)
33 | for letter in string.ascii_lowercase + string.ascii_uppercase
34 | }
35 | )
36 |
37 | itables.show(
38 | wide_df,
39 | fixedColumns={"start": 1, "end": 2},
40 | scrollX=True,
41 | )
42 | ```
43 |
--------------------------------------------------------------------------------
/docs/options/footer.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Footer
16 |
17 | Use `footer = True` if you wish to display a table footer.
18 |
19 | ```{code-cell} ipython3
20 | :tags: [full-width]
21 |
22 | import itables
23 |
24 | itables.init_notebook_mode()
25 |
26 | df = itables.sample_dfs.get_countries(html=False)
27 | itables.show(df, footer=True)
28 | ```
29 |
--------------------------------------------------------------------------------
/docs/options/keys.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Keys
16 |
17 | With the [KeyTable](https://datatables.net/extensions/keytable/) extension you can navigate in a table using the arrow keys:
18 |
19 | ```{code-cell} ipython3
20 | :tags: [full-width]
21 |
22 | import itables
23 |
24 | itables.init_notebook_mode()
25 |
26 | itables.show(
27 | itables.sample_dfs.get_countries(html=False),
28 | keys=True,
29 | )
30 | ```
31 |
32 | ```{tip}
33 | You can activate this option for all your tables with
34 |
35 | ~~~python
36 | itables.options.keys = True
37 | ~~~
38 | ```
39 |
40 | ```{warning}
41 | The KeyTable extension works in Jupyter Book (try it here in the documentation) but not in JupyterLab.
42 | ```
43 |
--------------------------------------------------------------------------------
/docs/options/layout.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Layout
16 |
17 | By default, datatables that don't fit in one page come with a search box, a pagination control, a table summary, etc.
18 | You can select which elements are actually displayed using
19 | DataTables' [`layout` option](https://datatables.net/reference/option/layout) with e.g.:
20 |
21 | ```{code-cell} ipython3
22 | import itables
23 |
24 | itables.init_notebook_mode()
25 | df = itables.sample_dfs.get_countries(html=False)
26 | ```
27 |
28 | ```{code-cell} ipython3
29 | itables.show(df, layout={"topStart": "search", "topEnd": None})
30 | ```
31 |
32 | The available positions are `topStart, topEnd, bottomStart, bottomEnd`. You can also use `top2Start`, etc... (see more
33 | in the [DataTables documentation](https://datatables.net/reference/option/layout)).
34 |
35 | Like for the other arguments of `show`, you can change the default value of the dom option with e.g.:
36 |
37 | ```
38 | itables.options.layout = {
39 | "topStart": "pageLength",
40 | "topEnd": "search",
41 | "bottomStart": "info",
42 | "bottomEnd": "paging"
43 | } # (default value)
44 | ```
45 |
46 | ```{tip}
47 | The `layout` option was introduced with `itables==2.0` and `DataTables==2.0`
48 | and replaced the former [`dom` option](https://datatables.net/reference/option/dom).
49 | ```
50 |
--------------------------------------------------------------------------------
/docs/options/length_menu.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # LengthMenu
16 |
17 | Select [how many entries](https://datatables.net/examples/advanced_init/length_menu.html) should appear at once in the table with either the `lengthMenu` argument of the `show` function, or with the global option `itables.options.lengthMenu`:
18 |
19 | ```{code-cell} ipython3
20 | import itables
21 |
22 | itables.init_notebook_mode()
23 | df = itables.sample_dfs.get_countries(html=False)
24 | ```
25 |
26 | ```{code-cell} ipython3
27 | :tags: [full-width]
28 |
29 | itables.show(df, lengthMenu=[2, 5, 10, 20, 50])
30 | ```
31 |
--------------------------------------------------------------------------------
/docs/options/options.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Options
16 |
17 | ## DataTable Options
18 |
19 | ITables is a wrapper for the Javascript DataTables library, which means that you can use more or less directly the DataTables [options](https://datatables.net/options) from within Python.
20 |
21 | Since ITables just maps these options to DataTables, you are invited to have a look at DataTable's great [documentation](https://datatables.net/), and to its huge collection of [examples](https://datatables.net/examples/index). The DataTable [forum](https://datatables.net/forums/) can be quite useful as well.
22 |
23 | A non-exhaustive list of the DataTable options, together with their expected types, is available at [`itables.typing.DataTableOptions`](https://github.com/mwouts/itables/blob/main/src/itables/typing.py).
24 |
25 | Option names and types are checked by default at run time when `typeguard>=4.4.1` is installed - you can deactivate this by setting `warn_on_undocumented_option=False`.
26 |
27 | If you see an option that you find useful and is not documented, or a type hint that is incorrect, please make a PR (and add an example to the documentation, too).
28 |
29 | ```{code-cell} ipython3
30 | :tags: [scroll-output]
31 |
32 | import inspect
33 |
34 | import itables
35 |
36 | print(inspect.getsource(itables.typing.DataTableOptions))
37 | ```
38 |
39 | ## ITable Options
40 |
41 | ITables itself adds a few options like `connected`, `maxBytes`, `allow_html` etc.
42 |
43 | The ITable options are documented at [`itables.typing.ITableOptions`](https://github.com/mwouts/itables/blob/main/src/itables/typing.py):
44 |
45 | ```{code-cell} ipython3
46 | :tags: [scroll-output]
47 |
48 | print(inspect.getsource(itables.typing.ITableOptions))
49 | ```
50 |
51 | ## Default values
52 |
53 | Some of the options have a default value set in [`itables.options`](https://github.com/mwouts/itables/blob/main/src/itables/options.py). You can change these defaults easily, and even set defauts for the options that don't have one yet with e.g.
54 |
55 | ```python
56 | import itables
57 |
58 | itables.options.maxBytes = "128KB"
59 | ```
60 |
61 | ```{code-cell} ipython3
62 | :tags: [scroll-output]
63 |
64 | print(inspect.getsource(itables.options))
65 | ```
66 |
--------------------------------------------------------------------------------
/docs/options/order.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Order
16 |
17 | Since ITables v1.3.0, the interactive datatable shows the rows in the same order as the original dataframe.
18 |
19 | You can pre-select a explicit order with the [`order`](https://datatables.net/reference/option/order) option:
20 |
21 | ```{code-cell} ipython3
22 | import pandas as pd
23 |
24 | import itables
25 |
26 | itables.init_notebook_mode()
27 |
28 | sorted_df = pd.DataFrame({"a": [2, 1]}, index=pd.Index([1, 2], name="i"))
29 | itables.show(sorted_df, order=[[1, "asc"]])
30 | ```
31 |
--------------------------------------------------------------------------------
/docs/options/paging.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Paging
16 |
17 | Use [`paging=False`](https://datatables.net/reference/option/paging) to show the table in full:
18 |
19 | ```{code-cell} ipython3
20 | import itables
21 |
22 | itables.init_notebook_mode()
23 | df = itables.sample_dfs.get_countries(html=False)
24 | ```
25 |
26 | ```{code-cell} ipython3
27 | :tags: [full-width]
28 |
29 | itables.show(df.head(8), paging=False)
30 | ```
31 |
--------------------------------------------------------------------------------
/docs/options/row_group.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | ```{code-cell} ipython3
16 | :tags: [remove-cell]
17 |
18 | # pyright: reportUnknownMemberType=false
19 | ```
20 |
21 | # RowGroup
22 |
23 | Use the [RowGroup](https://datatables.net/extensions/rowgroup/) extension to group
24 | the data according to the content of one colum. Optionally, you can hide the content of that column to avoid duplicating the information.
25 |
26 | ```{code-cell} ipython3
27 | :tags: [full-width]
28 |
29 | import itables
30 |
31 | itables.init_notebook_mode()
32 |
33 | df = itables.sample_dfs.get_countries(html=False)
34 |
35 | itables.show(
36 | df.sort_values("region"),
37 | rowGroup={"dataSrc": 1},
38 | columnDefs=[{"targets": 1, "visible": False}],
39 | )
40 | ```
41 |
--------------------------------------------------------------------------------
/docs/options/scroll_x.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Horizontal Scroll
16 |
17 | DataTables has a [`scrollX`](https://datatables.net/reference/option/scrollX) option that might be helpful for wide tables.
18 |
19 | ```{tip}
20 | Since ITables v2.1.2, the `.dt-layout-table` div has a default overflow equal to `auto`, so in most cases you don't need to use `scrollX`.
21 | ```
22 |
23 | ```{code-cell} ipython3
24 | :tags: [full-width]
25 |
26 | import itables
27 |
28 | itables.init_notebook_mode()
29 |
30 | df = itables.sample_dfs.get_dict_of_test_dfs()["wide"]
31 | itables.show(df, scrollX=True)
32 | ```
33 |
34 | ```{warning}
35 | Using `scrollX` on a table that is not wide enough can lead to an issue where the table headers are not aligned with the table content.
36 | ```
37 |
--------------------------------------------------------------------------------
/docs/options/scroll_y.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Vertical Scroll
16 |
17 | The [`scrollY`](https://datatables.net/examples/basic_init/scroll_y.html) parameter is an interesting alternative to the pagination:
18 |
19 | ```{code-cell} ipython3
20 | :tags: [full-width]
21 |
22 | import itables
23 |
24 | itables.init_notebook_mode()
25 |
26 | df = itables.sample_dfs.get_countries(html=False)
27 | itables.show(df, scrollY="350px", scrollCollapse=True, paging=False)
28 | ```
29 |
--------------------------------------------------------------------------------
/docs/options/search.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Search
16 |
17 | The [search option](https://datatables.net/reference/option/search) let you control the initial value for the search field, and whether the query should be treated as a regular expression or not:
18 |
19 | ```{code-cell} ipython3
20 | import itables
21 |
22 | itables.init_notebook_mode()
23 | ```
24 |
25 | ```{code-cell} ipython3
26 | :tags: [full-width]
27 |
28 | df = itables.sample_dfs.get_countries(html=False)
29 | itables.show(df, search={"regex": True, "caseInsensitive": True, "search": "s.ain"})
30 | ```
31 |
--------------------------------------------------------------------------------
/docs/options/search_builder.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Search Builder
16 |
17 | [SearchBuilder](https://datatables.net/extensions/searchbuilder/) let you build complex search queries. You just need to add it to the layout
18 | by passing e.g. `layout={"top1": "searchBuilder"}`.
19 |
20 | It is possible to set a predefined search, as we do in the below:
21 |
22 | ```{code-cell} ipython3
23 | :tags: [full-width]
24 |
25 | import itables
26 |
27 | itables.init_notebook_mode()
28 |
29 | df = itables.sample_dfs.get_countries(html=False, climate_zone=True)
30 |
31 | itables.show(
32 | df,
33 | layout={"top1": "searchBuilder"},
34 | searchBuilder={
35 | "preDefined": {
36 | "criteria": [
37 | {"data": "climate_zone", "condition": "=", "value": ["Sub-tropical"]}
38 | ]
39 | }
40 | },
41 | )
42 | ```
43 |
--------------------------------------------------------------------------------
/docs/options/search_panes.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Search Panes
16 |
17 | [SearchPanes](https://datatables.net/extensions/searchpanes/) is an extension that lets you select rows based on unique values. In the example below we have activated the cascade filtering through the [`searchPanes.cascadePanes`](https://datatables.net/extensions/searchpanes/examples/initialisation/cascadePanes.html) argument.
18 |
19 | Note that, in Jupyter, the [`searchPanes.layout`](https://datatables.net/extensions/searchpanes/layout) argument is required (otherwise the search panes are too wide).
20 |
21 | ```{code-cell} ipython3
22 | :tags: [full-width]
23 |
24 | import itables
25 |
26 | itables.init_notebook_mode()
27 |
28 | df = itables.sample_dfs.get_countries(html=False, climate_zone=True)
29 |
30 | itables.show(
31 | df.reset_index(),
32 | layout={"top1": "searchPanes"},
33 | searchPanes={"layout": "columns-3", "cascadePanes": True, "columns": [1, 6, 7]},
34 | )
35 | ```
36 |
37 | ```{warning}
38 | When searching, please keep in mind that ITables will [downsample](../downsampling.md) your table if it is larger than `maxBytes`, so you might not see the full dataset - pay attention to the downsampling message at the bottom left of the table.
39 | ```
40 |
--------------------------------------------------------------------------------
/docs/options/select.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Select
16 |
17 | The [select](https://datatables.net/extensions/select) extension let you select rows (or cells).
18 |
19 | ~~~{admonition} The `selected_rows` attribute
20 | :class: tip
21 | It is possible to access the `selected_rows` back in Python but for this you will have to use, instead of `show`, either
22 | - the `ITable` [Widget](../apps/widget.md)
23 | - the `ITable` [Dash component](../apps/dash.md)
24 | - the `interactive_table` [Streamlit component](../apps/streamlit.md)
25 |
26 | ⚠ When a table has been downsampled, only the visible rows can be selected.
27 | ~~~
28 |
29 | ```{tip}
30 | The `select` option also interacts with the [`buttons`](buttons.md) extension. If you click on the CSV or Excel export while having selected some rows, only those rows will be exported - see the example below.
31 | ```
32 |
33 | ```{code-cell} ipython3
34 | import itables
35 |
36 | itables.init_notebook_mode()
37 |
38 | itables.show(
39 | itables.sample_dfs.get_countries(html=False),
40 | select=True,
41 | selected_rows=[2, 4, 5],
42 | buttons=["copyHtml5", "csvHtml5", "excelHtml5"],
43 | )
44 | ```
45 |
46 | ```{tip}
47 | The `select` option accept multiple values, as documented [here](https://datatables.net/extensions/select):
48 | - `select=True` or `select="os"` let you select using single click, shift-click and ctrl-click
49 | - `select="single"` let you select a single row
50 | - `select="multi"` for single click selection of multiple rows, `select="multi+shift"`, ...
51 |
52 | With `select={"style": "os", "items": "cell"}` you can let the user select specific cells, however the cell selection is not exposed in Python, nor taken into account when exporting the data.
53 | ```
54 |
--------------------------------------------------------------------------------
/docs/options/show_index.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Show Index
16 |
17 | By default, the index of a Series/DataFrame is shown only when not trivial, i.e. when it has a name, or when it differs from a range index. If you prefer, you can change the value of `showIndex` to either `True` or `False` to always or never show the index (the default value being `"auto"`).
18 |
19 | ```{code-cell} ipython3
20 | import pandas as pd
21 |
22 | import itables
23 |
24 | itables.init_notebook_mode()
25 | ```
26 |
27 | You can change this behavior globally with e.g.
28 | ```python
29 | itables.options.showIndex = True
30 | ```
31 |
32 | or locally by passing an argument `showIndex` to the `show` function:
33 |
34 | ```{code-cell} ipython3
35 | df_with_range_index = pd.DataFrame({"letter": list("abcd")})
36 | itables.show(df_with_range_index, showIndex=True)
37 | ```
38 |
--------------------------------------------------------------------------------
/docs/options/state_save.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # State Save
16 |
17 | To preserve the table state, like the [selected columns](colvis.md) or the [row order](order.md) you can use the [`stateSave`](https://datatables.net/reference/option/stateSave) option. This will preserve the table state when the page is reloaded, or when an ITable component is updated.
18 |
19 | ```{tip}
20 | You might also want to adjust [`stateDuration`](https://datatables.net/reference/option/stateDuration) which defaults to 2 hours.
21 | ```
22 |
23 | Try the option on this page: re-order the table by clicking on one column, then refresh the page - the order will be preserved.
24 |
25 | ```{code-cell} ipython3
26 | :tags: [full-width]
27 |
28 | import itables
29 |
30 | itables.init_notebook_mode()
31 |
32 | df = itables.sample_dfs.get_countries(html=False)
33 |
34 | itables.show(df, stateSave=True)
35 | ```
36 |
--------------------------------------------------------------------------------
/docs/options/style.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Style
16 |
17 | The `show` function has a `style` argument that determines the layout for a particular table.
18 |
19 | The default value for `style` is `"table-layout:auto;width:auto;margin:auto;caption-side:bottom"`.
20 | - Without `width:auto`, tables with few columns are stretched to fit the container width.
21 | - Using `margin:auto` makes non-wide tables centered.
22 |
23 | ```{code-cell} ipython3
24 | :tags: [full-width]
25 |
26 | import pandas as pd
27 |
28 | import itables
29 |
30 | itables.init_notebook_mode()
31 |
32 | df_small = pd.DataFrame({"a": [2, 1]})
33 | ```
34 |
35 | You can set a specific width or position for a table using with the `style` argument of the show function:
36 |
37 | ```{code-cell} ipython3
38 | itables.show(df_small, style="table-layout:auto;width:50%;float:right")
39 | ```
40 |
41 | or you can also change it for all tables by changing `itables.options.style`:
42 |
43 | ```python
44 | itables.options.style = "table-layout:auto;width:auto"
45 | ```
46 |
47 | ```{tip}
48 | The height of a table is governed by either [`lengthMenu`](length_menu.md) or by [`scrollY`](scroll_y.md).
49 | ```
50 |
51 | If you are looking for changing the table content appeareance, see the page on [CSS](../css.md), or the page on [Pandas Style](../pandas_style.md).
52 |
--------------------------------------------------------------------------------
/docs/options/text_in_header_can_be_selected.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | # Select Text in Header
16 |
17 | The `text_in_header_can_be_selected` option, which defaults to `True`, is provided by ITables since v2.4.0.
18 |
19 | With that option set (the default), you can select the text in the table headers. This is useful in the context of data exploration where
20 | you need to copy back the column name to your code.
21 |
22 | ```{code-cell} ipython3
23 | import itables
24 |
25 | itables.init_notebook_mode()
26 | df = itables.sample_dfs.get_countries(html=False)
27 |
28 | itables.show(df, "A table in which column headers can be selected")
29 | ```
30 |
31 | When `text_in_header_can_be_selected=False`, the column title cannot be selected as clicking on the title sorts the table.
32 |
33 | ```{code-cell} ipython3
34 | itables.show(
35 | df,
36 | "A table in which column headers cannot be selected",
37 | text_in_header_can_be_selected=False,
38 | )
39 | ```
40 |
--------------------------------------------------------------------------------
/docs/py/README.md:
--------------------------------------------------------------------------------
1 | This folders contains the `py:percent` representation of the documentation notebooks.
2 |
3 | This is used to enforce formatting (black, isort) and quality checks (ruff, pyright) on the documentation.
4 |
5 | NB: These files are kept in sync with the markdown documentation thanks to our Jupytext pre-commit hook in `.pre-commit-config.yaml`. You can edit either the `.py` or `.md` version and the pre-commit hook will update the other.
6 |
--------------------------------------------------------------------------------
/docs/py/apps/dash.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # formats: docs///md:myst,docs/py///py:percent
5 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
6 | # text_representation:
7 | # extension: .py
8 | # format_name: percent
9 | # format_version: '1.3'
10 | # kernelspec:
11 | # display_name: itables
12 | # language: python
13 | # name: itables
14 | # ---
15 |
16 | # %% [markdown]
17 | # # Dash
18 | #
19 | # If you wish to display a DataFrame which content is fixed (not reacting to the other controls in the application), you just need to import `ITable` from `itables.dash` and add it to your layout like here:
20 | #
21 | # ```{include} ../../apps/dash/1_display_only.py
22 | # :code: python
23 | # ```
24 | #
25 | # ## Selected rows
26 | #
27 | # Listening to the selected rows is simply done by adding `select=True` to the `ITable` call, and then implementing a callback on `Input("my_dataframe", "selected_rows")`.
28 | #
29 | # ```{include} ../../apps/dash/2_selected_rows.py
30 | # :code: python
31 | # ```
32 | #
33 | # ## Updating the DataFrame
34 | #
35 | # The `ITable` component has many properties. These properties (table content, selected rows etc) need to be updated in a consistent way. Therefore we recommend that you list the outputs with `ITableOutputs("my_dataframe")` in your callback, and update them with `updated_itable_outputs` which takes the same arguments as `show`, e.g. `df`, `caption`, `selected_rows`, etc, like in the below (extracted from this [example app](https://github.com/mwouts/itables/tree/main/apps/dash/3_update_table.py)):
36 | #
37 | # ```python
38 | # from itables.dash import ITable, ITableOutputs, updated_itable_outputs
39 | #
40 | # # (...)
41 | #
42 | # @callback(
43 | # ITableOutputs("my_dataframe"),
44 | # [
45 | # Input("checklist", "value"),
46 | # Input("caption", "value"),
47 | # State("my_dataframe", "selected_rows"),
48 | # State("my_dataframe", "dt_args"),
49 | # ],
50 | # )
51 | # def update_table(checklist, caption, selected_rows, dt_args):
52 | # if checklist is None:
53 | # checklist = []
54 | #
55 | # kwargs = {}
56 | #
57 | # # When df=None and when the dt_args don't change, the table is not updated
58 | # if callback_context.triggered_id == "checklist":
59 | # kwargs["df"] = get_countries(html="HTML" in checklist)
60 | #
61 | # kwargs["select"] = "Select" in checklist
62 | # if "Buttons" in checklist:
63 | # kwargs["buttons"] = ["copyHtml5", "csvHtml5", "excelHtml5"]
64 | #
65 | # return updated_itable_outputs(
66 | # caption=caption, selected_rows=selected_rows, current_dt_args=dt_args, **kwargs
67 | # )
68 | # ```
69 |
--------------------------------------------------------------------------------
/docs/py/apps/html.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% tags=["remove-cell"]
18 | # pyright: reportUnknownVariableType=false
19 |
20 | # %% [markdown]
21 | # # HTML export
22 | #
23 | # To get the HTML representation of a Pandas DataFrame `df` as an interactive [DataTable](https://datatables.net/), you can use `to_html_datatable` as below:
24 |
25 | # %%
26 | from IPython.display import HTML, display
27 |
28 | import itables
29 |
30 | df = itables.sample_dfs.get_countries(html=False)
31 | html = itables.to_html_datatable(df.head(3), display_logo_when_loading=False)
32 |
33 | # %% [markdown]
34 | # You can then save the `html` variable to a text file (note: if you're writing an HTML application, you could consider using [Shiny](shiny.md) or [Streamlit](streamlit.md) instead), or print it:
35 |
36 | # %% tags=["scroll-output"]
37 | print(html)
38 |
39 | # %% [markdown]
40 | # or display it, like `show` does:
41 |
42 | # %%
43 | display(HTML(html))
44 |
45 | # %% [markdown]
46 | # ~~~{admonition} The `connected` argument
47 | # :class: tip
48 | # The `to_html_datatable` function has a `connected` argument which defaults to what you set in `init_notebook_mode` (it's `True` if you didn't call it).
49 | #
50 | # - With `connected=True` you get an autonomous HTML fragment that loads `dt_for_itables` from the Internet
51 | # - With `connected=False`, the HTML snippet works only after you add the output of `generate_init_offline_itables_html()` to your HTML document
52 | # ~~~
53 |
--------------------------------------------------------------------------------
/docs/py/apps/marimo.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Marimo
19 | #
20 | # ```{warning}
21 | # The `init_notebook_mode` and the `show` function do not work in Marimo. This is because they both use `IPython.display` to display the HTML representation of the table, which is not a good fit for Marimo.
22 | # ```
23 | #
24 | # In Marimo the recommended way to use ITable is through the `ITable` [widget](widget.md):
25 |
26 | # %%
27 | import pandas as pd
28 |
29 | from itables.widget import ITable
30 |
31 | df = pd.DataFrame({"x": [2, 1, 3]})
32 |
33 | ITable(df)
34 |
35 | # %% [markdown]
36 | # A Sample Marimo application is available at [`apps/marimo/widget.py`](https://github.com/mwouts/itables/tree/main/apps/marimo/widget.py).
37 | #
38 | #
39 | # ## Using HTML
40 | #
41 | # You can also use `to_html_datatable` in combination with `mo.iframe` like in this example - but, as there is no `init_notebook_mode` cell, you will have to use the connected mode:
42 | #
43 | # ```python
44 | # import marimo as mo
45 | #
46 | # from itables import to_html_datatable
47 | #
48 | # html = to_html_datatable(df, connected=True)
49 | # mo.iframe(html)
50 | # ```
51 | #
52 | # A Sample Marimo application that uses `to_html_datatable` is available at [`apps/marimo/html.py`](https://github.com/mwouts/itables/tree/main/apps/marimo/html.py).
53 |
--------------------------------------------------------------------------------
/docs/py/apps/notebook.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Notebook Mode
19 | #
20 | # Activate ITables in a Jupyter environment for all your tables with `init_notebook_mode`:
21 |
22 | # %%
23 | import itables
24 |
25 | itables.init_notebook_mode()
26 |
27 | # %% [markdown]
28 | # You can go back to the standard HTML representation of Pandas DataFrames with `init_notebook_mode(all_interactive=False)`.
29 | #
30 | # Note that the `init_connected_mode` function also activates ITable's offline mode, unless you call it with a `connected=False` argument.
31 | #
32 | #
33 | # ## Offline mode
34 | #
35 | # By default `init_connected_mode` configures ITables to work offline (except in Colab). No internet connection is required as the JavaScript code is embedded into the notebook itself when you execute `init_notebook_mode`.
36 | #
37 | # In some contexts (Jupyter Book, Google Colab, etc...) you might
38 | # prefer to load the libraries dynamically from the internet.
39 | # To do so, add the argument `connected=True` when you
40 | # execute `init_notebook_mode`. This will also make your notebook lighter by
41 | # about [700kB](https://github.com/mwouts/itables/blob/main/tests/test_connected_notebook_is_small.py). Note that, in Google Colab, `connected=True` is the only working option.
42 | #
43 | # ## Show
44 | #
45 | # If you prefer to render only certain tables using `itables`, or want to set additional options, use `show`:
46 |
47 | # %%
48 | df = itables.sample_dfs.get_countries(html=False)
49 |
50 | itables.show(
51 | df,
52 | caption="A DataFrame rendered with ITables",
53 | lengthMenu=[2, 5, 10, 25, 50, 100, 250],
54 | )
55 |
56 | # %% [markdown]
57 | # ## HTML
58 | #
59 | # The `show` function simply displays the HTML snippet for the table, which is obtained with `to_html_datatable`. See more in the section on [HTML export](html.md).
60 |
--------------------------------------------------------------------------------
/docs/py/contributing.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% tags=["remove-cell"]
18 | # pyright: reportUnknownVariableType=false
19 |
20 | # %% [markdown]
21 | # # Contributing
22 | #
23 | # Thanks for considering making a contribution to ITables. There are
24 | # many ways you can help!
25 | #
26 | # ## Report an issue
27 | #
28 | # If you see an issue, a possible improvement, or if you can't find
29 | # the answer to your question, then you are very welcome to create
30 | # an issue on this project. Please provide enough detail so that
31 | # we can reproduce the issue.
32 | #
33 | # ## Improve the documentation
34 | #
35 | # If you would like to add a new example,
36 | # or improve the documentation, feel free to make a pull request!
37 | #
38 | # You can render the documentation locally - see the section on
39 | # [Jupyter Book](developing.md#jupyter-book) in the developer guide.
40 | #
41 | # ## Give credit to ITables
42 | #
43 | # It's always great to see new stars coming to ITables!
44 | #
45 | #
46 | # If you wanted to share a link to ITables and DataTables (no obligation whatsoever), you could use something like this:
47 |
48 | # %%
49 | from IPython.display import HTML, display
50 |
51 | display(
52 | HTML(
53 | """
54 | Tables displayed with ITables,
55 | a Python wrapper for DataTables
56 | """
57 | )
58 | )
59 |
60 | # %% [markdown]
61 | # ## Support DataTables
62 | #
63 | # Allan Jardine, the main developer of DataTables, has done a fantastic work on [DataTables](https://datatables.net/).
64 | #
65 | # If you enjoy his library, you could become a
66 | # [supporter](https://datatables.net/supporters/) -
67 | # contributions range from 9 to 99$/year before VAT.
68 | # Or you could take a subscription for DataTable's [Editor](https://editor.datatables.net)
69 | # that ITables might support in the future (please subscribe to [#243](https://github.com/mwouts/itables/issues/243) for updates).
70 | #
71 | # ## Develop a new feature
72 | #
73 | # It is generally a good idea to get in touch with us first - e.g.
74 | # open an issue and let us know what you'd like to do.
75 | #
76 | # But you can also simply clone the project and test your ideas.
77 | # A guide on how to set up a development environment, and how to
78 | # run some tests, is available [here](developing.md).
79 |
--------------------------------------------------------------------------------
/docs/py/css.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% tags=["remove-cell"]
18 | # ruff: noqa: E402
19 | # pyright: reportUnknownVariableType=false
20 |
21 | # %% [markdown]
22 | # # CSS
23 | #
24 | # You can use CSS to alter how tables are rendered.
25 | #
26 | # For instance, we change the
27 | # [font size](https://developer.mozilla.org/en-US/docs/Web/CSS/font-size)
28 | # for all the tables in the document with this code:
29 |
30 | # %%
31 | from IPython.display import HTML, display
32 |
33 | css = """
34 | .dt-container {
35 | font-size: small;
36 | }
37 | """
38 | display(HTML(f"" ""))
39 |
40 | # %% [markdown]
41 | # This is helpful for instance in the context of
42 | # [Quarto presentations](apps/quarto.md).
43 | #
44 | # With this over CSS, we change _every datatable_ table header
45 | # in the notebook to bold/italic.
46 |
47 | # %%
48 | css = """
49 | .dataTable th {
50 | font-weight: bolder;
51 | font-style: italic;
52 | }
53 | """
54 | display(HTML(f"" ""))
55 |
56 | # %% [markdown]
57 | # You might also want to alter the style of specific tables only.
58 | # To do this, add a new class to the target tables, as
59 | # in the example below:
60 |
61 | # %%
62 | class_specific_css = ".table_with_monospace_font { font-family: courier, monospace }"
63 | display(HTML(f"" ""))
64 |
65 | # %% tags=["full-width"]
66 | import itables
67 |
68 | itables.init_notebook_mode()
69 |
70 | df = itables.sample_dfs.get_countries(html=False)
71 |
72 | itables.show(df, classes="display nowrap table_with_monospace_font")
73 |
--------------------------------------------------------------------------------
/docs/py/dark_mode.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # formats: docs///md:myst,docs/py///py:percent
5 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
6 | # text_representation:
7 | # extension: .py
8 | # format_name: percent
9 | # format_version: '1.3'
10 | # kernelspec:
11 | # display_name: itables
12 | # language: python
13 | # name: itables
14 | # ---
15 |
16 | # %% [markdown]
17 | # # Dark Themes
18 | #
19 | # When a notebook or application is rendered using a dark theme, DataTable requires that a `dark`
20 | # class be added to the HTML document. This can be done with the following Javascript snippet:
21 | # ```javascript
22 | # document.documentElement.classList.add('dark');
23 | # ```
24 | #
25 | # When ITables is used in a notebook, this is handled by
26 | # `init_notebook_mode` which displays the [`init_datatables.html`](https://github.com/mwouts/itables/blob/main/src/itables/html/init_datatables.html) snippet.
27 | #
28 | # Please open a PR if you see how to further improve the
29 | # support of light vs dark themes, and e.g. set the `dark`
30 | # class dynamically when the theme is changed.
31 |
--------------------------------------------------------------------------------
/docs/py/downsampling.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% tags=["remove-cell"]
18 | # pyright: reportUnusedExpression=false
19 |
20 | # %% [markdown]
21 | # # Downsampling
22 | #
23 | # When an interactive table is displayed by `itables`, the table data is embedded in the notebook output. As we don't want your notebook to become super heavy just because you displayed a large table, we have a downsampling mechanism in place.
24 | #
25 | # When the data in a table is larger than `maxBytes`, which is equal to 64KB by default, `itables` will display only a subset of the table - one that fits into `maxBytes`, and display a warning that points to the `itables` documentation.
26 | #
27 | # If you wish, you can increase the value of `maxBytes` or even deactivate the limit (with `maxBytes=0`). Similarly, you can set a limit on the number of rows (`maxRows`, defaults to 0) or columns (`maxColumns`, defaults to `200`).
28 |
29 | # %%
30 | import itables
31 |
32 | itables.init_notebook_mode()
33 |
34 | # %%
35 | itables.options.lengthMenu = [2, 5, 10, 20, 50, 100, 200, 500]
36 | itables.options.maxBytes = "8KB"
37 |
38 | df = itables.sample_dfs.get_indicators()
39 | itables.downsample.as_nbytes(itables.options.maxBytes), itables.downsample.nbytes(df)
40 |
41 | # %% tags=["full-width"]
42 | df
43 |
44 | # %% [markdown]
45 | # To show the table in full, we can modify the value of `maxBytes` either locally:
46 |
47 | # %% tags=["full-width"]
48 | itables.show(df, maxBytes=0)
49 |
50 | # %% [markdown]
51 | # or globally:
52 |
53 | # %% tags=["full-width"]
54 | itables.options.maxBytes = "1MB"
55 | df
56 |
--------------------------------------------------------------------------------
/docs/py/options/allow_html.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% tags=["remove-cell"]
18 | # pyright: reportUnknownVariableType=false
19 | # pyright: reportUnknownArgumentType=false
20 | # pyright: reportUnknownMemberType=false
21 |
22 | # %% [markdown]
23 | # # Allow HTML
24 | #
25 | # Since v2.4.0, the HTML content in your tables is escaped by default. You can change this by passing `allow_html=True`.
26 | #
27 | # ```{warning}
28 | # Please make sure that you trust the content of your tables before allowing HTML content. When `allow_html=True`, ITables passes the table content verbatim to datatables.net, and the HTML will be executed.
29 | #
30 | # If you did not produce the non-numeric content yourself, you should sanitize it using e.g. `rh3` or `bleach`.
31 | #
32 | # See also this datatable page on [security](https://datatables.net/manual/security).
33 | # ```
34 | #
35 | # ## Text formatting
36 | #
37 | # When you allow HTML content, you can have formatted text,
38 | # links or even images in your tables:
39 |
40 | # %%
41 | import pandas as pd
42 |
43 | import itables
44 |
45 | itables.init_notebook_mode()
46 |
47 | itables.show(
48 | pd.Series(
49 | [
50 | "bold",
51 | "italic",
52 | 'link',
53 | ],
54 | name="HTML",
55 | ),
56 | allow_html=True,
57 | )
58 |
59 | # %% [markdown]
60 | # ## Images in a table
61 |
62 | # %% tags=["full-width"]
63 | df = itables.sample_dfs.get_countries(html=False)
64 |
65 | df["flag"] = [
66 | ''
67 | '
'.format(code=code.lower(), country=country)
69 | for code, country in zip(df.index, df["country"])
70 | ]
71 | df["country"] = [
72 | '{}'.format(country, country)
73 | for country in df["country"]
74 | ]
75 | df["capital"] = [
76 | '{}'.format(capital, capital)
77 | for capital in df["capital"]
78 | ]
79 | itables.show(df, allow_html=True)
80 |
81 | # %% [markdown]
82 | # ## Base64 images
83 | #
84 | # [Base64 encoded image](https://stackoverflow.com/a/8499716/9817073) are supported, too:
85 |
86 | # %%
87 | itables.show(
88 | pd.Series(
89 | {
90 | "url": '
',
91 | "base64": '
',
94 | },
95 | name="Images",
96 | ),
97 | allow_html=True,
98 | )
99 |
--------------------------------------------------------------------------------
/docs/py/options/buttons.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Buttons
19 | #
20 | # The DataTables [buttons](https://datatables.net/extensions/buttons/) let you copy the table data, or export it as CSV or Excel files.
21 | #
22 | # To display the buttons, you need to pass a `buttons` argument to the `show` function:
23 |
24 | # %%
25 | import itables
26 |
27 | itables.init_notebook_mode()
28 |
29 | df = itables.sample_dfs.get_countries(html=False)
30 |
31 | itables.show(df, buttons=["pageLength", "copyHtml5", "csvHtml5", "excelHtml5"])
32 |
33 | # %% [markdown]
34 | # You can also specify a [`layout`](layout) modifier that will decide
35 | # the location of the buttons (the default is `layout={"topStart": "buttons"}`). And if you want to keep the pagination control too, you can add `"pageLength"` to the list of buttons - as done above.
36 | #
37 | # As always, it is possible to set default values for these parameters by setting these on `itables.options`. For instance, set
38 | # ```python
39 | # itables.options.buttons = ["copyHtml5", "csvHtml5", "excelHtml5"]
40 | # ```
41 | # to get the buttons for all your tables.
42 | #
43 | #
44 | # By default, the exported file name is the name of the HTML page. To change it, set a
45 | # [`title` option](https://datatables.net/extensions/buttons/examples/html5/filename.html) on the buttons, like
46 | # here:
47 |
48 | # %% tags=["full-width"]
49 | itables.show(
50 | df,
51 | buttons=[
52 | "pageLength",
53 | {"extend": "csvHtml5", "title": "download_filename"},
54 | {"extend": "excelHtml5", "title": "download_filename"},
55 | ],
56 | )
57 |
58 | # %% [markdown]
59 | # ```{tip}
60 | # Only the filtered or selected rows are exported to CSV/Excel. To filter the rows you can use the simple search box, the [SearchPanes](search_panes) and [SearchBuilder](search_builder) options, or the [select](select.md) extension.
61 | # ```
62 | #
63 | # ```{warning}
64 | # At the moment, the CSV and Excel buttons don't work well with large tables in some browsers.
65 | # Please subscribe to [#251](https://github.com/mwouts/itables/issues/251) if you wish to receive updates on this.
66 | # ```
67 | #
68 | # ```{warning}
69 | # The PDF button is not included in ITables' DataTable bundle. This is because the required PDF libraries have a large footprint on the bundle size. Still, you can add it to your custom bundle, see our page on how to bundle [custom extensions](../custom_extensions.md).
70 | # ```
71 |
--------------------------------------------------------------------------------
/docs/py/options/caption.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Caption
19 |
20 | # %% tags=["full-width"]
21 | import itables
22 |
23 | itables.init_notebook_mode()
24 |
25 | df = itables.sample_dfs.get_countries(html=False)
26 | itables.show(df, "Countries from the World Bank Database")
27 |
28 | # %% [markdown]
29 | # The caption appears at the bottom of the table by default: this is governed by `caption-side:bottom`
30 | # in the [`style` option](style) (but for some reason this is not effective in Jupyter Book 🤔).
31 |
--------------------------------------------------------------------------------
/docs/py/options/classes.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Classes
19 | #
20 | # Select how your table looks like with the `classes` argument (defaults to `"display nowrap"`) of the `show` function, or by changing `itables.options.classes`.
21 | #
22 | # Add `"compact"` if you want a denser table:
23 |
24 | # %% tags=["full-width"]
25 | import itables
26 |
27 | itables.init_notebook_mode()
28 |
29 | df = itables.sample_dfs.get_countries(html=False)
30 |
31 | itables.show(df, classes="display nowrap compact")
32 |
33 | # %% [markdown]
34 | # Remove `"nowrap"` if you want the cell content to be wrapped:
35 |
36 | # %% tags=["full-width"]
37 | itables.show(df, classes="display")
38 |
39 | # %% [markdown]
40 | # [More options](https://datatables.net/manual/styling/classes#Table-classes) like `"cell-border"` are available:
41 |
42 | # %% tags=["full-width"]
43 | itables.show(df, classes="display nowrap cell-border")
44 |
--------------------------------------------------------------------------------
/docs/py/options/column_defs.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% tags=["remove-cell"]
18 | # pyright: reportUnusedExpression=false
19 |
20 | # %% [markdown]
21 | # # Column Definitions
22 | #
23 | # The [`columnDefs.width`](https://datatables.net/reference/option/columns.width) argument let you adjust the column widths.
24 | #
25 | # Note that the default value of `style`, or of `autoWidth` (defaults to `True`), might override custom column widths,
26 | # so you might have to change their values as in the examples below.
27 | #
28 | # You can set a fixed width for all the columns with `"targets": "_all"`:
29 |
30 | # %% tags=["full-width"]
31 | import itables
32 |
33 | itables.init_notebook_mode()
34 |
35 | df = itables.sample_dfs.get_countries(html=False)
36 |
37 | itables.show(
38 | df,
39 | columnDefs=[{"width": "120px", "targets": "_all"}],
40 | style="width:1200px",
41 | autoWidth=False,
42 | )
43 |
44 | # %% [markdown]
45 | # You can also adjust the width of selected columns only:
46 |
47 | # %% tags=["full-width"]
48 | itables.show(
49 | df,
50 | columnDefs=[{"width": "30%", "targets": [2, 3]}],
51 | style="width:100%;margin:auto",
52 | )
53 |
54 | # %% [markdown]
55 | # If you wish you can also set a value for `columnDefs` permanently in `itables.options` as demonstrated in the cell alignment example below.
56 | #
57 | # You can use the DataTables [cell classes](https://datatables.net/manual/styling/classes#Cell-classes) like `dt-left`, `dt-center`, `dt-right` etc. to set the cell alignment. Specify it for one table by using the `columnDefs` argument of `show`
58 |
59 | # %% tags=["full-width"]
60 | itables.show(df, columnDefs=[{"className": "dt-center", "targets": "_all"}])
61 |
62 | # %% [markdown]
63 | # or globally by setting `itables.options.columnDefs`:
64 |
65 | # %% tags=["full-width"]
66 | itables.options.columnDefs = [{"className": "dt-center", "targets": "_all"}]
67 |
68 | df
69 |
--------------------------------------------------------------------------------
/docs/py/options/column_filters.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Column Filters
19 | #
20 | # Use `column_filters = "header"` or `"footer"` if you wish to display individual column filters
21 | # (remove the global search box with a [`layout`](layout) modifier if desired).
22 |
23 | # %%
24 | import pandas as pd
25 |
26 | import itables
27 |
28 | itables.init_notebook_mode()
29 |
30 | alpha_numeric_df = pd.DataFrame(
31 | [["one", 1.5], ["two", 2.3]], columns=["string", "numeric"]
32 | )
33 |
34 | itables.show(alpha_numeric_df, column_filters="header", layout={"topEnd": None})
35 |
36 | # %% [markdown]
37 | # As always you can set activate column filters by default with e.g.
38 |
39 | # %%
40 | itables.options.column_filters = "footer"
41 |
42 | # %% [markdown]
43 | # Column filters also work on dataframes with multiindex columns:
44 |
45 | # %%
46 | itables.sample_dfs.get_dict_of_test_dfs()["multiindex"]
47 |
48 | # %% tags=["remove-cell"]
49 | # Revert back to the default to avoid interactions with the tests
50 | itables.options.column_filters = False
51 |
--------------------------------------------------------------------------------
/docs/py/options/colvis.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Column Visibility
19 | #
20 | # The [column visibility](https://datatables.net/extensions/buttons/examples/column_visibility/index.html) buttons of DataTables let you select which columns are visible.
21 |
22 | # %% tags=["full-width"]
23 | import itables
24 |
25 | itables.init_notebook_mode()
26 |
27 | df = itables.sample_dfs.get_countries(html=False)
28 |
29 | itables.show(
30 | # column visibility works best with a flat header
31 | df.reset_index(),
32 | buttons=["columnsToggle"],
33 | )
34 |
35 | # %% [markdown]
36 | # ```{tip}
37 | # The column visibility button is available under many forms.
38 | #
39 | # Check-out `buttons=["colvis"]` for a [single](https://datatables.net/extensions/buttons/examples/column_visibility/simple.html) button.
40 | #
41 | # Extend the `colvis` button with the [collection layout](https://datatables.net/extensions/buttons/examples/column_visibility/layout.html).
42 | #
43 | # As always, when porting examples from DataTables to ITables, you will
44 | # have to convert the JavaScript notation (left) to Python (right) as in the below:
45 | # ::::{grid}
46 | #
47 | # :::{grid-item}
48 | # :outline:
49 | # :columns: 6
50 | # ~~~javascript
51 | # buttons: [
52 | # {
53 | # extend: 'colvis',
54 | # collectionLayout: 'fixed columns',
55 | # popoverTitle: 'Column visibility control'
56 | # }
57 | # ]
58 | # ~~~
59 | # :::
60 | # :::{grid-item}
61 | # :outline:
62 | # :columns: 6
63 | # ~~~python
64 | # buttons = [
65 | # {
66 | # "extend": "colvis",
67 | # "collectionLayout": "fixed columns",
68 | # "popoverTitle": "Column visibility control"
69 | # }
70 | # ]
71 | # ~~~
72 | # :::
73 | #
74 | # ::::
75 | #
76 | # ```
77 |
--------------------------------------------------------------------------------
/docs/py/options/fixed_columns.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # FixedColumns
19 | #
20 | # [FixedColumn](https://datatables.net/extensions/fixedcolumns/) is an extension
21 | # that let you fix some columns as you scroll horizontally.
22 |
23 | # %%
24 | import string
25 |
26 | import numpy as np
27 | import pandas as pd
28 |
29 | import itables
30 |
31 | itables.init_notebook_mode()
32 |
33 | wide_df = pd.DataFrame(
34 | {
35 | letter: np.random.normal(size=100)
36 | for letter in string.ascii_lowercase + string.ascii_uppercase
37 | }
38 | )
39 |
40 | itables.show(
41 | wide_df,
42 | fixedColumns={"start": 1, "end": 2},
43 | scrollX=True,
44 | )
45 |
--------------------------------------------------------------------------------
/docs/py/options/footer.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Footer
19 | #
20 | # Use `footer = True` if you wish to display a table footer.
21 |
22 | # %% tags=["full-width"]
23 | import itables
24 |
25 | itables.init_notebook_mode()
26 |
27 | df = itables.sample_dfs.get_countries(html=False)
28 | itables.show(df, footer=True)
29 |
--------------------------------------------------------------------------------
/docs/py/options/keys.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Keys
19 | #
20 | # With the [KeyTable](https://datatables.net/extensions/keytable/) extension you can navigate in a table using the arrow keys:
21 |
22 | # %% tags=["full-width"]
23 | import itables
24 |
25 | itables.init_notebook_mode()
26 |
27 | itables.show(
28 | itables.sample_dfs.get_countries(html=False),
29 | keys=True,
30 | )
31 |
32 | # %% [markdown]
33 | # ```{tip}
34 | # You can activate this option for all your tables with
35 | #
36 | # ~~~python
37 | # itables.options.keys = True
38 | # ~~~
39 | # ```
40 | #
41 | # ```{warning}
42 | # The KeyTable extension works in Jupyter Book (try it here in the documentation) but not in JupyterLab.
43 | # ```
44 |
--------------------------------------------------------------------------------
/docs/py/options/layout.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Layout
19 | #
20 | # By default, datatables that don't fit in one page come with a search box, a pagination control, a table summary, etc.
21 | # You can select which elements are actually displayed using
22 | # DataTables' [`layout` option](https://datatables.net/reference/option/layout) with e.g.:
23 |
24 | # %%
25 | import itables
26 |
27 | itables.init_notebook_mode()
28 | df = itables.sample_dfs.get_countries(html=False)
29 |
30 | # %%
31 | itables.show(df, layout={"topStart": "search", "topEnd": None})
32 |
33 | # %% [markdown]
34 | # The available positions are `topStart, topEnd, bottomStart, bottomEnd`. You can also use `top2Start`, etc... (see more
35 | # in the [DataTables documentation](https://datatables.net/reference/option/layout)).
36 | #
37 | # Like for the other arguments of `show`, you can change the default value of the dom option with e.g.:
38 | #
39 | # ```
40 | # itables.options.layout = {
41 | # "topStart": "pageLength",
42 | # "topEnd": "search",
43 | # "bottomStart": "info",
44 | # "bottomEnd": "paging"
45 | # } # (default value)
46 | # ```
47 | #
48 | # ```{tip}
49 | # The `layout` option was introduced with `itables==2.0` and `DataTables==2.0`
50 | # and replaced the former [`dom` option](https://datatables.net/reference/option/dom).
51 | # ```
52 |
--------------------------------------------------------------------------------
/docs/py/options/length_menu.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # LengthMenu
19 | #
20 | # Select [how many entries](https://datatables.net/examples/advanced_init/length_menu.html) should appear at once in the table with either the `lengthMenu` argument of the `show` function, or with the global option `itables.options.lengthMenu`:
21 |
22 | # %%
23 | import itables
24 |
25 | itables.init_notebook_mode()
26 | df = itables.sample_dfs.get_countries(html=False)
27 |
28 | # %% tags=["full-width"]
29 | itables.show(df, lengthMenu=[2, 5, 10, 20, 50])
30 |
--------------------------------------------------------------------------------
/docs/py/options/options.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Options
19 | #
20 | # ## DataTable Options
21 | #
22 | # ITables is a wrapper for the Javascript DataTables library, which means that you can use more or less directly the DataTables [options](https://datatables.net/options) from within Python.
23 | #
24 | # Since ITables just maps these options to DataTables, you are invited to have a look at DataTable's great [documentation](https://datatables.net/), and to its huge collection of [examples](https://datatables.net/examples/index). The DataTable [forum](https://datatables.net/forums/) can be quite useful as well.
25 | #
26 | # A non-exhaustive list of the DataTable options, together with their expected types, is available at [`itables.typing.DataTableOptions`](https://github.com/mwouts/itables/blob/main/src/itables/typing.py).
27 | #
28 | # Option names and types are checked by default at run time when `typeguard>=4.4.1` is installed - you can deactivate this by setting `warn_on_undocumented_option=False`.
29 | #
30 | # If you see an option that you find useful and is not documented, or a type hint that is incorrect, please make a PR (and add an example to the documentation, too).
31 |
32 | # %% tags=["scroll-output"]
33 | import inspect
34 |
35 | import itables
36 |
37 | print(inspect.getsource(itables.typing.DataTableOptions))
38 |
39 | # %% [markdown]
40 | # ## ITable Options
41 | #
42 | # ITables itself adds a few options like `connected`, `maxBytes`, `allow_html` etc.
43 | #
44 | # The ITable options are documented at [`itables.typing.ITableOptions`](https://github.com/mwouts/itables/blob/main/src/itables/typing.py):
45 |
46 | # %% tags=["scroll-output"]
47 | print(inspect.getsource(itables.typing.ITableOptions))
48 |
49 | # %% [markdown]
50 | # ## Default values
51 | #
52 | # Some of the options have a default value set in [`itables.options`](https://github.com/mwouts/itables/blob/main/src/itables/options.py). You can change these defaults easily, and even set defauts for the options that don't have one yet with e.g.
53 | #
54 | # ```python
55 | # import itables
56 | #
57 | # itables.options.maxBytes = "128KB"
58 | # ```
59 |
60 | # %% tags=["scroll-output"]
61 | print(inspect.getsource(itables.options))
62 |
--------------------------------------------------------------------------------
/docs/py/options/order.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Order
19 | #
20 | # Since ITables v1.3.0, the interactive datatable shows the rows in the same order as the original dataframe.
21 | #
22 | # You can pre-select a explicit order with the [`order`](https://datatables.net/reference/option/order) option:
23 |
24 | # %%
25 | import pandas as pd
26 |
27 | import itables
28 |
29 | itables.init_notebook_mode()
30 |
31 | sorted_df = pd.DataFrame({"a": [2, 1]}, index=pd.Index([1, 2], name="i"))
32 | itables.show(sorted_df, order=[[1, "asc"]])
33 |
--------------------------------------------------------------------------------
/docs/py/options/paging.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Paging
19 | #
20 | # Use [`paging=False`](https://datatables.net/reference/option/paging) to show the table in full:
21 |
22 | # %%
23 | import itables
24 |
25 | itables.init_notebook_mode()
26 | df = itables.sample_dfs.get_countries(html=False)
27 |
28 | # %% tags=["full-width"]
29 | itables.show(df.head(8), paging=False)
30 |
--------------------------------------------------------------------------------
/docs/py/options/row_group.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% tags=["remove-cell"]
18 | # pyright: reportUnknownMemberType=false
19 |
20 | # %% [markdown]
21 | # # RowGroup
22 | #
23 | # Use the [RowGroup](https://datatables.net/extensions/rowgroup/) extension to group
24 | # the data according to the content of one colum. Optionally, you can hide the content of that column to avoid duplicating the information.
25 |
26 | # %% tags=["full-width"]
27 | import itables
28 |
29 | itables.init_notebook_mode()
30 |
31 | df = itables.sample_dfs.get_countries(html=False)
32 |
33 | itables.show(
34 | df.sort_values("region"),
35 | rowGroup={"dataSrc": 1},
36 | columnDefs=[{"targets": 1, "visible": False}],
37 | )
38 |
--------------------------------------------------------------------------------
/docs/py/options/scroll_x.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Horizontal Scroll
19 | #
20 | # DataTables has a [`scrollX`](https://datatables.net/reference/option/scrollX) option that might be helpful for wide tables.
21 | #
22 | # ```{tip}
23 | # Since ITables v2.1.2, the `.dt-layout-table` div has a default overflow equal to `auto`, so in most cases you don't need to use `scrollX`.
24 | # ```
25 |
26 | # %% tags=["full-width"]
27 | import itables
28 |
29 | itables.init_notebook_mode()
30 |
31 | df = itables.sample_dfs.get_dict_of_test_dfs()["wide"]
32 | itables.show(df, scrollX=True)
33 |
34 | # %% [markdown]
35 | # ```{warning}
36 | # Using `scrollX` on a table that is not wide enough can lead to an issue where the table headers are not aligned with the table content.
37 | # ```
38 |
--------------------------------------------------------------------------------
/docs/py/options/scroll_y.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Vertical Scroll
19 | #
20 | # The [`scrollY`](https://datatables.net/examples/basic_init/scroll_y.html) parameter is an interesting alternative to the pagination:
21 |
22 | # %% tags=["full-width"]
23 | import itables
24 |
25 | itables.init_notebook_mode()
26 |
27 | df = itables.sample_dfs.get_countries(html=False)
28 | itables.show(df, scrollY="350px", scrollCollapse=True, paging=False)
29 |
--------------------------------------------------------------------------------
/docs/py/options/search.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Search
19 | #
20 | # The [search option](https://datatables.net/reference/option/search) let you control the initial value for the search field, and whether the query should be treated as a regular expression or not:
21 |
22 | # %%
23 | import itables
24 |
25 | itables.init_notebook_mode()
26 |
27 | # %% tags=["full-width"]
28 | df = itables.sample_dfs.get_countries(html=False)
29 | itables.show(df, search={"regex": True, "caseInsensitive": True, "search": "s.ain"})
30 |
--------------------------------------------------------------------------------
/docs/py/options/search_builder.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Search Builder
19 | #
20 | # [SearchBuilder](https://datatables.net/extensions/searchbuilder/) let you build complex search queries. You just need to add it to the layout
21 | # by passing e.g. `layout={"top1": "searchBuilder"}`.
22 | #
23 | # It is possible to set a predefined search, as we do in the below:
24 |
25 | # %% tags=["full-width"]
26 | import itables
27 |
28 | itables.init_notebook_mode()
29 |
30 | df = itables.sample_dfs.get_countries(html=False, climate_zone=True)
31 |
32 | itables.show(
33 | df,
34 | layout={"top1": "searchBuilder"},
35 | searchBuilder={
36 | "preDefined": {
37 | "criteria": [
38 | {"data": "climate_zone", "condition": "=", "value": ["Sub-tropical"]}
39 | ]
40 | }
41 | },
42 | )
43 |
--------------------------------------------------------------------------------
/docs/py/options/search_panes.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Search Panes
19 | #
20 | # [SearchPanes](https://datatables.net/extensions/searchpanes/) is an extension that lets you select rows based on unique values. In the example below we have activated the cascade filtering through the [`searchPanes.cascadePanes`](https://datatables.net/extensions/searchpanes/examples/initialisation/cascadePanes.html) argument.
21 | #
22 | # Note that, in Jupyter, the [`searchPanes.layout`](https://datatables.net/extensions/searchpanes/layout) argument is required (otherwise the search panes are too wide).
23 |
24 | # %% tags=["full-width"]
25 | import itables
26 |
27 | itables.init_notebook_mode()
28 |
29 | df = itables.sample_dfs.get_countries(html=False, climate_zone=True)
30 |
31 | itables.show(
32 | df.reset_index(),
33 | layout={"top1": "searchPanes"},
34 | searchPanes={"layout": "columns-3", "cascadePanes": True, "columns": [1, 6, 7]},
35 | )
36 |
37 | # %% [markdown]
38 | # ```{warning}
39 | # When searching, please keep in mind that ITables will [downsample](../downsampling.md) your table if it is larger than `maxBytes`, so you might not see the full dataset - pay attention to the downsampling message at the bottom left of the table.
40 | # ```
41 |
--------------------------------------------------------------------------------
/docs/py/options/select.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Select
19 | #
20 | # The [select](https://datatables.net/extensions/select) extension let you select rows (or cells).
21 | #
22 | # ~~~{admonition} The `selected_rows` attribute
23 | # :class: tip
24 | # It is possible to access the `selected_rows` back in Python but for this you will have to use, instead of `show`, either
25 | # - the `ITable` [Widget](../apps/widget.md)
26 | # - the `ITable` [Dash component](../apps/dash.md)
27 | # - the `interactive_table` [Streamlit component](../apps/streamlit.md)
28 | #
29 | # ⚠ When a table has been downsampled, only the visible rows can be selected.
30 | # ~~~
31 | #
32 | # ```{tip}
33 | # The `select` option also interacts with the [`buttons`](buttons.md) extension. If you click on the CSV or Excel export while having selected some rows, only those rows will be exported - see the example below.
34 | # ```
35 |
36 | # %%
37 | import itables
38 |
39 | itables.init_notebook_mode()
40 |
41 | itables.show(
42 | itables.sample_dfs.get_countries(html=False),
43 | select=True,
44 | selected_rows=[2, 4, 5],
45 | buttons=["copyHtml5", "csvHtml5", "excelHtml5"],
46 | )
47 |
48 | # %% [markdown]
49 | # ```{tip}
50 | # The `select` option accept multiple values, as documented [here](https://datatables.net/extensions/select):
51 | # - `select=True` or `select="os"` let you select using single click, shift-click and ctrl-click
52 | # - `select="single"` let you select a single row
53 | # - `select="multi"` for single click selection of multiple rows, `select="multi+shift"`, ...
54 | #
55 | # With `select={"style": "os", "items": "cell"}` you can let the user select specific cells, however the cell selection is not exposed in Python, nor taken into account when exporting the data.
56 | # ```
57 |
--------------------------------------------------------------------------------
/docs/py/options/show_index.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Show Index
19 | #
20 | # By default, the index of a Series/DataFrame is shown only when not trivial, i.e. when it has a name, or when it differs from a range index. If you prefer, you can change the value of `showIndex` to either `True` or `False` to always or never show the index (the default value being `"auto"`).
21 |
22 | # %%
23 | import pandas as pd
24 |
25 | import itables
26 |
27 | itables.init_notebook_mode()
28 |
29 | # %% [markdown]
30 | # You can change this behavior globally with e.g.
31 | # ```python
32 | # itables.options.showIndex = True
33 | # ```
34 | #
35 | # or locally by passing an argument `showIndex` to the `show` function:
36 |
37 | # %%
38 | df_with_range_index = pd.DataFrame({"letter": list("abcd")})
39 | itables.show(df_with_range_index, showIndex=True)
40 |
--------------------------------------------------------------------------------
/docs/py/options/state_save.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # State Save
19 | #
20 | # To preserve the table state, like the [selected columns](colvis.md) or the [row order](order.md) you can use the [`stateSave`](https://datatables.net/reference/option/stateSave) option. This will preserve the table state when the page is reloaded, or when an ITable component is updated.
21 | #
22 | # ```{tip}
23 | # You might also want to adjust [`stateDuration`](https://datatables.net/reference/option/stateDuration) which defaults to 2 hours.
24 | # ```
25 | #
26 | # Try the option on this page: re-order the table by clicking on one column, then refresh the page - the order will be preserved.
27 |
28 | # %% tags=["full-width"]
29 | import itables
30 |
31 | itables.init_notebook_mode()
32 |
33 | df = itables.sample_dfs.get_countries(html=False)
34 |
35 | itables.show(df, stateSave=True)
36 |
--------------------------------------------------------------------------------
/docs/py/options/style.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Style
19 | #
20 | # The `show` function has a `style` argument that determines the layout for a particular table.
21 | #
22 | # The default value for `style` is `"table-layout:auto;width:auto;margin:auto;caption-side:bottom"`.
23 | # - Without `width:auto`, tables with few columns are stretched to fit the container width.
24 | # - Using `margin:auto` makes non-wide tables centered.
25 |
26 | # %% tags=["full-width"]
27 | import pandas as pd
28 |
29 | import itables
30 |
31 | itables.init_notebook_mode()
32 |
33 | df_small = pd.DataFrame({"a": [2, 1]})
34 |
35 | # %% [markdown]
36 | # You can set a specific width or position for a table using with the `style` argument of the show function:
37 |
38 | # %%
39 | itables.show(df_small, style="table-layout:auto;width:50%;float:right")
40 |
41 | # %% [markdown]
42 | # or you can also change it for all tables by changing `itables.options.style`:
43 | #
44 | # ```python
45 | # itables.options.style = "table-layout:auto;width:auto"
46 | # ```
47 | #
48 | # ```{tip}
49 | # The height of a table is governed by either [`lengthMenu`](length_menu.md) or by [`scrollY`](scroll_y.md).
50 | # ```
51 | #
52 | # If you are looking for changing the table content appeareance, see the page on [CSS](../css.md), or the page on [Pandas Style](../pandas_style.md).
53 |
--------------------------------------------------------------------------------
/docs/py/options/text_in_header_can_be_selected.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% [markdown]
18 | # # Select Text in Header
19 | #
20 | # The `text_in_header_can_be_selected` option, which defaults to `True`, is provided by ITables since v2.4.0.
21 | #
22 | # With that option set (the default), you can select the text in the table headers. This is useful in the context of data exploration where
23 | # you need to copy back the column name to your code.
24 |
25 | # %%
26 | import itables
27 |
28 | itables.init_notebook_mode()
29 | df = itables.sample_dfs.get_countries(html=False)
30 |
31 | itables.show(df, "A table in which column headers can be selected")
32 |
33 | # %% [markdown]
34 | # When `text_in_header_can_be_selected=False`, the column title cannot be selected as clicking on the title sorts the table.
35 |
36 | # %%
37 | itables.show(
38 | df,
39 | "A table in which column headers cannot be selected",
40 | text_in_header_can_be_selected=False,
41 | )
42 |
--------------------------------------------------------------------------------
/docs/py/troubleshooting.py:
--------------------------------------------------------------------------------
1 | # ---
2 | # jupyter:
3 | # jupytext:
4 | # default_lexer: ipython3
5 | # formats: docs///md:myst,docs/py///py:percent
6 | # notebook_metadata_filter: -jupytext.text_representation.jupytext_version
7 | # text_representation:
8 | # extension: .py
9 | # format_name: percent
10 | # format_version: '1.3'
11 | # kernelspec:
12 | # display_name: itables
13 | # language: python
14 | # name: itables
15 | # ---
16 |
17 | # %% tags=["remove-cell"]
18 | # ruff: noqa: E402
19 | # pyright: reportUnusedExpression=false
20 | # pyright: reportDuplicateImport=false
21 |
22 | # %% [markdown]
23 | # # Troubleshooting
24 | #
25 | # ## Loading takes forever?
26 |
27 | # %% tags=["hide-input"]
28 | import pandas as pd
29 |
30 | import itables
31 |
32 | df = pd.DataFrame()
33 |
34 | itables.show(df, connected=False)
35 |
36 | # %% [markdown]
37 | # If a table says "Loading..." forever, then maybe
38 | # - You forgot to run `init_notebook_mode` (like in the example above), or you deleted that cell or its output
39 | # - Or you ran `init_notebook_mode(connected=True)` but you are not connected to the internet?
40 | #
41 | # ```{tip}
42 | # If you change the value of the `connected` argument in
43 | # the `init_notebook_mode` cell, you need to re-execute all the cells
44 | # that display interactive tables.
45 | # ```
46 | #
47 | # ## Trust your notebook
48 | #
49 | # It could also be that your notebook is not _trusted_. This happens when you
50 | # have not run the notebook in full yourself (e.g. the notebook was sent to you with outputs,
51 | # or the notebook was created by a tool like `papermill`). In that case, JavaScript
52 | # code cannot run (and the interactive tables won't display)
53 | # until you tell Jupyter that you trust the notebook content
54 | # (run "Trust Notebook" in View / Activate Command Palette).
55 | #
56 | # ## Check ITables' version
57 | #
58 | # If the above does not help, please check out the [ChangeLog](changelog.md)
59 | # and decide whether you should upgrade `itables`. You can tell the version
60 | # of ITables that you are using by looking at the loading message (from ITables v2.0.1 on)
61 | # or by running this code snippet:
62 |
63 | # %%
64 | import itables
65 |
66 | itables.__version__
67 |
--------------------------------------------------------------------------------
/docs/references.md:
--------------------------------------------------------------------------------
1 | # References
2 |
3 | ITables is a wrapper for the [datatables-net](https://datatables.net) Javascript library, for Python. That library is developped by [SpryMedia](https://sprymedia.co.uk/) and made available under a MIT license. It has an extensive [documentation](https://datatables.net/manual/), as well as a large set of [examples](https://datatables.net/examples/index).
4 |
5 | ## ITables and its alternatives
6 |
7 | ### Jupyter Widgets
8 |
9 | In Jupyter or VS Code, ITables can render your dataframes using either HTML (with either `init_notebook_mode()`, or `init_notebook_mode(all_interactive=False)` and `show`) or the [`ITable` widget](apps/widget.md).
10 |
11 | Other Jupyter widgets that let you render a dataframe in Jupyter are
12 | - [QGrid](https://github.com/quantopian/qgrid) by Quantopian
13 | - [IPyaggrid](https://dgothrek.gitlab.io/ipyaggrid/) by Louis Raison and Olivier Borderies
14 | - [IPySheet](https://github.com/QuantStack/ipysheet) by QuantStack.
15 |
16 | ### Dash component
17 |
18 | Since ITables v2.3.0 you can use our [`ITable` component](apps/dash.md) in Dash applications.
19 |
20 | Alternatives for rendering DataFrames in Dash are
21 | - [Dash DataTable](https://dash.plotly.com/datatable)
22 | - [Dash AG Grid](https://dash.plotly.com/dash-ag-grid).
23 |
24 | ### Streamlit
25 |
26 | In ITables v2.1.0 we added the [`interactive_table` component](apps/streamlit.md) that can be used in Streamlit applications.
27 |
28 | Alternative for rendering DataFrames in Streamlit are
29 | - [`st.dataframe`](https://docs.streamlit.io/develop/api-reference/data/st.dataframe)
30 | - [`streamlit-aggrid`](https://github.com/PablocFonseca/streamlit-aggrid).
31 |
32 | ### DT in R
33 |
34 | The R package [DT](https://rstudio.github.io/DT/) is a wrapper for [DataTables](https://datatables.net/) that you can use both in R notebooks and R Shiny applications.
35 |
36 | ### D-Tale
37 |
38 | [D-Tale](https://github.com/man-group/dtale) lets you explore your Python DataFrames in the browser, using a local server.
39 |
--------------------------------------------------------------------------------
/docs/show_df.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwouts/itables/8fa250a684a6433314d2a984ce375bc9d8b07429/docs/show_df.png
--------------------------------------------------------------------------------
/docs/supported_editors.md:
--------------------------------------------------------------------------------
1 | # Supported Editors
2 |
3 | ITables has been tested in the following environments
4 |
5 | ## Jupyter Notebook
6 |
7 | Try it on [](https://mybinder.org/v2/gh/mwouts/itables/main?filepath=docs/quick_start.md)
8 |
9 | 
10 |
11 | ## Jupyter Lab
12 |
13 | Try it on [](https://mybinder.org/v2/gh/mwouts/itables/main?urlpath=lab/tree/docs/quick_start.md)
14 |
15 | 
16 |
17 | ## Jupyter NB convert
18 |
19 | The tables are still interactive when you _download_ the notebook as an HTML file, or when you execute `jupyter nbconvert --to html`.
20 |
21 | 
22 |
23 | ## Jupyter Book
24 |
25 | The tables are interactive in interactive books powered by [Jupyter Book](https://jupyterbook.org), see e.g. the [ITables documentation](https://mwouts.github.io/itables/).
26 |
27 | ## Google Colab
28 |
29 | A short sample notebook is available [here](https://colab.research.google.com/drive/1JPZIasTiH3rIUysDr3eWDz4jgTTq00aq?usp=sharing)
30 |
31 | 
32 |
33 | ## VS Code
34 |
35 | In VS Code, `itables` works both for Jupyter Notebooks and Python scripts.
36 |
37 | 
38 |
39 | ## PyCharm
40 |
41 | In PyCharm we recommend to call `init_notebook_mode` with the `connected=True` argument,
42 | because otherwise the notebooks do not display the interactive tables when they are reloaded.
43 |
44 | 
45 |
46 | ## Quarto
47 |
48 | ITables works well with Quarto - check out our `html` and `revealjs` [examples](apps/quarto.md).
49 |
50 |
51 |
--------------------------------------------------------------------------------
/docs/troubleshooting.md:
--------------------------------------------------------------------------------
1 | ---
2 | jupytext:
3 | formats: docs///md:myst,docs/py///py:percent
4 | notebook_metadata_filter: -jupytext.text_representation.jupytext_version
5 | text_representation:
6 | extension: .md
7 | format_name: myst
8 | format_version: 0.13
9 | kernelspec:
10 | display_name: itables
11 | language: python
12 | name: itables
13 | ---
14 |
15 | ```{code-cell} ipython3
16 | :tags: [remove-cell]
17 |
18 | # ruff: noqa: E402
19 | # pyright: reportUnusedExpression=false
20 | # pyright: reportDuplicateImport=false
21 | ```
22 |
23 | # Troubleshooting
24 |
25 | ## Loading takes forever?
26 |
27 | ```{code-cell} ipython3
28 | :tags: [hide-input]
29 |
30 | import pandas as pd
31 |
32 | import itables
33 |
34 | df = pd.DataFrame()
35 |
36 | itables.show(df, connected=False)
37 | ```
38 |
39 | If a table says "Loading..." forever, then maybe
40 | - You forgot to run `init_notebook_mode` (like in the example above), or you deleted that cell or its output
41 | - Or you ran `init_notebook_mode(connected=True)` but you are not connected to the internet?
42 |
43 | ```{tip}
44 | If you change the value of the `connected` argument in
45 | the `init_notebook_mode` cell, you need to re-execute all the cells
46 | that display interactive tables.
47 | ```
48 |
49 | ## Trust your notebook
50 |
51 | It could also be that your notebook is not _trusted_. This happens when you
52 | have not run the notebook in full yourself (e.g. the notebook was sent to you with outputs,
53 | or the notebook was created by a tool like `papermill`). In that case, JavaScript
54 | code cannot run (and the interactive tables won't display)
55 | until you tell Jupyter that you trust the notebook content
56 | (run "Trust Notebook" in View / Activate Command Palette).
57 |
58 | ## Check ITables' version
59 |
60 | If the above does not help, please check out the [ChangeLog](changelog.md)
61 | and decide whether you should upgrade `itables`. You can tell the version
62 | of ITables that you are using by looking at the loading message (from ITables v2.0.1 on)
63 | or by running this code snippet:
64 |
65 | ```{code-cell} ipython3
66 | import itables
67 |
68 | itables.__version__
69 | ```
70 |
--------------------------------------------------------------------------------
/environment.yml:
--------------------------------------------------------------------------------
1 | name: itables
2 | channels:
3 | - conda-forge
4 | dependencies:
5 | - nodejs
6 | - python
7 | - jupyter
8 | - jupyterlab>=4
9 | - jupyter-book
10 | - jupytext
11 | - nbconvert
12 | - ipykernel
13 | - pandas
14 | - matplotlib
15 | - polars
16 | - pyarrow
17 | - pytest
18 | - pytest-xdist
19 | - pytest-cov
20 | - pre-commit
21 | - pyright
22 | - typeguard
23 | - pip
24 | - setuptools
25 | - twine
26 | - ghp-import
27 | - anywidget
28 | - dash
29 | - marimo
30 | - shiny
31 | - shinywidgets
32 | - streamlit
33 | - hatch
34 | - pip:
35 | - world_bank_data
36 | - sphinxext-rediraffe
37 |
--------------------------------------------------------------------------------
/packages/dt_for_itables/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 2.3.2+dev
2 |
3 | - We have made sure that the ordering icons on empty headers do not reappear when ordering a column
4 | - Wrapping Javascript functions definitions within parentheses before evaluating is now done on the Javascript side.
5 |
6 | # 2.3.2 (2025-05-17)
7 |
8 | - We have fixed an issue with `selected_rows` when `filtered_row_count` was not explicitly set.
9 |
10 | # 2.3.1 (2025-05-17)
11 |
12 | - We have improved the implementation of `text_in_header_can_be_selected`.
13 | - The indirect evaluation of `JavascriptCode` and `JavascriptFunction` will not proceed when prototype pollution attempts are detected.
14 |
15 | # 2.3.0 (2025-05-11)
16 |
17 | - This version uses `datatables.net-dt=2.3.0`. The extensions have been updated, too.
18 | - We have added a new class `ITable` that processes some of the arguments on the Javascript side.
19 | - We use an indirect eval to parse the `JavascriptCode` and `JavascriptFunction` passed from Python.
20 | - The table data is now passed as JSON through `data_json`, with support for BigInts and non finite float values.
21 |
22 | # 2.2.0 (2025-03-15)
23 |
24 | - We have upgraded `datatables.net-dt==2.2.2` and `datatables.net-select-dt==3.0.0`
25 |
26 | # 2.0.13 (2024-09-22)
27 |
28 | - We have added two functions `set_selected_rows` and `get_selected_rows` to set and retrieve selected rows
29 |
30 | # 2.0.12 (2024-09-08)
31 |
32 | - We have added the datetime extension for DataTables ([#288](https://github.com/mwouts/itables/issues/288))
33 |
34 | # 2.0.11 (2024-06-19)
35 |
36 | **Added**
37 | - The default CSS contains has `overflow:auto` on `.dt-layout-table>div`. This improves the horizontal scrolling in Jupyter, and discards the need for `scrollX` in Streamlit ([#282](https://github.com/mwouts/itables/pull/282))
38 |
39 |
40 | # 2.0.10 (2024-06-08)
41 |
42 | **Added**
43 | - The default css for `dt-container` is `{max-width:100%;}`
44 | - We have included the `colvis` button in the package
45 |
46 | **Changed**
47 | - Updated `datatables.net-dt==2.0.8` and `datatables.net-select-dt==2.0.3`
48 |
49 |
50 | # 2.0.7 (2024-05-26)
51 |
52 | **Added**
53 | - Added a default export and typings
54 |
55 | **Changed**
56 | - Updated `datatables.net-dt==2.0.7` and `datatables.net-select-dt==2.0.2`
57 |
--------------------------------------------------------------------------------
/packages/dt_for_itables/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024-2025 Marc Wouts
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/dt_for_itables/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | This package is a ESM bundle of [DataTables](https://datatables.net/)
4 | and some of its extensions for [ITables](https://github.com/mwouts/itables/).
5 |
6 | # How to compile the bundle
7 |
8 | Run the following commands:
9 | ```bash
10 | npm install
11 | npm run build:js
12 | ```
13 |
14 | # How to update the dependencies
15 |
16 | Run
17 | ```bash
18 | npm update --save
19 | ```
20 | and check whether there are any outdated package with `npm outdated`.
21 |
22 | # How to publish a new version
23 |
24 | Update the dependencies, bump the version in `package.json`, and then:
25 |
26 | ```bash
27 | # Package the extension
28 | npm pack
29 |
30 | # Publish the package on npm with
31 | npm publish --access=public
32 | ```
33 |
--------------------------------------------------------------------------------
/packages/dt_for_itables/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dt_for_itables",
3 | "version": "2.3.2",
4 | "description": "DataTables bundle for itables",
5 | "main": "src/index.js",
6 | "typings": "src/index.d.js",
7 | "files": [
8 | "README.md",
9 | "CHANGELOG.md",
10 | "LICENSE",
11 | "dt_bundle.js",
12 | "dt_bundle.css",
13 | "package.json",
14 | "src/index.js",
15 | "src/index.css",
16 | "src/index.d.ts"
17 | ],
18 | "scripts": {
19 | "build:js": "esbuild src/index.js --format=esm --bundle --outfile=dt_bundle.js --minify",
20 | "install:js": "cp dt_bundle.js dt_bundle.css ../../src/itables/html",
21 | "build": "npm run build:js && npm run install:js"
22 | },
23 | "author": "Marc Wouts",
24 | "license": "MIT",
25 | "dependencies": {
26 | "datatables.net-buttons": "^3.0.1",
27 | "datatables.net-buttons-dt": "^3.2.3",
28 | "datatables.net-datetime": "^1.5.5",
29 | "datatables.net-dt": "^2.3.0",
30 | "datatables.net-fixedcolumns-dt": "^5.0.4",
31 | "datatables.net-keytable-dt": "^2.12.1",
32 | "datatables.net-plugins": "^2.3.0",
33 | "datatables.net-rowgroup-dt": "^1.5.1",
34 | "datatables.net-searchbuilder-dt": "^1.8.2",
35 | "datatables.net-searchpanes-dt": "^2.3.3",
36 | "datatables.net-select-dt": "^3.0.0",
37 | "jquery": "^3.7.1",
38 | "jszip": "^3.10.1"
39 | },
40 | "devDependencies": {
41 | "esbuild": "^0.25.3"
42 | },
43 | "homepage": "https://mwouts.github.io/itables",
44 | "repository": {
45 | "type": "git",
46 | "url": "https://github.com/mwouts/itables.git"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/packages/dt_for_itables/src/index.css:
--------------------------------------------------------------------------------
1 | div.dt-container {
2 | max-width: 100%;
3 | }
4 |
5 | div.dt-layout-table>div {
6 | overflow: auto;
7 | max-width: 100%;
8 | }
9 |
--------------------------------------------------------------------------------
/packages/dt_for_itables/src/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'dt_for_itables';
2 |
--------------------------------------------------------------------------------
/packages/itables_anywidget/README.md:
--------------------------------------------------------------------------------
1 | # itables_anywidget
2 |
3 | ## Installation
4 |
5 | ```sh
6 | pip install itables_anywidget
7 | ```
8 |
9 | ## Development installation
10 |
11 | Create a virtual environment and and install itables_anywidget in *editable* mode with the
12 | optional development dependencies:
13 |
14 | ```sh
15 | python -m venv .venv
16 | source .venv/bin/activate
17 | pip install -e ".[dev]"
18 | ```
19 |
20 | You then need to install the JavaScript dependencies and run the development server.
21 |
22 | ```sh
23 | npm install
24 | npm run dev
25 | ```
26 |
27 | Open `example.ipynb` in JupyterLab, VS Code, or your favorite editor
28 | to start developing. Changes made in `js/` will be reflected
29 | in the notebook.
30 |
--------------------------------------------------------------------------------
/packages/itables_anywidget/js/widget.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwouts/itables/8fa250a684a6433314d2a984ce375bc9d8b07429/packages/itables_anywidget/js/widget.css
--------------------------------------------------------------------------------
/packages/itables_anywidget/js/widget.ts:
--------------------------------------------------------------------------------
1 | import type { RenderContext } from "@anywidget/types";
2 |
3 | // DataTable and its css
4 | import { ITable, set_or_remove_dark_class } from 'dt_for_itables';
5 | import 'dt_for_itables/dt_bundle.css';
6 |
7 | /* Specifies attributes defined with traitlets in ../src/itables_anywidget/__init__.py */
8 | interface WidgetModel {
9 | dt_args: object;
10 | caption: string;
11 | classes: string;
12 | style: string;
13 | }
14 |
15 | function render({ model, el }: RenderContext) {
16 | set_or_remove_dark_class();
17 | let table = document.createElement("table");
18 | el.classList.add("itables_anywidget");
19 | el.appendChild(table);
20 |
21 | function update_classes() {
22 | table.className = model.get("classes");
23 | }
24 | function update_style() {
25 | table.setAttribute('style', model.get("style"));
26 | }
27 | function update_caption() {
28 | let caption_text = model.get('caption');
29 | if (caption_text) {
30 | let caption = table.createCaption();
31 | caption.textContent = caption_text;
32 | } else { table.deleteCaption() };
33 | }
34 |
35 | // Set initial values
36 | update_classes();
37 | update_style();
38 | update_caption();
39 |
40 | // Update the table when one of these change
41 | model.on("change:classes", update_classes);
42 | model.on("change:style", update_style);
43 | model.on("change:caption", update_caption);
44 |
45 | // This variable is a place holder from the
46 | // DataTable instance. This way we can re-create it
47 | // from within 'create_table'
48 | let dt = null;
49 |
50 | let setting_selected_rows_from_model = false;
51 | function set_selected_rows_from_model() {
52 | // We use this variable to avoid triggering model updates!
53 | setting_selected_rows_from_model = true;
54 | dt.selected_rows = model.get('selected_rows');
55 | setting_selected_rows_from_model = false;
56 | };
57 |
58 | function update_table() {
59 | if (dt) {
60 | dt.destroy();
61 | }
62 |
63 | let dt_args = model.get('_dt_args');
64 | dt = new ITable(table, dt_args);
65 | set_selected_rows_from_model();
66 | }
67 | update_table();
68 |
69 | model.on('change:_dt_args', () => {
70 | update_table();
71 | });
72 |
73 | model.on("change:selected_rows", set_selected_rows_from_model);
74 |
75 | function export_selected_rows() {
76 | if (setting_selected_rows_from_model)
77 | return;
78 |
79 | model.set('selected_rows', dt.selected_rows);
80 | model.save_changes();
81 | };
82 |
83 | dt.dt.on('select', function (e: any, dt: any, type: any, indexes: any) {
84 | export_selected_rows();
85 | });
86 |
87 | dt.dt.on('deselect', function (e: any, dt: any, type: any, indexes: any) {
88 | export_selected_rows();
89 | });
90 | }
91 |
92 | export default { render };
93 |
--------------------------------------------------------------------------------
/packages/itables_anywidget/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dev": "npm run build -- --sourcemap=inline --watch",
4 | "build": "esbuild js/widget.ts --minify --format=esm --bundle --outdir=../../src/itables/widget/static",
5 | "typecheck": "tsc --noEmit"
6 | },
7 | "dependencies": {
8 | "dt_for_itables": "file:../dt_for_itables"
9 | },
10 | "devDependencies": {
11 | "@anywidget/types": "^0.2.0",
12 | "esbuild": "^0.25.3",
13 | "typescript": "^5.8.3"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/itables_anywidget/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": [
3 | "js"
4 | ],
5 | "compilerOptions": {
6 | "target": "ES2020",
7 | "module": "ESNext",
8 | "lib": [
9 | "ES2020",
10 | "DOM",
11 | "DOM.Iterable"
12 | ],
13 | "skipLibCheck": true,
14 | "moduleResolution": "bundler",
15 | "allowImportingTsExtensions": true,
16 | "resolveJsonModule": true,
17 | "isolatedModules": true,
18 | "noEmit": true,
19 | "jsx": "react",
20 | "strict": true,
21 | "noUnusedLocals": true,
22 | "noUnusedParameters": true,
23 | "noFallthroughCasesInSwitch": true
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/itables_for_dash/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env", "@babel/preset-react"],
3 | "env": {
4 | "production": {
5 | "plugins": ["@babel/plugin-proposal-object-rest-spread", "styled-jsx/babel"]
6 | },
7 | "development": {
8 | "plugins": ["@babel/plugin-proposal-object-rest-spread", "styled-jsx/babel"]
9 | },
10 | "test": {
11 | "plugins": ["@babel/plugin-proposal-object-rest-spread", "styled-jsx/babel-test"]
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/packages/itables_for_dash/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "itables_for_dash",
3 | "version": "0.2.0",
4 | "description": "ITables for Dash",
5 | "main": "build/index.js",
6 | "scripts": {
7 | "build:js": "webpack --mode production",
8 | "build:backends": "dash-generate-components src/lib/components itables_for_dash -p package-info.json",
9 | "move": "mv itables_for_dash/* ../../src/itables_for_dash && rmdir itables_for_dash",
10 | "build": "npm run build:js && npm run build:backends && npm run move"
11 | },
12 | "author": "Marc Wouts ",
13 | "license": "MIT",
14 | "dependencies": {
15 | "dt_for_itables": "file:../dt_for_itables"
16 | },
17 | "devDependencies": {
18 | "@babel/core": "^7.27.1",
19 | "@babel/plugin-proposal-object-rest-spread": "^7.20.7",
20 | "@babel/preset-env": "^7.27.1",
21 | "@babel/preset-react": "^7.27.1",
22 | "@plotly/dash-component-plugins": "^1.2.3",
23 | "@plotly/webpack-dash-dynamic-import": "^1.3.0",
24 | "babel-loader": "^9.2.1",
25 | "css-loader": "^6.11.0",
26 | "prop-types": "^15.8.1",
27 | "react": "^16.14.0",
28 | "react-docgen": "^5.4.3",
29 | "style-loader": "^3.3.4",
30 | "styled-jsx": "^5.1.7",
31 | "webpack-cli": "^5.1.4"
32 | },
33 | "engines": {
34 | "node": ">=8.11.0",
35 | "npm": ">=6.1.0"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/packages/itables_for_dash/src/lib/LazyLoader.js:
--------------------------------------------------------------------------------
1 | export const ITable = React.lazy(() => import(/* webpackChunkName: "ITable" */ './fragments/ITable.react'));
2 |
--------------------------------------------------------------------------------
/packages/itables_for_dash/src/lib/components/ITable.react.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { ITable as RealComponent } from '../LazyLoader';
4 |
5 | /**
6 | * ITable is a dash component for ITables
7 | */
8 | const ITable = (props) => {
9 | return (
10 |
11 |
12 |
13 | );
14 | };
15 |
16 | ITable.defaultProps = {};
17 |
18 | ITable.propTypes = {
19 | /**
20 | * The ID used to identify this component in Dash callbacks.
21 | */
22 | id: PropTypes.string.isRequired,
23 |
24 | /**
25 | * The table caption
26 | */
27 | caption: PropTypes.string,
28 |
29 | /**
30 | * The index of the selected rows (pass select=True to allow selection)
31 | */
32 | selected_rows: PropTypes.array.isRequired,
33 |
34 | /**
35 | * The table style
36 | */
37 | style: PropTypes.object.isRequired,
38 |
39 | /**
40 | * The table classes
41 | */
42 | classes: PropTypes.string.isRequired,
43 |
44 | /**
45 | * The arguments for DataTable e.g. select, buttons, layout etc.
46 | */
47 | dt_args: PropTypes.object.isRequired,
48 |
49 | /**
50 | * Dash-assigned callback that should be called to report property changes
51 | * to Dash, to make them available for callbacks.
52 | */
53 | setProps: PropTypes.func
54 | };
55 |
56 | export default ITable;
57 | export const defaultProps = ITable.defaultProps;
58 | export const propTypes = ITable.propTypes;
59 |
--------------------------------------------------------------------------------
/packages/itables_for_dash/src/lib/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/prefer-default-export */
2 | import ITable from './components/ITable.react';
3 |
4 | export {
5 | ITable
6 | };
7 |
--------------------------------------------------------------------------------
/packages/itables_for_streamlit/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | This is the ITables frontend for [Streamlit](https://streamlit.io/)
4 |
5 | It is build automatically when the ITables Python package is build,
6 | and the result of the build is moved to `src/itables/itables_for_streamlit`,
7 | cf. the `build` script.
8 |
9 | This package does not need to be published on `npm`.
10 |
--------------------------------------------------------------------------------
/packages/itables_for_streamlit/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "itables_for_streamlit",
3 | "version": "0.2.0",
4 | "private": true,
5 | "dependencies": {
6 | "@types/jest": "^29.5.14",
7 | "@types/node": "^20.17.32",
8 | "dt_for_itables": "file:../dt_for_itables",
9 | "react-scripts": "^5.0.1",
10 | "streamlit-component-lib": "^2.0.0",
11 | "typescript": "^4.9.5"
12 | },
13 | "overrides": {
14 | "nth-check": "2.1.1",
15 | "postcss": ">=8.4.31"
16 | },
17 | "scripts": {
18 | "start": "react-scripts start",
19 | "build_only": "react-scripts build",
20 | "build": "react-scripts build && rm -rf ../../src/itables/itables_for_streamlit && mv build ../../src/itables/itables_for_streamlit",
21 | "test": "react-scripts test",
22 | "eject": "react-scripts eject"
23 | },
24 | "eslintConfig": {
25 | "extends": "react-app"
26 | },
27 | "devDependencies": {
28 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11"
29 | },
30 | "homepage": ".",
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/packages/itables_for_streamlit/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ITables
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/packages/itables_for_streamlit/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { Streamlit, RenderData } from "streamlit-component-lib"
2 |
3 | import {ITable, set_or_remove_dark_class} from "dt_for_itables"
4 | import "dt_for_itables/dt_bundle.css"
5 |
6 | set_or_remove_dark_class();
7 | // Create a table element
8 | const span = document.body.appendChild(document.createElement("span"))
9 | const table = span.appendChild(document.createElement("table"))
10 | let dt = new ITable(table, {})
11 |
12 | function onRender(event: Event): void {
13 | // dt_args is the whole map of arguments passed on the Python side
14 | var other_args = (event as CustomEvent).detail.args.other_args;
15 | const dt_args = (event as CustomEvent).detail.args.dt_args;
16 |
17 | // As we can't pass the dt_args other than in the
18 | // DataTable constructor, we call
19 | // destroy and then re-create the table
20 | dt.destroy()
21 |
22 | // Set the class and style here otherwise the width
23 | // might become a fixed width
24 | table.setAttribute('class', other_args.classes)
25 | table.setAttribute('style', other_args.style)
26 |
27 | dt = new ITable(table, dt_args)
28 | if (other_args.caption) {
29 | dt.dt.caption(other_args.caption)
30 | }
31 |
32 | dt.selected_rows = other_args.selected_rows;
33 |
34 | function export_selected_rows() {
35 | Streamlit.setComponentValue({ selected_rows: dt.selected_rows });
36 | };
37 |
38 | dt.dt.on('select', function (e: any, dt: any, type: any, indexes: any) {
39 | export_selected_rows();
40 | });
41 |
42 | dt.dt.on('deselect', function (e: any, dt: any, type: any, indexes: any) {
43 | export_selected_rows();
44 | });
45 |
46 | // we recalculate the height
47 | Streamlit.setFrameHeight()
48 | }
49 |
50 | // connect the render function to Streamlit
51 | Streamlit.events.addEventListener(Streamlit.RENDER_EVENT, onRender)
52 | Streamlit.setComponentReady()
53 |
--------------------------------------------------------------------------------
/packages/itables_for_streamlit/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "esModuleInterop": true,
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "noEmit": true,
16 | "jsx": "react"
17 | },
18 | "include": ["src"]
19 | }
20 |
--------------------------------------------------------------------------------
/packages/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "build_all_itables_dependencies",
3 | "lockfileVersion": 3,
4 | "requires": true,
5 | "packages": {
6 | "": {
7 | "name": "build_all_itables_dependencies"
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "build_all_itables_dependencies",
3 | "description": "A script to build and install the npm packages used by ITables",
4 | "scripts": {
5 | "build:dt_for_itables": "cd dt_for_itables && npm install && npm run build",
6 | "build:itables_anywidget": "cd itables_anywidget && npm install && npm run build",
7 | "build:itables_for_dash": "cd itables_for_dash && npm install && npm run build",
8 | "build:itables_for_streamlit": "cd itables_for_streamlit && npm install && npm run build",
9 | "build": "npm run build:dt_for_itables && npm run build:itables_anywidget && npm run build:itables_for_dash && npm run build:itables_for_streamlit"
10 | },
11 | "author": "Marc Wouts"
12 | }
13 |
--------------------------------------------------------------------------------
/src/itables/__init__.py:
--------------------------------------------------------------------------------
1 | from itables import downsample, options, sample_dfs
2 |
3 | from .javascript import init_notebook_mode, show, to_html_datatable
4 | from .typing import DataFrameOrSeries, ITableOptions, JavascriptCode, JavascriptFunction
5 | from .version import __version__
6 |
7 | __all__ = [
8 | "__version__",
9 | "to_html_datatable",
10 | "show",
11 | "init_notebook_mode",
12 | "JavascriptCode",
13 | "JavascriptFunction",
14 | "DataFrameOrSeries",
15 | "ITableOptions",
16 | "options",
17 | "downsample",
18 | "sample_dfs",
19 | ]
20 |
--------------------------------------------------------------------------------
/src/itables/dash.py:
--------------------------------------------------------------------------------
1 | from itables import __version__
2 | from itables_for_dash import (
3 | ITABLE_PROPERTIES,
4 | ITable,
5 | ITableOutputs,
6 | updated_itable_outputs,
7 | )
8 |
9 | __all__ = [
10 | "ITable",
11 | "ITABLE_PROPERTIES",
12 | "ITableOutputs",
13 | "updated_itable_outputs",
14 | "__version__",
15 | ]
16 |
--------------------------------------------------------------------------------
/src/itables/html/datatables_template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
--------------------------------------------------------------------------------
/src/itables/html/init_datatables.html:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/itables/interactive.py:
--------------------------------------------------------------------------------
1 | """Activate the representation of Pandas dataframes as interactive tables"""
2 |
3 | from .javascript import init_notebook_mode
4 |
5 | init_notebook_mode()
6 |
--------------------------------------------------------------------------------
/src/itables/logo/loading.svg:
--------------------------------------------------------------------------------
1 |
90 |
--------------------------------------------------------------------------------
/src/itables/logo/logo.svg:
--------------------------------------------------------------------------------
1 |
42 |
--------------------------------------------------------------------------------
/src/itables/logo/text.svg:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/src/itables/logo/wide.svg:
--------------------------------------------------------------------------------
1 |
51 |
--------------------------------------------------------------------------------
/src/itables/logo/with_text.svg:
--------------------------------------------------------------------------------
1 |
50 |
--------------------------------------------------------------------------------
/src/itables/py.typed:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwouts/itables/8fa250a684a6433314d2a984ce375bc9d8b07429/src/itables/py.typed
--------------------------------------------------------------------------------
/src/itables/shiny.py:
--------------------------------------------------------------------------------
1 | from typing_extensions import Optional, Unpack
2 |
3 | import itables.options as opt
4 |
5 | from .javascript import (
6 | generate_init_offline_itables_html,
7 | to_html_datatable,
8 | )
9 | from .typing import ITableOptions
10 | from .utils import read_package_file
11 |
12 | _CONNECTED = True
13 |
14 |
15 | def init_itables(
16 | connected=False,
17 | dt_bundle=None,
18 | ):
19 | """Load the DataTables library and the corresponding css (if connected=False).
20 |
21 | Warning: make sure you keep the output of this cell when 'connected=False',
22 | otherwise the interactive tables will stop working.
23 | """
24 | if dt_bundle is None:
25 | dt_bundle = opt.dt_bundle
26 | global _CONNECTED
27 | _CONNECTED = connected
28 |
29 | html = read_package_file("html/init_datatables.html")
30 |
31 | if not connected:
32 | html = html + "\n" + generate_init_offline_itables_html(dt_bundle)
33 |
34 | return html
35 |
36 |
37 | def DT(df, caption: Optional[str] = None, **kwargs: Unpack[ITableOptions]):
38 | """This is a version of 'to_html_datatable' that works in Shiny applications."""
39 | kwargs["connected"] = kwargs.get("connected", _CONNECTED)
40 | html = to_html_datatable(
41 | df,
42 | caption,
43 | **kwargs,
44 | )
45 |
46 | html = html.replace("init_notebook_mode
", "init_itables
")
47 |
48 | if "table_id" not in kwargs:
49 | return html
50 |
51 | script_end = "\n });\n\n"
52 | assert html.endswith(script_end)
53 | assert "let dt = new ITable" in html
54 |
55 | selected_rows_code = f"""
56 | function set_selected_rows_in_shiny(...args) {{
57 | Shiny.setInputValue('{kwargs["table_id"]}_selected_rows', dt.selected_rows);
58 | }};
59 |
60 | set_selected_rows_in_shiny();
61 | dt.on('select', set_selected_rows_in_shiny);
62 | dt.on('deselect', set_selected_rows_in_shiny);"""
63 |
64 | return html.removesuffix(script_end) + selected_rows_code + script_end
65 |
--------------------------------------------------------------------------------
/src/itables/streamlit.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | import streamlit.components.v1 as components
4 | from typing_extensions import Unpack
5 |
6 | from .javascript import get_itables_extension_arguments
7 | from .typing import DataFrameOrSeries, ITableOptions
8 | from .utils import find_package_file
9 |
10 | _streamlit_component_func = components.declare_component(
11 | "itables_for_streamlit", path=find_package_file("itables_for_streamlit")
12 | )
13 |
14 |
15 | def interactive_table(
16 | df: Optional[DataFrameOrSeries],
17 | key: Optional[str] = None,
18 | caption: Optional[str] = None,
19 | **kwargs: Unpack[ITableOptions],
20 | ):
21 | """Render the DataFrame as an interactive datatable in Streamlit applications"""
22 | dt_args, other_args = get_itables_extension_arguments(df, caption, **kwargs)
23 | return _streamlit_component_func(key=key, dt_args=dt_args, other_args=other_args)
24 |
--------------------------------------------------------------------------------
/src/itables/utils.py:
--------------------------------------------------------------------------------
1 | from io import open
2 | from pathlib import Path
3 |
4 | UNPKG_DT_BUNDLE_URL = "https://www.unpkg.com/dt_for_itables@2.3.2/dt_bundle.js"
5 | UNPKG_DT_BUNDLE_CSS = UNPKG_DT_BUNDLE_URL.replace(".js", ".css")
6 | UNPKG_DT_BUNDLE_URL_NO_VERSION = "https://www.unpkg.com/dt_for_itables/dt_bundle.js"
7 | UNPKG_DT_BUNDLE_CSS_NO_VERSION = "https://www.unpkg.com/dt_for_itables/dt_bundle.css"
8 |
9 |
10 | def find_package_file(*path):
11 | """Return the full path to a file from the itables package"""
12 | current_path = Path(__file__).parent
13 | return Path(current_path, *path)
14 |
15 |
16 | def read_package_file(*path):
17 | """Return the content of a file from the itables package"""
18 | with open(find_package_file(*path), encoding="utf-8") as fp:
19 | return fp.read()
20 |
--------------------------------------------------------------------------------
/src/itables/version.py:
--------------------------------------------------------------------------------
1 | """ITables' version number"""
2 |
3 | __version__ = "2.4.1-dev"
4 |
--------------------------------------------------------------------------------
/src/itables_for_dash/ITable.py:
--------------------------------------------------------------------------------
1 | # AUTO GENERATED FILE - DO NOT EDIT
2 |
3 | import typing # noqa: F401
4 | import numbers # noqa: F401
5 | from typing_extensions import TypedDict, NotRequired, Literal # noqa: F401
6 | from dash.development.base_component import Component, _explicitize_args
7 | try:
8 | from dash.development.base_component import ComponentType # noqa: F401
9 | except ImportError:
10 | ComponentType = typing.TypeVar("ComponentType", bound=Component)
11 |
12 |
13 | class ITable(Component):
14 | """An ITable component.
15 | ITable is a dash component for ITables
16 |
17 | Keyword arguments:
18 |
19 | - id (string; required):
20 | The ID used to identify this component in Dash callbacks.
21 |
22 | - caption (string; optional):
23 | The table caption.
24 |
25 | - classes (string; required):
26 | The table classes.
27 |
28 | - dt_args (dict; required):
29 | The arguments for DataTable e.g. select, buttons, layout etc.
30 |
31 | - selected_rows (list; required):
32 | The index of the selected rows (pass select=True to allow
33 | selection)."""
34 | _children_props = []
35 | _base_nodes = ['children']
36 | _namespace = 'itables_for_dash'
37 | _type = 'ITable'
38 |
39 | @_explicitize_args
40 | def __init__(
41 | self,
42 | id: typing.Optional[typing.Union[str, dict]] = None,
43 | caption: typing.Optional[str] = None,
44 | selected_rows: typing.Optional[typing.Sequence] = None,
45 | style: typing.Optional[typing.Any] = None,
46 | classes: typing.Optional[str] = None,
47 | dt_args: typing.Optional[dict] = None,
48 | **kwargs
49 | ):
50 | self._prop_names = ['id', 'caption', 'classes', 'dt_args', 'selected_rows', 'style']
51 | self._valid_wildcard_attributes = []
52 | self.available_properties = ['id', 'caption', 'classes', 'dt_args', 'selected_rows', 'style']
53 | self.available_wildcard_properties = []
54 | _explicit_args = kwargs.pop('_explicit_args')
55 | _locals = locals()
56 | _locals.update(kwargs) # For wildcard attrs and excess named props
57 | args = {k: _locals[k] for k in _explicit_args}
58 |
59 | for k in ['id', 'classes', 'dt_args', 'selected_rows', 'style']:
60 | if k not in args:
61 | raise TypeError(
62 | 'Required argument `' + k + '` was not specified.')
63 |
64 | super(ITable, self).__init__(**args)
65 |
--------------------------------------------------------------------------------
/src/itables_for_dash/__init__.py:
--------------------------------------------------------------------------------
1 | from typing_extensions import Optional, Unpack
2 |
3 | from itables import __version__
4 | from itables.typing import ITableOptions
5 |
6 | from .ITable import ITable as ITableComponent
7 | from .properties import (
8 | ITABLE_PROPERTIES,
9 | ITableOutputs,
10 | get_itable_component_kwargs,
11 | updated_itable_outputs,
12 | )
13 |
14 | _js_dist = [
15 | {"namespace": "itables_for_dash", "relative_package_path": name, **kwargs}
16 | for name, kwargs in {
17 | "async-ITable.js": {"async": True},
18 | "async-ITable.js.map": {"dynamic": True},
19 | "itables_for_dash.min.js": {},
20 | "itables_for_dash.min.js.map": {"dynamic": True},
21 | }.items()
22 | ]
23 |
24 | _css_dist = []
25 |
26 |
27 | ITableComponent._js_dist = _js_dist # type: ignore
28 | ITableComponent._css_dist = _css_dist # type: ignore
29 |
30 |
31 | class ITable(ITableComponent):
32 | """An ITable component for Dash"""
33 |
34 | def __init__(
35 | self,
36 | id,
37 | df=None,
38 | caption: Optional[str] = None,
39 | **kwargs: Unpack[ITableOptions],
40 | ):
41 | """
42 | Initialize the ITable component.
43 |
44 | Parameters
45 | ----------
46 | id : str
47 | The ID of the component.
48 | **kwargs : dict
49 | Additional keyword arguments for the component.
50 | """
51 | if not isinstance(id, str):
52 | raise ValueError("The id must be a string.")
53 | if not id:
54 | raise ValueError("The id cannot be an empty string.")
55 |
56 | return super().__init__(
57 | id=id, **get_itable_component_kwargs(df, caption, **kwargs)
58 | )
59 |
60 |
61 | __all__ = [
62 | "ITable",
63 | "ITABLE_PROPERTIES",
64 | "ITableOutputs",
65 | "updated_itable_outputs",
66 | "__version__",
67 | ]
68 |
--------------------------------------------------------------------------------
/src/itables_for_dash/properties.py:
--------------------------------------------------------------------------------
1 | try:
2 | from dash import Output, no_update # type: ignore
3 | except ImportError as e:
4 | import_error = e
5 |
6 | def Output(*args, **kwargs):
7 | raise import_error
8 |
9 | def no_update(*args, **kwargs):
10 | raise import_error
11 |
12 |
13 | from typing import Optional
14 |
15 | from typing_extensions import Unpack
16 |
17 | from itables.javascript import (
18 | get_expanded_style,
19 | get_itables_extension_arguments,
20 | )
21 | from itables.typing import DataFrameOrSeries, DataTableOptions, ITableOptions
22 |
23 | ITABLE_PROPERTIES = (
24 | "caption",
25 | "style",
26 | "classes",
27 | "dt_args",
28 | "selected_rows",
29 | )
30 |
31 |
32 | def get_itable_component_kwargs(
33 | df: Optional[DataFrameOrSeries] = None,
34 | caption: Optional[str] = None,
35 | **kwargs: Unpack[ITableOptions],
36 | ):
37 | dt_args, other_args = get_itables_extension_arguments(df, caption, **kwargs)
38 |
39 | style = get_expanded_style(other_args.pop("style"))
40 | for key in style:
41 | # transform caption-side to captionSide
42 | words = key.split("-")
43 | if len(words) > 1:
44 | new_key = words[0] + "".join(word.capitalize() for word in words[1:])
45 | style[new_key] = style.pop(key)
46 |
47 | return {
48 | "dt_args": dt_args,
49 | "style": style,
50 | **other_args,
51 | }
52 |
53 |
54 | def ITableOutputs(id):
55 | return [Output(id, key) for key in ITABLE_PROPERTIES]
56 |
57 |
58 | def updated_itable_outputs(
59 | df: Optional[DataFrameOrSeries] = None,
60 | caption: Optional[str] = None,
61 | current_dt_args: Optional[DataTableOptions] = None,
62 | **kwargs: Unpack[ITableOptions],
63 | ):
64 | updated_properties = get_itable_component_kwargs(df, caption, **kwargs)
65 |
66 | if current_dt_args is not None:
67 | if df is None:
68 | for k in {
69 | "columns",
70 | "data_json",
71 | "filtered_row_count",
72 | "downsampling_warning",
73 | }:
74 | if k in current_dt_args:
75 | updated_properties["dt_args"][k] = current_dt_args[k]
76 |
77 | if current_dt_args == updated_properties["dt_args"]:
78 | updated_properties["dt_args"] = no_update
79 |
80 | ordered_list_of_updated_properties = [
81 | updated_properties.pop(k) for k in ITABLE_PROPERTIES
82 | ]
83 |
84 | if updated_properties:
85 | raise ValueError(f"Unexpected properties: {updated_properties}")
86 |
87 | return ordered_list_of_updated_properties
88 |
--------------------------------------------------------------------------------
/src/itables_for_dash/py.typed:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwouts/itables/8fa250a684a6433314d2a984ce375bc9d8b07429/src/itables_for_dash/py.typed
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mwouts/itables/8fa250a684a6433314d2a984ce375bc9d8b07429/tests/__init__.py
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import pytest
3 |
4 | from itables.sample_dfs import PANDAS_VERSION_MAJOR, get_dict_of_test_dfs
5 |
6 |
7 | @pytest.fixture(params=list(get_dict_of_test_dfs()))
8 | def df(request):
9 | name = request.param
10 | df = get_dict_of_test_dfs()[name]
11 | assert isinstance(df, pd.DataFrame)
12 | return df
13 |
14 |
15 | @pytest.fixture(params=["None", "1D-array", "2D-array"])
16 | def lengthMenu(request):
17 | if request.param == "1D-array":
18 | return [2, 5, 10, 20, 50]
19 | if request.param == "2D-array":
20 | return [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]]
21 | return None
22 |
23 |
24 | @pytest.fixture(params=[False, True])
25 | def connected(request):
26 | return request.param
27 |
28 |
29 | @pytest.fixture(params=[False, True] if PANDAS_VERSION_MAJOR >= 1 else [False])
30 | def use_to_html(request):
31 | return request.param
32 |
--------------------------------------------------------------------------------
/tests/test_changelog.py:
--------------------------------------------------------------------------------
1 | import re
2 | from pathlib import Path
3 |
4 | import pytest
5 |
6 |
7 | def replace_issue_number_with_links(text):
8 | return re.sub(
9 | r"([^\[])#([0-9]+)",
10 | r"\1[#\2](https://github.com/mwouts/itables/issues/\2)",
11 | text,
12 | )
13 |
14 |
15 | @pytest.mark.parametrize(
16 | "input,output",
17 | [
18 | (
19 | "Issue #535",
20 | "Issue [#535](https://github.com/mwouts/itables/issues/535)",
21 | ),
22 | (
23 | "Multiline\ntext (#123)",
24 | "Multiline\ntext ([#123](https://github.com/mwouts/itables/issues/123))",
25 | ),
26 | ],
27 | )
28 | def test_replace_issue_numbers_with_links(input, output):
29 | assert replace_issue_number_with_links(input) == output
30 |
31 |
32 | def test_update_changelog():
33 | changelog_file = Path(__file__).parent.parent / "docs" / "changelog.md"
34 | cur_text = changelog_file.read_text()
35 | new_text = replace_issue_number_with_links(cur_text)
36 | if cur_text != new_text:
37 | changelog_file.write_text(new_text) # pragma: no cover
38 |
--------------------------------------------------------------------------------
/tests/test_connected_notebook_is_small.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from jupytext.cli import jupytext
3 |
4 |
5 | def text_notebook(connected, display_logo_when_loading=True):
6 | return f"""# %%
7 | import itables
8 |
9 | itables.options.display_logo_when_loading = {display_logo_when_loading}
10 | itables.init_notebook_mode(connected={connected})
11 |
12 | # %%
13 | import pandas as pd
14 | pd.DataFrame()
15 | """
16 |
17 |
18 | @pytest.mark.parametrize("display_logo_when_loading", [True, False])
19 | def test_connected_notebook_is_small(tmp_path, display_logo_when_loading):
20 | nb_py = tmp_path / "nb.py"
21 | nb_ipynb = tmp_path / "nb.ipynb"
22 | nb_py.write_text(
23 | text_notebook(
24 | connected=True, display_logo_when_loading=display_logo_when_loading
25 | )
26 | )
27 | jupytext([str(nb_py), "--to", "ipynb", "--set-kernel", "itables", "--execute"])
28 | assert nb_ipynb.exists()
29 | if nb_ipynb.stat().st_size < (9000 if display_logo_when_loading else 5000):
30 | return
31 | raise AssertionError(
32 | f"Notebook size is too large: {nb_ipynb.stat().st_size} bytes:\n"
33 | f"{nb_ipynb.read_text()}"
34 | )
35 |
36 |
37 | def test_offline_notebook_is_not_too_large(tmp_path):
38 | nb_py = tmp_path / "nb.py"
39 | nb_ipynb = tmp_path / "nb.ipynb"
40 | nb_py.write_text(text_notebook(connected=False))
41 | jupytext([str(nb_py), "--to", "ipynb", "--set-kernel", "itables", "--execute"])
42 | assert nb_ipynb.exists()
43 | assert 750000 < nb_ipynb.stat().st_size < 850000
44 |
--------------------------------------------------------------------------------
/tests/test_documentation_notebooks_run.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | import jupytext
4 | import pytest
5 |
6 | import itables.options as opt
7 | from itables import init_notebook_mode
8 | from itables.javascript import pd_style
9 |
10 | try:
11 | import polars as pl
12 | except ImportError:
13 | pl = None
14 |
15 |
16 | def list_doc_notebooks():
17 | documentation_folder = Path(__file__).parent / ".." / "docs"
18 | for path in documentation_folder.iterdir():
19 | if path.suffix == ".md":
20 | yield path
21 | if path.is_dir():
22 | if path.name == ".ipynb_checkpoints":
23 | continue
24 | for file in path.iterdir():
25 | if file.suffix == ".md":
26 | yield file
27 |
28 |
29 | @pytest.mark.parametrize(
30 | "notebook", list_doc_notebooks(), ids=lambda notebook: notebook.stem
31 | )
32 | def test_run_documentation_notebooks(notebook):
33 | if "polars" in notebook.stem and pl is None:
34 | pytest.skip("Polars is not available")
35 | if "pandas_style" in notebook.stem and pd_style is None:
36 | pytest.skip("Pandas Style is not available")
37 | if "shiny" in notebook.stem:
38 | pytest.skip("shinywidgets makes the widget.md notebook fail")
39 | if "marimo" in notebook.stem or "widget" in notebook.stem:
40 | pytest.importorskip("anywidget")
41 |
42 | org_options = dir(opt)
43 |
44 | nb = jupytext.read(notebook)
45 | py_notebook = jupytext.writes(nb, "py:percent")
46 | exec(py_notebook, {})
47 |
48 | new_options = set(dir(opt)).difference(org_options)
49 | for name in new_options:
50 | delattr(opt, name)
51 |
52 | # Revert back to the non initialized mode
53 | init_notebook_mode(all_interactive=False, connected=True)
54 |
--------------------------------------------------------------------------------
/tests/test_extension_arguments.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from itables.javascript import get_itables_extension_arguments
4 |
5 |
6 | def test_get_itables_extension_arguments(df):
7 | try:
8 | dt_args, other_args = get_itables_extension_arguments(df)
9 | except NotImplementedError as e:
10 | pytest.skip(str(e))
11 |
12 | assert set(dt_args) <= {
13 | "table_html",
14 | "data_json",
15 | "column_filters",
16 | "layout",
17 | "order",
18 | "text_in_header_can_be_selected",
19 | "filtered_row_count",
20 | "downsampling_warning",
21 | }, set(dt_args)
22 | assert isinstance(dt_args["data_json"], str) # type: ignore
23 | assert isinstance(dt_args["table_html"], str) # type: ignore
24 |
25 | assert set(other_args) <= {
26 | "classes",
27 | "style",
28 | "caption",
29 | "selected_rows",
30 | }, set(dt_args)
31 | assert isinstance(other_args["classes"], str)
32 | assert isinstance(other_args["style"], str)
33 | assert other_args["caption"] is None
34 |
--------------------------------------------------------------------------------
/tests/test_html_in_table_header.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import pytest
3 |
4 | from itables import to_html_datatable
5 |
6 |
7 | @pytest.mark.parametrize("allow_html", [True, False])
8 | def test_html_in_table_header(allow_html, df=pd.DataFrame({"B": [1]})):
9 | html = to_html_datatable(df, allow_html=allow_html)
10 | print(html)
11 | assert ("B" in html) == allow_html
12 |
--------------------------------------------------------------------------------
/tests/test_init.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 |
3 | from itables import init_notebook_mode
4 |
5 |
6 | def test_init():
7 | assert not hasattr(pd.Series, "_repr_html_")
8 |
9 | init_notebook_mode(all_interactive=True)
10 | assert hasattr(pd.Series, "_repr_html_")
11 |
12 | init_notebook_mode(all_interactive=False)
13 | assert not hasattr(pd.Series, "_repr_html_")
14 |
15 | # No pb if we do this twice
16 | init_notebook_mode(all_interactive=False)
17 | assert not hasattr(pd.Series, "_repr_html_")
18 |
--------------------------------------------------------------------------------
/tests/test_itables_for_dash.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 |
4 | import pytest
5 |
6 | try:
7 | from itables.dash import ITable
8 | except ImportError as e:
9 | pytest.skip(str(e), allow_module_level=True)
10 |
11 |
12 | def check_ressource(relative_package_path, namespace, **kwargs):
13 | ressource_file = sys.modules[namespace].__file__
14 | assert isinstance(ressource_file, str), ressource_file
15 | module_path = os.path.join(os.path.dirname(ressource_file), relative_package_path)
16 | assert os.path.exists(module_path), module_path
17 |
18 |
19 | def test_itable_component_ressources():
20 | for js in ITable._js_dist: # type: ignore
21 | check_ressource(**js)
22 | for css in ITable._css_dist: # type: ignore
23 | check_ressource(**css)
24 |
25 |
26 | def test_create_empty_table():
27 | ITable(id="test")
28 |
--------------------------------------------------------------------------------
/tests/test_keys_to_be_evaluated.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from itables.javascript import JavascriptFunction, get_keys_to_be_evaluated
4 |
5 |
6 | @pytest.fixture()
7 | def coloredColumnDefs():
8 | return [
9 | {
10 | "targets": "_all",
11 | "createdCell": JavascriptFunction(
12 | "function (td, cellData, rowData, row, col) {if (cellData<0) {$(td).css('color', 'red')}}"
13 | ),
14 | }
15 | ]
16 |
17 |
18 | def test_get_keys_to_be_evaluated(coloredColumnDefs):
19 | keys_to_be_evaluated = get_keys_to_be_evaluated({"columnDefs": coloredColumnDefs})
20 |
21 | assert keys_to_be_evaluated == [["columnDefs", 0, "createdCell"]]
22 |
--------------------------------------------------------------------------------
/tests/test_marimo.py:
--------------------------------------------------------------------------------
1 | import importlib.util
2 | from pathlib import Path
3 |
4 | import pytest
5 |
6 | MARIMO_APPS_PATH = Path(__file__).parent / ".." / "apps" / "marimo"
7 | MARIMO_APPS_PY_FILES = [
8 | file for file in MARIMO_APPS_PATH.iterdir() if file.suffix == ".py"
9 | ]
10 |
11 | pytest.importorskip("marimo")
12 |
13 |
14 | @pytest.fixture(params=MARIMO_APPS_PY_FILES)
15 | def marimo_app(request) -> str:
16 | """Return the name of an example Marimo app"""
17 | return request.param.stem
18 |
19 |
20 | def test_marimo_apps_exist():
21 | assert (
22 | len(MARIMO_APPS_PY_FILES) > 0
23 | ), "No Marimo apps found in the apps/marimo directory."
24 |
25 |
26 | def test_marimo_apps_are_valid_python_scripts(marimo_app: str):
27 | """Test that the Marimo apps are valid Python scripts."""
28 | file_path = MARIMO_APPS_PATH / f"{marimo_app}.py"
29 | spec = importlib.util.spec_from_file_location(marimo_app, file_path)
30 | assert spec is not None, f"Could not find spec for {marimo_app}"
31 | app = importlib.util.module_from_spec(spec)
32 | spec.loader.exec_module(app) # type: ignore
33 | assert app is not None
34 |
--------------------------------------------------------------------------------
/tests/test_pandas_style.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | import pandas as pd
4 | import pytest
5 |
6 | from itables import to_html_datatable
7 | from itables.javascript import get_itable_arguments
8 |
9 | pytest.importorskip("jinja2")
10 |
11 |
12 | @pytest.mark.skipif(
13 | pd.__version__.startswith("0."),
14 | reason="AttributeError: 'Styler' object has no attribute 'to_html'",
15 | )
16 | def test_buttons_are_shown_on_pd_style_objects():
17 | df = pd.DataFrame({"A": ["a"]}).style
18 | html = to_html_datatable(
19 | df,
20 | buttons=["pageLength", "copyHtml5", "csvHtml5", "excelHtml5"],
21 | allow_html=True,
22 | )
23 |
24 | # Extract the dt_args passed to datatables
25 | dt_args = ""
26 | for line in html.splitlines():
27 | line = line.strip()
28 | if line.startswith("let dt_args"):
29 | dt_args = line.split("=", 1)[1]
30 | break
31 |
32 | assert dt_args.endswith(";"), dt_args
33 | dt_args = dt_args[:-1]
34 | dt_args = json.loads(dt_args)
35 |
36 | print(dt_args)
37 | assert "dom" not in dt_args
38 | assert "buttons" in dt_args
39 | assert "buttons" in dt_args["layout"].values()
40 |
41 |
42 | def test_non_trivial_index_of_styler_objects_are_included():
43 | """
44 | When a Pandas Styler index is non trivial, it should appear
45 | in the ITable output, see issue #393
46 | """
47 | df = pd.DataFrame({"A": [1]}, index=pd.Index([0], name="index"))
48 | dt_args = get_itable_arguments(df.style, table_id="T_id", allow_html=True)
49 | assert "table_html" in dt_args
50 | table_html = dt_args["table_html"]
51 | assert "index" in table_html, table_html
52 |
53 |
54 | @pytest.mark.parametrize("showIndex", [True, "auto"])
55 | def test_trivial_indexes_of_styler_objects_are_not_included(showIndex):
56 | """
57 | When a Pandas Styler index is trivial, it should appear
58 | in the ITable output only if showIndex is True
59 | """
60 | df = pd.DataFrame({"A": [1]})
61 | dt_args = get_itable_arguments(
62 | df.style, table_id="T_id", allow_html=True, showIndex=showIndex
63 | )
64 | assert "table_html" in dt_args
65 | table_html = dt_args["table_html"]
66 | assert ('class="blank level0"' in table_html) == (showIndex is True), table_html
67 |
--------------------------------------------------------------------------------
/tests/test_polars.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from itables import to_html_datatable
4 | from itables.javascript import datatables_rows
5 | from itables.sample_dfs import (
6 | get_dict_of_polars_test_dfs,
7 | get_dict_of_polars_test_series,
8 | )
9 |
10 | try:
11 | import polars as pl # noqa
12 | except ImportError as e:
13 | pytest.skip(str(e), allow_module_level=True)
14 |
15 |
16 | @pytest.mark.parametrize(
17 | "name,x", [(name, x) for name, x in get_dict_of_polars_test_series().items()]
18 | )
19 | def test_show_polars_series(name, x):
20 | to_html_datatable(x)
21 |
22 |
23 | @pytest.mark.parametrize(
24 | "name,df", [(name, df) for name, df in get_dict_of_polars_test_dfs().items()]
25 | )
26 | def test_show_polars_df(name, df):
27 | to_html_datatable(df)
28 |
29 |
30 | def test_encode_mixed_contents():
31 | # Make sure that the bigint escape works for mixed content # 291
32 | df = pl.DataFrame(
33 | {
34 | "bigint": [1666767918216000000],
35 | "int": [1699300000000],
36 | "float": [0.9510565400123596],
37 | "neg": [-0.30901700258255005],
38 | }
39 | )
40 | assert (
41 | datatables_rows(df)
42 | == "[[1666767918216000000, 1699300000000, 0.9510565400123596, -0.30901700258255005]]"
43 | )
44 |
45 |
46 | def test_render_polars_struct():
47 | df = pl.DataFrame(
48 | {
49 | "X": ["A", "A", "B", "C", "C", "C"],
50 | }
51 | )
52 | assert (
53 | datatables_rows(df.select(pl.col("X").value_counts(sort=True)))
54 | == '[["{\\"C\\",3}"], ["{\\"A\\",2}"], ["{\\"B\\",1}"]]'
55 | )
56 |
--------------------------------------------------------------------------------
/tests/test_shiny.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 | import warnings
3 | from pathlib import Path
4 |
5 | import pandas as pd
6 | import pytest
7 |
8 | from itables.downsample import downsample
9 | from itables.shiny import DT
10 |
11 | SHINY_APPS_PATH = Path(__file__).parent / ".." / "apps" / "shiny"
12 | SHINY_APPS_PY_FILES = [
13 | f"{directory.name}/{file.stem}"
14 | for directory in SHINY_APPS_PATH.iterdir()
15 | if directory.is_dir()
16 | for file in directory.iterdir()
17 | if file.suffix == ".py"
18 | ]
19 |
20 |
21 | def test_select_on_downsampled_df():
22 | """
23 | When a DF of 17 rows is downsampled to 3 rows,
24 | we can only select rows 0, 1, 16
25 | """
26 | df = pd.DataFrame({"x": range(17)})
27 | dn, _ = downsample(df, max_rows=3)
28 | assert len(dn) == 3
29 |
30 | with warnings.catch_warnings():
31 | warnings.simplefilter("error")
32 | DT(df, maxRows=3, selected_rows=[0, 1, 16])
33 |
34 | for row in [-1, 17]:
35 | with pytest.raises(IndexError, match="Selected rows out of range"):
36 | DT(df, maxRows=3, selected_rows=[row])
37 |
38 | for row in [2, 15]:
39 | with pytest.warns(match="no row with index between 2 and 15 can be selected"):
40 | DT(df, maxRows=3, selected_rows=[row])
41 |
42 |
43 | pytest.importorskip("shiny")
44 |
45 |
46 | @pytest.fixture(params=SHINY_APPS_PY_FILES)
47 | def shiny_app(request) -> str:
48 | """Return the name of an example Shiny app"""
49 | return request.param
50 |
51 |
52 | def test_shiny_apps_exist():
53 | assert (
54 | len(SHINY_APPS_PY_FILES) > 0
55 | ), "No Shiny apps found in the apps/shiny directory."
56 |
57 |
58 | def test_shiny_apps_are_valid_python_scripts(
59 | shiny_app: str,
60 | ignore_errors=[
61 | "RuntimeError: express.ui.page_opts() can only "
62 | "be used inside of a standalone Shiny Express app"
63 | ],
64 | ):
65 | """Test that the Shiny apps are valid Python scripts.
66 |
67 | Note that we don't use importlib here, as importing
68 | shinywidgets causes an interaction with the widget tests
69 | """
70 | file_path = SHINY_APPS_PATH / f"{shiny_app}.py"
71 | result = subprocess.run(
72 | ["python", str(file_path)], capture_output=True, text=True, timeout=10
73 | )
74 | for error in ignore_errors:
75 | if error in result.stderr:
76 | pytest.xfail(error)
77 | assert result.returncode == 0, f"Process failed: {result.stderr}"
78 |
--------------------------------------------------------------------------------
/tests/test_streamlit.py:
--------------------------------------------------------------------------------
1 | import importlib.util
2 | from pathlib import Path
3 |
4 | import pytest
5 |
6 | STREAMLIT_APPS_PATH = Path(__file__).parent / ".." / "apps" / "streamlit"
7 | STREAMLIT_APPS_PY_FILES = [
8 | file for file in STREAMLIT_APPS_PATH.iterdir() if file.suffix == ".py"
9 | ]
10 |
11 | pytest.importorskip("streamlit")
12 |
13 |
14 | @pytest.fixture(params=STREAMLIT_APPS_PY_FILES)
15 | def streamlit_app(request) -> str:
16 | """Return the name of an example Streamlit app"""
17 | return request.param.stem
18 |
19 |
20 | def test_streamlit_apps_exist():
21 | assert (
22 | len(STREAMLIT_APPS_PY_FILES) > 0
23 | ), "No Streamlit apps found in the apps/streamlit directory."
24 |
25 |
26 | def test_streamlit_apps_can_be_imported(streamlit_app: str):
27 | """Test that the Streamlit apps can be imported successfully."""
28 | file_path = STREAMLIT_APPS_PATH / f"{streamlit_app}.py"
29 | spec = importlib.util.spec_from_file_location(streamlit_app, file_path)
30 | assert spec is not None, f"Could not find spec for {streamlit_app}"
31 | app = importlib.util.module_from_spec(spec)
32 | spec.loader.exec_module(app) # type: ignore
33 | assert app is not None
34 |
--------------------------------------------------------------------------------
/tests/test_typing.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | import pandas as pd
4 | import pytest
5 |
6 | import itables
7 |
8 | if not itables.typing.is_typeguard_available():
9 | pytestmark = pytest.mark.skip(reason="Typeguard is not available")
10 |
11 |
12 | @pytest.fixture
13 | def df():
14 | return pd.DataFrame(
15 | {
16 | "a": [1, 2],
17 | }
18 | )
19 |
20 |
21 | def test_warns_on_incorrect_option(df):
22 | with pytest.warns(
23 | SyntaxWarning,
24 | match="These arguments are not documented in ITableOptions: .*'lengthMenuWithTypo'",
25 | ):
26 | itables.to_html_datatable(df, lengthMenuWithTypo=[2, 5, 10]) # type: ignore
27 |
28 |
29 | def test_warns_on_incorrect_type(df):
30 | with pytest.warns(SyntaxWarning, match=re.escape("does not match")):
31 | itables.to_html_datatable(df, lengthMenu=[2.2]) # type: ignore
32 |
--------------------------------------------------------------------------------
/tests/test_update_samples.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | import pytest
4 | import world_bank_data as wb
5 |
6 | pytestmark = pytest.mark.xfail(
7 | reason="HTTPError: 502 Server Error: Bad Gateway for url: http://api.worldbank.org/v2..."
8 | )
9 |
10 | SAMPLE_DIR = Path(__file__).parent / ".." / "src" / "itables" / "samples"
11 |
12 |
13 | def create_csv_file_if_missing(df, csv_file):
14 | if not csv_file.exists():
15 | with open(str(csv_file), "w") as fp: # pragma: no cover
16 | fp.write(df.to_csv())
17 |
18 |
19 | def test_update_countries(csv_file=SAMPLE_DIR / "countries.csv"):
20 | df = wb.get_countries()
21 | create_csv_file_if_missing(df, csv_file)
22 |
23 |
24 | def test_update_population(csv_file=SAMPLE_DIR / "population.csv"):
25 | x = wb.get_series("SP.POP.TOTL", mrv=1, simplify_index=True)
26 | create_csv_file_if_missing(x, csv_file)
27 |
28 |
29 | def test_update_indicators(csv_file=SAMPLE_DIR / "indicators.csv"):
30 | df = wb.get_indicators().sort_index().head(500)
31 | create_csv_file_if_missing(df, csv_file)
32 |
--------------------------------------------------------------------------------
/tests/test_widget.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | import itables.options as opt
4 |
5 | pytest.importorskip("anywidget")
6 |
7 |
8 | def test_create_widget_with_no_df():
9 | from itables.widget import ITable
10 |
11 | itable = ITable()
12 | assert itable._df is None
13 | assert itable.caption == ""
14 | assert itable.classes == opt.classes
15 | assert itable.style == opt.style
16 | assert itable.selected_rows == []
17 | assert itable._dt_args == {
18 | "text_in_header_can_be_selected": True,
19 | "order": [],
20 | "layout": {k: None for k in opt.layout},
21 | }
22 |
23 |
24 | def test_create_widget_with_df(df):
25 | from itables.widget import ITable
26 |
27 | itable = ITable(df)
28 | assert itable.df is df
29 | assert itable.caption == ""
30 | assert itable.classes == opt.classes
31 | assert itable.style == opt.style
32 | assert itable.selected_rows == []
33 | selected_dt_args = {
34 | k: v
35 | for k, v in itable._dt_args.items()
36 | if k
37 | not in [
38 | "data_json",
39 | "table_html",
40 | "filtered_row_count",
41 | "downsampling_warning",
42 | "layout",
43 | ]
44 | }
45 | assert selected_dt_args == {
46 | "text_in_header_can_be_selected": True,
47 | "order": [],
48 | }
49 |
--------------------------------------------------------------------------------