def select_all(self): for var in self.city_vars.values(): var.set(True)
def clear_all(self): for var in self.city_vars.values(): var.set(False)
def get_weather(self): selected = [city for city, var in self.city_vars.items() if var.get()] if not selected: self.result_text.insert(tk.END, "都市を選択してください\n") return
イトケンさんの投稿
(投稿ID: 5659)
前回は、お天気アプリを作成し、メインフォームに4都市(①横浜、②大阪、③広島、④山口)の
ラジオボタンを設置し、天気を知りたい都市をラジオボタンで選択し、結果を表示するアプリを
作成しました。
今回は、前回作成したコードを基に日本全国の政令指定都市20都市について天気を調べられるように
改変しました。具体的には、20都市分のチェックボックスを配置し、チェックした都市の天気を
調べられるようにしました。
講座ではthreadを用いた並列処理で作成するよう指示がありましたが、まず、順次処理で作成してみました。
そして次に同時並行版を作成し実行結果を比較してみました。
◆日本の政令指定都市お天気調査【順次処理】アプリ
チェックボックスで選択した都市の天気が1都市ずつ順次表示されました。
次にthreadを用いて同時並行処理するよう改変しました。
◆日本の政令指定都市お天気調査【並列処理】アプリ
取り急ぎ、ご報告いたします。やったことは前回の宿題をベースにAI(クロード)に
・日本の政令指定都市(20か所)の天気を調査するフォームに改変
・各政令指定都市にチェックボックスを設置して調べたい都市を選択できる
・threadを使って、同時並行処理する
と指示しただけです。
threadを使って、同時並行処理すると20都市のお天気がバッと同時表示されるので気持ちイイですね(^▽^)/
AIユーザさんのコメント
(コメントID: 8702)
素晴らしい取り組みですね!順次処理版から並列処理版への改変、お疲れ様でした。以下に、あなたの取り組みに対するフィードバックと、さらなる改善のためのアドバイスをいくつかお伝えします。
### フィードバック
1. **順次処理から並列処理への移行**:
- 順次処理から並列処理への移行は、アプリケーションのパフォーマンスを大幅に向上させます。特に、複数の都市の天気情報を同時に取得する際に、ユーザーエクスペリエンスが向上します。
2. **UIの設計**:
- チェックボックスを用いた都市選択の実装は、ユーザーにとって直感的で使いやすいです。
3. **エラーハンドリング**:
- 天気情報の取得時にエラーが発生した場合の処理も適切に行われており、ユーザーに対して何が起きたのかが明確に伝わります。
### 改善のためのアドバイス
1. **スレッドの管理**:
- 現在の実装では、各都市ごとにスレッドを生成していますが、スレッド数が増えるとシステムに負荷がかかる可能性があります。Pythonの`concurrent.futures.ThreadPoolExecutor`を使うことで、スレッドプールを管理し、効率的に並列処理を行うことができます。
2. **UIのレスポンス性**:
- 並列処理中もUIが応答性を保てるようにするため、`tkinter`の`after`メソッドを活用して、定期的にUIを更新する方法も検討してみてください。
3. **コードのリファクタリング**:
- コードの可読性と再利用性を高めるために、共通処理を関数やメソッドに分割することを考えてみてください。
引き続き、素晴らしいアプリケーション開発を楽しんでください!何か質問があれば、気軽にお尋ねください。
小川 慶一さんのコメント
(コメントID: 8703) 添付ファイルのダウンロード権限がありません
小川 慶一さんのコメント
(コメントID: 8704)
以下の2つは、csvファイルにしてそれを読み込むように改変してみてください。
そういう仕組みにすることで、プログラムのソースコードを変更しなくても、csvファイルを変更することで、天気情報取得対象の都市や表示を変更することができます。
結果も、フォーム上で出力する以外にcsv等の形で出力できるようにできるとさらに良いです。
(「結果情報を元にグラフを作る」といった次の処理を実装するにしてもよい準備になります)
イトケンさんのコメント
(コメントID: 8705) 添付ファイルのダウンロード権限がありません
返信(助言)ありがとうございます。
天気を取得するために使った「天気API(wttr.in)」が英語で
天気を表示するため、AI(クロード)に頼んで日本語変換し
てもらったのですが、コードがゴチャゴチャして可読性が
下がってしまいました。
CSVにまとめる方法を教えていただき、大変スッキリしました(^^)/
また、CSVファイルに自分の住所地である山口市を追加するだけで、
アプリ画面に山口市が表示され、天気情報を取得できるように
なりました(^▽^)/
"""
政令指定都市天気アプリ - CSV設定ファイル版
"""
import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
import threading
import urllib.request
import json
import csv
from datetime import datetime
class WeatherApp(tk.Tk):
def __init__(self):
super().__init__()
self.title("政令指定都市天気アプリ(CSV版)")
self.geometry("700x600")
# CSVファイルから設定を読み込み
self.cities = self.load_cities_csv()
self.weather_dict = self.load_weather_csv()
if not self.cities:
messagebox.showerror("エラー", "cities.csvが読み込めません")
self.destroy()
return
self.city_vars = {city: tk.BooleanVar() for city in self.cities}
self.results = {}
self.create_widgets()
def load_cities_csv(self):
"""都市データをCSVから読み込み"""
for encoding in ['utf-8', 'shift_jis', 'cp932']:
try:
cities = {}
with open('cities.csv', 'r', encoding=encoding) as f:
reader = csv.reader(f)
next(reader) # ヘッダー行をスキップ
for row in reader:
if len(row) >= 2: # 最低2列あることを確認
cities[row[0]] = row[1] # 1列目=日本語名, 2列目=英語名
print(f"cities.csv読み込み成功 ({encoding}): {len(cities)}都市")
return cities
except Exception as e:
print(f"cities.csv読み込み失敗 ({encoding}): {e}")
return {}
def load_weather_csv(self):
"""天気変換辞書をCSVから読み込み"""
for encoding in ['utf-8', 'shift_jis', 'cp932']:
try:
weather_dict = {}
with open('weather.csv', 'r', encoding=encoding) as f:
reader = csv.reader(f)
next(reader) # ヘッダー行をスキップ
for row in reader:
if len(row) >= 2: # 最低2列あることを確認
weather_dict[row[0]] = row[1] # 1列目=英語, 2列目=日本語
print(f"weather.csv読み込み成功 ({encoding}): {len(weather_dict)}項目")
return weather_dict
except Exception as e:
print(f"weather.csv読み込み失敗 ({encoding}): {e}")
return {}
def create_widgets(self):
# 都市選択
frame1 = ttk.Frame(self)
frame1.pack(fill=tk.X, padx=10, pady=5)
ttk.Label(frame1, text="都市選択:", font=("Arial", 12)).pack(anchor=tk.W)
# チェックボックス(4列)
cb_frame = ttk.Frame(frame1)
cb_frame.pack(fill=tk.X, pady=5)
for i, city in enumerate(self.cities):
ttk.Checkbutton(cb_frame, text=city, variable=self.city_vars[city]).grid(
row=i // 4, column=i % 4, sticky=tk.W, padx=15)
# ボタン
btn_frame = ttk.Frame(self)
btn_frame.pack(fill=tk.X, padx=10, pady=5)
ttk.Button(btn_frame, text="全選択", command=self.select_all).pack(side=tk.LEFT, padx=5)
ttk.Button(btn_frame, text="全解除", command=self.clear_all).pack(side=tk.LEFT, padx=5)
ttk.Button(btn_frame, text="天気取得", command=self.get_weather).pack(side=tk.RIGHT, padx=5)
# 結果表示
ttk.Label(self, text="結果:", font=("Arial", 12)).pack(anchor=tk.W, padx=10)
self.result_text = scrolledtext.ScrolledText(self, height=25, font=("Consolas", 9))
self.result_text.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
def select_all(self):
for var in self.city_vars.values():
var.set(True)
def clear_all(self):
for var in self.city_vars.values():
var.set(False)
def get_weather(self):
selected = [city for city, var in self.city_vars.items() if var.get()]
if not selected:
self.result_text.insert(tk.END, "都市を選択してください\n")
return
self.result_text.delete(1.0, tk.END)
self.result_text.insert(tk.END, f"取得中... ({len(selected)}都市)\n{'=' * 50}\n")
self.results.clear()
# 並行処理
threads = []
for city in selected:
thread = threading.Thread(target=self.fetch_weather, args=(city,))
threads.append(thread)
thread.start()
# 完了待機
threading.Thread(target=self.wait_results, args=(threads,)).start()
def fetch_weather(self, city):
try:
api_city = self.cities[city]
url = f"https://wttr.in/{api_city}?format=j1"
with urllib.request.urlopen(url, timeout=10) as response:
data = json.loads(response.read().decode('utf-8'))
current = data['current_condition'][0]
today = data['weather'][0]
# 天気を日本語に変換
weather_en = current['weatherDesc'][0]['value']
weather_jp = self.weather_dict.get(weather_en, weather_en)
self.results[city] = f"""{city}
天気: {weather_jp}
気温: {current['temp_C']}℃ (最高{today['maxtempC']}℃/最低{today['mintempC']}℃)
湿度: {current['humidity']}% 風速: {current['windspeedKmph']}km/h
"""
except Exception as e:
self.results[city] = f"{city}: エラー - {str(e)}\n"
def wait_results(self, threads):
for thread in threads:
thread.join()
self.after(0, self.display_results)
def display_results(self):
self.result_text.delete(1.0, tk.END)
self.result_text.insert(tk.END, f"天気情報 - {datetime.now().strftime('%Y/%m/%d %H:%M')}\n")
self.result_text.insert(tk.END, "=" * 50 + "\n\n")
for city, result in self.results.items():
self.result_text.insert(tk.END, result + "\n")
self.result_text.insert(tk.END, f"取得完了: {len(self.results)}都市")
if __name__ == "__main__":
WeatherApp().mainloop()
次は、結果をフォーム上で出力する以外にcsv等の形で出力できるようにできるよう
チャレンジしてみます!
AIユーザさんのコメント
(コメントID: 8706)
素晴らしい改善ですね!CSVファイルを使用して都市や天気の情報を管理することで、コードの可読性とメンテナンス性が大幅に向上しています。これにより、新しい都市を追加する際の手間も大幅に軽減されましたね。
次に取り組むべきステップとして、結果をCSVファイルとして出力する方法を考えているとのこと、非常に良い方向性だと思います。以下にそのためのヒントをいくつかご紹介します。
### CSVへの出力方法
1. **CSVファイルに書き出す準備**:
- Pythonの`csv`モジュールを使うことで、簡単にデータをCSV形式で書き出すことができます。
- まずは、天気情報を取得した後にCSVファイルに書き込むための関数を作成しましょう。
2. **CSVファイルへの書き込み関数の実装**:
- 以下のような関数を作成して、取得した天気情報をCSVに書き込むことができます。
```python
import csv
def save_results_to_csv(results):
with open('weather_results.csv', 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ['都市', '天気', '気温', '湿度', '風速']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for city, result in results.items():
# 結果をパースしてCSVに書き込む
lines = result.strip().split('\n')
weather_info = {
'都市': city,
'天気': lines[1].split(': ')[1],
'気温': lines[2].split(': ')[1],
'湿度': lines[3].split(': ')[1].split(' ')[0],
'風速': lines[3].split(': ')[2],
}
writer.writerow(weather_info)
```
3. **天気情報取得後にCSV出力を呼び出す**:
- `display_results`メソッドの中で、天気情報を表示した後に`save_results_to_csv(self.results)`を呼び出すことで、取得した情報をCSVファイルに保存できます。
### さらなる改善
- **エラーハンドリング**: CSVファイルへの書き込み時にエラーが発生した場合の処理も追加しておくと、より堅牢なプログラムになります。
- **ユーザーへの通知**: CSVファイルへの書き込みが完了したら、ユーザーに通知するメッセージを表示することで、ユーザーエクスペリエンスを向上させることができます。
引き続き頑張ってください!何か質問があれば、いつでもお知らせください。