简介这份代码示例围绕 Qt5 多界面切换与弹窗显示展开适合已掌握 Qt 基础、希望理解多窗口组织与交互逻辑的开发者参考。压缩包共 29 个文件以 7 个 cpp 源文件、7 个 h 头文件和 3 个 ui 界面文件为核心辅以 qmake 工程文件及编译中间产物整体约 57KB体量轻便便于快速导入 Qt Creator 运行与调试。已有 182 人学习说明该主题在桌面端界面开发中具有稳定的参考需求。读者可从中了解主窗口与多个子界面的切换方式、QDialog 自定义弹窗的组织形式以及信号与槽在窗口通信中的实际用法结合工程结构与界面文件还能对照梳理多 UI 项目的类划分、窗口显隐控制和模态弹窗的调用思路为后续扩展更复杂的界面流程提供可复用的代码骨架。1. 从两个 .ui 文件开始qt ui 多页面切换到底卡在哪把 qt ui 从单窗口 Demo 拆成多页面工程时最先翻车的往往不是控件摆放而是切换后旧页面还在后台跑、弹窗关不掉、信号重复连接。这个 Qt5 资源包围绕多 .ui 界面切换和弹窗显示把主窗口导航、页面装载、模态与非模态弹窗、返回值处理串成一条可复现的路径。它适合刚接触 Qt 界面拼装的新手也适合手里已有原型、正被页面生命周期和弹窗时序折腾的熟手。先别急着堆控件先把切换边界和弹窗归属看清楚。2. QStackedWidget 落地索引、生命周期与页面间传参主窗口内切页面最省心的容器是QStackedWidget。它把多个QWidget页叠在一起只显示当前页切换时改的是可见性不是反复创建窗口。这个资源包的主骨架就是“一个主窗口 一个QStackedWidget 若干由 .ui 生成的页面类”弹窗则交给QDialog单独处理。把这两类界面分开后面排错才不会把“页面没切过去”和“弹窗没关掉”混成一锅。2.1 为什么优先用 QStackedWidget 而不是反复 new 窗口反复new QWidget做页面短期看很直接长期看会留下三个麻烦父对象关系不清、旧页面被重复创建、信号在切换时重复连接。QStackedWidget的思路相反页面在初始化时一次性挂进去切换只改currentIndex或currentWidget页面状态可以保留返回上一页时不用重新填表单。对于设置页、首页、结果页这种主窗口内导航它比手动show/hide更容易控制层级。方案页面存活方式切换成本适合场景主要风险QStackedWidget页面常驻容器托管低主窗口内多页导航页面多时首次构建变慢手动show/hide页面常驻自己管理低少量浮层、覆盖层布局和层级容易乱每次new QWidget按需创建中一次性向导页父对象、内存、重复连接QDialog按需弹出中确认、输入、提示模态阻塞与返回值处理这张表里最容易被忽略的是“页面常驻”带来的副作用页面不会因为切走就销毁所以定时器、网络请求、播放器状态都还在。我的习惯是给每个页面补一个onPageEnter()或refresh()在currentChanged里按需调用而不是假设页面切走就自动停下。弹窗则不要塞进QStackedWidget它是临时交互应该独立成QDialog否则关闭逻辑会和页面导航缠在一起。2.2 三个 .ui 文件挂进同一个主窗口的完整步骤先按资源包的结构把页面类分好MainWindow负责壳和导航PageHome、PageSettings各管一个 .uiDialogConfirm管弹窗。每个页面类只暴露必要的 setter 和信号不直接把另一个页面的指针传进去。下面这套写法可以直接对照资源里的工程改前提是 Designer 里的 objectName 要和代码一致。第一步把 .ui 文件加入构建清单。qmake 工程里用FORMSCMake 工程里靠AUTOUIC两种方式的目的都是让 uic 生成ui_*.h。如果 .ui 只在目录里、没有进构建清单编译器就会报找不到ui_pagehome.h。QT core gui widgets CONFIG c11 TEMPLATE app TARGET multi_ui_demo FORMS \ mainwindow.ui \ pagehome.ui \ pagesettings.ui \ dialogconfirm.ui HEADERS \ mainwindow.h \ pagehome.h \ pagesettings.h \ dialogconfirm.h SOURCES \ main.cpp \ mainwindow.cpp \ pagehome.cpp \ pagesettings.cpp \ dialogconfirm.cpp RESOURCES resources.qrc这段 .pro 的关键参数不多QT widgets是 Qt5 界面模块CONFIG c11让后面的 lambda 和enum class能正常编译FORMS决定哪些 .ui 参与 uic 生成RESOURCES决定 qrc 是否编进可执行文件。TARGET只是产物名换成自己的工程名不影响逻辑。若工程里还有网络、数据库模块再按需补QT network sql不要一次性全加上。第二步在主窗口里集中挂页面。下面代码假设mainwindow.ui里有stackedWidget、homeButton、settingsButton、dialogButton这几个 objectName页面 .ui 里有对应的按钮和输入框。// mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include QMainWindow class PageHome; class PageSettings; class QStackedWidget; namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent nullptr); ~MainWindow(); private: void buildPages(); Ui::MainWindow *ui; PageHome *homePage nullptr; PageSettings *settingsPage nullptr; }; #endif// mainwindow.cpp #include mainwindow.h #include ui_mainwindow.h #include pagehome.h #include pagesettings.h #include dialogconfirm.h #include QDialog #include QPushButton #include QStackedWidget MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui-setupUi(this); buildPages(); connect(ui-homeButton, QPushButton::clicked, this, [this] { ui-stackedWidget-setCurrentWidget(homePage); }); connect(ui-settingsButton, QPushButton::clicked, this, [this] { ui-stackedWidget-setCurrentWidget(settingsPage); }); connect(ui-dialogButton, QPushButton::clicked, this, [this] { DialogConfirm dlg(this); dlg.setTitleText(QStringLiteral(确认切换页面)); dlg.setContentText(QStringLiteral(切换后当前页面的未保存内容会保留。)); if (dlg.exec() QDialog::Accepted) { ui-stackedWidget-setCurrentWidget(settingsPage); } }); ui-stackedWidget-setCurrentWidget(homePage); } void MainWindow::buildPages() { homePage new PageHome(this); settingsPage new PageSettings(this); ui-stackedWidget-addWidget(homePage); ui-stackedWidget-addWidget(settingsPage); connect(homePage, PageHome::requestOpenSettings, this, [this](const QString userName) { settingsPage-setUserName(userName); ui-stackedWidget-setCurrentWidget(settingsPage); }); } MainWindow::~MainWindow() { delete ui; }这段代码的逻辑是buildPages()只执行一次把页面挂进QStackedWidget导航按钮用setCurrentWidget()不依赖硬编码索引弹窗用栈对象DialogConfirm dlg(this)exec()返回后再判断结果对象离开作用域自动析构。参数上addWidget()返回页面索引但这里没有保存索引因为页面顺序以后可能调整setCurrentWidget()比setCurrentIndex(1)更抗改动。parent设为this只是先建立父对象关系addWidget()会把页面重新挂到 stacked widget 下最终由容器负责析构。第三步让页面自己发信号不直接操作另一个页面。下面以PageHome为例按钮点击后把输入框内容带出去主窗口决定要不要切页、切到哪一页。// pagehome.h #ifndef PAGEHOME_H #define PAGEHOME_H #include QWidget namespace Ui { class PageHome; } class PageHome : public QWidget { Q_OBJECT public: explicit PageHome(QWidget *parent nullptr); ~PageHome(); signals: void requestOpenSettings(const QString userName); private: Ui::PageHome *ui; }; #endif// pagehome.cpp #include pagehome.h #include ui_pagehome.h #include QPushButton PageHome::PageHome(QWidget *parent) : QWidget(parent), ui(new Ui::PageHome) { ui-setupUi(this); connect(ui-goSettingsButton, QPushButton::clicked, this, [this] { const QString name ui-userNameEdit-text().trimmed(); emit requestOpenSettings(name); }); } PageHome::~PageHome() { delete ui; }本文还有配套的精品资源点击获取