此片文章简单介绍布局管理的使用方法。通过实例先分别介绍分隔窗口QSplitter类、停靠窗口QDockWidget类及QStackedWidget类的使用,最后再通过一个实例介绍QLayout的使用。

分割窗口QSplitter

分隔窗口可以灵活地布局窗口,可以用在文件资源管理器地窗口设计中。下图显示一个简单的分割窗口功能,界面窗口由3个子窗口组成,各个窗口之间可随意拖拽改变大小。
在这里插入图片描述
上图为spliter工程运行效果,相关代码如下:

int main(int argc, char* argv[])
{
    QApplication a(argc, argv);
    QFont font("ZYSong18030", 12);                               //指定显示字体
    a.setFont(font);
    //主分割窗口
    QSplitter* splitterMain = new QSplitter(Qt::Horizontal, 0);
    QTextEdit* textLeft = new QTextEdit(QObject::tr("Left Widget"), splitterMain);
    textLeft->setAlignment(Qt::AlignCenter);
    //右部分分割窗口
    QSplitter* splitterRight = new QSplitter(Qt::Vertical, splitterMain);
    splitterRight->setOpaqueResize(false);
    QTextEdit* textUp = new QTextEdit(QObject::tr("Top Widget"), splitterRight);
    textUp->setAlignment(Qt::AlignCenter);
    QTextEdit* textBottom = new QTextEdit(QObject::tr("Bottom Widget"), splitterRight);
    textBottom->setAlignment(Qt::AlignCenter);
    splitterMain->setStretchFactor(1, 1);
    splitterMain->setWindowTitle(QObject::tr("Splitter"));
    splitterMain->show();

    //spliter w;
    //w.show();
    return a.exec();
}

关键代码说明如下:

  • QSplitter* splitterMain = new QSplitter(Qt::Horizontal, 0):创建QSplitter对象,并设置水平分隔窗口。
  • QTextEdit* textLeft = new QTextEdit(QObject::tr(“Left Widget”), splitterMain):新建QTextEdit对象并插添加到主分割窗口中。
  • QSplitter* splitterRight = new QSplitter(Qt::Vertical, splitterMain):新建一个QSplitter对象设置为垂直分割并添加到父窗口。
  • splitterRight->setOpaqueResize(false):设定分割窗口的分割线在拖拽时是否为实时更新显示,true则实时更新显示,false则在拖拽时只显示一条灰色的粗线条。
  • splitterMain->setStretchFactor(1, 1):此方法用于设定可伸缩控件,第一个参数指定设置的控件序号,空间序号按插入顺序从0开始编号;第二个参数大于0表示此控件为可伸缩控件。此实例中设定右边的分割窗口为可伸缩控件,当整个对话框的宽度发生变化,左边的文本编辑框宽度保持不变,右边的分割窗口宽度随调整个对话框大小的改变进行调整。

停靠窗口QDockWidget

以下是设置QDockWidget停靠窗口的一般流程:

  1. 创建QDockWidget对象的停靠窗口。
  2. 设置此停靠窗口的属性,通常调用setFeaturessetAllowedAreas两个方法。
  3. 新建一个要插入停靠窗口的控件。
  4. 将控件插入停靠窗口,调用setWidget方法。
  5. 使用addDockWidget方法在QMainWindow中加入此停靠窗体。

DockWindows工程运行的QDockWidget停靠窗口如下图所示:窗口1只可在主窗口的左边和右边停靠;窗口2只可在浮动窗口和右部停靠两种状态间切换,并且不可移动;窗口3可实现停靠窗口的各种状态。
在这里插入图片描述
界面布局核心代码如下:

