QML 從入門到放棄 第二卷


第二卷如何更快速的放棄,注重的是C++和QML的交互

<1>記事本。。

 

 

 

(1) 先測試下不在QML創建C++對象,僅僅在main.cpp添加一個屬性函數供調用. 注意只使用槽函數來做到。

TextStreamLoader.h

#ifndef TEXTSTREAMLOADER_H
#define TEXTSTREAMLOADER_H

#include <QObject>
#include <QTextStream>
#include <QDebug>
class TextStreamLoader : public QObject
{
    Q_OBJECT
public:
    explicit TextStreamLoader(QObject *parent = 0);
    void test2(){qDebug()<<"test 2 without slots";}
signals:
    void signal_readFile(QString buffer);
    void signal_error(QString errorMsg);
    void signal_saveFile(QString file,QString buffer);
public slots:
    void slot_readFile(QString file);
    void slot_saveFile(QString file,QString buffer);
    void slot_test(){qDebug() << "test C++";}
    QString slot_getBuffer();

private:
    QString _buffer;
};

#endif // TEXTSTREAMLOADER_H
View Code

 

 

TextStreamLoader.cpp

#include "TextStreamLoader.h"
#include <QFile>
#include <QUrl>
TextStreamLoader::TextStreamLoader(QObject *parent) : QObject(parent)
{
    qDebug() << "Construct the TextStreamLoader";
    connect(this,&TextStreamLoader::signal_saveFile,
            this,&TextStreamLoader::slot_saveFile);
}

void TextStreamLoader::slot_readFile(QString file) // read a file to the _buffer
{

    QUrl url(file);
    QString localFile = url.toLocalFile();


    QFile rfile(localFile);
    if(!rfile.open(QIODevice::ReadOnly))
    {
        QString errorMsg = "Could not open " + file + "\n";
        qDebug() << errorMsg;
        emit signal_error(errorMsg);
        return ;
    }

    QTextStream in(&rfile);
    _buffer = in.readAll();
    emit signal_readFile(_buffer);

    rfile.close();

}

void TextStreamLoader::slot_saveFile(QString file, QString buffer)
{
    QUrl url(file);
    QString localFile = url.toLocalFile();
    QFile wfile(localFile);
    if(!wfile.open(QFile::WriteOnly))
    {
        QString errorMsg = "Could not open " + localFile + "\n";
        qDebug() <<errorMsg;
        emit signal_error(errorMsg);
        return ;
    }

    QTextStream out(&wfile);
    out << buffer;
    wfile.close();
}

QString TextStreamLoader::slot_getBuffer()
{
    return _buffer;
}
View Code

 

 

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "TextStreamLoader.h"
int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    QQmlContext *context = engine.rootContext();


    // 注意對象是在C++里構建
    TextStreamLoader stream_01;
    context->setContextProperty("stream_01",&stream_01);
    // 構建完C++對象


    // 加載我們的QML界面,只能調用槽函數
    qDebug() << "load the main.qml";
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    qDebug() <<engine.rootObjects()[0]->objectName();  // this will be debug "Houdini"

    return app.exec();
}

 

main.qml 用最簡單的測試下我們的TextStreamLoader 里面的 "test()槽函數",一定要是槽函數才能被調用。

main.qml全部都是通過調用C++的對象的槽函數,而C++對象是在main.cpp創建,所以在qml隨時可以訪問 槽函數。

