0
点赞
收藏
分享

微信扫一扫

QT(7)自定义Layout

QT(7)自定义Layout_数据结构

我们进一步学习构建稍微复杂一点的布局。通过对抽象类QLayout的继承来进行自己的布局。在这个例子中,我们将重构QLayout类为BorderLayout,QLayout是一个用于布局管理的基础抽象类,而也是QBoxLayout,QGridLayout,QFormLayout和QStackedLayout的继承类。资料来源:

  1. http://doc.qt.nokia.com/latest/layouts-borderlayout.html
  2. http://www.kuqin.com/qtdocument/customlayout.html。

一、搭建project的主题框架

我们创建自己的布局类为BorderLayout,使用borderlayout.cpp和borderlayout.h,构造我们的窗口mywindow.h和mywindow.cpp,以及主程序qtmain.cpp。下面是mywindows.cpp的内容,执行后如图所示。

MyWindow:: MyWindow() 
 { 
     QTextBrowser * centralWidget = new QTextBrowser; 
     centralWidget->setPlainText(tr("Central widget"));     BorderLayout * layout = new BorderLayout; 
     layout -> addWidget (centralWidget,BorderLayout::Center); 
     layout -> addWidget (createLabel("North"),BorderLayout::North); 
     layout -> addWidget (createLabel("West"),BorderLayout::West); 
     layout -> addWidget (createLabel("East 1"),BorderLayout::East); 
     layout -> addWidget (createLabel("East 2"),BorderLayout::East); 
     layout -> addWidget (createLabel("South"),BorderLayout::South); 
     setLayout(layout); 
     setWindowTitle(tr("Border Layout")); 
 } QLabel * MyWindow::createLabel(const QString &text)
 { 
     QLabel * label = new QLabel(text); 
     label->setFrameStyle(QFrame::Box |QFrame::Raised); 
     return label; 
 }

二、重点在建立我们的类BorderLayout,我们先建立一个用来存放被布局处理的项目的数据结构QList,每个entry为ItemWrapper,存放组件和位置信息。QLayout的抽象组件为QLayoutItem,实际可以是QLabel等等具体的组件。我们在borderlayout.h中定义:

private: 
     struct ItemWrapper 
     { 
         ItemWrapper(QLayoutItem * i,Position p){ 
             item = i; 
             position = p; 
         } 
         QLayoutItem * item; 
         Position postion; 
     }; 
     enum SizeType {MinumumSize, SizeHint}; 
     QSize calculateSize(SizeType sizeType) const;  //用于后面

