Qt+C++串口调试接收发送数据曲线图

程序示例精选

Qt+C++串口调试接收发送数据曲线图

如需安装运行环境或远程调试,见文章底部个人QQ名片,由专业技术人员远程协助!

前言

这篇博客针对<<Qt+C++串口调试接收发送数据曲线图>>编写代码,代码整洁,规则,易读。 学习与应用推荐首选。


文章目录

一、所需工具软件

二、使用步骤

        1. 引入库

        2. 代码实现

        3. 运行结果

三、在线协助

一、所需工具软件

1. VS, Qt

2. C++

二、使用步骤

1.引入库

#include <QAction>
#include <QCheckBox>
#include <QDragEnterEvent>
#include <QDebug>
#include <QLineEdit>
#include <QMenu>
#include <QMenuBar>
#include <QtSerialPort/QSerialPort>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QGroupBox>
#include <QTextBrowser>
#include <QtWidgets/QFileDialog>
#include <QTimer>
#include <QtCore/QSettings>
#include <QtCore/QProcess>
#include <QStatusBar>
#include <QSplitter>
#include <data/SerialReadWriter.h>
#include <data/TcpServerReadWriter.h>
#include <data/TcpClientReadWriter.h>
#include <QRadioButton>
#include <QButtonGroup>
#include <data/BridgeReadWriter.h>
#include <QMimeData>
#include <QtSerialPort/QSerialPortInfo>
#include <data/SerialBridgeReadWriter.h>
#include <utils/FileUtil.h>
#include <QTextCodec>

2. 代码实现

代码如下:

