F10で右クリックメニューが表示されないようにする

Fedora17上で gnome-terminal を使っていたら、F10キーで右クリックメニューが表示されてしまう。
vimキーバインドで F10 を使っているのでこれは困った。

一応、メニューの「編集」→「キーボードショートカット...」で F10 を無効にする設定は存在する。これを「有効」に設定していると F10 キーを押した時点で上部のメニューが開く。で、無効にするとその挙動はなくなるのかなぁと思ったのだが、上部のメニューが開かない代わりに、マウスカーソルの位置に右クリックメニューが表示されてしまう。一応、vimキーバインドも機能はしているようだが、いちいちメニューが表示されるのはうっとうしい。

いろいろ調べてみたところ、Gnome3の設定ファイルを追加すればいいようだ。

~/.config/gtk-3.0/gtk.css というファイルを新規に作成し、以下の内容を記述。

 @binding-set NoKeyboardNavigation {
    unbind "<shift>F10"
 }
 
 * {
     gtk-key-bindings: NoKeyboardNavigation
 }

これでメニューは表示されなくなった。めでたしめでたし。

(参考) https://bbs.archlinux.org/viewtopic.php?id=129872

libxml2 で XMLTextReader を使ってみる

XML文書をパースする機能が必要になったので、libxml2 を使ってみた。
今までオイラは XML のパースは軽量言語(Perl、PHPなど)でしかやったことがなかった。libxml2をそのまんま使うとものすごく面倒なんだろうなぁと若干恐れおののいていたのだが、いざ使ってみると案外そうでもない。(少なくともフォーマットが分かっているXMLから必要な項目を取り出すだけなら)


ということで、XML文書から商品名と価格を取り出すという簡単なサンプルを作ってみた。

(例によってサンプルコード全体は github にあげてあります)


サンプルデータ(sample.xml)はこんな感じ。

<?xml version="1.0" encoding="UTF-8"?>
<fruits>
    <fruit>
        <item>りんご</item>
        <price>150</price>
    </fruit>
    <fruit>
        <item>みかん</item>
        <price>250</price>
    </fruit>
    <fruit>
        <item>メロン</item>
        <price>1000</price>
    </fruit>
</fruits>


で、これを処理してみる。プログラム側のざっくりとした流れは以下のようになる。

    xmlTextReaderPtr reader;
    int ret;

    //  Readerの作成
    reader = xmlNewTextReaderFilename("./sample.xml");

    //  次のノードに移動 
    ret = xmlTextReaderRead(reader);
    while (ret == 1) {
        //  現在のノードを処理(processNodeは別途定義)
        processNode(reader);

        //  次のノードに移動
        ret = xmlTextReaderRead(reader);
    }

    //  Reader のすべてのリソースを開放
    xmlFreeTextReader(reader);


ここで言う「ノード」とは、開始タグ・終了タグやテキストなどのこと。例えば

<item>りんご</item>

このようなXMLは、

  1. <item>
  2. りんご
  3. </item>


の3つのノードとして認識される。

    //  Readerの作成
    reader = xmlNewTextReaderFilename("./sample.xml");

xmlNewTextReaderFilename() は、ある XML リソースを読み込むためのReaderを作成する関数。引数に指定するのは、処理対象のリソースURI。今回はファイル名を指定したが、http://〜 で始まる文字列を指定すればネット上からリソースを取得することも可能。

    //  次のノードに移動 
    ret = xmlTextReaderRead(reader);
    while (ret == 1) {
        //  現在のノードを処理(processNodeは別途定義)
        processNode(reader);

        //  次のノードに移動
        ret = xmlTextReaderRead(reader);
    }

xmlTextReaderRead() は、XMLストリーム上の次のノードへ読み進める関数(初回は最初のノード)。戻り値は

  • 1: 正常終了
  • 0: 読み進めるべきノードがない
  • -1: エラー

よって、1 を返している間は読み進めたノードの処理を繰り返す。


処理本体は processNode() という別関数で定義した。

