WebEngine ウィジェット シンプルブラウザの例
Qt WebEngine ウィジェットをベースにしたシンプルなブラウザです。
を使用する方法を示します。 Qt WebEngine C++ classesを使用して、次の要素を含む小さなウェブブラウザアプリケーションを開発する方法を示します:
- 保存されたページを開き、ウィンドウとタブを管理するためのメニューバー。
- URL を入力し、ウェブページの閲覧履歴を前後に移動するためのナビゲーションバー。
- タブ内にウェブコンテンツを表示するためのマルチタブエリア。
- ホバーされたリンクを表示するステータスバー
- シンプルなダウンロードマネージャー
ウェブコンテンツは、新しいタブまたは別のウィンドウで開くことができます。ウェブページへのアクセスにHTTP認証とプロキシ認証を使用できます。
例の実行
からサンプルを実行するには Qt Creatorからサンプルを実行するには、Welcome モードを開き、Examples からサンプルを選択します。詳細については、「例の構築と実行」を参照してください。
クラス階層
まず、これから実装する主なクラスの図を描くことから始めます:
Browser
はアプリケーションのウィンドウを管理するクラスです。BrowserWindow
は 、メニュー、ナビゲーション・バー、 、ステータス・バーを示します。QMainWindowTabWidget
TabWidget
は で、1 つまたは複数のブラウザー・タブを含みます。QTabWidgetWebView
は で、 のビューを提供し、 のタブとして追加されます。QWebEngineViewWebPage
TabWidget
WebPage
は で、ウェブサイトのコンテンツを表します。QWebEnginePage
さらに、いくつかの補助クラスを実装します:
ブラウザのメインウィンドウの作成
この例では、Browser
オブジェクトが所有する複数のメインウィンドウをサポートしています。このクラスは、DownloadManagerWidget
も所有し、ブックマークや履歴マネージャなどのさらなる機能にも使用できます。
main.cpp
で、最初のBrowserWindow
インスタンスを作成し、Browser
オブジェクトに追加します。コマンドラインに引数が渡されなければ、Qtホームページを開きます:
int main(int argc, char **argv) { QCoreApplication::setOrganizationName("QtExamples"); QApplication app(argc, argv); app.setWindowIcon(QIcon(u":AppLogoColor.png"_s)); QLoggingCategory::setFilterRules(u"qt.webenginecontext.debug=true"_s); QWebEngineProfile::defaultProfile()->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true); QWebEngineProfile::defaultProfile()->settings()->setAttribute(QWebEngineSettings::DnsPrefetchEnabled, true); QWebEngineProfile::defaultProfile()->settings()->setAttribute( QWebEngineSettings::ScreenCaptureEnabled, true); QUrl url = commandLineUrlArgument(); Browser browser; BrowserWindow *window = browser.createHiddenWindow(); window->tabWidget()->setUrl(url); window->show(); return app.exec(); }
ウィンドウをOpenGLレンダリングに切り替えたときのちらつきを抑えるために、最初のブラウザ・タブが追加された後にshowを呼び出します。
タブの作成
BrowserWindow
コンストラクタは、必要なユーザーインターフェース関連オブジェクトをすべて初期化します。BrowserWindow
のcentralWidget
にはTabWidget
のインスタンスが含まれます。TabWidget
には、1つまたは複数のWebView
インスタンスがタブとして含まれ、現在選択されているものにシグナルとスロットを委譲します:
class TabWidget : public QTabWidget { ... signals: // current tab/page signals void linkHovered(const QString &link); void loadProgress(int progress); void titleChanged(const QString &title); void urlChanged(const QUrl &url); void favIconChanged(const QIcon &icon); void webActionEnabledChanged(QWebEnginePage::WebAction action, bool enabled); void devToolsRequested(QWebEnginePage *source); void findTextFinished(const QWebEngineFindTextResult &result); public slots: // current tab/page slots void setUrl(const QUrl &url); void triggerWebPageAction(QWebEnginePage::WebAction action); ... };
各タブはWebView
のインスタンスを含む:
WebView *TabWidget::createTab() { WebView *webView = createBackgroundTab(); setCurrentWidget(webView); return webView; } WebView *TabWidget::createBackgroundTab() { WebView *webView = new WebView; WebPage *webPage = new WebPage(m_profile, webView); webView->setPage(webPage); setupView(webView); int index = addTab(webView, tr("(Untitled)")); setTabIcon(index, webView->favIcon()); // Workaround for QTBUG-61770 webView->resize(currentWidget()->size()); webView->show(); return webView; }
TabWidget::setupView()
では、TabWidget
が常に現在選択されているWebView
のシグナルを転送するようにしています:
void TabWidget::setupView(WebView *webView) { QWebEnginePage *webPage = webView->page(); connect(webView, &QWebEngineView::titleChanged, [this, webView](const QString &title) { int index = indexOf(webView); if (index != -1) { setTabText(index, title); setTabToolTip(index, title); } if (currentIndex() == index) emit titleChanged(title); }); connect(webView, &QWebEngineView::urlChanged, [this, webView](const QUrl &url) { int index = indexOf(webView); if (index != -1) tabBar()->setTabData(index, url); if (currentIndex() == index) emit urlChanged(url); }); connect(webView, &QWebEngineView::loadProgress, [this, webView](int progress) { if (currentIndex() == indexOf(webView)) emit loadProgress(progress); }); connect(webPage, &QWebEnginePage::linkHovered, [this, webView](const QString &url) { if (currentIndex() == indexOf(webView)) emit linkHovered(url); }); connect(webView, &WebView::favIconChanged, [this, webView](const QIcon &icon) { int index = indexOf(webView); if (index != -1) setTabIcon(index, icon); if (currentIndex() == index) emit favIconChanged(icon); }); connect(webView, &WebView::webActionEnabledChanged, [this, webView](QWebEnginePage::WebAction action, bool enabled) { if (currentIndex() == indexOf(webView)) emit webActionEnabledChanged(action,enabled); }); connect(webPage, &QWebEnginePage::windowCloseRequested, [this, webView]() { int index = indexOf(webView); if (webView->page()->inspectedPage()) window()->close(); else if (index >= 0) closeTab(index); }); connect(webView, &WebView::devToolsRequested, this, &TabWidget::devToolsRequested); connect(webPage, &QWebEnginePage::findTextFinished, [this, webView](const QWebEngineFindTextResult &result) { if (currentIndex() == indexOf(webView)) emit findTextFinished(result); }); }
タブを閉じる
ユーザーがタブを閉じると、まず対応するWebView
のRequestClose Web アクションをトリガーします:
connect(tabBar, &QTabBar::tabCloseRequested, [this](int index) { if (WebView *view = webView(index)) view->page()->triggerAction(QWebEnginePage::WebAction::RequestClose); });
これにより、JavaScriptbeforeunload
のイベントリスナーが起動し、ページを閉じるかどうかを確認するダイアログが表示されます。この場合、ユーザーは閉じる要求を拒否し、タブを開いたままにすることができます。そうでなければ、windowCloseRequested シグナルが発せられ、タブを閉じます:
connect(webPage, &QWebEnginePage::windowCloseRequested, [this, webView]() { int index = indexOf(webView); if (webView->page()->inspectedPage()) window()->close(); else if (index >= 0) closeTab(index); });
WebView 機能の実装
WebView
はQWebEngineView から派生したもので、以下の機能をサポートしています:
renderProcess
が終了した場合のエラーメッセージの表示createWindow
リクエストの処理- コンテキストメニューへのカスタムメニュー項目の追加
まず、必要なメソッドとシグナルを持つWebView を作成します:
class WebView : public QWebEngineView { Q_OBJECT public: explicit WebView(QWidget *parent = nullptr); ... protected: void contextMenuEvent(QContextMenuEvent *event) override; QWebEngineView *createWindow(QWebEnginePage::WebWindowType type) override; signals: void webActionEnabledChanged(QWebEnginePage::WebAction webAction, bool enabled); ... };
エラーメッセージの表示
レンダー処理が終了した場合は、エラーコードとともにQMessageBox を表示し、ページを再読み込みします:
WebView::WebView(QWidget *parent) : QWebEngineView(parent) { ... connect(this, &QWebEngineView::renderProcessTerminated, [this](QWebEnginePage::RenderProcessTerminationStatus termStatus, int statusCode) { QString status; switch (termStatus) { case QWebEnginePage::NormalTerminationStatus: status = tr("Render process normal exit"); break; case QWebEnginePage::AbnormalTerminationStatus: status = tr("Render process abnormal exit"); break; case QWebEnginePage::CrashedTerminationStatus: status = tr("Render process crashed"); break; case QWebEnginePage::KilledTerminationStatus: status = tr("Render process killed"); break; } QMessageBox::StandardButton btn = QMessageBox::question(window(), status, tr("Render process exited with code: %1\n" "Do you want to reload the page ?").arg(statusCode)); if (btn == QMessageBox::Yes) QTimer::singleShot(0, this, &WebView::reload); }); }
WebWindows の管理
QWebEnginePage::WebWindowTypeたとえば、JavaScript プログラムが新しいウィンドウやダイアログでドキュメントを開くように要求した場合などです。この場合はQWebView::createWindow()
をオーバーライドします:
QWebEngineView *WebView::createWindow(QWebEnginePage::WebWindowType type) { BrowserWindow *mainWindow = qobject_cast<BrowserWindow*>(window()); if (!mainWindow) return nullptr; switch (type) { case QWebEnginePage::WebBrowserTab: { return mainWindow->tabWidget()->createTab(); }
QWebEnginePage::WebDialog
の場合、WebPopupWindow
クラスのインスタンスを作成します:
class WebPopupWindow : public QWidget { Q_OBJECT public: explicit WebPopupWindow(QWebEngineProfile *profile); WebView *view() const; private slots: void handleGeometryChangeRequested(const QRect &newGeometry); private: QLineEdit *m_urlLineEdit; QAction *m_favAction; WebView *m_view; };
コンテキストメニュー項目の追加
コンテキストメニューにメニュー項目を追加して、ユーザが右クリックでインスペクタを新しいウィンドウで開けるようにします。QWebEngineView::contextMenuEvent をオーバーライドし、QWebEnginePage::createStandardContextMenu を使用して、QWebEnginePage::WebAction アクションのデフォルトリストを持つデフォルトのQMenu を作成します。
QWebEnginePage::InspectElement アクションのデフォルト名はInspect です。わかりやすくするため、インスペクタがまだ存在しない場合はOpen Inspector In New Window に、すでに作成されている場合はInspect Element に名前を変更します。
また、QWebEnginePage::ViewSource アクションがメニューにあるかどうかもチェックする。もしなければ、セパレーターも追加しなければならないからだ。
void WebView::contextMenuEvent(QContextMenuEvent *event) { QMenu *menu = createStandardContextMenu(); const QList<QAction *> actions = menu->actions(); auto inspectElement = std::find(actions.cbegin(), actions.cend(), page()->action(QWebEnginePage::InspectElement)); if (inspectElement == actions.cend()) { auto viewSource = std::find(actions.cbegin(), actions.cend(), page()->action(QWebEnginePage::ViewSource)); if (viewSource == actions.cend()) menu->addSeparator(); QAction *action = menu->addAction("Open inspector in new window"); connect(action, &QAction::triggered, [this]() { emit devToolsRequested(page()); }); } else { (*inspectElement)->setText(tr("Inspect element")); } // add conext menu for image policy QMenu *editImageAnimation = new QMenu(tr("Image animation policy")); m_imageAnimationGroup = new QActionGroup(editImageAnimation); m_imageAnimationGroup->setExclusive(true); QAction *disableImageAnimation = editImageAnimation->addAction(tr("Disable all image animation")); disableImageAnimation->setCheckable(true); m_imageAnimationGroup->addAction(disableImageAnimation); connect(disableImageAnimation, &QAction::triggered, [this]() { handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy::Disallow); }); QAction *allowImageAnimationOnce = editImageAnimation->addAction(tr("Allow animated images, but only once")); allowImageAnimationOnce->setCheckable(true); m_imageAnimationGroup->addAction(allowImageAnimationOnce); connect(allowImageAnimationOnce, &QAction::triggered, [this]() { handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy::AnimateOnce); }); QAction *allowImageAnimation = editImageAnimation->addAction(tr("Allow all animated images")); allowImageAnimation->setCheckable(true); m_imageAnimationGroup->addAction(allowImageAnimation); connect(allowImageAnimation, &QAction::triggered, [this]() { handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy::Allow); }); switch (page()->settings()->imageAnimationPolicy()) { case QWebEngineSettings::ImageAnimationPolicy::Allow: allowImageAnimation->setChecked(true); break; case QWebEngineSettings::ImageAnimationPolicy::AnimateOnce: allowImageAnimationOnce->setChecked(true); break; case QWebEngineSettings::ImageAnimationPolicy::Disallow: disableImageAnimation->setChecked(true); break; default: allowImageAnimation->setChecked(true); break; } menu->addMenu(editImageAnimation); menu->popup(event->globalPos()); }
WebPageとWebView機能の実装
QWebEnginePage のサブクラスとしてWebPage
を実装し、QWebEngineView のサブクラスとしてWebView
を実装して、HTTP、プロキシ認証、およびウェブページへのアクセス時の SSL 証明書エラーの無視を有効にします:
class WebPage : public QWebEnginePage { Q_OBJECT public: explicit WebPage(QWebEngineProfile *profile, QObject *parent = nullptr); signals: void createCertificateErrorDialog(QWebEngineCertificateError error); private slots: void handleCertificateError(QWebEngineCertificateError error); void handleSelectClientCertificate(QWebEngineClientCertificateSelection clientCertSelection); void handleDesktopMediaRequest(const QWebEngineDesktopMediaRequest &request); }; class WebView : public QWebEngineView { Q_OBJECT public: explicit WebView(QWidget *parent = nullptr); ~WebView(); void setPage(WebPage *page); int loadProgress() const; bool isWebActionEnabled(QWebEnginePage::WebAction webAction) const; QIcon favIcon() const; protected: void contextMenuEvent(QContextMenuEvent *event) override; QWebEngineView *createWindow(QWebEnginePage::WebWindowType type) override; signals: void webActionEnabledChanged(QWebEnginePage::WebAction webAction, bool enabled); void favIconChanged(const QIcon &icon); void devToolsRequested(QWebEnginePage *source); private slots: void handleCertificateError(QWebEngineCertificateError error); void handleAuthenticationRequired(const QUrl &requestUrl, QAuthenticator *auth); void handlePermissionRequested(QWebEnginePermission permission); void handleProxyAuthenticationRequired(const QUrl &requestUrl, QAuthenticator *auth, const QString &proxyHost); void handleRegisterProtocolHandlerRequested(QWebEngineRegisterProtocolHandlerRequest request); #if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) void handleFileSystemAccessRequested(QWebEngineFileSystemAccessRequest request); void handleWebAuthUxRequested(QWebEngineWebAuthUxRequest *request); #endif void handleImageAnimationPolicyChange(QWebEngineSettings::ImageAnimationPolicy policy); private: void createWebActionTrigger(QWebEnginePage *page, QWebEnginePage::WebAction); void onStateChanged(QWebEngineWebAuthUxRequest::WebAuthUxState state); private: int m_loadProgress = 100; WebAuthDialog *m_authDialog = nullptr; QActionGroup *m_imageAnimationGroup = nullptr; };
上記のすべての場合において、適切なダイアログをユーザーに表示します。認証の場合、QAuthenticator オブジェクトに正しいクレデンシャル値を設定する必要があります:
void WebView::handleAuthenticationRequired(const QUrl &requestUrl, QAuthenticator *auth) { QDialog dialog(window()); dialog.setModal(true); dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint); Ui::PasswordDialog passwordDialog; passwordDialog.setupUi(&dialog); passwordDialog.m_iconLabel->setText(QString()); QIcon icon(window()->style()->standardIcon(QStyle::SP_MessageBoxQuestion, 0, window())); passwordDialog.m_iconLabel->setPixmap(icon.pixmap(32, 32)); QString introMessage(tr("Enter username and password for \"%1\" at %2") .arg(auth->realm(), requestUrl.toString().toHtmlEscaped())); passwordDialog.m_infoLabel->setText(introMessage); passwordDialog.m_infoLabel->setWordWrap(true); if (dialog.exec() == QDialog::Accepted) { auth->setUser(passwordDialog.m_userNameLineEdit->text()); auth->setPassword(passwordDialog.m_passwordLineEdit->text()); } else { // Set authenticator null if dialog is cancelled *auth = QAuthenticator(); } }
handleProxyAuthenticationRequired
シグナル・ハンドラは、HTTPプロキシの認証とまったく同じ手順を実装しています。
SSLエラーが発生した場合、メインフレームから発生したのか、ページ内のリソースから発生したのかを確認します。リソースのエラーは自動的に証明書拒否をトリガーします。それ以外のケースでは、ユーザが証明書を許可または拒否できるダイアログを表示します。
void WebPage::handleCertificateError(QWebEngineCertificateError error) { // Automatically block certificate errors from page resources without prompting the user. // This mirrors the behavior found in other major browsers. if (!error.isMainFrame()) { error.rejectCertificate(); return; } error.defer(); QTimer::singleShot(0, this, [this, error]() mutable { emit createCertificateErrorDialog(error); }); }
ウェブ・ページを開く
このセクションでは、新しいページを開くワークフローについて説明します。ユーザーがナビゲーション・バーに URL を入力し、Enter を押すと、QLineEdit::returnPressed
シグナルが発信され、新しい URL がTabWidget::setUrl
に引き渡される:
BrowserWindow::BrowserWindow(Browser *browser, QWebEngineProfile *profile, bool forDevTools) { ... connect(m_urlLineEdit, &QLineEdit::returnPressed, [this]() { m_tabWidget->setUrl(QUrl::fromUserInput(m_urlLineEdit->text())); }); ... }
呼び出しは現在選択されているタブに転送される:
void TabWidget::setUrl(const QUrl &url) { if (WebView *view = currentWebView()) { view->setUrl(url); view->setFocus(); } }
WebView
のsetUrl()
メソッドは、url
を関連するWebPage
に転送するだけで、 はバックグラウンドでページのコンテンツのダウンロードを開始する。
プライベートブラウジングの実装
プライベートブラウジング、インコグニートモード、または記録外モードは、多くのブラウザの機能で、クッキー、HTTP キャッシュ、または閲覧履歴のような、通常は永続的なデータがメモリ上にのみ保持され、ディスク上には痕跡を残しません。この例では、ウィンドウ・レベルでプライベート・ブラウジングを実装します。あるいは、タブレベルでプライベートブラウジングを実装し、ウィンドウ内のいくつかのタブを通常モードに、他のタブをプライベートモードにすることもできます。
Qt WebEngine を使えば、プライベートブラウジングの実装はとても簡単だ。QWebEngineProfile 、デフォルトのプロファイルの代わりにQWebEnginePage 。この例では、この新しいプロファイルはBrowser
オブジェクトによって所有されています:
class Browser { public: ... BrowserWindow *createHiddenWindow(bool offTheRecord = false); BrowserWindow *createWindow(bool offTheRecord = false); private: ... QScopedPointer<QWebEngineProfile> m_profile; };
プライベートブラウジングに必要なプロファイルは、その最初のウィンドウとともに作成されます。QWebEngineProfile のデフォルトのコンストラクタは、すでにそれをオフレコ・モードにしています。
BrowserWindow *Browser::createHiddenWindow(bool offTheRecord) { if (!offTheRecord && !m_profile) { const QString name = u"simplebrowser."_s + QLatin1StringView(qWebEngineChromiumVersion()); m_profile.reset(new QWebEngineProfile(name)); ...
あとは適切なプロファイルを適切なQWebEnginePage オブジェクトに渡すだけです。Browser
オブジェクトは、グローバル・デフォルト・プロファイル(QWebEngineProfile::defaultProfile を参照)か、共有のオフ・ザ・レコード・プロファイル・インスタンスのいずれかを、新しいBrowserWindow
に渡します:
... QObject::connect(m_profile.get(), &QWebEngineProfile::downloadRequested, &m_downloadManagerWidget, &DownloadManagerWidget::downloadRequested); } auto profile = !offTheRecord ? m_profile.get() : QWebEngineProfile::defaultProfile(); auto mainWindow = new BrowserWindow(this, profile, false); return mainWindow; }
BrowserWindow
とTabWidget
オブジェクトは、ウィンドウに含まれるすべてのQWebEnginePage オブジェクトがこのプロファイルを使用するようにします。
ダウンロードの管理
ダウンロードはQWebEngineProfile に関連付けられます。ウェブページでダウンロードがトリガーされるたびに、QWebEngineProfile::downloadRequested シグナルがQWebEngineDownloadRequest とともに発信され、この例ではDownloadManagerWidget::downloadRequested
に転送されます:
Browser::Browser() { // Quit application if the download manager window is the only remaining window m_downloadManagerWidget.setAttribute(Qt::WA_QuitOnClose, false); QObject::connect( QWebEngineProfile::defaultProfile(), &QWebEngineProfile::downloadRequested, &m_downloadManagerWidget, &DownloadManagerWidget::downloadRequested); }
このメソッドは、ユーザーにファイル名の入力を促し(あらかじめ入力された候補が表示される)、ダウンロードを開始する(ユーザーがSave As ダイアログをキャンセルしない限り):
void DownloadManagerWidget::downloadRequested(QWebEngineDownloadRequest *download) { Q_ASSERT(download && download->state() == QWebEngineDownloadRequest::DownloadRequested); QString path = QFileDialog::getSaveFileName(this, tr("Save as"), QDir(download->downloadDirectory()).filePath(download->downloadFileName())); if (path.isEmpty()) return; download->setDownloadDirectory(QFileInfo(path).path()); download->setDownloadFileName(QFileInfo(path).fileName()); download->accept(); add(new DownloadWidget(download)); show(); }
QWebEngineDownloadRequest オブジェクトは定期的にreceivedBytesChanged シグナルを発信し、潜在的なオブザーバーにダウンロードの進行状況を通知し、ダウンロードが終了したときやエラーが発生したときにstateChanged シグナルを発信します。これらのシグナルの処理方法の例については、downloadmanagerwidget.cpp
を参照してください。
WebAuth/FIDO UXリクエストの管理
WebAuth UXリクエストはQWebEnginePage 。認証機関がユーザーとの対話を要求するたびに、QWebEnginePage で UX リクエストがトリガーされ、QWebEngineWebAuthUxRequest でQWebEnginePage::webAuthUxRequested シグナルが発行されます。この例では、WebView::handleAuthenticatorRequired
に転送されます:
connect(page, &QWebEnginePage::webAuthUxRequested, this, &WebView::handleWebAuthUxRequested);
このメソッドは WebAuth UX ダイアログを生成し、UX リクエストフローを開始する。
void WebView::handleWebAuthUxRequested(QWebEngineWebAuthUxRequest *request) { if (m_authDialog) delete m_authDialog; m_authDialog = new WebAuthDialog(request, window()); m_authDialog->setModal(false); m_authDialog->setWindowFlags(m_authDialog->windowFlags() & ~Qt::WindowContextHelpButtonHint); connect(request, &QWebEngineWebAuthUxRequest::stateChanged, this, &WebView::onStateChanged); m_authDialog->show(); }
QWebEngineWebAuthUxRequest オブジェクトはstateChanged シグナルを定期的に発信し、潜在的なオブザーバに現在の WebAuth UX の状態を通知します。オブザーバはそれに応じて WebAuth ダイアログを更新する。これらのシグナルの処理方法の例については、webview.cpp
とwebauthdialog.cpp
を参照してください。
macOS の署名要件
macOS上でSimple Browserを動作させるときに、Webサイトが位置情報、カメラ、マイクにアクセスできるようにするには、アプリケーションに署名する必要があります。これはビルド時に自動的に行われますが、ビルド環境に有効な署名IDを設定する必要があります。
ファイルと属性
この例ではTango Icon Libraryのアイコンを使用しています:
Tangoアイコンライブラリ | パブリックドメイン |
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.