用 VS Code 搞 Qt6:让信号和槽自动建立连接

vs,code,qt6,信号,自动,建立,连接 · 浏览次数 : 668

小编点评

您的代码示例非常清晰地解释了如何使用 Qt 的 connectSlotsByName 方法来实现对象之间的信号与槽连接。以下是一些关于您代码的补充和建议: 1. **关于 `objectName` 的设置:** 您在 `initUi` 函数中设置了 `btn1` 和 `btn2` 的 `objectName`,但没有在 `MyWindow` 类中定义 `objectName`变量,所以它们的 `objectName` 属性将默认值为 `nullptr`。建议您在类定义中定义 `objectName` 属性并设置其值。 2. **关于 `Q_OBJECT` 的使用:** 您在 `MyWindow` 类中使用了 `Q_OBJECT` 宏,这对于自动连接信号和槽非常重要,因为 `QObject` 类中的所有子类都应该使用它。 3. **关于 `setObjectName` 的设置:** 您在 `btn1` 和 `btn2` 中设置了不同的 `objectName`,这可以让它们在自动连接时被分开。 4. **关于 `initUi` 函数的调用:** 您在 `initUi` 函数中调用了 `connectSlotsByName` 方法来建立信号与槽连接。`connectSlotsByName` 方法接受一个参数,该参数是 `QObject` 对象的指针。由于您在 `MyWindow` 中实例化了一个 `QObject` 对象,因此可以使用 `this` 指向实例化对象。 5. **关于 `on_b1_clicked` 和 `on_b2_clicked` 的实现:** 您在 `MyWindow` 类中定义了两个槽函数,`on_b1_clicked` 和 `on_b2_clicked`,并在 `initUi` 函数中分别关联了它们到 `btn1` 和 `btn2` 的 `clicked` 事件信号。 6. **关于 `QMessageBox` 的用法:** 您在 `MyWindow` 类中使用 `QMessageBox` 显示了弹出框,这可以用于提示用户或显示错误消息。 7. **关于 `QObject` 和 `QWidget` 的关系:** `QWidget` 是 `QObject` 的基类,因此所有 `QObject` 对象都继承自 `QWidget` 类。 总体而言,您的代码示例非常清晰地展示了如何使用 `connectSlotsByName` 方法建立对象之间的信号与槽连接。通过对代码的细致分析,您可以进一步理解信号与槽的相关知识,并根据需要进行调整。

正文

Qt 具备让某个对象的信号与符合要求的槽函数自动建立连接。弄起来也很简单,只要调用这个静态方法即可:

QMetaObject::connectSlotsByName(...);

connectSlotsByName 方法需要一个参数,此参数的指针指向一个实例,这个实例自身的信号,以及它的子级对象的信号都会自动连接。

不过,在用的时候要注意以下几点,否则 connectSlotsByName 方法是不起作用的。

1、如果类是从某个 QObject 类派生的,比如常见的 QWidget 类,在类的声明中一个定要加上 Q_OBJECT 宏。这条老周在上一篇中说过,不加这个信号和槽不能建立连接。

2、对象一定要有 Name,即用 setObjectName 方法设置。虽然对象可以使用重复的名字,但不建议这样做,因为 connectSlotsByName 方法只要找到一个名字匹配的对象,就会停止查找。所以,就算你设置了 10 个名为“myButton” 的对象,结果也只能有一个会自动绑定信号和槽,其他的同名对象会忽略。

3、一定要在所有对象都初始化完毕,包括调用 setObjectName 方法设置对象名称后调用 connectSlotsByName 方法。这样才会有效。

setObjectName 方法用起来很简单,只要传递对象的名字即可,字符串类型。名字你可以随便取。例如

QLabel lb ...
lb.setObjectName("bug");

这时候,标签对象的名字是“bug”。

Slot 要支持被自动连接,函数(方法)也是有严格的命名规则的。你必须按照这个规则来,否则不会被识别。槽函数命名规则如下:

on_XXX_SSS

1、以“on”开头,每一节用下划线连起来。

2、XXX 是对象名,注意是对象名,就是用 setObjectName 方法设置的名称,不是你代码中定义的变量名。这个得注意,不能搞错了。

3、SSS 是信号。

比如,按钮的 clicked 信号,你让要自己写的槽能够被自动连接,就得这样命名槽函数:on_mybtn_clicked。其中,“mybtn”是对象名。

------------------------------------------- 银河分隔线 ------------------------------------------

下面咱们来动手做个例子,就好理解了。

一、先弄好 CMake 文件。

cmake_minimum_required(VERSION 3.0.0)
project(TestApp VERSION 0.1.0)

find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
set(CMAKE_AUTOMOC YES)

add_executable(TestApp WIN32 main.cpp app.h app.cpp)
target_link_libraries(TestApp PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets)

这个示例有三个文件。main.cpp 是写 main 函数的地方。app.h 和 app.cpp 中是咱们自定义的窗口类——从 QWidget 类派生。

二、定义 MyWindow 类,基类是 QWidget。

/** app.h **/
#include <QMetaObject>
#include <QWidget>
#include <QApplication>
#include <QObject>
#include <QLabel>
#include <QPushButton>
#include <QMessageBox>
#include <QVBoxLayout>
#include <QHBoxLayout>

class MyWindow : public QWidget 
{
    // 这个宏很容易忘了,忘了就不能连接信号和槽了
    Q_OBJECT

public:
    // 构造函数
    MyWindow(QWidget* parent = nullptr);
    // 析构函数
    ~MyWindow();

private:
    // 私有函数
    void initUi(void);
    // 以下是用到的部件(控件)
    QPushButton *btn1;
    QPushButton *btn2;
    QLabel *lb;
    // 布局
    QVBoxLayout *layout;
    QHBoxLayout *sublayout;

    // 这几个函数是用于自动绑定的槽
private slots:
    void on_b1_clicked();
    void on_b2_clicked();
};

所有 QObject 的子类,想使用 Signal 和 Slot ,必须调用 Q_OBJECT 宏。这里有两个按钮,on_b1_clicked 和 on_b2_clicked 都是槽。要让两个按钮自动连接,必须分别设置它的 object name 为 “b1” 和 “b2”。

三、下面是 initUi 函数的实现代码,用于初始化窗体。

void MyWindow::initUi()
{
    // 按钮1
    btn1 = new QPushButton(this);
    // 设置按钮1的文本
    btn1 -> setText("左边");
    // 重要:给它个名字
    btn1 -> setObjectName("b1");

    // 按钮2
    btn2 = new QPushButton(this);
    // 设置按钮2的文本
    btn2 -> setText("右边");
    // 重要:设置名称
    btn2 -> setObjectName("b2");

    // 标签
    lb = new QLabel("请点击下面的按钮", this);

    // 布局
    layout = new QVBoxLayout(this);
    layout -> addWidget(lb, 0, Qt::AlignTop);
    sublayout = new QHBoxLayout(this);
    // 添加要布局的组件
    sublayout -> addWidget(btn1);
    sublayout -> addWidget(btn2);
    layout->addLayout(sublayout);

    // 窗口
    this -> setWindowTitle("示例王");
    this -> resize(240, 100);
}

调用按钮对象的 setObjectName 方法就可以为其分配名称。注意在调用 QPushButton 类的构造函数时,要把当前窗口的指针传递给 parent 参数,使用按钮成为 MyWindow 的子级对象。这样后面才能做信号与槽的自动连接。

四、在 MyWindow 类构造函数中,先调用 initUi ,再调用 connectSlotsByName 静态方法。

MyWindow::MyWindow(QWidget *parent)
    : QWidget::QWidget(parent)
{
    // 调用以下函数,初始化UI
    initUi();
    // 一定要在所有东东都初始化完毕后调用才有效
    QMetaObject::connectSlotsByName(this);
}

五、下面是两个槽函数的实现。功能简单,用 QMessageBox 显示弹出框。

void MyWindow::on_b1_clicked()
{
    QMessageBox::information(this, "好消息", "左转是男厕", QMessageBox::Ok);
}
void MyWindow::on_b2_clicked()
{
    QMessageBox::information(this, "好消息", "右转是女厕", QMessageBox::Ok);
}

六、在 main.cpp 中写 main 函数。

#include "app.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    // 实例化窗口
    MyWindow wind;
    // 显示窗口
    wind.show();
    // 消息循环
    return app.exec();
}

 

运行结果如下面超清动画所示。

从结果可以看到,名为“b1”的按钮自动将 clicked 信号连接到 on_b1_clicked 函数;名为“b2”的按钮自动将 clicked 信号连接到 on_b2_clicked 函数。