DockWindows::DockWindows(QWidget *parent)
    : QMainWindow(parent)
{
    setWindowTitle(tr("DockWindows"));	//设置主窗口的标题栏文字
    QTextEdit* te = new QTextEdit(this);	//定义一个QTextEdit对象作为主窗口
    te->setText(tr("Main Window"));
    te->setAlignment(Qt::AlignCenter);
    setCentralWidget(te);          		//将此编辑框设为主窗口的中央窗体
    //停靠窗口1
    QDockWidget* dock = new QDockWidget(tr("DockWindow1"), this);
    //可移动
    dock->setFeatures(QDockWidget::DockWidgetMovable);
    dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
    QTextEdit* te1 = new QTextEdit();
    te1->setText(tr("Window1,The dock widget can be moved between docks by the user" ""));
    dock->setWidget(te1);
    addDockWidget(Qt::RightDockWidgetArea, dock);
    //停靠窗口2
    dock = new QDockWidget(tr("DockWindow2"), this);
    dock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetFloatable); //可关闭、可浮动
    QTextEdit* te2 = new QTextEdit();
    te2->setText(tr("Window2,The dock widget can be detached from the main window,""and floated as an independent window, and can be closed"));
    dock->setWidget(te2);
    addDockWidget(Qt::RightDockWidgetArea, dock);
    //停靠窗口3
    dock = new QDockWidget(tr("DockWindow3"), this);
    dock->setFeatures(QDockWidget::AllDockWidgetFeatures);   //全部特性
    QTextEdit* te3 = new QTextEdit();
    te3->setText(tr("Window3,The dock widget can be closed, moved, and floated"));
    dock->setWidget(te3);
    addDockWidget(Qt::RightDockWidgetArea, dock);
}

其中有两个方法需要我们关注一下:

  • setFeatures:此方法用于设置停靠窗口的特性,函数声明如下:
    void setFeatures(QDockWidget::DockWidgetFeatures features)
    参数QDockWidget::DockWidgetFeatures指定停靠窗体的特性,包括以下几种参数。
    • QDockWidget::DockWidgetClosable:停靠窗口可关闭。
    • QDockWidget::DockWidgetMovable:停靠窗体可移动。
    • QDockWidget::DockWidgetFloatable:停靠窗体可浮动。
    • QDockWidget::DockWidgetVerticalTitleBar:dock小部件在左侧显示一个垂直的标题栏。这可以用来增加QMainWindow中的垂直空间。
    • QDockWidget::NoDockWidgetFeatures:不可移动、不可关闭、不可浮动。
  • setAllowedAreas:从方法用于设置停靠窗体可停靠的区域,函数声明如下:
    void setAllowedAreas(Qt::DockWidgetAreas areas)
    参数Qt::DockWidgetAreas指定停靠窗口可停靠区域,包括以下几种参数。
    • Qt::LeftDockWidgetArea:可在主窗口的左侧停靠。
    • Qt::RightDockWidgetArea:可在主窗口的右侧停靠。
    • Qt::TopDockWidgetArea:可在主窗口的顶部停靠。
    • Qt::BottomDockWidgetArea:可在主窗口的底部停靠。
    • Qt::AllDockWidgetAreas:可在主窗口的(以上四个)部位停靠。
    • Qt::NoDockWidgetArea:只可停靠在插入处。

堆栈窗体QStackedWidget

堆栈窗体QStackedWidget在实际应用中,经常与列表框QListWidget及下拉列表框QComboBox配合使用。
堆栈窗体QStackedWidget类的使用,当选择左侧列表框中不同的选项时,右侧显示所选的不同的窗体。效果图如下所示。
在这里插入图片描述
StackedWidget项目关键代码如下:

StackedWidget::StackedWidget(QWidget *parent)
    : QDialog(parent)
{
    setWindowTitle(tr("StackedWidget"));
    list = new QListWidget(this);	//新建一个QListWidget控件对象
    //在新建的QListWidget控件中插入三个条目,作为选择项
    list->insertItem(0, tr("Window1"));
    list->insertItem(1, tr("Window2"));
    list->insertItem(2, tr("Window3"));
    //创建三个QLabel标签控件对象,作为堆栈窗口需要显示的三层窗体
    label1 = new QLabel(tr("WindowTest1"));
    label2 = new QLabel(tr("WindowTest2"));
    label3 = new QLabel(tr("WindowTest3"));
    stack = new QStackedWidget(this);
    //新建一个QStackedWidget堆栈窗体对象
    //将创建的三个QLabel标签控件依次插入堆栈窗体中
    stack->addWidget(label1);
    stack->addWidget(label2);
    stack->addWidget(label3);
    QHBoxLayout* mainLayout = new QHBoxLayout(this);
    //对整个对话框进行布局
    mainLayout->setMargin(5);		//设定对话框(或窗体)的边距为5
    mainLayout->setSpacing(5);		//设定各个控件之间的间距为5
    mainLayout->addWidget(list);
    mainLayout->addWidget(stack, 0, Qt::AlignHCenter);
    mainLayout->setStretchFactor(list, 1);
    mainLayout->setStretchFactor(stack, 3);
    connect(list, SIGNAL(currentRowChanged(int)), stack, SLOT(setCurrentIndex(int)));
}

以下两个方法需要我们关注:

  1. mainLayout->setStretchFactor(list, 1):设定可伸缩控件,第一个参数用于指定设置的控件(序号从0开始),第二个参数的值大于0则表示此控件为可伸缩控件。
  2. connect(list, SIGNAL(currentRowChanged(int)), stack, SLOT(setCurrentIndex(int))):将QListWidget的currentRowChanged信号与堆栈窗体的setCurrentIndex槽函数连接起来,实现按选择显示窗体。此处的堆栈窗体index按插入的顺序从0起依次排序,与QListWidget的条目排序相一致。

基本布局QLayout

Qt提供了QHBoxLayout(水平排列布局)、QVBoxLayout(垂直排列布局)以及QGridLayout(网格排列布局)等布局管理类。各种布局类的派生关系如下:

QLayout
QGridLayout
QBoxLayout
QHBoxLayout
QVBoxLayout

在代码布局中常用的方法有addWidgetaddLayout两个方法。

addWidget方法用于加入需要布局的控件,方法原型如下:

void addWidget
(
	QWidget *widget,	// 需要插入的控件对象
	int fromRow, 		// 插入的行
	int fromColumn, 	// 插入的列
	int rowSpan, 		// 表示占用的行数
	int columnSpan, 	// 表示占用的列数
	Qt::Alignment alignment = Qt::Alignment() // 描述各个控件的对齐方式
)

addLayout方法用于加入子布局,方法原型如下:

void addLayout
(
	QLayout *layout, 	// 需要插入的子布局对象
	int row, 			// 插入的起始行
	int column, 		// 插入的起始列
	int rowSpan, 		// 占用的行数
	int columnSpan, 	// 占用的列数
	Qt::Alignment alignment = Qt::Alignment()	// 指定对齐方式
)

下面通过一个UserInfo工程实现一个“用户资料”的功能表单来介绍如何使用基本布局管理,界面效果图如下所示:
在这里插入图片描述
关键代码如下:

UserInfo::UserInfo(QWidget *parent)
    : QDialog(parent)
{
    setWindowTitle(tr("UserInfo"));
    /************** 左侧 ******************************/
    UserNameLabel = new QLabel(QString::fromLocal8Bit("用户名:"));
    UserNameLineEdit = new QLineEdit;
    NameLabel = new QLabel(QStringLiteral("姓名:"));
    NameLineEdit = new QLineEdit;
    SexLabel = new QLabel(QStringLiteral("性别:"));
    SexComboBox = new QComboBox;
    SexComboBox->addItem(QStringLiteral("女"));
    SexComboBox->addItem(QStringLiteral("男"));
    DepartmentLabel = new QLabel(QStringLiteral("部门:"));
    DepartmentTextEdit = new QTextEdit;
    AgeLabel = new QLabel(QStringLiteral("年龄:"));
    AgeLineEdit = new QLineEdit;
    OtherLabel = new QLabel(QStringLiteral("备注:"));
    OtherLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken);
    LeftLayout = new QGridLayout();
    //向布局中加入需要布局的控件
    LeftLayout->addWidget(UserNameLabel, 0, 0);     		 //用户名
    LeftLayout->addWidget(UserNameLineEdit, 0, 1);
    LeftLayout->addWidget(NameLabel, 1, 0);                  //姓名
    LeftLayout->addWidget(NameLineEdit, 1, 1);
    LeftLayout->addWidget(SexLabel, 2, 0);                  //性别
    LeftLayout->addWidget(SexComboBox, 2, 1);
    LeftLayout->addWidget(DepartmentLabel, 3, 0);        	//部门
    LeftLayout->addWidget(DepartmentTextEdit, 3, 1);
    LeftLayout->addWidget(AgeLabel, 4, 0);             	    //年龄
    LeftLayout->addWidget(AgeLineEdit, 4, 1);
    LeftLayout->addWidget(OtherLabel, 5, 0, 1, 2);       	//其他
    LeftLayout->setColumnStretch(0, 1);
    LeftLayout->setColumnStretch(1, 3);
    /*********右侧*********/
    HeadLabel = new QLabel(QStringLiteral("头像: "));      //右上角部分
    HeadIconLabel = new QLabel;
    QPixmap icon("312.png");
    HeadIconLabel->setPixmap(icon);
    HeadIconLabel->resize(icon.width(), icon.height());
    UpdateHeadBtn = new QPushButton(QStringLiteral("更新"));
    //完成右上侧头像选择区的布局
    TopRightLayout = new QHBoxLayout();
    TopRightLayout->setSpacing(20);		//设定各个控件之间的间距为20
    TopRightLayout->addWidget(HeadLabel);
    TopRightLayout->addWidget(HeadIconLabel);
    TopRightLayout->addWidget(UpdateHeadBtn);
    IntroductionLabel = new QLabel(QStringLiteral("个人说明:"));		//右下角部分
    IntroductionTextEdit = new QTextEdit;
    //完成右侧的布局
    RightLayout = new QVBoxLayout();
    RightLayout->setMargin(10);
    RightLayout->addLayout(TopRightLayout);
    RightLayout->addWidget(IntroductionLabel);
    RightLayout->addWidget(IntroductionTextEdit);
    /*--------------------- 底部 --------------------*/
    OkBtn = new QPushButton(QStringLiteral("确定"));
    CancelBtn = new QPushButton(QStringLiteral("取消"));
    //完成下方两个按钮的布局
    ButtomLayout = new QHBoxLayout();
    ButtomLayout->addStretch();	
    ButtomLayout->addWidget(OkBtn);
    ButtomLayout->addWidget(CancelBtn);
    /*---------------------------------------------*/
    QGridLayout* mainLayout = new QGridLayout(this);
    mainLayout->setMargin(15);					//设定对话框的边距为15
    mainLayout->setSpacing(10);
    mainLayout->addLayout(LeftLayout, 0, 0);
    mainLayout->addLayout(RightLayout, 0, 1);
    mainLayout->addLayout(ButtomLayout, 1, 0, 1, 2);
    mainLayout->setSizeConstraint(QLayout::SetFixedSize);
}

上述代码的一些内容在此解释一下:

  • OtherLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken):设置控件风格。setFrameStyle方法是QFrame类的方法,参数可以通过(|)符号设置多种风格。风格由形状(QFrame::Shape)和阴影(QFrame::Shadow)两项配合设定。其中,形状包括7种,分别是QFrame::NoFrame、QFrame::Box、QFrame::Panel、QFrame::StyledPanel、QFrame::HLine、QFrame::VLine、QFrame::WinPanel;阴影包括3种,分别是QFrame::Plain、QFrame::Raised、QFrame::Sunken。
  • LeftLayout = new QGridLayout():左侧布局,由于此布局管理器不是主布局管理器,所以不用指定父窗口。
  • LeftLayout->setColumnStretch(0, 1)、LeftLayout->setColumnStretch(1, 3):设定两列分别占用空间的比例,本比例为1:3。即时对话框框架大小改变了,两列之间的狂赌比依然保持不变。
  • ButtomLayout->addStretch():在按钮之前插入一个占位符,使两个按钮能够靠右对齐,并且在整个对话框的大小发生改变时,保证按钮的大小不发生变化。
  • QGridLayout* mainLayout = new QGridLayout(this):实现主布局,指定父窗口this,也可调用this->setLayout(mainLayout)实现。
  • mainLayout->setSizeConstraint(QLayout::SetFixedSize):设定最优化显示,并且使用户改变对话框的大小。所谓最优化显示,即控件都按其sizeHint()的大小显示。

工程源码

文章涉及的所有代码都可点击工程源码下载。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