尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Qt控件叠加显示:从布局到QGraphicsView的实战指南

Qt控件叠加显示:从布局到QGraphicsView的实战指南 1. 项目概述为什么需要控件叠加显示在桌面应用开发中尤其是使用Qt这样的成熟框架时我们经常会遇到一个看似简单却暗藏玄机的需求如何让多个控件在同一区域“和平共处”地显示比如你想在一个视频播放窗口上叠加一个半透明的控制面板或者在图表上动态显示实时数据标签又或者实现一个复杂的、由多层元素构成的仪表盘。这就是“控件叠加显示”要解决的核心问题。新手开发者最容易掉入的坑是直接把一个按钮放在另一个控件上结果发现底下的控件“消失”了或者鼠标事件变得混乱不堪。这背后的根本原因在于Qt默认的布局和父子窗口机制。一个控件QWidget通常占据一个矩形区域当子控件被添加到父控件时默认行为是子控件在父控件的坐标系内占据自己的位置如果位置重叠后添加的或特定Z序的控件会遮挡住其他控件。要实现灵活、可控的叠加我们需要跳出默认的“流式布局”思维深入到Qt的窗口系统、事件处理和绘图机制中去。我接手过不少从其他GUI框架如WinForms、MFC转过来的项目发现大家最初对Qt的叠加显示都会感到不适应。其实一旦掌握了正确的方法论和几个核心类你会发现Qt在这方面提供了极其强大和灵活的解决方案。本文将围绕“控件叠加显示”这一主题拆解其核心场景、技术选型、具体实现以及那些官方手册里不会写的“踩坑”实录。2. 核心场景与技术方案选型在动手写代码之前搞清楚“要在什么场景下、实现什么样的叠加”至关重要。不同的场景对应的最佳技术方案也截然不同。盲目选型后期可能会在性能、可维护性上付出巨大代价。2.1 常见叠加场景分析我们可以把常见的控件叠加需求分为以下几类浮动层叠加这是最常见的需求。例如视频播放器的控制栏播放/暂停、进度条、图片编辑器的工具栏、地图应用上的信息气泡。这些层需要悬浮在主内容之上位置相对固定或可跟随鼠标并且通常需要一定的透明度。装饰性叠加比如在按钮上显示一个角标“New” “Hot”在输入框右侧添加一个清空图标或者在表格单元格内叠加一个加载动画。这类叠加元素尺寸小与主控件绑定紧密更像是主控件的一个“装饰部件”。动态数据叠加在数据可视化场景中尤为普遍。例如在折线图QChart的某个数据点上悬停显示详细数值在波形图QCustomPlot或自绘上实时绘制光标和测量线。这类叠加需要极高的刷新效率和精准的坐标映射。复合控件构建有时为了创造一个新的、功能复杂的控件我们需要将多个基础控件如QLabel、QLineEdit、QPushButton组合并叠加在一起形成一个逻辑整体。例如一个搜索框QLineEdit 右侧的搜索图标按钮。2.2 核心方案对比与选型针对以上场景Qt主要提供了以下几种技术路径。选择哪一种取决于你的具体需求和对复杂度、性能的权衡。方案核心类/机制适用场景优点缺点与注意事项布局与提升法QStackedLayout,QWidget::raise()/lower(), 提升Z序简单的内容切换或少量静态叠加层。实现简单利用现有布局体系适合动态切换显示不同“页”的场景。不适合复杂、动态的叠加。所有子控件仍需在父控件坐标系内且同时只能有一个控件处于“顶层”可见对于QStackedLayout。直接调整Z序在控件数量多时管理困难。重叠布局法自定义布局或使用QGridLayout等设置重叠位置需要精确控制多个控件相对位置的静态叠加。比QStackedLayout更灵活可以同时显示多个叠加控件。需要手动计算和设置几何位置对动态调整窗口大小或内容变化不友好容易布局错乱。QGraphicsView架构QGraphicsScene,QGraphicsItem,QGraphicsProxyWidget复杂的、需要大量自定义绘图、动画、碰撞检测的2D场景。如游戏UI、CAD软件、复杂图表。功能极其强大内置Z值管理、坐标变换、动画框架、事件传播体系。性能优化好适合大量图形项。学习曲线陡峭架构较重。将传统QWidget嵌入为QGraphicsProxyWidget会有一定的性能损耗和功能限制。事件过滤器与自绘QObject::installEventFilter(),paintEvent()轻量级的装饰性叠加或需要完全自定义外观和行为。如绘制角标、自定义焦点框。极其轻量不引入新的控件对象性能最佳。与主控件融合度最高。对开发者要求高需要深入理解Qt事件流和绘图系统。代码复杂度集中在主控件内可能破坏其单一职责。独立窗口法无边框窗口 (Qt::FramelessWindowHint) 设置父子关系需要跨出父窗口边界显示或需要被操作系统单独管理的顶级浮动面板如工具窗。不受父窗口裁剪区域限制可以显示在任何位置。窗口管理由系统负责。同步移动、状态管理显示/隐藏变得复杂。失去与父控件在Qt层面的自动事件关联。选型心法 对于大多数浮动层叠加如视频控制栏我首推重叠布局法或QGraphicsView架构。如果叠加层结构简单、数量固定用重叠布局配合setAttribute(Qt::WA_TransparentForMouseEvents)处理事件穿透是性价比最高的方案。如果叠加元素多、有动画、需要旋转缩放那么QGraphicsView是不二之选。 对于装饰性叠加事件过滤器与自绘是王道它保持了控件的完整性且没有额外的内存开销。 对于动态数据叠加如果基于QChart可以利用其自带的QGraphicsScenechart-scene()来添加自定义的QGraphicsItem。如果是自绘波形图则在paintEvent中直接绘制是最高效的。 对于复合控件通常使用布局管理器组合或创建一个新的QWidget子类在内部用布局管理子控件这不算严格意义上的“叠加”而是“组合”。3. 实战使用重叠布局法实现视频控制栏让我们从一个最经典的例子入手实现一个视频播放器并在视频画面上叠加一个半透明的、可自动隐藏的控制栏。我们将采用“重叠布局法”因为它直观且能很好地诠释叠加的核心概念。3.1 界面与控件结构设计首先我们设计一个简单的播放器主窗口。核心区域是一个用于显示视频的QWidget或QVideoWidget我们称之为videoContainer。控制栏controlBar将作为一个独立的QWidget叠加在videoContainer之上。// PlayerWindow.h 概要 class PlayerWindow : public QWidget { Q_OBJECT public: PlayerWindow(QWidget *parent nullptr); private: // 视频显示容器 QWidget *videoContainer; // 叠加的控制栏 QWidget *controlBar; // 用于控制布局的容器 QWidget *overlayContainer; void setupUI(); void setupControlBar(); };关键点在于overlayContainer。我们将创建一个专门的QWidget作为videoContainer的父控件同时controlBar也以overlayContainer为父控件。这样videoContainer和controlBar就成为了兄弟控件它们拥有共同的父控件坐标系为叠加创造了条件。3.2 核心布局与叠加实现在setupUI()函数中我们进行如下布局void PlayerWindow::setupUI() { // 1. 创建叠加容器设置其背景透明以便videoContainer能透出来 overlayContainer new QWidget(this); overlayContainer-setStyleSheet(background-color: transparent;); // 2. 创建视频容器放入叠加容器中 videoContainer new QWidget(overlayContainer); // 父对象是overlayContainer videoContainer-setStyleSheet(background-color: black;); // 用黑色模拟视频 // 3. 创建控制栏同样放入叠加容器中与videoContainer成为兄弟 controlBar new QWidget(overlayContainer); setupControlBar(); // 初始化控制栏内部按钮、滑块等 controlBar-setStyleSheet(background-color: rgba(0, 0, 0, 150); border-radius: 4px;); // 半透明黑色背景 // 4. 使用QVBoxLayout管理overlayContainer在主窗口中的位置例如充满中央区域 QVBoxLayout *mainLayout new QVBoxLayout(this); mainLayout-addWidget(overlayContainer); mainLayout-setContentsMargins(0, 0, 0, 0); // !!! 核心步骤手动设置控件几何位置实现叠加 !!! // 当overlayContainer大小变化时需要同步调整videoContainer和controlBar的大小位置 // 我们通过重写overlayContainer的resizeEvent来实现 }这里最关键的技巧是放弃使用Qt的自动布局来管理videoContainer和controlBar的相对位置。因为自动布局如QVBoxLayout的设计目标是避免重叠。我们需要手动控制它们的几何形状。我们重写overlayContainer的resizeEvent// 在PlayerWindow类中声明 protected: void resizeEvent(QResizeEvent *event) override; // 实现 void PlayerWindow::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); // 调用基类处理 if (overlayContainer) { // videoContainer充满整个叠加容器 videoContainer-setGeometry(overlayContainer-rect()); // controlBar定位在底部留一些边距 int barHeight 50; int leftMargin 10; int rightMargin 10; int bottomMargin 10; QRect barRect(leftMargin, overlayContainer-height() - barHeight - bottomMargin, overlayContainer-width() - leftMargin - rightMargin, barHeight); controlBar-setGeometry(barRect); } }现在controlBar已经成功地叠加在videoContainer之上了。由于我们后创建controlBar或者默认情况下后添加的子控件Z序更高所以控制栏会显示在视频上面。3.3 事件处理与自动隐藏叠加之后鼠标事件的处理成了新问题。我们希望鼠标点击控制栏上的按钮事件应该被控制栏处理。鼠标点击控制栏之外的视频区域事件应该能穿透控制栏被底层的videoContainer或未来用于接收点击播放/暂停的区域处理。控制栏能够在一段时间无操作后自动隐藏鼠标移入播放器区域时又显示。实现事件穿透 Qt提供了WA_TransparentForMouseEvents属性。为controlBar设置这个属性后所有鼠标事件都会穿透它传递给下面的控件。但这不符合我们的需求因为我们需要点击按钮。所以我们不能对整个控制栏设置穿透。正确的做法是仅对控制栏的背景部分设置事件穿透而对上面的按钮等子控件保持正常事件响应。这可以通过为控制栏设置一个自定义的eventFilter来实现判断鼠标点击的位置是否在子控件上如果不是则忽略该事件。但更简单的方法是确保控制栏的背景部件本身不处理鼠标事件而让它的子控件去处理。我们可以通过样式表或setAttribute(Qt::WA_TransparentForMouseEvents)给controlBar设置但这样其所有子控件也会失效。一个更鲁棒的方法是让controlBar继承自QWidget并重写mousePressEvent等如果不希望处理就调用ignore()让事件继续传递。但这种方法比较繁琐。一个工程上更常见的实践是不处理穿透而是扩大视频容器的可点击区域。我们可以在videoContainer上安装一个事件过滤器监听整个overlayContainer的鼠标事件然后根据鼠标位置决定是触发视频控制如点击暂停还是忽略当鼠标在控制栏按钮上时。这需要对Qt事件系统有较深理解。为了简化本例采用另一种思路将控制栏的透明度设置得足够高并且只在特定区域如底部显示视频中间的大部分区域仍然可以直接接收事件。这通过我们上面计算的barRect已经实现了。实现自动隐藏 我们需要一个定时器QTimer和鼠标追踪。// PlayerWindow类内 private slots: void hideControlBar(); void onMouseActivity(); private: QTimer *autoHideTimer; // 初始化 autoHideTimer new QTimer(this); autoHideTimer-setInterval(3000); // 3秒后隐藏 connect(autoHideTimer, QTimer::timeout, this, PlayerWindow::hideControlBar); // 开启鼠标追踪以便检测鼠标移动 setMouseTracking(true); overlayContainer-setMouseTracking(true); videoContainer-setMouseTracking(true); controlBar-setMouseTracking(true); // 在PlayerWindow的mouseMoveEvent中 void PlayerWindow::mouseMoveEvent(QMouseEvent *event) { // 重置定时器并显示控制栏 controlBar-show(); autoHideTimer-start(); QWidget::mouseMoveEvent(event); } void PlayerWindow::hideControlBar() { // 检查鼠标是否真的离开了控制区域这里简化处理 controlBar-hide(); }实操心得在实现自动隐藏时一个常见的坑是鼠标移动到控制栏上的按钮时由于触发了按钮的enterEvent导致PlayerWindow的mouseMoveEvent可能接收不到信号从而错误地隐藏了控制栏。更健壮的做法是在controlBar及其所有子控件上也安装事件过滤器统一重置隐藏定时器。或者使用QApplication::setGlobalEventFilter来全局监控鼠标活动但这要谨慎使用避免性能问题。4. 进阶使用QGraphicsView架构构建复杂叠加界面当你的叠加需求超出简单的浮动条涉及到成百上千个可交互、可动画的图形元素时比如一个网络拓扑图编辑器节点上需要叠加状态图标、标签、连接点QGraphicsView架构就是你的“瑞士军刀”。4.1 QGraphicsView核心概念速览QGraphicsView架构是一个基于图形项的模型-视图模式。QGraphicsScene场景一个容器管理所有QGraphicsItem。它定义了场景的边界并负责事件传播给其中的项。QGraphicsItem图形项场景中的基本元素可以是矩形、椭圆、文本、图片甚至是嵌入的QWidget通过QGraphicsProxyWidget。每个项都有自己的坐标、Z值、旋转、缩放等属性。Z值setZValue()直接决定了叠加的上下关系值大的在上层。QGraphicsView视图用于将场景可视化的小部件。你可以有多个视图观察同一个场景的不同部分。4.2 实战创建一个可叠加图标的节点图假设我们要创建一个简单的流程图每个节点矩形上可以叠加一个表示状态的图标如运行中、错误。// NodeItem.h #include QGraphicsRectItem #include QGraphicsPixmapItem class NodeItem : public QObject, public QGraphicsRectItem { Q_OBJECT public: NodeItem(const QString name, QGraphicsItem *parent nullptr); void setStatus(Status status); // 状态改变时更新叠加的图标 private: QString m_name; QGraphicsTextItem *m_textItem; // 节点名称文本项 QGraphicsPixmapItem *m_statusIconItem; // 状态图标项 Status m_currentStatus; }; // NodeItem.cpp NodeItem::NodeItem(const QString name, QGraphicsItem *parent) : QGraphicsRectItem(0, 0, 100, 60, parent) , m_name(name) { // 1. 设置节点矩形样式 setBrush(QBrush(Qt::lightGray)); setPen(QPen(Qt::black, 2)); // 2. 创建并叠加文本项显示节点名 m_textItem new QGraphicsTextItem(m_name, this); // ‘this’作为父项建立父子关系 m_textItem-setDefaultTextColor(Qt::black); // 将文本居中于矩形内 QRectF textRect m_textItem-boundingRect(); m_textItem-setPos((rect().width() - textRect.width()) / 2, (rect().height() - textRect.height()) / 2); // 3. 创建状态图标项初始隐藏 QPixmap defaultIcon(:/icons/default.png); // 从资源文件加载 m_statusIconItem new QGraphicsPixmapItem(defaultIcon, this); // 将图标放置在矩形的右上角 m_statusIconItem-setPos(rect().width() - m_statusIconItem-boundingRect().width() - 5, 5); m_statusIconItem-setZValue(1); // !!! 关键确保图标显示在文本和矩形背景之上 !!! m_statusIconItem-hide(); // 4. 设置节点本身可被选中和移动 setFlag(QGraphicsItem::ItemIsSelectable, true); setFlag(QGraphicsItem::ItemIsMovable, true); } void NodeItem::setStatus(Status status) { if (m_currentStatus ! status) { m_currentStatus status; QPixmap iconPixmap; switch(status) { case Status::Running: iconPixmap.load(:/icons/running.png); break; case Status::Error: iconPixmap.load(:/icons/error.png); break; default: iconPixmap.load(:/icons/default.png); break; } m_statusIconItem-setPixmap(iconPixmap); m_statusIconItem-setVisible(status ! Status::Normal); } }代码解析与技巧父子关系m_textItem和m_statusIconItem在构造时以this即NodeItem本身作为父QGraphicsItem。这建立了图形项树。子项的位置pos()是相对于父项的局部坐标。当父项节点移动时所有子项会自动跟随移动这极大地简化了管理。Z值管理m_statusIconItem-setZValue(1)是叠加显示的关键。在同一个父项下Z值较高的项会绘制在Z值较低的项之上。这里确保图标盖在文本和矩形背景上。如果不设置绘制顺序可能由添加顺序决定导致不可预测的遮挡。坐标系统setPos()使用的是父项节点矩形的局部坐标系。例如setPos(rect().width() - ..., 5)是将图标的左上角定位在距节点矩形右边框一定距离、上边框5像素的位置。性能考虑QGraphicsView对于大量静态项有很好的性能。但如果图标需要频繁更新如旋转的加载动画应考虑使用QGraphicsItem的paint()方法自绘而不是频繁创建和销毁QGraphicsPixmapItem。对于动画可以使用QPropertyAnimation与图形项的属性如pos,rotation结合。4.3 将传统QWidget嵌入场景有时我们不得不在图形场景中使用一个复杂的传统控件比如一个QLineEdit用于节点重命名。这时就需要QGraphicsProxyWidget。// 在NodeItem的某个交互函数中如双击事件 void NodeItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) { // 创建一个行编辑框 QLineEdit *lineEdit new QLineEdit(m_name); lineEdit-selectAll(); // 全选文本方便修改 // 创建代理部件将QWidget嵌入场景 QGraphicsProxyWidget *proxy scene()-addWidget(lineEdit); // 将代理项设置为当前节点的子项并定位到文本位置 proxy-setParentItem(this); proxy-setPos(m_textItem-pos()); proxy-setZValue(10); // 确保在最顶层 // 连接编辑完成的信号 connect(lineEdit, QLineEdit::editingFinished, this, [this, lineEdit, proxy](){ m_name lineEdit-text(); m_textItem-setPlainText(m_name); // 清理代理和控件 scene()-removeItem(proxy); delete lineEdit; // proxy会被自动删除吗注意这里有个大坑。 }); // 设置焦点让用户可以立即输入 lineEdit-setFocus(); QGraphicsRectItem::mouseDoubleClickEvent(event); }踩坑实录QGraphicsProxyWidget的内存管理是个陷阱。在上面的代码中proxy由scene()-addWidget()创建其所有权ownership转移给了场景。当我们调用scene()-removeItem(proxy)时代理项从场景中移除但默认情况下Qt不会自动删除它。更糟糕的是我们手动delete lineEdit后代理项内部持有的QWidget指针就变成了野指针可能导致程序崩溃。正确的做法是让代理项管理QWidget的生命周期QGraphicsProxyWidget会在其析构时自动删除其托管的QWidget。所以我们通常不应该手动删除lineEdit。使用deleteLater()安全删除proxy-deleteLater();。这会安全地删除代理项及其托管的部件。或者更简洁地直接调用delete proxy;。因为proxy是QObject其父对象是场景但我们已经将其从场景移除所以手动删除是安全的。删除proxy会自动删除lineEdit。 我推荐的修改是将delete lineEdit;替换为proxy-deleteLater();。或者在Lambda表达式捕获列表中加入proxy并直接delete proxy。5. 避坑指南与性能优化在实际项目中实现控件叠加除了功能实现还有一大堆坑等着你。下面是我总结的几个关键问题和优化建议。5.1 事件冲突与穿透这是叠加显示中最头疼的问题之一。当多个可交互控件在空间上重叠时鼠标点击、悬停等事件应该由谁处理问题场景底层是一个可拖拽的背景图或地图上层叠加了一些可点击的标记点。你希望点击标记点时触发标记点的逻辑点击空白背景时触发拖拽。解决方案精细化事件过滤在顶层窗口或视图安装事件过滤器分析鼠标事件的globalPos()或映射后的场景坐标。判断该位置下有哪些图形项使用QGraphicsView::items()或QGraphicsScene::items()然后根据你的业务逻辑决定是否拦截或转发事件。使用QGraphicsItem的acceptHoverEvents和setAcceptedMouseButtons对于不需要响应的底层项可以关闭鼠标事件接收。例如背景图只响应右键菜单可以setAcceptedMouseButtons(Qt::RightButton)。itemAt()与事件转发在QGraphicsView的mousePressEvent中使用itemAt(event-pos())获取最顶层的项。如果不是你想要的项可以调用QGraphicsView::mousePressEvent(event)将事件传递给视图/场景进行默认处理或者手动调用底层项的鼠标事件处理函数这需要类型转换和谨慎设计。WA_TransparentForMouseEvents属性如前所述这是一个“一刀切”的方案慎用。通常只用于完全透明的、仅用于视觉装饰的覆盖层。5.2 刷新与性能问题叠加层特别是动态变化的叠加层如果处理不当很容易引起界面卡顿和闪烁。问题1局部刷新失效导致整个窗口重绘默认情况下一个QWidget的update()会标记整个区域为脏区域导致重绘。对于叠加在视频上的控制栏如果控制栏频繁变化如进度条更新会导致底层视频也被迫重绘消耗大量CPU。优化策略启用局部更新对于自绘控件在paintEvent中只绘制需要更新的部分。使用update(const QRect )或update(const QRegion )来指定脏区域。对于QGraphicsView确保QGraphicsView::setViewportUpdateMode()设置为QGraphicsView::MinimalViewportUpdate或SmartViewportUpdate。这些模式会尝试只重绘发生变化的部分场景区域。双缓冲对于复杂的自绘叠加考虑使用QWidget::setAttribute(Qt::WA_PaintOnScreen)或WA_OpaquePaintEvent但这对初学者难度较大。更简单的是在paintEvent中先将内容绘制到一个QPixmap缓存中然后再将缓存绘制到设备上但这通常不是性能瓶颈的首选解决方案。问题2过多叠加项导致QGraphicsScene卡顿当场景中有成千上万个QGraphicsItem时即使它们不可见场景的遍历、碰撞检测等操作也会消耗资源。优化策略使用QGraphicsItem::ItemIgnoresTransformations对于始终需要保持屏幕大小不随视图缩放而缩放的项如文本标签设置此标志可以避免视图缩放时昂贵的矩阵运算。层级管理与裁剪将不需要显示的项通过setVisible(false)隐藏或者使用QGraphicsItemGroup管理需要时再显示。对于视图外的项QGraphicsView默认会进行裁剪但确保你的视图视口viewport大小合理。简化项的形状QGraphicsItem的shape()和boundingRect()用于碰撞检测和绘制区域计算。对于复杂形状返回一个近似的、更简单的形状如矩形可以大幅提升性能。考虑使用QOpenGLWidget作为QGraphicsView的视口对于极其复杂的动态场景使用OpenGL后端进行渲染可以极大提升性能。通过QGraphicsView::setViewport(new QOpenGLWidget)即可实现。5.3 坐标映射的坑叠加显示中坐标转换错误是导致元素错位的常见原因。你有至少三个坐标系窗口坐标、父控件坐标、场景坐标、图形项局部坐标。黄金法则始终明确你当前所在的坐标系并使用正确的API进行转换。QWidget::mapToGlobal(),mapToParent(),mapFromGlobal()QGraphicsItem::mapToScene(),mapFromScene(),mapToParent(),mapFromParent()QGraphicsView::mapToScene(),mapFromScene()一个典型场景在QGraphicsView的视口上点击想获取场景中对应位置的项。void MyGraphicsView::mousePressEvent(QMouseEvent *event) { QPoint viewportPos event-pos(); // 视图窗口坐标 QPointF scenePos mapToScene(viewportPos); // 转换为场景坐标 QGraphicsItem *item scene()-itemAt(scenePos, QTransform()); // 获取场景坐标下的项 // ... 后续处理 }如果忘记mapToScene直接使用event-pos()作为场景坐标结果将是完全错误的。6. 总结与扩展思路控件叠加显示是Qt GUI开发中一项提升界面表现力和交互性的关键技术。从简单的浮动工具栏到复杂的图形编辑界面其核心思想都是对Qt窗口系统、事件机制和绘图管线的深入理解和灵活运用。回顾一下核心要点明确需求对号入座静态浮动条用重叠布局复杂交互图形用QGraphicsView轻量装饰用事件过滤和自绘。掌握核心类QWidget的几何属性与Z序、QGraphicsScene/Item/View的三件套、QGraphicsProxyWidget的桥梁作用。精细控制事件流理解事件的传递路径善用事件过滤器、accept()/ignore()、以及WA_TransparentForMouseEvents等工具解决事件冲突。警惕性能陷阱注意局部刷新管理好QGraphicsScene中项的数量和复杂度在需要时考虑OpenGL加速。扩展思路与现代Qt Quick/QML结合对于全新的项目Qt QuickQML在声明式UI、动画和视觉效果方面更加强大和便捷。在QML中叠加是天然的使用Item的z属性并且性能通常很好。你甚至可以在传统的QWidget应用中嵌入QQuickWidget来展示复杂的叠加UI。使用样式表QSS实现视觉叠加对于一些简单的颜色覆盖、边框效果可以尝试用层叠样式表来实现减少自定义控件的复杂度。例如通过border、background的叠加和伪状态:hover来创造视觉层次。探索QPainter的合成模式Composition Modes在自绘时QPainter::setCompositionMode()可以实现诸如叠加、变亮、变暗等Photoshop般的图层混合效果为自定义叠加绘制带来更多创意空间。最后调试叠加问题最实用的工具是Qt Creator内置的调试器以及QWidget的show()/hide()、setStyleSheet(border: 1px solid red;)来可视化控件边界。当你搞不清谁挡在谁前面时给它们都画个红色边框一切就一目了然了。
返回列表