Linux ZAURUSプログラミング
更新

目次:


2003/07/12 はじめに

LINUXザウルスを入手して約一年、その間のプログラミングの中心はJavaでした。
一度プログラムを作ってしまえば、PCだろうが、MIザウルスだろうが、SLザウルスだろうが動いてしまうJavaは、非常に魅力的な言語でした。
しかしながら、ゲームなどを作っているうちは特に不満は無かったものの、 今年に入って初のアプリケーションソフト FreeNoteを作りはじめてからというもの、 Virtual Machineであるがゆえの応答の遅さや、サポートの遅さに歯がゆい思いをすることが多くなりました。
もう、自分の満足のいくアプリケーションを作るには、ネイティブの環境で作るしかない…と考え、初のLinuxプログラミングにチャレンジすることにしました。

私は今まで、WindowsとJava中心にプログラムを作成していましたので、Linuxでのプログラミングは全くの素人です。
良い機会ですので、プログラミング環境の構築を含めて、開発の経緯を日記形式で綴っていこうと思います。
#追々Tips等はまとめていくつもりです。

目標は、FreeNoteのQt版を作成することとします。
現在多忙のため、どれだけのパワーが割けるか、心配ではありますが、長い目で見守っていただければと思います。
[TOPへ]


2003/07/12 開発環境の選定

Qtアプリケーションを作るためにまずは、開発環境を選定することにします。
私は、とてもヘタレなので、できれば、Windows上のIDEで開発したいと思い、
CSIDE for Linux Zaurus Developerを採用することにしました。
幸いにも、20日間フル機能を使える評価版が提供されており、前もって資料請求をしていたのです。
#資料請求によって、評価版が入手できます。
[TOPへ]

2003/07/12 開発環境の構築

CSIDEの評価版を入手したため、早速環境構築をすることにしました。
評価版のCDをドライブに入れると、インストールメニューが立ち上がるので、手順通りにインストール。
全部終わったところで、そのままサンプルを作って実行〜!。

…失敗〜 (T-T)

なぜか、ビルドがうまくいかない。

マニュアルを見ろ小僧!ということで、CSIDE for Linux Zaurus Developerのインストール方法を良く読む事にした…。

えーと、なになに…。
CSIDE for Linux Zaurus上からコンパイルするためには、「Default Text File Type 」の設定を必ず「DOS」に設定してください。

…! UNiXにしてたじゃん…。_| ̄|●

仕方ないので、全環境をアンインストールして再挑戦。
#ちなみに、cygwinのアンインストールは、インストールディレクトリ毎削除し、レジストリでcygwinのキーを持っている場所を削りました。

さて、再インストールが終わった後に、サンプルプログラムを作成して、ビルドすると…。
おお、ちゃんとビルド出来るではないか。
さっそく、SL-A300にデバッグサービス(csideserver)をインストールしてデバッグ開始。
何の問題もなく、SL-A300でサンプルアプリケーションが実行された。

すげぇ、すげぇぞ! CSIDE!!

この時点で、かなり満足。もうちょいと評価して、問題なければ、是非とも購入にあたりたい。
[TOPへ]


2003/07/12 イベントの実装

GUIアプリの実装には、イベントの仕組みの理解が必要です。
Javaでは、イベントのリスナーを部品に追加してやることでイベントを実装できますが、Qtではどうなのだろう?
もう夜だし、私はADSLも通わぬど田舎に住んでいますので、今からQtプログラミングの本など購入には行けません。(T-T)

こんなときは、先人に学べということで、とりあえず、CSIDEが自動生成するプログラムを参考にしてみることにします。
すると、コンストラクタに、

connect(quit, SIGNAL(clicked()), this, SLOT(goodBye()));

との記述を見つけました。
ソースを見るとgoodBye()がイベントの中身でしたので、これが、イベントの指定だとわかりました。
どうやら、SIGNALでイベントを指定し、SLOTで処理を紐付けるようです。

ちなみに、どのシグナルが用意されているかは、Qt DesignerF3を押した後+カーソルで部品を選択すると参照することができます。
あと、ヘッダーファイルを直接見ても良いです。signals:という宣言部があるので、そこがシグナルらしいです。

[TOPへ]


2003/07/12 マウスイベントの実装

