2012年12月30日

何のために勉強会をするのか?

Brunel University student study group
By Brunel University under CC BY-NC-SA

Shin x blog 勉強会なんてやらなくても良いという記事がちょっと注目を集めていました。かれこれ10年ぐらい勉強会を開催したり、3年前と去年には勉強会とかコミュニティの本を出したり、僕もそれなりに頭を悩ませた経験がある方かと思うので、僕なりの現在の考えをまとめてみます。勉強会に悩む人の一助になれば、と思います。

参考文献

自分がすごいと思っているものの存在を広めたい

続きを読む
posted by @shibukawa at 08:16 | TrackBack(0) | 日記 はてなブックマーク - 何のために勉強会をするのか?

2012年12月25日

ベジタリアンも感謝祭・クリスマスできるアメリカ

DSC_0541

アメリカでは、感謝祭を筆頭として、お祝いの場では七面鳥とか、巨大なスパイラルハムとかが食卓に並びます。その一方、日本だとヒンズー教やイスラム教の人が来ると店探しに相当苦労しますが(同時に来て店を探したこともありますが)、ベジタリアンが一定数いて、どのレストランに行ってもたいていベジタリアンメニューがあったりして、肉がダメ/牛がダメ/豚がダメという宗教・習慣上食べられないものがある人でもなんとかなるのがアメリカです。ベジタリアン向けのターキーっぽいものも売ってたりします。Tofurkyという、会社の製品です。ここ以外にも、Trader Joe'sが自社ブランドでも出してたりします。

上記の写真は、オーブンで調理中の図ですが、足のない七面鳥な雰囲気です。醤油とオリーブオイルでタレを作って、それをかけて野菜と一緒にオーブンで1時間半ほど温めるだけ。中には七面鳥の足っぽいものが付いているものもあるらしいです。グレービーという名の別物のソースもついてきます。本来は肉汁で作るのですが、これはパンの塊が入っていたりして(温めると崩れてとろとろになる)、ベジタリア仕様です。

DSC_0543

できあがりはこんな感じ。中には最初から詰め物っぽいものもされてたりして、七面鳥風です。味もかなり肉っぽい。食感はかまぼこみたいです。そんなに悪くないです。うちは嫁が鶏肉が嫌いなのでこれを買ってみましたが、なかなかおもしろい経験です。

DSC_0547

嫁が授乳中なので、乾杯はシャンパン風の炭酸ぶどうジュース。白ワインでも使われるシャルドネ100%で作られているみたい。炭酸弱めなのでぽんっとは抜けないのですが、それっぽい雰囲気がばっちり味わえます。

DSC_0556

デザートはpecan nutsのパイと、Eggnog。オトナはEggnogにラム酒をちょっと混ぜて楽しみます。Eggnogも定番の飲み物らしく、昨日にTrader Joe'sに行った時はEggnogの棚が空になってました。pecan nutsのパイは脅威的な甘さですね。これに関しては日本のケーキの方がいいかも・・・食べ物の多様性はアメリカの方が便利なことが多いですね。宗教上の理由だけでなく、周りの人の子がアレルギー持ちが多いし、日本でももっと食べ物の選択肢(料理ではなく原料の)が増えるといいなぁ、といろんな宗教の人と仕事するようになったり、子供の親になって強く思います。

日本ではケンタッキーとかコンビニとかデパ地下とかスーパーが大々的に宣伝していますが、鶏肉は人数が少なくてターキーでは大きすぎる場合の代替品。アメリカでは値段もターキーの方が安いですね。100g10円ぐらいだったか・・・それがプライマリーのような感じで行動する日本人はアメリカの人から奇異な目で見られます。「年越しで手打ち蕎麦食べに行きたかったけど、お店いっぱいだったからカップ麺でごまかそうテヘ」ぐらいの感じなので、アメリカ人女性を口説く予定の男性、もしくはその逆の方は要注意です。

posted by @shibukawa at 15:32 | TrackBack(0) | 日記 はてなブックマーク - ベジタリアンも感謝祭・クリスマスできるアメリカ

2012年12月23日

Python温泉に行って本を書いた話

つまみぐい勉強法売れています!

写真は出た直後の写真です。もう2年半も経つのでさすがに平積みはもうしてないですし、他の本と比べてバカ売れで底が見えそう、なのも今はないと思います。はい。

PySpaアドベントカレンダーに混ぜてもらいました。アメリカはまだ23日ですが、(JSTの)24日のエントリーとして参加します。

Python温泉に初めて行ったのは第4回だかなんだかで、当時はもう芳泉閣になっていて、必ず雨に降られるという点も含めてすでに恒例行事な雰囲気でした。Python温泉に参加した後につまみぐい勉強法という本を書いたのですが、この本でもPython温泉で学んだことが含まれています。Python温泉は勉強会のカテゴリのイベントの一つとして見てもなかなか面白いものがあります。このあたりを軽く紹介しようと思います。

勉強会としてのPython温泉

続きを読む
posted by @shibukawa at 15:30 | TrackBack(0) | 日記 はてなブックマーク - Python温泉に行って本を書いた話

2012年12月15日

Qt+JS: Mac OS Xについての注意点

MacはOSのバージョン間での互換がいろいろクセがあるので、古いバージョンで使っていた方法がそのまま他の方法で使えるとは限りません。

バイナリ配布版+10.6, 10.7

10.6, 10.7はそれほど苦労はないと思います。バイナリ配布されているQtと、XCodeのコマンドラインツールのgcc(4.2, Apple版)を使っておけば問題はないはずです。

バイナリ配布版+10.8

clangを使わないとビルドできないです。QtCreaterを使っている場合は、ツールチェーンをClangにしてください。コマンドラインからQMakeでビルドする際は次のオプションが必要となります。ちなみに、Qt 5系はいろいろトライしていますが、QtScriptバインディングがビルドできません。内部のプライベートなクラスがいろいろ変わっていたりしますが、その部分は解消して、ヘッダをパースしてC++を出すコードの基本部分は対応できました。ただし、生成したコードはまだビルドできないです。コードジェネレータ部分(文字列操作周り)をいじる必要がありそうです。

# Qt 4系
qmake -spec unsupported/macx-clang
# Qt 5系
qmake -spec macx-clang

MacPorts版

MacPortsの中に入っているqmakeがOS設定を知っているっぽいので、とくに-specを追加する必要はありません。

QtScriptバインディングビルド時の環境変数

QtScriptのバインディングをビルドするときは、それぞれ環境変数を追加する必要があります。デフォルトのインストール先に入れた場合の各設定を紹介します。

# Qt5用(ただしまだ成功してない)
export QTDIR=~/Qt5.0.0-rc2/5.0.0-rc2/clang_64/
export PATH=$QTDIR/bin:$PATH
# Qt4系 4.7, 4.8確認済み
export QTDIR=~/QtSDK/Desktop/Qt/4.8.1/gcc/
export PATH=$QTDIR/bin:$PATH
# MacPorts版
export QTDIR=/opt/local/
export PATH=$QTDIR/bin:$PATH

OSバージョン判定シェルスクリプト

sw_versというコマンドラインのコマンドがバージョン番号を文字列で返してくれます。これを使うとバージョンごとに処理を分けることもできそうです。

if expr `sw_vers -productVersion` : "10.8.*" > /dev/null; then
    # 10.8の時のコード
else
    # それ以外のコード
fi

どの環境も、デフォルトではコンパイルしたバージョン以上のOSでしか動かないバイナリを作ると思います。Macではアプリを古いOS向けにビルドするには、リンク時に次のオプションが必要となります。

-mmacosx-version-min=10.6
posted by @shibukawa at 03:33 | TrackBack(0) | 日記 はてなブックマーク - Qt+JS: Mac OS Xについての注意点

2012年12月12日

Qt+JS: コンテナウィジェットの使い方

