Qt をはじめよう! 第6回: 簡単なブラウザを作ってみよう!

Posted by 鈴木 佑 on 2010/04/13

前回の Hello World に引き続き、Qt Creator を使用して、URL の入力/表示欄と WebKit を用いたページの表示部をもつ簡単なブラウザを作成してみましょう。

今回は Mac OS X の Qt Creator を使用して作業をしますが、Windows や Linux でも基本的に同様です。

新しいプロジェクトの作成

ファイル(F) -> ファイル/プロジェクトの新規作成(N)… を選択してください。

今回も「Qt4 GUI アプリケーション」を選択します。

プロジェクト名とパス

simplebrowser という名前のアプリケーションを作成します。

必須モジュールの選択

今回は QtCoreQtGui モジュールに加え、QtWebKit モジュールを使用します。

右上の QtWebKit モジュールにチェックを入れて次へをクリックしてください。

※ Qt SDK 2010.04 / Qt Creator 2.0 以降ではプロジェクト作成ウィザードからこの設定が無くなっているため、手動でモジュールを追加する作業が必要となります。後述の「WebKit モジュールの追加」の作業を行ってください。2010/07/28 追記

クラス情報

クラス名:SimpleBrowser

基底クラス:QWidget を選択してください。

と入力して次へ進みます。

プロジェクト管理

生成されるファイルの一覧が表示されます。完了をクリックすると Simple Browser アプリケーションのひな形が生成されます。

UI のデザインの変更

左のウィジェットの一覧から URL の入力欄として使用する「Line Edit」と Web ページの表示に使用する「QWebView」をフォームに貼り付けてください。この時、上図のように LineEdit が QWebView よりも上部になるようにして置いてください。

次に、Qt のレイアウト管理機能を使用してこの二つのウィジェットをきれいに並べます。レイアウト管理についての説明はここでは省きます。詳細はドキュメント Layout Management を参照してください。

フォームの背景(グレーの部分) をクリックするか、 右上の「オブジェクト」ツリーの中から SimpleBrowser という名前のオブジェクトを選択し、上図のように SimpleBrowser ウィジェットが選択された状態にしてください。

次に、赤い丸で示したところにある「垂直に並べる」というボタンをクリックしてください。
「垂直に並べる」場合、上にあるウィジェットから順に配置を行います。

これで2つのウィジェットがきれいに上下に並びました。

URL 入力時の処理を実装

それでは次に、QLineEdit に入力した URL をリターンキーが押された時に QWebView に表示させる処理を実装しましょう。

QLineEdit を右クリックし、コンテキストメニューの中から「Go to slot…」を選択してください。

QLineEdit が持っているシグナルの一覧が表示されます。ここでは returnPressed() シグナルを選択して OK をクリックしてください。このシグナルは QLineEdit でリターンキーが押された場合に発生するシグナルです。

なお、ここで使用している シグナル/スロット という機能は Qt のオブジェクト間通信の仕組みです。今回は詳しい解説は省きますので、ドキュメントの Signals and Slots を参考にしてください。今後の記事で解説する予定です。

Qt Creator によってヘッダーファイル(simplebrowser.h)とソースファイル(simplebrowser.cpp)が変更され、空のスロット(=メソッド) on_lineEdit_returnPressed() が SimpleBrowser クラスに追加されました。

void SimpleBrowser::on_lineEdit_returnPressed()
{
    ui->webView->load(QUrl(ui->lineEdit->text()));
}

作成されたスロット(SimpleBrowser::on_lineEdit_returnPressed())を上記のように実装してください。ui の lineEdit の text() を URL(QUrl) に変換して、 ui の webView で load() します。

ここで ui は UI デザイナでデザインしている GUI のクラスのインスタンスのポインタ、ui->webView はデザイン時に貼り付けた QWebView のインスタンスのポインタ、ui->lineEdit は QLineEdit のインスタンスのポインタになります。デザイン画面上での各オブジェクトの objectName プロパティの値が ui のメンバ変数名に対応しています。

WebKit モジュールの追加

※ Qt SDK 2010.04 / Qt Creator 2.0 以降ではプロジェクト作成時のモジュールの設定が無くなっているため、手動でプロジェクトに WebKit モジュールを追加する必要があります。それ以前のバージョンの場合はこの変更は必要ありません。