好了,今天的主题咱们就聊到这儿了。

与用 VS Code 搞 Qt6:让信号和槽自动建立连接相似的内容:

用 VS Code 搞 Qt6:让信号和槽自动建立连接

Qt 具备让某个对象的信号与符合要求的槽函数自动建立连接。弄起来也很简单,只要调用这个静态方法即可: QMetaObject::connectSlotsByName(...); connectSlotsByName 方法需要一个参数,此参数的指针指向一个实例,这个实例自身的信号,以及它的子级对象的信

【Qt6】QWindow类可以做什么

原来的水文标题是“用 VS Code 搞 Qt6”,想想还是直接改为“Qt6”,反正这个用不用 VS Code 也能搞。虽然我知道大伙伴们都很讨厌 CMake,但毕竟这厮几乎成了 C++ 的玩家规范了。Qt 也算识大体,支持用 CMake 来构建程序。所以,只要你用的是能写 C++ 的工具,理论上都

VSCode中打开NodeJS项目自动切换对应版本的配置

这几年搞了不少静态站点,有的是Hexo的,有的是VuePress的。由于不同的主题对于NodeJS的版本要求不同,所以本机上不少NodeJS的版本。 关于如何管理多个NodeJS版本,很早之前就写过用nvm来管理的相关文章,这里就不赘述了,有需要的可以看这篇[Node.js环境搭建](https:/

【VS Code+Qt6】拖放操作

由于老周的示例代码都是用 VS Code + CMake + Qt 写的,为了不误导人,在标题中还是加上“VS Code”好一些。 上次咱们研究了剪贴板的基本用法,也了解了叫 QMimeData 的重要类。为啥要强调这个类?因为接下来扯到的拖放操作也是和它有关系。哦,对了,咱们先避开一下主题,关于剪

VS Code 有哪些好用的插件呢?【持续更新】

本文记录了笔者常用的 VS Code 插件,仅供参考,将持续更新。欢迎留言分享其他同样不做的插件。

桌面版vscode用免费的微软4核8G服务器做远程开发(编译运行都在云上,还能自由创建docker服务)

GitHub的Codespaces为个人用户提供了免费的4核8G服务器资源,今天就来实战如何用桌面版vscode连接codespace服务器做远程开发,把编译运行下载等耗时耗资源的操作都转移到云端进行,还能为应用创建各种docker服务,这都不要钱!

记一次 .NET 某餐饮小程序 内存暴涨分析

一:背景 1. 讲故事 前些天有位朋友找到我,说他的程序内存异常高,用 vs诊断工具 加载时间又太久,让我帮忙看一下到底咋回事,截图如下: 确实,如果dump文件超过 10G 之后,市面上那些可视化工具分析起来会让你崩溃的,除了时间久之外这些工具大多也不是用懒加载的方式,比如 dotmemory 会

文心一言 VS 讯飞星火 VS chatgpt (112)-- 算法导论10.2 3题

三、用go语言,用一个单链表L实现一个队列。要求操作 ENQUEUE 和 DEQUEUE 的运行时间仍为O(1)。 文心一言: 在 Go 语言中,使用单链表实现队列的操作,包括入队(ENQUEUE)和出队(DEQUEUE),并保持操作的时间复杂度为 O(1),需要利用两个指针,一个指向队头,另一个指

文心一言 VS 讯飞星火 VS chatgpt (110)-- 算法导论10.2 1题

一、用go语言,单链表上的动态集合操作 INSERT 能否在 O(1)时间内实现?DELETE 操作呢? 文心一言: 在单链表中,INSERT 和 DELETE 操作的时间复杂度通常是 O(n),其中 n 是链表中的元素数量。这主要是因为当你插入或删除元素时,你需要遍历链表来找到正确的插入或删除位置

文心一言 VS 讯飞星火 VS chatgpt (107)-- 算法导论10.1 5题

五、用go语言,栈插入和删除元素只能在同一端进行,队列的插入操作和删除操作分别在两端进行,与它们不同的,有一种双端队列(deque),其插入和删除操作都可以在两端进行。写出4个时间均为 O(1)的过程,分别实现在双端队列的两端插入和删除元素的操作,该队列是用一个数组实现的。 文心一言: 在 Go 语