6.1 控件布局技术
所谓GUI界面,归根结底,就是一堆可视化控件的叠加。创建一个窗口,把按钮放上面,把图标放上面,这样就成了一个界面。在放置时,控件的位置尤为重要。我们必须指定控件放在哪里,以便窗口能够按照我们需要的方式进行渲染。这就设计控件定位的机制,Qt提供了两种控件定位机制:绝对定位和布局定位。
绝对定位是一种最原始的定位方法:给出这个控件的坐标和长宽值。这样,Qt就知道该把控件放在哪里以及如何设置控件的大小。这样就带来一个问题是,如果用户改变了窗口大小,例如单击最大化按钮或者使用鼠标拖动窗口边缘,采用绝对定位的空间是不会有任何影响的。这也很自然,因为你并没有告诉Qt,在窗口变化时,窗体控件是否要更新自己以及如何更新。如果希望控件自动更新(就如同微软公司的Word在最大化时总会把稿纸区变大,把工具栏拉长),就要自己编写相应的函数来响应窗户口变化。当然还有更简单地方法:禁止用户改变窗口大小。
针对这种控件自适应窗口变化的需求,Qt提供了另外一种“布局机制”来解决这个问题。只要把控件放入某一种布局,布局由专门的布局管理器管理。当需要调整空间大小或者位置时,Qt使用相应的布局管理期自动进行管理。
Qt通过一些类实现布局管理,包括QHBoxLayout、QVBoxLayout、QGridLayout和QFormLayout。这些类型继承自QLayout,但QLayout并非继承自QWidget,而是直接派生于QObject。他们负责窗体中控件的布局管理。上述4个类的作用如下:
1 QHBoxLayout,配置widget控件成横向一行
2 QVBoxLayout,配置widget控件成垂直一列
3 QGridLayout,配置widget控件按照平面网格排列。
4 QFormLayout,配置widget控件用于表单布局。
设计界面时,一个布局类的使用过程一般如下
(1)创建各个控件
(2)定义一个布局类对象
(3)将控件加入布局类对象
(4)在某个窗体上设置该布局
6.1.1 水平布局
QHBoxLayout用于水平布局
【例6-1】QHBoxLayout的使用
第1步,建立一个基于QWidget类的应用程序。创建时取消UI界面文件
第2步,修改main.cpp
#include "widget.h"
#include <QApplication>
#include<QSpinBox>
#include<QSlider>
#include<QLayout>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
//添加两个空间
QSpinBox *spinBox=new QSpinBox(&w);//创建数字按钮
QSlider*slider=new QSlider(Qt::Horizontal,&w);//创建滑动条
spinBox->setValue(35);
slider->setValue(50);
//创建布局对象,将空间加入其中
QHBoxLayout *layout=new QHBoxLayout;
layout->addWidget(spinBox);
layout->addWidget(slider);
w.setLayout(layout);//窗体设置layout
w.show();
return a.exec();
return a.exec();
}
编译运行,得到下图所示的效果。可以看出,SpinBox和Slider两个空间是水平排列的。当窗体拉大时,Slider控件被拉长。控件的高度没有变,SpinBox的宽度也没有变。这是Qt智能拉伸控件的结果,系统认为滑动条应优先拉伸。
如果界面中只有一个SpinBox,那么当窗口拉大时,SpinBox也会横向拉长。同理,如果将例6-1中的SpinBox换成Button,当窗体拉大时仍然只有Slider被拉宽。但是当水平布局中有若干个按钮时,当窗体拉大,每个按钮都会同时被拉长。
6.1.2 垂直布局
QVBoxLayout用于水平布局
【例6-2】QVBoxLayout的使用
第一步,建立一个基于QWidget类的应用程序。创建时取消UI界面文件
第二步,修改main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc,argv);
Widget w;
//创建两个控件
QLineEdit *LEdit=new QLineEdit("a line text",&w);
QTextEdit*REdit=new QTextEdit(&w);
//将控件放入layout对象
QVBoxLayout *layout=new QVBoxLayout;
layout->addWidget(LEdit);
layout->addWidget(REdit);
w.setLayout(layout);
w.show();
return a.exec();
}
从上图可以看出,当窗体被拉大后,多行文本框被智能纵向拉长,并且两个编辑框都很像扩展填满了窗体。
6.1.3 网格布局
QGridLayout用于实现网格布局。网格是m行n列的样式,但是在通常的情况下,每个格子的尺寸是不同的,每个格子的大小受空间自身大小的影响,还可以人为设定行与行点的高度比或者列与列的宽度比。
【例6-3】QGridLayout的基本用法实例
第一步,建立一个基于QDialog的程序,取消UI文件的创建。
第二步,修改main.cpp:
#include "dialog.h"
#include <QApplication>
#include<QGridLayout>
#include<QLabel>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QString texts[]={"1","2","3","4","5","6","7","8","9"};
QWidget*window=new QWidget;
window->setWindowTitle("QGridLayout");
window->resize(250,100);
QGridLayout*gridLayout=new QGridLayout;
gridLayout->setSpacing(2);//设置单元间隔
gridLayout->setMargin(2);//设置队伍
for(int i=0,k=0;i<3;i++,k+=3)
{
for(int j=0;j<3;j++)
{
QLabel *label=new QLabel(texts[k+j]);
//设定label的显示方式,使其显示的更清楚
label->setFrameStyle(QFrame::Panel+QFrame::Sunken);
if(i<2)
label->setMinimumSize(55,0);
else
label->setMinimumSize(55,50);
label->setAlignment(Qt::AlignCenter);
gridLayout->addWidget(label,i,j);//添加控件到网格
}
}
//列宽比,第0列与第1列宽度只比为1:2
gridLayout->setColumnStretch(0,1);
gridLayout->setColumnStretch(1,2);
window->setLayout(gridLayout);
window->show();
return app.exec();
return app.exec();
}
编译运行如上图所示,由图可以看出,第2行的高度比较大,因为在程序中设置设置第2行的高度不小于50的结果,使用了如下语句label->setMinimumSize(50,50);当窗体拉大以后,由于每一行的高度都大于50,因此所有行的高度就均匀分布了
但是第0、1两列的宽度比一直是1:2,原因在于设定了宽度比例,语句为
gridLayout->setColumnStretch(0,1);//设定第0列为1
gridLayout->setColumnStretch(1,2);//设定第1列为2
类似的,还可以设定行与行的高度比,可使用下列函数
void QGridLayout::setRowStretch(int row,int stretch),该函数设定第row行的因子为stretch
网格布局中添加的控件在垂直或水平方向上可占据多个单元格,可使用下列函数添加控件:
void addWidget(QWidget*widget,int fromRow,int fromColumn,int rowSpan,int columSpan,Qt::Alignment alignment=0)
该函数将控件widget的左上角放在(fromRow,fromColumn),纵向占据rowSpan个单元,横向跨越columnSpan个单元,rowSpan和columSpan参数智能同时出现或同时忽略,若没有这两个参数,占据一个单元格。
【例6-4】用网格布局构造温度转换程序界面
假定要实现上图所示的界面(其中有7个空间。为了使网格布局实现这一界面,可以将界面划分成4行3列的网格,显然,第0行按钮占据1行3列。第1行左边的标签占据2列,右边的标签占一个单元,下面的摄氏温度标签以及调整温度的滑竿各站2行一列。液晶数字和转盘各占一个单元。于是可以按如下不走编写程序
第一步,建立一个基于QDialog的程序,取消UI文件的创建
第二步,修改dialog.cpp:
#include "dialog.h"
#include<QGridLayout>
#include<QLabel>
#include<QPushButton>
#include<QSlider>
#include<QLCDNumber>
#include<QDial>
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
QPushButton *m_QuitButton=new QPushButton("Quit",this);
QLabel*m_CenLabel=new QLabel("Centigrade",this);
QLabel*m_FahLabel=new QLabel("Fahrenheit",this);
// m_FahLabel->setAlignment(Qt::Alignment);
QLabel*m_Label=new QLabel("0",this);
QSlider*m_Slider=new QSlider(this);
QLCDNumber*m_LCDNumber=new QLCDNumber(this);
QDial*m_Dial=new QDial(this);
QGridLayout*layout=new QGridLayout(this);
layout->setSpacing(10);
layout->setMargin(20);
//Quit按钮,起始于(0,0),横跨3个单元格,即colSpan=3
layout->addWidget(m_QuitButton,0,0,1,3);
//Centigrade标签,起始于(1,0),横跨2个单元格,即colSpan=2
layout->addWidget(m_CenLabel,1,0,1,2);
//Fahrenheit标签,始于(1,2),占一个单元,不设rowSpan和colSpan
layout->addWidget(m_FahLabel,1,2);
//"0"标签,起始于(2,0),纵跨2个单元格,rowSpan=2
layout->addWidget(m_Label,2,0,2,1);
//滑竿,起始于(2,1),纵跨2个单元格,rowSpan=2
layout->addWidget(m_Slider,2,1,2,1);
//液晶数字,起始于(3,2),占用一个单元格
layout->addWidget(m_LCDNumber,2,2);
//转盘,起始于(3,2),占用一个单元格
layout->addWidget(m_Dial,3,2);
this->setLayout(layout);
}
Dialog::~Dialog()
{
}
6.1.4 表单布局
QFormLayout称为表单布局。这里的表单由两个列组成:第一格列用于显示信息,给与用户同时,一般称为label域;第二个是需要用户输入的,一般称为field域。表单就是由很多label域-field域两项(两列)内容组成的行的布局。label与field是关联的。
表单布局完全可以使用网格布局实现,是一种多行多列的表格,但表单布局提供了比较完善的策略,其主要有如下优点
(1)自适应不同操作系统,有不同的外观
例如在MacOS X的Aqua截面和Linux的KDE界面中,这种两列结构中的标签应该是右对齐的,而在windows和GNOME界面中这种标签通常是左对齐的。
(2)支持自动换行
如果输入的filed域设定的比较长,则filed域换一行显示,还可以设定filed域总是换一行显示,当然默认的情形时一行显示两个域。
(3)函数接口简单,label和field空间可以直接一对对的插入,使用下面的函数可以在表单布局中一次插入两个控件,即标签和文本框:void addRow(QWidget*label,QWidget*field)
addRow函数在插入一个label和field后,自动将两者设置为伙伴(buddy)关系。而伙伴关系的好处就是,加入label的快捷键是ALT+W,按下快捷键是,输入焦点自动跳到label的伙伴上(即filed上),下面的语句将创建下面的界面
QLineEdit*name=new QLineEdit;
QLineEdit*email=new QLineEdit;
QLineEdit*address=new QLineEdit;
下面的函数将自动创建一个文字为labelText的标签,再将标签、文本框一起插入到表单布局中:
void addRow(const QString &labelText,QWidget*field)
QFormLayout*formLayout=new QFormLayout;
formLayout->addRow(tr("&Name"),name);
formLayout->addRow(tr("&Email"),email);
formLayout->addRow(tr("&Address"),address);
setLayout(formLayout);
这里的"&Name:"中的&符号指明了快捷方式,表明按下ALT+N键就跳转到Name后的编辑框
如果在上述FormLayout的实现代码中添加下列语句:
formLayout->setLabelAlignment(Qt::AlignRight);
则标签文字右对齐。如果在上述FormLayout的实现代码中添加如下语句:
formLayout->setRowWrapPolicy(QFormLayout::WrapAllRows);
如果使用QGridLayout,为了实现上述的截面,需要以下代码:
QLineEdit*name=new QLineEdit;
QLineEdit*emial=new QLineEdit;
QLineEdit*address=new QLineEdit;
QLabel*nameLabel=new QLabel(tr("&Name:"));
nameLabel->setBuddy(name);
QLabel*emailLabel=new QLabel(tr("&email:"));
emailLabel->setBuddy(emial);
QLabel*addLabel=new QLabel(tr("&Adress:"));
addLabel->setBuddy(address);
QGridLayout *gridLayout=new QGridLayout=new QGridLayout;
gridLayout->addWidget(nameLabel,0,1);
gridLayout->addWidget(name,0,1);
gridLayout->addWidget(emailLabel,1,0);
gridLayout->addWidget(name,1,1);
gridLayout->addWidget(emailLabel,2,0);
gridLayout->addWidget(name,2,1);
setLayout(gridLayout);
6.1.5 综合布局实例
在做界面设计的时候,就是利用上面几种布局管理器对部件进行组合和排列,同时水平布局、垂直布局和网格布局可以互相嵌套,从而构造比较复杂的截面。
【例6-5】用水平布局和垂直布局构造温度转换程序界面
这里用水平布局和垂直布局相互嵌套的方式实现例6-4中的温度转换器的界面。对于水平布局和垂直布局,都可以利用下列函数将一个将一个布局添加到另一个布局中:
void QBoxLayout::addLayout(QLayout *layout,int stetch=0)
这里参数layout就是被加入的布局。对于网格布局也有类似函数,本例没有使用网格布局,香炉奥杰具体细节可以参照Qt文档
考察上图所示截面,共有七个部件(1个PushButton,3个Label,1个Slider,1个LCDNumber和一个Dial),可用下面的方法构建程序界面布局。
首先,将界面拆分成4个部分,如右图所示
第一部分是第一行,只有一个PushButton,记作区域1
第二部分是第二行,是两个水平排列的Label,可以用水平布局管理器将其放在一起,记作区域2.
第三部分是下方深色区域水平排列的Label和Slider,用于显示和调整温度,也可用水平布局管理器将其放在一起,记作区域3.
第四部分是垂直排列的LCDNumber和Dial,可以使用垂直布局管理器将其放在一起,记作区域4.
此后,整个窗体的布局变成自上到下三部分:区域1、区域2、区域A,而且这3个部分是垂直排列的,所以可再用垂直布局管理器江浙3个大部件再次组合。
至此,利用水平和垂直布局管理器不断嵌套组合的方法,就顺利完成了整个应用程序界面的布局。
程序实现步骤如下:
第一步,建立一个基于QDialog的程序,取消UI文件的创建
第二步,修改dialog.cpp
#include "dialog.h"
#include<QHBoxLayout>
#include<QVBoxLayout>
#include<QLabel>
#include<QPushButton>
#include<QSlider>
#include<QLCDNumber>
#include<QDial>
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
QPushButton *m_QuitButton=new QPushButton("Quit",this);
QLabel*m_CenLabel=new QLabel("Centigrade",this);
QLabel*m_FahLabel=new QLabel("Fahrenheit",this);
m_FahLabel->setAlignment(Qt::AlignCenter);
QLabel*m_Label=new QLabel("0",this);
QSlider*m_Slider=new QSlider(this);
QLCDNumber*m_LCDNumber=new QLCDNumber(this);
QDial *m_Dial=new QDial(this);
//将两个Label放到水平布局管理器(区域2中)
QHBoxLayout *layout2=new QHBoxLayout;
layout2->addWidget(m_CenLabel);
layout2->addWidget(m_FahLabel);
//将Label和QSlider放到水平布局管理器(区域3)
QHBoxLayout *layout3=new QHBoxLayout;
layout3->addWidget(m_Label);
layout3->addWidget(m_Slider);
//将LCDNumber和Dial放到垂直布局管理器(区域4)
QVBoxLayout *layout4=new QVBoxLayout;
layout4->addWidget(m_LCDNumber);
layout4->addWidget(m_Dial);
//将区域3和区域4放到水平布局管理器(区域A)
QHBoxLayout *layoutA=new QHBoxLayout;
layoutA->addLayout(layout3);
layoutA->addLayout(layout4);
//将区域1,2,A放到主布局管理器
QVBoxLayout*layout=new QVBoxLayout;
layout->addWidget(m_QuitButton);
layout->addLayout(layout2);
layout->addLayout(layoutA);
layout->setSpacing(10);
layout->setMargin(20);
this->setLayout(layout);
}
Dialog::~Dialog()
{
}
编译运行,程序界面如图所示,可以发现与前面的图很相似,但是任然稍有差别。从前面的讲解中可以知道,利用水平和垂直布局管理器也可以得到比较复杂的截面。但是当界面比较复杂是,需要利用比较多的布局其才能达到最终的效果。布局管理器相互嵌套,如同递归函数一样,作者增加了复杂性。
与使用水平布局管理器和垂直布局管理器的组合方式相比,使用网格布局管理器只需要消耗一个布局管理器即可完成整个界面的布局。但是这种那个方式的最大缺点是需要实现精确设计好每个部件的位置和占用尺寸,再不见数量比较大的情况下,仅仅网格布局管理器也显得力不重心了。
所以在做界面布局的时候,可以使用网格布局管理器做整体框架设计,然后在其中填充一些水平或垂直布局管理器或者他们的组合,以达到更好的效果。
6.2 窗口的切分与停靠
6.2.1 使用QSpliter实现分割窗口
分割窗口在应用程序中经常用到,他可以灵活设计窗口布局,经常用语类似文件资源管理器的窗口设计中。QSpliter控件就是一个可以包含一些其他窗口部件的部件。在QSpliter中的这些窗口部件会通过切分条(Spliter handle)分隔开。用户可以通过拖动这些分隔条改变QSpliter控件子窗口的大小。QSpliter中的子窗口部件将会自动按照创建时的书顺序一个一个的放在一起,并且以窗口分隔条来分割相邻的窗口。
【例6-6】QSpliter使用示例之一
第一步,建立一个基于QMainWindow的程序,取消UI文件的创建
第2步,修改mainwindow.cpp中包含的头文件和构造函数
#include "mainwindow.h"
#include<QSplitter>
#include<QTextEdit>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
//创建主分割窗口,设置为水平分割窗口(左右分割)
QSplitter *mainSplitter=new QSplitter(Qt::Horizontal);
//创建一个QTextEdit空间,设置其父控件为mainSplitter
QTextEdit*leftEdit=new QTextEdit(QObject::tr("左窗口"),mainSplitter);
//设置QTextEdit中文字的对其方式为居中显示
leftEdit->setAlignment(Qt::AlignCenter);
//创建右侧垂直分割(上下分割),设置其父控件为mainSpliter
QSplitter*rightSplitter=new QSplitter(Qt::Vertical,mainSplitter);
//设置拖动分隔条时,只显示灰线。拖动到位后在显示分隔条
rightSplitter->setOpaqueResize(false);
//设置右侧分割的上下两个窗口内容
QTextEdit*upEdit=new QTextEdit(QObject::tr("上窗口"),rightSplitter);
upEdit->setAlignment(Qt::AlignCenter);
QTextEdit*bottomEdit=new QTextEdit(QObject::tr("下窗口"),rightSplitter);
bottomEdit->setAlignment(Qt::AlignCenter);
//设置右窗口为可伸缩控件
mainSplitter->setStretchFactor(1,1);
mainSplitter->setWindowTitle(QObject::tr("分割窗口"));
//将主分割设为中央部件
setCentralWidget(mainSplitter);
mainSplitter->show();
}
MainWindow::~MainWindow()
{
}
分隔条可以拖动
在上个例子中,使用了QSplitter构造函数,原型如下:
QSplitter::QSplitter(Qt::Orientation orientation,QWidget*parent=0);
第一个参数通过Qt::Horizontal和Qt::Vertical来设定为水平分割和垂直分割。第二个参数是父窗口指针,0代表无父窗口。因此语句
QSplitter*rightSplitter=new QSplitter(Qt::Vertical,mainSplitter);
就设定了rightSplitter的父组件为mainSplitter,即右侧分割包含在主分割内部。
函数setOpaqueResize的父组件为mainSplitter。即右侧分割包含在主分割内部。
函数setOpaqueResize设置拖动时是否实时更新:
rightSplitter->setOpaqueResize(false);
就是在设定拖动分割时只显示一条灰色的线条,当把其中的参数改为true时,则分割将实时更新。
函数setStretchFactor设置可伸缩控件,下面是程序中该语句的用法:
mainSplitter->setStretchFactor(1,1);
其中第一个参数指定控件序号,该空间序号将插入的先后顺序从0开始变好,第二个参数大于0表示此控件为伸缩空间。此例中设置右侧窗口为伸缩空间。当把窗口向右拉伸后,左边的宽度不变
QSplitter每次向一个分割区添加一个控件,因此如果有多个控件布置在某个分区,可以将它们先放到一个Widget窗体中,然后将次窗体放入分区。
【例6-7】QSplitter使用示例之二
第一步,建立一个基于QDialog的程序,取消UI文件的创建
第二步,修改main.cpp
#include "dialog.h"
#include <QApplication>
#include<QSplitter>
#include<QTextEdit>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//创建3个编辑框
QTextEdit*editor1=new QTextEdit;
QTextEdit*editor2=new QTextEdit;
QTextEdit*editor3=new QTextEdit;
QSplitter splitter(Qt::Horizontal);//定义一个切分窗口
splitter.addWidget(editor1);//将文本空间加入到切分狂
splitter.addWidget(editor2);
splitter.addWidget(editor3);
editor1->setPlainText("One\nTwo\nThree");
editor2->setPlainText("1\n2\n3");
editor3->setPlainText("A\nB\nC");
splitter.setWindowTitle(QObject::tr("Splitter"));
splitter.show();
return a.exec();
return a.exec();
}
这个例子说明,QSplitter控件并不一定是将主窗口一分为二,而是根据加入的子窗口的数量自动分割,这里加入了3个空间,于是就产生了3个子窗口。
6.2.2 可停靠窗口QDockWidget
在许多程序中,有些窗口可以被拖动到另一个长体重,并合为一体,可以停留在主窗口的上下或左右两侧,还可以浮动在主窗口至上
【例6-8】停靠窗口示例
第一步,建立一个基于QMainWindow的应用,取消UI设计设计界面度选矿的选中状态
第二步,在源文件mainwindow.cpp中编写如下:
class MainWindow:public QMainWindow
{
Q_OBJECT
//添加组件、函数的定义
QTextEdit *textEdit;
QMenu*viewMenu;
QToolBar*viewToolBar;
public:
MainWindow(QWidget*parent=0);
~MainWindow();
}
第3步,修改mainwindow.cpp文件中的构造函数
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
//创建一个QTextEditor控件,作为主窗口
textEdit=new QTextEdit;
this->setCentralWidget(textEdit);
viewMenu=menuBar()->addMenu(tr("视图"));//添加菜单
viewToolBar=this->addToolBar(tr(" "));//添加工具条
createDockwidget();//创建停靠窗体
this->setWindowTitle(tr("停靠窗口"));
}
第4步,修改mainwindow.cpp文件,添加createDockWidget函数的实现:
#include "mainwindow.h"
#include<QTextEdit>
#include<QMenuBar>
#include<QToolBar>
#include<QDockWidget>//包含停靠窗口
#include<QCalendarWidget>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
//创建一个QTextEditor控件,作为主窗口
textEdit=new QTextEdit;
this->setCentralWidget(textEdit);
viewMenu=menuBar()->addMenu(tr("视图"));//添加菜单
viewToolBar=this->addToolBar(tr(" "));//添加工具条
createDockwidget();//创建停靠窗体
this->setWindowTitle(tr("停靠窗口"));
}
void MainWindow::createDockwidget()
{
//设置主窗体的第一个QDockWidget
QDockWidget*dock=new QDockWidget(this);
//设置dock窗口的名称
dock->setWindowTitle(tr("日期"));
//设置dock的可停靠区域,全部可停靠
dock->setAllowedAreas(Qt::AllDockWidgetAreas);
//设置dock内的控件
QCalendarWidget*calendar=new QCalendarWidget;
//将日历控件设置为dock的主空间
dock->setWidget(calendar);
//向主窗体中添加dock的第一个参数,表示初始化
//第二个参数是要添加的QDock控件
this->addDockWidget(Qt::RightDockWidgetArea,dock);
//向菜单和工具栏中添加显示和隐藏dock窗口的动作
viewMenu->addAction(dock->toggleViewAction());
viewToolBar->addAction(dock->toggleViewAction());
}
编译运行如上,这里停靠窗口可以移动到左右上下各个位置,并且停靠窗口还可以脱离主窗口(处于浮动状态)
停靠窗口QDockWidget的函数setAllowedAreas用于设定窗体的停靠区域,使得参数可以取以下的值
Qt::LeftDockWidgetArea,停靠在左侧
Qt::RightDockWidgetArea,停靠在右侧
Qt::TopDockWidgetArea,停靠在顶部
Qt::BottonDockWidgetArea,停靠在底部
Qt::AllDockWidgetAreas,可停靠在任何位置
6.3 多文档界面应用程序
使用Qt编写多文档界面(MDI)的应用主要会用到QMdiArea和QMidSubWindow两个类。
1 QMdiArea
这个类相当于一个MDI窗口管理器,用来管理添加到这个区域中的多个子窗口。在应用中新建的所有子窗口需要通过addSubWindow的中央部件,但是,也可以将它添加到任意的不居中。下面的代码就是将其添加到中央部件:
QMainWindow *mainWindow=new QMianWindow;
mainWindow->SetCentralWidget(mdiArea);
2 QMdiSubWindow
这个类继承自QWidget,主要用来创建MDI子窗体实例。然后,可以通过调用QMdiArea的addSubWindow方法将新建的子窗体实例添加到多文档界面区域。也可以不用QMdiSubWindow类来创建子窗体,而直接使用继承自QWidget的类,例如下面的代码
void MainWindow::actNewWindow()
{
QLabel*label=new QLabel;
m_mdiArea->addSubWindow(label);
}
不过,如果使用QMdiSubWindow类,就可以使用其提供的一些便捷的成员函数,下面给出一段实例代码
void MainWindow::actNewWindow()
{
QLabel*label=new QLabel;
QMdiSubWindow*subWin=new QMdiSubWindow;
subWin->setWidget(label);
subWin->setAttribute(Qt::WA_DeleteOnClose);
m_mdiArea->addSubWindow(suWin);
subWin->show();
}
这里设置的Qt::WA_DeleletOnClose的作用是,当关闭子窗口时,不是隐藏窗口,而是测地关闭窗口,回收其占用的资源。
【例6-9】多文档应用示例
第一步,使用Qt向导创建一个QMainWindow应用,并使用UI设计界面
第二步,在UI设计界面中添加文件菜单,并在其下添加一个New菜单栏,用来每次创建一个子窗体,并显示MDI区域
第三步,在mainwindow.h中添加头文件#include<QMdiArea>
同时在mainwindow.h中添加下面的变量
QMdiArea *m_mdiArea;
再添加槽函数的定义void actNewWindow();
第四步,修改mainwindow.cpp中的构造函数
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//创建MDI区域
this->setCentralWidget(m_mdiArea);
connect(ui->actionNew,SIGNAL(triggered()),this,SLOT(actNewWindow()));
}
第5步,在mainwindow.cpp中添加新建子窗体的槽函数
voidMainWindow:: actNewWindow()
{
//这里没格子窗体都是一个QLabel部件
QLabel *label=new QLabel(tr("MDI SubWindow"));
QMdiSubWindow*subWin=new QMdiSubWindow();
subWin->setWidget(label);
subWin->setAttribute(Qt::WA_DeleteOnClose);
subWin->resize(180,100);
m_mdiArea->addSubWindow(subWin);
subWin->show();
}