主页 | 所有的类 | 主要的类 | 注释的类 | 分组的类 | 函数 |
写你自己的布局管理器
这里我们提供了一个详细的例子。CardLayout类是从同名的Java布局管理器得到的灵感。它把项目(窗口部件或者嵌套的布局)布置到彼此的顶端,每个项目通过QLayout::spacing()偏移的。
要写自己的布局类,你必须像下面这样定义:
- 一个用来存放被布局处理的项目的数据结构。每一个项目就是一个QLayoutItem。我们将在例子中使用一个QPtrList。
- addItem(),如何向布局中添加一个项目。
- setGeometry(),如何构成一个布局。
- sizeHint(),布局优先选用的大小。
- iterator(),如何迭代布局。
在绝大多数情况下,你也需要实现minimumSize()。
card.h
#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
card.cpp
#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版
|