【翻译 + 整理】QObject详解

一、描述

1、QObject类是所有Qt对象的基类。

2、QObject是Qt对象模型的核心。该模型的核心特性是一种非常强大的无缝对象通信机制,称为信号和槽。可以使用connect()将信号连接到槽,并使用disconnect()销毁连接。为了避免永无止境的通知循环,可以使用blockSignals()临时阻止信号。受保护的函数connectNotify()和disconnectNotify()使跟踪连接成为可能。

3、对象通过对象树的形式组织。当创建一个以另一个对象为父对象的QObject时,该对象将自动将自己添加到父对象的children()列表中。父对象拥有对象的所有权,父对象将在析构函数中自动删除它的子对象。可以使用findChild()或findChildren()查找对象的子对象。

4、每个对象都有一个objectName(),其类名可以通过相应的metaObject()找到。可以使用inherits()函数确定对象的类是否继承QObject继承层次结构中的另一个类。

5、当一个对象被删除时,它会发出一个destromed()信号。

6、QObjects可以通过event()接收事件并过滤其他对象的事件。可以重新实现方便的处理程序childEvent(),以捕获子事件。

7、QObject在Qt中提供了基本的计时器支持。

8、对于实现信号、槽或属性的任何对象,Q_OBJECT宏都是必需的。还需要在源文件上运行元对象编译器。强烈建议在QObject的所有子类中使用此宏,不管它们是否实际使用信号、槽和属性,否则可能会导致某些函数表现出奇怪的行为。

9、所有Qt widgets都继承QObject。函数isWidgeType()返回一个对象是否是一个widget。它比qobject_cast<QWidget *>(obj)或obj->inherits(“QWidget”)快得多。

二、线程相关

1、当QObject接收到信号或发布的事件时,槽函数或事件处理程序将在对象所在的线程中运行。(如果QObject没有线程关联(即如果thread()返回nullptr),或者如果它位于没有运行事件循环的线程中,则它无法接收信号或发布的事件)

2、默认情况下,QObject存在于创建它的线程中。可以使用thread()查询对象的线程关联,并使用moveToThread()更改对象的线程关联。

扫描二维码关注公众号,回复: 12799056 查看本文章

3、所有QObject必须与其父对象位于同一线程中。因此:

  • 如果所涉及的两个QObject位于不同的线程中,setParent()将失败。
  • 当一个QObject对象被移动到另一个线程时,该对象的所有子线程也将被自动移动。
  • 如果QObject对象有父对象,moveToThread()将失败。
  • 如果QObject对象是在QThread::run()中创建的,则它们不能成为QThread对象的子对象,因为QThread对象不在调用QThread::run()的线程中。

注意:QObject的成员变量不会自动成为其子变量。必须通过向子构造函数传递指针或调用setParent()来设置父子关系。如果没有此步骤,调用moveToThread()时,对象的成员变量将保留在旧线程中。

三、无复制构造函数和赋值运算符

QObject既没有复制构造函数,也没有赋值运算符。它们是在宏Q_DISABLE_COPY()中设置了禁止生成。实际上,所有从QObject派生的Qt类(直接或间接)都使用这个宏来声明它们的复制构造函数和赋值操作符是私有的。

这带来的结果是应该使用指向QObject(或者指向您的QObject子类)的指针。例如,如果没有复制构造函数,就不能使用QObject的子类作为存储在某个容器类中的值,必须存储指针。

例:企图拷贝和赋值QWidget时会报错:

函数=delete

四、自动连接(ui文件)

Qt的元对象系统提供了一种机制来自动连接QObject子类及其子类之间的信号和槽。只要用合适的对象名定义对象,并且槽遵循简单的命名约定,就可以在运行时通过QMetaObject::connectSlotsByName()函数执行此连接。

uic生成调用此函数的代码,以便在使用Qt Designer创建的窗体上的小部件之间执行自动连接。

例:ui文件拖入一个按钮,objectName设置为btn,则按钮发出clicked()信号时就会调用on_btn_clicked()槽函数(此函数必须声明为槽)。他们之间通过QMetaObject::connectSlotsByName()关联起来的。

