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

参考情報

解説

Image

Image (libgdx API)

設定項目 説明
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

Label (libgdx API)

設定項目 説明
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

TextField (libgdx API)

設定項目 説明
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

TextArea (libgdx API)

TextField と同様。 異なる点は text に改行文字を含むことができ、width を超える場合には改行して表示される。

List

List (libgdx API)

設定項目 説明
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

SelectBox (libgdx API)

設定項目 説明
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

ProgressBar (libgdx API)

設定項目 説明
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 のサブクラス。

Slider (libgdx API)

設定項目 説明
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 が存在しないため、エミュレーターを使用します。 なお、エミュレーターは自作です。

確認環境

参考情報

前回の記事

nosix.hatenablog.com

解説

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 モジュールのエミュレーターを自作した。

github.com

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 で実行する

概要

macOSPython プログラムの開発環境を構築します。 macOS で作成したプログラムを Raspberry Pi で実行できる様にします。 前回の記事の続きです。

確認環境

参考情報

前回の記事

nosix.hatenablog.com

解説

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 では実行のみ行う様にする

確認環境

参考情報

前回の記事

nosix.hatenablog.com

解説

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 Pissh でログインできる。

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

Raspberry Pi Zero を USB ケーブル 1 本で遊ぶ

概要

Raspberry Pi Zero をディスプレイ、ネットワークアダプタなしで使用する方法です。 類似タイトルの記事がいくつかあったので、それらの記事に倣っています。 2016-11-25 版の Raspbian では ssh がデフォルト off となっているため、ssh を有効化する設定を追記しています。

確認環境

参考情報

解説

SDカードに Raspbian を書き込む

Raspbian Jessie Lite のイメージファイルをダウンロード。 4GB以上の容量のあるSDカードを用意する。

diskutil コマンド、dd コマンドを使用して、イメージファイルをSDカードに書き込む。環境により異なるため省略して記載する。

  • diskutil list でSDカードが接続されているデバイスを特定
  • sudo diskutil unmount /dev/<上記で調べたデバイス> でアンマウント
  • sudo dd bs=1m if=2016-11-25-raspbian-jessie-lite.img of=/dev/<上記で調べたデバイス> で書き込む

設定を変更する

SDカードへの書き込みが完了すると自動的にマウントされて Finder に boot が表示される。

config.txt, cmdline.txt を書き換える。

  • config.txt
    • dtoverlay=dwc2 を追記
  • cmdline.txt
    • ... rootwait modules-load=dwc2,g_ether quiet init=/usr/lib/raspi-config/init_resize.sh

ssh ファイルを作成する。2016-11-25 では ssh はデフォルトで off となっている。boot に ssh ファイルを置くことで on となる。

$ cd /Volumes/boot
$ touch ssh

Finder で boot を取り出す。以上でSDカードの準備は完了。

Raspberry Pi Zero を起動する

SDカードを挿してから、USBケーブルをPCと接続する。 接続するとOSが起動するので待つ。 電源はPCから供給される。 緑ランプの点滅が収まれば起動が完了していると思われる。 (USBケーブルは家にあった適当なケーブルを使ったら繋がった。 USBケーブルによっては動作しないとのことなので注意。 参考情報のサイトを参照。)

ssh で接続する

PCのターミナルを起動し、以下を実行する。password は raspberry

mac:~ nosix$ ssh pi@raspberrypi.local
The authenticity of host 'raspberrypi.local (169.254.225.196)' 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,169.254.225.196' (ECDSA) to the list of known hosts.
pi@raspberrypi.local's password: 

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.

SSH is enabled and the default password for the 'pi' user has not been changed.
This is a security risk - please login as the 'pi' user and type 'passwd' to set a new password.

pi@raspberrypi:~ $ 

セキュリティリスクがあるので passwd コマンドで新しいパスワードに設定するように書かれている。

Windows 10 でパーティションサイズを変更する

※1 : 2017.07.22 追記/修正
※2 : 2017.09.14 追記
※3 : 2017.12.06 追記

概要

VMware を使用しているとディスクサイズを自由に変更することができます。 ディスクサイズを増やした時に C ドライブのパーティションサイズを拡張することになりますが、 回復パーティションが存在するために拡張できません。 本記事では、回復パーティションを移動して、パーティションサイズを拡張する方法を説明します。

