Qt6.0开发 第二章 GUI程序设计基础

第二章 GUI程序设计基础


窗口相关文件

按照第一章所给提示创建一个新project,我们得到了下面的代码:

widget.h:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp:

#include "widget.h"
#include "ui_widget.h"

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

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

main.cpp:

#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

上面的内容便是一个完整的新项目的代码部分了.

下面我们将逐文件逐语句对其进行解释:

widget.h

首先对最外层的宏进行分析,不难发现,这是从C语言开始防止重复include的宏设计:

#ifndef WIDGET_H
#define WIDGET_H
//...
#endif // WIDGET_H

include语句将QWidget引入,以便对QWidget类进行继承:

#include <QWidget>

在include语句后又定义了一个继承自QWidget的自定义Widget类.

同时,其还声明了一个名称为Ui的名称空间:

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

注意:此处的Widget不是此文件中的Widget,而是ui_widget中定义的一个类,所以我们区分Ui::Widget是窗口ui类,是为了用于描述可视化设计的窗口界面的.

下面即为Widget的定义了.

namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
};

widget.cpp

在其中,首先就和一般C++多文件程序一样,同名的.cpp文件对包含类声明与形式定义的.h文件进行include.

#include "widget.h"

接着对自定义Widget类的构造函数进行了定义:

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

结合widget.h中的声明:

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
};

解释上面initial-list的代码,有:

:QWidget(parent)//从父QWidget继承,同时默认为nullptr
,ui(new Ui::Widget)//存在一个新分配的Ui::Widget

然后对构造函数函数体进行解释:

ui->setupUi(this);//通过Ui::Widget对自定义Widget的Ui进行装载

在setupUi()中会创建窗口上的所有界面组件,并且以Widget窗口作为所有组建的父容器.

而析构函数中则为简单的释放Ui::Widget对象.

delete ui;

注意:ui_widget.h是UI文件widget.ui被UIC编译后自动生成的文件.该文件中定义了窗口UI类.

#include "ui_widget.h"

main.cpp

开门见山,对自定义widget.h的include:

#include "widget.h"

接着由于函数体中要对QApplication进行初始化,对QApplication进行include:

#include <QApplication>

主函数体本身没有太大变化:

int main(int argc, char *argv[])
{
    //do something
}

而其中每一句语句的含义见下:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);//定义并创建应用程序
    Widget w;//定义并创建窗口对象
    w.show();//显示窗口
    return a.exec();//运行应用程序,开始应用程序的消息循环和事件处理
}

界面组件布局管理

Qt的UI设计具有布局(layout)功能.所谓布局,就是指界面上组件的排列方式.

伙伴关系

点击Qt Designer工具栏上的Edit Buddies按钮进入伙伴关系编辑状态.

在伙伴状态下可以选中一个标签,按住鼠标左键将其拖向控件.

将拥有伙伴关系的标签text属性改为<name>+(&+<char>)形式的字串.则在运行时按下alt+<char>便可跳转焦点至相关控件.

Tab顺序

点击Qt Designer工具栏上的Edit Tab Order按钮进入Tab顺序编辑状态.

Tab顺序是指程序在运行时,按下键盘上Tab键时输入焦点的移动顺序.

进入Tab顺序编辑状态后,界面上会显示具有Tab顺序的组件的Tab顺序编号,依次按希望的顺序点击组件就可以重排Tab顺序.

信号与槽

信号(signal): 特定情况下被发射的通知.GUI程序设计的主要工作就是对界面上各组件的信号进行响应.

槽(slot): 是对信号进行响应的函数.槽就是函数,所以也称槽函数.槽函数与一般函数不同的是:槽函数与信号关联,当信号被发射时,关联的槽函数被自动运行.

信号与槽是通过QObject::connect()实现的,使用connect()函数的基本格式为:

QObject::connect(sender, SIGNAL(signal()),receiver,SLOT(slot()));

其中,sender是发射信号的对象的名称;signal()是信号,信号可以看作特殊的函数,需要带有括号,有参数时还需要指名各参数类型;receiver是接收信号的对象的名称;slot()是槽函数,需要带有括号,有参数时还需要指名各参数类型.

SIGNAL和SLOT是Qt的宏,分别用于指明信号和槽函数,并将它们的参数转换为相应的字符串.

关于信号与槽的使用,有下列规则:

  • 一个信号可以连接多个槽函数.

  • 多个信号可以连接一个槽函数.

  • 一个信号可以连接另一个信号,如:

connect(spinNum,SIGNAL(signal_1(int)),this,SIGNAL(signal_2(int)));
  • 严格的情况下,信号与槽的参数个数与类型需要一致,至少信号参数不能少于槽参数.

  • 在使用信号与槽的类中,必须在类的定义中插入宏Q_OBJECT.

  • 当一个信号被发射时,与其关联的槽函数通常被立即运行,只有所有所有关联槽函数运行完,才运行发射信号后的代码.

Qt的界面组件都是从QWidget继承而来的,都支持信号与槽的功能.每个类都有一些内建的信号和槽函数.

信号与槽的使用

如果需要通过Qt Designer可视化设计某个组件的信号的槽函数,选择Qt Designer下侧Action编辑器旁边的 Signals and Slots Editor 进行新增即可.

如果希望手动对信号的槽函数进行定义,而非直接生成在ui_widget.h中.那么可以选择一个组件,然后右键选择 "转到槽..."

实例:第一个Qt应用程序

选择Dialog类开始,且不继承(即不需要ui界面辅助)

Dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>