五、动态属性

从Qt4.2开始,可以在运行时向QObject实例添加动态属性,也可以从QObject实例中删除动态属性。动态属性不需要在编译时声明,但是它们提供了与静态属性相同的优点,并且使用相同的API进行操作—使用property()读取它们,使用setProperty()写入它们。

从qt4.3开始,Qt Designer支持动态属性,并且标准Qt小部件和用户创建的表单都可以被赋予动态属性。

六、国际化(I18n)

所有QObject子类都支持Qt的翻译特性,使得将应用程序的用户界面翻译成不同的语言成为可能。要使用户可见的文本可翻译,必须将其包装在对tr()函数的调用中。

七、属性成员

QObject只有一个属性:objectName,用来包含对象的名称,当要查找子对象时可以使用findChild()通过对象名称查找对象。默认此属性为空

八、成员函数

1、QObject::QObject(QObject *parent = nullptr)构造函数

将parent设置为nullptr将构造一个没有父对象的对象。如果对象是一个QWidget,它将成为一个顶级窗口。这个函数可以通过元对象系统和QML调用。

2、[slot] void QObject::deleteLater()

发起一个事件,当事件循环处理到该事件时删除当前对象,源码:

void QObject::deleteLater()
{
    QCoreApplication::postEvent(this, new QDeferredDeleteEvent());
}

如果调用此函数时事件循环未运行,则一旦启动事件循环,该对象将被删除。如果在主事件循环停止后调用deleteLater(),则不会删除该对象。从Qt4.8开始,如果对没有运行事件循环的线程中的对象调用deleteLater(),则该对象将在线程完成时被销毁。

可参考:Qt deleteLater原理

3、[signal] void QObject::destroyed(QObject *obj = nullptr)

对象被删除之前发送此信号。

4、[signal] void QObject::objectNameChanged(const QString &objectName)

此信号在对象名称更改后发出。新对象名作为参数传递。注意:这是一个私有信号。它可以用于信号连接,但不能由用户发射。

5、QObject::~QObject()析构函数

销毁对象,删除其所有子对象。与对象关联的所有信号都将自动断开,并且对象的任何挂起的已发布事件都将从事件队列中删除。建议任何时候都不要直接delete一个QObject对象,应该用deleteLater()代替。

6、bool QObject::blockSignals(bool block)

如果block为true,则此对象发出的信号将被阻塞(即,发出信号将不会调用任何与之连接的槽对象)。如果block为false则取消阻塞。

建议使用QSignalBlocker代替,更安全。信号阻塞器QSignalBlocker

7、bool QObject::signalsBlocked() const

如果当前对象处于信号被阻塞状态返回true,否则返回false。

8、void QObject::childEvent(QChildEvent *event)

子对象事件处理,子对象事件就这几种:

例:

void Widget::childEvent(QChildEvent *event)
{
    QEvent::Type t = event->type();
    if(t == QEvent::ChildAdded)
    {
        if(QObject * child = event->child())
        {
            qDebug()<<"添加了子对象,对象名称:"<< child->objectName();
        }
    }
    else if(t == QEvent::ChildRemoved)
    {
        if(QObject * child = event->child())
        {
            qDebug()<<"移除了子对象,对象名称:"<< child->objectName();
        }
    }
}

void Widget::on_btn_clicked()
{
    QPushButton * btn = new QPushButton("新增按钮");
    btn->setObjectName("newBtn");
    btn->setParent(this);
    btn->show();
    btn->setGeometry(10,10,100,50);
}

void Widget::on_btn_2_clicked()
{
    if(QPushButton * btn = this->findChild<QPushButton *>("newBtn"))
    {
        btn->setParent(nullptr);
        btn->deleteLater();
    }
}

9、const QObjectList &QObject::children() const

获取对象的子对象列表。QObjectList 是QList<QObject*>的别名(typedef QList<QObject*> QObjectList;)

10、void QObject::connectNotify(const QMetaMethod &signal)

当有信号连接当前对象时,调用此函数。

 貌似只有本身的信号连接本身的函数时才会调用,其他对象的信号连接本身的函数不会调用。disconnectNotify()功能与之相反。

