前言
最近小工具界面上用到了ip编辑框,目前是直接放了个编辑框QLineEdit,因为属于自用,就不用写诸多限制。但是时间上有多余,就用官方的(即在QLineEdit基础上直接设置的)、网上的(QLineEdit拼接的)、自定义的(网上的加自我改良的)都进行个尝试。
效果
实现
官方(单个QLineEdit)
官方的就是在单个QLineEdit基础上直接设置正则表达式和mask,代码如下,但是效果不是很好:
- 光标是粗黑的,属于单个字符选中修改的模式;
- 内容没有分散在整个框内,对齐方式无论改为居中或者靠左都不好看;
- 还有就是如果设置占位符为空格,就根本看不出当前的是第几位,我经常选中的是第二位,然后输入192,直接就变成19.2了。
QRegExp rx("((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)");
QRegExpValidator *pReg = new QRegExpValidator(rx, this);
ui->lineEdit->setValidator(pReg);
ui->lineEdit->setInputMask("000.000.000.000; ");
正则表达式中各个符号说明:
- \d:匹配一个数字,等同于[0-9]
- \.:匹配一个小数点
- ?:表示重复前面内容的0次或1次
- 2[0-4]\\d:涵盖了200~249之间的值
- 25[0-5]:涵盖了250~255之间的值
- [01]?\\d\\d?:涵盖了0~199之间的值
正则表达式在线测试工具:正则表达式在线测试 | 菜鸟工具
网上(多个QLineEdit拼接)
我在github上找了一个,地址是:https://github.com/KingBright965/QtIPEdit/blob/master/IPEdit.cpp
- 整体和内容控制:他是由多个QLineEdit加QLabel拼接而成的,每个QLineEdit设置了正则表达式,同时为了控制每个部分长度最大为3,对编辑框的内容编辑进行了监听控制,当当前长度为3,转到下个编辑框;
void IPEdit::initForEdit(QLineEdit* edit)
{
edit->setFrame(false);
edit->setAlignment(Qt::AlignCenter);
edit->installEventFilter(this);
QRegExpValidator* validator = new QRegExpValidator(QRegExp("^(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)$"), this);
edit->setValidator(validator);
connect(edit, SIGNAL(textChanged(const QString&)), this, SLOT(editTextChanged(const QString&)));
}
void IPEdit::editTextChanged(const QString& text)
{
QLineEdit* curEdit = qobject_cast<QLineEdit*>(sender());
if (text.size() == 3)
{
QLineEdit* next = nextEdit(curEdit);
if (next)
{
next->setFocus();
next->selectAll();
}
}
}
- 事件响应:并且加了事件过滤,监听按键点(Qt::Key_Period)和粘贴事件(QKeySequence::Paste);
bool IPEdit::eventFilter(QObject* object, QEvent* event)
{
if (isEdit(object))
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Period)
{
QLineEdit* next = nextEdit(qobject_cast<QLineEdit*>(object));
if (next)
{
next->setFocus();
next->selectAll();
}
}
else if (keyEvent->matches(QKeySequence::Paste))
{
QString clip = QApplication::clipboard()->text(QClipboard::Clipboard);
if (clip.split(".").size() == 4)
{
setText(clip);
return true;
}
}
}
}
return QWidget::eventFilter(object, event);
}
- 输入、输出:
QString IPEdit::text()
{
QString sec1 = m_edit1->text().isEmpty() ? "0" : m_edit1->text();
QString sec2 = m_edit2->text().isEmpty() ? "0" : m_edit2->text();
QString sec3 = m_edit3->text().isEmpty() ? "0" : m_edit3->text();
QString sec4 = m_edit4->text().isEmpty() ? "0" : m_edit4->text();
return QString("%1.%2.%3.%4").arg(sec1).arg(sec2).arg(sec3).arg(sec4);
}
void IPEdit::setText(const QString& text)
{
QRegExp reg("^((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)$");
if (!reg.exactMatch(text))
return;
QStringList ips = text.split(".");
m_edit1->setText(ips.at(0));
m_edit2->setText(ips.at(1));
m_edit3->setText(ips.at(2));
m_edit4->setText(ips.at(3));
}
- 风格:为了使风格跟编辑框一致,重写绘制事件:
void IPEdit::paintEvent(QPaintEvent* event)
{
QPainter painter(this);
QStyleOptionFrame option;
option.initFrom(this);
option.lineWidth = 1;
style()->drawPrimitive(QStyle::PE_PanelLineEdit, &option, &painter, this);
QWidget::paintEvent(event);
}
自定义(网上改良过的)
我参考了Windows对IP的设置的编辑框,跟从网上的例子效果相比,稍微有点不同:
- tab按键的焦点切换不同,网上例子每个部分(QLineEdit)的焦点都响应切换,而Windows下并没有;
- 上下左右按键光标位置的跨越(相邻QLineEdit),网上的例子没有,而Windows下的有;
- 退格(Qt::Key_Backspace)删除的跨越。
我对这两方面进行了改良,代码如下:
- tab按键的焦点切换:编辑框默认响应Tab按键和点击的焦点切换(Qt::StrongFocus),所以我将除第一个编辑框,都设置为点击焦点切换,不再响应tab按键焦点切换。
ui->lineEdit_2->setFocusPolicy(Qt::ClickFocus);
ui->lineEdit_3->setFocusPolicy(Qt::ClickFocus);
ui->lineEdit_4->setFocusPolicy(Qt::ClickFocus);
上下左右按键光标位置的移动:
bool MyIpLinEdit::eventFilter(QObject *object, QEvent *event)
{
if(object==ui->lineEdit||object==ui->lineEdit_2||object==ui->lineEdit_3||
object==ui->lineEdit_4)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if(keyEvent->key()==Qt::Key_Left||keyEvent->key()==Qt::Key_Up){
cursorForwardPosition(static_cast<QLineEdit*>(object));
}else if(keyEvent->key()==Qt::Key_Right||keyEvent->key()==Qt::Key_Down){
cursorBackwardPosition(static_cast<QLineEdit*>(object));
}
...
}
}
return QWidget::eventFilter(object, event);
}
void MyIpLinEdit::cursorBackwardPosition(QLineEdit *lineEdit)
{
int pos=lineEdit->cursorPosition();
int len=lineEdit->text().length();
if(pos==len){
QLineEdit* pDestLineEdit=nullptr;
if(lineEdit==ui->lineEdit){
pDestLineEdit=ui->lineEdit_2;
}else if(lineEdit==ui->lineEdit_2){
pDestLineEdit=ui->lineEdit_3;
}else if(lineEdit==ui->lineEdit_3){
pDestLineEdit=ui->lineEdit_4;
}else if(lineEdit==ui->lineEdit_4){
pDestLineEdit=ui->lineEdit;
}
pDestLineEdit->setFocus();
pDestLineEdit->setCursorPosition(0);
}
}
void MyIpLinEdit::cursorForwardPosition(QLineEdit *lineEdit)
{
int pos=lineEdit->cursorPosition();
if(pos==0){
QLineEdit* pDestLineEdit=nullptr;
if(lineEdit==ui->lineEdit){
pDestLineEdit=ui->lineEdit_4;
}else if(lineEdit==ui->lineEdit_2){
pDestLineEdit=ui->lineEdit;
}else if(lineEdit==ui->lineEdit_3){
pDestLineEdit=ui->lineEdit_2;
}else if(lineEdit==ui->lineEdit_4){
pDestLineEdit=ui->lineEdit_3;
}
pDestLineEdit->setFocus();
int len=pDestLineEdit->text().length();
pDestLineEdit->setCursorPosition(len);
}
}
退格键:
bool MyIpLinEdit::eventFilter(QObject *object, QEvent *event)
{
....
else if(keyEvent->key()==Qt::Key_Backspace){
moveOfDeleteOnecharacter(qobject_cast<QLineEdit*>(object));
}
....
}
void MyIpLinEdit::moveOfDeleteOnecharacter(QLineEdit *lineEdit)
{
int len=lineEdit->text().length();
if(len==0){
QLineEdit* previous=previousEdit(lineEdit);
if(previous){
previous->setFocus();
int len=previous->text().length();
previous->setCursorPosition(len);
}
}
}
QLineEdit *MyIpLinEdit::previousEdit(QLineEdit *curEdit)
{
if (curEdit == ui->lineEdit_2)
return ui->lineEdit;
else if (curEdit == ui->lineEdit_3)
return ui->lineEdit_2;
else if (curEdit == ui->lineEdit_4)
return ui->lineEdit_3;
else
return NULL;
}