『 QT 』Hello World控件实现指南

文章目录


输入框实现 "Hello world"

用 Designer 实现

  1. 创建一个Widget项目

  2. 双击.ui文件进入Designer设计模式

  3. 将左侧工具栏找到Input Widget分类并将需要的控件拖动至程序框内

  4. 在控件中文本写入编辑内容

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

  5. 构建并运行

在使用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支持两种语法来实现连接信号与槽;

    这里主要演示的是新语法;

    cpp 复制代码
    QObject::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内部将会形成一个事件或是直接调用槽(取决于第五个参数的连接方式), 而调用槽时所在的线程是不同的, 当槽位上述几种函数时分为两种情况:

      1. receivernullptr

        槽函数在信号发射的线程中执行;

      2. receiver不为nullptr

        槽函数在receiver所在线程执行(具体方式由第五个参数Qt::ConnectionType type决定);

    • &ReceiverType::slot

      槽函数的函数指针, 可以是成员函数, 全局函数, 静态成员函数或者是Lamdba表达式;

    • Qt::ConnectionType type = Qt::AutoConnection

      连接情况分为以下几种:

      1. Qt::DirectConnection

        槽函数在信号发射线程立即执行;

      2. Qt::QueuedConnection

        槽函数在receiver所在线程的事件循环中执行;

      3. Qt::AutoConnection

        Qt自动判断(同线程情况下为DirectConnection, 不同线程为QueuedConnection);

      4. 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将会分析.uixml文件, 并在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;
}
相关推荐
用户805533698034 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner4 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz9 天前
QML Hello World 入门示例
qt
xcyxiner12 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner13 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner13 天前
DicomViewer (添加模型类)3
qt
xcyxiner14 天前
DicomViewer (目录调整) 2
qt
xcyxiner14 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00616 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术16 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript