WebEngine Quick 纳米浏览器

实现 Web 浏览器使用 WebEngineView QML 类型。

Quick 纳米浏览器 演示如何使用 Qt WebEngine QML types to develop a small web browser application that consists of a browser window with a title bar, toolbar, tab view, and status bar. The web content is loaded in a web engine view within the tab view. If certificate errors occur, users are prompted for action in a message dialog. The status bar pops up to display the URL of a hovered link.

A web page can issue a request for being displayed in fullscreen mode. Users can allow full screen mode by using a toolbar button. They can leave fullscreen mode by using a keyboard shortcut. Additional toolbar buttons enable moving backwards and forwards in the browser history, reloading tab content, and opening a settings menu for enabling the following features: JavaScript, plugins, fullscreen mode, off the record, HTTP disk cache, autoloading images, and ignoring certificate errors.

运行范例

要运行范例从 Qt Creator ,打开 欢迎 模式,然后选择范例从 范例 。更多信息,拜访 构建和运行范例 .

创建主浏览器窗口

When the browser main window is loaded, it creates an empty tab using the default profile. Each tab is a web engine view that fills the main window.

We create the main window in the BrowserWindow.qml 文件使用 ApplicationWindow 类型:

ApplicationWindow {
    id: browserWindow
    property QtObject applicationRoot
    property Item currentWebView: tabs.currentIndex < tabs.count ? tabs.getTab(tabs.currentIndex).item : null
    ...
    width: 1300
    height: 900
    visible: true
    title: currentWebView && currentWebView.title
					

We use the TabView Qt Quick control to create an empty tab view that fills the main window. We set the tab active first, to make sure that the tab item is immediately instantiated:

    TabView {
        id: tabs
        function createEmptyTab(profile) {
            var tab = addTab("", tabComponent);
            // We must do this first to make sure that tab.active gets set so that tab.item gets instantiated immediately.
            tab.active = true;
            tab.title = Qt.binding(function() { return tab.item.title ? tab.item.title : 'New Tab' });
            tab.item.profile = profile;
            return tab;
        }
        function indexOfView(view) {
            for (let i = 0; i < tabs.count; ++i)
                if (tabs.getTab(i).item == view)
                    return i
            return -1
        }
        function removeView(index) {
            if (tabs.count > 1)
                tabs.removeTab(index)
            else
                browserWindow.close();
        }
        anchors.top: parent.top
        anchors.bottom: devToolsView.top
        anchors.left: parent.left
        anchors.right: parent.right
        Component.onCompleted: createEmptyTab(defaultProfile)
					

The tab contains a web engine view that loads web content:

        // Add custom tab view style so we can customize the tabs to include a close button
        style: TabViewStyle {
            property color frameColor: "#999"
            property color fillColor: "#eee"
            property color nonSelectedColor: "#ddd"
            frameOverlap: 1
            frame: Rectangle {
                color: "#eee"
                border.color: frameColor
            }
            tab: Rectangle {
                id: tabRectangle
                color: styleData.selected ? fillColor : nonSelectedColor
                border.width: 1
                border.color: frameColor
                implicitWidth: Math.max(text.width + 30, 80)
                implicitHeight: Math.max(text.height + 10, 20)
                Rectangle { height: 1 ; width: parent.width ; color: frameColor}
                Rectangle { height: parent.height ; width: 1; color: frameColor}
                Rectangle { x: parent.width - 2; height: parent.height ; width: 1; color: frameColor}
                Text {
                    id: text
                    anchors.left: parent.left
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.leftMargin: 6
                    text: styleData.title
                    elide: Text.ElideRight
                    color: styleData.selected ? "black" : frameColor
                }
                Button {
                    anchors.right: parent.right
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.rightMargin: 4
                    height: 12
                    style: ButtonStyle {
                        background: Rectangle {
                            implicitWidth: 12
                            implicitHeight: 12
                            color: control.hovered ? "#ccc" : tabRectangle.color
                            Text {text: "x" ; anchors.centerIn: parent ; color: "gray"}
                        }}
                    onClicked: tabs.removeView(styleData.index)
                }
            }
        }
        Component {
            id: tabComponent
            WebEngineView {
                id: webEngineView
                focus: true
					

使用 动作 type to create new tabs:

    Action {
        shortcut: StandardKey.AddTab
        onTriggered: {
            tabs.createEmptyTab(tabs.count != 0 ? currentWebView.profile : defaultProfile);
            tabs.currentIndex = tabs.count - 1;
            addressBar.forceActiveFocus();
            addressBar.selectAll();
        }
					

使用 TextField Qt Quick Control within a ToolBar to create an address bar that shows the current URL and where users can enter another URL:

    toolBar: ToolBar {
        id: navigationBar
            RowLayout {
                anchors.fill: parent
    ...
                TextField {
                    id: addressBar
    ...
                    focus: true
                    Layout.fillWidth: true
                    text: currentWebView && currentWebView.url
                    onAccepted: currentWebView.url = utils.fromUserInput(text)
                }
					
					

处理证书错误

If the certificate of the site being loaded triggers a certificate error, we call the defer() QML method to pause the URL request and wait for user input:

                onCertificateError: function(error) {
                    error.defer();
                    sslDialog.enqueue(error);
                }
					

We use the MessageDialog type to prompt users to continue or cancel the loading of the web page. If users select Yes ,我们调用 ignoreCertificateError() method to ignore the error and continue loading content from the URL. If users select No ,我们调用 rejectCertificate() method to reject the request and stop loading content from the URL:

    MessageDialog {
        id: sslDialog
        property var certErrors: []
        icon: StandardIcon.Warning
        standardButtons: StandardButton.No | StandardButton.Yes
        title: "Server's certificate not trusted"
        text: "Do you wish to continue?"
        detailedText: "If you wish so, you may continue with an unverified certificate. " +
                      "Accepting an unverified certificate means " +
                      "you may not be connected with the host you tried to connect to.\n" +
                      "Do you wish to override the security check and continue?"
        onYes: {
            certErrors.shift().ignoreCertificateError();
            presentError();
        }
        onNo: reject()
        onRejected: reject()
        function reject(){
            certErrors.shift().rejectCertificate();
            presentError();
        }
        function enqueue(error){
            certErrors.push(error);
            presentError();
        }
        function presentError(){
            visible = certErrors.length > 0
        }
    }
					
					

进入和离开全屏模式

We create a menu item for allowing fullscreen mode in a settings menu that we place on the tool bar. Also, we create an action for leaving fullscreen mode by using a keyboard shortcut. We call the accept() method to accept the fullscreen request. The methdod sets the isFullScreen property to be equal to the toggleOn 特性。

                onFullScreenRequested: function(request) {
                    if (request.toggleOn) {
                        webEngineView.state = "FullScreen";
                        browserWindow.previousVisibility = browserWindow.visibility;
                        browserWindow.showFullScreen();
                        fullScreenNotification.show();
                    } else {
                        webEngineView.state = "";
                        browserWindow.visibility = browserWindow.previousVisibility;
                        fullScreenNotification.hide();
                    }
                    request.accept();
                }
					

When entering fullscreen mode, we display a notification using the FullScreenNotification custom type that we create in FullScreenNotification.qml .

使用 动作 type in the settings menu to create a shortcut for leaving fullscreen mode by pressing the escape key:

    Settings {
        id : appSettings
        property alias fullScreenSupportEnabled: fullScreenSupportEnabled.checked
        property alias autoLoadIconsForPage: autoLoadIconsForPage.checked
        property alias touchIconsEnabled: touchIconsEnabled.checked
        property alias webRTCPublicInterfacesOnly : webRTCPublicInterfacesOnly.checked
        property alias devToolsEnabled: devToolsEnabled.checked
        property alias pdfViewerEnabled: pdfViewerEnabled.checked
    }
    Action {
        shortcut: "Escape"
        onTriggered: {
            if (currentWebView.state == "FullScreen") {
                browserWindow.visibility = browserWindow.previousVisibility;
                fullScreenNotification.hide();
                currentWebView.triggerWebAction(WebEngineView.ExitFullScreen);
            }
            if (findBar.visible)
                findBar.visible = false;
        }
    }
					
					

文件和归属

范例使用的图标来自 Tango 图标库:

Tango 图标库 公共领域

范例工程 @ code.qt.io