最近有位淘宝客户来找我,想在他的软件打开之前加个登录
做完之后我突然意识到:
这不是一个“客户需求”,而是所有 Qt 初学者都绕不开的一个功能
今天我带大家一起用 Qt/C++ 来开发 结构清晰、可复用、风格现代 的 登录窗口
先看完成后的 demo

代码下载 https://thinkinginqt.com/20251121_thinkinginqt/logindialog.zip
很多同学第一次做登录功能时,代码一般是这样的:
在 LoginDialog 里面直接连数据库
登录成功就在里面 new MainWindow
登录失败就在里面弹 QMessageBox
main.cpp 里写一堆 if-else
最后代码耦合严重,无法复用
但正确的做法应该像这样:
这种写法才是“Qt 项目开发最佳实践”。
整个项目非常干净:
├── authservice.h├── logindialog.h/cpp/ui├── mainwindow.h/cpp/ui├── main.cpp└── CMakeLists.txt其中最关键的结构是:
AuthService:负责验证账号密码
LoginDialog:负责 UI + 输入逻辑 + 按回车触发
main.cpp:控制流程(先登录 → 再显示主窗口)
xclass AuthService{public: bool login(const QString &username, const QString &password) { QSqlQuery query; query.prepare("SELECT COUNT(*) FROM user WHERE username = :username AND password = :password"); query.bindValue(":username", username); query.bindValue(":password", password);
if (query.exec() && query.next()) { return query.value(0).toInt() > 0; } return false; }};这个类非常关键,它让整个项目变得“专业”:
✓ LoginDialog 不需要知道数据库如何验证
它只负责把用户名和密码交给 AuthService。
✓ 将来要换成 HTTP 登录、LDAP 登录、Token 登录,只改 AuthService
UI 完全不用改。
✓ 遵循 Qt 项目最重要的原则:职责清晰
LoginDialog 会在用户按下回车或点击按钮时发起验证:
xxxxxxxxxxvoid LoginDialog::on_login_clicked(){ if(m_auth.login(ui->name->text(), ui->password->text())) { accept(); // 登录成功 } else { ui->password->clear(); ui->password->setFocus(Qt::TabFocusReason); // 体验更好 }}这里有两个最佳实践:
让 main.cpp 控制流程才是正解
LoginDialog 是个“输入窗口”,不负责跳转
这也是 Qt Designer 生成对话框的标准做法
xxxxxxxxxxvoid LoginDialog::on_name_returnPressed(){ if(ui->password->text().isEmpty()) { ui->password->setFocus(); } else { on_login_clicked(); }}
void LoginDialog::on_password_returnPressed(){ on_login_clicked();}专业软件的“登录体验”就是这么做的。
xxxxxxxxxxint main(int argc, char *argv[]){ QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); QApplication a(argc, argv);
// 1. 打开数据库 QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName(QCoreApplication::applicationDirPath() + "/data.db"); if (!db.open()) { QMessageBox::critical(nullptr, "错误", "数据库打开失败"); return 1; }
// 2. 弹出登录窗口 LoginDialog login; if (login.exec() != QDialog::Accepted) { return 0; // 取消 / 关闭窗口 → 退出程序 }
// 3. 登录成功 → 进入主窗口 MainWindow w; w.show();
return a.exec();}这是 Qt 最推荐的登录流程写法:
因为:
数据库属于“应用级资源”
应该在程序启动时统一配置
不应该把“资源初始化”放到 LoginDialog 或 MainWindow
这是绝大多数管理软件的标准行为。
启动
打开数据库
显示 LoginDialog
登录成功 → 进入主界面
登录失败 / 取消 → 退出
整个流程都写在 main.cpp,任何人看到都懂,不用翻文件。
xxxxxxxxxxMainWindow::MainWindow(QWidget *parent) : QMainWindow(parent){ ui->setupUi(this);}MainWindow 只负责:
加载 UI
显示界面
它不负责:
登录
数据库验证
流程控制
管理 LoginDialog