2026/10/14 7:29:25

Qt5.9实战:复刻360安全卫士UI界面完整教程

Qt5.9实战:复刻360安全卫士UI界面完整教程 简介这份源码资源面向具备一定C基础的Qt初学者与界面开发爱好者围绕Qt 5.9框架完整实现了一套仿360安全卫士风格的桌面UI可用于学习主窗口搭建、控件布局与交互逻辑。压缩包共39个文件约2.98MB以28张png图片和1张jpg作为界面素材配合cpp与h源文件、pro工程文件、ui设计文件、qrc资源文件及qss样式表构成可直接编译运行的完整工程。内容涉及QMainWindow主窗口、QWidgets与QLayouts布局管理、自定义控件、信号与槽机制、Qt Designer可视化设计以及QPropertyAnimation动画效果等关键知识点并预留网络通信与数据模型视图的扩展空间。目前已有1730人学习下载适合通过阅读源码理解界面分层结构、样式美化与事件驱动编程快速积累Qt桌面项目实战经验。1. 用 Qt5.9 复刻 360 安全卫士主界面从标题到可运行工程很多人第一次看到「Qt5.9实例—制作360UI界面源码」这个标题会以为它只是把一张截图贴到窗口上或者用 QSS 随便糊几个按钮。真动手才发现360 安全卫士那套界面里藏着不少 Qt 桌面开发的基本功无边框窗口、自定义标题栏、左侧导航与右侧堆叠页联动、圆形头像与异形按钮、渐变背景和阴影。Qt5.9 是 LTS 版本QWidget、QSS、QStackedWidget、QPainter 这些模块都足够稳定拿它练手比追新版本更省心。这篇笔记面向的是想用 Qt Widgets 做一套「看起来像商业软件」的桌面 UI 的开发者尤其是刚学完信号槽、还没做过完整界面的人。下面按「先搭骨架、再填皮肤、最后调细节」的顺序把可复现的步骤和参数讲清楚。2. 先定架构QWidget 还是 QML无边框窗口怎么搭2.1 为什么这个界面选 QWidget 而不是 QML360 这类界面本质是「固定布局 大量自定义绘制 少量动画」不是数据驱动的动态列表。QWidget 配合 QSS 和 QPainter在 Qt5.9 上做圆角、渐变、阴影的成熟度比 QML 更稳而且网上能查到的老项目几乎都是 QWidget 路线遇到问题好搜。QML 当然也能做但 Qt5.9 的 Qt Quick Controls 2 在 Windows 上默认样式偏移动端改起来反而绕。常见做法是主窗口用 QWidget局部需要动画的地方用 QPropertyAnimation 补不引入 QML 引擎。选型确定后工程结构建议拆成三层main.cpp只负责启动widget.h/cpp是主窗口管无边框、拖动、导航切换titlebar.h/cpp、navbutton.h/cpp这类自定义控件单独成文件。这样后面改皮肤不会牵一发动全身。2.2 无边框窗口的最小可用代码Qt5.9 里去掉系统标题栏靠Qt::FramelessWindowHint但去掉之后窗口不能拖动、不能缩放必须自己补。下面是最小实现// widget.cpp #include widget.h #include QApplication #include QMouseEvent #include QPainter #include QPainterPath Widget::Widget(QWidget *parent) : QWidget(parent) { // 关键1去掉系统边框同时保留最小化等能力需自行实现 setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint); // 关键2开启背景透明否则圆角外会露出黑色直角 setAttribute(Qt::WA_TranslucentBackground); resize(900, 600); m_draging false; } // 鼠标按下记录起点用于拖动 void Widget::mousePressEvent(QMouseEvent *e) { if (e-button() Qt::LeftButton) { m_draging true; m_startPos e-globalPos() - frameGeometry().topLeft(); e-accept(); } } void Widget::mouseMoveEvent(QMouseEvent *e) { if (m_draging (e-buttons() Qt::LeftButton)) { move(e-globalPos() - m_startPos); e-accept(); } } void Widget::mouseReleaseEvent(QMouseEvent *e) { m_draging false; QWidget::mouseReleaseEvent(e); } // 自绘圆角背景替代 QSS 的 border-radius顶层窗口 QSS 圆角常失效 void Widget::paintEvent(QPaintEvent *) { QPainter p(this); p.setRenderHint(QPainter::Antialiasing); QPainterPath path; path.addRoundedRect(rect(), 6, 6); p.fillPath(path, QColor(245, 247, 250)); }逻辑说明WA_TranslucentBackground必须和自绘背景一起用只设透明不画背景窗口会变成全透明看不见。拖动逻辑用globalPos而不是pos否则窗口移动时坐标会抖。参数上圆角半径 6 是 360 主界面比较接近的值改大到 12 会显得太圆改到 0 就退回直角。提示如果发现窗口拖动时闪烁检查是否在mouseMoveEvent里调用了update()拖动阶段不需要重绘。2.3 自定义标题栏与导航按钮的落点标题栏单独做成TitleBar类里面放 logo、标题文字、最小化/关闭按钮。关闭按钮不要直接close()先发信号给主窗口方便以后加「最小化到托盘」逻辑。导航按钮用QPushButton派生重写paintEvent画选中态左侧的蓝色竖条比用 QSS 的border-left更可控。// navbutton.cpp 片段选中态左侧竖条 void NavButton::paintEvent(QPaintEvent *) { QPainter p(this); p.setRenderHint(QPainter::Antialiasing); if (m_checked) { p.fillRect(QRect(0, 8, 3, height() - 16), QColor(0, 160, 233)); } p.drawText(rect().adjusted(24, 0, 0, 0), Qt::AlignVCenter | Qt::AlignLeft, text()); }这里竖条高度用height() - 16而不是撑满是为了上下留白视觉上更接近原版。文字左边距 24 是配合 20 像素图标的位置图标用QIcon在paintEvent里drawPixmap画不要用setIcon否则选中态换色不好控制。3. 把界面填满QSS 皮肤、QStackedWidget 联动与资源组织3.1 QSS 写在哪、怎么避免全局污染QSS 有两种加载方式写进.qrc用QFile读或者直接字符串。工程里推荐前者方便改。但要注意qApp-setStyleSheet()是全局的会影响到所有控件包括你不希望被改的QMessageBox。更稳的做法是给主窗口setStyleSheet让样式只作用于子树。// main.cpp 片段 QFile f(:/style/main.qss); if (f.open(QFile::ReadOnly)) { QString qss QString::fromUtf8(f.readAll()); w.setStyleSheet(qss); // 只作用于主窗口及其子控件 }参数说明fromUtf8不能省QSS 里有中文注释时用fromLocal8Bit在部分环境会乱码。资源文件里图片建议按icons/、bg/、style/分目录.qrc前缀统一用/引用时写:/icons/nav_scan.png。3.2 QStackedWidget 做右侧页面切换左侧导航点一下右侧换一页这是最典型的QStackedWidget用法。关键是把「按钮索引」和「页面索引」绑定别用if-else堆。// widget.cpp 片段导航与堆叠页联动 connect(m_navGroup, QOverloadint::of(QButtonGroup::buttonClicked), this, [this](int id){ m_stack-setCurrentIndex(id); // 同步更新按钮选中态 for (int i 0; i m_navGroup-buttons().size(); i) { auto *btn qobject_castNavButton*(m_navGroup-button(i)); if (btn) btn-setChecked(i id); } });逻辑说明QButtonGroup的id从 0 开始按添加顺序分配所以添加导航按钮的顺序必须和addWidget到m_stack的顺序一致否则点「查杀」跳到「优化」。这是新手最容易翻车的地方。参数上setCurrentIndex切换是瞬时的如果想要淡入效果可以套QGraphicsOpacityEffect加QPropertyAnimation但 Qt5.9 上对QStackedWidget整体做透明度动画会有残影建议只对当前页做。3.3 圆形头像与渐变背景的绘制参数360 界面左上角有个圆形头像用 QSS 的border-radius对QLabel设圆形在图片不是正方形时会变椭圆。可靠做法是QPainter裁剪// avatar.cpp 片段 void Avatar::paintEvent(QPaintEvent *) { QPainter p(this); p.setRenderHint(QPainter::Antialiasing); QPainterPath path; path.addEllipse(rect()); p.setClipPath(path); // m_pix 已按 rect 尺寸缩放好 p.drawPixmap(rect(), m_pix); // 外圈描边 p.setClipping(false); p.setPen(QPen(QColor(255, 255, 255, 180), 2)); p.drawEllipse(rect().adjusted(1, 1, -1, -1)); }参数说明描边宽度 2、颜色带 180 透明度是为了在浅色背景上也能看出边界。头像源图建议不小于 80×80否则放大后锯齿明显。渐变背景用QLinearGradient起点(0,0)终点(0,height)颜色从#F5F7FA到#E8EDF3这个区间在 1080P 和 2K 屏上都不会显得脏。3.4 资源文件与图标尺寸的对应关系用途建议尺寸格式说明导航图标20×20PNG选中/未选中各一张或单张靠 QPainter 调色标题栏 logo24×24PNG带透明通道功能卡片图标48×48PNG圆角在绘制时裁不要预裁背景大图900×600PNG/JPG若用纯色渐变可省减小体积图标不要用 SVG 直接塞进 QSSQt5.9 的 SVG 渲染在部分 Windows 环境需要额外部署QtSvg模块PNG 最省事。4. 避坑与排查无边框窗口和 QSS 最容易翻车的 5 个点4.1 现象窗口圆角外一圈黑边原因只设了WA_TranslucentBackground但paintEvent里用fillRect填了整块矩形圆角外没留透明。解决改用QPainterPath::addRoundedRect后fillPath并且不要在paintEvent里先fillRect(rect(), ...)。如果还有黑边检查是否在构造函数里设了setAutoFillBackground(true)这个要关掉。4.2 现象QSS 里border-radius对顶层窗口无效原因顶层窗口的圆角由窗口管理器决定QSS 的border-radius只对子控件生效。解决顶层窗口圆角必须自绘子控件如按钮、卡片才用 QSS。两者不要混用在同一层。4.3 现象导航按钮点一次跳两页原因QButtonGroup同时连了buttonClicked(int)和buttonClicked(QAbstractButton*)两个重载或者按钮的autoExclusive和手动setChecked冲突。解决只连一个重载用QOverloadint::of明确指定手动同步选中态时先blockSignals(true)再改改完恢复。4.4 现象拖动窗口时鼠标移出窗口就丢失拖动原因mouseMoveEvent只在鼠标位于窗口内时触发。解决在mousePressEvent里调用grabMouse()mouseReleaseEvent里releaseMouse()。注意grabMouse期间不要弹出模态对话框否则会卡住。4.5 现象Qt5.9 编译报QOverload未定义原因QOverload需要 C11 以上且部分旧编译器需要#include QtGlobal。解决在.pro里加CONFIG c11头文件里显式包含QtGlobal。如果仍不行退回用static_castvoid(QButtonGroup::*)(int)(QButtonGroup::buttonClicked)这种老写法。5. 进阶技巧让界面「活」起来的三个细节与验证方法第一个细节是导航切换的过渡。纯setCurrentIndex太生硬可以在切换前对旧页做 150ms 的透明度 1→0再切索引再对新页做 0→1。Qt5.9 上对单个页面做QGraphicsOpacityEffect是稳的但记得动画结束后把 effect 置空否则该页后续重绘会走离屏渲染滚动列表时掉帧。第二个细节是按钮的 hover 反馈。QSS 的:hover在无边框窗口上偶尔不刷新尤其是鼠标快速划过时。可靠做法是重写enterEvent和leaveEvent在里面update()并改一个成员变量控制绘制颜色。这样虽然多写几行但不会出现「鼠标走了按钮还亮着」的玄学问题。第三个细节是高分屏适配。Qt5.9 对AA_EnableHighDpiScaling的支持已经可用在main里QApplication::setAttribute(Qt::AA_EnableHighDpiScaling)之后所有硬编码的像素值会被自动缩放但QPainter里手写的坐标不会。所以自绘控件里尽量用width()、height()算比例不要写死 20、48 这种数字。验证方法是把系统缩放调到 150%看圆形头像是否还是正圆、竖条是否还居中。验证整套界面是否达标我一般做三件事一是拖动窗口到屏幕边缘再拖回看圆角和阴影有没有残影二是快速连点导航十次看页面索引有没有错位三是把窗口缩到最小尺寸比如 640×480看布局有没有控件重叠。这三步能过基本就能拿去当课程设计或练手项目的底子了。我自己踩得最狠的一次是把所有 QSS 都塞进qApp-setStyleSheet结果调试用的QMessageBox按钮全变成透明查了半天才反应过来是全局样式污染。从那以后样式只挂在主窗口上谁用谁负责。希望帮到你。本文还有配套的精品资源点击获取