『 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;
}
相关推荐
在坚持一下我可没意见2 小时前
HTTP 协议基本格式与 Fiddler 抓包工具实战指南
java·开发语言·网络协议·tcp/ip·http·java-ee·fiddler
樱花开了几轉2 小时前
element ui下拉框踩坑
开发语言·javascript·ui
报错小能手2 小时前
C++笔记(面向对象)RTTI操作符
开发语言·c++·笔记
草明2 小时前
Go 的 IO 多路复用
开发语言·后端·golang
晟盾科技3 小时前
报表类系统后端API设计思路
开发语言·windows·php
AI纪元故事会3 小时前
冰泪与雨丝:一个AI的Python挽歌
开发语言·人工智能·python
我不是程序猿儿3 小时前
【C#】WinForms 控件句柄与 UI 刷新时机
开发语言·ui·c#
十五年专注C++开发3 小时前
Qt-Nice-Frameless-Window: 一个跨平台无边框窗口(Frameless Window)解决方案
开发语言·c++·qt
凯歌的博客4 小时前
python虚拟环境应用
linux·开发语言·python
祈祷苍天赐我java之术4 小时前
如何在Java中整合Redis?
java·开发语言·redis