데이터분석/Python

[streamlit] 튜토리얼 > 동적 탐색 메뉴 만들기

psystat 2024. 9. 19. 15:35

[출처] https://docs.streamlit.io/develop/tutorials/multipage/dynamic-navigation

 

Streamlit Docs

Join the community Streamlit is more than just a way to make data apps, it's also a community of creators that share their apps and ideas and help each other make their work better. Please come join us on the community forum. We love to hear your questions

docs.streamlit.io


1. Intro

st.navigation을 사용하면 동적 탐색 메뉴를 쉽게 만들 수 있습니다. 재실행할 때마다 st.navigation으로 전달되는 페이지 집합을 변경하면 탐색 메뉴가 그에 맞게 변경됩니다. 이 기능은 사용자 지정 역할 기반 탐색 메뉴를 만들 때 편리한 기능입니다.

이 튜토리얼에서는 Streamlit 버전 1.36.0에 도입된 st.navigation 및 st.Page를 사용합니다. pages/ 디렉토리 및 st.page_link를 사용하는 과거의 방법은 st.page_link를 사용하여 사용자 정의 탐색 메뉴 만들기를 참조하세요.

Applied concepts

  • st.navigation 및 st.Page를 사용하여 다중 페이지 앱을 정의합니다.
  • 동적인 역할 기반 탐색 메뉴를 만듭니다.

Prerequisites

  • 파이썬 환경에는 다음이 설치되어 있어야 합니다: streamlit>=1.36.0
  • your-repository 라는 깨끗한 작업 디렉터리가 있어야 합니다.
  • st.navigation 및 st.Page에 대한 기본적인 이해가 있어야 합니다.

Summary

이 예제에서는 현재 사용자의 역할에 따라 달라지는 다중 페이지 앱의 동적 탐색 메뉴를 구축하겠습니다. 예제를 단순화하기 위해 사용자 이름 및 자격 증명 사용을 추상화하겠습니다. 대신 선택 상자를 사용하여 사용자가 역할을 선택하고 로그인할 수 있도록 합니다.

엔트리포인트 파일인 streamlit_app.py는 사용자 인증을 처리합니다. 다른 페이지는 계정 관리(settings.py)와 세 가지 역할에 연결된 특정 페이지를 나타내는 스텁입니다: 요청자, 응답자, 관리자입니다. 요청자는 계정 및 요청 페이지에 액세스할 수 있습니다. 응답자는 계정에 액세스하여 페이지에 응답할 수 있습니다. 관리자는 모든 페이지에 액세스할 수 있습니다.

구축할 내용을 살펴보세요:

Directory structure:

your-repository/
├── admin
│   ├── admin_1.py
│   └── admin_2.py
├── images
│   ├── horizontal_blue.png
│   └── icon_blue.png
├── request
│   ├── request_1.py
│   └── request_2.py
├── respond
│   ├── respond_1.py
│   └── respond_2.py
├── settings.py
└── streamlit_app.py

streamlit_app.py:

import streamlit as st

if "role" not in st.session_state:
    st.session_state.role = None

ROLES = [None, "Requester", "Responder", "Admin"]


def login():

    st.header("Log in")
    role = st.selectbox("Choose your role", ROLES)

    if st.button("Log in"):
        st.session_state.role = role
        st.rerun()


def logout():
    st.session_state.role = None
    st.rerun()


role = st.session_state.role

logout_page = st.Page(logout, title="Log out", icon=":material/logout:")
settings = st.Page("settings.py", title="Settings", icon=":material/settings:")
request_1 = st.Page(
    "request/request_1.py",
    title="Request 1",
    icon=":material/help:",
    default=(role == "Requester"),
)
request_2 = st.Page(
    "request/request_2.py", title="Request 2", icon=":material/bug_report:"
)
respond_1 = st.Page(
    "respond/respond_1.py",
    title="Respond 1",
    icon=":material/healing:",
    default=(role == "Responder"),
)
respond_2 = st.Page(
    "respond/respond_2.py", title="Respond 2", icon=":material/handyman:"
)
admin_1 = st.Page(
    "admin/admin_1.py",
    title="Admin 1",
    icon=":material/person_add:",
    default=(role == "Admin"),
)
admin_2 = st.Page("admin/admin_2.py", title="Admin 2", icon=":material/security:")

account_pages = [logout_page, settings]
request_pages = [request_1, request_2]
respond_pages = [respond_1, respond_2]
admin_pages = [admin_1, admin_2]

st.title("Request manager")
st.logo("images/horizontal_blue.png", icon_image="images/icon_blue.png")

page_dict = {}
if st.session_state.role in ["Requester", "Admin"]:
    page_dict["Request"] = request_pages
if st.session_state.role in ["Responder", "Admin"]:
    page_dict["Respond"] = respond_pages