サイドバーのプロジェクトから simplebrowser.pro を開いて “QT” の設定に “webkit” を追加してください。

QT += core gui webkit

2010/07/28 追記

実行

それではビルドをして実行してみましょう。

保存していないファイルがあるというメッセージが出た場合にはダイアログの指示に従ってすべて保存ください。

それでは画面上部にある lineEdit に URL を入力してリターンキーを押してみましょう。

無事ウェブサイトが表示されましたか?

表示しているページが変わった際に URL の表示を更新する

ウェブサイト内のリンクをクリックすると、ブラウザのページは変わりますが QLineEdit に表示されている URL は変わりません。次はこれに対応しましょう。

simplebrowser.ui を開き、今度は QWebView を右クリックして 「Go to slot…」を選択します。

urlChanged(QUrl) を選択して OK をクリックしてください。このシグナルは QWebView の URL が変わった際に発生するシグナルです。

ここで Qt Creator によって生成されたスロットの定義を少し修正する必要があります。

ヘッダファイル(simplebrowser.h)を開いてください。Qt Creator では F4 キーでヘッダ/ソースファイルの切り替えができます。

6 行目に QUrl のクラスの宣言を以下のように追加し、

class QUrl;

25 行目のスロットの定義を

void on_webView_urlChanged(QUrl );

から

void on_webView_urlChanged(const QUrl &url);

に変更してください。

ソースファイルでも同様に引数の型を変更し、スロットの中身も実装してください。

void SimpleBrowser::on_webView_urlChanged(const QUrl &url)
{
    ui->lineEdit->setText(url.toString());
}

ここでは ui の webView の url() が変更された場合に、それをテキストとして ui の lineEdit に setText() で設定しています。
それでは実行して確認してみましょう。

今度はリンクをクリックして別のページが開いた際に、URL の表示も変更されます。

ページのタイトルをウィンドウのタイトルに設定

それでは最後に、ページのタイトルをウィンドウのタイトルに設定してみましょう。

ここでは新たにスロットを作成せずに、QWebView::titleChanged() シグナルを QWidget::setWindowTitle() スロットに接続することでこの機能を実現します。

simplebrowser.ui を開き、シグナル/スロットの編集ボタンをクリックしてください。

次に、webView をドラッグし、SimpleBrowser にドロップしてください。

左下の「Show signals and slots inherited from QWidget」チェックし、webView の titleChanged(QString) シグナルと SimpleBrowser の setWindowTitle(QString) スロットを選択して OK をクリックしてください。

それでは実行してみましょう。

ウィンドウの上部のバーのところにページのタイトルが表示されるようになりました。

終わりに

Qt Creator を使用して、とても簡単なブラウザアプリケーションを作ってみました。

QWebView には今回使用した機能以外にも iconChanged()loadProgress(int progress) などのシグナルや back(), forward(), reload(), stop() などのスロットがあります。これらに関連する機能の実装にチャレンジしてみてはいかがでしょうか。

Did you like this? Share it:

Related posts:

  1. QWebView 関連クラスのメンテナ募集
  2. Qt をはじめよう! 第16回: GUI デザイナでスロットを作成しよう
  3. Qt をはじめよう! 第10回: シグナルとスロット
  4. Qt をはじめよう! 第15回: GUI デザイナでシグナル/スロットを接続しよう
  5. Qt をはじめよう! 第19回: 独自ウィジェットを作成しデザイナで使用しよう

30 comments  (read them below or add one)

川崎尊 2010/04/20 17:46
 

実に簡単にぶブラウザアプリケーションが作成できて、感動しました。

寺戸 2010/05/04 13:10
 

はじめまして。Qtは今回はじめて使用しました。
シグナルをスロットに接続する手順が、なかなか判りにくかったです。
他の統合環境には見られない機能で、慣れればかなり便利そうですが・・・。

鈴木 佑 2010/05/06 12:07
 

@寺戸さん はじめまして。

フィードバックありがとうございます。
判りにくかったのは具体的にはどの作業か教えていただけますか?
改善できる点があればどんどん改善していきたいと思っていますので、他にもなにかあれば是非お知らせください。

寺戸健太 2010/05/08 00:13
 

