Qt学习笔记(二)布局管理

来源:互联网 发布:淘宝店数据包怎么做 编辑:程序博客网 时间:2024/06/05 16:07

布局管理主要用于在窗体中摆放每一个窗口部件,以及自动调整窗口部件在窗体中的大小和位置。 Qt提供了多个用于摆放窗口部件的类: QHBoxLayout 、 QVBoxLayout 、 QGridLayout 、 QStackedLayout 。

 

其中前 3种是最重要的布局管理器,其用法也很简单,使用 addWidget()将需要摆放的窗口部件添加到 Layout里面。 Layout本身也可以通过 addLayout()作为一个整体添加到上层 Layout里面。 addStretch()可以添加一个伸缩器用于占满空白空间。 QGridLayout 的用法要稍微麻烦些。他基于一个二维单元格,每个窗口部件可以占据一个或几个单元格。左上角的单元格为 (0, 0)。 QGridLayout :: addWidget()的用法是: layout->addWidget(widget, row, column, rowSpan, columnSpan); 其中 row和 column是窗口部件所占据的位置, rowSpan和 columnSpan是该部件所占用的行数和列数。

 

具体用法:

QHBoxLayout 和 QVBoxLayout 

 

label = new QLabel ( tr( "Label" ));

editor = new QLineEdit ( this );

 

label1 = new QLabel ( tr( "Label1" ));

editor1 = new QLineEdit ( this );

 

QHBoxLayout * layout1 = new QHBoxLayout ;

layout1-> addWidget( label);

layout1-> addWidget( editor);

   

QVBoxLayout * layout1 = new QVBoxLayout ;

layout1-> addWidget( label);

layout1-> addWidget( editor);

 

QVBoxLayout * mainLayout = new QVBoxLayout ;

mainLayout-> addLayout( layout1);

mainLayout-> addStretch();

mainLayout-> addLayout( layout2);

setLayout( mainLayout);

 

只需要将窗口部件按顺序添加即可,在所有需要使用 setLayout() 将处于最顶端的 Layout 设置到当前的窗体。

 

 

QGridLayout 

QGridLayout * leftLayout = new QGridLayout ;

leftLayout-> addWidget( subfoldersCheckBox, 2 , 0 , 1 , 2 );

 

QGridLayout 在添加窗口部件是需要指明位置和所占范围,其他都与 QHBoxLayout 和 QVBoxLayout 一样。

 

QStackedLayout 可以对一组子窗口部件进行摆放,以及对他们进行“分页”,一次只显示其中的一个,而将其他的子窗口部件或者分页隐藏起来。分页从 0 开始编号,可以使用 setCurrentIndex() 来设置当前显示的分页,通过indexOf() 获取子窗口部件的页号。

下例中对话框由左侧的 QListWidget和右侧的QStackedLayout组成。在QListWidget中的每一项,都分别对应于QStackedLayout中的不同页。

    listWidget = new QListWidget;
    listWidget->addItem(tr("Appearance"));
    listWidget->addItem(tr("Web Browser"));
    listWidget->addItem(tr("Mail & News"));
    listWidget->addItem(tr("Advanced"));
    stackedLayout = new QStackedLayout;
    stackedLayout->addWidget(appearancePage);
    stackedLayout->addWidget(webBrowserPage);
    stackedLayout->addWidget(mailAndNewsPage);
    stackedLayout->addWidget(advancedPage);
    connect(listWidget, SIGNAL(currentRowChanged(int)),
            stackedLayout, SLOT(setCurrentIndex(int)));
 

切分窗口 QSplitter

QSplitter就是一个可以包含一些其他窗口部件的窗口部件。这切分窗口中的这些窗口部件通过切分条分隔开来,用户可以通过拖动切分条来改变切分窗口中子窗口部件的大小。切分窗口常常可以用作布局管理器的替代品,从而把更多的控制权交给用户。

   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);
    splitter.setWindowTitle(QObject::tr("Splitter"));
    splitter.show();

