くろねこ日記

ソフトウェアに関する技術メモが多いです.

ArduinoをCLIベースに開発できるino

はじめに

最近は電子工作にArduinoを使うことが多いです. 近年では電子工作の裾野を広げる意味では一躍買っているマイコンですね.

Arduinoの公式では書き込みやプログラミングが行えるIDEが配布されています.多くのユーザはそちらのIDEを使うことが多いかもしれません.しかし,VimEmacsなど自分の好きなエディタやCLIを使いたい方も居ると思います. 僕も普段の開発にはVimを使うので,そのような場合は作成したコードをIDEに読み込ませてからArduinoへの書き込みを行なうという面倒なことをしてました.

今日はそんな面倒なことをしているかもしれない方へinoというCLIで扱えるArduino開発環境を紹介します.

inoを使えばプロジェクトのディレクトリ生成からビルド・書き込み,シリアル通信までできます.

inoのインストールと概要

inoはpythonで作られており,pypiにも登録されているのでpipを使ってインストールができます.

ただし,python3では動作しなかったので,python2.7で導入することを推奨します.

なおインストールは

pip install ino

からできます.

inoはプロジェクト生成からコードのビルド,マイコンへのプログラムアップロード,シリアル通信までサポートしています.

以下に普段の開発で最低限使うコマンドをまとめました.

  • コマンド

    • プロジェクトの生成:init
    • ビルド:build

      • -m:arduinoの種類を設定できる(ArduinoMiniの場合:ino build -m mini)
    • 書き込み:upload

    • serial:シリアルターミナル

といったところでしょうか.他にもコマンドが沢山あります.

ino -h

から確認ができます.

実際に使ってみる

ざっくり説明したところで,具体的な使い方を示しましょう.

ここではArduinoからシリアル経由でHelloWorldをひたすら送りつづけるようなプログラムを作ることを目的に説明します.

まずはプロジェクトを生成しましょう. 空のディレクトリを生成する必要があります.

ここでは

プロジェクト名:HelloSerial(任意)

としましょう.なので

mkdir HelloSerial

でプロジェクトディレクトリを生成できます.

HelloSerialに移動して

cd HelloSerial

そしてプロジェクトの生成をします.

ino init

すると

.
├── lib
└── src
    └── sketch.ino

というディレクトリが作成されます.

あとはsrcにあるsketch.inoにコードを書いていくだけです.

void setup()
{
    Serial.begin(9600);
}

void loop()
{
    Serial.println("HelloWorld");
}

書き込んで保存したら次はビルドです

ino build

で実行できます.

ビルドに成功したら

ino upload

Arduinoにアップロードします.

アップロードしたら今度はHelloWorldがシリアル通信で送られているか確認しましょう.

ino serial

ArduinoからHelloWorldが送られてくれば成功です.

まとめ

CLIベースのArduino開発環境inoの紹介を行った.

inoを使えば,コードのビルド,書き込み,アップロードなどArduinoで開発するときに必要なことが一通りできる.

シンプルなThreading

はじめに

昨日,Threadingを使用したコードを書きました. 書いた動機としては,もともとスレッド処理を書いたことがない僕は少してこづったことと,オブジェクト指向的なコードとかはググれば出るのですが簡単なのが少なかったからです.頭の中を整理するつもりでメモしておきます.

そもそもThreadって何?

まずスレッドの意味ですが,「プログラムの実行コード一つ辺りの単位」のことらしいです. つまり2スレッドは実行コードが2つ走っているということになります.

実行コードというのはプログラムが終了するまで実行しつづけようとします. ですので実行コードが終了するということはその実行コードのスレッドが一つ消えるということになります.

ではプログラム言語に搭載されている機能としてのスレッドとはどういう意味をなすのかですが,これは実行コードを複数扱ったり停止したり破壊したりと実行コードそのものを制御するためのものです.「実行コード中に停止させたい」,「いくつか実行コードがあってそれらを並列に動かしたい」という場面に有効です.

シンプルなThreadingコード

まずはfooという"foo"という文字列を吐きつづける実行コード(関数という形ですが)をthreadingから制御してみようと思います.

実行コードの生成と開始をして,1秒待ったあと停止させるというものです. つまり,1秒間メインコードが停止しているときでも関係なくfooは動いていることになります.ですので,1秒間fooを出力するはずです. なお,Pythonには停止にあたるものが無いので,ここではflagを使ってwhileループを抜けさせたりするのに使ってます.

import threading
import time

def foo():
    while flag:
        print("foo")

th=threading.Thread(target=foo)
flag=True
th.start()
time.sleep(1)
flag=False
th.join()

注目すべきはflagをTrueにしたりFalseにするタイミングですね.flagはfoo関数のwhileを無限ループさせるのに必要となる変数です.flag=Trueであればwhile Trueとなりprint("foo")が無限に実行されます.Falseにすればwhile Falseとなりwhile文を抜けます.

実はこのwhileの制御が少し悩みました.whileを使って書きたくないなと思いつつも,今のところはflagによる制御が簡単かなと思っています.もし二回個別に実行したいときには再びthreading.Threadで生成してあげる必要がありますので注意してください. 次のようにするともう一度実行できます.

import threading
import time

def foo():
    while flag:
        print("foo")

th=threading.Thread(target=foo)
flag=True
th.start()
time.sleep(1)
flag=False
th.join()
th=threading.Thread(target=foo)
flag=True
th.start()
time.sleep(1)
flag=False
th.join()

また,スレッドにはjoinというのがあります.これもよく使われるみたいです. これは実行コードが終了するまでメインコードの動きを止めることができます.

例えばfooとhogeの二つの関数があり,fooを終えたあとにhogeを開始したいとします. しかしスレッドではfooとhogeを次にように書くとほぼ同時に実行されてしまいます.

import threading

def foo():
    for i in range(10):
        print("foo:"+str(i))

def foo2():
    for i in range(10):
        print("hoge:"+str(i))
th=threading.Thread(target=foo)
th.start()
th2=threading.Thread(target=foo2)
th2.start()

結果

foo:0
foo:1
foo:2
foo:3
hoge:0
foo:4
hoge:1
foo:5
foo:6
hoge:2
foo:7
foo:8
hoge:3
foo:9
hoge:4
hoge:5
hoge:6
hoge:7
hoge:8
hoge:9

わかりやすくfooとhogeに対して数字を割りふりました. 同時に実行するとfooとhogeが混ざりあっているのがわかりますね.前半fooが多いのはstartのタイミングがfooのほうが早いからだと思われます.

ではfooをstart直後にjoinを置いてみましょう.

import threading

def foo():
    for i in range(10):
        print("foo:"+str(i))

def foo2():
    for i in range(10):
        print("hoge:"+str(i))
th=threading.Thread(target=foo)
th.start()
th.join()
th2=threading.Thread(target=foo2)
th2.start()

結果

foo:0
foo:1
foo:2
foo:3
foo:4
foo:5
foo:6
foo:7
foo:8
foo:9
hoge:0
hoge:1
hoge:2
hoge:3
hoge:4
hoge:5
hoge:6
hoge:7
hoge:8
hoge:9

このようにfooが終わるまで待ってくれています.この例だとシングルタスクで動かしても大差ありませんが,例えばfoo,hoge,barの3つのスレッドがあり,fooとhogeはほぼ同時に走らせてbarはfooとhogeの後に実行したいときなんかは有効ですね.

まとめ

スレッドの基本的な考えかたと使いかたをメモしました.

  • スレッドとは実行コードの単位を示す.

  • PythonにはThreadingというスレッドそのものを扱うライブラリがあり,実行コードを並列に実行したり直列に実行したりできる.

MongoEngineつかってみた

はじめに

前回MongoDBをつかってみたという記事を載せました.その記事ではpyMongoを利用したMongoDBの操作を紹介しましたが,今日はPython上でMongoDBをOSMとして扱うことのできるMongoEngineの紹介をします.

MongoEngineインストール

インストールは簡単

pip install mongoengine

のみです.

使い方

学生情報を扱うデータベースを例に話を進めていきます. まずは,ドキュメントの追加と参照について説明します

追加と参照

  • データベース名:studentinfo

  • クラス名: Student(学生情報を扱う)

    • 要素:Name(学生名)

    • 要素:Number(学生番号)

import mongoengine

mongoengine.connect("studentinfo")

class Student(mongoengine.Document):
    Name = mongoengine.StringField()
    Number = mongoengine.IntField()

#DBの初期化を行なう(ブログの内容には直接関係しない)
Student.drop_collection()

db=Student()
db.Name="Taro"
db.Number=1
db.save()

for data in Student.objects:
    print data.Name
    print data.Number

出力だけならこれだけです.

もし特定のデータ(Number1にしましょう)だけ取りだしたいなら

import mongoengine

mongoengine.connect("studentinfo")

class Student(mongoengine.Document):
    Name = mongoengine.StringField()
    Number = mongoengine.IntField()

#DBの初期化を行なう(ブログの内容には直接関係しない)
Student.drop_collection()

db=Student()
db.Name="Taro"
db.Number=1
db.save()

for data in Student.objects(Number=1):
    print data.Name
    print data.Number

のようにすればNumber1のデータだけが表示されます

親子関係にあるデータベース

では次はMongoEngineで親子関係にあるデータベースを組んでみましょう. 例題として先程のStudentの加えて試験の結果を子とします.

  • データベース名:studentinfo

  • クラス名:Student

    • 要素:Name(学生名)

    • 要素:Number(学生番号)

  • クラス名:Math

    • 要素:score(点数)
  • クラス名:Science

    • 要素:score(点数)
  • クラス名:English

    • 要素:score(点数)

とします.

この場合はStudentから各科目(Math,Science,English)を呼べるようにします.

import mongoengine

mongoengine.connect("studentinfo")

class Math(mongoengine.Document):
    score = mongoengine.IntField()

class English(mongoengine.Document):
    score = mongoengine.IntField()

class Student(mongoengine.Document):
    name = mongoengine.StringField()
    math = mongoengine.ReferenceField("Math")
    english = mongoengine.ReferenceField("English")

こうします.実際にStudentを経由してMathやEnglishのデータを書き込むには 次のようにします.ここでは[学生名taro,数学100点,英語50点]というデータと[学生名jiro,数学70点,英語80点]として書いていきます

taro = Student()
math = Math()
english = English()
math.score=100
math.save()
english.score=50
english.save()
taro.name="taro"
taro.math=math
taro.english=english
taro.save()

jiro = Student()
math = Math()
english = English()
math.score=70
math.save()
english.score=80
english.save()
jiro.name="jiro"
jiro.math=math
jiro.english=english
jiro.save()

とします.mathとenglishそれぞれのインスタンスに各点数を代入したのちstudentのクラス変数に渡してますね.

値を取り出すには

for student in Student.objects:
     print student.name
     print student.math.score
     print student.english.score

のようにします. おそらく

taro
100
50
jiro
70
80

と出力されるはずです.

もし複数件入力されており,名前が"taro"だけのデータが欲しいときには

for student in Student.objects(name="taro"):
     print student.name
     print student.math.score
     print student.english.score     

結果

taro
100
50

のようにobjectsにname="taro"のように指定するだけです.

ここの部分を一つのコードにまとめると

import mongoengine

mongoengine.connect("studentinfo")

#クラスの定義
class Math(mongoengine.Document):
    score = mongoengine.IntField()

class English(mongoengine.Document):
    score = mongoengine.IntField()

class Student(mongoengine.Document):
    name = mongoengine.StringField()
    math = mongoengine.ReferenceField("Math")
    english = mongoengine.ReferenceField("English")

#DBの初期化を行なう(ブログの内容には直接関係しない)
Student.drop_collection()

#taroとjiroのドキュメントを追加
taro = Student()
math = Math()
english = English()
math.score=100
math.save()
english.score=50
english.save()
taro.name="taro"
taro.math=math
taro.english=english
taro.save()
jiro = Student()
math = Math()
english = English()
math.score=70
math.save()
english.score=80
english.save()
jiro.name="jiro"
jiro.math=math
jiro.english=english
jiro.save()

#Studentクラスに書かれているドキュメントをすべて取得
for student in Student.objects:
     print student.name
     print student.math.score
     print student.english.score

#taroのドキュメントのみ取得
for student in Student.objects(name="taro"):
     print student.name
     print student.math.score
     print student.english.score

上書き

次に既存のデータを上書きしたときについてですが,上記のjiroをsaburoに変更してみましょう.

jiro = Student.objects(name="jiro")
jiro.update(set__name="saburo")

