目录

什么是停靠窗口

如何添加停靠窗口

QDockWidget::setWidget()

QMainWindow::addDockWidget()

设置停靠选项 (Options)

AnimatedDocks

AllowNestedDocks

AllowTabbedDocks

ForceTabbedDocks

VerticalTabs

GroupedDragging

设置窗口特性 (Features)

设置可停靠区域

设置角落区域

分割窗口

标签页显示 (tab)

设置窗口标题栏

保存停靠窗口状态

常见问题

拓展阅读

Application Example

Dock Widgets Example

MDI Example

Menus Example

SDI Example


什么是停靠窗口

在 Qt 中,停靠窗口 (dock window) 都是 QDockWidget 的实例,可以停靠在 QMainWindow 的中央部件 (central widget) 的上下左右四个区域,停靠的 QDockWidget 没有框架,有一个较小的标题栏;也可浮动出来作为独立窗口。

QDockWidget API 允许程序员控制停靠窗口移动、浮动和关闭的能力,以及它们可以放置的区域等。

相关文档:QDockWidget Class,QMainWindow Class

Qt 提供了很多相关示例,其中 Main Window 最为全面详细,我们今天就就着这个示例研究一下 QDockWidget 的用法及特性。

如何添加停靠窗口

先看一段代码关于如何创建停靠窗口,并将其添加到主窗口:

    QDockWidget *dockWidget = new QDockWidget(tr("Dock Widget"), this);dockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);dockWidget->setWidget(dockWidgetContents);addDockWidget(Qt::LeftDockWidgetArea, dockWidget);

QDockWidget::setWidget()

void QDockWidget::setWidget(QWidget *widget)

QDockWidget 充当该 widget 的包装器。注意:调用这个函数之前必须为 widget 添加布局,否则它将不可见,而且如果此时 QDockWidget 已经可见了,那我们必须显式地 widget->show() 。

QMainWindow::addDockWidget()

void QMainWindow::addDockWidget(Qt::DockWidgetArea area, QDockWidget *dockwidget)

使用这个函数可以为 QMainWindow 在指定区域(上/下/左/右)添加一个停靠窗口。

设置停靠选项 (Options)

void QMainWindow::setDockOptions(QMainWindow::DockOptions options)

