一个layout内的边缘和子widget之间的空格由当前widget的style决定. 也可以使用QLayout::setContentsMargins()和QLayout::setSpacing()来改变 QGridLayout的语法: layout->addWidget(widget, row, column, rowSpan, columnSpan); addStretch() 用来告知layout 管理器填充该处的空白. layout 管理器的优点: 添加或者移除一个widget, layout会自动修正以适应新的情况, 对hide()和show()也有同样的效果. 当子widget的size hint发生改变时, layout 管理器会自动修改以适应新的size hint 根据子widget的size hint和最小值来设置layout的最小值 有时我们需要修改size policy和widget的size hint来实现我们需要的layout size policy的值: Fixed --- 固定的layout, 不能拉伸和收缩. 使用size hint的大小 Minimum --- 表示该widget的最小值就是size hint. 不能够收缩至比该值更小的值. 可以填充可用的空间给widget Maximum --- 表示该widget的最大值就是size hint, 该widget可以收缩至其最小值 minimum size hint Preferred --- 该widget的preferred值就是size hint, 在需要的时候可以收缩和拉伸 Expanding --- 表示该widget可以收缩和拉伸, 但其最好选择拉伸 Expanding 和 Preferred的区别: 当一个form包含两者的widget, 该form大小变化时, 额外的空白处则给予Expanding widget, 而Preferred widget保持为其size hint的大小 Minimum, Expanding 和 Ignored这两个Size Policy不再经常使用, 后者忽略widget的size hint和最小值hint 为了补充水平和垂直部分的size policy, 我们还设定了拉伸因子(strectch factor), 可以设置widget在水平或垂直方向的拉伸 如果对一个widget不满意, 我们还可以派生该widget类, 重写其sizeHint()函数 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))); listWidget->setCurrentRow(0); QSplitter的子widget根据创建的顺序自动排列在一起. 相邻的widget之间有splitter bar. 下面是创建的代码 int main(int argc, char *argv[]) QApplication app(argc, argv); 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.show(); return app.exec(); QSplitter 派生自QWidget, 像其他的widget一样使用. MailClient的例子 MailClient::MailClient() rightSplitter = new QSplitter(Qt::Vertical); rightSplitter->addWidget(messagesTreeWidget); rightSplitter->addWidget(textEdit); rightSplitter->setStretchFactor(1, 1); mainSplitter = new QSplitter(Qt::Horizontal); mainSplitter->addWidget(foldersTreeWidget); mainSplitter->addWidget(rightSplitter); mainSplitter->setStretchFactor(1, 1); setCentralWidget(mainSplitter); setWindowTitle(tr("Mail Client")); readSettings(); QSettings settings("Software Inc.", "Mail Client"); settings.beginGroup("mainWindow"); settings.setValue("geometry", saveGeometry()); settings.setValue("mainSplitter", mainSplitter->saveState()); settings.setValue("rightSplitter", rightSplitter->saveState()); settings.endGroup(); // 读取设置 QSettings settings("Software Inc.", "Mail Client"); settings.beginGroup("mainWindow"); restoreGeometry(settings.value("geometry").toByteArray()); mainSplitter->restoreState( settings.value("mainSplitter").toByteArray()); rightSplitter->restoreState( settings.value("rightSplitter").toByteArray()); settings.endGroup();

6.4 Scrolling Areas

如果需要使用滚动条, 最好使用QScrollArea而不是自己实现QScrollBar和滚动功能, 因为这样太复杂 使用QScrollArea的方法是调用setWidget使得该widget成为QScrolllArea视口的子类. 访问视口, QScrollArea::viewport() QScrollArea scrollArea; scrollArea.setWidget(iconEditor); scrollArea.viewport()->setBackgroundRole(QPalette::Dark); scrollArea.viewport()->setAutoFillBackground(true); scrollArea.setWindowTitle(QObject::tr("Icon Editor")); 缺省情况是当视口比widget更小的时候才显示滚动条, 如果想滚动条永远显示, 则使用以下代码: scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); scrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); QScrollArea派生自QAbstractScrollArea, QTextEdit和QAbstractItemView的基类为QAbstractScrollBar.

6.5 Dock Windows and Toolbars

Dock Window表示那些可以在QMainWindow Dock的窗口以及可以独立出来的窗口. QMainWindow 提供了四个浮动区域, 上,下, 左, 右. 每个dock window都有其标题条, 可通过QDockWidget::setFeatures() 设置其属性 QMainWindow::setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea); // 上面的函数设置左上角区域属于左边的dock widget区域 如何在一个QDockWidget包装一已存widget, 且插入右边的dock区域 QDockWidget *shapesDockWidget = new QDockWidget(tr("Shapes")); shapesDockWidget->setObjectName("shapesDockWidget"); shapesDockWidget->setWidget(treeWidget); shapesDockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); addDockWidget(Qt::RightDockWidgetArea, shapesDockWidget); QToolBar *fontToolBar = new QToolBar(tr("Font")); fontToolBar->setObjectName("fontToolBar"); fontToolBar->addWidget(familyComboBox); fontToolBar->addWidget(sizeSpinBox); fontToolBar->addAction(boldAction); fontToolBar->addAction(italicAction); fontToolBar->addAction(underlineAction); fontToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea); addToolBar(fontToolBar); 保存和回复设置 // 保存 QSettings settings("Software Inc.", "Icon Editor"); settings.beginGroup("mainWindow"); settings.setValue("geometry", saveGeometry()); settings.setValue("state", saveState()); settings.endGroup(); // 恢复 QSettings settings("Software Inc.", "Icon Editor"); settings.beginGroup("mainWindow"); restoreGeometry(settings.value("geometry").toByteArray()); restoreState(settings.value("state").toByteArray()); settings.endGroup(); QMainWindow还给dock window和Toolbar提供了右键菜单

6.6 多文档接口

一个MDI应用程序通过使用QMdiArea类作为中心widget以及让每个文档窗口为一个QMdiArea的子窗口 子窗口菜单-MDI应用程序提供了一系列菜单选项表示所有的文档窗口, 当前的文档窗口会有选中标志 在构造函数中, 创建一个QMdiArea对象并设置为中心widget, 并将subWindowActivated()信号发送给一个slot, 实现菜单的更新 在构造函数的结尾部分有一行代码: QTimer::singleShot(0, this, SLOT(loadFiles())); 表示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(); connect(editor, SIGNAL(copyAvailable(bool)), cutAction, SLOT(setEnabled(bool))); connect(editor, SIGNAL(copyAvailable(bool)), copyAction, SLOT(setEnabled(bool))); QMdiArea的addSubWindow() 函数可以创建一个新的QMdiSubWindow: QMdiSubWindow *subWindow = mdiArea->addSubWindow(editor); QActionGroup 确保只有一个窗口菜单选项被选中. QMdiArea::activeSubWindow() --- 返回其活跃窗口 qobject_cast<Editor *> --- 用于强制转换. QTextCursor::hasSelection () --- 返回当前文本光标是否选择了文本 QAction::setChecked() --- 设置选中该Action QScintilla --- 代码编辑的widget 每个子窗口设置 Qt::WA_DeleteOnClose 属性, 当关闭的时候删除该窗口, 以免内存泄漏