11、void QObject::customEvent(QEvent *event)

处理自定义事件,见:QT事件:自定义事件

12、void QObject::dumpObjectInfo() const        

将信号的信息转存到调试输出。

13、void QObject::dumpObjectTree() const

将对象树信息转存到调试输出。

14、QList<QByteArray> QObject::dynamicPropertyNames() const (实用)

获取所有动态属性名。

15、bool QObject::event(QEvent *e)

事件处理,一般由子类重写。

16、bool QObject::eventFilter(QObject *watched, QEvent *event)、QObject::installEventFilter(QObject *filterObj)、void QObject::removeEventFilter(QObject *obj)

事件过滤器,监视注册过的对象,要过滤掉事件,即停止进一步处理,则返回true;否则返回false。

  MainWindow::MainWindow()
  {
      textEdit = new QTextEdit;
      textEdit->installEventFilter(this);
  }

  bool MainWindow::eventFilter(QObject *obj, QEvent *event)
  {
      if (obj == textEdit) {
          if (event->type() == QEvent::KeyPress) {
              QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
              qDebug() << "Ate key press" << keyEvent->key();
              return true;
          } else {
              return false;
          }
      } else {
          // pass the event on to the parent class
          return QMainWindow::eventFilter(obj, event);
      }
  }

在上面的示例中,未处理的事件被传递给基类的eventFilter()函数,因为基类可能出于自身的内部目的重新实现了eventFilter()。

某些事件,如QEvent::ShortcutOverride,必须被显式接受(通过对它们调用accept()),以防止传播。

17、T QObject::findChild(const QString &name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const

查找单个子对象,参数1是objectName,参数2是标识是否递归查找(在对象的子对象中查找)。

18、QList<T> QObject::findChildren(const QString &name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const

       QList<T> QObject::findChildren(const QRegularExpression &re, Qt::FindChildOptions options = Qt::FindChildrenRecursively) const

查找满足条件的子对象,第二个版本的objectName与正则表达式匹配。

19、bool QObject::inherits(const char *className) const

获取某个类是否继承自另一个类。

  QTimer *timer = new QTimer;
  timer->inherits("QTimer");          // true
  timer->inherits("QObject");         // true

20、bool QObject::isSignalConnected(const QMetaMethod &signal) const (实用)

某信号是否至少有一个接收者。

    connect(this,&Widget::linked,this,&Widget::test);
    static const QMetaMethod linkedSignal = QMetaMethod::fromSignal(&Widget::linked);
    qDebug()<<isSignalConnected(linkedSignal);//true
    disconnect(this,&Widget::linked,this,&Widget::test);
    qDebug()<<isSignalConnected(linkedSignal);//false

当需要仅在某些东西连接到信号时才执行昂贵代价的初始化时此函数很有用。

21、bool QObject::isWidgetType() const

是否是一个QWidget类型。(相当于调用:inherits("QWidget");)

22、const QMetaObject *QObject::metaObject() const

获取当前对象的元对象。

元对象包含有关继承QObject的类的信息,例如类名、父类名、属性、信号和槽。每个包含Q_OBJECT宏的QObject子类都有一个元对象。

信号/槽连接机制和属性系统需要元对象信息。inherits()函数也使用元对象。

  QObject *obj = new QPushButton;
  obj->metaObject()->className();             // returns "QPushButton"

23、void QObject::moveToThread(QThread *targetThread)

更改此对象及其子对象的线程关联。如果对象有父对象,则无法移动。事件处理将在targetThread中继续。

要将对象移动到主线程,请使用QApplication::instance()检索指向当前应用程序的指针,然后使用QApplication::thread()检索应用程序所在的线程。例如:

  myObject->moveToThread(QApplication::instance()->thread());

targetThread为nullptr,则此对象及其子对象的所有事件处理都将停止,因为它们不再与任何线程关联。

targetThread如果不是nullptr,则发布到此对象的任何新事件都将在targetThread中处理;如果是nullptr,则不会对该对象或其子对象进行事件处理,因为它们不再与任何线程关联。

对象的所有活动计时器都将重置。计时器首先在当前线程中停止,然后在targetThread中重新启动(间隔相同)。

在更改线程关联之前,会向该对象发送QEvent::ThreadChange事件。

24、QObject *QObject::parent() const

获取父对象。

25、QVariant QObject::property(const char *name) const

获取属性,如果属性不存在,获取的QVariant是无效的。

26、int QObject::receivers(const char *signal) const     (实用)

获取一个信号有多少个接收者。(信号连接信号的情况也算)

signals:
    void linked();
    void linked2();

    qDebug()<<receivers(SIGNAL(linked()));//0
    connect(this,&Widget::linked,this,&Widget::test);
    qDebug()<<receivers(SIGNAL(linked()));//1
    connect(this,&Widget::linked,this,&Widget::linked2);
    qDebug()<<receivers(SIGNAL(linked()));//2

27、QObject *QObject::sender() const

获取信号发送者。

如果在由信号激活的槽函数中调用,则返回指向发送信号的对象的指针;否则返回nullptr。

当从与此对象线程不同的线程通过Qt::DirectConnection调用槽时,此函数的返回值无效。不要在这种情况下使用此函数。

28、int QObject::senderSignalIndex() const

获取信号在元对象中方法的索引。

29、void QObject::setParent(QObject *parent)

设置父对象。

30、bool QObject::setProperty(const char *name, const QVariant &value)

设置属性。

  • 如果该属性已经在类中使用Q_PROPERTY定义,则在成功时返回true,否则返回false。
  • 如果该属性没有使用Q_PROPERTY定义,因此没有列在元对象中,则将其添加为动态属性并返回false。

有关所有可用属性的信息都是通过metaObject()和dynamicPropertyNames()提供的。

可以使用property()查询动态属性,并且可以通过将属性值设置为无效的QVariant来删除动态属性。更改动态属性的值会将QDynamicPropertyChangeEvent发送到对象。

注意:以“_q_开头的动态属性保留于内部使用。

31、QThread *QObject::thread() const

获取对象所在线程。

32、QString QObject::tr(const char *sourceText, const char *disambiguation = nullptr, int n = -1)

获取字符串的翻译文本,若是没有合适的文本则返回QString::fromUtf8(sourceText),参数2是标识字符串,用作:如果上下文有两个一样的源字符串但在翻译里表示不同的意思,则可以用标识字符串消除歧义。

九、成员变量

const QMetaObject QObject::staticMetaObject

静态元对象。

  QPushButton::staticMetaObject.className();  // returns "QPushButton"

十、相关非成员

T qobject_cast(const QObject *object)

如果给定的对象属于T类型(或子类),则返回转换为T类型的对象;否则返回nullptr。如果对象是nullptr,那么它也将返回nullptr。

类T必须继承(直接或间接)QObject并用Q_OBJECT宏声明。

类被认为是继承自身的。

十一、宏

1、QT_NO_NARROWING_CONVERSIONS_IN_CONNECT

信号槽参数禁止窄化转换。

比如信号参数为double类型,对应的槽函数类型为int,信号槽关联后运行起来是没问题的,在槽函数中处理时double类型的值会窄化转成int类型的值,在pro文件中加入此宏:

DEFINES += QT_NO_NARROWING_CONVERSIONS_IN_CONNECT

编译就会报错。

2、Q_CLASSINFO               (实用)

给类设置额外信息。

    QMetaClassInfo info = this->staticMetaObject.classInfo(0);
    qDebug()<<info.name() <<" : "<<info.value();
    info = this->staticMetaObject.classInfo(1);
    qDebug()<<info.name() <<" : "<<info.value();

3、Q_DISABLE_COPY(Class)

禁止拷贝和赋值。

4、Q_DISABLE_MOVE(Class)

禁止移动构造及移动赋值。

5、Q_DISABLE_COPY_MOVE(Class)

禁止拷贝和移动。

6、Q_EMIT、Q_SIGNALQ_SLOT

Q_EMIT的效果和emit一样。一些第三方库如boost会使用emit作为关键字。如果使用了这些第三方库则使用此宏代替emit。Q_SIGNALQ_SLOT、Q_SIGNALSQ_SLOTS类似

注:使用之前在pro文件中添加:

  CONFIG += no_keywords

7、Q_ENUM

向元对象系统注册枚举,必须放在Q_OBJECTQ_GADGET的声明后面。

    enum class Priority { High, Low, VeryHigh, VeryLow };
    Q_ENUM(Priority)
    qDebug()<< Widget::Priority::High<< Widget::Priority::Low;
    QMetaEnum m = QMetaEnum::fromType<Widget::Priority>();
    qDebug()<< "key To Value:"<< m.keyToValue("VeryHigh");
    qDebug()<< "value To Key:"<< m.valueToKey(static_cast<int>(Widget::Priority::VeryHigh));
    qDebug()<< "key Count:"<< m.keyCount();

注册过后的枚举可以用qDebug直接打印出来,key和value互转也很方便。

8、Q_FLAG、Q_NAMESPACEQ_ENUM_NSQ_FLAG_NS

见:QFlags详解

9、Q_GADGET

对于不继承QObject但仍希望使用QMetaObject提供的某些反射功能的类,Q_GADGET宏是Q_OBJECT宏的轻量级版本。与Q_OBJECT宏一样,它必须出现在类定义的私有部分中。

Q_GADGET可以具有Q_ENUM、Q_PROPERTY和Q_INVOKABLE,但它们不能有信号或槽。

Q_GADGET使类成员staticMetaObject可用。staticMetaObject类型为QMetaObject

10、Q_INTERFACES

这个宏告诉Qt类实现了哪些接口。实现插件时使用的。如:

  class BasicToolsPlugin : public QObject,
                           public BrushInterface,
                           public ShapeInterface,
                           public FilterInterface
  {
      Q_OBJECT
      Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.PlugAndPaint.BrushInterface" FILE "basictools.json")
      Q_INTERFACES(BrushInterface ShapeInterface FilterInterface)

  public:
      ...
  };

插件实现了 BrushInterface、ShapeInterface、FilterInterface这3个接口。

11、Q_INVOKABLE

将此宏应用于成员函数的声明,以允许通过元对象系统调用它们。宏写在返回类型之前,如:

  class Window : public QWidget
  {
      Q_OBJECT

  public:
      Window();
      void normalMethod();
      Q_INVOKABLE void invokableMethod();
  };

12、Q_NAMESPACE_EXPORT

13、Q_OBJECT

Q_OBJECT宏必须出现在类定义的private部分,使用此宏可以声明自己的信号和槽、使用Qt的元对象系统。这个宏要求类是QObject的子类。

14、Q_PROPERTY

此宏用于在继承QObject的类中声明属性。

  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])

