Qt 对话框、QLineEdit 与布局管理实战详解

目录

引言

[一、模态对话框:accept /reject 消息机制](#一、模态对话框:accept /reject 消息机制)

[1. 项目搭建](#1. 项目搭建)

[2. 主界面 ↔ 登录界面互相切换](#2. 主界面 ↔ 登录界面互相切换)

二、QT内置标准对话框

[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,频繁出现内存泄漏或者偶发崩溃。吃透本篇内容,后续开发表单、登录系统、配置弹窗、向导页面都可以直接复用这套思路。

相关推荐
炸薯条!1 小时前
博客系统测试报告
开发语言·lua
Evand J1 小时前
【MATLAB例程12】永磁同步电机基于增广无迹卡尔曼滤波的未知负载转矩在线观测
开发语言·matlab
时间的拾荒人2 小时前
CMake 实战指南:从基础语法到静态库/动态库制作与链接
开发语言·c++·面试
xyouyuweizhu2 小时前
Java InetAddress 详解:从 IP 地址表示到主机名解析
java·开发语言·python
布莱克60511 小时前
C/C++ 中变量的声明与定义:区别、联系与常见误区
c语言·开发语言·c++·声明·定义
LabVIEW开发11 小时前
LabVIEW 局部变量为何引发竞争条件?四种同步机制
开发语言·labview
benchmark_cc12 小时前
A 股、港股、美股如何建立统一的标的主数据格式?
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
Zenova EdgeOS13 小时前
Go 工业边缘错误处理实战:用 errors 构建可追溯错误链
开发语言·数据库·golang
取名真是13 小时前
Python 算法知识点从基础到进阶
开发语言·python·算法