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_OBJECT
Q_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);
// 生成随机客户端ID
m_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);
}
// 连接到Broker
m_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 重载方法,无需手动创建 QMqttMessage
qint64 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: mainWindow
visible: true
width: 1024
height: 768
title: 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: parent
columns: 3 //3列
rowSpacing: 10
columnSpacing: 10
anchors.margins: 10
// 连接配置面板
GroupBox {
id: connectionGroup
title: qsTr("连接配置")
label: GroupBoxTitleLabel {
text: connectionGroup.title
}
Layout.column: 0
Layout.row: 0
Layout.columnSpan:1
Layout.rowSpan: 2
Layout.fillHeight: true
Layout.fillWidth: true
Layout.maximumWidth: 300
Layout.minimumWidth: parent.width/3
background: Rectangle{
color: "#fafafa"
radius: 5
}
//滚动试图
ScrollView {
id: connScrollView //不加 id 时,parent 指向 ScrollView 内部的 viewport,而 viewport 宽度依赖内容,导致循环依赖
anchors.fill: parent
Column {//从上到下的垂直方向依次排列
anchors.margins: 10
spacing: 15
width: connScrollView.width - 20
// 连接状态
Row {
spacing: 10
width: parent.width
Label {
text: qsTr("连接状态:")
Layout.alignment: Qt.AlignVCenter
}
Label {
text: myMqttClient.connectionStatus
color: statusStyle.substring(statusStyle.indexOf("color:") + 6) // 文字颜色
background: Rectangle {
radius: 4
implicitWidth: parent.implicitWidth + 10 // 等价于之前的 text.implicitWidth + 10
implicitHeight: 20
color: statusStyle.substring(
statusStyle.indexOf("background-color:") + 19,
statusStyle.indexOf(";")
)
}
Layout.alignment: Qt.AlignVCenter
}
}
// Broker地址
Column {
spacing: 10
width: parent.width
Label {
text: qsTr("Broker地址")
font.bold: true
font.pointSize: 10
}
RoundedTextField {
id: brokerUrlField
text: myMqttClient.brokerUrl
placeholderText: qsTr("mqtt://host:port")
onTextChanged: myMqttClient.brokerUrl = text
width: parent.width
}
}
// 端口
Column {
spacing: 10
width: parent.width
Label {
text: qsTr("端口")
font.bold: true
font.pointSize: 10
color: textPrimary
}
RoundedTextField {
id: portTextField
text: myMqttClient.mqttPort
inputMethodHints: Qt.ImhDigitsOnly
onTextChanged: myMqttClient.mqttPort = text ? parseInt(text) : 0
width: parent.width / 2
}
}
Column {
spacing: 5
width: parent.width
Label {
text: qsTr("客户端ID")
font.bold: true
font.pointSize: 10
color: textPrimary
}
RoundedTextField {
id: clientIdField
text: myMqttClient.clientId
onTextChanged: myMqttClient.clientId = text
width: parent.width
}
}
Column {
width: parent.width
spacing: 5
Label {
text: qsTr("心跳间隔(秒):")
font.bold: true
font.pointSize: 10
color: textPrimary
}
RoundedTextField {
id: keepAliveField
text: myMqttClient.keepAlive
inputMethodHints: Qt.ImhDigitsOnly
onTextChanged: myMqttClient.keepAlive = text ? parseInt(text) : 0
width: parent.width / 2
}
}
//清除会话
Row {
spacing: 5
width: parent.width
Label {
text: qsTr("清除会话")
font.bold: true
font.pointSize: 10
anchors.verticalCenter: parent.verticalCenter
color: textPrimary
}
CheckBox {
id: cleanSessionCheck
checked: myMqttClient.cleanSession
onCheckedChanged: myMqttClient.cleanSession = checked
anchors.verticalCenter: parent.verticalCenter
}
}
// 账号
Column {
spacing: 10
width: parent.width
Label {
text: qsTr("账号")
font.bold: true
font.pointSize: 10
color: textPrimary
}
RoundedTextField {
id: usernameField
placeholderText: qsTr("lock-mqtt")
text: myMqttClient.username
onTextChanged: myMqttClient.username = text
width: parent.width
}
}
//密码
Column {
spacing: 10
width: parent.width
Label {
text: qsTr("密码")
font.bold: true
font.pointSize: 10
color: textPrimary
}
RoundedTextField {
id: passwordField
placeholderText: qsTr("123456")
echoMode: TextField.Password
text: myMqttClient.password
onTextChanged: myMqttClient.password = text
width: parent.width
}
}
// 连接按钮
Row {
spacing: 10
width: parent.width
RoundedButton {
id: connectButton
text: qsTr("连接")
primaryColor: control.pressed ? "#36D08A" : "#cccccc"
enabled: myMqttClient.connectionStatus !== "已连接" && myMqttClient.connectionStatus !== "连接中"
width: parent.width / 2
onClicked: myMqttClient.connectToBroker()
}
RoundedButton {
id: disconnectButton
text: qsTr("断开")
enabled: myMqttClient.connectionStatus === "已连接"
onClicked: myMqttClient.disconnectFromBroker()
width: parent.width / 2
primaryColor: control.pressed ? "#36D08A" : "#cccccc"
contentItem: Text {//重写文字
text: control.text
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
}
}
}
}
}
}
// 订阅面板
GroupBox {
id: subscribeGroup
title: qsTr("订阅主题")
label: GroupBoxTitleLabel {
text: subscribeGroup.title
}
Layout.column: 0
Layout.row: 2
Layout.columnSpan:1
Layout.rowSpan: 1
Layout.fillHeight: true
Layout.fillWidth: true
Layout.maximumWidth: 300
Layout.minimumWidth: parent.width/3
background: Rectangle {
color: "#fafafa"
radius: 5
}
Column {
anchors.fill: parent
anchors.margins: 10
spacing: 10
// 订阅主题输入
Column {
width: parent.width
spacing: 5
Label {
text: qsTr("订阅主题:")
font.bold: true
font.pointSize: 10
}
TextField {
id: subscribeTopicField
width: parent.width
text: myMqttClient.subscribeTopic
placeholderText: qsTr("例如: test/#")
onTextChanged: myMqttClient.subscribeTopic = text
}
}
// QoS级别选择
Column {
width: parent.width
spacing: 5
Label {
text: qsTr("QoS级别:")
font.bold: true
font.pointSize: 10
}
ComboBox {
id: subscribeQosCombo
width: parent.width
model: [0, 1, 2]
currentIndex: 1 // 默认QoS 1
onCurrentIndexChanged: myMqttClient.subscribeQos = currentIndex
}
}
// 订阅按钮
RoundedButton {
id: subscribeButton
text: qsTr("订阅")
enabled: myMqttClient.connectionStatus === "已连接" && subscribeTopicField.text !== ""
onClicked: myMqttClient.subscribe()
}
// 已订阅主题列表
Column {
width: parent.width
spacing: 5
Label {
text: qsTr("已订阅主题:")
font.bold: true
font.pointSize: 10
}
// 已订阅主题列表视图
ListView {
id: subscribedTopicsList
width: parent.width
height: 150
model: myMqttClient.subscribedTopics
clip: true
delegate: Item {
width: parent.width
height: 30
Row {
spacing: 10
width: parent.width
Text {
text: modelData
width: parent.width - 40
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
RoundedButton {
text: "取消"
onClicked: myMqttClient.unsubscribe(modelData)
}
}
}
// 空列表提示
Label {
anchors.centerIn: parent
text: qsTr("未订阅任何主题")
visible: subscribedTopicsList.count === 0
color: "#999999"
font.italic: true
}
}
}
}
}
// 发布消息面板
GroupBox {
id: publishGroup
title: qsTr("发布消息")
Layout.column: 1
Layout.row: 2
Layout.columnSpan: 2
Layout.rowSpan: 1
Layout.fillWidth: true
Layout.fillHeight: true
label: GroupBoxTitleLabel {
text: publishGroup.title
}
background: Rectangle{
color: "#fafafa"
radius: 5
}
Column {
anchors.margins: 10
spacing: 10
width: parent.width - 20
// 发布主题
TextField {
id: publishTopicField
text: myMqttClient.publishTopic
placeholderText: qsTr("例如: test/message")
onTextChanged: myMqttClient.publishTopic = text
width: parent.width
}
// QoS和保留消息设置
Row {
spacing: 20
width: parent.width
Column {
spacing: 5
Label {
text: qsTr("QoS级别:")
font.pointSize: 10
}
ComboBox {
id: publishQosCombo
model: [0, 1, 2]
currentIndex: 1 // 默认QoS 1
onCurrentIndexChanged: myMqttClient.publishQos = currentValue
width: 100
}
}
Column {
spacing: 5
Label {
text: qsTr("保留消息:")
font.pointSize: 10
}
ComboBox {
id: retainCombo
model: [qsTr("否"), qsTr("是")]
currentIndex: 0 // 默认不保留
onCurrentIndexChanged: myMqttClient.retainMessage = currentIndex === 1
width: 100
}
}
}
// 消息内容
TextArea {
id: messageArea
text: myMqttClient.message
placeholderText: qsTr('例如: {"temperature": 25.5, "humidity": 60}')
onTextChanged: myMqttClient.message = text
width: parent.width
height: 100
}
// 发布按钮
Row {
spacing: 10
width: parent.width
RoundedButton {
text: qsTr("清空")
onClicked: messageArea.text = ""
primaryColor: "#36D08A"
}
RoundedButton {
text: qsTr("发布")
enabled: myMqttClient.connectionStatus === "已连接" && myMqttClient.publishTopic && myMqttClient.message
onClicked: myMqttClient.publishMessage()
primaryColor: "#36D08A"
}
}
}
}
GroupBox {
id: logGroup
title: qsTr("消息日志")
label: GroupBoxTitleLabel {
text: logGroup.title
}
Layout.column: 1
Layout.row: 0
Layout.rowSpan: 2
Layout.columnSpan: 2
Layout.fillWidth: true
Layout.fillHeight: true
background: Rectangle{
color: "#fafafa"
radius: 5
}
ColumnLayout {
anchors.fill: parent
anchors.margins: 10
spacing: 10
Row{
spacing: 10
width: parent.width
// 清空日志按钮
RoundedButton {
text: qsTr("清空日志")
primaryColor: "#333333"
}
// 自动滚动按钮
RoundedButton {
primaryColor: mouseAreaAuto.pressed
? (autoScroll ? "#4CAF5080" : "#cccccc")
: (autoScroll ? "#4CAF50" : "#e0e0e0")
text: autoScroll ? qsTr("自动滚动: 开启") : qsTr("自动滚动: 关闭")
}
}
// 日志内容
ScrollView {
id: logScrollView
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
ListView {
id: logListView
anchors.fill: parent
model: ListModel { id: logModel }
delegate: Item {
width: parent.width
height: textItem.implicitHeight + 8
Column {
anchors.fill: parent
anchors.margins: 4
spacing: 2
Text {
id: textItem
text: "[" + timestamp + "] " + message
font.family: "Courier New, monospace"
font.pointSize: 10
color: {
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.width
height: 1
color: "#f0f0f0"
}
}
}
}
}
}
// 处理日志添加
Connections {
target: myMqttClient
onLogAdded: {
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