以下笔记来自视频:https://www.bilibili.com/video/BV1Ay4y1W7xd/
QML常用组件¶
可视和不可视元素¶
不可视:如Column垂直分布器
可视:如Button、Label等元素
元素使用示例
元素使用例程可查看手册搜索Qt Quick Controls QML Types
信号和槽操作¶
例如button,按键按下的槽函数,每个控件的元素改变也可以发出一个信号,有对应的槽处理函数。
Button{
text: "click"
//元素改变时的槽处理函数
onTextChanged: {
console.log("hello")
}
//按键按下时的槽处理函数
onClicked: {
console.log("btn click")
text = "hello"
}
}
方法的建立¶
建立方法,function无变量数据类型
Button{
text: "click"
onClicked: {
console.log(add(12,39))
}
function add(num1,num2){
return num1 + num2;
}
}
导入js文件中的方法¶
新建一个js文件,做一个类构造函数
function OperatorNumber(number)
{
this.number = number;
this.add = function(num){
return number + num;
}
this.sub = function(num){
return number - num;
}
}
导入文件后,做一个简单的使用
Button{
text: "click"
onClicked: {
let oper = new BJ.OperatorNumber(888);
console.log('add',oper.add(100))
console.log('sub',oper.sub(100))
}
}
XYZ属性和动态表达式绑定¶
以父对象为参考的坐标偏移,Z越大图层越高
动态表达式可以随着初始配置元素的变化而变化。
测试鼠标带动Button动
布局¶
锚点布局¶
主要是anchors的使用。
先建立一个父对象,然后fill。然后该对象的子对象就可以根据父对象布局
Item{
anchors.fill:parent
Rectangle{
anchors.top: parent.top
//anchors.verticalCenter: parent.top
width: 100
height: 100
color: 'red'
}
}
margin, border ,padding 布局实现
主要是margin的使用,下面这个例子,主要是rect1实现了一个间隔,整体效果是一个顶部、左部、右部的效果
Item{
anchors.fill:parent
Rectangle{
id: rect1
anchors.fill: parent
anchors.topMargin: 100
anchors.leftMargin: 100
color: 'red'
}
Rectangle{
id: rect2
anchors.top: parent.top
anchors.right: parent.right
anchors.left: parent.left
height: rect1.anchors.topMargin
color: 'orange'
}
Rectangle{
id: rect3
anchors.top: rect2.bottom
anchors.left: parent.left
anchors.right: rect1.left
anchors.bottom: parent.bottom
color: 'green'
}
}
行列布局¶
主要是Row和Column的使用,还有ColumnLayer和RowLayer的使用,大致差不多,Column会将元素进行垂直布局,Row会将元素水平布局
Column{
RadioButton{
checked: true
text: '第一项'
}
RadioButton{
text: '第二项'
}
RadioButton{
text: '第三项'
}
}
Row{
RadioButton{
checked: true
text: '第一项'
}
RadioButton{
text: '第二项'
}
RadioButton{
text: '第三项'
}
}
Flow布局¶
超过该布局设定区域后会自动换行,例如
Flow{
anchors.left: parent.left
anchors.right: parent.right
// flow: Flow.TopToBottom
Rectangle{
height: 100
width: parent.width/2
color: 'orange'
}
Rectangle{
height: 100
width: parent.width/2
color: 'red'
}
Rectangle{
height: 100
width: parent.width/2
color: 'green'
}
}
网格布局¶
Grid的使用,类似流布局
Grid {
id: grid
columns: 3 //选择为3列
spacing: 2 //元素间间隔为2
Text { text: "Three"; font.bold: true; }
Text { text: "words"; color: "red" }
Text { text: "in"; font.underline: true }
Text { text: "a"; font.pixelSize: 20 }
Text { text: "row"; font.strikeout: true }
}
基本类型¶
初始化¶
组件初始化运行
基本的数据类型、别名、子元素¶
bool、double、enumeration、int、list、real、string、url、var
基本使用
起别名,给一些对象起别名
子元素children,children只是可以读一级元素的参数
Column{
id:column
Rectangle{
id:rect1
width: 100
height: 100
color: 'black'
}
Rectangle{
id:rect2
width: 100
height: 100
color: 'green'
}
Component.onCompleted: {
console.log(column.children.length)
console.log(column.children[0].color)
console.log(column.children[1].width)
}
}
基础控件¶
CheckBox¶
基础使用
CheckBox {
checked: true
text: qsTr("First")
onCheckedChanged: {
if(checked)
console.log('First')
}
}
Calendar¶
该控件在QtQuick.Controls 1.4中所以需要import QtQuick.Controls 1.4 as Ctr1
基础使用
Column{
Ctr1.Calendar{
minimumDate: new Data(2017,0,1)
maximumDate: new Data()
onSelectedDateChanged: {
let strData = Qt.formatDateTime(selectedDate,"yyyy-MM-dd hh:mm:ss")
console.log(strData)
}
}
}
ComboBox¶
当model文件为数组字符串类型时
ComboBox{
id:combo
model: ['first','second','third']
onActivated: {
console.log(index)
console.log(textAt(index))
console.log(model[index])
}
}
当model文件为项目组时,ListModel建立一个组,ListElement就是具体的项目类型,注意加一个textRole确定Combo显示数据的类型
ComboBox{
id:combo
textRole: 'text'
model: ListModel{
ListElement{
text:'first'
value:100
}
ListElement{
text:'second'
value:200
}
ListElement{
text:'third'
value:300
}
}
onActivated: {
let obj = model.get(index)
console.log(obj.text,obj.value)
}
}
ApplicationWindow¶
该窗口主要包含四个常用空间,菜单栏、工具条、操作空间、状态栏
菜单栏简单使用,MenuSeparator为分割符,加上&后会在名称最前面的那个字母加一个下划线,Ttiggered为点击该项后的功能
MenuBar {
Menu {
title: qsTr("&File")
Action { text: qsTr("&New...") }
Action { text: qsTr("&Open...") }
Action { text: qsTr("&Save") }
Action { text: qsTr("Save &As...") }
MenuSeparator { }
Action {
text: qsTr("&Quit")
onTriggered: {
close()
}
}
}
}
状态栏的使用,注意StatusBar为1.4版本中的
footer: Ctr1.StatusBar{
Row{
Label{
text: 'Menu ' + ApplicationWindow.menuBar.count + ' count'
font.italic: true
}
}
}
工具栏的使用,和菜单栏使用基本差不多
附加属性¶
可以附加到其他组件上面去的属性,如
ListView¶
基础生成¶
model为项目值,delegate项目代理,像对他的属性做一个添加修改
ListView{
id:lqhList
anchors.fill: parent
model: ['第一个项目','第二个项目']
delegate: ItemDelegate {
width:lqhList.width
text: modelData
background:Rectangle{
color: getColor()
function getColor(){
var colorObject = BJ.getColorRandom()
return Qt.rgba(colorObject.red,colorObject.green,colorObject.blue)
}
}
onClicked: console.log(modelData)
}
ScrollBar.vertical: ScrollBar{}
}
封装颜色生成器¶
在js文件中封装一个颜色生成器
function getRangeRandom(max)
{
return Math.floor(Math.random() * max)
}
function getColorRandom()
{
var red = getRangeRandom(256)
var green = getRangeRandom(256)
var blue = getRangeRandom(256)
return {red:red/255,green:green/255,blue:blue/255}
}
function getColorBulider(rgbFunction){
return function(){
var color = getColorRandom()
return rgbFunction(color.red,color.green,color.blue)
}
}
然后再在QML文件中,做一个控制器
property var myColorBulider: Bj.getColorBulider(Qt.rgba)
ListView{
id:lqhList
anchors.fill: parent
model: ['第一个项目','第二个项目']
delegate: ItemDelegate {
width:lqhList.width
text: modelData
background:Rectangle{
color: myColorBulider()
}
onClicked: console.log(modelData)
}
ScrollBar.vertical: ScrollBar{}
}
添加组件¶
Component.onCompleted: {
var modelArray = lqhList.model
modelArray.push('WangWu')
lqhList.model = modelArray
}
Index的使用¶
该实例为查看选中状态的Index和实际的Index是否相同
ListView{
id:lqhList
anchors.fill: parent
model:[
{
name:'ZhangSan',
age : 26
},
{
name:'LiSi',
age : 36
}
]
delegate: ItemDelegate {
width:lqhList.width
text: modelData.name + modelData.age + (lqhList.currentIndex === index ? ' Select':'')
background:Rectangle{
color: myColorBulider()
}
onClicked: {
console.log(JSON.stringify(modelData))
lqhList.currentIndex = index;
}
}
}
ListModel¶
在model中使用
简单使用
ListView{
id:lqhList
anchors.fill:parent
model: ListModel{
ListElement{
name:'zhangsan'
age:67
}
ListElement{
name:'lisi'
age:88
}
}
delegate: Rectangle{
width:lqhList.width
height: lqhList.height*0.1
color: lqhItem.myColorBulider()
Text {
anchors.fill: parent
text: qsTr("name "+name+" age "+age)
font.pointSize: 16
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}
}
初始化时添加model
Component.onCompleted: {
lqhList.model.append({'name':'wangwu','age':77})
myListModel.append({'name':'wemz','age':22})
}
MouseArea的基础使用
MouseArea{
anchors.fill: parent
onClicked: {
var data = myListModel.get(index)
console.log(data.name,data.age)
lqhList.currentIndex = index
}
}
section¶
先选择列表对象中的一个数据,然后可直接可以对选择对象做一个代理
section.property: 'group'
section.delegate: Rectangle{
width:lqhList.width
height: 30
color: lqhItem.myColorBulider()
Text {
anchors.centerIn: parent
text: section
font.pointSize: 20
}
}
附加属性¶
例如
Timer¶
做一个定时器
Timer{
interval:1000//定时时间
repeat:true//是否重复
running:true//是否运行
triggeredOnStart:true//开启前触发一次
onTriggered: {
parent.text = Qt.formatDateTime(new Date(),'yyyy-MM-dd hh:mm:ss')
}
}
Component¶
先建立一个Component组件,给他一个ID,这里的示例给的ID是dateLabelTime
Component.onCompleted: {
for(var i=0; i < 10 ; i++)
{
dateLabelTime.createObject(this,{text:'lqh Example'})
console.log('建立成功')
}
}
自定义一个控件¶
建立一个自定义控件名相同的qml文件,然后将上一个示例中Component的内容写法加到该文件中
然后文件中直接调用该控件就可以了
JS异步加载(失败)¶
function createDateTimeLabelToColumn(nums, cmp, date){
if(cmp && (cmp.status === Component.Ready)){
for(var i = 0;i < nums; i++)
cmp.createObject(this,date)
}
}
Component.onCompleted: {
var cmp = Qt.createComponent('./LqhDateTimeLabel.qml',Component.PreferSynchronous,lqhItem)
createDateTimeLabelToColumn(5,cmp,{colorBuilder:lqhItem.myColorBulider})
}
Loader¶
普通加载组件
加载带参数的路径文件
Loader{id:loader}
Component.onCompleted: { loader.setSource('./LqhDateTimeLabel.qml'{text:'hello',colorBulider:myCo
lorBulider})
}
MouseArea¶
mouse.x,mouse.y可以获取鼠标参数
propageteComposedEvents:true鼠标穿透属性
mouse.accepted = false鼠标穿透属性
Rectangle{
width: 100
height: 100
color: 'orange'
MouseArea{
anchors.fill:parent
onClicked: {
console.log(mouse.x,mouse.y)
}
}
Text {
id: myText
anchors.centerIn:parent
text: qsTr("Hello")
MouseArea{
anchors.fill:parent
propagateComposedEvents: true
onClicked: {
console.log('Hello')
mouse.accepted = false
}
}
}
}
StackView¶
实现页面跳转,一个压栈和出栈的操作
Component{
id:rect1
Rectangle{
anchors.fill:parent
MouseArea{
anchors.fill: parent
onClicked: {
stack.pop()
}
}
}
}
StackView{
id:stack
anchors.fill:parent
initialItem: Component{
Rectangle{
anchors.fill: parent
color: 'red'
MouseArea{
anchors.fill: parent
onClicked: {
stack.push(rect1,{color:lqhItem.myColorBulider()})
}
}
}
}
}
StackLayout¶
栈布局,可以提前设定几个空间,然后通过改变他的currentIndex来改变现在的空间
示例代码
Row{
id:btn
Button{
text: 'Home'
onClicked: {
stack.currentIndex = 0
}
}
Button{
text: 'Text'
onClicked: {
stack.currentIndex = 1
}
}
}
StackLayout{
id:stack
anchors.top: btn.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
currentIndex: 0
Rectangle{
anchors.fill: parent
color: 'red'
}
Rectangle{
anchors.fill: parent
color: 'green'
}
}
对上面的代码做一个简单封装,例如按钮段
Row{
id:header
Component{
id:headerButton
Button{
property string menuText
property var layout
property int index
text: menuText
onClicked: layout.currentIndex = index
}
}
Component.onCompleted: {
var menus = ['Home','Test']
menus.forEach(function(item,index){
headerButton.createObject(header{layout:stack,menuText:item,index:index})
})
}
}
对stackLayout做一个简单封装
StackLayout{
id:stack
anchors.top: header.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
currentIndex: 0
Component{
id: pageComponent
Rectangle{
}
}
Component.onCompleted: {
for(var i=0;i<2;i++){
pageComponent.createObject(stack,{color: lqhItem.myColorBulider()})
}
}
}
Repeater中继器¶
在其中创建的对象父对象为中继器上一级
Row{
objectName:'lqh obj'
Repeater{
model:3
Button{
text: index+''
onClicked: {
console.log(parent.objectName)
}
}
}
}
SwipeView和TabBar¶
SwipeView是可以滑动的视图控件,常和PageIndicator共用
TabBar{
id:headerButton
anchors.left: parent.left
anchors.right: parent.right
currentIndex: view.currentIndex
Repeater{
model:lqhItem.page
TabButton{
text: modelData
onClicked: {
view.currentIndex = index
}
}
}
}
SwipeView{
id: view
currentIndex: 1
anchors.left: parent.left
anchors.right: parent.right
anchors.top: headerButton.bottom
anchors.bottom: parent.bottom
Repeater{
model:lqhItem.page.length
Rectangle {
color: lqhItem.myColorBulider()
}
}
}
PageIndicator {
id: indicator
count: view.count
currentIndex: view.currentIndex
anchors.bottom: view.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
Button的用户自定义¶
在帮助文档中,找Qt 5.12
Qt Quick Controls
Customizing Qt Quick Controls 2
示例代码,记得要给按钮加ID才能自己修改
Button {
id: control
text: qsTr("Button")
contentItem: Text {
text: control.text
font: control.font
opacity: enabled ? 1.0 : 0.3
color: control.down ? "#17a81a" : "#21be2b"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
background: Rectangle {
implicitWidth: 100
implicitHeight: 40
opacity: enabled ? 1 : 0.3
color: control.down ? 'red':'white'
border.color: control.down ? "#17a81a" : "#21be2b"
border.width: 1
radius: 2
}
}
State状态¶
给一个对象定义一个状态实现,主要是要给状态定义一个目标
Rectangle{
id:rect
implicitHeight: 200
implicitWidth: 200
color: 'red'
states: State {
name: "myState"
PropertyChanges {
target: rect
color: lqhItem.myColorBulider()
}
}
MouseArea{
anchors.fill:parent
onClicked: {
rect.state = rect.state === 'myState'?' ':'myState'
}
}
}
还可以使用when来触发状态
states: State {
name: "myState"
when:myMouse.pressed
PropertyChanges {
target: rect
color: lqhItem.myColorBulider()
}
}
状态机的实现¶
其实就是不同的状态下,控件或者其他元素有着不同的实现
SwipeView{
id: view
currentIndex: 1
anchors.top: parent.top
anchors.bottom: btn.top
anchors.left: parent.left
anchors.right: parent.right
Rectangle {
id: firstPage
color: 'red'
}
Rectangle {
id: secondPage
color: 'blue'
}
Rectangle {
id: thirdPage
color: 'green'
}
}
PageIndicator {
id: indicator
count: view.count
currentIndex: view.currentIndex
anchors.bottom: view.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
Button{
id:btn
text: 'button'
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
states:[
State {
when: view.currentIndex === 0
PropertyChanges {
target: btn
text: 'home'
}
},
State {
when: view.currentIndex === 1
PropertyChanges {
target: btn
text: 'help'
}
},
State {
when: view.currentIndex === 2
PropertyChanges {
target: btn
text: 'about'
}
}
]
}
对话框¶
创建一个对话框,注意需要import QtQuick.Dialogs 1.2 as Old
Behavior¶
动画效果,选择需要改变的元素对象,duration,变化时间
Behavior on width {
NumberAnimation{
duration: 1000
}
}
MouseArea{
anchors.fill: parent
onClicked: {
if(rect.width === 100)
{
rect.width = 300
}else
{
rect.width = 100
}
}
}
动画顺序执行和并行¶
顺序执行,组件为SequentialAnimation
SequentialAnimation{
running: true
loops: Animation.Infinite
NumberAnimation{
property: 'x'
target: rect
to: 300
duration: 1000
}
NumberAnimation{
property: 'x'
target: rect
to: 0
duration: 1000
}
}
并行执行,两个动画任务将同时执行
ParallelAnimation{
running: true
NumberAnimation{
property: 'x'
target: rect
to: 300
duration: 1000
}
ColorAnimation {
property: 'color'
target: rect
to: lqhItem.myColorBulider()
duration: 1000
}
}
使用状态来过渡
transitions: Transition {
NumberAnimation{
property: 'x'
target: rect
to: 300
duration: 1000
}
ColorAnimation {
property: 'color'
target: rect
to: lqhItem.myColorBulider()
duration: 1000
}
}
states: State {
name: "demo"
PropertyChanges {
target: rect
x: 300
color:'orange'
}
}
MouseArea{
anchors.fill: parent
onClicked: {
rect.state = rect.state ? '':'demo'
}
}
QML和CPP¶
从QML中获取数据到CPP¶
先在main.qml中定义一个Label控件
现在想要在cpp文件中调用,注意头文件
//获取所有的对象
auto root = engine.rootObjects();
//找到对象中的子对象文件
auto myLabelTest = root.first()->findChild<QObject*>("labelTest");
//定义一个可以获取值的数据类型
QVariant ret;
//调用子对象的方法
QMetaObject::invokeMethod(myLabelTest,"getText",Q_RETURN_ARG(QVariant,ret),Q_ARG(QVariant,"1"));
qDebug()<<ret;
从CPP中注册数据到QML¶
上下文注册,Person类似注册的组件名,注意包含头文件
engine.rootContext()->setContextProperty("Person",&p1);
auto context = engine.rootContext();
context->setContextProperty("Person",&p1);
注册单例文件
连接QML的信号和CPP的槽¶
在QML中建立一个Button信号
然后在CPP文件中
//获取所有的对象
auto root = engine.rootObjects();
//找到对象中的子对象文件
auto myButton = root.first()->findChild<QObject*>("myBtn");
//连接信号和槽
QObject::connect(myButton,SIGNAL(clicked()),&p1,SLOT(clickDemo()));
在QML中建立信号,然后CPP中接收
Button{
property int count: 0
objectName: 'myBtn'
text: 'click'
signal demo_click(int _count)
onClicked: {
count++
if(count>10)
{
demo_click(count)
count = 0
}
}
}
带参数的信号和槽
C++的信号在QML中使用¶
5.12版本,Person是建立的类型名
在QML中注入一个C++类¶
先创建一个C++类,记得继承QQuickItem才能嵌套,因为有个描点,Q_PROPERTY是将变量可以在QML中调用,int只能调用32位
在改变时,发送信号
class Info: public QObject{
Q_OBJECT
Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
Q_PROPERTY(qint32 age READ getAge WRITE setAge NOTIFY ageChanged)
public:
Info();
void setName(QString _name);
QString getName();
void setAge(qint32 _age);
qint32 getAge();
signals:
void nameChanged();
void ageChanged();
private:
QString name;
qint32 age;
};
然后注入类型
然后在QML中建立一个组件,便可以直接使用了
元数据注入(失败)64¶
QML_ELEMENT
函数的附加属性(失败)65¶
可注册但是不可以有实例信息,可使用附加信息,但是不允许创建
画家API继承¶
paint
C++中创建列表类 68需要重看¶
Model¶
c++中建立模型然后QML中调用
如何自建一个listModel和列表项
Web 暂时跳过¶
Material样式¶
材质包
需要包含
在cpp中
main函数中
在pro文件中
Canvas元素¶
自己绘制,requestPaint()是重新画图
Column{
Button{
text: 'click'
onClicked: {
myCanvas.rectColor = 'green'
}
}
Canvas{
id:myCanvas
width: 200
height: 200
property color rectColor: Qt.rgba(1, 0, 0, 1)
onPaint: {
var ctx = getContext("2d");
ctx.fillStyle = rectColor
ctx.fillRect(0, 0, width, height);
}
onRectColorChanged:{
requestPaint()
}
}
}
TableView¶
简单示例,记得包含以下文件
TableView{
anchors.fill: parent
model:ListModel{
ListElement{
title: 'shuihu'
author:'lisi'
}
ListElement{
title: 'sanguo'
author:'zhangshan'
}
}
TableViewColumn {
role: "title"
title: "Title"
width: 100
}
TableViewColumn {
role: "author"
title: "Author"
width: 200
}
style: TabViewStyle{
rowDelegate:Rectangle{
}
itemDelegate:Rectangle{
}
}
}
TreeView (失败)¶
卡在实例注册
