QT串列埠助手(四):資料傳送

zzssdd2發表於2021-01-30

作者:zzssdd2

E-mail:zzssdd2@foxmail.com

一、前言

開發環境:Qt5.12.10 + MinGW

實現的功能

  • 串列埠資料的傳送
  • ascii字元與hex字元的相互轉換
  • 自動追加回車換行符(\r\n)
  • 傳送資料的統計與顯示
  • 傳送清零
  • 定時傳送

涉及的知識點

  • QSerialPort類的使用
  • 資料格式的轉換
  • QTimer類的使用
  • 控制元件QPlainTextEditQCheckBoxQPushButtonQLabel的使用

二、功能實現

《QT串列埠助手(三):資料接收》實現了接收模組的功能,本章講解傳送模組的各個功能。

2.1、字元判斷

若勾選了HEX格式傳送,那麼需要對傳送框的字元進行合法判斷。這裡使用到QPlainTextEdittextChanged訊號來監測傳送框資料的改變,在槽函式中對資料進行判別:

/*傳送文字框訊號槽*/
connect(ui->Send_TextEdit, &QPlainTextEdit::textChanged, this, [=](){
    //獲取傳送框字元
    SendTextEditStr = ui->Send_TextEdit->document()->toPlainText();
    if (SendTextEditStr.isEmpty())
    {
        return;
    }
    //勾選hex傳送則判斷是否有非法hex字元
    if (ui->HexSend_checkBox->isChecked())
    {
        char ch;
        bool flag = false;
        uint32_t i, len;
        //去掉無用符號
        SendTextEditStr = SendTextEditStr.replace(' ',"");
        SendTextEditStr = SendTextEditStr.replace(',',"");
        SendTextEditStr = SendTextEditStr.replace('\r',"");
        SendTextEditStr = SendTextEditStr.replace('\n',"");
        SendTextEditStr = SendTextEditStr.replace('\t',"");
        SendTextEditStr = SendTextEditStr.replace("0x","");
        SendTextEditStr = SendTextEditStr.replace("0X","");
        //判斷資料合法性
        for(i = 0, len = SendTextEditStr.length(); i < len; i++)
        {
            ch = SendTextEditStr.at(i).toLatin1();
            if (ch >= '0' && ch <= '9') {
                flag = false;
            } else if (ch >= 'a' && ch <= 'f') {
                flag = false;
            } else if (ch >= 'A' && ch <= 'F') {
                flag = false;
            } else {
                flag = true;
            }
        }
        if(flag) QMessageBox::warning(this,"警告","輸入內容包含非法16進位制字元");
    }
    //QString轉QByteArray
    SendTextEditBa = SendTextEditStr.toUtf8();
});

[signal]void QPlainTextEdit::textChanged()

This signal is emitted whenever the document's content changes; for example, when text is inserted or deleted, or when formatting is applied.

Note: Notifier signal for property plainText.

這樣我們在進行輸入時,如果包含非法字元就會有彈框提示(我這裡對','、"0x"、"0X"等字元過濾是為了方便有時從程式碼中直接複製陣列資料傳送):

2.2、資料轉換

通過是否勾選HEX傳送判斷使用ascii格式還是hex格式傳送資料,使用QCheckBoxstateChanged訊號對勾選狀態進行檢測,然後在對應的槽函式中進行資料格式的轉換。

/*HEX傳送chexkBox訊號槽*/
connect(ui->HexSend_checkBox,&QCheckBox::stateChanged,this,[=](int state){
    if (SendTextEditStr.isEmpty())
    {
        return;
    }
    //asccii與hex轉換
    if (state == Qt::Checked)
    {
        //轉換成QByteArray -> 轉換成16進位制數,按空格分開 -> 轉換為大寫
        SendTextEditBa = SendTextEditBa.toHex(' ').toUpper();
        ui->Send_TextEdit->document()->setPlainText(SendTextEditBa);
    }
    else
    {
        //從QByteArray轉換為QString
        SendTextEditStr = SendTextEditBa.fromHex(SendTextEditBa);
        ui->Send_TextEdit->document()->setPlainText(SendTextEditStr);
    }
});

[signal]void QCheckBox::stateChanged(int state)

This signal is emitted whenever the checkbox's state changes, i.e., whenever the user checks or unchecks it.

state contains the checkbox's new Qt::CheckState.

2.3、手動傳送

當點選傳送按鈕時觸發QPushButton的點選訊號,在對應的槽函式中將傳送框的資料按照選定格式傳送出去,程式主體如下:

/*
    函   數:on_Send_Bt_clicked
    描   述:傳送按鍵點選槽函式
    輸   入:無
    輸   出:無
*/
void Widget::on_Send_Bt_clicked()
{
    if (isSerialOpen != false)
    {
        /*將傳送框資料傳送*/
        SerialSendData(SendTextEditBa);
    }
    else
    {
        QMessageBox::information(this, "提示", "串列埠未開啟");
    }
}

QpushButton繼承自QAbstractButton。關於按鍵點選訊號的描述:

[signal]void QAbstractButton::clicked(bool checked = false)

This signal is emitted when the button is activated (i.e., pressed down then released while the mouse cursor is inside the button), when the shortcut key is typed, or when click() or animateClick() is called. Notably, this signal is not emitted if you call setDown(), setChecked() or toggle().

