【QT】Qt核心特点

目录

1.概述

2.元对象系统

3.属性系统

(1)属性定义

(2)属性的使用

(3)动态属性

(4)类的附加信息

4.信号与槽

5.元对象特性实例

1.概述

    Qt本身并不是一种编程语言,它实质上是一种跨平台的C++开发类库,是用标准C++编写的类库,它为开发GUI应用程序和非GUI应用程序提供了各种类。
    Qt元对象编译器(Meta-Object Compliler,Moc):Moc是一个预处理器,在源程序被编译前先将Qt特性的程序转换为标准C++兼容的形式,再由标准C++编译器进行编译。例如在使用信号与槽机制的类里,必须添加一个Q_OBJECT宏的原因,因为有这个宏,Moc才能对类里的信号与槽的代码进行预处理。
    Qt Core模块:Qt类库的核心,所有其他模块都依赖于此模块,如果使用qmke来构建项目,Qt Core模块则是自动被加入。Qt为C++语言增加的特性就是在Qt Core模块实现,这些扩展特性由Qt的元对象系统实现。
     Qt的元对象系统包括信号与槽机制、属性系统、动态转换等。

2.元对象系统

    Qt的元对象系统(Meta-Object System)提供了对象之间通信的信号与槽机制、运行时类型信息和动态属性系统。
    元对象系统由以下三个基础组成。
  • QObject类是所有使用元对象系统的类的基类。
  • 在一个类的private部分声明 Q_OBJECT宏,使得类可以使用元对象的特性,如 动态属性、信号与槽
  • MOC(元对象编译器)为每个QObject的子类提供必要的代码来实现元对象系统的特性。
        构建项目时,MOC工具读取C++源文件,当它发现类的定义里有Q_OBJECT宏时,他就会为这个类生成另外一个包含有元对象支持代码的C++源文件,这个生成的源文件连同类的实现文件一起被编译和连接。
        除了信号与槽机制外,元对象还提供如下一些功能。
  • QObect::metaObject()函数:返回类关联的元对象,元对象类QMetaObject包含元对象的一些接口函数。
        
    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,QTimer不是QAbstractButton的子类
  • QObject::tr()和QObject::trUtf8()函数可翻译字符串,用于多语言界面设计。
  • QObject::setProperty()和QObject::property()函数用于通过属性名称动态设置和获取属性值。
  • qobiect_cast()函数动态投射:假设QMyWidget是QWidget的子类并且在类定义中声明了QOBJECT宏(动态属性需要定义这个宏),动态投射可以使程序在运行时对不同的对象做不同的处理。
QObject *obj = new QMyWidget;//变量obj定义为QObject指针,但它实际指向QMyWidget类
QWidget *widget = qobiect_cast(obj);//obj实际是QMyWidget类,是QWidget的子类,动态投射成功
//qobiect_cast()并不区分Qt内建的类型和用户自定义的类型
QMyWidget *myWidget = qobiect_cast(obj);//obj实际是QMyWidget类,故投射其自身,也可以动态投射成功
QLabel *label = qobiect_cast(obj);//返回label为NILL,因为QMyWidget不是QLabel的子类。

3.属性系统

(1)属性定义

        属性定义基于元对象系统实现, 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]
             [STOREO  bool]
             [USER  bool]
             [CONSTANT]
             [FINAL]
        )
