| 主页 | 所有的类 | 主要的类 | 注释的类 | 分组的类 | 函数 |
这里我们提供了一个详细的例子。CardLayout类是从同名的Java布局管理器得到的灵感。它把项目(窗口部件或者嵌套的布局)布置到彼此的顶端,每个项目通过QLayout::spacing()偏移的。
要写自己的布局类,你必须像下面这样定义:
在绝大多数情况下,你也需要实现minimumSize()。
#ifndef CARD_H
#define CARD_H
#include <qlayout.h>
#include <qptrlist.h>
class CardLayout : public QLayout
{
public:
CardLayout( QWidget *parent, int dist )
: QLayout( parent, 0, dist ) { }
CardLayout( QLayout* parent, int dist)
: QLayout( parent, dist ) { }
CardLayout( int dist )
: QLayout( dist ) { }
~CardLayout();
void addItem(QLayoutItem *item);
QSize sizeHint() const;
QSize minimumSize() const;
QLayoutIterator iterator();
void setGeometry(const QRect &rect);
private:
QPtrList<QLayoutItem> list;
};
#endif
#include "card.h"
首先我们为布局定义一个迭代。布局迭代被布局系统用作内部处理图形窗口部件删除操作的。它们也可以被提供给应用程序员。
这里有两个两个不同的相关类:QLayoutIterator是一个可以提供给应用程序员可见的类,它是明确地被共享。QLayoutIterator包括一个可以做任何事情地QGLayoutIterator。我们必须生成一个知道如何迭代我们地布局类的QGLayoutIterator的子类。
在这种情况下,我们选择一个简单的实现:我们把一个整数索引和指针存储到一个列表中。每一个QGLayoutIterator的子类都必须实现current()、next()和takeCurrent(),还有一个构造函数。在我们的例子中我们不需要析构函数。
class CardLayoutIterator : public QGLayoutIterator
{
public:
CardLayoutIterator( QPtrList<QLayoutItem> *l )
: idx( 0 ), list( l ) { }
QLayoutItem *current()
{ return idx < int(list->count()) ? list->at(idx) : 0; }
QLayoutItem *next()
{ idx++; return current(); }
QLayoutItem *takeCurrent()
{ return list->take( idx ); }
private:
int idx;
QPtrList<QLayoutItem> *list;
};
我们必须实现QLayout:iterator()返回一个这个布局的QLayoutIterator。
QLayoutIterator CardLayout::iterator()
{
return QLayoutIterator( new CardLayoutIterator(&list) );
}
addItem()实现了布局项目的默认布置策略。它必须被实现。它被QLayout::add()使用,被把一个布局作为父布局的QLayout的构造函数使用,并且它被用来实现自动添加这一特性。如果你的布局有需要参数的高级布置选项,你将必须提供像QGridLayout::addMultiCell()一样的额外的访问函数。
void CardLayout::addItem( QLayoutItem *item )
{
list.append( item );
}
布局对项目的增加负有责任。因为QLayoutItem不继承QObject,我们必须人工地删除这些项目。QLayout::deleteAllItems()函数使用我们前面定义的迭代来删除布局中的所有项目。
CardLayout::~CardLayout()
{
deleteAllItems();
}
setGeometry()函数实际上执行了这个布局。作为参数提供的矩形不包括margin()。如果相关的话,项目之间的距离请使用spacing()。
void CardLayout::setGeometry( const QRect &rect )
{
QLayout::setGeometry( rect );
QPtrListIterator<QLayoutItem> it( list );
if (it.count() == 0)
return;
QLayoutItem *o;
int i = 0;
int w = rect.width() - ( list.count() - 1 ) * spacing();
int h = rect.height() - ( list.count() - 1 ) * spacing();
while ( (o = it.current()) != 0 ) {
++it;
QRect geom( rect.x() + i * spacing(), rect.y() + i * spacing(),
w, h );
o->setGeometry( geom );
++i;
}
}
sizeHint()和minimumSize()通常情况下在实现中非常相似。这两个函数返回的大小应该包括spacing(),但不包括margin()。
QSize CardLayout::sizeHint() const
{
QSize s( 0, 0 );
int n = list.count();
if ( n > 0 )
s = QSize( 100, 70 ); // start with a nice default size
QPtrListIterator<QLayoutItem> it( list );
QLayoutItem *o;
while ( (o = it.current()) != 0 ) {
++it;
s = s.expandedTo( o->minimumSize() );
}
return s + n * QSize( spacing(), spacing() );
}
QSize CardLayout::minimumSize() const
{
QSize s( 0, 0 );
int n = list.count();
QPtrListIterator<QLayoutItem> it( list );
QLayoutItem *o;
while ( (o = it.current()) != 0 ) {
++it;
s = s.expandedTo( o->minimumSize() );
}
return s + n * QSize( spacing(), spacing() );
}
这个布局没有实现heightForWidth()。
我们忽略了QLayoutItem::isEmpty(),这也就是说这个布局将会把隐藏的窗口部件显示出来。
对于复杂的布局,通过存储计算结果可以使速度得到很大的提高。在这种情况下,实现QLayoutItem::invalidate()来把被存储的数据弄脏。
调用QLayoutItem::sizeHint(),其它的也许更浪费时间,如果你在同一个函数中再一次稍晚的情况下需要这个值,你应该把它存储成局部变量。
你不应该在同一个函数中对同一项目调用QLayoutItem::setGeometry()两次。如果这个项目有几个子窗口部件的话,它会很浪费时间,因为它将不得不每次都执行一个完整的布局。相反,计算几何位置并且设置它。(这不仅仅是应用于布局,如果你实现了你自己的resizeEvent()你应该做同样的事情。)
| Copyright © 2002 Trolltech | Trademarks | 译者:Cavendish | Qt 3.0.5版
|