RからPySimpleGUIを使ってみた

# 1行でまとめると

[R reticulate](https://rstudio.github.io/reticulate/)で[PySimpleGUI](https://pysimplegui.readthedocs.io/en/latest/
)が使えました

## はじめに

Rには[Shiny](https://shiny.rstudio.com/)というWebアプリケーションをつくるライブラリがあります.  
しかし個人的に記法に慣れないことと, デスクトップアプリケーションには向いていないために別のライブラリを探しました. 

Pythonには良いデスクトップアプリケーションのライブラリがあり, その中でもPySimpleGUIは名前の通り非常に簡潔にGUIアプリが作成できるため, こちらをR環境で使用できるかどうか試してみました.

結果としていくつか細かい注意点はありましたが, 問題なくRでPySimpleGUIが動きましたので報告いたします. 


## 仮想環境の作製

まず`conda`を用いてRとPySimpleGUIをもつ仮想環境を作りました.

```sh
conda create -y -n r-sg r-base r-essentials
conda install -y -n r-sg -c conda-forge pysimplegui
conda activate r-sg
```

PySimpleGUI v4.31.0がインストールできました. 

```sh
conda list | grep pysimplegui
# pysimplegui               4.31.0             pyhd3deb0d_0    conda-forge
```


## Pythonパスの設定

次にRに先ほど作った`r-sg`を認識させたいのですが, 私の環境では`reticulate::use_condaenv("r-sg")`では設定できませんでした.

```R
options(repos = "https://cloud.r-project.org/")
if (!requireNamespace("reticulate", quietly = TRUE)) install.packages("reticulate")
reticulate::use_condaenv("r-sg")
reticulate::py_config()
# python:         /home/t-taro/.local/share/r-miniconda/envs/r-reticulate/bin/python
# libpython:      /home/t-taro/.local/share/r-miniconda/envs/r-reticulate/lib/libpython3.6m.so
# pythonhome:     /home/t-taro/.local/share/r-miniconda/envs/r-reticulate:/home/t-taro/.local/share/# r-miniconda/envs/r-reticulate
# version:        3.6.11 | packaged by conda-forge | (default, Aug  5 2020, 20:09:42)  [GCC 7.5.# 0]
# numpy:          /home/t-taro/.local/share/r-miniconda/envs/r-reticulate/lib/python3.6/# site-packages/numpy
# numpy_version:  1.18.5
```

そのため, 一度Rを再起動させたあとで`RETICULATE_PYTHON`を環境変数に書き込むことで解決しました.

```R
library(tidyverse)
conda_path <- reticulate::conda_list()$python %>% str_subset("r-sg/bin/python")
Sys.setenv(RETICULATE_PYTHON = conda_path)
reticulate::py_config()
# python:         /home/t-taro/anaconda3/envs/r-sg/bin/python
# libpython:      /home/t-taro/anaconda3/envs/r-sg/lib/libpython3.7m.so
# pythonhome:     /home/t-taro/anaconda3/envs/r-sg:/home/t-taro/anaconda3/envs/r-sg
# version:        3.7.8 | packaged by conda-forge | (default, Nov 27 2020, 19:24:58)  [GCC 9.3.0]
# numpy:           [NOT FOUND]
#
# NOTE: Python version was forced by RETICULATE_PYTHON
```

## PySimpleGUIのテスト

ここからは[PySimpleGUIの日本語公式ドキュメント](https://github.com/PySimpleGUI/PySimpleGUI/blob/master/readme.ja.md)にしたがって試していきます.

### 例 1 - ワンショットウィンドウ

注意点は下記のとおりです.  

- `layout`変数の定義のときに`reticulate::r_to_py()`を用いてPythonの`list`に変換する必要がありました.
- `values = window$read()`での返り値が2層のlistのため, `flatten`で1層のlistにしました.

```R
library(tidyverse)
conda_path <- reticulate::conda_list()$python %>% str_subset("r-sg/bin/python")
Sys.setenv(RETICULATE_PYTHON = conda_path)

sg <- reticulate::import("PySimpleGUI")

# ウィンドウの内容を定義する
layout <- list( c(sg$Text("お名前はなんですか？")),
                c(sg$Input()),
                c(sg$Button("はい"))) %>%
        reticulate::r_to_py(.)

# ウィンドウを作成する
window = sg$Window('ウィンドウタイトル', layout)

# ウィンドウを表示し、対話する
values = window$read()

values = values %>% flatten

# 収集された情報で何かをする
glue::glue('ハロー ', values$"0", "! PySimpleGUIを試してくれてありがとう")

# 画面から削除して終了
window$close()
```

### 例 2 - インタラクティブウィンドウ

注意点は以下の2つです. 

- `c(sg$Text(size=c(55L,1L), key="-出力-"))`の`size`の中身は整数型を指定します
- 私の環境では`sg$WINDOW_CLOSED`の返り値は終了ボタンを押す押さないにかかわらず常に`NULL`のようでした. 終了ボタンを押すと`event`にはNULLが返ってくるため, `is.null(event)`で代用することにしました. 

```R
library(tidyverse)
conda_path <- reticulate::conda_list()$python %>% str_subset("r-sg/bin/python")
Sys.setenv(RETICULATE_PYTHON = conda_path)

sg <- reticulate::import("PySimpleGUI")

# ウィンドウの内容を定義する
layout <- list( c(sg$Text("お名前はなんですか？")),
                c(sg$Input(key="-入力-")),
                c(sg$Text(size=c(55L,1L), key="-出力-")),
                c(sg$Button("はい"), sg$Button("終了"))) %>%
        reticulate::r_to_py(.)

# ウィンドウを作成する
window = sg$Window('ウィンドウタイトル', layout)

# イベントループを使用してウィンドウを表示し、対話する
while (TRUE){
    input <- window$read()
    event <- input[[1]]
    values <- input %>% flatten

    # ユーザーが終了したいのか、ウィンドウが閉じられたかどうかを確認してください
    if (event == "終了" || is.null(event))
        break

    # Output a message to the window
    window['-出力-']$update(glue::glue('ハロー ', values$'-入力-',  "! PySimpleGUI をお試しいただきありがとうございます"))
}
# 画面から削除して終了
window$close()

```

## まとめ

R reticulateをもちいてPySimpleGUIのチュートリアルを再現することができました.  
conda環境のセッティングにすこし苦労しましたが, PySimpleGUIはほとんど問題がなく実行できました.

イベントループ内でRの処理してPySimpleGUIに返すこともできそうです. 試してみようと思います.


## 実行環境

<details><summary>sessionInfo</summary><div>

```R
> sessionInfo()
R version 4.0.3 (2020-10-10)
Platform: x86_64-conda-linux-gnu (64-bit)
Running under: Linux Mint 20

Matrix products: default
BLAS/LAPACK: /home/t-taro/anaconda3/envs/r-sg/lib/libopenblasp-r0.3.12.so

locale:
 [1] LC_CTYPE=C.UTF-8       LC_NUMERIC=C           LC_TIME=C.UTF-8       
 [4] LC_COLLATE=C.UTF-8     LC_MONETARY=C.UTF-8    LC_MESSAGES=C.UTF-8   
 [7] LC_PAPER=C.UTF-8       LC_NAME=C              LC_ADDRESS=C          
[10] LC_TELEPHONE=C         LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C   

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] forcats_0.5.0   stringr_1.4.0   dplyr_1.0.2     purrr_0.3.4    
[5] readr_1.4.0     tidyr_1.1.2     tibble_3.0.4    ggplot2_3.3.2  
[9] tidyverse_1.3.0

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.5        pillar_1.4.7      compiler_4.0.3    cellranger_1.1.0 
 [5] dbplyr_2.0.0      tools_4.0.3       lattice_0.20-41   jsonlite_1.7.1   
 [9] lubridate_1.7.9.2 lifecycle_0.2.0   gtable_0.3.0      pkgconfig_2.0.3  
[13] rlang_0.4.9       Matrix_1.2-18     reprex_0.3.0      cli_2.2.0        
[17] rstudioapi_0.13   DBI_1.1.0         haven_2.3.1       withr_2.3.0      
[21] xml2_1.3.2        httr_1.4.2        rappdirs_0.3.1    generics_0.1.0   
[25] vctrs_0.3.5       fs_1.5.0          hms_0.5.3         grid_4.0.3       
[29] tidyselect_1.1.0  reticulate_1.18   glue_1.4.2        R6_2.5.0         
[33] fansi_0.4.1       readxl_1.3.1      modelr_0.1.8      magrittr_2.0.1   
[37] ps_1.4.0          backports_1.2.0   scales_1.1.1      ellipsis_0.3.1   
[41] assertthat_0.2.1  rvest_0.3.6       colorspace_2.0-0  stringi_1.5.3    
[45] munsell_0.5.0     broom_0.7.2       crayon_1.3.4     

```

</div>