Files
bot-rates/bot_rates_gui.py

131 lines
4.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import csv
import io
import threading
import tkinter as tk
import urllib.request
from datetime import datetime
from tkinter import ttk
URL = "https://rate.bot.com.tw/xrt/flcsv/0/day"
TARGETS = [
("USD", "美元"),
("JPY", "日幣"),
("EUR", "歐元"),
]
def fetch_rates():
req = urllib.request.Request(URL, headers={"User-Agent": "Mozilla/5.0"})
with urllib.request.urlopen(req, timeout=10) as resp:
raw = resp.read().decode("utf-8-sig", errors="replace")
reader = csv.reader(io.StringIO(raw))
next(reader, None)
rates = {}
wanted = {code for code, _ in TARGETS}
for row in reader:
if not row:
continue
code = row[0].strip()
if code in wanted:
rates[code] = float(row[13])
return rates
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("台灣銀行 即期賣出匯率")
self.geometry("360x320")
self.configure(bg="#f5f6fa")
self.resizable(False, False)
style = ttk.Style(self)
try:
style.theme_use("vista")
except tk.TclError:
style.theme_use("clam")
style.configure("TFrame", background="#f5f6fa")
style.configure("Title.TLabel",
background="#f5f6fa",
foreground="#1f2937",
font=("Microsoft JhengHei UI", 14, "bold"))
style.configure("Name.TLabel",
background="#ffffff",
foreground="#374151",
font=("Microsoft JhengHei UI", 12))
style.configure("Rate.TLabel",
background="#ffffff",
foreground="#0f766e",
font=("Consolas", 14, "bold"))
style.configure("Hint.TLabel",
background="#f5f6fa",
foreground="#6b7280",
font=("Microsoft JhengHei UI", 9))
style.configure("Accent.TButton",
font=("Microsoft JhengHei UI", 10, "bold"),
padding=6)
container = ttk.Frame(self, padding=(18, 16))
container.pack(fill="both", expand=True)
ttk.Label(container, text="台灣銀行 即期賣出", style="Title.TLabel").pack(anchor="w")
ttk.Label(container, text="資料來源rate.bot.com.tw",
style="Hint.TLabel").pack(anchor="w", pady=(2, 12))
card = tk.Frame(container, bg="#ffffff", highlightthickness=1,
highlightbackground="#e5e7eb")
card.pack(fill="x")
self.rate_labels = {}
for i, (code, name) in enumerate(TARGETS):
row = tk.Frame(card, bg="#ffffff")
row.pack(fill="x", padx=14, pady=10)
ttk.Label(row, text=f"{name} {code}", style="Name.TLabel").pack(side="left")
lbl = ttk.Label(row, text="--", style="Rate.TLabel")
lbl.pack(side="right")
self.rate_labels[code] = lbl
if i < len(TARGETS) - 1:
tk.Frame(card, bg="#f1f2f6", height=1).pack(fill="x", padx=10)
bottom = ttk.Frame(container)
bottom.pack(fill="x", pady=(14, 0))
self.status = ttk.Label(bottom, text="載入中…", style="Hint.TLabel")
self.status.pack(side="left")
self.btn = ttk.Button(bottom, text="重新整理", style="Accent.TButton",
command=self.refresh)
self.btn.pack(side="right")
self.after(100, self.refresh)
def refresh(self):
self.btn.state(["disabled"])
self.status.configure(text="載入中…", foreground="#6b7280")
threading.Thread(target=self._worker, daemon=True).start()
def _worker(self):
try:
rates = fetch_rates()
self.after(0, self._on_success, rates)
except Exception as e:
self.after(0, self._on_error, e)
def _on_success(self, rates):
for code, _ in TARGETS:
value = rates.get(code)
text = f"{value:.4f}" if value is not None else "查無資料"
self.rate_labels[code].configure(text=text)
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.status.configure(text=f"更新時間:{now}", foreground="#6b7280")
self.btn.state(["!disabled"])
def _on_error(self, err):
self.status.configure(text=f"載入失敗:{err}", foreground="#b91c1c")
self.btn.state(["!disabled"])
if __name__ == "__main__":
App().mainloop()