news 2026/9/7 8:35:47

Qt Advanced Docking System实战指南:替代QDockWidget的现代停靠方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Qt Advanced Docking System实战指南:替代QDockWidget的现代停靠方案

简介:这是基于开源Qt-Advanced-Docking-System实现的高级窗口停靠系统示例资源,面向需要为Qt应用集成复杂可停靠窗口布局的开发者,解决原生QDockWidget在多区域、多窗口管理上的不足。它支持多区域灵活布局、内嵌与悬浮切换、弹出式窗口、布局保存恢复等功能,能提升多窗口应用的组织效率。资源共68个文件,压缩包仅253KB,以21个头文件和21个源文件为主体,涵盖DockManager、DockAreaWidget等核心类实现;另含2个UI界面、2个pro工程文件、样式表与SVG图标,以及可直接运行的exe与配套dll。已有571人学习下载,适合有一定Qt基础、希望扩展窗口管理能力的开发者。通过这份示例,可完整查看高级停靠系统的工程架构与关键实现,理解DockManager如何统一管理各类停靠区域,掌握创建可拖拽停靠窗口、动态调整布局、保存/恢复窗口状态等代码写法,为自研编辑器、IDE类工具提供可复用的界面基础。

1. 为什么我在放弃Qt原生Dock后转向Advanced Docking System

先交代一下背景。我之前做过一个工控类的桌面软件,界面复杂度不算低:左侧设备树、中间主视图区、右侧属性面板、底部还有日志输出。一开始图省事,直接用Qt自带的QDockWidget硬怼,结果做到中期就有点后悔了。

原生QDockWidget的痛点,用过的朋友应该都懂:默认没有标签页模式(Tab化需要自己处理tabifyDockWidget)、拖拽预览很粗糙、浮动窗口嵌套麻烦、布局状态保存恢复全靠手动序列化,而且界面一复杂,停靠区的拖拽逻辑就开始“任性”,同一个Dock在不同QMainWindow之间拖来拖去,经常出现焦点错乱。

后来调研了一圈,开源方案里真正成熟、社区活跃度高的就是Qt-Advanced-Docking-System(后面简称ADS)。这个库最初是给Qt Creator用的,后来拆出来独立维护,现在在GitHub上几千个star,商用友好(LGPLv2.1),在视觉上几乎复刻了Visual Studio那种现代IDE的停靠体验。

我需要的功能它基本都覆盖了:任意方向停靠、标签页停靠、拖拽预览、浮动窗口、嵌套布局、布局持久化、侧边栏收起展开,甚至能实现跨窗口拖拽。最关键的,它对现有代码的侵入性很低,核心只要求一个CDockManager替换QMainWindow的角色,其他业务Widget照常使用。

说白了,这是一个“换掉停靠框架,而不是重写界面”的方案,这也是我最终选择它的核心原因。

2. 编译集成:最容易被拦住的几个坎

ADS的集成不算复杂,但确实有几个细节不处理会浪费很多时间。我先说环境,我的开发环境是Windows 10 + Qt 5.15.2 + MSVC2019 64位,这套组合在ADS下跑得很稳。理论上Qt 5.12以上的版本都支持,但建议用较新的Qt版本,老版本编译时可能遇到C++17标准相关的问题。

2.1 源码获取与目录结构

直接把仓库克隆下来,或者下载release包。ADS的项目结构大概分三块:

  • src:核心源码,全部需要参与编译
  • examples:官方示例,强烈建议先跑一遍
  • tests:单元测试,一般不用管

我把整个ADS目录放到了项目third_party文件夹下,而不是安装在系统路径里,这样打包发布时不会遗漏依赖,团队协作时其他同事clone代码后直接编译即可,省去每个人都要手动配置的环境步骤。

2.2 qmake集成方式

如果你的项目仍然使用qmake,在.pro文件里这样写:

include($$PWD/../third_party/Qt-Advanced-Docking-System/src/ads.pri)

ADS官方提供了一份ads.pri,它会把需要的源文件、头文件和编译宏一次性引进来。这里有一个关键宏必须要知道:ADS_STATIC

官方源码默认可以被编译成动态库或静态库。如果你希望直接把ADS源码编译进你自己的可执行文件(即静态集成),需要在ads.pri之前定义这个宏:

