Spaces:
Running
Running
File size: 12,350 Bytes
2dcae4b 1b65622 a024ea5 1b65622 2dcae4b a024ea5 2dcae4b a024ea5 2dcae4b 1f036fb b0b8733 1f036fb b0b8733 1f036fb b0b8733 1f036fb 8fe26ab 2dcae4b 1f036fb f72f5ad 2dcae4b a024ea5 ef3829e a024ea5 2dcae4b a024ea5 8fe26ab ebf8ad6 8f70f76 ebf8ad6 2dcae4b ebf8ad6 2dcae4b ebf8ad6 a024ea5 ebf8ad6 a024ea5 ebf8ad6 1f036fb b0b8733 1f036fb a024ea5 ebf8ad6 1f036fb ebf8ad6 db91bf7 ebf8ad6 2dcae4b ebf8ad6 1f036fb ebf8ad6 a024ea5 2dcae4b d584c36 1b65622 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# 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
|