私の環境(下記の確認環境)にて実施して確認した手順です。 他の環境にて動作を保証するものではありません。 他の環境のサポートは致しかねます。(※1)

確認環境

  • Windows 10 Pro
    • バージョン 1607 (※1)
    • OS ビルド 14393.479 (※1)
  • VMware Fusion 8.5.8 (※1)

参考情報

解説

全体の手順は以下の通り。

  1. 回復パーティションにドライブレターを割り当てる
  2. 回復イメージを保存する
  3. 回復パーティションを削除する
  4. 回復ドライブを作成できないことを確認する
  5. C ドライブを拡張する
  6. 新規パーティション(回復パーティションになる)を作成する
  7. 回復パーティションに設定する
  8. 回復パーティションのドライブレターを削除する
  9. 回復ドライブを作成できることを確認する

コマンドは全て管理者として実行する。

回復パーティションにドライブレターを割り当てる

回復パーティションにドライブレター R を割り当てる。

C:\WINDOWS\system32>diskpart

Microsoft DiskPart バージョン 10.0.14393.0

Copyright (C) 1999-2013 Microsoft Corporation.
コンピューター: XXX

DISKPART> list disk

  ディスク      状態           サイズ   空き   ダイナ GPT
  ###                                          ミック
  ------------  -------------  -------  -------  ---  ---
  ディスク 0    オンライン            64 GB    29 GB

DISKPART> select disk 0

ディスク 0 が選択されました。

DISKPART> list partition

  Partition ###  Type                Size     Offset
  -------------  ------------------  -------  -------
  Partition 1    プライマリ              500 MB  1024 KB
  Partition 2    プライマリ               33 GB   501 MB
  Partition 3    回復                 450 MB    33 GB

DISKPART> select partition 3

パーティション 3 が選択されました。

DISKPART> assign letter=R

DiskPart はドライブ文字またはマウント ポイントを正常に割り当てました。

DISKPART> exit

DiskPart を終了しています...

回復イメージを保存する

R ドライブに隠されている回復イメージ Winre.wim を参照できるようにした後に、コピーする。

C:\WINDOWS\system32>attrib R:\Recovery\WindowsRE\Winre.wim -h -s -r

C:\WINDOWS\system32>copy R:\Recovery\WindowsRE\Winre.wim C:\
        1 個のファイルをコピーしました。

回復パーティションを削除する

ドライブレターを削除した後にパーティションを削除する。 ドライブレターの削除を忘れると、後で同じドライブレターを使用できなくなるので注意。

C:\WINDOWS\system32>diskpart

Microsoft DiskPart バージョン 10.0.14393.0

Copyright (C) 1999-2013 Microsoft Corporation.
コンピューター: XXX

DISKPART> select disk 0

ディスク 0 が選択されました。

DISKPART> select partition 3

パーティション 3 が選択されました。

DISKPART> remove letter=R

DiskPart はドライブ文字またはマウント ポイントを正常に削除しました。

DISKPART> delete partition override

DiskPart は選択されたパーティションを正常に削除しました。

DISKPART> exit

DiskPart を終了しています...

回復ドライブを作成できないことを確認する

以下に示す手順により回復ドライブを作成できないことを確認する。

回復パーティションが削除された状態で回復ドライブの作成を行うと以下の結果となる。

C ドライブを拡張する

回復パーティションのサイズを覚えておく。

ドライブ C のパーティションを拡張する。回復パーティションのサイズ分だけ残す。

回復パーティションの領域を残して拡張した結果。

新規パーティション(回復パーティションになる)を作成する

新規パーティションを作成し、R ドライブとして設定する。

C:\WINDOWS\system32>diskpart

Microsoft DiskPart バージョン 10.0.14393.0

Copyright (C) 1999-2013 Microsoft Corporation.
コンピューター: XXX

DISKPART> select disk 0

ディスク 0 が選択されました。

DISKPART> create partition primary

DiskPart は指定したパーティションの作成に成功しました。

DISKPART> format quick fs=ntfs

  100% 完了しました

DiskPart は、ボリュームのフォーマットを完了しました。

DISKPART> assign letter=R

DiskPart はドライブ文字またはマウント ポイントを正常に割り当てました。

DISKPART> exit

DiskPart を終了しています...

回復パーティションに設定する

R ドライブにフォルダを作成し、回復イメージを配置する。 回復イメージの場所を設定し、有効化する。

(本記事執筆時の私の環境では下記の方法で設定できていましたが、 Windows 10 では REAgentC の仕様が変更されているとコメントにて情報を頂きました。 https://msdn.microsoft.com/ja-jp/library/windows/hardware/dn938308(v=vs.85).aspx (※2))

C:\WINDOWS\system32>md R:\Recovery\WindowsRE

C:\WINDOWS\system32>copy C:\Winre.wim R:\Recovery\WindowsRE
        1 個のファイルをコピーしました。

C:\WINDOWS\system32>C:\Windows\System32\reagentc /setosimage /path R:\Recovery\WindowsRE /index 1
ディレクトリは次に設定されています: \\?\GLOBALROOT\device\harddisk0\partition3\Recovery\WindowsRE

REAGENTC.EXE: 操作は成功しました。

C:\WINDOWS\system32>C:\Windows\System32\reagentc /enable
REAGENTC.EXE: 操作は成功しました。 (※1)

回復パーティションのドライブレターを削除する

設定は完了したため、ドライブレターは削除する。 回復パーティション(id=27)に設定する。

C:\WINDOWS\system32>diskpart

Microsoft DiskPart バージョン 10.0.14393.0

Copyright (C) 1999-2013 Microsoft Corporation.
コンピューター: XXX

DISKPART> select disk 0

ディスク 0 が選択されました。

DISKPART> list partition

  Partition ###  Type                Size     Offset
  -------------  ------------------  -------  -------
  Partition 1    プライマリ              500 MB  1024 KB
  Partition 2    プライマリ               63 GB   501 MB
  Partition 3    プライマリ              451 MB    63 GB

DISKPART> select partition 3

パーティション 3 が選択されました。

DISKPART> remove letter=R

DiskPart はドライブ文字またはマウント ポイントを正常に削除しました。

DISKPART> set id=27

DiskPart は、パーティション ID を設定しました。

DISKPART> exit

DiskPart を終了しています...

set id=27 では、「指定された種類は正しい形式ではありません。」とエラーになることがあるとの情報を頂きました。 その場合には、以下の様にするとのことです。(https://technet.microsoft.com/ja-jp/library/dd744301(v=ws.10).aspx) (※3)

set id=de94bba4-06d1-4d40-a16a-bfd50179d6ac

回復ドライブを作成できることを確認する

「回復ドライブを作成できないことを確認する」で行った手順を行う。 成功していれば以下の結果になる。

Thymeleaf で isXxx プロパティを参照するとエラーになる

概要

Thymeleaf の変数式で ${aVariable.isXxx} とするとエラーになって困った時の記録です。

確認環境

  • Spring Boot 1.4.2
    • Spring 4.3.4
    • Thymeleaf 2.1.5, 3.0.0
  • Kotlin 1.0.5

解説

Kotlin で次のクラスを作成したとする。

data class User(
    val name: String,
    val age: Int,
    val address: Address,
    val isEnabled: Boolean)

Java ならば以下のようになる。

public class User {

    private final String name;
    private final int age;
    private final Address address;
    private final boolean isEnabled;

    public boolean isEnabled() {
        return isEnabled;
    }

    ... (setter/getter, toString, equals, hashCode メソッドと続くが省略)
}

Thymeleaf では User オブジェクトの isEnabled プロパティを参照するために以下のように書く。

<span th:text="${user.isEnabled}">どうなるでしょう?</span>

これはエラーになる。isEnabled プロパティが見つからないと。 isEnabled ではなく enabled とすることで参照できる。

<span th:text="${user.enabled}">参照できる</span>

何故エラーになるのかと思い、デバッガで確認した。

org.springframework.expression.spel.support.ReflectivePropertyAccessor クラスの findGetterForProperty メソッドでは、 propertyName=isEnabled を IsEnabled に変換した後に findMethodForProperty メソッドを呼ぶ。

findMethodForProperty メソッドでは、prefix=is と methodSuffix=IsEnabled を結合して isIsEnabled とした上でメソッド名と equals で比較。

プロパティ名を enabled とすれば、isEnabled となるため参照できる。

Thymeleaf は内部的に SpEL (非SpringではOGNL) を使って式を解釈している。 上記は SpEL の一部に該当する。