import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Controls 2.1
import QtQuick.Dialogs 1.2
Window
{
    id:root
    objectName: "Houdini"
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
    color:"#202020"
    function loadTextToTextEdit(text)
    {
        textEdit.clear()
        var buffer = stream_01.slot_getBuffer()
        textEdit.append(buffer)

    }
    function saveTextToDisk(file,buffer)
    {
        stream_01.slot_saveFile(file,buffer)
    }


    Column
    {
        id:mainLayout
        padding: 5
        spacing: 10
        Row
        {
            id:buttonLayout
            spacing: 10
            Button
            {
                id:loadButton
                text:"load file"
                highlighted: true
                onClicked:
                {
                    openDialog.open()
                }
            }
            Button
            {
                id:saveButton
                highlighted: true
                text:"save file"
                onClicked:
                {
                    saveDialog.open()
                }
            }

        }
        Rectangle
        {
            height: 1
            width: root.width
            id:menuRect
            color:"brown"
        }

        Flickable
        {
            id:flick
            width: root.width; height: root.height;
            contentWidth: textEdit.paintedWidth
            contentHeight: textEdit.paintedHeight
            clip: true
            function ensureVisible(r)
            {
                if (contentX >= r.x)
                    contentX = r.x;
                else if (contentX+width <= r.x+r.width)
                    contentX = r.x+r.width-width;
                if (contentY >= r.y)
                    contentY = r.y;
                else if (contentY+height <= r.y+r.height)
                    contentY = r.y+r.height-height;
            }
            TextEdit
            {
                width: flick.width
                height: flick.height
                anchors.margins: 10
                focus: true
                id:textEdit
                text: ""
                color:"brown"
                font.family: "Helvetica"
                font.pointSize: 10
                font.bold: true
                cursorVisible: true
                selectByKeyboard: true
                selectByMouse: true
                wrapMode:TextEdit.WrapAnywhere
                onCursorRectangleChanged: flick.ensureVisible(cursorRectangle)

            }
        }


    }
    FileDialog
    {
        id:openDialog
        title: "Please choose a file"
        folder: shortcuts.home
        onAccepted:
        {
            console.log("You chose: " + openDialog.fileUrls)
            stream_01.slot_readFile(openDialog.fileUrls)
            var buffer = stream_01.slot_getBuffer()
            loadTextToTextEdit(buffer)

        }
        onRejected:
        {
            console.log("Canceled")
        }
    }
    FileDialog
    {
        id:saveDialog
        title:"Save to a file"
        folder: shortcuts.home
        selectExisting : false
        onAccepted:
        {
            console.log("Save file : " + saveDialog.fileUrls)
            var text = textEdit.text;
            saveTextToDisk(saveDialog.fileUrl,text);

        }
        onRejected:
        {
            console.log("Canceled")
        }

    }

}
View Code

 

(2)

這次變化使用了QML里的connections.

可以調用C++里的signal,signal帶參數也可以傳遞過來。注意查看新的main.qml

 代碼區別就是讀取用的C++信號讀取的。

保存時用的信號在QML發射,然后調用C++的信號槽鏈接,來執行slot_saveFile()函數。

 main.qml:

import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Controls 2.1
import QtQuick.Dialogs 1.2
Window
{
    id:root
    objectName: "Houdini"
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
    color:"#202020"
    function loadTextToTextEdit(text)
    {
        textEdit.clear()
        var buffer = stream_01.slot_getBuffer()
        textEdit.append(buffer)

    }
    function saveTextToDisk(file,buffer)
    {
        stream_01.slot_saveFile(file,buffer)
    }


    Column
    {
        id:mainLayout
        padding: 5
        spacing: 10
        Row
        {
            id:buttonLayout
            spacing: 10
            Button
            {
                id:loadButton
                text:"load file"
                highlighted: true
                onClicked:
                {
                    openDialog.open()
                }
            }
            Button
            {
                id:saveButton
                highlighted: true
                text:"save file"
                onClicked:
                {
                    saveDialog.open()
                }
            }

        }
        Rectangle
        {
            height: 1
            width: root.width
            id:menuRect
            color:"brown"
        }

        Flickable
        {
            id:flick
            width: root.width; height: root.height;
            contentWidth: textEdit.paintedWidth
            contentHeight: textEdit.paintedHeight
            clip: true
            function ensureVisible(r)
            {
                if (contentX >= r.x)
                    contentX = r.x;
                else if (contentX+width <= r.x+r.width)
                    contentX = r.x+r.width-width;
                if (contentY >= r.y)
                    contentY = r.y;
                else if (contentY+height <= r.y+r.height)
                    contentY = r.y+r.height-height;
            }
            TextEdit
            {
                width: flick.width
                height: flick.height
                anchors.margins: 10
                focus: true
                id:textEdit
                text: ""
                color:"brown"
                font.family: "Helvetica"
                font.pointSize: 10
                font.bold: true
                cursorVisible: true
                selectByKeyboard: true
                selectByMouse: true
                wrapMode:TextEdit.WrapAnywhere
                onCursorRectangleChanged: flick.ensureVisible(cursorRectangle)

            }
        }


    }

    Connections
    {
        target: stream_01
        onSignal_readFile://當讀取文件的時候回觸發這個信號
        {
            var readText = buffer //buffer是signal_readFile(buffer)參數
            textEdit.clear()
            textEdit.append(readText)
        }
    }

    // 讀取文件的窗口
    FileDialog
    {
        id:openDialog
        title: "Please choose a file"
        folder: shortcuts.home
        onAccepted:
        {
            console.log("You chose: " + openDialog.fileUrl)

            //這句話會觸發signal_readFile信號
            stream_01.slot_readFile(openDialog.fileUrl)
        }
        onRejected:
        {
            console.log("Canceled")
        }
    }
    //保存文件窗口
    FileDialog
    {
        id:saveDialog
        title:"Save to a file"
        folder: shortcuts.home
        selectExisting : false
        onAccepted:
        {

            console.log("Save file : " + saveDialog.fileUrl)
            var text = textEdit.text;

            //保存觸發信號,在C++中這個信號會觸發保存
            stream_01.signal_saveFile(saveDialog.fileUrl,text)

        }
        onRejected:
        {
            console.log("Canceled")
        }

    }

}
View Code

 

 

