欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 幼教 > Qt CAN总线发送和接收案例

Qt CAN总线发送和接收案例

2025/5/8 5:57:29 来源:https://blog.csdn.net/weixin_45672157/article/details/139468518  浏览:    关键词:Qt CAN总线发送和接收案例

文章目录

  • 设置比特率类
    • 设置比特率类实现
  • 发送数据帧类
    • 发送数据帧类的实现
    • m_ui 发送帧界面
  • 连接类
    • 连接类实现
    • 连接类UI设计
  • 主窗口类
    • 主窗口类实现
    • 主界面UI
  • 整体UI
  • QT案例

设置比特率类

// 文件: BitRateBox.h
// 作用: 定义了一个用于选择比特率的组合框类 BitRateBox,该类继承自QComboBox。
//       提供了设置和获取比特率、以及是否启用可变数据速率的功能。#ifndef BITRATEBOX_H
#define BITRATEBOX_H#include <QComboBox> // 引入Qt的组合框类QComboBox// 使用QT宏来引入QIntValidator类的命名空间
QT_BEGIN_NAMESPACE
class QIntValidator;
QT_END_NAMESPACE// BitRateBox 类定义,继承自QComboBox
class BitRateBox : public QComboBox
{
public:// 构造函数,接受一个可选的父窗口指针explicit BitRateBox(QWidget *parent = nullptr);// 析构函数~BitRateBox();// 获取当前选定的比特率int bitRate() const;// 查询是否启用了灵活的数据速率(Flexible Data Rate)bool isFlexibleDataRateEnabled() const;// 设置灵活数据速率的启用状态void setFlexibleDateRateEnabled(bool enabled);protected slots: // 更正:应为private slots,因为这些槽函数不应在类外部被直接调用// 根据下拉菜单的选择项检查并应用自定义速度策略void checkCustomSpeedPolicy(int idx);private:// 初始化比特率选项void fillBitRates();// 标记是否启用了灵活数据速率,默认为禁用int m_isFlexibleDataRateEnabled; // 注意:此处类型应为bool而非int以匹配其用途// 用于验证用户输入的自定义比特率的整数验证器QIntValidator *m_customSpeedValidator;
};#endif // BITRATEBOX_H // 头文件结束标记

设置比特率类实现

#include "bitratebox.h"
#include <QLineEdit> // 包含QLineEdit头文件,用于访问lineEdit()方法// BitRateBox构造函数,初始化组合框并设置验证器
BitRateBox::BitRateBox(QWidget *parent) :QComboBox(parent),m_customSpeedValidator(new QIntValidator(0, 1000000, this)) // 初始化验证器,允许0到1000000的整数输入
{fillBitRates(); // 填充默认比特率选项// 连接下拉菜单选项变化的信号到checkCustomSpeedPolicy槽函数connect(this, QOverload<int>::of(&QComboBox::currentIndexChanged),this, &BitRateBox::checkCustomSpeedPolicy);
}// 析构函数,释放m_customSpeedValidator所占内存
BitRateBox::~BitRateBox()
{delete m_customSpeedValidator;
}// 返回当前选中的比特率
int BitRateBox::bitRate() const
{// 如果选中的是"Custom"项,则直接解析文本为整数返回if (currentIndex() == (count() - 1))return currentText().toInt();// 否则,从itemData获取预设比特率值return itemData(currentIndex()).toInt();
}// 查询灵活数据速率是否启用
bool BitRateBox::isFlexibleDataRateEnabled() const
{return m_isFlexibleDataRateEnabled;
}// 设置灵活数据速率的启用状态,并根据状态调整验证器上限
void BitRateBox::setFlexibleDateRateEnabled(bool enabled)
{m_isFlexibleDataRateEnabled = enabled;// 根据状态调整自定义速度的最大值m_customSpeedValidator->setTop(enabled ? 10000000 : 1000000);fillBitRates(); // 重新填充比特率选项以反映设置
}// 根据当前选中项决定是否进入自定义速度模式并设置验证器
void BitRateBox::checkCustomSpeedPolicy(int idx)
{const bool isCustomSpeed = !itemData(idx).isValid(); // 判断是否选择了"Custom"setEditable(isCustomSpeed); // 是否允许编辑(自定义输入)if (isCustomSpeed) {clearEditText(); // 清除已有编辑文本lineEdit()->setValidator(m_customSpeedValidator); // 设置验证器控制输入合法}
}// 填充比特率选项到组合框
void BitRateBox::fillBitRates()
{// 静态比特率列表const QList<int> rates = {10000, 20000, 50000, 100000, 125000, 250000, 500000, 800000, 1000000};// 灵活数据速率列表,仅当功能启用时显示const QList<int> dataRates = {2000000, 4000000, 8000000};// 清空现有选项clear();// 添加静态比特率选项for (int rate : rates)addItem(QString::number(rate), rate);// 根据配置添加灵活数据速率选项if (isFlexibleDataRateEnabled()) {for (int rate : dataRates)addItem(QString::number(rate), rate);}// 添加"Custom"选项,允许用户输入自定义比特率addItem(tr("Custom"));// 默认选择500000 bits/secsetCurrentIndex(6);
}

