Spaces:
Running
Running
# ui/layouts.py (最終完整版) | |
import gradio as gr | |
from config import content, data, defaults | |
from core import callbacks | |
def create_ui(visit_count_html: str, theme: gr.Theme): | |
""" | |
Creates and returns the Gradio UI Blocks. | |
Args: | |
visit_count_html: The Markdown string to display the visit count. | |
theme: The Gradio theme object to apply to the UI. | |
""" | |
with gr.Blocks(theme=theme, title="地球物理學") as demo: | |
# --- 自訂頂部 Header --- | |
gr.Markdown( | |
""" | |
<style> | |
.custom-header { | |
background: linear-gradient(135deg, #005f73, #0a9396); | |
color: #fff; | |
padding: 4rem 2rem; | |
text-align: center; | |
border-radius: 0 0 20px 20px; | |
margin-bottom: 2rem; | |
box-shadow: 0 8px 15px rgba(0,0,0,0.1); | |
} | |
.custom-header h1 { | |
font-size: 3.5rem; | |
font-weight: 700; | |
margin-bottom: 0.8rem; | |
line-height: 1.2; | |
} | |
.custom-header p { | |
font-size: 1.6rem; | |
font-weight: 400; | |
opacity: 0.9; | |
margin-top: 0; | |
} | |
/* Style for instruction cards */ | |
.instruction-card { | |
background-color: #ffffff; | |
border-left: 5px solid #0a9396; | |
padding: 20px 25px; | |
margin: 20px 0; | |
border-radius: 8px; | |
box-shadow: 0 4px 12px rgba(0,0,0,0.08); | |
} | |
.instruction-card h3 { | |
margin-top: 0; | |
color: #005f73; | |
font-size: 1.5rem; | |
} | |
.instruction-card p, .instruction-card ul { | |
font-size: 1.1rem; | |
color: #34495e; | |
line-height: 1.8; | |
} | |
.instruction-card code { | |
background-color: #e9ecef; | |
padding: 4px 8px; | |
border-radius: 5px; | |
font-size: 1rem; | |
color: #c7254e; | |
} | |
/* Style for the homepage image */ | |
.homepage-image-container { | |
margin-top: 1rem; /* 圖片與上方 Header 的間距 */ | |
margin-bottom: 2rem; /* 圖片與下方 Tabs 的間距 */ | |
border-radius: 12px; | |
overflow: hidden; /* 確保圖片圓角 */ | |
box-shadow: 0 6px 20px rgba(0,0,0,0.2); | |
} | |
.homepage-image-container img { | |
width: 100%; | |
height: auto; | |
display: block; | |
} | |
@media (max-width: 768px) { | |
.custom-header { padding: 3rem 1rem; border-radius: 0; } | |
.custom-header h1 { font-size: 2.5rem; } | |
.custom-header p { font-size: 1.2rem; } | |
/* ✨ 為手機顯示調整 Tabs 樣式,強制換行並平均分佈 */ | |
/* 目標 Gradio 的 Tab 容器類別為 .gradio-tabs */ | |
.gradio-tabs .tabs-nav { | |
display: flex; /* 使用 flexbox 佈局 */ | |
flex-wrap: wrap; /* 允許項目換行 */ | |
justify-content: space-around; /* 均勻分佈項目 */ | |
gap: 5px; /* 項目間距 */ | |
padding: 5px; /* 內邊距 */ | |
} | |
/* 目標 Gradio 的單個 Tab 項目類別為 .gradio-tabs > button */ | |
.gradio-tabs .tabs-nav > button { | |
flex: 1 1 auto; /* 允許項目彈性增長和縮小,並有基礎寬度 */ | |
min-width: calc(33% - 10px); /* 每行顯示3個,考慮到 gap */ | |
max-width: calc(50% - 10px); /* 小螢幕時至少顯示2個 */ | |
box-sizing: border-box; /* 確保 padding 和 border 包含在寬度內 */ | |
text-align: center; | |
white-space: normal; /* 允許文字換行 */ | |
padding: 8px 5px; /* 調整 padding */ | |
font-size: 0.85rem; /* 調整字體大小 */ | |
} | |
} | |
</style> | |
<div class="custom-header"> | |
<h1>地球物理概論</h1> | |
<p>在這裡,您不僅能學習理論,更能透過「互動實驗室」親手編寫程式碼、視覺化地震資料,並隨時向「AI 助教」提問,獲取課程解答與即時的全球地震資訊。立即開始您的地球物理探索之旅吧!</p> | |
</div> | |
""" | |
) | |
with gr.Row(elem_classes="homepage-image-container"): | |
gr.Image(value="homepage_image.png", show_label=False, interactive=False, container=False) | |
with gr.Tabs(): | |
with gr.TabItem("🎯 課程目標", id="goals_tab"): | |
gr.Markdown(content.course_goals_md) | |
with gr.TabItem("🗓️ 課程進度"): | |
gr.Markdown("### 每週課程安排") | |
gr.DataFrame(data.schedule_df, wrap=True) | |
with gr.TabItem("💯 成績計算"): | |
gr.Markdown(content.grading_policy_md) | |
with gr.TabItem("🚀 互動體驗區 (程式碼實驗室)"): | |
gr.Markdown("## 🚀 互動程式碼實驗室") | |
gr.Markdown("歡迎來到這裡!直接修改下方的 Python 程式碼,點擊「執行」,即可在右側看到成果。這是學習程式與地球物理最直接的方式!") | |
gr.Info("注意:執行環境已受限,僅支援資料視覺化相關操作。請勿嘗試檔案讀寫或網路請求。") | |
with gr.Accordion("🌍 地圖繪製實驗室 (PyGMT/Cartopy 概念)", open=True): | |
with gr.Row(): | |
with gr.Column(scale=2): | |
gr.Markdown("### 說明\n這段程式碼使用 `cartopy` 和 `matplotlib` 函式庫來繪製地理地圖。\n\n**您可以試著:**\n1. 修改 `center_lon`, `center_lat` 來改變地圖中心。\n2. 調整 `extent_lon`, `extent_lat` 來縮放地圖。\n3. 將 `coastline_color` 改成 'red' 或其他顏色。\n4. **在 `symbols` 列表中新增或修改字典,來繪製自訂的符號(例如:標示您所在的城市)。**") | |
map_code = gr.Code(label="可編輯的 Python 程式碼", value=defaults.DEFAULT_MAP_CODE, language="python", lines=25) | |
map_run_button = gr.Button("執行程式碼", variant="primary") | |
with gr.Column(scale=3): | |
map_plot_output = gr.Plot(label="地圖輸出") | |
map_console_output = gr.Textbox(label="執行結果 / 錯誤訊息", lines=8, interactive=False) | |
with gr.Accordion("📈 震波圖繪製實驗室 (ObsPy 概念)", open=False): | |
with gr.Row(): | |
with gr.Column(scale=2): | |
gr.Markdown("### 說明\n這段程式碼使用 `numpy` 產生模擬的地震波數據,並用 `matplotlib` 將其視覺化。\n\n**您可以試著:**\n1. 修改 `p_wave_arrival` 和 `s_wave_arrival` 來改變 P/S 波的抵達時間。\n2. 調整 `main_freq` 來改變地震波的頻率(數值越大,波形越密集)。\n3. 將 `decay_rate` 調小,觀察振幅衰減變慢的效果。") | |
seismo_code = gr.Code(label="可編輯的 Python 程式碼", value=defaults.DEFAULT_SEISMO_CODE, language="python", lines=25) | |
seismo_run_button = gr.Button("執行程式碼", variant="primary") | |
with gr.Column(scale=3): | |
seismo_plot_output = gr.Plot(label="震波圖輸出") | |
seismo_console_output = gr.Textbox(label="執行結果 / 錯誤訊息", lines=8, interactive=False) | |
with gr.TabItem("❓ AI 助教說明"): | |
gr.Markdown("## 🤖 AI 課程助教使用指南") | |
gr.Markdown("歡迎使用課程 AI 助教!我整合了課程知識庫與多種即時資訊來源,您可以透過多種方式與我互動。") | |
with gr.Blocks(): | |
gr.Markdown( | |
""" | |
<div class="instruction-card"> | |
<h3>📚 課程知識庫</h3> | |
<p>我可以回答關於本課程的各種靜態問題。您可以隨意提問,例如:</p> | |
<ul> | |
<li><code>這門課的評分標準是什麼?</code></li> | |
<li><code>Anaconda 和 Colab 有什麼不同?</code></li> | |
<li><code>如何安裝 anaconda?</code></li> | |
</ul> | |
</div> | |
<div class="instruction-card"> | |
<h3>⚡ 即時資訊查詢</h3> | |
<p>我能幫您查詢最新的動態資訊,您可以直接輸入下方的<b>快捷指令</b>或對應的<b>文字問題</b>:</p> | |
<ul> | |
<li>快捷指令 <code>#1</code> 或輸入 <code>今天有什麼重要新聞?</code></li> | |
<li>快捷指令 <code>#2</code> 或輸入 <code>最新的CWA顯著地震報告</code></li> | |
<li>快捷指令 <code>#3</code> 或輸入 <code>現在有地震預警嗎?</code></li> | |
<li>快捷指令 <code>#4</code> 或輸入 <code>最近全球有哪些大地震?</code></li> | |
<li>快捷指令 <code>#5</code> 或輸入 <code>pws info</code></li> | |
<li>快捷指令 <code>#6</code> 或輸入 <code>最新的pws地震警報</code></li> | |
</ul> | |
</div> | |
<div class="instruction-card"> | |
<h3>🔎 進階地震查詢</h3> | |
<p>您可以指定<b>日期範圍</b>與<b>規模</b>來查詢全球地震紀錄。輸入快捷指令 <code>#7</code> 可以查看格式說明,或直接依照以下格式提問:</p> | |
<ul> | |
<li><code>查詢 2024-04-01 到 2024-04-07 規模 6.0 以上地震</code></li> | |
</ul> | |
<p>您可以自由更換其中的日期與規模數值。</p> | |
</div> | |
""" | |
) | |
with gr.TabItem("🤖 AI 課程助教"): | |
with gr.Group(): | |
gr.Markdown("### 🤖 AI 課程助教") | |
gr.Markdown("歡迎使用 AI 助教!您可以直接在下方對話框提問,或參考「❓ **AI 助教說明**」分頁了解所有支援的功能與快捷指令。") | |
custom_textbox = gr.Textbox( | |
placeholder="對課程有什麼問題嗎?或輸入 #1、#2... 查詢即時資訊", | |
show_label=False, | |
container=False, | |
) | |
gr.ChatInterface( | |
callbacks.ai_chatbot_with_kb, | |
chatbot=gr.Chatbot(height=500, type="messages", avatar_images=(None, "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png")), | |
title=None, | |
description=None, | |
textbox=custom_textbox, | |
) | |
# --- 連接按鈕與後端執行函式 --- | |
map_run_button.click( | |
fn=lambda code: callbacks.execute_user_code(code, "地圖繪製"), | |
inputs=[map_code], | |
outputs=[map_plot_output, map_console_output] | |
) | |
seismo_run_button.click( | |
fn=lambda code: callbacks.execute_user_code(code, "震波圖"), | |
inputs=[seismo_code], | |
outputs=[seismo_plot_output, seismo_console_output] | |
) | |
return demo | |