Qt第一个项目(元对象系统)

效果是这样的,点击boy长大一岁或者girl长大一岁

 qt的文件构造都是两个头文件三个源文件,源文件中有个cpp文件,它是程序的入口,一个项目中只能有一个main函数

#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    //QApplication 类管理 GUI 应用程序的控制流和主设置
    QApplication a(argc, argv);
    //Widget w;: 创建一个 Widget 类的实例 w。这通常会设置应用程序的主窗口以及其内部的各种控件和局
    //Widget类继承自QWidget,所以自带show方法
    Widget w;
    //w.show();: 调用 Widget 实例的 show 方法来显示窗口。在创建窗口后,默认情况下窗口是隐藏的,调            
    //用 show 方法会使窗口可见。
    w.show();
    //调用 exec 方法启动应用程序的事件循环。
    return a.exec();
}

接下来实现一个元对象,首先,我们把这个元对象命名为TPerson

首先创建元对象New file->C++class->选择->命名为TPerson->Base class(基类)选择QObject->下一步->完成

#ifndef TPERSON_H
#define TPERSON_H

#include <QObject>

class TPerson : public QObject
{
    Q_OBJECT
    //首先我们定义了类的信息(class information),后续可以通过元对象系统查询
    Q_CLASSINFO("author","Yu");
    Q_CLASSINFO("company","SCNU");
    Q_CLASSINFO("version","1.0.0");
    //用于声明属性,READ是读取方法,WRITE是写入方法,NOTIFY是信号,MEMBER是成员变量
    //目的:增加代码可读性。
    Q_PROPERTY(int age READ age WRITE setAge NOTIFY ageChanged);
    Q_PROPERTY(QString name MEMBER m_name);
    Q_PROPERTY(int score MEMBER m_score);
public:
    //元对象要有名字和基类,而该基类设为nullptr
    explicit TPerson(QString name,QObject *parent = nullptr);

signals:
    
private:
    //QString m_sex;
    //定义这个类的信息,一个人有名字,有年龄……
    QString m_name;
    int m_age=10;
    int m_score=79;
};

#endif // TPERSON_H

其中explicit(明确的)关键字可以避免隐式类型转换,及要明确指明类型,而不能由其它类型转换为该类型。

例如

class MyClass {
public:
    explicit MyClass(int x) { /* ... */ }
};

void func(MyClass m) { /* ... */ }

int main() {
    func(10); // 编译错误,不能隐式转换(int转MyClass)
    func(MyClass(10)); // 明确的转换,符合预期
}

接下来我们在元对象的public下面实现四个接口

public:
    explicit TPerson(QString name,QObject *parent = nullptr);
    ~TPerson();
    int age();
    void setAge(quint8 ageValue);
    void incAge();

右键接口->重构(reconfiguration)到tperson.cpp文件当中。接下来我们在重构中实现这几个函数 。

//析构函数,退出销毁时打印一个销毁信息
TPerson::~TPerson()
{
    qDebug("TPerson类的对象被删除");
}
//age函数,调用时返回该对象的年龄
int TPerson::age()
{
    return m_age;
}
//设置年龄,ageValue是替换值,而m_age是它原本的年龄,如果你希望年龄只能增不能减可以把
//m_age!=ageValue改成m_age<ageValue,emit用于后面的槽函数
void TPerson::setAge(quint8 ageValue)
{
    if(m_age!=ageValue){
        m_age=ageValue;
        //发送信号
        emit ageChanged(m_age);
    }
}
//年龄增加函数,调用时++,发送回去
void TPerson::incAge()
{
    ++m_age;
    emit ageChanged(m_age);
}

下面我们要用signal关键字定义一个信号,用于上面的emit,

在 Qt 中,signals 关键字用于声明信号。信号是 Qt 对象间通信的一种机制,通过它,一个对象可以广播特定事件的发生,而其他对象可以通过槽(slots)来监听这些事件。void ageChanged(int ageValue); 是信号的声明。具体来讲,这一行定义了一个名为 ageChanged 的信号,当某个对象的年龄改变时,这个信号可以被发射(emit)。int ageValue 是传给监听该信号的槽函数的参数,表明年龄已更改为 ageValue。

signals:
    void ageChanged(int ageValue);

 目前的流程是定义函数->定义信号->在函数里使用emit发送信号,接下来还需要定义槽函数->使用connect函数把signals信号函数(ageChanged)和槽函数连接起来。

现在我们先来捋一下目前都做了哪些事情1.定义的两个类一个Widget类它的基类是QWidget,第二个是TPerson类它的基类是QObject。2.在TPerson中使用Q_CLASSINFO写了几个信息。定义了一个信号ageChanged并在三个函数中使用。在public中实现了三个函数age用于返回对象的年龄,setage用于更改对象的年龄,incage用于增加对象的年龄

