1 Star 11 Fork 6

胡歌-此生不换/Qt-Project

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

QT 的布局

1. 简介

Qt 中的布局有三种方式: 水平布局,垂直布局,栅格布局。

2. 通过 ui 设置布局

我们先创建一个窗口应用程序,程序名叫 layout,基类选择 QDialogQt 会为我们自动生成 login.cpplogin.hlogin.ui 文件。

Alt text

我们进入 ui 文件编辑,添加一个 label,提示改为"用户"。

在后边添加一个 lineEdit 控件,按住 ctrl 鼠标依次点击这两个控件选中后,再点击工具栏的水平布局按钮就可以看到用户 label 和输入框处于同一水平线了。但是输入框会被拉长,而且 label 和输入框占满了整个水平空间。这时我们可以通过拖动左侧控件列表中的 Horizonal Spacer,将其放入用户标签的左侧,再拖动一个 Horizonal Spacer 将其放在输入框的右侧,就可以看到用户标签和输入框被挤在中间了,并且两侧留有空间了。

Spacer 可以设置几种模式,包括 fixedexpandingmaximumminimum 等模式。

依次类推,我们在添加密码标签和输入框,以及登录和注册按钮,通过 ui 界面的控件调整布局。

Alt text

3. 通过代码设置布局

上面我们通过 ui 设置了布局,接下来我们通过代码设置布局,设置注册界面的布局。

注册类的声明如下:

#ifndef REGISTER_H
#define REGISTER_H

#include <QDialog>
#include <memory>

using namespace std;

class Login; // 声明一下

namespace Ui {
class Register;
}

class Register : public QDialog
{
    Q_OBJECT

public:
    explicit Register(QWidget *parent = nullptr);
    ~Register();

    void set_login(const weak_ptr<Login>& login);
    void showLogin();

private:
    Ui::Register *ui;
    // 因为要实现登录和注册界面之间的切换,所以 Register 类包含了 Login 类的弱指针
    // weak_ptr 对象的 lock 成员函数可以取出其对应的 shared_ptr 对象
    weak_ptr<Login> login_;
    QPushButton* register_button_;
};

#endif // REGISTER_H

因为要实现登录和注册界面之间的切换,所以 Register 类包含了 Login 类的弱指针。Register 类的具体实现如下:

用代码的话,弹簧是不分垂直和水平的,放在垂直布局中就是垂直弹簧,放在水平布局中就是水平弹簧。

#include "register.h"
#include "ui_register.h"

#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QSpacerItem>

Register::Register(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Register)
{
    ui->setupUi(this);

    this->setMaximumSize(QSize(300,350));
    this->setMinimumSize(QSize(300,350));
    auto vbox_layout = new QVBoxLayout();

    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();
    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);
    register_button_ = new QPushButton();
    register_button_->setText("注册");
    auto regbtn_layout = new QHBoxLayout();
    regbtn_layout->addItem(reg_btn_item1);
    regbtn_layout->addWidget(register_button_, 5);
    QSpacerItem* reg_btn_item2 = new QSpacerItem(40,20, QSizePolicy::Fixed, QSizePolicy::Minimum);

    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);

    // register 页面跳转到 login 页面
    connect(register_button_, &QPushButton::clicked, this, &Register::showLogin);
}

Register::~Register()
{
    delete ui;
}

void Register::set_login(const weak_ptr<Login>& login){
    login_ = login;
}

void Register::showLogin(){
    this->close();

//    this->login_.lock()->show();

    //    auto shared_ptr_login = login_.lock();
    std::shared_ptr<Login> shared_ptr_login = login_.lock();
    shared_ptr_login->show();
}

Register 的构造函数中用代码的方式创建了一个垂直布局,垂直布局中增加了两个 spacer,分别是 verticalSpacer1verticalSpacer4,以及三个水平布局 pwd_layoutname_layout 以及 regbtn_layout,然后分别用代码的方式在三个布局中添加 spacer 和控件。

Login 类的声明如下:

#ifndef LOGIN_H
#define LOGIN_H

#include <QDialog>
#include <memory>

class Register;

using namespace std;

QT_BEGIN_NAMESPACE
namespace Ui { class Login; }
QT_END_NAMESPACE

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_registerButton_clicked();

private:
    Ui::Login *ui;

    std::shared_ptr<Register> register_;
};
#endif // LOGIN_H

注意

  1. std::shared_ptr 互引用问题Login 类里面有 std::shared_ptr<Register> register_ 可以用来调用 register 页面(那么在 register 好了之后肯定得跳转到 login 页面,所以在 register 类里面也得有调用显示 login 页面的 Login 对象的啊),但是如果 register 类也用 std::shared_ptr<Login> 的话,就会出现互为引用的空间不能释放的问题(register 类得用 std::weak_ptr<Login> 解决这个问题)。

  2. 在自己的类里面,拿到已创建的 std::shared_ptr,共享引用计数: Login 类继承 public std::enable_shared_from_this<Login> 可以实现在 Login 自己的类里面,用 this->shared_from_this() 取到自己的 std::shared_ptr<Login> 传递给 register 去用 std::weak_ptr<Login> 来记录自己这个 Login 对象。

Login 实现如下:

#include "login.h"
#include "ui_login.h"

#include <QBitmap>
#include <QPainter>
#include "register.h"

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(this); // error: 右值不能 绑定到弱指针里面的
    register_->set_login(std::make_shared<Register>(this)); // error: 两个智能指针管理 this,会有两次析构的问题
    // 从本类转化为共享的智能指针给 register 类
    register_->set_login(shared_from_this()); // 在调用这一行之前得有一个 std::shared_ptr<Login>,不然会有 std::bad_weak_ptr 的报错
}

void Login::on_registerButton_clicked() // login 界面 registerButton 按钮的点击事件
{
    this->close();
    register_->show();
}

main 函数的实现如下:

#include "login.h"

#include <QApplication>
#include <memory>

using namespace std;

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
//    Login w;
//    w.show();
    std::shared_ptr<Login> login = std::make_shared<Login>(); // 在下面使用 shared_from_this() 之前得有一个 std::shared_ptr<Login> 对象的哦
    login->initSignals();
    login->show();

    return a.exec();
}

点击运行按钮,程序运行起来就可以从登录界面切换到注册界面了,注册页面注册好了之后可以点击注册按钮跳转到登陆页面。

马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/ax020913/qt-project.git
git@gitee.com:ax020913/qt-project.git
ax020913
qt-project
Qt-Project
master

搜索帮助