void MainWindow::openReadWriter() {
    if (_readWriter != nullptr) {
        _readWriter->close();
        delete _readWriter;
        _readWriter = nullptr;
        emit serialStateChanged(false);
    }

    bool result;

    if (readWriterButtonGroup->checkedButton() == serialRadioButton) {
        _serialType = SerialType::Normal;
        auto settings = new SerialSettings();
        settings->name = serialPortNameComboBox->currentText();
        settings->baudRate = serialPortBaudRateComboBox->currentText().toInt();

        settings->dataBits = (QSerialPort::DataBits) serialPortDataBitsComboBox->currentText().toInt();
        settings->stopBits = (QSerialPort::StopBits) serialPortStopBitsComboBox->currentData().toInt();
        settings->parity = (QSerialPort::Parity) serialPortParityComboBox->currentData().toInt();
        auto readWriter = new SerialReadWriter(this);
        readWriter->setSerialSettings(*settings);
        qDebug() << settings->name << settings->baudRate << settings->dataBits << settings->stopBits
                 << settings->parity;
        result = readWriter->open();
        if (!result) {
            showWarning(tr("消息"), tr("串口被占用或者不存在"));
            return;
        }
        _readWriter = readWriter;
        _serialType = SerialType::Normal;
    } else if (readWriterButtonGroup->checkedButton() == tcpServerRadioButton) {
        _serialType = SerialType::TcpServer;
        auto address = tcpAddressLineEdit->text();
        bool ok;
        auto port = tcpPortLineEdit->text().toInt(&ok);
        if (!ok) {
            showMessage("", tr("端口格式不正确"));
            return;
        }

        auto readWriter = new TcpServerReadWriter(this);
        readWriter->
                setAddress(address);
        readWriter->
                setPort(port);
        qDebug() << address << port;
        result = readWriter->open();
        if (!result) {
            showWarning("", tr("建立服务器失败"));
            return;
        }
        connect(readWriter, &TcpServerReadWriter::currentSocketChanged, this, &MainWindow::updateTcpClient);
        connect(readWriter, &TcpServerReadWriter::connectionClosed, this, &MainWindow::clearTcpClient);
        _readWriter = readWriter;
    } else if (readWriterButtonGroup->checkedButton() == tcpClientRadioButton) {
        _serialType = SerialType::TcpClient;
        auto address = tcpAddressLineEdit->text();
        bool ok;
        auto port = tcpPortLineEdit->text().toInt(&ok);
        if (!ok) {
            showMessage("", tr("端口格式不正确"));
            return;
        }

        auto readWriter = new TcpClientReadWriter(this);
        readWriter->setAddress(address);
        readWriter->setPort(port);

        qDebug() << address << port;
        result = readWriter->open();
        if (!result) {
            showError("", tr("连接服务器失败"));
            return;
        }
        _readWriter = readWriter;
    } else if (readWriterButtonGroup->checkedButton() == serialBridgeRadioButton) {
        _serialType = SerialType::SerialBridge;

        auto settings1 = new SerialSettings();
        settings1->name = serialPortNameComboBox->currentText();
        settings1->baudRate = serialPortBaudRateComboBox->currentText().toInt();

        settings1->dataBits = (QSerialPort::DataBits) serialPortDataBitsComboBox->currentText().toInt();
        settings1->stopBits = (QSerialPort::StopBits) serialPortStopBitsComboBox->currentData().toInt();
        settings1->parity = (QSerialPort::Parity) serialPortParityComboBox->currentData().toInt();

        auto settings2 = new SerialSettings();
        settings2->name = secondSerialPortNameComboBox->currentText();
        settings2->baudRate = secondSerialPortBaudRateComboBox->currentText().toInt();

        settings2->dataBits = (QSerialPort::DataBits) secondSerialPortDataBitsComboBox->currentText().toInt();
        settings2->stopBits = (QSerialPort::StopBits) secondSerialPortStopBitsComboBox->currentText().toInt();
        settings2->parity = (QSerialPort::Parity) secondSerialPortParityComboBox->currentText().toInt();

        auto readWriter = new SerialBridgeReadWriter(this);

        readWriter->setSettings(*settings1, *settings2);
        result = readWriter->open();
        if (!result) {
            showWarning(tr("消息"), QString(tr("串口被占用或者不存在,%1")).arg(readWriter->settingsText()));
            return;
        }

        connect(readWriter, &SerialBridgeReadWriter::serial1DataRead, [this](const QByteArray &data) {
            showSendData(data);
        });

        connect(readWriter, &SerialBridgeReadWriter::serial2DataRead, [this](const QByteArray &data) {
            showReadData(data);
        });

        _readWriter = readWriter;
    } else {
        _serialType = SerialType::Bridge;
        auto settings = new SerialSettings();
        settings->name = serialPortNameComboBox->currentText();
        settings->baudRate = serialPortBaudRateComboBox->currentText().toInt();

        settings->dataBits = (QSerialPort::DataBits) serialPortDataBitsComboBox->currentText().toInt();
        settings->stopBits = (QSerialPort::StopBits) serialPortStopBitsComboBox->currentData().toInt();
        settings->parity = (QSerialPort::Parity) serialPortParityComboBox->currentData().toInt();

        auto address = tcpAddressLineEdit->text();
        bool ok;
        auto port = tcpPortLineEdit->text().toInt(&ok);
        if (!ok) {
            showMessage("", tr("端口格式不正确"));
            return;
        }

        auto readWriter = new BridgeReadWriter(this);

        readWriter->setSettings(*settings, address, static_cast<qint16>(port));
        result = readWriter->open();
        if (!result) {
            showWarning(tr("消息"), tr("串口被占用或者不存在"));
            return;
        }

        connect(readWriter, &BridgeReadWriter::currentSocketChanged,
                this, &MainWindow::updateTcpClient);
        connect(readWriter, &BridgeReadWriter::connectionClosed,
                this, &MainWindow::clearTcpClient);
        connect(readWriter, &BridgeReadWriter::serialDataRead, [this](const QByteArray &data) {
            showSendData(data);
        });
        connect(readWriter, &BridgeReadWriter::tcpDataRead, [this](const QByteArray &data) {
            showReadData(data);
        });

        _readWriter = readWriter;
    }
    connect(_readWriter, &AbstractReadWriter::readyRead,
            this, &MainWindow::readData);


    emit serialStateChanged(result);
}

void MainWindow::closeReadWriter() {
    stopAutoSend();
    if (_readWriter != nullptr) {
        _readWriter->close();
        delete _readWriter;
        _readWriter = nullptr;
    }
    emit serialStateChanged(false);
}

