基于Qt QML和C++的MQTT测试客户端(CMakeLists实现)
1.项目图片
2.项目代码架构
3.源码 C++混合QML
经典的嵌入式C++混合QML学习很好的例子
MQTT C++文件
#ifndef MYMQTTCLIENT_H
#define MYMQTTCLIENT_H#include <QObject>
#include <QString>
#include <QDateTime>
#include <QtMqtt/qmqttclient.h>
#include <QtMqtt/qmqttsubscription.h>
#include <QtMqtt/qmqttpublishproperties.h>class mymqttclient : public QObject
{Q_OBJECTQ_PROPERTY(QString brokerUrl READ brokerUrl WRITE setBrokerUrl NOTIFY brokerUrlChanged)Q_PROPERTY(QString clientId READ clientId WRITE setClientId NOTIFY clientIdChanged)Q_PROPERTY(int mqttPort READ mqttPort WRITE setmqttPort NOTIFY mqttPortChanged)Q_PROPERTY(int keepAlive READ keepAlive WRITE setKeepAlive NOTIFY keepAliveChanged)Q_PROPERTY(bool cleanSession READ cleanSession WRITE setCleanSession NOTIFY cleanSessionChanged)Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY usernameChanged)Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)Q_PROPERTY(QString connectionStatus READ connectionStatus NOTIFY connectionStatusChanged)Q_PROPERTY(QString subscribeTopic READ subscribeTopic WRITE setSubscribeTopic NOTIFY subscribeTopicChanged)Q_PROPERTY(int subscribeQos READ subscribeQos WRITE setSubscribeQos NOTIFY subscribeQosChanged)Q_PROPERTY(QString publishTopic READ publishTopic WRITE setPublishTopic NOTIFY publishTopicChanged)Q_PROPERTY(int publishQos READ publishQos WRITE setPublishQos NOTIFY publishQosChanged)Q_PROPERTY(bool retainMessage READ retainMessage WRITE setRetainMessage NOTIFY retainMessageChanged)Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged)Q_PROPERTY(QStringList subscribedTopics READ subscribedTopics NOTIFY subscribedTopicsChanged)
public:explicit mymqttclient(QObject *parent = nullptr);~mymqttclient() override;// 连接相关属性QString brokerUrl() const;void setBrokerUrl(const QString &brokerUrl);QString clientId() const;void setClientId(const QString &clientId);int mqttPort() const;void setmqttPort(int mqttPort);int keepAlive() const;void setKeepAlive(int keepAlive);bool cleanSession() const;void setCleanSession(bool cleanSession);QString username() const;void setUsername(const QString &username);QString password() const;void setPassword(const QString &password);QString connectionStatus() const;// 订阅相关属性QString subscribeTopic() const;void setSubscribeTopic(const QString &subscribeTopic);int subscribeQos() const;void setSubscribeQos(int subscribeQos);// 发布相关属性QString publishTopic() const;void setPublishTopic(const QString &publishTopic);int publishQos() const;void setPublishQos(int publishQos);bool retainMessage() const;void setRetainMessage(bool retainMessage);QString message() const;void setMessage(const QString &message);QStringList subscribedTopics() const;public slots:// 连接控制void connectToBroker();void disconnectFromBroker();// 订阅控制void subscribe();void unsubscribe(const QString &topic);// 发布消息void publishMessage();// 日志控制void clearLog();
signals:// 属性变化信号void brokerUrlChanged();void clientIdChanged();void mqttPortChanged();void keepAliveChanged();void cleanSessionChanged();void usernameChanged();void passwordChanged();void connectionStatusChanged();void subscribeTopicChanged();void subscribeQosChanged();void publishTopicChanged();void publishQosChanged();void retainMessageChanged();void messageChanged();void subscribedTopicsChanged();// 日志信号,用于通知QML添加日志条目void logAdded(const QString ×tamp, const QString &message, const QString &type);public slots:void onConnected();void onDisconnected();void onErrorOccurred(QMqttClient::ClientError error);void onMessageReceived(const QByteArray &message, const QMqttTopicName &topic);void onSubscriptionStateChanged(QMqttSubscription::SubscriptionState state);void onPingResponse();void onStateChanged();private:QMqttClient *m_client;QHash<QString, QMqttSubscription*> m_subscriptions;// 连接属性QString m_brokerUrl;QString m_clientId;int m_keepAlive;int m_mqttPort;bool m_cleanSession;QString m_username;QString m_password;QString m_connectionStatus;// 订阅属性QString m_subscribeTopic;int m_subscribeQos;// 发布属性QString m_publishTopic;int m_publishQos;bool m_retainMessage;QString m_message;// 已订阅主题列表QStringList m_subscribedTopics;// 添加日志void addLog(const QString &message, const QString &type);};#endif // MYMQTTCLIENT_H
CPP
#include "mymqttclient.h"
#include <QDebug>
#include <QUuid>mymqttclient::mymqttclient(QObject *parent) : QObject(parent),m_keepAlive(60),m_cleanSession(true),m_connectionStatus("未连接"),m_subscribeQos(1),m_publishQos(1),m_retainMessage(false)
{// 初始化MQTT客户端m_client = new QMqttClient(this);// 生成随机客户端IDm_clientId = QString("mqtt-test-client-%1").arg(QUuid::createUuid().toString().left(8));// 设置默认主题m_subscribeTopic = "test/#";m_publishTopic = "test/message";m_message = "{\"hello\": \"world\"}";// 连接信号与槽// 连接核心信号槽(参考官方示例,补充 ping 响应)connect(m_client, &QMqttClient::connected, this, &mymqttclient::onConnected);connect(m_client, &QMqttClient::disconnected, this, &mymqttclient::onDisconnected);connect(m_client, &QMqttClient::errorChanged, this, &mymqttclient::onErrorOccurred);connect(m_client, &QMqttClient::messageReceived, this, &mymqttclient::onMessageReceived);connect(m_client, &QMqttClient::pingResponseReceived, this, &mymqttclient::onPingResponse);connect(m_client, &QMqttClient::stateChanged, this, &mymqttclient::onStateChanged);addLog("初始化完成,请连接到MQTT Broker", "info");
}mymqttclient::~mymqttclient()
{disconnectFromBroker();delete m_client;
}// 连接属性的getter和setter
QString mymqttclient::brokerUrl() const
{return m_brokerUrl;
}void mymqttclient::setBrokerUrl(const QString &brokerUrl)
{if (m_brokerUrl != brokerUrl) {m_brokerUrl = brokerUrl;emit brokerUrlChanged();}
}QString mymqttclient::clientId() const
{return m_clientId;
}void mymqttclient::setClientId(const QString &clientId)
{if (m_clientId != clientId) {m_clientId = clientId;emit clientIdChanged();}
}int mymqttclient::mqttPort() const
{return m_mqttPort;
}void mymqttclient::setmqttPort(int mqttPort)
{if (m_mqttPort != mqttPort && mqttPort > 0) {m_mqttPort = mqttPort;emit mqttPortChanged();}
}int mymqttclient::keepAlive() const
{return m_keepAlive;
}void mymqttclient::setKeepAlive(int keepAlive)
{if (m_keepAlive != keepAlive && keepAlive > 0) {m_keepAlive = keepAlive;emit keepAliveChanged();}
}bool mymqttclient::cleanSession() const
{return m_cleanSession;
}void mymqttclient::setCleanSession(bool cleanSession)
{if (m_cleanSession != cleanSession) {m_cleanSession = cleanSession;emit cleanSessionChanged();}
}QString mymqttclient::username() const
{return m_username;
}void mymqttclient::setUsername(const QString &username)
{if (m_username != username) {m_username = username;emit usernameChanged();}
}QString mymqttclient::password() const
{return m_password;
}void mymqttclient::setPassword(const QString &password)
{if (m_password != password) {m_password = password;emit passwordChanged();}
}QString mymqttclient::connectionStatus() const
{return m_connectionStatus;
}// 订阅属性的getter和setter
QString mymqttclient::subscribeTopic() const
{return m_subscribeTopic;
}void mymqttclient::setSubscribeTopic(const QString &subscribeTopic)
{if (m_subscribeTopic != subscribeTopic) {m_subscribeTopic = subscribeTopic;emit subscribeTopicChanged();}
}int mymqttclient::subscribeQos() const
{return m_subscribeQos;
}void mymqttclient::setSubscribeQos(int subscribeQos)
{if (m_subscribeQos != subscribeQos && subscribeQos >= 0 && subscribeQos <= 2) {m_subscribeQos = subscribeQos;emit subscribeQosChanged();}
}// 发布属性的getter和setter
QString mymqttclient::publishTopic() const
{return m_publishTopic;
}void mymqttclient::setPublishTopic(const QString &publishTopic)
{if (m_publishTopic != publishTopic) {m_publishTopic = publishTopic;emit publishTopicChanged();}
}int mymqttclient::publishQos() const
{return m_publishQos;
}void mymqttclient::setPublishQos(int publishQos)
{if (m_publishQos != publishQos && publishQos >= 0 && publishQos <= 2) {m_publishQos = publishQos;emit publishQosChanged();}
}bool mymqttclient::retainMessage() const
{return m_retainMessage;
}void mymqttclient::setRetainMessage(bool retainMessage)
{if (m_retainMessage != retainMessage) {m_retainMessage = retainMessage;emit retainMessageChanged();}
}QString mymqttclient::message() const
{return m_message;
}void mymqttclient::setMessage(const QString &message)
{if (m_message != message) {m_message = message;emit messageChanged();}
}QStringList mymqttclient::subscribedTopics() const
{return m_subscribedTopics;
}// 连接控制槽函数
void mymqttclient::connectToBroker()
{if (m_brokerUrl.isEmpty()) {addLog("Broker地址不能为空", "error");return;}if (m_client->state() == QMqttClient::Connected) {addLog("已经处于连接状态", "info");return;}// 设置MQTT客户端参数m_client->setHostname(m_brokerUrl);m_client->setPort(m_mqttPort); // 默认MQTT端口m_client->setClientId(m_clientId);m_client->setKeepAlive(m_keepAlive);m_client->setCleanSession(m_cleanSession);if (!m_username.isEmpty()) {m_client->setUsername(m_username);}if (!m_password.isEmpty()) {m_client->setPassword(m_password);}// 连接到Brokerm_client->connectToHost();addLog(QString("正在连接到 %1...").arg(m_brokerUrl), "info");m_connectionStatus = "连接中";emit connectionStatusChanged();
}void mymqttclient::disconnectFromBroker()
{if (m_client->state() == QMqttClient::Connected) {m_client->disconnectFromHost();addLog("正在断开连接...", "info");} else {addLog("未处于连接状态", "info");}
}// 订阅控制槽函数
void mymqttclient::subscribe()
{if (m_client->state() != QMqttClient::Connected) {addLog("请先连接到Broker", "error");return;}if (m_subscribeTopic.isEmpty()) {addLog("订阅主题不能为空", "error");return;}// 检查是否已经订阅if (m_subscriptions.contains(m_subscribeTopic)) {addLog(QString("已经订阅过主题: %1").arg(m_subscribeTopic), "info");return;}// 订阅主题auto subscription = m_client->subscribe(m_subscribeTopic, m_subscribeQos);if (subscription) {m_subscriptions[m_subscribeTopic] = subscription;connect(subscription, &QMqttSubscription::stateChanged,this, &mymqttclient::onSubscriptionStateChanged);addLog(QString("正在订阅主题: %1 (QoS: %2)").arg(m_subscribeTopic).arg(m_subscribeQos), "info");} else {addLog(QString("订阅主题失败: %1").arg(m_subscribeTopic), "error");}
}void mymqttclient::unsubscribe(const QString &topic)
{if (m_client->state() != QMqttClient::Connected) {addLog("请先连接到Broker", "error");return;}if (topic.isEmpty()) {addLog("主题不能为空", "error");return;}if (!m_subscriptions.contains(topic)) {addLog(QString("未订阅主题: %1").arg(topic), "info");return;}// 取消订阅m_client->unsubscribe(topic);auto subscription = m_subscriptions.take(topic);if (subscription) {subscription->deleteLater();}m_subscribedTopics.removeAll(topic);emit subscribedTopicsChanged();addLog(QString("已取消订阅主题: %1").arg(topic), "info");
}// 发布消息槽函数
void mymqttclient::publishMessage()
{if (m_client->state() != QMqttClient::Connected) {addLog("请先连接到Broker", "error");return;}if (m_publishTopic.isEmpty()) {addLog("发布主题不能为空", "error");return;}if (m_message.isEmpty()) {addLog("消息内容不能为空", "error");return;}// 直接调用 publish 重载方法,无需手动创建 QMqttMessageqint64 msgId = m_client->publish(m_publishTopic, // 主题(QString 类型)m_message.toUtf8(), // 消息内容(QByteArray)m_publishQos, // QoS 等级m_retainMessage // 保留位);if (msgId != -1) {addLog(QString("已发布消息到主题 [%1] (QoS: %2, 保留: %3): %4").arg(m_publishTopic).arg(m_publishQos).arg(m_retainMessage ? "是" : "否").arg(m_message), "send");} else {addLog(QString("发布消息到主题 [%1] 失败").arg(m_publishTopic), "error");}
}// 日志控制
void mymqttclient::clearLog()
{emit logAdded(QDateTime::currentDateTime().toString("HH:mm:ss"),"日志已清空", "info");
}// 内部槽函数
void mymqttclient::onConnected()
{m_connectionStatus = "已连接";emit connectionStatusChanged();addLog(QString("已成功连接到 %1").arg(m_brokerUrl), "success");
}void mymqttclient::onDisconnected()
{m_connectionStatus = "未连接";emit connectionStatusChanged();// 清空订阅列表qDeleteAll(m_subscriptions.values());m_subscriptions.clear();m_subscribedTopics.clear();emit subscribedTopicsChanged();addLog("已与Broker断开连接", "info");
}void mymqttclient::onErrorOccurred(QMqttClient::ClientError error)
{QString errorMsg;switch (error) {case QMqttClient::NoError:errorMsg = "正常通信";break;case QMqttClient::InvalidProtocolVersion:errorMsg = "协议版本不兼容";break;case QMqttClient::IdRejected:errorMsg = "客户端ID被拒绝";break;case QMqttClient::ServerUnavailable:errorMsg = "服务器不可用";break;case QMqttClient::BadUsernameOrPassword:errorMsg = "用户或密码错误";break;case QMqttClient::NotAuthorized:errorMsg = "权限不足";break;default:errorMsg = QString("发生错误 (代码: %1)").arg(error);}m_connectionStatus = "未连接";emit connectionStatusChanged();addLog(errorMsg, "error");
}void mymqttclient::onPingResponse()
{const QString content = QDateTime::currentDateTime().toString()+ QLatin1String(" PingResponse")+ QLatin1Char('\n');addLog(content,"onPingResponse");
}void mymqttclient::onStateChanged()
{const QString content = QDateTime::currentDateTime().toString()+ QLatin1String(": State Change")+ QString::number(m_client->state())+ QLatin1Char('\n');//ui->editLog->insertPlainText(content);addLog(content,"onStateChanged");
}void mymqttclient::onMessageReceived(const QByteArray &message, const QMqttTopicName &topic)
{addLog(QString("收到主题 [%1] 的消息: %2").arg(topic.name()).arg(QString(message)), "receive");
}void mymqttclient::onSubscriptionStateChanged(QMqttSubscription::SubscriptionState state)
{auto subscription = qobject_cast<QMqttSubscription*>(sender());QString topic = subscription->topic().filter();if (state == QMqttSubscription::Subscribed) {if (!m_subscribedTopics.contains(topic)) {m_subscribedTopics.append(topic);emit subscribedTopicsChanged();}addLog(QString("已成功订阅主题: %1 (QoS: %2)").arg(topic).arg(subscription->qos()), "success");} else if (state == QMqttSubscription::Unsubscribed) {m_subscribedTopics.removeAll(topic);m_subscriptions.remove(topic);emit subscribedTopicsChanged();addLog(QString("已取消订阅主题: %1").arg(topic), "info");} else if (state == QMqttSubscription::Error) {
// QMqttSubscription::Error err = subscription->error();
// addLog(QString("订阅主题 %1 失败:错误代码 %2")
// .arg(topic)
// .arg(static_cast<int>(err)),
// "error");
// m_subscriptions.remove(topic);addLog(QString("订阅主题 %1 失败:订阅状态错误").arg(topic), "error");m_subscriptions.remove(topic);}
}// 添加日志
void mymqttclient::addLog(const QString &message, const QString &type)
{QString timestamp = QDateTime::currentDateTime().toString("HH:mm:ss");emit logAdded(timestamp, message, type);
}
QML
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.3
//import "."ApplicationWindow {id: mainWindowvisible: truewidth: 1024height: 768title: qsTr("MQTT客户端测试工具")background: Rectangle{color: "#2c3e47"}// 主题颜色// 替换原有的颜色定义property color primaryColor: "#4361EE" // 更柔和的蓝色property color secondaryColor: "#3A86FF" // 明亮的蓝色property color successColor: "#4CC9F0" // 清新的蓝绿色property color warningColor: "#F8961E" // 温暖的橙色property color dangerColor: "#F94144" // 柔和的红色property color darkColor: "#2B2D42" // 深蓝灰property color lightColor: "#F8F9FA" // 更白的背景property color textPrimary: "#212529" // 主文本颜色property color textSecondary: "#495057" // 次文本颜色// 自动滚动日志property bool autoScroll: true// 连接状态样式property string statusStyle: {switch (myMqttClient.connectionStatus) {case "已连接": return "background-color: " + successColor + "; color: white";case "连接中": return "background-color: " + warningColor + "; color: white";default: return "background-color: #e0e0e0; color: #666666";}}// 网格布局主布局GridLayout {anchors.fill: parentcolumns: 3 //3列rowSpacing: 10columnSpacing: 10anchors.margins: 10// 连接配置面板GroupBox {id: connectionGrouptitle: qsTr("连接配置")label: GroupBoxTitleLabel {text: connectionGroup.title}Layout.column: 0Layout.row: 0Layout.columnSpan:1Layout.rowSpan: 2Layout.fillHeight: trueLayout.fillWidth: trueLayout.maximumWidth: 300Layout.minimumWidth: parent.width/3background: Rectangle{color: "#fafafa"radius: 5}//滚动试图ScrollView {id: connScrollView //不加 id 时,parent 指向 ScrollView 内部的 viewport,而 viewport 宽度依赖内容,导致循环依赖anchors.fill: parentColumn {//从上到下的垂直方向依次排列anchors.margins: 10spacing: 15width: connScrollView.width - 20// 连接状态Row {spacing: 10width: parent.widthLabel {text: qsTr("连接状态:")Layout.alignment: Qt.AlignVCenter}Label {text: myMqttClient.connectionStatuscolor: statusStyle.substring(statusStyle.indexOf("color:") + 6) // 文字颜色background: Rectangle {radius: 4implicitWidth: parent.implicitWidth + 10 // 等价于之前的 text.implicitWidth + 10implicitHeight: 20color: statusStyle.substring(statusStyle.indexOf("background-color:") + 19,statusStyle.indexOf(";"))}Layout.alignment: Qt.AlignVCenter}}// Broker地址Column {spacing: 10width: parent.widthLabel {text: qsTr("Broker地址")font.bold: truefont.pointSize: 10}RoundedTextField {id: brokerUrlFieldtext: myMqttClient.brokerUrlplaceholderText: qsTr("mqtt://host:port")onTextChanged: myMqttClient.brokerUrl = textwidth: parent.width}}// 端口Column {spacing: 10width: parent.widthLabel {text: qsTr("端口")font.bold: truefont.pointSize: 10color: textPrimary}RoundedTextField {id: portTextFieldtext: myMqttClient.mqttPortinputMethodHints: Qt.ImhDigitsOnlyonTextChanged: myMqttClient.mqttPort = text ? parseInt(text) : 0width: parent.width / 2}}Column {spacing: 5width: parent.widthLabel {text: qsTr("客户端ID")font.bold: truefont.pointSize: 10color: textPrimary}RoundedTextField {id: clientIdFieldtext: myMqttClient.clientIdonTextChanged: myMqttClient.clientId = textwidth: parent.width}}Column {width: parent.widthspacing: 5Label {text: qsTr("心跳间隔(秒):")font.bold: truefont.pointSize: 10color: textPrimary}RoundedTextField {id: keepAliveFieldtext: myMqttClient.keepAliveinputMethodHints: Qt.ImhDigitsOnlyonTextChanged: myMqttClient.keepAlive = text ? parseInt(text) : 0width: parent.width / 2}}//清除会话Row {spacing: 5width: parent.widthLabel {text: qsTr("清除会话")font.bold: truefont.pointSize: 10anchors.verticalCenter: parent.verticalCentercolor: textPrimary}CheckBox {id: cleanSessionCheckchecked: myMqttClient.cleanSessiononCheckedChanged: myMqttClient.cleanSession = checkedanchors.verticalCenter: parent.verticalCenter}}// 账号Column {spacing: 10width: parent.widthLabel {text: qsTr("账号")font.bold: truefont.pointSize: 10color: textPrimary}RoundedTextField {id: usernameFieldplaceholderText: qsTr("lock-mqtt")text: myMqttClient.usernameonTextChanged: myMqttClient.username = textwidth: parent.width}}//密码Column {spacing: 10width: parent.widthLabel {text: qsTr("密码")font.bold: truefont.pointSize: 10color: textPrimary}RoundedTextField {id: passwordFieldplaceholderText: qsTr("123456")echoMode: TextField.Passwordtext: myMqttClient.passwordonTextChanged: myMqttClient.password = textwidth: parent.width}}// 连接按钮Row {spacing: 10width: parent.widthRoundedButton {id: connectButtontext: qsTr("连接")primaryColor: control.pressed ? "#36D08A" : "#cccccc"enabled: myMqttClient.connectionStatus !== "已连接" && myMqttClient.connectionStatus !== "连接中"width: parent.width / 2onClicked: myMqttClient.connectToBroker()}RoundedButton {id: disconnectButtontext: qsTr("断开")enabled: myMqttClient.connectionStatus === "已连接"onClicked: myMqttClient.disconnectFromBroker()width: parent.width / 2primaryColor: control.pressed ? "#36D08A" : "#cccccc"contentItem: Text {//重写文字text: control.textcolor: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenteranchors.fill: parent}}}}}}// 订阅面板GroupBox {id: subscribeGrouptitle: qsTr("订阅主题")label: GroupBoxTitleLabel {text: subscribeGroup.title}Layout.column: 0Layout.row: 2Layout.columnSpan:1Layout.rowSpan: 1Layout.fillHeight: trueLayout.fillWidth: trueLayout.maximumWidth: 300Layout.minimumWidth: parent.width/3background: Rectangle {color: "#fafafa"radius: 5}Column {anchors.fill: parentanchors.margins: 10spacing: 10// 订阅主题输入Column {width: parent.widthspacing: 5Label {text: qsTr("订阅主题:")font.bold: truefont.pointSize: 10}TextField {id: subscribeTopicFieldwidth: parent.widthtext: myMqttClient.subscribeTopicplaceholderText: qsTr("例如: test/#")onTextChanged: myMqttClient.subscribeTopic = text}}// QoS级别选择Column {width: parent.widthspacing: 5Label {text: qsTr("QoS级别:")font.bold: truefont.pointSize: 10}ComboBox {id: subscribeQosCombowidth: parent.widthmodel: [0, 1, 2]currentIndex: 1 // 默认QoS 1onCurrentIndexChanged: myMqttClient.subscribeQos = currentIndex}}// 订阅按钮RoundedButton {id: subscribeButtontext: qsTr("订阅")enabled: myMqttClient.connectionStatus === "已连接" && subscribeTopicField.text !== ""onClicked: myMqttClient.subscribe()}// 已订阅主题列表Column {width: parent.widthspacing: 5Label {text: qsTr("已订阅主题:")font.bold: truefont.pointSize: 10}// 已订阅主题列表视图ListView {id: subscribedTopicsListwidth: parent.widthheight: 150model: myMqttClient.subscribedTopicsclip: truedelegate: Item {width: parent.widthheight: 30Row {spacing: 10width: parent.widthText {text: modelDatawidth: parent.width - 40elide: Text.ElideRightverticalAlignment: Text.AlignVCenter}RoundedButton {text: "取消"onClicked: myMqttClient.unsubscribe(modelData)}}}// 空列表提示Label {anchors.centerIn: parenttext: qsTr("未订阅任何主题")visible: subscribedTopicsList.count === 0color: "#999999"font.italic: true}}}}}// 发布消息面板GroupBox {id: publishGrouptitle: qsTr("发布消息")Layout.column: 1Layout.row: 2Layout.columnSpan: 2Layout.rowSpan: 1Layout.fillWidth: trueLayout.fillHeight: truelabel: GroupBoxTitleLabel {text: publishGroup.title}background: Rectangle{color: "#fafafa"radius: 5}Column {anchors.margins: 10spacing: 10width: parent.width - 20// 发布主题TextField {id: publishTopicFieldtext: myMqttClient.publishTopicplaceholderText: qsTr("例如: test/message")onTextChanged: myMqttClient.publishTopic = textwidth: parent.width}// QoS和保留消息设置Row {spacing: 20width: parent.widthColumn {spacing: 5Label {text: qsTr("QoS级别:")font.pointSize: 10}ComboBox {id: publishQosCombomodel: [0, 1, 2]currentIndex: 1 // 默认QoS 1onCurrentIndexChanged: myMqttClient.publishQos = currentValuewidth: 100}}Column {spacing: 5Label {text: qsTr("保留消息:")font.pointSize: 10}ComboBox {id: retainCombomodel: [qsTr("否"), qsTr("是")]currentIndex: 0 // 默认不保留onCurrentIndexChanged: myMqttClient.retainMessage = currentIndex === 1width: 100}}}// 消息内容TextArea {id: messageAreatext: myMqttClient.messageplaceholderText: qsTr('例如: {"temperature": 25.5, "humidity": 60}')onTextChanged: myMqttClient.message = textwidth: parent.widthheight: 100}// 发布按钮Row {spacing: 10width: parent.widthRoundedButton {text: qsTr("清空")onClicked: messageArea.text = ""primaryColor: "#36D08A"}RoundedButton {text: qsTr("发布")enabled: myMqttClient.connectionStatus === "已连接" && myMqttClient.publishTopic && myMqttClient.messageonClicked: myMqttClient.publishMessage()primaryColor: "#36D08A"}}}}GroupBox {id: logGrouptitle: qsTr("消息日志")label: GroupBoxTitleLabel {text: logGroup.title}Layout.column: 1Layout.row: 0Layout.rowSpan: 2Layout.columnSpan: 2Layout.fillWidth: trueLayout.fillHeight: truebackground: Rectangle{color: "#fafafa"radius: 5}ColumnLayout {anchors.fill: parentanchors.margins: 10spacing: 10Row{spacing: 10width: parent.width// 清空日志按钮RoundedButton {text: qsTr("清空日志")primaryColor: "#333333"}// 自动滚动按钮RoundedButton {primaryColor: mouseAreaAuto.pressed? (autoScroll ? "#4CAF5080" : "#cccccc"): (autoScroll ? "#4CAF50" : "#e0e0e0")text: autoScroll ? qsTr("自动滚动: 开启") : qsTr("自动滚动: 关闭")}}// 日志内容ScrollView {id: logScrollViewLayout.fillWidth: trueLayout.fillHeight: trueclip: trueListView {id: logListViewanchors.fill: parentmodel: ListModel { id: logModel }delegate: Item {width: parent.widthheight: textItem.implicitHeight + 8Column {anchors.fill: parentanchors.margins: 4spacing: 2Text {id: textItemtext: "[" + timestamp + "] " + messagefont.family: "Courier New, monospace"font.pointSize: 10color: {switch (type) {case "success": return "green";case "error": return "red";case "receive": return "#7B68EE";case "send": return "#FF8C00";default: return "#333333";}}wrapMode: Text.WordWrap}Rectangle {width: parent.widthheight: 1color: "#f0f0f0"}}}}}}// 处理日志添加Connections {target: myMqttClientonLogAdded: {logModel.append({timestamp: timestamp,message: message,type: type});// 限制日志数量if (logModel.count > 1000) {logModel.remove(0, logModel.count - 1000);}// 自动滚动到底部if (autoScroll) {logListView.positionViewAtEnd();}}}}}
}
不太好贴出来,大家可以学习,要完整源码可到下面连接
https://download.csdn.net/download/weixin_57695658/91911445