OpenWeatherMapとPySimpleGUIでお天気表示GUIを作る

#はじめに
OpenWeatherMapでデータを取得してPySimpleGUIで天気を表示しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2424954/6928ce55-dbda-8f08-3d89-29eae3fc431a.png)
#OpenWeatherMapについて
実装コードにはOpenWeatherMapのAPIを利用しています。
利用するにはAPIkeyの取得が必要です。
取得方法については以下にまとめられています。
[無料天気予報APIのOpenWeatherMapを使ってみる](https://qiita.com/nownabe/items/aeac1ce0977be963a740)

#実装コード
```python
from tkinter import font
import requests
import json
import PySimpleGUI as sg


class MyWeather:
    API_Endpoint = "http://api.openweathermap.org/data/2.5/forecast"
    API_Key = "あなたのkeyを入力"
    location = "Osaka"
    query = {"q": location, "APPID": API_Key, "lang": "ja"}
    data = requests.get(API_Endpoint, params=query).json()
    WEATHER_LENGTH = 10#天気を返す数 三時間ごと

    def __init__(self):
        pass

    def getTime(self, num):
        # 時間 19XX-01-01 00:00:00のように返ってくる
        time = self.data["list"][num]["dt_txt"]
        return time

    def getIconID(self,num):#デバッグ用
        weather = self.data["list"][num]["weather"]
        icon = weather[0]["icon"]
        return icon
    def getWeather(self, num):  # 天気アイコンのパスを返す

        sunnyImage = "weather/icon/sunny_icon.png"
        fewCloudsImage = "weather/icon/fewClouds_icon.png"
        brokenCloudsImage = "weather/icon/brokenClouds_icon.png"
        cloudyImage = "weather/icon/cloudy_icon.png"
        rainyImgae = "weather/icon/rainy_icon.png"
        snowImage = "weather/icon/snow_icon.png"
        thunderImage = "weather/icon/thunder_icon.png"
        errorImage = "weather/icon/error_icon.png"

        weather = self.data["list"][num]["weather"]
        icon = weather[0]["icon"]
        if icon == "01n" or icon == "01d":
            return sunnyImage
        elif icon == "02d" or icon == "02n":
            return fewCloudsImage
        elif icon == "03d" or icon == "03n":
            return cloudyImage
        elif icon == "04d" or icon == "04n":
            return brokenCloudsImage
        elif icon == "09d" or icon == "09n":
            return rainyImgae
        elif icon == "10d" or icon == "10n":
            return rainyImgae
        elif icon == "11d" or icon == "11n":
            return thunderImage
        elif icon == "13d" or icon == "13n":
            return snowImage
        else:
            return errorImage

    def drawScreen(self):
        sg.theme("Python")#GUIの配色

        title = [
            [sg.Text(str(self.getTime(0))+'～' +
                     str(self.getTime(self.WEATHER_LENGTH-1)+'の'+self.location+'の'+'天気'), font=('メイリオ', 20))]
        ]
        titleFrame = sg.Frame(
            '', [[sg.Column(title, justification='c')]], size=(self.WEATHER_LENGTH*150, 100))


        timeText = [[]]
        timeFrame = [[]]
        timeFrameBig = [[]]
        for i in range(self.WEATHER_LENGTH):
            time = str(self.getTime(i).split(' ', 1)[1])
            time = time.split(':', 1)[0]
            timeText = [sg.Column
                        ([[sg.Text(time+'時', font=('メイリオ', 30))]], justification='c')]
            timeFrame[0].extend(
                [sg.Frame('', [timeText], size=(150, 100))])
        timeFrameBig = [[sg.Frame('', [timeFrame[0]])]]


        icon = [[]]
        iconFrame = [[]]
        iconFrameBig = [[]]
        for i in range(self.WEATHER_LENGTH):
            icon = [
                sg.Column([[sg.Image(self.getWeather(i))]], justification='c')]
            iconFrame[0].extend(
                [sg.Frame('', [icon], size=(150, 120))])
        iconFrameBig = [[sg.Frame('', [iconFrame[0]])]]


        layout = [[titleFrame], [timeFrameBig], [iconFrameBig]]
        window = sg.Window("お天気", layout, resizable=True)
        while True:
            event, values = window.read()
            if event == sg.WIN_CLOSED:
                break

        window.close()

def main():
    hoge = MyWeather()
    hoge.drawScreen()
    return 0


if __name__ == "__main__":
    main()

```
##API部(OpenWeatherMap部) 
```python
class MyWeather:
    API_Endpoint = "http://api.openweathermap.org/data/2.5/forecast"
    API_Key = "あなたのkeyを入力"
    location = "Osaka"
    query = {"q": location, "APPID": API_Key, "lang": "ja"}
    data = requests.get(API_Endpoint, params=query).json()
```
classの先頭でAPIに渡す情報を作っています。
queryの内容やAPIの返答内容(dataの中身)は[OpenWeatherMapのFAQみたいなところ](https://openweathermap.org/current)に書いてあります。
参考にした記事:[APIの仕組みが分かる・使いこなせる人材になれる記事（Pythonコード付き）](https://qiita.com/Saku731/items/6ae290f72e98723f165d)


##描画部(PySimpleGUI部)
```python
    def drawScreen(self):
        sg.theme("Python")#GUIの配色

        title = [
            [sg.Text(str(self.getTime(0))+'～' +
                     str(self.getTime(self.WEATHER_LENGTH-1)+'の'+self.location+'の'+'天気'), font=('メイリオ', 20))]
        ]
        titleFrame = sg.Frame(
            '', [[sg.Column(title, justification='c')]], size=(self.WEATHER_LENGTH*150, 100))


        timeText = [[]]
        timeFrame = [[]]
        timeFrameBig = [[]]
        for i in range(self.WEATHER_LENGTH):
            time = str(self.getTime(i).split(' ', 1)[1])
            time = time.split(':', 1)[0]
            timeText = [sg.Column
                        ([[sg.Text(time+'時', font=('メイリオ', 30))]], justification='c')]
            timeFrame[0].extend(
                [sg.Frame('', [timeText], size=(150, 100))])
        timeFrameBig = [[sg.Frame('', [timeFrame[0]])]]


        icon = [[]]
        iconFrame = [[]]
        iconFrameBig = [[]]
        for i in range(self.WEATHER_LENGTH):
            icon = [
                sg.Column([[sg.Image(self.getWeather(i))]], justification='c')]
            iconFrame[0].extend(
                [sg.Frame('', [icon], size=(150, 120))])
        iconFrameBig = [[sg.Frame('', [iconFrame[0]])]]


        layout = [[titleFrame], [timeFrameBig], [iconFrameBig]]
        window = sg.Window("お天気", layout, resizable=True)
        while True:
            event, values = window.read()
            if event == sg.WIN_CLOSED:
                break

        window.close()
```
描画部分です。


```python
        layout = [[titleFrame], [timeFrameBig], [iconFrameBig]]
        window = sg.Window("お天気", layout, resizable=True)
        while True:
            event, values = window.read()
            if event == sg.WIN_CLOSED:
                break

```
layout内にGUIのレイアウトを記述します。レイアウトについては以下の記事が参考になると思います。

[pythonでも簡単にGUIは作れる](https://qiita.com/konitech913/items/61dc715ddaad54505a29)
[PySimpleGUIのレイアウトの設定方法](https://qlitre-weblog.com/pysimplegui-layout/)

###タイトル部
```python
   def drawScreen(self):
        sg.theme("Python")#GUIの配色

        title = [
            [sg.Text(str(self.getTime(0))+'～' + #self.getTime(i)の返り値→ 19XX-YY-ZZ xx:yy:zz
                     str(self.getTime(self.WEATHER_LENGTH-1)+'の'+self.location+'の'+'天気'), font=('メイリオ', 20))]
        ]
        titleFrame = sg.Frame(
            '', [[sg.Column(title, justification='c')]], size=(self.WEATHER_LENGTH*150, 100))


```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2424954/b81b774c-3d0d-59e2-b775-55e41317fb1c.png)
赤枠の部分です。sg.Text単体では文章を中央揃えにできなさそうなので、sg.Columnを介して中央揃えにしています。

###時間部
```python

        timeText = [[]]
        timeFrame = [[]]
        timeFrameBig = [[]]
        for i in range(self.WEATHER_LENGTH):#self.getTime(i)の返り値→ 19XX-YY-ZZ xx:yy:zz
            time = str(self.getTime(i).split(' ', 1)[1])#xx:yy:zz
            time = time.split(':', 1)[0]#xx
            timeText = [sg.Column
                        ([[sg.Text(time+'時', font=('メイリオ', 30))]], justification='c')]
            timeFrame[0].extend(
                [sg.Frame('', [timeText], size=(150, 100))])
        timeFrameBig = [[sg.Frame('', [timeFrame[0]])]]
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2424954/a4bb01a6-ef80-c1d1-8ad7-0eebe54390bb.png)
赤枠の部分です。リストの入れ子構造を間違えてエラーが頻発しました。
ここもsg.Columnで中央揃えにしています。

###アイコン部
```python
        icon = [[]]
        iconFrame = [[]]
        iconFrameBig = [[]]
        for i in range(self.WEATHER_LENGTH):
            icon = [
                sg.Column([[sg.Image(self.getWeather(i))]], justification='c')]#self.getWeatherの返り値は画像のパス
            iconFrame[0].extend(
                [sg.Frame('', [icon], size=(150, 120))])
        iconFrameBig = [[sg.Frame('', [iconFrame[0]])]]

```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2424954/111d0bac-8b81-ca90-eaac-70d7554ced8b.png)
赤枠の部分です。self.getWeather(i)は画像のパスを返します。
####getWeather
```python
    def getWeather(self, num):  # 天気アイコンのパスを返す

        sunnyImage = "weather/icon/sunny_icon.png"
        fewCloudsImage = "weather/icon/fewClouds_icon.png"
        brokenCloudsImage = "weather/icon/brokenClouds_icon.png"
        cloudyImage = "weather/icon/cloudy_icon.png"
        rainyImgae = "weather/icon/rainy_icon.png"
        snowImage = "weather/icon/snow_icon.png"
        thunderImage = "weather/icon/thunder_icon.png"
        errorImage = "weather/icon/error_icon.png"

        weather = self.data["list"][num]["weather"]
        icon = weather[0]["icon"]
        if icon == "01n" or icon == "01d":
            return sunnyImage
        elif icon == "02d" or icon == "02n":
            return fewCloudsImage
        elif icon == "03d" or icon == "03n":
            return cloudyImage
        elif icon == "04d" or icon == "04n":
            return brokenCloudsImage
        elif icon == "09d" or icon == "09n":
            return rainyImgae
        elif icon == "10d" or icon == "10n":
            return rainyImgae
        elif icon == "11d" or icon == "11n":
            return thunderImage
        elif icon == "13d" or icon == "13n":
            return snowImage
        else:
            return errorImage

```
天気アイコンは[icoon-mono](https://icooon-mono.com/tag/%E5%A4%A9%E6%B0%97/) 様のものをお借りしました。
また、[OpenWetherMap](https://openweathermap.org/weather-conditions)に詳細なアイコンのIDがあります。