void MainWindow::createConnect() {

    connect(readWriterButtonGroup, QOverload<QAbstractButton *, bool>::of(&QButtonGroup::buttonToggled),
            [=](QAbstractButton *button, bool checked) {
                if (checked && isReadWriterOpen()) {
                    SerialType serialType;
                    if (button == tcpServerRadioButton) {
                        serialType = SerialType::TcpServer;
                    } else if (button == tcpClientRadioButton) {
                        serialType = SerialType::TcpClient;
                    } else if (button == bridgeRadioButton) {
                        serialType = SerialType::Bridge;
                    } else {
                        serialType = SerialType::Normal;
                    }

                    if (serialType != _serialType) {
                        if (showWarning("", tr("串口配置已经改变,是否重新打开串口?"))) {
                            openReadWriter();
                        }
                    }
                }
            });

    connect(this, &MainWindow::serialStateChanged, [this](bool isOpen) {
        setOpenButtonText(isOpen);
        QString stateText;
        if (isOpen) {
            stateText = QString(tr("串口打开成功,%1")).arg(_readWriter->settingsText());
        } else {
            stateText = QString(tr("串口关闭"));
        }
        skipSendCount = 0;
        updateStatusMessage(stateText);
    });

    connect(this, &MainWindow::readBytesChanged, this, &MainWindow::updateReadBytes);
    connect(this, &MainWindow::writeBytesChanged, this, &MainWindow::updateWriteBytes);
    connect(this, &MainWindow::currentWriteCountChanged, this, &MainWindow::updateCurrentWriteCount);

    connect(openSerialButton, &QPushButton::clicked, [=](bool value) {
        if (!isReadWriterOpen()) {
            openReadWriter();
        } else {
            closeReadWriter();
        }
    });

    connect(refreshSerialButton, &QPushButton::clicked, [=] {
        _dirty = true;
        updateSerialPortNames();
    });

    connect(saveReceiveDataButton, &QPushButton::clicked, this, &MainWindow::saveReceivedData);
    connect(clearReceiveDataButton, &QPushButton::clicked, this, &MainWindow::clearReceivedData);

    connect(saveSentDataButton, &QPushButton::clicked, this, &MainWindow::saveSentData);
    connect(clearSentDataButton, &QPushButton::clicked, this, &MainWindow::clearSentData);

    connect(autoSendCheckBox, &QCheckBox::clicked, [this] {
        autoSendTimer->stop();
    });

    connect(loopSendCheckBox, &QCheckBox::stateChanged, [this] {
        _loopSend = loopSendCheckBox->isChecked();
    });

    connect(resetLoopSendButton, &QPushButton::clicked, [this] {
        skipSendCount = 0;
        serialController->setCurrentCount(0);
        emit currentWriteCountChanged(0);
    });

    connect(currentSendCountLineEdit, &QLineEdit::editingFinished, [this] {
        bool ok;
        auto newCount = currentSendCountLineEdit->text().toInt(&ok);
        if (ok) {
            serialController->setCurrentCount(newCount);
        } else {
            currentSendCountLineEdit->setText(QString::number(serialController->getCurrentCount()));
        }
    });

    connect(sendLineButton, &QPushButton::clicked, [this] {
        if (!isReadWriterConnected()) {
            handlerSerialNotOpen();
            return;
        }

        if (autoSendState == AutoSendState::Sending) {
            stopAutoSend();
        } else {
            if (_dirty) {
                _dirty = false;
                _sendType = SendType::Line;
                updateSendData(hexCheckBox->isChecked(), sendTextEdit->toPlainText());
                updateSendType();
            }
            sendNextData();
            startAutoSendTimerIfNeed();
        }

        if (autoSendState == AutoSendState::Sending) {
            sendLineButton->setText(tr("停止"));
        } else {
            resetSendButtonText();
        }
    });

    connect(processTextButton, &QPushButton::clicked, [this] {
        openDataProcessDialog(sendTextEdit->toPlainText());
    });

    connect(clearTextButton, &QPushButton::clicked, [this]{
       sendTextEdit->clear();
    });

    connect(lineReturnButtonGroup, QOverload<QAbstractButton *, bool>::of(&QButtonGroup::buttonToggled),
            [=](QAbstractButton *button, bool checked) {
                if (checked) {
                    if (button == sendRReturnLineButton) {
                        lineReturn = QByteArray("\r");
                    } else if (button == sendNReturnLineButton) {
                        lineReturn = QByteArray("\n");
                    } else {
                        lineReturn = QByteArray("\r\n");
                    }
                }
            });

    connect(autoSendTimer, &QTimer::timeout,
            [this] {
                sendNextData();
            });
    connect(hexCheckBox, &QCheckBox::stateChanged, [this] {
        this->_dirty = true;
    });

    connect(sendTextEdit, &QTextEdit::textChanged, [this] {
        this->_dirty = true;
    });
}

void MainWindow::setOpenButtonText(bool isOpen) {
    if (isOpen) {
        openSerialButton->setText(tr("关闭"));
    } else {
        openSerialButton->setText("打开");
    }
}