「webView をドラッグし、SimpleBrowser にドロップ」までは、すぐに出来たのですが、「”Show signals and slots inherited from QWidget”にチェックする」のを忘れて、ドロップ先を間違えたのかと不安に思い、何度か同じ手順を繰り返してしまいました。
落ち着いて読めば、しっかり記載してあったのですが・・・。
判ってしまえば、非常に簡単な手順ですね。

田中誠 2010/07/14 13:19
 

すみません。分からない点があるので、
お教え頂ければ幸いです。

ここで記述されている通りにやっても、
ビルドすると
collect2: ld returned 1 exit status
というメッセージが表示されてしまいます。

環境は
OS: Windows 7 Enterprise
です。
qt-sdk-win-opensource-2010.04.exe
をインストールし、日本語化ファイル
qtcreator_ja.qmもインストールしています。

ちなみに、VistaやLinux環境でも試しましたが、
同様になってしまいます。

環境変数に
QTDIR C:\Qt\2010.04
Path %QTDIR%\bin;%QTDIR%\mingw\bin;%QTDIR%\qt\bin
を設定しています。

すみません。よろしくお願い致します。

鈴木 佑 2010/07/14 14:27
 

田中さん、はじめまして。

> collect2: ld returned 1 exit status
このエラーメッセージだけでは詳細がわかりません。このメッセージ以前に具体的なエラーの箇所などのメッセージが出ていると思うので教えていただけますか。エラーを解決する際はできる限り多くの情報が必要です。

最近の Qt SDK ではプロジェクトウィザードの「必須モジュールの選択」の手順がなくなっています。このため、これに相当する作業が必要になります。
プロジェクトファイル simplebrowser.pro を開いて以下の一行を追加してください。
QT += webkit
これはこのプロジェクトで QtWebKit モジュールを使用するための設定です。

田中誠 2010/07/14 15:04
 

どうも早速、本当にありがとうございます。
すみません。
collect2: ld returned 1 exit status
というエラーメッセージだけが表示されている状態でした。

> プロジェクトファイル simplebrowser.pro を開いて以下の一行を追加してください。
> QT += webkit

この通りにしたら、無事にエラーが消え、問題なく動作しました。
どうも、本当にありがとうございました。
とっても、助かりました。

田中誠 2010/07/19 11:13
 

たびたび、すみません。

もしおわかりでしたら、教えて頂きたいのですが、
QTを使って、ホームページのURLを指定して、そのページ内
のHTMLのソースコードを取得(ダウンロード)する方法は
どうすればよいでしょうか。

このページで教えて頂いている
ui->webView->load(QUrl(ui->lineEdit->text()));
という方法だと、直接ページが表示されるのですが、
WebページのHTMLのソースコードをQStringなどの形式で
取得できたらうれしいのですが。

本やWebで検索しても、この方法がよくわかりません。
すみません。お願い致します。

鈴木 佑 2010/07/20 07:59
 

田中さん、HTMLのソースコードは QWebFrame クラスが保持していますので、ui->webView->page()->mainFrame()->toHtml() とすることで取得可能です。

QWebView, QWebPage, QWebFrame の関係については QWebView クラスのドキュメントの解説の「Elements of QWebView」セクションや各クラスのドキュメントを参照ください。

田中誠 2010/07/20 10:31
 

鈴木さん、本当にありがとうございます。
早速、各クラスのドキュメントを勉強してみます。

田中誠 2010/07/20 12:14
 

どうも、本当にすみません。
例えば、textEditの部品を貼り付け、そこに
HTMLソースも同時に表示する場合なのですが、

ui->webView->load(QUrl(ui->lineEdit->text()));
QString HtmlData = ui->webView->page()->mainFrame()->toHtml();
ui->textEdit->setHtml(HtmlData);

のようにHtmlDataでデータを獲得するという方法
では、textEditに何も表示されません。

どのように改善すれば、textEditにHTMLソース
を表示可能になるでしょうか。

お教え頂ければ、幸いです。

鈴木 佑 2010/07/20 16:36
 

田中さん、QWebView では load() メソッドを実行した後にバックグラウンドでデータを取得しページを表示します。このため、HTMLのソースの取得はデータの取得完了後に実行する必要があります。QWebView ではデータの取得が終わった際に loadFinished() シグナルが発生しますので、このシグナル用のスロットを作成しその中でHTML取得の処理をしてください。

