はじめに
これまで4回に渡ってQtの基本部分について説明して来ました。今回と次回は、Qt WebKitでのブラウザ機能の利用方法、そして、ブラウザ機能とQtのウィジェットを混在して利用する方法について説明します。前回の後に来年1月にリリース予定のQt 4.5のTechnology Previewが2008/10/21にリリースされているので、これについても触れておきます。
Qt 4.5 Technology Preview
Technology Previewには、Qt 4.5で予定されている改善や機能がすべて盛り込まれてはいませんが、いくつかを実際に確認できます。ただし、改善点や新しい機能の評価用というのがTechnology Previewのリリース目的なため、落ちてしまったり今まで通りには動かないことがあります。
パフォーマンス改善
Qt 4.4まででは、2D描画機能のバックエンドの描画エンジンとして、それぞれのプラットフォームの描画機能とOpenGL を利用できます。何もしなければ各プラットフォームの描画機能が使われ、以下のようにすればOpenGLを2D描画に使って描画速度を改善できるようになっています。
- Graphics View
setViewpor(QGLWidget(QGLFormat(QGL::SampleBuffers))) を呼び出す。
- ウィジェット
QGLWidget(QGLFormat(QGL::SampleBuffers)) を親ウィジェットにする。
描画精度や描画速度を上げようとすると3つの問題があります。
- プラットフォーム固有の描画機能には、描画上で細かな差異があります。そこで、プラットフォームに依存せずに正確な描画をしたい場合には、QImageに描画をしてからQImageを表示するという方法を取ることとなります。しかし、一般にQImageがクライアントアプリケーション中に存在するデータということから、プラットフォームによっては描画コストが高くなります。
- OpenGLを描画エンジンにすると速度は改善されますが描画精度が落ちます。正確な描画のためには各プラットフォームの描画エンジンを用いるか、1)の方法を取ることとなります。
- OpenGLを描画エンジンに使用するには、僅かな修正ですがすべてのウィジェットのベースクラスを変更しなければなりません。アプリケーション全体で簡単にOpenGLを描画エンジンに切換える方法はありません。
Qt 4.5 Technology Previewではこれらの問題への解決が試みられています。ひとつは、ラスター(つまりQImageを使用した描画と表示)とOpenGLをアプリケーションの描画エンジンとして使用できるようにする機能です。
使い方は簡単で、上記のようにアプリケーション起動時のパラメータに指定するだけで、アプリケーションのソースコードの変更は要りません。Qt Labs Blogsに掲載されている描画エンジンのベンチマーク結果は以下のようになっています。
表1 2D描画バックエンドのベンチマーク(単位:Fps)
描画エンジン | Windows | X11 | Mac OS X |
プラットフォームの描画機能 | 60 | 20 | 9 |
ラスター | 60 | 36 | 30 |
OpenGL | 245 | 92 | 215 |
※出典:Qt Labs Blogs So Long and Thanks for the Blit!
一般に、QPixmapの実装には描画速度に適した画像リソースを使い、QImageはアプリケーション空間のデータに持ってラスター操作に適したデータとして扱うようにしていますが、ラスター描画エンジンのベンチマーク結果に違いがあります。
Windowsでラスター描画エンジンの効果がないのは、Windowsでは元々ラスター描画をしているためです。実際、WindowsでのQPixmapの実装はラスターとなっていて、QImageを内部に抱えているような構造になっています。
X11では、QPixmapはX Window Systemのサーバ側のリソースPixmapで、QImageはラスターデータXImageとなっているので、単にラスター描画をするだけではQImageをサーバ側に転送するためかなりのボトルネックになります。Qt 4.5 Technology Preview では、X Shared Memory Extension(MIT-SHM)を使い、同一ホスト内のアプリケーションであれば、サーバへの転送を避けるようにしています。MIT-SHMを利用するようになったので、QPixmapとQPixmapの相互変換も改善されるはずで、計測してみると次のようになりました。
表2 X11でのQPixmapとQImageの変換処理のベンチマーク(単位 ms)
| 1024x1024 | 2048x2048 |
| 4.4.3 | 4.5 TP1 | 4.4.3 | 4.5 TP1 |
QPixmap::toImage() | 9286 | 0 | 26685 | 0 |
QPixmap::fromImage() | 9185 | 814 | 31185 | 3450 |
かなりの改善結果です。画像のスケールをスライダで変えて表示するコードを書いて、サイズが3000×3000の画像で、X11でラスター描画エンジンを試してみると、OpenGLを描画エンジンにしたのと同じように滑らかにスケールが変わるようになっています。
描画速度は改善されるのですが、問題はあります。ラスター描画エンジンを使用して、イメージを表示しようとして読み込むと、ボタンのテキストが文字化けをしたりします。また、OpenGLを描画エンジンにする機能は、4.5ではまだ実験的なレベルとなるそうです。OpenGLを使用しているプログラムに対して、グラフィックスシステムにOpenGLを指定すると、OpenGLで描画する図形がずれて表示されたりするなどの問題がありました。一方、ラスター描画エンジンは全プラットフォームでフルサポートされます。
WebKitの改善
第1回で挙げた機能やキャッシュが実装されていて、YouTubeなどで動画の再生がデスクトップ版のQtだけではなくWindows CEでもできるようになっています。
その他
ざっと見ただけですが、以下のような追加がされていました。
- Qt/Mac OS XのCocoaブランチの取り込み。
- QtTestモジュールへのベンチマーク機能の追加。
- QtXmlPatternsモジュールでのXSL-Tサポート。
- ODF (Open Document Format) のサポート。
- Qt Designerの操作性改善
- Qt/Mac OS Xでのバンドル作成支援ツールmacdeployの追加。
- リファレンスマニュアルへのQt for Windows CEについての記述の追加。
Qt WebKitの動かし方
Qt WebKitを動かすには3通りの方法があります。
(1) Qt 4.4.3のQt WebKit
現時点での正式リリース版です。Qtにはリリース時のWebKitのソースコードが含まれています。Qt 4.4.3では、Qt WebKitでYouTubeなどの動画再生はまだできません。
Qt 4.5向けのQt WebKitの開発中のソースコードは、開発中のWebKitに含まれています。Phononをバックエンドとして、Qt WebKitでFlashビデオの再生ができます。Qt本体は4.4.3なので、最新のQt WebKitを安定して動くQtで試せます。
(3) Qt 4.5 Technology Preview
Qt本体がQt 4.5の開発版となる他は(2)と同様です。
Qt WebKitのビルド方法
Qt 4.4.3やQt 4.5 Technology Previewには、各Qtで使えるWebKitが同梱されているので、Qtをインストールすれば、Qt WebKitが使えます。
Qt 4.4.3とオープンソースWebKitを用いる場合には、まず、Qt 4.4.3をインストールしておき、次にWebKitをビルドします。
1.SVNリポジトリからWebKitを引出します。1Gバイト以上あります。
2.ビルド環境の設定
オプション--releaseまたは--debugを指定してビルドモードを設定し、環境変数QTDIRを設定します。
3.ビルド
ビルド方法は以下のようにします。WebKit/WebKitBuild/Releaseのlibやbinにライブラリやサンプルブラウザが作成されます。
4.実行
最小限のブラウザ機能を実装したWebKit/qt/QtLaucherもビルドされ、WebKit/WebKitBuild/Release/binの下にでき、以下のようにすれば動かせます。
Qt WebKitの構成
Qt WebKitは、表3に挙げたクラスから構成されていて、QtからWebKitの種々の機能をアクセスできるようにしています。
表3 Qt WebKitのクラス一覧
クラス | 機能 |
QWebFrame | Webページ内のフレーム |
QWebHistory | QWebPageのヒストリー |
QWebHistoryInterface | リンクヒストリーの実装とのインターフェース |
QWebHistoryItem | QWebPageのヒストリー項目 |
QWebHitTestResult | Webページのヒットテスト結果 |
QWebPage | Webドキュメントオブジェクト |
QWebPluginFactory | Webページに組込むプラグイン生成 |
QWebSettings | QWebPageとQWebFrameの設定内容の格納 |
QWebView | Webドキュメントの表示と編集をするウィジェット |
図2に、これらのクラスの関連を示します。QWebPageはWebのページに対応し、ページ内のフレームはQWebFrameで構成されています。実際に目に見える表示をするのはQWebViewで、QWebPage内のページがQWebViewに表示されます。
Webページの表示
リスト1は、コマンドラインで指定したURLを開いて表示するプログラムです。実行は以下のようにします。
QWebViewのインスタンスを生成し、URLを指定してロードしているだけです。図3は、その実行結果です。右ボタンをクリックして表示されるコンテキストメニューによって、ページを行き来するナビゲーションもできます。
QWebView::load() の実装コードでは、表示対象のQWebPageオブジェクトのインスタンスのメインフレームのQWebFrame::load() にURLを指定して呼出しています。
Qt 4.5 Technology PreviewかWebKitを用いれば、リスト2の行番号14~15のコードを付け加えるだけで、http://jp.youtube.comをコマンドラインで指定すれば、YouTubeの動画も再生できるようになります。
QWebSettingsにはフォントのファミリーやサイズ設定、イメージ読み込みの可否やJavaScriptの有効無効などの設定が格納され、QWebPageとQWebFrameでその設定値が使われます。
Webブラウザらしくする
いくつか機能を追加してWebブラウザらしくすると、図4のようになります。本格的なブラウザ機能を実装するには、Qt Demo Browser(ソースコードはQt配布コード中のdemos/browser/)のように1万行以上のコード記述が必要です。この簡単なブラウザには辿れないリンクがあるなど不備はありますが、3ファイルで合わせて120行程度でブラウザらしいプログラムができてしまいます(リスト3~5)。
QMainWindowを用いて、メニューバー、ツールバー、ステータスバーを持つ一般的なアプリケーションを作ります。
QWebViewのインスタンスを処理の中心となるウィジェットとして設定しています。
ステータスバーにロード状況を表示するためにプログレスバーを埋め込みます。この他に、ステータスバーには、ポインタの下にリンクがあればそのURLも表示しします。
URLの入力欄です。横に広がりやすくするようにサイズポリシーを設定し、リターンキーが押されたらスロットchangeLocation()を呼出します。
ナビゲーションのためのツールバーを作成しています。QActionのインスタンスは、アイコンも含めてQWebViewが用意しているのでとても簡単です。このようなコードだけで、ナビゲーションアイコンが付いたツールバーが作れてしまいます。
QWebViewは、操作やWebページの表示状況に対応して、適切なシグナルを数多く用意しているので、必要な処理をするスロットをシグナルと接続します。setWindowTitle()はQWidgetのスロットで、QWebViewのタイトルが変わったならば、それを表示するようにしています。
URLが入力されてリターンキーが押されると呼出されるスロットです。入力値の頭と末尾の空白を取り除くようにしています。ロード後にQWebViewにフォーカスを設定して、表示領域にキー入力を移しています。
リンクを辿って表示した場合に、表示されたページのURLをURL入力欄に表示しています。
ポインタがリンクの上にあるときにそのURLをステータスバーに表示し、ポインタがリンクの外に出たらステータスバーを消去しています。
QMainWindowを継承してカスタマイズしたMainWindowのインスタンスを生成し表示しています。
Webページのレンダリング結果を画像で保存する
GUI機能を用いずQt WebKitの機能のみでWebベースのユーティリティプログラムを作成してみましょう。表3に挙げたQt WebKit関連のクラスの中で、ウィジェットを継承するクラスはQWebViewのみとなっています。Webページを構成するWebフレームのQFrame::load(const QUrl&)でURLを読み込めるようになっていて、QFrame::render()は引数にQPainterを取るようになっていることに注目しましょう。つまり、ウィジェットがなくともWebアクセスをしてHTMLをレンダリングできるのです。
QWebPageのリファレンスマニュアル に、URLを指定してレンダリングをし、その結果を画像ファイルとして保存するコードが載っています。このコードを元にして次のようにして使うコマンドを作りましょう。
このコマンドを実行すると指定したURLのWebページをレンダリングし、その結果が画像ファイルとしてthumbnail.pngに保存されます。デフォルトのスケールは1.0で、0.5ならば1/2に縮小され、2.0ならば倍に拡大されます。
リスト6が実装コードです。行を追って説明します。
Webページへのアクセスは、ネットワークを含めてQtのイベントループの中で行われ、シグナルによって処理状況が通知されるので、QObjectを継承したクラスを用いています。
新たに追加したシグナルでWebページをロードし、画像ファイルとして保存が完了したときに送信されます。
QWebPageでのロードが完了したときに呼び出す処理スロットです。
Webページpageのメインフレームに指定されたURLを渡してロードしています。レンダリング終了はQWebPageのシグナルloadFinished(bool) でわかるので、このシグナルをスロットrender()に接続してすれば、ロードしたWebページを扱えます。
QWebPageのビューポート(表示領域)のサイズを、QWebPageのメインフレーム(最外部のフレーム)のサイズと同じにします。
ラスターイメージデータQImageをQWebPageのビューポートと同じサイズで用意します。
Qtの2D描画機能を使うには、描画対象オブジェクトに対してQPainterのインスタンスを作成して、その描画機能を呼び出して使います。ここでは、そのインスタンスをQWebFrame::render() に渡して、QWebFrame内で描画をさせています。
スケーリングした画像を格納するためにQImageのデータthumbnailを用いています。スケールが1.0のときにthumbnailにimageを代入していますが、QImageは暗黙の共有クラスですから画像データ全体がコピーされず、imageと画像データが共有されます。スケールが1.0でないときには、QImage::scaled()を用い、Qt::SmoothTransformationを指定してスケーリング結果が滑らかになるようにしています。そして、QImage::save()で画像ファイルを保存し、処理が完了したことをシグナルfinished()で送信します。
コマンドラインで指定したURLのレンダリングが完了したならば、イベントループから抜けてプログラムを終了させるために、Thumbnailerのシグナルfinished()をQApplicationのスロットquit()に接続しています。
SVGファイルのレンダリング結果を画像で保存する
Qt Labsには、Qtの開発元の開発者が実験中の機能やQtの突っ込んだ使い方が掲載されています。その中から、Qt WebKitを使ってSVG(Scalable Vector Graphics)を描画し、PNG形式の画像ファイルとして保存するプログラムを紹介します。
- Qt Labs Blogs - WebKit-based SVG rasterizer
- http://labs.trolltech.com/blogs/2008/08/06/webkit-based-svg-rasterizer/
上記ページに書かれている説明から要点のみ説明しましょう。
コマンドをこのように実行すると300x300の大きさにSVGをレンダリングし、PNGファイルに保存されます。図5はそこで生成されたPNG画像です。
リスト7とリスト8が上記のコマンドの実装コードです。このコードは、
- svn://labs.trolltech.com/svn/graphics/dojo/svg2png
から入手できます。行を追って説明します。
先に説明したサンプルと同様に、SVGファイルのロードがされたときに送信されるシグナルloadFinished(bool)をスロットsaveResult(bool)に接続して、saveResult()で画像を保存するようにしています。
Qt 4.4には、まだDOM APIが用意されていないので、JavaScriptを実行し、その結果を得ることで、ロードしたSVGファイルの情報にアクセスします。このマクロはそのための定義です。
ちょっとわかり難いコードかもしれません。QApplication::exec() で開始されたイベントループを終了コードを指定して抜けるようにしています。イベントループを実際に抜けるのは、このメンバー関数を抜けてイベントループに戻った後なので、return で戻る必要があることに注意してください。なお、QApplication::quit() は exit(0) と同じです。
JavaScriptでドキュメントのノード名を取出し、SVGファイルどうかをチェックしています。
こちらも同様にしてJavaScriptを使って、SVGの画像サイズのチェックをしています。
このサンプルの説明があるWebページから入手できるSVGファイルは、昔からPostScriptでよく引用されている虎の画像ファイルです。そのままだと、このプログラムで使うとエラーになってしまうので、SVGファイルをエディタで編集してwidthとheightの属性を両方600に指定して使っています。
先に説明したサンプルとの描画方法と違い、QPainterにアフィン変換を適用してスケーリングしています。
SVGを変換しつつ描画をしてから、ファイルに保存しています。
SVGのレンダリングをしているWebKitSVGRenderでは、QWebViewを継承したクラスはウィジェットであって、インスタンスを生成するとウィンドウとなっています。このためclose() を何ヵ所かで呼び、ウィンドウを閉じてプログラムを終了させています。あまり見慣れないコードですが、close()の呼び出しをしないとウィンドウが存在し続けるので、SVGからPNGが生成されてもプログラムは終了しません。
まとめと次回の予告
今回は、Qtでの簡単なブラウザの作成方法とブラウザ機能の利用方法について説明しました。WebKitはブラウザエンジンであって、ブラウザそのものではありません。同じようにQt WebKitもブラウザではなく、WebKitというブラウザエンジンをQtから利用して、いろいろなアプリケーションを作成するための機能です。
次回は、ブラウザ機能とQtのウィジェットを混在して利用する方法を説明します。