void MainWindow::createActions() {
    openAct = new QAction(tr("&打开(&O)"), this);
    openAct->setShortcut(QKeySequence::Open);
    openAct->setStatusTip(tr("打开一个文件"));
    connect(openAct, &QAction::triggered, this, &MainWindow::open);

    saveAct = new QAction(tr("&保存(&S)"), this);
    saveAct->setShortcut(QKeySequence::Save);
    saveAct->setStatusTip(tr("保存一个文件"));
    connect(saveAct, &QAction::triggered, this, &MainWindow::save);

    validateDataAct = new QAction(tr("计算校验(&E)"), this);
    validateDataAct->setShortcut(tr("Ctrl+E"));
    validateDataAct->setStatusTip(tr("计算数据校验值"));
    connect(validateDataAct, &QAction::triggered, this, &MainWindow::openDataValidator);

    convertDataAct = new QAction(tr("数据转换(&T)"));
    convertDataAct->setShortcut(tr("Ctrl+T"));
    convertDataAct->setStatusTip(tr("数据转换"));
    connect(convertDataAct, &QAction::triggered, this, &MainWindow::openConvertDataDialog);

    dataProcessAct = new QAction(tr("数据处理(&P)"));
    dataProcessAct->setShortcut(tr("Ctrl+P"));
    dataProcessAct->setStatusTip(tr("数据处理"));
    connect(dataProcessAct, &QAction::triggered, [this] {
        openDataProcessDialog("");
    });
}

void MainWindow::createMenu() {
    fileMenu = menuBar()->addMenu(tr("文件(&F)"));
    fileMenu->addAction(openAct);
    fileMenu->addAction(saveAct);

    toolMenu = menuBar()->addMenu(tr("工具(&T)"));
    toolMenu->addAction(validateDataAct);
    toolMenu->addAction(convertDataAct);
    toolMenu->addAction(dataProcessAct);
}

void MainWindow::open() {
    auto lastDir = runConfig->lastDir;
    QString fileName = QFileDialog::getOpenFileName(this, tr("打开数据文件"), lastDir, "");
    if (fileName.isEmpty()) {
        return;
    }

    QFile file(fileName);
    if (file.open(QIODevice::ReadOnly)) {
        runConfig->lastDir = getFileDir(fileName);
        auto data = file.readAll();
        sendTextEdit->setText(QString::fromLocal8Bit(data));
    }
}

void MainWindow::save() {
    saveReceivedData();
}

void MainWindow::openDataValidator() {
    CalculateCheckSumDialog dialog(this);
    dialog.setModal(true);
    dialog.exec();
}

void MainWindow::openConvertDataDialog() {
    ConvertDataDialog dialog(this);
    dialog.setModal(true);
    dialog.exec();
}

void MainWindow::openDataProcessDialog(const QString &text) {
    DataProcessDialog dialog(text, this);
    dialog.setModal(true);

    int result = dialog.exec();
    if (result == QDialog::Accepted) {
        sendTextEdit->setText(dialog.text());
    }
}

void MainWindow::displayReceiveData(const QByteArray &data) {

    if (pauseReceiveCheckBox->isChecked()) {
        return;
    }

    static QString s;

    s.clear();

    if (addReceiveTimestampCheckBox->isChecked()) {
        s.append("[").append(getTimestamp()).append("] ");
    }

    if (!s.isEmpty()) {
        s.append(" ");
    }
    if (displayReceiveDataAsHexCheckBox->isChecked()) {
        s.append(dataToHex(data));
    } else {
        s.append(QString::fromLocal8Bit(data));
    }

    if (addLineReturnCheckBox->isChecked() || addReceiveTimestampCheckBox->isChecked()) {
        receiveDataBrowser->append(s);
    } else {
        auto text = receiveDataBrowser->toPlainText();
        text.append(s);
        receiveDataBrowser->setText(text);
        receiveDataBrowser->moveCursor(QTextCursor::End);
    }
}

void MainWindow::displaySentData(const QByteArray &data) {
    if (displaySendDataAsHexCheckBox->isChecked()) {
        sendDataBrowser->append(dataToHex(data));
    } else {
        sendDataBrowser->append(QString::fromLocal8Bit(data));
    }
}