//  1つのノードを処理する
void processNode(xmlTextReaderPtr reader)
{
 …


現在 reader がポイントしているノードのさまざまな情報を取得する関数が用意されている。例えば以下のようなものがある。

項目名 取得用関数 概要
NodeType xmlTextReaderNodeType() ノードの種類
Name xmlTextReaderName() ノード名
Value xmlTextReaderValue() Textノードの場合、そのText
Depth xmlTextReaderDepth() XMLツリー内の階層の深さ(root=0)

NodeType は整数の値で表される。libxml/xmlreader.h では以下の18種類の値が定義されている。

typedef enum {
    XML_READER_TYPE_NONE = 0,
    XML_READER_TYPE_ELEMENT = 1,
    XML_READER_TYPE_ATTRIBUTE = 2,
    XML_READER_TYPE_TEXT = 3,
    XML_READER_TYPE_CDATA = 4,
    XML_READER_TYPE_ENTITY_REFERENCE = 5,
    XML_READER_TYPE_ENTITY = 6,
    XML_READER_TYPE_PROCESSING_INSTRUCTION = 7,
    XML_READER_TYPE_COMMENT = 8,
    XML_READER_TYPE_DOCUMENT = 9,
    XML_READER_TYPE_DOCUMENT_TYPE = 10,
    XML_READER_TYPE_DOCUMENT_FRAGMENT = 11,
    XML_READER_TYPE_NOTATION = 12,
    XML_READER_TYPE_WHITESPACE = 13,
    XML_READER_TYPE_SIGNIFICANT_WHITESPACE = 14,
    XML_READER_TYPE_END_ELEMENT = 15,
    XML_READER_TYPE_END_ENTITY = 16,
    XML_READER_TYPE_XML_DECLARATION = 17
} xmlReaderTypes;

今回のサンプルでは、1 = 開始タグ、3 = テキスト、15 = 終了タグ の3つのみ使用する。


さて、さっそく現在処理中のノードタイプとノード名を取得してみる。

    xmlElementType nodeType;
    xmlChar *name, *value;

    //  ノード情報の取得
    nodeType = xmlTextReaderNodeType(reader);       //  ノードタイプ
    name = xmlTextReaderName(reader);               //  ノード名
    if (!name)
        name = xmlStrdup(BAD_CAST "---");

name は常に取得可能とは限らず、NULL が返る場合もある。今回は取得出来なかった場合 "---" という文字列を代わりに代入することにした。BAD_CAST というのは、libxml2 で定義されているマクロで、通常の文字列を xmlChar* 型にキャストしても安全と分かっている場合のキャスト用に使用する。

    if (nodeType == XML_READER_TYPE_ELEMENT) {              //  開始
        if ( xmlStrcmp(name, BAD_CAST "item") == 0 ) {
            state = STATE_ITEM;

        } else if ( xmlStrcmp(name, BAD_CAST "price") == 0 ) {
            state = STATE_PRICE;
        }

ノードが開始タグである場合は、ノード名を調べる。"item" または "price" というノードなら変数 state を変更して、現在それらのノードの中にいることが分かるようにする。

    } else if (nodeType == XML_READER_TYPE_END_ELEMENT) {   //  終了
        state = STATE_NONE;

ノードが終了タグである場合は、"item" または "price" タグから抜けたということを表すため state を変更する。"item" "price" 以外のタグから抜けた場合でも無駄に処理されるが気にしない。w

    } else if (nodeType == XML_READER_TYPE_TEXT) {          //  テキスト
        //  テキストを取得する
        value = xmlTextReaderValue(reader);

        if (!value)
            value = xmlStrdup(BAD_CAST "---");

        if ( state == STATE_ITEM ) {
            printf("品名: %s\n", value);

        } else if ( state == STATE_PRICE ) {
            printf("価格: %s\n", value);
        }

ノードがテキストノードだった場合は、現在どのノード内にいるかを調べ、適切な文字列を表示する。テキストノードの値を取得するには xmlTextReaderValue() を使用する。

        xmlFree(value);
    }

    xmlFree(name);
}  

xmlChar* 型の変数を宣言し、かつ xmlTextReader〜() 関数を使って文字列を取得できた場合は、使用後に xmlFree() で適切に開放しなければならない。

    //  Reader のすべてのリソースを開放
    xmlFreeTextReader(reader);

すべての XML パース処理が終わったら、Reader のリソースをすべて開放する。


コンパイルは以下のとおり行う。ライブラリやインクルードファイルのパス指定は、xml2-config を使うと楽。

gcc -Wall -o xmlread xmlread.c `xml2-config --cflags --libs`


実行結果

-----------------------
品名: りんご
価格: 150円
-----------------------
品名: みかん
価格: 250円
-----------------------
品名: メロン
価格: 1000円
-----------------------

[GTK+] GtkTreeView を使う

GtkTreeView とは?

http://library.gnome.org/devel/gtk/stable/GtkTreeView.html
ツリー構造やリストを表示するためのウィジェット
GtkTreeView というからにはツリー構造、つまり階層を持つデータ構造を表示するのが唯一の用途のようにも見える。しかし階層構造の有無を問わず、行と列を使って2次元的に表示したいのであれば、GtkTreeView での表示に適している。

GtkTreeView の構造

GtkTreeView はいわゆる MVC(Model-View-Controller)構造をとっていて、データを保持する Model、表示方法を決める View、そしてそれらを利用しながら全体をコントロールする Controller が分離されている。
Model と View については、専用のクラスが用意されている (Modelであれば、GtkListStore, GtkTreeStore など、View であれば GtkTreeViewColumn など)。これらのクラスを用途に応じて選び、大本の GtkTreeView に関連付けて使うことになる。

GtkTreeView の生成

(サンプルコードのソース)


生成自体は gtk_tree_view_new() でできる。

#include <gtk/gtk.h>

int main(int argc, char **argv)
{
    GtkWidget *window, *treeview;

    gtk_init(&argc, &argv);

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "明日の天気");
    g_signal_connect(G_OBJECT(window), "destroy",
                     G_CALLBACK(gtk_main_quit), NULL);

    /*  GtkTreeView の生成  */
    treeview = gtk_tree_view_new();

    gtk_container_add(GTK_CONTAINER(window), treeview);
    gtk_widget_show_all(window);

    gtk_main();
    return 0;
}

もちろん中のデータもその表示方法も定義していない状態では、真っ白のウィンドウが表示されるだけで、面白いことは何も起きない。それでも、一応ウィンドウ上に GtkTreeView ウィジェットは存在しているはず(多分)。

Model について(GtkListStore)

というわけで、この上に何か意味のあるものを表示させていこうと思う。まずはデータを格納するための Model = GtkTreeModel の準備から。

GtkTreeModel は、データを保持して GtkTreeView にデータを提供するための仕組みを備えている。といっても GtkTreeModel 自身はインタフェースにすぎず、実際には GtkTreeModel を実装したクラスを使用する。ビルトインで用意されているものとして

があるので、このどちらかを使っていればよい。

GtkListStore と GtkTreeStore の異なる点は、扱うことのできるデータ構造。GtkListStore は、階層構造を持たない、線形リストのみ扱うことができる。対して GtkTreeStore は、階層構造を含んだデータを扱うことができる。一見 GtkTreeStore のほうが優秀で、こちらだけあれば良いようにも見えるが、GtkListStore は構造が単純な分処理が高速なため、階層構造が特に必要なければ GtkListStore を使ったほうがよいだろう。(使い方もやや単純)

というわけで、まずは GtkListStore を使ってみる。

まずは今回作るものを決めよう。今回は、各地の天気予報データを表示するサンプルアプリを作ることにした。都市名と、天気、最高・最低気温を表示していくことにする。データ自体は Livedoor の Weather Hacks から、2010年12月4日時点での明日の天気を取得させていただいた。

場所 天気 最高気温 最低気温
東京 晴のち曇 17 9
群馬 晴時々曇 18 5
大阪 晴れ 16 7


最初にデータの格納先について定義する。

/* 
 *  Model の定義
 */
void create_model(GtkWidget *treeview)
{
    GtkListStore *store;

    /*  GtkListStore の生成、および列定義  */
    store = gtk_list_store_new(
                4,              //  要素の個数
                G_TYPE_STRING,  //  場所
                G_TYPE_STRING,  //  天気
                G_TYPE_INT,     //  最高気温
                G_TYPE_INT      //  最低気温
            );

    /*  treeview に結びつける  */
    gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(store));

    /*  一旦結びつけたら store 自体は開放してよい */
    g_object_unref(store);
}

gtk_list_store_new() の引数を使って、格納データの形式を定義する。RDBでいうDDLのようなものだと思うとイメージつかみやすいかも。まず列の数を指定し、その後にそれぞれの型を順番に指定していく。型の指定はここの冒頭にある #define 文あたりを参考にすれば大抵の用途には充分かと。

指定が終わったら、大元の GtkTreeView に関連付ける。これでこの GtkListStore が treeview 上で使用可能になる。

/*
 * Model に実データ追加
 */
void set_data(GtkWidget *treeview)
{
    GtkListStore *store;
    GtkTreeIter iter;

    //  treeview に結びつけられている Model を取得する。
    //  一旦、中のデータをすべて消去する。
    store = GTK_LIST_STORE( gtk_tree_view_get_model( GTK_TREE_VIEW(treeview) ) );
    gtk_list_store_clear(store);

    /*  新しいレコードを生成する(3レコード) */
    gtk_list_store_append(store, &iter);
    gtk_list_store_set(store, &iter,    //  格納先レコード
                        0, "東京",      //  0番めの値
                        1, "晴のち曇",   //  1番めの値
                        2, 17,         //  2番めの値
                        3, 9,          //  3番めの値
                        -1              //  終端
    );

    gtk_list_store_append(store, &iter);
    gtk_list_store_set(store, &iter, 0, "群馬", 1, "晴時々曇", 2, 18, 3, 5, -1);

    gtk_list_store_append(store, &iter);
    gtk_list_store_set(store, &iter, 0, "大阪", 1, "晴れ", 2, 16, 3, 7, -1);
}


次に、この GtkListStore に対して、実際のデータをセットする。データをセットするには、まずレコード格納用の領域を生成する必要がある。それを行っているのが gtk_list_store_append()。レコードが生成されると、変数 iter に新規レコードへの参照が格納される。
そして新規レコードに対して値をセットする。1番目のレコードのみ見やすいようコメントを入れたが、格納先レコードを指定したあと、第3引数以降で、データの位置-値のセットを指定していく。


そしてこれらを main()関数から呼び出す。

    /*  Model の定義  */
    create_model(treeview);

    /*  Model に実データ追加  */
    set_data(treeview);


もちろんこの時点で実行しても何も表示されない。最後に、データの表示を行うための view を追加しよう。

View について

View の定義は「列」単位で行われる。列を定義するために使用されるクラスは GtkTreeViewColumn。このインスタンスを、必要な列の数だけ TreeView に追加していく。


GtkTreeViewColumn 自体は、列の「枠」を用意するにすぎない。実際、GtkTreeViewColumn の properties を見てみると、列幅やヘッダ行に表示されるタイトルなどの外形的なものを定義できるだけで、その中に表示される具体的なデータには何ら関心を持っていないことが分かる。

データを表示するためには、その「枠」の中に、表示機能を司る「何か」を入れてやらなければならない。その「何か」というのがGtkCellRenderer。GtkTreeViewColumn に含めることのできる、具体的なデータを扱うためのオブジェクトだ。GtkCellRendererのサブクラスとして、以下の4つがビルトインで用意されている。

  • GtkCellRendererText
  • GtkCellRendererPixbuf
  • GtkCellRendererProgress
  • GtkCellRendererToggle

例えば GtkCellRendererText を GtkTreeViewColumn に追加すれば、その列にテキストを表示することが可能になる。その他のクラスを選べば、画像やプログレスバー、トグルスイッチを表示させることもできる。追加可能な GtkCellRenderer は1つとは限らないので、例えば1つの列にアイコン (GtkCellRendererPixbuf) とテキスト(GtkCellRendererText) を並べて表示させるということも可能だ。

また、GtkCellRenderer はデータの表示に関する一切のことを請け負っているため、データ表示そのものだけでなく、その表示のされ方についても設定することができる。例えばテキストであれば、表示色やフォントについても GtkCellRenderer(Text) に対して指定することになる。

まぁ今回はシンプルに、テキストのみを扱うことにする。

/*
 * treeview に列を1つ追加
 */
void append_column_to_treeview(GtkWidget *treeview, const char *title, const int order)
{
    GtkTreeViewColumn   *column;
    GtkCellRenderer *renderer;

    /*   CellRenderer の作成  */
    renderer = gtk_cell_renderer_text_new();

    /*
     *  列を作成し、CellRenderer を追加する。 
     *   さらに Model との関連を定義する
     */
    column = gtk_tree_view_column_new_with_attributes(
                title,          //  ヘッダ行に表示するタイトル
                renderer,       //  CellRenderer
                "text",  order, //  Model の order 番めのデータを、renderer の属性 "text" に結びつける
                NULL            //  終端
             );

    /*   今生成した列を、大元の treeview に追加する  */
    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
}


/*
 * treeview に View を定義する
 */
void create_view(GtkWidget *treeview)
{
    append_column_to_treeview(treeview, "場所", 0);
    append_column_to_treeview(treeview, "天気", 1);
    append_column_to_treeview(treeview, "最高気温", 2);
    append_column_to_treeview(treeview, "最低気温", 3);    
}  


gtk_tree_view_column_new_with_attributes()について補足しておく。renderer の "text" 属性に、Model の order 番めのデータを結びつけているが、ここでは Model と View のマッピングを行っている。例えば一番最初に作成した列であれば order = 0 なので、Model に設定した 0 番目の情報、つまり場所情報(データでいうと「東京」「群馬」「大阪」)が、この列の GtkCellRenderer の属性 "text"、つまり表示される文字列そのものとしてセットされる。

ちなみに設定可能な GtkCellRenderer の属性は、text だけに限らない。例えば foreground に Model の値をセットするようにすれば、色の指定も可能となる。

実行結果

Django クイックスタートの巻

Python 上で動く Web フレームワークDjango の超クイックスタート。簡素化しまくってるのでご容赦を。
またプログラム中、コメント行はすべて省略。


環境は以下のとおり。PythonDjango はすでにインストールされているものとする。

またネーミングは以下のとおり。

  • プロジェクト名: myproject
  • アプリケーション名: myapp
  • ビュー名: myhello

プロジェクト作成

django-admin startproject myproject

アプリケーション作成

cd myproject
python manage.py startapp myapp

アクティブにするアプリを定義

settings.py を編集。INSTALLED_APPS に今回作成するアプリケーション(myproject.myapp)を追加。

INSTALLED_APPS = (
    ...(中略)...
    'myproject.myapp',
)

URL とビューのマッピング

urls.py を編集。'myhello/' という URL がリクエストされたら、ビュー myhello が適用されるようにマッピングを行う。ビュー myhello は、このあと myproject/myapp/views.py で定義するつもりなので、myproject.myapp.views.myhello と指定する。

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^myhello/$', 'myproject.myapp.views.myhello'),
)

ビューの定義

myapp/views.py を編集し、ビュー myhello を定義する。

from django.http import HttpResponse

def myhello(request):
    html = '<html><body>Hello, world.</body></html>'
    return HttpResponse(html)

開発用サーバ起動

python manage.py runserver

結果確認

http://127.0.0.1:8000/myhello/ にアクセスして、以下のようなページが表示されたらOK。


Social Games that Sway Behavior - Technology Review


最近ソーシャルゲームがあちこちで話題になっているが、そのソーシャルゲームを現実世界と結びつけようという試みが始まっているらしい。
この記事で最初に紹介されている HealthSeeker は、Facebook上でプレイすることができるソーシャルゲームなのだが、プレイヤーとして想定しているのは「現実世界で」糖尿病を患っている人たち。

