Wiki

Как отыскать "детей" и управлять ими

Отношения родитель - ребенок между объектами являются "сердцем" QT. Основным является следующее правило : когда удален "родитель" - его "дети", и дети детей до последнего колена тоже удаляются. Для компонентов часто имеет значение то, что отношения между "родителем" и "ребенком" означает отображение процесса - "ребенка" только внутри зоны "родителя".

Рождение, существование, смерть

"Родители" обычно задаются в тот момент, когда строятся QObject`ы. Если объект является QWidget, то родительский компонент может быть изменен, при помощи функции QWidget::reparent(); иначе его можно изменить функциями QObject::InsertChild() и removeChild.

В тот момент, когда объект получает нового "ребенка", он также получает ChildInserted событие, соответственно и наоборот, при потере процесса - "ребенка" будет получено событие ChildRemoved. При реимплементации QObject::childEvent(), процесс - "родитель" может предостережен от этих событий. Много из встроеных в QT объектов, таких как QGroupBox, QMainWindow, QSplitter и QWorkspace реимплементируют эту функцию, для того, чтобы перехватить контроль за геометрическими параметрами своих "детей" (позиция и размер).

Находим детей

Q_CHILD является одним из наименее известных макросов, и своей неизвестностью он обязан классической QT документации. Этот макрос получает имя ребенка, и накладывает его на указатель.
QListBox *list = Q_CHILD( parent, QListBox, "list" );
Функция вернет null указатель, если не будет найден объект - "ребенок" соответствующего типа.

Функция QObject::child() подобна Q_CHILD(), за исключением того, что она поддерживает рекурсивный поиск :

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

Рекурсивный поиск может быть выключен, если третьим аргументом будет FALSE.

Функция QObject::queryList() гораздо более гибкая. Она возвращает указатель на новораспределенный QObjectList, который содержит указатели на все наследованые объекты, удовлетворяющие определенному критерию. Вот прототип этой функции :

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

QObjectList является typedef для объявленного QPtrList). Следующий код вернет всех наследников: "детей", "внуков", и т.д. объекта:
 
QObjectList *list = obj->queryList();

Этот код вернет только наследников, которые наследуют QListBox:
QObjectList *list = obj->queryList( "QListBox" );

Этот код возвращает все obj наследников, чьи имена начинаются с 'lb'
 
QObjectList *list = obj->queryList( 0, "lb.*" );

Возвращает всех "детей" (но не "внуков" !), данного obj, которые наследуют QFrame, и чье имя 'frame' :
 
    QObjectList *list = obj->queryList( "QFrame", "frame", FALSE, FALSE );

Функцию queryList() хорошо использовать для получения доступа к другим детям объектов, у них за спиной. Она была успешно использована для изменения текста QFileDialog`s кнопки "OK" начинающими.
 
const QObjectList *list = obj->children();
QObjectListIt it( *list );
while ( it.current() ) {
    do_something( it.current() );
    ++it;
}

Подобным образом, статическая функция QObject::objetctTrees() возвращает список «верхнеуровневых объектов» (объектов без родителей):
const QObjectList *list = QObject::objectTrees();

Эти списки будут автоматически обновлены, когда дети будут добавлены, или удалены.

Кто чей?

Функция QObject::dumpObjectTree() [1] печатает дерево объекта. К примеру для QTabDialog в этом дереве покажутся его наследники.

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>

Функция QTabDialog имеет трех детей : 'unnamed' (QBoxLayout), 'ok' (QPushButton), and 'tab widget' (QTabWidget).

При соединении dumpObjectTree() с objectTrees(), вы можете легко вывести на экран все объекты в памяти, на протяжении выполнения вашей программы.

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

Функция QObject::dumpObjectTree() имеет близнеца dumpObjectInfo(), которая печатает дополнительную информацию об объекте. Ее очень удобно использовать для отладки signal-to-slot соединений.

Функция dumpAllObjectTrees() показывает информацию в более удобном виде в 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;
    }
}

[1]
[1] QObject::dumpObjectTree() не делает ничего, если библиотека QT скопмпилирована в release режиме.


Copyright © 2002 Trolltech. Trademarks