void MainWindow::sendNextData() {
    if (isReadWriterConnected()) {
        if (skipSendCount > 0) {
            auto delay = skipSendCount * sendIntervalLineEdit->text().toInt();
            updateStatusMessage(QString("%1毫秒后发送下一行").arg(delay));
            skipSendCount--;
            return;
        }

        qDebug() << "sendNextData readEnd:" << serialController->readEnd() << "current:"
                 << serialController->getCurrentCount();
        if (!_loopSend && autoSendCheckBox->isChecked() && serialController->readEnd()) {
            serialController->setCurrentCount(0);
            stopAutoSend();
            return;
        }
        auto data = serialController->readNextFrame();
        if (data.isEmpty()) {
            updateStatusMessage(tr("空行,不发送"));
            if (autoSendCheckBox->isChecked()) {
                auto emptyDelay = emptyLineDelayLindEdit->text().toInt();
                auto sendInterval = sendIntervalLineEdit->text().toInt();
                if (emptyDelay > sendInterval) {
                    skipSendCount = emptyDelay / sendInterval;
                    if (emptyDelay % sendInterval != 0) {
                        skipSendCount += 1;
                    }
                    skipSendCount--;
                    updateStatusMessage(QString(tr("空行,%1毫秒后发送下一行")).arg(emptyDelay));
                }
            }
            emit currentWriteCountChanged(serialController->getCurrentCount());
            return;
        }
        writeData(data);
        if (sendLineReturnCheckBox->isChecked()) {
            writeData(lineReturn);
        }
        if (hexCheckBox->isChecked()) {
            updateStatusMessage(QString(tr("发送 %1")).arg(QString(dataToHex(data))));
        } else {
            updateStatusMessage(QString(tr("发送 %1")).arg(QString(data)));
        }
        emit currentWriteCountChanged(serialController->getCurrentCount());
    } else {
        handlerSerialNotOpen();
    }
}

void MainWindow::updateSendData(bool isHex, const QString &text) {
    if (serialController != nullptr) {
        QStringList lines = getLines(text);
        QList<QByteArray> dataList;
        if (isHex) {
            for (auto &line :lines) {
                dataList << dataFromHex(line);
            }
        } else {
            for (auto &line:lines) {
                dataList << line.toLocal8Bit();
            }
        }
        serialController->setData(dataList);
        totalSendCount = serialController->getTotalCount();
        updateTotalSendCount(totalSendCount);
    }
}