If the button is checkable, checked is true if the button is checked, or false if the button is unchecked.

/*
    函   數:SendData
    描   述:串列埠傳送資料
    輸   入:無
    輸   出:無
*/
void Widget::SerialSendData(QByteArray baData)
{
    if (baData.isEmpty() != true)
    {
        /*是否加回車換行*/
        if (ui->AddNewLine_Box->isChecked())
        {
            baData.append("\r\n");
        }
        
        if (ui->HexSend_checkBox->isChecked())  // hex傳送
        {
            /*獲取hex格式的資料*/
            baData = baData.fromHex(baData);
            /*傳送hex資料*/
            serial->write(baData);
            /*是否顯示時間戳*/
            if (ui->TimeDisp_checkBox->isChecked())
            {
                QString strdata = baData.toHex(' ').trimmed().toUpper();
                ui->Receive_TextEdit->setTextColor(QColor("blue"));
                ui->Receive_TextEdit->append(QString("[%1]TX -> ").arg(QTime::currentTime().toString("HH:mm:ss:zzz")));
                ui->Receive_TextEdit->setTextColor(QColor("black"));
                ui->Receive_TextEdit->insertPlainText(strdata);
            }
        }
        else     //ascii傳送
        {
            /*傳送ascii資料*/
            serial->write(baData);
            /*是否顯示時間戳*/
            if (ui->TimeDisp_checkBox->isChecked())
            {
                QString strdata = QString(baData);
                ui->Receive_TextEdit->setTextColor(QColor("red"));
                ui->Receive_TextEdit->append(QString("[%1]TX -> ").arg(QTime::currentTime().toString("HH:mm:ss:zzz")));
                ui->Receive_TextEdit->setTextColor(QColor("black"));
                ui->Receive_TextEdit->insertPlainText(strdata);
            }
        }
        //移動游標到末尾
        ui->Receive_TextEdit->moveCursor(QTextCursor::End);
        //更新傳送計數
        serialDataTotalTxCnt += baData.length();
        ui->TxCnt_label->setText(QString::number(serialDataTotalTxCnt));
    }
    else
    {
        QMessageBox::warning(this, "警告", "資料為空");
    }
}

如果勾選了顯示時間戳則在每次資料傳送後將資料填充到接收框進行顯示;程式碼中對傳送的不同格式資料進行了不同顏色的標記;傳送後對傳送計數框進行更新。

QSerialPort繼承自QIODevice,串列埠傳送資料就是使用QIODevice類的write方法:

qint64 QIODevice::write(const QByteArray&byteArray)

This is an overloaded function.

Writes the content of byteArray to the device. Returns the number of bytes that were actually written, or -1 if an error occurred.

2.4、定時傳送

使用QT中的定時器QTimer按照設定的時間對傳送框資料進行自動傳送。呼叫定時器的超時訊號來觸發槽函式中的傳送操作:

/*定時傳送定時器*/
TimerSend = new QTimer(this);
/*定時器超時訊號槽*/
connect(TimerSend, &QTimer::timeout, this, [=](){
    SerialSendData(SendTextEditBa);
});

[signal]void QTimer::timeout()

This signal is emitted when the timer times out.

Note: This is a private signal. It can be used in signal connections but cannot be emitted by the user.

定時傳送被勾選後,會觸發QCheckBoxstateChanged訊號來設定時間、開啟定時器:

/*
    函   數:on_TimeSend_checkBox_stateChanged
    描   述:定時傳送框勾選訊號對應槽函式
    輸   入:無
    輸   出:無
*/
void Widget::on_TimeSend_checkBox_stateChanged(int arg1)
{
    int time;

    /*判斷串列埠是否開啟*/
    if (false == isSerialOpen)
    {
        if (ui->TimeSend_checkBox->isChecked())
        {
            QMessageBox::information(this, "提示", "串列埠未開啟");
        }
        return;
    }
    /*判斷是否有資料*/
    if (ui->Send_TextEdit->document()->isEmpty() == true)
    {
        if (ui->TimeSend_checkBox->isChecked())
        {
            QMessageBox::warning(this, "警告", "資料為空");
        }
        return;
    }
	/*判斷勾選狀態*/
    if (arg1 == Qt::Checked)
    {
        /*獲取設定時間*/
        time = ui->TimeSend_lineEdit->text().toInt();
        if (time > 0) {
            TimerSend->start(time);
        } else {
            QMessageBox::warning(this, "警告", "時間必須大於0");
        }
        ui->TimeSend_lineEdit->setEnabled(false);
    }
    else
    {
        /*停止傳送*/
        TimerSend->stop();
        ui->TimeSend_lineEdit->setEnabled(true);
    }
}
  • [slot]void QTimer::start(int msec)

Starts or restarts the timer with a timeout interval of msec milliseconds.

If the timer is already running, it will be stopped and restarted.

If singleShot is true, the timer will be activated only once.

  • [slot]void QTimer::stop()

    Stops the timer.

三、總結

本章主要講解資料的傳送與格式轉換。除了要知道各控制元件的訊號槽應用之外,還應該對QT中資料型別的一些操作方法有所瞭解。比如上面的程式碼中使用到的一些QStringQByteArray的常用資料操作方法:QString::replaceQString::atQString::toUtf8QByteArray::toHexQByteArray::fromHex等。

相關文章