现在TPerson写好了,该写Widget了。在这之前我们要先在ui界面中把按钮放在合适的位置。并更改名称。

详细见下面这个up主的视频,拖动图片的操作不好讲,看视频更加合适

3.3.5元对象系统功能示例_哔哩哔哩_bilibili 

有前置操作。我们再来写Widget

这是头文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
//声明TPerson类才能使用
class TPerson;
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
private:
    //声明两个对象,一个boy一个girl
    TPerson* boy;
    TPerson* girl;
private slots:
    //声明六个槽函数
    void do_ageChanged(int value);
    void do_spinChanged(int arg1);
    void on_boyinc_clicked();

    void on_girlinc_clicked();

    void on_clear_clicked();

    void on_metainfo_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

接下来写具体实现 

在这之前先讲讲connect,第一个参数是对象发送者,也就是TPerson,在它的实现里,是不是实现了两个带有emit的函数,触发时他会发送SIGNAL给到接收者也就是第三个参数,然后执行SLOT中的函数。

//把带有定义的头文件都引入
#include "widget.h"
#include "ui_widget.h"
#include "tperson.h"
#include<QMetaProperty>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //创建并用setProperty设置信息,后面会用property获取这些信息
    boy=new TPerson("小明",this);
    boy->setProperty("sex","boy");
    boy->setProperty("age","10");
    boy->setProperty("score","70");
    girl=new TPerson("小丽",this);
    girl->setProperty("sex","girl");
    girl->setAge(20);
    //便于后面找到它是男孩还是女孩,我猜测setProperty应该是个map,映射结构那种
    ui->boyage->setProperty("isBoy",true);
    ui->girlage->setProperty("isBoy",false);
    //当触发信号时,会传递给boyage,boyage是一个spinbox,本身自带setValue函数用于修改界面的显  
    //示.
    connect(boy,SIGNAL(ageChanged(int)),ui->boyage,SLOT(setValue(int)));
    connect(girl,SIGNAL(ageChanged(int)),ui->girlage,SLOT(setValue(int)));
    //触发控件,do_ageChanged用来将信息打印到plainTextEdit上,详见下面的实现
    //为什么使用this,因为do_ageChanged函数是Widget类里的,
    connect(boy,SIGNAL(ageChanged(int)),this,SLOT(do_ageChanged(int)));
    connect(girl,SIGNAL(ageChanged(int)),this,SLOT(do_ageChanged(int)));
    //这个也是同理,说白了,就是一个类里的函数发送了信号,另一个类调用指定函数
    //两个类之间的通讯
    connect(ui->boyage,SIGNAL(valueChanged(int)),this,SLOT(do_spinChanged(int)));
    connect(ui->girlage,SIGNAL(valueChanged(int)),this,SLOT(do_spinChanged(int)));
}
//构析函数,没什么好讲的
Widget::~Widget()
{
    delete ui;
}
//
void Widget::do_ageChanged(int value)
{
    //获取发送者
    TPerson*person=qobject_cast<TPerson*>(sender());
    //用property调用信息,名字,性别年龄
    QString str=QString("%1,%2,年龄=%3").arg(person->property("name").toString()).arg(person->property("sex").toString()).arg(value);
    //显示到plainTextEdit上
    ui->plainTextEdit->appendPlainText(str);
}

void Widget::do_spinChanged(int arg1)
{
    Q_UNUSED(arg1);
    QSpinBox *spinBox=qobject_cast<QSpinBox*>(sender());
    //true就set boy age
    if(spinBox->property("isBoy").toBool())
        boy->setAge(arg1);
    else
        girl->setAge(arg1);
}
//下面两个其实没用到
void Widget::on_boyinc_clicked()
{
    boy->incAge();
}


void Widget::on_girlinc_clicked()
{
    girl->incAge();
}

//调用自带的clear函数
void Widget::on_clear_clicked()
{
    ui->plainTextEdit->clear();
}


