QT学习笔记-QStringList,QTimer

news2024/12/27 3:30:45

QStringList-存储和管理一系列的字符串

在Qt框架中,QStringList 是一个模板类 QList<QString> 的特化,专门用于处理 QString 对象(即Qt中的字符串)的列表。当你看到这样的声明:

QStringList m_rec_topicList;

这里,m_rec_topicList 是一个 QStringList 类型的变量,用于存储和管理一系列的字符串(QString 对象)。以下是对 QStringList 在这个上下文中的详细讲解:

一、基本特性

  • 类型QStringList 是 QList<QString> 的一个特化,因此它继承了 QList 的所有功能,并针对字符串进行了优化。
  • 存储QStringList 内部使用动态数组来存储字符串,这意味着它可以根据需要自动调整大小。
  • 线程安全QStringList 本身并不是线程安全的。如果在多线程环境中使用,需要采取适当的同步措施。

二、主要功能

  1. 添加和移除字符串
    • append(const QString &str):在列表末尾添加一个字符串。
    • prepend(const QString &str):在列表开头添加一个字符串。
    • insert(int i, const QString &str):在指定位置插入一个字符串。
    • removeAt(int i):移除指定位置的字符串。
    • removeOne(const QString &str):移除列表中第一次出现的指定字符串。
    • removeAll(const QString &str):移除列表中所有出现的指定字符串。
  2. 访问字符串
    • at(int i) const:返回指定位置的字符串(带边界检查)。
    • operator[](int i) const:返回指定位置的字符串(不带边界检查,如果索引超出范围,结果未定义)。
    • first() const:返回列表中的第一个字符串。
    • last() const:返回列表中的最后一个字符串。
    • value(int i, const QString &defaultValue = QString()) const:返回指定位置的字符串,如果索引超出范围,则返回默认值。
  3. 遍历
    • 可以使用 foreach 循环(Qt特有的语法糖)或基于范围的 for 循环(C++11及以后)来遍历 QStringList
  4. 排序
    • sort(Qt::SortOrder order = Qt::AscendingOrder):对列表进行排序,可以指定升序或降序。
  5. 查找
    • indexOf(const QString &str, int from = 0) const:返回指定字符串在列表中第一次出现的索引,从指定位置开始搜索。
    • lastIndexOf(const QString &str, int from = -1) const:返回指定字符串在列表中最后一次出现的索引,从指定位置开始向后搜索(如果 from 为 -1,则从列表末尾开始搜索)。
    • contains(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const:检查列表中是否包含指定字符串,可以指定是否区分大小写。
  6. 连接和分割
    • join(const QString &separator) const:将列表中的字符串连接成一个字符串,使用指定的分隔符。
    • (注意:分割操作通常使用 QString 的 split() 方法,而不是 QStringList 的方法,因为分割的结果可以直接赋值给 QStringList。)

三、使用示例

#include <QStringList>
#include <QDebug>

int main() {
QStringList topicList;
topicList.append("Qt");
topicList.append("C++");
topicList.prepend("Programming");

qDebug() << "Topic list:" << topicList;

// 排序
topicList.sort();
qDebug() << "Sorted topic list:" << topicList;

// 查找
int index = topicList.indexOf("C++");
if (index != -1) {
qDebug() << "C++ found at index:" << index;
}

// 连接
QString joinedList = topicList.join(", ");
qDebug() << "Joined list:" << joinedList;

return 0;
}

 

在这个示例中,我们创建了一个 QStringList 变量 topicList,并向其中添加了一些字符串。然后,我们对列表进行了排序、查找和连接操作。

QTimer-指定的时间间隔后发出信号

在C++中,特别是使用Qt框架时,QTimer 是一个常用的类,它用于在指定的时间间隔后发出信号。当你看到这样的声明:

QTimer *publishTimer = nullptr;

 

这里,publishTimer 是一个指向 QTimer 对象的指针,并且它被初始化为 nullptr,意味着它当前不指向任何有效的 QTimer 对象。以下是对这个声明的详细讲解:

一、QTimer 类

  • 功能QTimer 类提供了定时器功能,它可以在指定的时间间隔后发出 timeout() 信号。这个信号可以被连接到任何槽函数,以实现定时执行某些操作的目的。
  • 继承QTimer 继承自 QObject 类,因此它拥有 QObject 的所有功能和特性,比如信号和槽机制、对象树管理等。
  • 精度QTimer 的精度取决于底层操作系统的定时器机制,通常对于毫秒级别的定时是足够的,但对于更精确的定时(比如微秒级别),可能需要使用其他方法。

二、指针和nullptr

  • 指针:在C++中,指针是一种变量类型,它存储了另一个变量的内存地址。通过指针,你可以直接访问和操作内存中的数据。
  • nullptrnullptr 是C++11及以后版本中引入的一个字面常量,用于表示空指针。它取代了旧的 NULL 宏(通常定义为 0 或 (void*)0),提供了更好的类型安全性和可读性。
  • 初始化:将指针初始化为 nullptr 是一种良好的编程习惯,它可以防止指针在未经初始化的情况下被使用,从而避免潜在的内存访问错误。

三、publishTimer 的使用

  1. 创建 QTimer 对象
    在使用 publishTimer 之前,你需要为它分配一个有效的 QTimer 对象。这通常通过 new 关键字来完成:

publishTimer = new QTimer(this); // 假设当前代码在 QObject 的子类中,且 this 指向该对象
  1. 这里,this 参数指定了 QTimer 对象的父对象。在Qt中,将对象设置为另一个对象的子对象意味着子对象的生命周期将由父对象管理。当父对象被销毁时,它的所有子对象也会被自动销毁。

  2. 连接信号和槽
    创建 QTimer 对象后,你通常需要连接它的 timeout() 信号到一个槽函数,以便在定时器超时时执行特定的操作:

    connect(publishTimer, &QTimer::timeout, this, &YourClass::yourSlotFunction);

    这里,YourClass 是包含槽函数的类名,yourSlotFunction 是要在定时器超时时调用的槽函数名。

  3. 启动定时器
    使用 start() 方法来启动定时器,并指定时间间隔(以毫秒为单位):

    publishTimer->start(1000); // 每1000毫秒(即1秒)发出一次timeout信号
  4. 停止定时器
    使用 stop() 方法来停止定时器:

    publishTimer->stop();
  5. 清理
    如果你不再需要 publishTimer,并且想要释放它占用的内存,你应该先停止定时器(如果它正在运行),然后删除指针指向的对象。但是,由于 publishTimer 是父对象的子对象,在父对象被销毁时,publishTimer 也会被自动销毁。因此,在大多数情况下,你不需要手动删除它。然而,如果你将 publishTimer 设置为 nullptr(例如,在对象被销毁之前),这可以作为一种防御性编程措施,以防止在对象生命周期的后期意外地使用该指针。

    publishTimer->stop();
    publishTimer = nullptr; // 可选,但推荐作为防御性编程措施

请注意,在现代C++中(特别是使用Qt框架时),通常建议使用智能指针(如 QScopedPointer 或 QSharedPointer)来管理动态分配的对象,以避免手动管理内存和指针带来的复杂性。然而,在Qt中,由于对象树和父子关系的存在,以及 deleteLater() 方法的提供,手动管理内存的情况并不常见。

QScopedPointer /QSharedPointer-自动管理动态分配的内存,从而避免内存泄漏和悬挂指针

QScopedPointer-独占所有权-局部变量

  • 所有权QScopedPointer 提供了严格的独占所有权语义。这意味着一个 QScopedPointer 实例在其生命周期内拥有它所指向的对象,并且该对象不能被其他 QScopedPointer 或裸指针(raw pointer)共享。
  • 生命周期QScopedPointer 的生命周期与其所在的作用域(scope)紧密相关。当 QScopedPointer 超出其作用域时,它所指向的对象会被自动删除。
  • 用途QScopedPointer 通常用于局部变量,特别是当对象的生命周期应该严格局限于某个函数或代码块时。
  • 转移所有权:虽然 QScopedPointer 不支持复制,但它可以通过 QScopedPointer<T>::take() 方法转移所有权给另一个 QScopedPointer 或裸指针,并重置自身为 nullptr
#include <QScopedPointer>
#include <iostream>

int main() {
    // 使用 QScopedPointer 管理一个动态分配的整数数组
    QScopedPointer<int[]> scopedArray(new int[10]);

    // 初始化数组
    for (int i = 0; i < 10; ++i) {
        scopedArray[i] = i * i; // 例如,存储平方值
    }
    // 打印数组内容
    for (int i = 0; i < 10; ++i) {
        std::cout << scopedArray[i] << " ";
    }
    std::cout << std::endl;
    // 不需要手动删除数组,QScopedPointer 会在作用域结束时自动删除它
    return 0;
}

QSharedPointer-共享所有权-多个对象或线程

  • 所有权QSharedPointer 提供了共享所有权语义。这意味着多个 QSharedPointer 实例可以共享同一个对象,并且只有当最后一个 QSharedPointer 被销毁或重置时,对象才会被删除。
  • 生命周期QSharedPointer 的生命周期与其自身的生命周期以及它所指向的对象的共享计数有关。只要至少有一个 QSharedPointer 指向对象,对象就会保持存活。
  • 用途QSharedPointer 通常用于需要在多个对象或线程之间共享数据的场景。
  • 线程安全QSharedPointer 是线程安全的,可以在多个线程之间安全地共享和传递。

注意事项

  • 避免循环引用:在使用 QSharedPointer 时,要注意避免循环引用,这可能会导致内存泄漏。循环引用发生在两个或多个对象相互持有对方的 QSharedPointer 时。
  • 性能考虑:虽然 QSharedPointer 提供了方便的共享所有权管理,但它也引入了额外的开销,包括维护共享计数和可能的线程同步。因此,在性能敏感的场景中,要谨慎使用 QSharedPointer
  • 裸指针的使用:尽量避免使用裸指针来管理动态内存,而是使用智能指针来确保内存的正确释放。如果必须使用裸指针,请确保在适当的时候手动删除对象,并避免悬挂指针的问题。
#include <QSharedPointer>
#include <iostream>

class MyString {
public:
    MyString(const std::string &str) : data(new std::string(str)) {}
    ~MyString() {
        std::cout << "MyString destructor called, data: " << *data << std::endl;
        delete data;
    }
    std::string* getData() const {
        return data;
    }
private:
    std::string *data;
};

int main() {
    // 使用 QSharedPointer 管理一个动态分配的 MyString 对象
    QSharedPointer<MyString> sharedString1(new MyString("Hello, World!"));

    {
        // 创建另一个 QSharedPointer,它共享同一个 MyString 对象
        QSharedPointer<MyString> sharedString2 = sharedString1;

        // 打印字符串内容
        std::cout << *sharedString2->getData() << std::endl;

        // 在这个作用域结束时,sharedString2 会被销毁,但它不会删除 MyString 对象,
        // 因为还有 sharedString1 在共享它。
    }

    // 打印字符串内容(再次证明对象仍然存在)
    std::cout << *sharedString1->getData() << std::endl;

    // 当 sharedString1 被销毁时,它是最后一个共享 MyString 对象的 QSharedPointer,
    // 因此 MyString 对象会被删除,并且其析构函数会被调用。
    return 0;
}

QFile-创建、读取、写入和查询文件

在C++中,特别是当使用Qt框架时,QFile 类是一个用于文件操作的类。它提供了创建、读取、写入和查询文件的方法。当你看到这样的声明:

QFile *logFile;

这里,logFile 是一个指向 QFile 对象的指针。以下是对这个声明的详细讲解:

一、QFile 类

  • 功能QFile 类提供了对文件的读写访问。它继承自 QIODevice,因此具有所有基本的输入输出功能。
  • 创建文件:你可以使用 QFile 的构造函数来创建一个指向文件的指针,但此时文件并不会被立即打开。要打开文件,你需要调用 open() 方法。
  • 读写操作:一旦文件被打开,你可以使用 read()readLine()write() 等方法来读取和写入文件内容。
  • 错误处理QFile 提供了 error() 和 errorString() 方法来报告和处理文件操作中发生的错误。
  • 文件信息:你可以使用 size()exists()isReadable()isWritable() 等方法来查询文件的属性。

二、指针的使用

  • 动态分配:通常,你会使用 new 关键字来动态分配一个 QFile 对象,并将返回的指针赋值给 logFile

    logFile = new QFile("path/to/your/logfile.txt");
     

    这里,"path/to/your/logfile.txt" 是你想要操作的文件的路径。

  • 初始化:在声明 logFile 时,它并没有被初始化为指向任何有效的 QFile 对象。因此,在使用它之前,你需要确保它已经被正确地初始化和分配了内存。

  • 生命周期管理:由于 logFile 是一个指针,你需要负责它的生命周期管理。这包括在适当的时候删除它以避免内存泄漏:

    delete logFile;
    logFile = nullptr; // 可选,但推荐作为防御性编程措施
     

    然而,在Qt中,更常见的做法是使用智能指针(如 QScopedPointer 或 QSharedPointer)来自动管理内存。但请注意,QFile 通常不需要共享所有权,因此 QScopedPointer 可能是更合适的选择。然而,由于 QFile 的生命周期通常与它的使用场景紧密相关(例如,在打开和关闭文件期间),并且 QFile 对象通常不会很大,因此在实际应用中,直接使用裸指针并手动管理其生命周期也是常见的做法。

  • 使用RAII:在Qt中,更推荐的做法是使用RAII(Resource Acquisition Is Initialization)原则来管理资源。这意味着你应该在对象的构造函数中获取资源(如打开文件),并在析构函数中释放资源(如关闭文件)。这可以通过将 QFile 对象作为类的成员变量来实现,而不是使用指针。这样,当对象被销毁时,它的析构函数会自动被调用,从而释放资源。

三、示例代码

以下是一个使用 QFile 的简单示例,演示了如何打开文件、写入内容、关闭文件:

#include <QFile>
#include <QTextStream>
#include <QDebug>

int main() {
QFile logFile("logfile.txt");
if (!logFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
qDebug() << "Cannot open file for writing:" << logFile.errorString();
return -1;
}

QTextStream out(&logFile);
out << "This is a log message.\n";

logFile.close();
return 0;
}

 

在这个例子中,logFile 是一个 QFile 对象(而不是指针),它在作用域结束时会自动被销毁,从而关闭文件并释放相关资源。这是Qt中管理文件和其他资源的推荐方式之一。如果你确实需要使用指针(例如,因为你需要将 QFile 对象传递给多个函数或类,并且不想在每个地方都复制它),请确保你正确地管理了指针的生命周期。

QTextStream-基于文本的输入/输出功能

在Qt框架中,QTextStream 类是一个提供基于文本的输入/输出功能的类。它既可以用于读取数据(如从文件或字符串中),也可以用于写入数据(如到文件或字符串中)。当你看到这样的声明:

QTextStream *logStream;

 

这里,logStream 是一个指向 QTextStream 对象的指针。以下是对这个声明的详细讲解:

一、QTextStream 类

  • 功能QTextStream 类提供了基于文本的输入/输出操作。它支持多种编码格式,并可以自动处理平台相关的换行符。
  • 构造函数QTextStream 可以与多种类型的设备关联,包括 QFileQIODeviceQString 和 QByteArray。构造函数需要传入一个指向这些设备的指针。
  • 读写操作:一旦与设备关联,你可以使用 QTextStream 的 << 和 >> 运算符来写入和读取文本数据。
  • 格式化QTextStream 提供了多种格式化选项,如设置字段宽度、精度和对齐方式。
  • 编码:你可以指定 QTextStream 使用的编码格式,例如 UTF-8、Latin1 等。

二、指针的使用

  • 动态分配:通常,你会使用 new 关键字来动态分配一个 QTextStream 对象,并将返回的指针赋值给 logStream。但是,请注意,QTextStream 对象通常不需要动态分配,因为它们通常与特定的输入/输出设备相关联,并且这些设备的生命周期通常与 QTextStream 的使用场景紧密相关。

    QFile logFile("logfile.txt");
    if (logFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
    QTextStream logStream(&logFile); // 直接与 QFile 对象关联,无需动态分配
    logStream << "This is a log message.\n";
    logFile.close();
    }
  • QTextStream out(&logFile); 创建了一个 QTextStream 对象 out,并将其与已经打开的 QFile 对象 logFile 关联起来。
  • 通过 out 对象,你可以使用流操作符(如 <<)将文本数据写入到 logFile 指定的文件中。
  • 一旦 QTextStream 对象 out 与 QFile 对象 logFile 关联起来,你就可以使用 out 来输出文本了。
  • 例如,out << "This is a log message.\n"; 将字符串 "This is a log message.\n" 写入到文件中,并在末尾添加一个换行符。
  • 在这个例子中,logStream 是一个局部 QTextStream 对象,它直接与 logFile 对象关联。当 logFile 对象被销毁时(例如,当离开作用域时),它会自动关闭文件,并且与 logFile 关联的 QTextStream 对象也会自动失效。

  • 生命周期管理:如果你确实需要使用指针(例如,因为你需要将 QTextStream 对象传递给多个函数或类,并且不想在每个地方都复制它),请确保你正确地管理了指针的生命周期。但是,请注意,由于 QTextStream 通常与特定的输入/输出设备相关联,并且这些设备的生命周期通常是可以预测的,因此直接使用局部对象而不是指针通常是更好的选择。

  • 避免内存泄漏:如果你选择使用指针,并且动态分配了 QTextStream 对象,请确保在适当的时候删除它以避免内存泄漏。然而,由于 QTextStream 通常不需要动态分配,因此这种情况很少发生。

  • 使用智能指针:虽然 QTextStream 通常不需要动态分配,但如果你确实需要这样做,并且想要自动管理内存,你可以考虑使用 QScopedPointer(对于独占所有权的场景)或 QSharedPointer(对于共享所有权的场景)。但是,请注意,由于 QTextStream 的生命周期通常与它所关联的输入/输出设备的生命周期紧密相关,因此使用智能指针可能是不必要的,甚至可能导致不必要的复杂性。

三、推荐做法

在大多数情况下,建议直接使用局部 QTextStream 对象,并将它们与特定的输入/输出设备(如 QFileQString 等)直接关联。这样做可以简化代码,并减少内存管理的复杂性。如果你需要将 QTextStream 对象传递给多个函数或类,并且不想在每个地方都复制它,请考虑传递对输入/输出设备的引用或指针,并在需要的地方创建局部的 QTextStream 对象。

QMutex 

在Qt框架中,QMutex 是一个互斥锁(Mutex)类,它用于在多线程环境中保护共享数据或代码段,以防止同时访问导致的竞争条件(race conditions)或数据损坏。当你看到这样的声明:

QMutex logMutex;

 

这里,logMutex 是一个 QMutex 对象,它可以用作一个同步原语来确保只有一个线程可以访问特定的资源或代码段。以下是对这个声明的详细讲解:

一、QMutex 的作用

  • 互斥性QMutex 提供了互斥性,确保同一时间只有一个线程可以持有锁。
  • 保护共享资源:在多线程应用中,多个线程可能会尝试同时访问和修改共享资源(如全局变量、数据结构或文件)。QMutex 可以用来保护这些资源,防止数据竞争和不一致性。
  • 代码同步:除了保护数据外,QMutex 还可以用于同步代码的执行顺序,确保特定的操作按预期的顺序执行。

二、QMutex 的使用

  • 锁定和解锁:要使用 QMutex,你需要在访问共享资源之前调用 lock() 方法来获取锁。在访问完成后,你应该调用 unlock() 方法来释放锁。重要的是要确保在每条执行路径上都能释放锁,以避免死锁(deadlock)的发生。

    QMutex logMutex;
    
    // 在线程函数或某个方法中
    logMutex.lock();
    // 访问和修改共享资源
    // ...
    logMutex.unlock();
  • RAII 风格的锁管理:为了避免忘记解锁或由于异常导致的解锁失败,Qt 提供了 QMutexLocker 类。这是一个基于 RAII(Resource Acquisition Is Initialization)原则的锁管理器,它在构造时自动获取锁,并在析构时自动释放锁。

    QMutex logMutex;
    
    // 在线程函数或某个方法中
    {
    QMutexLocker locker(&logMutex);
    // 访问和修改共享资源
    // ...
    // 当 locker 对象离开作用域时,它会自动调用 unlock()
    }
  • 尝试锁定QMutex 还提供了 tryLock() 方法,它尝试获取锁但不阻塞。如果锁可用,tryLock() 将返回 true 并获取锁;如果锁不可用,它将立即返回 false 而不等待。

三、注意事项

  • 避免死锁:确保在每条执行路径上都能释放锁,特别是在使用异常处理时。
  • 性能考虑:频繁地锁定和解锁可能会影响性能,特别是在高并发环境中。因此,应该尽量减小锁的作用范围,并考虑使用其他同步机制(如读写锁、信号量)来优化性能。
  • 递归锁QMutex 默认不支持递归锁定(即同一个线程不能多次获取同一个锁)。如果需要递归锁定,请使用 QRecursiveMutex

四、应用场景

  • 日志记录:如变量名 logMutex 所暗示的,互斥锁常用于保护日志记录操作,确保多个线程不会同时写入日志文件,从而导致日志混乱。
  • 线程间通信:在多线程应用中,QMutex 可以与其他同步机制(如条件变量、信号槽)结合使用,以实现线程间的协调和通信。
  • 保护共享数据结构:当多个线程需要访问和修改同一个数据结构时(如链表、树、哈希表等),QMutex 可以用来确保数据的一致性和完整性。

 MQTT

在Qt框架中,QMqttClient 类是用于与MQTT协议服务器进行通信的一个客户端类。MQTT(Message Queuing Telemetry Transport)是一个轻量级的、基于发布/订阅模式的消息传输协议,广泛应用于物联网(IoT)场景中,以实现设备之间的可靠通信。当你看到这样的声明:

QMqttClient m_client;

这里,m_client 是一个 QMqttClient 对象,它表示一个MQTT客户端实例。以下是对这个声明的详细讲解:

一、QMqttClient 的作用

  • 连接服务器QMqttClient 允许你连接到MQTT协议的消息代理(broker)服务器。
  • 发布消息:一旦连接成功,你可以使用 QMqttClient 来发布(publish)消息到特定的主题(topic)。
  • 订阅主题:你还可以订阅(subscribe)一个或多个主题,以便接收这些主题上的消息。
  • 处理消息:当订阅的主题上有新消息发布时,QMqttClient 会通过信号和槽机制将消息传递给应用程序进行处理。

二、QMqttClient 的使用

  • 设置服务器地址和端口:在使用 QMqttClient 之前,你需要设置MQTT服务器的地址和端口。这通常通过调用 setHostname() 和 setPort() 方法来完成。

  • 设置客户端ID:每个MQTT客户端都需要一个唯一的客户端ID来标识自己。你可以通过调用 setClientId() 方法来设置它。

  • 建立连接:使用 connectToHost() 方法尝试连接到MQTT服务器。连接成功后,QMqttClient 会发出 connected 信号。

  • 发布消息:使用 publish() 方法发布消息到指定的主题。你可以指定消息的质量服务等级(QoS)和是否保留消息。

  • 订阅主题:使用 subscribe() 方法订阅一个或多个主题。当这些主题上有新消息时,QMqttClient 会发出 messageReceived() 信号。

  • 处理断开连接:如果连接断开,QMqttClient 会发出 disconnected 信号。你可以通过重新连接来处理这种情况。

三、信号和槽

QMqttClient 提供了多个信号来通知应用程序关于连接状态、消息接收等事件。你可以使用Qt的信号和槽机制来连接这些信号到你的槽函数中,以便处理这些事件。

四、注意事项

  • 线程安全QMqttClient 不是线程安全的。你应该确保在一个线程中创建和使用 QMqttClient 对象,并避免在多个线程之间共享它。
  • 错误处理:在处理MQTT连接和消息时,你应该添加适当的错误处理代码来处理可能的失败情况,如连接失败、发布消息失败等。
  • 资源释放:在不再需要 QMqttClient 对象时,你应该确保正确释放它所占用的资源。这通常意味着调用 disconnectFromHost() 方法来断开与MQTT服务器的连接,并删除 QMqttClient 对象。
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QTextEdit>
#include <QMqttClient>
#include <QDebug>

class MqttWidget : public QWidget {
    Q_OBJECT

public:
    MqttWidget(QWidget *parent = nullptr) : QWidget(parent), mqttClient(new QMqttClient(this)) {
        QVBoxLayout *layout = new QVBoxLayout(this);

        connectButton = new QPushButton("Connect to MQTT", this);
        publishButton = new QPushButton("Publish Message", this);
        subscribeButton = new QPushButton("Subscribe to Topic", this);
        textEdit = new QTextEdit(this);
        textEdit->setReadOnly(true);

        layout->addWidget(connectButton);
        layout->addWidget(publishButton);
        layout->addWidget(subscribeButton);
        layout->addWidget(textEdit);

        // 设置MQTT服务器地址和端口
        mqttClient->setHostname("mqtt.example.com"); // 替换为你的MQTT服务器地址
        mqttClient->setPort(1883); // 替换为你的MQTT服务器端口

        // 设置客户端ID
        mqttClient->setClientId("myUniqueId"); // 替换为一个唯一的客户端ID

        // 连接信号和槽
        connect(mqttClient, &QMqttClient::connected, this, &MqttWidget::onConnected);
        connect(mqttClient, QOverload<QAbstractSocket::SocketError>::of(&QMqttClient::disconnected),
                this, &MqttWidget::onDisconnected);
        connect(mqttClient, &QMqttClient::messageReceived, this, &MqttWidget::onMessageReceived);

        connect(connectButton, &QPushButton::clicked, this, &MqttWidget::onConnectClicked);
        connect(publishButton, &QPushButton::clicked, this, &MqttWidget::onPublishClicked);
        connect(subscribeButton, &QPushButton::clicked, this, &MqttWidget::onSubscribeClicked);
    }

private slots:
    void onConnectClicked() {
        mqttClient->connectToHost();
    }

    void onDisconnected(QAbstractSocket::SocketError error) {
        qDebug() << "Disconnected from MQTT server with error:" << error;
        textEdit->append("Disconnected from MQTT server.");
    }

    void onConnected() {
        qDebug() << "Connected to MQTT server.";
        textEdit->append("Connected to MQTT server.");
    }

    void onMessageReceived(const QByteArray &message, const QMqttTopicName &topic) {
        qDebug() << "Received message on topic:" << topic.name() << "Message:" << message;
        textEdit->append(QString("Received message on topic: %1\nMessage: %2").arg(topic.name()).arg(message));
    }

    void onPublishClicked() {
        // 这里应该添加发布消息的代码,例如:
        // QByteArray payload = "Hello MQTT";
        // mqttClient->publish("my/topic", payload, QMqttQualityOfService::AtMostOnce);
        // 但为了简化示例,这里暂时不发布消息。
        qDebug() << "Publish button clicked, but no message is published in this example.";
    }

    void onSubscribeClicked() {
        mqttClient->subscribe("my/topic");
        textEdit->append("Subscribed to topic 'my/topic'.");
    }

private:
    QMqttClient *mqttClient;

先到这里吧,请多指教

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2252902.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pytest(二)excel数据驱动

一、excel数据驱动 excel文件内容 excel数据驱动使用方法 import openpyxl import pytestdef get_excel():excel_obj openpyxl.load_workbook("../pytest结合数据驱动-excel/data.xlsx")sheet_obj excel_obj["Sheet1"]values sheet_obj.valuescase_li…

类和对象(中)(类的默认成员函数)+日期类实现

1.类的默认成员函数 默认成员函数就是用户没有显示实现&#xff0c;编译器会自动生成的成语函数称为默认成员函数。一个类&#xff0c;我们不写的情况下编译器会默认生成6个默认成员函数。C11后还增加了两个默认成员函数&#xff0c;移动构造和移动赋值&#xff08;这一节暂时…

详解版本控制工作原理及优势,常见的版本控制系统对比(HelixCore、Git、SVN等)

什么是版本控制软件&#xff1f;从基础层面来说&#xff0c;版本控制&#xff08;也可称版本管理&#xff09;就是随时间跟踪和管理文件变更的过程&#xff0c;而版本控制软件有助于实现这一过程的自动化。但这仅仅是其功能及其重要性的开端。 什么是版本控制&#xff1f; 版本…

记录一次网关异常

记一次网关异常 网关时不时就会出现下面的异常。关键是不知道什么时候就会报错&#xff0c;并且有时候就算什么都不操作&#xff0c;也会导致这个异常。 ERROR org.springframework.scheduling.support.TaskUtils$LoggingErrorHandler - Unexpected error occurred in schedul…

SpringMVC跨域问题解决方案

当Web应用程序尝试从一个源&#xff08;例如 http://localhost:9090&#xff09;向另一个不同的源&#xff08;例如 http://localhost:8080&#xff09;发起请求时&#xff0c;发现报错&#xff1a; 报错原因&#xff1a;请求被CORS策略拦截了 跨域问题概述 当Web应用程序尝试…

现在的电商风口已经很明显了

随着电商行业的不断发展&#xff0c;直播带货的热潮似乎正逐渐降温&#xff0c;而货架电商正成为新的焦点。抖音等平台越来越重视货架电商&#xff0c;强调搜索功能的重要性&#xff0c;预示着未来的电商中心将转向货架和搜索。 在这一转型期&#xff0c;AI技术与电商的结合为…

芯驰X9SP与汽车麦克风-打造无缝驾驶体验

当今汽车技术的进步不仅提升了驾驶体验&#xff0c;还改变了我们与车辆互动的方式。汽车麦克风作为车内语音控制系统的重要组成部分&#xff0c;正逐渐成为现代汽车的标配。 技术原理 汽车麦克风主要依赖于声音传感技术&#xff0c;通常包括电容式麦克风和动圈式麦克风。这些…

vue3项目搭建-6-axios 基础配置

axios 基础配置 安装 axios npm install axios 创建 axios 实例&#xff0c;配置基地址&#xff0c;配置拦截器,目录&#xff1a;utils/http.js 基地址&#xff1a;在每次访问时&#xff0c;自动作为相对路径的根 // axios 基础封装 import axios from "axios";…

【北京迅为】iTOP-4412全能版使用手册-第三十二章 网络通信-TCP套字节

iTOP-4412全能版采用四核Cortex-A9&#xff0c;主频为1.4GHz-1.6GHz&#xff0c;配备S5M8767 电源管理&#xff0c;集成USB HUB,选用高品质板对板连接器稳定可靠&#xff0c;大厂生产&#xff0c;做工精良。接口一应俱全&#xff0c;开发更简单,搭载全网通4G、支持WIFI、蓝牙、…

量子人工智能产业发展现状及趋势(上)

文章目录 前言一、量子人工智能产业发展现状1.产业链上游&#xff1a;涵盖基础硬件与量子计算整机开发&#xff0c;参与厂商众多&#xff0c;发展相对成熟2.产业链中游&#xff1a;涉及人工智能算法与应用开发&#xff0c;参与企业均在积极探索以赢得市场先机3.产业链下游&…

企业如何构建自己的 AI 编码能力

文章摘要 在数字化转型的浪潮中&#xff0c;企业对于提升开发效率和代码质量的需求日益迫切。AI 编码能力作为一种新兴的技术力量&#xff0c;正逐渐成为企业技术竞争力的关键。本文将探讨企业如何结合代码大模型和私域数据&#xff0c;构建属于自己的 AI 编码能力。 全文阅读…

算法日记 40 day 单调栈

最后两题了&#xff0c;直接上题目。 题目&#xff1a;接雨水 42. 接雨水 - 力扣&#xff08;LeetCode&#xff09; 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1…

SpringBoot集成Kafka和avro和Schema注册表

Schema注册表 为了提升kafka的性能&#xff0c;减少网络传输和存储的数据大小&#xff0c;可以把数据的schema部分单独存储到外部的schema注册表中&#xff0c;整体架构如下图所示&#xff1a; 1&#xff09;把所有数据需要用到的 schema 保存在注册表里&#xff0c;然后在记…

c++领域展开第一幕——入门基础(命名空间、iostream、缺省参数、函数重载、nullptr、inline(内联函数))超详细!!!!

文章目录 前言一、c的第一个程序二、命名空间2.1 namespace 的价值2.2 namespace 的定义2.3 命名空间的使用 三、c的输入和输出四、缺省参数五、函数重载六、nullptr七、inline总结 前言 今天小编带着大家进入c的大门&#xff0c;虽然c难&#xff0c;但好事多磨&#xff0c;一起…

Java Web 1HTML快速入门

目录 一、Web开发介绍 1.什么是Web&#xff1f; 2.初识Web前端 二、HTML快速入门 1.什么是HTML、CSS&#xff1f; 2、案例练习 3.小结 三、VS Code开发工具 四、基础标签&样式&#xff08;HTML&#xff09; 2、实现标题--样式1&#xff08;新闻标题的颜色&#xff0…

【Python网络爬虫笔记】7-网络爬虫的搜索工具re模块

目录 一、网络爬虫中的正则表达式和re模块&#xff08;一&#xff09;数据提取的精确性&#xff08;二&#xff09;处理复杂的文本结构&#xff08;三&#xff09;提高数据处理效率 二、正则表达式的内涵&#xff08;一&#xff09;、常用元字符&#xff08;二&#xff09;、量…

42_GAN网络详解(2)---常见的GAN

DCGAN CGAN 条件生成对抗网络&#xff08;Conditional Generative Adversarial Networks, CGAN&#xff09;是生成对抗网络&#xff08;Generative Adversarial Networks, GAN&#xff09;的一种变体&#xff0c;由Mehdi Mirza和Simon Osindero在2014年提出。CGAN的主要改进在…

PC端阅读器--koodo reader

官网&#xff1a;请在必应搜索引擎上输入 koodo reader GitHub&#xff1a;GitHub - koodo-reader/koodo-reader: Windows, macOS, Linux and Web 123云windows版&#xff1a;Koodo-Reader-1.5.1.exe下载 提取码&#xff1a;4455 优&#xff1a; 1.开源&#xff0c;懂&#x…

PyQt设计界面优化 #qss #ui设计 #QMainWindow

思维导图 通过qss实现ui界面设计优化 Qss是Qt程序界面中用来设置控件的背景图片、大小、字体颜色、字体类型、按钮状态变化等属性&#xff0c;它是用来美化UI界面。实现界面和程序的分离&#xff0c;快速切换界面。 首先我们在Pytchram创建一个新目录 然后将我们所需要的图片打…

多维数组及其应用————13

1. 二维数组 如果我们把 ⼀维数组做为数组的元 素&#xff0c;这时候就是⼆维数组&#xff0c; ⼆维数组作为数组元素的数组被为三维数组&#xff0c;⼆维数组以上的数组统称 为多维数组。 1.1 二维数组的创建 先行后列 其实也可以这样理解&#xff1a;把二维数组当成特殊的一维…