詳細は QWebView のドキュメント に記載されていますのでそちらをお読みください。

また、QTextEdit に HTML をソースとして表示させるであれば setHtml ではなく setPlainText を使用する必要があるかもしれません、こちらもドキュメントをよく読んだ上でお使い分けください。

田中誠 2010/07/20 23:05
 

鈴木さん、どうも本当にありがとうございます。
ドキュメント類を読んでみます。
とても助かりました。

田中誠 2010/07/22 13:20
 

すみません。文字コードに関することで、分からない
ので、もしお分かりでしたら教えて頂ければ幸いです。

QNetworkAccessManagerなどを使って、日本語で書かれた
HTMLのソースを取得し、中身を少し書き換えてから
ui->webView->setHtml(HtmlData, url);
のような感じにHTMLソースを表示することを考えています。

QApplication a(argc, argv);
の直後に、
QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());
のようにして、HTMLソースの文字コードを自動的に設定するようにして
みたのですが、Shift-JISだと問題なく表示できるのですが、
EUCやUTF-8では、文字化けしてしまいます。

やはり、自分でHTML内のcharsetなどの情報から、文字コードを取得して、
QTextCodec::setCodecForCStrings(QTextCodec::codecForName(“utf-8″));
QTextCodec::setCodecForCStrings(QTextCodec::codecForName(“euc-jp”));
のような感じに、設定しないと処理できないのでしょうか。

それとも、
QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());
以外の方法で、自動的に文字コードを正しく設定する方法がある
のでしょうか。

鈴木 佑 2010/07/28 11:42
 

田中さん、QNetworkAccessManager を使用してデータを取得した場合には文字コードの判別とそれに応じた処理をご自身で行っていただく必要があります。

QNetworkAccessManager を直接使用せずに QWebView でページを読み込んだ場合には文字コード関連の処理は WebKit の中で行われ、変換後の文字列が QWebFrame::toHtml() で取得可能ですので、場合によっては一度 QWebView で読み込んだページのソースに対して処理をしてそれを再度 QWebView に設定する方が手間が少ないかもしれません。

本澤 2011/01/15 14:10
 

Qtはじめて利用してみました。
実際に使ってみると非常に楽しいです。
今までWin環境でVisualStuidioばかりでしたが、
これを気に新しく乗り換えてしまおうかと思うほどです。
本題ですが、このチュートリアルを通りに作成した。
ブラウザでサイトを見てみると、
どのサイトもfont-familyスタイルシートが有効にならないのですが、
なにか設定があるのでしょうか?
具体的には、すべて標準フォント指定で固定されています。

http://www.htmq.com/style/font-family.shtml
※サンプルのfancybrowserでも同様でした。

環境は、OSX10.5.5 / QtCreator 2.0.1 / Qt4.7.1です。
ご教授頂ければ幸いです。

本澤 2011/01/15 18:00
 

追記です。
safari / Chrome / webkitでは、
ゴシック・明朝とfont-familyが有効です。
よろしくお願いします。

鈴木 佑 2011/01/17 18:59
 

本澤さん、コメントありがとうございます。

QtWebKit での font-family ですが、特に設定等は無く標準でサポートされています。
本記事のサンプルや Qt のデモに含まれるブラウザ等で
http://w3g.jp/css/font/font-family のサンプルの項目などを見ると
対応している family についてはそのフォントを使用していることが確認できると思います。
こちらでは OSX10.6.6 / Qt 4.7.1 で確認しました。

しかし、 http://www.htmq.com/style/font-family.shtml の例のように
複数の font-family が指定されている場合で
最初の family のフォントが見つからなかった際には意図した通りの表示を行わない現象が確認できました。

こちらについては引き続き調査を行って、何か分かり次第こちらで報告しますので、しばらくお待ちいただければと思います。

本澤 2011/01/18 10:16
 

鈴木様

早速のご回答、ありがとうございます。

>http://w3g.jp/css/font/font-family
の欧文フォントの指定は、私の環境でも確認できました。

>複数の font-family が指定されている場合で
>最初の family のフォントが見つからなかった際には意図した通りの表示を行わない現象が確認できました。
なるほど、そういう現象なのですね。

