【PySimpleGUI】設定ファイルをJSONで簡単に取り扱う

PySimpleGUIで設定ファイルを取り扱いたい時に、`configparser`モジュールを利用したやり方が多く紹介されています。
しかし、PySimpleGUIのみで設定ファイルを取り扱うことができるので紹介します。
日本語の記事が見当たらなかったので投稿しました。

# バージョン
4.60.4

# 公式リファレンス
基本ここ見ればいいです。
[Call reference \- PySimpleGUI](https://www.pysimplegui.org/en/latest/call%20reference/)

# メソッドの説明
- `user_settings_filename(filename = None, path = None)`
設定ファイルのパスとファイル名を指定します。
最初に指定をしなければ、デフォルト値として
path    : C:/Users/<ユーザー名>/AppData/Local/PySimpleGUI/settings
filename: アプリケーション名（Pythonファイル名） + '.json'
が指定されます。（Windowsの場合）
**他のメソッドも、filename,pathを引数で指定するものに関しては同様です。**

- `user_settings_file_exists(filename = None, path = None)`
設定ファイルの存在有無の確認をします。

- `user_settings_load(filename = None, path = None)`
設定ファイルを読み込みます。
設定ファイルがないと空のDictionaryが返されます。

- `user_settings_save(filename = None, path = None)`
設定ファイルを保存します。
`user_settings_load()`で読み込んだDictionaryに変更を加えて`user_settings_save()`を実行すれば
自動的に変更した内容が保存されます。

- `user_settings_write_new_dictionary(settings_dict)`
Dictionaryを指定して設定ファイルを保存します。

- `user_settings_set_entry(key, value)`
設定ファイルの特定のキーに値をセットします。

- `user_settings_get_entry(key, default = None)`
設定ファイルの特定のキーを読み込みます。
`default`引数を指定すれば、キーが存在しなかった場合のデフォルト値を設定可能です。


# サンプルコード
## user_settings_load, save を使ったやり方
```python
import PySimpleGUI as sg

def load_setting_json(cfg_name,cfg_path):
    """
    設定ファイル場所の設定
    user_settings_filename()は、
    指定しない場合下記のデフォルトが使用される
    path    : C:/Users/<ユーザー名>/AppData/Local/PySimpleGUI/settings
    filename: アプリケーション名（Pythonファイル名） + '.json'
    """
    sg.user_settings_filename(cfg_name,cfg_path)

    if not sg.user_settings_file_exists():
        default_dic = {'SETTING_VALUE':1}
        sg.user_settings_write_new_dictionary(default_dic)

    return  sg.user_settings_load()


def read_window_main(settings):
    """
    メイン画面
    """
    menu_bar = [
        ['ファイル(&F)',['設定(&S)']]
    ]

    layout = [
        [sg.MenuBar(menu_bar, size=(5,1), key='-MENU-')],
        [sg.Text('設定値'), sg.Input(key='-INPUT-', default_text=settings['SETTING_VALUE'])]
        ]

    window = sg.Window('設定テスト', layout)

    while True:
        event, values = window.read()
        # ウィンドウのXボタンを押したときの処理
        if event == sg.WIN_CLOSED:
            break
        if event == '設定(S)':
            read_window_settings(settings)
            window['-INPUT-'].update(settings['SETTING_VALUE'])

    window.close()

def read_window_settings(settings):
    """
    設定画面
    """
    layout = [
        [sg.Text('設定値'), sg.Input(key='-SETTING_VALUE-', default_text=settings['SETTING_VALUE'])],
        [sg.Button('保存', key='-SAVE-')]
        ]

    window = sg.Window('設定画面', layout, modal=True)

    while True:
        event, values = window.read()
        # ウィンドウのXボタンを押したときの処理
        if event == sg.WIN_CLOSED:
            break
        if event == '-SAVE-':
            settings['SETTING_VALUE'] = values['-SETTING_VALUE-']
            sg.user_settings_save()
            break

    window.close()


if __name__ == '__main__':
    CFG_PATH = './'
    CFG_NAME = 'config.json'
    settings = load_setting_json(CFG_NAME, CFG_PATH)
    read_window_main(settings)
```

## user_settings_get_entry, set_entry を使ったやり方
save,loadのやり方とわかりやすいようにDIFFで表示させてます。
```diff_python
import PySimpleGUI as sg

def load_setting_json(cfg_name,cfg_path):
    """
    設定ファイル場所の設定
    user_settings_filename()は、
    指定しない場合下記のデフォルトが使用される
    path    : C:/Users/<ユーザー名>/AppData/Local/PySimpleGUI/settings
    filename: アプリケーション名（Pythonファイル名） + '.json'
    """
    sg.user_settings_filename(cfg_name,cfg_path)
-   if not sg.user_settings_file_exists():
-       default_dic = {'SETTING_VALUE':1}
-       sg.user_settings_write_new_dictionary(default_dic)

+   setting_value = sg.user_settings_get_entry('SETTING_VALUE', 1)
+   settings={}
+   settings['SETTING_VALUE'] = setting_value

-   return  sg.user_settings_load()

+   return settings


def read_window_main(settings):
    """
    メイン画面
    """
    menu_bar = [
        ['ファイル(&F)',['設定(&S)']]
    ]

    layout = [
        [sg.MenuBar(menu_bar, size=(5,1), key='-MENU-')],
        [sg.Text('設定値'), sg.Input(key='-INPUT-', default_text=settings['SETTING_VALUE'])]
        ]

    window = sg.Window('設定テスト', layout)

    while True:
        event, values = window.read()
        # ウィンドウのXボタンを押したときの処理
        if event == sg.WIN_CLOSED:
            break
        if event == '設定(S)':
            read_window_settings(settings)
            window['-INPUT-'].update(settings['SETTING_VALUE'])

    window.close()

def read_window_settings(settings):
    """
    設定画面
    """
    layout = [
        [sg.Text('設定値'), sg.Input(key='-SETTING_VALUE-', default_text=settings['SETTING_VALUE'])],
        [sg.Button('保存', key='-SAVE-')]
        ]

    window = sg.Window('設定画面', layout, modal=True)

    while True:
        event, values = window.read()
        # ウィンドウのXボタンを押したときの処理
        if event == sg.WIN_CLOSED:
            break
        if event == '-SAVE-':
            settings['SETTING_VALUE'] = values['-SETTING_VALUE-']
-           sg.user_settings_save()
+           sg.user_settings_set_entry('SETTING_VALUE', values['-SETTING_VALUE-'])
            break

    window.close()


if __name__ == '__main__':
    CFG_PATH = './'
    CFG_NAME = 'config.json'
    settings = load_setting_json(CFG_NAME, CFG_PATH)
    read_window_main(settings)
```

# 動作
- 設定ファイルが存在しない時は、デフォルト値が表示されます。
設定ファイルが存在する時は、設定値が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180204/d86eb5c4-854e-5a2a-e83e-031b31d0f1e2.png)

- メニューバーの「設定」から設定画面を開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180204/443186be-fac8-98c9-b887-4f2ff1b91718.png)

:::note info
ちなみに、MenuBarエレメントは、各種メニューに「&＋アルファベット」をつけることで
Altキーに対応したショートカットになります。
eventを拾うときは「&」を抜いた名称になります。
:::

- 設定画面が開くと、デフォルトの値が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180204/4df0c0bc-0afd-b00f-316a-f53307ef2967.png)

- 設定値を変更して「保存」を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180204/c695e8dc-ad8e-9272-9b5f-1c96ede14fa5.png)


- 設定値が反映されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180204/aa7d2639-e770-a8ba-78af-54bed8c5c930.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180204/8f84cab5-1b1a-649c-8906-6dcbe4da2b73.png)

:::note warn
UI上はInputエレメントのupdateメソッドを実行することで反映しています。
設定ファイルを保存しただけではUIに反映されません。
:::

以上。