s880453 commited on
Commit
c297ae2
·
verified ·
1 Parent(s): 9a186a0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +125 -84
app.py CHANGED
@@ -1273,7 +1273,7 @@ def recommend_chart_settings(df):
1273
  """
1274
  Gradio 應用程式:進階數據可視化工具
1275
  作者:Gemini
1276
- 版本:1.0 (分段提供 - Part 3)
1277
  描述:此部分包含 Gradio UI 介面定義、事件處理和應用程式啟動。
1278
  """
1279
 
@@ -1329,7 +1329,8 @@ with gr.Blocks(css=CUSTOM_CSS, title="進階數據可視化工具 V2", theme=gr.
1329
  gr.HTML('<div class="section-title">2. 數據預覽與導出</div>')
1330
  with gr.Group(elem_classes=["card"]):
1331
  gr.Markdown("下方將顯示載入或解析後的數據預覽。")
1332
- data_preview = gr.Dataframe(label="數據表格預覽", interactive=False, height=350)
 
1333
 
1334
  with gr.Row():
1335
  export_format = gr.Dropdown(
@@ -1535,6 +1536,13 @@ with gr.Blocks(css=CUSTOM_CSS, title="進階數據可視化工具 V2", theme=gr.
1535
  preview_df = df if df is not None else pd.DataFrame()
1536
  # 更新所有列選擇下拉列表
1537
  col_updates = update_columns(df) # 返回 4 個 Dropdown 更新對象
 
 
 
 
 
 
 
1538
  return [df, status_msg, preview_df] + list(col_updates) * 2 # 更新兩組下拉列表
1539
 
1540
  upload_button.click(
@@ -1600,15 +1608,26 @@ with gr.Blocks(css=CUSTOM_CSS, title="進階數據可視化工具 V2", theme=gr.
1600
  outputs=[chart_output_1]
1601
  )
1602
  # 當任何相關設置改變時,自動更新圖表一
 
 
 
 
 
 
 
 
 
 
1603
  for input_component in chart_inputs_1:
1604
  # 避免狀態變量觸發無限循環,只監聽 UI 組件的變化
1605
- if isinstance(input_component, (gr.Dropdown, gr.Textbox, gr.Slider, gr.Checkbox)):
1606
  input_component.change(
1607
- create_plot,
1608
- inputs=chart_inputs_1,
1609
  outputs=[chart_output_1]
1610
  )
1611
 
 
1612
  # --- 圖表一:導出圖表 ---
1613
  download_button_1.click(
1614
  download_figure,
@@ -1620,30 +1639,41 @@ with gr.Blocks(css=CUSTOM_CSS, title="進階數據可視化工具 V2", theme=gr.
1620
  def apply_recommendation(rec_dict):
1621
  """將推薦字典應用到圖表一的 UI 組件"""
1622
  if not isinstance(rec_dict, dict):
1623
- return [gr.update()] * 5 # 返回空更新以避免錯誤
1624
-
1625
- # 如果聚合是計數,Y 軸應為空或不更新
1626
- y_val = None if rec_dict.get("agg_function") == "計數" else rec_dict.get("y_column")
1627
-
 
 
 
 
 
 
 
1628
  return [
1629
- gr.Dropdown(value=rec_dict.get("chart_type")),
1630
- gr.Dropdown(value=rec_dict.get("x_column")),
1631
- gr.Dropdown(value=y_val), # 使用處理過的 Y 值
1632
- gr.Dropdown(value=rec_dict.get("group_column", "無")),
1633
- gr.Dropdown(value=rec_dict.get("agg_function"))
1634
  ]
1635
 
 
 
 
 
1636
  recommend_button_1.click(
1637
  recommend_chart_settings,
1638
  inputs=[data_state],
1639
- outputs=None # 先不直接輸出到 Textbox
1640
  ).then(
1641
  apply_recommendation,
1642
- inputs=None, # 使用 click 的輸出作為 then 的輸入
1643
  outputs=[chart_type_1, x_column_1, y_column_1, group_column_1, agg_function_1]
1644
  ).then( # 應用推薦後立即觸發一次圖表更新
1645
- create_plot,
1646
- inputs=chart_inputs_1, # 重新收集所有輸入
1647
  outputs=[chart_output_1]
1648
  )
1649
 
@@ -1675,10 +1705,13 @@ with gr.Blocks(css=CUSTOM_CSS, title="進階數據可視化工具 V2", theme=gr.
1675
  outputs=[chart_output_2]
1676
  )
1677
  # 當任何相關設置改變時,自動更新圖表二
 
 
 
1678
  for input_component in chart_inputs_2:
1679
- if isinstance(input_component, (gr.Dropdown, gr.Textbox, gr.Slider, gr.Checkbox)):
1680
  input_component.change(
1681
- create_plot,
1682
  inputs=chart_inputs_2,
1683
  outputs=[chart_output_2]
1684
  )
@@ -1693,69 +1726,75 @@ with gr.Blocks(css=CUSTOM_CSS, title="進階數據可視化工具 V2", theme=gr.
1693
  # --- 圖表類型改變時更新 UI 元素可見性 (針對兩個圖表) ---
1694
  def update_element_visibility(chart_type):
1695
  """根據圖表類型更新 Y 軸、分組列、大小列的標籤和可見性"""
1696
- # 圓餅圖、環形圖、漏斗圖、樹狀圖:主要關心類別 (X) 和數值 (Y)
1697
- is_pie_like = chart_type in ["圓餅圖", "環形圖", "漏斗圖", "樹狀圖"]
1698
- # 直方圖:主要關心 X 軸分佈,Y 軸是計數
1699
- is_histogram = chart_type == "直方圖"
1700
- # 箱型圖、小提琴圖:Y 軸是數值,X 軸是可選的分組
1701
- is_box_violin = chart_type in ["箱型圖", "小提琴圖"]
1702
- # 甘特圖:Y 軸是開始時間,分組列是結束時間
1703
- is_gantt = chart_type == "甘特圖"
1704
- # 熱力圖:需要 X, Y 和分組列
1705
- is_heatmap = chart_type == "熱力圖"
1706
- # 雷達圖:Theta (X), R (Y), Color (Group)
1707
- is_radar = chart_type == "雷達圖"
1708
-
1709
- # Y 軸的標籤和需求
1710
- y_label = "Y / 數值"
1711
- y_needed = True
1712
- if is_histogram:
1713
- y_label = "Y軸 (自動計數)"
1714
- y_needed = False # 不需要用戶選擇 Y
1715
- elif is_pie_like:
1716
- y_label = "數值列 (用於大小/值)"
1717
- elif is_box_violin:
1718
- y_label = "數值列"
1719
- elif is_gantt:
1720
- y_label = "開始時間列"
1721
- elif is_radar:
1722
- y_label = "徑向值 (R)"
1723
-
1724
-
1725
- # 分組列的標籤和需求
1726
- group_label = "分組列"
1727
- group_needed = chart_type in [
1728
- "堆疊長條圖", "百分比堆疊長條圖", "群組長條圖", "水平長條圖", # 可選
1729
- "折線圖", "多重折線圖", "階梯折線圖", # 可選
1730
- "區域圖", "堆疊區域圖", "百分比堆疊區域圖", # 可選
1731
- "散點圖", "氣泡圖", # 可選
1732
- "箱型圖", "小提琴圖", # 可選,用於 X 軸
1733
- "熱力圖", # 必需,用於 Index 或 Columns
1734
- "雷達圖", # 必需,用於區分線條
1735
- "極座標圖" # 可選
1736
- ]
1737
- if is_gantt:
1738
- group_label = "結束時間列"
1739
- group_needed = True
1740
- elif is_heatmap:
1741
- group_label = "行/列 分組"
1742
- group_needed = True
1743
-
1744
-
1745
- # 大小列的需求
1746
- size_label = "大小列"
1747
- size_needed = chart_type in ["氣泡圖", "散點圖"] # 甘特圖顏色也可以用 Size 列
1748
- if is_gantt:
1749
- size_label = "顏色列 (可選)"
1750
- size_needed = True # 允許選擇顏色列
1751
-
 
 
 
 
 
 
 
 
 
 
 
 
1752
 
1753
- # 返回更新對象
1754
- return (
1755
- gr.update(label=y_label, visible=y_needed),
1756
- gr.update(label=group_label, visible=group_needed),
1757
- gr.update(label=size_label, visible=size_needed)
1758
- )
1759
 
1760
  # 將 chart_type 的變化連接到更新函數,並應用到兩個圖表的對應組件
1761
  chart_type_1.change(
@@ -1775,4 +1814,6 @@ with gr.Blocks(css=CUSTOM_CSS, title="進階數據可視化工具 V2", theme=gr.
1775
  if __name__ == "__main__":
1776
  # 在 Hugging Face Spaces 上,通常不需要 share=True
1777
  # debug=True 可以在開發時提供更詳細的錯誤信息
1778
- demo.launch(debug=True)
 
 
 
1273
  """
1274
  Gradio 應用程式:進階數據可視化工具
1275
  作者:Gemini
1276
+ 版本:1.1 (修正 Dataframe height 參數錯誤)
1277
  描述:此部分包含 Gradio UI 介面定義、事件處理和應用程式啟動。
1278
  """
1279
 
 
1329
  gr.HTML('<div class="section-title">2. 數據預覽與導出</div>')
1330
  with gr.Group(elem_classes=["card"]):
1331
  gr.Markdown("下方將顯示載入或解析後的數據預覽。")
1332
+ # 修正:移除 height 參數
1333
+ data_preview = gr.Dataframe(label="數據表格預覽", interactive=False)
1334
 
1335
  with gr.Row():
1336
  export_format = gr.Dropdown(
 
1536
  preview_df = df if df is not None else pd.DataFrame()
1537
  # 更新所有列選擇下拉列表
1538
  col_updates = update_columns(df) # 返回 4 個 Dropdown 更新對象
1539
+ # 檢查 col_updates 是否為 None 或空,避免解包錯誤
1540
+ if col_updates is None or len(col_updates) != 4:
1541
+ # 如果更新失敗,返回原始狀態或空更新
1542
+ print("警告: update_columns 未返回預期的 4 個組件更新。")
1543
+ # 返回空更新避免錯誤,但下拉列表可能不會更新
1544
+ return [df, status_msg, preview_df] + [gr.update()] * 8
1545
+
1546
  return [df, status_msg, preview_df] + list(col_updates) * 2 # 更新兩組下拉列表
1547
 
1548
  upload_button.click(
 
1608
  outputs=[chart_output_1]
1609
  )
1610
  # 當任何相關設置改變時,自動更新圖表一
1611
+ # 將 .change 事件綁定移到循環外,以避免在循環內部重複定義函數
1612
+ def auto_update_chart_1(*inputs):
1613
+ # inputs 是一個包含所有 chart_inputs_1 值的元組
1614
+ # 需要將這些值解包傳遞給 create_plot
1615
+ # 注意:Gradio 的 .change 會將所有 input 組件的當前值按順序傳遞
1616
+ # 我們需要確保這個順序與 create_plot 的參數順序匹配
1617
+ # 或者,更安全的方式是從 inputs 列表創建一個字典
1618
+ # 但這裡直接按順序傳遞應該可行,因為 inputs 列表順序固定
1619
+ return create_plot(*inputs)
1620
+
1621
  for input_component in chart_inputs_1:
1622
  # 避免狀態變量觸發無限循環,只監聽 UI 組件的變化
1623
+ if not isinstance(input_component, gr.State): # 排除 State 組件
1624
  input_component.change(
1625
+ auto_update_chart_1, # 使用輔助函數
1626
+ inputs=chart_inputs_1, # 傳遞所有需要的輸入
1627
  outputs=[chart_output_1]
1628
  )
1629
 
1630
+
1631
  # --- 圖表一:導出圖表 ---
1632
  download_button_1.click(
1633
  download_figure,
 
1639
  def apply_recommendation(rec_dict):
1640
  """將推薦字典應用到圖表一的 UI 組件"""
1641
  if not isinstance(rec_dict, dict):
1642
+ # 如果輸入不是字典,返回空更新列表
1643
+ print("警告:apply_recommendation 收到非字典輸入。")
1644
+ return [gr.update()] * 5
1645
+
1646
+ # 安全地獲取推薦值,提供默認值
1647
+ chart_type_val = rec_dict.get("chart_type")
1648
+ x_col_val = rec_dict.get("x_column")
1649
+ agg_func_val = rec_dict.get("agg_function")
1650
+ y_col_val = None if agg_func_val == "計數" else rec_dict.get("y_column")
1651
+ group_col_val = rec_dict.get("group_column", "無") # 默認為 "無"
1652
+
1653
+ # 返回更新列表
1654
  return [
1655
+ gr.Dropdown(value=chart_type_val),
1656
+ gr.Dropdown(value=x_col_val),
1657
+ gr.Dropdown(value=y_col_val),
1658
+ gr.Dropdown(value=group_col_val),
1659
+ gr.Dropdown(value=agg_func_val)
1660
  ]
1661
 
1662
+
1663
+ # 創建一個狀態來存儲推薦結果,以便在 then() 中使用
1664
+ recommendation_state = gr.State({})
1665
+
1666
  recommend_button_1.click(
1667
  recommend_chart_settings,
1668
  inputs=[data_state],
1669
+ outputs=[recommendation_state] # 將結果存儲在狀態中
1670
  ).then(
1671
  apply_recommendation,
1672
+ inputs=[recommendation_state], # 從狀態讀取推薦結果
1673
  outputs=[chart_type_1, x_column_1, y_column_1, group_column_1, agg_function_1]
1674
  ).then( # 應用推薦後立即觸發一次圖表更新
1675
+ create_plot, # 直接調用 create_plot
1676
+ inputs=chart_inputs_1, # 再次收集所有當前輸入
1677
  outputs=[chart_output_1]
1678
  )
1679
 
 
1705
  outputs=[chart_output_2]
1706
  )
1707
  # 當任何相關設置改變時,自動更新圖表二
1708
+ def auto_update_chart_2(*inputs):
1709
+ return create_plot(*inputs)
1710
+
1711
  for input_component in chart_inputs_2:
1712
+ if not isinstance(input_component, gr.State):
1713
  input_component.change(
1714
+ auto_update_chart_2,
1715
  inputs=chart_inputs_2,
1716
  outputs=[chart_output_2]
1717
  )
 
1726
  # --- 圖表類型改變時更新 UI 元素可見性 (針對兩個圖表) ---
1727
  def update_element_visibility(chart_type):
1728
  """根據圖表類型更新 Y 軸、分組列、大小列的標籤和可見性"""
1729
+ try: # 添加 try-except 塊以捕獲潛在錯誤
1730
+ # 圓餅圖、環形圖、漏斗圖、樹狀圖:主要關心類別 (X) 和數值 (Y)
1731
+ is_pie_like = chart_type in ["圓餅圖", "環形圖", "漏斗圖", "樹狀圖"]
1732
+ # 直方圖:主要關心 X 軸分佈,Y 軸是計數
1733
+ is_histogram = chart_type == "直方圖"
1734
+ # 箱型圖、小提琴圖:Y 軸是數值,X 軸是可選的分組
1735
+ is_box_violin = chart_type in ["箱型圖", "小提琴圖"]
1736
+ # 甘特圖:Y 軸是開始時間,分組列是結束時間
1737
+ is_gantt = chart_type == "甘特圖"
1738
+ # 熱力圖:需要 X, Y 和分組列
1739
+ is_heatmap = chart_type == "熱力圖"
1740
+ # 雷達圖:Theta (X), R (Y), Color (Group)
1741
+ is_radar = chart_type == "雷達圖"
1742
+
1743
+ # Y 軸的標籤和需求
1744
+ y_label = "Y軸 / 數值"
1745
+ y_needed = True
1746
+ if is_histogram:
1747
+ y_label = "Y軸 (自動計數)"
1748
+ y_needed = False # 不需要用戶選擇 Y
1749
+ elif is_pie_like:
1750
+ y_label = "數值列 (用於大小/值)"
1751
+ elif is_box_violin:
1752
+ y_label = "數值列"
1753
+ elif is_gantt:
1754
+ y_label = "開始時間列"
1755
+ elif is_radar:
1756
+ y_label = "徑向值 (R)"
1757
+
1758
+
1759
+ # 分組列的標籤和需求
1760
+ group_label = "分組列"
1761
+ group_needed = chart_type in [
1762
+ "堆疊長條圖", "百分比堆疊長條圖", "群組長條圖", "水平長條圖", # 可選
1763
+ "折線圖", "多重折線圖", "階梯折線圖", # 可選
1764
+ "區域圖", "堆疊區域圖", "百分比堆疊區域圖", # 可選
1765
+ "散點圖", "氣泡圖", # 可選
1766
+ "箱型圖", "小提琴圖", # 可選,用於 X
1767
+ "熱力圖", # 必需,用於 Index 或 Columns
1768
+ "雷達圖", # 必需,用於區分線條
1769
+ "極座標圖" # 可選
1770
+ ]
1771
+ if is_gantt:
1772
+ group_label = "結束時間列"
1773
+ group_needed = True
1774
+ elif is_heatmap:
1775
+ group_label = "行/列 分組"
1776
+ group_needed = True
1777
+
1778
+
1779
+ # 大小列的需求
1780
+ size_label = "大小列"
1781
+ size_needed = chart_type in ["氣泡圖", "散點圖"] # 甘特圖顏色也可以用 Size 列
1782
+ if is_gantt:
1783
+ size_label = "顏色列 (可選)"
1784
+ size_needed = True # 允許選擇顏色列
1785
+
1786
+
1787
+ # 返回更新對象
1788
+ return (
1789
+ gr.update(label=y_label, visible=y_needed),
1790
+ gr.update(label=group_label, visible=group_needed),
1791
+ gr.update(label=size_label, visible=size_needed)
1792
+ )
1793
+ except Exception as e:
1794
+ print(f"Error in update_element_visibility: {e}")
1795
+ # 返回默認更新以避免應用程序崩潰
1796
+ return (gr.update(), gr.update(), gr.update())
1797
 
 
 
 
 
 
 
1798
 
1799
  # 將 chart_type 的變化連接到更新函數,並應用到兩個圖表的對應組件
1800
  chart_type_1.change(
 
1814
  if __name__ == "__main__":
1815
  # 在 Hugging Face Spaces 上,通常不需要 share=True
1816
  # debug=True 可以在開發時提供更詳細的錯誤信息
1817
+ # 增加 server_port 以便在本地運行時指定端口,避免衝突
1818
+ # demo.launch(debug=True, server_port=7860)
1819
+ demo.launch(debug=True) # 保持原樣,Gradio 會自動選擇端口