DEFINES += ADS_STATIC

如果不加这个宏,链接时会出现LNK2019这类符号无法解析的错误,因为默认的导出宏逻辑期望的是动态库方式。我一开始没注意这个细节,浪费了一个下午查链接错误,后来翻issue才反应过来。

2.3 CMake集成方式

现在很多新项目已经全面切到CMake,ADS也支持得很完善。直接在CMakeLists.txt里写:

add_subdirectory(third_party/Qt-Advanced-Docking-System) target_link_libraries(your_project PRIVATE QtAdvancedDockingSystem)

如果你不想用add_subdirectory,也可以先单独编译安装ADS,然后通过find_package的方式引用。不过个人推荐add_subdirectory,因为方便调试、方便跟踪源码、方便修改,真正出了问题你还能直接断点到ADS内部去排查,而不用去符号服务器里找PDB。

2.4 编译期的高频报错

编译ADS时,有几个错误属于高频问题,我直接给出来:

  • C++17 standard not supported:ADS源码使用了std::optional等C++17特性,需要指定编译标准。MSVC2019默认比较保守,在.pro里加CONFIG += c++17,CMake里加set(CMAKE_CXX_STANDARD 17)即可。

  • 无法找到 QOpenGLWidgetQOpenGLFunctions相关头文件:ADS的浮动窗口预览用到了OpenGL相关模块,如果Qt安装组件时没勾选OpenGL模块,就会碰到这个问题。去Qt安装器里勾上Qt OpenGL相关组件,或者在.pro里加上QT += opengl widgets

  • ADS_DEBUGADS_RELEASE库混用:如果你先编译了动态库版本,又切到静态版本,构建缓存没清理干净,会出现新旧宏定义冲突。切换编译模式前,老老实实clean一次再做全量重编。

3. 核心概念与最小可用示例:先把DockManager跑起来

ADS里最重要的三个类:

  • CDockManager:整个停靠系统的管理器,类似QMainWindow的角色
  • CDockWidget:被停靠的窗口容器,对应原来的QDockWidget
  • CDockAreaWidget:停靠区域,一个区域可以包含多个标签页

CDockManager用法上跟QMainWindow很像,但它并不继承QMainWindow,而是继承QWidget。所以你需要让主窗口自己继承QMainWindow,然后把一个CDockManager实例设成它的中央部件。

一个最小可运行的示例:

#include "DockManager.h" #include "DockWidget.h" // 主窗口构造函数 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { // 创建DockManager并设置为主窗口的中央部件 ADS::CDockManager::setConfigFlags(ADS::CDockManager::DefaultOpaqueConfig); m_dockManager = new ADS::CDockManager(this); setCentralWidget(m_dockManager); // 创建设备树面板 QWidget *deviceTreeWidget = createDeviceTreeWidget(); // 业务逻辑Widget ADS::CDockWidget *deviceDock = new ADS::CDockWidget("设备树"); deviceDock->setWidget(deviceTreeWidget); deviceDock->setFeature(ADS::CDockWidget::DockWidgetClosable, true); deviceDock->setIcon(QIcon(":/icons/device.png")); // 添加到左侧停靠区域 m_dockManager->addDockWidget(ADS::LeftDockWidgetArea, deviceDock); }

这里重点说说setConfigFlags,它决定整个ADS的交互风格。有几个常用的配置分支:

  • DefaultOpaqueConfig:默认不透明拖拽预览,拖拽时直接用虚线框或透明窗口预览,所见即所得
  • DefaultFlattenConfig:适合多层级嵌套的场景,拖拽时自动把嵌套层级压平
  • DisableTranslucentFloatingWindow:禁用浮动窗口半透明效果,对性能较差的显卡更友好

这些配置可以按位或组合,比如:

ADS::CDockManager::setConfigFlags( ADS::CDockManager::DefaultOpaqueConfig | ADS::CDockManager::FocusHighlighting | ADS::CDockManager::DockAreaHasCloseButton);

FocusHighlighting是我个人很推荐开的选项,它会在Dock获得焦点时给标签页一个高亮边框,在多窗口场景下你能一眼看出当前操作的面板是哪个,不用眯着眼找光标。

4. 进阶使用:布局持久化、侧边栏与多窗口协作

跑通最小示例之后,ADS真正的生产力优势才开始体现出来。

