赞
踩
Qt的元对象系统提供了对象之间通信的信号与槽机制、运行时类型信息和动态属性系统。
元对象系统由以下三个基础组成。
1、QObject类是所有元对象系统的类的基类。
2、在一个类的private部分声明Q_OBJECT宏,使得类可以使用元对象的特性,如动态属性、信号与槽。
3、MOC(元对象编译器)为每个QObjcet的子类提供必要的代码来实现元对象系统的特性。
例如:
QObject::metaObject() 函数返回类关联的元对象,元对象类QMetaObject包含了访问元对象的一些接口函数,例如:QMetaObject::className()函数可在运行时返回类的名称字符串。
QObject *obj=new QPushButton;
obj->metaObject()->className();//返回“QPushButton”
QMetaObject::newInstance()函数创建类的一个新的实例。
QObject::inherits(const char *className)函数判断一个对象实例是否为className的类或QObject的子类的实例。例如:
QTimer *timer=new QTimer;//QTimer是QObject的子类
timer->inherits("QTimer");//返回true
timer->inherits("QObject");//返回true
timer->inherits("QAbstractButton");//返回false,不是QAbstractButton子类
QObject::tr()和QObject::trUtf8()函数可翻译字符串,用于多语言界面设计
QObject::setProperty()和QObject::property()函数用于通过属性名称动态设置和获取属性值。
对于QObject及其子类,还可以使用qobject_cast()函数进行动态投射(dynamic cast)。例如,假设QMyWidget是QWidget的子类并且在类定义中声明了Q_OBJECT宏。创建实例使用下面的语句:
QObject *obj=new QMyWidget;
变量obj定义为QObject指针,但它实际指向QMyWidget类,所以可以正确投射为QWidget,即:
QWidget *widget=qobject_cast<QWidget *>(obj);
从QObject 到QWidget投射成功,obj是 QMyWidget类,是QWidget的子类。也可以投射到QMyWidget类。
QMyWidget *myWidget=qobject_cast<QMyWidget *>(obj);
obj投射到QLabel失败。 QMyWidget不是QLabel子类,返回指针label为NULL。
QLabel *label =qobject_cast<QLabel *>(obj);
Q_PROPERTY()宏可以定义属性,也是基于元对象系统实现的。Qt的属性系统与C++编译器无关。
QObject子类中,Q_PROPERTY()宏定义属性,使用格式如下:
Q_PROPERTY(type name
(READ getFunction
[WRITE setFunction]|
MEMBER memberName[[READ getFunction]|[WRITE setFunction]])
[RESET resetFunction]
[NOTIFY notifySignal]
[REVISION int]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[FINAL])
Q_PROPERTY宏定义一个返回值类型为type,名称为name的属性,用READ、WRITE关键字定义属性的读取、写入函数,还有其他的一些关键字定义属性的一些操作特性。属性的类型可以是QVariant支持的任何类型,也可以是用户自定义类型。
一个属性的行为就像类的数据成员,但是它还具有附加的特性,这些特性可以被元数据对象系统操作。这些特性是:
需要一个READ访问器函数。用于读属性的值。理想情况下,有一个不变的函数用于此目的,并且它必须返回属性的类型的值或指针或引用。例如,QWidget::focus是一个只读的属性,它对应一个读函数:QWidget::hasFocus()。
一个可选的WRITE访问器函数。它用于设置属性的值。它必须返回空并且至少具有一个参数,参数是属性类型的值或指针或引用。例如:QWidget::enabled具有WRITE函数QWidget::setEnable()。只读属性不需要写函数。例如,QWidget::focus没有对应的写函数。
MEMBER指定一个成员变量与属性关联,成为可读可写的属性,无需再设置READ和WRITE。
一个可选的RESET函数。用于设置属性的值到它的默认值。例如:QWidget::cursor具有典型的READ和WRITE函数,QWidget::cursor()和QWidget::setCursor(),并且它也具有一个RESET函数,QWidget::unsetCursor()。RESET函数必须返回void并且不带有任何参数。
一个可选的NOTIFY信号。如果被定义了,信号将在属性的值改变时发出。信号必须带有一个参数,这个参数的类型必须与属性相同;参数保存的是属性的新值。
**一个DESIGNABLE变量表明此属性是否在界面设计器的属性编辑器中出现。**大多数属性是可见的,除了为这个变量传入true或false,你还可以指定一个bool型的成员函数。
SCRIPTABLE变量表明这个属性是否可以被一个脚本引擎操作(默认是true)。你也可以赋予它true或false或bool型函数。
STORED变量表明了属性是否被认为是独立存在还是依赖于其它的值而存在。它也表明是否在保存对象状态时保存此属性的值。大多数属性都是需要保存的,但是,如QWidget::minimumWidth()就是不被保存的,因为它的值是从另一个属性QWidget::minimumSize()得来的。
USER变量表明属性是否被设计为面向用户的或用户可修改的类属性。通常,每个类只有一个USER属性。例如,QAbstractButton::checked是按钮类的用户可修改属性。注意QItemDelegate获取和设置widget的USER属性。
CONSTANT的出现表明属性的值是不变的。对于一个object实例,常量属性的READ方法在每次被调用时必须返回相同的值。此常量值可能在不同的object实例中不相同。一个常量属性不能具有WRITE方法或NOYIFY信号。
FINAL变量的出现表明属性不能被派生类所重写。有些情况下,这可以用于效率优化,但不是被moc强制的。程序员必须永远注意不能重写一个FINAL属性。
#ifndef QPERSON_H
#define QPERSON_H
#include <QObject>
class QPerson : public QObject
{
Q_OBJECT
Q_CLASSINFO("author","Wang")
Q_CLASSINFO("company","UPC")
Q_CLASSINFO("version","1.0.0")
Q_PROPERTY(int age READ age WRITE setAge NOTIFY ageChanged)
Q_PROPERTY(QString name MEMBER m_name)
Q_PROPERTY(int score MEMBER m_score)
private:
int m_age=10;
QString m_name;
int m_score=79;
public:
explicit QPerson(QString fName, QObject *parent = nullptr);
int age();
void setAge(int value);
void incAge();
signals:
void ageChanged( int value);
public slots:
};
#endif // QPERSON_H
#include "qperson.h"
QPerson::QPerson(QString fName,QObject *parent) : QObject(parent)
{ //构造函数
m_name=fName;
}
int QPerson::age()
{ //返回age
return m_age;
}
void QPerson::setAge(int value)
{//设置age
m_age=value;
emit ageChanged(m_age); //发射信号
}
void QPerson::incAge()
{
m_age++;
emit ageChanged(m_age);//发射信号
}
#ifndef QMYWIDGET_H
#define QMYWIDGET_H
#include <QWidget>
#include "qperson.h"
namespace Ui {
class QmyWidget;
}
class QmyWidget : public QWidget
{
Q_OBJECT
private:
QPerson *boy;
QPerson *girl;
public:
explicit QmyWidget(QWidget *parent = 0);
~QmyWidget();
private:
Ui::QmyWidget *ui;
signals:
private slots:
//自定义槽函数
void on_ageChanged(int value);
void on_spin_valueChanged(int arg1);
//界面按钮的槽函数
void on_btnClear_clicked();
void on_btnBoyInc_clicked();
void on_btnGirlInc_clicked();
void on_btnClassInfo_clicked();
};
#endif // QMYWIDGET_H
#include "qmywidget.h"
#include "ui_qmywidget.h"
#include <QMetaProperty>
QmyWidget::QmyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::QmyWidget)
{//构造函数
ui->setupUi(this);
boy=new QPerson("王小明");
boy->setProperty("score",95);
boy->setProperty("age",10);
boy->setProperty("sex","Boy");//动态属性
// connect(boy,SIGNAL(ageChanged(int)),this,SLOT(on_ageChanged(int)));
connect(boy,&QPerson::ageChanged,this,&QmyWidget::on_ageChanged);
girl=new QPerson("张小丽");
girl->setProperty("score",81);
girl->setProperty("age",20);
girl->setProperty("sex","Girl");//动态属性 sex属性在QPerson类中是没有的,因此这个属性是动态属性。
connect(girl,&QPerson::ageChanged,this,&QmyWidget::on_ageChanged);
ui->spinBoy->setProperty("isBoy",true); //动态属性
ui->spinGirl->setProperty("isBoy",false);
// 不能使用此形式,因为QSpinBox有两种参数形式的valueChanged()信号
// connect(ui->spinGirl,&QSpinBox::valueChanged,
// this,&QmyWidget::on_spinBoy_valueChanged);
connect(ui->spinGirl,SIGNAL(valueChanged(int)),
this,SLOT(on_spin_valueChanged(int)));
connect(ui->spinBoy,SIGNAL(valueChanged(int)),
this,SLOT(on_spin_valueChanged(int)));
}
QmyWidget::~QmyWidget()
{
delete ui;
}
void QmyWidget::on_ageChanged( int value)
{//响应QPerson的ageChanged()信号
Q_UNUSED(value);
QPerson *aPerson = qobject_cast<QPerson *>(sender()); //类型投射
QString hisName=aPerson->property("name").toString(); //姓名
// QString hisName=aPerson->name(); //获取姓名,错误
QString hisSex=aPerson->property("sex").toString(); //动态属性
int hisAge=aPerson->age();//通过接口函数获取年龄
// int hisAge=aPerson->property("age").toInt();//通过属性获得年龄
ui->textEdit->appendPlainText(hisName+","+hisSex
+QString::asprintf(",年龄=%d",hisAge));
}
void QmyWidget::on_btnClear_clicked()
{//"清空文本框"按钮
ui->textEdit->clear();
}
void QmyWidget::on_btnBoyInc_clicked()
{//"boy长大一岁"按钮
boy->incAge();
}
void QmyWidget::on_btnGirlInc_clicked()
{//"girl长大一岁"按钮
girl->incAge();
}
void QmyWidget::on_spin_valueChanged(int arg1)
{//响应界面上spinBox的valueChanged(int)信号
Q_UNUSED(arg1);
QSpinBox *spinBox = qobject_cast<QSpinBox *>(sender());
if (spinBox->property("isBoy").toBool())
boy->setAge(spinBox->value());
else
girl->setAge(spinBox->value());
}
void QmyWidget::on_btnClassInfo_clicked()
{//"类的元对象信息"按钮
// const QMetaObject *meta=boy->metaObject();
const QMetaObject *meta=girl->metaObject();
// const QMetaObject *meta=ui->spinBoy->metaObject();
ui->textEdit->clear();
ui->textEdit->appendPlainText("==元对象信息==\n");
ui->textEdit->appendPlainText(QString("类名称:%1\n").arg(meta->className()));
ui->textEdit->appendPlainText("property");
for (int i=meta->propertyOffset();i<meta->propertyCount();i++)
{
const char* propName=meta->property(i).name();
ui->textEdit->appendPlainText(
QString("属性名称=%1,属性值=%2").arg(propName).arg(boy->property(propName).toString()));
}
ui->textEdit->appendPlainText("");
ui->textEdit->appendPlainText("classInfo");
for (int i=meta->classInfoOffset();i<meta->classInfoCount();++i)
{
QMetaClassInfo classInfo=meta->classInfo(i);
ui->textEdit->appendPlainText(
QString("Name=%1; Value=%2").arg(classInfo.name()).arg(classInfo.value()));
}
}
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QmyWidget</class>
<widget class="QWidget" name="QmyWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>410</width>
<height>320</height>
</rect>
</property>
<property name="windowTitle">
<string>MetaObject的使用</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>设置男生年龄</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="spinBoy">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>200</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="btnBoyInc">
<property name="text">
<string>boy长大一岁</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="btnClassInfo">
<property name="text">
<string>类的元对象信息</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>设置女生年龄</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="spinGirl">
<property name="maximum">
<number>200</number>
</property>
<property name="value">
<number>20</number>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="btnGirlInc">
<property name="text">
<string>girl长大一岁</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="btnClear">
<property name="text">
<string>清空文本框</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPlainTextEdit" name="textEdit"/>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
#include "qmywidget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QmyWidget w;
w.show();
return a.exec();
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。