发送数据帧类

// 文件: SendFrameBox.h
// 作用: 定义了一个用于构造和管理发送CAN总线数据帧界面的类SendFrameBox,该类继承自QGroupBox。
//       包括两个自定义验证器HexIntegerValidator和HexStringValidator,分别用于验证十六进制整数和十六进制字符串输入的有效性。#ifndef SENDFRAMEBOX_H
#define SENDFRAMEBOX_H#include <QCanBusFrame> // CAN总线数据帧类
#include <QGroupBox>    // Qt中的分组框基类
#include <QRegularExpression> // 正则表达式类,可能用于输入验证辅助
#include <QValidator>   // 输入验证基类QT_BEGIN_NAMESPACE
namespace Ui { class SendFrameBox; } // 前向声明Ui命名空间内的SendFrameBox类,通常由Qt Designer生成
QT_END_NAMESPACE// HexIntegerValidator类定义,用于验证输入是否为有效的十六进制整数
class HexIntegerValidator : public QValidator
{Q_OBJECT // 支持信号槽机制public:// 构造函数,可选传入父对象指针explicit HexIntegerValidator(QObject *parent = nullptr);// 重写了QValidator的validate方法,用于实际的输入验证逻辑QValidator::State validate(QString &input, int &) const;// 设置验证器允许的最大十六进制数值void setMaximum(uint maximum);private:uint m_maximum = 0; // 最大允许的十六进制数值
};// HexStringValidator类定义,用于验证输入是否为有效的十六进制字符串
class HexStringValidator : public QValidator
{Q_OBJECTpublic:// 构造函数explicit HexStringValidator(QObject *parent = nullptr);// 重写的验证逻辑QValidator::State validate(QString &input, int &pos) const;// 设置验证器允许的最长输入长度void setMaxLength(int maxLength);private:int m_maxLength = 0; // 允许的最长输入长度
};// SendFrameBox类定义,负责构建和管理发送CAN帧的UI界面
class SendFrameBox : public QGroupBox
{Q_OBJECT // 支持信号槽机制public:// 构造函数,可选传入父对象指针explicit SendFrameBox(QWidget *parent = nullptr);// 析构函数~SendFrameBox();// 信号,当用户操作完成并准备发送CAN帧时发出signals:void sendFrame(const QCanBusFrame &frame);private:Ui::SendFrameBox *m_ui; // 指向由Qt Designer生成的用户界面对象HexIntegerValidator *m_hexIntegerValidator; // 十六进制整数验证器HexStringValidator *m_hexStringValidator;   // 十六进制字符串验证器
};#endif // SENDFRAMEBOX_H // 头文件结束标记

发送数据帧类的实现

#include "sendframebox.h"
#include "ui_sendframebox.h"// 定义标准ID和扩展ID的最大值
enum {MaxStandardId = 0x7FF, // 标准CAN帧ID的最大值MaxExtendedId = 0x10000000 // 扩展CAN帧ID的最大值
};// 定义CAN帧数据域最大负载长度,区分经典CAN与FD(灵活数据速率)CAN
enum {MaxPayload = 8, // 经典CAN帧最大负载字节数MaxPayloadFd = 64 // FD CAN帧最大负载字节数
};// HexIntegerValidator构造函数,初始化最大验证值为标准CAN ID的最大值
HexIntegerValidator::HexIntegerValidator(QObject *parent) :QValidator(parent),m_maximum(MaxStandardId)
{
}// 验证输入的十六进制整数是否有效
QValidator::State HexIntegerValidator::validate(QString &input, int &)
{bool ok;uint value = input.toUInt(&ok, 16); // 将输入转换为无符号整数if (input.isEmpty()) // 空输入视为中间状态,等待更多输入return Intermediate;if (!ok || value > m_maximum) // 转换失败或超过最大值则无效return Invalid;return Acceptable; // 否则,输入有效
}// 设置验证的最大十六进制数值
void HexIntegerValidator::setMaximum(uint maximum)
{m_maximum = maximum;
}// HexStringValidator构造函数,初始化最大验证长度为经典CAN负载长度
HexStringValidator::HexStringValidator(QObject *parent) :QValidator(parent),m_maxLength(MaxPayload)
{
}// 验证输入的十六进制字符串是否有效,同时格式化输入(每两个字符后加空格)
QValidator::State HexStringValidator::validate(QString &input, int &pos)
{const int maxSize = 2 * m_maxLength; // 计算最大字符长度(每字节2字符)const QChar space = QLatin1Char(' '); // 空格字符QString data = input.remove(space); // 移除所有空格进行验证if (data.isEmpty()) // 空输入视为中间状态return Intermediate;// 检查是否超过最大长度或以空格结尾(不允许尾随空格)if ((data.size() > maxSize) || (data.size() == maxSize && input.endsWith(space)))return Invalid;// 使用正则表达式检查所有字符是否为十六进制const QRegularExpression re(QStringLiteral("^[[:xdigit:]]*$"));if (!re.match(data).hasMatch())return Invalid;// 每两个十六进制字符后插入空格,如果需要const QRegularExpression insertSpace(QStringLiteral("(?:[[:xdigit:]]{2} )*[[:xdigit:]]{3}"));if (insertSpace.match(input).hasMatch()) {input.insert(input.size() - 1, space); // 在倒数第二个字符后插入空格pos = input.size(); // 更新光标位置}return Acceptable; // 输入有效
}// 设置验证的最大十六进制字符串长度
void HexStringValidator::setMaxLength(int maxLength)
{m_maxLength = maxLength;
}// SendFrameBox构造函数,初始化界面及连接信号槽
SendFrameBox::SendFrameBox(QWidget *parent) :QGroupBox(parent),m_ui(new Ui::SendFrameBox)
{m_ui->setupUi(this); // 使用Qt Designer生成的UI// 设置ID和数据的验证器m_hexIntegerValidator = new HexIntegerValidator(this);m_ui->frameIdEdit->setValidator(m_hexIntegerValidator);m_hexStringValidator = new HexStringValidator(this);m_ui->payloadEdit->setValidator(m_hexStringValidator);// 数据帧类型切换时,控制“灵活数据速率”选项的可用性connect(m_ui->dataFrame, &QRadioButton::toggled, [this](bool set) {if (set) m_ui->flexibleDataRateBox->setEnabled(true);});connect(m_ui->remoteFrame, &QRadioButton::toggled, [this](bool set) {if (set) {m_ui->flexibleDataRateBox->setEnabled(false);m_ui->flexibleDataRateBox->setChecked(false);}});connect(m_ui->errorFrame, &QRadioButton::toggled, [this](bool set) {if (set) {m_ui->flexibleDataRateBox->setEnabled(false);m_ui->flexibleDataRateBox->setChecked(false);}});// 扩展ID选项改变时,更新ID验证器的最大值connect(m_ui->extendedFormatBox, &QCheckBox::toggled, [this](bool set) {m_hexIntegerValidator->setMaximum(set ? MaxExtendedId : MaxStandardId);});// 灵活数据速率选项改变时,更新数据验证器的最大长度和控制位速率切换选项connect(m_ui->flexibleDataRateBox, &QCheckBox::toggled, [this](bool set) {m_hexStringValidator->setMaxLength(set ? MaxPayloadFd : MaxPayload);m_ui->bitrateSwitchBox->setEnabled(set);if (!set) m_ui->bitrateSwitchBox->setChecked(false);});// 监听ID编辑框文本变化,启用或禁用发送按钮auto frameIdTextChanged = [this]() {bool hasFrameId = !m_ui->frameIdEdit->text().isEmpty();m_ui->sendButton->setEnabled(hasFrameId);m_ui->sendButton->setToolTip(hasFrameId? QString() : tr("Cannot send because no Frame ID was given."));};connect(m_ui->frameIdEdit, &QLineEdit::textChanged, frameIdTextChanged);frameIdTextChanged(); // 初始化时也检查一次// 发送按钮点击事件处理,构造并发送CAN帧connect(m_ui->sendButton, &QPushButton::clicked, [this]() {uint frameId = m_ui->frameIdEdit->text().toUInt(nullptr, 16);QString data = m_ui->payloadEdit->text();QByteArray payload = QByteArray::fromHex(data.remove(QLatin1Char(' ')).toLatin1()); // 转换为字节数组QCanBusFrame frame(frameId, payload); // 创建CAN帧frame.setExtendedFrameFormat(m_ui->extendedFormatBox->isChecked()); // 设置扩展帧格式frame.setFlexibleDataRateFormat(m_ui->flexibleDataRateBox->isChecked()); // 设置灵活数据速率格式frame.setBitrateSwitch(m_ui->bitrateSwitchBox->isChecked()); // 设置位速率切换if (m_ui->errorFrame->isChecked())frame.setFrameType(QCanBusFrame::ErrorFrame); // 错误帧else if (m_ui->remoteFrame->isChecked())frame.setFrameType(QCanBusFrame::RemoteRequestFrame); // 远程请求帧emit sendFrame(frame); // 发射信号,携带构造好的CAN帧});
}// 析构函数,释放UI指针
SendFrameBox::~SendFrameBox()
{delete m_ui;
}

m_ui 发送帧界面

在这里插入图片描述

连接类

/** 文件名:ConnectDialog.h* 描述:此文件定义了ConnectDialog类,这是一个用于配置和选择CAN总线设备的对话框。*       用户可以通过该对话框选择CAN插件、设备接口以及配置CAN设备的特定参数。*/#ifndef CONNECTDIALOG_H
#define CONNECTDIALOG_H// 包含必要的头文件
#include <QCanBusDevice>      // 提供CAN总线设备操作接口
#include <QCanBusDeviceInfo>  // 表示一个物理CAN设备的信息
#include <QDialog>           // 继承自QDialog,作为基础对话框类// 包含自动生成的用户界面类头文件(由Qt Designer生成)
QT_BEGIN_NAMESPACE
namespace Ui {
class ConnectDialog;
}
QT_END_NAMESPACE// 前向声明Settings结构体,用于存储当前的设置信息
class ConnectDialog : public QDialog {Q_OBJECTpublic:// 使用QPair存储配置键和对应的值typedef QPair<QCanBusDevice::ConfigurationKey, QVariant> ConfigurationItem;// 定义一个结构体来保存所有设置struct Settings {QString pluginName;          // CAN总线插件名称QString deviceInterfaceName; // 设备接口名称QList<ConfigurationItem> configurations; // 额外的设备配置项列表bool useConfigurationEnabled;           // 是否使用自定义配置标志};// 构造函数,可选传入父窗口指针explicit ConnectDialog(QWidget *parent = nullptr);// 析构函数~ConnectDialog();// 获取当前设置信息Settings settings() const;private slots:// 插件改变时的槽函数void pluginChanged(const QString &plugin);// 接口改变时的槽函数void interfaceChanged(const QString &interface);// 确定按钮点击时的槽函数void ok();// 取消按钮点击时的槽函数void cancel();private:// 根据配置键获取配置值QString configurationValue(QCanBusDevice::ConfigurationKey key);// 恢复默认设置void revertSettings();// 更新界面上显示的设置信息void updateSettings();// 私有成员变量Ui::ConnectDialog *m_ui;            // 对话框的用户界面指针Settings m_currentSettings;         // 当前对话框中的设置QList<QCanBusDeviceInfo> m_interfaces; // 可用的CAN设备接口信息列表
};#endif // CONNECTDIALOG_H

连接类实现

#include "connectdialog.h"
#include "ui_connectdialog.h"// 引入QCanBus相关头文件,用于访问CAN总线设备操作接口和设备信息
#include <QCanBus>// ConnectDialog类的实现开始
ConnectDialog::ConnectDialog(QWidget *parent) :QDialog(parent), // 初始化父窗口指针m_ui(new Ui::ConnectDialog) // 初始化用户界面指针并分配内存
{m_ui->setupUi(this); // 使用Qt Designer生成的用户界面配置此对话框// 设置错误过滤器编辑框的验证器,允许0至0x1FFFFFFF之间的整数m_ui->errorFilterEdit->setValidator(new QIntValidator(0, 0x1FFFFFFFU, this));// 为"回环"和"接收自身消息"组合框添加选项m_ui->loopbackBox->addItem(tr("unspecified"), QVariant());m_ui->loopbackBox->addItem(tr("false"), QVariant(false));m_ui->loopbackBox->addItem(tr("true"), QVariant(true));m_ui->receiveOwnBox->addItem(tr("unspecified"), QVariant());m_ui->receiveOwnBox->addItem(tr("false"), QVariant(false));m_ui->receiveOwnBox->addItem(tr("true"), QVariant(true));// 添加CAN-FD支持选项m_ui->canFdBox->addItem(tr("false"), QVariant(false));m_ui->canFdBox->addItem(tr("true"), QVariant(true));// 允许数据速率框支持灵活数据速率m_ui->dataBitrateBox->setFlexibleDateRateEnabled(true);// 连接信号和槽connect(m_ui->okButton, &QPushButton::clicked, this, &ConnectDialog::ok); // 确定按钮点击connect(m_ui->cancelButton, &QPushButton::clicked, this, &ConnectDialog::cancel); // 取消按钮点击connect(m_ui->useConfigurationBox, &QCheckBox::clicked, m_ui->configurationBox, &QGroupBox::setEnabled); // 配置使用状态改变connect(m_ui->pluginListBox, &QComboBox::currentTextChanged, this, &ConnectDialog::pluginChanged); // 插件选择变化connect(m_ui->interfaceListBox, &QComboBox::currentTextChanged, this, &ConnectDialog::interfaceChanged); // 接口选择变化// 隐藏原始过滤器编辑框及其标签,默认不显示m_ui->rawFilterEdit->hide();m_ui->rawFilterLabel->hide();// 加载可用的CAN总线插件到插件列表框m_ui->pluginListBox->addItems(QCanBus::instance()->plugins());// 初始化设置并更新UIupdateSettings();
}ConnectDialog::~ConnectDialog()
{delete m_ui; // 释放UI指针占用的内存
}// 获取当前设置
ConnectDialog::Settings ConnectDialog::settings() const
{return m_currentSettings;
}// 插件改变时更新界面和设备列表
void ConnectDialog::pluginChanged(const QString &plugin)
{m_ui->interfaceListBox->clear(); // 清空现有设备列表m_interfaces = QCanBus::instance()->availableDevices(plugin); // 获取新插件下的设备for (const QCanBusDeviceInfo &info : qAsConst(m_interfaces))m_ui->interfaceListBox->addItem(info.name()); // 添加设备接口到列表
}// 接口改变时更新接口相关信息
void ConnectDialog::interfaceChanged(const QString &interface)
{// 重置并更新当前选定接口的相关信息m_ui->isVirtual->setChecked(false);m_ui->isFlexibleDataRateCapable->setChecked(false);for (const QCanBusDeviceInfo &info : qAsConst(m_interfaces)) {if (info.name() == interface) {m_ui->descriptionLabel->setText(info.description());QString serialNumber = info.serialNumber();if (serialNumber.isEmpty())serialNumber = tr("n/a");m_ui->serialNumberLabel->setText(tr("Serial: %1").arg(serialNumber));m_ui->channelLabel->setText(tr("Channel: %1").arg(info.channel()));m_ui->isVirtual->setChecked(info.isVirtual());m_ui->isFlexibleDataRateCapable->setChecked(info.hasFlexibleDataRate());break;}}
}// 点击确定按钮时执行的操作
void ConnectDialog::ok()
{updateSettings(); // 更新设置accept(); // 接受对话框
}// 点击取消按钮时执行的操作
void ConnectDialog::cancel()
{revertSettings(); // 恢复原始设置reject(); // 拒绝对话框
}// 根据键获取配置的值
QString ConnectDialog::configurationValue(QCanBusDevice::ConfigurationKey key)
{QVariant result;// 遍历配置项寻找匹配的键for (const ConfigurationItem &item : qAsConst(m_currentSettings.configurations)) {if (item.first == key) {result = item.second;break;}}// 特殊处理未指定情况if (result.isNull() && (key == QCanBusDevice::LoopbackKey ||key == QCanBusDevice::ReceiveOwnKey)) {return tr("unspecified");}// 返回配置值的字符串表示形式return result.toString();
}// 恢复到初始设置状态
void ConnectDialog::revertSettings()
{// 重置各UI组件到上次保存的设置状态m_ui->pluginListBox->setCurrentText(m_currentSettings.pluginName);m_ui->interfaceListBox->setCurrentText(m_currentSettings.deviceInterfaceName);m_ui->useConfigurationBox->setChecked(m_currentSettings.useConfigurationEnabled);// ...省略其他设置恢复逻辑
}// 根据UI当前状态更新设置
void ConnectDialog::updateSettings()
{// 从UI获取最新选择和输入值,更新设置结构体m_currentSettings.pluginName = m_ui->pluginListBox->currentText();m_currentSettings.deviceInterfaceName = m_ui->interfaceListBox->currentText();m_currentSettings.useConfigurationEnabled = m_ui->useConfigurationBox->isChecked();// ...省略其他设置更新逻辑,包括循环遍历配置项并添加到m_currentSettings.configurations
}

连接类UI设计

在这里插入图片描述
在这里插入图片描述

主窗口类

/** 主窗口类(MainWindow)的声明文件** 该文件定义了MainWindow类,继承自QMainWindow,用于管理应用程序的主要界面和交互逻辑。* 包含了对CAN总线设备的连接、数据发送接收、错误处理以及界面更新等功能。*/#ifndef MAINWINDOW_H // 防止多重包含的预处理器宏
#define MAINWINDOW_H// 引入必要的头文件
#include <QCanBusDevice> // 包含CAN总线设备相关的错误类型
#include <QMainWindow> // 继承自QMainWindow的基础类// 类的前向声明,减少编译依赖
class ConnectDialog; // 连接对话框类的前向声明
class QLabel; // 标签控件类的前向声明,用于显示状态和写入帧的数量
class QTimer; // 定时器类的前向声明,用于监控总线状态// 命名空间QT的开始与结束,包含自定义或Qt相关的内容
QT_BEGIN_NAMESPACE// 前向声明QCanBusFrame,用于CAN数据帧处理
class QCanBusFrame;// 结束命名空间QT
QT_END_NAMESPACE// MainWindow类的声明开始
class MainWindow : public QMainWindow
{Q_OBJECT // 必须的宏,启用Qt的信号和槽机制public:// 构造函数,可选传入父窗口指针explicit MainWindow(QWidget *parent = nullptr);// 析构函数~MainWindow();private slots: // 私有槽函数部分// 处理接收到的数据帧void processReceivedFrames();// 发送数据帧void sendFrame(const QCanBusFrame &frame) const;// 处理CAN总线错误void processErrors(QCanBusDevice::CanBusError) const;// 连接CAN设备void connectDevice();// 总线状态更新void busStatus();// 断开与CAN设备的连接void disconnectDevice();// 处理已写入数据帧的数量更新void processFramesWritten(qint64);protected: // 保护成员函数,子类可访问// 重写的关闭事件处理函数void closeEvent(QCloseEvent *event) override;private: // 私有成员函数和变量// 初始化动作与信号槽连接void initActionsConnections();// 记录已写入数据帧的数量qint64 m_numberFramesWritten;// 界面相关的UI指针Ui::MainWindow *m_ui; // 主窗口界面QLabel *m_status; // 显示状态的标签QLabel *m_written; // 显示已写入帧数的标签// 连接对话框实例,用于设备连接设置ConnectDialog *m_connectDialog;// CAN总线设备指针,采用unique_ptr智能指针管理std::unique_ptr<QCanBusDevice> m_canDevice;// 用于定期检查总线状态的定时器QTimer *m_busStatusTimer;
};

主窗口类实现