if st.session_state.role == "Admin":
    page_dict["Admin"] = admin_pages

if len(page_dict) > 0:
    pg = st.navigation({"Account": account_pages} | page_dict)
else:
    pg = st.navigation([st.Page(login)])

pg.run()

2. Build the example

2.1. Initialize your app

1. your_repository에서 streamlit_app.py라는 파일을 생성합니다.

2. 터미널에서 디렉터리를 your_repository로 변경하고 앱을 시작합니다.

streamlit run streamlit_app.py

아직 코드를 추가해야 하므로 앱이 비어 있습니다.

3. streamlit_app.py에 다음을 작성합니다:

import streamlit as st

4. streamlit_app.py 파일을 저장하고 실행 중인 앱을 확인합니다.

5. '항상 다시 실행'을 클릭하거나 실행 중인 앱에서 'A' 키를 누르세요.

streamlit_app.py에 변경 사항을 저장하면 실행 중인 미리보기가 자동으로 업데이트됩니다. 미리보기는 여전히 비어 있습니다. 코드로 돌아갑니다.

2.2. Add your page and image files

1. your_repositoy에서 settings.py라는 파일을 만듭니다.

2. settings.py에 다음 내용을 추가합니다.

import streamlit as st

st.header("Settings")
st.write(f"You are logged in as {st.session_state.role}.")

이후 단계에서는 현재 사용자의 역할을 st.session_state.role에 저장하는 인증 방법을 만들 것입니다. 사용자가 로그인할 때까지 이 페이지에 대한 액세스를 차단할 것이므로 이 페이지에 대한 세션 상태의 "role" 키를 초기화할 필요가 없습니다.

3. 다음 6개 페이지에 대해 st.header의 값을 변경하여 유사한 내을 만듭니다:

your-repository/
├── admin
│   ├── admin_1.py
│   └── admin_2.py
├── request
│   ├── request_1.py
│   └── request_2.py
└── respond
    ├── respond_1.py
    └── respond_2.py

예를 들어 admin/admin_1.py는 다음과 같아야 합니다:

import streamlit as st

st.header("Admin 1")
st.write(f"You are logged in as {st.session_state.role}.")

4. 리포지토리에 이미지 하위 디렉터리를 만들고 다음 두 파일을 추가합니다:

이제 앱을 빌드하는 데 필요한 모든 파일이 준비되었습니다.

2.3. Initialize global values

1. streamlit_app.py로 돌아가서 세션 상태의 "role"을 초기화합니다.

if "role" not in st.session_state:
    st.session_state.role = None

이 값을 사용하여 앱에 대한 액세스를 게이트키핑합니다. 이 값은 현재 인증된 사용자의 역할을 나타냅니다.

2. 사용 가능한 역할을 정의합니다.

ROLES = [None, "Requester", "Responder", "Admin"]

None은 인증되지 않은 사용자에 해당하는 값이므로 역할에 포함되지 않습니다.

2.4. Define your user authentication pages

st.navigation을 사용하면 Python 함수에서 페이지를 정의할 수 있습니다. 여기서는 파이썬 함수에서 로그인 및 로그아웃 페이지를 정의합니다.

1. 로그인 페이지(기능 정의)를 시작합니다.

def login():

2. 페이지의 헤더를 추가합니다.

    st.header("Log in")

3. 사용자가 역할을 선택할 수 있는 선택 상자를 만듭니다.

    role = st.selectbox("Choose your role", ROLES)

4. 사용자 역할을 세션 상태에 커밋하는 버튼을 추가합니다.

    if st.button("Log in"):
        st.session_state.role = role
        st.rerun()

이것은 인증 워크플로우의 추상화입니다. 사용자가 버튼을 클릭하면 스트림릿은 역할을 세션 상태에 저장하고 앱을 다시 실행합니다. 이후 단계에서는 st.session_state.role의 값이 변경될 때 사용자를 역할의 기본 페이지로 안내하는 로직을 추가합니다. 이것으로 로그인 페이지 함수가 완성되었습니다.

5. 로그아웃 페이지(기능 정의)를 시작합니다.

def logout():

6. 즉시 역할을 None으로 설정하고 앱을 다시 실행합니다.

    st.session_state.role = None
    st.rerun()

로그아웃 페이지 기능은 세션 상태를 즉시 업데이트하고 재실행하므로 사용자는 이 페이지를 볼 수 없습니다. 이 페이지는 순식간에 실행되며 재실행 시 앱은 사용자를 로그인 페이지로 전송합니다. 따라서 페이지에 추가 요소가 렌더링되지 않습니다. 원하는 경우 이 페이지에 로그인 페이지와 유사한 버튼을 포함하도록 변경할 수 있습니다. 버튼을 사용하면 사용자가 실제로 로그아웃할 의사가 있는지 확인할 수 있습니다.

2.5. Define all your pages

