QML(Qt6)

B站25号底片编程Qt5/Qt6/QML教程,带你手把手搓一个网易云音乐前端的Qml基础知识部分
记录以供自己查询使用,零基础推荐以视频为主(直接看可能会有的摸不着头脑)
结合个人使用需要,25-28 图像处理相关、29 Blend、38-39 问题汇总 跳过

目录

一、基础概念

1)属性

2)函数

3)基础控件 Item

4)锚布局 Item及子类都支持

二、简单控件

1)Text

2)TextField

3)TextArea

4)Image

三、事件系统

1)鼠标事件

2)键盘事件(代码接上)

3)拖拽事件 (鼠标事件相关)

四、带事件的控件

1)Button(继承自AbstractButton)

2)DelayButton 延时按钮(继承自Button)

3)Switch(继承自AbstractButton)

4)RadioButton 单选按钮(继承自AbstractButton)

五、弹出型窗口控件

1)通用型 Popup

2)Dialog

3)FileDialog(继承自Dialog)

4)FolderDialog(继承自Dialog)

5)ColorDialog 颜色选择对话框

6)FontDialog 字体

7)MessageDialog 弹出一个消息

六、图形效果&动画系统

1)state 状态(每个 Item 都有)

2)Transaction 过渡(代码接上)

3)PropertyAnimation 属性动画

4)Behavior 预设行为动画(写在作业控件中)

6)组合动画

七、图像处理相关(略)

八、图像的混合(高级图像处理)

1)ColorOverlay 颜色覆盖

2)多图片混合 Blend 略

九、投影和发光(模糊略)

1)DropShadow 投影

2)InnerShadow 内阴影

3)Glow 外边框发光

4)RectanglarGlow 内部发光

十、布局系统

1)Row 行布局

2)Column 列布局(类似 Row,无 layoutDirection)

3)Grid 网格布局

4)RowLayout 行布局

5)ColumnLayout 列布局(同RowLayout)

6)GridLayout 网格布局

十一、ListView 列表(视图、代理、模型)(些许抽象)(支持高亮滚动、添加、删除及动画)

1)列表视图

2)高亮

3)表头表尾

4)添加、删除及对应动画

5)Section 分组

6)一些其它方法

十二、自定义控件

1)同级目录

 2)控件目录下

十三、C++ 访问控件

0)main.qml

1)通过C++修改QML文件中的属性

2)使用QML中的信号

3)使用QML中的方法

4)C++中使用信号与槽

十四、QML访问C++

1)访问单一属性

2)访问C++的类

十五、WorkerScript 多线程异步处理数据

0)未使用 WorkerScript 方案

1)搭建异步框架

 2)主线程往子线程传参

3)子线程往主线程传参


一、基础概念

1)属性

1. F1可以查看当前类的属性
2. 拥有所有父类属性
3. 可以自定义属性:type 常见 int、real、double、string、list、color…
        ① default property type name: value
        ② readonly property type name: value // 只读属性,不可修改
        ③ required property 
type name
             name: value // 定义后程序才可运行

2)函数

Component.onCompleted: {        // 在渲染时
        console.log("adfsads")        // 输出内容,同 qDebug()、cout
        aaa.aaaColor = "#333333" // 更改颜色
}

3)基础控件 Item

1. width,height // 略
        z:堆叠层级,大的遮小的,相同时后者遮前者
        visible:true / false // 是否可见
        scale:1.5 // 放大到原来的1.5倍
        rotation:50 // 旋转角度
        opacity:0.5 // 透明度
2. x,y // Rectangle { …… Rectangle { …… } } 内部的偏移以外部偏移后的坐标为原点
3. clip:true // 限制子控件展示在本控件范围内

4)锚布局 Item及子类都支持

1. 给控件命名 id(例 Rectangle { id: aaa }        Rectangle { id: bbb } )
2. anchors.left: aaa.right // bbb的左侧与aaa的右侧相连,此外还有 top、right、bottom
3. anchors.leftMargin: 10 // 左边距为10,可以为负
4. anchors.centerIn: aaa // bbb 永远在 aaa 的中心
5. anchors.verticalCenter: aaa.verticalCenter // bbb在aaa的垂直中心,同理有horizontalCenter
6. anchors.fill: aaa // bbb变形以填充aaa

二、简单控件
1)Text

Text {
        id: text
        width: 150 // 与wrepMode共同决定是否换行
        text: "hello world"
        font.family: "黑体"
        font.pixlSize: 30
        color: "blue"
        wrepMode: Text.NoWrap // 不换行
                        // Text.WordWrap 单词间隙间换行
                        // Text.WrapAnywhere 超过宽度就换行
}

2)TextField

