2013年2月8日金曜日

逆ポーランド記法による演算

.NETならReflectionなどを使って解決する方法もあるけど、
もっと軽量なものがほしいなと思って、逆ポーランド記法による演算をやってみた。
どのようなものができたかというと、基本的な四則演算と、括弧による優先度、変数を扱えるようなもの。

ポイントは3つ。
・括弧内演算は、再帰処理によって実現した。
・四則演算は、優先度を設けて「+-」は最後、「*/」は最初に演算するようにした。
・変数は、逆ポーランドで演算前に、デコード処理を行うようにした。


Public Sub New()
    Console.WriteLine(Calc("1+2-3"))
    Console.WriteLine(Calc("1+2*3"))
    Console.WriteLine(Calc("(1+2)*3"))
    Console.WriteLine(Calc("(1+2)/3"))
End Sub


Module ReversePolishNotation
    Private param As New Dictionary(Of String, Integer) '変数
    Private dat As String = ""              '計算する文字データ
    Private idx As Integer = 0              '解析インデックス
    Private stk As New Queue(Of String)     '後置記法バッファ
    Public Function Calc(ByVal data As String) As Integer
        dat = data
        idx = 0
        stk.Clear()
        INtoPN()        '中置記法→後置記法へ変換
        Return RPN()    '逆ポーランド記法による演算
    End Function
    Private Sub INtoPN()
        '中置記法→後置記法へ変換
        Dim L1 As New Queue(Of String)  '後置記法確定バッファ
        Dim R1 As New Stack(Of String)  '符号格納バッファ
        Dim wrd As String = ""          '切り出した文字列
        Dim hugo As String = ""         '優先される符号格納(「*、/」)
        While dat.Length > idx
            Dim tmp As String = CStr(dat(idx))  'データ読み出し
            idx += 1
            Select Case tmp
                Case "("
                    INtoPN()             '括弧内優先で演算する
                Case ")"
                    Exit While
                Case "+", "-"
                    '優先度の低い演算子は最後に計算
                    If wrd <> "" Then L1.Enqueue(wrd)
                    If hugo <> "" Then L1.Enqueue(hugo)
                    R1.Push(tmp)
                    hugo = ""
                    wrd = ""
                Case "*", "/"
                    '優先度の高い演算子は最初に計算
                    If wrd <> "" Then L1.Enqueue(wrd)
                    If hugo <> "" Then L1.Enqueue(hugo)
                    hugo = tmp
                    wrd = ""
                Case " ", "\t"
                    '無視するキーワード
                Case Else
                    '演算する文字列
                    wrd += tmp
            End Select
        End While
        If wrd <> "" Then L1.Enqueue(wrd)
        If hugo <> "" Then L1.Enqueue(hugo)
        While 0 < L1.Count : stk.Enqueue(L1.Dequeue()) : End While
        While 0 < R1.Count : stk.Enqueue(R1.Pop()) : End While
    End Sub
    Private Function RPN() As Integer
        '逆ポーランド記法による演算
        Dim dat As New Stack(Of Integer)
        While 0 < stk.Count
            Dim tmp As String = stk.Dequeue()
            Dim R As Integer = stk.Pop()
            Dim L As Integer = stk.Pop()
            Select Case data
                Case "+" : stk.Push(L + R)
                Case "-" : stk.Push(L - R)
                Case "*" : stk.Push(L * R)
                Case "/" : stk.Push(L \ R)
                Case Else : dat.Push(Decoder(tmp))
            End Select
        End While
        Return dat.Pop()
    End Function
    Private Function Decoder(ByVal data As String) As Integer
        '変数→数字変換
        Dim ret As Integer = 0
        If Int32.TryParse(data, ret) Then Return ret
        Return param(data)
    End Function
End Module
無駄なバッファが多いので、後値記法へ変換しながら演算するようにしてみた。

