WebEngine Widgets Einfacher Browser Beispiel

Ein einfacher Browser, der auf Qt WebEngine Widgets basiert.

Simple Browser demonstriert, wie man mit der Qt WebEngine C++ classes um eine kleine Webbrowser-Anwendung zu entwickeln, die die folgenden Elemente enthält:

  • Menüleiste zum Öffnen gespeicherter Seiten und zur Verwaltung von Fenstern und Registerkarten.
  • Navigationsleiste zur Eingabe einer URL und zum Zurück- und Vorwärtsbewegen in der Webseiten-Browsing-Historie.
  • Multi-Tab-Bereich für die Anzeige von Webinhalten innerhalb von Tabs.
  • Statusleiste für die Anzeige von Links, über die man den Mauszeiger bewegt.
  • Ein einfacher Download-Manager.

Der Webinhalt kann in neuen Tabs oder separaten Fenstern geöffnet werden. HTTP und Proxy-Authentifizierung können für den Zugriff auf Webseiten verwendet werden.

Ausführen des Beispiels

Zum Ausführen des Beispiels von Qt Creatorzu starten, öffnen Sie den Modus Welcome und wählen Sie das Beispiel unter Examples aus. Weitere Informationen finden Sie unter Erstellen und Ausführen eines Beispiels.

Klassenhierarchie

Wir beginnen mit der Skizzierung eines Diagramms der wichtigsten Klassen, die wir implementieren werden:

  • Browser ist eine Klasse, die die Anwendungsfenster verwaltet.
  • BrowserWindow ist eine QMainWindow, die das Menü, eine Navigationsleiste, TabWidget und eine Statusleiste enthält.
  • TabWidget ist eine QTabWidget und enthält eine oder mehrere Browser-Registerkarten.
  • WebView ist eine QWebEngineView, bietet eine Ansicht für WebPage und wird als Registerkarte in TabWidget hinzugefügt.
  • WebPage ist eine QWebEnginePage, die den Inhalt der Website darstellt.

Zusätzlich werden wir einige Hilfsklassen implementieren:

  • WebPopupWindow ist eine QWidget für die Anzeige von Popup-Fenstern.
  • DownloadManagerWidget ist eine QWidget, die die Liste der Downloads implementiert.

Erstellen des Browser-Hauptfensters

Dieses Beispiel unterstützt mehrere Hauptfenster, die im Besitz eines Browser Objekts sind. Diese Klasse besitzt auch die DownloadManagerWidget und kann für weitere Funktionen wie Lesezeichen und Verlaufsmanager verwendet werden.

In main.cpp erstellen wir die erste Instanz BrowserWindow und fügen sie dem Objekt Browser hinzu. Wenn keine Argumente auf der Kommandozeile übergeben werden, öffnen wir die Qt Homepage:

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();
}

Um ein Flackern beim Umschalten des Fensters auf OpenGL-Rendering zu unterdrücken, rufen wir show auf, nachdem der erste Browser-Tab hinzugefügt wurde.

Tabs erstellen

Der BrowserWindow Konstruktor initialisiert alle notwendigen Objekte der Benutzeroberfläche. Das centralWidget von BrowserWindow enthält eine Instanz von TabWidget. Das TabWidget enthält eine oder mehrere Instanzen von WebView als Tabs und delegiert seine Signale und Slots an den aktuell ausgewählten Tab:

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);
    ...
};

Jede Registerkarte enthält eine Instanz von 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;
}

In TabWidget::setupView() sorgen wir dafür, dass TabWidget immer die Signale des aktuell ausgewählten WebView weiterleitet:

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);
    });
}

Schließen von Tabs

Wenn der Benutzer eine Registerkarte schließt, lösen wir zunächst die RequestClose Web-Aktion auf der entsprechenden WebView aus:

    connect(tabBar, &QTabBar::tabCloseRequested, [this](int index) {
        if (WebView *view = webView(index))
            view->page()->triggerAction(QWebEnginePage::WebAction::RequestClose);
    });

Dies ermöglicht das Auslösen von JavaScript beforeunload Event-Listenern, die den Benutzer mit einem Dialog auffordern können, zu bestätigen, dass er die Seite schließen möchte. In diesem Fall kann der Benutzer die Aufforderung zum Schließen ablehnen und die Registerkarte offen lassen, andernfalls wird das Signal windowCloseRequested ausgelöst und die Registerkarte geschlossen:

    connect(webPage, &QWebEnginePage::windowCloseRequested, [this, webView]() {
        int index = indexOf(webView);
        if (webView->page()->inspectedPage())
            window()->close();
        else if (index >= 0)
            closeTab(index);
    });

WebView-Funktionalität implementieren