void Widget::on_metainfo_clicked()
{
    //获取元对象
    const QMetaObject * meta=boy->metaObject();
    ui->plainTextEdit->appendPlainText(QString("类名称:%1\n").arg(meta->className()));

    ui->plainTextEdit->appendPlainText("属性:");
    //offset开端,count数量
    for(int i=meta->propertyOffset();i<meta->propertyCount();i++){
        //用property(i).name()获取键
        const char* propName=meta->property(i).name();
        //获取值
        QString propValue=boy->property(propName).toString();
        //打印到plain上,arg参数
        ui->plainTextEdit->appendPlainText(QString("属性名称=%1,属性值=%2").arg(propName).arg(propValue));

    }

    ui->plainTextEdit->appendPlainText("\n类信息(classInfo):");
    //获取类信息,类信息类型是QMetaClassInfo
    for(int i=meta->classInfoOffset();i<meta->classInfoCount();i++){
        QMetaClassInfo classInfo=meta->classInfo(i);
        ui->plainTextEdit->appendPlainText(QString("Name=%1,Value=%2").arg(classInfo.name()).arg(classInfo.value()));
    }
}

总结一下:

1.类名->setProperty(键,值)对应char * name=property(i).name()键,property(name)值。propertyOffset起始下标,propertyCount()数量

2. 同理,Q_CLASSINFO("author","Yu");设置键值对,classInfo()是一个pair(QMetaClassInfo类型),classInfo.name键,value值。

3.ui->setupUi(this);设置这个QWidget为界面。

4.connect(ui->boyage,SIGNAL(valueChanged(int)),this,SLOT(do_spinChanged(int)));发送者,发送者里的函数,接受者,接受者里的函数。

5.spinbox具有valueChanged(类型)函数还有setValue(类型)函数。

6.signals:可以定义信号emit可以发送信号。sender()可以获取发送者地址

7.当新增文件时需要在pro文件里加入项目名称

差不多就这些了,到此第一个项目就完美结束了。

不得不说写一个简单的小程序还是很难的。算法更多的是难懂,项目更多的是复杂性,结构和逻辑都很重要,

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/363567.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C#用正则表达式Regex.Matches 方法检查字符串中重复出现的词

目录 一、Regex.Matches 方法 1.重载 二、Matches(String, String, RegexOptions, TimeSpan) 1.定义 2.示例 三、Matches(String, String, RegexOptions) 1.定义 2.示例 3.示例&#xff1a;用正则表达式检查字符串中重复出现的词 四、Matches(String, Int32) 1.定义…

DRV8301 踩坑记,Status1 D10 老是 Fault

波形如上&#xff1a; 看第一个时钟出来的数据&#xff08;Status1 读完自动清除&#xff1f;&#xff09;&#xff0c;因此数据是&#xff1a;0x20 输入结构体解析&#xff1a; 可以看到&#xff0c;FETHA_OC了也就是A桥上管过流了&#xff1b; 检查一下硬件看看&#xff1…

nodejs+vue+ElementUi高校创业项目申报系统w6f1g

此系统设计主要采用的是nodejs语言来进行开发&#xff0c;采用vue框架技术&#xff0c;框架分为三层&#xff0c;分别是控制层Controller&#xff0c;业务处理层Service&#xff0c;持久层dao&#xff0c;能够采用多层次管理开发&#xff0c;对于各个模块设计制作有一定的安全性…

Lazysysadmin

信息收集 # nmap -sn 192.168.1.0/24 -oN live.port Starting Nmap 7.94 ( https://nmap.org ) at 2024-01-30 21:10 CST Nmap scan report for 192.168.1.1 (192.168.1.1) Host is up (0.00075s latency). MAC Address: 00:50:56:C0:00:08 (VMware) Nma…

强化学习-google football 实验记录

google football 实验记录 1. gru模型和dense模型对比实验 实验场景&#xff1a;5v5(控制蓝方一名激活球员)&#xff0c;跳4帧&#xff0c;即每个动作执行4次 实验点&#xff1a; 修复dense奖励后智能体训练效果能否符合预期 实验目的&#xff1a; 对比gru 长度为16 和 dens…

机器学习算法决策树

决策树的介绍 决策树是一种常见的分类模型&#xff0c;在金融风控、医疗辅助诊断等诸多行业具有较为广泛的应用。决策树的核心思想是基于树结构对数据进行划分&#xff0c;这种思想是人类处理问题时的本能方法。例如在婚恋市场中&#xff0c;女方通常会先询问男方是否有房产&a…

Outlook技巧:如何插入可以用指定浏览器打开的链接

Outlook中的链接&#xff0c;有时直接点击无法打开&#xff0c;找本地Edge才能打开。如何让Url能够指定打开的浏览器呢&#xff1f; 插入链接时&#xff0c;直接加上前缀Microsoft-edge即可。 操作步骤&#xff1a; 编辑邮件界面&#xff0c;菜单选择插入-》链接 在链接地址…

如何使用淘宝客?

1.定义&#xff1a;是一种按成交计费的推广工具&#xff0c;由淘宝客帮助商家推广商品&#xff0c;买家通过推广链接进入完成交易后&#xff0c;商家按照设置佣金支付给淘宝客费用。 2.优势&#xff1a; &#xff08;1&#xff09;展示、点击全免费。 &#xff08;2&#xf…

Redis核心技术与实战【学习笔记】 - 10.浅谈CPU架构对Redis性能的影响

概述 可能很多人都认为 Redis 和 CPU 的关系简单&#xff0c;Redis 的线程在 CPU 上运行&#xff0c;CPU 快 Reids 处理请求的速度也很快。 其实&#xff0c;这种认知是片面的&#xff0c;CPU 的多核架构及多 CPU 结构&#xff0c;也会影响到 Redis 的性能。如果不了解 CPU 对…

嵌入式学习第十五天

内存管理: 1.malloc void *malloc(size_t size); 功能: 申请堆区空间 参数: size:申请堆区空间的大小 返回值: 返回获得的空间的首地址 失败返回NULL 2.free void free(void *ptr); 功能: 释放堆区空间 注…

【芯片设计- RTL 数字逻辑设计入门 番外篇 8.1 -- memory repair 详细介绍】

文章目录 memory repair 详细介绍Memory Repair 方法Memory Repair 过程举例memory repair 详细介绍 SoC (System on Chip) 的 Memory Repair 是一种技术,用于检测和修复内存中的损坏单元。由于SoC内部集成了大量的逻辑和存储单元,包括RAM(随机访问存储器)、ROM(只读存储…

使用 vite 配置请求代理

介绍vite vue官方提供的前端构建工具。 由两个部分组成 开发服务器&#xff1a;基于ES模块提供丰富的内建功能 构建指令&#xff1a;使用 Rollup 打包代码&#xff0c;提供预设配置 Rollup&#xff1a; Rollup 是一个 JavaScript 模块打包器&#xff0c;它可以将多个模块打包成…

UG949 适用于 FPGA 和 SoC 的UltraFast 设计方法指南

使用RTL创建设计 定义RTL设计层级 模块边界输出进行寄存 即寄存器输出&#xff0c;打一拍 IP的使用 AMBA AXI

BPF 管理器 bpfman 简介

1. 背景 Fedora 40 提案建议将 bpfman 作为默认的程序管理器 &#xff0c;开源项目 bpfman 可以实现对 eBPF 运行状态的深入了解&#xff0c;从而实现更轻松地管理 eBPF 程序&#xff08;包括加载、卸载、运行状态查看等&#xff09;。该提案还需要 Fedora 工程和指导委员会 (…

AIGC专题:从0到1精益创新 AIGC产品应用及商业化落地实践

今天分享的是AIGC系列深度研究报告&#xff1a;《AIGC专题&#xff1a;从0到1精益创新 AIGC产品应用及商业化落地实践》。 &#xff08;报告出品方&#xff1a;易点天下&#xff09; 报告共计&#xff1a;38页 企业内部增效-AI知识库 企业内部IT、运维、人力资源、行政等等日…

Unity 模板方法模式(实例详解)

文章目录 简介示例1&#xff1a;游戏关卡流程示例2&#xff1a;测试试卷类示例3&#xff1a;游戏场景构建流程示例4&#xff1a;游戏动画序列示例5&#xff1a;游戏对象初始化过程 简介 Unity中的模板方法模式是一种行为设计模式&#xff0c;它在父类中定义了一个算法的框架&a…

微软新的内部开发部门发现了第一个 Windows 12 版本

Windows 11 被证明让很多人有点失望&#xff0c;很多 Windows 10 用户认为没有理由升级。 这意味着有大量用户渴望一些大而令人印象深刻的东西——而这正是 Windows 12 所希望的。 无论您是 Windows 10 的忠实拥趸&#xff0c;还是渴望更新、更闪亮的 Windows 11 采用者&#x…

笔记本电脑Win11重装系统教程

在笔记本电脑Win11操作过程中&#xff0c;用户如果遇到很严重的系统问题&#xff0c;就可以重新正常的Win11系统&#xff0c;快速解决Win11系统问题。但是&#xff0c;部分新手用户不知道不知道如何操作才能给Win11笔记本电脑重装系统&#xff1f;以下小编分享笔记本电脑Win11重…

分布式事务(五)——基于本地消息和可靠消息的解决方案

系列目录&#xff1a; 《分布式事务&#xff08;一&#xff09;—— 事务的基本概念》 《分布式事务&#xff08;二&#xff09;—— CAP和Base理论》 《分布式事务&#xff08;三&#xff09;—— 两阶段提交解决方案&#xff08;2PC&#xff09;》 《分布式事务&#xff0…

安卓滚动视图ScrollView

<?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:orientatio…