Qt Quick執行個體之挖頭像,qtquick執行個體頭像

來源:互聯網
上載者:User

Qt Quick執行個體之挖頭像,qtquick執行個體頭像

    Android手機有個挺好的功能,它允許你往案頭上放視窗小組件(widget),有一個叫相框的小組件,可以讓你選擇一張相片,截取一部分,放在相框裡。我案頭上就放了幾個相框,裡面是我女兒的照片,隔陣子換一換,挺喜歡。這次的執行個體受相框小組件啟發而成,我稱之為挖頭像,先看看運行效果。

運行效果    電腦上的運行效果1:
                           圖1 電腦挖頭像    Android手機上運行效果2:

項目建立    項目建立過程參考《Qt Quick 之 Hello World 圖文詳解》,安卓配置參考《Windows下Qt 5.2 for Android開發入門》和《Qt on Android:圖文詳解Hello World全過程》。    項目名稱是PickThumb,Android包名是an.qt.PickThumb,其它的木啥咧。源碼分析C++代碼

    為了能夠讓PickThumb正常退出,我給QGuiApplication安裝了事件過濾器,過濾BACK按鍵。下面是main.cpp檔案:

#include <QGuiApplication>#include <QQmlApplicationEngine>#include <QKeyEvent>class KeyBackQuit: public QObject{public:    KeyBackQuit(QObject *parent = 0)        : QObject(parent)    {}    bool eventFilter(QObject *watched, QEvent * e)    {        switch(e->type())        {        case QEvent::KeyPress:            if( ((QKeyEvent*)e)->key() == Qt::Key_Back )            {                e->accept();                return true;            }            break;        case QEvent::KeyRelease:            if( ((QKeyEvent*)e)->key() == Qt::Key_Back )            {                e->accept();                qApp->quit();                return true;            }            break;        default:            break;        }        return QObject::eventFilter(watched, e);    }};int main(int argc, char *argv[]){    QGuiApplication app(argc, argv);    app.installEventFilter(new KeyBackQuit);    QQmlApplicationEngine engine;    engine.load(QUrl(QStringLiteral("qrc:///main.qml")));    return app.exec();}

    KeyBackQuit類重寫eventFilter()方法來過濾Key_Back按鍵,調用QCoreApplication的quit()方法退出應用。過濾器在main()函數中被安裝到QGuiApplication執行個體上。
