用于十六进制输入的 QValidator

发布于 2024-08-30 20:07:45 字数 1989 浏览 7 评论 0原文

我有一个 Qt 小部件,它应该只接受十六进制字符串作为输入。将输入字符限制为 [0-9A-Fa-f] 非常简单,但我希望它在“字节”之间显示分隔符,例如,如果分隔符是空格,并且用户输入 0011223344 我希望行编辑显示 00 11 22 33 44 现在,如果用户按退格键 3 次,那么我希望它显示00 11 22 3

几乎得到了我想要的,到目前为止,只有一个微妙的错误涉及使用删除键删除分隔符。有谁有更好的方法来实现这个验证器?到目前为止,这是我的代码:

class HexStringValidator : public QValidator {
public:
    HexStringValidator(QObject * parent) : QValidator(parent) {}

public:
    virtual void fixup(QString &input) const {
        QString temp;
        int index = 0;

            // every 2 digits insert a space if they didn't explicitly type one 
        Q_FOREACH(QChar ch, input) {
            if(std::isxdigit(ch.toAscii())) {

                if(index != 0 && (index & 1) == 0) {
                    temp += ' ';
                }

                temp += ch.toUpper();
                ++index;
            }
        }

        input = temp;
    }

    virtual State validate(QString &input, int &pos) const {
        if(!input.isEmpty()) {
            // TODO: can we detect if the char which was JUST deleted
            // (if any was deleted) was a space? and special case this?
            // as to not have the bug in this case?

            const int char_pos  = pos - input.left(pos).count(' ');
            int chars           = 0;
            fixup(input);

            pos = 0;

            while(chars != char_pos) {
                if(input[pos] != ' ') {
                    ++chars;
                }
                ++pos;
            }

            // favor the right side of a space
            if(input[pos] == ' ') {
                ++pos;
            }
        }
        return QValidator::Acceptable;
    }
};

目前此代码功能足够,但我希望它能够 100% 按预期工作。显然,理想的是将十六进制字符串的显示与 QLineEdit 内部缓冲区中存储的实际字符分开,但我不知道从哪里开始,我想这是一个非-微不足道的事业。

本质上,我想要一个符合此正则表达式的验证器: "[0-9A-Fa-f]( [0-9A-Fa-f])*" 但我不'不希望用户必须键入空格作为分隔符。同样,当编辑他们输入的内容时,应该隐式管理空格。

I have a Qt widget which should only accept a hex string as input. It is very simple to restrict the input characters to [0-9A-Fa-f], but I would like to have it display with a delimiter between "bytes" so for example if the delimiter is a space, and the user types 0011223344 I would like the line edit to display 00 11 22 33 44 Now if the user presses the backspace key 3 times, then I want it to display 00 11 22 3.

I almost have what i want, so far there is only one subtle bug involving using the delete key to remove a delimiter. Does anyone have a better way to implement this validator? Here's my code so far:

class HexStringValidator : public QValidator {
public:
    HexStringValidator(QObject * parent) : QValidator(parent) {}

public:
    virtual void fixup(QString &input) const {
        QString temp;
        int index = 0;

            // every 2 digits insert a space if they didn't explicitly type one 
        Q_FOREACH(QChar ch, input) {
            if(std::isxdigit(ch.toAscii())) {

                if(index != 0 && (index & 1) == 0) {
                    temp += ' ';
                }

                temp += ch.toUpper();
                ++index;
            }
        }

        input = temp;
    }

    virtual State validate(QString &input, int &pos) const {
        if(!input.isEmpty()) {
            // TODO: can we detect if the char which was JUST deleted
            // (if any was deleted) was a space? and special case this?
            // as to not have the bug in this case?

            const int char_pos  = pos - input.left(pos).count(' ');
            int chars           = 0;
            fixup(input);

            pos = 0;

            while(chars != char_pos) {
                if(input[pos] != ' ') {
                    ++chars;
                }
                ++pos;
            }

            // favor the right side of a space
            if(input[pos] == ' ') {
                ++pos;
            }
        }
        return QValidator::Acceptable;
    }
};

For now this code is functional enough, but I'd love to have it work 100% as expected. Obviously the ideal would be the just separate the display of the hex string from the actual characters stored in the QLineEdit's internal buffer but I have no idea where to start with that and I imagine is a non-trivial undertaking.

In essence, I would like to have a Validator which conforms to this regex: "[0-9A-Fa-f]( [0-9A-Fa-f])*" but I don't want the user to ever have to type a space as delimiter. Likewise, when editing what they types, the spaces should be managed implicitly.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

习ぎ惯性依靠 2024-09-06 20:07:45

Evan,试试这个:

QLineEdit * edt = new QLineEdit( this );  
edt->setInputMask( "Hh hh hh hh" );

inputMask 负责间距,“h”代表可选的十六进制字符(“H”代表非可选)。唯一的缺点:您必须提前知道最大输入长度。我上面的例子只允许四个字节。

此致,
罗宾

Evan, try this:

QLineEdit * edt = new QLineEdit( this );  
edt->setInputMask( "Hh hh hh hh" );

The inputMask takes care of the spacing, and the "h" stands for a optional hex character (the "H" for a non-optional). Only drawback: You have to know the maximum input length in advance. My example above allows only for four bytes.

Best regards,
Robin

痴梦一场 2024-09-06 20:07:45

我将提出三种方法:

QLineEdit 光标左侧的字符是空格时,您可以重新实现 QLineEdit::keyPressEvent() 以不同方式处理反斜杠。使用这种方法,您还可以在键入新字符时自动添加空格。

另一种方法是创建一个新槽,连接到 QLineEdit::textChanged() 信号。当文本更改时会发出此信号。在此槽中,您可以根据需要处理空间的创建和删除。

最后,您可以创建一个派生自 QLineEdit 的新类,该类重新实现 QLineEdit::paintEvent() 方法。通过这种方法,您可以显示未存储在 QLineEdit 缓冲区中的十六进制字之间的空格。

I will propose three approaches :

You can reimplement the QLineEdit::keyPressEvent() to handle backslash differently when the character just left to the QLineEdit's cursor is a space. Using this approach, you can also automatically add spaces when a new character is typed.

Another approach is to create a new slot, connected to the QLineEdit::textChanged() signal. This signal is emitted when the text is changed. In this slot, you can handle the creation and deletion of spaces accordingly to your needs.

Finally, you can create a new class, derived from QLineEdit that reimplements the QLineEdit::paintEvent() method. With this approach, you can display space between your hex words that are not stored in the QLineEdit buffer.

相思故 2024-09-06 20:07:45

Robin 的解决方案很好并且有效。但我认为你可以做到最好!
使用它作为输入掩码:

ui->lineEdit->setInputMask("HH-HH-HH-HH");

在 ui 中,R-单击 lineEdit ->转到老虎机... ->文本已更改。
在槽函数中编写以下代码:

int c = ui->lineEdit->cursorPosition();
ui->lineEdit->setText(arg1.toUpper());
ui->lineEdit->setCursorPosition(c); // to not jump cursor's position

现在您有一个带有十六进制输入的 lineEdit,大写,带有破折号分隔符。

祝你代码愉快:)

Robin's solution is good and works. But I think you can do best!
Use this for input mask:

ui->lineEdit->setInputMask("HH-HH-HH-HH");

and in the ui, R-click on lineEdit -> Go to Slots... -> textChanged.
In the slot function write this code:

int c = ui->lineEdit->cursorPosition();
ui->lineEdit->setText(arg1.toUpper());
ui->lineEdit->setCursorPosition(c); // to not jump cursor's position

Now you have a lineEdit with Hex input, in uppercase, with dash-separators.

have a good code time :)

日暮斜阳 2024-09-06 20:07:45

我有同样的要求,用空格分隔 2 个十六进制半字节,谷歌搜索让我来到这里。正如Lohrun所建议的,我最初尝试了基于paintEvent的方法,并且它按键盘输入的预期工作。但是,我无法正确处理鼠标点击。接下来,我尝试了他的第一个建议,即重新实现 keyPressEvent 以及 mousePressEvent,我可以使用键盘和鼠标来实现要求。
我的实现 QHexTextEdit.cpp

#include "QHexTextEdit.h"

QHexTextEdit::QHexTextEdit(QWidget *parent) : QTextEdit(parent)
{
    setLineWrapMode(QTextEdit::NoWrap); // Disable word wrap
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Disable vertical scrollbar
    mText = "";
    mCurserPositionPre = 0;
}

void QHexTextEdit::keyPressEvent(QKeyEvent *event)
{
    int pressedKey = event->key();
    QTextCursor cursor = textCursor();
    int cursorPosition = cursor.position();
    int newCursorPosition = cursorPosition;
    int index = cursorPosition - int(cursorPosition/3);
    if(((pressedKey>=Qt::Key_0) && (pressedKey<=Qt::Key_9)) || ((pressedKey>=Qt::Key_A) && (pressedKey<=Qt::Key_F)) )
    {
        mText.insert(index, event->text());
        newCursorPosition = cursorPosition + (cursorPosition % 3 + 1);
    }
    else if (pressedKey == Qt::Key_Backspace)
    {
        if(index!=0)
        {
            mText.remove(index-1,1);
            newCursorPosition = cursorPosition -2 + (cursorPosition)%3;
        }
    }
    else if (pressedKey == Qt::Key_Delete)
    {
        if(index!=mText.length())
        {
            mText.remove(index,1);
            newCursorPosition = cursorPosition;
        }
    }
    else if (pressedKey == Qt::Key_Left)
    {
        if(index!=0)
        {
            newCursorPosition = cursorPosition -2 + (cursorPosition)%3;
        }
    }
    else if (pressedKey == Qt::Key_Right)
    {
        if(index!=mText.length())
        {
            newCursorPosition = cursorPosition + (cursorPosition+1)%3;
        }
    }
    // Allow only single-line editing
    else if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
    {
        event->ignore(); // Ignore Enter key press to prevent new lines
    }


    QString temp;
    for(int i=0; i<mText.length(); i++)
    {
        temp.append(mText.at(i).toUpper());
        if(i%2==1)
            temp.append(" ");
    }

    setText(temp);
    cursor.setPosition(newCursorPosition);
    setTextCursor(cursor);
    mCurserPositionPre = newCursorPosition;
}

void QHexTextEdit::mousePressEvent(QMouseEvent *event)
{
    QTextCursor cursor = cursorForPosition(event->pos());
    int cursorPosition = cursor.position();
    int newCursorPosition = cursorPosition;    
    if(cursorPosition%3==2)
    {
        newCursorPosition = cursorPosition + 1;
        cursor.setPosition(newCursorPosition);
        mCurserPositionPre = newCursorPosition;        
    }
    QTextEdit::mousePressEvent(event);
    setTextCursor(cursor);
}

QHexTextEdit::~QHexTextEdit()
{

}

QHexTextEdit.h

#ifndef Q_HEXTEXTEDIT_H
#define Q_HEXTEXTEDIT_H

#include <QTextEdit>
#include <QPainter>
#include <QStyleOptionFrame>
#include <QTextCursor>
#include <QTextLayout>

class QHexTextEdit : public QTextEdit
{
    Q_OBJECT
public:
    QHexTextEdit(QWidget *parent = nullptr);
    ~QHexTextEdit() override;
    
protected:
    void keyPressEvent(QKeyEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;

private:
    QString mText;
    int mCurserPositionPre;
};

#endif

I had the same requirement of separating 2 hex nibbles with a space and a Google search landed me here. As suggested by Lohrun, I attempted paintEvent based approach initially and it worked as expected for keyboard inputs. However, I could not handle the mouse clicks properly. Next, I tried his first suggestion of re-implementing the keyPressEvent along with mousePressEvent and I could achieve the requirement with both keyboard and mouse.
My implementation QHexTextEdit.cpp

#include "QHexTextEdit.h"

QHexTextEdit::QHexTextEdit(QWidget *parent) : QTextEdit(parent)
{
    setLineWrapMode(QTextEdit::NoWrap); // Disable word wrap
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Disable vertical scrollbar
    mText = "";
    mCurserPositionPre = 0;
}

void QHexTextEdit::keyPressEvent(QKeyEvent *event)
{
    int pressedKey = event->key();
    QTextCursor cursor = textCursor();
    int cursorPosition = cursor.position();
    int newCursorPosition = cursorPosition;
    int index = cursorPosition - int(cursorPosition/3);
    if(((pressedKey>=Qt::Key_0) && (pressedKey<=Qt::Key_9)) || ((pressedKey>=Qt::Key_A) && (pressedKey<=Qt::Key_F)) )
    {
        mText.insert(index, event->text());
        newCursorPosition = cursorPosition + (cursorPosition % 3 + 1);
    }
    else if (pressedKey == Qt::Key_Backspace)
    {
        if(index!=0)
        {
            mText.remove(index-1,1);
            newCursorPosition = cursorPosition -2 + (cursorPosition)%3;
        }
    }
    else if (pressedKey == Qt::Key_Delete)
    {
        if(index!=mText.length())
        {
            mText.remove(index,1);
            newCursorPosition = cursorPosition;
        }
    }
    else if (pressedKey == Qt::Key_Left)
    {
        if(index!=0)
        {
            newCursorPosition = cursorPosition -2 + (cursorPosition)%3;
        }
    }
    else if (pressedKey == Qt::Key_Right)
    {
        if(index!=mText.length())
        {
            newCursorPosition = cursorPosition + (cursorPosition+1)%3;
        }
    }
    // Allow only single-line editing
    else if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
    {
        event->ignore(); // Ignore Enter key press to prevent new lines
    }


    QString temp;
    for(int i=0; i<mText.length(); i++)
    {
        temp.append(mText.at(i).toUpper());
        if(i%2==1)
            temp.append(" ");
    }

    setText(temp);
    cursor.setPosition(newCursorPosition);
    setTextCursor(cursor);
    mCurserPositionPre = newCursorPosition;
}

void QHexTextEdit::mousePressEvent(QMouseEvent *event)
{
    QTextCursor cursor = cursorForPosition(event->pos());
    int cursorPosition = cursor.position();
    int newCursorPosition = cursorPosition;    
    if(cursorPosition%3==2)
    {
        newCursorPosition = cursorPosition + 1;
        cursor.setPosition(newCursorPosition);
        mCurserPositionPre = newCursorPosition;        
    }
    QTextEdit::mousePressEvent(event);
    setTextCursor(cursor);
}

QHexTextEdit::~QHexTextEdit()
{

}

QHexTextEdit.h

#ifndef Q_HEXTEXTEDIT_H
#define Q_HEXTEXTEDIT_H

#include <QTextEdit>
#include <QPainter>
#include <QStyleOptionFrame>
#include <QTextCursor>
#include <QTextLayout>

class QHexTextEdit : public QTextEdit
{
    Q_OBJECT
public:
    QHexTextEdit(QWidget *parent = nullptr);
    ~QHexTextEdit() override;
    
protected:
    void keyPressEvent(QKeyEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;

private:
    QString mText;
    int mCurserPositionPre;
};

#endif
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文