Module ReversePolishNotation
    Private param As New Dictionary(Of String, Integer) '変数
    Private dat As String = ""              '計算する文字データ
    Private idx As Integer = 0              '解析インデックス
    Private stk As New Stack(Of Integer)    '演算バッファ
    Public Function Calc(ByVal data As String) As Integer
        dat = data
        idx = 0
        stk.Clear()
        RPN()               '演算
        Return stk.Pop()    '結果を返す
    End Function
    Private Sub RPN()
        '中置記法→後置記法へ変換しながら演算を行う
        Dim R1 As New Stack(Of String)  '符号格納バッファ
        Dim wrd As String = ""          '切り出した文字列
        Dim hugo As String = ""         '優先される符号格納(「*、/」)
        While dat.Length > idx
            Dim tmp As String = CStr(dat(idx))  'データ読み出し
            idx += 1
            Select Case tmp
                Case "(" : RPN()            '括弧内優先で演算する
                Case ")" : Exit While
                Case "+", "-"               '優先度の低い演算子は最後に計算
                    If wrd <> "" Then stk.Push(Decoder(wrd))
                    If hugo <> "" Then enzan(hugo)
                    R1.Push(tmp)
                    hugo = ""
                    wrd = ""
                Case "*", "/"               '優先度の高い演算子は最初に計算
                    If wrd <> "" Then stk.Push(Decoder(wrd))
                    If hugo <> "" Then enzan(hugo)
                    hugo = tmp
                    wrd = ""
                Case " ", "\t"              '無視するキーワード
                Case Else                   '演算する文字列
                    wrd += tmp
            End Select
        End While
        If wrd <> "" Then stk.Push(Decoder(wrd))
        If hugo <> "" Then enzan(hugo)
        While 0 < R1.Count : enzan(R1.Pop()) : End While
    End Sub
    Private Sub enzan(ByVal data As String)
        Dim R As Integer = stk.Pop()
        Dim L As Integer = stk.Pop()
        Select Case data
            Case "+" : stk.Push(L + R)
            Case "-" : stk.Push(L - R)
            Case "*" : stk.Push(L * R)
            Case "/" : stk.Push(L \ R)
        End Select
    End Sub
    Private Function Decoder(ByVal data As String) As Integer
        Dim ret As Integer = 0
        If Int32.TryParse(data, ret) Then Return ret
        Return param(data)
    End Function
End Module

どっちがいいかは不明。

2013年1月23日水曜日

.NET 軽量ORマッパー


.NETでORマッパー関連が充実していなくて困った。
.NET標準のADO.net Entity frameworkを使えばモデリングからある程度自動生成してくれるが、
pythonのdjangoのORマッパーを味わってしまうとめんどくさい。。
Ironpythonを使って、メイン言語をPythonにして画面だけWPFにしようとも考えたけど、これもめんどくさい。。

MySQLなど大規模なファイルでない、ちょっとしたパラメータをファイルに保存しておきたいだけなのに、どうしたものか。
探しても無いから自分でJson形式なORマッパーを作ってみた。

仕様は、
 ・データ保存形式は、 jsonを使用。
 ・ORマッパーでModelクラス経由でアクセス。
 ・検索機能は、Linqを使う。

これだけで十分と思う人向け。


Imports Newtonsoft.Json.Linq
Imports Newtonsoft.Json
Imports System.ComponentModel
Imports System.IO

Public Class BookModel
    <displayname("書籍名")>
    Public Property Name As String = ""
    <displayname("バーコード")>
    Public Property BCode As String = ""
    <displayname("ページ数")>
    Public Property Pages As Integer = 0
    <displayname("価格")>
    Public Property Price As Integer = 0

    Public Sub New(ByVal name As String, ByVal bcode As String, ByVal pages As Integer, ByVal price As Integer)
        With Me
            .Name = name
            .BCode = bcode
            .Pages = pages
            .Price = price
        End With
    End Sub
End Class

Class BookCollection : Implements IListSource
    Public Property models As New List(Of BookModel)
#Region "IListSource"
    Private bindsrc As New BindingSource
    Public ReadOnly Property ContainsListCollection As Boolean Implements IListSource.ContainsListCollection
        Get
            Return False
        End Get
    End Property
    Public Function GetList() As IList Implements IListSource.GetList
        bindsrc.DataSource = models
        Return bindsrc
    End Function