FreeNoteを作るには、何はなくともマウスイベントが必要です。
Javaでは、MouseAdapterやMouseMotionAdapter等を追加するのですが、Qtではどうするのでしょう?

多分、シグナルが用意されているはず…。とWindow(Widget)のシグナルを探しても無い。

なんでかな?

ものは試しに、mouseMoved等のキーワードでヘッダファイルをgrepしてみると…WidgetのベースクラスであるQWidgetは、mouseMoved等のマウス系イベントを処理するメソッドを持っていることがわかりました。

あ、なるほど。

マウスイベントはインタフェースが既に用意されているので、上書きして使うってことか!

ということで、
void mousePressEvent(QMouseEvent *);
void mouseMoveEvent(QMouseEvent *);
void mouseReleaseEvent(QMouseEvent *);
を追加してやると、めでたくマウスの動きを追えるようになりました。

ちなみに反応ですが、ネイティブだけあってすさまじく良いです。

てなわけで、Qt版FreeNoteには、かなり期待できそうです。

[TOPへ]


2003/07/13 ダブルバッファ

ちらつき無く描画するためには、ダブルバッファの実装が必要です。
Javaでは、Imageオブジェクトに書き込みを行い、 paintイベント内でスクリーンにがばっと上書き(drawImage)するとちらつき無く表示できます。
Qtでは、QPixmapというクラスを使って描画バッファを作成し、 QPainterクラスを経由して描画を行います。

プログラムでは、こんな感じです。
px,py:描画の開始ポイント
pix:QPixmapオブジェクト:QPixmap* pix = new QPixmap(画面サイズ);

QPainter p; p.begin(pix); QPen pen(black, 1); p.setPen(pen); p.drawLine(px, py, evt->x(), evt->y()); p.end();

これをpaintイベントでスクリーンに上書きします。
プログラムでは、こんな感じです。

void FrmMain::paintEvent(QPaintEvent* evt) { if (NULL != pix) { bitBlt(this, 0, 0, pix); } }

これで、FreeNoteの基礎となる部分は完成しました。

汚くて恥ずかしいのですが、一応、ソースを公開します。
興味のある方はご覧下さい。(以外と簡単ですよ)
[TOPへ]


2003/07/17 C760のパフォーマンス問題

前回、FreeNoteの基礎部分のソースを公開したものの、速度を調べてみると、A300での速度は申し分ないが、C760での速度はいまいちな感じ。 (それでも、Javaに比べれば全然ましなんだけど…)
どうやら、描画に手間を取られて、取りこぼしが生じているっぽいです。

QTのリファレンスを見てみると、QWidgetよりもQCanvasの方が高速に描画するとのことなので、 実際の実装は、QCanvasの方にするべきだと思うのだけど、QCanvasの使い方がいまいち分かってない。
うまくすると、FreeNoteのコアなロジックを殆ど書かなくても良いような感じなのだけど…。

とはいえ、実際は、書き込む領域の大きさをQVGA指定した場合は、C760でのパフォーマンスはVGAでも問題ないことまでは分かっており、遅い原因がQPixmapにあるのかQWidgetにあるのかはまだ分かっていません。
これがどちらの原因かは、今後(必要があれば)追求してみたいと思います。

この一週間ほど、仕事が忙しすぎて、正直全くコーディングの時間がとれないこともあり、クラスの使い方なども深く調べられないところもあるけれども、 りなざうには、GPLによる優れたソースが多く公開されているので、そのへんを参考にしながら、少しずつでも進捗させたいと思っています。

とりあえずは、次の3連休でどれだけ時間が取れるかがミソですな。
[TOPへ]


2003/07/18 スクロールバーを使ってみる

レスポンスのチューニングはおいおいやるとして、とりあえずは、FreeNoteに必須の機能であるスクロールバーの実装を試してみることにしました。
Qtでスクロールバーを扱うには、QScrollBarを親Widgetに追加するか、QScrollView(もしくはそのサブクラス)を利用するからしいのですが、 先日からQCanvasの利用を検討中ということもあり、QScrollViewのサブクラスであるQCanvasViewを使うことにしました。

QCanvasを親Widgetに追加するには、以下のようにします。

QCanvas* canvas; QCanvasView cv = new QCanvasView(canvas = new QCanvas(240, 320), this);

このままだと、QCanvasViewにスクロールバーが表示されないので、setVScrollBarModeとsetHScrollBarModeをAlwaysOnに設定します。

cv->setVScrollBarMode(QScrollView::AlwaysOn); cv->setHScrollBarMode(QScrollView::AlwaysOn);
[TOPへ]

2003/07/18 楽々サイズ調整

さて、QCanvasViewの用意が整い、これで、FreeNoteの下地となる外観ができあがるぞ!と、実行させてみたところ…

なんと、QCanvasViewが左上に小さく表示された状態のままでわないかっ! Σ(゜д゜) ハゥッ

そういえば、GUI部品って追加したあと、サイズを調整する必要があったわいな〜。
いくつかのGPLソースには、setGeometryでサイズを調整しているものがあったのですが、 SL-C760などの画面の大きさやレイアウトが勝手に切り替わる環境を踏まえて、部品の配置は、レイアウトマネージャで行うこととしました。

FreeNoteはツールバーとViewPortしかないので、レイアウトは、縦に並べるQVBoxLayoutにすることにしました。

メインウィンドウに追加したQCanvasView(cv)をレイアウトするコードは以下のようになります。

QBoxLayout * l = new QVBoxLayout(this); l->addWidget(cv); さて、これで、きちんとレイアウトされた表示が可能となるわいな〜。

と、勇んで実行!!!

…あれ? うまく配置されない?(^^;

どうもレイアウトが効かない様子…(--;

原因究明のために、色んなソースコードを見て回っていると、あるサンプルソースに以下の記述が…

l->activate();

えー、レイアウトマネージャは、activateしないとならべてくれないんですか、そうですか。

紆余曲折あったものの、これで、どんなサイズでも調整できる画面なるものが出来上がりました。
[TOPへ]


2003/07/18 QCanvasを使ってみる その1

さて、いよいよ描画領域の調整に入ります。
ここで使用するQCanvasの特徴は…
(1).Line, Polygon, Text, Spriteなどの部品(QCanvasItem)の描画を管理。
(2).標準でダブルバッファ処理を行う。
(3).QWidgetよりも高速。
です。

FreeNoteは、PolyLineの集合体なので、(おそらく)QCanvasPolygonのサブクラスを実装することになると思いますが、 Java版で独自に描画情報を管理していた部分をQCanvasに委託できるのであれば、更に効率よくプログラミングできそうです。
また、座標管理もキャンバス内で行えるため、Java版のように、今、スクロールバーがどの値なので、 原点からnドットずらして表示なんていう七面倒臭い処理はしなくて済みそう。

うーん、ス・テ・キですね。

ただ、ちょっと気がかりなのは、
(1).描画の途中の軌跡をどう管理するか?
(2).QCanvasをリサイズした際のメモリコストは、どれくらいか?
ということです。
FreeNoteは、際限なくどこまでも描ける感覚が重要なため、例えば、10000 x 10000のキャンバスだと全く動きません!
という話になると、結構厳しい…

このまま、楽々プログラミングできるのか、それとも、Java版同様、ちょっと複雑な処理が必要となるのかは、 この辺の動作次第といったところでしょうか?

何はともあれ、使い方次第では、非常に面白くなることが予測されるQCanvasは、今後の開発でもいろいろと活躍してくれそうな雰囲気です。
#空想が膨らみます。

[TOPへ]


2003/07/18 QCanvasを使ってみる その2

QCanvasを使ってみる その1で、気がかりだ。と言っていた件について実験君 & 確認してみました。

(1).描画の途中の軌跡をどう管理するか?
QWidgetであれば、QPainterクラスを使用して、そのままダブルバッファに描画するだけです。
QPainterを利用するには、QPaintDeviceのサブクラスである必要がありますが、QCanvasは残念ながら、そうではありません。

要は、QCanvasItemのサブクラスで表現するほか無いようです。

実験君をしているうちに、QCanvasに追加されたQCanvasItemが、QVanvas外で解放された場合、QCanvasの管理からも外される(ように見える)事に気がつきました。

この性質を利用して、軌跡は以下の様に処理することにしました。
1.ペンが動いている(mouseMoved)うちは、QCanvasLineを追加する。

2.追加したQCanvasLineはQCanvas外で管理(QList)する。

3.ペンが離れたら(mouseReleased)今まで追加したQCanvasLineをQCanavasPolygonのサブクラス(自作)に変換。

4.(3)のオブジェクトをQCanvasに追加。

5.管理している軌跡をクリア。
 #あらかじめ、QList::setAutoDelete(true)を設定しておくことで、自動的にメモリから解放する。

これにより、C760でも追従性が落ちることなく、描画可能となりました。
#参考ソースを眺めているうちに気づいたのですが、QWidgetに描画しているにもかかわらず反応が良いソフトもあります。
#遅いのは、私の実装に問題があったという事みたいです。(T-T)

(2).QCanvasをリサイズした際のメモリコストは、どれくらいか?
最初、私は、QCanvasは空間の定義だけ管理しており、サイズを大きくしてもメモリを食わないのではないか?と考えていました。
が、どうやらそうではないようです。

確かに、6400*4800位は余裕で持てますが、12800*9600くらいになるとさすがにパフォーマンスが落ちてきます。
Qtのスクロールバーの対応範囲は、100000ポイントなので、できれば限界近くまで行って欲しかったのですが、 このあたりが限界みたいです。

また、6400*4800でもVGAの100画面分…、必要十分な広さでは?とも思うのですが、C700のようにメモリ不足が懸念されるマシンでも、 快適に動かす為には、なるべくメモリの消費は抑えたいところです。

以上の事から、Qtのステキな機能を利用して楽々コーディングする野望は、ちょっとあきらめて、 スクロールに関しては自分で制御することに決めました。


[TOPへ]


2003/07/21 途中で反応が落ちる?

前回まで、FreeNoteの描画部分は、QCnavasを利用する事にしていました。
これは、QCanvasがQCanvasItemという描画アイテムを管理し、ダブルバッファやらなんやらを制御してくれていたためで、 管理するのが楽そうなのと、QWidgetよりもQCanvasの描画が速いということからでした。

ところが、QCanvasを利用したFreeNoteで描画を続けていくと、 描き進めるうちにだんだんとペンの反応が鈍くなる現象が見られるようになりました。(T-T)

結局、QCanvasを使うのは、使用感からして無理と判断し、QWidgetで再実装することにしました。

ところで以前、QWidgetでの描画は反応が無理っぽいという話をしましたが、実は、 実装がタコだったことが判明しました。

先日、Qtプログラミング入門を入手したのですが、これには、簡単なお絵かきアプリの例が出ています。

これは、ダブルバッファも含めていろいろ役に立つサンプルなのですが、この通りに実装したところ、なんと、 SL-C760でも全然反応が落ちない!

わたしは、ダブルバッファに描画をし、repaintで表示させるというロジックを組んでいましたが、 このサンプルでは、ダブルバッファとQWidgetに両方描き込むことによって、なるべくrepaintをさせない。

というものでした。

repaintのコストは結構なものなので、なるべくrepaintしない方が反応がよいということですね。

[TOPへ]


2003/07/30 自動スクロール機能の実装

FreeNoteの特徴的な機能の一つに、自動スクロールというものがあります。
これは、最後のペン動作から800msの間入力が無かった場合、ペンの最終位置をチェックして、自動的に(スクロールして)描く場所を確保する。 という機能です。

Java版のFreeNoteは、描画を一定間隔で行っていたため、再描画の発生するタイミングでスクロールさせていたのですが、 Qt版は、リアルタイムで描画しているため、この仕組みは利用できません。

Windowsプログラミング等で言うところのTimerイベントって無いのでしょうか?

…と思い探してみると、やっぱりありました。
そのクラスは、QTimerです。

使い方は、以下のようになります。

QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(timerDone())); timer->start(3000, true); // 3秒のシングルショットタイマー timer->start()を実行してから、3秒後にtimerDone関数が1回だけ起動されます。
#ちなみに、timer->startの第2引数をfalseにすると繰り返し実行となります。

さて、シングルショットタイマーでタイマーイベントを起動すると、指定時間後にきっかり実行されてしまうわけですが、 FreeNoteのように「ペン動作から800msの間入力が無かった場合」というのは、どうやって実装するのでしょうか?

これは、動作中のtimerを止めることができれば良いので、QTimer::stop()メソッドを使用します
これにより、タイマーの実行が中断されます。

実際のところ、Qt版FreeNoteでは、mouseReleaseイベントでタイマーを起動し、mousePressedイベントで、タイマーを停止させています。
こうすることで、自動スクロール機能の起動を実現しています。


[TOPへ]