ただあわせて、和文フォントしていを確認しましたが、
QtWebkit上では、和文指定ができない様に思われます。

Safari 5.0.3
http://i.min.us/idYDbw.png
FancyBrowser Qt4.7.1
http://i.min.us/iecZFK.png

お手数をお掛けいたしますが、あわせてご検証頂ければ幸いです。

鈴木 佑 2011/01/18 11:39
 

本澤様、

複数の font-family が指定されている場合の件については [Qt] @font-face does not work with local and remote fonts にて対応中ですのでご覧ください。
この現象の対応には Qt のフォントの機能拡張が必要になるため、修正には多少時間がかかるかもしれません。

和文フォントの件についてもこれから確認します。

鈴木 佑 2011/01/18 16:13
 

本澤様、

font-family: “Hiragino Kaku Gothic Pro”
のような指定については現在の最新版 Qt 4.7.1 では対応していませんが、Qt の開発版では使用可能になっています。
このため、Qt 4.8 では利用可能になると思います。

font-family: “MS 明朝” のような日本語での指定については開発版でも未対応でしたので、
こちらについてはこれから調査していきます。

ご報告ありがとうございました。

サイトウ 2011/02/26 14:30
 

分からないことがあるので、
お教え頂ければ幸いです。

このブラウザに印刷ボタンを付け、印刷プレビューを行いました。
しかし、以下のようになり一部(この画像では「google検索」の部分等)の表示がおかしいです。プレビューをせずに印刷しても同様になりました。
http://www1.axfc.net/uploader/Img/so/109041

ブラウザ上の表示は正常で、このようなことにはなっていません。
以下のような具合で行ったのですが、どこか間違ってるところがあればお教え頂ければ幸いです。

環境
OS:windows7 32bit
qt-sdk-win-opensource-2010.05.exe をインストール

source============
void Widget::on_printButton_clicked()
{
QPrinter printer;
QPrintPreviewDialog preview(&printer, this);
connect(&preview, SIGNAL(paintRequested(QPrinter *)), SLOT(printPreview(QPrinter *)));
preview.exec();
}

void Widget::printPreview(QPrinter *printer){
ui->webView->print(printer);
}

鈴木 佑 2011/02/28 18:05
 

サイトウさん、コメントありがとうございます。

プレビューを表示しているコード自体は問題なく、WebKit 側の問題だと思います。
WebKit のバグトラッカーにこの件のバグレポートを作成しましたので、こちらを追いかけていただければと思います。
https://bugs.webkit.org/show_bug.cgi?id=55356

また、こちらで確認した限りではプレビュー画面とプリンターへの印刷時には再現しましたが、
プレビュー画面や印刷ダイアログから(PDF)ファイルに出力した場合には正常に印刷されました。

サイトウ 2011/03/01 22:48
 

鈴木さん

検証ありがとうございます。
WebKitのバグですか…

直るまで気長に待ってみます。

ひろみつ 2011/09/16 01:35
 

