西安公积金 网站建设,华文细黑做网站有版权吗,网页设计策划案例,17zwd一起做网站广州第二章Qt元对象系统 文章目录 第二章Qt元对象系统1.什么是元对象#xff1f;2.元对象系统组成3.信号与槽信号和槽的本质绑定信号与槽自定义槽定义槽函数必须遵循一下规则槽函数的类型自定义槽案例 自定义信号自定义信号需要遵循以下规则信号和槽重载二义性问题 4.内存管理1. 简…第二章Qt元对象系统 文章目录 第二章Qt元对象系统1.什么是元对象2.元对象系统组成3.信号与槽信号和槽的本质绑定信号与槽自定义槽定义槽函数必须遵循一下规则槽函数的类型自定义槽案例 自定义信号自定义信号需要遵循以下规则信号和槽重载二义性问题 4.内存管理1. 简介2.关联图3.详解4.智能指针 5.属性系统获取/设置属性值自己声明属性值绑定属性QObjectBindableProperty 6.实时类型信息枚举QMetaObject元信息 元对象系统是一个基于标准C的扩展为QT提供了新号与槽机制、实时类型信息、动态属性系统 1.什么是元对象
在计算机科学中元对象是这样一个东西它可以操纵、创建、描述或执行其他对象。元对象描述的的对象称为基对象。元对象可能存在这样的信息基础对象的类型、接口、类、方法、属性、变量、控制结构等。
2.元对象系统组成 QObject 是QT对象模型的核心绝大部分的QT类都是从这个类继承而来 Q_OBJECT Q_OBJECT宏必须出现在类定义的私有部分用来开启信号和槽、动态属性系统或QT元对象系统提供的其他服务 MOC MOC编译器为QObject子类提供了一些实现元对象特性所需要的一些代码。比如说信号大家只是在类声明的时候声明了所需要的信息在MOC编译时会为信号添加函数定义。
3.信号与槽
所谓信号槽实际就是观察者模式发布-订阅模式。当某个时间发生之后比如按钮检测到自己被点击了一下它就会发出一个信号(signal)。这种发出是没有目的的类似广播。如果有对象对这个信号感兴趣它就会使用连接(connect)函数意思是将想要处理的信号和自己的一个函数(称为槽-slot)绑定来处理这个信号。也就是说信号发出时被连接的槽函数会自动被回调。
信号和槽的本质
信号是由对象发射出去的消息信号实际上是一个特殊的函数不需要由程序员实现而是由QT的moc实现。 槽实际上就是普通的函数。 当我们把对象的信号和槽绑定在一起之后当信号触发时与之绑定的槽函数将会自动调用并把信号参数传递给槽函数。
绑定信号与槽
使用QObject::connect()函数实现
# Mainwindow.ui
# 创建一个PushButton# Mainwindow.h 在下面加入代码
public slots:void onButtonClicked();# Mainwindow.cpp
#include MainWindow.h
#include ./ui_MainWindow.hMainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui-setupUi(this);auto con connect(ui-pushButton,SIGNAL(clicked()),this,SLOT(onButtonClicked()));QObject::connect(ui-pushButton_2,QPushButton::clicked,this,MainWindow::onButtonClicked);//lambda函数写法connect(ui-pushButton_3,QPushButton::clicked,[](){disconnect(con);qInfo()断开1的连接;});
}
MainWindow::~MainWindow()
{delete ui;
}void MainWindow::onButtonClicked()
{qInfo()我被点击了;
}自定义槽 槽函数就是信号的处理动作自定义槽函数和自定义的普通函数写法是一样的。只不过自定义的槽函数一般放在public slots:后面。 定义槽函数必须遵循一下规则 槽函数的返回类型必须是void槽函数的参数必须等于或少于信号的参数当信号与槽函数的参数数量相同时它们参数类型要完全一致。 槽函数的类型
成员函数 普通成员函数静态成员函数 全局函数lambda表达式
自定义槽案例
#include QApplication
#include QWidget
#includeQPushButtonclass MyWindow:public QWidget
{Q_OBJECT
public:MyWindow():QWidget(NULL){}
public slots:void showWindow(){this-show();}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);QPushButton* btn new QPushButton(打开);btn-show();MyWindow *mwnew MyWindow();auto con QObject::connect(btn,QPushButton::clicked,mw,MyWindow::showWindow);return a.exec();
}#include main.moc
自定义信号 Qt框架提供的信号在某些特定场景下是无法满足我们的项目需求的因此我们还设计自己需要的信号同样还是使用connect()对接 自定义信号需要遵循以下规则
信号是类的成员函数并且返回类型必须是void信号函数只需要声明不需要定义参数可以随意指定信号也支持重载信号需要使用signals关键字进行声明在程序中发送自定义信号发送信号的本质就是调用信号函数emit mysignals()emit是个空宏没有特殊含义仅用来表示这个语句是发射一个信号不写可以但不推荐
#include QApplication
#include QWidget
#includeQPushButtonclass MyWindow:public QWidget
{Q_OBJECT
public:MyWindow():QWidget(NULL){}
signals:void isShow(QString text);
public slots:void showWindow(){this-show();emit isShow(我出来了);}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);QPushButton* btn new QPushButton(打开);btn-show();MyWindow *mwnew MyWindow();auto con QObject::connect(btn,QPushButton::clicked,mw,MyWindow::showWindow);QObject::connect(mw,MyWindow::isShow,btn,QPushButton::setText);return a.exec();
}#include main.moc
信号和槽重载二义性问题
解决方法 #include QApplication
#include QWidget
#includeQPushButtonclass MyWindow:public QWidget
{Q_OBJECT
public:MyWindow():QWidget(NULL){}
signals:void isShow();void isShow(QString text);public slots:void showWindow(){this-show();emit isShow(我出来了);}void showWindow(bool t){this-close();emit isShow();}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);QPushButton* btn new QPushButton(打开);btn-move(10,10);btn-show();QPushButton* btn1 new QPushButton(关闭);btn1-move(10,100);MyWindow *mwnew MyWindow();//用overload重载QObject::connect(btn,QPushButton::clicked,mw,QOverload::of(MyWindow::showWindow));QObject::connect(mw,QOverloadQString::of(MyWindow::isShow),btn,QPushButton::setText);//用qt4方式SIGNAL和SLOT宏QObject::connect(btn1,SIGNAL(clicked(bool)),mw,SLOT(showWindow(bool)));//SIGNAL和SLOT要前后一致QObject::connect(mw,SIGNAL(isShow()),btn1,SLOT(close()));QObject::connect(mw,SIGNAL(isShow(QString)),btn1,SLOT(show()));return a.exec();
}#include main.moc
4.内存管理
1. 简介
C中new 和delete必须配对使用防止内存泄露。Qt中使用了new却很少delete,因为Qt实现了其独特的内存管理机制。 QObject以对象树的形式组织起来。当为一个对象创建子对象时子对象会自动地添加到父对象的children()列表中。父对象拥有子对象的所有权比如父对象可以在自己的析构函数中删除它的子对象。使用findChild()或findChildren()通过名字和类型查询孩子对象。
QObject(QObject *parentnullptr)QObject及其派生类的对象如果其parent非nullptr,那么其parent析构时会析构该对象。父子关系父对象、子对象、父子关系。这是Qt中所特有的与类的继承关系无关传递参数是与parent有关
2.关联图
在Qt中最基础和核心的类是QObject,QObject内部有一个名为children的QObjectList列表会保存所有子对象还有一个指针parent,用来指向父对象当自己析构时会先把自己从parent列表中删除并且析构所有的child
3.详解
指定父对象释放自己也释放 #include QApplication
#include QWidget
#includeQPushButtonclass MyWindow:public QWidget
{Q_OBJECT
public:MyWindow(QWidget* parentnullptr):QWidget(parent){}~MyWindow(){qInfo()__FUNCTION__释放了;}
signals:void isShow();void isShow(QString text);public slots:void showWindow(){this-show();emit isShow(我出来了);}void showWindow(bool t){this-close();emit isShow();}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);QPushButton* btn new QPushButton(打开);btn-move(10,10);btn-show();QPushButton* btn1 new QPushButton(关闭);btn1-move(10,100);MyWindow *mwnew MyWindow(btn1);//用overload重载QObject::connect(btn,QPushButton::clicked,mw,QOverload::of(MyWindow::showWindow));QObject::connect(mw,QOverloadQString::of(MyWindow::isShow),btn,QPushButton::setText);//用qt4方式SIGNAL和SLOT宏QObject::connect(btn1,SIGNAL(clicked(bool)),mw,SLOT(showWindow(bool)));//SIGNAL和SLOT要前后一致QObject::connect(mw,SIGNAL(isShow()),btn1,SLOT(close()));QObject::connect(mw,SIGNAL(isShow(QString)),btn1,SLOT(show()));btn1-deleteLater();return a.exec();
}#include main.moc
查找子对象 mw-setObjectName(window);auto wd btn1-findChildMyWindow*(window); //找子对象if(wd)qInfo()存在;4.智能指针
智能指针描述QPointerQObject或子类对象释放时会自动指向nullpterQScopedPointer【独享指针】超出作用域自动释放管理的对象QSharedPoiner【共享指针】QWeakPointer【监视指针】QScopedArrayPointer【独享数组指针】超出作用域自动释放管理的对象数组QSharedDataPointer【隐式共享指针】读时共享写时拷贝QExplicitlySharedDataPointer【显式共享指针】读时共享写时需要手动拷贝(通过detach()函数)
使用方法 #includeQPointerQPointerQPushButton btn1 new QPushButton(关闭);btn1-move(10,100);MyWindow *mwnew MyWindow(btn1.data());#includeQSharedPointerQSharedPointerQPushButton btn1(new QPushButton(关闭));QObject::connect(btn1.data(),SIGNAL(clicked(bool)),mw,SLOT(showWindow(bool)));//SIGNAL和SLOT要前后一致QObject::connect(mw,SIGNAL(isShow()),btn1.data(),SLOT(close()));QObject::connect(mw,SIGNAL(isShow(QString)),btn1.data(),SLOT(show()));#includeQScopedArrayPointerQScopedArrayPointerint arrptr(new int[10]);arrptr[1] 666;5.属性系统
属性的行为类似于数据成员但它具有通过元对象系统访问的附加特性
获取/设置属性值
获取 MyWindow *mwnew MyWindow();mw-show();qInfo() mw-property(pos);//结果QVariant(QPoint, QPoint(639,249))设置 QObject::connect(btn,QPushButton::clicked,[](){auto pos mw-property(pos).toPoint();mw-setProperty(pos,QPoint(pos.x()-2,pos.y()-2));});自己声明属性值
要声明属性请在继承QObject的类中使用Q_PROPERTY()宏
Q_PROPRETY(type name(READ getFunction [WRITE setFunction]|MEMBER memberName [(READ getFunction|WRITE setFunction)])[RESET restFunction][NOTIFY notifySignal][REVISION int | REVISION(int[,int])][DESIGNABLE bool][SCRIPTABLE bool][STORED bool][USER bool][BINDABLE bindableProperty][CONSTANT][FINAL][REQUIRED])属性的行为类似于数据成员但它具有通过元对象系统访问的附加特性。
如果未指定MEMBER变量则需要READ访问器函数。它用于读取属性值。理想情况下const函数用于此目的它必须返回属性的类型或对该类型的const引用。例如QWidget::focus是一个带有READ函数的只读属性QWidget::hasFocus()。WRITE访问器函数是可选的。它用于设置属性值。它必须返回void并且必须只接受一个参数该参数可以是属性类型也可以是指向该类型的指针或引用。例如QWidget::enabled具有WRITE函数QWidget::setEnabled()。只读属性不需要WRITE函数。例如QWidget::focus没有Write功能。如果未指定READ访问器函数则需要MEMBER变量关联。这使得给定的成员变量可读可写而无需创建READ和WRITE访问器函数。如果您需要控制变量访问除了MEMBER变量关联之外仍然可以使用READ或WRITE访问器函数。RESET功能是可选的。它用于将属性设置回特定于上下文的默认值。NOTIFY信号是可选的。如果已定义则应指定该类中的一个现有信号该信号在属性值更改时发出。MEMBER变量的NOTIFY信号必须采用零个或一个参数该参数必须与属性的类型相同。该参数将采用属性的新值。NOTIFY信号只应在属性真正被更改时发出以避免在QML中不必要地重新评估绑定。REVISION编号或REVISION()宏是可选的。如果包含它定义了要在API的特定修订版中使用的属性及其通知信号(通常用于暴露于QML)。如果不包含则默认为0.DESIGNABLE属性指示该属性是否应在GUI设计工具(如Qt Designer)的属性编辑器中可见。大多数属性是可设计的默认为true。有效值为真和假。SCRIPTABLE属性指示脚本引擎是否可以访问此属性(默认为true)。有效值为真和假。STORED属性指示该属性是否应该被认为是独立存在的或取决于其他值。它还指示在存储对象的状态时是否必须保存属性值。大多数属性值是STORED的(默认为true)。USER属性指示该属性是被指定为该类的面向用户的属性还是用户可编辑的属性。通常每个类只有一个USER属性(默认false)。BINDABLE bindableProperty属性表明该属性支持绑定并且可以通过对元对象系统设置和检查该属性的绑定。bindableProperty命名QBindable类型的类成员其中T是属性类型。CONSTANT属性的存在表明属性值是常量。对于给定的对象实例常量属性的READ方法在每次调用时都必须返回相同的值。对于对象的不同实例该常数值可能不同。常量属性不能有WRITE方法或NOTIFY信号。FINAL属性的存在表明该属性不会被派生类覆盖。这在某些情况下可用于性能优化但并非由moc强制执行。必须注意永远不要覆盖FINAL属性。REQUIRED属性的存在表明该属性应该由该类的用户设置。这不是由moc强制执行的并且对于暴露给QML的类最有用。在QML中除非设置了所有的REQUIRED属性否则无法实例化具有REQUIRED属性的类。 #include QApplication
#include QWidget
#includeQPushButton
#includeQPointerclass MyWindow:public QWidget
{Q_OBJECTQ_PROPERTY(QString name READ getname WRITE setname NOTIFY nameChanged FINAL)QString m_name;
public:MyWindow(QWidget* parentnullptr):QWidget(parent){}~MyWindow(){qInfo()__FUNCTION__释放了;}
signals:void isShow();void isShow( QString text);
signals:void nameChanged();
public slots:QString getname(){qInfo()__FUNCTION__;return m_name;}void setname(QString name){qInfo()__FUNCTION__;m_name name;emit nameChanged();}
public slots:void showWindow(){this-show();emit isShow(我出来了);}void showWindow(bool t){this-close();emit isShow();}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);QPushButton* btn new QPushButton(打开);btn-move(10,10);btn-show();MyWindow *mwnew MyWindow();mw-showWindow();QObject::connect(btn,QPushButton::clicked,[](){auto pos mw-property(pos).toPoint();mw-setProperty(pos,QPoint(pos.x()-2,pos.y()-2));mw-setProperty(name,abc);//自定义的name属性改变});QObject::connect(mw,MyWindow::nameChanged,[,btn](){auto windowName mw-property(name);btn-setText(windowName.toString());});return a.exec();
}#include main.moc
直接关联也可以 Q_PROPERTY(QString name MEMBER m_name NOTIFY nameChanged FINAL)绑定属性 #include QApplication
#includeQPropertystruct Rectangle
{Rectangle() {}int w,h,area;Rectangle(int w,int h):w(w),h(h){area this-w*this-h;}
};
struct Rect
{Rect() {}QPropertyint w,h,area;Rect(int w,int h):w(w),h(h){//设置绑定属性area.setBinding([]{return this-w*this-h;});}
};int main(int argc, char *argv[])
{QApplication a(argc, argv);//没设置绑定属性Rectangle rl(10,10);qInfo()rl.area;rl.h 20;qInfo()rl.area;//2个结果一样都是100没改变//设置绑定属性后Rect r(10,10);qInfo()r.area;r.h 20;qInfo()r.area;//结果分别是100和200return a.exec();
}
QObjectBindableProperty #include QApplication
#includeQProperty
#includeQWidget
#include QPushButtonclass MyWindow:public QWidget
{Q_OBJECT
public:MyWindow(QWidget* parentnullptr):QWidget(parent){}signals:void myxChanged();public:Q_OBJECT_BINDABLE_PROPERTY(MyWindow,int,myx,MyWindow::myxChanged);};int main(int argc, char *argv[])
{QApplication a(argc, argv);QPushButton* btn new QPushButton(打开);btn-move(10,10);btn-show();MyWindow* mw new MyWindow();QObject::connect(btn,QPushButton::clicked,[]{static int a0;mw-show();mw-myx a;});QObject::connect(mw,MyWindow::myxChanged,[]{qInfo()x修改了;});return a.exec();
}#include main.moc6.实时类型信息
判断继承自XX类
qInfo()mw-inherits(QObject);//true枚举
#include QMetaEnumnamespace Yerennuo {
//命名空间
Q_NAMESPACE
enum Type
{Player,Enemy,Bullet
};
//注册枚举
Q_ENUM_NS(Type)
}qInfo() Yerennuo::Type::Bullet;//Yerennuo::Bullet也可以在类里面
class Test:public QObject
{Q_OBJECT
public:enum Type{Player,Enemy,Bullet};Q_ENUM(Type)
};qInfo()Test::Type::Player;//Test::PlayerQMetaObject元信息 #include qmetaobject.h
#include QApplication
#includeQWidget
#include QMetaObjectclass MyWindow:public QWidget
{Q_OBJECTQ_CLASSINFO(author,yerennuo)
public:MyWindow(QWidget* parentnullptr):QWidget(parent){}};int main(int argc, char *argv[])
{QApplication a(argc, argv);MyWindow* mw new MyWindow();auto metaobj mw-metaObject();qInfo()keymetaobj-classInfo(0).name()valuemetaobj-classInfo(0).value();//key author value yerennuoreturn a.exec();
}#include main.moc