Skip to content

Commit

Permalink
Merge pull request #101 from blackary/native-mpa
Browse files Browse the repository at this point in the history
Convert st-pages into a thin wrapper around new st.navigation
  • Loading branch information
blackary authored Jul 26, 2024
2 parents 1afb267 + 282b665 commit fd33fdb
Show file tree
Hide file tree
Showing 26 changed files with 1,183 additions and 1,706 deletions.
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ exclude =
.env
.direnv
streamlit_patches.py
per-file-ignores =
per-file-ignores =
13 changes: 0 additions & 13 deletions .github/workflows/linting.yml

This file was deleted.

4 changes: 2 additions & 2 deletions .github/workflows/pypi_action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Build and publish to pypi
uses: JRubics/poetry-publish@v1.13
uses: JRubics/poetry-publish@v2.0
with:
pypi_token: ${{ secrets.PYPI_TOKEN }}
12 changes: 6 additions & 6 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10"]
python-version: ["3.9", "3.11"]

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- uses: abatilo/actions-poetry@v2
- uses: abatilo/actions-poetry@v3
with:
poetry-version: "1.5.1"
- name: Install dependencies
Expand All @@ -26,10 +26,10 @@ jobs:
python -m pip install tox tox-gh-actions pytest-playwright pytest-rerunfailures
- name: Install playwright dependencies
run: |
playwright install --with-deps
playwright install chromium --with-deps
- name: Test with tox
run: tox
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: failure()
with:
name: screenshots
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,8 @@ __pycache__

.streamlit/secrets.toml

# Ignore all pngs except logo.png, so that screenshots are ignored by git
*.png
.venv
!logo.png