/窗口布局
    rightSplitter = new QSplitter(Qt::Vertical);
    rightSplitter->addWidget(messagesTreeWidget);  //在右窗口中添加一个邮件列表
    rightSplitter->addWidget(textEdit);                         //显示文本邮件
    rightSplitter->setStretchFactor(1, 1);                      //保证textEdit伸展到多余的空间

    mainSplitter = new QSplitter(Qt::Horizontal);
    mainSplitter->addWidget(foldersTreeWidget);
    mainSplitter->addWidget(rightSplitter);
    mainSplitter->setStretchFactor(1, 1);                     //使用两个setStretchFactor保证了把多余的额外空间都留给textEdit
    setCentralWidget(mainSplitter);
    setWindowTitle(tr("Mail Client"));

效果如下图:

QSplitter - 春天 - 若能一切随他去,便是人间自在人


滚动区域

QScrollArea类提供了一个可以滚动的窗口和两个滚动条。如果想给一个窗口部件添加一个滚动条,则可以使用一个QScrollArea类实现。这可能要比我们自己初始化QScrollBar,然后再实现它的滚动功能要简单。

QScrollArea的使用方法,就是以我们想要添加滚动条的窗口部件为参数调用setWidget()。如果这个窗口部件的父对象不是视口,QScrollArea会自动把这个窗口部件的父对象定义为该视口(可以通过QScrollArea::viewPort()来访问),并且让他成为视口的子对象。

IconEditor *iconEditor = new IconEditor;

iconEditor->setIconImage(QImage(":/images/mouse.png"));

QScrollArea scrollArea;

scrollArea.setWidget(iconEditor);

scrollArea.viewport()->setBackgroundRole(QPalette::Dark);

scrollArea.viewport()->setAutoFillBackground(true);

scrollArea.setWindowTitle(QObject::tr("Icon Editor"));

scrollArea.show();

////////////////////////////////////////////////////////////////////////////////////////////////

QScrollArea会以窗口部件的当前大小来显示它,或者在没有重新改变窗口部件大小的时候以它的大小提示来显示它。通过调用setWidgetResizable(true),可以告诉QScrollArea要自动重新改变该窗口部件的大小,以利用超过他的大小提示之外的任何多余空间。

默认情况下,只有在视口的大小小于子窗口部件的大小时,才会把滚动条显示出来。但通过设置滚动条的策略,可以强制滚动条总是可见:

scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);

scrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);


停靠窗口和工具栏

停靠窗口是指一些可以停靠在QMainWindow中或是浮动为独立窗口的窗口。QMainWindow提供了4个停靠窗口区域,分别在中央窗口部件的上下左右。每个停靠窗口都有自己的标题栏,即时它是处于停靠时也是如此。通过拖拽标题栏,用户可以把停靠窗口从一个停靠区域移动到另一个停靠区域。通过把这个停靠窗口拖动到其他停靠区域的外部,就可以把停靠窗口从一个停靠区域中分离出来,成为一个独立的窗口。自由浮动的停靠窗口总是显示在他们的主窗口的上面,通过点击窗口部件标题栏上的“关闭”按钮,可以关闭QDockWidget。通过调用QDockWidget::setFeatures(),可以禁用所有的这些特性以及他们的任意组合。

四个角上的区域可以属于两个相邻停靠区域中的任意一个,例如假定我们需要让左上角属于左侧的停靠区域,调用QMainWindow::setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea)即可。

以下程序说明了如何对QDockWidget中已经存在的窗口部件进行封装,并把它插入到右侧的停靠区域:

QDockWidget *shapeDockWidget = new QDockWidget;

shapeDockWidget->setObjectName("shapeDockWidget");

shapeDockWidget->setWidget(treeWidget); //本例中,封装在QDockWidget中的窗口部件为 QTreeWidget实例 treeWidget

shapeDockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);//允许该dock部件放在左或右dock区域中,允许拖动的范围

addDockWidget(Qt::RightDockWidgetArea, shapeDockWidget);//将该dock部件放在右侧 dock区域

如果没有明确的指定可以放置的区域,setAllowedAreas,则用户可以拖动该dock部件到任意四个位置。

每个QObject都有一个对象名,setObjectName,这在程序调试的时候非常有用,并且一些测试工具也会用到它。在创建一些停靠窗口和工具栏时,如果希望使用QMainWindow::saveState()和QMainWindow::restoreState()来保存、恢复停靠窗口和工具栏的几何形状和状态,则给定窗口部件的名字就很重要。

下例代码显示了创建工具栏的过程:

QToolBar *fontToolBar = new QToolBar(tr("Font"));

fontToolBar->setObjectName("fontToolBar");

fontToolBar->addWidget(familyComboBox);

fontToolBar->addWidget(sizeSpinBox);

fontToolBar->addAction(boldAction);

fontToolBar->addAction(underlineAction);

fontToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);

addToolBar(fontToolBar);


多文档界面

在主窗口的中央区域能够提供多个文档,为多文档MDI。

在Qt中,通过把QMdiArea类作为中央窗口部件,并且通过让每一个文档窗口都成为这个QMidArea的子窗口部件,就可以创建一个多文档应用程序界面。

对于多文档应用程序有一个惯例,就是为它提供一个Window菜单,这个菜单包含一些管理这些窗口以及窗口列表的命令。激活窗口会使用一个选择标记标识出来,用户通过在Window菜单中单击代表特定窗口的一项,就可以激活窗口。

MainWindow::MainWindow()
{
    mdiArea = new QMdiArea;
    setCentralWidget(mdiArea);
    connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)),
            this, SLOT(updateActions()));
    createActions();
    createMenus();
    createToolBars();
    createStatusBar();
    setWindowIcon(QPixmap(":/images/icon.png"));
    setWindowTitle(tr("MDI Editor"));
    QTimer::singleShot(0, this, SLOT(loadFiles()));
}
在MainWindow构造函数中创建了一个QMdiArea窗口部件,并且让他成为中央窗口部件。把QMdiArea的subWindowActivate()信号与将要用来保持更新window菜单的槽连接起来,并且会根据应用程序的状态来启用或禁用那些动作。

在上例最后,把单触发定时器的时间间隔设置为0,毫秒,以调用loadFiles()函数。对于这样的定时器,只要事件循环一空闲就会触发它。实际上,这意味着只要构造函数结束,同时主窗口显示出来之后就会调用loadFiles()。如果不这样做,而且如果还需要加载许多大文件的话,那么该构造函数在文件加载完毕之前就无法结束。在此期间,用户极有可能在屏幕上看不到任何东西,这样他可能认为应用程序启动失败 了。

void MainWindow::loadFiles()
{
    QStringList args = QApplication::arguments();
    args.removeFirst();
    if (!args.isEmpty()) {
        foreach (QString arg, args)
            openFile(arg);
        mdiArea->cascadeSubWindows();
    } else {
        newFile();
    }
    mdiArea->activateNextSubWindow();
}
如果用户在命令行中启动该应用程序时使用了一个或多个文件名,那么这个函数就会加载每个文件,并且会按照逐级层叠的方式显示这些子窗口,以便用户能够轻松的看到他们。 Qt的一些特殊命令行选项,如-style, -font,QApplication的构造函数会自动把他们从参数列表中剔除出去。因此,如果命令行为 mdieditor -style motif  readme.txt

QApplication::arguments()就会返回一个含有两项(mdieditor readme.txt)的QStringList, 那么MDIEditor程序就会在启动的时候打开readme.txt。

对于activeNextSubWindow()的调用意味着对编辑器窗口赋予焦点,并且可以确保调用updateActions()函数来更新window菜单,以及根据应用程序的状态来启用或禁用一些动作。

void MainWindow::addEditor(Editor *editor)
{
    connect(editor, SIGNAL(copyAvailable(bool)),
            cutAction, SLOT(setEnabled(bool)));
    connect(editor, SIGNAL(copyAvailable(bool)),
            copyAction, SLOT(setEnabled(bool)));
    QMdiSubWindow *subWindow = mdiArea->addSubWindow(editor);
    windowMenu->addAction(editor->windowMenuAction());
    windowActionGroup->addAction(editor->windowMenuAction());
    subWindow->show();
}

QMdi::addSubWindow()函数创建一个新的的QMdiSubWindow,把作为参数的该窗口部件放进子窗口中,并且返回该子窗口。


void MainWindow::closeEvent(QCloseEvent *event)
{
    mdiArea->closeAllSubWindows();
    if (!mdiArea->subWindowList().isEmpty()) {
        event->ignore();
    } else {
        event->accept();
    }
}

重新实现closeEvent()函数来关闭所有子窗口,从而使得每个子窗口都要接收一个关闭事件。如果这些子窗口之一“忽略”了它的关闭事件(可能是用户撤销了一个“未保存变化“消息框),那么也就忽略对MainWindow的关闭事件;否则接受他,这样Qt就会关闭整个应用程序