属性名和类型以及READ函数是必需的。类型可以是QVariant支持的任何类型,也可以是用户定义的类型。其他项是可选的,但WRITE函数是常见的。属性默认为true,但USER默认为false。例:

    Q_PROPERTY(QString title READ title WRITE setTitle USER true)

15、Q_REVISION

设置槽函数和属性在元对象系统中的修订版本。

打印修订版本大于1的属性和方法: 

int main(int argc, char *argv[])
{
    QApplication a(argc,argv);
    Widget w;
    w.show();
    int expectedRevision = 1;
    const QMetaObject *windowMetaObject = w.metaObject();

    for (int i=0; i < windowMetaObject->methodCount(); i++)
        if (windowMetaObject->method(i).revision() > expectedRevision)
            debug windowMetaObject->method(i).name();

    for (int i=0; i < windowMetaObject->propertyCount(); i++)
        if (windowMetaObject->property(i).revision() > expectedRevision)
            debug windowMetaObject->property(i).name();

    a.exec();
}

16、Q_SET_OBJECT_NAME    (实用,可以便捷设置objectName)

把一个对象变量名(指针变量也可以)设为对象的objectName。(QObject及其子类才有用)

    Widget w666;
    Q_SET_OBJECT_NAME(w666);
    w666.show();
    debug w666.objectName();//w666

相关博文:

Qt事件:定时器事件

信号槽连接和断开的几种常用形式

猜你喜欢

转载自blog.csdn.net/kenfan1647/article/details/114693013