该函数可设置停靠窗口的一些行为。QMainWindow::DockOptions 是枚举类型,定义为:

    enum DockOption {AnimatedDocks = 0x01,AllowNestedDocks = 0x02,AllowTabbedDocks = 0x04,ForceTabbedDocks = 0x08,  // implies AllowTabbedDocks, !AllowNestedDocksVerticalTabs = 0x10,      // implies AllowTabbedDocksGroupedDragging = 0x20    // implies AllowTabbedDocks};Q_ENUM(DockOption)Q_DECLARE_FLAGS(DockOptions, DockOption)Q_FLAG(DockOptions)

默认值: AnimatedDocks | AllowTabbedDocks.

注意:这些选项仅控制如何在 QMainWindow 中放置停靠窗口,它们不会重新排列已有的停靠窗口以符合指定的选项。因此,应在停靠窗口添加到主窗口之前设置它们。 AnimatedDocks 和 VerticalTabs 选项除外,它们可以随时设置。

AnimatedDocks

默认设置该选项,对停靠窗口和工具栏的操作进行动画处理。当在主窗口上拖动停靠窗口或工具栏时,主窗口会调整其内容以指示窗口在放置时停靠的位置,这一过程以平滑的动画移动。若清除该选项则无动画效果。

同 animated 属性,查询及设置函数:
bool isAnimated() const
void setAnimated(bool enabled)

注意:如果主窗口有的 widgets 在调整大小或重新绘制自身时很缓慢,则该属性可能会被清除。

AllowNestedDocks

默认不设置该选项,每个停靠区域只能包含单行停靠窗口(即垂直方向的停靠区域只有一列,水平区域的停靠区域只有一行);若设置该属性,则停靠区域可以在任一方向拆分以包含更多停靠窗口(即垂直方向的停靠区域可以有多列,水平方向的停靠区域可以有多行)。

同 dockNestingEnabled 属性,查询及设置函数:
bool isDockNestingEnabled() const
void setDockNestingEnabled(bool enabled)

注意:仅在包含大量停靠窗口的应用程序中才需要设置该属性,它为用户组织主窗口提供了更多的自由。 然而由于有更多方法可以将停靠窗口放置在停靠区域中,也更复杂,且不太直观。

另外,很多人将这个属性称为“是否允许嵌套的一个属性”,我觉得这里"nested"翻译成嵌套并不合适,这个属性并没有允许停靠窗口嵌套布局,我觉得“巢状布局”更合适。

AllowTabbedDocks

若设置该选项,则允许用户以选项卡 (tab) 方式组织多个停靠窗口,即若将一个停靠窗口 dock1 拖动到另一个 dock2 的区域,会出现一个选项卡栏(QTabBar),每个停靠窗口都在一个单独的标签页里,dock1 为活动窗口。

ForceTabbedDocks

若设置该选项,则 AllowNestedDocks 无效,只能以选项卡 (tab) 方式组织多个停靠窗口。 换句话说,停靠窗口不能在停靠区域中彼此相邻放置。

VerticalTabs

若设置该选项,则暗示 AllowTabbedDocks,允许用户以选项卡 (tab) 方式组织多个停靠窗口,且垂直停靠区域的选项卡在垂直方向显示;若未设置此选项,则所有停靠区域都会在底部显示其选项卡。

另外可通过 void QMainWindow::setTabPosition() 为指定停靠区域设置选项卡位置,但VerticalTabs 选项优先。

GroupedDragging

若设置该选项,则暗示 AllowTabbedDocks,允许用户以选项卡 (tab) 方式组织多个停靠窗口,且在拖动停靠窗口时,将拖动所有带有标签的选项卡 。 但如果某些 QDockWidget 允许的区域有限制,则效果不佳。 (这个枚举值是在 Qt 5.6 中添加的。)

void MainWindow::setDockOptions()
{DockOptions opts = 0;QList<QAction*> actions = mainWindowMenu->actions();if (actions.at(0)->isChecked())opts |= AnimatedDocks;if (actions.at(1)->isChecked())opts |= AllowNestedDocks;if (actions.at(2)->isChecked())opts |= AllowTabbedDocks;if (actions.at(3)->isChecked())opts |= ForceTabbedDocks;if (actions.at(4)->isChecked())opts |= VerticalTabs;if (actions.at(5)->isChecked())opts |= GroupedDragging;QMainWindow::setDockOptions(opts);
}

设置窗口特性 (Features)

void QDockWidget::setFeatures(QDockWidget::DockWidgetFeatures features)

该函数可以设置停靠窗口的一些特性,QDockWidget::DockWidgetFeatures 枚举定义为:

    enum DockWidgetFeature {DockWidgetClosable    = 0x01,DockWidgetMovable     = 0x02,DockWidgetFloatable   = 0x04,DockWidgetVerticalTitleBar = 0x08,DockWidgetFeatureMask = 0x0f,AllDockWidgetFeatures = DockWidgetClosable|DockWidgetMovable|DockWidgetFloatable, // ### Qt 6: removeNoDockWidgetFeatures  = 0x00,Reserved              = 0xff};
  • 注意:QDockWidget::AllDockWidgetFeatures 已弃用,可指定为:QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable。这也是默认的特性:可关闭,可移动,可浮动

  • QDockWidget::DockWidgetVerticalTitleBar 在 QDockWidget 左侧显示一个垂直标题栏,这可用于增加垂直空间。
void ColorSwatch::changeClosable(bool on)
{ setFeatures(on ? features() | DockWidgetClosable : features() & ~DockWidgetClosable); }void ColorSwatch::changeMovable(bool on)
{ setFeatures(on ? features() | DockWidgetMovable : features() & ~DockWidgetMovable); }void ColorSwatch::changeFloatable(bool on)
{ setFeatures(on ? features() | DockWidgetFloatable : features() & ~DockWidgetFloatable); }

设置可停靠区域

void QDockWidget::setAllowedAreas(Qt::DockWidgetArea areas)

这个函数可以设置停靠窗口可以停靠的区域,Qt::DockWidgetArea 是一个枚举,给出了四个停靠区域:左边、右边、顶部和底部,默认是 Qt::AllDockWidgetAreas。

    enum DockWidgetArea {LeftDockWidgetArea = 0x1,RightDockWidgetArea = 0x2,TopDockWidgetArea = 0x4,BottomDockWidgetArea = 0x8,DockWidgetArea_Mask = 0xf,AllDockWidgetAreas = DockWidgetArea_Mask,NoDockWidgetArea = 0};
void ColorSwatch::allow(Qt::DockWidgetArea area, bool a)
{Qt::DockWidgetAreas areas = allowedAreas();areas = a ? areas | area : areas & ~area;setAllowedAreas(areas);
}

设置角落区域

void QMainWindow::setCorner(Qt::Corner corner, Qt::DockWidgetArea area)

文章最开始的 QMainWindow 的示意图中,有四个虚线标出的停靠区域重叠的角落,使用这个函数可以指定哪个停靠区域可以占据该区域。

Qt::Corner 是个枚举类型,定义:

    enum Corner {TopLeftCorner = 0x00000,TopRightCorner = 0x00001,BottomLeftCorner = 0x00002,BottomRightCorner = 0x00003};

分割窗口

void QMainWindow::splitDockWidget(QDockWidget *first, QDockWidget *second, Qt::Orientation orientation)

first 停靠窗口所在的空间分成两部分,first 移动到第一部分,second 移动到第二部分。

orientation 指定空间的划分方式:Qt::Horizontal 将 second 停靠部件放置在 first 的右侧; Qt::Vertical 将 second 停靠部件放置在 first 下方。

注意:

  • 如果 first 当前位于选项卡式停靠区域中,则 second 将作为新的标签页添加,因为单个标签页只能包含一个停靠窗口。
  • Qt::LayoutDirection 会影响划分区域的两个部分中停靠窗口的顺序,当启用从右到左 (Qt::RightToLeft) 的布局方向时,停靠窗口的放置将被颠倒。
void ColorSwatch::splitInto(QAction *action)
{ColorSwatch *target = findByName(mainWindow, action->text());if (!target)return;const Qt::Orientation o = action->parent() == splitHMenu? Qt::Horizontal : Qt::Vertical;mainWindow->splitDockWidget(target, this, o);
}

标签页显示 (tab)

void QMainWindow::tabifyDockWidget(QDockWidget *first, QDockWidget *second)

在主窗口中创建一个选项卡式停靠区域,将 first 和 second 停靠窗口置于不同的标签页中,second 为当前活动窗口。

void ColorSwatch::tabInto(QAction *action)
{if (ColorSwatch *target = findByName(mainWindow, action->text()))mainWindow->tabifyDockWidget(target, this);
}

设置窗口标题栏

void QDockWidget::setTitleBarWidget(QWidget *widget)

将 widget 设置为停靠窗口的标题栏。如果widget 为0,则之前在停靠窗口上设置的自定义标题栏将被删除,但标题栏不会被删除,而将使用默认标题栏。

            BlueTitleBar *titlebar = new BlueTitleBar(swatch);swatch->setTitleBarWidget(titlebar);

如果设置了标题栏,则 QDockWidget 在浮动时不会使用原生窗口装饰。

以下是实现自定义标题栏的一些技巧:

  • 标题栏未明确处理的鼠标事件必须通过调用 QMouseEvent::ignore() 来忽略,从而将这些事件传递给父对象 QDockWidget 以通常的方式处理,比如拖动标题栏时移动、双击时停靠/取消停靠等。
  • 当在 QDockWidget 上设置 DockWidgetVerticalTitleBar 时,标题栏会相应地重新定位。在 resizeEvent() 中,标题栏应该检查它应该采用的方向:
 QDockWidget *dockWidget = qobject_cast<QDockWidget*>(parentWidget());if (dockWidget->features() & QDockWidget::DockWidgetVerticalTitleBar) {// I need to be vertical} else {// I need to be horizontal}
  • 标题栏必须有一个有效的 QWidget::sizeHint() 和 QWidget::minimumSizeHint()。这些功能应考虑标题栏的当前方向。
QSize sizeHint() const override { return minimumSizeHint(); }QSize BlueTitleBar::minimumSizeHint() const
{QDockWidget *dw = qobject_cast<QDockWidget*>(parentWidget());Q_ASSERT(dw != 0);QSize result(leftPm.width() + rightPm.width(), centerPm.height());if (dw->features() & QDockWidget::DockWidgetVerticalTitleBar)result.transpose();return result;
}
  • 虽然无法从停靠窗口中删除标题栏,但是通过将默认构造的 QWidget 设置为标题栏,可以实现类似的效果。

使用如上所示的 qobject_cast(),标题栏可以访问到其父对象 QDockWidget。因此,它可以执行停靠窗口的停靠、隐藏等操作。

保存停靠窗口状态

  • QByteArray QMainWindow::saveState(int version = 0) const
  • bool QMainWindow::restoreState(const QByteArray &state, int version = 0)

保存工具栏和停靠窗口的位置,包括使用 setCorner() 对角落区域的设置,以使下一次运行程序时能够恢复它们的状态。

void MainWindow::saveLayout()
{...QByteArray geo_data = saveGeometry();QByteArray layout_data = saveState();...
}void MainWindow::loadLayout()
{...if (ok)ok = restoreGeometry(geo_data);if (ok)ok = restoreState(layout_data);...
}

常见问题

  • QDockWidget 是否必须在 QMainWindow 中使用?是否可在其他布局里添加 ?

可以,但这样它就和一个普通的 widget 没多大不一样,不过多一个浮动、停靠的功能,没有必要,而 QMainWindow 为 QDockWindow 增加了很多功能,QDockWindow 在 QMainWindow 中使用,才能发挥最大的作用。

  •  如何约束停靠窗口的大小?

虽然 QDockWidget 继承自 QWidget,也继承了QWidget::setFixedSize(), QWidget::setMinimumSize(), QWidget::setMaximumSize() 等函数,但是QDockWidget 只是一个包装器,会根据是否停靠而变化,不应在 QDockWidget 本身上设置大小约束。自定义 sizeHint、minimumSize、maximumSize、sizePolicy 都应在它的 widget 中实现。QDockWidget 会尊重它们,调整自己的约束以包含框架和标题。

Qt 文档中有介绍到:

Note: Creating a main window without a central widget is not supported. You must have a central widget even if it is just a placeholder.

注意:不支持创建没有中央小部件的主窗口。 即使它只是一个占位符,您也必须有一个中央小部件。

但从 Qt 5.2 开始提供了 QWidget *QMainWindow::takeCentralWidget() 函数,可以将中央窗体从主窗口中移除。

QWidget* p = takeCentralWidget();
if(p) delete p;

效果如下:

拓展阅读

本地示例源码路径:Qt安装目录\Qt5.12.11\Examples\Qt-5.12.11\widgets\mainwindows

该工程除了示例  Main Window 外,还有其他几个示例:

Application Example

该示例展示了如何实现带有菜单栏、工具栏和状态栏的标准 GUI 应用程序,该示例本身是一个围绕 QPlainTextEdit 构建的简单文本编辑器程序。

详见:Application Example | Qt Widgets

Dock Widgets Example

该示例展示了如何向应用程序添加停靠窗口,它还展示了如何使用 Qt 的富文本引擎。

详见:Dock Widgets Example | Qt Widgets

MDI Example

该示例展示了如何使用 Qt 的 QMdiArea 类实现多文档界面。

详见:MDI Example | Qt Widgets

Menus Example

菜单示例演示了如何在主窗口应用程序中使用菜单。

菜单可以是菜单栏中的下拉菜单,也可以是独立的上下文菜单。 当用户单击相应的 item 或按下指定的快捷键时,菜单栏会显示下拉菜单。 上下文菜单通常由一些特殊的键盘键或右键单击来调用。

详见:Menus Example | Qt Widgets

SDI Example

该示例展示了如何创建单文档界面,它使用多个顶级窗口来显示不同文本文件的内容。

详见:SDI Example | Qt Widgets

Qt Widgets 之 QDockWidget(停靠窗口)相关推荐

  1. QT界面:QDockWidget停靠窗口使用小结

    QDockWidget停靠窗口 Qt构建停靠窗口使用的是QDockWidget类. 窗口特性 停靠窗口特性可以通过setFeatures(QDockWidget::AllDockWidgetFeatu ...

  2. 【转】QDockWidget 停靠窗口和工具栏

    转:http://blog.csdn.net/a649518776/article/details/6636490 下面用代码实现上面窗口的设计: 1.建立项目时基类选择QMainWindow,取消G ...

  3. 【QT】QT从零入门教程(六):QDockWidget停靠窗口

    QDockWidget   上节我们引出了QDockWidget的概念,这节进行讲解并加以引用.   常用函数:   1.addDockWidget:添加停靠控件,用于指定或更改停靠控件的位置以及方向 ...

  4. 【学习笔记】C++ GUI Qt4 第六章 6.4 滚动区域 和 6.5 停靠窗口和工具栏

    文章目录 6.4 滚动区域 6.5 停靠窗口和工具栏 6.4 滚动区域 QScrollArea类提供了一个可以滚动的视口和两个滚动条.如果想给一个窗口部件添加一个滚动条,则可以使用一一个QScroll ...

  5. c语言mfc窗口跳到下个窗口,MFC停靠窗口实现(CDockablePane)

    工作中编写MFC界面程序时用到了停靠窗口,为了避免之后用到时再去查询,这里记录下. 步骤 1.定义一个继承自CDockablePane的类 Class CDockableTest : public C ...

  6. Qt5之布局管理之分割窗口、停靠窗口、堆栈窗口

    一.分割窗口 效果图 分割窗口功能的实现非常简单,这里通过代码实现分割窗口,效果图如下: 代码及说明 #include "QtWidgetsApplication1.h" #inc ...

  7. Qt5—嵌入停靠窗口QDockWidget

    参考链接:http://blog.csdn.net/summer_xiyer/article/details/12875899 新建一个GUI工程: QDockWidget是QWidget的子类,也等 ...

  8. Qt Widgets——主窗口及其主要组成部分

    Main Window and Related Classes QAction 动作类,用于当做一个菜单项或工具项插入菜单或工具栏 QActionGroup 动作组,用于管理多个动作,设置它们之间的互 ...

  9. PyQt主窗体设置停靠窗口(QDockWidget)的叠加顺序

    PyQt提供了方便的停靠窗口控件,我们可以很方便的编写一个停靠窗口,代码和效果如下: # -*- coding: utf-8 -*- from PyQt4 import QtGui, QtCore c ...

最新文章

  1. java joptionpane 例子_java JOptionPane类的介绍
  2. Vertical Menu ver4
  3. [-] Handler failed to bind to x.x.x.x:port排错
  4. php提交之前验证数据ajax提示,在通过Ajax请求提交之前使用jQuery进行表单验证
  5. 【LeetCode】1. 盛最多水的容器:C#三种解法
  6. C语言项目:图形马赛克处理技术
  7. 项目启动居然如此重要!
  8. AliOS Things全链路优化-CoAP FOTA
  9. hdu 3641 数论 二分求符合条件的最小值数学杂题
  10. MYSQL学习04--数据导入导出及实战练习
  11. 农学院计算机考试题,天津农学院计算机考试参考.doc
  12. java docx转pdf_在java中将docx转换为pdf
  13. 在windows 2003 中,启动SQL Server 2000 提示“没有找到MSVCR71.dll因此这个应用程序未能启动”
  14. chrome pdf 打印边距问题处理
  15. 重启linux没有网络配置文件,Linux 网络配置 修改DNS配置文件/etc/resolv.conf后,重启网络,DNS配置丢失...
  16. 信用证的好处!只要有信用证,没有不符点,就能收到钱!
  17. 赠与大学毕业生_如何出售或赠与您的Kindle
  18. linux下最好用的安卓模拟器!
  19. 一文搞懂│王者游戏中荣耀水晶难抽?探索游戏中的抽奖算法
  20. 五子棋java百度文库_JAVA课程设计 五子棋(内附完整代码).doc

热门文章

  1. 《A Graduate Course in Applied Cryptography》Chapter 21 Authenticated Key Exchange (1)
  2. 互联网不再迷恋北上广
  3. el-carousel加载缓慢
  4. 自动登录AppStore for Mac 1.1
  5. RPC通信基本原理 -- 浅析RPC远程过程调用基本原理
  6. 视频录制与编辑架构设计
  7. JS 验证码生成及校验(详细注释)
  8. 云计算笔记part.1——系统管理
  9. Android 设备接入扫码枪
  10. JS中如何定义全局变量