import streamlit as st
from typing import List, Dict, Tuple, Optional, Any
import traceback
class StreamlitTutorial:
"""
Main class for the Streamlit Tutorial application.
Handles rendering of all tutorial content, user interaction, and state management.
Provides modular components for different tutorial topics and interactive examples.
"""
def __init__(self):
"""
Initializes the Streamlit Tutorial application.
Sets up page configuration, session state, and custom styling.
Prepares the application environment for tutorial content.
"""
st.set_page_config(page_title="Learn Streamlit", layout="wide")
self.init_session_state()
self.setup_styles()
def init_session_state(self) -> None:
"""
Initializes session state variables for persistent data storage.
Creates state variables for code input and current topic tracking.
Ensures consistent state across reruns.
Returns:
None
"""
if 'code_input' not in st.session_state:
st.session_state.code_input = ""
st.session_state.current_topic = "Basic Text Elements"
def setup_styles(self) -> None:
"""
Configures custom CSS styles for the application.
Sets up consistent styling for code examples, outputs, and layout.
Enhances visual presentation of tutorial content.
Returns:
None
"""
st.markdown("""
""", unsafe_allow_html=True)
# Add these helper methods in the StreamlitTutorial class
def _get_basic_concepts_content(self) -> str:
"""
Provides content for Basic Concepts section.
Contains fundamental explanations and examples.
Returns:
str: Markdown formatted basic concepts content
"""
return """
**What are Text Elements?**
- Basic building blocks for displaying text
- Range from titles to formatted text
- Support markdown formatting
**Key Components:**
1. Title & Headers
2. Regular text
3. Formatted text
4. Colored text
"""
def _get_best_practices_content(self) -> str:
"""
Provides content for Best Practices section.
Contains guidelines and recommended approaches.
Returns:
str: Markdown formatted best practices content
"""
return """
**Writing Tips:**
1. Use appropriate heading levels
2. Keep text concise and clear
3. Use formatting for emphasis
4. Add visual hierarchy with headers
**Code Structure:**
```python
st.title('Main Title')
st.header('Section Header')
st.subheader('Sub-section')
st.write('Regular text')
```
"""
def _get_examples_content(self) -> str:
"""
Provides content for Examples section.
Contains practical examples and use cases.
Returns:
str: Markdown formatted examples content
"""
return """
**Common Patterns:**
1. Page Structure
```python
st.title('Dashboard')
st.header('Sales Data')
st.subheader('Monthly Trends')
```
2. Formatted Text
```python
st.markdown('**Bold** and *italic*')
st.markdown(':blue[Colored text]')
```
3. Mixed Elements
```python
st.title('Report')
st.write('Regular text')
st.markdown('- Bullet point')
```
"""
def _get_common_mistakes_content(self) -> str:
"""
Provides content for Common Mistakes section.
Contains typical errors and how to avoid them.
Returns:
str: Markdown formatted common mistakes content
"""
return """
1. Skipping header levels
2. Overusing formatting
3. Inconsistent styling
4. Missing hierarchy
**How to Avoid:**
- Plan your page structure
- Use consistent formatting
- Test different screen sizes
- Keep it simple
"""
def _get_widgets_help_content(self) -> str:
"""
Enhanced help content for widgets section
"""
return """
**Widget Best Practices:**
1. Input Validation
```python
# Add validation to inputs
age = st.number_input('Age', min_value=0, max_value=150, key='age_1')
if age < 18:
st.warning('Must be 18 or older')
```
2. Default Values
```python
# Provide sensible defaults
st.text_input('Name', value='Guest User', key='name_2')
st.slider('Rating', 1, 5, value=3, key='rating_1')
```
3. Responsive Widgets
```python
# React to widget changes
if st.checkbox('Show advanced options', key='advanced_1'):
st.number_input('Threshold', key='threshold_1')
st.slider('Sensitivity', key='sensitivity_1')
```
4. Form Submission
```python
with st.form('my_form'):
st.text_input('Username', key='form_user')
st.text_input('Password', type='password', key='form_pass')
submitted = st.form_submit_button('Login')
```
5. File Handling
```python
file = st.file_uploader('Upload CSV', key='file_2')
if file is not None:
# Handle file upload
st.success('File uploaded!')
```
6. Interactive Filters
```python
col1, col2 = st.columns(2)
with col1:
category = st.selectbox('Category', ['A', 'B', 'C'], key='cat_1')
with col2:
subcategory = st.multiselect('Subcategory', ['X', 'Y', 'Z'], key='subcat_1')
```
**Tips for Widget Usage:**
- Use clear, concise labels
- Group related widgets together
- Provide help text when needed
- Use appropriate widget types
- Handle all possible states
- Validate inputs properly
- Always use unique keys
- Consider mobile responsiveness
"""
def _get_advanced_tips_content(self) -> str:
"""
Provides content for Advanced Tips section.
Contains advanced usage and techniques.
Returns:
str: Markdown formatted advanced tips content
"""
return """
**Advanced Formatting:**
```python
# Custom HTML
st.markdown('''
Custom styled text
''', unsafe_allow_html=True)
# Complex Markdown
st.markdown('''
# Title
## Subtitle
* Point 1
* Subpoint
> Quote
''')
```
**Layout Combinations:**
```python
col1, col2 = st.columns(2)
with col1:
st.header('Column 1')
with col2:
st.header('Column 2')
```
"""
def get_text_elements(self) -> List[Tuple[str, str]]:
"""
Provides collection of text element examples with corresponding code.
Defines basic text manipulation and display examples.
Used for generating code examples and quick snippets.
Returns:
List[Tuple[str, str]]: List of (element name, code snippet) pairs
"""
return [
("Title", "st.title('Main Title')"),
("Header", "st.header('Header')"),
("Subheader", "st.subheader('Subheader')"),
("Normal text", "st.write('Normal text')"),
("Markdown text", "st.markdown('**Bold** and *italic*')"),
("Colored text", "st.markdown(':blue[Blue text]')")
]
def render_text_elements(self, col: st.delta_generator.DeltaGenerator) -> None:
"""
Renders text element examples in the specified column.
Displays code snippets with live output for each text element.
Creates interactive learning environment for text elements.
Arguments:
col: Streamlit column object for content rendering
Returns:
None
"""
with col:
st.markdown("### 📝 Code Examples")
for title, code in self.get_text_elements():
with st.container(border=True):
st.markdown(f"**{title}**")
st.code(code)
st.markdown("Live output:")
with st.container(border=True):
exec(code)
def get_input_elements(self) -> List[Tuple[str, str]]:
"""
Cross-validated collection of input widgets with unique keys
"""
return [
("Text Input", """st.text_input('Enter your name',
key='text_input_demo_1',
placeholder='John Doe')"""),
("Text Area", """st.text_area('Enter long text',
key='text_area_demo_1',
height=100,
placeholder='Write something...')"""),
("Number Input", """st.number_input('Enter a number',
key='number_input_demo_1',
min_value=0,
max_value=100,
value=50,
step=5)"""),
("Slider", """st.slider('Select value',
key='slider_demo_1',
min_value=0,
max_value=100,
value=50,
step=5)"""),
("Select Box", """st.selectbox('Choose an option',
key='select_box_demo_1',
options=['Option 1', 'Option 2', 'Option 3'],
index=0)""")
]
def render_input_elements(self, col: st.delta_generator.DeltaGenerator) -> None:
"""
Renders input examples with unique keys for both examples and live output
"""
with col:
st.markdown("### 📝 Code Examples")
for idx, (title, code) in enumerate(self.get_input_elements()):
with st.container(border=True, key=f"demo_container_{idx}"):
st.markdown(f"**{title}**")
st.code(code)
st.markdown("Live output:")
with st.container(border=True, key=f"output_container_{idx}"):
try:
# Create runtime version with unique key
runtime_code = code.replace('demo_1', f'runtime_{idx}')
exec(runtime_code)
except Exception as e:
st.error(f"Error: {str(e)}")
def render_help_section(self, col: st.delta_generator.DeltaGenerator) -> None:
"""
Renders comprehensive help content in the specified column.
Provides educational content, best practices, and examples.
Creates expandable sections for different learning topics.
Arguments:
col: Streamlit column object for content rendering
Returns:
None
"""
with col:
st.markdown("### 📚 Learning Guide")
# Basic Concepts Section
with st.expander("🎯 Basic Concepts", expanded=True):
st.markdown(self._get_basic_concepts_content())
# Best Practices Section
with st.expander("💡 Best Practices"):
st.markdown(self._get_best_practices_content())
# Examples Section
with st.expander("🔍 Examples & Use Cases"):
st.markdown(self._get_examples_content())
# Common Mistakes Section
with st.expander("⚠️ Common Mistakes"):
st.markdown(self._get_common_mistakes_content())
# Advanced Tips Section
with st.expander("🚀 Advanced Tips"):
st.markdown(self._get_advanced_tips_content())
def render_playground(self, col: st.delta_generator.DeltaGenerator, snippets: Dict[str, str]) -> None:
"""
Renders interactive coding playground with live execution.
Arguments:
col: Streamlit column object for rendering
snippets: Dictionary of available code snippets
"""
with col:
st.markdown("### 💻 Practice Playground")
# Code input area
code_input = st.text_area(
"Try Streamlit commands:",
key="playground_input",
value=st.session_state.get('code_input', ''),
height=200,
placeholder="Example:\nst.title('My Title')"
)
# Quick Snippets section
st.markdown("#### Quick Snippets")
# Split into two columns with better ratio for button alignment
snippet_col, button_col = st.columns([4, 1])
with snippet_col:
selected_snippet = st.selectbox(
"Choose snippet:",
list(snippets.keys()),
label_visibility="collapsed"
)
with button_col:
if st.button("Add", type="primary", use_container_width=True):
if 'code_input' not in st.session_state:
st.session_state.code_input = snippets[selected_snippet]
else:
# Add new line only if there's existing code
existing_code = st.session_state.code_input.strip()
new_code = snippets[selected_snippet]
st.session_state.code_input = f"{existing_code}\n{new_code}" if existing_code else new_code
st.rerun()
# Control buttons in equal columns
col1, col2, col3 = st.columns(3)
with col1:
if st.button("▶️ Run", use_container_width=True):
try:
if code_input.strip():
exec(code_input)
except Exception as e:
st.error(f"Error: {str(e)}")
with col2:
if st.button("🔄 Reset", use_container_width=True):
st.session_state.code_input = ""
st.rerun()
with col3:
if st.button("💾 Copy", use_container_width=True):
st.code(code_input)
# Live output section
st.markdown("#### 🎨 Live Output")
with st.container(border=True):
if code_input.strip():
try:
exec(code_input)
except Exception as e:
st.error(f"Error: {str(e)}")
# Tips section
with st.expander("💡 Tips & Help"):
st.markdown("""
**Quick Tips:**
- Type or paste Streamlit commands
- Use snippets for quick start
- Click Run to see results
- Reset to clear all code
""")
def render_widget_help(self, col: st.delta_generator.DeltaGenerator) -> None:
"""
Renders comprehensive widget help content
"""
with col:
st.markdown("### 📚 Widget Guide")
with st.expander("🎯 Basic Concepts", expanded=True):
st.markdown("""
**Widget Types:**
- Input widgets (text, numbers, dates)
- Selection widgets (dropdown, checkbox, radio)
- File widgets (uploaders, downloads)
- Display widgets (progress, status)
**Key Features:**
- Real-time updates
- State management
- Input validation
- Responsive layout
- Form handling
- Unique widget keys
""")
with st.expander("💡 Best Practices"):
st.markdown(self._get_widgets_help_content())
with st.expander("🔍 Examples"):
st.code("""
# Basic form example
with st.form('contact'):
name = st.text_input('Name', key='contact_name')
email = st.text_input('Email', key='contact_email')
message = st.text_area('Message', key='contact_message')
submit = st.form_submit_button('Send')
if submit:
st.success('Message sent!')
""")
def get_topic_tips(self, topic: str) -> str:
"""
Provides topic-specific tips and guidance.
Returns formatted markdown string with helpful information.
Customizes content based on current tutorial topic.
Arguments:
topic: Current tutorial topic name
Returns:
str: Markdown formatted tips and help content
"""
tips = {
"Basic Text Elements": """
**Quick Tips:**
- Use markdown for formatting
- Try different header levels
- Combine text elements
- Use colored text with :color[text]
""",
"Input Widgets": """
**Quick Tips:**
- Use unique keys for widgets
- Store widget values in variables
- Add validation for inputs
- Combine widgets for complex inputs
"""
}
return tips.get(topic, "Tips coming soon!")
def run(self) -> None:
"""
Main execution method for the Streamlit Tutorial application.
Handles topic selection and content rendering.
Manages overall application flow and state.
"""
with st.sidebar:
st.title("Streamlit Tutorial")
st.session_state.current_topic = st.radio(
"Choose a Topic:",
["Basic Text Elements", "Input Widgets", "Layouts & Containers",
"Data Display", "Charts & Plots", "Interactive Components"]
)
if st.session_state.current_topic == "Basic Text Elements":
cols = st.columns([1.2, 1, 1])
self.render_text_elements(cols[0])
self.render_help_section(cols[1])
self.render_playground(cols[2], dict(self.get_text_elements()))
elif st.session_state.current_topic == "Input Widgets":
cols = st.columns([1.2, 1, 1])
self.render_input_elements(cols[0])
self.render_help_section(cols[1])
self.render_playground(cols[2], dict(self.get_input_elements()))
def main():
try:
app = StreamlitTutorial()
app.run()
except Exception as e:
st.error(f"Application Error: {str(e)}")
st.write(f"Traceback: {traceback.format_exc()}")
if __name__ == "__main__":
main()