#include <QCheckBox>
#include <QPushButton>
#include <QRadioButton>
#include <QPlainTextEdit>

#include <QHBoxLayout>
#include <QVBoxLayout>

class QPlainTextEdit;
class QRadioButton;
class QPushButton;
class QCheckBox;

class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = nullptr);
    ~Dialog();
private:
    QCheckBox *chkBoxUl,*chkBoxIt,*chkBoxBf;
    QPushButton *btnOk,*btnExit,*btnClear;
    QRadioButton *radioBlack,*radioRed,*radioBlue;
    QPlainTextEdit *txtEdit;

private slots:
    //勾选框
    void do_checkBoxUl(bool checked);
    void do_checkBoxBf(bool checked);
    void do_checkBoxIt(bool checked);

    //按钮
    void do_pushButtonOk();
    void do_pushButtonExit();
    void do_pushButtonClear();

    //选择框
    void do_radioColor();

    //测试自定义信号
    void do_reactionToSignal();
signals:
    void do_checkedRadio();
};

#endif // DIALOG_H

Dialog.cpp

#include "dialog.h"

void Dialog::do_checkBoxUl(bool checked)
{
    QFont font= txtEdit->font();
    font.setUnderline(checked);
    txtEdit->setFont(font);
    return;
}

void Dialog::do_checkBoxIt(bool checked)
{
    QFont font= this->txtEdit->font();
    font.setItalic(checked);
    this->txtEdit->setFont(font);
    return;
}

void Dialog::do_checkBoxBf(bool checked)
{
    QFont font= this->txtEdit->font();
    font.setBold(checked);
    this->txtEdit->setFont(font);
    return;
}

void Dialog::do_pushButtonOk()
{
    emit do_checkedRadio();
    //this->accept();
    return;
}

void Dialog::do_pushButtonExit()
{
    this->close();
    return;
}

void Dialog::do_pushButtonClear()
{
    txtEdit->clear();
    return;
}

void Dialog::do_radioColor()
{
    QPalette plet= txtEdit->palette();

    if(radioBlack->isChecked())
        plet.setColor(QPalette::Text,Qt::black);
    if(radioRed->isChecked())
        plet.setColor(QPalette::Text,Qt::red);
    if(radioBlue->isChecked())
        plet.setColor(QPalette::Text,Qt::blue);
    txtEdit->setPalette(plet);

    return;
}

void Dialog::do_reactionToSignal()
{
    QPalette plet= txtEdit->palette();
    plet.setColor(QPalette::Text,Qt::green);
    txtEdit->setPalette(plet);
    return;
}

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    //新建竖向布局
    QVBoxLayout *VLay= new QVBoxLayout;

    //勾选框
    chkBoxUl= new QCheckBox("下划线");
    chkBoxIt= new QCheckBox("倾斜体");
    chkBoxBf= new QCheckBox("加粗体");

    QHBoxLayout *HLay1= new QHBoxLayout;
    HLay1->addWidget(chkBoxUl);
    HLay1->addWidget(chkBoxIt);
    HLay1->addWidget(chkBoxBf);

    connect(chkBoxUl,SIGNAL(clicked(bool)),this,SLOT(do_checkBoxUl(bool)));
    connect(chkBoxIt,SIGNAL(clicked(bool)),this,SLOT(do_checkBoxIt(bool)));
    connect(chkBoxBf,SIGNAL(clicked(bool)),this,SLOT(do_checkBoxBf(bool)));

    //选择框
    radioBlack= new QRadioButton("黑色");
    radioRed= new QRadioButton("红色");
    radioBlue= new QRadioButton("蓝色");

    QHBoxLayout *HLay2= new QHBoxLayout;
    HLay2->addWidget(radioBlack);
    HLay2->addWidget(radioRed);
    HLay2->addWidget(radioBlue);

    connect(radioBlack,SIGNAL(clicked()),this,SLOT(do_radioColor()));
    connect(radioRed,SIGNAL(clicked()),this,SLOT(do_radioColor()));
    connect(radioBlue,SIGNAL(clicked()),this,SLOT(do_radioColor()));
    //按钮
    btnOk= new QPushButton("确定");
    btnClear= new QPushButton("清除");
    btnExit= new QPushButton("退出");

    QHBoxLayout *HLay3= new QHBoxLayout;
    HLay3->addWidget(btnOk);
    HLay3->addWidget(btnClear);
    HLay3->addWidget(btnExit);

    connect(btnOk,SIGNAL(clicked()),this,SLOT(do_pushButtonOk()));
    connect(btnClear,SIGNAL(clicked()),this,SLOT(do_pushButtonClear()));
    connect(btnExit,SIGNAL(clicked()),this,SLOT(do_pushButtonExit()));
    //文本栏
    txtEdit= new QPlainTextEdit;
    txtEdit->setPlainText("Qt6.0 学习\n第二章 项目二");

    //测试自定义信号函数
    connect(this,SIGNAL(do_checkedRadio()),this,SLOT(do_reactionToSignal()));

    VLay->addLayout(HLay1);
    VLay->addLayout(HLay2);
    VLay->addWidget(txtEdit);
    VLay->addLayout(HLay3);

    //布局应用
    setLayout(VLay);
}

Dialog::~Dialog() {}

main.cpp则默认即可.

最后,最重要的建议:去看视频跟着学,里面很多书里没有的操作https://www.bilibili.com/video/BV1km4y1k7CW

posted @ 2024-02-09 22:42  Mesonoxian  阅读(29)  评论(0编辑  收藏  举报