跳转到内容
新建笔记

QGraphicsDropShadowEffect:独立实例与效果所有权

一个 QGraphicsDropShadowEffect 实例只能作为一个控件当前安装的图形效果。把同一个效果先交给 filemanager_memeryArea、再交给 workspaceWidget,第二次调用会把效果从前一个控件移走。要让两个区域同时显示阴影,应创建两个实例,并复用创建和配置它们的函数。

本文使用 Qt 6 Widgets、C++17。原例的参数是偏移 (5, 5)、颜色 QColor(43, 43, 43)、模糊半径 8;下面完整保留这些值和两个区域名称。

安装效果会转移所有权

跳转到“安装效果会转移所有权”

QWidget::setGraphicsEffect() 有三个需要一起理解的行为:控件接管效果;从其他控件传入同一效果时会迁移效果;给已有不同效果的控件安装新效果时,会删除旧效果。不能继续使用指向被替换效果的普通指针。setGraphicsEffect

工厂每次 new 一个效果,返回供本次安装使用的独立对象。这减少了重复配置代码,但不会把多个控件的绘制成本合并成一次,也不是已经测得的性能优化。不要让工厂缓存一个共享效果实例。

main.cpp
#include <QApplication>
#include <QColor>
#include <QFrame>
#include <QGraphicsDropShadowEffect>
#include <QHBoxLayout>
#include <QLabel>
#include <QVBoxLayout>
#include <QWidget>
QGraphicsDropShadowEffect* createShadowEffect() {
auto* shadow = new QGraphicsDropShadowEffect;
shadow->setOffset(5, 5);
shadow->setColor(QColor(43, 43, 43));
shadow->setBlurRadius(8);
return shadow;
}
class ShadowWindow : public QWidget {
public:
explicit ShadowWindow(QWidget* parent = nullptr) : QWidget(parent) {
auto* layout = new QHBoxLayout(this);
layout->setContentsMargins(24, 24, 24, 24);
layout->setSpacing(24);
filemanager_memeryArea = createPanel("Memory area", this);
filemanager_memeryArea->setObjectName("filemanager_memeryArea");
workspaceWidget = createPanel("Workspace", this);
workspaceWidget->setObjectName("workspaceWidget");
layout->addWidget(filemanager_memeryArea);
layout->addWidget(workspaceWidget);
filemanager_memeryArea->setGraphicsEffect(createShadowEffect());
workspaceWidget->setGraphicsEffect(createShadowEffect());
}
QFrame* filemanager_memeryArea;
QFrame* workspaceWidget;
private:
static QFrame* createPanel(const QString& text, QWidget* parent) {
auto* panel = new QFrame(parent);
panel->setMinimumSize(160, 100);
panel->setStyleSheet("QFrame { background: white; border: 1px solid #999; }");
auto* layout = new QVBoxLayout(panel);
auto* label = new QLabel(text, panel);
label->setAlignment(Qt::AlignCenter);
label->setStyleSheet("border: none; background: transparent;");
layout->addWidget(label);
return panel;
}
};
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
ShadowWindow window;
window.setWindowTitle("Independent widget shadows");
window.resize(460, 180);
window.show();
return app.exec();
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(widget_shadows LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
find_package(Qt6 REQUIRED COMPONENTS Widgets)
add_executable(shadow-example main.cpp)
target_link_libraries(shadow-example PRIVATE Qt6::Widgets)

保存两个文件到同一目录后构建运行,会显示两个分别带阴影的区域。效果创建时暂不指定 QObject 父对象,随即交给目标控件接管,避免把“创建它的窗口”和“安装它的控件”混为同一份释放责任。父窗口销毁两个区域时,已安装的效果也会释放。

控件的图形效果作用于它自身以及子控件的合成结果。给整个容器加阴影,与分别给每个子控件加阴影,得到的形状不同。如果需要多种效果,单个控件的 setGraphicsEffect() 也不是一个可以反复追加的效果列表。

阴影会扩展到控件本体外侧,布局不会自动为全部阴影预留空间。示例用内容边距和间距留出绘制余量;嵌入狭窄容器、滚动视口或截图时,还要检查裁剪边界。模糊半径和面积越大,通常需要更多绘制工作,具体性能应在目标环境测量。QGraphicsDropShadowEffect

该机制有平台与渲染路径边界,官方文档明确列出 QOpenGLWidget、QQuickWidget 等控件不支持这种 Widgets 图形效果,不应靠给这些控件重复安装效果解决问题。普通背景和边框样式见QSS 自定义背景。