2025年8月2日 星期六

Open data 水庫每日營運狀況---Python TKinter

 Open data  水庫每日營運狀況---Python TKinter

主要係彙整現有公告之水庫,水庫名稱、水庫編號、記錄時間、有效容量、呆水位、滿水位、集水區雨量、進水量、取用水量、排洪道流量、溢洪道流量、放流量合計、出水量合計…等資料。本資料集係由各水庫管理單位,資料來源為水利署水利防災中心之災害緊急應變系統內登錄所轄公告水庫之每日營運相關資料。資料庫管理系統所提供資料,主要為提供給關心台灣地區水庫水情之各界,了解目前水庫最新之營運情形,作為旅遊、經濟建設分析、學術研究、防災防汛及水源調配…等之依據。本資料集所示為各水庫每日營運狀況,各項資料由各水庫管理單位在每日上午9時30分前輸入(星期六、日之資料則在星期一統一輸入)。

BasinRainfall(集水區雨量,前一日累積降雨量[單位為毫米])、
Capacity(有效容量,單位為萬立方公尺)、
CrossFlow(放流量合計,單位為萬立方公尺)、
DateTime(統計日期時間,由各水庫管理單位在每日上午輸入前一日水庫蓄水統計資料[星期六、日之資料則在星期一統一輸入])、
DWL(呆水位,單位為公尺)、
Inflow(進水量,單位為萬立方公尺)、
NWLMax(滿水位,單位為公尺)、
Outflow(取用水量,單位為萬立方公尺)、
OutflowDischarge(排洪道流量,單位為萬立方公尺)、
OutflowTotal(出水量合計,單位為萬立方公尺)、
RegulatoryDischarge(溢洪道流量,單位為萬立方公尺)、
ReservoirIdentifier(水庫代碼)、
ReservoirName(水庫名稱)

https://data.gov.tw/dataset/41568
https://opendata.wra.gov.tw/Service/OpenData.aspx?format=json&id=50C8256D-30C5-4B8D-9B84-2E14D5C6DF71


JSON是JavaScript Object Notation的縮寫,是JavaScript程式語言的物件表示法。它是一個輕量級的資料交換語言,最主要的特色是以文字的方式以簡單的格式來描述要交換的資料內容,除了人們容易閱讀之外,對於程式來說也非常容易解析。

在JSON格式中有兩種結構,分別是物件(Object)和陣列(Array)。物件是以大括號{}含括起來的內容,而陣列則是以中括號[]來含括。每一個物件都是一個「{name:value}」的格式,而陣列則是「[value, value]」的型式,這點和串列結構是類似的。



import urllib.request, json, ssl

# 資料來源 URL
url = 'https://data.wra.gov.tw/Service/OpenData.aspx?format=json&id=50C8256D-30C5-4B8D-9B84-2E14D5C6DF71'

# 建立不驗證 SSL 憑證的 context
context = ssl._create_unverified_context()

try:
    # 使用正確的 utf-8-sig 編碼來讀取和解碼 JSON 資料
    with urllib.request.urlopen(url, context=context) as jsondata:
        data = json.loads(jsondata.read().decode('utf-8-sig'))

    # 存取巢狀結構中的資料
    data = data['DailyOperationalStatisticsOfReservoirs_OPENDATA']

    # 迴圈印出水庫名稱和有效容量
    for d in data:
        print("{}:{}:{}".format(d['ReservoirName'], d['Capacity'] , '萬立方公尺'))

except urllib.error.URLError as e:
    print(f"無法連線到資料來源,請檢查網路連線或 URL。錯誤訊息:{e}")
except json.JSONDecodeError as e:
    print(f"JSON 解析錯誤,請檢查資料格式。錯誤訊息:{e}")
except KeyError as e:
    print(f"資料結構錯誤,找不到指定的鍵:{e}")
except Exception as e:
    print(f"發生未知錯誤:{e}")

>>> %Run ex3-1.py
士林攔河堰:68.23:萬立方公尺
日月潭水庫:12949.09:萬立方公尺
烏山頭水庫:7737.941:萬立方公尺
牡丹水庫:2622.08:萬立方公尺
直潭壩:174.98:萬立方公尺
寶山水庫:500.83:萬立方公尺
明德水庫:1242.28:萬立方公尺
集集攔河堰:602.88:萬立方公尺
白河水庫:1404.156:萬立方公尺
西湖水庫:41.41:萬立方公尺
武界壩:60:萬立方公尺
七美水庫:22.5:萬立方公尺
小池水庫:20.5:萬立方公尺
烏溝蓄水塘:2.7:萬立方公尺
瓊林水庫:29.32:萬立方公尺
勝利水庫:18.1842:萬立方公尺
珠螺水壩:0.4746:萬立方公尺
儲水沃上壩:2.2874:萬立方公尺
新山水庫:995.96:萬立方公尺
羅東攔河堰::萬立方公尺
南化水庫:8522.63:萬立方公尺
甲仙攔河堰::萬立方公尺
陽明湖水庫:27.65:萬立方公尺
蓮湖水庫:5.07:萬立方公尺
津沙水庫:4.5806:萬立方公尺
東湧水庫:8.5047:萬立方公尺
西勢水庫:39.6:萬立方公尺
翡翠水庫:36997.6:萬立方公尺
榮華壩:6.4:萬立方公尺
寶山第二水庫:3379.78:萬立方公尺
永和山水庫:2993.43:萬立方公尺
虎頭埤水庫:106.1:萬立方公尺
成功水庫:121:萬立方公尺
榮湖水庫:35.96:萬立方公尺
擎天水庫:23.65:萬立方公尺
金湖水庫:38.11:萬立方公尺
鯉魚潭水庫:11584.35:萬立方公尺
石岡壩:108.93:萬立方公尺
內埔子水庫:69:萬立方公尺
德元埤水庫:229.8:萬立方公尺
阿公店水庫:1467.924:萬立方公尺
興仁水庫:74:萬立方公尺
赤崁地下水庫:51:萬立方公尺
金沙水庫:43.47:萬立方公尺
儲水沃水庫:4.1742:萬立方公尺
德基水庫:18796.18:萬立方公尺
霧社水庫:2868.56:萬立方公尺
蘭潭水庫:921.804:萬立方公尺
鹽水埤水庫:47.1:萬立方公尺
東衛水庫:32.3:萬立方公尺
西安水庫:23.6:萬立方公尺
太湖水庫:148.08:萬立方公尺
山西水庫:19.02:萬立方公尺
秋桂山水庫:3.1539:萬立方公尺
坂里水庫:14.1115:萬立方公尺
石門水庫:20526:萬立方公尺
青潭堰:69.6:萬立方公尺
大埔水庫:529.9:萬立方公尺
明湖下池水庫:757.3:萬立方公尺
湖山水庫:5041.002:萬立方公尺
仁義潭水庫:2465.68:萬立方公尺
曾文水庫:45563:萬立方公尺
鏡面水庫:96.567:萬立方公尺
澄清湖水庫:261.36:萬立方公尺
田埔水庫:48.62:萬立方公尺
菱湖水庫:8.25:萬立方公尺
蘭湖:17.62:萬立方公尺
津沙一號水庫:1.2625:萬立方公尺
后沃水庫:41.6251:萬立方公尺
>>> 





import urllib.request, json, ssl
import tkinter as tk
from tkinter import ttk, messagebox

# 資料來源 URL
url = 'https://data.wra.gov.tw/Service/OpenData.aspx?format=json&id=50C8256D-30C5-4B8D-9B84-2E14D5C6DF71'
# 建立不驗證 SSL 憑證的 context
context = ssl._create_unverified_context()

def get_reservoir_data():
    """從 URL 獲取水庫資料並返回一個包含水庫資訊的字典列表。"""
    try:
        with urllib.request.urlopen(url, context=context) as jsondata:
            data = json.loads(jsondata.read().decode('utf-8-sig'))
        return data['DailyOperationalStatisticsOfReservoirs_OPENDATA']
    except Exception as e:
        messagebox.showerror("資料載入錯誤", f"無法獲取水庫資料。\n錯誤訊息:{e}")
        return None

def show_detail_window(reservoir_data):
    """建立一個新的視窗來顯示特定水庫的詳細資訊。"""
    detail_window = tk.Toplevel(root)
    detail_window.title(f"{reservoir_data['ReservoirName']} 詳細資訊")
    detail_window.geometry("500x550")
    
    # 使用 ttk.LabelFrame 來更好地組織內容
    info_frame = ttk.LabelFrame(detail_window, text="水庫詳細數據", padding=(10, 5))
    info_frame.pack(padx=10, pady=10, fill="both", expand=True)

    # 顯示所有詳細資訊,並使用不同的顏色標示單位
    info_labels = [
        ("水庫名稱", reservoir_data.get('ReservoirName', 'N/A')),
        ("水庫代碼", reservoir_data.get('ReservoirIdentifier', 'N/A')),
        ("統計日期時間", reservoir_data.get('DateTime', 'N/A')),
        ("集水區雨量", f"{reservoir_data.get('BasinRainfall', 'N/A')} (mm)"),
        ("有效容量", f"{reservoir_data.get('EffectiveCapacity', 'N/A')} (萬立方公尺)"),
        ("滿水位", f"{reservoir_data.get('NWLMax', 'N/A')} (公尺)"),
        ("呆水位", f"{reservoir_data.get('DWL', 'N/A')} (公尺)"),
        ("進水量", f"{reservoir_data.get('Inflow', 'N/A')} (萬立方公尺)"),
        ("出水量合計", f"{reservoir_data.get('OutflowTotal', 'N/A')} (萬立方公尺)"),
        ("排洪道流量", f"{reservoir_data.get('OutflowDischarge', 'N/A')} (萬立方公尺)"),
        ("溢洪道流量", f"{reservoir_data.get('RegulatoryDischarge', 'N/A')} (萬立方公尺)"),
        ("放流量合計", f"{reservoir_data.get('CrossFlow', 'N/A')} (萬立方公尺)"),
        ("取用水量", f"{reservoir_data.get('Outflow', 'N/A')} (萬立方公尺)"),
    ]

    for i, (label_text, value) in enumerate(info_labels):
        label = ttk.Label(info_frame, text=f"{label_text}:")
        label.grid(row=i, column=0, sticky="w", padx=5, pady=2)
        
        value_label = ttk.Label(info_frame, text=value, foreground="blue")
        value_label.grid(row=i, column=1, sticky="w", padx=5, pady=2)

def create_main_window(data):
    """建立主視窗,顯示所有水庫名稱按鈕。"""
    global root
    root = tk.Tk()
    root.title("全台水庫資料查詢")
    root.geometry("400x600")

    main_frame = ttk.Frame(root, padding="10")
    main_frame.pack(fill="both", expand=True)
    
    # 建立一個 Canvas 和 Scrollbar
    canvas = tk.Canvas(main_frame)
    scrollbar = ttk.Scrollbar(main_frame, orient="vertical", command=canvas.yview)
    scrollable_frame = ttk.Frame(canvas)

    scrollable_frame.bind(
        "<Configure>",
        lambda e: canvas.configure(
            scrollregion=canvas.bbox("all")
        )
    )

    canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
    canvas.configure(yscrollcommand=scrollbar.set)
    
    canvas.pack(side="left", fill="both", expand=True)
    scrollbar.pack(side="right", fill="y")

    title_label = ttk.Label(scrollable_frame, text="請點選水庫以查看詳細資訊", font=("Helvetica", 14, "bold"))
    title_label.pack(pady=10)

    # 根據資料建立按鈕
    for reservoir in data:
        btn_text = reservoir.get('ReservoirName', '未知水庫')
        # 使用 lambda 函數將水庫資料傳遞給 show_detail_window 函數
        btn = ttk.Button(scrollable_frame, text=btn_text, command=lambda r=reservoir: show_detail_window(r))
        btn.pack(pady=5, padx=10, fill="x")

    root.mainloop()

# 程式主邏輯
if __name__ == "__main__":
    reservoir_data = get_reservoir_data()
    if reservoir_data:
        create_main_window(reservoir_data)





import urllib.request, json, ssl
import tkinter as tk
from tkinter import ttk, messagebox

# 資料來源 URL
url = 'https://data.wra.gov.tw/Service/OpenData.aspx?format=json&id=50C8256D-30C5-4B8D-9B84-2E14D5C6DF71'
# 建立不驗證 SSL 憑證的 context
context = ssl._create_unverified_context()

# 全域變數,用於儲存所有水庫資料
all_reservoir_data = None
# 全域字典,用於儲存右側詳細資訊的 Label 控件,方便後續更新
detail_labels = {}

def get_reservoir_data():
    """從 URL 獲取水庫資料並返回一個包含水庫資訊的字典列表。"""
    try:
        with urllib.request.urlopen(url, context=context) as jsondata:
            data = json.loads(jsondata.read().decode('utf-8-sig'))
        return data['DailyOperationalStatisticsOfReservoirs_OPENDATA']
    except Exception as e:
        messagebox.showerror("資料載入錯誤", f"無法獲取水庫資料。\n錯誤訊息:{e}")
        return None

def update_detail_view(reservoir_data):
    """
    這個函式會根據使用者點選的水庫資料,
    來更新右側面板中各個 Label 的文字內容。
    """
    # 定義要顯示的欄位及其對應的中文名稱和單位
    info_dict = {
        "水庫名稱": reservoir_data.get('ReservoirName', 'N/A'),
        "水庫代碼": reservoir_data.get('ReservoirIdentifier', 'N/A'),
        "統計日期時間": reservoir_data.get('DateTime', 'N/A'),
        "集水區雨量": f"{reservoir_data.get('BasinRainfall', 'N/A')} (mm)",
        "有效容量": f"{reservoir_data.get('EffectiveCapacity', 'N/A')} (萬立方公尺)",
        "滿水位": f"{reservoir_data.get('NWLMax', 'N/A')} (公尺)",
        "呆水位": f"{reservoir_data.get('DWL', 'N/A')} (公尺)",
        "進水量": f"{reservoir_data.get('Inflow', 'N/A')} (萬立方公尺)",
        "出水量合計": f"{reservoir_data.get('OutflowTotal', 'N/A')} (萬立方公尺)",
        "排洪道流量": f"{reservoir_data.get('OutflowDischarge', 'N/A')} (萬立方公尺)",
        "溢洪道流量": f"{reservoir_data.get('RegulatoryDischarge', 'N/A')} (萬立方公尺)",
        "放流量合計": f"{reservoir_data.get('CrossFlow', 'N/A')} (萬立方公尺)",
        "取用水量": f"{reservoir_data.get('Outflow', 'N/A')} (萬立方公尺)",
    }

    # 迴圈更新 detail_labels 字典中每個 Label 的文字
    for key, value in info_dict.items():
        if key in detail_labels:
            detail_labels[key].config(text=value)

def create_main_window(data):
    """建立主視窗,包含左側的水庫按鈕和右側的詳細數據面板。"""
    global root, detail_labels
    root = tk.Tk()
    root.title("全台水庫資料查詢")
    root.geometry("800x600")

    # 建立主框架,用於容納左右兩個子框架
    main_frame = ttk.Frame(root, padding=10)
    main_frame.pack(fill="both", expand=True)

    # 左側框架:顯示水庫按鈕列表
    reservoir_frame = ttk.LabelFrame(main_frame, text="水庫列表", padding=5)
    reservoir_frame.pack(side="left", fill="y", expand=True, padx=5, pady=5)

    # 右側框架:顯示詳細數據
    detail_frame = ttk.LabelFrame(main_frame, text="水庫詳細數據", padding=5)
    detail_frame.pack(side="right", fill="both", expand=True, padx=5, pady=5)

    # 建立一個 Canvas 和 Scrollbar,以便在水庫數量過多時可以滾動
    canvas = tk.Canvas(reservoir_frame)
    scrollbar = ttk.Scrollbar(reservoir_frame, orient="vertical", command=canvas.yview)
    scrollable_frame = ttk.Frame(canvas)

    scrollable_frame.bind(
        "<Configure>",
        lambda e: canvas.configure(
            scrollregion=canvas.bbox("all")
        )
    )

    canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
    canvas.configure(yscrollcommand=scrollbar.set)

    canvas.pack(side="left", fill="both", expand=True)
    scrollbar.pack(side="right", fill="y")
    
    # 調整按鈕排版為 4 列
    if data:
        cols = 4  # 指定列數為 4
        for i, reservoir in enumerate(data):
            row = i // cols
            col = i % cols
            
            btn_text = reservoir.get('ReservoirName', '未知水庫')
            btn = ttk.Button(scrollable_frame, text=btn_text, command=lambda r=reservoir: update_detail_view(r))
            btn.grid(row=row, column=col, sticky="ew", padx=5, pady=5)
            scrollable_frame.grid_columnconfigure(col, weight=1)

    # 初始化右側詳細數據面板的 Labels
    info_titles = [
        "水庫名稱", "水庫代碼", "統計日期時間", "集水區雨量", "有效容量", 
        "滿水位", "呆水位", "進水量", "出水量合計", "排洪道流量", 
        "溢洪道流量", "放流量合計", "取用水量"
    ]

    for i, title in enumerate(info_titles):
        label_title = ttk.Label(detail_frame, text=f"{title}:")
        label_title.grid(row=i, column=0, sticky="w", padx=5, pady=2)
        
        value_label = ttk.Label(detail_frame, text="請選擇水庫", foreground="blue")
        value_label.grid(row=i, column=1, sticky="w", padx=5, pady=2)
        detail_labels[title] = value_label

    root.mainloop()

# 程式主邏輯
if __name__ == "__main__":
    all_reservoir_data = get_reservoir_data()
    if all_reservoir_data:
        # 修正:將 all_reservoir_data 作為參數傳遞
        create_main_window(all_reservoir_data)

沒有留言:

張貼留言

ESP32 (ESP-IDF in VS Code) MFRC522 + MQTT + PYTHON TKinter +SQLite

 ESP32 (ESP-IDF in VS Code) MFRC522 + MQTT + PYTHON TKinter +SQLite  ESP32 VS Code 程式 ; PlatformIO Project Configuration File ; ;   Build op...