ここではRPGのようにいくつかのミッションが用意されているのだが、それは例えば「コーヒーを飲むときに砂糖を入れない」など、糖尿病の人たちの生活を改善するためのものばかりだ。プレイヤーはその中からチャレンジしたいミッションを選ぶ。そしてそれを現実の世界で実践することができたら、自己申告によってミッションのレベルに応じた経験値がもらえる。またミッションに成功するとFacebook上の知り合いに知らされ、お祝いのプレゼント(仮想世界の)がもらえたりするそうだ。

#オイラはFacebookやってなくて実際にプレイしてないので、細部の紹介が間違ってるかもしれないけど(汗


悪い生活習慣って、改善しようと思っても意志が強くないとなかなかできない。でっかい紙に「○○をやめる!」と書いて壁にはっておいたところで、せいぜい1〜2日守れるかどうかってとこ。(←体験者。w)
しかしそれは現実世界のみで解決しようとした場合の話。最近流行りのソーシャルゲームを現実世界の問題を解決するために応用するって考え方は、まさに今の時代ならでは。特に、目標を立ててそれを成し遂げようとするときに、人に見守られている、ちょっとでも進歩があればみんなからの賞賛を受けられる、というのは何歳になっても嬉しいものだし、ソーシャルゲームはそれを、現実世界よりかなり上手く実現することができるはずだ。

禁煙とか、もっといろんなところに応用の余地がありそうだ。

カレンダーを使う

Gtk+ではカレンダーウィジェットというのが用意されているので、今回はこれを使ってみる。

カレンダー上の日付を選択すると、その日付をテキストエリアに表示するという簡単なアプリケーションを作ってみた。

(ソースは以下の場所に置いてあります)
http://github.com/egawata/hatena_blog/tree/master/calendar/

 39     //  GtkCalendar オブジェクトの生成
 40     calendar = gtk_calendar_new();
 41 
 42     //  日付表示用エリア
 43     text = gtk_entry_new();

カレンダーオブジェクトを作成する。ついでに、選択された日付を表示するためのテキストエリアも用意しておく。

 47     g_signal_connect(G_OBJECT(calendar), "day-selected",
 48                      G_CALLBACK(disp_day), (gpointer)text);

GtkCalendar はいくつかのシグナルを扱うことができる が、カレンダー上の日付がクリックされたら何か処理を行いたい、ということはよくあるだろう。そのときは "day-selected" というシグナルが発生する。
上記では、"day-selected" シグナルが発生したときに、disp_day 関数(10-22行)をコールするように指示している。


disp_day 関数は、カレンダー上で選択された日付を、年月日の文字列に直してテキストエリアに表示する処理を行っている。

 10 static void disp_day(GtkCalendar *calendar, GtkEntry *text)
 11 {
 12     guint year, month, day;
 13     gchar ymd[100];
 14 
 15     //  カレンダー上で選択された年月日を得る
 16     gtk_calendar_get_date(calendar, &year, &month, &day);
 17     month++;    //  月は 0-11 の値で取得されるので +1
 18 
 19     snprintf(ymd, 100, "%d%d%d日", year, month, day);
 20 
 21     gtk_entry_set_text(text, ymd);
 22 }

gtk_calendar_get_date() を使って、カレンダー上で選択された年月日を取得することができる。
ここで注意したいのは、GtkCalendar は月を 0-11 の範囲で扱うということ。したがって上記のように月を取得すると、実際の月より1少ない値が返ってくる(10月なら 9)。逆にカレンダーの表示月を指定するような場面では、1少ない値を月に指定する必要がある。今回のサンプルでは、年月日を取得したあと、月の値をプラス1している。

 57     g_signal_emit_by_name(calendar, "day-selected", text);

アプリケーション起動直後、カレンダー上では今日の日付が選択された状態になっている。しかしテキストエリアには日付が表示されていない。前述の disp_day 関数が起動後一度も実行されていないからだ。
これではカッコ悪いので、g_signal_emit_by_name() を使って "day-selected" シグナルを意図的に発生させ、今日の日付がテキストエリアに表示された状態にしておく。


単純な例で実用性に乏しいかもしれないが、コールバック関数を、例えば指定された日付の予定を表示するような処理に置き換えれば、スケジュール管理アプリみたいになって良いかも。