みつきんのメモ

組み込みエンジニアです。Interface誌で「Yocto Projectではじめる 組み込みLinux開発入門」連載中

Flask-Bootstrapを使ってみる

前回のつづき

pythonでWebアプリケーションをつくる場合、Flaskが便利。

Webアプリケーションでは、デザインを定義するためにCSSが必要だが意外とめんどくさい。 そこで定義済みのCSSフレームワークを使用することで、手間を軽減することができる。

そのうちの一つにBootstrapがある。

Flaskの使い方を探してみると、htmlファイルの作成時にBootstrapを使用している例が多い。

Flask-Bootstrapを使用すると、更にhtmlファイルの記述量を減らすことができる。

インストールは例によってpipでできるので省略する。

使い方

使い方の例を探してみると、簡単に書けるせいかいきなり凝ったものが多く、 意外とこれと言ったものがなかった。

ここでは何も凝らずに、ものすごく単純なものを作成する。

ディレクトリ構成

ディレクトリ構成はFlaskの時と同じ。

.
├── bootstrap.py
└── templates
    └── bootstrap.html

bootstrap.py(pythonスクリプト)

# coding: utf-8
from flask import Flask, render_template
from flask_bootstrap import Bootstrap

app = Flask(__name__)
bootstrap = Bootstrap(app)


@app.route('/')
def index():
    return render_template('bootstrap.html')


if __name__ == '__main__':
    app.run()

基本はFlaskの時と同じで、Bootstrapオブジェクトの作成が追加されている。 サイトのルートにアクセスすると、「bootstrap.html」の内容を返す。

bootstrap.html(htmlファイル)

たったこれだけの記述で、Bootstrapを使用したWebページを作成することができる。

{% extends "bootstrap/base.html" %}

{% block title %}Hello{% endblock %}

{% block content %}
<div class="container">
    <div class="page-header">
    <h1>Hello World!</h1>
    </div>
</div>
{% endblock %}

実体は、{% block content %}〜{% endblock %}の間の部分だけ。

{% extends "bootstrap/base.html" %}の行で、Flask-Bootstrapが予め定義しているテンプレートを参照しており、 この中で、BootstrapのCSSの定義や基本的なレイアウトが定義されている。

共通のテンプレートを参照することで、デザインの統一もより簡単に行なうことができる。

Flaskの機能も健在なのでjinja2の記法によって、pythonスクリプトからのデータの受け渡しも行なうことができる。

Flaskでpythonのウェブアプリケーションを作る

pythonでWebアプリケーションを作成する方法はいくつかあるが、 Flaskを使用すると非常に簡単に作成することができる。

インストールはpipでできるので、詳細は省く。

使い方

ディレクトリ構成

作業ディレクトリのルートにpythonスクリプトを置き、「templates」ディレクトリ配下にhtmlファイルを置く。

次に例を示す。

.
├── hello.py
└── templates
    └── hello.html

hello.py(pythonスクリプト)

スクリプトの例を示す。

# coding: utf-8
from flask import Flask, render_template
app = Flask(__name__)


@app.route("/")
def hello():
  return "Hello World!"


@app.route("/hello")
def index():
   hello = 'hello'
   return render_template('hello.html', message=hello)


if __name__ == "__main__":
    app.run()

この例では、サイトのルートにアクセスされると、「Hello World!」とブラウザに返し、 「/hello」にアクセスされると、「hello.html」の内容をブラウザに返す。

この時、引数messageを介して「hello」という文字列をhtmlファイルへ渡している。

htmlファイル

「hello.html」の例を次に示す。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
    <title>Title</title>
</head>
<body>
<div class="container">
    <div class="page-header">
        <h1>{{message}}</h1>
    </div>
</div>
</body>
</html>

{{{message}}で、pythonスクリプトから渡された文字列を表示する。 スクリプトから渡されたデータはjanja2の記法によってhtmlから参照することができる。

Flaskを使用すると、pythonで計算した結果などを簡単にブラウザに表示できるようになるのが便利。

また、見栄えを良くするためにBootstrapを読み込んでいる。Flaskを使用するアプリを紹介しているサイトでは、Bootstrapと組み合わせているものが多かった。

ブラウザからアクセス

スクリプトを実行し、同じPC上でブラウザから「http://127.0.0.1:5000」にアクセスすると動作を確認することができる。

デフォルトでは他のマシンからのアクセスは許可されていないため、これを許可するには、pythonスクリプト側で次のようにする。

app.run(host='0.0.0.0')

ポートを変更する場合は次のようにする。

app.run(host='0.0.0.0', port=8080)

参考サイト

下記のサイトが非常に参考になった。

今回の例では、特にhtml側でデータの有無をチェックせずに参照しているが、このサイトではそれらのチェックの方法や、 String以外のデータの受け渡しなども紹介している。

上記のサンプルはほぼ丸パクリ。

yoctoでの使用方法

meta-pythonに次のパッケージがある。

Yocto raspberrypi3でbootchartを試す(systemd編)

Yoctoのbootchartのおはなし systemdを使用する場合は「systemd-bootchart」が使用できる。

本当は、systemdの時もbootchart2を使用して同じ条件で計測してみたいところだけど。 raspbianはsystemdなので、そちらとの比較の場合は有効な手段かもしれない。

local.confの修正

以下の設定でsystemdを有効化する。

# systemd
DISTRO_FEATURES_append = " systemd"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

bootchart関連のためにさらに以下を追加する。

IMAGE_INSTALL_append = " systemd-bootchart"
CMDLINE_append = " initcall_debug printk.time=y init=/lib/systemd/systemd-bootchart"

追加パッケージ

systemdの場合は「systemd-bootchart」の追加だけでよい。

カーネルオプション

CMDLINE変数に次を追加しsystemd-bootchartが起動されるようにする。

initcall_debug printk.time=y init=/lib/systemd/systemd-bootchart

次のコマンドでsystemd-bootchartのログを見ると、「/run/log」以下にsvg形式の画像が出力されていることがわかる。

# systemctl status systemd-bootchart
● systemd-bootchart.service - Boot Process Profiler
   Loaded: loaded (/lib/systemd/system/systemd-bootchart.service; enabled; vendor preset: enabled)
   Active: inactive (dead) since Wed 2018-03-07 05:24:34 UTC; 3min 39s ago
     Docs: man:systemd-bootchart.service(1)
           man:bootchart.conf(5)
  Process: 113 ExecStart=/lib/systemd/systemd-bootchart -r (code=exited, status=0/SUCCESS)
 Main PID: 113 (code=exited, status=0/SUCCESS)

Mar 07 05:24:34 raspberrypi3 systemd-bootchart[113]: Error reading disk model for mmc: No such file or directory
Mar 07 05:24:34 raspberrypi3 systemd-bootchart[113]: systemd-bootchart wrote /run/log/bootchart-20180307-0524.svg
Mar 07 05:24:34 raspberrypi3 systemd-bootchart[113]: Bootchart created: /run/log/bootchart-20180307-0524.svg
Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.

yocto raspberrypi3でbootchartを試す(sysvinit編)

bitbakeで作成したlinuxの起動時の状況を知るためにbootchartを使用したい。 pokyには「bootchart2」が収録されているので、これを使用する。

meta-raspberrypiの組み込みおよび、bitbake rpi-basic-imageまで作成できている事を前提とする。

local.confの修正

local.confに次の内容を追加する。

PACKAGE_INSTALL_append = " bootchart2 bootchartd-stop-initscript pybootchartgui"
CMDLINE_append = " initcall_debug printk.time=y init=/sbin/bootchartd"

bootchart2関連のパッケージと、OS起動時にbootchart2を実行するためのカーネルオプションの設定を追加している。

追加パッケージ

次のパッケージを追加する。

パッケージ 機能
bootchart2 bootchart2本体
bootchart-stop-initscript OS起動後にbootchart2の終了
pybootchartgui bootchart.pngの生成

bootchart2はサービスの終了時に、「/var/log/bootchart.tgz」を作成するが、 デフォルトでは、手動で終了させるまで実行され続けるため、 ブートプロセスの終了と同時に、bootchart2を終了させるために「bootchart-stop-initscript」をインストールする。

また、bootchart.tgzはそのままではただのログデータのアーカイブなので、 これをpng形式のチャートに変換するためにpybootchartguiをインストールする。

pybootchartguiをインストールしておくと、ブートプロセス終了時に実行され、 bootchart.tgzからpng画像が作成されるようになる。

カーネルオプション

meta-raspberrypiではカーネルへ渡すコマンドラインの文字列はCMDLINE変数で設定されるため、この変数に次を追加する。

initcall_debug printk.time=y init=/sbin/bootchartd

これで、起動時にbootchart2が実行されるようになる。

BBC micro:bit上のmicropythonのプログラムをPyCharmで開発する

BBC micro:bitを購入した。

任意のウェブブラウザーからブロックエディター、JavascriptPython、Scratchなどを使ってプログラムすることができます。ソフトのインストールは必要ありません。

とのことで、micropythonでプログラミングの学習に使用するつもり。

開発環境

BBC micro:bit MicroPython ドキュメンテーションによると、

開発環境としてはMuを使うらしい。

PyCharm

しかし。Pythonの開発環境としては筆者はPyCharmが使いたい。

PyCharmはAndroid StudioのベースとなるIntelliJ IDEAを開発しているJET BRAINSが開発しているPython向けの開発環境である。

Android Studioを初めて使用した時は衝撃的だった。

嬉しいことにCommuityエディションを提供してくれている。商用利用するわけではないためこれを使わせてもらう。

Ubuntuでは

Ubuntu 16.04以降ではSnapイメージが提供されているので、次のコマンドでインストールすることができる。

$ sudo snap install pycharm-community --classic

インストールが完了すると、次のコマンドで起動できるようになる。

$ pycharm-community

初回起動時は、初期設定のダイアログが表示されるが基本的には画面の指示通りに操作するだけで良い。

MicroPython pulugin

PyCharmでPythonスクリプトの開発はできるが、MycroPythonやmicro:bitは使えるのか心配。

しかし、ありがたいことにプラグインが存在する。

JET BRAINS製のIDEはとても便利なので、プラグインのインストールもすべてIDEから行うことができる。

PyCharmを起動すると、次の画面が表示されるので画面左下の「Configure」->「Plugins」を選択する。

f:id:mickey_happygolucky:20180224181211p:plain

「Plugins」ダイアログが表示されるので、画面下の「Browse repositories...」ボタンを押下する。

f:id:mickey_happygolucky:20180224181228p:plain

「Browse Repositories」ダイアログが表示されるので、画面左上の検索フィルタで「micropython」と入力すると「MicroPythonプラグイン」が見つかるので、「Install」ボタンでインストールする。

f:id:mickey_happygolucky:20180224181235p:plain

プラグインの有効化

この時点ではプラグインは有効になっていないため、有効化する必要がある。

プロジェクトが作成してあったほうが都合が良いので、適当に「hello_microbit」というプロジェクトを作成する。

すると、プロジェクトが作成され、メインウィンドウが表示されるので、メニューから「File」 -> 「Settings」を選択し、「Settings」ダイアログを表示する。

右側のツリーで「Language & Frameworks」->「MicroPython」を選択する

f:id:mickey_happygolucky:20180224181243p:plain

「Enable MicroPython support」にチェックし、「Device type」に「Micro:bit」を選択、このままOKを押して、一度ダイアログを閉じる。

プラグインが動作するために必要なパッケージが入っていない場合、画面上部にメッセージが出るため「Install Required Packages」のリンクをクリックすると自動的にインストールされる。

筆者環境では、uflashとpyserialが追加でインストールされた。

micro:bitをPCに接続し、再度、MicroPythonの設定ダイアログを開く。 Device Pathの行の「Detect」ボタンを押すと「/dev/ttyACM0」が自動で追加される。

f:id:mickey_happygolucky:20180224181310p:plain

「/dev/ttyACM0」はmicro:bitと通信するためのUARTデバイスのこと。

OKボタンを押下してダイアログを閉じる、

これで、PyCharmとmicro:bitが会話できるようになった。

プログラムの作成

画面右側の「Project 」ツリーの「hello_microbit」を右クリックし 「New」->「Python File」

New Python fileダイアログで「hello.py」としてOKを押下する。

ここの最初のプログラムを入力する。

from microbit import *
display.scroll("Hello, World!")

これをmicro:bit本体に書き込む必要がある。

書き込むための設定を行うため、画面右側の「Project」ツリー上の「hello.py」を右クリックし「Create 'Flush hello.py'...」を選択する。

f:id:mickey_happygolucky:20180224181319p:plain

「Create Run/Debug Configuration: 'Flash hello.py'」ダイアログが表示されるので、内容を確認してOKボタンを押下する。

f:id:mickey_happygolucky:20180224181326p:plain

メイン画面右上のドロップダウンで「Flash hello.py」が選択できるようになる。

f:id:mickey_happygolucky:20180224181333p:plain

実行するとプログラムがmicro:bitに書き込まれ、hello.py実行される。

5x5のLEDにHello World!の文字列がスクロールしたら成功。

PyCharm便利

PyCharmのインテリセンスや機能は非常に秀逸なので、これらを使用してmicro:bitで遊べるのは大変嬉しい。

yocto raspberrypi3でNFSブート

raspberrypi3のルートファイルシステムNFSでマウントする。

ベース環境の取得

$ git clone git://git.yoctoproject.org/poky.git -b pyro
$ cd poky
$ git clone git://git.yoctoproject.org/meta-raspberrypi -b pyro

使用するのはpyroブランチ。

oe-init-build-envの読み込み

下記を実行する。

$ cd ~/rpi3
$ source poky/oe-init-build-env

これにより、クロスコンパイルに必要な環境変数が設定され、ビルドディレクトリに移動される。

meta-raspberrypiをビルド対象に追加

次のコマンドを実行する。

$ bitbake-layers add-layer ../poky/meta-raspberrypi

local.confの修正

マシンの設定と、カーネルのブートパラメータを設定する。

MACHINE ?= "raspberrypi3"

CMDLINE_remove = "root=/dev/mmcblk0p2 rootfstype=ext4 rootwait"
CMDLINE_append = " ip=192.168.1.100 root=/dev/nfs nfsroot=192.168.1.10:/exports rw rootwait"

動作確認

bitbakeの実行

$ bitbake rpi-basic-image

SDカードの作成

本来はブートパーティションだけで良いが、手順が複雑になるので、通常通りSDカードイメージをddで書き込む

$ sudo dd if=./tmp/deploy/images/raspberrypi3/core-image-sato-raspberrypi3.rpi-sdimg of=/dev/sdb bs=100M

nfsサーバ

インストール

$ sudo apt-get install -y nfs-kernel-server

公開ディレクトリの設定

tftpサーバとは異なり、デフォルトの公開ディレクトリは設定されていないため、この設定をおこなう。

/etc/exportsに下記の内容を追加する。

/exports 192.168.1.0/255.255.255.0(rw,no_root_squash,subtree_check)

/exportsを作成する。

$ sudo mkdir /exports

NFSデーモンの再起動

変更した設定を読み込むためにNFSデーモンを再起動

$ sudo systemctl restart nfs-kernel-server

ファイルの展開

bitbakeで生成されたrootfsのイメージを/exportsに展開する。

$ sudo tar xvf ./tmp/deploy/images/raspberrypi3/rpi-basic-image-raspberrypi3.tar.bz2 -C /exports/

ラズパイ起動

ここまでやってSDカードをraspberrypi3に挿入して起動すると、ルートファイルをNFSでマウントした状態で起動される。

yoctoで作ったLinuxでQt5 アプリケーションのフォントが表示されない

yoctoでmeta-qt5を組み込んで、Qt5アプリケーションを起動させた時に、次のようなエラーメッセージが表示され、文字が正しく表示されない問題が発生した。

QFontDatabase: Cannot find font directory /usr/lib/fonts.
Note that Qt no longer ships fonts. Deploy some (from http://dejavu-fonts.org for example) or switch to fontconfig.
QFontDatabase: Cannot find font directory /usr/lib/fonts.
Note that Qt no longer ships fonts. Deploy some (from http://dejavu-fonts.org for example) or switch to fontconfig.
QFontDatabase: Cannot find font directory /usr/lib/fonts.
Note that Qt no longer ships fonts. Deploy some (from http://dejavu-fonts.org for example) or switch to fontconfig.
QFontDatabase: Cannot find font directory /usr/lib/fonts.
Note that Qt no longer ships fonts. Deploy some (from http://dejavu-fonts.org for example) or switch to fontconfig.
QFontDatabase: Cannot find font directory /usr/lib/fonts.
Note that Qt no longer ships fonts. Deploy some (from http://dejavu-fonts.org for example) or switch to fontconfig

local.confでは次のように定義してあるため、ttf-dejavu関連のパッケージはインストールしてある。

# fonts
IMAGE_INSTALL_append = " \
             ttf-dejavu-common \
             ttf-dejavu-sans \
             ttf-dejavu-serif \
"

これらのパッケージは、/usr/share/fonts/truetype以下にttfファイルをインストールするようになっているが、 Qt5が/usr/lib/fontsにフォントファイルが配置されていることを期待しているため、フォントを見つけられないということらしい。

対策

ここの一連のスレッドを読むと、同じ問題に引っかかっている人が、 フォントのレシピにbbappendを作成して、インストール先を変更することで、問題を解決している。

#
# This method from 
#  https://lists.yoctoproject.org/pipermail/yocto/2016-October/032453.html
#

do_install() {
    install -d ${D}${libdir}/fonts/
    find ./ -name '*.tt[cf]' -exec install -m 0644 {} ${D}${libdir}/fonts/ \;
    install -d ${D}${sysconfdir}/fonts/conf.d/
    install -m 0644 ${WORKDIR}/30-dejavu-aliases.conf ${D}${sysconfdir}/fonts/conf.d/
}

FILES_${PN}-sans            = "${libdir}/fonts/DejaVuSans.ttf ${libdir}/fonts/DejaVuSans-*.ttf"
FILES_${PN}-sans-mono       = "${libdir}/fonts/DejaVuSansMono*.ttf"
FILES_${PN}-sans-condensed  = "${libdir}/fonts/DejaVuSansCondensed*.ttf"
FILES_${PN}-serif           = "${libdir}/fonts/DejaVuSerif.ttf ${libdir}/fonts/DejaVuSerif-*.ttf"
FILES_${PN}-serif-condensed = "${libdir}/fonts/DejaVuSerifCondensed*.ttf"
FILES_${PN}-mathtexgyre     = "${libdir}/fonts/DejaVuMathTeXGyre.ttf"

bbappendは次のコマンドで作成すると適切な場所に空のファイルを作成しエディタで開いてくれるため簡単。

$ recipetool newappend meta-local ttf-dejavu -e

ここでは予めmeta-localというレイヤを作ってあることを前提としている。

meta-localも次のように作ることができる。

$ yocto-layer create local 10
$ bitbake-layers add-layer ./meta-local

ディレクトリ構成は手抜き。