Q_PROPERTY宏定义一个返回值类型为type,名称为name的属性,用READ、WRITE关键
字定义属性的读取、写入函数,还有其他的一些关键字定义属性的一些操作特性。属性的类型可
以是QVariant支持的任何类型,也可以用户自定义类型。
Q_PROPERTY宏定义属性的一些主要关键字的意义如下。
READ指定一个读取属性值的函数,没有MEMBER关键字时必须设置READ。
WRITE指定一个设定属性值的函数,只读属性没有WRITE设置。
MEMBER指定一个成员变量与属性关联,成为可读可写的属性,无需再设置READ和WRITE。
RESET是可选的,用于指定一个设置属性缺省值的函数。
NOTIFY是可选的,用于设置一个信号,当属性值变化时发射此信号。
DESIGNABLE表示属性是否在QtDesigner里可见,缺省为true。
CONSTANT表示属性值是一个常数,对于一个对象实例,READ指定的函数返回值是常
数,但是每个实例的返回值可以不一样。具有CONSTANT关键字的属性不能有WRITE
和NOTIFY关键字。
FINAL表示所定义的属性不能被子类重载。
QWidget类定义属性的一些例子如下:
Q_PROPERTY(bool  focus  READ  hasFocus)
Q_PROPERTY(bool   enabled  READ  isEnabIed  WRITE  setEnab1ed)
Q_PROPERTY(QCursor cursor  READ   cursor   WRITE  setCursor   RESET   unsetCursor)

(2)属性的使用

不管是否用READ和WRITE定义了接口函数,只要知道属性名称,就可以通过QObject::property()
读取属性值,并通过QObject::setProperty()设置属性值。例如:
QPushButton*button  = new QPushButton;
QOject*object = button;
object->setProperty("flat",true);
bool isFlat=object->property("flat");

(3)动态属性

QObject::setProperty()函数可以在 运行时为类定义一个 新的属性,称之为 动态属性。动态属性
针对类的实例定义的。
动态属性可以使用QObject::property()查询,就如在类定义里用Q_PROPERTY宏定义的属性
例如,在数据表编辑界面上,一些字段是必填字段,就可以在初始化界面时为这些字段的关
联显示组件定义一个新的required属性,并设置值为“true”,如:
editName->setProperty("required',"true");
comboSex->setProperty("required',''true");
checkAgree->set?roperty("required","true");

(4)类的附加信息

属性系统还有一个宏 Q_CLASSINFO() ,可以为类的元对象定义 “ 名称一一一值 ” 信 息,如:
class QMyClass:public  QObject
{
    Q_OBJECT
    Q_CLASSINFO("author","Wang"
    Q_CLASSINFO("company“,"UPC")
    Q_CLASSINFO("version","3.0.1")
    public:
    ...
};
用Q_CLASSINFO()宏定义附加类信息后,可以通过元对象的一些函数获取类的附加信息,如
classlnfo(int)获取某个附加信息,函数原型定义如下:
QMetaCIassInfo  QMetaObject::classlnfo(int  index)  const
返回值是QMetaClassinfo类型,有name()和value()两个函数,可获得类附加信息的名称和值。

4.信号与槽

Qt使用信号与槽的机制实现对象间通信, 由 Qt 的元对象系统支持实 现,它隐藏了复杂的底层实现,完成信号与槽的关联后, 发射信号时并不需要知道 Qt 是如何找到槽函数的。
某些开发架构使用回调函数(callback)实现对象间通信。与回调函数相比,信号与槽的执行
速度稍微慢一点,因为需要查找连接的对象和槽函数,但是这种差别在应用程序运行时是感觉不
到的,而其提供的灵活性却比回调函数强很多。

(1)connect()函数的不同参数形式

QObject::connect()函数有多重参数形式,一种参数形式的函数的 句法如下:
connect(sender,SIGNAL(signal()),receiver,  SLOT(slot()));
这里使用了宏SIGNAL()和SLOT()指定信号和槽函数,而且如果信号和槽函数带有参数,还
需注明参数类型,如:
connect(spinNum,SIGNAL(valueChanged(int)),this,SLOT(updateStatus(int));
另外一种参数形式的connect()函数是对于具有默认参数的信号与槽(即信号名称是唯一的,没有参数不同而同名的两个信号),可 以使用函数指针形式进行关联。
connect(lineEdit,&QLineEdit::textChanged, this, &widget::on_textChanged);
而对于具有不同参数的同名信号就不能采用函数指针的方式进行信号与槽的关联,例如
QSpinBox有两个valueChanged()信号,分别是:
void  QSpinBox::valueChanged(inti)
void  QSpinBox::valueChanged(const  QString &text)
即使在自定义窗体widget里定义了一个槽函数,如:
void onVaIueChanged(int i);
在使用下面的语句进行关联时,编译会出错。
connect(spinNum,&QSpinBox::valueChanged,this,&widget::onValueChanged);
不管是哪种参数形式的connect()函数,最后都有一个参数Qt::connectionType  type,缺省值为
Qt::AutoConnection。枚举类型Q::ConnectionType表示了信号与槽之间的关联方式,有以下几种
取值。
  • Qt::AutoConnection(缺省值):如果信号的接收者与发射者在同一个线程,就使用Qt::Direct
  • Connection方式:否则使用Qt::QueuedConnection方式,在信号发射时自动确定关联方式。
  • Qt::DirectConnection:信号被发射时槽函数立即执行,槽函数与信号在同一个线程。
  • Qt::QueuedConnection:在事件循环回到接收者线程后执行槽函数,槽函数与信号在不同
的线程。
  • Qt::BlockingQueuedConnection:与Qt::QueuedConnection相似,只是信号线程会阻塞直到槽
        函数执行完毕。当信号与槽函数在同一个线程时绝对不能使用这种方式,否则会造死锁。

(2)使用sender()获得信号发射者

在槽函数里,使用QObject::sender()可以获取信号发射者的指针。如果知道信号发射者的类型,
可以将指针投射为确定的类型,然后使用这个确定类的接口函数。
例如,在QSpinBox的valueChanged(int)信号的槽函数里,可以通过sender()和qobject_cast
获得信号发射者的指针,从而对信号发射者进行操作。
QSpinBox  *spinBox =  qobject_cast ( sender() );

(3) 自定义信号及其使用

在自己设计的类里也可以自定义信号,信号就是在类定义里声明的一个函数,但是这个函数
无需实现,只需发射(emit)o
例如,在下面的自定义类QPerson的signals部分定义一个信号ageChanged(int)。
Class  QPerson : public QObject
{
    Q_OBJECT
    private:
    int m_age = 10;
    public:
    void incAge();
    signals:
    void ageChanged(int value);
};
信号函数必须是无返回值的函数,但是可以有输入参数。信号函数无需实现,只需在某些条
件下发射信号。例如,在incAge()函数中发射信号,其代码如下。
void QPerson::incAge()
{
        m_age++;
        emit ageChanged(m_age);//发射信号
}
    在incAge()函数里,当私有变量m_age变化后,发射信号ageChanged(int),表示年龄发生了
变化。至于是否有与此信号相关联的槽函数,信号发射者并不管。如果在使用QPerson类对象的
程序中为此信号关联了槽函数,在incAge()函数里发射此信号时,就会执行相关联的槽函数。至
于是否立即执行槽函数,发射信号的线程是否等待槽函数执行完之后再执行后面的代码,与
connect()函数设置信号与槽关联时设置的连接类型以及信号与槽是否在同一个线程有关。

5.元对象特性实例

(1)定义Person类
//qperson.h文件
#ifndef QPERSON_H
#define QPERSON_H


#include 


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
//qperson.cpp文件
#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);//发射信号
}
(2)定义QmyWidget类
//qmywidget.h文件
#ifndef QMYWIDGET_H
#define QMYWIDGET_H


#include 
#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
//qmywidget.cpp文件
#include "qmywidget.h"
#include "ui_qmywidget.h"
#include    

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");//动态属性
    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(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(sender());
    if (spinBox->property("isBoy").toBool())
    {
        boy->setAge(spinBox->value());
        //spinBox->blockSignals(true);
    }
    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();ipropertyCount();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();iclassInfoCount();++i)
    {
       QMetaClassInfo classInfo=meta->classInfo(i);
        ui->textEdit->appendPlainText(
           QString("Name=%1; Value=%2").arg(classInfo.name()).arg(classInfo.value()));
    }
}

(3)main()函数
//main()函数
#include "qmywidget.h"
#include 


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QmyWidget w;
    w.show();


    return a.exec();
}
(4)界面示意图
【QT】Qt核心特点_第1张图片

你可能感兴趣的:(Qt,开发语言,qt)