import streamlit as st import pandas as pd import requests import json from io import StringIO import plotly.express as px import qrcode from PIL import Image import io # 自訂 CSS 確保標題不換行、調整圖片大小、優化按鈕布局、右下角資訊並調整頒獎台樣式 st.markdown(""" """, unsafe_allow_html=True) # 圓周率與無限猴子定理的介紹與分頁圖片展示 st.title("探索圓周率與無限猴子定理的奇妙世界") st.write(""" 歡迎參加這個有趣的活動!我們將一起探索圓周率 (π) 和無限猴子定理的奧秘,並透過幸運數字查詢系統,發現你選的數字在 π 中的秘密。 """) # 定義圖片和標題 image_data = [ {"path": "m1.GIF", "caption": "圓周率與無限猴子定理的奇妙關聯"}, {"path": "m2.GIF", "caption": "重複與不重複"}, {"path": "m3.GIF", "caption": "圓周率裡藏著你的幸運號碼"}, {"path": "m4.GIF", "caption": "無限猴子定理是什麼?"}, {"path": "m5.GIF", "caption": "圓周率是無限長的數字打字機"}, ] # 初始化 session state 用於追蹤當前頁面 if 'page' not in st.session_state: st.session_state.page = 0 # 顯示當前頁面的圖片,放大至幾乎全版面 st.image(image_data[st.session_state.page]["path"], caption=image_data[st.session_state.page]["caption"], use_container_width=True) # 換頁按鈕 col1, col2 = st.columns(2) with col1: if st.button("上一頁", disabled=st.session_state.page <= 0): st.session_state.page -= 1 st.rerun() with col2: if st.button("下一頁", disabled=st.session_state.page >= len(image_data) - 1): st.session_state.page += 1 st.rerun() # 顯示當前頁數 st.write(f"頁面: {st.session_state.page + 1} / {len(image_data)}") # 分隔線 st.markdown("---") # 查詢數字的函數 def process_number(number): """查詢特定數字並返回結果""" url = f"https://www.angio.net/newpi/piquery?q={number}" try: response = requests.get(url) response.raise_for_status() data = response.json() # 從回應中提取 'p' 值 if data.get('status') == 'OK' and data.get('r'): return data['r'][0].get('p') return None except Exception as e: st.error(f"處理數字 {number} 時發生錯誤: {str(e)}") return None def generate_qr_code(url): """生成 QR Code 並返回圖片""" qr = qrcode.QRCode( version=1, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=10, border=4, ) qr.add_data(url) qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") return img def main(): st.title("幸運數字查詢系統") # 新增功能:輸入表單連結並生成 QR Code st.subheader("請輸入 Google 表單連結") form_url = st.text_input("表單連結", placeholder="請輸入 Google 表單的完整 URL") if form_url: try: # 驗證 URL 是否有效(簡單檢查) if not form_url.startswith("https://"): st.error("請輸入有效的 URL(以 https:// 開頭)") else: st.write("以下是表單連結的 QR Code,學生可以使用平板掃描填寫:") # 生成 QR Code qr_img = generate_qr_code(form_url) # 將 PIL 圖片轉為字節流以顯示在 Streamlit buf = io.BytesIO() qr_img.save(buf, format="PNG") byte_im = buf.getvalue() st.image(byte_im, caption="掃描此 QR Code 前往表單", use_container_width=False) except Exception as e: st.error(f"生成 QR Code 時發生錯誤: {str(e)}") # 分隔線 st.markdown("---") # 讓使用者選擇幸運數字的位數 selected_digits = st.selectbox( "請選擇幸運數字的位數", options=range(4, 11), # 4 到 10 位 index=3 # 預設選 7 位 ) # 檔案上傳部分 uploaded_file = st.file_uploader("請上傳CSV檔案", type=['csv']) if uploaded_file: try: # 嘗試讀取 CSV,假設有表頭 df = pd.read_csv( StringIO(uploaded_file.getvalue().decode('utf-8')), header=0, # 預設有表頭 dtype=str # 所有欄位都以字串形式讀取 ) # 定義可能的欄位映射 required_columns = { "您的姓名": "姓名", "你的幸運號碼是?(可重複,前面也可以是0。Ex. 0000013、1111111)": "幸運號碼", "姓名": "姓名", "自選號碼": "幸運號碼" } # 檢查是否有必要的欄位 available_columns = [col for col in required_columns.keys() if col in df.columns] if not available_columns or len(available_columns) < 2: # 若無表頭或缺少必要欄位,假設無表頭,第一列為姓名,第二列為幸運號碼 df = pd.read_csv( StringIO(uploaded_file.getvalue().decode('utf-8')), header=None, dtype=str ) if len(df.columns) >= 2: df.columns = ["姓名", "幸運號碼"] else: st.error("CSV 檔案格式錯誤,缺少 '姓名' 或 '幸運號碼' 欄位。") return # 篩選並重新命名需要的欄位,忽略「時間戳記」 df = df[[col for col in df.columns if col in required_columns]].rename(columns=required_columns) # 顯示原始資料 st.subheader("原始資料") st.dataframe(df) # 處理每個幸運數字 st.subheader("處理結果") # 建立進度條 progress_bar = st.progress(0) status_text = st.empty() # 新增結果欄位 df['查詢結果'] = None # 處理每個數字 total_rows = len(df) for index, row in df.iterrows(): # 更新進度 progress = (index + 1) / total_rows progress_bar.progress(progress) status_text.text(f"正在處理: {index + 1}/{total_rows}") # 獲取幸運數字並檢查位數 original_number = str(row['幸運號碼']) number_length = len(original_number) if number_length > selected_digits: # 超過指定位數,標記為不符規定 df.at[index, '幸運號碼'] = original_number # 保留原始輸入 df.at[index, '查詢結果'] = "數字不符規定" else: # 小於或等於指定位數,補零後查詢 lucky_number = original_number.zfill(selected_digits) result = process_number(lucky_number) df.at[index, '幸運號碼'] = lucky_number # 更新為補零後的數字 df.at[index, '查詢結果'] = result # 顯示結果 st.subheader("完整結果") st.dataframe(df) # 提供下載功能,確保輸出時保留前導零 csv = df.to_csv(index=False, encoding='utf-8') st.download_button( label="下載處理結果", data=csv, file_name="processed_results.csv", mime="text/csv" ) # 新增長條圖功能 st.subheader("查詢結果長條圖") # 篩選有效數據(查詢結果不為 "數字不符規定" 且不為 None) valid_df = df[df['查詢結果'].notna() & (df['查詢結果'] != "數字不符規定")].copy() if not valid_df.empty: # 將查詢結果轉為數值型 valid_df['查詢結果'] = valid_df['查詢結果'].astype(float) # 拼接姓名和幸運號碼作為橫軸標籤 valid_df['姓名_幸運號碼'] = valid_df['姓名'] + "_" + valid_df['幸運號碼'] # 按照查詢結果從大到小排序 valid_df = valid_df.sort_values(by='查詢結果', ascending=False) # 使用 Plotly 繪製長條圖 fig = px.bar( valid_df, x='姓名_幸運號碼', y='查詢結果', labels={'姓名_幸運號碼': '姓名與幸運號碼', '查詢結果': '查詢結果數值'}, title="幸運數字查詢結果長條圖(按數值從大到小排序)" ) # 調整圖表佈局 fig.update_layout( xaxis_title="姓名與幸運號碼", yaxis_title="查詢結果數值", xaxis_tickangle=-45, # 旋轉橫軸標籤以避免重疊 margin=dict(l=50, r=50, t=80, b=150) # 增加底部邊距以顯示完整標籤 ) # 顯示圖表 st.plotly_chart(fig, use_container_width=True) # 新增頒獎台功能 st.subheader("頒獎台:查詢結果前三名") # 獲取前三名(已按查詢結果降序排序) top_3 = valid_df.head(3) if len(top_3) > 0: # 正常顯示頒獎台 col1, col2, col3 = st.columns(3) # 創建三列 with col1: if len(top_3) >= 2: second = top_3.iloc[1] st.markdown( f'
' f'
' # 填充 30px f'
{int(second["查詢結果"])}
' f'
{second["姓名"]}\n{second["幸運號碼"]}
' f'
', unsafe_allow_html=True ) else: st.markdown('
', unsafe_allow_html=True) with col2: if len(top_3) >= 1: first = top_3.iloc[0] st.markdown( f'
' f'
{int(first["查詢結果"])}
' f'
{first["姓名"]}\n{first["幸運號碼"]}
' f'
', unsafe_allow_html=True ) else: st.markdown('
', unsafe_allow_html=True) with col3: if len(top_3) >= 3: third = top_3.iloc[2] st.markdown( f'
' f'
' # 填充 60px f'
{int(third["查詢結果"])}
' f'
{third["姓名"]}\n{third["幸運號碼"]}
' f'
', unsafe_allow_html=True ) else: st.markdown('
', unsafe_allow_html=True) else: st.warning("無有效的查詢結果可供顯示頒獎台。") else: st.warning("無有效的查詢結果可供繪製長條圖或頒獎台。請確認資料是否正確。") except Exception as e: st.error(f"處理檔案時發生錯誤: {str(e)}") # 添加右下角資訊 st.markdown('', unsafe_allow_html=True) if __name__ == "__main__": main()