TextField { // 文本输入框
        width: 300
        height: 50
        font.family: "黑体"
        placeholderText: "请输入密码" // 输入文本前的提示
        echoMode: TextInput.Password // 输入的字符以“·”显示
        selectByMouse: true // 鼠标选中
        maximumLength: 3 // 输入字符最大长度
        horizontalAlignment: Text.AlignHCenter // H:输入的文本在水平方向居中
        background: Rectangle { // 设置背景颜色
                anchors.fill: parent // Rectangle填充母控件
                border.color: "red" // 边框颜色
                border.width: 1 // 边框宽度
                color: "yellow" // Rectangle颜色
                radius: 20 // 圆角,since Qt 6.7 有 topLeftRadius、topRightRadius、bottomLeftRadius、bottomRightRadius ← Qt5 可以由 clip 间接实现,
        }
}

补充:horizontalAlignment: Text.AlignHCenter 可以改为
leftPadding: 100 // 输入文本的左边距,同理有rightPadding、topPadding、bottomPadding,高度、字符大小、上或下内边距同时限制可能导致密码字符只显示一半,如下图:

3)TextArea

TextArea { // 输入大文本并进行文本预览的控件,继承自 TextEdit
        id: ttt
        anchors.fill: parent
        font.underline: true //下划线
        wrapMode、font、selectByMouse 等同上
        background: Rectangle { 同上 }
}

4)Image

Image {
        // 支持锚布局
        sourece: "/img/img/pic.png"
}
补充:往项目中添加资源文件
项目根目录右键 → 添加新文件 → Qt - Qt Reasource File → 添加前缀 → 添加文件(选中图片)
目录中选择资源文件右键 → copy path( 删除 “ :” )→ source: "/img/img/pic.png"

三、事件系统
1)鼠标事件

Rectangle {
        ……
        MouseArea {
                anchors.fill: parent // 可以检测到鼠标事件的区域
                hoverEnabled: true // 是否支持鼠标移入移出效果
                onClicked: { // F1 查看 Signals 即为可以触发的事件,on+"事件(首字母大写)"
                       aaa.color = "yellow" // aaa 变为黄色
                }
                onEntered: {
                        aaa.color = "red"
                }
                onExited: {
                        aaa.color = "cyan"
                }

                onWheel: { // 鼠标滚轮事件
                        if ( wheel.angleDelta.y>0 ) {
                                console.log("滚轮往前滚了")
                        }
                        else {
                                console.log("滚轮往后滚了")
                        }
                }

        }

2)键盘事件(代码接上)

        focus: true // 聚焦键盘事件
        Keys.onPressed: {
                if ( event.key
== Qt.Key_A ) {
                        console.log ( "AAAA" )
                }
        }

}

补充:关于等号,“ === ” 比 “ == ” 判断强度更强
var a = 1
var b = "1"
console.log ( a, b, a == b, a === b ) // 结果为 1,1,true,false

3)拖拽事件 (鼠标事件相关)

DropArea { // 大区域,被拖进的区域,不可见的
        width: 300
        height: 300
        Rectangle { // 因可见而被用于设置背景颜色,① 默认为白色
                id: ppp
                anchors.fill: parent
        }
        onEntered: {
                ppp.color = "pink" // ② 有东西被拖进后变为粉色
        }
}

Rectangle { // 小区域,被拖的
        id: aaa
        width: 100
        height: 100
        color: "cyan" // 区分颜色
        Drag.active: area.drag.active // 鼠标区域 area 激活拖动
        MouseArea {
                id: area
                anchors.fill: parent
                drag.target: aaa // 鼠标拖动时,移动的是id为aaa的Rectangle
                drag.axis: XAndYAxis // XAxis 只能水平移动、YAxis 只能垂直移动
                onReleased: ppp.color = "white" // ③ 释放时被拖的区域变(恢复)为白色
        }

}

四、带事件的控件
1)Button(继承自AbstractButton)

Button {
        width:
        height:
        text: "text"
        anchors.centerIn: parent
        icon.source: "……" // 图标在文字前,同TextBesideIcon,还有IconOnly、TextOnly
        display: AbstractButton.TextUnderIcon // 文字在图标下
        onClicked: console.log ( "按钮被按下+松开" ) // 鼠标释放在按钮点击范围内完成
        onCanceled: console.log ( "按钮被取消" ) // 鼠标释放在按钮外完成
        onPressed: console.log ( "按钮被按下" )
        onPressAndHold: console.log ( "按钮被长按" )
}

2)DelayButton 延时按钮(继承自Button)

DelayButton { // 长按触发某事件,有进度条
        delay: int // 延迟时间
        onProgressChanged: {
                console.log ( " 当前进度 ", progress ) // 打印长按延时按钮的进度,0 → 1
        }
        onActivated: { // 槽函数(Activated 是信号 signal ),类似 onClicked
                console.log ( " time out " )
        }
}

3)Switch(继承自AbstractButton)

Switch {
        onPositionChanged : {
                console.log ( "postion: ", position ) //观察属性 position 随按钮位置的变化,type是real
        }
        onVisualPositionChanged : {
                console.log ( "visualpostion: ", position ) //观察属性 visualposition,同position
        }
        onCheckedChanged : {
                console.log ( "checked: ", checked ) // bool类型,判断开关状态
        }
}

