文章目录
- 
- 输入框实现 "Hello world"
 - 
- [用 Designer 实现](#用 Designer 实现)
 - 用代码实现
 
 - 按钮实现 "Hello world"
 
 
输入框实现 "Hello world"
用 Designer 实现
- 
创建一个
Widget项目 - 
双击
.ui文件进入Designer设计模式 - 
将左侧工具栏找到
Input Widget分类并将需要的控件拖动至程序框内
 - 
在控件中文本写入编辑内容

也可通过右侧的属性编辑区进行修改;

 - 
构建并运行

 
在使用Designer中添加/设计的控件一般是一XML的形式写进.ui(FromFile)文件中, 通过元编程的方式将XML转化为代码, 该项目所使用的框架是Widget框架, 因此在编译后项目中会存在一个ui_widget.h头文件, 在这个文件中可以看到使用Designer中添加的控件;

因此使用Designer添加的控件无需手动去设置链接对象树;
用代码实现
代码实现通常在使用框架的源文件中的构造函数中对控件进行编辑;
例如此处使用的是widget框架, 对应的需要再widget.cpp中的class Widget的构造函数中对控件进行编辑, 或者如果有一个子控件类继承了这个类, 则在这个派生类的构造函数中去设置控件;
            
            
              cpp
              
              
            
          
          #include "widget.h"
#include "ui_widget.h"
#include <QLineEdit> // 包含头文件 <QLineEdit>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QLineEdit* _edit = new QLineEdit(this); // 创建一个 QLineEdit 实例并指名父对象为 this
    _edit->setText("Hello World"); // 在 QLineEdit 中设置文本 "Hello World"
}
Widget::~Widget()
{
    delete ui;
}
        
在这里的控件位于左上角, 本质上是没有设置控件在窗口的具体位置;
按钮实现 "Hello world"
用 Designer 实现
使用Designer实现与文本框的实现方式一致;
即拖动Buttons中的控件至UI界面完成实现;

构建后运行结果为如下:

但在这个按钮中点击是无反应的, 本质上是没有设置信号和槽;
- 
信号和槽
信号和槽本质上是通过按钮对应的一些行为绑定对应的处理函数, 如当点击按钮后, 可能触发的是一个
click事件, 这个事件会调用所绑定的处理函数从而进行一些变化;通常情况下载
QT中使用connect()来绑定一个控件具体某个行为的处理函数;该
connect()与socket中的connect()用途不同, 属于同名但不同作用和意义;需要注意的是, 槽并不是一个容器, 与可能抽象理解的凹槽不太一样;

并不能抽象理解为这种槽;
槽本身是一种函数, 全程为槽函数, 本质上是与信号通过
connect()函数的绑定关系;硬要说的话可以这么说:
槽函数就是普通的成员函数, 没有"空槽"的概念, 一个信号可以连接槽函数, 也可以不连接任何槽函数, 当连接槽函数时收到对应信号将会调用槽函数对信号进行处理, 若是不连接任何槽函数时将会忽略发送的信号;
 
以该例子来说, 所采用的控件为QPushButton, 该控件的信号通常有如下:
- 
pressed()信号鼠标按下时触发;
对应的事件处理函数是
mousePressEvent(); - 
clicked()信号鼠标松开时触发, 如果鼠标拖拽到按钮区域外再释放则不会触发;
对应的事件处理函数是
mouseReleaseEvent(); - 
released()信号鼠标松开时触发, 按下后鼠标拖拽到按钮区域外再释放仍然会触发;
对应的事件处理函数是
mouseReleaseEvent(); - 
toggled()信号设置
setCheckable(true)后单击按钮才会触发该信号;一般用于多个按钮组成
QButtonGroup(true)设置按钮间互斥;其中
toggled()信号主要关心的是按钮状态的变化, 而不是按钮是否被点击; 
信号和槽本质上是这样的, 假设信号是clicked()信号, 当一个按钮被单击(单击包含点击和释放)后, 这个单击本身就是一个事件, 按下和释放时将会自动调用对应的处理函数(按下的处理函数为mousePressEvent(), 释放为mouseReleaseEvent()), 判断按下和释放的时间(两个事件处理的时间), 按下和释放是否在同一控件区域内, 按下后鼠标是否移出按钮区域等条件来向控件发送对应的信号, 当发送clicked()信号后, 由于connect()绑定了对应信号的槽函数, 将会调用对应的信号处理函数进行一个处理;
按钮添加点击处理
上文提到, 若是需要使用点击需要设置信号和槽, 即通过connect()函数来绑定对应信号的信号处理函数(槽函数);
- 
connect()函数经过版本的迭代,
QT支持两种语法来实现连接信号与槽;这里主要演示的是新语法;
cppQObject::connect(sender, &SenderType::signal, receiver, &ReceiverType::slot, Qt::ConnectionType type = Qt::AutoConnection);参数解释如下:
- 
sender发送信号的对象指针;
如果控件对象是通过
Qt Designer来创建的, 那么需要使用ui -> button_name的方式, 即通过ui对象指向对应指针的方式来进行传参;否则可以直接传入一个
QObject子类对象, 如使用代码创建控件时可以直接将控件变量名直接传入(前提是控件对象是new出来的); - 
&SenderType::signal发送者类型的信号成员函数地址;
表示槽函数对应的信号种类, 可能为
pressed,clicked等信号, 信号一定要匹配对应的控件对象类型; - 
receiver接收信号的对象指针;
表示谁来处理这个信号, 通常为槽函数的拥有者, 需要传入该对象的指针;
当然如果槽函数是全局函数,
lamdba表达式或是静态成员函数,receiver可以是任意对象, 甚至是nullptr, 因为本质上并不是由receiver来调用这些函数,receiver本质上是管理线程的生命周期, 即当信号被发射后Qt内部将会形成一个事件或是直接调用槽(取决于第五个参数的连接方式), 而调用槽时所在的线程是不同的, 当槽位上述几种函数时分为两种情况:- 
receiver为nullptr槽函数在信号发射的线程中执行;
 - 
