[QML]从零开始QML开发(二)QML开发,浅谈控件、槽函数、锚等基本概念。QML和C++怎么交互?贯彻落实MVC原则
[QML]从零开始QML开发(二)QML开发,浅谈控件、槽函数、锚等基本概念。QML和C++怎么交互?贯彻落实MVC原则
先看代码:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
Window {
visible: true
width: 320
height: 480
title: qsTr("Hello World")
color: "gray"
Button{
x:100 //设置按钮的横坐标
y:100 //设置纵坐标
text:"我是按钮" //按钮标题
//一个类似JS风格的函数
function slotAnyway(){
console.log("slotAnyway")
}
//信号槽连接
onClicked: {
slotAnyway()
console.log("点击")
}
}
Button
{
id:btn1
x:100
y:100
text:"按钮1"
width: 100
height: 30
background: Rectangle {
color:"#0857C9"
}
}
Button
{
id:btn2
x:200
y:160
text:"按钮2"
width: 100
height: 30
//设置按钮背景颜色
background: Rectangle {
color: Qt.rgba(77/255,76/255,167/255,1)
}
}
Button
{
id:btn3
x:10
y:220
text:"按钮3"
width: 100
height: 30
//设置按钮背景颜色
background: Rectangle {
color: Qt.rgba(54/255,54/255,167/255,1)
}
}
Button
{
id:btn4
x:100
y:280
text:"按钮4"
width: 100
height: 30
//设置按钮背景颜色
background: Rectangle {
color: Qt.rgba(177/255,76/255,67/255,1)
}
}
}
槽函数
由上面这个代码,我们了解这个槽函数就通过这段代码来看:

在这个qml脚本中,我们仍然是通过on + 槽函数 来调用这样一个槽函数,比如onClicked,这个函数可以构造一个类javaScript语言的写法,具体怎么写,你就看看这个代码吧,我也很难形容....
然后我们可以通过function的方法生命一个函数,在里面调用一些方法,然后再槽函数里面去调用这样的函数。当然了,我们也可以直接在槽函数内调用已经写好的槽函数。
锚
锚应该也是一个前端的概念,anchors需要通过控件的id来设计布局,例如上面的实现思路:先指定第一个控件的位置,其它控件根据它的位置进行布局。btn2的顶部和btn1的顶部对其,btn2的左边处在btn1的右边,距离为10.btn3,btn4类似的设计
可以看另外一段代码:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
Window {
visible: true
width: 320
height: 480
title: qsTr("Hello World")
color: "gray"
Button
{
id:btn1
x:10
y:10
text:"按钮1"
width: 100
height: 30
//设置按钮背景颜色
background: Rectangle {
color: "blue"
}
}
Button
{
id:btn2
// x:200
// y:160
text:"按钮2"
width: 100
height: 30
//设置按钮背景颜色
background: Rectangle {
color: Qt.rgba(77/255,76/255,167/255,1)
}
anchors.left: btn1.right
anchors.leftMargin: 20
anchors.top: btn1.top
}
Button
{
id:btn3
// x:10
// y:220
text:"按钮3"
width: 100
height: 30
//设置按钮背景颜色
background: Rectangle {
color: Qt.rgba(54/255,54/255,167/255,1)
}
anchors.top: btn1.bottom
anchors.topMargin: 10
anchors.left: btn1.left
}
Button
{
id:btn4
// x:100
// y:280
text:"按钮4"
width: 100
height: 30
//设置按钮背景颜色
background: Rectangle {
color: Qt.rgba(177/255,76/255,67/255,1)
}
anchors.left: btn2.left
anchors.top: btn2.bottom
anchors.topMargin: 10
}
}
从上我们就可以看得出,在这里实际上是用的一个锚来做的一个简单相对关系,而不是通过layout来进行一个整体布局。这样做的好处非常明显,就是控件之间的关系异常明显,而不是layout那样的黑盒---当layout整体放大或者缩小时,我将无法的得知这个控件会以何种方式放大或者缩小。
在Qt开发中,QML中的anchors和QWidget中的Layout都是用于界面布局的机制,但它们有着不同的优劣势。
对于QML中的anchors(锚点)来说,它是一种相对定位的方式。通过设置元素之间的关系,如上下左右的锚点关联,可以使得界面元素能够自动适应不同分辨率、窗口大小和设备方向的变化。这种灵活性使得开发者可以更加方便地设计自适应的界面,在移动设备或不同平台上具有良好的兼容性。同时,QML还支持动态切换布局,使得界面的交互效果更加流畅。
而在QWidget中,可以使用各种不同的布局管理器(Layout)来实现界面布局。布局管理器提供了一种自动化的方式来管理界面元素的位置和大小。通过布局管理器,开发者可以以更加直观的方式定义界面的结构和组织,而无需手动计算和设置每个元素的位置。此外,布局管理器还可以根据窗口大小自动调整布局,保持界面的整体美观和一致性。
总体来说,QML中的anchors适合用于需要较为自由和灵活的界面设计,特别是针对移动设备或多平台的开发;而QWidget中的Layout适合用于传统桌面应用程序的界面设计,更强调自动化的布局管理。选择使用哪种方式,可以根据具体的需求、开发者的习惯和项目特点来决定。
qml C++交互
当然了,需要注意的一点是,QML和C++已经完全是两个体系了,不像我们在QWidget中和C++的交互那样浑然天成,或者直接通过信号槽就可以进行的,而是需要一些别的手段来获取QML中的控件或者信息,以下介绍几种方法:
一、qml调用C++
Qt 提供了两种在 QML 环境中使用 C++对象的方式∶
方式1:在C+中实现一个类,注册为 QML 环境的一个类型,在 QML 环境中使用该类型创建对象。
方式2:在 C++中构造一个对象,将这个对象设置为 QML 的上下文属性,在QML 环境中直接使用该属性。
不管哪种方式,对要导出的 C++类都有要求,不是一个类的所有方法、变量都可以在 QML 语境中使用,定义可以导出的 C++类 前提条件 要想将一个类或对象导出到 QML 中,下列的前提条件必须满足∶
(1)从 QObject 或 QObject 的派生类继承,并使用Q_OBJECT宏,这和使用信号与槽的前提条件一样的,这两个条件是为了让一个类能够进入Qt强大的元对象系统(meta-object system)中,只有使用元对象系统,一个类的某些方法或属性才可能通过字符串形式的名字来调用,才可以在 QML 中被访问。
(2)成员函数想在qml中被调用,则需要在声明前加上Q_INVOKABLE
(3)槽函数可以用类对象在qml代码中直接调用
(4)C++的成员属性可以用Q_PROPERTY宏设置
(5)枚举体需要用Q_ENUMS导出
现在来举个例子:
#ifndef TESTBOX_H
#define TESTBOX_H
#include <QObject>
class TestBox : public QObject
{
Q_OBJECT
Q_ENUMS(ColorType)
Q_PROPERTY(int mValue READ getValue WRITE setValue)
public:
explicit TestBox(QObject *parent = nullptr);
enum ColorType
{
Red,
Green,
Blue
};
// 成员函数想在qml中被调用,则需要在声明前加上Q_INVOKABLE
Q_INVOKABLE int fun1();
int getValue()
{
return m_value;
}
Q_INVOKABLE void setValue(int value)
{
m_value = value;
}
signals:
void sig_Value();
public slots:
void on_Get();
private:
int m_value = 0;
};
#endif // TESTBOX_H
写完的类需要在main.cpp中注册一下,内容如下:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "testbox.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
//将这个类注册进qml系统内
qmlRegisterType<TestBox>("cpp.qt.TestBox", 1, 0, "TestBox");
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
这样我们就可以在QML中使用我们声明的函数了,举例如下:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
import cpp.qt.TestBox 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
TestBox{
id:tb
mValue:123 //可以在这里给对象赋值
}
Button{
id:btn1
text:"getValue"
anchors.left: parent.left
anchors.leftMargin: 40
anchors.top: parent.top
anchors.topMargin: 60
onClicked: {
tb.on_Get() //调用槽函数
}
}
Button{
id:btn2
text:"setValue"
anchors.left: btn1.left
anchors.top: btn1.bottom
anchors.topMargin: 20
onClicked: {
tb.setValue(3456)
}
}
Button{
id:btn3
text:"getenum"
anchors.left: btn2.left
anchors.top: btn2.bottom
anchors.topMargin: 10
onClicked: {
valueTextFeild.text = TestBox.Blue //调用枚举
}
}
Button{
id:btn4
text:"invoke fun"
anchors.left: btn3.left
anchors.top: btn3.bottom
anchors.topMargin: 10
onClicked: {
valueTextFeild.text = tb.fun1() //调用普通成员函数
}
}
TextField
{
id:valueTextFeild
anchors.left: btn1.right
anchors.leftMargin: 15
anchors.top: btn1.top
}
//链接信号槽
Connections{
target: tb
onSig_Value:{
valueTextFeild.text = tb.mValue //获取成员属性值
}
}
}
但是这也有个问题,就相当于是你是在这个qml内创建了一个c++对象,那问题来了,这不就不符合我们的mvc模式了吗?
我知道你很急,但是你先别急,除了qml调用c++,也请让我们来看看c++调用qml,毕竟我们更多的是希望把QML当成一个纯粹的界面组件使用,类似前后端的模式,并不是说让qml去申请C++的对象,否则不是我们本末倒置了吗?
C++调用qml
在C++代码中获取qml控件时,需要用到objectName
Text{
objectName: "textLabel"
text:"Hello World"
anchors.centerIn: parent
font.pixelSize: 26
}
Button{
objectName: "quitBtn"
anchors.right: parent.right
anchors.rightMargin: 10
anchors.bottom: parent.bottom
anchors.bottomMargin: 10
text:qsTr("退出")
}
上面的控件在声明时,都设置了属性objectName, qt程序在初始化时有个对象树,在对象树中根据objectName调用findChild可以获取到控件对象指针
//C++获取qml的控件
QObject* pQuitBtn = pRoot->findChild<QObject*>("quitBtn");
if(pQuitBtn)
{
QObject::connect(pQuitBtn, SIGNAL(clicked()), &app, SLOT(quit()));
}
QObject *pText = pRoot->findChild<QObject*>("textLabel");
if(pText)
{
bool bRet = QMetaObject::invokeMethod(pText, "setText", Q_ARG(QString, "AAAA"));
qDebug() << "bRet = " << bRet; //调用失败,没有该方法
pText->setProperty("color", QColor::fromRgb(255,0,0));
}
也就是说,实际上是把QML的东西打包起来,当成一个QObject来使用,通过信号槽来交互,所有的事件绑定都写在总的进程中。这算是一种更好地实现MVC模式的方案,也就是说,我们需要把界面和业务拆开,肯定是不希望二者过度融合,而是将其做成一个单独的组件。
[QML]从零开始QML开发(二)QML开发,浅谈控件、槽函数、锚等基本概念。QML和C++怎么交互?贯彻落实MVC原则的更多相关文章
- 浅谈控件(组件)制作方法一(附带一delphi导出数据到Excel的组件实例)(原创)
来自:http://blog.csdn.net/zhdwjie/article/details/1490741 -------------------------------------------- ...
- iOS开发UI篇—使用picker View控件完成一个简单的选餐应用
iOS开发UI篇—使用picker View控件完成一个简单的选餐应用 一.实现效果 说明:点击随机按钮,能够自动选取,下方数据自动刷新. 二.实现思路 1.picker view的有默认高度为162 ...
- iOS开发UI篇—Quartz2D(自定义UIImageView控件)
iOS开发UI篇—Quartz2D(自定义UIImageView控件) 一.实现思路 Quartz2D最大的用途在于自定义View(自定义UI控件),当系统的View不能满足我们使用需求的时候,自定义 ...
- Windows Phone开发(11):常用控件(下)
原文:Windows Phone开发(11):常用控件(下) WP控件大部分都可以从Silverlight中继承过来,这里我也只能拿一部分作演示,对于其它控件如何使用,可以参考SDK相关说明以及Sil ...
- Windows Phone开发(10):常用控件(上)
原文:Windows Phone开发(10):常用控件(上) Windows Phone的控件有几个来源,和传统的桌面应用程序开发或Web开发一样,有默认提供的控件和第三方开者发布的控件.一般而言,如 ...
- iOS项目开发实战——学会使用TableView列表控件(四)plist读取与Section显示
文本将会实现把数据存储到plist文件里.然后在程序中进行读取.在TableView控件中依据不同的类别显示Section. 有关TableView 的其它实现,请參考<iOS项目开发实战--学 ...
- winform快速开发平台 -> 快速绑定ComboBox数据控件
通常我们在处理编辑窗体时.往往会遇到数据绑定.例如combobox控件绑定数据字典可能是我们经常用到的.然而在我的winform快速开发平台中我是如何处理这个频繁的操作呢? 首先,我们要绑定combo ...
- winform快速开发平台 -> 基础组件之分页控件
一个项目控件主要由及部分的常用组件,当然本次介绍的是通用分页控件. 处理思想:我们在处理分页过程中主要是针对数据库操作. 一般情况主要是传递一些开始位置,当前页数,和数据总页数以及相关关联的业务逻辑. ...
- ASP.NET2.0自定义控件组件开发 第六章 深入讲解控件的属性
原文:ASP.NET2.0自定义控件组件开发 第六章 深入讲解控件的属性 深入讲解控件的属性持久化(一) 系列文章链接: ASP.NET自定义控件组件开发 第一章 待续 ASP.NET自定义控件组件开 ...
- iOS 9应用开发教程之使用开关滑块控件以及滚动部署视图
iOS 9应用开发教程之使用开关滑块控件以及滚动部署视图 使用ios9中的开关.滑块控件 开关和滑块也是用于和用户进行交互的控件.本节将主要讲解这两种控件. ios9开关 开关控件常用来控制某个功能的 ...
随机推荐
- [USACO2007NOVS] Milking Time S
题目描述 Bessie 可以在接下来 \(N\) 个小时内产奶,为了方便,我们把这 \(N\) 个小时 \(0\dots N-1\) 编号. FJ 在这 \(N\) 个小时内有 \(M\) 段时间可以 ...
- flask中使用pyjwt
**pyjwt使用教程: ** https://pyjwt.readthedocs.io/en/stable/ 使用案例 import datetime from flask import Flask ...
- SpringCloud OpenFeign的使用
SpringCloud OpenFeign的使用 是什么: 声明式http客户端. 目的: 使远程调用更简单 作用: 提供了http请求模板,仅需编写简单接口和插入注解,就可以定义好原始http请求的 ...
- BeanCurrentlyInCreationException解决当前容器创建异常、循环依赖问题
BeanCurrentlyInCreationException解决当前容器创建异常.循环依赖问题 一.什么是循环依赖呢? 类A依赖类B,类B也依赖类A,这种情况就会出现循环依赖. Bean A → ...
- LLaMA大型语言模型
LLaMA (Large Language Model Meta AI)是Meta公司发布的大型语言模型系列,近日LLaMA种子文件被合并到了GitHub 上,同时一些项目维护者给予了批准,目前该项目 ...
- Python——第五章:hashlib模块
hashlib 模块(hash)算法 hashlib 模块是 Python 中用于加密散列(hash)算法的模块.它提供了对常见的哈希算法(如MD5.SHA-1.SHA-256等)的支持,使得开发者可 ...
- Python——第四章:函数的递归调用
递归: 函数自己调用自己 递归如果没有任何东西拦截的话. 它默认就是一个死循环 def func() func() func() 因此递归调用的时候需要有判断,来退出循环 def func() if ...
- Python——第一章:用户交互
变量 = input(提示语)首先会在屏幕中显示出提示语, 用户输入内容. 然后把用户输入的内容交给前面的变量 案例1: a = input("请输入第一个数字:") #括号里是提 ...
- JavaFx css样式(三)
JavaFx css样式(三) JavaFX 从入门入门到入土系列 JavaFx css样式,前面我说过它类似html,他有css控制样式,不过最新的css标准并不支持,同时javafx的css样式都 ...
- CUDA驱动深度学习发展 - 技术全解与实战
全面介绍CUDA与pytorch cuda实战 关注TechLead,分享AI全维度知识.作者拥有10+年互联网服务架构.AI产品研发经验.团队管理经验,同济本复旦硕,复旦机器人智能实验室成员,阿里云 ...