void MainWindow::readSettings() {

    qDebug() << "readSettings";

    updateSerialPortNames();

    QSettings settings("Zhou Jinlong", "Serial Wizard");

    settings.beginGroup("Basic");
    auto serialType = SerialType(settings.value("serial_type", static_cast<int >(SerialType::Normal)).toInt());
    if (serialType == SerialType::TcpServer) {
        tcpServerRadioButton->setChecked(true);
    } else if (serialType == SerialType::TcpClient) {
        tcpClientRadioButton->setChecked(true);
    } else if (serialType == SerialType::Bridge) {
        bridgeRadioButton->setChecked(true);
    } else if (serialType == SerialType::SerialBridge) {
        serialBridgeRadioButton->setChecked(true);
    } else {
        serialRadioButton->setChecked(true);
    }

    _serialType = serialType;

    settings.beginGroup("SerialSettings");
    auto nameIndex = settings.value("name", 0).toInt();
    auto baudRateIndex = settings.value("baud_rate", 5).toInt();
    auto dataBitsIndex = (QSerialPort::DataBits) settings.value("data_bits", 3).toInt();
    auto stopBitsIndex = (QSerialPort::StopBits) settings.value("stop_bits", 0).toInt();
    auto parityIndex = (QSerialPort::Parity) settings.value("parity", 0).toInt();
    auto sendText = settings.value("send_text", "").toString();

    auto maxCount = serialPortNameComboBox->maxCount();
    if (nameIndex > maxCount - 1) {
        nameIndex = 0;
    }
    serialPortNameComboBox->setCurrentIndex(nameIndex);
    serialPortBaudRateComboBox->setCurrentIndex(baudRateIndex);
    serialPortDataBitsComboBox->setCurrentIndex(dataBitsIndex);
    serialPortStopBitsComboBox->setCurrentIndex(stopBitsIndex);
    serialPortParityComboBox->setCurrentIndex(parityIndex);

    auto name2Index = settings.value("name2", 0).toInt();
    auto baudRate2Index = settings.value("baud_rate2", 5).toInt();
    auto dataBits2Index = (QSerialPort::DataBits) settings.value("data_bits2", 3).toInt();
    auto stopBits2Index = (QSerialPort::StopBits) settings.value("stop_bits2", 0).toInt();
    auto parity2Index = (QSerialPort::Parity) settings.value("parity2", 0).toInt();

    auto maxCount2 = serialPortNameComboBox->maxCount();
    if (name2Index > maxCount2 - 1) {
        name2Index = 0;
    }
    secondSerialPortNameComboBox->setCurrentIndex(name2Index);
    secondSerialPortBaudRateComboBox->setCurrentIndex(baudRate2Index);
    secondSerialPortDataBitsComboBox->setCurrentIndex(dataBits2Index);
    secondSerialPortStopBitsComboBox->setCurrentIndex(stopBits2Index);
    secondSerialPortParityComboBox->setCurrentIndex(parity2Index);

    settings.beginGroup("SerialReceiveSettings");
    auto addLineReturn = settings.value("add_line_return", true).toBool();
    auto displayReceiveDataAsHex = settings.value("display_receive_data_as_hex", false).toBool();
    auto addTimestamp = settings.value("add_timestamp", false).toBool();

    addLineReturnCheckBox->setChecked(addLineReturn);
    displayReceiveDataAsHexCheckBox->setChecked(displayReceiveDataAsHex);
    addReceiveTimestampCheckBox->setChecked(addTimestamp);

    settings.beginGroup("SerialSendSettings");
    auto sendAsHex = settings.value("send_as_hex", false).toBool();
    auto displaySendData = settings.value("display_send_data", false).toBool();
    auto displaySendDataAsHex = settings.value("display_send_data_as_hex", false).toBool();
    auto autoSend = settings.value("auto_send", false).toBool();
    auto autoSendInterval = settings.value("auto_send_interval", 100).toInt();
    auto emptyLineDelay = settings.value("empty_line_delay", 0).toInt();

    auto loopSend = settings.value("loop_send", false).toBool();

    hexCheckBox->setChecked(sendAsHex);
    displaySendDataCheckBox->setChecked(displaySendData);
    displaySendDataAsHexCheckBox->setChecked(displaySendDataAsHex);
    autoSendCheckBox->setChecked(autoSend);
    loopSendCheckBox->setChecked(loopSend);
    sendIntervalLineEdit->setText(QString::number(autoSendInterval));
    emptyLineDelayLindEdit->setText(QString::number(emptyLineDelay));

    auto sendLineReturn = settings.value("send_line_return", false).toBool();
    sendLineReturnCheckBox->setChecked(sendLineReturn);

    auto sendLineReturnType = LineReturn(
            settings.value("send_line_return_type", static_cast<int >(LineReturn::RN)).toInt());
    if (sendLineReturnType == LineReturn::R) {
        sendRReturnLineButton->setChecked(true);
    } else if (sendLineReturnType == LineReturn::N) {
        sendNReturnLineButton->setChecked(true);
    } else {
        sendRNLineReturnButton->setChecked(true);
    }

    settings.beginGroup("TcpSettings");
    auto ipList = getNetworkInterfaces();
    auto ipAddress = settings.value("tcp_address", "").toString();
    QString selectAddress = "";
    if (!ipAddress.isEmpty() && !ipList.isEmpty()) {
        auto found = false;
        for (const auto &ip:ipList) {
            if (getIpAddress(ip) == ipAddress) {
                selectAddress = ipAddress;
                found = true;
                break;
            }
        }
        if (!found) {
            selectAddress = getIpAddress(ipList.first());
        }
    }
    if (selectAddress.isEmpty()) {
        if (!ipList.isEmpty()) {
            do {
                for (const auto &ip:ipList) {
                    if (ip.type() == QNetworkInterface::Wifi && !getIpAddress(ip).isEmpty()) {
                        selectAddress = getIpAddress(ip);
                        break;
                    }
                }
                if (!selectAddress.isEmpty()) {
                    break;
                }
                for (const auto &ip:ipList) {
                    if (ip.type() == QNetworkInterface::Ethernet && !getIpAddress(ip).isEmpty()) {
                        selectAddress = getIpAddress(ip);
                    }
                }
                if (!selectAddress.isEmpty()) {
                    break;
                }

                selectAddress = getIpAddress(ipList.first());
            } while (false);
        }
    }

    tcpAddressLineEdit->setText(selectAddress);

    auto tcpPort = settings.value("tcp_port").toInt();
    tcpPortLineEdit->setText(QString::number(tcpPort));

    sendTextEdit->setText(sendText);

    settings.beginGroup("RunConfig");
    auto lastDir = settings.value("last_dir", "").toString();
    auto lastFilePath = settings.value("last_file_path", "").toString();

    runConfig = new RunConfig;
    runConfig->lastDir = lastDir;
    runConfig->lastFilePath = lastFilePath;

    _loopSend = loopSend;

    serialController = new LineSerialController();

    updateSendType();
}