receiver不为nullptr槽函数在
receiver所在线程执行(具体方式由第五个参数Qt::ConnectionType type决定); 
 - 
 - 
&ReceiverType::slot槽函数的函数指针, 可以是成员函数, 全局函数, 静态成员函数或者是
Lamdba表达式; - 
Qt::ConnectionType type = Qt::AutoConnection连接情况分为以下几种:
- 
Qt::DirectConnection槽函数在信号发射线程立即执行;
 - 
Qt::QueuedConnection槽函数在
receiver所在线程的事件循环中执行; - 
Qt::AutoConnectionQt自动判断(同线程情况下为DirectConnection, 不同线程为QueuedConnection); - 
Qt::BlockingQueuedConnection类似
Queued, 但发送线程会阻塞等待; 
 - 
 
 - 
 
在之前的操作中已经创建了一个QPushButton按钮控件对象, 该按钮使用Designer创建, 因此在使用connect()函数时sender需要通过ui对象指向该控件对象的objectName(本质上就是控件指针变量名), 此处变量名为pushButton_1;
在此之前需要一个信号处理函数(槽函数), 这里的槽函数选择在Widget中创建对应的成员函数, 同样在widget.h中进行声明, 在widget.cpp中进行实现;
本次实现为点击按钮进行文本的转换, 单击信号为clicked信号;
            
            
              cpp
              
              
            
          
          ////////////////////////////////////////
/////////////*  widget.h  */////////////
////////////////////////////////////////
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    void handleClick_1(); // 槽函数声明
    ~Widget();
private:
    Ui::Widget *ui;
};
////////////////////////////////////////
/////////////* widget.cpp */////////////
////////////////////////////////////////
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(ui->pushButton_1, &QPushButton::clicked, this, &Widget::handleClick_1); // 通过 connect 函数来绑定信号与槽
}
void Widget::handleClick_1(){ // 槽函数定义
    
    // 判断 pushButton_1 控件中的文本 是否为 "Click me!" 
    // 是则改变文本为 "Hello world"
    if(ui->pushButton_1->text() == QString("Click me!")){ 
        ui->pushButton_1->setText("Hello world");
    }
    else{
        // 否则改变文本为 "Click me!"
        ui->pushButton_1->setText("Click me!");
    }
}
Widget::~Widget()
{
    delete ui;
}
        在这段代码中的Widget::Widget(QWidget *parent)构造函数中的connect()函数中第一个参数采用了ui->pushButton_1进行传参, 这里需要传的参数是信号发送的控件指针;
在项目构建完毕后, 可以从项目结构里的ui_widget.h文件中可以看到;

通过构建后, class Ui_Widget类中出现了一个QPushButton *pushButton_1的成员变量;
该成员则是所传的参数;
一般情况下当使用QtCreator中的Designer创建控件对象后, 当保存.ui文件时, QtCreator将会分析.ui的xml文件, 并在ui_xxx.h中补全相应代码;
因此可以直接使用ui->的方式对Designer创建的控件对象进行指向;
用代码实现
此次用代码实现的按钮Button功能与上文相同, 同样为点击前文本为"Click me!", 点击后为"Hello world";
通常情况下, 所创建的控件可能需要在槽中传入或是调用自己的成员, 而指针变量是局部变量, 当构造函数结束后这个局部变量(指针变量)的生命周期也会随之结束, 因此无法再槽中调用这个指针变量, 而较好的解决办法是将控件对象作为框架的成员变量进行创建定义;
            
            
              cpp
              
              
            
          
          class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    void handleClick();
    ~Widget();
private:
    Ui::Widget *ui;
    QPushButton *myButton;
};
        通常也支持推荐使用这种方式创建, 这样既可以对其进行更好的生命周期管理, 也能增加访问便利性;
通常情况下若是将控件对象作为成员函数, 通常建议在类中声明, 在构造函数中的初始化列表或是构造函数函数体内进行初始化;
            
            
              cpp
              
              
            
          
          Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
    , myButton(new QPushButton(this))
{
    ui->setupUi(this);
    myButton->setText("Click me!");
    connect(myButton, &QPushButton::clicked, this, &Widget::handleClick);
}
void Widget::handleClick(){
    if(myButton->text() == QString("Click me!")){
        myButton->setText("Hello world");
    }
    else{
        myButton->setText("Click me!");
    }
}
Widget::~Widget()
{
    delete ui;
}