Qt+JS: QtDesignerによるレイアウト(2)のラストで、コンテナウィジェットのレイアウトについて簡単に紹介しました。これ以外のコンテナウィジェットの注意点を軽く紹介します。

オブジェクトの取得方法

Screen Shot 2012-12-13 at 1.42.32 AM

上記のようなスクロールエリアを置いたとします。このウィジェットの直下にscrollAreaWidgetContentsというオブジェクトがいます。きっとこいつにこの名前でアクセスすれば子供が得られるに違いない!と思うと失敗するのがいやらしいところです。ルートがthis.widgetに格納されているとすると、次のようにアクセスします。

this.pushButton = detail.scrollArea.widget().pushButton;

widget()メソッドを使うと、直下の子供が得られます。それでは次のケースはどうでしょうか?

Screen Shot 2012-12-13 at 1.43.31 AM

これもほぼ一緒です。widget()メソッドに数値でページ番号(ゼロスタート)を渡すとオブジェクトが得られます。コンテナ自身がユーザの操作を受けて反応はしますが、中の子供からすると、特に特別な実装を子供がしなければならないことはありません。QWidgetやQFrameが親の場合と大差ありません。

this.pushButton = detail.tabWidget.widget(0).pushButton;

タブの名前を変更する

Screen Shot 2012-12-13 at 1.44.50 AM

レイアウトを操作するときは、親のQTabWidgetなどを選択して変更すると以前に紹介しましたが、タブの名前も同じです。親のQTabWidgetを選択します。QtDesignerのプロパティシートの最下部にQTabWidgetの設定があります。ここで現在のページに関する項目も並んでいるので、タブ名やオブジェクト名(使いませんが)を選択できます。

posted by @shibukawa at 23:59 | TrackBack(0) | 日記 はてなブックマーク - Qt+JS: コンテナウィジェットの使い方

Sphinx-Users.jpを作った時のお話

Scrabble game
taken by jcolman under CC BY-NC-SA

缶切りでツナ缶を開けようとしたら、缶切りの方が耐久度が低くて壊れたり、パン粉を作るためにプラスチック製のおろし金でパンをすりおろしたら、おろし金の方が削られてツルツルになってしまうような修羅の国アメリカで仕事しています。

僕が日本にいたときに設立したSphinx-Users.jpは代表が変わったあともMLで活発な議論が行われたり、イベントが定期開催されたり、うまく離陸して活動が継続しています。また、当初のもくろみ通りに、単にツールの使い方だけではなく、ドキュメントとは何か?どう書くべきか?ということを考えて発表してくれる人も増えています。

Sphinxのアドベントカレンダーということで、コミュニティについて話をします。easy_installしましょうとかは書きません。Sphinx-Users.jpを作る上で考えたことを思い出しながら書いてみます。

  • 2009/04: Vに勧められてSphinxを触り始めて気に入りドキュメントを翻訳
  • 2009/09: ドメイン取得
  • 2009/11: Sphinx-Users.jpのMLにミッション・ステートメントを決めようとメールを投稿
  • 2010/01: 清水川さんがエキスパートPythonの目次だけSphinxで日本語訳を作って公開したらはてブが400を超えて翻訳が決まる事件
  • 2010/02: 毎月第二火曜日PM10:00から1時間のスタッフミーティング開始
  • 2010/07: 集まってオフ会しましょうかと思ったら、偶然その日に1.0がリリースされて急遽世界最速リリースパーティー開催事件
  • 2011/12: 総会で世界の小宮が代表に

コミュニティを作る上でまず考えたのがスコープ。オープンソースのコミュニティというとツールを作るコミュニティもあれば、使うユーザのコミュニティも、翻訳のコミュニティもあったりします。ここではツールを使うコミュニティにしたいと考えました。アート・オブ・コミュニティ の日本語訳が出版されたのは2011年の4月ですが、翻訳権が取れる前に自主的に翻訳をし始めていて、その2章の組織のスタートアップの章は参考にしました。スタッフMLにも日本語訳を投下しました。

コミュニティづくりはゲームづくり

続きを読む
posted by @shibukawa at 14:14 | Comment(373) | TrackBack(0) | 日記 はてなブックマーク - Sphinx-Users.jpを作った時のお話

2012年12月11日

Qt+JS: 画像を表示する

Qtでは画像を表示する方法が何通りかあります。

アイコンとして表示

テーブルでもツリービューでも使える方法です。サイズを細かく調整することはできませんが、お手軽に表示できます。

var imageLabel = new QTableWidgetItem("", 0);
imageLabel.setIcon(new QIcon(imageFilePath));
this.tableWidget.setItem(1, 1, imageLabel);

ラベルとして表示

ラベルにはPixmapを貼ることもできます。ただ、Pixmapを貼ってしまうとテキストが書けません。ただ画像を表示するだけなら問題ありませんが、ちょっとトリッキーなHTMLを利用する方法を使えば、かなりのことが表現できます。ここでは、テキストと画像を同時に表示しています。