4.1 布局保存与恢复

桌面软件几乎都要求记住用户的面板布局,否则每次启动都重置,用户会抓狂。ADS布局持久化做得极其简单,接口就两个:

// 保存布局 QByteArray layout = m_dockManager->saveState(); QSettings settings; settings.setValue("main_window_layout", layout.toBase64());
// 恢复布局 QSettings settings; QByteArray layout = QByteArray::fromBase64(settings.value("main_window_layout").toByteArray()); m_dockManager->restoreState(layout);

这里有一个非常重要的隐含要求:saveStaterestoreState保存的DockWidget是通过对象名(objectName)来匹配的,所以你在创建CDockWidget必须显式设置objectName,而且不能重复。我早期的版本没设置objectName,结果保存恢复后所有面板全部堆在了同一块区域,完全错乱。

ADS::CDockWidget *deviceDock = new ADS::CDockWidget("设备树"); deviceDock->setObjectName("DeviceTreeDock"); // 必须设置,且保持不变

如果需要针对不同用户保存不同的布局,可以在key里加上用户ID,比如layout_user_1001,互不影响。

4.2 侧边栏与折叠面板

ADS的侧边栏(CDockSideBar)可以做出类似Visual Studio那种图标化的收起面板。用法也很直接:

ADS::CDockWidget *outputDock = new ADS::CDockWidget("输出窗口"); outputDock->setObjectName("OutputDock"); // 添加到底部区域 m_dockManager->addDockWidget(ADS::BottomDockWidgetArea, outputDock); // 将Dock切换为侧边栏模式 m_dockManager->setDockWidgetFloating(outputDock); outputDock->toggleView(true); // 设置侧边栏位置和样式 m_dockManager->sidebar()->setSidebarPosition(ADS::CDockSideBar::SideBarBottom);

侧边栏模式下,窗口默认是隐藏的,点击侧边栏图标才会弹出,鼠标离开后自动收起。这种交互很适合资源面板、图层管理器这类“偶尔用、但不需要一直占着屏幕”的界面。

4.3 嵌套布局与分组

ADS最强的地方是支持复杂的嵌套布局。你可以做到:左侧是一个大的停靠区域,这个区域内部再分上、下两层,每层还能继续分多列。这种结构的操作在运行期直接通过拖拽就能完成,不需要写一行代码,因为ADS的拖拽引擎会自动处理嵌套关系。

但如果你希望启动时直接呈现一个预设好的复杂布局,可以手动add多次,比如:

// 先把设备树放到左侧 m_dockManager->addDockWidget(ADS::LeftDockWidgetArea, deviceDock); // 把属性面板也放到左侧,并且与设备树上下排列 m_dockManager->addDockWidget(ADS::LeftDockWidgetArea, propertyDock, Qt::Vertical); // 把日志面板放到底部 m_dockManager->addDockWidget(ADS::BottomDockWidgetArea, logDock); // 把参数曲线放到右边 m_dockManager->addDockWidget(ADS::RightDockWidgetArea, curveDock);

这里注意addDockWidget的第三个参数Qt::Orientation,它决定了当同一个Area里已经有Dock时,新的Dock是水平方向(Qt::Horizontal)排列还是垂直方向(Qt::Vertical)排列。默认情况下,同区域的Dock会自动合并成标签页,只有指定了排列方向,才会出现上下或左右分割的效果。

这个细节官方文档写得不明显,很容易被忽略,但实际上决定布局手感的关键就在这里。

4.4 跨主窗口拖拽

ADS还支持同一个CDockManager下的浮动窗口再拆分,甚至可以让不同的主窗口共享同一个CDockManager,从而实现跨窗口拖拽。这在多显示器场景下非常实用:用户可以把某个面板拖到第二个显示器浮起来用,也可以在需要时再拖回主窗口停靠。

实现方式是创建多个QMainWindow,但共用同一个CDockManager实例,然后为第二个窗口也setCentralWidget同一个CDockManager。虽然看起来像是把一个控件塞进两个窗口中,ADS内部会处理这种特殊关系。

不过说实话,这个场景的复杂度较高,我目前只在官方示例里体验过,还没有用到实际项目中。如果你确实有这种需求,建议先跑通官方MultiWindow示例再动手。

5. 实战中的坑与我的处理方案

