
PythonでGUIライブラリを使ってみたかったので、FreeSimpleGUIライブラリを使って簡単なプログラムの作成をするべくGeminiに聞いてみた。
ライブラリをインストールする
新たにインストールするライブラリ
- requests
Open-Meteoにリクエストを送るために必要 - FreeSimpleGUI(旧PySimpleGUI)
GUIを構築するために必要
仮想環境にインストールする場合
$ pip3 install requests FreeSimpleGUI
通常の環境にインストールする場合
$ pip3 install requests FreeSimpleGUI --break-system-packages
※通常の環境にインストールする場合にはLinux Mintのシステム保護機能を無視するための --break-system-packages フラグを付ける必要がある。(自己責任で)
プログラムの解説
プログラムの解説はGoogle Geminiに生成してもらったものを若干修正した。
プログラムは大きく分けて**「①画面の作成と待機(メインループ)」「②データ取得と変換(裏側の処理)」「③画面の書き換え(表示の更新)」**の3つの要素で構成されています。
1. プログラムの全体的な動作の流れ
スクリプトを起動(python3 shizuoka_weather.py)してから終了するまでの流れは以下の通りです。
起動と初期設定:
プログラムが実行されると、まず一番下の if __name__ == ‘__main__’: から main() 関数が呼び出されます。
画面(GUI)の構築:
main() の中で、タイトルやテキスト、ボタンの配置(レイアウト)が定義され、ウィンドウが生成されます。
初回のデータ取得:
ウィンドウが表示されると同時に update_display(window) が1回だけ自動で呼ばれ、現在の天気を取得して画面に表示します。
イベントループ(待機状態):
while True: のループに入り、ユーザーが何か操作をするまでプログラムは待機します。
ボタン操作による分岐:
「最新に更新」ボタンを押す ➔ 再度 update_display(window) が呼ばれ、最新データを取得して画面を書き換えます。
「終了」ボタン、またはウィンドウの「✕」を押す ➔ ループを抜け出してプログラムを終了します。
2. 各関数の役割と内容
スクリプト内に定義した主要な関数の内容です。
① get_weather_data() (気象データの取得)
このプログラムの「心臓部」です。インターネットを通じてOpen-MeteoのAPIから静岡市の気象データを引っ張ってきます。
通信先: url = “https://api.open-meteo.com/v1/forecast”
パラメータ: 静岡市の緯度・経度、欲しいデータ(気温、湿度、気圧、天候コード)を指定します。
キャッシュ対策の工夫: nocache: time.time() をつけることで、「毎回違うURL(パラメータ)」でリクエストを送り、Linux環境やネットワークが古いデータ(キャッシュ)を返すのを防いでいます。
結果の返却: 取得したJSONデータから必要な部分を抜き出し、辞書型(dictionary)にまとめて返します。
② get_weather_description(code) (天候コードの翻訳)
APIは天候を「0」や「61」といったWMO(世界気象機関)の数字コードで返してきます。この関数は、その数字を「快晴」や「弱い雨」といった日本語のテキストに変換する「辞書」の役割を果たします。
③ update_display(window) (画面表示の更新処理)
ボタンが押されたときや起動時に、データの取得から画面の書き換えまでを一手に引き受ける「現場監督」です。
通信時刻の記録: まず、プログラムが動いた瞬間のPCの現在時刻を取得し、「最終通信確認」として画面を更新します(これにより、フリーズしていないことが分かります)。
データの受け取り: get_weather_data() を呼び出して、先ほどの気象データを受け取ります。
画面の書き換え: 受け取ったデータ(API側のデータ時刻、天候、気温、湿度、気圧)を、FreeSimpleGUIの機能(window[‘-KEY-‘].update(…))を使って、画面の該当箇所にサクッと上書きします。
④ main() (メイン処理とGUI設定)
全体の土台となる部分です。
sg.theme(‘SystemDefaultForReal’) でLinux Mintの標準デザインに馴染む色合いを設定。
layout = […] のリスト構造で、「どこに」「どんな文字やボタンを置くか」を設計図のように記述しています。各項目に付けた key=’-TEMP-‘ などの名前が、あとでデータを書き換えるための「目印」になっています。
今回のスクリプトの優れたポイント
APIの仕様を理解した設計: 「API側は15分間隔でしかデータが更新されない」という特性を考慮し、「APIのデータ時刻(鮮度)」と「アプリが通信した時刻(動作確認)」を明確に分けたことで、ユーザーが混乱しない親切な作りになっています。
エラーに強い: try…except 構文を入れているため、もし一時的にインターネットが切れていても、プログラムが突然クラッシュ(強制終了)せず、「エラー」や「データ取得失敗」として優しく知らせてくれます。
Pythonプログラム
import requests
import FreeSimpleGUI as sg
from datetime import datetime
import time
# 静岡市の緯度と経度
LATITUDE = 34.9756
LONGITUDE = 138.3828
def get_weather_data():
url = "https://api.open-meteo.com/v1/forecast"
params = {
"latitude": LATITUDE,
"longitude": LONGITUDE,
"current": "temperature_2m,relative_humidity_2m,surface_pressure,weather_code",
"timezone": "Asia/Tokyo",
"nocache": time.time()
}
try:
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
current = data.get("current", {})
# APIが持っている「データの有効時刻」
raw_time = current.get("time", "")
if raw_time:
dt = datetime.fromisoformat(raw_time)
api_time = dt.strftime("%H:%M")
else:
api_time = "--:--"
return {
"api_time": api_time,
"temp": current.get("temperature_2m", "--"),
"hum": current.get("relative_humidity_2m", "--"),
"press": current.get("surface_pressure", "--"),
"code": current.get("weather_code", -1)
}
except Exception as e:
print(f"Error: {e}")
return None
def get_weather_description(code):
weather_map = {
0: "快晴", 1: "晴れ", 2: "一部曇り", 3: "曇り",
45: "霧", 48: "着氷性の霧", 51: "弱い霧雨", 53: "霧雨", 55: "強い霧雨",
61: "弱い雨", 63: "雨", 65: "強い雨", 80: "にわか雨", 95: "雷雨"
}
return weather_map.get(code, f"不明({code})")
def update_display(window):
# 1. まず「アプリが通信を試みた時刻」を即座に更新
exec_time = datetime.now().strftime("%H:%M:%S")
window['-EXEC_TIME-'].update(f"最終通信確認: {exec_time}")
window.refresh()
# 2. データを取得
data = get_weather_data()
if data:
# 3. 各項目を更新
window['-API_TIME-'].update(f"{data['api_time']} 時点のデータ")
window['-WEATHER-'].update(get_weather_description(data['code']))
window['-TEMP-'].update(f"{data['temp']} ℃")
window['-HUMIDITY-'].update(f"{data['hum']} %")
window['-PRESSURE-'].update(f"{data['press']} hPa")
else:
sg.popup_error("データの取得に失敗しました。ネット接続を確認してください。")
def main():
sg.theme('SystemDefaultForReal') # Linux Mintのデスクトップに馴染む色調
layout = [
[sg.Text('静岡市 気象情報', font=('Helvetica', 16, 'bold'))],
[sg.Text('通信状態:', size=(10, 1)), sg.Text('---', key='-EXEC_TIME-', text_color='darkgreen')],
[sg.HorizontalSeparator()],
# API側のデータ時刻(ここが15分間隔で変わる部分)
[sg.Text('データ時刻:', size=(10, 1)), sg.Text('--:-- 時点のデータ', key='-API_TIME-', font=('Helvetica', 10, 'italic'))],
[sg.Text('天候:', size=(10, 1)), sg.Text('--', key='-WEATHER-', size=(20, 1), font=('Helvetica', 12, 'bold'))],
[sg.Text('気温:', size=(10, 1)), sg.Text('-- ℃', key='-TEMP-', size=(20, 1))],
[sg.Text('湿度:', size=(10, 1)), sg.Text('-- %', key='-HUMIDITY-', size=(20, 1))],
[sg.Text('気圧:', size=(10, 1)), sg.Text('-- hPa', key='-PRESSURE-', size=(20, 1))],
[sg.HorizontalSeparator()],
[sg.Button('最新に更新', key='-REFRESH-', button_color=('white', '#2c3e50')), sg.Button('終了')]
]
window = sg.Window('Weather Checker', layout, finalize=True)
update_display(window)
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, '終了'):
break
if event == '-REFRESH-':
update_display(window)
window.close()
if __name__ == '__main__':
main()