Google App Engine スタンダード環境で Kotlin + Spring Boot を動かす
概要
2017.6.28 に Google App Engine スタンダード環境が Java8 をサポートしたと告知されました。 告知記事を読んでいると Kotlin + Spring Boot で動かすサンプルが目にとまったので、動かしてみました。
確認環境
- macOS 10.12.5
- Google Cloud SDK 161.0.0
- 158.0.0 でインストール後にアップデート
- App Engine Java 1.9.54
- IntelliJ IDEA Community 2017.1.4
参考情報
- Google Cloud Platform Blog: Google App Engine standard now supports Java 8
- 告知記事
- getting-started-java/appengine-standard-java8/kotlin-springboot-appengine-standard at master · GoogleCloudPlatform/getting-started-java · GitHub
- Google App Engine + Kotlin + Spring Boot のサンプル
- Gradle と App Engine プラグインを使用する | Java の App Engine スタンダード環境 | Google Cloud Platform
- Gradle で環境構築するための方法
解説
Google Cloud Platform
- プロジェクトを作成
(設定手順をメモしていませんでした…。)
Google Cloud SDK
手順は以下の通り。
ダウンロード後、ファイルの SHA256 チェックサムを検証してからファイルを展開。
install.sh を実行する前に好みのディレクトリに配置する。
筆者はユーザーのホームディレクトリに置いている (/Users/user_name/google-cloud-sdk
) 。
install.sh を実行することで、インストール済みコンポーネントを確認でき、コンポーネントのインストール/アンインストール方法を知れる。
また、環境変数 PATH
の自動設定を行える。
$ echo "6572e086d94ab2a6b3242a966b688a382a80a417101315d59d38ac804b5a366e google-cloud-sdk-158.0.0-darwin-x86_64.tar.gz" | shasum -a 256 -c google-cloud-sdk-158.0.0-darwin-x86_64.tar.gz: OK $ tar xvfz google-cloud-sdk-158.0.0-darwin-x86_64.tar.gz $ cd google-cloud-sdk $ ./install.sh ... Do you want to help improve the Google Cloud SDK (Y/n)? ... Modify profile to update your $PATH and enable shell command completion? Do you want to continue (Y/n)? ... [/Users/user_name/.bash_profile]: ...
途中の Y/n は全て初期設定 (Enter
) を選択。~/.bash_profile
にて PATH
の設定が追加される。
Terminal を起ち上げ直して、以下を続行。 Java Extension コンポーネントをインストールして、SDK をアップデートする。 その後にアカウントを初期化する。
$ gcloud components install app-engine-java $ gcloud components update $ gcloud init ... You must log in to continue. Would you like to log in (Y/n)? ... Please enter numeric choice or text value (must exactly match list item): google_cloud_platform_project_id ... API [compute-component.googleapis.com] not enabled on project [xxxxxxxxxxx]. Would you like to enable and retry? (Y/n)? ...
途中の Y/n は初期設定 (Enter
) を選択。ブラウザが起動して認証を求められる。
SDK の設定諸々は以下のコマンドで確認できる。
$ gcloud info ... Installation Root: [/Users/user_name/google-cloud-sdk] ...
IntelliJ IDEA でプロジェクト作成
- Gradle Project を作成
- appengine-web.xml 作成
- Java8 スタンダード環境を指定
- build.gradle 作成
- Application.kt 作成
- JsonObjectMapper の設定
- TemplateEngine の設定
- JsonObjectMapper 作成
- Controller, Template などを作成 (省略)
- appengineRun タスクで実行
- ローカル環境で動かす
- appengineDeploy タスクで配置
- Google の環境で動かす
appengine-web.xml, build.gradle, Application.kt, JsonObjectMapper は以下の Gist を参照のこと。
Google App Engine Standard Environment in Java8 wi …
Serializer/Deserializer クラスは ObjectMapper の addSerializer/addDesirializer メソッドに型を合わせるために用意している。
IntelliJ IDEA の Terminal にて、以下を実行すればローカル環境で試せる。
$ ./gradlew appengineRun ... 04, 2017 4:56:51 午前 com.google.appengine.tools.development.AbstractModule startup 情報: Module instance default is running at http://localhost:8080/ 7 04, 2017 4:56:51 午前 com.google.appengine.tools.development.AbstractModule startup 情報: The admin console is running at http://localhost:8080/_ah/admin 7 04, 2017 1:56:51 午後 com.google.appengine.tools.development.DevAppServerImpl doStart 情報: Dev App Server is now running
http://localhost:8080/
をダブルクリックで選択し、コンテキストメニューの Open as URL
を選択するとブラウザで開ける。
(もっと楽に起動できる方法は?)
同じく、以下を実行すれば Google Cloud Platform 環境に配置される。
$ ./gradlew appengineDeploy ...
配置後、gcloud app browser
とすることでデフォルトブラウザが開くが、デフォルトブラウザの設定が不明。
OS のデフォルトは Chrome に設定されているのに、Firefox が開く。
(どこに設定が?)
他の設定もしていた build.gradle から余計な設定を削除したため、もしかしたら削除し過ぎていて問題が起こるかも。 IntelliJ IDEA に Google Cloud Tools plugin をインストールしているが、無くてもよいはずなので記載していない。 Google Cloud Tools を使うことで App Engine に配置できるはずが機能しない。 (何か設定しないとだめ?)
IntelliJ IDEA で Emacs 風キーマップを作る
概要
IntelliJ IDEA の keymap 設定を Emacs にしても、使いたい機能のショートカットが複雑で覚えられません。 そこで、Emacs 風のショートカットに設定を変更します。
筆者は最初に覚えたエディタが Emacs でしたが、その後は Emacs のライトユーザーです。 ヘビーユーザーからしたら Emacs 風ではないと思われるかもしれません。
確認環境
- IntelliJ IDEA Community 2017.1.4
参考情報
解説
Emacs と言えば、ESC
キーと Ctrl
キーから連なるショートカットが特徴的かと思います。
IntelliJ IDEA ではショートカットキーに Second stroke を設定することにより、
Emacs 風のショートカットキーを設定できます。
私は、Ctrl-[
を ESC
キーとして使用しています(Emacs 標準のはず)。
そのため、Ctrl-[
, Ctrl-x
, Ctrl-c
, Ctrl-h
には Second stroke を設定しています。
Ctrl-x
と Ctrl-c
で使い分けがあったかもしれませんが無視することにして設定しています。
Ctrl-h
はヘルプ系のアクションを割り当てています。
また、Emacs とは異なりますが、Navigation 系のアクションを Ctrl-j
(Jump のつもり) に割り当てています。
基本的には、アクションの意味を表すようにキーを選択しているつもりです(Project ならば p
, Intention なら i
とか)。
使用頻度の低いと思われるアクションは Ctrl-[ x
で Find Action を起動して実行する方針です。
設定した内容を整理したスプレッドシートを公開しています。 設定を変更していないアクションについては記載していません。
アクションの一覧は intellJ IDEA Community のソースコードから取得しています。
intellij-community/ActionsBundle.properties at master · JetBrains/intellij-community · GitHub
IntellJ IDEA の便利機能をまだまだ知らないので、こんな便利機能があるよ!という話があれば伺いたいです。 また、Emacs の標準的なショートカットキーで、このキー設定は鉄板!という話があれば伺いたいです。
libGDX で iOS アプリを起動できない
概要
libGDX で Multi-OS Engine を使用した iOS アプリを起動するとエラーが発生しました。 libGDX が初期設定で指定している Multi-OS Engine は 1.2.3 ですが 1.2.5 に変更する必要があります。
確認環境
- AndroidStudio 2.2.3
- libGDX 1.9.5
- Multi-OS Engine 1.2.3
- Xcode 8.2.1
参考情報
解説
Intel Multi-OS Engine 1.2.3 で実行すると以下のエラーが表示される。
Terminating app due to uncaught exception 'NSInvalidArgumentException'
build.gradle で moe-gradle のバージョンを 1.2.5 にすると解決する。
classpath 'org.multi-os-engine:moe-gradle:1.2.5'
libGDX 2D UI Library - Widgets
概要
libGDX の 2D UI Library のうち、Button 系以外の Widget についてまとめます。
目次
確認環境
- OS X El Capitan (10.11.6)
- Xcode 8.0
- Android Studio 2.2.2
- Multi-OS Engine Plugin 1.2.1
- Kotlin 1.0.4
- buildToolVersion 25.0.0
- compileSdkVersion 24
- libGDX 1.9.5-SNAPSHOT
参考情報
- Scene2d · libgdx/libgdx Wiki · GitHub
- Scene2d 公式文書。
- Viewports · libgdx/libgdx Wiki · GitHub
- Viewport 公式文書。
- Scene2d.ui · libgdx/libgdx Wiki · GitHub
- Scene2d UI 公式文書。
- libGDXの基礎5 Scene2Dを使う - Qiita
- 概要。
解説
Image
設定項目 | 型 | 説明 |
---|---|---|
drawable | Drawable | 描画対象 |
Skin, String | ||
NinePatch | ||
Texture | ||
TextureRegion | ||
scaling | Scaling | Scaling.none, Scaling.fit, Scaling.fill, Scaling.stretch (default)。(fill, stretch には X, Y のみがある。) |
align | Int | Align.topLeft, Align.top, Align.topRight, Align.right, Align.bottomRight, Align.bottom, Align.bottomLeft, Align.left, Align.center (default)。 |
サイズ
min | pref | max | |
---|---|---|---|
width/height | 0 | drawable のサイズ | 0 |
Scaling (align = Align.center の場合)
Scaling.none | Scaling.fit |
---|---|
Scaling.fill | Scaling.stretch |
サンプルコード
private val actor by lazy { Image(Texture("badlogic.jpg")).apply { width = this@MyGdxGame.stage.width height = this@MyGdxGame.stage.height setScaling(Scaling.none) //setScaling(Scaling.fit) //setScaling(Scaling.fill) //setScaling(Scaling.stretch) setAlign(Align.center) } } override fun create() { ... stage.addActor(actor) }
Label
設定項目 | 型 | 説明 |
---|---|---|
text | CharSequence | 描画対象。改行文字 ('\n') で改行される。 |
style | Label.LabelStyle | background, font, fontColor |
Skin | ||
labelAlign | Int | Label 全体の配置。Align.topLeft, Align.top, Align.topRight, Align.right, Align.bottomRight, Align.bottom, Align.bottomLeft, Align.left (default), Align.center。 |
lineAlign | Int | 各行の配置。Align.left (default), Align.center, Align.right。 |
ellipsis | Boolean | true ならば width を超えないように末尾を "..." に置き換える。(default: false) |
String | null 以外ならば width を超えないように末尾を指定した String に置き換える。 | |
fontScaleX | Float | X 方向の拡大率。 |
fontScaleY | Float | Y 方向の拡大率。 |
wrap | Boolean | true ならば width を超える場合に改行する。ellipsis が優先される。(default: false) |
サイズ
min | pref | max | |
---|---|---|---|
width/height | prefと同じ | text が収まるサイズを算出 | 0 |
text = "Hello World\nHello Beautiful World!!"、fontScaleX = 5、fontScaleY = 10 の場合の表示例。
labelAlign = topLeft, lineAlign = center | labelAlign = center, lineAlign = center | labelAlign = bottomRight, lineAlign = center |
---|---|---|
labelAlign = center, lineAlign = left | labelAlign = center, lineAlign = center | labelAlign = center, lineAlign = right |
---|---|---|
サンプルコード
private val actor by lazy { Label("Hello World\nHello Beautiful World!!", Label.LabelStyle(BitmapFont(), Color.WHITE)).apply { width = this@MyGdxGame.stage.width height = this@MyGdxGame.stage.height setFontScale(5f, 10f) setAlignment(Align.center, Align.center) } } override fun create() { ... stage.addActor(actor) }
TextField
設定項目 | 型 | 説明 |
---|---|---|
text | String | 入力欄に設定される文字列。改行文字 ('\n') は除去される。 |
style | TextField.TextFieldStyle | BitmapFont (font, messageFont), Color (fontColor, messageFontColor, disabledFontColor, focusedFontColor), Drawable (background, disabledBackground, focusedBackground, cursor, selection) |
Skin | ||
alignment | Int | Align.left, Align.center, Align.right。 |
maxLength | Int | 入力可能文字数。 |
messageText | String | text 未入力時に表示する文字列。入力を削除しても再表示されない。 |
onlyFontChars | Boolean | true (default) ならば、text に set する際に BitmapFont に含まれない文字を除去する。false ならば、含まれない文字を空白に置き換える。 |
passwordMode | Boolean | true ならば、入力文字列を passwordCharacter で表示する。false (default) ならば、入力文字列をそのまま表示する。 |
passwordCharacter | Char | passwordMode が true のときに使用する文字。設定しないと passwordMode = true でもマスクされない。 |
サイズ
min | pref | max | |
---|---|---|---|
width/height | prefと同じ | ? | 0 |
サンプルコード
private val actor by lazy { TextField("", TextField.TextFieldStyle().apply { font = BitmapFont().apply { data.scale(10f) } messageFont = font fontColor = Color.WHITE messageFontColor = Color.RED }).apply { width = this@MyGdxGame.stage.width height = this@MyGdxGame.stage.height maxLength = 10 messageText = "message" isPasswordMode = false setPasswordCharacter('x') setAlignment(Align.right) setOnlyFontChars(false) //text = "aとbとc" } } override fun create() { ... stage.addActor(actor) }
TextArea
TextField と同様。 異なる点は text に改行文字を含むことができ、width を超える場合には改行して表示される。
List
設定項目 | 型 | 説明 |
---|---|---|
items | Array |
リストの項目。文字列として表示される。 |
style | List.ListStyle | BitmapFont (font), Color (fontColorSelected, fontColorUnselected), Drawable (background, selection) |
Skin |
サイズ
min | pref | max | |
---|---|---|---|
width/height | prefと同じ | ? | 0 |
実行例
サンプルコード
private val actor by lazy { List<String>(List.ListStyle().apply { font = BitmapFont().apply { data.scale(10f) } fontColorUnselected = Color.WHITE fontColorSelected = Color.RED selection = SpriteDrawable(Sprite(Texture("badlogic.jpg"))) }).apply { width = this@MyGdxGame.stage.width height = this@MyGdxGame.stage.height setItems("pen", "apple", "pineapple") } } override fun create() { ... stage.addActor(actor) }
SelectBox
設定項目 | 型 | 説明 |
---|---|---|
items | Array |
リストの項目。文字列として表示される。 |
style | SelectBox.SelectBoxStyle | BitmapFont (font), Color (fontColor, disabledFontColor), Drawable (background, backgroundOpen, backgroundOver, backgroundDisabled), List.ListStyle (BitmapFont (font), Color (fontColorSelected, fontColorUnselected), Drawable (background, selection)), ScrollPane.ScrollPaneStyle (Drawable (background, corner, hScroll, hScrollKnob, vScroll, vScrollKnob)) |
Skin | ||
maxListCount | Int | ドロップダウン時に表示する項目数。(default: items の要素数) |
scrollingDisabled | Boolean | true ならば、ドロップダウン時のスクロールを無効にする (maxListCount の個数しか表示されない)。false (default) ならば、有効にする。 |
サイズ
min | pref | max | |
---|---|---|---|
width/height | prefと同じ | ? | 0 |
実行例
default | maxListCount = 3 | maxListCount = 3, scrollingDisabled = true |
---|---|---|
サンプルコード
private val actor by lazy { val commonFont = BitmapFont().apply { data.scale(10f) } SelectBox<String>(SelectBox.SelectBoxStyle().apply { font = commonFont fontColor = Color.WHITE background = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.YELLOW }) backgroundOpen = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.GRAY }) listStyle = List.ListStyle().apply { font = commonFont fontColor = Color.GRAY fontColorUnselected = Color.GREEN fontColorSelected = Color.RED selection = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.RED }) } scrollStyle = ScrollPane.ScrollPaneStyle().apply { vScroll = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.BLUE }) vScrollKnob = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.MAGENTA }) } }).apply { val stageW = this@MyGdxGame.stage.width val stageH = this@MyGdxGame.stage.height setPosition(stageW/2 - width/2, stageH - height) setItems("1", "2", "3", "4", "5", "6", "7", "8", "9", "0") //maxListCount = 3 //setScrollingDisabled(true) } } override fun create() { ... stage.addActor(actor) }
ProgressBar
設定項目 | 型 | 説明 |
---|---|---|
min | Float | 最小値。 |
max | Float | 最大値。 |
stepSize | Float | 最小値から最大値を分割したときの一区画のサイズ。value は近くの区画に丸められる。 |
vertical | Boolean | true ならば、縦方向に表示。false ならば、横方向に表示。 |
style | ProgressBar.ProgressBarStyle | Drawable (background, knob, knobBefore, knobAfter, disabledBackground, disabledKnob, disabledKnobBefore, disabledKnobAfter) |
Skin | ||
visualInterpolation | Interpolation | value に set して、表示が更新される際のアニメーション表示方法。 |
animateDuration | Float | アニメーション時間。単位は秒。 |
animateInterpolation | Interpolation | setAnimationDuration を実行した際に行われるアニメーションの表示方法。 |
サイズ
min | pref | max | |
---|---|---|---|
width/height | prefと同じ | ? | 0 |
実行例
vertical = false | vertical = true |
---|---|
サンプルコード
private val actor by lazy { ProgressBar(0f, 100f, 25f, false, ProgressBar.ProgressBarStyle().apply { background = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.WHITE }) knob = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.BLUE }) knobBefore = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.RED }) knobAfter = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.GREEN }) } ).apply { width = this@MyGdxGame.stage.width height = this@MyGdxGame.stage.height setVisualInterpolation(Interpolation.fade) setAnimateDuration(10f) value = 60f setAnimateInterpolation(Interpolation.bounce) setAnimateDuration(20f) } } override fun create() { ... stage.addActor(actor) }
Slider
ProgressBar のサブクラス。
設定項目 | 型 | 説明 |
---|---|---|
min | Float | 最小値。 |
max | Float | 最大値。 |
stepSize | Float | 最小値から最大値を分割したときの一区画のサイズ。value は近くの区画に丸められる。 |
vertical | Boolean | true ならば、縦方向に表示。false ならば、横方向に表示。 |
style | Slider.SliderStyle | Drawable (background, knob, knobBefore, knobAfter, knobDown, knobOver, disabledBackground, disabledKnob, disabledKnobBefore, disabledKnobAfter) |
Skin | ||
visualInterpolationInverse | Interpolation | value に現在の value よりも小さい値を set して、表示が更新される際のアニメーション表示方法。 |
サイズ
min | pref | max | |
---|---|---|---|
width/height | prefと同じ | ? | 0 |
サンプルコード
private val actor by lazy { Slider(0f, 100f, 25f, false, Slider.SliderStyle().apply { background = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.WHITE }) knob = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.BLUE }) knobBefore = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.RED }) knobAfter = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.GREEN }) knobDown = SpriteDrawable(Sprite(Texture("badlogic.jpg")).apply { color = Color.MAGENTA }) } ).apply { width = this@MyGdxGame.stage.width height = this@MyGdxGame.stage.height value = 60f setVisualInterpolationInverse(Interpolation.smooth) setAnimateDuration(3f) value = 10f } } override fun create() { ... stage.addActor(actor) }
GPIO 操作するプログラムを macOS で開発する
概要
macOS 環境で RPi.GPIO モジュールを使用します。 macOS 環境では GPIO が存在しないため、エミュレーターを使用します。 なお、エミュレーターは自作です。
確認環境
- Raspberry Pi Zero
- RASPBIAN JESSIE LITE 2016-11-25
- Python 3.4.2
- MacBook Pro 2016 Late
参考情報
- Raspberry Pi GPIO Emulator | Roderick Vella Galea
- Raspberry Pi GPIO Emulator
- GitHub - nosix/raspberry-gpio-emulator: RPi.GPIO emulator
- 自作の RPi.GPIO Emulator
前回の記事
解説
Raspberry Pi には RPi.GPIO モジュールがインストールされているが、macOS 環境にはインストールされていない。 例えば、以下のプログラムを macOS 環境の PyCharm で作成すると No module named RPi とのエラーメッセージが表示される。
hello_gpio.py
from time import sleep import RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) GPIO.setup(25, GPIO.OUT) while True: GPIO.output(25, GPIO.HIGH) sleep(0.5) GPIO.output(25, GPIO.LOW) sleep(0.5)
macOS 環境に RPi モジュールをインストールする代わりに RPi モジュールのエミュレーター(もしくは、シミュレーター)を探した。 いくつか見つかったが、モジュール名を変更しなければいけないなど、ソースコード(上記の例では hello_gpio.py)に全く手を入れずに使えるものではなかった。そこで、RPi モジュールのエミュレーターを自作した。
RPi.GPIO エミュレーターをインストールする
GitHub にある Python パッケージを pip を使って仮想環境にインストール。
mac:~ nosix$ source ~/pyenv/python3/bin/activate (python3) mac:~ nosix$ (python3) mac:~ nosix$ pip install git+https://github.com/nosix/raspberry-gpio-emulator/ Collecting git+https://github.com/nosix/raspberry-gpio-emulator/ Cloning https://github.com/nosix/raspberry-gpio-emulator/ to /private/var/folders/kz/wv_kdss90634rlck2k9d852m0000gn/T/pip-6akufwi3-build Installing collected packages: RPi Running setup.py install for RPi ... done Successfully installed RPi-0.1.0
インストールが完了するとエラーは消える。
macOS 環境、Raspberry Pi 環境のいずれでも変更なしで実行できる。
macOS 環境で実行した場合には、エミュレーターが起動されてピンの状態を表示。
Raspberry Pi 環境で実行した場合には、GPIO が操作される。
なお、エミュレーターの UI が気に入らない場合には、自作 UI に差し替えられる設計になっている。 また、各 GPIO の状態変化を監視するプラグインを追加できる様になっている。
macOS で Python プログラム開発環境を構築して Raspberry Pi で実行する
概要
macOS で Python プログラムの開発環境を構築します。 macOS で作成したプログラムを Raspberry Pi で実行できる様にします。 前回の記事の続きです。
確認環境
- Raspberry Pi Zero
- RASPBIAN JESSIE LITE 2016-11-25
- Python 3.4.2
- MacBook Pro 2016 Late
参考情報
- パッケージ管理システム Homebrew - Qiita
- macOS のパッケージ管理ツール HomeBrew
前回の記事
解説
Python3 環境を構築する
HomeBrew が導入されていなければ導入する。 HomeBrew は macOS においてソフトウェアパッケージを管理するツール。 ソフトウェアパッケージのインストール、アンインストール、アップグレードなどを行うことができる。
mac:~ nosix$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew コマンドを使用して Python3, virtualenv をインストール。 pip は Python パッケージを管理するツール。 Python プログラムで使用できる便利な機能 (ライブラリ) をインストールするために使用する。 virtualenv は仮想環境(実験室)を作成するツール。 Python バージョンやライブラリの組み合わせを切り替えることが容易になる。
mac:~ nosix$ brew install python3 mac:~ nosix$ pip3 install virtualenv
pip を使用した際に新しいバージョンの通知がある場合は、指示に従ってアップグレードする。
You are using pip version 8.1.2, however version 9.0.1 is available. You should consider upgrading via the 'pip install --upgrade pip' command. mac:~ nosix$ pip3 install --upgrade pip
Python3 用の virtualenv (仮想環境) を作成する。 作成後に --version オプションを指定して python コマンドを実行し、バージョンを確認する。
mac:~ nosix$ mkdir ~/pyenv mac:~ nosix$ virtualenv -p python3 ~/pyenv/python3 mac:~ nosix$ source ~/pyenv/python3/bin/activate (python3) mac:~ nosix$ (python3) mac:~ nosix$ python --version Python 3.5.2
PyCharm を導入する
PyCharm Community をダウンロードし、インストール。 もしくは、IntelliJ IDEA など JetBarins 製 IDE を使用しているならば、Python Community Edition プラグインをインストール。 以降の画面例は IntelliJ IDEA に Python Community Edition プラグインをインストールしたもの。 なお、有償版を使うと他のコンピューターで動作している Python プログラムをデバッガで監視できるらしい。
以下はプラグインのインストール。Preferences... から
Python プログラムを作る
PyCharm (もしくは、IntelliJ IDEA + Plugin) を使用して Python プログラムを作る。
仮想環境を指定してプロジェクトを作成する。仮想環境を指定するにあたって、PyCharm に作成済みの仮想環境を登録する。
PyCharm に作成済みの仮想環境を登録。~/pyenv/python3/bin/python
を選択することで登録される。
登録した仮想環境を選択。
プロジェクト名はお好みで。
Python プログラムを作成して、実行する。
hello.py
from time import sleep while True: print("Hello") sleep(1)
Raspberry Pi で実行する
前回、pidep コマンドを作成して Raspberry Pi にログインせずに macOS から実行できる環境を作った。 pidep コマンドを PyCharm で実行できる様にする。
Preferences... を開き Tools > External Tools と移動。
pidep コマンドを使って hello.py を Raspberry Pi で実行する。
再ログインした際に pidep コマンドを実行すると以下のエラーとなることがある。
Permission denied, please try again. Permission denied, please try again. Permission denied (publickey,password). lost connection
このような場合には Terminal で ssh-add コマンドを実行して認証を済ませておく。
Raspberry Pi の Python 実行環境を整える
概要
USB ケーブル 1 本で Raspberry Pi Zero と接続できる様になったので、Python プログラムを Raspberry Pi Zero で動かしてみます。 しかし、今後の試行錯誤を考え、(自分にとっての)プログラムを作りやすい環境を整えてみました。 具体的には以下を行なっています。
- ssh でのログインをパスワードなしにする
- Python 2.x 系と 3.x 系を切り替えられる様にする
- Mac でプログラムを作成し、Raspberry Pi Zero では実行のみ行う様にする
確認環境
- Raspberry Pi Zero
- RASPBIAN JESSIE LITE 2016-11-25
- MacBook Pro 2016 Late
- macOS 10.12
参考情報
- Raspberry Pi ZeroをUSBケーブル1本で遊ぶ | Japanese Raspberry Pi Users Group
- Raspberry Pi Zero からインターネットに接続する方法
- signals - Ctrl-C handling in SSH session - Unix & Linux Stack Exchange
- ssh で接続した時に Ctrl-C をリモートに渡す方法
- 引数を処理する | UNIX & Linux コマンド・シェルスクリプト リファレンス
- bash でコマンド引数を扱う方法
- シェル変数のデフォルト値を設定する、未初期化時にエラーメッセージを出力してスクリプトを強制終了する - 百日半狂乱
- シェル変数にデフォルト値を設定する方法
- virtualenvでpython環境を管理する - Qiita
- virtualenv の使い方
前回の記事
解説
ssh コマンドでパスワードを入力せずにログインする
前回、ssh コマンドでログインできる様になった。 しかし、ssh コマンドでログインする度にパスワードを入力するのは面倒だ。 そこで、公開鍵認証を使用してパスワード入力せずにログインできる様にする。
Mac で鍵ペアを生成して、公開鍵のみを Raspberry Pi の ~/.ssh/authorized_keys に登録する。 秘密鍵は絶対に外部に漏らしてはいけない。 ssh-keygen コマンドは鍵ペアを生成する。 鍵ペアは ~/.ssh ディレクトリに id_rsa (秘密鍵), id_rsa.pub (公開鍵) として生成される。 安全のためにパスフレーズ (passphrase) は入力した方が良い。 id_rsa.pub を Raspberry Pi の ~/.ssh/authorized_keys に登録する。
mac:~ nosix$ ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/Users/nosix/.ssh/id_rsa): Created directory '/Users/nosix/.ssh'. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /Users/nosix/.ssh/id_rsa. Your public key has been saved in /Users/nosix/.ssh/id_rsa.pub. ...省略... mac:~ nosix$ scp ~/.ssh/id_rsa.pub pi@raspberrypi.local:~ id_rsa.pub 100% 404 0.4KB/s 00:00 mac:~ nosix$ ssh pi@raspberrypi.local ...省略... pi@raspberrypi:~ $ mkdir .ssh pi@raspberrypi:~ $ cat id_rsa.pub >> .ssh/authorized_keys pi@raspberrypi:~ $ rm id_rsa.pub pi@raspberrypi:~ $ exit
authorized_keys は存在していないため cat コマンドではなく mv コマンドでも構わないが、
既に authorized_keys が存在する場合には追記する必要がある。
cat src_file >> dst_file
で追記できることを知っておくと便利かと思う。
以上で設定は完了したので、ssh コマンドでログインしてみる。 途中で接続を続けるかと尋ねられるので yes とする。 Raspberry Pi を識別するための情報(ECDSA key fingerpring)が Mac の ~/.ssh/known_hosts に登録される。 次回からは途中で尋ねられることなくログインできる。
mac:~ nosix$ ssh pi@raspberrypi.local The authenticity of host 'raspberrypi.local (<省略>)' can't be established. ECDSA key fingerprint is SHA256:<省略>. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added 'raspberrypi.local,<省略>' (ECDSA) to the list of known hosts. The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Fri Dec 16 04:36:43 2016 from <省略> pi@raspberrypi:~ $
ログインできれば成功。 Raspberry Pi (の authorized_keys) に登録した Mac からはパスワードなしでログインできる。 Mac 側では ssh-keygen で入力したパスフレーズを 1 度だけ入力することがある。 パスフレーズで認証しておくことで、ssh でログインする際にはパスワードが不要になる。 パスワードは passwd コマンドで複雑なパスワードに変更する。
~/.bash_profile にコマンドのエイリアス(別名)を登録しておくと便利。 以下の 1 行を追加すると pish コマンドで Raspberry Pi に ssh でログインできる。
alias pish='ssh pi@raspberrypi.local'
PC に再ログインした場合には、ssh-add コマンドを実行して認証する。 認証が完了していない場合には、ssh の実行の際にパスフレーズの入力が求められる。
mac:~ nosix$ pish Enter passphrase for key '/Users/nosix/.ssh/id_rsa':
Python3 環境を Raspberry Pi に作る
Python には 2.x 系と 3.x 系が存在する。 2.x 系と 3.x 系で異なる部分があり、2.x 系で動くからと言って 3.x 系では動かないという場合がある。 もちろん、反対もあり得る。 Raspberry Pi に標準でインストールされている Python は 2.x 系。 3.x 系の Python も導入し、2.x 系と 3.x 系を切り替えられる様にしておく。
Raspberry Pi に python3, pip (Python のパッケージ管理ツール), virtualenv (実行環境を切り替えるツール) をインストールする。 pip を使うことで、多くの人が作ってくれた Python の便利道具(パッケージ)を簡単に導入できる。 パッケージにはバージョンがあり、バージョンの組み合わせによっては動作しないこともある。 様々な実験を行うために個別の実験室(仮想環境; virtualenv)を用意しておくと便利。(実験室は比喩。) パッケージを導入する際には実験室を指定して導入し、他の実験室には影響が及ばない様にする。 また、実験室毎に Python2 と Python3 のいずれを使うかを決めておくことができる。
まず、Raspberry Pi からインターネットに接続できる様にする。 Mac でインターネット共有を設定する。 RNDIS/Ethernet Gadget にチェックを入れる。
次に、python3, python3-rpi.gpio, pip, virtualenv をインストールする。
pi@raspberrypi:~ $ sudo apt-get install python3 ...省略... pi@raspberrypi:~ $ sudo apt-get install python3-rpi.gpio ...省略... pi@raspberrypi:~ $ sudo apt-get install python-pip ...省略... pi@raspberrypi:~ $ sudo pip install virtualenv ...省略...
実験室 (仮想環境; virtualenv) を作成する。 例では、~/pyenv/python2, ~/pyenv/python3 を作成している。 apt-get で install した python3-rpi.gpio を使用するために、--system-site-packages を指定している。 (ディレクトリはお好みで。)
pi@raspberrypi:~ $ mkdir ~/pyenv pi@raspberrypi:~ $ virtualenv -p python2 ~/pyenv/python2 Running virtualenv with interpreter /usr/bin/python2 New python executable in /home/pi/pyenv/python2/bin/python2 Also creating executable in /home/pi/pyenv/python2/bin/python Installing setuptools, pip, wheel...done. pi@raspberrypi:~ $ virtualenv --system-site-packages -p python3 ~/pyenv/python3 Running virtualenv with interpreter /usr/bin/python3 Using base prefix '/usr' New python executable in /home/pi/pyenv/python3/bin/python3 Also creating executable in /home/pi/pyenv/python3/bin/python Installing setuptools, pip, wheel...done.
試しに実験室に入る。activate を source コマンドで読み込むと仮想環境が有効になる。
pi@raspberrypi:~ $ source ~/pyenv/python3/bin/activate (python3) pi@raspberrypi:~ $ (python3) pi@raspberrypi:~ $ python --version Python 3.4.2
実験室から出る(仮想環境を無効にする)には、deactivate コマンドを使う。
(python3) pi@raspberrypi:~ $ deactivate pi@raspberrypi:~ $
以降、pip でパッケージをインストールする際には、実験室に入ってから行う。 全ての実験室で共通して使いたいパッケージの場合には、実験室に入らずに pip でインストールする。
Mac 環境でソースコードを作成して、Raspberry Pi で動かす
Raspberry Pi で IDLE を使用してソースコードを編集しても良いが、Mac 環境の方がツールも多く便利である。 Mac 環境でソースコードを作成して、Raspberry Pi にコピーして Raspberry Pi で実行する仕組みを用意する。 (この節の内容は試行中のものです。)
~/bin/pidep コマンドを自作する。(dep は Deploy の意。) Mac から Raspberry Pi にファイルを scp でコピーして、Mac から ssh で実行するスクリプトになっている。
コマンドを実行できる様にする。
mac:~ nosix$ chmod +x ~/bin/pidep
Raspberry Pi に ~/pyhome ディレクトリと仮想環境 ~/pyenv/python3 を用意しておく。
pi@raspberrypi:~ $ mkdir ~/pyhome
pidep の PI_HOME と PI_ENV で指定している。 PI_HOME はコピー先のルート、PI_ENV は実行時に使用する仮想環境の指定。
使い方
まずは、Python3 プログラム(hello.py)を用意する。1 秒毎に Hello と表示される。
from time import sleep while True: print("Hello") sleep(1)
このプログラムを pidep で Raspberry Pi にコピーして実行する。 -s はコピー元、-r は実行するプログラム(スクリプト)を指定する。
mac:tmp nosix$ pidep -s hello.py -r hello.py hello.py 100% 68 0.1KB/s 00:00 Hello Hello Hello Hello ^CTraceback (most recent call last): File "hello.py", line 5, in <module> sleep(1) KeyboardInterrupt Connection to raspberrypi.local closed.
-d でコピー先を指定できる。 -r を省略するとコピーのみ、-s を省略すると実行のみ行われる。
mac:tmp nosix$ pidep -s hello.py -d loop.py -r loop.py mac:tmp nosix$ pidep -r hello.py mac:tmp nosix$ pidep -s hello.py