3. 运行结果

动画演示

 

三、在线协助:

如需安装运行环境或远程调试,见文章底部个人 QQ 名片,由专业技术人员远程协助!
1)远程安装运行环境,代码调试
2)Qt, C++, Python入门指导
3)界面美化
4)软件制作

当前文章连接:Python+Qt桌面端与网页端人工客服沟通工具_alicema1111的博客-CSDN博客

博主推荐文章:python人脸识别统计人数qt窗体-CSDN博客

博主推荐文章:Python Yolov5火焰烟雾识别源码分享-CSDN博客

                         Python OpenCV识别行人入口进出人数统计_python识别人数-CSDN博客

个人博客主页:alicema1111的博客_CSDN博客-Python,C++,网页领域博主

博主所有文章点这里alicema1111的博客_CSDN博客-Python,C++,网页领域博主

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Android】 No matching variant of com.android.tools.build:gradle:[版本号] was found

项目报错 No matching variant of com.android.tools.build:gradle:8.1.1 was found. The consumer was configured to find a library for use during runtime, compatible with Java 8, packaged as a jar, and its dependencies declared externally, as well as attribute …

数据结构与算法:通往编程高地的必修课(文末送书)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

批量爬虫采集大数据的技巧和策略分享

作为一名专业的爬虫程序员&#xff0c;今天主要要和大家分享一些技巧和策略&#xff0c;帮助你在批量爬虫采集大数据时更高效、更顺利。批量爬虫采集大数据可能会遇到一些挑战&#xff0c;但只要我们掌握一些技巧&#xff0c;制定一些有效的策略&#xff0c;我们就能在数据采集…

linux安装 MySQL8 并配置开机自启动

目录 1.下载 mysql 安装包 2.上传并解压 mysql 3.修改 mysql 文件夹名 4.创建mysql 用户和用户组 5.数据目录 &#xff08;1&#xff09;创建目录 &#xff08;2&#xff09;赋予权限 6.初始化mysql &#xff08;1&#xff09;配置参数 &#xff08;2&#xff09;配置环…

VK1640B冰箱LED数显驱动IC资料介绍显示模式(8 段×12 位)CMOS 工艺制作

产品型号&#xff1a;VK1640B 封装形式&#xff1a;SSOP24 产品年份&#xff1a;新年份 概述 : VK1640B 是一款 LED&#xff08;发光二极管显示器&#xff09;驱动控制专用电路&#xff0c;内部集成有 MCU 数字接口、数据锁存器、LED 高压驱动。本产品采用 CMOS 工艺…

链表的顶级理解

目录 1.链表的概念及结构 2.链表的分类 单向或者双向 带头或者不带头 循环或者非循环 3.无头单向非循环链表的实现 3.1创建单链表 3.2遍历链表 3.3得到单链表的长度 3.4查找是否包含关键字 3.5头插法 3.6尾插法 3.7任意位置插入 3.8删除第一次出现关键字为key的节点 …

疲劳驾驶检测和识别4:C++实现疲劳驾驶检测和识别(含源码,可实时检测)

疲劳驾驶检测和识别4&#xff1a;C实现疲劳驾驶检测和识别(含源码&#xff0c;可实时检测) 目录 疲劳驾驶检测和识别4&#xff1a;C实现疲劳驾驶检测和识别(含源码&#xff0c;可实时检测) 1.疲劳驾驶检测和识别方法 2.人脸检测方法 3.疲劳驾驶识别模型(Python) &#xf…

家庭智慧管控中心——酷开系统全时AI

智能家居入口在哪&#xff1f; 或许你家已经有了&#xff01;举个例子&#xff0c;智能电视就是其中的一种&#xff01;智能家电的变革进度逐渐加深&#xff0c;人们对于智慧生活的需求也逐渐提高。伴随着人工智能、物联网、大数据等信息技术的发展&#xff0c;家电产品的智能化…

财务数据分析模板有哪些,能满足决策吗?

虽然企业的业务经营各有不同&#xff0c;但在财务数据分析上却有着相似的需求与流程&#xff0c;因此财务数据分析是可以形成一套标准化模板的。奥威BI数据可视化工具从多年丰富的BI项目中总结经验&#xff0c;形成一套标准化、系统化的财务数据分析模板&#xff0c;内含资产负…

FlashAttention算法详解

这篇文章的目的是详细的解释Flash Attention&#xff0c;为什么要解释FlashAttention呢&#xff1f;因为FlashAttention 是一种重新排序注意力计算的算法&#xff0c;它无需任何近似即可加速注意力计算并减少内存占用。所以作为目前LLM的模型加速它是一个非常好的解决方案&…

Redis数据结构之List

Redis 中列表&#xff08;List&#xff09;类型是用来存储多个有序的字符串&#xff0c;列表中的每个字符串成为元素 Eelement&#xff09;&#xff0c;一个列表最多可以存储 2^32-1 个元素。 在 Redis 中&#xff0c;可以对列表两端插入&#xff08;push&#xff09;和弹出&am…

[Go版]算法通关村第十二关黄金——字符串冲刺题

目录 题目&#xff1a;最长公共前缀解法1&#xff1a;纵向对比-循环内套循环写法复杂度&#xff1a;时间复杂度 O ( n ∗ m ) O(n*m) O(n∗m)、空间复杂度 O ( 1 ) O(1) O(1)Go代码 解法2&#xff1a;横向对比-两两对比&#xff08;类似合并K个数组、合并K个链表&#xff09;复…

<c++开发>通信工具 -之-SOME/IP移植部署 第一篇文章

&#xff1c;c开发&#xff1e;通信工具 -之-SOME/IP移植ubuntu部署 第一篇文章 一 前言 SOME/IP (Scalable service-Oriented MiddlewarE over IP) 是一种通信协议&#xff0c;主要用于嵌入式系统和车载网络中的服务导向通信。SOME/IP是AUTOSAR&#xff08;AUTomotive Open …

基于ssm的CRM客户管理系统(spring + springMVC + mybatis)营销业务信息java jsp源代码

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 基于ssm的CRM客户管理系统&#xff08;spring spring…

深入浅出 TCP/IP 协议栈

TCP/IP 协议栈是一系列网络协议的总和&#xff0c;是构成网络通信的核心骨架&#xff0c;它定义了电子设备如何连入因特网&#xff0c;以及数据如何在它们之间进行传输。TCP/IP 协议采用4层结构&#xff0c;分别是应用层、传输层、网络层和链路层&#xff0c;每一层都呼叫它的下…

ubuntu查看网速

使用speedomster测试网速 sudo apt-get install speedometer 查询需要测速的网卡 speedometer -r ens33 -t ens33 -r: 指定网卡的接收速度 -t: 指定网卡的发送速度 使用nload测试 sudo apt-get install nload 测速 nload -t 200 -i 1024 -o 128 -U M 参数含义&#xff0…

Temu闯关日韩受挫?跨境电商卖家如何打磨好营销链路

海外版拼多多 Temu 先后在日本和韩国上线&#xff0c;然而效果不似预期&#xff0c;日韩市场对这套“低价补贴”策略并不买账。作为一个尚未被日韩消费者熟悉的网站&#xff0c;其价格之便宜无法让消费者信任。除此之外更大的问题是&#xff0c;在日本卷不过线下零售与百元店&a…

qq windows版客户端0day复现——远程代码执行(七夕小礼物)

##ps&#xff1a;本文章仅用来分享&#xff0c;请勿将文章内的相关技术用于非法目的&#xff0c;请勿将文章内的相关技术用于非法目的&#xff0c;请勿将文章内的相关技术用于非法目的&#xff01;&#xff01;如有非法行为与本文章作者无任何关系。一切行为以遵守《中华人民共…

5.物联网LWIP之Socket编程优化与实现(补充4)

UDP编程模型 1.UDP C/S模型 2.UDP API socket int socket(int domain, int type, int protocol); domain: AF_INET 这是大多数用来产生socket的协议&#xff0c;使用TCP或UDP来传输&#xff0c;用IPv4的地址 AF_INET6 与上面类似&#xff0c;不过是来用IPv6的地址 …

GitLab与GitLab Runner安装(RPM与Docker方式),CI/CD初体验

背景 GitLab 是一个强大的版本控制系统和协作平台&#xff0c;记录一下在实际工作中关于 GitLab 的安装使用记录。 一开始使用 GitLab 时&#xff0c;是在 CentOS7 上直接以 rpm 包的方式进行安装&#xff0c;仅作为代码托管工具来使用&#xff0c;版本&#xff1a; 14.10.4 …