Trolltech | Documentation | Qt Quarterly | « Forgot a tr()?

How to Find and Control Children
The parent-child relationship between QObjects is at the heart of Qt. Its essence is ownership: When the parent is deleted, its children and children's children to the last generation are also deleted. For widgets, the parent-child relationship often also means that the child is displayed within the parent's area.

Birth, Adoption, Death

Parents are usually set when QObjects are constructed. If the object is a QWidget, the parent widget can be changed dynamically using QWidget::reparent(); otherwise, it can be changed using QObject::insertChild() and removeChild().

Whenever an object gets a new child, it receives a ChildInserted event; and whenever it loses a child, it receives a ChildRemoved event. By reimplementing QObject::childEvent(), a parent can be made aware of these events. Many of Qt's built-in widgets, including QGroupBox, QMainWindow, QSplitter, and QWorkspace, reimplement this function to take control of their children's geometries (positions and sizes).

Finding Children

Q_CHILD() is one of those lesser-known macros that owe much of their anonymity to Qt's class-centric documentation. The macro retrieves a child by name and casts it to the right pointer type:

    QListBox *list = Q_CHILD( parent, QListBox, "list" );

It returns a null pointer if there is no child of the specified name and type.

The QObject::child() function is similar to Q_CHILD(), except that it supports recursive search:

    QListBox *clients = (QListBox *)
        parent->child( "clients", "QListBox", TRUE );

Recursive search is disabled by passing FALSE as the third argument.

The QObject::queryList() function is even more flexible. It returns a pointer to a newly-allocated QObjectList that contains pointers to all descendant objects that meet certain criteria. Its prototype is

    QObjectList *queryList( const char *inheritsClass = 0,
                            const char *objName = 0,
                            bool regexpMatch = TRUE,
                            bool recursiveSearch = TRUE );

(QObjectList is a typedef for QPtrList declared in .) The following code returns all descendants (children, grandchildren, etc.) of obj:

    QObjectList *list = obj->queryList();

This returns only the descendants that inherit QListBox:

    QObjectList *list = obj->queryList( "QListBox" );

This returns all obj descendants whose names start with 'lb':

    QObjectList *list = obj->queryList( 0, "lb.*" );

This returns all children (but not grandchildren etc.) of obj that inherit QFrame and whose name is 'frame':

    QObjectList *list = obj->queryList( "QFrame", "frame", FALSE, FALSE );

The queryList() function is very useful for accessing another object's children behind its back. It has been successfully used to change the text of the QFileDialog's "OK" button by apprentice reverse-engineers and partially reformed Perl programmers.

The QObject::children() function returns a const pointer to the QObject's internal child list:

    const QObjectList *list = obj->children();
    QObjectListIt it( *list );
    while ( it.current() ) {
        do_something( it.current() );
        ++it;
    }

Similarly, the static function QObject::objectTrees() returns the list of top-level objects (those with no parent):

    const QObjectList *list = QObject::objectTrees();

These lists are automatically updated when children are added or removed.

Who's Whose?

The function QObject::dumpObjectTree()[1] prints the descendants' tree of an object. For the QTabDialog, shown below right, it printed this tree:

    QTabDialog::dialog <574,451,133,123>
      QBoxLayout::unnamed
        QBoxLayout::unnamed
        QBoxLayout::unnamed
      QPushButton::ok <45,91,82,26>
      QTabWidget::tab widget <6,6,119,75>
        QTabBar::tab control <0,0,48,24>
          QToolButton::qt_right_btn I
          QToolButton::qt_left_btn I
          QAccel::tab accelerators
        QWidget::tab base I
        QWidgetStack::tab pages <0,22,119,51>
          QFrame::first <2,2,115,47>
          QObject::unnamed
          QWidgetStackPrivate::Invisible::unnamed <2,2,115,47>

The QTabDialog has three children: 'unnamed' (QBoxLayout), 'ok' (QPushButton), and 'tab widget' (QTabWidget). An <x, y, width, height> quadruple is output for visible widgets, and an 'I' is output for invisible widgets.

By combining dumpObjectTree() with objectTrees(), you can easily display all objects in memory during the execution of your application:

    const QObjectList *list = QObject::objectTrees();
    QObjectListIt it( *list );
    while ( it.current() ) {
        it.current()->dumpObjectTree();
        ++it;
    }

The QObject::dumpObjectTree() function has a twin, dumpObjectInfo(), that prints additional information about an object. It is very useful for debugging signal-to-slot connections.

The dumpAllObjectTrees() function below displays this information more readably in a QListView.

    void dumpAllObjectTrees()
    {
        dumpRecursive( QObject::objectTrees(), 0 );
    }

    void dumpRecursive( const QObjectList *list,
                        QListViewItem *parent )
    {
        if ( list == 0 )
            return;
        QListView *listView = 0;
        QListViewItem *child;
        if ( parent == 0 ) {
            listView = new QListView( 0 );
            listView->setRootIsDecorated( TRUE );
            listView->addColumn( "Class" );
            listView->addColumn( "Name" );
            listView->addColumn( "Geometry" );
            listView->setSorting( -1 );
            listView->show();
        }
        QObjectListIt it( *list );
        QObject *obj;
        while ( (obj = it.current()) ) {
            if ( obj == listView ) {
                ++it;
                continue;
            }
            QString flags;
            if ( obj->isWidgetType() ) {
                QWidget *w = (QWidget *) obj;
                if ( w->isVisible() ) {
                    flags.sprintf( "<%d,%d,%d,%d>", w->x(),
                                   w->y(), w->width(),
                                   w->height() );
                } else {
                    flags = "invisible";
                }
            }
            child = parent ? new QListViewItem( parent )
                           : new QListViewItem( listView );
            child->setText( 0, obj->className() );
            child->setText( 1, obj->name() );
            child->setText( 2, flags );
            dumpRecursive( it.current()->children(), child );
            ++it;
        }
    }

These tricks and techniques allow us to find, watch, and control objects with an ease that even Big Brother would envy!


[1] QObject::dumpObjectTree() does nothing if the Qt library is compiled in release mode.


This document is licensed under the Creative Commons Attribution-Share Alike 2.5 license.

Copyright © 2002 Trolltech. Trademarks Qt Quarterly