.venv
43 changes: 23 additions & 20 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
---
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.3.3
hooks:
- id: ruff
args:
- --fix
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.9.0
hooks:
- id: mypy
args:
- --ignore-missing-imports
- --follow-imports=silent
additional_dependencies:
- types-all
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0 # Use the ref you want to point at
hooks:
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.4.9"
hooks:
- id: ruff
args:
- --fix
- id: ruff-format
args:
- --config=pyproject.toml

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.10.0
hooks:
- id: mypy
language_version: python3.9
additional_dependencies:
- types-all

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: end-of-file-fixer
120 changes: 34 additions & 86 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
[![Releases](https://img.shields.io/pypi/v/st-pages)](https://pypi.org/project/st-pages/)
[![Build Status](https://img.shields.io/github/actions/workflow/status/blackary/st_pages/testing.yml?branch=main)](https://github.com/blackary/st_pages/actions?query=workflow%3A%22testing%22+branch%3Amain)
![Python Versions](https://img.shields.io/pypi/pyversions/st_pages.svg)
![Streamlit versions](https://img.shields.io/badge/streamlit-1.21.0--1.24.0-white.svg)
![Streamlit versions](https://img.shields.io/badge/streamlit-1.36.0-white.svg)
![License](https://img.shields.io/github/license/blackary/st_pages)
[![Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)

[![Streamlit App](https://static.streamlit.io/badges/streamlit_badge_black_white.svg)](https://st-pages.streamlit.app)

Expand All @@ -27,91 +27,43 @@ Example with sections: https://st-pages-sections.streamlit.app/

## Why st-pages?

> Summary: st-pages allows you to set the page names, order, and icons (and optionally
> group the pages into sections) in a multipage Streamlit app from your code without
> having to rename the files.
Previously, st-pages allowed for a much more customizable and flexible declaration of
pages in a Streamlit app, and was independent of the actual filenames of the python
files in your project.

![image](https://user-images.githubusercontent.com/4040678/204576356-a436713f-93e4-41e3-82b9-6efeff744355.png)
As of 1.0.0, st-pages is now a tiny wrapper that provides an easy method for defining
the pages in your app in a toml file, as well as a few utility methods to let you
add the current page's title to all pages, etc.

Streamlit has native support for [multi-page apps](https://blog.streamlit.io/introducing-multipage-apps/)
where page filenames are the source of truth for page settings. But, it's a bit annoying
to have to change the filename to change the names in the sidebar or reorder the pages
in your app. Even more, I really dislike having to put emojis in filenames.

This is an experimental package to try out how page-management might work if
you could name the pages whatever you wanted, and could manage which pages are visible,
and how they appear in the sidebar, via a setup function.

This enables you to set page _name_, _icon_ and _order_ independently of file name/path,
while still retaining the same sidebar & url behavior of current streamlit multi-page
apps.
You are welcome to continue to use older versions of this package, but most of the
old use-cases are now easy to do with native streamlit, so I would recommend
checking out the [documentation](https://docs.streamlit.io/develop/concepts/multipage-apps/page-and-navigation)
for more information.

## How to use

### Method one: declare pages inside your streamlit code

```python
from st_pages import Page, show_pages, add_page_title

# Optional -- adds the title and icon to the current page
add_page_title()

# Specify what pages should be shown in the sidebar, and what their titles and icons
# should be
show_pages(
[
Page("streamlit_app.py", "Home", "🏠"),
Page("other_pages/page2.py", "Page 2", ":books:"),
]
)
```

If you want to organize your pages into sections with indention showing which pages
belong to which section, you can do the following:

```python
from st_pages import Page, Section, show_pages, add_page_title

# Either this or add_indentation() MUST be called on each page in your
# app to add indendation in the sidebar
add_page_title()

# Specify what pages should be shown in the sidebar, and what their titles and icons
# should be
show_pages(
[
Page("streamlit_app.py", "Home", "🏠"),
Page("other_pages/page2.py", "Page 2", ":books:"),
Section("My section", icon="🎈️"),
# Pages after a section will be indented
Page("Another page", icon="💪"),
# Unless you explicitly say in_section=False
Page("Not in a section", in_section=False)
]
)
```

### Method two: declare pages inside of a config file
### Declare pages inside of a toml file

Contents of `.streamlit/pages.toml`

```toml
[[pages]]
path = "streamlit_app.py"
path = "page1.py"
name = "Home"
icon = "🏠"

[[pages]]
path = "other_pages/page2.py"
name = "Page 2"
icon = ":books:"
url_path = "my_books" # You can override the default url path for a page
```

Example with sections:

```toml
[[pages]]
path = "streamlit_app.py"
path = "page1.py"
name = "Home"
icon = "🏠"

Expand All @@ -121,49 +73,45 @@ name = "Page 2"
icon = ":books:"

[[pages]]
name = "My second"
name = "My section"
icon = "🎈️"
is_section = true

# Pages after an `is_section = true` will be indented
[[pages]]
name = "Another page"
icon = "💪"

# Unless you explicitly say in_section = false`
[[pages]]
name = "Not in a section"
in_section = false
```

Streamlit code:

```python
from st_pages import show_pages_from_config, add_page_title
import streamlit as st
from st_pages import add_page_title, get_nav_from_toml

st.set_page_config(layout="wide")

nav = get_nav_from_toml(".streamlit/pages_sections.toml")

st.logo("logo.png")

pg = st.navigation(nav)

# Either this or add_indentation() MUST be called on each page in your
# app to add indendation in the sidebar
add_page_title()
add_page_title(pg)

show_pages_from_config()
pg.run()
```

# Hiding pages

You can now pass a list of page names to `hide_pages` to hide pages dynamically for each
user. Note that these pages are only hidden via CSS, and can still be visited by the URL.
However, this could be a good option if you simply want a way to visually direct your
user where they should be able to go next.
You can now pass a list of page names to `hide_pages` to hide pages from now on.

NOTE: You should only hide pages that have also been added to the sidebar already.
This list of pages is custom to each viewer of the app, so you can hide pages
from one viewer but not from another using this method. You can see another example of
hiding pages in the docs [here](https://docs.streamlit.io/develop/tutorials/multipage/dynamic-navigation)

```py
show_pages(
[
Page("streamlit_app.py", "Home", "🏠"),
Page("another.py", "Another page"),
]
)
from st_pages import hide_pages

hide_pages(["Another page"])
```
28 changes: 12 additions & 16 deletions example_app/.streamlit/pages.toml
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
[[pages]]
path = "example_app/streamlit_app.py"
name = "Home"
icon = ":house:"
path = "example_one.py"
name = "st-pages"
icon = "📄"

[[pages]]
path = "example_app/example_one.py"
name = "Example One"
icon = ":books:"
path = "example_two.py"
name = "Example Two"
icon = ":material/history:"

[[pages]]
path = "example_app/example_four.py"
name = "Example Four"
path = "example_four.py"
name = "Try hiding pages"
icon = "📖"

[[pages]]
path = "example_app/example_two.py"
name = "Example Two"
icon = "✏️"

[[pages]]
path = "example_app/example_three.py"
path = "example_three.py"

[[pages]]
path = "example_app/example_five.py"
path = "example_five.py"
name = "Example Five"
icon = "🧰"
icon = "🧰"
url_path = "a_very_long_page_title"
29 changes: 12 additions & 17 deletions example_app/.streamlit/pages_sections.toml
Original file line number Diff line number Diff line change
@@ -1,38 +1,33 @@
[[pages]]
path = "example_app/streamlit_app_sections.py"
name = "Home"
icon = ":house:"

[[pages]]
path = "example_app/example_one.py"
name = "Example One"
icon = ":books:"
path = "example_one.py"
name = "st-pages"
icon = "📄"

[[pages]]
name = "Cool apps"
icon = ":pig:"
is_section = true

[[pages]]
path = "example_app/example_four.py"
name = "Example Four"
icon = "📖"
path = "example_two.py"
name = "Example Two"
icon = ":material/history:"

[[pages]]
path = "example_app/example_two.py"
name = "Example Two"
icon = "✏️"
path = "example_four.py"
name = "Try hiding pages"
icon = "📖"

[[pages]]
name = "Other apps"
icon = ":horse:"
is_section = true

[[pages]]
path = "example_app/example_three.py"
path = "example_three.py"

[[pages]]
path = "example_app/example_five.py"
path = "example_five.py"
name = "Example Five"
icon = "🧰"
in_section = false
url_path = "a_very_long_page_title"
Loading

0 comments on commit fd33fdb

Please sign in to comment.