(3)Q_PROPERTY宏,如果你想暴露一些member給QML對象。

Q_OBJECT

Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged)
public:
    QString message(){return _msg;}
    void setMessage(QString msg)
{
_msg = msg;
emit messageChanged();
}

類型如下:
Q_PROPERTY(任意類型 QML訪問屬性名 READ 讀取函數名 WRITE 寫的函數名  NOTIFY 信號觸發)

 

 

<2>Q_INVOKABLE 宏,讓QML可以隨心所以調用函數。跟槽槽函數,信號一樣調用。

 

<3>在C++修改QML對象的屬性,從C++call javaScript

 (1)修改qml root object的對象屬性

qDebug() <<engine.rootObjects()[0]->objectName();  // this will be debug "Houdini"
    QObject *root_object = engine.rootObjects().value(0); // houdini object ,it's the main object

    // set QML object property
    //root_object->setProperty("x",600);
    QQmlProperty::write(root_object, "x", 600);

    // read QML object property
    qDebug() << root_object->property("x").toInt();
    qDebug() << QQmlProperty::read(root_object,"x").toInt();
    
    // read root object child by name
    //QObject *rect = root_object->findChild<QObject*>("rect");
View Code

 (2) 假如qml root object 有個java函數:

function javefunctest(arg)
{
     console.log(arg);
     return "I'm jave script"
}

C++訪問:

QObject *root_object = engine.rootObjects().value(0); // houdini object ,it's the main object
QVariant firstArg("I am C++ arg");
QVariant retValue;

    // call the jave script
QMetaObject::invokeMethod(root_object,
                              "javefunctest",
                              Q_RETURN_ARG(QVariant,retValue),
                              Q_ARG(QVariant,firstArg));

qDebug() << "ret value is " << retValue;

輸出:

qml: I am C++ arg

ret value is QVariant(QString, "I'm java script")

 

<4> EMCA:

(1) 基本類型

var flag =false //a boolean

var x =1,y=2

var str = 'abc' / "abc"

var test = {x:2,y:3}

console.log(test.x) //2

console.log(test.y) //3

test// object



(1) typeof()類型 關鍵字

To query the type of a variable, use the typeof keyword. typeof returns the name of the
type as a string.

var x=1;

typeof(x) //"number"

typeof {x:1} //'object '

typeof typeof { x : 1 } // ’string’ 因為typeof()返回的是字符串.



(2) 轉換類型

1.3333333.toFixed(2) // ’1.33’
7..toString() // ’7’



(3) 可以顯式的把boolean ,number,string轉換成對象:

typeof 1. // ’number’
typeof new Number(1.) // ’object’
typeof new String(’Hi!’) // ’object’
4)

Objects themselves can be expressed using an array or object literal. Arrays have no separate
type, but are specialized objects which use array indexes as properties:
var o = { name: ’Werner’, age: 84 } // allocate simple object
print(o.name, o[age])
// both notations are valid, but [] notation allows generated strings
var a = [’a’, ’b’, 7, 11.]
// an array, equivalent to {’0’: ’a’, ’1’: ’b’, ’2’: 7, ’3’: 11.}
typeof o, a // ’object’, ’object’
View Code

 

(2)函數

函數:所有的函數都會evaluates to something:

function f() {} //evaluates as 'undefined'

function f() {} +1 // evaluates as 1 ,because 'undefined' is casted to 0

(function f() {}) //evaluates to a function object

(function () {return 0;}) () /evaluates as 0

 (3)

for loop :

i 作為了index

 (4)

 

delete p.z // remove p.z

p.z //undefined

 

 (5)在列表中存入,或者在json對象中存在函數

(6) 創建對象new關鍵字

(1)

 

(2)創建對象默認構造函數:

這個時候Point其實就是個類。

 

 為Point類添加個函數.

Each function in JavaScript can be used as a constructor in combination with the new operator.
To support inheritance, each function has a default property named prototype. Objects
created from a constructor inherit all properties from the constructor’s prototype. Consider the
following example:

 

 

 其實 prototype里面的方法屬於派生出來的,如何檢查一個方法,或者一個類對象是原有的:

 

 <2>公司一個小項目

按鈕效果模仿的是Google material design風格。流動起來。參考上篇有詳細代碼.

 

V2:

 

3,CG Browser

New Version:0.00001

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM