• Qt元对象系统 day4


    Qt元对象系统 day4

    元对象

    • 元对象系统是一个基于标准C++的扩展,为Qt提供了信号与槽机制、实时类型信息、动态属性系统。
    • 元对象可以操作、创建、描述或是执行其他对象,元对象又称为基对象
    • 元对象组成
      • QObject: QT 对象模型的核心,绝大部分的 Qt类都是从这个类继承而来
      • Q_OBJECT:Q_OBJECT宏必须出现在类定义的私有部分,用来开启信号和槽、动态属性系统,或Qt元对象系统提供的其他服务。使用信号与槽时就得包含这个宏
      • MOC:MOC编译器为QObject子类提供了一些实现元对象特性所需要的一些代码。就比如说信号,大家知识在类声明的时候声明了所需要的信号,在MOC编译时,会为信号添加函数定义。
    #include 
    #include 
    
    class Widget :public QWidget
    {
        Q_OBJECT
    public:
        Widget(QWidget* parent =nullptr):QWidget(parent)
        {
    
        }
    };
    
    int main(int argc, char* argv[])
    {
        QApplication a(argc, argv);
        Widget w;
        w.show();
        return a.exec();
    }
    
    //如果把类和main这个文件写在了同一个文件,那么必须在代码最后加上#include[空格]"文件名.moc" 
    //这行预处理指令,告诉moc这个文件需要进行元编译,以实现Q_OBJECT宏中声明的函数
    #include "main.moc"
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    使用按钮控件

    • 包含头文件#include <QPushButton>
    #include 
    #include 
    #include 
    class Widget :public QWidget
    {
        //只要用到信号与槽就必须加这个宏
        Q_OBJECT
    public:
        Widget(QWidget* parent =nullptr):QWidget(parent)
        {
            //设置窗口大小
            resize(600, 600);
            //添加按钮,放到在自己主屏幕上
            QPushButton *btn = new QPushButton(this);
            //添加文本
            btn->setText("小瓜");
            //移动按钮位置
            btn->move({300,300});
            //当点击按钮的时候进行自定义操作
            connect(btn, &QPushButton::clicked, this, &Widget::on_btn_clicked);
        }
        //槽函数
        void on_btn_clicked()
        {
            qDebug() << "你点击了小瓜";
        }
    };
    
    
    int main(int argc, char* argv[])
    {
        QApplication a(argc, argv);
        Widget w;
        w.show();
        return a.exec();
    }
    
    //如果把类和main这个文件写在了同一个文件,那么必须在代码最后加上#include[空格]"文件名.moc" 
    //这行预处理指令,告诉moc这个文件需要进行元编译,以实现Q_OBJECT宏中声明的函数
    #include "main.moc"
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 运行结果
      在这里插入图片描述

    信号与槽

    • 信号与槽:实际就是一个观察者模式(发布-订阅模式),例如按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。这样就让互不干扰的对象建立了联系

    • 槽实际上就是普通的函数,成员函数、全局函数、静态函数、lambda函数都可以!

    • 当我们把对象的信号和槽绑定在一起之后,当信号触发时,与之绑定的槽函数将会自动调用,并把信号的参数传递给槽函数

    绑定信号与槽

    • 信号与槽绑定使用QObject::connent()函数实现,其基本格式如下:
     [static] QMetaObject::Connection connect(
         const QObject *sender, 
         const QMetaMethod &signal, 
         const QObject *receiver, 
         const QMetaMethod &method,
     	, Qt::ConnectionType type = Qt::AutoConnection)
         
     [static] QMetaObject::Connection connect(
         const QObject *sender, 
         PointerToMemberFunction signal, 
         Functor functor)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • sender:信号发送者,需要传递一个QObject的对象
    • signal:发出的具体信号,需要传递一个函数指针
    • receiver:信号接收者,需要传递一个QObject族的对象
    • method:接收到信号之后,信号接收者处理动作,需要传递一个函数指针(槽函数)
    • type:第一个connect函数独有的参数,表示信号和槽的连接类型;有默认值,一般不需要修改

    标准信号与槽

    • 在Qt提供的很多类中都可以对用户触发的某些特定事件进行检测, 当事件被触发后就会产生对应的信号, 这些信号都是Qt类内部自带的, 因此称之为标准信号。
    • 系统自带的信号和槽通常如何查找呢,这个就需要利用帮助文档了,在帮助文档中比如我们上面的按钮的点击信号,在帮助文档中输入QPushButton,首先我们可以在Contents中寻找关键字 signals,信号的意思,但是我们发现并没有找到,这时候我们应该看当前类从父类继承下来了哪些信号,因此我们去他的父类QAbstractButton中就可以找到该关键字,点击signals索引到系统自带的信号有如下几个
    • QPushButton的信号
      在这里插入图片描述
    • QWidget的槽
      在这里插入图片描述
    • 断开连接和连接是一样的语法
    QObject::disconnect(m_btn, &QPushButton::released, this, &Widget::on_btn_released);
    
    • 1
    • 使用连接标识断开连接
    QMetaObject::Connection  con = QObject::connect(m_btn,&QPushButton::clicked,this,&Widget::on_btn_released);
    
    QObject::disconnect(con);
    
    • 1
    • 2
    • 3
    #include 
    #include 
    #include 
    class Widget :public QWidget
    {
        //只要用到信号与槽就必须加这个宏
        Q_OBJECT
    public:
        //初始化按钮成员
        Widget(QWidget* parent =nullptr):QWidget(parent),m_btn(new QPushButton("小瓜", this))
        {
            //设置窗口大小
            resize(300, 300);
    
            //连接m_btn信号
            m_con = connect(m_btn, &QPushButton::clicked, this, &Widget::on_btn_clicked);
            connect(m_btn, &QPushButton::pressed, this, &Widget::on_btn_pressed);
            connect(m_btn, &QPushButton::released, this, &Widget::on_btn_released);
        }
        //槽函数
        void on_btn_clicked()
        {
            qDebug() << "click";
        }
        void on_btn_pressed()
        {
            //如果按钮按下,断开released信号连接
            disconnect(m_btn, &QPushButton::released, this, &Widget::on_btn_released);
            //使用连接标识断开连接
            disconnect(m_con);
            qDebug() << "press";
        }
        void on_btn_released()
        { 
            qDebug() << "releas";
        }
    protected:
        QPushButton* m_btn{};
        //使用连接标识断开连接
        QMetaObject::Connection m_con{};
    };
    
    
    int main(int argc, char* argv[])
    {
        QApplication a(argc, argv);
        Widget w;
        w.show();
        return a.exec();
    }
    
    //如果把类和main这个文件写在了同一个文件,那么必须在代码最后加上#include[空格]"文件名.moc" 
    //这行预处理指令,告诉moc这个文件需要进行元编译,以实现Q_OBJECT宏中声明的函数
    #include "main.moc"
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 运行结果
      在这里插入图片描述

    04 各种槽(成员函数、静态函数、全局函数、labmda)

    • 槽函数的返回值必须是void,槽函数的参数个数不能多于信号的参数个数,信号也可以作为槽函数
    #include 
    #include 
    #include 
    void on_btn_clicked_global()
    {
        qDebug() << __FUNCTION__;
    }
    class Widget :public QWidget
    {
        //只要用到信号与槽就必须加这个宏
        Q_OBJECT
    public:
        //初始化按钮成员
        Widget(QWidget* parent =nullptr):QWidget(parent),m_btn(new QPushButton("小瓜", this))
        {
            //设置窗口大小
            resize(300, 300);
    
            //连接m_btn信号
            m_con = connect(m_btn, &QPushButton::clicked, this, &Widget::on_btn_clicked);
            connect(m_btn, &QPushButton::pressed, this, &Widget::on_btn_pressed);
            connect(m_btn, &QPushButton::released, this, &Widget::on_btn_released);
            //把静态函数作为槽函数
            connect(m_btn, &QPushButton::released, this, &Widget::on_btn_clicked_static);
            //全局函数作为槽函数
            connect(m_btn, &QPushButton::released, on_btn_clicked_global);
            //lambda表达式作为槽函数
            connect(m_btn, &QPushButton::clicked, []() {qDebug() << "lambda"; });
            //lambda捕获组件中的文本
            connect(m_btn, &QPushButton::clicked, [this]() {qDebug() << m_btn->text(); });
      		//获取信号传递的参数
            connect(m_btn, &QPushButton::clicked, [this](bool checked) {qDebug() << m_btn->text() << checked; });
            m_btn->setCheckable(true);//设置按钮可以选中
        }
        //一般槽函数都加上slots这个标识宏
    public slots:
        void on_btn_clicked()
        {
            qDebug() << "click";
        }
        void on_btn_pressed()
        {
            //如果按钮按下,断开released信号连接
            disconnect(m_btn, &QPushButton::released, this, &Widget::on_btn_released);
            //使用连接标识断开连接
            disconnect(m_con);
            qDebug() << "press";
        }
        void on_btn_released()
        { 
            qDebug() << "releas";
        }
        static void on_btn_clicked_static()
        {
            qDebug() << __FUNCTION__;
        }
    protected:
        QPushButton* m_btn{};
        //使用连接标识断开连接
        QMetaObject::Connection m_con{};
    };
    
    
    int main(int argc, char* argv[])
    {
        QApplication a(argc, argv);
        Widget w;
        w.show();
        return a.exec();
    }
    
    //如果把类和main这个文件写在了同一个文件,那么必须在代码最后加上#include[空格]"文件名.moc" 
    //这行预处理指令,告诉moc这个文件需要进行元编译,以实现Q_OBJECT宏中声明的函数
    #include "main.moc"
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 运行结果
      在这里插入图片描述

    自定义信号和重载解决方案

    • 信号是类的成员函数,并且返回类型必须是 void 类型
    • 信号函数只需要声明, 不需要定义(没有函数体实现)
    • 参数可以随意指定, 信号也支持重载
    • 信号需要使用 signals 关键字进行声明, 使用方法类似于public等关键字
    • 在程序中发送自定义信号: 发送信号的本质就是调用信号函数
    emit mysignals(); //发送信号
    
    • 1
    • emit是一个空宏,没有特殊含义,仅用来表示这个语句是发射一个信号,不写当然可以,但是不推荐。
    #include 
    #include 
    #include 
    #include 
    
    class Login :public QWidget
    {
        Q_OBJECT
    public:
        //初始化登录界面的组件
        Login(QWidget* parent = nullptr) :QWidget(parent)
            , userNameEdit(new QLineEdit(this))
            , passwordEdit(new QLineEdit(this))
            , login(new QPushButton("登录", this))
        {
            //设置窗口大小
            resize(600, 400);
            //设置控件位置居中位,窗口宽度-控件的宽度/2,高度就自行设置
            userNameEdit->move((width() - userNameEdit->width()) / 2, 50);
            passwordEdit->move((width() - passwordEdit->width()) / 2, 100);
            login->move((width() - login->width()) / 2, 150);
           
    
            //连接信号与槽
            connect(login, &QPushButton::clicked, [=]()
                {
                    auto userName = userNameEdit->text();
                    auto password = passwordEdit->text();
                    //是否验证成功
                    if (1)
                    {
                        emit sig_loginSucceed();//emit无任何作用,仅作为标识
                        emit sig_loginSucceed(userName, password);
                    }
                });
            //信号转发
            connect(this, QOverload<>::of(&Login::sig_loginSucceed),this, &Login::login_OK);
        }
    //signals 下面只能放信号
    signals:
        void sig_loginSucceed();
        void sig_loginSucceed(const QString& userName,const QString& password);
        void login_OK();
    protected:
        QLineEdit* userNameEdit{};
        QLineEdit* passwordEdit{};
        QPushButton* login{};
    };
    
    int main(int argc, char* argv[])
    {
        QApplication a(argc, argv);
        Login w;
        w.show();
        //信号重载的二义性问题
        //1.使用函数指针解决
        void (Login:: * sig_loginSucceed_ptr)(const QString & userName, const QString & password) = &Login::sig_loginSucceed;
        QObject::connect(&w, sig_loginSucceed_ptr, [](const QString& userName, const QString& password)
            {
                qDebug() << "登录成功" << "用户名:" << userName << "密码:" << password;
            });
        //2.使用QOverload类解决
        QObject::connect(&w, QOverload<const QString&,const QString&>::of(&Login::sig_loginSucceed), [](const QString& userName, const QString& password)
            {
                qDebug() << "登录成功2" << "用户名:" << userName << "密码:" << password;
            });
        //信号转发
        QObject::connect(&w, &Login::login_OK, []()
            {
                qDebug() << "login_Ok";
            });
       
        return a.exec();
    }
    
    //如果把类和main这个文件写在了同一个文件,那么必须在代码最后加上#include[空格]"文件名.moc" 
    //这行预处理指令,告诉moc这个文件需要进行元编译,以实现Q_OBJECT宏中声明的函数
    #include "main.moc"
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 运行结果
      在这里插入图片描述

    窗口切换

    • 新建几个头文件与cpp,在CMAkeLists中添加这几个资源

    Widget.h

    #ifndef WIDGET_H_
    #define WIDGET_H_
    #include 
    #include 
    #include "SubWidget.h"
    class Widget :public QWidget
    {
    	Q_OBJECT
    public:
    	Widget(QWidget* parent = nullptr);
    protected:
    	//初始化为空指针
    	SubWidget*   m_subWidget{};
    	QPushButton* m_curBtn{};
    };
    #endif // !WIDGET_H_
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Widget.cpp

    #include "Widget.h"
    Widget::Widget(QWidget* parent) :QWidget(parent)
    	,m_subWidget(new SubWidget)
    	,m_curBtn(new QPushButton("切换到子窗口",this))
    {
    	//设置标题
    	setWindowTitle("主窗口");
    	resize(600, 400);
    	connect(m_curBtn, &QPushButton::clicked, [=]()
    		{
    			//隐藏主窗口组件
    			this->hide();
    			//切换到子窗口
    			m_subWidget->show();
    		}
    	);
    	//接收信号,切换回主窗口
    	connect(m_subWidget, &SubWidget::showMainWidget, [=]()
    		{
    			this->show();
    			m_subWidget->hide();
    		});
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    SubWidget.h

    #ifndef SUBWIDGET_H_
    #define SUBWIDGET_H_
    #include 
    #include 
    
    class SubWidget :public QWidget
    {
    	Q_OBJECT
    public:
    	SubWidget(QWidget* parent = nullptr);
    signals:
    	//切换窗口信号
    	void showMainWidget();
    protected:
    	QPushButton* m_btn{};
    };
    #endif // !WIDGET_H_
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    SubWidget.cpp

    #include "SubWidget.h"
    
    SubWidget::SubWidget(QWidget* parent):QWidget(parent)
    		,m_btn(new QPushButton("切换到主窗口",this))
    {
    	//设置标题
    	setWindowTitle("子窗口");
    
    	resize(600, 400);
    	connect(m_btn, &QPushButton::clicked, [=]() {
    		this->hide();
    	//发送切换窗口信号
    	emit showMainWidget();
    	});
    	//或者一句话搞定,和上面一样
    	//connect(m_btn, &QPushButton::clicked, this, &SubWidget::showMainWidget);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 运行结果
      在这里插入图片描述
  • 相关阅读:
    Redis缓存(笔记一:缓存介绍和数据库启动)
    HBase(超级无敌详细PROMAX讲解版)
    C++套接字库sockpp介绍
    Spring事件机制之ApplicationEvent
    Linux文件管理知识:文本处理
    动态内存管理
    GitHub上超牛的Java进阶教程,Java核心技术及大公司架构案例汇总
    保研机试算法训练个人记录笔记(二)
    openlayers 绘制tin数据导文
    微信小程序合集7(体育赛事+高仿知乎+微赞论坛+数独游戏+小熊日记)
  • 原文地址:https://blog.csdn.net/qq_44924388/article/details/133553799