のようにobjectsでデータを抜きだしてからupdate文で更新します. 更新の際にはset__のようにqueryを指定することができます.set__ query以外にもpush__ , unset__ , pull__などがあります 詳しくはこちらをお読みください(http://mongoengine-odm.readthedocs.org/guide/querying.html) .

まとめ

MongoEngineでデータの追加や参照,更新についてまとめました.

MongoDBつかってみた

はじめに

Flaskやsinatraを使ってウェブアプリケーションを作成する際に,どのDBを採用するかというのは仕様決定上重要になります.

僕はこれまでDBというとMySQLSQLiteのようなRDBMSを使っていたのですが,sinatra札幌さんのほうでflask上でNoSQLであるMongoDBを使用する機会を頂き,ここにMongoDBの概要と使い方,Pythonからどう制御するのかについて簡単にまとめました.

MongoDBとは

RDBMSを使用していた方にとってはMongoDBはちょっと異質に見えるかもしれません. RDBMSではデータ構造を明確化し,数学的な堅牢性を持っているイメージですね.SQLとよばれる言語を使用することでデータのソーティングなどを行ないます.

MongoDBではデータ構造はRDBほど明確化しなくても使用することができ,SQLのような言語を使う必要がありません.ドキュメント指向データベースと呼ばれており,データのやりとりはドキュメントとよばれる構造データにJSON形式で記述して使います.そのためプログラミング言語からSELECT * FROM テーブル名のようにコード中にSQLを書くこと必要がなく,使用言語がJSONフォーマットを扱えさえすれば配列やリストのような形でDBを操作できます.

MongoDBの用語

RDBMSを使用しているとデータベースの選択やレコードの選択などを考えてしまいますが,MongoDBは独特のドキュメント指向データベースと呼ばれている通りRDBMSとは異なる概念を持っています. ここではその概念と用語についてまとめてみました.

  • データベース:一つのアプリケーションで使用するコレクションを格納しておくもの.コレクションは複数持つことができる.

  • コレクション:ドキュメントを格納しておくもの.複数のドキュメントを持つことができる.

  • ドキュメント:商品ID,品名,価格などのデータそのものを格納できる.商品ID,品名のみなど可変長に扱える.入力形式はハッシュのようにKeyとValueの関係で保持される.

つまり包含関係で言えば「ドキュメント⊆コレクション⊆データベース」となります.

とりあえず動かしてみる

MongoDBを起動した状態で「mongodb」とシェル上からタイプするとMongoDBにアクセスできます.ここでは次のようなものを満すデータベースを作ってみましょう.

  • データベース名:examDBという学校の試験を

*コレクション:Math, Englishの2教科を現わすコレクション

*ドキュメント:Name,Gradeの2項目.ただしGradeはテストを受けていない場合はつけない場合がある.

*要素:NameはTaro,Jiro, Saburoの3人.Taroは「Math40点,Englishは受けていない」. JIroは「Math100点,Englishは80点」.Saburoは「Mathは90点Englishは70点」.

データベースの生成や選択は

 use examDB

としEnterを叩くだけです.簡単ですね 次にコレクション名とドキュメントを生成します.コレクションはMathとEnglishの2項目,ドキュメントはNameとGradeの2項目ですね.MongoDBではそれらを直接指定できます. まずはMathにそれぞれ項目を入力していきましょう.

db.Math.insert({Name : "Taro", Grade: "40"})
db.Math.insert({Name : "Jiro", Grade: "100"})
db.Math.insert({Name : "Saburo", Grade: "90"})

です. dbは上記のuseコマンドでexamDBをすでに選択しています.db.Mathとすることでコレクション名Mathを生成し操作できます.次にドキュメントを挿入したいのでdb.Math.insertのように指定できます.insertについては中身はJSONフォーマットで入力するだけなのでdb.Math.insert({ドキュメント項目名(Key) : ドキュメント項目値(Value), ・・・})です. 上記では3項目あるので3行分記述してます.

次にEnglishについても同様に打ちましょう.

db.English.insert({Name : "Taro"})
db.English.insert({Name : "Jiro", Grade: "80"})
db.English.insert({Name : "Saburo", Grade:"70"})

ですね.Taroは受けていないのでGradeの項目は入力してません.

では次は挿入されたデータを呼び出してみましょう. たとえば,Mathを呼び出したいときには MongoDBでは

db.Math.find()

だけです.これで入力した3項目が返ってきます.

MathのうちTaroのデータだけ呼びたいときには

db.Math.find(Name:"Taro")

だけです.

ここでは触れてませんが,コレクションの中にも子コレクションを生成することもできます. その場合もコレクション名が増えるだけで操作は,findやinsertを使用することができます.

PythonからMongoDBを操作する

PythonからMongoDBを操作してみましょう. PythonからMongoDBを扱えるライブラリにpymongoというのがあります. 「pip install pymongo」とタイプすることでインストールができると思います.

では先程の操作をpymongoからやってみましょう

from pymongo import MongoClient

client = MongoClient()#接続先を指定Localhostの場合はvoidで良い
db = client['examDB']#データベースを選択
#Mathの項目追加
db.Math.insert({'Name' : 'Taro', 'Grade':'40'})
db.Math.insert({'Name' : 'Jiro', 'Grade': '100'})
db.Math.insert({'Name' : 'Saburo', 'Grade': '90'})
#Englishの項目追加
db.English.insert({'Name' : 'Taro'})
db.English.insert({'Name' : 'Jiro', 'Grade': '80'})
db.English.insert({'Name' : 'Saburo', 'Grade':'70'})
#Mathのfind
for data in db.Math.find():
     print(data)
#MathのTaroだけ呼びだす
for data in db.Math.find({'Name':'Taro'}):
     print(data)

これらの形であればリストに格納したりイテレータのような形で扱うことができますね.

まとめ

MongoDBはRDBMSとは違ったSQLを使用しないデータベースであり,Jsonフォーマットでデータを扱えます.

概念用語としてデータベース,コレクション,ドキュメントがあります. ここでは学校のテストデータを例にmongoDBとPython上で試してみました.

kivyでデスクトップアプリケーション

はじめに

クロスプラットフォームGUIアプリを開発するためのフレームワークにkivyがあります kivyのホームページ

ここ最近,デスクトップアプリケーション開発といえば個人的にkivyを使用することが多くなりました.pythonにはpyQTwxpython,Tkinterなど多くありますが,pyQTはウェブや書籍を参考にできるほど文献が少ないこと,wxpythonの場合は更新がすでに止まっており,採用には至りませんでした.Tkinterを使うことも考えられますが,kivyのほうがスマートフォン上で動作するという違いがあり興味が湧いたのでkivyを使うに至りました.

ここではkivyの基本構成について忘れないようにメモしておきます.

必要なリファレンスについては

kivy documentation

PythonでかんたんiOSアプリプログラミング―Kivyによるマルチタッチアプリケーション制作

を参考にしました.

kivyのインストール

少し前のブログをお読みください kivyをpipからインストールしてみた

本記事ではPythonは2.7.6,kivyのバージョンは1.8を対象に書いております.

kivyの開発

まずはHelloWorld

kivyには様々なウィジェットがあります.そのなかにもラベルと呼ばれるアプリケーション上に文字列を表現するものがあります.ここではラベル上に「HelloWorld」を出力するコードを示します.

from kivy.app import App
from kivy.uix.label import Label

class TestApp(App):
    def build(self):
        return Label(text='Hello World')

TestApp().run()

これだけですね.

from kivy.app import Appについてはアプリのインポート,二行目はラベルのインポートです.

公式サイトのhomepageにも載っていますが,labelとLabelの箇所をそれぞれbutton,Buttonに変更すればボタン上にHelloWorldが表示されます.

pythonでファイルを指定すれば実行できます.

ちょっとまともなGUIアプリ

さて,HelloWorldの例を読むだけではreturnでボタンを返すだけでは面白くないかもしれません. ここでは僕が普段利用しているファイル関係を示しておこうと思います.

以下に基本的なファイル関係を示しておきます.

├── main.py
├── my.kv

main.pyという実行ファイル,my.kvというGUIレイアウトなどを記述するファイルです. メインとなる実行ファイルについてはHelloWorldを表示したようにPythonで処理を記述するものです.

my.kvはkivyにおけるデザイナの位置やIDと呼ばれるGUI部品とコードを紐付けるものを定義するものです.pythonコードで部品を設置しなくてもmy.kvに部品を配置することで,main.pyでは見た目を気にせず処理内容にだけ注力することができます.

また,名前を変更することも可能ですが,初期設定ではmy.kvという名前のファイルを読むように設定されており,そこについてはここでは触れません.

以下に,ボタンを押したらラベルの値に現在時刻を表示するアプリを例にmain.pyとmy.kvの設定や関係を説明していきます..

まずはmy.kvの説明をします.

今回作るアプリはボタンを押したらLabel上に現在時刻を表示するものです. そのため,ウィジェット名とラベル,ボタンをそれぞれ一個づつ配置する必要がありますので配置していきましょう.

まずは,ウィジェット名と任意の名前をラベル・ボタンそれぞれに付けていきましょう.

ここでは

  • ウィジェット名はtimerWidget

  • ラベルの名前はlabeldatetime

  • ボタンの名前はbuttoncount

としました.

それぞれの部品設定を設定を以下に示します.

labeldatetimeの設定

  • id:labeldatetimeval

  • フォントサイズ:50pt

  • 位置(横,縦):(中央,アプリ上部)

  • 初期設定の文字列:「time」

buttoncountの設定

*ID: buttoncountval

*フォントサイズ:50pt

*位置(横,縦):(中央,アプリ上部から下方向に向かって180ピクセル)

*ボタンサイズ(幅,高さ):(140,70)

*設定の文字列:「Click!」

これをkvファイルで表現すると次のようになります.

my.kv

#:kivy 1.8.0

<timerWidget>:
    labeldatetime: labeldatetimeval
    buttoncount: buttoncountval

    #Labeldatetime
    Label:
        id: labeldatetimeval
        font_size: 50
        center_x: (root.width/2)
        top: root.top
        text: 'time'
    #buttoncount
    Button:
        id: buttoncountval
        font_size: 50
        center_x: (root.width/2)
        size: 140,70
        top: root.top-180
        text: 'Click!'

まず一番上で,kivyのバージョンとウィジェット名,部品IDとpythonコードで使用する部品名を紐付けています. あとはそれぞれフォントサイズや位置,部品サイズ,部品の文字列などを定義しているだけです.

次にmain.pyで処理を書いていきましょう.

ここではウィジェットとして定義した部品を操作する箇所です.ボタンが押されたらその都度表示時刻を現在時刻に置きかえるというものです.

main.py

# -*- coding: utf-8 -*-

import kivy
kivy.require('1.8.0')

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import ObjectProperty
import datetime

class timerWidget(Widget):
    labeldatetime = ObjectProperty(None)
    buttoncount = ObjectProperty(None)

class MyApp(App):

    def buttoncount_clicked(self, src):
        self.root.labeldatetime.text = str(datetime.datetime.now())

    def build(self):
        self.root = timerWidget()
        self.root.buttoncount.bind(on_press=self.buttoncount_clicked)
        return self.root


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

上から説明するとまずは,kivyのバージョン,アプリ起動に必要なAppとWidget,kvファイルで定義したWidgetを取りだすObjectProperty,現在時刻を扱うdatetimeをインポートしています.

class timerWidget(Widget)ではmy.kvで紐付けられている部品を取りだしたり描画する箇所です. my.kvで定義した変数を左辺に置いて右辺にはObjectProperty(None)と記述するだけです.

class MyApp(App)では起動に必要な準備やイベントハンドラと呼ばれるボタンをクリックしたときの処理などを書いていきます.

timerWidgetを生成. つぎにボタンをクリックしたときの処理をbind(on_press= )で定義しています. 最後にウィジェット自体を返すだけです.

次にMyApp().run()で実行です

以下に実行画面を示します

実行画面(クリック時)

f:id:kuroneko0208:20140315162302p:plain

まとめ

簡単なkivyの使い方をまとめてみました. 必要な部品などの配置はkvファイルが担当し,部品を操作するのはmain.pyです.

kivyにはもちろんこれ以外にもたくさんの部品があります.

また形には見えない部品もあります.例を挙げれば一定の時間で自動で実行するClockなどです.機会があればその辺についても書きたいと思います.

kivyをpipからインストールしてみた[2014/9再編集]

2014年9月現在一部インストール方法ではうまく導入できなかったので少し修正しました.

はじめに

Pythonにはkivyというクロスプラットフォームで動作するPython用のGUI開発ライブラリがあります。 それを使用することで、MacOSX,Linux,Windowsの3つのプラットフォームで動くほか、iOSAndroid上でも動作するアプリも開発ができます。

今日はこのインストールで少し躓いたのでpyenvという様々なバージョンを導入できるツールからkivyまでの導入をメモとして残しておきます。

環境はUbuntu13.04 32bit、シェルはzshです。 pyenvでは「2.7.6」をインストールするものとします。

pyenv

pyenvのインストールと2.7.6をインストールする例を交えながら使い方を説明します。

pyenvのインストール

まずは、apt-get(またはaptitude)でpyenvのための必要パッケージを導入していきます。

sudo apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev

そのあとは、https://github.com/yyuu/pyenv#installationに従ってインストールするだけです。

cd
git clone git://github.com/yyuu/pyenv.git .pyenv
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zprofile
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zprofile
echo 'eval "$(pyenv init -)"' >> ~/.zprofile
exec $SHELL

ですね。

pyenvの使い方

こちらもさきほどのページの通りです(https://github.com/yyuu/pyenv#command-reference)

メモしておきますと

インストール可能なバージョン一覧

pyenv install -l

任意のバージョンのPythonをインストール(ここでは2.7.6)

pyenv install 2.7.6

任意のバージョンに切り替える(グローバルな設定)

pyenv global 2.7.6
pyenv rehash

これでpython2.7.6を使用できるようになりました。

kivy

pyenvでインストールしたPythonにはpipが内蔵されています。 今回はこのpipを使ってkivyのインストールを行ないます。 こちらに書いてあるインストール手順を踏んで行ないました。 http://kivy.org/docs/guide2/basic.html

インストール手順ページにある通り、kivyはcythonが必要なのでインストールし、その後はなぜかpygameをインストールしてから、kivyをインストールするという順のようですね(ここのページに辿りつくのに苦労しました・・・)。 この記事でもこの流れに沿って書いていきます。

cythonのインストール

cythonは次のコマンドで一発だと思います。

pip install cython

pygameのインストール

ここのURLを参照して必要パッケージを導入しました。 http://stackoverflow.com/questions/7652385/where-can-i-find-and-install-the-dependencies-for-pygame

sudo apt-get install python-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libsdl-ttf2.0-dev libsdl1.2-dev libsmpeg-dev python-numpy subversion libportmidi-dev ffmpeg libswscale-dev libavformat-dev libavcodec-dev

これにさらにfreetypeのconfigがうまくいってない旨のエラーがでましたので、

sudo apt-get install freetype6-dev

で追加しておきました。

あとはpygamemercurialリポジトリを経由してpipでインストールします。

pip install hg+http://bitbucket.org/pygame/pygame

kivyのインストール

いよいよkivy本体のインストールですね

pip install git+https://github.com/kivy/kivy.git

でインストールが成功すれば終わりです。

pip install kivy では2014年9月現在ではコケてしまいましたので, githubのmasterブランチから直接インストールしてあげるという方法を取りました.

まとめ

pyenvからkivyまでをインストールしました。

pyenvで2.7.6をインストールしたあとはpipを使ってcython,pygameという順にインストールしていき、最後にkivyをインストールしました。

pyenvやpygameをインストールするときには必要パッケージをaptから取り込んでおく必要があります。また、zshbashを使う環境によって設定を記述するファイルを分ける必要があります。今回はzshで導入を行ないました。

Sinatraでグラフ画像を表示

SinatraAdventCalendar11日目の記事です(http://www.adventar.org/calendars/262)

f:id:kuroneko0208:20131211022345p:plain

昨日はRSSFeed(http://advent.nzwsch.com/rss-feed)でした.

今回は,sinatraで遊んでみたような内容です.

力を抜いて読んでください.

はじめに

今年の初頭あたりにipodに入れていた歩数計のデータをウェブブラウザ上からグラフ表示してみたのですが,当時,僕はフレームワークを使ったことがありませんでした.内容としてはpythonからipodに入っていた歩数計データを読みだして,matplotlibでグラフ生成したあと,htmlに読み込ませるというものです.

フレームワークを使えばもっと素早く簡単にできるだろうと考えて,今回RubySinatraでウェブ上にグラフ画像を表示してみました.

内容は同じでは面白みがないので,東京電力の提供している電力使用量をグラフ化するコードにしました. 一年間の温度変化などでも良かったのですが,更新頻度の高いデータのほうが動的なアプリケーションとして面白いと判断したため このような題材を選びました.

他には二酸化炭素などの公害の原因となる物質の量をグラフ化してみようと考えましたが,きっちりとWebAPI化が進んでいるようで, 面白みが無いと判断して今回はやめました.

処理の内容

ユーザが訪れるたびにcsvファイルをダウンロードしてGruffというライブラリでグラフ画像を生成し,ウェブ上に表示するというものです.

一応,csvダウンロード先のウェブサイトへの負担を考えて,5分立たなければダウンロードできない仕組みとしてます.

f:id:kuroneko0208:20131210051417p:plain

図:処理のフローチャート

各ファイルの構成は次のようになっています

.
├── grufftest.rb
├── juyo-j.csv
├── public
│   └── PowerUsageGraph.png
└── views
    ├── index.erb
    └── layout.erb

必要なライブラリの解説

まずはsinatraですね.こちらは必須です. gem install sinatraでインストールが可能です.

今回はgruffというRubyで見た目重視のグラフライブラリを使用してみました.

matplotlibはGnuPlotのように見やすいのですが,飾り気がないので面白みがありません(グラフに本来求める要素ではないですからね).

こちらのライブラリはgem install gruffでインストールできます.

gruffの使い方は簡単で,公式サイト(http://nubyonrails.com/pages/gruff)に沿えばすぐに使い方がわかると思います.

以下にサンプルのコードを示します

require 'rubygems'
require 'gruff'

g = Gruff::Line.new
g.title = "My Graph" 

g.data("Apples", [1, 2, 3, 4, 4, 3])
g.data("Oranges", [4, 8, 7, 9, 8, 9])
g.data("Watermelon", [2, 3, 1, 5, 6, 8])
g.data("Peaches", [9, 9, 10, 8, 7, 9])

g.labels = {0 => '2003', 2 => '2004', 4 => '2005'}

g.write('my_fruity_graph.png')

g.dataには項目名と数値リテラルの配列を渡し,g.labelsにはハッシュで横軸の項目を入れているんですね. そこまで整ったらg.writeで保存したいグラフ画像名を入れて保存です.

コード

ではさっそくコードを載せます.

main.rb

require 'gruff'
require 'csv'
require 'sinatra'
require 'date'

def graphgenerate(y,x_name,y_name,graphname) 
    g = Gruff::Line.new
    g.title = graphname
    g.data(graphname,y)
    g.labels = {0 => '0',12 => '1',24 => '2',36 => '3',48 => '4',60 => '5',72 => '6',84 => '7',96 => '8',108 => '9',120 => '10',132 => '11',144 => '12',156 => '13',168 => '14',180 => '15',192 => '16',204 => '17',216 => '18',228 => '19',240 => '20',252 => '21',264 => '22',276 => '23'}
    g.x_axis_label = x_name
    g.y_axis_label = y_name
    g.write('./public/'+graphname)
end

def filedownload(url,filename)
    system('wget '+url+' -O'+ filename)
    system('nkf -w --overwrite '+ filename)
end

def fileanalysis
    filedownload('http://www.tepco.co.jp/forecast/html/images/juyo-j.csv','juyo-j.csv')
    powerdata=[]
    count = 0
    datatime=""
    CSV.foreach("./juyo-j.csv","r") do |data|
        if(count==0) then
            datatime=data[0]
        end
        if(count>46) then
            powerdata << data[2].to_f*10000/10**6#kWはわかりにくいのでMW
        end
        count=count+1
    end
    graphgenerate(powerdata,'Hour','Power Usage[MW]','PowerUsageGraph.png')
    return datatime

end

get '/' do
    if((DateTime.now.to_time-File.stat("./juyo-j.csv").mtime.to_time).to_i > 300) then
        @graphdatetime=fileanalysis
    end
    @graphimage="PowerUsageGraph.png"
    erb :index
end

index.erb

<h1>本日の電力使用状況(東京電力)</h1>
<p>参照:http://www.tepco.co.jp/forecast/html/images/juyo-j.csv</p>
<img src=<%= @graphimage  %>>
<h2><%= @graphdatetime %></h2>

layout.erb

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>本日の電力使用量</title>
</head>
<body>
    <%= yield %>
</body>
</html>

簡単な解説

次のような3つのメソッドの呼び出しで動作を実現しています.

・graphgenerate(y,x_name,y_name,graphname)

・filedownload(url,filename)

・fileanalysis

・graphgenerateですが,こちらでグラフ画像を生成しています. サンプルにあるものをいじっただけです.

・filedownloadはwgetコマンドをrubyの上から実行し,ダウンロードしてきたcsvファイルをutf-8に変換しています. rubyの上でも実現できるかもしれませんが,てっとり早い方法なのでこのような処理をしています.

・fileanalysisは上二つのメソッドをまとめたものです. get '/' do以下のメソッドで記述しても良いですが,長くなったのでこのようなメソッドにまとめました. CSVを読み込んでいるところではcountが0であるときと,47以上のときに実行されるif文があります.これは更新日時が1行目にあり,グラフ化したいデータが47行目にあるためです.Rubyではこのあたりもっと簡潔に書けそうですが何かないでしょうか?

なお,生成した画像はpublicディレクトリに移さなければerb側で表示ができませんのでおいておきました.

erbの配置については,前のブログに書いたとおりです.よろしければ参考にしてください (http://kuroneko0208.hatenablog.com/entries/2013/11/26).

表示結果

localhost:4567にアクセスすると次のように表示されます.

f:id:kuroneko0208:20131211014925p:plain

まとめ

sinatraを使って東京電力の提供している電力使用量をグラフを表示してみました.

グラフ生成にはGruffというビジュアル重視のグラフ生成ライブラリを使用することで,グラフ画像の生成を行ないます.

グラフ画像はpublicにおかなければ反映されないのでおく必要があります.