pythonとTkeasyGUIで天気情報取得アプリを作る

# はじめに
なにか作りたいなと思い、主にpythonで天気情報取得アプリを作ってみました。

TkEasyGUIを用いてGUIを実装し、 画面から操作出来るようにしてみました。

初めて記事を書くので、お手柔らかにお願いします:sweat_smile:

# 経緯
「新しく言語勉強したい」
「無性に何か作りたい」
「天気情報を取得できるの面白い！」

で作成に至ります。

# 全体の構成
### 画面
TkEasyGUIを使って、2つの画面を作成しています。
①天気情報取得画面
②場所コード検索画面

### コード
①GUI.py(画面表示)
②get_weather.py(天気情報取得)
③search.py(場所コード検索)

以上のようになっています！

# コード
:::note info
画面側
:::

```python
#TkEasyGUIをインポート
import TkEasyGUI as sg
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import get_weather as gw
import search as sr

#テーマ設定
style = ttk.Style()
style.theme_use('classic')

# Frame要素のスタイル設定
style.configure('TFrame', borderwidth=5, relief = 'solid')

#コンポーネント設定
layout1 = [ [sg.Text('天気予報')],
            [sg.Text('場所コードと、予報日を教えてください。')],
            [sg.Text('場所', key = 'city_cd'), sg.Button('参照', key = 'ref'), 
             sg.InputText('', key = 'city'),sg.Text('予報日'), sg.Combo((['今日', '明日', '明後日']), default_value = '今日', key = 'day')],
            [sg.Button('OK', key = 'read'), sg.Button('キャンセル', key = 'cancel')],
            [sg.Text(key = 'output', size = (40, 5))] ]

#ウィンドウの生成
window1 = sg.Window('天気予報', layout1)

# Output用のTextウィジェットを特定 (最後の要素)
output_text = layout1[-1][0]
result_text = layout1[2][2]

try:

    #イベントループ
    while True:
            event1, values1 = window1.read()

            if event1 == 'ref':

                #-------------
                #参照画面(window2)を開く
                #-------------
                layout2 = [ [sg.Text('場所コード検索')],
                            [sg.InputText('', key = 'suggest'), sg.Button('検索', key = 'search')],
                            [sg.Text('', key = 'cd', size = (17, 1)), sg.Button('選択', key = 'choice')] ]

                #検索結果表示要素の指定
                display_text = layout2[2][0]
                #検索結果用変数
                conseq = ''
                
                window2 = sg.Window('場所コード検索', layout2, size = (500, 500))

                while True:
                    event2, values2 = window2.read()

                    if event2 == sg.WIN_CLOSED:
                        break
                
                    elif event2 == 'search':
                        conseq = sr.city_search(values2['suggest'])
                        display_text.update(conseq)
                    
                    if event2 == 'choice':
                        if conseq == '' or display_text == '':
                            messagebox.showwarning("警告", "検索を実行してください。")
                        
                        if conseq != '':
                            result_text.update(conseq)
                            window2.close()
                            break
                    
                window2.close()
                        
            if event1 == sg.WIN_CLOSED:
                break

            if event1 == 'cancel':
                break

            if event1 == 'read':

                if values1['city'] == '' or values1['day'] == '':
                    messagebox.showwarning("警告", "情報を入力してください。")
                    continue

                if values1['day'] == '今日':
                    values1['day'] = 0
                elif values1['day'] == '明日':
                    values1['day'] = 1
                else:
                    values1['day'] = 2
                
                result = gw.get_weather_info(values1['city'], int(values1['day']))
                #print(result) #printはあくまでコンソールに出力
                output_text.update(result)

    window1.close()

except Exception as e:
    print(f"エラーが発生しました:{e}")

```


:::note info
天気情報取得
:::