#End Region
#Region "ファイル操作"
    Public Sub Save(ByVal fname As String)
        Dim json As String = JsonConvert.SerializeObject(models)
        File.WriteAllText(fname, json)
    End Sub
    Public Sub Load(ByVal fname As String, typ As Type)
        Dim json As String = File.ReadAllText(fname)
        json = json.Replace(vbTab, "").Replace("\", "\\")
        models = JsonConvert.DeserializeObject(Of List(Of BookModel))(json)
        bindsrc.ResetBindings(False)
    End Sub
#End Region
End Class
使い方例:
        'データ定義
        Dim bc As New BookCollection()
        bc.models.Add(New BookModel("書籍1", "4987185712345", 100, 100))
        bc.models.Add(New BookModel("書籍2", "4987185712346", 110, 200))
        bc.models.Add(New BookModel("書籍3", "4987185712347", 120, 300))

        '保尊
        co.Save("d:\test.json")

        '読込
        co.Load("d:\test.json", rack.GetType())

        '条件検索
        Dim filter = From x In bc.models Where x.Price >= 200 Select x
        For Each model As BookModel In filter
            Console.WriteLine(model.Name)
        Next

        'DataGridViewで表示する
        DataGridView1.DataSource = co.models
        DataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill
        DataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells
機能拡張など、こうしたら良いなどアイデアがあれば教えてください。

2013年1月21日月曜日

つるとんたん新宿店

つるとんたん
http://www.tsurutontan.co.jp/



坂本龍の番組だったか
カトープレジャー グループの社長が出演していた回の放送をみて知ったお店である。

番組内で「わざわざ来てくれるお店」を作るのがやり方のようで、
どんなお店か行ってみた。


行ってきた場所は、つるとんたん新宿店。

歌舞伎町方面、元コマ劇場があった所から1本奥の通りを右方向に進むと、地下1階にお店があった。






内装はまるで、キャバクラをうどん店へ改装した感じの作りになっていて、
カウンター席は、ディズニーのホンデットマンションの椅子みたいなでっかい椅子で二人が座れる席。
テーブル席もいくつかあるが、席に座ってる人達を見てみると器が通常の2倍サイズくらいあって迫力満点。
15時頃に入ったときは空いていたのか、20分くらい待って、さっそく案内されて注文したのはコレ。


釜揚げうどん。これにアナゴとネギトロ丼を頼んでみた。

注文した時に聞いたのだが、替え玉が3回まで無料でOKみたい。


さっそく食べてみた感想。
麺はつるつるで太麺の讃岐うどん。
味はシンプルかなー。一緒に行った人のも食べてみたけど同じ感想。
でもお店の内装や器、いろんな所は他のうどん屋に無いね。かなり個性的。
夜はライブもやるみたいなので、行列間違いなしだと思う。


個人的にうどんなら三国一!

C#とipythonでDjango

VS2012 Professional版使っていれば、PTVSをインストール、
VS2012 express版でも別途shellをインストールすれば済む話じゃないかっという話だけど、
今回は自力でやってみる方法を選んでみた。

 1.インストール
  ironpython、easy_installなど必要なものを順次インストールしていく。
 IronPython
  http://ironpython.codeplex.com/easy_setup
  http://peak.telecommunity.com/dist/ez_setup.py  

2.環境変数に下記のパスを追加。
 Path
   C:\Program Files (x86)\IronPython 2.7\
   C:\Program Files (x86)\IronPython 2.7\Scripts\
   C:\Program Files (x86)\IronPython 2.7\Lib\
   C:\Program Files (x86)\IronPython 2.7\Lib\site-packages\

  PythonHome
   C:\Program Files (x86)\IronPython 2.7\


3.モジュールを入れる。
 ipy64 Scripts\easy_install sqlite ipy64 Scripts\easy_install django


4.このままだとunicodeエラーが発生するので、djangoにパッチをあてる。 https://bitbucket.org/jdhardy/django-ironpython/commits/b70eeacda60c


.\lib\site-packages\django-1.4.3-py2.7.egg\django\utils\funcional

---95行目付近
 - assert not (cls._delegate_str and cls._delegate_unicode), "Cannot call lazy() with both str and unicode return types."
 + assert (str is unicode) or not (cls._delegate_str and cls._delegate_unicode), "Cannot call lazy() with both str and unicode return types."
---

5.プロジェクト作成
 ipy64 -X:FullFrames Scripts\django-admin.py startproject project_name

 ipy64の引数「-X:FullFrames」これが入ってなくて一度つまづいた所である。

詳しくはipy64 -hで参照すること。

2012年12月28日金曜日

Raspberry Piが届いた

今までMini2440を扱ってきたけど、今度は液晶パネル無しのRaspberry PiをRSコンポーネント経由で購入してみた。
部品不足らしく、注文してから2ヶ月ほど掛かるので、注文する方は直ぐには届かないと思っておいた方がいいと思います。

詳細スペック
 http://ja.wikipedia.org/wiki/Raspberry_Pi

 さっそく箱を開けてみると、名刺サイズの基板が入っていた。
700MHzのCPUと512MBのメモリーとGPIOにUSB、LANなどもついて3500円。
自分で基板を作ってやるよりも安くできそうだが、SDカードからブートになるため、起動時間はNANDフラッシュで起動可能なMINI2440と比べて遅くなる上、カードを抜き取られると中身が全て丸見えになるので、セキュリティー面で難あり。
だが基板単体で遊ぶには値段も手ごろでよさそうだ。

明後日から年末休みになるので、debianのインストールや無線LANを使って色々やってみようと思う。

2012年9月19日水曜日

djangoでjsonpを返す

クロスドメインでjsonデータのやり取りするアプリを開発していた時に
ブラウザからアクセスするとちゃんと返ってくるのに、
「$.getJSON」や「$.ajax」を使うとHTTPレスポンス200なのにエラーが返ってくる現象に出くわした。

しばらく悩んでたらajaxのポリシーでクロスドメインにアクセスするときはjsonpを使う必要があるようだった。

どういったものなのかというと、通常はjsonそのまま渡すのに対して、
jsonpは、queryに書かれたcallback名で囲ってapplication/javascriptで返すようになる。


jsonの場合
 http://127.0.0.1/sample/

 {data1:1, data2:"hello"}

jsonpの場合
 http://127.0.0.1/sample/?callback=jsonp_1234&_=2345

 jsonp_12345({data1:1, data2:"hello"});



実装方法は下にまとめてみた。

まずはjavascript側から


$.getJSON('http://127.0.0.1/sample/?callback=?', function(data){console.dir(data);});

$.ajax({url:'http://127.0.0.1/sample/', dataType='jsonp', success: function(data){console.dir(data);});


次にサーバ側(django)もjsonpに対応

def JSONP(f):
    """
        Usage:

        @JSONP
        def func(request):
            dic = { 'data1': 1, 'data2': 'hello'}
            return dic
    """
    def _func(*args, **kwargs):
        request = args[0]
        callback = request.GET.get('callback', None)
        dic = f(request)
        ret = json.dumps(dic)
       
        if callback is None:
            ct = 'application/json'
        else:
            ret = '%s(%s);' % (callback, ret)
            ct = 'application/javascript'

        return HttpResponse(ret, mimetype=ct)

    return _func

2012年9月14日金曜日

jquery mobile + backbone サンプル(2)

前回の続き。


今回はViewにイベントを追加してみる。

一応Viewのおさらいをしておくと、View=画面になる。
画面にはボタンなどが配置されていて、 マウスやタッチパネルで操作ができ、ユーザからイベント発行される。
今回はそのボタンとイベントを作成してみようと思う。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>example</title>
        <link rel="stylesheet" href="./jquery.mobile-1.1.1/jquery.mobile-1.1.1.min.css" />
        <link rel="stylesheet" href="my.css" />
        <style>
        </style>
        <script src="underscore.js"></script>
        <script src="json2.js"></script>
        <script src="jquery-1.7.1.min.js"></script>
        <script src="backbone-min.js"></script>
        <script src="./jquery.mobile-1.1.1/jquery.mobile-1.1.1.min.js"></script>
        <script src="my.js"></script>
    </head>
    <body>
        <div data-role="page" id="page1">
            <div data-theme="a" data-role="header"><h3>sample2</h3></div>
            <div data-role="content" style="padding: 15px">
        <!-- content start -->
        <div id="myview"></div>
        <!-- content end -->
            </div>
            <div data-theme="a" data-role="footer" data-position="fixed"><h3></h3></div>
        </div>

    <!-- underscore template start -->
    <script type="text/template" id="hello-template">
        <button id="btn_push">Push</button>
        <h3><%= part1 %>, <%= part2 %>!</h3>
        <h3>counter <%= counter %></h3>
        </script>
    <!-- underscore template end -->

    <!-- backbone start -->
        <script>
        (function($){
                MyView = Backbone.View.extend({
            el: '#myview'
            ,initialize: function(){
                this.counter = 0;
                this.render();
            }
            ,render: function(){
                var data = { part1: 'hello', part2: 'backbone!', counter: this.counter }
                var tmpl = _.template($("#hello-template").html(), data);
                this.$el.html(tmpl);
                this.$el.trigger('create');
            }
            ,events: {
                'click button#btn_push': 'evt_push'
                    }
            ,evt_push: function(){
                this.counter++;
                this.render();
            }
            });
            var myview = new MyView();
        })(jQuery);


    </script>
    <!-- backbone end -->
    </body>
</html>


前回のソースから、テンプレートにボタンを配置して、ボタンが押されたらカウンターが+1されるようにした。
 ボタンID = btn_push  
 イベントID=evt_push





1つ注意しておいてほしいのが、render内にタグを書き換えた後にtriggerでcreateしている所。
これを行わないとjquery mobileのボタン装飾などがされなくなるので入れておく必要がある。



関連記事
jquery mobile + backbone サンプル(1)

2012年9月13日木曜日

jquery mobile + backbone サンプル(1)

jquery mobileを使っていろんな情報を見て覚えて使ってみて、やっぱまともじゃない。jqmは使えない!って何度も思ったことか。ほんとWeb周りの技術ってごちゃごちゃしてて苦難の道のりである。
  • jquery mobileをそのまま使ってみると、ページ遷移やajax周りで泣く。(過去記事参照)
  • なんとか普通に動くようになったからコード整理しようと思いテンプレートエンジンの導入を考える。
  • angularJSを使ってみるとjqmと相性が悪くて使えない。angularJS-jquery-adapterなるものを使ってみる。
  • イベント周りとかテンプレートが強力でほんとjqmバイパス感が強いけど使えるんだけど、adapter側の情報が少ないし、画面遷移周りでフリーズすることもあり次を探しに出る。
  • クライアントサイド側のMVCフレームワークで一番情報が豊富と書かれていたbackbone.jsに手を出してみる。←今ココ!

というわけで行き着いた先はbackbone.jsであった。
チュートリアルを見てもちんぷんかんぷんで、なんじゃこりゃー!と思いつつ1つ1つ解いていくことにする。

Backbone.JSって何?
 Javascriptで書かれているMVCフレームワークである。
フレームワークの構成はRouter、Model、Collect、Viewの4構成で出来ている。
各々の説明すると、

 ・ページ単位で切り分けるRouter、
 ・データ定義するModel 、
 ・複数のデータを管理するCollect、
 ・画面周りを担当するView

という感じになる。


次にフレームワークの基本的な流れ。

 ページアクセス
  ↓
 Routerで振り分け
  ↓
 Viewでページ作成
  ↓
 Collect、Model必要なデータを引っ張ってくる。
  ↓
 画面表示

MVCフレームワークを使った事がある人であれば馴染みやすいと思う。

またこの他に、
 HTMLを吐き出すテンプレート「underscore」
 画面のデコレーション担当「jquery mobile」

があって今回のサンプルプログラムで使ったコードを張っておく。


ここまで頭の中を文章に吐き出してみた。
あとは使って覚えて知識の補強を繰り返していけば、実用投入ができるかもしれない。
ちなみに、このbackbone.jsは軽量って謳ってるけど、軽量という言葉は最近の流行なのだろうか。
体重100kg軽量級とか軽量ってタイトルが付くと、一瞬だけ軽そうって気分になるけど。


準備するもの

 jQuery mobile + backboneするのに今回は以下のライブラリを用意する。

http://jquery.com/http://documentcloud.github.com/underscore/
https://github.com/douglascrockford/JSON-js
http://backbonejs.org/
http://jquerymobile.com/


いよいよ Hello, Bavkbone! を書く
 
ベースのHTMLコードはjquery mobile サイトのデザイナツールで作りました。
それに加えてbackboneのViewとunderscoreを今回使用してます。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>example</title>
        <link rel="stylesheet" href="./jquery.mobile-1.1.1/jquery.mobile-1.1.1.min.css" />
        <link rel="stylesheet" href="my.css" />
        <style>
        </style>
        <script src="underscore.js"></script>
        <script src="json2.js"></script>
        <script src="jquery-1.7.1.min.js"></script>
        <script src="backbone-min.js"></script>
        <script src="./jquery.mobile-1.1.1/jquery.mobile-1.1.1.min.js"></script>
        <script src="my.js"></script>
    </head>
    <body>
        <div data-role="page" id="page1">
            <div data-theme="a" data-role="header"><h3>sample1</h3></div>
            <div data-role="content" style="padding: 15px">
        <!-- content start -->
        <div id="myview"></div>
        <!-- content end -->
            </div>
            <div data-theme="a" data-role="footer" data-position="fixed"><h3></h3></div>
        </div>

    <!-- underscore template start -->
    <script type="text/template" id="hello-template">
        <h3><%= part1 %>, <%= part2 %>!</h3>
        </script>
    <!-- underscore template end -->

    <!-- backbone start -->
        <script>
            MyView = Backbone.View.extend({
            el: '#myview'
            ,initialize: function(){
                this.render();
            }
            ,render: function(){
                var data = { part1: 'hello', part2: 'backbone!' }
                var tmpl = _.template($("#hello-template").html(), data);
                this.$el.html(tmpl);
            }
        });

        var myview = new MyView();

    <!-- backbone end -->
    </script>
    </body>
</html>


簡単に解説

   タグで囲った部分が3ブロック構成、コンテンツ、テンプレート、backboneで出来てます。 上から、

    1.コンテンツ部分。
    動的にしたい部分divタグにidを付けたものを挿入します。
   2.テンプレート部分。
    動的に表示させるHTMLをunderscoreの記述にしたがって記述します。
   3.Backbone部分
     どういったデータを加工して表示させるのかを記述します。
     ・elが対応する1のdivタグのID
     ・Viewの定義、initializeと、renderを記述。


動作の説明





実行画面


 できたら最後にビューのインスタンスを作成して終えてます。
 これをやるとどうなるかというと、 Myviewのinitialize()が呼び出されてrender()を呼ぶ。
render()内で今回用意したデータとテンプレートを使ってHTMLを作って、 elにデータセットして終えてます。
 最初に説明したフローで説明すると、


 ページアクセス
 ↓
Routerで振り分け (スキップ)
 ↓
Viewでページ作成
 ↓
Collect、Model必要なデータを引っ張ってくる。 (スキップ)
 ↓
画面表示
だいぶ省略してますが、backboneのViewとテンプレートを使ったHTMLレンダリングの仕組みが見えてくると思う。

2012年9月10日月曜日

jquery mobile のページ切り替えの挙動(イラッ

とにかくajaxの挙動がおかしい!それでも使いたい!って方用にメモを残しておく。

ページ切替イベントに
http://dev.screw-axis.com/doc/jquery_mobile/api/events/page_transition/about/

pagebeforehide
pagebeforeshow
pagehide
pageshow
の4つがあると記載してあるけど、実はもう一つあって
ページ遷移先のdiv[data-role="page"]タグ内にある<script>タグがある。

scriptタグはページを読込んだ直後に実行されるので、
pagebeforchangeよりも早く実行される。
data-prefetchした場合は裏でajax通信した時に読込む。
これが一番の問題。

どういうことかというと、ページ切替前にscriptが解釈されてしまうので、
当然ページ切替後に反映される事はない。

リロードしたときはうまくいくのに、ページ移動だとうまく行かないっていうのは
ここが原因となっているので、
ウェジット回りのイベント処理などは、素直に

jquery mobileでイベント処理をやらない


これが対策。

jquery mobileでイベント処理をやらない

がとても重要。

  具体的には、
1.ページ切替などそういった処理はヘッダー内scriptに記述して、できるだけ
処理を入れないようにする。
2.ウェジット回りのイベントはhtml内のdiv[page]内scriptタグで記述して、
onclickタグやonchangeタグを使って呼び出すようにする。

これだけ制約すれば素直に動いてくれるはず。

2012年9月7日金曜日

jquery mobileの悪い所

ajaxをONにしてうまく使えないか試行錯誤してたらべからず集ができそうなくらい問題が出てきた。

・DOMキャッシュ機能をPOST先のページがおかしくなる。
・location.hrefだと履歴やハッシュが付かなくなって次表示するとき更新されない。
・ときどきページが巻き戻る。
・ときどきGUIが素の状態になる。
・ときどきscriptタグ内の処理がスキップする。
・ときどきブラウザがフリーズする。
・WEBアプリ側でリダイレクトするとものアウト。
・リダイレクトしちゃった場合はcontent内に正式なURLを埋め込まないとリダイレクト前のURLになる。
・contentタグ内のページ名はユニークにしないとだめ。
・ラジオボタンをクリックしたときとかchangepageでページ遷移できない。


 こんだけ問題に直面すると時間がいくらあっても足りないしストレスとカフェインの量が増えてくる。

.NETとかがものすごく洗練された開発環境だなあと思えてきた。

2012年9月5日水曜日

jquery mobileでajaxを使う。

右も左も知らないまま、jquery mobileを使ってajax通信を使って動的なページを作ろうと思ったときに陥った罠を紹介。

今日の問題テーマ
1.pageinitでsetIntervalを使って定期的にajax通信を行う仕組みを入れると、ページ切り替えた後もajax通信を行っている。
2.ページ遷移先のjavascript(pageinit部分)が処理されない。
3.jquery mobile側のページ遷移で使われてるajaxをoffにすれば解決してしまう。(悪くないがせっかくなので使いたい)
4.ページ内でajaxを使うと時々エラーが発生する。

 これくらいの内容を紹介。
ここからたんたんと書いておく。

 jquery mobileとは  
  基本的な部品とページ遷移するときのアニメーションなどを提供してくれるライブラリでバックエンドにはajaxが使われている。ajaxをオフにするとページ遷移時のアニメーションがしなくなるなどの制限が付く。 あと、jquery mobileを使って自分でもajax通信するときは、多重通信するとエラーが起きるのでそのへんの制御を作りこまないといけないし、ページ遷移前には通信してない状態にもっていかないとだめである。 イベントフック自体はjquery mobileに提供されているものを使うことがリファレンスでも書かれているので目を当しておくこと。

jquery mobileがajaxONだとどうなるの?
 最初に開いたページがベースとなるので、ヘッダ、ボディー共に読み込まれる。ページ遷移はajax通信でリンク先のページデータを読み込んで、ヘッダタグはスキップしてボディータグ内にある
タグで囲まれたページを読み込んで、ページを書き換えるついでにアニメーションをはさむようになる。OFFにした場合は通常のページ移動するのでページ遷移先のヘッダーとボディーを再度読み込む。


自分でもajax使う実装はどうやるの?
 ページ遷移先のDivタグ内にscriptタグを仕込んでおいてそこに自分でajax通信をするプログラムを書いてあげればよい。Divタグ内で完結するscriptタグなので、グローバル変数が他ページとかぶっても置き換わる事もないし、引き継がれない。もしグローバル変数を使うならdivタグで囲まれたページタグ以外のエリアにメモしておけばいいと思う。もし、定期タイマーでajax通信する場合は、pageshow、pagehideでタイマーのON/OFF制御してあげればアクティブなときだけ通信するようになる。ちなみにjquery mobile公式ページにある画面レイアウト作成できるのでソースをエクスポートした場合、scriptタグがdivタグの外側にはみ出してるので厳重に注意しておくこと。

ここまでで1~4の解決策は書けた。
最後に、jquery標準のreadyじゃなくpageinitを使え!ってリファレンスに書かれてるからどこの記事でもpageinitで解決って感じに書かれているけど著者の意図通りに使われてないケースをよく見かける。
そもそもpageinitを使うケースは共通の初期化を行う時くらいなもんだろう。delegateとかするときはinitで、より使うほうはページ切り替え時の処理がメインなんじゃないかなと思う。でもまあ、このあたりは必要とされる技術を適材適所に使っていればいいので、どういうイベントがあるのかは頭の隅にでも覚えておくと良いと思います。

あとは全体の実装方法なんだけど、すっきりしたやり方がまだまとまってないので今日はここまで。

2012年8月31日金曜日

jquery mobileでpageinitするときの注意点

見事にはまった。

jquery自体始めて使うので、右も左も分からない事もあるんだけども
ドキュメント通りにコピペしてもpageinitは動かない!という問題にずっぽりはまってしまった。
色々足掻いてたら解決したのでメモっておく。

まずjquery mobileは通常ajaxがONになっていて、ページ切り替え時は
ajaxで次のページを読込んでからアニメーションして画面が切り替わる仕組みになっている。
こいつが動いてると初めに読込んだscript以外は読込まない挙動っぽい。

つまり対策方法は、
 ・どのページでもヘッダーに共通のスクリプトを読込むようにしておき、スクリプト内に画面の設定を記述しておく。
 ・ページ切り替えるタグにパラメータで「data-ajax="false"」を付け加えておく。

この2つに絞られる。

前者のがページ切り替わる時のアニメーションが付いてお手ごろなのだが
ページ数が多くなると厄介なので、今のところはsubmitやタグのところに後者のdata-ajax=falseを入れて対応しておいた。


django + jquery mobileで組込みアプリを作る(準備編)

産業関係で未だにMS-DOSで動くシステムなどがありますが、
20年続いたシステムでもハードウェア面でメンテナンスが出来なくなって
最新のシステムに置き換える事例などがあるかと思います。
今から作るなら.NETを使って実装が王道だけど、今回はWindows、Linux両方で動く
Pythonを使った方法を考えてみました。

 仕様イメージは組込み開発でWindowsやLinuxが動くパソコンを使って装置を動かすというもの。
パソコンに求められるものといえば、
 ・シリアルポートやLANを使う
 ・画面で運転状況を表示する
 ・リアルタイム制御を求める

この3点くらいだと思います。
どういったものを使えば出来るかというと、
 1つ目、シリアルポートやLANは、pyserial、twistedなどを使うことで解決。
 2つ目、最近の流れに沿って、スマートフォンで操作できるように、WEBアプリ開発でDjango、jQuery mobileを使えば対応できる。
 3つ目、WEBフレームワークDjangoのバックグラウンドにハード制御を行うスレッド又はプロセスを常駐させることで対応できる。

今回はDjangoを使うので、管理画面が最初からついてきます。しかもデータ操作が可能なものです。
それを使えばパラメータ設定などはDjangoの機能を使えば簡単に出来るし、データベースをSqlite3に
すれば1ファイルで管理できるメリットずくめ。後はWEBサーバが必要になるのでnginxを使うとします。

ここまでの構成をまとめると、
 nginx + django + jquery mobile + pyserial + twisted + sqlite3


だいぶ使うものが増えてきました。

[画面周り]
 ・ブラウザ経由(ネットワーク経由で遠隔操作なども可能)
 ・スマホ対応のライブラリjquery mobileを使うことで幅広くなる
 ・リアルタイム性はajaxを用いて、常時ハードウェアのステータスを拾うようにする。

[制御]
 ・WEBアプリ(画面周り)と切り離してバックエンドで動かすドライバ的なものにする。
 ・URL経由でハードを動かしたり、ステータスを取得できるようにする。

[パラメータ]
 ・sqlite3で管理する。DB操作はdjangoを使って楽する。
 ・ログなどもdjangoを使って楽をする。

画面周り+ハード制御用ドライバを実装して、これらをURL経由で操作できるようにすれば
WEB系を取り入れた組込み開発になる。

こんな感じになると組込み系の醍醐味?だと思うLCDにキャラクターパターンをUART経由で送信して表示するというような事はしなくなりそうです。

今日はここまで。

Androider