WebView wird von QWebEngineView abgeleitet, um die folgenden Funktionen zu unterstützen:

  • Anzeige von Fehlermeldungen, falls renderProcess abstürzt
  • Behandlung von createWindow Anfragen
  • Hinzufügen von benutzerdefinierten Menüpunkten zu Kontextmenüs

Zuerst erstellen wir WebView mit den notwendigen Methoden und Signalen:

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);
    ...
};
Anzeige von Fehlermeldungen

Wenn der Rendervorgang abgebrochen wird, zeigen wir eine QMessageBox mit einem Fehlercode an und laden dann die Seite neu:

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 verwalten

Die geladene Seite kann Fenster des Typs QWebEnginePage::WebWindowType erzeugen, z. B. wenn ein JavaScript-Programm ein Dokument in einem neuen Fenster oder Dialog öffnen möchte. Dies wird durch Überschreiben von QWebView::createWindow() gehandhabt:

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();
    }

Im Fall von QWebEnginePage::WebDialog erstellen wir eine Instanz einer benutzerdefinierten Klasse 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;
};
Hinzufügen von Kontextmenüpunkten

Wir fügen dem Kontextmenü einen Menüpunkt hinzu, so dass die Benutzer mit der rechten Maustaste einen Inspektor in einem neuen Fenster öffnen können. Wir überschreiben QWebEngineView::contextMenuEvent und verwenden QWebEnginePage::createStandardContextMenu um ein Standard QMenu mit einer Standardliste von QWebEnginePage::WebAction Aktionen zu erstellen.

Der Standardname für die Aktion QWebEnginePage::InspectElement ist Inspect. Der Übersichtlichkeit halber benennen wir sie in Open Inspector In New Window um, wenn noch kein Inspektor vorhanden ist, und in Inspect Element, wenn sie bereits erstellt wurde.

Wir prüfen auch, ob die Aktion QWebEnginePage::ViewSource im Menü enthalten ist, denn wenn dies nicht der Fall ist, müssen wir auch ein Trennzeichen hinzufügen.

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());
}

Implementierung der WebPage- und WebView-Funktionalität

Wir implementieren WebPage als Unterklasse von QWebEnginePage und WebView als Unterklasse von QWebEngineView, um HTTP, Proxy-Authentifizierung und das Ignorieren von SSL-Zertifikatsfehlern beim Zugriff auf Webseiten zu ermöglichen:

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;
};

In allen oben genannten Fällen zeigen wir dem Benutzer den entsprechenden Dialog an. Im Falle der Authentifizierung müssen wir die richtigen Werte für die Anmeldeinformationen auf dem QAuthenticator Objekt setzen:

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();
    }
}

Der handleProxyAuthenticationRequired signal handler implementiert die gleichen Schritte für die Authentifizierung von HTTP-Proxys.

Bei SSL-Fehlern wird geprüft, ob sie vom Hauptrahmen oder von einer Ressource innerhalb der Seite stammen. Ressourcenfehler lösen automatisch eine Zertifikatsablehnung aus, da der Benutzer nicht genügend Kontext hat, um eine Entscheidung zu treffen. In allen anderen Fällen lösen wir einen Dialog aus, in dem der Benutzer das Zertifikat zulassen oder ablehnen kann.

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); });
}

Öffnen einer Web-Seite

In diesem Abschnitt wird der Arbeitsablauf für das Öffnen einer neuen Seite beschrieben. Wenn der Benutzer eine URL in die Navigationsleiste eingibt und Enter drückt, wird das Signal QLineEdit::returnPressed ausgelöst und die neue URL an TabWidget::setUrl übergeben:

BrowserWindow::BrowserWindow(Browser *browser, QWebEngineProfile *profile, bool forDevTools)
{
    ...
        connect(m_urlLineEdit, &QLineEdit::returnPressed, [this]() {
            m_tabWidget->setUrl(QUrl::fromUserInput(m_urlLineEdit->text()));
        });
    ...
}

Der Aufruf wird an die aktuell ausgewählte Registerkarte weitergeleitet:

void TabWidget::setUrl(const QUrl &url)
{
    if (WebView *view = currentWebView()) {
        view->setUrl(url);
        view->setFocus();
    }
}

Die setUrl() -Methode von WebView leitet lediglich das url an das zugehörige WebPage weiter, das wiederum im Hintergrund mit dem Herunterladen des Seiteninhalts beginnt.

Implementierung von Private Browsing