我集成ADS到正式项目的过程中,前后花了大约一周时间,把主要功能框架搭完。这一节把踩过的坑集中列出来,希望能帮你省一些排查时间。

5.1 与QMainWindow原生菜单栏/工具栏的协调问题

这是最让我困惑的一个坑。因为CDockManager继承自QWidget,而不是QMainWindow,所以你仍然需要自己的QMainWindow来承载菜单栏、工具栏和状态栏。但不要再使用QMainWindow::addDockWidget方法去塞任何面板了,否则两者会互相干扰,界面上会出现一种“停靠内容被另外一面板挤压”的错位现象。

解决思路:菜单栏、工具栏、状态栏全部挂在你的QMainWindow上,面板内容全部交给CDockManager管理。中央部件区域彻底交给CDockManager,不要在里面再嵌套其他布局。简单说,QMainWindow当壳,ADS当芯。

5.2 高DPI缩放下的拖拽偏移

我的开发机是4K屏+125%缩放,DPI缩放打开后,ADS的拖拽预览框会出现明显的偏移现象——鼠标已经移动了一段距离,预览框却还停留在原地。

处理办法是在程序入口显式开启高DPI支持:

int main(int argc, char *argv[]) { #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) QApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); #endif QApplication app(argc, argv); // ... }

如果你用的是Qt 6,高DPI已经默认开启,这个坑基本不会再遇到。但Qt 5的用户,建议保留这段代码。

5.3 布局恢复时Dock内容为空

第一次使用restoreState恢复布局时,我发现恢复出来的Dock窗口内容区域一片空白。排查了一下,原因是恢复布局时,对应的CDockWidget还没有来得及setWidget,或者setWidget传入的Widget还没有初始化完成。

解决方案:严格遵循“先创建DockWidget→setWidget→添加到DockManager→再restoreState”的顺序。不要试图在restoreState之后再去补setWidget,这样大概率会出现空窗口。如果实在有复杂的依赖关系,可以等所有Dock都创建并添加完毕之后,再在QTimer::singleShot(0, ...)里执行恢复操作,确保界面事件循环已经处理完一轮。

5.4 关闭按钮的语义与二次确认

默认情况下,CDockWidget右上角的关闭按钮会把Dock关闭(隐藏),但用户并不会知道它还能通过侧边栏或视图菜单重新打开。如果你的软件不希望出现“面板关了找不到”的体验,有两个方案:

  • 去掉关闭按钮:setFeature(ADS::CDockWidget::DockWidgetClosable, false)
  • 保留关闭按钮,但拦截close信号,统一改为隐藏加提示

ADS提供了setCloseHandler的机制(或者重写CDockWidget子类的closeDockWidget),可以自定义关闭行为。我的建议是:核心功能面板(比如设备树)保留关闭按钮但二次确认,辅助面板(比如日志窗口)直接去掉关闭按钮,用侧边栏控制显隐。

5.5 多语言切换与标题刷新

ADS内部的标签页文本直接取自CDockWidget的标题,所以如果你的软件支持中英文切换,必须像刷新业务界面一样刷新Dock的标题,否则UI语言会一半中文一半英文。

我实现了一个简单的广播信号,语言切换后遍历所有CDockWidget,调用setWindowTitlesetTabText刷新标题文本。这里提醒一下,CDockWidget::setWindowTitle会同时影响浮动窗口的标题栏,setTabText才只影响标签页文本,两个方法都要调用。

6. 自定义样式与深色主题适配

Qt的QSS一定程度上可以作用于ADS内部的界面元素,但ADS的结构层次较深,有些控件名称必须通过objectName去精确匹配。分享一下我的深色主题适配思路。

ADS内部的关键控件objectName主要有:

  • DockWidget:单个Dock标签页
  • DockAreaWidget:停靠区域
  • DockManager:总管理器
  • ClosableDockWidget:带关闭按钮的Dock标题栏

常见的QSS配置:

ADS__CDockWidget[objectName="DockWidget"] QLabel { color: #D0D0D0; background: transparent; } ADS__CDockAreaWidget { background: #2D2D30; border: 1px solid #3F3F46; } ADS__CDockWidgetTab { background: #2D2D30; color: #D0D0D0; padding: 4px 8px; } ADS__CDockWidgetTab[activeTab="true"] { background: #007ACC; color: #FFFFFF; }

需要注意的是,ADS的QSS选择器对类名的写法比较严格,通常要带上ADS__前缀,这是Qt的命名空间转换规则。如果你在QSS里写CDockWidget不生效,试试带完整前缀的写法。

另外,拖拽预览窗口、浮动窗口标题栏的QSS样式也是可以自定义的。一个很实用的技巧:在DockWidget的标题栏里嵌入QToolButton,替换默认的关闭、浮动、停靠按钮图标,配合深色UI背景,视觉上更加统一。

我在实际项目中用的主题色就是典型的VS Code深色系背景(#252526),配合#CCCCCC的字体颜色,整体效果很干净。如果用了第三方的QSS主题库,比如QDarkStyleSheet,ADS的样式需要单独补充,因为QDarkStyleSheet没有覆盖ADS的控件名。

7. 我在实际选型时的几个判断维度

最后聊一聊“要不要用ADS”这个问题。网上常常有人问:ADS到底值不值得引入到正式项目里?我的判断维度大致有这几条:

  • 是否重度依赖多面板交互:如果你的软件就是多面板协作型(像IDE、建模工具、数据看板),ADS带来的拖拽自由度和布局记忆能力价值很大
  • 界面自定义需求高不高:ADS的默认样式偏向IDE风格,如果你要做的是一款极简风格工具,用ADS还要额外写不少样式覆盖代码,反而不如原生QWidget布局直接
  • 团队是否愿意接受第三方依赖:ADS是LGPL协议,动态链接下商用很友好;如果你要静态链接,需要注意LGPL要求的重链接义务。团队内部最好提前确认这个合规问题

我在正式项目中使用ADS已经有大半年了,稳定性和性能都没有出现明显短板。偶尔遇到Bug,去GitHub issue搜一下基本能找到解决方案,作者维护频率也比较稳定。

如果你正在做一个面板复杂、停靠需求多的桌面应用,ADS值得你花一两天时间试用一下。先把官方示例跑通,再按我这篇文章的步骤集成到你的项目里,基本可以平稳落地。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 8:35:45

QT翻金币游戏实战:场景动画、绘图优化与打包发布

简介:一份基于QT5的翻金币游戏完整源码包,面向正在学习Qt跨平台开发或想完成课程设计的开发者。项目包含开始界面、菜单界面、关卡选择界面三大模块,涉及QLabel、QPushButton、QStackedWidget、QPropertyAnimation等组件的综合运用&#xff0…

作者头像 李华
网站建设 2026/9/7 8:35:32

IAR工具链提效实战:嵌入式开发效率与生态协作

1. 一次合作背后的行业信号 前阵子圈子里有条消息挺值得玩味:IAR和东软睿驰签了战略合作。做嵌入式工具链的IAR,和做汽车基础软件、自动驾驶方案的东软睿驰走到了一起。很多朋友第一反应是“这两家怎么搭上的”,但干过几年嵌入式软件的人应该…

作者头像 李华
网站建设 2026/9/7 8:35:23

FastColoredTextBox中文修正:彻底解决光标偏移与样式错位

简介:FastColoredTextBox中文修正版V2是一套针对开源高亮代码文本框控件的完整修复源码包,主要面向C#、WinForm开发者,以及需要在项目中集成代码编辑、自定义高亮显示的中高级程序员。该版本在原版基础上重点修复了中文双字节显示异常、光标定…

作者头像 李华
网站建设 2026/9/7 8:35:16

轻量Markdown写作同步与小程序阅读工作流搭建指南

开头先从一个真实场景讲起。前段时间我每天写技术文章的工作流是:电脑上用 Typora 写,写完后用网盘传一份,再通过微信文件传输助手发到手机,晚上躺床上想改稿时,还要在手机里专门找一个 Markdown 阅读器。听起来不算太…

作者头像 李华
网站建设 2026/9/7 8:33:44

Remax实战:用真正的React运行时开发小程序

简介:Remax是一套以真正React语法开发跨端小程序的框架,面向已掌握React、希望将同一套业务代码输出到微信、支付宝、头条等多端小程序的前端工程师,也适合想了解小程序底层运行机制的中高级开发者。该代码包是Remax项目的完整源码&#xff0…

作者头像 李华