OmniAICreator/Qiita-1.07M huggingface dataset:
Unity で Windows の透過ウィンドウを作る（操作も透過）

# はじめに
VTuber的な、3Dモデルと画面のキャプチャを統合する方法を考えたときに以下があると思う。※実際の方法・最適解は知らない
・ モデルと画面を別撮りして、編集で合成する方法
・ 画面のキャプチャとモデルを並列して表示するアプリケーション

そんな統合方法の内、先に画面へモデルを表示しようというコンセプトがこの記事。

この方法を使うと、モデルを表示するアプリケーションは独立しつつも、キャプチャ画面と同時にモデルをディスプレイに表示できる。

## 動作環境
WindowsAPIを使用しているので、Windows限定
### 確認環境
・Windows10
・Unity(2018.3.2f1)

## 使用例
こんなことができますよって
![wa.gif](https://qiita-image-store.s3.amazonaws.com/0/341662/45927615-c4e5-5799-6391-900dd1fa2b34.gif)
（前面にUnityのアプリケーションを実行しつつも、裏のUnityも操作できる。）

# 使用方法
1. **Camera**の**Clear Flags**を**Solid Color**にする
2. **Camera**の**Background**を黒色にする
3. 
[TransparentWindow.cs](https://gist.github.com/wakagomo/fe74ec4a7b74f2e3299a7f2ecc359722)を適当なオブジェクトにアタッチする

※ソースコードは（[TransparentWindow.cs](https://gist.github.com/wakagomo/fe74ec4a7b74f2e3299a7f2ecc359722)）からダウンロード

※Unity Editor上での動作は把握していない(WindowsAPIを使用している)ので、EXE形式にビルドしてから実行する。

# 解説
詳しくは、MSDNを見れば解決！！　・・・　とはいかないかも？

## GetActiveWindow
```C#:GetActiveWindow
IntPtr windowHandle = GetActiveWindow();
```
これから操作を行うウィンドウハンドルを取得する。

## SetWindowLong
```C#:SetWindowLong
const int GWL_STYLE   = -16;
const int GWL_EXSTYLE = -20;
const uint WS_POPUP = 0x80000000;
const uint WS_EX_LAYERD = 0x080000;
const uint WS_EX_TRANSPARENT = 0x00000020;

SetWindowLong(windowHandle, GWL_STYLE, WS_POPUP);
SetWindowLong(windowHandle, GWL_EXSTYLE, WS_EX_LAYERD | WS_EX_TRANSPARENT);
```

| 変数名 | 意味合い |
|:--|:--|
| GWL_STYLE | ウィンドウスタイルの設定 |
| GWL_EXSTYLE | ウィンドウ拡張スタイルの設定 |
| WS_POPUP | ポップアップウィンドウにする |
| WS_EX_LAYERD | マウスイベントを貫通させる |
| WS_EX_TRANSPARENT | ウィンドウの裏にある画面の描画 |

## SetWindowPos
```C#:SetWindowPos
IntPtr HWND_TOPMOST = new IntPtr(-1);
const uint SWP_NOSIZE = 0x0001;
const uint SWP_NOMOVE = 0x0002;
const uint SWP_NOACTIVE = 0x0010;
const uint SWP_SHOWWINDOW = 0x0040;

SetWindowPos(windowHandle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVE | SWP_SHOWWINDOW);
```
`HWND_TOPMOST `でウィンドウを最前面に表示し続けるように設定する。

## DwmExtendFrameIntoClientArea
```C#:DwmExtendFrameIntoClientArea
MARGINS margins = new MARGINS()
{
    cxLeftWidth = -1
};

DwmExtendFrameIntoClientArea(windowHandle, ref margins);
```
ウィンドウのフレームをクライアントエリアに拡張させる。
`cxLeftWidth = -1`の時はクライアントエリア全体に影響が及ぶ。

## 試した所感
上の説明はMSDNから概要（自己解釈）を取ってきただけで、実際にその通りに動く感じはなかった（少なくとも自分の環境では）。
### ウィンドウのフレームを消し、透明にする
`WS_POPUP + DwmExtendFrameIntoClientArea`
ポップアップウィンドウで消えたフレームを、クライアントエリアに拡張するイメージ。
自分の場合、この設定でウィンドウを透明にできた。

### ウィンドウのマウスイベントを透過する
`WS_EX_LAYERD + WS_EX_TRANSPARENT`
`WS_EX_LAYERD`だけでもよさそうだが、どちらも設定しないとマウスイベントをウィンドウが吸ってしまっていた。

マウスイベントを拾わないようになったのですぐに非アクティブになるが、非アクティブ時でも前面に表示するために`HWND_TOPMOST`を設定する。


# おわりに
使用例こそショボいけど、制限は黒色を使えないだけなので、使い方次第ではデスクトップに常駐したゲームとか作れるかもしれないし、アイディア次第ではもっと面白いアプリケーションが作れるかもしれない。
Nordic  SDK の ble_app_uart でスリープに入らないようにする


ble_app_uart は Nordic の nRF52 の SDK の中にある ble_peripheral カテゴリのサンプルプログラムで、BLE で UART のように送受信するもの。

この中の、

```

#define APP_ADV_DURATION                18000                                       /**< The advertising duration (180 seconds) in units of 10 milliseconds. */
```
でアドバタイズの時間が指定されている。180秒経過するとスリープに入る。これを3000とすると30秒でスリープに入る。では、30000 とするとアドバタイズ期間が5分間になる？　そうはいかなかった。

```
<error> app: ERROR 7 [NRF_ERROR_INVALID_PARAM] at /home/nanbuwks/Downloads/Nordic/SDK/myProjects/singleTest/ble_NordicUART_sleeptest/main.c:583
PC at: 0x0002CB89
<error> app: End of error report
```
ということで、18000より大きな値を指定してもエラーとなる。

さて、APP_ADV_DURATION の期間が経過すると以下の BLE_ADV_EVT_IDLE の処理が行われる。

```


static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
{
    uint32_t err_code;

    switch (ble_adv_evt)
    {
        case BLE_ADV_EVT_FAST:
            err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
            APP_ERROR_CHECK(err_code);
            break;
        case BLE_ADV_EVT_IDLE:
            sleep_mode_enter();
            break;
        default:
            break;
    }
}
```

これについて、以下のように書き換えると希望の動作になる。


```

static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
{
    uint32_t err_code;

    switch (ble_adv_evt)
    {
        case BLE_ADV_EVT_FAST:
            err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
            APP_ERROR_CHECK(err_code);
            break;
        case BLE_ADV_EVT_IDLE:
            //sleep_mode_enter();
            err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
            APP_ERROR_CHECK(err_code);         
            break;
        default:
            break;
    }
}
```
スリープに入らずに、アドバタイジングを再開することで、永続的にアドバタイズできるようになる。
【PHP7】 CURLOPT_POST=true を付与する場合は CURLOPT_POSTFIELDS は必須

罠にハマってなかなか辛かったので残しておきます。

# curl が飛ばない？

プロダクトの目立たない機能で API を叩いている実装があったのですが、今日になってそれが死んでいることが判明。
開発環境やステージング環境では同じコードで正常に動いているので、うーん・・・ :thinking: 

# 調査

ということで調査。

手元で実際に叩いている URL に curl コマンドでリクエストすると正常なレスポンスが返ってきた。
本番サーバーで叩いてみても正常だった。

仕方なくプロダクトコードにログを仕込んでデプロイ。
ログを見てみると curl が false を返している :thinking:
しかも実行速度が早いのでリクエストが飛んでいない様子。

本番サーバーは最近 PHP のバージョンを 7.2 に上げたということで、開発環境とステージング環境の PHP のバージョンを確認してみたところ、 5.6 だった。
ここで、 PHP 7.2 では問題がある実装になっているのかも？と気付く。

# 問題のコード

こんな感じ

```php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $api_url);
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
$result = curl_exec($ch);
```

これが PHP 5.6 では動作しました。

これを PHP 7.2 の本番サーバー上で配置して実行してみると `$result` が `false` になります。

そもそも GET でリクエストする API なんだから `CURLOPT_POST` は不要では？と思い、コメントアウトしてみたところ

```php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $api_url);
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// curl_setopt($ch, CURLOPT_POST, true);
$result = curl_exec($ch);
```

正常なレスポンスが取得できました :tada:

ちなみに、 `CURLOPT_POSTFIELDS` を指定すると動作します。

# PHP のバージョンによる違い

この POST=true, POSTFIELDS なしのコードをいくつかの環境で確認したところこんな感じでした。

## 動作する
* 5.6.11
* 5.6.26
* 7.1.16

## 動作しない
* 7.2.4
* 7.2.14

ということで、 7.2 系ではだめっぽいです。

# 結論

CURLOPT_POST=true を付与する場合は CURLOPT_POSTFIELDS は必須。

PHP 7.2 にして curl がおかしくなったら疑ってみましょう。
あと、本番サーバーとステージングサーバーのバージョンは合わせましょう。

(古のコードと付き合うのはつらい)
データマイニングの観点から見る脳波判別

# はじめに

本記事は以下の記事に補足する位置づけです。
[脳波を機械学習で判別することを考えたときの注意点](https://qiita.com/shu3051/items/52956dc1bad50a8af267)

今回も例によってコード実装による説明ではなく考え方としての記事になります。

# 機械学習とデータマイニングの違い
　機械学習は既知の特徴量を基に未知のデータを分類する手法である反面、データマイニングは集めたデータの中から未知の特徴量を抽出する作業といえます。ただ、昨今のディープラーニングに代表される技術は特徴量を自動的に抽出することが出来るといわれています。しかしこれはあくまで「機械が画像を判別するために必要な特徴量」を自動的に抽出しているのであり、人間は画像を見ることで何がどこにあるか分かるため、人間にとっても未知の特徴量を抽出している訳ではないと考えられます。
　データマイニングは膨大なデータの中からクラスタリングや可視化、統計学を駆使して人間にとっても今まで未知であった特徴量を抽出する作業と言えます。つまり、脳波から思考やタスクを行っているときの特徴量を抽出する作業はデータマイニングを行っていると言えると思います。

# 脳波データを見る視点について
　脳波データはタスクや実験対象や環境によってさまざまなとらえ方ができると思います。例えば「Aを行った後Bを見る」のように時系列を伴うタスク時に計測した脳波は時系列データと見て解析を行う事が有効だと思われます。一方で「ある一定時間、同じ刺激提示を行う」ようなものに対しては波形をパターンとしてみるか、FFTによる周波数解析などによる分析が有効だと思われます。また、個人差があるため多人数で同じパターンが現れるのか、個人ごとに異なるパターンが出るのか分からない事も多くあります。更に同じ人物でも日によって同じタスク時の脳波に差が出ることも多々あります。
このように、有効な特徴量を見つけ出すことの難易度が高いことが脳波の特徴ともいえます。それでも、明らかに差分を判別可能な特徴が個人ごとに何かしら見つかれば、それは有用な特徴として機能する期待が持てます。

# 解析手法について
解析は時系列でもそうでないパターンデータも、「計測」→「多変量ベクトル化」→「次元圧縮＆可視化」の手順が良いと思います。計測は脳波計で被検者の脳波を計測することです。

## 多変量ベクトル化
多変量ベクトル化とは、計測したデータを多次元のベクトルに変換することです。ベクトル化には様々な方法があり、生波形をある時間ごとに区切る事も多変量ベクトルに変換してるととらえることが出来ます。その他FFTによる周波数分析の結果をそのままベクトル化することや、時系列解析で使われる動的時間伸縮法 / DTW (Dynamic Time Warping) やディープラーニングによる埋め込み表現やリカレントニューラルネットワークによるSeq2Seqなど様々な手法があります。分析において一番大切なことは多変量ベクトル化する手法を選ぶところにあります。

## 次元圧縮＆可視化
多変量ベクトル化に用いた手法を評価するために、得られたベクトルを３次元以下に圧縮し、可視化を行う必要があります。
可視化には主成分分析（PCA）やt-SNEなどの手法があります。これらの手法でベクトルが区別可能が判断し、区別することが可能であれば特徴量の抽出に成功したと考えられます。一方で、区別ができなかった場合、以下の理由を切り分ける必要が出てきます。
・多変量ベクトル化に選んだ手法がデータに適していない
・計測した脳波に問題があるか、計測した部位に問題がある
・タスクによる違いが脳波に現れない

#### 多変量ベクトル化に選んだ手法がデータに適していない
こちらを判断するためには、既存の文献や知見として「脳波に差はでる」事が根拠として示されているかが大事になります。つまり、何かしらの方法を試すことで必ず差は出ることが保証されている上で、別の手法でも差が抽出できないか試す場合です。この場合、本当に新しい特徴量というよりは、既存の上に成り立つ改善手法の提案が主だと思います。

例：「運動想起脳波を簡易脳波計で計測する」「fNIRSやfMRI向けタスクを脳波計で行う」


#### 計測した脳波に問題があるか、計測した部位に問題がある
こちらは既存の手法が存在する場合は既存に沿う計測を行うか、既存の手法に示された部位からの信号が、現在計測している部位の信号に影響を与えていると仮説を立てて検証する必要があります。影響を与えているのか確かめるには、既存の文献に示された脳波の特徴と計測された脳波の関係を見る必要があります。具体的な検証を行うための手法が提案できた場合それは新たな成果になると思います。

未知の手法を試している場合は後述の「タスクによる違いが脳波に現れない」事との切り分けが必要になります。

#### タスクによる違いが脳波に現れない
ここまで切り分けを行った場合は、タスクに根拠があるか有識者と相談するか、脳の部位と脳活動の関係を今一度整理する必要があると思います。ここに来るともはやデータマイニングではなく生理学や医学、心理学などの要素が関わるため整理をしっかり行わないとハマります。
議論の後はとにかく実践あるのみで、「計測」→「多変量ベクトル化」→「次元圧縮＆可視化」を繰り返すことが大切です。

脳波分野は根拠がある状態で、闇雲でないならば泥臭く攻めることも時には大事です。


# 最後に
脳波関連の記事を見つけようとすると、論文は見つかるが考え方の説明記事などはまだあまりない印象を受けます。その影響か、脳波関連の調査を始めると、ノウハウを手探りで見つけるようなところから始めることが多いと思われます。一方で機械学習関連などの記事は論文に加えブログで解説や議論がされているのが見受けられます。脳波関連の情報に気軽にアクセスできることも発展には不可欠だと考えていますので、もしご要望等ございましたらコメント等お願いいたします。


脳波に限らず一般的な学習でも、データセットの重要性について下記の記事で述べられています。
特に「付記：人にとっては学習できているか？」は重要なことであると改めて感じました
[機械学習のデータセットの重要性](https://qiita.com/nonbiri15/items/b29fe079d359d531bf85)


簡易脳波計に関する知見などの需要がありましたら、記事を書こうと思います。
Raspberry Pi3 でカメラ映像をgscam経由でros publishする

# 背景
最終的にはROSのgscamノード経由でカメラ映像をpublishしたい。
raspicam_nodeを試したが、数十秒でpublishが止まってしまう（mmalのcallbackが止まっちゃう）。
あまり深堀りしたくないので、gstreamerに手を出してみる。
https://github.com/UbiquityRobotics/raspicam_node/issues/65
他にも報告があがっているので、そのうち解決されるとは思う。。

# 試してみる
## 環境
Raspberry Pi3 model B
Raspbian Stretch (2018-11-13)
事前にraspi-configでカメラを有効にしておくこと

## カメラモジュールのv4l2有効
/etc/modulesに以下を追記

```
bcm2835-v4l2
```

## gstreamerの動作確認
インストール

```
sudo apt-get install gstreamer1.0-tools gstreamer1.0-plugins-good
```

/dev/video*へのアクセス権の設定

```
sudo usermod -a -G video $USER
```

試してみる。
コマンドを実行して、いいところでCtrl-Cで止める。

```shell
pi@raspberrypi:~ $ gst-launch-1.0 -e v4l2src device=/dev/video0  ! jpegenc ! avimux ! filesink location=test_.avi
Setting pipeline to PAUSED ...
Pipeline is live and does not need PREROLL ...
Setting pipeline to PLAYING ...
New clock: GstSystemClock
^Chandling interrupt.
Interrupt: Stopping pipeline ...
EOS on shutdown enabled -- Forcing EOS on the pipeline
Waiting for EOS...
Got EOS from element "pipeline0".
EOS received - stopping pipeline...
Execution ended after 0:00:04.880463524
Setting pipeline to PAUSED ...
Setting pipeline to READY ...
Setting pipeline to NULL ...
Freeing pipeline ...
```

SCPなんかでPCに転送して、再生してみる。
ちゃんと撮れていればOK

## gscamで試す

### ビルド
githubからcatkin_ws/srcにコードをcloneしてビルドする。

```
$ cd catkin_ws/src
$ git clone https://github.com/ros-drivers/gscam
$ cd catkin_ws
$ sudo rosdep install --from-paths src --rosdistro=melodic --ignore-src -y
$ catkin build
```

launchファイル

```xml:v4l_rpi.launch
<launch>
  <!-- This launchfile should bring up a node that broadcasts a ros image
       transport on /webcam/image_raw -->

  <arg name="DEVICE" default="/dev/video0"/>
  <!-- The GStreamer framerate needs to be an integral fraction -->
  <arg name="FPS" default="10/1"/>

  <node ns="v4l" name="gscam_driver_v4l" pkg="gscam" type="gscam" output="screen">
    <param name="camera_name" value="default"/>
    <param name="camera_info_url" value="package://gscam/examples/uncalibrated_parameters.ini"/>
    <param name="gscam_config" value="v4l2src device=$(arg DEVICE) ! video/x-raw,width=640,height=480,framerate=10/1 ! videoconvert"/>
    <param name="frame_id" value="/v4l_frame"/>
    <param name="sync_sink" value="true"/>
  </node>

</launch>
```

### ノードを立ち上げる

```
$ roslaunch gscam v4l_rpi.launch
```

### ROSの入ったPCから確認

```shell
$ rostopic list
```



# 番外編：Camera Module v2壊れてる？
v1は接続できたが、v2を接続するとエラーが出る

```
pi@raspberrypi:~ $ raspistill -o image.jpg
mmal: mmal_vc_component_enable: failed to enable component: ENOSPC
mmal: camera component couldn't be enabled
mmal: main: Failed to create camera component
mmal: Failed to run camera app. Please check for firmware updates
```

アップデートしてみる。

```
pi@raspberrypi:~ $ sudo rpi-update
pi@raspberrypi:~ $ sudo reboot
```

が、ダメ。
試しに他のRaspberry Pi3に接続してみたが駄目だった。

ちなみに私が買ったv2カメラはこれ
https://www.amazon.co.jp/gp/product/B01ER2SKFS/ref=ppx_yo_dt_b_asin_title_o00__o00_s00?ie=UTF8&psc=1
壊れていたのか、壊したのかは謎です。。
VisualStudio_フォームのHighDPI対応(デザイナと実行時の表示が異なる時の対応)

#1.解決したい問題
VisualStudioでフォームアプリケーションを作成する際、デザイナと実行時の表示に差異がある(下図)。
表示が一部崩れ、データグリッドビューのサイズが大きくなり、ウィンドウからはみ出している。
![2019-02-08 (1).png](https://qiita-image-store.s3.amazonaws.com/0/336212/459c2e27-0ee0-9c2c-ee4f-a85013d9944b.png)


#2. 原因
「コントロールパネル→ディスプレイ→テキスト、アプリ、その他の項目」
の拡大率が100%以外になっている。
ただし、拡大率を変更すると見辛くなるため、100%にしたくない。
なお、筆者の設定は下図のとおり。
![2019-02-08 (5).png](https://qiita-image-store.s3.amazonaws.com/0/336212/b7fe2b35-209c-6eb0-1007-73c276b67c75.png)

#3. 解決策
(1)プロジェクトに以下のとおりマニュフェストを追加する。
(2)画像の選択部のコメントアウトを外す(=有効化する)。

![2019-02-08 (2).png](https://qiita-image-store.s3.amazonaws.com/0/336212/45f0b87b-e363-2277-b65a-998b10def601.png)

![2019-02-08 (3).png](https://qiita-image-store.s3.amazonaws.com/0/336212/fdc3e374-58f1-f235-8251-823097b056f7.png)

#4. 結果
デザイナと実行結果が一致した(下図)。
![2019-02-08 (4).png](https://qiita-image-store.s3.amazonaws.com/0/336212/131187f3-dd96-cec3-b1a5-acf360bbf752.png)

#5. 備考
・HighDPIのディスプレイに対する設定を有効化するものらしい(筆者もよくわかってない)。
・筆者のディスプレイは13.5インチ3000×2000ピクセルであり、DPIが高い。
　このため、テキスト拡大率は200%(推奨)にしている(100%は見辛い)。



#6.出典
[田中達彦のブログ：アプリの高DPI(High DPI)対応について 第3回 ～ マニフェストでアプリのDPI対応レベルを変更する ～](https://blogs.msdn.microsoft.com/ttanaka/2014/08/22/dpihigh-dpi-3-12503/)
Go moduleを使ったCircleCIのconfig.yml

# 環境
- AWS Cloud9 (シンガポールリージョンのEC2上で動作）
- Amazon Linux2
- zsh

# Go 1.11より go mod が使えるようになる
## go modとは
https://github.com/golang/go/wiki/Modules

# .circleci/config.yml

```yml

version: 2
jobs:
  build:
    working_directory: /go/src/github.com/teixy/

    docker:
      - image: circleci/golang:1.11.5
        environment:
          GO111MODULE: "on"
    
    steps:
      - checkout

      - restore_cache:
          name: Restore go modules cache
          keys:
              - v1-mod-{{ .Branch }}-{{ checksum "go/go.mod" }}

      - run:
          name: Vendoring
          command: go mod download
          
      - save_cache:
          name: Save go modules cache
          key: v1-mod-{{ .Branch }}-{{ checksum "go/go.mod" }}
          paths:
              - ~/go/pkg/mod/cache

      - run:
          name: Build go binary
          command: |
              go build -v -o go/main go/main.go

workflows:
    version: 2
    build:
      jobs:
        - build

```
Pythonでページ遷移を繰り返しながらスクレイピング

# requestsとBeautifulSoupでページ遷移をしながらPythonでスクレイピング
日本では機械学習などの統計分析のために、クローリング・スクレイピングでデータ収集することが許可されています。
ここではページ遷移をしながらのスクレイピングについて解説します。

スクレイピングのルールや注意点に関しては以下の記事が詳しいのでこちらを参照してください。

https://vaaaaaanquish.hatenablog.com/entry/2017/12/01/064227

# プログラムの構成
構成としては初めに基準となるURLから詳細情報のリンク先のURLを列挙し、そのURLに順にアクセスしてスクレイピング。
そしてURLを一通り巡回したらページ遷移のボタンがあるので、次のページがあればページ遷移。なければプログラム終了という流れです。

概要を図にも示しておきます
![concept.png](https://qiita-image-store.s3.amazonaws.com/0/285306/4c4adfeb-f611-7f21-0046-959640199b6f.png)


# 実装

## 使うモジュール
```python
import requests
from bs4 import BeautifulSoup
import time
import os
import pandas as pd
import codecs
from urllib.parse import urljoin
```

## html取得、解析

```python
res = requests.get(url)
res.raise_for_status()
html = BeautifulSoup(res.text, 'lxml')
```

## htmlから情報抽出
```python
detail_url_list = html.find_all("取得したいURLのhtmlタグ")
next_page = html.find("次のページのURLのhtmlタグ")
```

## ページ遷移

```python
if bool(next_page) == False:
    break
dynamic_url = urljoin(base_url, next_page.a.get("href"))
```

## 全体的な流れ

```python
data_col = ["information1", "information2"]

while True:

    res = requests.get(dynamic_url)
    res.raise_for_status()

    html = BeautifulSoup(res.text, 'lxml')

    detail_url_list = html.find_all("取得したいURLのhtmlタグ")
    next_page = html.find("次のページのURLのhtmlタグ")

    for i in range(len(detail_url_list)):

        res2 = requests.get(urljoin(base_url, detail_url_list[i].a.get("href")))
        res2.raise_for_status()
        html2 = BeautifulSoup(res2.text, 'lxml')

        # 抜き出す情報に合わせて抽出するタグの変更
        information1 = html2.img.get("alt")
        information2 = html2.a.get("href")
        
        s = pd.Series([information1, information2],
                      index=data_col)
        df = df.append(s, ignore_index=True)
        df.to_csv(save_csv)
        time.sleep(5)
        
    if bool(next_page) == False:
        break
    dynamic_url = urljoin(base_url, next_page.a.get("href"))
    time.sleep(5)

```

WEBサイトのHTMLはそれぞれ異なる構造をしていますが、ページ遷移をしながらのスクレイピングはこれらの方法がおおよそ適用できると思います。スクレイピングのインターバルは1秒あれば良心的だそうですが、日本では1秒のインターバルでも攻撃とみなされて捕まった例もあるので長めにしておいた方がいいと思います。
resourceとresourcesの違い

#resources
resourcesは、複数のリソースに対するCRUD処理を行うためのルーティングを生成する（7つのCRUDすべて。）
※リソース名がbooksと複数形になっていることにも注意

#resource
resourceは、ただ１つのリソースに対するCRUD処理を行うためのルーティングを生成する。
※リソース名がbookと単数形になっていることにも注意

こちらの場合は、リソースは１つだけなのでurlに:idを含む必要がない。
そして、一覧画面（indexアクション）が必要ないので、/bookに対するGETリクエストはそのリソースの表示***（showアクション）***であることが分かる。

(例としてrails routesでのpath)

```
/mobile/password_resets/:id(.:format) 　←idが必要 
/mobile/account/edit(.:format)     　　　←idがいらない
```

##つまりは「idがいるかいらないか」。pathにidが含まれなくなる。

resourcesは「人」や「本」などのようにアプリケーション上に「複数存在する」リソースを扱う場合に用いていました。
自身のプロフィールの様に、ログインユーザーからみてアプリケーション上、１つしか存在しない様なリソースが存在します。その様なリソースについて、REST的なルーティングを定義したい場合には、resourceを定義をします。

>たとえば、/profileでは常に「現在ログインしているユーザー自身」のプロファイルを表示し、他のユーザーidを参照する必要がないとします。このような場合には、単数形リソース (singular resource) を使用してshowアクションに (/profile/:idではなく) /profileを割り当てることができます。

参考
https://railsguides.jp/routing.html
https://rails-study.net/resources-resource/
https://qiita.com/Atsushi_/items/bb22ce67d14ba1abafc5
Google Play Developer APIのPurchases.subscriptions: defer を試してみた結果・・・

##〇概要
（公式）https://developers.google.com/android-publisher/api-ref/purchases/subscriptions/defer
Androidアプリ定期購読の有効期限をAPIで自由に設定できるとのことです。
本番環境でのテストは時間がかかりすぎるので、Sandboxでのテスト結果になります。

##〇流れ
OAuthにてアクセストークンを取得
↓
アクセストークン、パッケージ名、サブスクリプションID、課金トークンを利用してAPIを叩く

##〇アクセストークンを取得
https://developers.google.com/android-publisher/authorization
こちらを参考に短い時間使えるアクセストークンを取得
curl -F "client_id=XXXXXXXXX" -F "client_secret=XXXXXXXXX" -F "grant_type=XXXXXXXXX" -F "refresh_token=XXXXXXXXX"　"https://accounts.google.com/o/oauth2/token"

##〇Purchases.subscriptions: deferのAPIを叩く
これが公式のドキュメントを見るだけではわかりづらかった・・・
ネット上にほとんど情報がないので、
"message": "The expected expiry time for the subscription did not match the existing subscription expiry time (maybe due to a concurrent change).
"message": "This API does not support parsing form-encoded input."
"message": "The desired expiry time for the subscription is not valid."
といったエラーと戦いつつ、たどり着いたアクセス方法がこちら

curl -X POST -H "Authorization: アクセストークン" -H "Content-Type: application/json" -d '{"deferralInfo":{"expectedExpiryTimeMillis":1549546368311,"desiredExpiryTimeMillis":1550546368311}}' "https://www.googleapis.com/androidpublisher/v3/applications/パッケージ名/purchases/subscriptions/サブスクリプションID/tokens/課金トークン:defer"

expectedExpiryTimeMillisはPurchases.subscriptions: getで取得した値でないといけません。Sandbox環境ではすぐに時間が変わるので面倒です。
また、POSTが必須で、JSON情報を渡すためヘッダーにそのことを記載。
そして、変更したい有効日時をdesiredExpiryTimeMillisで設定できるのですが、Sandbox環境でも現在日時に近すぎると駄目なようで、どの値なら設定できるかの判断がよくわかりませんでした。
失敗すると"message": "The desired expiry time for the subscription is not valid."が返ってきます。
また、設定に成功してもすぐに有効期限が設定した時間より少しずれて、地域コード(priceCurrencyCode)が"JPY"から"US"に切り替わるという現象が起きました。
（成功例）
{
"newExpiryTimeMillis": "1550546368311"
}

##〇結果
設定できる日時がわからない
設定できても日時に調整が入ってしまうらしい
地域コードがアメリカになる
実際のユーザーの有効期限が本当に問題なく更新されるのか挙動を追うのがしんどい
問題発覚時の対応もできるのかわからない（最悪謝罪と返金）

以上の点から現段階ではサービスでの利用は難しいですね。。。
パイプ演算子の途中結果を後から使う

### やりたいこと

パイプ演算子でつなげている途中結果から、関数に入れた結果を保持して、後からそれを参照したい

[pipe中毒になったら、更にわがままな人間になった](https://qiita.com/uri/items/17c213bf2bca7c95b154) の記事をみてたのですが、うまくできなかったので、調べたメモを残しておく

参考 : [R pipe (%>%) capabilities - storage and partial use?](https://stackoverflow.com/questions/42422215/r-pipe-capabilities-storage-and-partial-use)

### 例

```R
save_to <- function(x, v, fun) {
  var <- substitute(v)
  eval(bquote(.(var) <- .(fun(x))), envir = globalenv())
  x
}

mtcars %>%
  group_by(cyl, carb) %>%
  dplyr::summarise(sgear = sum(gear)) %>%
  save_to(num_cols, ncol) %>%
  kable() %>% 
  kableExtra::kable_styling(full_width = T) %>% 
  kableExtra::add_header_above(c("Group" = num_cols - 1, "Value" = 1))
```

途中で列数を数えた結果をnum_colsに入れて、あとからそれを使っている

### おわりに

magrittrにありそう。pipeRは最近見ないが今でもactiveなのだろうか
O'reilly本はOOO語の単語ででてきている

```text:oreilly.txt
Hands.Machine.Learning.Scikit.Learn.Tensorflow.5225.pdf
the big data market.pdf
introduction-to-machine-learning-mueller(www.ebook-dl.com).pdf
integrated analytics.pdf
data-science-banking-and-fintech.pdf
fluent-python-2015-.pdf
janssens2014.pdf
Programming Perl, 4th Edition.pdf
Programming Python, 4th ed [O`Reilly].pdf
Python for Data Analysis.pdf
Sed & Awk 2nd Edition.pdf
Classic Shell Scripting.pdf
Unix Power Tools.pdf
[Joel_Grus]_Data_Science_from_Scratch_First_Princ.pdf
Learning Perl. 5th Edition.pdf
OReilly.Perl.Cookbook.pdf
```

`pdftotext`でoreilly.txtに書いてあるすべてのpdfファイルをtxt形式に変換し、標準出力に表示します。

```sh:oreilly.txtにあるpdfファイルをすべてテキスト化して標準出力に表示
<oreilly.txt | xargs -I{} pdftotext {} -
```

> **並列処理**
> parallel[^1]コマンドによる並列処理でfor+xargsに勝る処理速度を実現します。
> 3ファイルだけ変換してみた結果2倍以上は早くなっている様子。
>

```shell-session:parallel処理比較
# parallelあり
$ time \ls | parallel -a - "pdftotext {} -" > /dev/null 2>&1
\ls  0.00s user 0.00s system 81% cpu 0.004 total
parallel -a - "pdftotext {} -" > /dev/null 2>&1  2.88s user 0.18s system 252% cpu 1.215 total

# parallelなし
$ time \ls | xargs -I{} pdftotext {} > /dev/null 2>&1
\ls  0.00s user 0.00s system 73% cpu 0.001 total
xargs -I{} pdftotext {} > /dev/null 2>&1  2.64s user 0.11s system 97% cpu 2.807 total
```


`tr`でtxtにある文字列の大文字、小文字を統一し、`grep`で記号を排除します。-oでマッチした文字のみ(only), -Eで拡張正規表現(extended regexp)を使用します。
ソートしてユニークにしてソートして…よくある頻度カウンターにパイプします。
最後にawkで空白揃えし、列を入れ替えます。

```sh:出現単語のカウント
<oreilly.txt | xargs -I{} pdftotext {} - |
  tr '[A-Z]' '[a-z]' |                  # 小文字に正規化
  grep -oE '[a-z]{2,}' |                # 2文字以上の英字(記号・数字を除く)
  sort |
  uniq -c |                             # 行数カウント
  sort -k1nr |                          # 1列目を数字としてソート, 逆順表示
  awk '{printf "%16s %4d\n",$2,$1;}' |  # 列入れ替えと空白埋め
  head                                  # 最初の10行だけ表示

             the 138844
              to 64866
              in 50969
              of 50764
             and 48537
              is 38182
             you 29710
             for 28060
            that 26829
              it 23518
```

theが13万回、toが6万回、inが5万回出現しています。


単語の種類は`wc -l`で行数カウントします。

```sh:単語の種類カウント
<oreilly.txt | xargs -I{} pdftotext {} - |
  tr '[A-Z]' '[a-z]' |                # 小文字に正規化
  grep -oE '[a-z]{2,}' |              # 2文字以上の英字(記号・数字を除く)
  sort |
  uniq -c |                           # 重複カウント
  wc -l                               # 行数カウント

35002
```

ということで「O'reilly本は35002語の単語でできている」という結果になりました。

[次](https://qiita.com/u1and0/items/b9d904d0e4f28c569dd4)


[^1]: [2014-08-10 コマンドを並列に実行するGNU parallelがとても便利](http://bicycle1885.hatenablog.com/entry/2014/08/10/143612)
node-redをRaspberryPi環境で、systemdを使い自動起動する

unit設定ファイルが、node-redをあとからインストールしたため存在しなかった。そのため書く必要があった。
そのためのメモ書き。

この記事をベースに書き直した。[Node-REDをsystemdでサービス化](https://qiita.com/pitekusu/items/f4522f3de850971bf58f) 

これだとバージョンアップするたびに書き換えないといけないので、`node-red`コマンドのRaspberryPi用コマンド`node-red-pi`を使い、起動するようにした。


ユーザー名は適宜読み替えてください。ハードにアクセスするコードを書かなければ、ユーザにrootを指定する必要はありません。セキュリティ上危ないと思います。

```plaintext:node-red.service
[Unit]
After=syslog.target network.target
Documentation=http://nodered.org/

[Service]
Environment="NODE_OPTIONS=--max-old-space-size=128"
Environment="NODE_RED_OPTIONS=--max-old-space-size=128 --userDir /home/<username>/.node-red/"
ExecStart = /usr/bin/node-red-pi $NODE_RED_OPTIONS
WorkingDirectory=/home/<username>/.node-red/
User=<username>
Nice=10
SyslogIdentifier=Node-RED
StandardOutput=syslog
Restart=on-failure
KillSignal=SIGINT

[Install]
WantedBy=multi-user.target
```

オプションについては、こちらを参考にしてください。[Node-RED User Group Japan|設定](https://nodered.jp/docs/configuration)

# 余談

`node-red-start`コマンドだとsudoで起動する必要があり、visudoでパスなしsudoを許可する必要があったり、なぜか--settingsオプションが無視されたりした。そこで悪戦苦闘していた。
`node-red`もしくは`node-red-pi`ならsudoなしで起動できるので、それを用いてサービス化している。


これで、準備ができると例えばこんな事ができます

* [高音質のCloud Text-To-Speechを使ってnode-red + Google Homeで読み上げる](https://qiita.com/tinoue@github/items/4bb71799b40f09446767)
ansibleで変数に入れたyes/noの扱いに苦しんだ

タイトルをどうつければ伝わるか悩んだ結果こうなった・・・

### 背景

Ansibleを使っていると、ある1つの環境だけ特別に実行したいとかあると思います。
（雲行きが怪しいぞ・・・という反応がうかがえます。）
例えば3台立っているアプリケーションサーバに、cronを仕込みたいけど、3台で動かしたくないなーといった場合を考えてみます。
（ちゃんとグループを分けなさい、ケチらずバッチサーバを用意して分けなさいという意見が聞こえてきます。）

こんな感じにインベントリ内に変数`is_main`を持たせました。

``` hosts:hosts
[app]
foo-app-01 is_main=yes
foo-app-02 is_main=no
foo-app-03 is_main=no
```

このyes/noはansibleを書いているとよく出てくる値です。
serviceなら自動実行を開始させるために`enabled: yes`みたいに使います。今回はcronの`disabled`の値として使うイメージです。

次にタスクの内容です。`is_main=yes`なところだけが有効になってほしかったのでタスクはこんな感じにした。

``` tasks/main.yml
cron:
  # ...
  disabled: "{{ is_main == False }}" # NG!! 
```

今回の条件では disabledに入る値は常にFalse になるため、全部の環境でcronが有効になります。ちなみに、`{{ not is_main }}` でも同じです。

やりたい事と違う！


### 原因の推測

何度かdebugタスクで表示して確認した上での推測です。全然裏が取れてないです。

- まずinventoryで定義した変数`is_main`は文字列として扱っている
- ファイル自体はYAMLなので、まず最初にYAMLとして処理される。[YAMLではYes,No等の値はBool値](https://yaml.org/type/bool.html)として解釈される。なので`disabled: no`等の記載なら、期待通り動く。
- 今回の場合、`disabled: "{{ is_main == False }}"`はYAML的にはただの文字列。
- しかし、テンプレート構文なのでJinja2で解釈した結果になる
- Jinja2的には変数`is_main`の値は文字列と解釈して「1文字以上の文字列だから`True`」と判断している
- なのでこの式は、`disabled: "{{ is_main == False }}"` → `disabled: "{{ True == False }}"` → `disabled: False` となる

あっているでしょうか・・・？


### 対応

自然な感じで書きたいのだけど、どうしたら良いか調べたところ bool フィルターを使うと良さそうとのことだった。boolフィルターは良くあるポジティブな値はTrueにしてくれます。今回求めてたやつですね！

``` tasks/main.yml
cron:
  # ...
  disabled: "{{ is_main | bool == False }}"
```

しかしこの見た目は不吉な匂いを感じます。
これなら文字列が入ってくる前提で考えて `{{ is_main == 'no' }}` とかの方がまだ良い気がしないでもない・・・。

やはりグループを分けるべきだったか・・・。

Ansibleはこういうミスは忘れたころに何度も踏んでる気がする・・・頭の悪さを感じてとても辛い。


### 追記: インベントリファイル(INI形式)の変数の扱い

コメントにて、インベントリファイルの変数で対処する方法等を教えていただきました。
また、インベントリファイルのbooleanの扱いについてBlogでまとめておられます。ありがたし。

https://tekunabe.hatenablog.jp/entry/2019/02/09/ansible_ini_bool_vars
CMakeLists.txtでOpenCVを使う


# 経緯


Dlibの[webcamを使った顔検出のサンプル](http://dlib.net/webcam_face_pose_ex.cpp.html)を動かす際、CMakeLists.txtにてOpenCVを見つけさせたかった

# 結論

OpenCV以外の部分は省きます。
ちなみにCMakeLists.txtの例は[dlibのexample/CMakeLists.txt](http://dlib.net/example/CMakeLists.txt.html)がわかりやすかったです。（自分はこれを見た）

```CMake
# OpenCVのパッケージを探させて
find_package(OpenCV REQUIRED)
# インクルードディレクトリを加えて
target_include_directories(wabcam_face_pose_ex
  PRIVATE ${OpenCV_INCLUDE_DIRS}
  )
# ターゲットとリンクさせる
target_link_libraries(wabcam_face_pose_ex ${OpenCV_LIBS})
```

`${OpenCV_INCLUDE_DIRS}`と`${OpenCV_LIBS}`は自動で生成されるようです。`find_package`のおかげということであってますか...?

# 参考URL

* [CMakeLists.txt から始めよう](https://qiita.com/nonbiri15/items/c77c046f0e10f8140cfe)
* [dlib -- example/CMakeLists.txt](http://dlib.net/examples/CMakeLists.txt.html)
React Hooksの掟と舞台裏

React 16.8でついに正式版となったReact Hooksですが、使うにあたっていくつかの掟があります。そして、その掟がどのような理由で存在するのかについても解説していきます。

## React Hooksの掟

0. Hooks関数を関数コンポーネントやCustom Hooksの外から呼んではならない
1. Hooks関数の呼び出しは、関数コンポーネントやCustom Hooksのトップレベルに置かなければならない
2. ある関数コンポーネントについて、Hooks関数の呼び出しは毎回同じ順序・回数でなければならない

## 掟の存在理由

クラスコンポーネントであれば、明確なデータの保管場所として「インスタンス」がありますし、[前に`WeakMap`への保管をしてみた](https://qiita.com/jkr_2255/items/5306e7b7b264792b9b0d)ときも、保存用の`WeakMap`オブジェクトと、キーとなるオブジェクトが存在しました。では、そういったインスタンスとコード上で結びつかない関数コンポーネントの場合、Hooksのデータをどう管理すればいいのでしょうか。

実は、それはReact内部でのコンポーネント管理に組み込まれています。コンポーネントと関連付けたデータ領域が存在して、そこへHooksの実行に関して必要な情報が格納されていきます。

ここで、掟に従った関数呼び出しを行うことで、「単なる関数呼び出し」をHooksのデータ領域へ正しく結び付けられるようになります。つまり、1を破った「コンポーネント外での呼び出し」では対応付けるデータ領域が見つからずエラーとなって、2番目3番目を破って「一貫しないHooksの呼び出し」を行ってしまうと、どれとデータのヒモ付けをすればいいかわからなくなります[^1]（開発版Reactでは`invariant`によるチェックが入っていて、エラーとなります）。

## 掟破りしそうな場面

### 早期リターン

条件を満たしていない場合、レンダリングが最低限の文言あるいは`null`でいいので、早めに`return`したくなることがあるかと思います。しかし、上記のように、Hooksの呼び出しパターンがコンポーネントについて一貫していないとエラーになりますので、「早期リターン」といっても、Hooksをすべてセットした「後」でなければなりません。

```jsx
function SomeComponent({items}) {
  const [value, setValue] = useState(0);
  // if(!items) return null; // ここに書くとHooksの実行がずれる
  useEffect(() => /* 略 */);

  // Hooksのセットが完了してから
  if(!items) return null;
}
```

### コレクションに対して`useEffect`

コレクションの要素全てに何かを仕掛ける場合も、ループさせて`useEffect`、というわけには行きません。逆に、`useEffect`の**中**でループさせる必要があります。

```jsx
function AnotherComponent({collection}) {
  // これはできない！
  collection.forEach(item => useEffect(() => /* 略 */));

  // 中でループさせる
  useEffect(() => {
    collection.forEach(item => /* 略 */);
  });
}
```

### その他

Hooksの含まれる関数コンポーネントを単なる関数として実行することも不可能です（Hooksをコンポーネントと無関係な場面で実行することとなってしまいます）。

## ESLintによる支援
[eslint-plugin-react-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks)というReactのプラグインがあって、この掟を守るためにも役立ちます。

## Reactサイトへのリンク

* [Rules of Hooks](https://reactjs.org/docs/hooks-rules.html)
* [How does React associate Hook calls with components? (Hooks FAQ)](https://reactjs.org/docs/hooks-faq.html#how-does-react-associate-hook-calls-with-components)

[^1]: もちろん、理屈の上では制御構造に入っていても「同じ順序・同じ回数だけ」呼べばいいのでしょうけれど、基本的に制御構造は変化させるためにあるものなので、やめたほうがいいとは思います。
Windows環境でのCOBOL開発

とある理由で数年前からCOBOLを書くようになりました。
COBOL未経験でまず最初に困ったのがIDEやエディタです。
既に開発が終了しているものまたは有料で高額なものが多い中、Windows環境で使えそうな一例を挙げます。

## Visual Studio Code + COBOL Source colouriser
Visual Studio CodeにCOBOLのextentionを入れる。
軽量でVisual Studio Codeの機能も使えるため機能が多くGO TOやPERFORMの定義元にジャンプしてくれる。
COBOL Source colouriserは現在も更新されているので今後も機能追加されそう。


#### サイト
https://marketplace.visualstudio.com/items?itemName=bitlang.cobol

#### 主な機能
 - シンタックスハイライト
 - 入力補完
 - コードの整形
 - アウトライン表示
 - 定義元ジャンプ

#### プラットフォーム
Windows、macOS、Linux

#### インストール方法
1. 以下からVisual Studio Codeをダウンロードしインストールを行う。
https://azure.microsoft.com/ja-jp/products/visual-studio-code/

2. extentionでcobolを検索しCOBOL Source colouriserをインストールする。
　<img src="https://qiita-image-store.s3.amazonaws.com/0/316842/f99e3a00-78f4-8ec7-4d53-c8a05a9e5745.png" width=80%>
　<img src="https://qiita-image-store.s3.amazonaws.com/0/316842/55b8f077-efb6-9867-599d-7ed0d9e6583d.png" width=80%>


## Eclipse + Cobos
EclipseにCOBOLのプラグインを入れる。
Eclipseの機能が使えるため機能は豊富だが重い。Cobosも現在も更新されていて機能も追加されている。
COBOL Source colouriserに比べGO TOやPERFORMの定義元にジャンプは出来ないが、コンパイルまで行えるため用途によっては開発しやすい。

#### サイト
http://cobos.metrixware.com/

#### 主な機能
 - シンタックスハイライト
 - 入力補完
 - コードの整形
 - アウトライン表示
 - コンパイル

#### プラットフォーム
Windows、macOS、Linux

#### インストール方法
1. 以下からEclipseをダウンロードしインストールを行う。
   https://www.eclipse.org/downloads/

2. Eclipseインストール後、以下からCobosをダウンロードしEclipseの新規ソフトウェアのインストールからインストールを行う。
http://cobos.metrixware.com/download/
　<img src="https://qiita-image-store.s3.amazonaws.com/0/316842/0c186d94-3dbb-93a2-464a-cada97804117.png" width=80%>

## OpenCobolIDE
Cobolのみに特化したIDE。
軽量なので、開発当初よく使っていたが現在は更新されておらず他に比べて機能は少ない。
こちらもGO TOやPERFORMの定義元にジャンプは出来ない。

#### サイト
https://launchpad.net/

#### 主な機能
 - シンタックスハイライト
 - 入力補完
 - アウトライン表示
 - コンパイル

#### プラットフォーム
Windows、macOS、Linux

#### インストール方法
1. 以下からOpenCobolIDEをダウンロードしインストールを行う。<br />
https://launchpad.net/cobcide/+download/<br />
　<img src="https://qiita-image-store.s3.amazonaws.com/0/316842/4261bb53-0c24-f1a0-e1e3-41b30b21ca50.png" width=80%>
Anaconda : condaコマンドについて

環境
OS : macOS Mojave
Anaconda : python3.6.7

## conda　基本コマンド一覧
・仮想環境の作成

```
conda create -n (仮想環境名)
```
・仮想環境の作成(Pythonバージョン指定)

```
conda create -n (仮想環境名) python=(バージョン<例:3.5>)
```
・仮想環境の削除

```
conda remove -n (仮想環境名)
```
・仮想環境に入る

```
source activate (仮想環境名)
```
・仮想環境から出る

```
conda deactivate
```
・仮想環境の一覧

```
conda info -e
```
・現在の仮想環境へのパッケージの一覧

```
conda install (パッケージ名)
```
・現在の環境のパッケージ一覧

```
conda list
```

## その他
・Pythonのバージョン確認

```
python --version
```
イージングを選んでみる

ボタンへのマウスオンなど、ちょっとした演出につかうイージング...デフォトルにしていませんか...！？
普段あまり意識しないちょっとした演出につけるイージングのどうするか考えてみたいと思います。
<span style="color: #aaa">(サイトの雰囲気に合わせるのが一番ですが、ここではある程度汎用的に考えてみます)</span>

## イージングの種類
![スクリーンショット 2019-02-08 19.09.33.png](https://qiita-image-store.s3.amazonaws.com/0/27115/96d19037-14fc-cabd-543b-1d2b1227e85f.png)

左から、<span style="font-size: 150%">**ease-in**</span> 、<span style="font-size: 150%">**ease-out**</span> 、<span style="font-size: 150%">**ease-in-out** </span>です。
細かくは他にも色々ありますが、基本はこの三つ + それぞれに強度がある感じ。  
![スクリーンショット 2019-02-08 19.25.37.png](https://qiita-image-store.s3.amazonaws.com/0/27115/c2e7d83a-7564-708c-4cca-5249abbfd69e.png)

<span style="color: #aaa"><span style="font-size: 80%">(よりどりみどり)</span></span>


## どのイージングがオススメか？
個人的に、とりあえず選ぶなら <span style="font-size: 150%">**ease-out** </span>
なぜ！？

## 理由
なぜ、ease-outなのか。
一応僕なりに理由があります。

### 初動が早い
これです！
3つのタイプの中でease-outが一番初動が早いのです。
![スクリーンショット-2019-02-08-19.09.33.png](https://qiita-image-store.s3.amazonaws.com/0/27115/02f182d3-e6d9-bdac-a1af-2b7cb4e615fa.png)
仮にdurationを0.3秒とすると、最初の0.1秒の変化量が一番多いのがease-outです。
次点でease-in-out、一番変化しにくいのはease-inとなっています。

### なぜ初動が早いと良いのか
ユーザーが自分の操作がその反応を引き起こしたと感じられる最長の感覚が約0.1秒<span style="font-size: 80%">※1</span>と言われています。
自己帰属感<span style="font-size: 80%">※2</span>などと言われたりもしますね！

0.1秒の間に変化がないと、ユーザーが感じる「サクサク動く」とか「軽い」みたいな感覚を得にくくなります。
ease-in-outやease-inも変化がないわけではないのですが、変化の量が少ないためユーザーへ与える印象が薄くなりがちです。
その点、ease-outは最初にたくさん変化が起こるため他二つと比べると、良い印象を与えやすいことになります。

<span style="font-size: 80%">**※1** </span>出典：「[UIデザインの心理学―わかりやすさ・使いやすさの法則](https://www.amazon.co.jp/dp/B00VJF2044/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)」(書籍)
<span style="font-size: 80%">**※2** </span>出典：「[融けるデザイン　ハード×ソフト×ネット時代の新たな設計論](https://www.amazon.co.jp/%E8%9E%8D%E3%81%91%E3%82%8B%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3-%E3%83%8F%E3%83%BC%E3%83%89%C3%97%E3%82%BD%E3%83%95%E3%83%88%C3%97%E3%83%8D%E3%83%83%E3%83%88%E6%99%82%E4%BB%A3%E3%81%AE%E6%96%B0%E3%81%9F%E3%81%AA%E8%A8%AD%E8%A8%88%E8%AB%96-%E6%B8%A1%E9%82%8A-%E6%81%B5%E5%A4%AA-ebook/dp/B00TPLKYEM/ref=sr_1_1?s=digital-text&ie=UTF8&qid=1549624792&sr=1-1&keywords=%E8%9E%8D%E3%81%91%E3%82%8B%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3)」(書籍)
## 迷ったらease-out
というわけで極端ではありますが、もし迷うことがあればとりあえずease-outを設定してみて、そこから調整してみてはいかがでしょうか！

とくにボタンやテキストリンクのマウスオンなど、ユーザーがよく使うものにはオススメです。

## いいわけ
「そもそもそこにアニメーションいらなくない？」や「設定するdurationにもよるよね」などいろいろあるとおもいますが、今回はイージングに絞ってみました。
独断と偏見でメモ程度に書いたのであくまで参考までに。
beamer + pdfpc で最強プレゼンテーション

パワポも、LibreOfficeも、Googleスライドも必要ない。
そう、beamerならね。

# beamerとは

TeXでプレゼンが作れる。以上。

# pdfpc

beamerでスライドのPDFを生成してまあ適当なソフト（evinceとか）を使ってプレゼンをするわけですが、どうも使い勝手が良くないです。
一般的なプレゼンソフトによくある、

* 手元のディスプレイでは経過時間を表示、とか
* 次のスライドを表示、
* タイル状にスライドを並べて表示

みたいな機能があればなー。ないかなー。
と思っていたところ、pdfpcと出会いました。

pdfpc:
https://pdfpc.github.io

Ubuntuとかだと

```bash
$ sudo apt install pdf-presenter-console
```
でインストールできて

```bash
$ pdfpc super_presentation.pdf
```
でプレゼンができます。かんたん。

## 動画の表示

まさか動画の表示にも対応しているとは。恐るべし。

beamerを使ってビデオを表示するサンプル:
https://pdfpc.github.io/demo/pdfpc-video-example.zip

やり方は上のzipを落としてきて察してください。

動画はPDFに埋め込まれるわけではなく、pdfpcがgstreamerを使って動的に動画を読み込んでいます。
そのためpdfpc以外のビューワで表示しても何も起こりません。

また、動画の形式もgstreamerが対応しているものである必要があります。
aviとかmp4なら問題なし。

あと、少なくともUbuntuだと動画の再生のためにはgstreamerをインストールしておく必要があります。
これやらなかったら動かなかったです。

```bash
$ sudo apt install gstreamer1.0-gtk3
```

# おわりに

動画すらも再生できてしまったのでもはやbeamer以外の選択肢が自分の中から消えてしまった。
それでは良いbeamerライフを。
kintoneでレコード詳細閲覧画面のヘッダ部分に印刷ボタンを追加するには


kintoneの印刷は標準だと２アクションしなければいけないため、頻繁に印刷する場合は１アクションにするのがベターです。
そのためにヘッダ領域にボタン（kintone UI componentのButtonを使ってみました) を追加して、それをクリックすると印刷画面が表示されるサンプルソースコードを紹介します。

　kintone UI component の Button： https://kintone.github.io/kintone-ui-component/Reference/Button/
　※ 下のコードを動かすには、別途 kintone UI component用のJavascriptファイルとcssファイルをアップロード＋jQueryを追加する必要があります。

```
 //レコード詳細表示イベント
 kintone.events.on(["app.record.detail.show","mobile.app.record.detail.show"],function(e){
      // ヘッダ領域に印刷ボタンを追加
      let btnPrint = new kintoneUIComponent.Button({ text: '印刷/PDF保存' }); 
      $('.gaia-argoui-app-toolbar-statusmenu').append(btnPrint.render()); //renderメソッドを使わないとdom要素にならないので注意！

      // 印刷画面を表示
      btnPrint.on('click',function(e){
          // 閲覧時のURLの例：https://サブドメイン名.cybozu.com/k/18/show#record=4&l.view=5520052&l.q&l.sort_0=f5520035&l.order_0=DESC&l.next=3&l.prev=5
          // 印刷用URLの例　：https://サブドメイン名.cybozu.com/k/18/print?record=4
          let 印刷用URL = location.href.replace('/show#','/print?');
          window.open(印刷用URL);
      });
 });
```
印刷用画面がURLだけで開けることを知りませんでした。
@sy250f さん、ありがとうございます。

参考にしたサイト
 - kintoneの詳細画面にkintoneライクなUIを使った印刷ボタンを置いてみた https://qiita.com/sy250f/items/f8f54ae1443ddbbe22e4
@nuxt/babel-preset-appのtargetsにpackage.jsonのbrowserslistを使う

#はじめに
ChromeBot(Chrome41相当らしい)の対策にBabelのブラウザターゲットの設定をしたくなって調べたのをメモ

#package.json
```package.json
"browserslist": [
  "last 2 versions",
  "ie >= 9",
  "chrome >= 41"
]
```

#nuxt.config.js


```nuxt.config.js
const pkg = require('./package')

/* 中略 */

build: {
  babel: {
    presets({ isServer }) {
      return [
        [
          require.resolve('@nuxt/babel-preset-app'),
          {
            targets: isServer
              ? { node: 'current' }
              : { browsers: pkg.browserslist }
          }
        ]
      ]
    }
  }
}
```

#おわりに
どうせならAutoprefixerのほうと設定を同一にしようとなった結果だったりするのだけど
Chrome41はあくまでChromeBot対策なのでAutoprefixerとは設定を分けたほうが良いのかなーと思ったりもする。
ただ、自分の手元ではそんなにファイルサイズ変わらなかったので同一で良いかなとなった。

#参考
https://github.com/nuxt/nuxt.js/issues/4900
https://github.com/nuxt/nuxt.js/issues/271
CentOS6 yumをつかってWebminのアップグレード（バージョン切り戻しも出来ました。）

## CentOS6 yumをつかってWebminのアップグレード（バージョン切り戻しも出来ます）
アップグレードはGUIからでも出来るのですが、もし動かなくなった際の保険として、バージョンを切り分ける方法を検証しました。（GUIからアップグレードする情報しか見つけれられず、切り戻す方法がわかなかった。）

## 実はかなり簡単でした。(vagrantで検証)（注意：本番環境では未実施）

### 検証してみた。
```
■念のため、Webmin 設定・領域をバックアップ
mkdir tmp
cd tmp
tar cfvzp etc_webmin_20190208.tar.gz /etc/webmin/* 
tar cfvzp var_webmin_20190208.tar.gz /var/webmin/*

■作業前
[webmin-centos6 tmp]# yum list installed |grep webmin
webmin.noarch                       1.740-1                   @/webmin-1.740-1.noarch

■アップデート
yum update http://download.webmin.com/download/yum/webmin-1.900-1.noarch.rpm

[webmin-centos6 tmp]# yum list installed |grep webmin
webmin.noarch                       1.900-1                   @/webmin-1.900-1.noarch

yumで特定のバージョンに戻したい
■バージョン切り戻し
yum downgrade http://download.webmin.com/download/yum/webmin-1.740-1.noarch.rpm

[webmin-centos6 tmp]# yum list installed |grep webmin
webmin.noarch                       1.740-1                   @/webmin-1.740-1.noarch

※webminパッケージサイト： http://download.webmin.com/download/yum/
```
## 動作確認
- 日本語環境設定が引き継がれていた。
- Linuxユーザーの情報も問題なし。
- MySQLのDBの情報も問題なし
- ApacheのWEB領域の情報も問題なし

ぐらいを確認してました。

GUIからアップデートして起動しなくなったなどもあるようでその時は `/etc/webmin/config` を見てみてください。空になってることがあるようです。（自分はならなったが。）
ログはこちら -> webminのエラーログのパス : `/var/webmin/miniserv.error`
Xcodeでなんか知らんけど原因不明なエラーが大量に出る

# Xcodeで原因不明なExpected ~~~ なエラーが出る

## これを読んで解決するかもしれない人

* 意味不明なエラーが出てビルドが通らない人
* その中でEmacsやそのキーバインディングをいつも使っている人
* その上Xcodeを使っている人

## 環境

macOS: 1.14.2
Xcode: 10.1(この問題自体は前々からある模様)

## TL;DR

* Xcode + C++ + Siv3Dでゲームの設計中に謎のエラー
* 謎のエラーというわけではなく、エラーメッセージは単純だが、出る***原因***がわからない
* 出るやつ
    * Expected member name or ';' after declaration specifiers
    * Expected unqualified-id
    * Expected Expression

## どういう所で出たのか
```c++:Looper.hpp
class Looper final {
public:
    Looper();
    bool loop() const;        //Expected Expression
};
```

```c++:Looper.cpp
// 前略
bool Looper::loop() const {
    return true;              //Expected Expression
}
```

```c++:SystemMain.hpp
class SystemMain final {
public:
    SystemMain() = default;;
    
    bool init();
    bool main();
    bool exit();
};
                              //この行でExpected unqualified-id
```

```c++:Define.cpp
class Define final {
public:
    const static int WINDOW_W;
    const static int WINDOW_H;
    
    const static double PI;   //Expected member name or ';' after declaration specifiers
};
```

## 考察

* 文法的に正しいように見える所でも発生している
* 空行ですら発生している
    * 文法的問題ではない？
    * 全角スペースではないことを確認した  
        -> 全角スペース以外の見えない文字  
        -> 制御文字か？

## 検証

問題の部分をバイナリエディタで見てみる

```text:Define.hpp
00000100  20 20 20 20 0a 20 20 20  20 63 6f 6e 73 74 20 73  |    .    const s|
00000110  74 61 74 69 63 20 64 6f  75 62 6c 65 20 13 50 49  |tatic double .PI|
00000120  3b 0a 7d 3b 0a 0a 0a 23  65 6e 64 69 66 20 2f 2a  |;.};...#endif /*|
```

```text:SystemMain.hpp
00000120  65 78 69 74 28 29 3b 0a  7d 3b 0a 13 0a 23 65 6e  |exit();.};...#en|
```

なんかDefine.hppの`0x11D`とSystemMain.hppの`0x12B`入力するはずのない`0x13`が見える...?
Asciiコード表で確認してみると

>| 0x13 | DC3（装置制御３） |

...なんで？

## 原因

* 原因はXcodeでミスってc-x sを押しまくってしまうこと

`Xcode ascii 0x13`とかでググってみても情報が無い、
けどふと思った、いつもEmacs使っているのでよく間違って C-x sをやっている...
-> 入力するとそこに0x13が挿入されることを確認
-> なんか知らんけどXcodeはc-xをすると0x13が挿入されてしまう模様

## 解決策

Preference -> KeyBindingsを見てみると
<img width="412" alt="スクリーンショット 2019-02-08 20.49.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/341127/a64a0f1e-03e2-52b1-a2fb-6bf0cf091680.png">
こんなのを発見、おそらくXcodeでのMark Yankをするために内部的に0x13を入れているのだと思われる(要出典)
どうせこれは使わないので無効化して、今ある0x13を消すと同様の問題は発生しなくなった

Xcodeでも完璧にEmacsのキーバインディングが使いたい....
Googre fromのGASでgetResponseが使えない

#TL;DR

そんなものは無い。

```js
function onFormSubmit(e){
  Logger.log(Object.keys(e.response));
}
/* 
=> [
    getGradableResponseForItem, 
    submit,
    withItemResponse,
    getResponseForItem,
    getRespondentEmail,
    withItemGrade, getId,
    toPrefilledUrl,
    getGradableItemResponses,
    getItemResponses,
    getEditResponseUrl,
    getTimestamp,
    toString]
*/
```

# ```getItemResponses```で回答一覧が取れる

リストで変えるので、インデックスで好きな質問にアクセスできます。

```js
function onFormSubmit(e){
  Logger.log(e.response.getItemResponses()[0].getResponse());
}
```


https://developers.google.com/apps-script/reference/forms/form-response#getItemResponses()
データベース 応用情報技術者試験 対策日記@14

#データベース
##正規化
更新時異常を排除するために、テーブルを分けてデータの重複を排除し、一つのデータを一箇所に保管することによりデータの整合性を保つ行為。

###キー
「全ての属性を一意に特定する属性または属性の組で最小のもの」を**候補キー**という。
例えば、ユーザーテーブルに(ID、メールアドレス、名前)の三つの属性があるとき、ユーザーごとに一意である(ID、メールアドレス)が候補キーとなる。その中で、**NULLを含まないもの**を**主キー**という。メールアドレスは空欄であり得るため、この場合IDが主キーとなる。

###正規化の手順
1. **第一正規形** データベースの1マスにデータが一つだけ入っている状態。
2. **第二正規形** 全ての非キー属性が候補キーの全てに関数従属している状態。候補キーの一部だけに関数従属している属性を排除して、別の表にする。
3. **第三正規形**　さらに、全ての非キー属性が候補キー以外の属性に関数従属していない状態にする。していたら、別の表にする。

[このサイト](http://ext-web.edu.sgu.ac.jp/HIKO/Prog03/SenpaiKyozai/shiohara/formalize.html)がわかりやすい。

##E-R図
ER図とは、リレーションシップの対応関係の種類(1対１、1対多、多対1、多対多)を表した図。色々な書き方があるみたいやから、とりあえずそれだけ理解しとけば大丈夫かと。[こちら](https://www.lucidchart.com/pages/ja/er-diagram)はあくまで一例。

##SQL文
[SQL ZOO](https://sqlzoo.net/wiki/SQL_Tutorial)
これを全部やってSQL文をマスターしましょう。

------
[ホームへ](https://qiita.com/k-y/items/dc2c9a3a8425712253e8)
Omron Groveコネクタ付き　センサーとラズパイをつなげるよ

2019年2月8日オムロン本社にて開催された
Groveコネクタ付きオムロンセンサとラズパイハンズオン (isaax勉強会#25)
の内容です。

![IMG_9439.JPG](https://qiita-image-store.s3.amazonaws.com/0/285344/739a9783-1a58-34a8-e03c-36b059fb961b.jpeg)

絶対圧センサ評価モジュール（Groveコネクタ付）
2SMPB-02E
https://www.switch-science.com/catalog/5329/
をGroverPi+を通して接続します。

詳細は、テキストを見て頂くとして
https://camp.isaax.io/ja/examples/2smpb-02e-raspberry-pi

ミスをしたのは、ブランチの設定です。
![isaax-brunch.jpg](https://qiita-image-store.s3.amazonaws.com/0/285344/188a9692-ce3a-01b8-de4f-1092d1b8122f.jpeg)

ここを最初は、masterにしましょう。
最初からanbientなどにするとデータが表示されません。

![omuronPi.jpg](https://qiita-image-store.s3.amazonaws.com/0/285344/fe6898de-ab66-55c1-34ff-cafe6da53fb0.jpeg)

みんなグラフ表示して、課題をクリアしていきました。
20時30分で完了って結構早いですよね。
![IMG_9440.JPG](https://qiita-image-store.s3.amazonaws.com/0/285344/a911cec7-31bf-f53a-fbe4-9391d800f7b8.jpeg)
SentryをSlackに連携させてAlert Ruleを追加する際にアクセス権限でエラーが出る際の対処

# エラーが発生した状況
* [公式のドキュメント](https://docs.sentry.io/workflow/integrations/global-integrations/slack/)に従ってSentryをSlackにintegrationしようとしていた
* Slackのworkspaceとの連携まではできた
* Alert Ruleを作成しようとRuleを定義し、Save Ruleをクリックしたところ以下のようなエラーが発生した
    * `The slack resource "{チャンネル名}" does not exist or has not been granted access in the hey Slack workspace`
    * Alert Ruleで入力したチャンネル名は間違っていない

# 対処方法
* Slackの該当チャンネルで `/invite @sentry` を行なう

参考: https://github.com/getsentry/sentry/issues/7873

# 参考にさせていただいたサイト

https://github.com/getsentry/sentry/issues/7873

https://docs.sentry.io/workflow/integrations/global-integrations/slack/
GKE上のnginxのアクセスログフォーマットをStackdriverのフォーマットにする

すごく雑に書きます
<img width="766" alt="スクリーンショット 2019-02-08 19.37.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/6046/12995a91-1a6c-33cc-5a20-8df8c157dcff.png">

https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
このフォーマットに当てはまるとStackdriverのコンソール上でタグ付け表示などがされ、フィルタリングしやすくなります

# やり方

```
log_format acc_json_format escape=json '{"severity": "INFO",'
    '"timestamp": "$time_iso8601",'
    '"httpRequest": {'
            '"requestMethod": "$request_method",'
            '"requestUrl": "$request",'
            '"requestSize": "$request_length",'
            '"status": $status,'
            '"responseSize": "$body_bytes_sent",'
            '"userAgent": "$http_user_agent",'
            '"remoteIp": "$remote_addr",'
            '"serverIp": "$server_addr",'
            '"referer": "$http_referer",'
            '"latency": "$upstream_response_time",'
            '"cacheLookup": false,'
            '"cacheHit": false,'
            '"cacheValidatedWithOriginServer": false,'
            '"cacheFillBytes": "",'
            '"protocol": "$server_protocol"'
    '},'
    # その他の情報
    '"uuid": "$x_request_id",'
    '"host": "$http_host",'
    '"user_id": "$http_x_user_id",'
    '"session_id": "$http_x_session_id"}';

access_log  logs/access.log acc_json_format;
```

httpRequestに当てはまらないフィールドもjsonPayloadとして記録されるので便利です
OpenSSH <= 7.8 と knife zero の落とし穴

## OpenSSH 7.8 以降の ssh-keygen
* OpenSSH 7.8 以降の ssh-keygen で作成した秘密鍵のデフォルトのフォーマットは OpenSSH フォーマットに変更された（OpenSSH 7.8 より古いバージョンでは OpenSSL's PEM フォーマット）。
https://www.openssh.com/txt/release-7.8

* macOS 10.14.3 のターミナルに入っている SSH のバージョンは 7.9 なので、macOS の ssh-keygen で作成した秘密鍵はデフォルトで OpenSSH フォーマットとなる。
<pre>
$ ssh -V
OpenSSH_7.9p1, LibreSSL 2.7.3
</pre><pre>
$ ssh-keygen -t rsa -f ~/.ssh/test
ゴニョゴニョ
　
$ cat ~/.ssh/test
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
〜
-----END OPENSSH PRIVATE KEY-----
</pre>ちなみに、これまでのOpenSSL's PEM フォーマットは<pre>
-----BEGIN RSA PRIVATE KEY-----
〜
-----END RSA PRIVATE KEY-----
</pre>

* 他サーバへのSSH接続であれば、特に問題はないので、この変更に気付かなかった。
<pre>
$ ssh -l ec2-user -i ~/.ssh/id_rsa <接続先サーバ>
</pre>

## OpenSSH フォーマットのキーペアでは knife zero でエラーが起きる
* 例えば、リモートサーバに対して bootstrap を実行するために次のコマンドを実行すると・・
<pre>
$ knife zero bootstrap <接続先サーバ> -x ec2-user -i ~/.ssh/id_rsa --sudo -N node01
</pre>次のようなエラーが。。<pre>
〜
Connecting to <接続先サーバ>
ERROR: NotImplementedError: OpenSSH keys only supported if ED25519 is available
net-ssh requires the following gems for ed25519 support:
　* rbnacl (>= 3.2, < 5.0)
　* rbnacl-libsodium, if your system doesn't have libsodium installed.
　* bcrypt_pbkdf (>= 1.0, < 2.0)
See https://github.com/net-ssh/net-ssh/issues/478 for more information
Gem::MissingSpecError : "Could not find 'rbnacl' (>= 3.2.0, < 5.0) among 295 total gem(s)
Checked in 'GEM_PATH=/home/ec2-user/.chefdk/gem/ruby/2.5.0:/opt/chefdk/embedded/lib/ruby/gems/2.5.0', execute `gem env` for more information"
</pre>

## 従来の OpenSSL's PEM フォーマットで作成するには
* OpenSSH 7.8 のリリースノートにある通り、 "-m PEM" を付ければいいらしい
<pre>
$ ssh-keygen -t rsa -m PEM
</pre>

## OpenSSH フォーマットの秘密鍵のパスフレーズ剥がし
<pre>
$ chmod 0600 秘密鍵

$ ssh-keygen -p -f 秘密鍵
Enter old passphrase: <設定済みパスフレーズを入力>
Key has comment '＊＊＊＊＊'
Enter new passphrase (empty for no passphrase): <空入力>
Enter same passphrase again: <空入力>
Your identification has been saved with the new passphrase.

$ chmod 0400 秘密鍵
</pre>
SPAでFirebase Hostingするときの注意メモ

Firebase HostingでSPAモードしていなかったときにちょっと困るのでメモ。
SPAでURLを直打ちできるようにしたいけれど、サーバの設定が必要で、例えば、

webpack dev serverだったら、configに

```
devServer: {
    historyApiFallback: true,
}
```

と入れておけばよいが、Firebase Hostingに関しては、

```
      "rewrites": [{
        "source": "**",
        "destination": "/index.html"
      }]
```

とfirebase.jsonに定義する必要ある。

これは、firebase initしたときに、hostingを選んで、SPAか聞かれてyes！したときの設定と同じようなので、
普通にやってたら問題ないのだが、一度レールから逸れると調べないとアカンので記す。
EXCEL VBA プロシージャ名一覧をイミディエイトウィンドウに出力する

## プロシージャ名一覧をイミディエイトウィンドウに出力する

```vb
Sub プロシージャ名一覧をイミディエイトウィンドウに出力する()
    Dim buf As String, ProcNames As String, i As Long
    With ThisWorkbook.VBProject.VBComponents("Module1").CodeModule
        For i = 1 To .CountOfLines
            If buf <> .ProcOfLine(i, 0) Then
                buf = .ProcOfLine(i, 0)
                ProcNames = ProcNames & buf & vbCrLf
            End If
        Next i
    End With
    Debug.Print ProcNames
End Sub
```
EXCEL VBA シート名一覧をイミディエイトウィンドウに出力する

## シート名一覧をイミディエイトウィンドウに出力する

```vb
Sub シート名一覧をイミディエイトウィンドウに出力する()
  Dim sh As Object  ' WorksheetまたはChart
  For Each sh In Sheets
    Debug.Print sh.Name
  Next sh
End Sub
```