1. 편의를 위해 st.session_state.role을 변수에 저장하세요.

role = st.session_state.role

2. 계정 페이지를 정의합니다.

logout_page = st.Page(logout, title="Log out", icon=":material/logout:")
settings = st.Page("settings.py", title="Settings", icon=":material/settings:")

이렇게 하면 각 페이지에 멋진 제목과 아이콘을 지정하여 탐색 메뉴를 깔끔하고 깔끔하게 만들 수 있습니다.

3. 요청 페이지를 정의합니다.

request_1 = st.Page(
    "request/request_1.py",
    title="Request 1",
    icon=":material/help:",
    default=(role == "Requester"),
)
request_2 = st.Page(
    "request/request_2.py", title="Request 2", icon=":material/bug_report:"
)

st.navigation에서 기본 페이지를 수동으로 선언하지 않으면 첫 번째 페이지가 자동으로 기본 페이지가 됩니다. 메뉴의 첫 페이지는 메뉴의 '계정' 섹션에 있는 '로그아웃'이 됩니다. 따라서 각 사용자가 기본적으로 어떤 페이지로 이동해야 하는지 스트림릿에 알려주어야 합니다.

이 코드는 역할이 "Requester" 인 경우 기본값을 default=True로 설정하고 그렇지 않은 경우 기본값을 False로 동적으로 설정합니다.(defualt 설정을 안해주면 아무 페이지도 안보임)

4. 남은 페이지를 정의합니다.

respond_1 = st.Page(
    "respond/respond_1.py",
    title="Respond 1",
    icon=":material/healing:",
    default=(role == "Responder"),
)
respond_2 = st.Page(
    "respond/respond_2.py", title="Respond 2", icon=":material/handyman:"
)
admin_1 = st.Page(
    "admin/admin_1.py",
    title="Admin 1",
    icon=":material/person_add:",
    default=(role == "Admin"),
)
admin_2 = st.Page("admin/admin_2.py", title="Admin 2", icon=":material/security:")

 

5. 요청 페이지와 마찬가지로 다른 역할의 기본 페이지에도 기본 매개변수가 설정됩니다.

account_pages = [logout_page, settings]
request_pages = [request_1, request_2]
respond_pages = [respond_1, respond_2]
admin_pages = [admin_1, admin_2]

로그인한 사용자가 사용할 수 있는 모든 페이지입니다.

2.6. Define your common elements and navigation

1. 모든 페이지에 표시할 제목을 추가합니다.

st.title("Request manager")

엔트리포인트 파일에서 제목 명령을 호출하기 때문에 이 제목은 모든 페이지에 표시됩니다. 엔트리포인트 파일에서 생성된 요소는 모든 페이지에 공통 요소의 프레임을 만듭니다.

2. 앱에 로고를 추가하세요.

st.logo("images/horizontal_blue.png", icon_image="images/icon_blue.png")

다시 한 번 말하지만, 엔트리포인트 파일에서 이 명령을 호출하기 때문에 각 페이지 내에서 이 명령을 호출할 필요는 없습니다.

3. 페이지 목록 딕셔너리를 초기화합니다.

page_dict = {}

다음 단계에서는 사용자의 역할을 확인하고 사용자가 액세스할 수 있는 페이지를 딕셔너리에 추가합니다. st.navigation이 페이지 목록의 딕셔너리를 받으면 페이지 그룹과 섹션 헤더가 포함된 탐색 메뉴를 만듭니다.

4. 사용자의 역할을 확인하여 허용된 페이지의 딕셔너리를 구축합니다.

if st.session_state.role in ["Requester", "Admin"]:
    page_dict["Request"] = request_pages
if st.session_state.role in ["Responder", "Admin"]:
    page_dict["Respond"] = respond_pages
if st.session_state.role == "Admin":
    page_dict["Admin"] = admin_pages

5. 사용자가 페이지에 액세스할 수 있는지 확인하고 허용된 경우 계정 페이지를 추가합니다.

if len(page_dict) > 0:
    pg = st.navigation({"Account": account_pages} | page_dict)

page_dict가 비어 있지 않으면 사용자가 로그인한 것입니다. 연산자는 두 사전을 병합하여 계정 페이지를 처음에 추가합니다.

6. 사용자가 로그인하지 않은 경우 로그인 페이지로 폴백합니다.

else:
    pg = st.navigation([st.Page(login)])

7. st.navigation이 반환한 페이지를 실행합니다.

pg.run()

8. streamlit_app.py 파일을 저장하고 앱을 확인하세요!

로그인, 페이지 전환, 로그아웃을 시도해 보세요. 다른 역할로 다시 시도해 보세요.


(1) None으로 로그인

- None으로 로그인하면 사이드바에 아무 페이지도 나오지 않음

(2) Requester로 로그인

(3) Responder로 로그인

(4) Admin으로 로그인