Private Browsing, Inkognito-Modus oder Off-the-Record-Modus ist eine Funktion vieler Browser, bei der normalerweise dauerhafte Daten wie Cookies, der HTTP-Cache oder der Browserverlauf nur im Speicher gehalten werden und keine Spuren auf der Festplatte hinterlassen. In diesem Beispiel wird das private Browsing auf Fensterebene implementiert, wobei sich alle Registerkarten in einem Fenster entweder im normalen oder im privaten Modus befinden. Alternativ könnten wir das private Browsing auf der Ebene der Registerkarten implementieren, wobei einige Registerkarten in einem Fenster im normalen Modus, andere im privaten Modus sind.

Die Implementierung von Private Browsing ist mit Qt WebEngine recht einfach. Alles, was man tun muss, ist, ein neues QWebEngineProfile zu erstellen und es in QWebEnginePage anstelle des Standardprofils zu verwenden. In diesem Beispiel gehört dieses neue Profil dem Objekt Browser:

class Browser
{
public:
    ...
    BrowserWindow *createHiddenWindow(bool offTheRecord = false);
    BrowserWindow *createWindow(bool offTheRecord = false);
private:
    ...
    QScopedPointer<QWebEngineProfile> m_profile;
};

Das erforderliche Profil für das private Browsing wird zusammen mit seinem ersten Fenster erstellt. Der Standardkonstruktor für QWebEngineProfile versetzt es bereits in den Off-the-Record-Modus.

BrowserWindow *Browser::createHiddenWindow(bool offTheRecord)
{
    if (!offTheRecord && !m_profile) {
        const QString name = u"simplebrowser."_s + QLatin1StringView(qWebEngineChromiumVersion());
        m_profile.reset(new QWebEngineProfile(name));
    ...

Alles, was noch zu tun ist, ist, das entsprechende Profil an die entsprechenden QWebEnginePage Objekte weiterzugeben. Das Objekt Browser übergibt an jedes neue BrowserWindow entweder das globale Standardprofil (siehe QWebEngineProfile::defaultProfile) oder eine gemeinsame Instanz des Off-the-Record-Profils:

    ...
        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;
}

Die Objekte BrowserWindow und TabWidget sorgen dann dafür, dass alle in einem Fenster enthaltenen QWebEnginePage Objekte dieses Profil verwenden.

Verwaltung von Downloads

Downloads sind mit einem QWebEngineProfile verbunden. Immer wenn ein Download auf einer Webseite ausgelöst wird, wird das Signal QWebEngineProfile::downloadRequested mit einem QWebEngineDownloadRequest ausgegeben, das in diesem Beispiel an DownloadManagerWidget::downloadRequested weitergeleitet wird:

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);
}

Diese Methode fordert den Benutzer zur Eingabe eines Dateinamens auf (mit einem vorausgefüllten Vorschlag) und startet den Download (sofern der Benutzer den Save As Dialog nicht abbricht):

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();
}

Das Objekt QWebEngineDownloadRequest sendet in regelmäßigen Abständen das Signal receivedBytesChanged, um potenzielle Beobachter über den Fortschritt des Downloads zu informieren, und das Signal stateChanged, wenn der Download beendet ist oder ein Fehler auftritt. Siehe downloadmanagerwidget.cpp für ein Beispiel, wie diese Signale gehandhabt werden können.

Verwalten von WebAuth/FIDO UX-Anfragen

WebAuth UX-Anfragen sind mit QWebEnginePage verbunden. Wann immer ein Authentifikator eine Benutzerinteraktion erfordert, wird eine UX-Anfrage auf QWebEnginePage ausgelöst und das Signal QWebEnginePage::webAuthUxRequested mit QWebEngineWebAuthUxRequest ausgegeben, das in diesem Beispiel an WebView::handleAuthenticatorRequired weitergeleitet wird:

    connect(page, &QWebEnginePage::webAuthUxRequested, this, &WebView::handleWebAuthUxRequested);

Diese Methode erstellt einen WebAuth UX-Dialog und initiiert den UX-Anfragefluss.

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();
}

Das QWebEngineWebAuthUxRequest Objekt sendet in regelmäßigen Abständen das stateChanged Signal aus, um potentielle Beobachter über den aktuellen WebAuth UX Status zu informieren. Die Beobachter aktualisieren den WebAuth-Dialog entsprechend. Siehe webview.cpp und webauthdialog.cpp für ein Beispiel, wie diese Signale gehandhabt werden können.

Signieranforderung für macOS

Um Websites den Zugriff auf den Standort, die Kamera und das Mikrofon zu ermöglichen, wenn Simple Browser auf macOS ausgeführt wird, muss die Anwendung signiert werden. Dies geschieht automatisch bei der Erstellung, aber Sie müssen eine gültige Signieridentität für die Erstellungsumgebung einrichten.

Dateien und Attribute

Das Beispiel verwendet Icons aus der Tango Icon Library:

Tango-SymbolbibliothekÖffentlicher Bereich

Beispielprojekt @ code.qt.io

© 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.