4)RadioButton 单选按钮(继承自AbstractButton)

RadioButton{ // 不设置ButtonGroup、在同一个母控件下时,默认多个只能选中一个
        onCheckedChanged : {
                checked? console.log ( "被选中了" ) : console.log ( "被取消选中了" )
        }
}

分组方式 1 —— 用母控件分类

Item{
        RadioButton{
        }
        RadioButton{
        }
}
Item{
        RadioButton{
        }
        RadioButton{
        }
        RadioButton{
        }
}
效果:按钮1、2互斥,按钮3、4、5互斥

分组方式 2 —— ButtonGroup

ButtonGroup {
        id: groupA
        exclusive: false // 组内按钮不互斥
}
ButtonGroup {
        id: groupB
        exclusive: true // 互斥
}
RadioButton{
        y: 0 // 避免重叠
        ButtonGroup.group: groupA // 分组
}
RadioButton{
        y: 40
        ButtonGroup.group: groupA
}
RadioButton{
        y: 80
        ButtonGroup.group: groupA
}
RadioButton{
        y: 120
        ButtonGroup.group: groupB // 分组
}
RadioButton{
        y: 160
        ButtonGroup.group: groupB
}
效果:按钮1、2、3不互斥,按钮4、5互斥


五、弹出型窗口控件
1)通用型 Popup

Button {
        onClicked: {        // 弹出动作需要事件触发,故设置按钮用于弹出窗口
                pop.open() // open()、close() 为 Popup 的方法 Methods,默认点空白区域能关闭
        }
}
Popup {
        id: pop
        modal: true        // 模态对话框 —— 当前对话框未关闭无法点击母控件
        closePolicy: Popup.NoAutoClose // 无法自动关闭
        anchors.centerIn: parent // 因为没有边,所以锚布局只能使用这个
        background: Rectangle{        // 属性 —— 背景,区别于信号Signal 、方法 Method
                anchors.fill: parent
                color: "red"
        }
        onClosed: {        // Closed 信号用于槽函数 onClosed,与 Methods(用于对象本身)区分
                console.log ( "closed。" )
        }
        onOpened:{
                console.log ( "opened。" )
        }
        onAboutToHide : {        // 在关闭之前,同理还有signal:aboutToShow()在打开之前
                consold.log ( "hide..." )
        }
}

补充:其它枚举类型
还有Popup.CloseOnEscape(默认)
Popup.CloseOnPressOutside(默认)
Popup.CloseOnPressOutsideParent(母窗口之外)、
Popup.CloseOnReleaseOutside(松开后)
Popup.CloseOnReleaseOutsideParent

补充:其他属性

2)Dialog

Dialog {
        flags: // 控制是否显示边框上方的标题栏 → 是否为无边框窗口
        modality: NonModal // 非模态,未关闭时可以操作母窗口
        title: string // 未设置时沿用母窗口标题
       
standardButtons: Dialog.Ok | Dialog.Cancel // 默认有Ok,没有cancel,右上关闭“x”close
        onAccepted : {
                console.log ( "ok…" ) // 因为默认有OK所以可直接用,onRejected需先启用cancel按钮
        }
}

补充:其它枚举类型
Qt.WindowModal 未关闭时可以操作与母子窗口没有关系的窗口,例如 cpp 文件中的 widget 与 qml 文件中的 Dialog 没有联系,同一 Item(母)控件下的 Window 无法在 Dialog 关闭前进行操作
Qt.ApplicationModal 未关闭时无法操作其他任何窗口

补充的补充:新建一个widget
1.pro 文件中第一行加入 QT += widgets
2.main.cpp:
        ① #include <QWidget>
        ② main中加入以下代码
                Qwidget subWidget ;
                subWidget.setWindowRitle( "Sub Window" ) ;
                subWidget.show() ;

3)FileDialog(继承自Dialog)

Platform.FileDialog {        // Qt5 需 import Qt.labs.platform 1.1 as Platform 多模块同控件指定版本
        id: pop // 用于弹出窗口
        acceptLabel: "打开" // 按钮重命名,还有rejectLabel
        fileMode: OpenFiles // 选中多个文件,默认OpenFile,还有SaveFile
        onAccepted: {
                console.log (currentFile) // 用低版本的FileDialog
        }
}

4)FolderDialog(继承自Dialog)

Platform.FolderDialog {        // 选择文件夹
        id:pop // 用于弹出窗口
        onAccepted: {
                console.log (currentFolder)
        }
}
since Qt6.3:import QtQuick.Dialogs since
Qt5:import Qt.labs.platform 1.1 as Platform

5)ColorDialog 颜色选择对话框

ColorDialog {        //有默认宽高,继承自系统
        id:pop
        onAccepted: {
                console.log (currenrColor)
        }
}

6)FontDialog 字体

FontDialog {
        id:pop
        onAccepted: {
                console.log (currenrFont.bold, currenrFont.family)        // 复合属性Font.xxx
        }
}

7)MessageDialog 弹出一个消息

Platform.MessageDialog {
        id:pop
        buttons: MessageDialog.Ok | MessageDialog.Cancel
        text: "这是一个消息对话框" //默认有Ok、关闭
}

补充:使用时 pro文件 += widgets,main.cpp:QGuiApplocation → QApplication

六、图形效果&动画系统
1)state 状态(每个 Item 都有)

Rectangle {
        id: btn
        ……
        states: {
                State {
                        name: "normal"
                        PropertyChanges {
                                target: btn
                                color: "red"
                        }
                        PropertyChanges {
                                target: txt
                                text: "放进来了"
                        }
                } // 不要忽略逗号
                State {
                        name: "hovered"
                        PropertyChanges {
                                target: btn
                                color: "yellow"
                        }
                        PropertyChanges {
                                target: txt
                                text: "移出去了"
                        }
                }
        }
        ……

2)Transaction 过渡(代码接上)

        transitions: {
                Transition {
                        from: "normal"
                        to: "hovered"
                        ColorAnimation {
                                target: btn
                                duration: 2000 // 2s
                        }
                },  // 不要忽略逗号
                Transition {
                        from: "hovered"
                        to: "normal"
                        ColorAnimation {
                                target: btn
                                duration: 2000
                        }
                }
        }
        Text {
                id:txt
        }

        MouseArea {
                anchors.fill: parent
                hoverEnabled: true
                onRntered: {
                        btn.state = "hovered"
                }
                onExited: {
                        btn.state = "normal"
                }
        }
}

3)PropertyAnimation 属性动画

1. 方法一:用 Button 触发动画
Button {       
        onClicked: ani1.restart() // ani1.start()
}
PropertyAnimation {
        id: ani1
        target: btn // 上方1) 2) 中的Rectangle
        property: "width" // 发生动画的属性
        from: 100
        to: 400
        easing.type: Easing.OutQuart //枚举类型,不同曲线变速动画效果
}
2. 方法二:直接在目标中使用,程序启动时执行
Rectangle {
        ……
        PropertyAnimation on width { to: 400; duration: 1000 }        //按钮矩形宽度由 100 拉伸 400
}
3. 方法三:把属性 PropertyAnimation 写到过渡动画 Transition 中
Rectangle {
        id: btn
        width: 100
        ……
        transitions: {
                Transition {
                        from: "normal"        // states复用1) 中定义
                        to: "hovered"
                        ColorAnimation {        // 可以直接由 PropertyAnimation 实现,property 改为 "color"
                                target: bth
                                duration: 300
                        }
                        PropertyAnimation {        // 对比方法一少了id、from,easing.type改duration
                                target: btn
                                property: "width" // 发生动画的属性
                                to: 300
                                duration: 300
                        }
                }
                Transition {
                        from: "hovered"
                        to: "normal"
                        ColorAnimation {
                                target: bth
                                duration: 300
                        }
                        PropertyAnimation {        
                                target: btn
                                property: "width"
                                to: 100
                                duration: 300
                        }
                }
        }
}
效果:鼠标移入时,按钮由宽度由100变为300、颜色由红变黄,鼠标移出时恢复

补充:没有id是因为Rectangle有id了,不能重复;少了from,是因为width已经有了;改为duration是为了与颜色过渡动画实现视觉同步

4)Behavior 预设行为动画(写在作业控件中)

Rectangle {
        id: btn
        width: 100
        ……
        Behavior on
width {        // 只有宽度的数值随事件的变化而变化时使用
               
NumberAnimation { duration: 1000 } // 数值动画
        }
        MouseArea {
                ……
                onClicked: {                        // 事件1
                     
  parent.width = 400        // 宽度数值变化时自动使用已经预设的行为动画
                }
                onDoubleClicked: {                // 事件2
                       
parent.width = 700        // 宽度数值变化
                }
        }
}

6)组合动画

ParallelAnimation {        // ani1 与 ani2 一起封装为并行动画
        id: pAni       
        PropertyAnimation {
                id: ani1
                ……
                onStopped: {
                        ani2.restart
                }

        }
        PropertyAnimation {
                id: ani2
                ……
        }
}
Rectangle {
        id: btn
        ……
        MouseArea {
                ……
                onClicked: {
                        pAni.start ()        // 执行并行动画
                }
        }
}
串行动画 SequentialAnimation 同理

七、图像处理相关(略)

25-28 节跳过

八、图像的混合(高级图像处理)
1)ColorOverlay 颜色覆盖

Item {
        id: item
        width: 400
        height: 400
        anchors.centerIn: parent
}
Image {
        id: img1
        scale: 0.1
        source: "/img/img/apple_green.png"
}
ColorOverlay {        // 常用来动态修改ui的图标颜色
        anchors.fill: item        // 若img1,覆盖对象则为图片,而需覆盖的是item控件内可见的某对象
        source: img1             // 进行图形混合的对象
        color: "purple"        // 透明度略
}

2)多图片混合 Blend 略

九、投影和发光(模糊略)
1)DropShadow 投影

Rectangle {
        id: rect
        ……
}
DropShadow {
        anchors.fill: rect        // 大小
        source: rect                // 产生阴影的对象
        horizontalOffset: 10        // 水平偏移
        verticalOffset
        radius: 10        // 模糊半径
        samples: radius * 2        // 采样结果,通常设为radius * 2
        transparentBorder: true        // 边界过渡
}

设模糊半径 radius 效果

设模糊半径 radius & 采样结果 samples 效果

设模糊半径 radius & 采样结果 samples & 边界过渡transparentBorder 效果

2)InnerShadow 内阴影

属性同投影(外阴影)

3)Glow 外边框发光

Glow {
        anchors.fill: rect
        source: rect
        color: "green"
        radius: 10        // 发光往外延伸的半径
        samples: radius * 2        // 固定计算公式
}

设模糊半径 radius 效果

设模糊半径 radius & 采样结果 samples 效果

4)RectanglarGlow 内部发光

RectanglarGlow {
        anchors.fill: rect
        color: "red"
        glowRadius: 10        // 发光半径,距边框 10 像素时渐变消失
        cornerRadius: Math.min(rect.width, rect.height)/2 + glowRadius // 发光圆角,常用公式
}

设发光半径 glowRadius 效果

设发光半径 glowRadius & 圆角半径 cornerRadius 效果


十、布局系统
1)Row 行布局

Row {
        spacing: 10
        anchors.centerIn: parent
        layoutDirection: Qt.RightToLeft        // 从右至左,默认为从左至右
        topPadding: 300        // 向下偏移 300 像素
        Rectangle {
                id: rect1
                width: 100
                height: 100
                color: " red "
        }
        Rectangle {
                id: rect2
                width: 100
                height: 100
                color: " green "
        }
        Rectangle {
                id: rect3
                width: 100
                height: 100
                color: " pink "
        }
}

2)Column 列布局(类似 Row,无 layoutDirection)
3)Grid 网格布局

Grid {
        spacing: 10
        columns: 2        // 列数,一列满两个后自动下一列
        flow: Grid.TopToButtom        // 默认先从左往右
        anchors.centerIn: parent
        Rectangle {
                id: rect1
                width: 100
                height: 100
                color: " red "
        }
        Rectangle {
                id: rect2
                width: 100
                height: 100
                color: " green "
        }
        Rectangle {
                id: rect3
                width: 100
                height: 100
                color: " pink "
        }
}

4)RowLayout 行布局

与 Row 区别:自动拉伸,随窗口大小变化,即固定与初始窗口大小的比例

RowLayout {
        spacing: 10
        anchors.fill: parent
        Rectangle {
                id: rect1
                width: 100
                height: 100
                color: " red "
                Layout.minimumWidth: 50        // 限制最大最小宽高
                Layout.minimumHeight: 50
                Layout.fillHeight: true        // 自动填充母控件
                Layout.fillWidth: true
        }
        Rectangle {
                id: rect2
                width: 100
                height: 100
                color: " green "
                ……
        }
        Rectangle {
                id: rect3
                width: 100
                height: 100
                color: " pink "
                ……
        }
}

5)ColumnLayout 列布局(同RowLayout)
6)GridLayout 网格布局

GridLayout {
        rowSpacing: 10
        columnSpacing: 20
        columns: 2        // 先把网格布局的区域根据columns和Rectangle个数分为2行2列
        anchors.fill: parent
        Rectangle {
                id: rect1
                width: 100
                height: 100
                color: "red"
                Layout.alignment: Qt.AlignHCenter | Qt.AlignTop        // 枚举类型
        }
        Rectangle { …… }
        Rectangle { …… }
}

十一、ListView 列表(视图、代理、模型)(些许抽象)(支持高亮滚动、添加、删除及动画)

必备模块(Qt5):
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Dialogs 1.3
import Qt.labs.platform 1.1 as Platform
import QtGraphicalEffects 1.15
import QtQml.Models 2.15

代理 delegate:每一条数据应该显示的形式
模型 model:数据内容

1)列表视图

ListModel {        // 列表模型
        id: listModel
        ListElement {        // 列表元素 “ 属性名:属性值 ”
                name: " 张三 "
                age: 14
        }
}

ListView {        //继承自 Flickable 可弹动控件
        id: listView
        anchors.fill: parent
        // model: [ "A", "B", "C" ]
        model: listModel
        spacing: 10        // 间距
        cacheBuffer: int        // 提前缓存好还未显示的数据
        delegate: Item {        // 为避免干扰 Rectangle 中样式,由 Rectangle 改为不可见的 Item
                width: listView.width // 通常与母控件等宽,也可使用parent.width
                height: 50

                // 形式一:直接填Text,生成与母控件等宽的列表项
                // 使用 Rectangle 后需要在其中分别定义样式
                // color: "red"
                // border.color: "yellow"
                // border.width: 1


                // Text: {
                //         anchors.centerIn: parent
                //         font.pixelSize: 30

                //         // text: modelData        // 对应 model 字符串数组形式时的文本内容
                //         text: name        // 使用 ListModel 时直接填属性名,还能为 "name + age"
                // }


                // 形式二: 类似窗口中的描述,如直接使用两个 Rectangle 分开显示两个属性
                Rectangle {
                        id: leftRect
                        anchors.left: parent.left
                        anchors.top: parent.top
                        anchors.bottom: parent.bottom
                        width: parent.width/2 - 5        // -5 是因为间距为10
                        color: "red"
                        border.color: "yellow"
                        border.width: 1

                        Text: {                                        // Rectangle 中的 Text
                                anchors.centerIn: parent
                                font.pixelSize: 30
                                text: name
                        }
                }
                Rectangle {
                        id: rightRect
                        anchors.right: parent.right
                        anchors.top: parent.top
                        anchors.bottom: parent.bottom
                        width: parent.width/2 - 5
                        color: "red"
                        border.color: "yellow"
                        border.width: 1

                        Text: {
                                anchors.centerIn: parent
                                font.pixelSize: 30
                                text: age
                        }
                }
        }
}

2)高亮

ListView {
        ……
        onCountChanger: {
                decrementCurrentIndex()        // 确保高亮位置不变,不随数据添加而移动
                // 可以直接用 MouseArea 中的 listView.currentIndex 加一、减一
        }
        highlight: {        // 高亮类别的代理,同表头,删除文本内容,更换color。需先设MouseArea
                ……
                z: 2        //
未设置时 highlight 会被 delegate 覆盖
                function setText (name,age) { // 定义方法供 delegate 中的 MouseArea 调用
                                                                //未设置时 highlight 效果只有高亮没有文本

                        nameHighlightText.text = name
                       
ageHighLightText.text = age
                }
                Rectangle {

                        id: nameHighlightText
                        ……
                }
                Rectangle {

                        id: ageHighlightText
                        ……
                }
        }
        delegate {
                ……

                MouseArea {        // 因为一开始数据展示在delegate上,所以要在 delegate中设置
                        anchors.fill: parent
                        hoverEnabled: true
                        onEntered: {
                                listView.currentIndex = index // 内置属性,当前 delegate 是视图中第几条数据
                                                                      // 设置后可以用 currentItem 调用当前 delegate的Item
                                listView.highlightItem.setText ( name, age )        // 调用 highlight中的函数
                        }
                }
        }
}

3)表头表尾

ListView {
        ……
        footerPositioning: listView.OverlayFooter        // 固定在窗口底部,避免在列表底部被隐藏
        headerPositioning: listView.OverlayHeader
        header: Item { …… } // text 使用字符串如 “姓名”、“年龄” 而非 ListModel 的属性
                                                // 删除 id 因为无用且重复
                                                // 更改 color,其它同 delegate,本质是增加一个格式相同的表头
        footer: Rectangle {        // 本质形式与 header、delegate 通用
                width: listView.width
                height: 50
                color: "pink"
                text: "表尾"
                z: 3        // 避免固定后被滚动的数据内容遮盖
        }
        delegate: Item { …… }
}

4)添加、删除及对应动画

Column {
        spacing: 10
        anchors.right: parent.right
        Button {
                text: "添加一条数据"
                onClicked: {
                        listModel.append ( { name: "王二" + listView.count , age:14 } )
                        // 尾部插入,属性: 属性值
                        // listModel.insert ( 0, { name: "王二" + listView.count , age:14 } )
                        // 指定插入位置 → 头插法
                }
        }
        Button {
                text: "删除一条数据"
                onClicked: listModel.remove (listModel.count - 4)        // 删除倒数第四条数据
        }
}
ListView {
        id: rect1
        anchors.fill: parent
        anchors.rightMargin: 200        //空出右侧放添加和删除的按钮
        model: listModel
        ……
        add: Transition {        // 在最下方添加数据时,被添加数据从初始位置移动到目标位置的动画
                NumberAnimation { properties: "y"; duration: 2000 }              
        }
        addDisplaced: Transition {        // 因新数据加入而移动的旧数据的位置变化的过渡动画
                NumberAnimation { properties: "y"; duration: 300 }
        }
        remove: Transition {        // 被删除数据渐渐消失
                NumberAnimation { properties: "opacity"; from: 1; to: 0; duration: 300 }
        }
        removeDisplaced: Transition {        // 某条数据被删后,其他数据向上滑动的动画
                NumberAnimation { properties: "y"; duration: 300 }
        }
        ……
}

5)Section 分组

ListView {
        ……
        // 动画结束
        section {
                properity: "name"
                labelPosition: ViewSection.CurrentLabelAtStart        // 只在最开始显示当前分组的标签
                criteria: ViewSection.FirstCharacter        // 显示第一个字符
                delegate: Rectangle {        // 分组标签的形式
                        height: 50
                        width: 50
                        radius: 25        // 分组标签的形式:球
                        Text {
                                anchors.centerIn: parent
                                text: section        // 内置属性
                        }
                }
        }
        ……
        // 高亮、表头、表尾、代理部分代码
}

6)一些其它方法

int indexAt (real x, real y)        // 根据坐标返回当前数据的索引
Item itemAt (real x, real y)        // 根据坐标返回当前 item 的内容
Item itemAtIndex (int index)        // 根据索引返回当前 item 的内容
// 获得Item可以直接调用function(),例如:
ListView {
        ……
        delegate: Item {
                ……
                function dy () { console.log ("打印") }
                ……
        }
        MouseArea {
                ……
                onClicked: {
                        ListView.itemAtIndex(0).dy()
                }
        }
}

positionViewAtBeginning ()        // 每次数据更新后,自动定位到开头。如头插法每次查看最新数据
positionViewAtEnd ()                // 数据更新后自动定位到列表尾部
positionViewAtAtIndex ()        // 数据更新后自动定位到指定索引对应的数据的位置

Column {
        ……
        Button {
                text: "添加一条数据"
                onClicked: {
                        listModel.append( { name: "王二" + listView.count, age:14 } )
                        listView.positionViewAtEnd ()
                }
        }
        Button { …… }
}

十二、自定义控件
1)同级目录

1. 创建控件




首字母大写

直接把 main.qml 中 Window 的控件定义放到新建控件文件中

2. 使用控件

同级目录直接调用:MyButton {  }
构建 - 执行qmake - 运行

3. 灵活使用

background 的宽高与 Button 的绑定,调用组件时定义的宽高会覆盖组件文件里原本的定义

 2)控件目录下

1. 创建控件

根目录中创建文件夹 controls,在其中创建 qml 文件 MyRect.qml(新建 txt 文件后改后缀)

添加现有文件,选中刚创建的qml文件

MyRect.qml(把前文 MyButton 中的 background 的 Rectangle 提出来)

2. 使用控件

main.qml 中 import "./controls" 相对路径,然后调用 MyRect {  }
构建 - 执行qmake - 运行

十三、C++ 访问控件
0)main.qml

Window {        // 将用到的定义
        ……
        signal sig()        // 定义信号
        onSig: {
                console.log("sig is triggered")
        }
        function test Arg (arg) {        // 定义函数
                console.log ("the arg is:", arg)
                return "recieved"
        }
        Rectangle {
                objectName: "rect"        // 不是id
                width: 300
                height: 300
                color: "red"
                anchors.centerIn: parent
        }
}

补:id 与 objectName 区别
id 通常用于在QML文件中引用对象。
objectName 主要用于在Qt应用程序的C++代码中引用QML对象。

main.cpp        // 1) 2) 3)中代码连续

1)通过C++修改QML文件中的属性

#include <QQmlProperty>        // 修改qml属性时需要
#include <QGuiApplication>
#include <QmlApplicationEngine>
int main(int argc, char *argv[ ] )
{
        ……
        engine.load(url)

        Qobject* obj = engine.rootObjects().first();        // 只有一个 root 即 Window
        Qobject* rect = obj -> findChild<QObject*>("rect"); // 在Window中找objectName为rect的控件
        QmlProperty(obj, "title").write("hello window");        // 把 Window 的 title 改为 "hello window"
        QmlProperty(rect, "color").write("yellow");        // 把 rect 的 color 属性改为 "yellow"

2)使用QML中的信号

        QmetaObject::invokeMethod(obj,"sig")        // 发出信号sig,QML 中的 onSig() 被触发

3)使用QML中的方法

        Qvariant ret;        // 接收返回值
        QMetaObject::invokeMethod(obj,"testArg", Q_RETURN_ARG(QVARIANT,ret), Q_ARG(QVariant,"import_argc");
        // 调用QML中 Window 定义的方法 testArg,返回类型为 QVARIANT 的参数 ret,传入类型为 QVARIANT (弱类型)的参数 import_argc
        return app.exec( );
}

4)C++中使用信号与槽

1. 创建新的 class(包括头文件)

2. testqmlsignal.h

class TestQmlSignal : public QObject
{
        ……

        public slots:
                void print ();        // 声明当前类的函数

};

3. testqmlsignal.cpp        // 类名大写,文件名小写。定义当前类里的某函数
#include "testqmlsignal.h"
#include "qdebug.h"
TestQmlSignal::TestQmlSignal(QObject *parent)
        : QObject {parent}
{}

void TestQmlSignal::print()
{
        qdebug() << "hello c++";
}

4. main.cpp                // 将信号与槽绑定
#include <testqmlsignal.h>
int main(int argc, char *argv[ ] )
{
        ……
        engine.load(url)

        TextQmlSignal* test = new TestQmlSignal        // 创建C++类文件时定义的类名
        QObject::connect (obj , SIGNAL(sig()) , test , SLOT(print()) );
        // QML文件中obj(Window)发出信号sig()时,触发test中的print()槽函数
        return app.exec( );
}

十四、QML访问C++
1)访问单一属性

main.cpp
#include <……>
#include <QQmlContext>
int main(int argc,char *argv[ ])
{
        ……
        engine.load(url);

        engine.rootContext() -> setContextProperty("globalProperty","assvfbdrb");
        // 注册一个值为assvfbdrb的属性globalProperty
        return app.exec();
}

main.qml
Window{
        ……
        rectangle {
                ……
                Text {
                        id: name
                       
text: globalProperty        // 代码不会自动补全
                }
        }
}

2)访问C++的类

1. 在头文件中定义类的属性

2.  鼠标移动到属性名称上,Alt+Enter
自动在对应cpp文件中生成get、set方法
自动在头文件中生成对应public函数、signals

自动在头文件中属性定义的下方添加:
Q_PROPERTY(int pro1 READ getPro1 WRITE setPro1 NOTIFY pro1Changed FINAL)
// 用 getPro1() 来读int类型的属性pro1,用 setPro1() 来写,用 pro1Changed 来通知变化 

手动在头文件的 public 函数前添加 Q_INVOKABLE 

 3. 把C++类注册为模块的某个控件,在 engine.load(url) 渲染之前完成注册
main.cpp

qmlRegisterType<TestQmlSignal>("TestQmlSignal", 1, 0, "TestQmlSignal");
// <类型>("模块名",主版本号,副版本号,"控件名(一模块可以有多个控件)")

4. qml中使用
main.qml
import TestQmlSignal 1.0
Window {
        ……
        TestQmlSignal {
                id: test
        }
        Rectangle {
                ……
                Text {
                        id: name
                        text: test.pro1
                        Component.onCompleted: {
                                // test.pro1 = 56        //  方法一:直接赋值
                                test.setPro1(66)        // 方法二:使用函数
                        }
                }
        }
}

十五、WorkerScript 多线程异步处理数据
0)未使用 WorkerScript 方案

main.qml
Window {
        ……
        Timer {
                interval: 1        // 每1ms执行一次代码
                running: true        // 运行中
                repeat: true        // 重复运转
                onTriggered: {
                        for (let i = 0; i < listModel.count; i++) {
                                let data = listModel.get (i)        // 获取某一条数据
                                listModel.set( i, {val1: data.val1 + 0.001, val2: data.val2 + 0.001, val3: data.val3 + 0.001, val4: data.val4 + 0.001, val5: data.val5 + 0.001})
                        }
                }
        }
        ListModel {        // 在一开始就往 ListView 中添加一万条数据
                id: listModel
                Component.onCompleted: {
                        for( let i = 0; i<10000; i++) {
                                listModel.append( {vall:0.001, val2:0.002, val3:0.003, val4:0.004, val5:0.005})
                        }
                }
        }
        ……
}

1)搭建异步框架

Window {
        ……
        WorkerScript {
                id: worker
                source: "qrc:/worker.js"        // 创建新js文件后复制的url路径
        }
        ……
}

 2)主线程往子线程传参

worker.js
WorkerScript.onMessage = function func(msg) {        // 重写函数 WorkerScript.onMessage
        // let listModel = msg
        let listModel = msg.model        // 对应onTriggered中传入参数的变化
        for (let i = 0; i < listModel.count; i++) {        // 原本 main.qml-Timer-onTriggered 中的内容
                let data = listModel.get (i)        // 获取某一条数据
                listModel.set( i, {val1: data.val1 + 0.001, val2: data.val2 + 0.001, val3: data.val3 + 0.001, val4: data.val4 + 0.001, val5: data.val5 + 0.001})

        listModel.syne()        // 多线程中有效,将异步处理的数据同步更新到界面中
}

main.qml
Window{
        ……
        Timer {
                ……
               
onTriggered: {
                       
 // worker.sendMessage(listModel)
                        worker.sendMessage({model:listModel, val:111})        // 还可以传送对象
                }
        }
        ……
}

3)子线程往主线程传参

worker.js
WorkerScript.onMessage = function func(msg) {        // 重写函数 WorkerScript.onMessage
        WorkerScript.sendMessage("safvbawrhetjs")
        listModel.syne()        // 多线程中有效,将异步处理的数据同步更新到界面中
}

main.qml
Window {
        ……
        WorkerScript {
                id: worker
                source: "qrc:/worker.js"

                onMessage: {
                        console.log(
messageObject, typeof(messageObject) )
                        // 打印结果:
safvbawrhetjs string
                }
        }
        ……
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值