三、建立我们自己的布局管理器,需要实现QLayout中的几个virtual的函数。addItem(), sizeHint(), setGeometry(), itemAt() and takeAt(),此外还需要实现minimumSize()用于确保我们的布局没有因为space太小而重置为0。我们去查QLayout的参考(http://doc.qt.nokia.com/latest/qlayout.html),其中pure virtual的有:addItem(),intemAt(),takeAt(),count(),这些都是用于处理item的,分别是增加(存储),查找,删除,总数。我们同时在destory函数中清除所有item。

/********** borderlayout.h PART 2 ***********/
 public: 
     void addItem (QLayoutItem * item); 
     QLayoutItem * itemAt(int index) const; 
     QLayoutItem * takeAt(int index); 
     int count() const; //在我们实际应用中,我们通常不使用addItem,而是使用addWidget,由于我们增加了一个方位因子,所以需要补充一个addWidget的方法。
     void addWidget(QWidget * w, Position p); /********** borderlayout.cpp PART 2 ***********/
BorderLayout::~BorderLayout() 
 { 
     QLayoutItem *l; 
     while((l = takeAt(0)) != NULL) 
         delete l; 
 } //在我们存储的数据中加入元素,addItem在实际上是很少调用的,一般都会使用addWidget,它将使用addItem。
 void BorderLayout :: addItem(QLayoutItem * item) 
 { 
     list.append(new ItemWrapper(item,West)); 
 } //takeAt表示从list中删除index的item,并返回它,而index必须是有效的数值,因此需要先行检查:0 <= i < size()).
 QLayoutItem * BorderLayout :: takeAt(int index) 
 { 
     if(index >= 0 && index < list.size()){ 
         ItemWrapper * layoutStruct = list.takeAt(index); 
         return layoutStruct -> item; 
     } 
     return 0; 
 } QLayoutItem * BorderLayout :: itemAt(int index) const
 { 
     ItemWrapper * wrapper = list.value(index); 
     if(wrapper) 
         return wrapper->item; 
     else 
         return 0; 
 } int BorderLayout::count() const 
 { 
     return list.size(); 
 }void BorderLayout :: addWidget(QWidget * item, Position position)
 { 
     list.append(new ItemWrapper(new QWidgetItem (item),position)); 
 }

四、编译:undefined reference to `BorderLayout::sizeHint() const QLayoutItem是QLayout操作的一个抽闲的item,有一些方法从QLayoutItem中继承,例如sizeHint表示implemented in subclasses to return the preferred size of this item,那么对于layout则表示整个layout的大小。而在QLayout中说明: sizeHint(), 此外通常还需要实现minimumSize(),我们一起补上,此外,我们还加上了expandingDirections(用于表明这个layout可以使用比sizeHint()更多的空间,并允许向那个维度扩展)

/************* borderlayout.cpp PART 3 ***************/
 Qt::Orientations BorderLayout::expandingDirections() const 
 { 
     return Qt::Vertical | Qt::Horizontal; //两个方向均允许扩展。
}QSize BorderLayout :: sizeHint() const 
 { 
     return calculateSize(MinimumSize); 
 } QSize BorderLayout :: minimumSize() const 
 { 
     return calculateSize(SizeHint); 
 }//将各item的大小进行统计获得layout的大小
QSize BorderLayout::calculateSize(SizeType sizeType) const 
 { 
     QSize totalSize;     for(int i = 0 ; i < list.size(); i++){ 
         ItemWrapper * wrapper = list.at(i); 
         Position position = wrapper -> position; 
         QSize itemSize;         if(sizeType == MinimumSize) 
             itemSize = wrapper->item->minimumSize(); 
         else // SizeHint         if(position == North || position == South || position == Center)
             totalSize.rheight() += itemSize.height();         if(position == West || position == East || position == Center)
             totalSize.rwidth() += itemSize.width(); 
     } 
     return totalSize; 
 }

五、setGeometry()是最重要的一个,用于描述这个布局,我们重造该方法:

void BorderLayout::setGeometry(const QRect &rect)
 { 
      QLayout::setGeometry(rect); 
      /* Add you code here*/ 
}

在这里,我们向描述一下我们Layout的情况。

在这个图的基础上我们来布局。我们从QList中读出我们的组件,根据组件的位置属性,我们先完成南北向的组件布局,在完成东西向的组件布局,布局使用QRect来确定每个组件的大小,每个组件都有一个缺省的大小item->sizeHint().width()和item->sizeHint().height(),对于南北,宽度等同于整个layout,而东西,高度修正为同center的大小。明确了正阳的布局,我们编写下面的代码:

void BorderLayout::setGeometry(const QRect &rect)
 { 
     ItemWrapper * center = NULL; 
     int eastWidth = 0,westWidth = 0,northHeight = 0 ,southHeight = 0, centerHeight = 0;    for(int i = 0; i < list.size(); i ++){ 
         ItemWrapper * wrapper = list.at(i); 
         QLayoutItem * item = wrapper -> item; 
         Position position = wrapper->position; 
         if(position == North){ 
             item->setGeometry(QRect(rect.x(),northHeight, rect.width(),item->sizeHint().height()));
             northHeight += item->sizeHint().height() +spacing(); 
         }else if(position == South){ 
             item->setGeometry(QRect(rect.x(), rect.height()-southHeight-item->sizeHint().height(),rect.width(), item->sizeHint().height()));
             southHeight += item->sizeHint().height() + spacing(); 
         }else if(position == Center){ 
             center = wrapper; 
         } 
     }     centerHeight = rect.height()- northHeight-southHeight;
     for(int i = 0; i < list.size(); i ++){ 
         ItemWrapper * wrapper = list.at(i); 
         QLayoutItem * item = wrapper -> item; 
         Position position = wrapper->position; 
         if(position == West){ 
             item->setGeometry(QRect(rect.x() + westWidth,northHeight,item->sizeHint().width(),centerHeight));
              westWidth += item->sizeHint().width()+spacing(); 
         }else if(position == East){ 
              item->setGeometry(QRect(rect.width()-eastWidth-item->sizeHint().width(),northHeight,
                                               item->sizeHint().width(),centerHeight));
              eastWidth += item->sizeHint().width() + spacing(); 
         } 
     } 
     if(center) 
         center->item->setGeometry(QRect(westWidth,northHeight, rect.width()-eastWidth-westWidth,centerHeight));
 }


举报

相关推荐

0 条评论