QML程式碼分析    該主角登場了,main.qml檔案有200多行代碼,內容如下:
import QtQuick 2.2import QtQuick.Window 2.1import QtQuick.Controls 1.2import QtQuick.Controls.Styles 1.2import QtQuick.Dialogs 1.1Window {    visible: true    width: 480;    height: 320;    minimumHeight: 320;    minimumWidth: 480;    color: "black";    onWidthChanged: mask.recalc();    onHeightChanged: mask.recalc();    Image {        id: source;        anchors.fill: parent;        fillMode: Image.PreserveAspectFit;        visible: false;        asynchronous: true;        onStatusChanged: {            if(status == Image.Ready){                console.log("image loaded");                mask.recalc();            }        }    }    FileDialog {        id: fileDialog;        title: "Please choose an Image File";        nameFilters: ["Image Files (*.jpg *.png *.gif)"];        onAccepted: {            source.source = fileDialog.fileUrl;        }    }    Canvas {        id: forSaveCanvas;        width: 128;        height: 128;        contextType: "2d";        visible: false;        z: 2;        anchors.top: parent.top;        anchors.right: parent.right;        anchors.margins: 4;        property var imageData: null;        onPaint: {            if(imageData != null){                context.drawImage(imageData, 0, 0);            }        }        function setImageData(data){            imageData = data;            requestPaint();        }    }    Canvas {        id: mask;        anchors.fill: parent;        z: 1;        property real w: width;        property real h: height;        property real dx: 0;        property real dy: 0;        property real dw: 0;        property real dh: 0;        property real frameX: 66;        property real frameY: 66;        function calc(){            var sw = source.sourceSize.width;            var sh = source.sourceSize.height;            if(sw > 0 && sh > 0){                if(sw <= w && sh <=h){                    dw = sw;                    dh = sh;                }else{                    var sRatio = sw / sh;                    dw = sRatio * h;                    if(dw > w){                        dh = w / sRatio;                        dw = w;                    }else{                        dh = h;                    }                }                dx = (w - dw)/2;                dy = (h - dh)/2;            }        }        function recalc(){            calc();            requestPaint();        }        function getImageData(){            return context.getImageData(frameX - 64, frameY - 64, 128, 128);        }        onPaint: {            var ctx = getContext("2d");            if(dw < 1 || dh < 1) {                ctx.fillStyle = "#0000a0";                ctx.font = "20pt sans-serif";                ctx.textAlign = "center";                ctx.fillText("Please Choose An Image File", width/2, height/2);                return;            }            ctx.clearRect(0, 0, width, height);            ctx.drawImage(source, dx, dy, dw, dh);            var xStart = frameX - 66;            var yStart = frameY - 66;            ctx.save();            ctx.fillStyle = "#a0000000";            ctx.fillRect(0, 0, w, yStart);            var yOffset = yStart + 132;            ctx.fillRect(0, yOffset, w, h - yOffset);            ctx.fillRect(0, yStart, xStart, 132);            var xOffset = xStart + 132;            ctx.fillRect(xOffset, yStart, w - xOffset, 132);            //see through area            ctx.strokeStyle = "red";            ctx.fillStyle = "#00000000";            ctx.lineWidth = 2;            ctx.beginPath();            ctx.rect(xStart, yStart, 132, 132);            ctx.fill();            ctx.stroke();            ctx.closePath ();            ctx.restore();        }    }    MultiPointTouchArea {        anchors.fill: parent;        minimumTouchPoints: 1;        maximumTouchPoints: 1;        touchPoints:[            TouchPoint{                id: point1;            }        ]        onUpdated: {            mask.frameX = point1.x;            mask.frameY = point1.y;            mask.requestPaint();        }        onReleased: {            forSaveCanvas.setImageData(mask.getImageData());            actionPanel.visible = true;        }        onPressed: {            actionPanel.visible = false;        }    }    Component {        id: flatButton;        ButtonStyle {            background: Rectangle{                implicitWidth: 70;                implicitHeight: 30;                border.width: control.hovered ? 2: 1;                border.color: control.hovered ? "#c0c0c0" : "#909090";                color: control.pressed ? "#a0a0a0" : "#707070";            }            label: Text {                anchors.fill: parent;                font.pointSize: 12;                horizontalAlignment: Text.AlignHCenter;                verticalAlignment: Text.AlignVCenter;                text: control.text;                color: (control.hovered && !control.pressed) ? "blue": "white";            }        }    }    Row {        anchors.horizontalCenter: parent.horizontalCenter;        anchors.bottom: parent.bottom;        anchors.bottomMargin: 20;        id: actionPanel;        z: 5;        spacing: 8;        Button {            style: flatButton;            text: "Open";            onClicked: fileDialog.open();        }        Button {            style: flatButton;            text: "Save";            onClicked: {                forSaveCanvas.save("selected.png");                actionPanel.visible = false;            }        }        Button {            style: flatButton;            text: "Cancel";            onClicked: actionPanel.visible = false;        }    }}

    代碼的邏輯是這樣的:點擊“Open”按鈕開啟一個對話方塊,使用者選擇一張圖片,使用隱藏的Image對象載入,載入成功後觸發Canvas對象繪製圖片;當使用者用手指(或按下滑鼠左鍵)拖動時,選中框中心跟隨手指移動,框內映像是正常亮度;當使用者抬起手指後,彈出操作菜單,如選擇“Save”,則通過一個隱藏的Canvas把選中地區的映像儲存到檔案中。    QML中用到的Row、Button、ButtonStyle、Component、Image、FileDialog等我都有文章講過,參考我的專欄《Qt Quick簡明教程》;MultiPointTouchArea和Canvas沒講過,參考Qt協助吧。這裡咱單說“整個照片變暗而唯有選中框內正常顯示”這種效果的實現。
    我定義了一個id為mask的Canvas,它使用id為source的Image對象繪製圖片。圖片在最底層繪製,然後在它上面繪製使用透明色填充的矩形,於是圖片就變暗了。整個Canvas被分成一個“回”字形,中間是完全透明的矩形,周圍是半透明的。半透明部門由頂部、底部、左面、右面四個矩形組成,分別填充即可。
    圖片是按比例顯示的,等圖片載入成功後,先計算了繪製時需要的目標矩形,繪製時直接引用,避免重複計算。而案頭版本為了適應視窗大小變化,實現了onWidthChanged和onHeightChanged兩個訊號處理器來更新繪製參數。
    當使用者選擇儲存時,把mask的透明地區內的像素挖出來(getImageData),產生一個CanvasImageData對象,交給另一個Canvas對象去顯示,調用它的save()方法把內容寫入到檔案。
        這就是全部了。
    回顧我Qt Quick系列的文章:
  • Qt Quick 簡介
  • QML 語言基礎
  • Qt Quick 之 Hello World 圖文詳解
  • Qt Quick 簡單教程
  • Qt Quick 事件處理之訊號與槽
  • Qt Quick事件處理之滑鼠、鍵盤、定時器
  • Qt Quick 事件處理之捏拉縮放與旋轉
  • Qt Quick 組件與對象動態建立詳解
  • Qt Quick 布局介紹
  • Qt Quick 之 QML 與 C++ 混合編程詳解
  • Qt Quick 影像處理執行個體之美圖秀秀(附源碼下載)
  • Qt Quick 之 PathView 詳解


qt quick與qt designer不同

qt designer是用來設計Qt的C++代碼介面的。designer也有原始碼,就是***.ui那個檔案,編譯的時候,編譯器把***.ui轉換成moc_****.cpp合并到代碼中了。
但是開發C++介面對某些使用者有難度,所以,在Qt現有的代碼基礎上進行擴充,增加了一個QtQuick庫。QtQuick的代碼不是C++,而是qml指令碼(類似於網頁中的javascript)。QtQuick完成qml指令碼到c++的轉換(就像網頁中的javascript不需要編譯,可以直接運行一樣)。這樣不需要編譯,就可以直接改指令碼來實現功能,在嵌入式裝置裡用起來很方便。
 
Qt Widget Based Application與 Qt Quick Application的不同?

不知道你有沒有用過 html 寫網頁.
使用 qml 寫Qt程式. 趕腳就好像在用 html 寫案頭應用一樣. 而且你還不需要考慮瀏覽器的支援.
趕腳很哈皮...
用 widget 寫就和傳統 mfc 比較接近了..
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.