WebKitを使った、ブラウザ作成がとても面白いです。
日本語のサンプルもけっこうあり、外国のと比べながら、色々作っています。Qt自体もおもしろいので、とてもはかどります。
WindowsAPIやMFCなど(C#もありますが)に比べ、手軽に出来るような気がします。また、Javaではまだブラウザコンポーネントが本格的なものがないのでQtのブラウザコンポーネント(WebKit)はとても気に入っています。

ただ、どこをどう探しても見つからない(外国のサイトを見ても見つかりません)サンプルがあるので、是非、鈴木佑さんに記事を書いていただきたいのです。
リンクをクリックして、別ウィンドウ(タブ)で開くリンクがあると思うのですが、あれが実現できません。
自分で色々試したり、外国のサイトを見ても、よいサンプルが載っていません。

是非、鈴木さんに記事を紹介していただきたいです。お願いします。

鈴木 佑 2011/09/16 19:34
 

ひろみつさん、コメントありがとうございます。

Qt のソースコードのデモに含まれるブラウザ(demos/browser/)にて、別ウィンドウ/別タブでリンクを開く機能が実装されていますので、そちらを参考にしてください。

この記事のサンプルでは1つのウィンドウしか表示していませんが、複数のウィンドウを表示するための改良やタブへの対応が必要になります。
これに関連して、QWebView をそのまま使用するのではなく、QWebView(および QWebPage) の派生クラスを作成することになると思います。

いくつかのヒントを以下に示しますので、是非ご自身で試してみてください。

* リンクのコンテキストメニューに「新しいウィンドウで開く」、「新しいタブで開く」を追加
   QWebView::contextMenuEvent() を再実装し、リンク上でコンテキストメニューが表示された場合に独自のメニューを表示します。
   参考: webview.cpp#line217
* 新しいウィンドウで表示する
   QWebPage::createWindow() を再実装し、新しいウィンドウを作成します。
   参考: webview.cpp#line119

これらを試す上で、具体的に分からないことを質問したい場合は、Qt Developer Network という開発者が集まるサイトに 日本語で質問ができるフォーラム がありますので、こちらもご活用ください。

ひろみつ 2011/09/16 23:14
 

どうも、丁寧なご返事恐縮でございます。

やりかたの手順として、コンテキストメニューに自前でメニューを追加する→そのクリックをした際に、createWindowが呼ばれるのでcreateWindow関数をオーバーライドするということですね。なるほど、手順としましては良く分かりました。とてもありがたいヒントをありがとうございます。

一つ一つできることから作っていきたいです。まだ、WebKit自体をあまり触ったことないので、知らないことばかりですが、色々できてびっくりしています。

分からないところは、教えていただきました、フォーラムで質問しようと思います。

とっかかりの大変有用なヒントをいただきまして、ありがとうございます。WebKitになれながら、参考URLのソースを解読していこうとおもいます。

どうもありがとうございました。

砂岡 克也 2011/09/26 18:01
 

お世話になります。
Embedded LinuxでMIPS向けにgfxプラグインを作って動かしていますが、それとQtWebkitを使って日本語表示に対応させようと思っています。
例えばexamples/webkit/fancybrowserのサンプルブラウザでいきなり日本語ページを指定すると英文字以外は表示されません。
いまいち4.7(.4)+QtWebkit上でのフォントファミリーの扱いがピンと来ていないのですが、とりあえず日本語を出すには上記サンプルの冒頭あたりでQWebSettingsのsetFontFamilyあたりを設定すれば出るのでしょうか。

以上 よろしくお願いいたします。

長谷川 2012/01/23 02:09
 

とても参考になります。
ひとつ、質問があります。シナリオファイルに従って特定の処理を自動化してくれるウェブブラウザを作りたいのですが、上手くloadFinishedと絡めることができません。

以下のように、QQueueを使ってURLのロードはシナリオ化できました。

———-
void MainWindow::on_pushButtonStart_clicked()
{
enqueueUrl(“http://www.yahoo.co.jp/”);
enqueueUrl(“http://www.google.co.jp/”);
enqueueUrl(“http://www.bing.co.jp/”);
dequeueUrlsAndLoad();//Queue内のurlを順番に読み込み
}

void MainWindow::on_webView_loadFinished(bool finished)
{
if(finished) dequeueUrlsAndLoad();
}

void MainWindow::enqueueUrl(QString url)
{
loadQueue.enqueue(QUrl(url));
}

void MainWindow::dequeueUrlsAndLoad()
{
if(!loadQueue.isEmpty())
{
ui->webView->load(loadQueue.dequeue());
}
}
———-

ただ、シナリオファイルではDOM操作(要素の書き換えやformのsubmitなど)も定義したいと考えているので、上記のような単純なURLの順次ロードだけですと不足です。

この様な事をしたい場合、どの様な流れでプログラムを組むのがベストプラクティスでしょうか。
案としては、メンバ変数bool isLoadingを用意し、loadStarted(),loadFinished()でこれを適時書き換え、シナリオを実行するスレッドからisLoadingをwhileで監視するという事を考えています。

鈴木 佑 2012/01/25 13:08
 

長谷川さん、コメントありがとうございます。

ご質問の内容ですが、この記事の範囲を超えたものになりますので、こちらのコメント欄ではなく Qt Developer Network日本語のフォーラム の方へ移動していただいてもよろしいでしょうか?

コメントする

前の投稿へ:

次の投稿へ: