Matthias Pospiech
Matthias Pospiech

Reputation: 3494

Qt Example with HexStringValidator fails

I am using the following code from this example: https://doc.qt.io/qt-5/qtserialbus-can-example.html

enum {
    MaxPayload = 8,
    MaxPayloadFd = 64
};

HexStringValidator::HexStringValidator(QObject *parent) :
    QValidator(parent),
    m_maxLength(MaxPayload)
{
}

HexStringValidator::HexStringValidator(QObject *parent, uint maxLength) :
    QValidator(parent),
    m_maxLength(maxLength)
{
}

class HexStringValidator : public QValidator
{
    Q_OBJECT

public:
    explicit HexStringValidator(QObject *parent = nullptr);
    explicit HexStringValidator(QObject *parent, uint maxLength);

    QValidator::State validate(QString &input, int &pos) const;

    void setMaxLength(int maxLength);

private:
    uint m_maxLength = 0;
};

The problem is in this function: I have a lineedit with this validator and call it with a valid Hex String.

ui->lineEditCANCommand->setValidator(new HexStringValidator(this, 4));
ui->lineEditCANCommand->setText("000003e9");

QValidator::State HexStringValidator::validate(QString &input, int &pos) const
{
    const int maxSize = 2 * static_cast<int>(m_maxLength);
    const QChar space = QLatin1Char(' ');
    QString data = input;
    data.remove(space);

    if (data.isEmpty())
        return Intermediate;

    // limit maximum size and forbid trailing spaces
    if ((data.size() > maxSize) || (data.size() == maxSize && input.endsWith(space)))
        return Invalid;

    // check if all input is valid
    const QRegularExpression re(QStringLiteral("^[[:xdigit:]]*$"));
    if (!re.match(data).hasMatch())
        return Invalid;

    // insert a space after every two hex nibbles
    const QRegularExpression insertSpace(QStringLiteral("(?:[[:xdigit:]]{2} )*[[:xdigit:]]{3}"));
    if (insertSpace.match(input).hasMatch()) {
        input.insert(input.size() - 1, space);
        pos = input.size();
    }

    return Acceptable;
}

The function is supposed to change this to "00 00 03 e9"

It however runs endless and creates

000003e 9

000003e 9

000003e 9

000003e 9

What is going wrong? Note, this code is from Qt and not mine.

Upvotes: 1

Views: 224

Answers (1)

eugesh
eugesh

Reputation: 111

In Qt6 problem should be gone.
There was related to this problem issue and two following commits:
Can example: Fix crash in payload hex editor,
CAN Example: Properly format payload hex string
It is possible to test since Qt 6.2.0.

First, I changed insertSpace regular expression to threeDigits:

QStringLiteral("[[:xdigit:]]{3}")

and insert space if 3 digits were added sequentially:

input.insert(match.capturedEnd() - 1, space);

That solves crash problem (infinite recursion).
Then I added auxilliary condition isEvenHex and formatter formatHexData with oneDigitAndSpace and threeDigits regular expressions that removes all extra spaces and add space after every two hex nibbles. I call them respectively on text change and sendButton click:

connect(m_ui->payloadEdit, &QLineEdit::textChanged, frameIdOrPayloadChanged);

connect(m_ui->sendButton, &QPushButton::clicked, [this]() {
...
m_ui->payloadEdit->setText(formatHexData(data));
...
}

Trying to solve your task I added:

QValidator::State HexStringValidator::validate(QString &input, int &pos) const
{
    ...        
    QString data = input;

    const QRegularExpression twoSpaces(QStringLiteral("([\\s]{2})"));
    if (twoSpaces.match(data).hasMatch())
        return Invalid;

    data.remove(space);
    ...

But there is one problem: if you are editing string in payload editor you may remove digits and two spaces can occure. The validator won't allow you remove characters between spaces. As a compromise I would introduce

threeSpaces(QStringLiteral("([\\s]{3})"));

validator instead of twoSpaces. With that one you may enter two spaces sequentially but still can edit string. I may ask QtSerialBus developers to include that change too.

Upvotes: -1

Related Questions