Qt QML 入門 — 使用C++定義QML類型
注冊C++類
注冊可實例化的類型
如果一個C++類繼承自QObject,如果需要在QML中使用創建對象,則需要注冊為可實例化的QML類型。
使用qmlRegisterType()注冊可實例化的QML類型,具體查看qmlRegisterType()的文檔說明。
//Message.cpp
class Message : public QObject
{
Q_OBJECT
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged)
public:
// ...
};
//main.cpp
#include <QtQml>
...
qmlRegisterType<Message>("com.mycompany.messaging", 1, 0, "Message");
...
//aQmlFile.qml
import com.mycompany.messaging 1.0
Message {
author: "Amelie"
creationDate: new Date()
}
注冊不實例化的QML類型
1. qmlRegisterType()不帶參數 這種方式無法使用引用注冊的類型,所以無法在QML中創建對象。
2. qmlRegisterInterface() 這種方式用於注冊C++中的虛基類。
3. qmlRegisterUncreatableType()
4. qmlRegisterSingletonType() 這種方法可以注冊一個能夠在QML中使用的單例類型。
附帶屬性
在QML語法中有一個附帶屬性的概念。
這里使用C++自定義QML類型的時候,也可以定義附帶屬性。
核心的亮點就是
static <AttachedPropertiesType> *qmlAttachedProperties(QObject *object);
和
QML_DECLARE_TYPEINFO()
中聲明 QML_HAS_ATTACHED_PROPERTIES
標志
例如:
//Message.cpp
class Message : public QObject
{
Q_OBJECT
Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged)
public:
// ...
};
//MessageBoardAttachedType.cpp
class MessageBoardAttachedType : public QObject
{
Q_OBJECT
Q_PROPERTY(bool expired READ expired WRITE expired NOTIFY expiredChanged)
public:
MessageBoardAttachedType(QObject *parent);
bool expired() const;
void setExpired(bool expired);
signals:
void published();
void expiredChanged();
};
//MessageBoard.cpp
class MessageBoard : public QObject
{
Q_OBJECT
public:
static MessageBoard *qmlAttachedProperties(QObject *object)
{
return new MessageBoardAttachedType(object);
}
};
QML_DECLARE_TYPEINFO(MessageBoard, QML_HAS_ATTACHED_PROPERTIES)
//在QML中的使用
Message {
author: "Amelie"
creationDate: new Date()
MessageBoard.expired: creationDate < new Date("January 01, 2015 10:45:00")
MessageBoard.onPublished: console.log("Message by", author, "has been
published!")
}
//main.cpp
...
Message *msg = someMessageInstance();
MessageBoardAttachedType *attached =
qobject_cast<MessageBoardAttachedType*>(qmlAttachedPropertiesObject<MessageBoard>(msg));
qDebug() << "Value of MessageBoard.expired:" << attached->expired();
...
MessageBoard這個類中首先實現了static *qmlAttachedProperties(QObject *object),然后又用QML_DECLARE_TYPEINFO(MessageBoard, QML_HAS_ATTACHED_PROPERTIES)聲明MessageBoard為附帶屬性。
水平有限,還請不吝指正!
文章屬原創,轉載請注明轉自>>[Thuai’s blog][http://www.thuai.com]
Qt QML — QML 和 JavaScript 整合
在QML中使用JavaScript
PRAGAM LIBRARY
C/C++中關鍵字#pragam,就是指定編譯器執行一些特定動作的指令。這里也可以在JavaScript中使用這個關鍵字,告訴編譯器生成一個shared library。
因為在QML component 中如果使用了JavaScript文件,則會每個component都會有 獨立的 JavaScript實例 的 copy,但是如果我們不想每個Component都有獨立的JavaScript實例呢?這怎么辦呢?
這時候就需要在JavaScript文件中使用.pragam library告訴編譯器,這個JavaScript文件是可以共享的。
導入JAVASCRIPT
同樣也是使用import來導入JavaScript文件到QML文件中使用。
例如:
import "./myJs" as MyJs
這里我使用相對路徑來指定js文件,也可以使用絕對路徑來指定js文件。為什么這樣寫呢?其實這就是告訴編譯器,需要使用到的qml文件和js文件的目錄位置方便引用解析。如此,你可以將QML文件和js文件按照層級關系和不同的作用,分類放置不用放在和main.qml文件同級目錄,從而有個清晰的項目文件結構。
在JAVASCRIPT文件中引用其他JAVASCRIPT文件
在QtQuick1.x中是不支持,在js文件中引用另外的非無狀態js文件的。
QtQuick2.0版本則支持在一個非無狀態的js文件中引用另外一個無狀態的js文件或者QML模塊。
使用.pragam library定義無狀態的JavaScript。
使用.imports則可以導入其他js文件和QML模塊。
.import TypeNamespace MajorVersion.MinorVersion as Qualifier
使用Qt.include包含其他非無狀態的js文件。文檔中有這樣一個例子,可以很明了的看出如何使用Qt.include
//main.qml
import QtQuick 2.0
import "script.js" as MyScript
Item {
width: 100; height: 100
MouseArea {
anchors.fill: parent
onClicked: {
MyScript.showCalculations(10)
console.log("Call factorial() from QML:",
MyScript.factorial(10))
}
}
}
// script.js
Qt.include("factorial.js")
function showCalculations(value) {
console.log("Call factorial() from script.js:",
factorial(value));
}
// factorial.js
function factorial(a) {
a = parseInt(a);
if (a <= 0)
return 1;
else
return a * factorial(a - 1);
}
script.js中使用Qt.include來包含factorial.js,而在main.qml文件中只是使用了一個qualifier MyScript就訪問了兩個js文中定義的function。
使用JavaScript動態創建QML object
動態創建QML object 有兩種方式:
-
使用Qt.createComponent()
-
使用Qt.createQmlObject()
如果你自定義了一個QML組件,則使用Qt.createComponent()動態創建對象會方便一些。方便重用自定義component。
可以參看我的這篇Qt QML– 動態創建QML objects和銷毀
JavaScript的運行環境
QML引擎提供的JavaScript運行環境和瀏覽器提供的JavaScript運行環境是不一樣的。QML的運行環境實現了”ECMAScript Language Specification”的標准,支持ECMAScript標准的內建類型和函數,如Object,Array,Math等。關於詳細的ECMAScript標准有哪些內建類型,可以參看ECMA-262 第5版的文檔。
水平有限,還請不吝指正,謝謝!
文章屬原創,轉載請注明轉自>>Thuai’s blog
發表在 Qt、Qt QML | 標簽有 QML、Qt | 發表回復
Qt QML — 調試QML程序
使用Console調試
Log
console.log 打印日志信息
console.debug 打印調試信息
console.info 打印普通信息
console.warn 打印警告信息
console.error 打印錯誤信息
Assert
就像C++就的assert,判斷表達式是否成立。QML中的console.assert在表達式不成立時並不會終止程序,而是會打印出錯誤的代碼位置。
console.assert 斷言
Timer
console.time 和 console.timeEnd 用來查看代碼運行所花費的時間。
Trace
在JavaScript的函數中加入console.trace()就可以跟蹤代碼的調用過程,但是只能夠跟蹤最后的10次調用。
Count
console.count 會打印出代碼調用的次數
Profile
JavaScript 函數的性能分析, console.profile 開始,console.profileEnd 結束。
Exception
console.exception 打印出錯誤信息和堆棧調用信息。
包含模塊的調試 (Debugging module imports)
添加環境變量QML_IMPORT_TRACE=1,可以查看import的module是否正確。然后在終端中使用qmlscene命令運行指定的qml文件,終端就會輸出import信息。
水平有限,還請不吝指正,謝謝!
原創文章,轉載請注明轉自>>Thuai’s blog
發表在 Qt、Qt QML | 標簽有 QML、Qt | 發表回復
Qt Quick 2 — QML類型(QML Types)
- 可視類型
- 可視的實用功能項
- 可視項的生成器
- 可視項的變換
- 獲取用戶輸入
- 文本輸入的實用工具項
- 用戶輸入事件
- 位置
- 狀態
- 轉變和動畫
- 與類型相關的動畫
- 底層動畫
- 路徑動畫
- 數據模型
- 視圖
- 數據存儲
- 圖形效果
- 實用方便的類型
- 畫布
注意: 末尾寫了一個2的,表示QtQuick2中才出現的。對於部分新出現的一些類型,我自己也不怎么熟悉,所以暫時不寫中文,免得誤人子弟就不好了。以后我會再次更新這篇文章。
可視類型 (visual types)
- Item — QML 基本的試圖類型,其他可視類型都是從Item繼承來的
- Rectangle — 矩形區域
- Image — 圖片
- BorderImage — 邊框背景
- AnimatedImage — 播放一張GIF圖片
- AnimatedSprite — 播放一系列幀動畫 2
- SpriteSequence — 播放一系列幀動畫中的部分幀 2
- Text — 顯示文本
- Window — 顯示一個頂層窗口 2
可視的實用功能項 (Visual Item Utility)
- Accessible — 提供Component的獲取性 2
- Gradient — 漸變
- GradientStop — 漸變閾值
- SystemPalette — 系統調色板
- Screen — 獲取設備的屏幕寬高橫向參數 2
- Sprite — 顯示特定的Sprite動畫 2
- FontLoader — 字體加載器
可視項的生成器 (Visual Item Generation)
- Repeater — 能夠根據model生成多個可視化的項
- Loader — QML component動態加載器
可視項的變換 (Visual Item Transformations)
- Transform — 變形 2
- Scale — 縮放
- Rotation — 旋轉
- Translate — 平移
獲取用戶輸入 (User Input)
- MouseArea — 鼠標區域
- Keys — 按鍵
- KeyNavigation — 導航鍵 2
- FocusScope — 焦點區域
- Flickable — 橡皮筋區域
- PinchArea — 捏拽區域
- MultiPointTouchArea — 多點觸控區域 2
- Drag –拖動區域 2
- DropArea — 掉落區域 2
- TextInput — 文本輸入區域
- TextEdit — 文本編輯區域
文本輸入的實用工具項 (Text Input Utility)
- IntValidator — 整數校驗器
- DoubleValidator — 雙精度浮點校驗器
- RegExpValidator — 正則表達式校驗器
用戶輸入事件 (User input events)
- TouchPoint — 觸摸點擊事件 2
- PinchEvent — 捏拽事件
- WheelEvent — 鼠標滾輪事件 2
- MouseEvent — 鼠標點擊事件
- KeyEvent — 按鍵事件
- DragEvent — 拖動事件 2
位置 (Positioning)
- Positioner — Item在scene中的位置信息(附帶屬性) 2
- Column — 子對象豎直方向排列
- Row — 子對象水平方向排列
- Grid — 子對象按照網格形式排列
- Flow — 流動
- LayoutMirroring — 布局鏡像(附帶屬性) 2
狀態 (States)
- State — 狀態
- PropertyChanges — 屬性變更
- StateGroup — 狀態組
- StateChangeScript — 狀態變更腳本
- ParentChange — 父對象變更
- AnchorChanges — 錨點變更
轉變和動畫 (Transitions and Animations)
- Transition — 轉變動畫
- ViewTransition — 視圖轉變 2
- SequentialAnimation — 串行動畫序列
- ParallelAnimation — 並行動畫序列
- Behavior — 特定屬性變化時的行為
- PropertyAction — 在動畫序列中執行屬性變更動作
- SmoothedAnimation — 屬性值平滑變化動畫
- SpringAnimation — 彈力動畫
- ScriptAction — 在動畫序列中執行腳本(主要用於動畫中執行腳本)
與類型相關的動畫 (Type-specific Animations)
- PropertyAnimation — 屬性動畫
- NumberAniamtion — 數值動畫
- Vector3dAnimation — Vector3d屬性的動畫
- ColorAnimation — color屬性的動畫
- RotationAnimation — rotation屬性的動畫
- ParentAnimation — parent屬性動畫
- AnchorAnimation — anchor屬性動畫
- PathAnimation — path動畫 2
底層動畫 (Lower-level Animation Types)
- PathInterpolator — path修改器 2
- AnimationController — 動畫控制器 2
路徑動畫 (Animation paths)
- Path — 路徑
- PathLine — 路徑為直線
- PathQuad — 路徑為二次方程式貝爾曲線
- PathCubic — 路徑為三次方程式貝爾曲線
- PathArc — 路徑為弧線 2
- PathCurve — 路徑為曲線 2
- PathSvg — SVG 路徑 2
- PathAttribute — 在path中設置屬性
- PathPercent — 修改path中item的間距
數據模型 (Model and Model Data)
- ListModel
- ListElement
- VisualItemModel
- VisualDataModel
- VisualDataGroup
- XmlListModel
- XmlRole
視圖 (Views)
- ListView
- GridView
- PathView
- Pack age
數據存儲 (Data Storage)
- QtQuick.LocalStorage 2 — 本地存儲模塊 2
圖形效果 (Graphical Effects)
- Flipable
- ShaderEffect 2
- ShaderEffectSource
- GridMesh 2
- QtQuick.Particles 2 2
實用方便的類型 (Convenience Types)
- Connections
- Binding — 綁定器
- Timer — 定時器
- WorkScript
畫布 (Canvas)
- Canvas 2
- Context2D 2
- CanvasGradient 2
- CanvasPixelArray 2
- CanvasImageData 2
- TextMetrics 2
水平有限,還請不吝指正,謝謝!
原創文章,轉載請注明 >> Thuai’s blog
發表在 Qt、Qt QML、Qt Quick | 標簽有 QML | 發表回復
Qt QML– 動態創建QML objects和銷毀
動態創建Qml對象
使用JavaScript動態創建Qml對象有兩種方式,一是使用Qt.createComponent,二是使用Qt.createQmlObject
如果你已經有一個qml文件定義了一個component,則使用Qt.createComponent()這種方式會比較好。如果本身對象就是在Qml運行期生成的對象且簡單,則使用Qt.createQmlObject(),你肯定不想在一條語句中寫上百來行代碼定義一個component對吧!
QT.CREATECOMPONENT
先看代碼
//Sprite.qml
import QtQuick 2.0
Rectangle { width: 80; height: 50; color: "red" }
//main.qml
import QtQuick 2.0
import "componentCreation.js" as MyScript
Rectangle {
id: appWindow
width: 300; height: 300
Component.onCompleted: MyScript.createSpriteObjects();
}
//componentCreation.js
var component;
var sprite;
function createSpriteObjects() {
component = Qt.createComponent("Sprite.qml");
if (component.status == Component.Ready)
finishCreation();
else
component.statusChanged.connect(finishCreation);
}
function finishCreation() {
if (component.status == Component.Ready) {
sprite = component.createObject(appWindow, {"x": 100, "y": 100});
if (sprite == null) {
// Error Handling
console.log("Error creating object");
}
} else if (component.status == Component.Error) {
// Error Handling
console.log("Error loading component:", component.errorString());
}
}
從上面的代碼看出,先自定義了一個qml文件Sprite.qml,這個qml文件中有個寬80、高50、顏色為紅色的Rectangle組件。在main.qml首先是導入了componentCreation.js這個JavaScript腳本,並去了別名MyScript。rootObject則是一個寬300、高300、id為appWindow的Rectangle組件。當這個Rectanle類型的組件加載完成的時候就會調用componentCreation.js中的createSpriteObjects()方法動態創建對象。
核心就是createSpriteObjects()這個方法了。
可以看出使用Qt.createComponet()這種方法動態創建qml對象,需要兩個步驟:1.使用Qt.createComponent(url,parent, mode). url即為自定義好的需要創建qml對象的qml文件。parent和mode通常可以不寫。具體可以查看文檔中Qt.createComponent()的詳細說明。因為Qt.createCompoent()成功則會返回一個Component類型的對象,所以需要第二步。2.使用createObject()創建實例,createObject()方法是Component類型的一個方法。createObject()返回的才是Script.qml的實例。
在上面的這段代碼中,還可以看到,component.statusChanged這個signal連接了一個createScriptObjects.js中的另外一個方法,通過component.status和Component組件中的不同枚舉值對比,進行錯誤的判斷和處理。
總結: Qt.createComponent()這個function是 Qt 這個Qml全局類型中的一個方法,這個方法返回一個Component類型的對象。需要得到自定義qml組件的實例,還需要使用Component.createObject()方法。如果需要使用非阻塞方式創建qml object則可以使用incubateObject()方法。
QT.CREATEQMLOBJECT()
如果需要在qml運行期創建一個qml對象且這個對象定義並不復雜時,則使用Qt.createQmlObject()這種方法比較好。
import QtQuick 2.0
Rectangle {
id: rect
width: 360
height: 360
Text {
anchors.centerIn: parent
text: "Hello World"
}
MouseArea {
anchors.fill: parent
onClicked: {
createObject();
}
}
function createObject()
{
//第一種寫法
/*
var object = Qt.createQmlObject('import QtQuick 2.0;' +
'Rectangle {' +
'width:30; ' +
'height:30;' +
'colo: "red"}', rect, "error");
*/
//第二種寫法
//var newObject = Qt.createQmlObject('import QtQuick 2.0; ' + 'Rectangle { width: 20; height: 20; colo: "red"}', rect, "dynamicSnippet1");
//第三種寫法
var newObject = Qt.createQmlObject('import QtQuick 2.0; \n Rectangle {width: 20; height: 20; colo: "red"}', rect, "error3");
}
}
從上面的代碼可以看出Qt.createQmlObject()有三個參數,第一個為定義qml對象的字符串。第二個為創建的qml對象的父對象。第三個則為出錯時候的文件路徑提示。並且提示會出現行數和列數,可以分別試驗三種寫法看錯誤提示有何不同。
維護動態創建的Qml對象
動態創建Qml對象,你必須知道它所存在的context的生命周期,必須保證context的生命周期比動態創建的Qml對象生命周期要長,因為context銷毀了,其中綁定的動態創建的Qml對象也會失效。動態創建的Qml對象的context取決於它創建的方式。
-
Qt.createComponet(),使用這種方法,則context就是調用這個Qt.createComponent方法的QQmlContext。QQmlContext跟Qt4.8.x中的QDecalartiveContext()其實差不多。
-
Qt.createQmlObject(),使用這種方法,則context就是這個方法的的parent參數。
-
如果使用Component{}定義一個對象,然后在這個Component對象中調用createObject()或者incubateObject()方法,這時動態創建出的對象的context就是這個Component的context。
動態銷毀對象
使用Object.destroy()方法刪除對象。
在多數情況下,你可以使用改變可視對象的opacity值為0或者將它移出屏幕就可以了,而不是delete它。如果你有很多動態創建的對象,delete那些不用的對象,則是值得做的事情。
記住,不要去刪除不是你手動創建的對象,例如Repeater和Loader創建的對象。
例如下面這段代碼中SelfDestroyingRect就不能使用destroy來銷毀它。
Item {
SelfDestroyingRect {
// ...
}
}
水平有限,還請不吝指正,謝謝!
文章屬原創,轉載請注明轉自>>Thuai’s blog
Qt QML—QML signal與signal handler系統
QML 的signal 和 signal handler機制的存在,是為了應用能夠和UI組件之間相互交互。signal就是button clicked、mouse area pressed這些event,signal handler則就是這些事件的響應。
當一個signal emitted,相應的signal handler就會被調用,在signal handler中執行一些scripts或是其他操作,已完成event的響應。
signal和signal handler的定義
signal
signal <signalName>[([<type> <parameter name>[, ...]])]
signal handler
on<SignaleName>
可以參考另一篇文章 Qt QML入門– QML 語法 (2)
Property Change Signal Handler
當Property的值改變了,就會自動emitted一個signal,這種property changed signal,有property Signal handler與之對應。你只需要實現signal handler即可。
on<PropertyName>Changed
使用Connections Type
很多人都知道Qt一個很好的東西,就是signal和slot機制。你可以很方便的connect的signal和slot。QML中的Connections,它能夠讓你接收指定object的任意signal,從而能夠在signal聲明的那個Object之外接收到聲明的signal,從而實現自己想要logic。
import QtQuick 2.0
Rectangle {
id: rect
width: 100; height: 100
MouseArea {
id: mouseArea
anchors.fill: parent
}
Connections {
target: mouseArea
onClicked: {
rect.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1);
}
}
}
上面的代碼中,Connections中的target綁定的是ouseArea,從而在rect中能夠接收到mouseArea的signal,如clicked、pressed、released等等signal。
附帶的signal handler
附帶的signal handler是附帶組件的signal與之對應的signal handler,並不是使用附帶組件本身Object的signal的signal handler。
可以參看另一篇文章 Qt QML入門– QML 語法 (2)
signal與signal的連接,signal與method的連接
signal 都有一個connect()方法,可以連接method或者signal。
可以看下面兩段代碼
signal connect signal
Rectangle {
id: forwarder
width: 100; height: 100
signal send()
onSend: console.log("Send clicked")
MouseArea {
id: mousearea
anchors.fill: parent
onClicked: console.log("MouseArea clicked")
}
Component.onCompleted: {
mousearea.clicked.connect(send)
}
}
注意:mousearea.clicked.connect(send),這里send是signal但是卻沒有加或括號。emitted signal的時候則不論是否signal聲明的時候有無括號,都須要加上括號。
signal connect method
Rectangle {
id: relay
signal messageReceived(string person, string notice)
Component.onCompleted: {
relay.messageReceived.connect(sendToPost)
relay.messageReceived.connect(sendToTelegraph)
relay.messageReceived.connect(sendToEmail)
}
function sendToPost(person, notice) {
console.log("Sending to post: " + person + ", " + notice)
}
function sendToTelegraph(person, notice) {
console.log("Sending to telegraph: " + person + ", " + notice)
}
function sendToEmail(person, notice) {
console.log("Sending to email: " + person + ", " + notice)
}
}
注意:這里使用connect連接是的method,同樣沒有加上括號。
既然能夠connect,那有沒有disconnect呢?當然有。
有時候,你不使用disconnect,你某些動態create的對象都無法distroy。
Rectangle {
id: relay
//...
function removeTelegraphSignal() {
relay.messageReceived.disconnect(sendToTelegraph)
}
}
disconnect跟connect就只是一個單詞差別而已,沒有其他特別不同的地方。
水平有限,還請不吝指正,謝謝!
原創文章,轉載請注明 >> Thuai’s blog
Qt QML—QML 屬性的綁定(Property Binding)
QML 屬性綁定 (Property Binding)
屬性的綁定能夠更好的使用QML的特性-QML object動態行為變化的自動響應。這是QML一個很重要的特性。
注意:綁定的一個很重要的標志就是“:”–冒號
當QML object 的屬性既可以分配一個static value,也可以綁定一個JavaScript表達式,也可以使用JavaScript的自帶的Date Math這些對象。因為QML uses a standards compliant JavaScript engine。
Rectangle {
id: parentRect
width: 200; height: 200
Rectangle {
id: childRect
width: 100; height: parent.height
color: "blue"
}
}
上面的代碼中,childRect的height綁定到了parent的height,當parentRect的height改變的時候,QML engine會重新計算childRect.height
import QtQuick 2.0
Rectangle {
width: 100
height: width * 2
focus: true
Keys.onSpacePressed: {
height = width * 3
}
}
在看上面的這段代碼,Rectangle的height首先綁定了width2,然后在Key.onSpacePressed這個附帶的signal handler中,height 被賦值width3,注意是賦值,不是綁定。
所以之前的bind被這個賦值語句移除了,也就是說以后Rectangle的width變化了,height不會自動變成width的2倍。
如果是想要重新binding而不是賦值,則需要使用Qt.binding()。
this的使用
使用JavaScript綁定時,QML允許使用Javascript中this關鍵字。
例如下面的代碼:
Item {
width: 500
height: 500
Rectangle {
id: rect
width: 100
color: "yellow"
}
Component.onCompleted: {
rect.height = Qt.binding(function() { return this.width * 2 })
console.log("rect.height = " + rect.height) // prints 200, not 1000
}
}
rect.heigth屬性的綁定使用了JavaScript的語句,所以可以使用this,這里的this表示的是Item而不是rect
水平有限,還請不吝指正,謝謝!
原創文章,轉載請注明 >> Thuai’s blog
Qt QML入門– QML 語法 (2)
QML 對象的屬性
一個QML對象所具有的屬性有以下幾種:
- id -id 標識
- property attributes –屬性(其中包括繼承自Item的attributes,自定義的attributes)
- signal attributes –信號
- signal handler attributes –信號處理
- method attributes –函數
- attached propeties and attached signal handler attributes –附帶的屬性 和 附帶的signal handler
id
id 用來標識QML對象,id不能夠以大寫字母開頭,同樣method也不能夠以大寫字母開頭。請記住這點,不然就會有“xxx cannot begin with an upper case letter”這樣的error。 有了id你就能夠通過id引用id為xxx的對象了。所以盡量的給你的每個QML object都寫上id吧! QML object 一旦實例化,id值就無法改變。
Property Attributes
自定義屬性的語法格式:
[default] property <propertyType> <propertyName>
例如:
property int myAge 25;
default property string myName thuai
//MyCustomRectangle.qml
property MyCustomRectangle myCusRect
property color myFavoriteColor: "blue"
default關鍵字表示,該QML object有個默認屬性,你使用這個屬性的時候不需要顯式的聲明。
例如:
// MyLabel.qml
import QtQuick 2.0
Text {
default property var someText
text: "Hello, " + someText.text
}
上面的MyLabel類型中中有個default屬性someText,所以在MyLable使用的時候,
MyLabel {
Text { text: "world!" }
}
//和這段代碼是等效的
MyLable {
someText: Text { text: "world!" }
}
這就是default property
單行語句的分號是可寫不用寫的。但是一行寫多條語句,語句則必須要用分號分隔!
propertyType 可以是QML的基本類型,一種QML對象類型,也可以是QML可以是C++通過Q_PROPERTY宏注冊的類,還可以是JavaScript中的var,var它可以表示任何類型的數據,還可以是自定義的QML類型,如MyCustomRectangle
為PROPERTY ATTRIBUTES賦值
-
一是初始化的時候賦值
import QtQuick 2.0 Item{ x: 10 y: 10 width: 100 height: 100 }
-
一是使用的時候賦值。
import QtQuick 2.0 Rectangle { id: rect Component.OnCompleted: { rect.color = “blue” } }
屬性組(GROUPED PROPERTIES)
將相同的屬性寫成一句代碼 例如:
Text {
//dot notation
font.pixelSize: 12
font.b: true
}
Text {
//group notation
font { pixelSize: 12; b: true }
}
font.pixelSize 和 font.blod用括號括起來寫成了一句,注意 pixelSize和 b之間有個;
屬性別名(PROPERTY ALIASES)
格式: [default] property alias <name>: <alias reference>
// Button.qml
import QtQuick 2.0
Rectangle {
property alias buttonText: textItem.text
width: 100; height: 30; color: "yellow"
Text { id: textItem }
}
Button中的屬性buttonText是textItem.text屬性的別名
Button { buttonText: "Click Me" }
所以對Button的buttonText屬性賦值就相當於給Text的text屬性賦值
注意: Property Aliases 必須在組件所有初始化工作都完成之后,才能夠使用,否則會出錯。
import QtQuick 2.0
Rectangle {
width: 360
height: 360
property alias aText: te
//error before Rectangle initialized completed
//aText: "init text"
Component.onCompleted: {
aText.text = "init text onCompleted"
}
Text {
id: te
anchors.centerIn: parent
}
}
如果你在component初始化完成,對alias properties賦初始值,QtCreator會報“Cannot assign to non-existent property “aText”錯誤!
這里又看到了一個有意思的東西。Component.onCompleted,這是一個QtQuick所有的Object都有的一個附帶信號處理函數。組件初始化創建完成就會觸發這個處理函數。后面在關於signal的文章中有更詳細的講解。
PROPERTY MODIFIER OBJECTS
<PropertyModifierTypeName> on <propertyName> {
// attributes of the object instance
}
這里Property Modifier Objects我沒有用中文,因為我也不知道該譯成什么。具體后面的例子中大家可以看到Property Modifier Objects的應用。如下面代碼中的NumberAnimation
import QtQuick 2.0
Rectangle {
width: 360
height: 360
Rectangle {
id: moveRect
width: 50
height: 50
radius: 5
color: "red"
NumberAnimation on x {
from: 0
to: 100
duration: 200
}
}
}
信號(Signal Attributes)
信號屬性定義語法:
signal <signalName>[([<type> <parameter name>[, ...]])]
[]
表示可選
例如:
//不帶參數和括號
signal mySignal
//帶空括號
signal mySignal2()
//signal 信號名(參數類型 參數名, 參數a類型 參數a)
signal mySignal3(string name, var any)
對應的signal handler的語法格式則是:
on<SignalName>
如上例中的mySignal,對應的signal handler 就寫成 onMySignal
注意: mySignal的第一個字母S成了大寫的了。因為這里onMySignal,on作為開頭了。所以按照QML的語法,mySignal中的m就要寫成M, signal 還可以以下划線_開頭, 比如_mySignal4,對應的signal handler則寫成on_MySignal4,下划線的數目也可以不同。再比如__mySignal4和_mySignal4**就是兩個不同的signal(前者一個下划線,后者兩個下划線)
signal mySignal3的 signal handler:
onMySignal3: {
console.debug("i'm a signal handler")
console.debug("name"en);
}
QML有內建的屬性signal,當屬性的value變化的時候,就會emitted signal. 這個就是文檔中所說的Property Changed Signal
import QtQuick 2.0
Item {
width: 100; height: 100
MouseArea {
anchors.fill: parent
onClicked: {
console.log("Click!")
}
}
}
MouseArea有clicked signal, 當鼠標點擊MouseArea 的時候,就會emit clicked signal。 signal handler onClicked 就會觸發,console 就會打印出“Click!“
Signal Handler是一種特殊的method屬性。當你在QML中文件中聲明一個singal的時候,QML會自動幫你關聯一個signal handler,這個signal handler默認是沒有實現的。所以你只需要實現這個signal handler就可以了,然后在emitted一個signal的時候,與之關聯的signal handler就會自動的被QML引擎調用。
例如文檔中的一個例子:
// SquareButton.qml
Rectangle {
id: root
signal activated(real xPosition, real yPosition)
signal deactivated //注意我沒有括號哦!
width: 100; height: 100
MouseArea {
anchors.fill: parent
onPressed: root.activated(mouse.x, mouse.y) //emit activated signal and parameter
onRelased: root.deactivated() //emit deactivated signal 注意我有括號哦!
}
}
// myapplication.qml
SquareButton {
//implement onActivated signal
onActivated: console.log("Activated at " + xPosition + "," + yPosition)
//implement onDeactivated signal
onDeactivated: console.log("Deactivated!")
}
在SquareButton.qml中的MouseArea,pressed、released都emitted了一個signal。 由此可以看出QML中emitted一個signal,你只需要調用它就行了。
注意:在QML中聲明無參signal你可以不帶括號,但是emitted它的時候,就必須要帶括號了。否則,它不會報錯,但是它也不會emitted signal
而在Qt C++代碼中你要想emittd一個signal,你就必須使用emit <定義的信號>,才能夠emit一個signal。
在myapplication.qml中你使用SquareButton這個自定義的Component時,只需實現下onActivated、onDeactivated這兩個signal handler就行,因為QML已經幫你declare並connected了。
signal不僅有signal handler,它還可以與后面講到的method連接(connect),Qt 一個很重要的機制就是信號和槽機制,其實在QML中也有這個,只是叫法不一樣而已。QML中所有的method都是slot。
PROPERTY CHANGE SIGNAL HANDLERS
語法格式:
on<Property>Changed
signal 有signal handler,property呢? property有property changed signal handler(屬性變化信號處理方法,呵呵有點拗口,翻譯水平不行,不糾結在這種意會的層次,能夠理解就行),既然也是signal hanlder那就不需要你去declare它並關聯到信號了。你也只需要使用它就行了。
例文檔中的:
import QtQuick 2.0
TextInput {
text: "Change this!"
onTextChanged: console.log("Text has changed to:", text)
}
方法(Method Attributes)
QML定義一個method:
function <functionName>([<parameterName>[, ...]]){ <body> }
QML中的method定義不像signal的定義,需要聲明參數(parameter)的類型(type),QML中method的參數類型是var,也只能是var,不論它是在QML中定義的method還是C++中定義的指定參數的method然后注冊到QML system中使用的。
對JavaScript熟悉的朋友,應該知道var這個關鍵字。(此處可以問歌哥或者度娘,建議歌哥,因為度娘都是copy的相似答案)
這是QML定義method的語法格式,C++中的method,可以用Q_INVOKABLE和 slots注冊到QML系統中,這樣QML中就可以訪問你C++中寫的method方法了。具體我會在后面關於C++與QML交互的文章中詳細表述。
這里先暫不寫JavaScript中的method如何在QML object中訪問。先理解QML object定義的method如何正確使用,再拓展其他方法定義的method如何使用。
import QtQuick 2.0
Item {
width: 200; height: 200
MouseArea {
id: msArea
anchors.fill: parent
onClicked: label.moveTo(mouse.x, mouse.y)
}
Text {
id: label
function moveTo(newX, newY) {
label.x = newX;
label.y = newY;
}
text: "Move me!"
}
}
上面的代碼中,id為label的Text中有個method moveTo(newX, newY)
更改label的x、y值,從而移動label,當點擊Item中的任意一點,Item中MouseArea msArea自動emitted clicked signal, signal handler onClicked 自動調用label.moveTo()。
從這個例子,你應該能夠理解了如何定義一個QML method以及如何調用一個QML method。所以我建議我同事都最好每個component都加上id,不要圖少敲幾個鍵,而讓代碼的可讀性降低。
附帶的屬性和附帶的信號處理函數 (Attached Properties and Attached Signal Handlers)
定義的語法格式:
<AttachingType>.<propertyName>
<AttachingType>.on<SignalName>
import QtQuick 2.0
ListView {
width: 240; height: 320
model: 3
delegate: Rectangle {
width: 100; height: 30
color: ListView.isCurrentItem ? "red" : "yellow"
}
}
在上面的代碼中,isCurrentItem就是ListView的一個附帶屬性。ListView還有很多附帶的屬性,詳見QtCreator中的幫助。
Component.onCompleted則是一個附帶的Signal handler。
水平有限,還請不吝指正!
非常感謝網友lei,為本文勘正
原創文章,轉載請注明 >> Thuai’s blog
發表在 Qt、Qt QML | 標簽有 QML、Qt | 發表回復
Qt QML入門– QML 語法 (1)
一個qml文件的結構
- 需要包含的模塊
- 唯一一個root對象
包含模塊
import ModuleIdentifier Version.Number [as <Qualifier>]
例如:
//HelloWorld.qml
import QtQuick 2.0
或者
import QtQuick 2.0 as Quick
詳見文檔–Import Statements
有且僅有一個ROOT對象
import QtQuick 2.0
Rectangle { width: 200; height: 200; color: "red" }
Rectangle { width: 200; height: 200; color: "blue" } // invalid!
這種在一個qml文件中寫兩個root object是錯誤的,只能有一個root object,下面的代碼就是正確的形式。
import QtQuick 2.0
Rectangle { width: 200; height: 200; color: "red" }
qml的基本語法
模塊的包含
QML 模塊的包含,只能使用關鍵字 import 包含qml文件需要用到模塊,import關鍵字后面的內容可以有三種:
- 注冊的帶有版本好的模塊
- 指定路徑的qml文件
- 一個JavaScript文件
例如:
import QtQuick 2.0
import QtQuick.LocalStorage 2.0 as Database
import "../privateComponents"
import "somefile.js" as Script
其中第一種和第二種都是包含帶有版本號注冊的模塊。
對象的聲明
Rectangle { width: 100 height: 100 color: "red" }
上面的代碼就使用Rectangle元素聲明了一個寬100,高100,顏色為red的對象。
子對象的聲明
例1
import QtQuick 2.0
Rectangle {
id: rect
width: 100
height: 100
gradient: Gradient {
id: grad
GradientStop {id: gStopYellow; position: 0.0; color: "yellow" }
GradientStop {id: gStopGreen; position: 1.0; color: "green" }
}
}
例2
import QtQuick 2.0
Rectangle {
id: rect
width: 200
height: 200
color: "red"
Text {
id: text
anchors.centerIn: parent
text: "Hello, QML!"
}
}
上面的兩個例子都使用了子對象。這里引出了一個概念
- QML對象樹中,的父子關系
- 試圖上的父子對象
例1中Rectangle對象rect中的gradient屬性中有個子對象grad,grad中又有兩個GradientStop子對象。這些父子對象的上下文環境(context)是QML object tree 中的父子對象關系 ,而不是visual scene 上下文環境的父子對象關系。
例2中的Text與Rectangle的父子關系即是object tree中的父子關系也是visual scene中的父子對象關系。
注釋
和大多數語言一樣,有單行注釋// 和 塊注釋 /* */
水平有限,還請不吝指正,謝謝!
非常感謝網友lei,為本文堪正!
原創文章,轉載請注明轉自 >> Thuai’s blog