OmniAICreator/Qiita-1.07M huggingface dataset:
Windows10とWSLでTensorFlow for Poetsを実行する（中編）

#本稿の目的
機械学習をやりたいと思って、色々試行錯誤した結果、windows10上でWindows Subsystem for Linux (WSL)を使い、ようやく機械学習の門をたたくことが出来ました！

今回行った機械学習は
googleが公開しているチュートリアルです。花５種の画像から機械学習と推論をすることができます。
> https://codelabs.developers.google.com/codelabs/tensorflow-for-poets/#5

実際に環境を構築していくと、その過程で苦労するところが多かったので、後進の方々が同じ轍を踏まない様に、ここにそのステップを残していこうと思います。

#前提条件
- OS:windows10 
- メモリ:16GB
- CPU:i3-8100


#前編の内容
> https://qiita.com/tanabee111/items/6418904f68977c9e863d
ざっくり言うと、WSLを頑張って(windowsに)インストールしてます

#中編（本稿）の内容
- 仮想環境(venv)のインストール
- 仮想環境の設定

時間取れなくて、記事書ききれませんでした汗
残りは後編でやります！
では、中編！進めていきます！

#仮想環境(venv)のインストール
仮想環境用のパッケージvenvをインストールします。
pyvenvやpipenv、virtualenvなど仮想環境管理ツールは似ているようなものが多いですが、
今回はpython3系（python3.3以降）で標準パッケージになっているvenvを使います。

ただ、これ、標準パッケージなので、インストール要らない気もします。
（不要でも、まぁインストールできるならしとけ派と、変なことはやらずにインストールしない派がいると思うので、ここはおまかせします。この次の手順の仮想環境の設定でエラー出たら、インストールするくらいでもいいかも）

```
$sudo apt-get install python3-venv
```
でインストールがされるはずです。
これで仮想環境を作る用意ができました。
[venv: Python 仮想環境管理](https://qiita.com/fiftystorm36/items/b2fd47cf32c7694adc2e)

#仮想環境の設定
仮想環境を設定するディレクトリを作成します。
この時点でWSLのrootディレクトリは
C:\Users\ユーザー名\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\LocalState\rootfs\home\WSLでのアカウント名
になっていると思います。
この状態で、
```
$mkdir test_tensorflow
```
を実行することで、そのrootディレクトリ直下にtest_tensorflowというディレクトリが作られます。
そしたら、
```
$cd test_tensorflow
```
を実行して、そのディレクトリに移動しましょう。
```
$python3 -m venv venv
```
で仮想環境を作成します（-mはpython3のオプションコマンドでpython path以下のモジュールをscriptで実行することを意味します）
また、ややこしいことしちゃってますが、最初のvenvがコマンドで、
最後のvenvがtest_tensorflow直下に作成する仮想環境フォルダ名です。
ディレクトリ名はvenvじゃなくてもいいですが、仮想環境ということがわかりやすいので、venvにしました。

次に、作成した仮想環境は今のままでは活性化していないので、
```
$source bin/activate
```
を実行することで、活性化(activate）します。
活性化すると、↓図のように、コマンドラインの文頭に(venv)がつきます
![image6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408801/6b40901e-e30d-694e-f08f-0bad6983471a.png)

また、実行されるpythonがこのvenv内のものをつかっているを確認するために以下のコマンドを実行して、
以下の図のようにvenv/bin以下のpythonが出力されていれば、成功です。
![image7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408801/f22fb445-410b-238f-ddfe-449935afaa6a.png)

最後に、仮想環境の活性化を終了するときは
```
$deactivate
```
です。これで、文頭の(venv)が消えて、通常のコマンドラインになります。

ちょっとボリューム少なかったですが、中編はここまでです。
正直ここまでくれば、自分と同じように躓いている人はもうチュートリアル見て、tensorflowできるかと思いますが、
来週、頑張って、後編書きます。
宜しくお願いします！

#後編の内容
- tensorflowのインストール
- tensor for poetsへの挑戦（うまくいった）
- tensorBoardのさわり
[Vue][JS]「ローカルストレージ対応 超簡易メモアプリ」を作ったので、仕組みを説明してみた

# 今日のコード
<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="js,result" data-user="riotam4" data-slug-hash="bPWqEv" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="[Vue]メモアプリ">
  <span>See the Pen <a href="https://codepen.io/riotam4/pen/bPWqEv/">
  [Vue]メモアプリ</a> by riotam (<a href="https://codepen.io/riotam4">@riotam4</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
localStotageに、1行メモを保存していくだけのアプリを作りました。
使い方は見たままかと思うので、割愛します。
本題は[こちら](https://qiita.com/riotam/items/f6655dade484a3a507ad#%E6%9C%AC%E9%A1%8C%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B8%E3%81%AB%E3%83%A1%E3%83%A2%E3%82%92%E4%BF%9D%E5%AD%98)
# 解説
## メモの入力
入力欄には、

```vuejs:HTML側7行目
<form @submit.prevent="addItem">
    <input type="text" v-model="newItem">
    <input type="submit">
</form>
```
このコードが待機しています。
もともと、この「newItem」には、

```javascript:JS側3行目
data: {
    newItem: '',
    memos: []
},
```
ここで、空配列が代入されていましたが、`newItem = {入力したメモ}`として上書きされます。
そして、送信ボタンによってaddItemメソッドが発火されます。
## メモの受け取り
addItemメソッドはこちら。

```javascript:JS側19行目
addItem(){
    let item = {body: this.newItem}
    this.memos.push(item);
    this.newItem = '';
},
```
item変数にオブジェクトとして代入します。
bodyキーに「this.newItem」、つまり先ほど入力したメモそのものが代入され、先ほどのJS側3行目にも出ていた、memosに追加されます。追加された後、newItemには再び空文字を代入して、初期化しておきます。
## メモをフロントに反映
このアプリはフロント側が、

```vuejs:HTML側2行目
<ul>
    <li v-for="memo in memos">
        <span>{{ memo.body }}</span>
    </li>
</ul>
```
このように準備していて、memosのbodyキーに値が入れば、自動的に反映されるようになっています。
この辺の説明は、Vue.jsの基本的な部分なので、ここでは割愛します。
## 本題！　ローカルストレージにメモを保存
今回のアプリでは**作成されたメモは、ブラウザのローカルストレージに保存される仕様となっています。**
これにより、ブラウザにメモのデータが残っているので、リロードしてもメモが消えることがありません。
### ローカルストレージって何
HTML5から導入された、Cookieによく似たシステムで、ブラウザに5MBもの容量を、永続的に保存しておけます。
詳しくは、わかりやすいサイトがあったので、[こちら](https://www.granfairs.com/blog/staff/local-storage-01)をどうぞ。
### ローカルストレージのコード実装部分
今回のアプリでは、
1. ローカルストレージへの保存部分
2. 読み込み部分
これら2つが実装してあります。それぞれのコードは以下の通りです。

```javascript:保存部分JS側7行目
watch: {
    memos: {
        handler: function() {
            localStorage.setItem('memos', JSON.stringify(this.memos));
        },
        deep: true
    }
},
```
watchフックでmemosをdeepモードで監視しています。詳細については[こちら](https://qiita.com/riotam/items/ee5f0c59643d7e67c614)。
何か変更があったら、handlerの中のメソッドが起動します。
**保存については、「localStorage.setItem()」が使われます。**
引数1で「memos」キー、引数2の「JSON.stringify(this.memos)」によって、dataのmemosがJSON形式に変換されたものが、値として保存されています。

```javascript:読み込み部分JS側15行目
mounted: function() {
    this.memos = JSON.parse(localStorage.getItem('memos')) || [];
},
```
mountedフックで、このページがマウントされたタイミングで、このメソッドが実行されます。
dataにある、memosに、ローカルストレージに保存されているデータを代入しています。

**保存されているデータは「localStorage.getItem()」で読み込まれます。**引数に、指定したいキーを入力しています。
今回は、保存時に「memos」として保存したので、その名前を入れています。
データはJSON形式で返ってくるので、「JSON.parse()」でオブジェクト化して認識しています。
もし、データがなければ、空配列を代入するようにしています。
# さいごに
すごいざっくりしたアプリですが、ローカルストレージを使ってみたくて、ささっと作ってみました。
削除機能も削った、お粗末な仕様ですが、説明用のサンプルアプリとしてご容赦頂きたいです。

最後までありがとうございました。
リスト内包表記が苦手すぎたのでメモ

###まずは通常のリスト作成
`list_1 = []
for i in range(10):
　　　　list_1.append(i)
list_1`

### リスト内包表記 simple
`list_2 = [i for i in range(10)]
list_2`

### 文字列リストver
`countries = np.array(['France','Japan','USA','Russia','USA','Mexico','Japan'])
list_3 = [country for country in countries]
list_3`

###  文字列リストver(重複削除のおまけつき)
`list_4 = [country for country in np.unique(countries)]
list_4`

### if文ありver
`list_5= [i for i in range(10) if i % 2 == 0] 
list_5`

### if elseありver
`list_6= [i if i%2 == 0 else "奇数" for i in range(10)]
list_6`

### if else orありver(世界のなべあつ風)
`list_7 = ["サン" if i%3 ==0 or str(i)[-1] == '3' else i for i in range(1,101)]
list_7`
Qiita を無理なく続けるコツ

株式会社オズビジョンのユッコ (@terra_yucco) です。
内容の充実度はさておき、2019-05-09 から開始した 1 日 1 Qiita 活動も、45 記事続きました。
この記事は 46 記事目なのですが、続けるにあたって工夫していることをゆるっと書こうと思います。


# 仕事中にちょっとでも調べたことなどがあったら Qiita にメモする

`bash while ssh 1回目だけ`

`Nagios プラグイン 標準出力`

こんなレベルで OK


# ネタがあるときに貯める

- 下書きは数に限りがあるので、限定共有投稿で置いておく
    - メンション付けると通知行っちゃうので注意
    - Twitter 連携もしちゃうと URL が公開ちゃうので注意


# 文章の書き方をある程度決める

- 前書きのテンプレ
- 章立てのテンプレ
- 結びのテンプレ

# (おまけ) 連日投稿の仕組みを作る

- [Qiita の限定共有記事を予約投稿機能として使えるようにした話](https://qiita.com/terra_yucco/items/9b63dd6359dcfb1aff22)

# Conclusion

もちろん内容も大事ですが、その前に、書きやすくするための工夫をしてみることで、ハードルはぐっと下がるのではないかと思います。
他にもこんな工夫があるよ！というものがあれば、ぜひお寄せください :smiley:
Entity Component System

# この記事について
Entity Component System(以後ECS)について解説します。また、ECSのC++での実装[EnTT](https://github.com/skypjack/entt)について紹介します。

# Entity Component Systemについて

Entity Component Systemは設計パターンの一つです。継承よりも委譲を優先する原則に従い、
エンティティ（ゲームシーンを構成する敵キャラ、ドア、弾丸、etc...）を部品を組み合わせることで実装できるようにします。この手法は長い継承関係による不透明な実装を回避して、設計をクリーンにします。一方で、実行時のオーバーヘッドという短所もあります。

ECSは以下の3つの要素からなります。

1. Entity  
   コンポーネントを追加するコンテナです。通常、階層構造をとっています。(通常、Entityは SubEntityを持ちます。)
2. Component  
   オブジェクトの振る舞い、見た目、データを定義するクラスです。
3. System  
   SystemはEntityとComponentを利用して、データに基づいた振る舞いとロジックを保持します。

UMLで表すとこんな感じです。
Entityに対して複数のComponentが紐付けられます。

![ECS.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77676/a8b71cb9-2ae1-b017-85b4-60cd72e70853.png)


Systemは、Entityを介してEntityの構成要素のComponentにアクセスできます。また、Entityを介さずに全てのComponentに対してループできたりします。

ECSは従来のOOP(オブジェクト指向)のアプローチに比べて以下のメリットがあります。
1. EntityはポインタではなくIDとして保持できます。このことによりダングリングポインタ(指している先が無効な状態になっているポインタ)が発生しません。
2. 外部に状態を保持しやすなります。状態を読み込むときにポインタを再構築する必要がありません。
3. メモリ上のデータの位置を再配置できます。
4. ポインタを使用しないので、ネットワークを介してEntityを送受信できます。

# やってみよう！！

上を読んでみてECSで遊んでみたくなったと思います。早速、C++でのECSの実装の[EnTT](https://github.com/skypjack/entt)を使ってECSを実装してみましょう。

```c++:Sample.cpp
#include <entt/entt.hpp>
#include <cstdint>
struct position {
    float x;
    float y;
};
struct velocity {
    float dx;
    float dy;
};

// Systemに当たる部分。Entityの位置を更新する。
void update_position(std::uint64_t dt, entt::registry &registry) {
    registry.view<position, velocity>().each([dt](auto &pos, auto &vel) {
        // gets all the components of the view at once ...
        pos.x += vel.dx * dt;
        pos.y += vel.dy * dt;
        // ...
    });
}
int main() {
    entt::registry registry;
    std::uint64_t dt = 16;
    for(auto i = 0; i < 10; ++i) {
        auto entity = registry.create();
        registry.assign<position>(entity, i * 1.f, i * 1.f);
        if(i % 2 == 0) { registry.assign<velocity>(entity, i * .1f, i * .1f); }
    }
    update_position(dt, registry);
}
```

描画したいときは新しいSystemとComponentを追加すればできます。

```c:draw.cpp
struct Drawable {
    Image image; // 描画に使う画像
};
void update_drawing(entt::registry& registry)
{  
    registry.view<Drawable, const position>().each([dt](auto &pos, auto &vel) {
      // 描画に使うロジック
    });
}
```

# 参考文献

* [Entity Systems Wiki](http://entity-systems.wikidot.com/)
* [Entity component system (ECS) explained with tennis players and courts](http://iolivia.me/posts/entity-component-system-explained/)
* [EnTT](https://github.com/skypjack/entt)
Selenium環境でChromeDriverのインストールする方法

##Seleniumのインストール

`$ pip install selenium`

##Chrome Driverのインストール
`$ brew cask install chromedriver`


##インストール確認

`$ brew cask list`

このあとchromedriverが表示されれば成功です。


>参考サイト
>https://dot-blog.jp/news/selenium-chromedriver-install-how-to/
bundle install ができないときの解消法

## bundle install ができないときの解消法について
#### アプリ開発中に、bundle installができないという事態に陥ったので、その解消法について、説明する記事です。

###注目する点は、bundlerのバージョンの差です。

gemをGemfileに記述し、bundle installを行ったところ、

```
Traceback (most recent call last):
	2: from /Users/kenta/.rbenv/versions/2.5.1/bin/bundle:23:in `<main>'
	1: from /Users/kenta/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems.rb:308:in `activate_bin_path'
/Users/kenta/.rbenv/versions/2.5.1/lib/ruby/2.5.0/rubygems.rb:289:in `find_spec_for_exe': can't find gem bundler (>= 0.a) with executable bundle (Gem::GemNotFoundException)
```
bundle installできず、
このようなターミナル上のエラー文が出てきました。

```
can't find gem bundler
```

と書いてあります。
調べてみたところ、PCにインストールしたbundlerとGemfile.lock中のbundlerのバージョンが異なることが原因のようです。

```:Gemfile.lock
BUNDLED WITH
   2.0.1
```

自分のPC上のbundlerは2.0.2でした。

2.0.2を

```
gem uninstall bundler -v 2.0.2
```

アンインストールし、

```
gem install bundler -v 2.0.1
```

でバージョンを変更しました。
その後、正常にbundle installすることができました。
超速入門Ansible2018年12月 感想

# 概要
今年のAWS SummitでRedHat社で無料で配布されていた書籍についての感想と今後の自分の業務で役立ちそうだと思ったことを書いていこうと思います。

ちなみに書こうと思った理由は、この本サミット終了後に転売で出されてるのを見て「ちゃんと欲しくて読んだ人もいる！」ということを示したかったからです。


# 書籍について
ansibleを使うメリット、用途、実例などが紙面の半分ほどに記載されています。
他、サマータイム導入についてやVSCodeの特集、シェルの応用的な使い方などなどが記載されていて、ansibleオンリーではなく他の技術についても記載されていました。

# ansibleとは
そもそもansibleとは何か。

ansibleはオープンソースで開発されている「Automation（自動化）」のソフトウェアです。「命令文を記載したファイル」と「どこに対して実行するか」を記載したファイルを用意すれば実行出来ます。何十台とあるサーバに同じ設定を施したいとなった時に、命令文を書いたファイルさえあれば、あとは対象サーバを記載したファイルさえ用意すればあとは自動でサーバに対して命令を実行してくれます。

# ansibleは何が良いのか
読んで思ったことと、自分の経験に照らし合わせて書きます。

1. ansibleはファイルを実行するサーバにだけ入れていればよく、実行されるサーバ等にインストールする必要が無い。

個人的に一番有難いと思うのはここです。
ここ最近は仮想、クラウドが主流になりつつある中で検証中ならサーバを頻繁に壊して作り直すことがあります。その度対象サーバにインストールし直していたらかなり手間です。
ansibleは対象サーバにpythonが入っていればansibleをインストールする必要が無いので楽です。
あとは、本番稼働しているサーバに新たなソフトを入れることになれば調査、検証、保守環境でテストを実行して影響が無いか確認しないといけなくなります。
ansibleを使う際にこういった調査が不要なので（記載した命令文がちゃんと動くかの検証は必要だと思いますが）割とすぐにどんな環境でも実践できます。

2. 命令文が見やすい

シンプルに記載出来るようになっているので、ぱっと見でも何がしたいか流れを追うだけで何となくですが理解することが出来ます。私が初見で見た時も少し調べれて内容を追えば概要は簡単につかむことが出来ました。

### どういう風に使っているか
私は今仕事でansibleを使用しています。ミドルウェアをサーバにインストールする時にansibleを起動させ自動で構築させています。
手動で行えば1時間以上はかかる上に人為的ミスのリスクもありますが、ansibleを使えば5分ほどで構築が完了し、コマンド実行ミスもなくなります。

# 感想
少し書籍の情報から逸れましたが、本書籍にはansibleを使えばどう便利なのかを書いた漫画や、実践方法、抑えていた方が良いサイトの情報など、果てはこの書籍の一部を担当している人が作成したオンライン学習教材の情報もありました。

こちらのサイト少し見ましたが、実際にコマンドを打って試せるみたいです。本の知識と実際にこのサイトで使ってみれば少なからず自分の業務の一部をansibleを使って自動化出来ると思います。

例えば私の業務だと、仮想サーバ作ったあとプロキシの設定をしないと通信ができないのですが、ここの設定を命令文で作ってしまえば今後サーバを立てるたびにサーバに入ってコマンドを実行する必要がなくなる、なんて利用が出来そうです。

ansibleだけを知りたいなら60ページ弱で済むので少しでも興味があれば読んでみるのも良いかと思います。それだけで大なり小なり業務の自動化が出来る情報は入っていると思います。
nuxt.jsのdevelopをhttpsで立ち上げる

AmazonPayやFacebookのoauthはhttpsが必須ですが、nuxt.jsのdevelopではhttpで立ち上がってしまうため、組み込みができません。

以下のようにコマンドでwatchしながらbuildできればnginxでホスティング…と思いますが実装の方針はないようです。

```
$ nuxt build --watch
```


いろいろ調べて起動用のスプリクト書いたりしましたが、普通に公式の方法がありました。

## SSL証明書を作成する

### mkcertのインストール
```
$ brew install mkcert
```

### localCAを作成
```
$ mkcert -install
```


### 証明書作成
```
$ mkcert localhost
```

### 証明書を配置する
```
server/localhost.pem
server/localhost-key.pem
```

## nuxt.confid.jsに設定を追加
```
server: {
		https: {
			key: fs.readFileSync(
				path.resolve(
					__dirname,
					'server/localhost-key.pem'
				)
			),
			cert: fs.readFileSync(
				path.resolve(__dirname, 'server/localhost.pem')
			)
		}
	}
```

## 開発環境環境立ち上げ
```
$ yarn run dev
```


## おしまい
serverプロパティはdev環境でも有効なんですね
ついでにこの環境だとchromeにhttpsエラーも表示されないのでとても快適です。
chocolateyをアップグレード

# 経緯
ログ保存と自分への手順メモ

# 今のバージョン

```:PowerShell
PS C:\Users\take> chocolatey -v
0.10.11
```
`choco -v`でも同じっぽい

# アップグレード
PowerShell管理者で起動して以下を実行

```:PowerShell（管理者）
PS C:\WINDOWS\system32> choco upgrade chocolatey
Chocolatey v0.10.11
Upgrading the following packages:
chocolatey
By upgrading you accept licenses for the packages.

You have chocolatey v0.10.11 installed. Version 0.10.15 is available based on your source(s).
Progress: Downloading chocolatey 0.10.15... 100%

chocolatey v0.10.15 [Approved]
chocolatey package files upgrade completed. Performing other installation steps.
The package chocolatey wants to run 'chocolateyInstall.ps1'.
Note: If you don't run this script, the installation will fail.
Note: To confirm automatically next time, use '-y' or consider:
choco feature enable -n allowGlobalConfirmation
Do you want to run the script?([Y]es/[N]o/[P]rint): Y

Creating ChocolateyInstall as an environment variable (targeting 'Machine')
  Setting ChocolateyInstall to 'C:\ProgramData\chocolatey'
WARNING: It's very likely you will need to close and reopen your shell
  before you can use choco.
Restricting write permissions to Administrators
We are setting up the Chocolatey package repository.
The packages themselves go to 'C:\ProgramData\chocolatey\lib'
  (i.e. C:\ProgramData\chocolatey\lib\yourPackageName).
A shim file for the command line goes to 'C:\ProgramData\chocolatey\bin'
  and points to an executable in 'C:\ProgramData\chocolatey\lib\yourPackageName'.

Creating Chocolatey folders if they do not already exist.

WARNING: You can safely ignore errors related to missing log files when
  upgrading from a version of Chocolatey less than 0.9.9.
  'Batch file could not be found' is also safe to ignore.
  'The system cannot find the file specified' - also safe.
WARNING: Not setting tab completion: Profile file does not exist at 'C:\Users\take\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1'.
Chocolatey (choco.exe) is now ready.
You can call choco from anywhere, command line or powershell by typing choco.
Run choco /? for a list of functions.
You may need to shut down and restart powershell and/or consoles
 first prior to using choco.
Environment Vars (like PATH) have changed. Close/reopen your shell to
 see the changes (or in powershell/cmd.exe just type `refreshenv`).
 The upgrade of chocolatey was successful.
  Software install location not explicitly set, could be in package or
  default install location if installer.

Chocolatey upgraded 1/1 packages.
 See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log).
PS C:\WINDOWS\system32>

```

```:PowerShell
>chocolatey -v
0.10.15
```
よし。大丈夫そう。

# 参考リンク
[chocolatey公式ドキュメント](https://chocolatey.org/docs/commandsupgrade#upgrade-command-choco-upgrade)
今使用しているvimのプラグインをまとめた

# 概要
- vimとはエディタのことです。sublimeとかatomとかが類似でしょうか。(emacs)
- ココ最近一気にいろいろ覚えたので忘れないようにメモ&紹介します。
- プラグインとは、（ここでは）vimの機能をより便利にしてくれるツールのことです。
- プラグインはいちいちvimでコマンド実行しないといけないので`ショートカット`も載せておきます。ショートカットは `vim.rc` に書きます。
- ちなみにshellは`zsh`を使用しています。
  - zshのこと https://qiita.com/ktr_type23/items/3eb782f98c7a5f4c60b0

## プラグインを入れる前に
- vimの**プラグインマネージャ**`volt`でこれら（↓で紹介する）のプラグインを楽チンにインストールしました。
- voltのインストール `brew install volt`
- See -> https://github.com/vim-volt/volt
- `volt get 'plugname/plugname'` でプラグインがインストールできるようになります。プラグインに合わせてインストールする面倒が省けます😱。


## NERDTree
- See -> https://github.com/scrooloose/nerdtree
- カレントディレクトリからファイルツリーが表示できる。ファイル構造を確認したいときに便利。ファイル内をj,kで移動してenter（選択）してファイルを開ける
- インストール -> `volt get 'scrooloose/nerdtree' `
- 通常、vimを起動したら`:NERDTree`と入力する必要がありますが、あまりにも面倒なのでショートカットを登録。 `shift + t`で開けるようになりました。↓
  - ショートカットをvim.rcに登録 -> `nnoremap T :NERDTree<cr>`
  - vimは`alias v='vim'`でvim.rcにショートカットを登録しています。

![nt-Jun-22-2019-13-41-42-compressor.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165196/87b8cb00-0e4b-9567-c217-ee4b133c8948.gif)

## FZF
- See -> https://github.com/junegunn/fzf.vim 
- ファイルを**ファイル名で検索できます。**あやふや(fuzzy)に検索できるので正確にファイル名を入力しなくてもOK。
- まずはshellでfzfをインストールします。 `brew install fzf`
  - `ctr + r`で打ったコマンドの履歴が見れます。
   
- 次に、vimでもインストールします。 `volt get junegunn/fzf.vim`
  - `vim.rc`に`set rtp+=/usr/local/opt/fzf`を追記します。インストールの最後に書かれていました。
  - ショートカットは、`スペース + f` でvim.rcに登録しました。 -> `nnoremap <Space>f :FZF<cr>`

![fzf-Jun-22-2019-13-53-16-compressor.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165196/a5eab760-8819-bea5-dd0c-6955066f0b43.gif)

## Ag
- FZF↑は**ファイル名**で検索しましたが、こちらは**ファイル内の語句**で検索します。FZF,Agを組み合わせるとかなりのスピードでファイル操作ができるようになります。
- FZFが予めインストールされ、動いている必要があります。
- インストールは`volt get 'rking/ag.vim'`
  - ショートカット `nnoremap <Space>a :Ag<cr>`
- See https://github.com/rking/ag.vim

![ag-Jun-22-2019-17-34-45-compressor.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165196/9933fd02-5caa-cb89-d461-a2669c50c944.gif)

## winresizer

- `:sv`で縦に、`:vs`で横に分割して作業ができるのですが、**分割サイズを変更することができます。**（リンク飛んでもらったほうがわかりやすい。）
- `volt get 'simeji/winresizer'`
- https://github.com/simeji/winresizer
- `ctr + e`でサイズ変更

---

#### その他

- 現在開いているファイルを、**vimのタブで開く**
  - ショートカット `nnoremap <Space>t :tabe %<cr>`
- タブの移動
  - ショートカット `noremap gr gT`
  - もともと`gT`で左のタブに移動できるのですが、`gt`がタブの右なのと、shiftキーを押さないで済むことを考えるとこちらのほうがやりやすいと感じました。

![tab-Jun-23-2019-21-45-39-compressor.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165196/68db523f-0019-5168-2d9c-76214dad90de.gif)



#### Buffers(fzfから呼び出す)
- 一度開いたファイルを探せるので、似たようなファイルが多いときや、すぐに直前のファイルを編集したいときに役立ちます。
- ショートカット `nnoremap <Space>b :Buffers<cr>`

![buffers-Jun-22-2019-17-42-56-compressor.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165196/04728921-de54-2696-8534-3fc93fcd8299.gif)

## おわりに
- 今では必須のプラグインと設定でした。すべてを使いこなしているとはまだ言えませんが、使用頻度は高く、無くてはならないものです。人によって使う使わないはあるので、まずは試してみるのが良いです。
Railsチュートリアルでつまづいた点　まとめ

# 概要
Railsチュートリアル(https://railstutorial.jp/chapters/sign_up?version=5.1)  
でつまづいた点をまとめます。

#1.formのエラー表示が崩れる
下記画像みたいにinputのエラーを表すデザインが変。

<img width="755" alt="スクリーンショット 2019-06-23 21.07.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437312/99b966fa-f5cf-9d4e-ecd2-9fdb6968924d.png">

下記scssが原因だった！ここだけコメントアウトしました。

```app/assets/stylesheets/scaffolds.scss
.field_with_errors {
  padding: 2px;
  background-color: red;
  display: table;
}
```

#2.will_paginateをインストールするとエラー表示される件【Railsチュートリアル第10章】
https://qiita.com/KazuhoE/items/a08cd98b7a5430c0fa71
この方の記事を参考にしてうまく解決しました。バグみたい

バグっぽいですね


#3.チュートリアル図11.2でエラー
チュートリアル図11.2が表示されず以下エラーにhostもあってはずだしなんで？・・・rails serverを再起動すると治りました。再起動大事！

```
ArgumentError in Users#create

edit account activation url(@user.activation token, email: @user.email)
Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true
```
プログラミング勉強記【1週目】2019/6/17 ～ 2019/6/23

#はじめに
初めまして、プロ猫と申します！
2019年2月頃からプログラミングを勉強しました学生です。
今までは主にWeb系の言語を勉強していたのですが、色々調べていくうちに機械学習に興味を持ち始め、Pythonの勉強を始めることにしました。
内容は曜日ごとに分けて書いていきます。**正直ただの記録用なので大したものではありません。温かい目でご覧ください。**
コメントやアドバイスなどいただけたらモチベにつながるのでよろしくお願いします！
#今週の内容
今週は月曜日から水曜日にかけてテストがあったので木曜日からPythonの勉強を始めました。
環境構築、Pythonのデータの種類などを主に学習しました。
区切りよく来週の月曜日から始めても良かったのですがPythonの勉強をするのが楽しみすぎて待ちきれませんでした。
#水曜日
- Pythonの環境構築
    - Anacondaのインストール

一言: anaconda navigatorが起動しないトラブルがあったがなんとか解決できた。
#木曜日
- Pythonのコードを実際に実行してみる
    - print()
 
一言: コマンドプロンプトを使っての初めてのアウトプット。慣れて使いこなせるようにしたい。
#金曜日
- jupyterを使ってみる
- GitとGitHubの基礎を学習
- Pythonのデータの種類の概要
    - Integers
    - Floating point
    - Strings
    - Lists
    - Dictionaries
    - Tuples
    - Sets
    - Booleans
 
一言: jupyter notebookが実際にコードを簡単に実行できてそれをノートとして保存できるので本当に使いやすい。
 
#土曜日
- 数字について学習(Numbers)
    - IntegersとFloating pointの違い
    - 演算子
- 変数の宣言
    - 名前のルール
    - type()
 
#日曜日
- 文字列について学習(Strings)
    - 文字列の記述方法
    - len()
    - IndexingとSlicing(日本語でなんて言うの)
- 文字列のメソッド
    - upper() 大文字にする。
    - lower() 小文字にする。
    - split() スペースごとに分割する。引数に文字を入れるとその文字ごとに分割する。
- コメントの書き方(#)
- 文字列の編集方法

**format()** 

```python:qiita.py
name=Bob
print('I am {}.'.format(name))
#I am Bob.が表示される
```


**f文字列**

```python:qiita.py
name=Bob
print(f'I am {name}.')
#I am Bob.が表示される
```

- リストについて学習(List)
    - リストの記述方法

- リストのメソッド 
    - append() リストの最後に引数にわたされた値を加える。
    - pop() リストの最後の値を取り除く。
    - sort() リストを昇順にする。値は返さない。
    - reverse() リストを降順にする。値は返さない。

#教材
- Udemy
    - [Complete Python Bootcamp: Go from zero to hero in Python 3](https://www.udemy.com/share/100058BkESeVlTRX4=/)
- 書籍
    - [スラスラ読める Pythonふりがなプログラミング (ふりがなプログラミングシリーズ)](https://www.amazon.co.jp/dp/4295003867/ref=cm_sw_em_r_mt_dp_U_SzWdDb00RVEY7)

#最後に
Twitterやってるので良ければフォローよろしくお願いします！
https://twitter.com/pronekocode
【AWS】cloud9へのアクセスのみが可能なユーザーを作成してコードを共有してみる

はじめまして！東京でシステムエンジニアをしているkeidといいます。
今回は自分のアカウントでCloud9を使って友人とコードをシェアして一緒に開発する必要ができたため、
1.AwsCloud9にのみアクセス可能なユーザーを払い出し、
2.もとのアカウントで作成していた環境を共有
してみたいと思います。

# AWS Cloud9へのアクセスのみが可能なユーザーを作成する

## 1.IAMグループの新規作成
まずはルートユーザあるいは権限のあるユーザを使ってcloud9シェア専用のアカウントの払い出しを行います。
 1.マネジメントコンソール>IAM>グループへ移動し"新しいグループの作成”を選択する。
 2.任意のグループ名を入力しポリシーのアタッチ画面へ移動。ここで今回割り当てる権限を選択する。が、試しに検索フォームに"cloud9"と入力すると以下のように3つのポリシーが表示される。
![スクリーンショット 2019-06-23 20.22.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437085/57ee5660-584a-8cbd-095b-e4fa53682939.png)

### 適切な権限を与える。
[awsのユーザーガイド](https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/auth-and-access-control.html)を参照してみるとそれぞれ以下のような違いがあるらしい。

- AWSCloud9Administrator

  Amazon EC2: AWS アカウントの複数 Amazon VPC とサブネットリソースに関する情報を取得する。
  AWS Cloud9: AWS アカウントのすべての AWS Cloud9 アクション。
  IAM: AWS アカウントの IAM ユーザーに関する情報を取得し、必要に応じて AWS アカウントに AWS Cloud9 サービスにリンクされたロールを作成する。

- AWSCloud9User

 Amazon EC2: AWS アカウントの複数 Amazon VPC とサブネットリソースに関する情報を取得する。
 AWS Cloud9: environments に関する情報を作成して取得し、その environments のユーザー設定を取得して変更する。
 IAM: AWS アカウントの IAM ユーザーに関する情報を取得し、必要に応じて AWS アカウントに AWS Cloud9 サービスにリンクされたロールを作成する。

- AWSCloud9EnvironmentMember

AWS Cloud9: 招待された environments の情報を取得し、招待された environments のユーザー設定を取得する。
IAM: AWS アカウントの IAM ユーザーに関する情報を取得する。

---------------------------------------
少し分かりづらいが、だいたいこんな感じか。

- AWSCloud9Administrator
 Cloud9に関してルートユーザーとほぼ同じ権限(環境のシェア、新規作成、削除などほぼ全て行うことができる)

- AWSCloud9User
 Cloud9でシェアされた環境にアクセス、編集ができる。また、新しい環境の作成ができる。

- AWSCloud9EnvironmentMember
 Cloud9でシェアされた環境にアクセスができる。編集はできない。

今回の目的は環境のシェアと編集の権限なので私はAWSCloud9Userを選択しました。それぞれの目的に応じて選択しましょう。
長くなりましたが、ポリシーを選択したら確認画面をクリックしてグループの作成完了です。

## 2.IAMユーザの作成、グループの割り当て
次はユーザを作成し、先ほど作成したグループに割り当てます。
 1.マネジメントコンソール>IAM>ユーザを追加 へ移動し任意のユーザ名を入力する。
 2."ユーザをグループに追加"の画面では先ほど作成したグループを選択する。
以上でユーザの作成と権限の割り当てが完了です。

## 3.Cloud9で環境のシェア
ユーザの作成が完了したのでいよいよCloud9の環境シェアをしていきます。
1.今回シェアするCloud9の環境を持っているユーザーでログインする。
2.マネジメントコンソール>cloud9からシェアしたい環境のIDEを開く。
3.Window>Shareを選択。画面下部にあるInvite Membersにシェアしたいユーザ名(先ほど作成したユーザ)を入力しInviteをクリック。(この際入力フォーム右のボタンでcloud9へのアクセス権限を選択する。R...Read,RW...Read&Write)
![スクリーンショット 2019-06-23 20.53.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437085/31274ea2-6423-419d-6690-5ce8244f3b35.png)
4. 新しくユーザが追加されたことを確認したらDoneをクリックしてシェア完了！

## シェアされた環境にアクセスしてみる
環境のシェアが完了したはずなので確認してみましょう。
1.シェアされたアカウントでログインしなおす。
2.マネジメントコンソール>cloud9>Sharedwithyou を選択。シェアされた環境が表示されていることを確認。
![スクリーンショット 2019-06-23 21.01.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437085/70c504b3-6f43-6da5-1a31-ddafa353b3fa.png)

---------------------------------------

やってみればなんてことないですが、AWS初心者にとってはポリシーの違いなど理解しづらくて時間がかかるかもですね。少しずつ慣れていきたいですね。。
computedの値にバリデーションを追加したい

## 要件など
- フォームの内容に不備があった場合には「つぎへ」のボタンを押せなくする
- dataとinputをバインドするのではなく、computedのset,getプロパティでvuexと紐づけたやつをバリデーションしたい

## つまりどころ
vuelidateを使っていたのだけど、なんだか面倒臭そう。。。
参考 : [Validating computed properties returning an object #241](https://github.com/vuelidate/vuelidate/issues/241)

## vee-validateなら簡単にできそう

いろいろ調べた結果、vee-validateを使えば、やりたいことがあっさりと実装できそう。
公式リファレンスに、[Validating Vuex State](https://baianat.github.io/vee-validate/examples/vuex.html)という項目があるくらいなのでこっちに乗り換えました。

## vee-validateの使い方

使い方に関しては、この辺りを参考にしました。
公式がめちゃくちゃ充実していてありがたい。

- [公式リファレンス](https://baianat.github.io/vee-validate/)
- [Vue.jsを使ってて、フロントサイドのバリデーションには VeeValidate が便利](https://qiita.com/acro5piano/items/2be6068b0647ecffcd86)

## ボタンの実装
入力項目にエラーがある場合には、ボタンを押せなくする処理は、
[Flags](https://baianat.github.io/vee-validate/guide/flags.html)のやり方で簡単に実装できました。

flagsにある項目は、自動的に`fields`配下に登録されている。要件に合う条件をcomputedに記述してやればOK。

条件は以下の通り

> - touched: indicates that the field has been touched or focused.
> - untouched: indicates that the field has not been touched nor focused.
> - dirty: indicates that the field has been manipulated.
> - pristine: indicates that the field has not been manipulated.
> - valid: indicates that the field has passed the validation.
> - invalid: indicates that the field has failed the validation.
> - pending: indicates that the field validation is in progress.
> - validated: indicates that the field has been validated at least once by an > - - event or manually using validate() or validateAll().
> - changed: indicates that the field value has been changed (strict check).

touchedは、フォーカスされたときに発火して、dirtyは値の入力があったときに発火って感じ。
vuelidateのtouchedとdirtyを合わせてdirty１つだったのでこの辺りも使い勝手がよさそう。

### サンプルコード
vee-validate自体は、main.jsで読み込む前提で作ってみた。

```vuejs:InputName.Vue
<template>
  <div>
    <input
      type="text"
      name="name"
      v-validate="'required|min:3'"
      v-model="name"
    >
    
    <button
      :disable="isDisable"
    />
  </div>
</template>

<script>
  export default {
    computed: {
      name: {
        set(val) { this.$store.dispatch('setName', val) },
        get() { return this.$store.getters('getName') }
      }
    },
    isDisable() {
      // 
      return Object.keys(this.fields).some(key => this.fields[key].invalid)
    }
  }
</script>
```
TweetDeck 風 Slack Viewer を作ってみた

Slack を使っていると「少しチャンネルの一覧性が低いかな」と感じていました。
なので TweetDeck 的に複数のチャンネルを同時に見られるツールをプロトタイピングしてみました！
<img width="701" alt="Screen Shot 2019-10-31 at 21.49.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144707/4e75909b-1d7a-bcfb-e880-cc6819477a7e.png">
本体は [GitHub/slackdeck-proto](https://github.com/blue0513/slackdeck-proto) に置いてあります。

+ **2019/08/14追記**: Windows版アプリ配布
+ **2019/08/10編集**: 設定ファイルについての記載を変更
+ **2019/07/15追記**: Mac版アプリ配布
+ **2019/06/26追記**: 複数ワークスペース対応

## どんなツール？

Slack の TweetDeck 的なビューワです。
MacOS, Windows版は [こちら](https://github.com/blue0513/slackdeck-proto/releases) から入手できます。
[![Image from Gyazo](https://i.gyazo.com/0613980a28e63cfcb3a3a194c6d48875.gif)](https://gyazo.com/0613980a28e63cfcb3a3a194c6d48875)
### 特徴

+ マルチカラム対応
+ ボタン操作でカラムを増減可能
+ 初回起動時のカラム数、チャンネル等を設定ファイルでカスタムできる
+ 複数ワークスペース対応
+ [アプリ版の配布](https://github.com/blue0513/slackdeck-proto/releases)

## 前準備

以下を用意します。[Electron](https://electronjs.org/) については[公式インストール手順](https://electronjs.org/docs/tutorial/installation)を参考にしてください。

+ Slack アカウント
+ Electron（version 5.x 以上）

## とりあえず実行

### 1. npm install

必要な諸々をインストールする

```sh
$ cd slackdeck-proto
$ npm install
```

### 2. 設定ファイルの準備

`settings.json.sample` を `settings.json` にリネームします。

### 3. 設定ファイルの書き換え

`settings.json` の `"url"` 部分を自分の Slack workspace の URL に変更します。

```json:settings.json
{
  "url": "https://your-slack.slack.com/", <= ここね
  "contents": [
    {
      "channel_id": "YOUR_CHANNEL_ID",
      "style": "channel-only",
      "width": "small-tab"
    },
    ..... 中略 ....
  ]
}
```

Slack アプリを使ってる人は、画面のここに出ているやつですね。
画像の場合は `https://blue-private.slack.com` になります。

<img width="272" alt="Screen Shot 2019-06-23 at 20.17.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144707/6428422f-6b22-3f71-85f7-55140c4c0f40.png">


### 4. 実行

ターミナルに移動して以下を実行すれば Electron アプリが立ち上がります！

```sh
$ cd slackdeck-proto
$ npm start
```

ファイル選択画面が開いたら、先ほど設定した `settings.json` を選んでください。これで設定が反映されます。


## カスタマイズ

初回起動時に `settings.json` を読み込んでカラム数やCSS、チャンネルを規定しています。

+ `url`: 今回対象とする Slack workspace の URL です
+ `contents`: 以下の要素を含みます。この数を増減させると、初回起動時に開くカラム数が変化します

以下が `contents` 内の細かいパラメータです。

+ `channel_id`: カラムで開くチャンネルの ID です。以下の手法で取得できます。
    + Web 版 Slack を開き、該当チャンネルに移動。その際の URL の末尾が ID です
    + アプリ版 Slack を開き、チャンネルリストのチャンネル名を右クリック。 `Copy Link` で取得できる URL の末尾です
+ `style`: カラムに適用するCSSを規定します。取れる値は以下です
  + `channel-only`: 左側のチャンネルリストだけが表示されます
  + `body-only`: 真ん中のメイン画面だけが表示されます
  + `sidebar-only`: 右側の画面（Activity, Files など）だけが表示されます
+ `width`: カラムの幅を規定します。取れる値は以下です
  + `small-tab`: 細いです。`channel-only` や `sidebar-only` と合わせて使うのがオススメです
  + `large-tab`: 太いです。`body-only` と合わせて使うのがオススメです

## よくある質問

+ **Q**: カラムを足したり消したりしたいよ
  + **A**: 各カラム上部の Add や Remove ボタンを押してください
+ **Q**: チャンネル移動ができないよ
  + **A**: ショートカット移動を推奨してます。`Cmd + k` がオススメです
+ **Q**: 文字がはみ出して読めないよ
  + **A**: フォントを小さくするのがオススメです。`Cmd + -` で大丈夫だと思います

## 最後に

Proto とあるようにまだプロトタイプです。なのでバグレポート、質問、ご意見、Pull Request 等じゃんじゃんいただけると励みになります。[GitHub の issues](https://github.com/blue0513/slackdeck-proto/issues) や Qiita のコメントなど、どこでも嬉しいです！
在页面中，添加导出功能。

- 1、外部插件导入到自己的项目中
文件名字是 ： xlsx.full.min.js 
方法project 下  js的文件夹里。 



- 2、在页面中引入外部的插件
```<script type="text/javascript" src="http://localhost:8080/p_StudentManagerSystem/js/xlsx.full.min.js"></script>
```

- 3、在页面中添加一个按钮来完成导出功能

```
<div style="float: left;">

<a id="exportData" href="javascript:;" class="easyui-linkbutton" data-options="iconCls:'icon-undo',plain:true">导出</a>

</div>
```

- 4、添加插件提供的代码到js块中,放在js代码中，给按钮做设置的地方。
 
```
//导出表格方法
		$("#exportData").click(function(){
			var data_rows = $('#dataList').datagrid("getData").rows;//获取当前页面的数组数据
			
			var ws =  XLSX.utils.json_to_sheet(data_rows);//封装入表格
			doit(ws, " 企业数据-");//导出，下载数据  
		});
	  	
		 //下载数据 	ws 表格，title 要保存的名字  
	     function doit(ws,title,fn, dl) {
	    	 var wb  = XLSX.utils.book_new();
	          XLSX.utils.book_append_sheet(wb, ws, title);
	          return dl ?
	              XLSX.write(wb, {bookType:type, bookSST:true, type: 'base64'}) :
	              XLSX.writeFile(wb, fn || (title+'.' + ('xlsx' || 'xls')));//哪种格式放前面默认导出哪种
	    }
```
オフラインどう書く第三回参考問題の解

問題はこちら->https://qiita.com/Nabetani/items/ebd8a56b41711ba459f9

g/3で新しいカウントを求めそれをltos/3で文字列にしてつなげているだけです。
Prolog向きかと思って解いてみましたが、どの言語でも容易に書けるのかな。

```prolog
%swi-prolog version 7.4.2
%start.
%:-initialization(start).    %ideone

g(o,[_,_,O],L):-O=:=3->L=[0,0,0];L=[0,0,O].
g(s,[S,B,O],L):-S1 is S+1,(S1=:=3->(O1 is O+1,g(o,[0,0,O1],L));L=[S1,B,O]).
g(b,[S,B,O],L):-B1 is B+1,(B1=:=4->L=[0,0,O];L=[S,B1,O]).
g(h,[_,_,O],[0,0,O]).
g(p,[_,_,O],L):-O1 is O+1,g(o,[0,0,O1],L).
g(f,[S,B,O],[Y,B,O]):-S=2->Y=S;Y is S+1.

ltos(SR,L,S):-
     maplist(number_chars,L,[[X],[Y],[Z]]),atom_string([X,Y,Z],S1),
     (SR==""->S=S1;concat_atom([SR,S1],",",S)).

solve([],_,S,S).
solve([H|T],L,SR,R):-
     g(H,L,L1),L1=[St,B,O],ltos(SR,[O,St,B],S),solve(T,L1,S,R).

start:-str(S),split_string(S,"\n","",L),maplist(split,L,L1),go(L1),!.

split(S,L):-split_string(S,"\s","\s",L).

go([]).
go([[A,_,Q]|T]):-atom_chars(A,SL),solve(SL,[0,0,0],"",S),disp(S,Q),go(T).

disp(S,Q):-
     atom_string(S,S1),
     (Q==S1->Str=" pass ";Str=" fail  "),write(Str),writeln(S1-Q).

str("s -> 010
sss -> 010,020,100
bbbb -> 001,002,003,000
ssbbbb -> 010,020,021,022,023,000
hsbhfhbh -> 000,010,011,000,010,000,001,000
psbpfpbp -> 100,110,111,200,210,000,001,100
ppp -> 100,200,000
ffffs -> 010,020,020,020,100
ssspfffs -> 010,020,100,200,210,220,220,000
bbbsfbppp -> 001,002,003,013,023,000,100,200,000
sssbbbbsbhsbppp -> 010,020,100,101,102,103,100,110,111,100,110,111,200,000,100
ssffpffssp -> 010,020,020,020,100,110,120,200,210,000").

```
モーダルビューをフルスクリーンで表示させる（iOS13以降）


## はじめに
iOS13からモーダル表示のデフォルト遷移方法が自動（[UIModalPresentationStyle.automatic](https://developer.apple.com/documentation/uikit/uimodalpresentationstyle/automatic)）に変更されました。

この変更で、デフォルトはフルスクリーンではなく、全面に重なるビューが表示されるようになりました。
(iPhone版のPageSheet表示!?)
[UIModalPresentationStyle.pageSheet - UIModalPresentationStyle | Apple Developer Documentation](https://developer.apple.com/documentation/uikit/uimodalpresentationstyle/pagesheet)

iOS12でのボイスメモアプリのように下方にスライドするだけでモーダル画面を消すことができるので、
大画面の端末での操作性は上がるでしょう。
しかし開発者側としてはレイアウトなどの関係で、いままでのフルスクリーンのままがよいと思う場面もあります。

そこでフルスクリーン表示に戻すを共有します。

## 方法
やり方は至極単純でUIViewControllerのmodalPresentationStyleを指定するだけです。

``` swift
let vc = UIViewController()
// 遷移方法にフルスクリーンを指定
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true, completion: nil)
```

[modalPresentationStyle - UIViewController | Apple Developer Documentation](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621355-modalpresentationstyle)


## 参照

[viewcontroller - Presenting modal in iOS 13 fullscreen - Stack Overflow](https://stackoverflow.com/questions/56435510/presenting-modal-in-ios-13-fullscreen)
測域センサをタッチパネル化する

チームラボなどがやってる、『測域センサを使ってプロジェクション面をタッチパネル化する』をTouchDesignerで組んでみました。  
[サンプル](https://github.com/ToyoshiMorioka/HokuyoTouchExample)はこちら。
が、測域センサがない状態で組みなおしたのでエラーがあるかも・・・。
[こちら](https://qiita.com/snafkinliberty/items/b917cb02c70c7c95e64b)を参考にしています。
toxなど作らなくても、やり方さえ覚えておけば現場でも一から組めそうです。

# 1. 測る
ワンテンで測域センサを使ったコンテンツを設置する場合、影を少なくするために2台以上をペアで納品することが多いのですが、その場合変数が非常に多く、キャリブレーションに時間がかかりがちです。
実際の面積を測ったり、図面を見たりして初期値を入力できたほうが追い込みしやすくなります。

![無題のプレゼンテーション.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152718/68a54b0a-f0bc-f1fc-fa4d-954fd7bd1a05.png)

例えばこの場合はざっくり2台の測域センサをプロジェクション面を挟むように2台設置しています。
また、2台は向かい合うように設置されています。ざっくりY軸に対して時計周りに30度、210度ぐらいですね。
また、1台が原点にあるとした場合、片方はX軸5m、y軸3mずれたところに設置されてそうです。
スクリーンの位置はよくわかりませんね・・・まぁなんとかなるでしょう。
測域センサの調整は通常、以下の3ステップで行われます。
  
1. 測域センサ同士の座標合わせ
2. 測域センサの値とスクリーンの座標合わせ
3. タッチ検出閾値の調整
 
# 2. 組む
## 2-1. 測域センサの取得範囲を見える化する
タッチ検出閾値をBlobTrack TOPで実装したいこと、また、スクリーンとの位置合わせはTOPになっておくと便利なことから測域センサの値をSOPにし、Render TOPでTOPに焼き付けていきます。
HOKUYO CHOPを2つ用意し、それぞれのLidarに繋がるように設定します。
この際、あまり意味ないのですが明らかに使わない範囲の値を捨てるように調整しました。
値はCartesian Coordinatesにしています。
本来測域センサは角度と距離でしか出てこないので三角関数計算を自前で実装しなければならないのですがTouchDesignerは最初からその機能を入れてくれてますね。
便利。

今回はちゃんと設置されていれば90度の範囲の値しか使わないのですが、若干設置の誤差を見込んで±10度で110度取るようにしています。
また、SOPにする際に原点と結ばないとカタチが綺麗に出ないので、Splice CHOPでHOKUYO CHOPのSampleの最初に原点(tx:0, ty:0)を挿入しています。
![visualize.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152718/2d43dafc-71b6-df75-2514-2ba2df7d08b5.png)

この状態でCHOPtoSOP SOPを使って、『同じだけの頂点を持った平面図形』のtxとtyに値を挿入すれば原点から飛び出すイガイガした形の、測域センサの値のSOPが完成します。

## 2-2. レンダリング、マージ、そして測域センサ同士の座標合わせ
txtyの単位系は1mなので、Geometry COMPのTranslateを調整します。
今回は1台目の測域センサが原点にあると仮定して、2台目をtxに5、tyに3と入れました。
また、1台目2台目ともに回転させます。
TouchDesignerは反時計回りの回転系なので、時計回り30度→330度、時計周り210度→150度となります。
回転軸はZ軸。
TouchDesignerは通常はScale, Rotate, Translateの順で処理しますが、今回の場合はRotateの前にTranslateさせておくとスムーズです。
![marge.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152718/3feabfdc-d9c0-4fa8-aa93-343330e3c72d.png)
Camera COMPはProjectionをOrthgraphicにしておきましょう。
また、Caemraの画角に収まるようにCamera COMPのUniform Scaleをいじります。
出来上がった画像をOverすると、『どちらかがセンシングしている範囲は白』で『どちらもセンシングしていない範囲は透明』の画像ができると思います。
この状態で測定エリアに手を入れたりして、回転角度や移動量の誤差を微調整していきましょう。
そのままだと若干見づらいので、Edge TOPで枠線を出してOverすると若干見やすくなります。 

## 2-3. 測域センサの値とスクリーンの座標合わせ
Window COMPを使ってOverしたビジュアルをスクリーンに全面表示し、『触っているところ』と『絵に出ているところ』が合うようにTransform TOPで調整します。
![settodisplay.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152718/531ec350-358d-d57e-9f25-c45af065c531.png)


## 2-4. タッチ検出閾値の調整
調整が終わったら最後はタッチ検出の調整です。Overで作った白-透明の画像をLevel TOPで反転させて検出範囲だけ白くなるようにし、その上でBlobTrack TOPで座標を検出させます。

## 3. 感想
かなり簡潔に書けるな、という印象です。
ただし、結局調整自体は手作業になるため、[スマッチ](https://www.1-10.com/drive/products/smarttouchsystem)のような自動調整ツールを利用しないと調整には2時間程度見る必要がある感じですね。
Windows 10 May 2019 Update(1903)日本語IMEを起動するとシステムが停止する

#どうやら未発見らのエラーらしい
「Windows 10 May 2019 Update」には既知の不具合が12件 ～手動更新には十分注意
自動配信を待つのがおすすめ
https://forest.watch.impress.co.jp/docs/news/1186179.html
2019.06.21
Microsoft、Windows 10 May 2019 UpdateにアップグレードできないPCに通知を表示
https://www.softantenna.com/wp/windows/windows-10-version-1903-manual-widnows-update-for-all-users/

#Firefoxのアドイン/プラグインのバグかもしれない
複数あるので、どれかわかりません。アドインも一つ一つ上げても意味がないしなあ。それに商売道具は秘密ですわ。しかし自作のプラグインとかはないです。アク　　　かマカ　　　か。広　　　　か。リフレッシュしたとき立ち上がったやつ？

#再現のしかた
この時外付けHDDがついています。
Windowsをアップグレードします。
この時一度失敗します（しなくてもいいと思うけど）
失敗したあとにもう一度アップグレードします。
今度は成功します。
Firefoxを立ち上げてQiitaの原稿を書こうとします。
システムが停止し、電源スィッチを強制切断しないと全く動きません。
またかりにFirefoxを終了しても、ファイルを移動させたり、ファイルを移動させて、ファイル名を日本語入力にしようとしてIMEを立ち上げると、システムが終了します。

#修復方法
##回復できない場合のプランBを用意します
まずお金を用意します。
次に32GBのUSBと１TB以上のハードディスク、プリンターのインクとプリンター用紙A4と、新しいPCを買います。
これは半分本当です。だいたいエラーが出てきたら買い替え時なので。
PCは無理でも2つは用意します。自分は粗悪品なのでまた買えますが、同じものを買うのはそれでもつらいです。
しかし、もう一つの理由は業務が集中しているときに起きるからです。危なくて仕方ないわけです。

これについてのマニュアルはいっぱいありますが、下のリンク先がわかりやすい印象です。
少なくとも自分がごちゃごちゃ書くよりはるかにわかりやすいです。これ作るのはすごく大変です。一つ一つスクリーンショットをとるなんて。印刷すると40ページ以上になります。しかし20ページくらいは印刷しましょう（落ちて読めない）。
次に、CドライブをIMEやFireFoxを立ち上げずできる限り削除します。
容量の20%、または20GB以上あけてください。
おそらく20GBがカギのようです。
これは起動USBをつくる時間がかかるためです。少なくともまず回復メディアの準備に1時間、そこからUSBを作成するのに7時間以上かかります。これが20GB以下だととんでもない時間がかかるようです。（途中から加速したので、どうもCドライブの空きは重要みたいです。。
けちって16GBにしないでください。回復メディアの準備ができて32gbという指示を無視して16gbを入れるとクリーンにしてだまって終了します。
また回復USBの間にはなにもしない方がよいので、容量を開けるのは回復メディアの準備時までにやります。
##Drop Onedriveがゾンビのように復活して容量を食うのに注意
とくにOneDriveはドキュメントフォルダの顔をしています。せっかく容量を開けてもまた戻ります。立ち上げないようにしましょう

###スケジュールシャットダウン、メール送信をタスクスケジューラから落とす
定期的に動くようにしていると作成している間に作動して妨害します。無慈悲な無効化を行いましょう。


##マニュアルはこちらです
https://www.pasoble.jp/windows/10/clean-install.html
これにしたがって起動USBの作成と、イメージバックアップをします。
だいたいこの時点で24時間があっさり経過します。

そしてここまで終わった時点でFirefoxをリフレッシュ起動します。

##もしこれでだめだったら、プランAに移行します。
ここからUSBで初期化し、新しいバージョンをいれて、イメージを戻すという作業になります。
おそらくここでもっと時間がかかります。

##最初からプランBが早いかも
まあそういうことです。

#原因
マイクロソフト社ではこの問題の原因をわかるはずがありません。多分プラグインがコンフリクトしているのでしょう。自分もこれを偶然発見したので。
しかしシステムが落ちるというのが謎です。
##Additional
Error occured again!
##続報
このあとさらにタスクマネージャーを解析すると、Windowsのエラー報告システムが立ち上がっていることが確認できました。
今回の最大の犯人はフリー版のマカフィーウェブアドバイザーです。
これを入れていないと、おすすめをしているのですが、実は裏でそのメッセージを頻繁に起動に失敗しており、1日で数百回起動に失敗していました。

これとバージョンアップしたWindowsの負荷がファイヤーフォックスでIMEを立ち上げた瞬間にエラー報告が連続していたシステムが限界を迎えシステムが停止する。これが今回の現象の結論です。


また、仮にこれをブラウザに組み込んでも何らかのタイミングでFirefoxは外れてしまい、その場合またメッセージを起動するために失敗し続ける。という挙動を示します。
これとフリー版のマカフィーのインターネットセキュリティはアプリの削除で削除できますので、有償版をいれて削除しましょう。
Pythonデータベース連携(confirm.py編)

未来電子でインターンさせてもらっているものです。今回も前回に引き続き、データベース連携についてみていきます。

Pythonのデータベース連携についてみていきます。

前回の続きからなので、inputファイルで入力された情報を引き継いだ上で確認画面を表示させるのが目的です。

```
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import cgi
import sys
import io
import sqlite3
```

まずは上記の4つをインポートします。

```
form = cgi.FieldStorage()
name = form.getfirst('name','')
number = form.getfirst('number','')
age = form.getfirst('age','')
note = form.getfirst('note','')
```

これは先ほどのファイルのフォーム部分のname属性を使って入力値を変数に代入してテーブルにデータの挿入ができるようにする工程です。

```
conn = sqlite3.connect('sample.db')
c = conn.cursor()

# データの挿入
c.execute('INSERT INTO users(gakuseki,namae,nennrei,bikou) VALUES(?,?,?,?)',[number, name, age, note])

# 挿入した結果を保存（コミット）する
conn.commit()

# データベースへのアクセスが終わったら close する
conn.close()
```
この部分で疑問に思うのが、データの挿入の部分だと思います。

```
# データの挿入
c.execute('INSERT INTO users(gakuseki,namae,nennrei,bikou) VALUES(?,?,?,?)',[number, name, age, note])
```

これは、「テーブル名がusersであるカラム名がgakuseki、namae、nennrei、bikouであるテーブルに数値を入れていく。入れていく数字はユーザーの入力値であり、そこには変数number、name、age、noteが入る」ということです。

```
print(

"""
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>学生入力フォーム</title>
</head>
<body>
<h3>確認</h3>
  <p>学籍番号</p>{0}<br>
  <p>名前</P>{1}<br>
  <p>年齢</p>{2}<br>
  <p>備考</p>{3}<br>
  <p>こちらでよろしいでしょうか</p>
<a href = "complete.py"> 送信 </a>
</body>
</html>
"""
.format(number,name,age,note)
)
```

HTML部分はこのような感じです。

フォーマット関数でリストの中の入力値をインデックスで取り出せるようにしてあります。

全体のコードとしてはこんな感じにしました。

```
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import cgi
import sys
import io
import sqlite3


#name属性を使って入力値を変数に代入してテーブルにデータの挿入ができるようにする。
form = cgi.FieldStorage()
name = form.getfirst('name','')
number = form.getfirst('number','')
age = form.getfirst('age','')
note = form.getfirst('note','')




conn = sqlite3.connect('sample.db')
c = conn.cursor()

# データの挿入
c.execute('INSERT INTO users(gakuseki,namae,nennrei,bikou) VALUES(?,?,?,?)',[number, name, age, note])

# 挿入した結果を保存（コミット）する
conn.commit()

# データベースへのアクセスが終わったら close する
conn.close()

# 以下のコードを書かないと、htmlとして読み込んでもらえない。
print("Content-type: text/html; charset=utf-8")


# htmlの部分。printでHTMLコードを表示させることで、ブラウザがHTMLコードとして認識してくれる。

print(

"""
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>学生入力フォーム</title>
</head>
<body>
<h3>確認</h3>
  <p>学籍番号</p>{0}<br>
  <p>名前</P>{1}<br>
  <p>年齢</p>{2}<br>
  <p>備考</p>{3}<br>
  <p>こちらでよろしいでしょうか</p>
<a href = "complete.py"> 送信 </a>
</body>
</html>
"""
.format(number,name,age,note)
)
```

<img width="364" alt="スクリーンショット 2019-06-15 17.26.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276221/d974e4d3-6052-2dae-e84c-4840a2226987.png">

ブラウザではこのように表示されます。

今回はconfirm.pyファイルについてみていきました。次回はcomplete.pyファイルについてみていきます。

僕はプログラミング初心者なので間違っていることも多々あると思います。その場合は指摘していただけるとありがたいです。
微妙に古い linux でシンボリックリンクした unit ファイルに sudo systemctl enable したら "Failed to execute operation: No such file or directory" になる時

# 結論
/etc/systemd/system への ln -s をやめて、cp する

# 背景
いつも unit file を /etc/systemd/system に ln -s して使っていたのですが、debian 8.0 でやってみると

```bash:
Failed to execute operation: No such file or directory
```

と謎のエラー、対象の unit ファイルは確かに /etc/systemd/system の下にシンボリックリンクとして存在してるのでなぜ no such file なんて言われるのかわからない

ググると [systemctl enable with an absolute path as argument cannot be used to create template instances #661](https://github.com/systemd/systemd/issues/661) がみつかる

# 感想
systemd やっぱ好きになれない
新入社員なので「わかばちゃんと学ぶGit使い方入門」を読んだ

#概要(3行で)
* **[わかばちゃんと学ぶGit使い方入門]( https://www.amazon.co.jp/dp/4863542178/ref=cm_sw_r_tw_dp_U_x_B-1dDbQNGBDPX)を読んだ。** 
* **漫画＆実践で理解しにくい概念を説明してくれる。**
* **新米エンジニアは全員読むべき良書だと思った。**

# 何が良かったか
### 一つ目
今までGitを利用する機会が業務上しかなかったため

- commit
- push
- pull

など基本的なことはわかるが

* リモートとローカルの違い
* プルリクエスト
* リバート
* リベース
* フェッチ

など細かい概念がふわっとしていた。
本書はそういったふわっとしがちなGitの概念を補完してくれた。

### 二つ目
漫画での丁寧な解説と1作業ずつ進む実践がどのパートにもあったので、
**途中で飽きずに読了することができた**(大切)

### 三つ目
使い方を説明して終わりではなく、実際に陥りがちな状況を例に挙げて実践できるので理解が早かった。

# 今後
本書を繰り返し読んで**Git チョット サワレル**になりたい。

次回 -> [新入社員なので「わかばちゃんと学ぶWebサイト制作の基本」を読んだ](https://qiita.com/yano0717/items/d87a1cbb48d51153d2fe)
DAWがシステムロード警告で止まってしまう原理

こんにちは。新米エンジニアのnoyです。
これから自分の思考整理・言語化の練習のためQiitaを活用していきたいと思います。

今回は最近始めた趣味であるDTMについてです。

#解決したい問題

Logic Pro Xでトラックを再生中に**「システムが過負荷です。」と表示され止まってしまう。**

<img width="937" alt="スクリーンショット 2019-06-23 18.57.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187718/b0ae81e3-3ac3-732f-6731-562d1420c81e.png">


#原因
「オーディオエンジンが、必要なデータの一部を時間内に処理できませんでした。」と書いてあります。
つまり、**データ処理が追いつかなくなって止まってしまう**ようです。


必要なデータとは何か？

コンピュータで音声を再生するとき、アナログデータは一定の時間単位で区切って、その時間ごとの信号レベルを標本として抽出してデジタルデータ化しています。（これを**標本化、サンプリング**という）

どのくらいの時間間隔で区切ってサンプリングしているかを表すのが**サンプルレート（サンプリング周波数）**です。

今回のプロジェクトのサンプルレートはいくつになっているかを確認します。
「ファイル」＞「プロジェクト設定」＞「オーディオ」と選択。

<img width="1172" alt="1F9963E9-7624-456F-9493-F621365D11A0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187718/1e8a3624-7c1e-b552-f822-a203afe07d06.png">


サンプルレート：44.1 kHzと書いてあります。
つまり**「1秒間に44100回、標本を取っている」**ということです。

ちなみにこの数値がどれくらいかというと一般の音楽CDと同じです。
サンプルレートは、他のオーディオデータと同じになっていないと再生スピードにズレが出てしまうので統一されています。

さらに「標本一つを何ビットのデータで表すか」を**量子化ビット数**と言います。
この値が大きいほど、より原音に近い音を再現することができます。
音楽CDでは16bitが一般的です。


今回のプロジェクトでは量子化ビット数はいくつになっているか確認します。
「Logic Pro X」＞「環境設定」＞「録音」と選択。

<img width="922" alt="C38737E1-E4F4-4DB4-BF6F-A304D402419E.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187718/00d04c6f-f868-ba38-a5ff-1f8b692cdc3b.png">



「24ビットレコーディング」にチェックが入っているためオーディオデータは量子化ビット数24ビットで録音されています。


このオーディオデータを再生する場合、1秒間に何ビット読み込む必要があるでしょうか。

サンプルレート　× 量子化ビット数　＝　一秒間のデータ量
44100 回　×  24ビット　＝　1058400ビット　＝　132300バイト　＝　**約129キロバイト**

約129キロバイトのデータ量になり、このようなオーディオデータを何個も何秒も再生していった結果、データ量がどんどん増えていき、CPUで処理できる限界に達したとき（ここの説明は勉強中）止まるということです。

#解決策
**「I/Oバッファサイズを大きくする」**ことをLogic Pro Xが勧めてくれています。

I/Oバッファサイズとはコンピュータの処理が間に合わなくなり問題が発生することを防ぐためにデータを一時的に溜めておける量を表したものです。

現状のI/Oバッファサイズを確認します。
「Logic Pro X」＞「環境設定」＞「オーディオ」＞「デバイス」と選択。

<img width="922" alt="B9F31623-4000-42CC-A4C9-01E7D54E0A79.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187718/e640c71d-eb87-433f-e87e-e8a122e5bec7.png">


現在は128サンプルになっています。
このサンプルというのは先ほどのサンプルレートと同じサンプルです。
つまり、1/44100サンプル × 128のデータをバッファできます。

しかし、これでは足りないということでこのサイズを大きくします。

<img width="922" alt="D7D65401-D30D-45E0-A8F3-FD7844C45955.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187718/7a4499a0-8299-f4f6-9503-25535d6caf73.png">


I/Oバッファサイズを128から256に一段階大きくしました。
これでトラックを再生すると先ほどよりもすぐに止まらなくなりました。

#最後に

実は、今回の方法ではレイテンシ（音の遅延）が増えてしまうので、まだ解決すべき問題は残っていますが。。。
一旦調べたことの整理としてここまで記しておきます。

他にも方法はたくさんあってAppleからの公式記事がありますので、続きはこちらを参照ください。（すみません）
https://support.apple.com/ja-jp/HT203930

それにしてもDAWの原理、奥が深すぎて途方もない。
一歩ずつ分かることを増やしていくしかないですね。
【Python】GIFにテキストをつける

[【Python】動画からGIFをつくる](https://qiita.com/kusobako/items/466d29cef344da26a08e) の続き。
[GIF Maker - Video to GIF Creator Tools | GIPHY](https://giphy.com/create/gifmaker) に少しでも近づけるようにテキストをつけられるようにした。
### コード
```python:追加したコード
from collections import namedtuple

from PIL import ImageDraw, ImageFont

# テキスト関連のデータまとめ用のタプル
Cc = namedtuple("Cc", [
    "start_index",
    "stop_index",
    "text",
    "font",
    "rgba",
    "point"
])


def make_cc(gif_fps, start_sec, stop_sec, text, font, rgba, point):
    start_index = start_sec * gif_fps
    stop_index = stop_sec * gif_fps
    return Cc(start_index, stop_index, text, font, rgba, point)


def add_text_to_image(im_list, cc_list):   
    new_im_list = []
    for index, im_obj in enumerate(im_list):
        new_im = None
        for cc in cc_list:
            if index >= cc.start_index and index <= cc.stop_index:
                if new_im is None:
                    new_im = add_text_to_image_object(im_obj, cc)
                else:
                    new_im = add_text_to_image_object(new_im, cc)
        
        if new_im is None:
            new_im_list.append(im_obj)
        else:
            new_im_list.append(new_im)
    
    return new_im_list


def add_text_to_image_object(im_obj, cc):
    base = im_obj.convert('RGBA')
    # make a blank image for the text, initialized to transparent text color
    text_img = Image.new('RGBA', base.size, (255,255,255,0))

    # get a drawing context
    d = ImageDraw.Draw(text_img)
    
    # draw text
    d.text(cc.point, cc.text, font=cc.font, fill=cc.rgba)

    out = Image.alpha_composite(base, text_img)
    return out
```
```python:メイン処理
def main():
    """メイン処理"""
    video_file = "Wildlife.wmv"

    fps, count = get_fps_n_count(video_file)
    if fps is None:
        print("動画ファイルを開けませんでした")
        return

    # gifにしたい範囲を指定
    start_sec = 0
    stop_sec = 8

    start_frame = int(start_sec * fps)
    stop_frame = int(stop_sec * fps)
    step_frame = 3

    print("開始(けっこう時間がかかる)")
    im_list = get_frame_range(video_file, start_frame, stop_frame, step_frame)
    if im_list is None:
        print("動画ファイルを開けませんでした")
        return
    
    # テキスト関連の処理
    gif_fps = len(im_list) // (stop_sec - start_sec)
    font1 = ImageFont.truetype(font=r"C:\Windows\Fonts\meiryo.ttc", size=32)
    font2 = ImageFont.truetype(font=r"C:\Windows\Fonts\BAUHS93.TTF", size=32)
    cc1 = make_cc(gif_fps, 0, 2, "あいうえお", font1, (255, 0, 0, 255), (5, 5))
    cc2 = make_cc(gif_fps, 1, 3, "ABCDE", font2, (0, 255, 0, 255), (5, 50))
    cc3 = make_cc(gif_fps, 2, 4, "かきくけこ", font1, (0, 0, 255, 255), (5, 100))
    cc4 = make_cc(gif_fps, 3, 5, "FGHIJ", font2, (255, 255, 255, 255), (5, 150))
    new_im_list = add_text_to_image(im_list, [cc1, cc2, cc3, cc4])

    make_gif('どうぶつ2.gif', new_im_list)
    print("終了")
```
### 出来たGIF
![どうぶつ2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407268/44c0de43-14b5-0ed5-069c-874355c92799.gif)
### 参考
https://pillow.readthedocs.io/en/latest/reference/ImageDraw.html



おわり