尧图精选

QML笔记

🕒 发布时间:2026/9/3 22:37:44 📁 来源:尧图网络
1. windowimport QtQuick import QtQuick.Controls //控件库button等 Window { //root控件 其父窗口是主界面 width: 640 //width等叫做属性 height: 480 visible: true title: qsTr(Hello World) x:200 //刚开始显示时的坐标 y:200 property int myValue: 0 //自定义属性也会自动产生信号MyValueChanged和槽函数onMyValueChanged signal mysig() //自定义了一个信号 onMysig: { //无论是自定义信号还是默认信号槽函数名称都是固定的名字叫on信号名(首位大写) } onWidthChanged: { //信号widthchanged的槽函数(宽度改变时触发) console.log(width:,width) } Button { id: but1 width:50 height:50 objectName: but1 background: Rectangle { //background:按键的背景rectangle:以矩形作为按钮的背景 border.color: but1.focus?blue:black //判断该按键是否是焦点从而绘制边框颜色 } onClicked:{ console.log(but1 is clicked) } //Keys.onLeftPressed是一个键盘事件处理器只会在当前拥有焦点的元素上触发。 Keys.onRightPressed: { //键盘左键则把焦点移到button1 but2.focustrue //把焦点给but2 } } Button { id: but2 width:50 height:50 objectName: but2 x:100 y:0 background: Rectangle { border.color: but2.focus?blue:black //判断该按键是否是焦点 } onClicked:{ console.log(but2 is clicked) } Keys.onLeftPressed:{ but1.focustrue } } onActiveFocusItemChanged: { //activeFocusItem保存了焦点控件可以确定焦点位置 console.log(active focus changed ,activeFocusItem, activeFocusItem.objectName) } }2. 自定义控件使用另一个qml文件的自定义控件时命名开头必须大写然后要在pro中添加。然后可以在main.qml文件中直接使用文件名来调用里面的控件。import QtQuick 2.15 import QtQuick.Controls 2.15 Window { id: win1 width: 640 height: 480 visible: true title: qsTr(Hello World) Rectangle { id:rect1 x:50 y:50 width:100 height:50 color:black focus:true //获得焦点 property int num:0 MouseArea { //这里是声明了一个子元素声明子元素不需要: anchors.fill:parent //填充满父元素 onClicked:{ rect1.num //使用父元素的属性要标明 console.log(lets go , rect1.num) } } Keys.onReturnPressed: { //当为焦点时回车键才会对这个rectangle执行 num console.log(lets go ,num) } } Rectangle{ id: rect2 width: 100 height: 50 color:blue //anchor锚点: anchors.left: rect1.right //rect2的左边和rect1的右边对齐 anchors.top: rect1.top //rect2的上边和rect1对齐 anchors.leftMargin: 20 //左边空20 anchors.topMargin: 50 } Rectangle{ id: rect3 width: 100 height: 50 color:red //anchors.centerIn: parent //居中于父控件 anchors.horizontalCenter: parent.horizontalCenter //x横向居中 verticalCenter是纵向 } Apy { //Apy.qml是一个文件直接使用文件名调用里面的最顶层的控件 x:200 y:300 } }3.QML-statesqml中states语句有点像if语句。qml中有if else语句语法4.component和loader可以实现一个自定义组件的复用import QtQuick import QtQuick.Controls Window { width: 640 height: 480 visible: true title: qsTr(Hello World) color: white Component.onCompleted:{ //window组件创建时会自动调用该槽函数 console.log(oncompleted ,width,height,x,y) } Component.onDestruction: { console.log(ondestruction) } //自定义组件。不会自动显示需要用loader加载。 //作用是封装一个控件然后可以被多个loader调用 Component{ id:com Rectangle{ width:100 height:50 x:300 color:red } } //loader可以加载component自定义控件可以多个loader加载一个component做到复用 Loader{ //loader元素 id:myloader asynchronous: true //异步。如果加载的控件很大很慢用异步的话程序不会卡住这时候状态status为2可以做一些等待动画之类 //source:./Apy.qml //source可以加载其他文件的控件 sourceComponent: com //sourceComponent加载一个本文件的控件 onStatusChanged: { //加载控件状态改变槽函数 console.log(status: .status) } width:500 //可以直接操作加载的控件 } Button{ y:300 width:100 height:100 onClicked: { //myloader.sourceComponent null //关闭component myloader.item.width 200 //在loader外部要通过loader的item属性来操作加载的属性 myloader.item.colorblue } } }5.信号与槽第一种方法首先是直接使用默认产生的槽汉数如onClicked等缺点只能用于当前对象或其子对象中定义的信号第二种方法使用Connections连接自定义的信号和槽需要连接非当前对象或其子对象的信号时第三种方法connect不常用6.自定义组件与信号槽的使用main.qml:import QtQuick import QtQuick.Controls Window { id: win width: 640 height: 480 visible: true title: qsTr(Hello World) Component{ //实现另一个文件的组建的具体内容 id:com Button{ signal btnSig(int value) //定义一个信号 onClicked: { btnSig(10) } } } Test{ com1:com //设置另一个文件的属性 com2:com } }Test.qml:import QtQuick Rectangle { width:300 height:200 property Component com1 //先自定义两个组件属性com1 com2然后在main.qml里赋值 property Component com2 border.color: black Loader{ id:loader1 sourceComponent: com1 //加载组件没有具体的组件button等 //确定位置 anchors.bottom: parent.bottom anchors.bottomMargin: 20 anchors.left: parent.left anchors.leftMargin: 20 Connections{ target: loader1.item //这里必须用loader1.item来确定loader的组件不能直接用com1 ignoreUnknownSignals: true //如果没有相应的信号那么忽略对应的槽函数 function onBtnSig(value){ //btnSig的槽函数 console.log(left,value) } } } Loader{ id:loader2 sourceComponent: com2 anchors.bottom: parent.bottom anchors.bottomMargin: 20 anchors.right: parent.right anchors.rightMargin: 20 Connections{ target: loader2.item //和上面那个一样 ignoreUnknownSignals: true function onBtnSig(value){ //btnSig的槽函数 console.log(right,value) } } } }7.qml与C交互MVC设计结构view model controllerview显示UIQMLmodeldata获取QML需要的数据设置QML的属性获取QML端的数据controller暴露外部接口控制model的数据QML 和 C 交互是 Qt 开发的核心它允许你将强大的后端逻辑C与灵活漂亮的前端界面QML结合起来。主要有三种方式(1)将 C 对象暴露为 QML 上下文属性适用于注入单个实例或全局对象。(2)将 C 类型注册为可实例化的 QML 类型允许 QML 像使用内置类型一样new出 C 对象。(3)在 C 中直接加载和操作 QML 对象适用于从 C 端查找和修改 QML 元素。下面我们分别详细说明这三种方法。方法一将 C 对象暴露为 QML 上下文属性这是最简单直接的方法特别适合将一些单例式的、工具类的或管理器式的 C 对象暴露给 QML。第一步创建 C 类并添加 QML 交互特性你的 C 类需要继承自QObject并使用Q_PROPERTY暴露属性使用Q_INVOKABLE暴露方法使用signals暴露信号。myclass.h:#include QObject #include QString // 必须继承自 QObject class MyClass : public QObject { Q_OBJECT // 定义一个可读可写的属性 message并带有通知信号 messageChanged Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged) public: explicit MyClass(QObject *parent nullptr); // Q_INVOKABLE 宏使得这个方法可以被 QML 调用 Q_INVOKABLE void showMessage(const QString msg); // 属性的 READ 函数 QString message() const; // 属性的 WRITE 函数 void setMessage(const QString message); signals: // 属性的通知信号要用signals声明 void messageChanged(); // 一个普通信号可供 QML 连接 void somethingHappened(); private: QString m_message; // 私有成员变量存储属性值 };myclass.cpp:#include myclass.h #include QDebug MyClass::MyClass(QObject *parent) : QObject(parent) { } void MyClass::showMessage(const QString msg) { qDebug() C received message from QML: msg; emit somethingHappened(); // 触发信号 } QString MyClass::message() const { return m_message; } void MyClass::setMessage(const QString message) { if (m_message ! message) { m_message message; emit messageChanged(); // 属性改变时必须发射通知信号 } }第二步在 main.cpp 中暴露对象给 QMLmain.cpp#include QGuiApplication #include QQmlApplicationEngine #include QQmlContext // 需要包含这个头文件 #include myclass.h int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; // 1. 创建 C 对象 MyClass myObj; // 2. 将对象设置为 QML 根上下文的属性 // 参数字符串名在QML中使用实际对象 engine.rootContext()-setContextProperty(myExposedObject, myObj); // 3. 加载 QML 文件 engine.load(QUrl(QStringLiteral(qrc:/main.qml))); return app.exec(); }第三步在 QML 中使用暴露的对象main.qml:import QtQuick 2.15 import QtQuick.Controls 2.15 ApplicationWindow { width: 400 height: 300 visible: true // 访问暴露的 C 对象的属性双向绑定 Text { text: myExposedObject.message // 读取 C 属性 anchors.centerIn: parent } Button { text: Call C Method anchors.bottom: parent.bottom onClicked: { // 调用 C 的 Q_INVOKABLE 方法 myExposedObject.showMessage(Hello from QML!); // 写入 C 属性这会触发 messageChanged 信号更新上面的 Text myExposedObject.message New message set by QML; } } Connections { target: myExposedObject // 连接 C 对象发出的信号 onSomethingHappened: { console.log(The C object emitted somethingHappened!); } // 连接属性改变的通知信号 onMessageChanged: { console.log(The message property changed to:, myExposedObject.message); } } }优点简单快捷适合全局对象。缺点对象名称在 QML 中是硬编码的字符串不利于重构不适合需要创建多个实例的场景。方法二将 C 类型注册为可实例化的 QML 类型这种方法将你的 C 类变成一个 QML 元素就像Button或Rectangle一样可以在 QML 中声明和创建多个实例。第一步创建 C 类同上同样使用Q_PROPERTY,Q_INVOKABLE,signals。类定义和上面一样。第二步使用 qmlRegisterType 注册类型通常在main.cpp中注册。#include QGuiApplication #include QQmlApplicationEngine #include QQmlContext #include myclass.h int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); // 在加载 QML 引擎之前注册类型 // 参数uri(QML 中 import 后的内容相当于头文件名)主版本号次版本号QML中的类型名 qmlRegisterTypeMyClass(MyCompany.Library, 1, 0, MyClass); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral(qrc:/main.qml))); return app.exec(); }第三步在 QML 中导入并实例化main.qml:import QtQuick 2.15 import QtQuick.Controls 2.15 import MyCompany.Library 1.0 // 导入我们注册的模块 ApplicationWindow { width: 400 height: 300 visible: true // 声明一个 MyClass 类型的实例id 为其句柄 MyClass { id: myClassInstance // 可以像普通属性一样设置初始值 message: Initial Message // 可以连接信号 onSomethingHappened: { console.log(Something happened in the registered type!); } } Text { text: myClassInstance.message anchors.centerIn: parent } Button { text: Create and Use onClicked: { // 甚至可以动态创建 var dynamicInstance Qt.createQmlObject( import MyCompany.Library 1.0; MyClass { message: Dynamic } , parent, DynamicObject); dynamicInstance.showMessage(); } } }优点更符合 QML 的声明式风格可创建多个实例类型安全在 QML 中有代码补全。缺点比上下文属性稍微复杂一点。方法三在 C 中直接加载和操作 QML 对象这种方法是从 C 端主动出击查找 QML 加载后形成的对象树中的特定对象并直接调用其方法或设置其属性。第一步确保 QML 对象有 objectName// main.qml Text { objectName: myTextObject // 为C查找提供标识 text: Im from QML }第二步在 C 中查找该对象并与之交互// main.cpp #include QGuiApplication #include QQmlApplicationEngine #include QDebug #include QQmlComponent #include QObject // 用于 dynamicCall 和 setProperty int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral(qrc:/main.qml))); // 引擎加载完成后获取根对象通常是ApplicationWindow QObject *rootObject engine.rootObjects().first(); // 通过 objectName 查找子对象 QObject *textObject rootObject-findChildQObject*(myTextObject); if (textObject) { // 方法1使用 setProperty 设置属性 textObject-setProperty(text, Text changed from C!); textObject-setProperty(color, red); // 方法2使用 QMetaObject 调用方法如果知道方法名和参数 // QMetaObject::invokeMethod: 在运行时动态地调用 textObject 对象的名为 append 的方法并传递一个 QString 类型的参数 QMetaObject::invokeMethod(textObject, append, Q_ARG(QString, Appended text.)); } else { qWarning() Object not found!; } return app.exec(); }优点C 端拥有完全控制权。缺点破坏了 QML 的声明式特性C 和 QML 耦合较紧QML 结构变化容易导致 C 代码失效。8. 元属性Q_PROPERTY详解c中定义, qml中调用在Qt框架中Q_PROPERTY是一个用于声明元属性Meta-Property的宏它允许将类的成员变量或逻辑属性暴露给Qt的元对象系统Meta-Object System从而支持信号槽机制、QML绑定、动态属性查询等高级特性。主要是用于给qml界面提供动态的数据。比如Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged)宏的结构Q_PROPERTYQt的元属性声明宏必须放在继承自QObject的类中使用且类需用Q_OBJECT宏标记。QString属性的数据类型此处为Qt的字符串类型。message属性的名称标识符。READ指定读取属性值的成员函数必须为const函数。message对应函数名为message()返回QString类型值。WRITE指定写入属性值的成员函数。setMessage对应函数名为setMessage(const QString value)用于设置属性值。NOTIFY指定属性值变更时触发的信号用于通知监听者。messageChanged对应信号名为messageChanged()通常在WRITE函数中触发。完整示例代码class MyClass : public QObject { Q_OBJECT Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged) public: explicit MyClass(QObject *parent nullptr); QString message() const { return m_message; } //READ void setMessage(const QString value); //WRITE signals: void messageChanged(); //NOTIFY信号注意要用signals声明一下 private: QString m_message; //m_message起一个中转站的作用READ和WRITE读写它并且可以在c中修改 }; // 实现文件WRITE时发NOTIFY信号 void MyClass::setMessage(const QString value) { if (m_message ! value) { m_message value; emit messageChanged(); //要用emit手动发信号 } }9.qml中的属性Qt.binding()函数1qml中的属性如何判断qml中的数据定义为属性还是定义为普通变量2Qt.binding()函数作用定义或改变一个属性的绑定规则举个例子Item { property real applePrice: 5.0 //苹果单价 property int appleCount: 2 //苹果数量 property real bananaPrice: 3.0 //香蕉单价 property int bananaCount: 3 //香蕉数量 // 这个显示值最初绑定的是“总花费” property real displayValue: (applePrice * appleCount) (bananaPrice * bananaCount) Component.onCompleted: { // 过了一会儿你动态地改变了它的绑定规则 // 从计算“总花费”变为计算“总数量” displayValue Qt.binding(function() { return appleCount bananaCount; }); } }10.环形进度条(使用Canvas画遮罩)import QtQuick 2.12 import QtQuick.Controls 2.12 import QtGraphicalEffects 1.0 Item { id: control //公共属性 property real value:0 //进度值 //尺寸 width: 355 height: 355 //背景圆环(底条) Image{ id: backgroundRing anchors.fill: parent fillMode: Image.PreserveAspectFit source: ../images/ProgressBar_off.png } //进度圆环(前景) Image{ id: progressRing anchors.fill: parent fillMode: Image.PreserveAspectFit source:../images/ProgressBar_on.png layer.enabled: true //启用图层化渲染 layer.effect: OpacityMask{ //OpacityMask遮罩 maskSource: Item{ //Item是画布 width: progressRing.width height: progressRing.height Canvas{ //Canvas是画笔 id: progressMask property real currentValue: control.value onCurrentValueChanged: { requestPaint() } anchors.fill: parent onPaint:{ var ctx getContext(2d) ctx.clearRect(0, 0, width, height); var centerX width/2 var centerY height/2 var radius Math.min(centerX,centerY) var startAngle -Math.PI/2 var endAngle startAngle currentValue*2*Math.PI ctx.beginPath() //开始路径 ctx.moveTo(centerX,centerY) //将画笔移动到指定坐标不画线 ctx.arc(centerX,centerY,radius,startAngle,endAngle,false) //画一个弧最后一个参数是表示顺时针 ctx.closePath() //闭合路径 // OpacityMask规则 黑色/白色 显示透明 隐藏 ctx.fillStyle white ctx.fill() } } } } } }11. ListModel组件1append() 方法参数是一个 JavaScript 对象可以向ListModel对象中插入自定义条目的属性和值// 简单添加 stateList.append({name: temperature, value: 25}) // 添加多个属性 stateList.append({ name: device, value: active, color: green, timestamp: 2024-01-15 }) // 动态构建对象 var newData {} newData[name] pressure newData[value] 100 newData[unit] kPa stateList.append(newData)2setProperty() 方法listModel.setProperty(int index, string propertyName, variant value)3get()方法//获取指定条目 var item stateList.get(2)4set()方法// 完全替换指定索引的条目 stateList.set(2, {name: replaced, value: new, extra: data})5remove()- 删除条目stateList.remove(3) // 删除第4个条目6insert() 插入条目// 在指定位置插入条目 stateList.insert(2, {name: new, value: data}) // 插入到第3个位置 // insert比appendmove更高效 stateList.insert(0, {name: first, value: 1}) // 插入到开头12.模型 - 视图 - 委托1视图ListView (一行行显示)ListView { width: 200 height: 300 model: ListModel { ListElement { name: Alice; age: 25 } ListElement { name: Bob; age: 30 } ListElement { name: Charlie; age: 35 } } delegate: Rectangle { width: 200 height: 50 color: index % 2 ? lightgray : white Text { text: model.name ( model.age 岁) anchors.centerIn: parent } } }GridView显示网格状的数据格式与ListView类似Repeater简单重复需要在布局容器中使用Column { //需要在布局容器中使用 Repeater { model: [red, green, blue] delegate: Rectangle { width: 100 height: 40 color: modelData // modelData包含数组元素 Text { text: modelData anchors.centerIn: parent } } } }TableView表格数据TableView { anchors.fill: parent columnSpacing: 1 rowSpacing: 1 model: TableModel { //每个TableModelColumn可以指定role和titlerole是数据字段名title是显示标题 TableModelColumn { role: name; title: 姓名 } TableModelColumn { role: age; title: 年龄 } TableModelColumn { role: city; title: 城市 } rows: [ { name: 张三, age: 25, city: 北京 }, { name: 李四, age: 30, city: 上海 } ] } delegate: Rectangle { border.color: gray Text { text: model.display anchors.centerIn: parent } } }我用的时候显示unknown component不知道为啥SwipeView页面切换不需要modelSwipeView { id: swipeView anchors.fill: parent currentIndex: tabBar.currentIndex Page { title: 首页 } Page { title: 设置 } Page { title: 关于 } } TabBar { id: tabBar anchors.top: parent.top width: parent.width TabButton { text: 首页 } TabButton { text: 设置 } TabButton { text: 关于 } }2模型13.在qml中使用ffmpeg播放视频开源项目https://github.com/yuan631111976/qtavplayer.git在.frag文件开始位置添加 #version 12014.qml类型相互转换方法QML 里没有真正的 int 类型只有 number。int 只是 QML 属性系统提供的一个“整数视图”15 时间显示获取当前时间var now new Date()显示HH:mm:ss function currentTime() { var now new Date() return now.toLocaleTimeString() }时间戳显示msvar timestamp Date.now() // 或 var timestamp new Date().getTime()16.状态管理state属性每个Item及其派生类都有state属性用于控制该组件的当前状态。代码示例Rectangle{ id:rect1 Rectangle{ id:rect2 color:red } Rectangle{ id:rect3 color:green } Component.onCompleted:{ rect1.stateisHD //给state属性赋值 } //rect1的state改变时执行state对应的PropertyChanges动作。这里用state来控制组件的显示状态 states:[ State{ name: isHD //when: param true //也可以用when来控制什么时候切换到该状态 PropertyChanges { target: rect2 visible:false } PropertyChanges { target: rect3 visible:true } }, State{ name:notHD PropertyChanges { target: rect2 visible:true } PropertyChanges { target: rect3 visible:false } } ] }
上一篇/下一篇内容由系统自动关联 返回资讯列表 →