1. 程式人生 > >Qt/QML開發支援下載的簡單瀏覽器

Qt/QML開發支援下載的簡單瀏覽器

之前有篇文章介紹過開發簡易瀏覽器(文章標題:QML開發簡單瀏覽器(載入H5)),當時主要介紹瞭如何使用QML的WebEngineView開發一個可以進行簡單瀏覽網頁的應用(僅僅是進行網頁的瀏覽哦^_^),所以沒有涉及到網頁中下載的情況,這篇文章主要介紹如何利用QML開發出可以進行下載的瀏覽器,實現將網上的圖片和檔案下載到本地。

話不多說,先奉上【乾貨程式碼】,再奉上【效果圖】,最後有【深度解析】。
【乾貨程式碼】如下:
import QtQuick 2.4
import QtQuick.Layouts 1.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Window 2.2
import QtWebEngine 1.2

QtObject {
    id: windowParent
    property bool isInit: true
    // Create the initial browsing windows and open the startup page.
    Component.onCompleted: {
        var firstWindow = windowComponent.createObject(windowParent)
        firstWindow.webView.url = "https://www.baidu.com"
    }

    property Component windowComponent: Window {
        // Destroy on close to release the Window's QML resources.
        // Because it was created with a parent, it won't be garbage-collected.
        id: wnd
        onClosing: destroy();
        visible: true

        width: 800
        height: 600

        property var downloads;
        property QtObject defaultProfile: WebEngineProfile {
            storageName: "Default"
        }

        property WebEngineView webView: webView_
        WebEngineView {
            id: webView_
            anchors.fill: parent

            // Handle the signal. Dynamically create the window and
            // use its WebEngineView as the destination of our request.
            onNewViewRequested: {
                var newWindow = windowComponent.createObject(windowParent)
                request.openIn(newWindow.webView)

                if (windowParent.isInit) {
                    profile.downloadRequested.connect(newWindow.onDownloadRequested)
                    profile.downloadFinished.connect(newWindow.onDownloadFinished)
                    windowParent.isInit = false;
                }
            }
        }

        Rectangle {
            id: downloadView
            visible: false
            width: parent.width - 100
            height: 20
            color: "green"
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter
            property real progress_val;
            Rectangle {
                id: progress_complete
                color: "red"
                anchors.left: parent.left
                width: parent.width*downloadView.progress_val
                height: parent.height
            }
            Label {
                anchors.left: progress_complete.right
                anchors.leftMargin: 10
                anchors.verticalCenter: progress_complete.verticalCenter
                text:parseInt(downloadView.progress_val*100) + "%"
                font.bold: true
                color: "red"
            }
        }
        Timer {
            id: reloadTimer
            interval: 10
            running: false
            repeat: true
            onTriggered: {
                downloadView.progress_val = downloads.receivedBytes / downloads.totalBytes
            }
        }
        function onDownloadRequested(download) {
            downloadView.visible = true
            downloadView.progress_val = download.receivedBytes / download.totalBytes
            var arr = download.path.split('/');
            var name = arr[arr.length-1];
            download.path = "E:/Qt_Test/"+name;
            downloads = download;
            console.log("download->path=", download.path);
            reloadTimer.start();
            reloadTimer.running = true;
            wnd.width = 300;
            wnd.height = 130;
            download.accept();
        }
        function onDownloadFinished(download){
            console.log("onDownloadFinished")
            reloadTimer.stop();
            reloadTimer.running = false;
            if (download.state === 2)
            {
                downloadView.progress_val = 1.0;
            }
            downloadView.visible = false;
            wnd.close();
        }
    }
}
以上為核心程式碼,接下來奉上執行【效果圖】:

【深度解析】:

相比開發簡單瀏覽器,要支援下載,必須要給WebEngineView設定profile屬性,而profile屬性即為WebEngineProfile,檢視Qt幫助,對WebEngineView有如下兩個訊號


所以,當給WebEngineView設定了profile屬性後,當WebEngineProfile傳送downloadRequested訊號時,我們就可以轉入到我們自己的函式中,涉及到訊號與槽,就必須對訊號槽進行連線,因此,在開啟新頁面初次,我們添加了如下程式碼:
if (windowParent.isInit) {
    profile.downloadRequested.connect(newWindow.onDownloadRequested)
    profile.downloadFinished.connect(newWindow.onDownloadFinished)
    windowParent.isInit = false;
}
將傳送下載請求訊號和下載完成訊號與對應的槽函式進行連線。至於為什麼要用windowParent.isInit進行控制,大家都知道,Qt中如果訊號和槽連線多次,那麼當訊號發出時,就會多次呼叫連線的槽函式,為了保證只調一次,這裡做一控制。
這裡需要注意:Qt的幫助有說:
| downloadRequested(WebEngineDownloadItem download)
|This signal is emitted whenever a download has been triggered. The download argument holds |the state of the download. The download has to be explicitly accepted with |WebEngineDownloadItem::accept() or the download will be cancelled by default.
即:在下載請求轉讓我們對應的槽函式onDownloadRequested中後,必須設定download.accept();才表示我們接受下載請求,否則表示我們拒絕下載。
做完以上工作後,我們就可以將網上的圖片和檔案下載到本地了(預設下載路徑為使用者的系統下載目錄)。

接下來介紹一些優化項:

(1)下載的圖片只能下載到使用者的系統下載目錄嗎?不,我們要自己設定自己的下載路徑,因此,這裡就需要對downloadRequested訊號所對應的槽函式onDownloadRequested所帶的引數WebEngineDownloadItem進行設定,如下:

var arr = download.path.split('/');
var name = arr[arr.length-1];
download.path = "E:/Qt_Test/"+name;
這樣,就將預設的下載路徑改變為我們自己的"E:/Qt_Test/"目錄下了。
(2)我們想要獲取下載的進度,怎麼辦?對downloadRequested訊號所對應的槽函式onDownloadRequested所帶的引數WebEngineDownloadItem進行檢視幫助,我們能發現其有兩個屬性 receivedBytes:int 和 totalBytes:int 屬性,通過這兩個屬性,我們就能夠計算出下載進度了^_^。但是問題來了,我們怎樣才能夠實時的去接收這兩個屬性的變化值呢?由於訊號只連線一次,因此不能夠讓訊號多次連線來更新這兩個屬性值,經過搜尋資料以及檢視Qt幫助,我們發現,當接收到downloadRequested訊號轉入對應槽onDownloadRequested以後,只要我們記住這個槽攜帶的引數,實時讀取這個引數的值,就可以更新進度了(個人猜測這個傳入的引數是引用或指標,Qt在幫助中應該告訴開發者的,否則很難想通的,如果不檢視他的demo和幫助,也很少有人想到這裡的),因此設定了一個全域性屬性property var downloads;來記住這個值,然後啟用定時器通過downloads對介面進行更新,這樣,就達到了對下載進度的顯示^_^。

至此,QML開發瀏覽並器支援下載功能基本實現了,接下來就是美化和優化介面的事了,這裡就不一一細細道來。