目录
[一、模态对话框:accept /reject 消息机制](#一、模态对话框:accept /reject 消息机制)
[1. 项目搭建](#1. 项目搭建)
[2. 主界面 ↔ 登录界面互相切换](#2. 主界面 ↔ 登录界面互相切换)
[2.1 QColorDialog 颜色对话框](#2.1 QColorDialog 颜色对话框)
[2.2 QFileDialog 文件选择对话框](#2.2 QFileDialog 文件选择对话框)
[2.3 QInputDialog 输入对话框(4 种类型)](#2.3 QInputDialog 输入对话框(4 种类型))
[2.4 QMessageBox 消息提示框](#2.4 QMessageBox 消息提示框)
[2.5 QProgressDialog 进度对话框](#2.5 QProgressDialog 进度对话框)
[2.6 QWizard 向导对话框](#2.6 QWizard 向导对话框)
[三、QLineEdit 单行输入框详解](#三、QLineEdit 单行输入框详解)
[3.1 四种 EchoMode 回显模式](#3.1 四种 EchoMode 回显模式)
[3.2 实战训练](#3.2 实战训练)
[3.3 QRegularExpressionValidator 正则校验](#3.3 QRegularExpressionValidator 正则校验)
[四、Qt 布局管理](#四、Qt 布局管理)
[4.1 通过ui设置布局](#4.1 通过ui设置布局)
[4.2 通过代码设置布局](#4.2 通过代码设置布局)
引言
Qt GUI 开发中,弹窗、输入框、布局是基础模块。初学者常混淆模态对话框 exec() 返回逻辑、分不清标准对话框适用场景、控件错乱或窗口变形、界面切换内存泄漏、窗口隐藏/关闭逻辑踩坑。
本文从模态对话框消息机制讲起,实现登录与主界面跳转;覆盖 Qt 标准对话框(颜色、文件、输入框、消息提示、进度条、向导);讲解 QLineEdit 回显模式、输入掩码、正则校验;演示 Qt Designer 与纯 C++ 手动布局,并结合登录/注册切换案例,用 shared_ptr 与 weak_ptr 解决循环引用内存泄漏。代码可直接编译运行,适合作学习资料与速查手册。
前置说明:本文使用 Qt6 语法,信号槽采用新式函数指针写法,所有示例代码可直接新建 Qt Widgets 项目测试

一、模态对话框:accept /reject 消息机制
我们创建一个模态对话框,调用exec函数后可以根据其返回值进行不同的处理,exec的返回值有两种,Qt的官方文档记录的为
cpp
QDialog::Accepted
QDialog::Rejected
Accepted 表示接受消息, Rejected表示拒绝消息。
**常见误区:**很多同学以为accept()/reject()是信号,它们本质是 QDialog 的成员函数,函数内部会自动发出 accept/reject 信号,最终让exec()结束阻塞并返回对应枚举。
1. 项目搭建
新建 Qt Widgets Application,主窗口类MainWindow;再新增 Qt 设计师界面类MyDialog(基类QDialog,作为登录弹窗)。
在mydialog.ui放置两个按钮:
pushButton:文本【进入主界面】
pushButton_2:文本【退出程序】

在设计师界面点击Edit Signal/Slots 信号槽的按钮,进入信号槽编辑界面, 鼠标按住退出程序按钮不松开拖动,将另一端连接到MyDialog对话框 ,QPushbutton这边信号选择clicked() , MyDialog信号选择reject ,这样就将两个信号连接起来了,我们点击退出程序按钮,会触发MyDialog发送reject信号,因为MyDialog调用exec后等待信号返回,此时收到reject信号,exec就会返回Rjected值。
同样道理,MyDialog发送accetp()信号后, exec返回值为Accepted。

我们也能用手写代码的方式,点击Edit Widget 按钮, 然后右键点击键入主界面按钮,点击进入槽,在Qt 为我们生成的槽函数里添加accetp()信号发送逻辑
cpp
void MyDialog::on_pushButton_clicked()
{
accept(); // 触发Accepted,exec返回QDialog::Accepted
}
main 函数逻辑:先弹出登录对话框,根据返回值决定是否打开主窗口:
cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
MyDialog dialog;
if(dialog.exec() == QDialog::Accepted){
w.show();
return a.exec();
}else{
return 0;
}
}
运行后,我们点击进入主界面按钮,界面跳转至主界面,点击退出按钮程序退出。
2. 主界面 ↔ 登录界面互相切换
现在我们要实现主界面和登录界面的互相切换,我们刚才创建的对话框界面MyDialog界面是登录界面,主界面是MainWindow类。
我们在mainwindow.ui添加两个按钮,返回登录按钮和退出程序按钮, 再添加一个label表示这是主界面.
然后点击Edit Signal/Slot 进入信号编辑界面,点击退出程序按钮不松开拖动到尾部连接MainWindow的主界面上,选择退出程序的clicked()信号,MainWindow选择reject信号,将两个信号连接起来。

然后右击登录按钮转到槽,在槽函数里添加这个逻辑
cpp
void MainWindow::on_pushButton_clicked()
{
this->close(); // close()只是隐藏窗口,对象没有销毁
MyDialog mydialog;
if(mydialog.exec() == QDialog::Accepted)
{
this->show(); // 登录确认,重新显示主窗口
}
else
{
// 登录弹窗选择退出,不再打开主界面
return;
}
}
点击返回登陆按钮就会close主窗口,但是并不是真的关闭,只是将主窗口隐藏,Qt回收机制是所有窗口都关闭后才回收。
再次运行程序,点击进入主界面按钮就可以进入主界面,点击返回登录按钮就可以返回登录界面,可以实现两个界面的切换了。
注意:
close()不等于对象析构。对于栈上创建的窗口,close 只是隐藏;只有堆 new 出来的窗口,设置setAttribute(Qt::WA_DeleteOnClose)之后,close 才会自动释放内存。本案例中 MainWindow 在 main 函数是栈对象,所以 close 只是隐藏,后续可以再次 show。
二、QT内置标准对话框
本文介绍几种标准对话框,都是Qt封装好的,我们先创建一个界面,添加几个按钮,然后分别在几个按钮的回调函数里添加创建不同对话框的逻辑

2.1 QColorDialog 颜色对话框
颜色对话框用来选择颜色,创建后会显示各种颜色和透明度信息
cpp
//创建颜色对话框
void MainWindow::on_colorBtn_clicked()
{
//默认父窗口是主窗口,蓝色
QColorDialog colorDlg(Qt::blue,this);
//开启透明度通道(Alpha),可以选择带透明的颜色
colorDlg.setOption(QColorDialog::ShowAlphaChannel);
//阻塞等待用户操作
colorDlg.exec();
//获取当前颜色
QColor color=colorDlg.currentColor();
qDebug()<<"color is "<<color;
}

2.2 QFileDialog 文件选择对话框
getOpenFileName静态函数最常用,支持文件过滤器,用;;分隔多种文件类型:
cpp
void MainWindow::on_pushButton_2_clicked()
{
QString path = QDir::currentPath();
QString title = tr("文件对话框");
QString filter = tr("文本文件(*.txt);;图片文件(*.jpg *.gif *.png);;所有文件(*.*)");
QString aFileName=QFileDialog::getOpenFileName(this,title,path,filter);
qDebug() << aFileName << endl;
}

2.3 QInputDialog 输入对话框(4 种类型)
统一参数:最后一个bool *ok用来判断用户是点击【确定】还是【取消】。
输入对话框分几种,包括文本输入对话框,整数输入对话框,浮点数输入对话框,条目输入对话框。
文本输入对话框
cpp
void MainWindow::on_pushButton_3_clicked()
{
bool ok = false;
auto text = QInputDialog::getText(this, tr("文字输入对话框"), tr("请输入用户的姓名"), QLineEdit::Normal, tr("admin"), &ok);
if(ok){
qDebug() << text << endl;
}
}
整形输入对话框
cpp
void MainWindow::on_pushButton_4_clicked()
{
bool ok = false;
auto intdata = QInputDialog::getInt(this,tr("数字输入对话框"),tr("请输入数字"),200,-200,400,10,&ok);
if(ok){
qDebug() << intdata << endl;
}
}

浮点输入对话框
cpp
//浮点数对话框
void MainWindow::on_pushButton_clicked()
{
bool ok;
auto floatdata=QInputDialog::getDouble(this,tr("浮点数对话"),tr("请输入浮点数"),0.1,-2,4,2,&ok);
if(ok)
{
qDebug()<<floatdata<<"\n";
}
}
条目输入对话框
cpp
//条目输入对话框
void MainWindow::on_pushButton_5_clicked()
{
QStringList items; //创建字符串列表,下拉选项
items<<tr("条目1")<<tr("条目2");
bool ok;
//允许编辑
auto itemData=QInputDialog::getItem(this,tr("条目对话框"),tr("输入或选择条目"),items,0,true,&ok);
if(ok)
{
qDebug()<<"item is "<<itemData<<"\n";
}
}

2.4 QMessageBox 消息提示框
四种级别:question 提问 /information 通知 /warning 警告 /critical 严重错误
cpp
//提示/通知/警告/关键提示 对话框
void MainWindow::on_pushButton_6_clicked()
{
auto ret=QMessageBox::question(this,tr("提问对话框"),tr("你喜欢读书吗?"));
if(ret==QMessageBox::Yes){
qDebug()<<"ret is "<<ret;
qDebug()<<"你很牛!";
}else{
return;
}
auto ret2=QMessageBox::information(this,tr("通知对话框"),tr("你好读书人"),QMessageBox::Yes);
if(ret2==QMessageBox::Yes)
{
qDebug()<<"ret2 is"<<ret2;
}else{
return;
}
auto rets=QMessageBox::warning(this,tr("警告对话框"),tr("不要过度学习,小心秃头"),QMessageBox::Yes);
if(rets==QMessageBox::Yes)
{
qDebug()<<"rets is "<<rets;
}else{
return ;
}
auto ret4=QMessageBox::critical(this,tr("关键提示对话框"),tr("试炼尽头,是花开万里"),QMessageBox::Yes);
if(ret4==QMessageBox::Yes)
{
qDebug()<<"ret4 is "<<ret4<<"\n";
}else{
return;
}
}

2.5 QProgressDialog 进度对话框
当我们复制或进行文件传输时,会显示一个进度条的对话框,我们可以使用QProcessDialog来操作。
cpp
void MainWindow::on_pushButton_8_clicked()
{
// 进度提示框一闪而逝,不是逻辑问题,是5000量级太小了
QProgressDialog progressDialog(tr("正在复制"),tr("取消复制"),0,5000,this);
progressDialog.setWindowTitle(tr("文件复制进度对话框"));
progressDialog.setWindowModality(Qt::ApplicationModal);
progressDialog.show();
for(int i = 0; i < 5000; i++){
progressDialog.setValue(i);
QApplication::processEvents();
if(progressDialog.wasCanceled()){
break;
}
}
progressDialog.setValue(5000);
}
因为循环很快完成,所以进度条一闪而逝,那么我们可以通过定时器的方式定时更新进度,这样就可以更清楚的看到进度条对话框更新情况了
cpp
void MainWindow::on_pushButton_8_clicked()
{
//改用定时器
_progressDialog = new QProgressDialog(tr("正在复制"),tr("取消复制"),0,5000,this);
_progressDialog->setWindowTitle(tr("文件复制进度对话框"));
_progressDialog->setWindowModality(Qt::ApplicationModal);
_timer = new QTimer(this);
connect(_timer, &QTimer::timeout, this, &MainWindow::on_updateProgressDialog);
connect(_progressDialog, &QProgressDialog::canceled, this, &MainWindow::on_cancelProgressDialog);
_timer->start(2);
qDebug("复制结束");
}
上面代码连接了定时器的timeout信号和主窗口的on_updateProgressDialog函数, 该函数的主要作用就是定时更新进度条,逻辑如下
cpp
//加载对话框
void MainWindow::on_updateProgressDialog()
{
_counter++;
if(_counter>5000)
{
_timer->stop();
delete _timer;
_timer =nullptr;
delete _progressDialog;
_counter=0;
return ;
}
_progressDialog->setValue(_counter); //调用 `setValue()`、进度开始变化之后,才自动弹出窗口。
}
另外我们需要在进度条满了的时候发消息通知主窗口关闭
cpp
//通过取消关闭对话框
void MainWindow::on_cancelProgressDialog(){
_timer->stop();
delete _timer;
_timer=nullptr; //避免二次删除,崩溃
delete _progressDialog;
_progressDialog=nullptr;
_counter=0;
return;
}
这样进度条满了之后我们就可以回收定时器资源了。

2.6 QWizard 向导对话框
当我们想引导用户操作时可以采用向导对话框,类似软件安装向导,由多个QWizardPage页面组成,自带上一步 / 下一步 / 完成按钮
cpp
//向导对话框
void MainWindow::on_pushButton_7_clicked()
{
//向导容器 管理多个页面
QWizard wizard(this);
wizard.setWindowTitle(tr("全民偶像"));
//每一页单独的页面
QWizardPage* page1=new QWizardPage();
page1->setTitle(tr("快来pick你心仪的偶像吧!"));
auto label1=new QLabel(); //展示文本、图片的标签
label1->setText(tr("该程序助力于爱豆出道"));
//`QVBoxLayout`:垂直布局管理器,控件从上往下竖直排列
QVBoxLayout *layout=new QVBoxLayout();
layout->addWidget(label1); //将标签添加到垂直布局中
page1->setLayout(layout); //布局加到页面中
wizard.addPage(page1); //布局加到向导中,成为向导第一页
QWizardPage* page2=new QWizardPage();
page2->setTitle(tr("选择心动类型"));
//按钮组由page2管理,开启互斥逻辑,只能选一个
QButtonGroup *group=new QButtonGroup(page2);
QRadioButton *btn1=new QRadioButton();
btn1->setText(tr("小奶狗"));
group->addButton(btn1);
QRadioButton *btn2=new QRadioButton();
btn2->setText(tr("成熟年上"));
group->addButton(btn2);
QRadioButton *btn3=new QRadioButton();
btn3->setText(tr("阳光年下"));
group->addButton(btn3);
QRadioButton *btn4=new QRadioButton();
btn4->setText(tr("顶级天蝎男"));
group->addButton(btn4);
QRadioButton *btn5=new QRadioButton();
btn5->setText(tr("清风霁月君子"));
group->addButton(btn5);
QRadioButton *btn6=new QRadioButton();
btn6->setText(tr("腹黑病娇男配"));
group->addButton(btn6);
QVBoxLayout *vboxLayout2 =new QVBoxLayout();
//将所有按钮添加到垂直布局
for(int i=0;i<group->buttons().size();i++)
{
vboxLayout2->addWidget(group->buttons()[i]);
}
page2->setLayout(vboxLayout2);
wizard.addPage(page2);
QWizardPage* page3=new QWizardPage();
page3->setTitle(tr("出道名单即将确定"));
auto label3=new QLabel();
label3->setText(tr("感谢助力,期待首场团体演出!"));
QVBoxLayout *layout3=new QVBoxLayout();
layout3->addWidget(label3);
page3->setLayout(layout3);
wizard.addPage(page3);
wizard.show();
wizard.exec();
}

三、QLineEdit 单行输入框详解
QLineEdit用于单行文本录入,核心两大功能:回显模式 + 输入校验(掩码 / 正则)
3.1 四种 EchoMode 回显模式
QLineEdit 属于输入插件,用来实现单行录入。支持几种录入模式。
Normal 表示正常录入,录入的信息会显示在QLineEdit上。
Password 表示密码录入的方式,录入的信息不显示QLineEdit,只是通过黑色圆点显示。
NoEcho 表示不显示录入信息,类似于Linux输入密码时,显示的是一片空白。
PasswordEchoOnEdit 表示在输入的一刹那可以看到字符,但是立刻变为不可见的黑色圆点显示。
3.2 实战训练
我们创建一个QApplication项目,名字叫editline。项目主界面取名MainWindow类,继承于QMainWindow。Qt为我们自动生成ui文件。
然后在ui界面里添加四个QLineEdit和Label

我将ip的QLineEdit名字改为ipedit,设置ipedit的输入方式为Normal,这样可以在输入时看到输入的字符。
我们可以给ipedit设置mask规定输入的字符只能为0~9的数字
cpp
// IP地址掩码 000.000.000.000,下划线是占位符
ui->ipedit->setEchoMode(QLineEdit::Normal);
QString ip_mask = "000.000.000.000;_";
ui->ipedit->setInputMask(ip_mask);
具体的mask规则可以参考Qt官方文档,这里列举一下常见的mask规则

利用上面的规则我们可以编写一些常用的mask

接下来我们设置mac地址的mask和录入模式,mac地址的edit被命名为macedit
cpp
// MAC地址掩码,H代表十六进制 0-9 A-F
ui->macedit->setEchoMode(QLineEdit::Normal);
QString mac_mask = "HH:HH:HH:HH:HH:HH;_";
ui->macedit->setInputMask(mac_mask);
通过设置mask,规定mac的输入内容只能为A~F以及0~9的字符。
3.3 QRegularExpressionValidator 正则校验
其实除了通过mask限制edit录入的内容外,还可以通过正则表达式限制,比如我们设置邮箱edit的录入内容
cpp
ui->emailedit->setEchoMode(QLineEdit::Normal);
//正则类 邮箱正则表达式
QRegularExpression regx("[a-zA-Z0-9_-]+@[a-zA-Z0-9]+\.[a-zA-Z0-9]+");
//正则校验,第二个参数为父对象
QValidator * validator=new QRegularExpressionValidator(regx,ui->emailEdit);
//给输入框绑定校验原则
ui->emailEdit->setValidator(validator);
我们通过正则表达式限定了有限的规则为 "字符串和数子组合 + @ + 字符串和数子组合 + . + 字符串和数子组合"。
具体的正则表达式可以去Qt文档查看,也可以看看下边这个图

接下来我们可以设置密码输入框的输入模式为Password,保证录入时不显示密码
cpp
ui->passwdedit->setEchoMode(QLineEdit::Password);

四、Qt 布局管理
布局的核心目的:窗口拉伸时,控件自动适配、不会错位。Qt 三大基础布局:
QHBoxLayout:水平布局(横向排列)QVBoxLayout:垂直布局(纵向排列)QGridLayout:栅格布局(表格多行多列)
弹簧QSpacerItem(Spacer)用来占位、留白、控制对齐方式,sizePolicy 控制拉伸策略
4.1 通过ui设置布局
我们先创建一个窗口应用程序,程序名叫layout,基类选择QMainWindow。但我们不使用这个mainwindow,我们创建一个Qt应用程序类Login,Qt会为我们自动生成login.ui文件。我们进入ui文件编辑,添加一个label,提示改为用户: , 在后边添加一个lineedit控件,按住ctrl鼠标依次点击这两个控件选中后,再点击工具栏的水平布局按钮就可以看到用户label和输入框处于同一水平线了。但是输入框会被拉长,而且label和输入框占满了整个水平空间。这时我们可以通过拖动左侧控件列表中的Horizonal Spacer,将其放入用户标签的左侧,再拖动一个Horizonal Spacer将其放在输入框的右侧,就可以看到用户标签和输入框被挤在中间了,并且两侧留有空间了。
Spacer可以设置几种模式,包括fixed,expanding, maximum, minimum等模式。
依次类推,我们在添加密码标签和输入框,以及登录和注册按钮,通过ui界面的控件调整布局。

4.2 通过代码设置布局
上面我们通过ui设置了布局,接下来我们通过代码设置布局,设置注册界面的布局
注册类的声明如下
cpp
#ifndef REGISTER_H
#define REGISTER_H
#include<memory>
#include <QDialog>
class Login;
namespace Ui {
class Register;
}
class Register : public QDialog
{
Q_OBJECT
public:
explicit Register(QWidget *parent = nullptr);
~Register();
void set_login(const std::weak_ptr<Login> &login);
void slot_register();
private:
Ui::Register *ui;
//std::shared_ptr<Login> _login; //互相锁住,内存泄漏
std::weak_ptr<Login> _login; //弱引用,不增加引用计数
};
#endif // REGISTER_H
因为要实现登录和注册界面之间的切换,所以Register类包含了Login类的弱指针
Register类的具体实现如下
cpp
#include "register.h"
#include "ui_register.h"
#include<QHBoxLayout>
#include<QSpacerItem>
#include<QLabel>
#include<QLineEdit>
#include<QVBoxLayout>
#include<QPushButton>
#include"login.h"
Register::Register(QWidget *parent)
: QDialog(parent)
, ui(new Ui::Register)
{
ui->setupUi(this);
//用户窗口尺寸锁死
this->setMaximumSize(QSize(400,550));
this->setMinimumSize(QSize(400,550));
auto vbox_layout=new QVBoxLayout(); //创建垂直布局,主布局
//QSpacerItem 不用手动删
//加到主垂直布局,放在最顶部,用来把下面内容往下挤。
auto verticalSpacer1=new QSpacerItem(40,20,QSizePolicy::Minimum,QSizePolicy::Expanding);
vbox_layout->addItem(verticalSpacer1);
QSpacerItem *name_item1=new QSpacerItem(40,20,QSizePolicy::Fixed,QSizePolicy::Minimum);
QLabel *name_label =new QLabel();
name_label->setText("邮箱:");
QLineEdit *name_edit=new QLineEdit();
//邮箱一行,使用水平布局 QHBoxLayout
auto name_layout=new QHBoxLayout();
name_layout->addItem(name_item1);
name_layout->addWidget(name_label);
name_layout->addWidget(name_edit);
QSpacerItem *name_item2=new QSpacerItem(40,20,QSizePolicy::Fixed,QSizePolicy::Minimum);
name_layout->addItem(name_item2);
vbox_layout->addLayout(name_layout); //将水平布局一行加入垂直布局
QLabel *pwd_label=new QLabel();
pwd_label->setText("密码:");
QLineEdit *pwd_edit=new QLineEdit();
auto verticalSpacer2=new QSpacerItem(40,20,QSizePolicy::Maximum,QSizePolicy::Maximum);
//密码行之前,先加一个垂直方向的间隔弹簧:
vbox_layout->addItem(verticalSpacer2);
auto pwd_layout=new QHBoxLayout();
QSpacerItem *pwd_item2=new QSpacerItem(40,20,QSizePolicy::Fixed,QSizePolicy::Minimum);
QSpacerItem *pwd_item1=new QSpacerItem(40,20,QSizePolicy::Fixed,QSizePolicy::Minimum);
pwd_layout->addItem(pwd_item1);
pwd_layout->addWidget(pwd_label);
pwd_layout->addWidget(pwd_edit);
pwd_layout->addItem(pwd_item2);
vbox_layout->addLayout(pwd_layout);
//密码行 和 注册按钮之间的间隔:
auto verticalSpacer3=new QSpacerItem(40,30,QSizePolicy::Fixed,QSizePolicy::Maximum);
vbox_layout->addItem(verticalSpacer3);
QSpacerItem *reg_btn_item1=new QSpacerItem(150,20,QSizePolicy::Fixed,QSizePolicy::Minimum);
QSpacerItem *reg_btn_item2=new QSpacerItem(150,20,QSizePolicy::Fixed,QSizePolicy::Minimum);
auto _reg_btn=new QPushButton();
_reg_btn->setText("注册");
auto regbtn_layout=new QHBoxLayout();
regbtn_layout->addItem(reg_btn_item1);
regbtn_layout->addWidget(_reg_btn,5); //拉伸系数,这一行多余空间,优先分给按钮
regbtn_layout->addItem(reg_btn_item2);
vbox_layout->addLayout(regbtn_layout);
//最底部弹性弹簧
auto verticalSpacer4=new QSpacerItem(40,20,QSizePolicy::Fixed,QSizePolicy::Expanding);
vbox_layout->addItem(verticalSpacer4);
this->setLayout(vbox_layout);
connect(_reg_btn,&QPushButton::clicked,this,&Register::slot_register);
}
Register::~Register()
{
delete ui;
}
void Register::set_login(const std::weak_ptr<Login> &login)
{
_login=login;
}
void Register::slot_register()
{
this->close();
auto shared_login=_login.lock();
shared_login->show();
}
Register的构造函数中用代码的方式创建了一个垂直布局,垂直布局中增加了两个spacer,分别是verticalSpacer1和verticalSpacer4,以及三个水平布局pwd_layout,name_layout以及regbtn_layout,然后分别用代码的方式在三个布局中添加spacer和控件。
Login类的声明如下
cpp
#ifndef LOGIN_H
#define LOGIN_H
#include <QDialog>
QT_BEGIN_NAMESPACE
namespace Ui {
class Login;
}
QT_END_NAMESPACE
//做个声明
class Register;
class Login : public QDialog,public std::enable_shared_from_this<Login>
{
Q_OBJECT
public:
Login(QWidget *parent = nullptr);
~Login();
void initSignals(); //初始化信号和槽
private slots:
void on_pushButton_2_clicked();
private:
Ui::Login *ui;
std::shared_ptr<Register> _register; //智能指针
};
#endif // LOGIN_H
Login实现如下
cpp
#include "login.h"
#include "ui_login.h"
#include"register.h"
#include<QPainter>
Login::Login(QWidget *parent)
: QDialog(parent)
, ui(new Ui::Login)
{
ui->setupUi(this);
}
Login::~Login()
{
delete ui;
}
void Login::initSignals(){
_register=std::make_shared<Register>();
_register->set_login(shared_from_this()); //返回login类的智能指针
}
void Login::on_pushButton_2_clicked()
{
this->close();
_register->show();
}
main函数的实现如下
cpp
#include "login.h"
#include <QStyleHints>
#include <QApplication>
#include"register.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 全局浅色样式,所有控件生效
a.setStyleSheet(R"(
QWidget {
background: #ffffff;
color: #202020;
}
QPushButton {
background: #f5f5f5;
border: 1px solid #cccccc;
padding: 4px;
}
QLineEdit {
background: white;
border:1px solid #aaa;
}
QDialog {
background-color: white;
}
)");
std::shared_ptr<Login> w=std::make_shared<Login>();
w->initSignals();
w->show();
return a.exec();
}
点击运行按钮,程序运行起来就可以从登录界面切换到注册界面了
重要纠正:
weak_ptr.lock()必须判断返回值,防止 Login 对象已经销毁,野指针访问崩溃。


结语
对话框、单行输入框、布局是 Qt GUI 开发的地基。模态对话框exec()阻塞逻辑、accept/reject 返回值控制弹窗流转;标准对话框把高频弹窗场景封装到位,不用重复造轮子;QLineEdit 配合掩码、正则实现表单输入校验,是登录注册页面必备;布局管理器解决窗口自适应问题,而动态代码布局结合智能指针,能安全实现多界面跳转,规避循环引用内存泄漏的经典坑。
很多 Qt 初学者写界面只靠绝对坐标固定控件位置,一旦窗口大小变化界面直接崩盘;弹窗逻辑分不清 hide、close、delete,频繁出现内存泄漏或者偶发崩溃。吃透本篇内容,后续开发表单、登录系统、配置弹窗、向导页面都可以直接复用这套思路。