#include "mainwindow.h"#include "ui_mainwindow.h"#include "connectdialog.h"// 引入Qt CAN总线模块相关的头文件#include <QCanBus>#include <QCanBusFrame>#include <QCloseEvent> // 处理窗口关闭事件#include <QDesktopServices> // 用于打开外部链接,如文档或网页#include <QTimer> // 定时器,用于定期检查CAN总线状态// MainWindow构造函数,负责建立用户界面及基础设置MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) // 继承自QWidget的构造函数, m_ui(new Ui::MainWindow) // 使用Qt User Interface Compiler产生的界面类, m_busStatusTimer(new QTimer(this)) // 初始化一个定时器来监控CAN总线状态{m_ui->setupUi(this); // 调用UI设置方法,加载设计的界面布局// 初始化连接对话框,用于选择CAN设备m_connectDialog = new ConnectDialog;// 在状态栏添加两个标签,分别显示总线状态和已发送帧的数量m_status = new QLabel;m_ui->statusBar->addPermanentWidget(m_status);m_written = new QLabel;m_ui->statusBar->addWidget(m_written);// 初始化界面元素与功能之间的连接关系initActionsConnections();// 稍后自动显示连接设备对话框,给予用户时间准备QTimer::singleShot(50, m_connectDialog, &ConnectDialog::show);// 设置定时器触发时调用busStatus函数检查CAN总线状态connect(m_busStatusTimer, &QTimer::timeout, this, &MainWindow::busStatus);}// 析构函数,清理分配的资源MainWindow::~MainWindow(){delete m_connectDialog; // 删除连接对话框实例delete m_ui; // 删除界面设置实例}// 初始化窗口中的动作(如菜单项)与槽函数的关联void MainWindow::initActionsConnections(){// 初始化界面按钮状态,初始时只有“连接”可用m_ui->actionDisconnect->setEnabled(false);m_ui->sendFrameBox->setEnabled(false);// 当用户在SendFrameBox中点击发送时,触发sendFrame槽处理发送CAN帧connect(m_ui->sendFrameBox, &SendFrameBox::sendFrame, this, &MainWindow::sendFrame);// “连接”菜单项点击后显示连接设备对话框connect(m_ui->actionConnect, &QAction::triggered, [this]() {// 断开当前连接(如果存在),然后显示连接对话框m_canDevice.release()->deleteLater();m_connectDialog->show();});// 对话框确认后尝试连接选定的CAN设备connect(m_connectDialog, &QDialog::accepted, this, &MainWindow::connectDevice);// “断开”菜单项点击后调用disconnectDevice函数断开CAN总线connect(m_ui->actionDisconnect, &QAction::triggered, this, &MainWindow::disconnectDevice);// “重置控制器”菜单项点击后调用CAN设备的resetController方法connect(m_ui->actionResetController, &QAction::triggered, this, [this]() {m_canDevice->resetController();});// “退出”菜单项点击后关闭主窗口connect(m_ui->actionQuit, &QAction::triggered, this, &QWidget::close);// “关于Qt”菜单项点击后显示Qt的关于信息connect(m_ui->actionAboutQt, &QAction::triggered, qApp, &QApplication::aboutQt);// “清除日志”菜单项点击后清空接收消息的文本编辑框connect(m_ui->actionClearLog, &QAction::triggered, m_ui->receivedMessagesEdit, &QTextEdit::clear);// “插件文档”菜单项点击后打开Qt CAN Bus插件的在线文档页面connect(m_ui->actionPluginDocumentation, &QAction::triggered, this, []() {QDesktopServices::openUrl(QUrl("http://doc.qt.io/qt-5/qtcanbus-backends.html"));});}
//处理CAN总线设备发生的错误,根据不同的错误类型更新状态栏显示错误信息。
void MainWindow::processErrors(QCanBusDevice::CanBusError error) const
{switch (error) {case QCanBusDevice::ReadError:case QCanBusDevice::WriteError:case QCanBusDevice::ConnectionError:case QCanBusDevice::ConfigurationError:case QCanBusDevice::UnknownError:m_status->setText(m_canDevice->errorString());break;default:break;}
}
//连接CAN总线设备的过程,包括设置设备、配置参数、连接状态更新和界面反馈。
void MainWindow::connectDevice()
{// 读取连接对话框中的设置const ConnectDialog::Settings p = m_connectDialog->settings();// 尝试创建CAN设备QString errorString;m_canDevice.reset(QCanBus::instance()->createDevice(p.pluginName, p.deviceInterfaceName, &errorString));// 错误处理if (!m_canDevice) {m_status->setText(tr("Error creating device '%1', reason: '%2'").arg(p.pluginName).arg(errorString));return;}// 初始化帧计数m_numberFramesWritten = 0;// 连接错误、接收帧、写入帧的信号连接connect(m_canDevice.get(), &QCanBusDevice::errorOccurred, this, &MainWindow::processErrors);connect(m_canDevice.get(), &QCanBusDevice::framesReceived, this, &MainWindow::processReceivedFrames);connect(m_canDevice.get(), &QCanBusDevice::framesWritten, this, &MainWindow::processFramesWritten);// 应用设置if (p.useConfigurationEnabled) {for (const ConnectDialog::ConfigurationItem &item : p.configurations)m_canDevice->setConfigurationParameter(item.first, item.second);}// 连接设备if (!m_canDevice->connectDevice()) {// 错误处理m_status->setText(tr("Connection error: %1").arg(m_canDevice->errorString()));m_canDevice.reset();} else {// 更新界面状态m_ui->actionConnect->setEnabled(false);m_ui->actionDisconnect->setEnabled(true);m_ui->sendFrameBox->setEnabled(true);// 更新状态栏显示连接信息const QVariant bitRate = m_canDevice->configurationParameter(QCanBusDevice::BitRateKey);if (bitRate.isValid()) {const bool isCanFd = m_canDevice->configurationParameter(QCanBusDevice::CanFdKey).toBool();const QVariant dataBitRate = m_canDevice->configurationParameter(QCanBusDevice::DataBitRateKey);if (isCanFd && dataBitRate.isValid()) {m_status->setText(tr("Plugin: %1, connected to %2 at %3 / %4 kBit/s").arg(p.pluginName).arg(p.deviceInterfaceName).arg(bitRate.toInt() / 1000).arg(dataBitRate.toInt() / 1000));} else {m_status->setText(tr("Plugin: %1, connected to %2 at %3 kBit/s").arg(p.pluginName).arg(p.deviceInterfaceName).arg(bitRate.toInt() / 1000));}} else {m_status->setText(tr("Plugin: %1, connected to %2").arg(p.pluginName).arg(p.deviceInterfaceName));}// 总线状态定时器if (m_canDevice->hasBusStatus())m_busStatusTimer->start(2000);elsem_ui->busStatus->setText(tr("No CAN bus status available."));}}
//更新并显示CAN总线的状态,如Good, Warning, Error等。
void MainWindow::busStatus()
{// 检查设备和状态if (!m_canDevice || !m_canDevice->hasBusStatus()) {m_ui->busStatus->setText(tr("No CAN bus status available."));m_busStatusTimer->stop();return;}// 更新总线状态switch (m_canDevice->busStatus()) {case QCanBusDevice::CanBusStatus::Good:m_ui->busStatus->setText("CAN bus status: Good.");break;case QCanBusDevice::CanBusStatus::Warning:m_ui->busStatus->setText("CAN bus status: Warning.");break;case QCanBusDevice::CanBusStatus::Error:m_ui->busStatus->setText("CAN bus status: Error.");break;case QCanBusDevice::CanBusStatus::BusOff:m_ui->busStatus->setText("CAN bus status: Bus Off.");break;default:m_ui->busStatus->setText("CAN bus status: Unknown.");break;}
}
//断开与CAN总线设备的连接并更新界面状态。
void MainWindow::disconnectDevice()
{// 断开设备if (!m_canDevice)return;m_busStatusTimer->stop();m_canDevice->disconnectDevice();// 更新界面状态m_ui->actionConnect->setEnabled(true);m_ui->actionDisconnect->setEnabled(false);m_ui->sendFrameBox->setEnabled(false);m_status->setText(tr("Disconnected"));
}
//处理写入帧完成的回调,更新已写入帧计数。
void MainWindow::processFramesWritten(qint64 count)
{m_numberFramesWritten += count;m_written->setText(tr("%1 frames written").arg(m_numberFramesWritten));
}
//关闭事件处理,先关闭连接对话框再接受关闭事件。
void MainWindow::closeEvent(QCloseEvent *event)
{m_connectDialog->close();event->accept();
}
//根据CAN帧标志位返回字符串表示,如B代表BitrateSwitch开启,E代表Error State Indicator,L代表Local Echo。
static QString frameFlags(const QCanBusFrame &frame)
{QString result = QLatin1String(" --- ");if (frame.hasBitrateSwitch())result[1] = QLatin1Char('B');if (frame.hasErrorStateIndicator())result[2] = QLatin1Char('E');if (frame.hasLocalEcho())result[3] = QLatin1Char('L');return result;
}
//处理接收到的CAN帧,解析并显示到界面。
void MainWindow::processReceivedFrames()
{if (!m_canDevice)return;while (m_canDevice->framesAvailable()) {const QCanBusFrame frame = m_canDevice->readFrame();QString view;if (frame.frameType() == QCanBusFrame::ErrorFrame)view = m_canDevice->interpretErrorFrame(frame);elseview = frame.toString();const QString time = QString::fromLatin1("%1.%2  ").arg(frame.timeStamp().seconds(), 10, 10, QLatin1Char(' ')).arg(frame.timeStamp().microSeconds() / 100, 4, 10, QLatin1Char('0'));const QString flags = frameFlags(frame);m_ui->receivedMessagesEdit->append(time + flags + view);}
}
//发送CAN帧到设备。
void MainWindow::sendFrame(const QCanBusFrame &frame) const
{if (!m_canDevice)return;m_canDevice->writeFrame(frame);
}

主界面UI

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

整体UI

在这里插入图片描述

QT案例

在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词