```python
#HTTPリクエストを行うための「requests」ライブラリをインポート
import requests
import datetime

#天気情報取得メソッド
def get_weather_info(city_no, day):

    try:
        #リクエストurlを作成
        url = f"https://weather.tsukumijima.net/api/forecast?city={city_no}"
        #レスポンスを辞書形式に変換して取得
        response_data = requests.get(url).json()
        #天候の情報を取得　※forecastsをキーに指定
        weather_data = response_data["forecasts"][day]

        #各情報を取得
        date = weather_data['date'] #日付
        title = response_data['title']  #タイトル
        weather = weather_data['telop'] #天候情報
        max_temp = weather_data['temperature']["max"]["celsius"] #最高気温
        min_temp = weather_data['temperature']["min"]["celsius"] #最低気温

        #現在時刻取得
        now = datetime.datetime.now().time().strftime("%H:%M")

        # 明日の降水確率（平均値）
        chanceOfRainOfKey = weather_data['chanceOfRain']

        #今日日付の場合
        if day == 0:
            #過ぎた時間以外の降水確率を取得する
            if now <= "06:00" and chanceOfRainOfKey['T00_06'] != '--%':
                chanceOfRainList = {
                "T00_06": int(chanceOfRainOfKey['T00_06'].replace('%', ''))
                }
            if now <= "12:00" and chanceOfRainOfKey['T06_12'] != '--%':
                chanceOfRainList = {
                "T06_12": int(chanceOfRainOfKey['T06_12'].replace('%', ''))
                }
            if now <= "18:00" and chanceOfRainOfKey['T12_18'] != '--%':
                chanceOfRainList = {
                "T12_18": int(chanceOfRainOfKey['T12_18'].replace('%', ''))
                }
            if now <= "24:00" and chanceOfRainOfKey['T18_24'] != '--%':
                chanceOfRainList = {
                "T18_24": int(chanceOfRainOfKey['T18_24'].replace('%', ''))
                }
        #今日以外の未来日付の場合
        else:
            chanceOfRainList = {
                "T00_06": int(chanceOfRainOfKey['T00_06'].replace('%', '')),
                "T06_12": int(chanceOfRainOfKey['T06_12'].replace('%', '')),
                "T12_18": int(chanceOfRainOfKey['T12_18'].replace('%', '')),
                "T18_24": int(chanceOfRainOfKey['T18_24'].replace('%', ''))
                }
        
        #降水確率の平均を計算
        avgChanceOfRain = sum(chanceOfRainList.values()) / len(chanceOfRainList)

        minTempMsg = ("最低気温を取得することが出来ませんでした" if min_temp is None else f"最低気温は{min_temp}度です")

        results = f"{date}の{title}は、{weather}です。\n最高気温は{max_temp}度\n{minTempMsg}。\n降水確率は{avgChanceOfRain}%です。"
        return results
    
    except requests.exceptions.RequestException as e:
        return f"天気情報の取得に失敗しました: {e}"

    except KeyError as e:
        return f"キーが見つかりません: {e}"

print(get_weather_info('090010', 0))

```

:::note info
検索処理※別画面
:::

```python
def city_search(city_name):

    # 辞書の作成
    codes = {'仙台': '040010',
             '東京': '130010',
             '千葉': '120010',
             '宇都宮': '090010', 
             '大阪': '270000'}

    value = city_name
    if value in codes:
        return codes.get(value)
```

# 挙動

・画面表示
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953952/517b2ac9-84a6-ad9e-4c51-82627bcca1da.png">


・参照ボタンを押下して、検索画面を表示
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953952/e1ac49eb-5d86-7f89-a45d-78990abbb1e0.png">

・検索を実施。そして「選択」ボタンをクリック。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953952/fc1981f4-5fd1-7d00-74b0-b8920b61f91f.png">

・元画面の場所テキストボックスに検索したコードが入る。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953952/bfac9c50-7631-bebb-e08f-f58c4381378b.png">

・そのまま「OK」ボタン押下で、天気情報を取得して表示。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953952/bd64d09c-ef00-b353-64ad-f6ec41e87a15.png">

以上です！

検索画面を経由しなくても、そのまま場所コードを入力すれば天気情報取得は可能です。

# まとめ
初めてpythonで一通りの機能を持ったものを実装しました。GUIというものも知ることができ、よい経験でした...:hugging:

今回は天気情報取得のために場所コードを辞書型変数で用意していますが、情報量が多くなると見にくいだろうし、処理も遅くなるかもしれません。pythonを使って、データベースも触りたいなあと思っています。

コードを行う上で、わからない所をQiitaで質問したときに答えてくださった方がいました。お力添えありがとうございます。

さて、次は何を作りましょう。

# 参考
https://qiita.com/07aikiro/items/de209cb8732362e3f103
https://note.com/programming_sc/n/ndb4a56b7373c
https://qiita.com/sesame/items/93acd90803c8f9edfdfb