var imagePath = ':/images/label.png';
var label = "hello world";
var text = [
    '',
    '',
    '

', ' ' + label + '

' ].join(''); this.labelWidget.text = text;

カスタムのウィジェットを作成する

画像を表示するウィジェットを作成すると、さまざまな表現が可能になります。QPainterの豊富なメソッドを使ってさまざまなことが表現できます。拡大縮小、色の変更、合成などなどです。カスタムのウィジェットはprotectedなメソッドをオーバーライドする必要が有るため、コードでクラスを作成してnewする必要があります。

QPainter自身はQImageにバインドすれば、画像処理に使うことができますが、画面に表示させるためのQPainterは、paintEventメソッド内でしか使うことができません。以下のコードでは、最大サイズを決めて、それ以下の場合には縮小表示を行なっています。また、縮小するときにはバイリニア補完を使うように設定しています。このように、単にpaintEvent()をオーバーライドすれば、画面表示が行えます。

function ImageView(parent, path)
{
    QWidget.call(this, parent);
    var fileinfo = new QFileInfo(path);
    if (fileinfo.exists())
    {
        this.image = new QImage(path);
    }
}

ImageView.prototype = new QWidget();

ImageView.prototype.paintEvent = function ()
{
    if (this.ratio && this.image)
    {
        var painter = new QPainter();
        painter.begin(this);
        painter.setRenderHint(QPainter.Antialiasing, true);
        painter.setRenderHint(QPainter.SmoothPixmapTransform, true);
        painter.scale(this.ratio, this.ratio);
        painter.drawImage(0, 0, this.image);
        painter.end();
    }
};

ImageView.prototype.calcSize = function ()
{
    if (!this.image)
    {
        return;
    }
    var availableWidth = 340;
    var availableHeight = 300;
    var ratio = Math.min(1, Math.min(availableWidth / this.image.width(), availableHeight / this.image.height()));
    var height = Math.round(this.image.height() * ratio);
    var width = Math.round(this.image.width() * ratio);
   
    this.resize(width, height);
    this.setMinimumSize(width, height);
    this.setMaximumSize(width, height);
    this.ratio = ratio;
};

タイマーで自動更新

もしアニメーションなど、定期的な画面更新が必要であればタイマーをこのオブジェクトに仕込む必要があります。

function ImageView(parent, path)
{
    QWidget.call(this, parent);
    var fileinfo = new QFileInfo(path);
    if (fileinfo.exists())
    {
        this.image = new QImage(path);
    }
    this.timer = new QTimer(this);
    this.updateHandler = this.onTimeout.bind(this);
    this.timer.timeout.connect(this.updateHandler);
    this.timer.start(16); // 16ミリ秒ごとに更新
}

ImageView.prototype.onTimeout = function ()
{
    this.repaint(); // これを呼ぶと強制的に描画イベント発行
};
posted by @shibukawa at 19:47 | Comment(453) | TrackBack(0) | 日記 はてなブックマーク - Qt+JS: 画像を表示する

2012年12月10日

Qt+JS: QTreeWidget (3)

前回のエントリーで、ドラッグ・アンド・ドロップを実装しましたが、その代償としてGUIツールでQTreeWidgetを置いてはいけない、というものでした。基本的なコードでの設定+アルファを紹介します。

// 作成・設置
this.fileTable = new QTreeWidget();
this.widget.layout().insertWidget(0, this.fileTable);
var policy = QSizePolicy.Policy(QSizePolicy.Preferred), QSizePolicy.Policy(QSizePolicy.Preferred);
this.fileTable.setSizePolicy(policy);
// 設定いろいろ
this.fileTable.headerHidden = false;
this.fileTable.rootIsDecorated = true;
this.fileTable.itemsExpandable = true;
this.fileTable.expandsOnDoubleClick = true;
// 選択モードはExtended一択
// OS提供のものとほぼ一緒。Ctrl+Click/Shift+Clickも可になる
this.fileTable.selectionMode = QAbstractItemView.SelectionMode(QAbstractItemView.ExtendedSelection);
// コンテキストメニュー&コールバック
this.fileTable.contextMenuPolicy = Qt.CustomContextMenu;
this.fileTable.customContextMenuRequested.connect(this, this.onFileContextMenuRequested);
// キーボードのフック
this.fileTable.keyPressEvent = this.fileTableKeyTriggered.bind(this);
// その他のコールバック
this.fileTable.itemSelectionChanged.connect(this, this.onFileItemSelected);
this.fileTable.itemDoubleClicked.connect(this, this.onFileItemRenameTriggered);

selectionModeをExtendedSelectionにすると、複数の要素が同時選択できるようになります。currentItemプロパティで選択されたQTreeWidgetItemを取得することも可能ですが、最後に選択されたものしか取得できないため、コード内のcurrentItemを参照している処理はすべてselectedItems()メソッド呼び出しに変える必要があります。

キーボードのフックは次のような感じで書けます。シグナルではなくてオーバーライドなので、これも自分でnewしないと使えない機能な気がします。acceptしないと、親の方にキーイベントが中継されていってしまいます。シンプルに書いてますが、実際にはselectedItems()を取得して、選択状態に応じたifとかを入れる必要があるでしょう。

MyDialog.prototype.fileTableKeyTriggered = function (event)
{
    if (event.matches(QKeySequence.Delete) || event.matches(QKeySequence.Back))
    {
        // 処理するコードをここに書く
        event.accept();
    }
};

コンテキストメニュー表示は以下のように書きます。事前にメニューオブジェクトを作っておけば、コールバックの中は座標取得して表示して終了となりますが、大抵の場合はツリー内で選択されたアイテム数や選択された項目の種類に応じてメニュー項目を増減する必要があると思うので、コールバック内で作ることになると思います。

MyDialog.prototype.onFileContextMenuRequested = function (relativePoint)
 {
    // 何も選択されていない時は何もしない
    if (this.fileTable.selectedItems().length === 0)
    {
        return;
    }
    // 表示位置取得
    var globalPoint = this.assetKeyTable.mapToGlobal(relativePoint);

    var contextMenu = new QMenu('File Menu');
    // メニューに項目を追加
    var renameAction = contextMenu.addAction(new QIcon(), qsTr("Rename"));
    renameAction.triggered.connect(this, this.onRenameTriggered);
    // セパレータ
    assetContextMenu.addSeparator();
    // 階層構造も定義できる
    var sendMenu = new QMenu(qsTr('Send To'));

    var sendFileAction = contextMenu.addAction(new QIcon(), qsTr("File"));
    sendFileAction.triggered.connect(this.onSendTriggered.bind(this, "file"));
    var sendMailAction = contextMenu.addAction(new QIcon(), qsTr("Mail"));
    sendMailAction.triggered.connect(this.onSendTriggered.bind(this, "mail"));

    contextMenu.addMenu(sendMenu);
    // 表示
    assetContextMenu.exec(globalPoint);
};

ドラッグ・アンド・ドロップ以外の細かい設定は以上のような感じです。これらを設定するだけで、ユーザの細かい操作に対応できるツリーが作成できます。

posted by @shibukawa at 08:56 | Comment(405) | TrackBack(0) | 日記 はてなブックマーク - Qt+JS: QTreeWidget (3)

2012年12月09日

Qt+JS: QTreeWidget (2)

ファイルの一覧を表示するQTreeWidgetを作ってみます。ファイルをFinder/Explorerからドロップできるようにしてみます。ファイル名と、サイズを表示します。残念ながら、QtDesignerで作ったオブジェクトでは無理で、コードでnewしたQTreeWidgetでしか動きませんでした。レイアウトツール上ではそのスペースを適当にあけてレイアウトしておきます。

こちらで解説しているような、ダイアログ表示クラスの中に埋め込むコードを前提としています。UI用のクラスをJSで作って、this.widgetにロードした後に差し込む用のコードです。

this.fileTable = new QTreeWidget();
// 挿入位置はレイアウトによって変えてください
this.widget.layout().insertWidget(0, this.fileTable);
var policy = QSizePolicy.Policy(QSizePolicy.Preferred), QSizePolicy.Policy(QSizePolicy.Preferred);
this.fileTable.setSizePolicy(policy);
// ドロップ設定
this.fileTable.acceptDrops = true;
this.fileTable.dropIndicatorShown = true;
this.fileTable.dragDropMode = QAbstractItemView.DropOnly;
this.fileTable.defaultDropAction = Qt.CopyAction;
// オーバーライドするメソッド
this.fileTable.supportedDropActions = this.supportedDropActions.bind(this);
this.fileTable.mimeTypes = this.mimeTypes.bind(this);
this.fileTable.dropMimeData = this.fileDropMimeData.bind(this);
// その他見た目の設定
this.fileTable.columnCount = 2;
var fileTableHeaderItem = this.fileTable.headerItem();
fileTableHeaderItem.setText(0, qsTr("File Name"));
fileTableHeaderItem.setText(1, qsTr("Size"));
this.fileTable.setColumnWidth(0, 200);
this.fileTable.setColumnWidth(1, 80);

オーバーライドするメソッド定義を3個ほど作っておきます。C++なら事前にQTableWidgetの子クラスを作っておかなければなりませんが、適当な場所に動的に関数を作って差し込めば動いてくれるのは動的言語ならではですね。ここでは、MyDialogクラスのメソッドにしておきます。なぜ.uiファイルから読み込んだウィジェットではダメなのかは分かっていませんが・・・

// この2つの定義は定型文
MyDialog.prototype.supportedDropActions = function ()
{
    return Qt.ActionMask;
};

MyDialog.prototype.mimeTypes = function ()
{
    return ["text/uri-list"];
};
// こちらは実際のコードに合わせてカスタマイズが必要
MyDialog.prototype.fileDropMimeData = function (item, index, data, action)
{
    var urls = data.urls();
    for (var i = 0; i < urls.length; i++)
    {
        var filepath = QDir.toNativeSeparators(urls[i].toLocalFile());
        var fileinfo = new QFileInfo(filepath);
        var treeitem = new QTreeWidgetItem(this.fileTable);
        treeitem.setText(0, fileinfo.fileName());
        treeitem.setText(1, fileinfo.size());
    }
    // これがないとFinderが受け取れなくてファイルを戻すアニメーション
    // を実行する。受け取れない形式だったらreturn falseにする。
    return true;
};

これはExplorerやFinderからファイルをドロップする処理ですが、QTreeWidget内部でのドラッグ・アンド・ドロップも実装できます。まずは設定を変更します。

this.fileTable.dragDropMode = QAbstractItemView.DragDropMode(
    QAbstractItemView.DropOnly,
    QAbstractItemView.InternalMove);

次にドロップ時のコールバックを変更します。コード全体を説明はしませんが、どちらの処理かは最後に紹介したコールバックのactionを見れば判別できます。

MyDialog.prototype.fileDropMimeData = function (item, index, data, action)
{
    if (action === Qt.MoveAction)
    {
        // 外部からドロップ
        return true;
    }
    else if (action === Qt.CopyAction)
    {
        // 内部でのドラッグ・アンド・ドロップ
        // マウスで移動した項目たち
        var movedItems = this.fileTable.selectedItems();
        this.fileTable.clearSelection();
        // 移動先はitemに格納
        var moveTargetItem = item;
        // ここで移動後に行われる処理を書く
        // 最後はreturn trueが必要です。
        return true;
    }
};
posted by @shibukawa at 09:40 | Comment(212) | TrackBack(0) | 日記 はてなブックマーク - Qt+JS: QTreeWidget (2)

2012年12月08日

Qt+JS: QTreeWidget (1)

GUIでの設計方法を3回にわたって紹介してきました。次は具体的なコンポーネントをいくつか紹介します。まずはよく使う代表的なものとして、QTreeWidgetを紹介します。

ボタンとテキストエリアだけのUIであればブラウザベースでも全然問題ありませんが、数百項目、場合によっては1000件近いツリー要素をハイパフォーマンスで動かせるのは、デスクトップアプリの利点ですよね。ブラウザベースよりも、より複雑なデータを気軽に扱うことができます。GUIツールでレイアウトをしており、treeWidgetという名前で登録されているものとします。

QTreeWidgetで項目を追加するには、QTreeWidgetItemを使います。

// ルート直下にノードを追加
var treeWidget = this.widget.treeWidget;
var item = new QTreeWidgetItem(treeWidget, 10);
item.setText(0, "Hello World");
item.setIcon(0, new QIcon(':/graphics/flower.png'));

ツリーを使って行を足す場合にこのあたりが最低限のコードになるでしょう。最初のコンストラクタでは、表示対象のQTreeWidgetを指定します。これの次の"10"という数値は、省略可能です。ノードの識別を簡単に行うために使うことができます。後から、var type = item.type(); で取得可能です。ただし変更できません。setTextとsetIconの0はカラムのIDです。これは後述します。最後の行でアイコンを設定しています。コロンから始まるパスは、Qtの画像リソースに登録したファイルを参照するための方法です。もちろん、普通のコンピュータ上の画像を指すパスも使えます。

ツリーなので階層にしないと面白くありません。この時は、QTreeWidgetItemの最初のパラメータに、QTreeWidgetの代わりに親となるノードを渡すだけです。他は一緒です。

// 孫の要素を追加
var grandChild = new QTreeWidgetItem(item, 20);
grandChild.setText(0, "Hello World");
grandChild.setIcon(0, new QIcon(':/graphics/flower.png'));

孫から子を参照するにはparent()メソッドでアクセスできます。子から親を参照しようとするとどうなるでしょうか?この場合はnullが帰ってくるので使い分けましょう。

QTreeWidgetの直下にあるノードの一覧の取得する方法は次の通りです。子供から孫へも同じようにchild(n)メソッドで取得できます。ノードはsetHidden(true)で見えなくすることも可能ですが、この方法を使えば非表示でもそうじゃなくてもすべての項目を取得できます。

var topCount = tableWidget.topLevelItemCount;
for (var i = 0; i < topCount; i++)
{
    var node = tableWidget.topLevelItem(i);
}

先ほど、識別のためのデータを設定できると紹介しましたが、変更できませんでした。変更できるデータももてます。JSONモジュールがあるとしたら、次のようなコードが書けます。

item.setData(0, Qt.UserRole, JSON.stringify({name: "Yoshiki"}));

QTreeWidgetは、名前はツリービューですが、行指向のテーブルを表示するのにも使えます。QTableWidgetはセルごとに編集ができるような高性能な部品となっています。その分セルごとにオブジェクトを設定しなければならなかったりと複雑です。こちらは行ごとにしか選択できませんが、その分、1つのQTreeWidgetItemを使えば1行すべての表示がまかなえます。。setText()を必要なカラム番号付きで呼び出せばいいだけななのでテーブルっぽい表示をするのは難しくありません。ここではコードでカラム数を変更していますが、GUIのツール上でも編集できます。

// ルート直下にノードを追加
var treeWidget = this.widget.treeWidget;
// 列が3つになる。
treeWidget.columnCount = 3;
var item = new QTreeWidgetItem(treeWidget, 10);
item.setText(0, "Hello");
item.setText(1, "World");
item.setText(2, "!!);
item.setIcon(0, new QIcon(':/graphics/flower.png'));

駆け足でしたが、かなり高性能なウィジェットであることがわかったと思います。このコンポーネントの使い方が分かるだけでも世界が大きく広がります。このウィジェットはかなり大掛かりなものなので、何回かシリーズで紹介していきます。

posted by @shibukawa at 02:47 | Comment(204) | TrackBack(0) | 日記 はてなブックマーク - Qt+JS: QTreeWidget (1)

2012年12月07日

Qt+JS: QtDesignerによるレイアウト(3)

部品を並べる時の方法として、QWidget、QFrame、QGroupBox、レイアウトといくつか選択肢があります。どのような基準で使い分けるかを紹介します。

QWidgetとレイアウト

Screen Shot 2012-12-07 at 9.08.22 AM

ツール上はレイアウトは赤枠が表示されますが、プログラム上ではどちらも見た目はほぼ変わりません。QWidgetも内部にレイアウトを持ち、レイアウトを直接置くのと同じパラメータを設定すれば結果は同じです。

レイアウトのメリットとしては、ツリー上に階層にしても、ソースコードから操作する場合は無いものとして扱われる、という点です。つまり「見た目を改善するために、入れ子にしてチェックボックスの横にラベルを追加したよ」という場合も、ソースコードを修正する必要はありません。コードに影響を与えずにちょっとした修正ができるので使えるところはレイアウトを使ったほうがいいでしょう。下記のレイアウトにはボタンが2つありますが、プログラムからは、両方共、"Parent"と書かれたWidgetの子供に見えます。

Screen Shot 2012-12-07 at 9.14.20 AM

QWidgetにもメリットはあります。QWidgetに乗っている複数の子供のウィジェットをまとめてvisible = falseしたり、enabled = falseしたり、ステータスを一括で変更する場合に便利です。

QFrameとQGroupBox

Screen Shot 2012-12-07 at 9.07.35 AM

枠です。どれも枠です。QGroupBoxはflatというオプションを入れると見た目が左下のようになります。QFrameも線の種類をいくつか選べるようになっています。基本は好みで選べばいいと思います。

ラベルを入れたい場合はQGroupBox、いらない場合はQFrame。タブの中とか枠線が多すぎてうるさそうならFlatのQGroupBoxとか、そんな感じで使ってます。FlatじゃないQGroupBoxも入れ子になったり、たくさん並ぶとちょっとうるさい感じがしますね。

たまに部品が多くて小さい画面だと厳しそうだなー、と思う時はQScrollAreaを代わりに使うこともあります。

このあたり使い分けられるようになると、「おれデザイン作っている感」が味わえるようになります。

posted by @shibukawa at 09:34 | Comment(205) | TrackBack(0) | 日記 はてなブックマーク - Qt+JS: QtDesignerによるレイアウト(3)

2012年12月06日

Qt+JS: QtDesignerによるレイアウト(2)

QtDesignerによるレイアウトで、おや?と思うケースがいくつかあったりします。

レイアウトが設定できない?

コンテナ要素は最低1個でも何かウィジェットが置かれないと、レイアウト設定が選べません。

ドラッグ&ドロップではインジケータに注目

Screen Shot 2012-12-06 at 8.21.22 AM

何層も階層になってくると、部品をぽんっと置いても、思ったのと違うところに置かれてしまうことがあります。今カーソルがある位置は次の2つの情報から読取ることができます。特に、親ウィジェットが子供を持ってなくて、小さくリサイズされてしまっている場合などに分かりにくかったりします。

  • 置こうと思っているコンテナ要素・レイアウトがハイライトされて色が変わる
  • 青いカーソルが出る(レイアウトが設定されていて、同じ階層にすでに部品がある場合)

この隙間に部品を置きたいのだけど、青いカーソルが出ない!

Screen Shot 2012-12-06 at 8.24.22 AM

例えば2つグループボックスがあって、この隙間にもう一つ入れたいとします。置きたいのは、グループボックスが置かれている親のWidgetです。ですが、操作してみると、インジケータはグループボックスにしか合ってくれず、親のWidgetには合ってくれません。

この場合は、一時的に親のWidgetのレイアウト設定をいじって、隙間を大きくします。間に入れるときはlayoutSpacingで隙間を、上下左右に差し込みたい場合は、layoutTopMarginなどのマージンを操作します。

Screen Shot 2012-12-06 at 8.24.11 AM

カーソルが出てきました!部品を置いたら、忘れずにレイアウトを戻しましょう。コンテナ系は上下左右の隙間のデフォルトが12になっているので、上下左右に入れるのはそんなに困らないのですが、Layout欄にあるシリーズは隙間がデフォルトがゼロなのでこの操作が必要になります。

Screen Shot 2012-12-06 at 8.26.22 AM

部品のコピー&ペースト

Screen Shot 2012-12-06 at 8.30.23 AM

QtDesignerで部品をコピペしようとすると、このダイアログが出ることがあります。ようするに、レイアウトが設定されているところには置くことができませんよ、ということです。この場合は、いったんBreak Layoutを選んでフリーレイアウトにしてからペーストすればOKです。ただし、レイアウトを変更すると、マージンの値などのレイアウトに設定していた値はクリアされてしまうので要注意です。

Screen Shot 2012-12-06 at 8.32.23 AM

タブ・スクロールでレイアウト設定が出ない

Screen Shot 2012-12-06 at 9.08.02 AM

Tab Widget, Scroll Areaなどのコンテナ要素を置くと、最初から、tab, tab_2, scrollAreaWidgetContentsといったオブジェクトが作られます。ツリーで見ると、あたかもこれらの子供のオブジェクトがそれぞれレイアウトを持っているように見えます。実際にそうなのですが、レイアウト設定の勝手がちょっと異なります。

Tab Widgetを例に説明します。まずは、子供のタブに孫ウィジェットを置きます。その後、親のTabWidgetを選択して、レイアウトを設定します。そうすると、現在アクティブな子どものタブのレイアウトが設定されます。選択対象(TabWidget)と、変更対象(アクティブな子供)が違うのはやや違和感がありますが、そういう仕様なので仕方がありません。

アクティブなタブページの情報も保存される

地味であまり実害は大きくないのですが、タブが5枚ぐらいおかれている設定ウィンドウがあったとします。4ページ目のタブにボタンとラベルを追加してその場で保存、アプリ起動してみると、最初のページが4ページになってしまいます。タブなどを操作した場合は、忘れずに最初のページを選択してから保存しなければなりません。

posted by @shibukawa at 09:17 | Comment(275) | TrackBack(0) | 日記 はてなブックマーク - Qt+JS: QtDesignerによるレイアウト(2)

2012年12月05日

Qt+JS: QtDesignerによるレイアウト(1)

ガンガンGUIを作っていく時には、QtDesignerを使うのが便利です。Qtの場合はソースコードを使って一からGUIを作り上げていくことも可能ですが、ビジュアルなツールを使っている方が、ちょびっとツールを勉強したUIデザイナーさん(いれば)が、文言やアイコン、レイアウトの微調整をしたりしてくれることもあります。また、ラフなプロトタイプ作成にも良いですし、全体像がツールでつかみやすいので、複数人で仕事をしていて、一つの画面を複数人が修正する可能性がある場合もビジュアルなツールがオススメです。

逆に、エンジニアしか修正しない場合、画面ごとに担当が完全にわかれている場合などはソースコードでもいいと思います。また、一部をGUI作成ツールで作って、ソースコードベースのレイアウトと組み合わせていくことも可能です。また、注意点としては、GUIツールでは提供されていない部品があったり、ツールを作っておいたウィジェットでは使えない機能が少しあったりします(protectedメソッドのオーバーライドとか)。

QtCreater、QtDesignerを起動したら、QtDesignerのFormを選択します。後はファイル名や置き場を決めて行きます。プロジェクトを作っている場合には、最後にプロジェクトへの登録、バージョン管理を使っていればそれらへのファイルの登録も行えます。

Screen Shot 2012-12-05 at 1.17.07 AM

このようなレイアウトツールが起動します。操作性は基本的にはVisual Basic / Delphi / C++Builderなどと同じですが、コードはここからは書きません。あくまでもUIを作るためのものです。

Screen Shot 2012-12-05 at 1.20.04 AM

手順としてはだいたい次のような順番で繰り返していけばいいと思います。

  • 目に見えるContainer要素と、縦横に伸びる大きな要素を入れていく
  • ボタンなどの小さいウィジェットをContainer要素の中に並べる
  • Layout設定を設定してきゅっと締めたり、伸びを調整したり
  • ウィジェット類のプロパティの設定をする

目に見えるContainer要素と、縦横に伸びる大きな要素を入れていく

Screen Shot 2012-12-05 at 7.53.20 AM

並べるときは左側のツールボックスからドラッグしてくればウインドウの絵の中に置くことができます。たまに僕の環境だとツールボックスのドラッグの挙動がおかしくなって使えなくなりますが、その場合はQtDesignerを再起動する必要があります。

QGroupBox(枠とタイトル付き)、QFrame(枠付き)、QTabWidgetが目に見えるContainer要素です。まず全体像を想像しながら、これらの要素を置いておきます。

大きな要素も、GUIの大部分を占める要素ですので、これもどんどん並べていきます。ItemWidgetsのカテゴリのコンポーネントとか、ブラウザコンポーネントとかがこれに該当します。TreeとTable、Listは2種類ずつありますが、動的言語の実装のしやすさを考えると、オススメはItem-Basedの◯◯Widgetの方です。

ボタンなどの小さいウィジェットをContainer要素の中に並べる

Screen Shot 2012-12-05 at 7.56.46 AM

次に、ボタンやラベルなどを並べていきます。右側のツリー構造も確認し、変な親子関係になっていないかも確認しましょう。また要素の名前もきちんと設定します。これらの階層や名前はコードからUI部品にアクセスする上で大切です。コード上で部品を置く箇所には、QWidget(枠線を表示したいときはQFrame)などを置いておくと良いです。

Layout設定を設定してきゅっと締めたり、伸びを調整したり

Screen Shot 2012-12-05 at 7.58.32 AM

次にレイアウトを設定していきます。Qtの場合、Container要素はすべて「レイアウト」を設定できるようになっています。レイアウトをHorizontalにすると、すべての子供の要素が水平に並びます。要素を選択して(ツリーでも部品が並んでいるビューでもどちらでも)右クリックをすると、一番したにレイアウト選択が出てきます。グリッド、縦、横などを駆使して、レイアウトを作っていきます。レイアウトは右側のツリーで見ることができます。☓じるしがなくなるまで設定していきます、次に大きさの計算をするための各種情報を入力していきます。Layoutの項目にある各種部品を使うと、レイアウトを階層化して複雑なUIを作成することができます。例えば、基本はグリッド状に要素を並べていきたいけど、特定のセルだけウィジェットを複数個並列に置きたい場合には、Horizontal Layoutを入れ子状に置いてレイアウトします。レイアウトはQtでは大事な概念なのでインターネット上でもたくさん解説ページが見つかります。

空白があいてしまって間延びするときは、Spacerを入れます。これできゅっと締まります。図の右下のバネみたいなのがSpacerです。Treeのウィジェットとか、大きいコンポーネントがないときはたいていゆるゆるなのでSpacerを入れると良いです。minimumSizeとかmaximumSizeをいじって微調整すると、リサイズしたときにおかしなことになったりするし、他の要素を足したりして変更するときに面倒になるので、これらは最終手段と考えた方がいいです。ウィジェットを数十個並べたとしても、多くても1個か2個だけ設定するだけで済むはず。それ以上設定したくなっちゃうときは、レイアウト関係のコンポーネントやそちらの設定をいじる方が正解となることが多いです。

ウィジェット類のプロパティの設定

Screen Shot 2012-12-05 at 8.18.28 AM

最後にコンポーネントのプロパティを修正していきます。値が固定のコンボボックスの要素を設定したりとか、読み込み専用フラグを立てたり・・・

これらのステップを繰り返してよりよいものにしていきます。一回ではキマらないと思いますので、何度も何度も繰り返すことになるでしょう。また、実際にコード部分を書いて動かしてみると、また調整したくなります。以下の状態がゴールです。例え固定サイズのウインドウだったとしても、可変サイズのつもりで設計しておく方が、、ちょっと部品を追加したくなったときの修正がラクですし、見た目も良くなることが多いのでオススメです。

  • コードで追加するコンポーネントを除いて、他の部品が置いてある
  • コードで追加するコンポーネント用のQWidget/QFrameを除いて、すべてのレイアウトが設定してある(右側ツリーの赤丸がない)
  • 大きなコンポーネントがなくてゆるいところにはSpacerが置いてきゅっとしてる。
  • デザイナー上でリサイズしてみてもおかしなことにならない。
  • コードから値を取得したり、イベントを設定するコンポーネントにきちんと名前をつけている
posted by @shibukawa at 08:13 | Comment(282) | TrackBack(0) | 日記 はてなブックマーク - Qt+JS: QtDesignerによるレイアウト(1)

2012年12月04日

Qt+JS: 数値を入力するには?

ビルドの仕方、Qtライブラリを組み込んだJS環境の作り方、ドキュメントの見かたまで説明してきました。そろそろ具体的なTipsに入っていこうと思います。

数値入力にはどのウィジェットを使うのがいいのでしょうか?QLineEditを使って編集されたら数値に変換するというのはどうでしょうか?フォームにQLineEditの部品が置いてあるものとしてサンプルを書きます。

this.lineEdit = this.widget.lineEdit;
this.lineEditChanged = function ()
{
    this.lineEdit.textChanged.disconnect(this.lineChangedHandler);
    var value = Number(this.lineEdit.text);
    this.lineEdit.text = value;
    this.lineEdit.textChanged.connect(this.lineChangedHandler);
}
this.lineEditChangedHanlder = this.lineEditChanged.bind(this);
this.lineEdit.textChanged.connect(this.lineChangedHandler);

イベントの中で無駄にイベントが発生しないように一度イベントを切ってから値を変更しています。バリデータを使う方法もあります。

this.lineEdit = this.widget.lineEdit;
var validator = new QRegExpValidator();
validator.regExp = new QRegExp('0|^[1-9][0-9]*$');
this.lineEdit.setValidator(validator);

もちろん、最初から特定のフォーマットのみを扱う別のウィジェットもあります。QSpinBoxです。小数を扱えるQDoubleSpinBox、時間入力のQDateTimeEditもその仲間です。

this.spinBox = this.widget.spinBox;

最後が圧倒的に短いですね。もちろん、最初からこれが使えたらこれしかないのですが、微妙な制約で最短距離を薦めなくて迂回せざるを得なくなるのがGUIプログラミングでよくあることなので、他のも存在を知っておくのも悪くないです。

QLineEditの自力方法のメリットとデメリットは:

  • ◯: どんな複雑な、条件や状態を持つようなバリデーションであっても書くことができる
  • ◯: 空白状態を実現できる(iTunesの曲名入力で、複数の曲を選択して、それぞれの曲の情報が一致しなかった時、みたいな)
  • ☓: 長い

QLineEditのバリデータ方法のメリットとデメリットは:

  • ◯: 正規表現で実現できる範囲で制御できる
  • △: 空白状態を実現できる(空白条件もバリデーションに入れる必要あり)
  • ☓: 範囲指定を正規表現で書くのが面倒
  • ☓: やや長い

専用の入力ウィジェットのメリットとデメリットは:

  • ◯: 短い
  • ◯: 値の範囲などの指定などが便利
  • ◯: 値を変更するボタンが付いている。増減量も設定可能。
  • ◯: pxなど、単位などの文字を前後(prefix, suffix)に追加することも可能。
  • ☓: 空白条件が設定できない。ドキュメントを見るとできそうだけどできなかった・・・
posted by @shibukawa at 09:04 | Comment(245) | TrackBack(0) | 日記 はてなブックマーク - Qt+JS: 数値を入力するには?

2012年12月03日

Qt+JS: ドキュメントの読み替え方

QtをJSから使う場合、C++版Qtのドキュメントを読み替えながら使うことになります。これはきっと他言語のバインディングからC++のヘビーなライブラリを使う場合(Qtだけじゃなくて、GTKとか、wxWidgetとかも)も同様かと思います。当然C++は他の言語にない特徴をいくつか持っているため、そのまま利用できないケースもあります。

QComboBoxを例にして説明します。

まず、publicなメソッドと、publicなシグナルはそのまま使えます。

// publicなメソッド
comboBox.addItem("Times New Roman");
// publicなスロット
comboBox.clear();

publicなプロパティは関数呼び出しではなくて、値を直接代入したり取得して使います。プロパティと似たような働きのシグナル等(enabledに対する、setDisabled)が提供されていることもありますが、値の取得との対称性を考えると、なるべくプロパティの方を優先して使う方が読みやすいコードになると思います。これはJSに限った話じゃなくてQt全般に言えることかも。

// 取得
var index = comboBox.currentIndex;
// 設定
comboBox.enabled = false;

publicなシグナルはこんな感じで使います。他にもメソッドがあるのかな・・・今のところ使っているのはこのあたりのみ

// 無名関数を渡す。
comboBox.editTextChanged.connect(function () {
   // 処理
});
// thisを指定して渡す。
comboBox.editTextChanged.connect(this, this.onChanged);
// bindして渡す。パラメータを任意個追加できる。
var handler = this.onChanged.bind(this, "param");
comboBox.editTextChanged.connect(handler);
// bindしたやつのみ、disconnectできる
comboBox.editTextChanged.disconnect(handler);

QStringを受け取るものはJavaScriptの文字列、QStringListを渡すものはJavaScriptの文字列配列でOKです。

comboBox.addItems(["Times New Roman", "Courier New"]);

引数違いで多重定義されているシグナルは以下のように呼び分けます。あまり美しくないけど仕方がないですね。

// int版
comboBox["currentIndexChanged(int)"].connect(handler);
// QString版
comboBox['currentIndexChanged(QString)'].connect(handler);

enum値の渡し方はちょっと特殊です。ドキュメントのMember Type Documentationと書かれたところの名前(enum QComboBox::SizeAdjustPolicy)と、表中のConstantの列の名前が大事です。enumのグループ名の関数に、定数を渡します。可変長引数の関数なので、複数個の値のORを渡したい時は、全部この関数に渡します。大体長い行になってしまいがちですが・・・

var policy = QComboBox.SizeAdjustPolicy(QComboBox.AdjustToContents);
comboBox.sizeAdjustPolicy = policy;

protectedなオーバーライドするメソッドも利用できますが、これについては別の日にpaintEventのオーバーライド例を紹介しようと思います。QtDesignerで作ったuiファイルに書かれているコンポーネントを後からメソッドだけ差し替えてもうまく動きません。

posted by @shibukawa at 09:12 | Comment(171) | TrackBack(0) | 日記 はてなブックマーク - Qt+JS: ドキュメントの読み替え方

2012年12月02日

Qt+JS: QtDesignerダイアログを表示する

前回のエントリーのQtのクラスをJSから触れるようにするで、QtのクラスがJSが使えるようになりましたので、ダイアログを表示します。Qtのクラスはすべてグローバルに置かれているので、PySideやPyQtに慣れている人は戸惑うかもしれません。

QtにはGUIをグラフィカルに設計するツールがあります。単体のツールになっているQtDesignerと、統合ツールのQtCreatorです。まあどちらもGUI作成に関しては同じです。ツールを使って.uiファイルを作成します。作成するのはQWidgetとします。スクリーンショットはめんどうなので、後回しにしますが、Root直下にボタン(QPushButton)が2個、もしくはRoot直下にレイアウトを置いてボタンが置かれているものとします。名前はdoButton, closeButtonとします。あと、こちらのCommonJSスタイルのrequireが使えるようにしてあるものとします。

// MyDialog.js
var MyDialog = function ()
{
    QDialog.call(this);

    var layout = new QGridLayout();
    var loader = new QUiLoader();
    var file = new QFile(__dirname + '/../dialogs/MyDialog.ui');
    if (file.exists())
    {
        if (file.open(QIODevice.ReadOnly))
        {
            this.widget = loader.load(file);

            // つけた名前でアクセスできる
            this.doButton = this.widget.doButton;
            this.closeButton = this.widget.closeButton;
            // こんな感じでシグナルを使う
            this.doButton.clicked.connect(this, this.onDoButtonClicked);
            this.closeButton.clicked.connect(this, this.onCloseButtonClicked);
            layout.addWidget(this.widget, null, null);

            file.close();
        }
    }

    this.setLayout(layout);
};

// 継承
MyDialog.prototype = new QDialog();

MyDialog.prototype.onDoButtonClicked = function ()
{
    // プロパティへのアクセス。代入で行けます。
    this.doButton.text = "Button Clicked";
};

MyDialog.prototype.onCloseButtonClicked = function ()
{
    this.done(QDialog.Accepted);
    // もしくは
    this.done(QDialog.Rejected);
};
exports.MyDialog = MyDialog;

このコードを使う場合は:

var MyDialog = require('MyDialog').MyDialog;
var dialog = new MyDialog();
if (dialog.exec()) // doneにAcceptedが渡されると真
{
    // Closeが押された後の処理
}

このコードはそんなに長くないですが以下の要素が含まれています:

  • Qtのクラスへのアクセス
  • Qtクラスの継承
  • フォームのロード
  • フォームの要素のウィジェットへのアクセス
  • シグナルとプロパティの利用
posted by @shibukawa at 09:04 | Comment(215) | TrackBack(0) | 日記 はてなブックマーク - Qt+JS: QtDesignerダイアログを表示する

2012年12月01日

Qt+JS: QtのクラスをJSから触れるようにする

QtではJavaScriptを使ってコードを書くこともできます。詳しくはこちら参照。ベースはC++で、QtScriptエンジン(JavaScriptエンジン)を動かします。これはPureなECMAScriptのエンジンで、追加のコンポーネントとかは一切ないのですが、C++からクラスとか関数を登録できます。QtScriptBindingGeneratorで作ったバインディングを実際に使えるようにします。

なお、現在のQtScriptBindingGeneratorはQt4系にしか対応していません。Qt5はまだダメでした。

まず、QtScriptバインディングのファイルをplugins/scriptフォルダに置きます。ビルド時にこれらのファイルもデプロイされるようにプロジェクトファイルに追加します。

folder_01.source = js
folder_01.target = .
folder_02.source = plugins
folder_02.target = .
DEPLOYMENTFOLDERS = folder_01 folder_02

これらのバインディングを実行時に使えるようにするには、もろもろの設定をする必要があります。ちょっと長いですが、基本は「アプリケーションのsetLibraryPathsで作ったバインディングを読みこませる」「QScriptEngineでimportExtensionsして、Qtのクラスを登録する」の2点です。

QDir pluginDir(QApplication::applicationDirPath());
#ifdef Q_OS_MAC
pluginDir.cdUp();
pluginDir.cd("Resources");
#endif
if (!pluginDir.cd("plugins")) {
    fprintf(stderr, "plugins folder does not exist -- did you build the bindings?\n");
    return(-1);
}
QStringList paths = app.libraryPaths();
paths << pluginDir.absolutePath();
app.setLibraryPaths(paths);

QStringList extensions;
extensions << "qt.core"
           << "qt.gui"
           << "qt.xml"
           << "qt.svg"
           << "qt.network"
           << "qt.sql"
           << "qt.opengl"
           << "qt.webkit"
           << "qt.xmlpatterns"
           << "qt.uitools";
foreach (const QString &ext, extensions) {
    QScriptValue ret = engine.importExtension(ext);
    if (ret.isError())
    {
        qDebug() << "Error occured to load extionsion: " << ret.toVariant() << ext;
    }
}

これで、QScriptEngineに渡すスクリプトファイルから、Qtの機能にアクセスすることができます。基本的にはC++のライブラリリファレンスを見てもらえばほぼそのまま使えるのですが注意点も少々あったりします。

  • 使えないクラスもたまにある。ファイルシステムをツリービューで見せるコンポーネントとかなかった。
  • QStringの代わりに素のJSの文字列が使える。
  • QStringListとかは、素のJSの配列に文字列を入れて渡せば良い。
  • 日付オブジェクトの代わりに、素のJSのDateオブジェクトが返ってくる。
  • オーバーロードされているメソッド名が変わる点に注意。
posted by @shibukawa at 11:19 | Comment(141) | TrackBack(0) | 日記 はてなブックマーク - Qt+JS: QtのクラスをJSから触れるようにする

2012年09月30日

Pythonはなぜ?str.join(seq)なのか?

Screen Shot 2012-09-29 at 10.13.21 AM

PythonのAPI設計の中で、たまに思い出したように話題が出てくるのが、配列に入った文字列を結合するメソッド。Pythonではstr.join(iterable)です。他の言語(僕がよく知っているRubyとJavaScript)はArray.join(String)となっています。どちらでもありえる話ですが、個人的にはPythonの方が自然だな、と感じていました。ですが、他の言語の方がいいという人も多く、Pythonプログラマーの中でも好き嫌いが出たりもします。せっかく、弾さんがPerlの国からやってきて適度にガソリンをまいて炎上したところなので、Pythonの歴史を紐解いてみました。

軽くjoinの歴史について語っているサイトはないか探してみる

軽くぐぐってみると、何箇所か言及しているところがありました。

まずは無料のPython教育資料のDive Into Python。1.14. Joining lists and splitting stringsの項目の中に、歴史に関するメモ(Historical Note)という欄があります。ここによると:

  • 1.6以前のPythonの文字列には便利なメソッドがなかった(実際、配列に毛が生えた程度)
  • 文字列操作はstringというモジュールをimportして行っていた
  • 2.0で文字列のメソッドが強化された時に他のモジュール関数と一緒にstring.joinも文字列のメソッドになった
  • 当時はハードコアなPythonプログラマでもこれに反対(配列に入れるべき)の人がたくさんいた

他には、Stack Over FlowのPython join, why is it string.join(list) instead of list.join(string)?というスレッドもひっかかりましたが、この中で技術的に語っているのもありました。

  • リストやタプル、ジェネレータなど、いろんなシーケンスに対応する必要がある
  • 文字列、バイト列など、いろいろな対象があり、対象ごとに処理が変わる

それに対して、それならシーケンスの共通親クラスから対象のjoinを呼べばいいだけやんか!とツッコミが入っています。まあこのツッコミでは足りないのですが、元の回答も足りないのは確かです。どちらの回答者も、逆転裁判をプレイして論理思考を訓練すべきですね。

補足

@atsuoishimotoさんより、Pythonは2.2まではCで書かれた組み込みクラスの継承はできなかったとのコメントがありました。たしかにそうだった。

これは多重ディスパッチと呼ばれる問題の1つです。2種類のクラス群が関係しています。シーケンスのクラスがn個あって、文字列の種類がm個あれば、n×m個の組み合わせがあります。大抵の場合は、nとmのうち、変化の少ない方、あるいは数が少ない方のメソッドとして実装する、あるいはまったく属さないで独立の関数として実装する方法が取られますが、まあどちらに属させても正しいかどうかは状況による、といった感じです。後者の場合はパターンマッチ(関数型言語)やオーバーロード(C++)などの言語機能とセットになりますが、Pythonなどの動的言語にはそれらの言語機能がないので、前者の実装となります。

いろいろ見てみましたが、状況が分かっただけで、「なぜPythonがこうしたのか?」の手がかりはまだ得られていません。ここは本丸を攻めるしかありません。敵はpython.orgにあり!

PEPを見てみる

続きを読む
posted by @shibukawa at 02:31 | Comment(42) | TrackBack(0) | 日記 はてなブックマーク - Pythonはなぜ?str.join(seq)なのか?

2012年09月28日

Adobe Edge Animateの今後期待できる用途

さきほどのブログエントリーはあくまでもFlashの代替と考えた場合のお話でした。Flashはあくまでも枠の中で動かす的な用途が多いのですが、Edge Animateは「枠を作る」用途で使えそうな気がします。

アプリケーションの枠を作るのに必要な機能

例えば、HTMLベースのメールアプリを作るとして、ログインフォームが出て、そこからメール一覧をとってきて、メールのリストを表示するといったケースを考えてみます。今後数年間のトレンドを先取り・・・かは分からないですが、ログインフォームの表示、認証待ち、クローズ、メールのロード、リストの表示などなど、さまざまなアクションにアニメーションをつけるといったことがしたくなるでしょう。今までのWebアプリよりももっと動的に動いて見えるアプリ。Qtなどでは作りにくいような遷移を伴ったアプリです。あるいはウィジェットが満載の(iGoogleのようなアプリ)などです。こういったものを作る上で、Edge Animateが活躍しそうです。

Edge Animateの各要素はすべてHTMLのタグになります。idが標準で付きますし、classも設定できます。テキストであればdivタグ以外もいろいろ選択できます(pタグとかh1タグとか)。現状でフォームを作成する機能はないのですが、そこはDjangoのフォーム作成などを利用して、フォームをAjaxで送ってタグの中に差し込めば、全体のレイアウトをEdge Animateで構成することが可能になります。

ドラッグ・アンド・ドロップでウィジェット(シンボルで表現)を整列するような機能をEdge Animateで作りこんで、それをアプリケーションのテンプレートとして配布すれば、誰でも似たような高機能なウェブサイトが作れるようになりそうです。JavaScriptの世界はどちらかというと「部品」がライブラリだったのですが、ウインドウマネージャのような仕組みをライブラリとして作って配布、みたいな感じですね。

また、シンボル単位でエクスポートすることもできるので、JavaScriptでいろいろ作りこんでそれ単位で配布することが可能です。jQueryも使えるし、ツリービューアーとかそういう部品として配布できそうです。jQueryのプラグインの場合、関数として定義して使う形式になりますが、Edge Animateでは、よりコンポーネントに近い形式で作り込んで配布が可能です。

今後増えて欲しい機能

より高度なCSSが設定できるようになればいいな、と思います。ボーダーとかグラデーションとか、もっと自由に設定できるようになると良さそうです。まぁ、CSSでいろいろやりすぎると今のモバイルのブラウザのパフォーマンスが良くないということであえて入れてないのかもしれませんが。現在は各要素がデザインを持っていて、CSSのようにclassでデザイン指定がないのですが、そのあたりもできるようになると良いかな、と。特にAjaxでフォームを差し込んだりするのであれば、フォームのデザイン機能は欲しいですよね。グローバルなJavaScriptのファイルのロードとかも今はどこでやればいいのか見当たらないです。ウインドウシステムのように使うのであれば、9パッチのようなものが簡単に作れると捗りそうです。

後はエクスポートするときにCSSスプライトが生成されるよ!とかになると面白そうです。

とは言え、現状でも、進撃のバハムートやファイナルファンタジーブリゲード的なミッション画面や合成画面っぽいUI(画像でウインドウとかボタンを作る)とか、カードゲームの管理画面とかを作りこむには十分な機能を持っていますし、ソーシャルゲーム的な世界から積極的に活用されるようになる可能性もありそうな気がします。

なんにせよ、いろいろ用途が広がりそうですし、将来が楽しみなツールに思えました。

posted by @shibukawa at 00:50 | Comment(28) | TrackBack(0) | 日記 はてなブックマーク - Adobe Edge Animateの今後期待できる用途

2012年09月27日

Adobe Edge Animate 1.0を試してみた

Screen Shot 2012-09-26 at 7.19.42 PM

HTML5は、HTML5という名前のくせに98%ほどはJavaScriptという、詐欺っぽい名前でありながら一世を風靡したとおもいきや、FacebookのCEOの発言で一悶着あったりと、まだまだいろいろな話題を提供し続けてくれています。個人的にはGPUサポートやスマートフォンの高速化などがこなれてくれば、さまざまな用途で活用できるようになるだろうな、と思っています。HTMLのレイアウトがGPGPUとかで行われるようになれば、CPUでレイアウトを処理している今時のGUIツールキットよりも高速に動くようになる時代が来てもおかしくないですしね。今はまだまだHTML 5に投資し続ける時ですよ!バカ発見器のTwitterのTLになんとなく踊らされた人たちが寝転んでいる今がチャンスです。

そんな中、アドビ社がAdobe Edge Animate 1.0を無償提供することを発表しました。Flash四天王のうちの最弱なモバイル端末のFlash Playerはやられてしまいましたが、なんだかんだでFlashとAdobeの影響力はまだまだ強いな、と感じています。ゲームエンジンとしてしのぎを削っているUnreal EngineとUnity 3DはそれぞれFlash向けのエクスポータを開発中です。10年近くパフォーマンスチューニングされ続けたFlashは、マルチプラットフォームなブラウザ用のVMとして強力ということでしょう。AAAタイトルのゲームのエンディングのロールを見ると、だいたいScaleFormという文字が出てきます。これはゲームコンソールやモバイル端末用の特殊なFlashプレーヤーです。インタラクティブなメニューやちょっとしたカットシーンはFlashが活用されているということです。AIRもスマートフォン向けのアプリ開発環境として変換ツールが開発されていますし、インタラクティブなアート作成環境として、一定数のスキルをもったデザイナーさんが確保できるツールはFlash以外は見当たりません。

HTML5、とりわけCSS3がFlashキラーといっても、デザイナーさんがFlashのように自在に使えるツールの決定版は今まではありませんでした。Sencha Animatorとかはありましたが・・・本とかもFlashほどは見かけないですよね?僕自身、Adobeのツールの中で一番使い方が分かっているのがFlashなので(5, MX, CS5と来て、ほぼマクロメディア時代の知識ですし、教わったわけではないので勘違いしているところもあると思いますが)、数時間触ってみて分かる範囲でFlashとの比較をしてみようと思います。作った絵にセンスがなくてすんません。

Edge Animate 1.0とFlashで大きく変わったところ

続きを読む
posted by @shibukawa at 16:13 | Comment(88) | TrackBack(0) | 日記 はてなブックマーク - Adobe Edge Animate 1.0を試してみた
検索ボックス

Twitter

www.flickr.com
This is a Flickr badge showing public photos and videos from shibukawa.yoshiki. Make your own badge here.
<< 2019年02月 >>
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28    
最近の記事
カテゴリ
過去ログ
Powered by さくらのブログ