我正在Qt中寻找一个类似于MFC的IP地址控制的小部件。有谁知道这样的小部件,或者我如何创建一个小部件?
我不知道什么是MFC IP Widget,但看起来它是一个输入IP地址的Widget。 您需要使用带有 inputMask“000.000.000.000;_”的 QLineEdit
QLineEdit *ipEdit = new QLineEdit();
ipEdit->setInputMask("000.000.000.000;_");
ipEdit->show();
对jpo38的代码进行一点改进...
#include <QFrame>
#include <QLineEdit>
#include <QIntValidator>
#include "stdint.h"
#include <QHBoxLayout>
#include <QFont>
#include <QLabel>
#include <QKeyEvent>
class IPCtrl : public QFrame
{
Q_OBJECT
public:
IPCtrl(QWidget *parent = 0);
~IPCtrl();
virtual bool eventFilter( QObject *obj, QEvent *event );
public slots:
void slotTextChanged( QLineEdit* pEdit );
signals:
void signalTextChanged( QLineEdit* pEdit );
private:
enum
{
QTUTL_IP_SIZE = 4,// число октетов IP адресе
MAX_DIGITS = 3 // число символов в LineEdit
};
QLineEdit *(m_pLineEdit[QTUTL_IP_SIZE]);
void MoveNextLineEdit (int i);
void MovePrevLineEdit (int i);
};
IPCtrl::IPCtrl(QWidget *parent) : QFrame(parent)
{
setFrameShape( QFrame::StyledPanel );
setFrameShadow( QFrame::Sunken );
QHBoxLayout* pLayout = new QHBoxLayout( this );
setLayout( pLayout );
pLayout->setContentsMargins( 0, 0, 0, 0 );
pLayout->setSpacing( 0 );
for ( int i = 0; i != QTUTL_IP_SIZE; ++i )
{
if ( i != 0 )
{
QLabel* pDot = new QLabel( ".", this );
pDot->setStyleSheet( "background: white" );
pLayout->addWidget( pDot );
pLayout->setStretch( pLayout->count(), 0 );
}
m_pLineEdit[i] = new QLineEdit( this );
QLineEdit* pEdit = m_pLineEdit[i];
pEdit->installEventFilter( this );
pLayout->addWidget( pEdit );
pLayout->setStretch( pLayout->count(), 1 );
pEdit->setFrame( false );
pEdit->setAlignment( Qt::AlignCenter );
QFont font = pEdit->font();
font.setStyleHint( QFont::Monospace );
font.setFixedPitch( true );
pEdit->setFont( font );
QRegExp rx ( "^(0|[1-9]|[1-9][0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))$" );
QValidator *validator = new QRegExpValidator(rx, pEdit);
pEdit->setValidator( validator );
}
setMaximumWidth( 30 * QTUTL_IP_SIZE );
connect( this, SIGNAL(signalTextChanged(QLineEdit*)),
this, SLOT(slotTextChanged(QLineEdit*)),
Qt::QueuedConnection );
}
IPCtrl::~IPCtrl()
{
}
void IPCtrl::slotTextChanged( QLineEdit* pEdit )
{
for ( unsigned int i = 0; i != QTUTL_IP_SIZE; ++i )
{
if ( pEdit == m_pLineEdit[i] )
{
if ( ( pEdit->text().size() == MAX_DIGITS && pEdit->text().size() == pEdit->cursorPosition() ) || ( pEdit->text() == "0") )
{
// auto-move to next item
if ( i+1 != QTUTL_IP_SIZE )
{
m_pLineEdit[i+1]->setFocus();
m_pLineEdit[i+1]->selectAll();
}
}
}
}
}
bool IPCtrl::eventFilter(QObject *obj, QEvent *event)
{
bool bRes = QFrame::eventFilter(obj, event);
if ( event->type() == QEvent::KeyPress )
{
QKeyEvent* pEvent = dynamic_cast<QKeyEvent*>( event );
if ( pEvent )
{
for ( unsigned int i = 0; i != QTUTL_IP_SIZE; ++i )
{
QLineEdit* pEdit = m_pLineEdit[i];
if ( pEdit == obj )
{
switch ( pEvent->key() )
{
case Qt::Key_Left:
if ( pEdit->cursorPosition() == 0 )
{
// user wants to move to previous item
MovePrevLineEdit(i);
}
break;
case Qt::Key_Right:
if ( pEdit->text().isEmpty() || (pEdit->text().size() == pEdit->cursorPosition()) )
{
// user wants to move to next item
MoveNextLineEdit(i);
}
break;
case Qt::Key_0:
if ( pEdit->text().isEmpty() || pEdit->text() == "0" )
{
pEdit->setText("0");
// user wants to move to next item
MoveNextLineEdit(i);
}
emit signalTextChanged( pEdit );
break;
case Qt::Key_Backspace:
if ( pEdit->text().isEmpty() || pEdit->cursorPosition() == 0)
{
// user wants to move to previous item
MovePrevLineEdit(i);
}
break;
case Qt::Key_Comma:
case Qt::Key_Period:
MoveNextLineEdit(i);
break;
default:
emit signalTextChanged( pEdit );
break;
}
}
}
}
}
return bRes;
}
void IPCtrl::MoveNextLineEdit(int i)
{
if ( i+1 != QTUTL_IP_SIZE )
{
m_pLineEdit[i+1]->setFocus();
m_pLineEdit[i+1]->setCursorPosition( 0 );
m_pLineEdit[i+1]->selectAll();
}
}
void IPCtrl::MovePrevLineEdit(int i)
{
if ( i != 0 )
{
m_pLineEdit[i-1]->setFocus();
m_pLineEdit[i-1]->setCursorPosition( m_pLineEdit[i-1]->text().size() );
//m_pLineEdit[i-1]->selectAll();
}
}
我同意little_su的观点:带有输入掩码的QLineEdit的外观和行为不如标准Windows IP控件那么好。我制定了一个完整的基于 QWidget 的 IP 控件,嵌入 4 个 QLineEdit 和 3 个 QLabel(用于点)。它的外观和行为与 MFC/Windows IP 控件一样完美。
这是代码:
class IPCtrl : public QFrame
{
typedef QFrame baseClass;
Q_OBJECT
public:
IPCtrl(QWidget *parent);
~IPCtrl();
#define QTUTL_IP_SIZE 4
virtual bool eventFilter( QObject *obj, QEvent *event );
public slots:
void slotTextChanged( QLineEdit* pEdit );
signals:
void signalTextChanged( QLineEdit* pEdit );
private:
QLineEdit *(m_pLineEdit[QTUTL_IP_SIZE]);
static std::string getIPItemStr( unsigned char item );
};
class IPItemValidator : public QIntValidator
{
public:
IPItemValidator( QObject* parent ) : QIntValidator( parent )
{
setRange( 0, UCHAR_MAX );
}
~IPItemValidator() {}
virtual void fixup( QString & input ) const
{
if ( input.isEmpty() )
input = "0";
}
};
IPCtrl::IPCtrl(QWidget *parent) : baseClass(parent)
{
setFrameShape( QFrame::StyledPanel );
setFrameShadow( QFrame::Sunken );
QHBoxLayout* pLayout = new QHBoxLayout( this );
setLayout( pLayout );
pLayout->setContentsMargins( 0, 0, 0, 0 );
pLayout->setSpacing( 0 );
for ( int i = 0; i != QTUTL_IP_SIZE; ++i )
{
if ( i != 0 )
{
QLabel* pDot = new QLabel( ".", this );
pDot->setStyleSheet( "background: white" );
pLayout->addWidget( pDot );
pLayout->setStretch( pLayout->count(), 0 );
}
m_pLineEdit[i] = new QLineEdit( this );
QLineEdit* pEdit = m_pLineEdit[i];
pEdit->installEventFilter( this );
pLayout->addWidget( pEdit );
pLayout->setStretch( pLayout->count(), 1 );
pEdit->setFrame( false );
pEdit->setAlignment( Qt::AlignCenter );
QFont font = pEdit->font();
font.setStyleHint( QFont::Monospace );
font.setFixedPitch( true );
pEdit->setFont( font );
pEdit->setValidator( new IPItemValidator( pEdit ) );
}
setMaximumWidth( 30 * QTUTL_IP_SIZE );
connect( this, SIGNAL(signalTextChanged(QLineEdit*)),
this, SLOT(slotTextChanged(QLineEdit*)),
Qt::QueuedConnection );
}
IPCtrl::~IPCtrl()
{
}
std::string IPCtrl::getIPItemStr( unsigned char item )
{
std::strstream str;
str << (int) item;
str << std::ends;
return str.str();
}
void IPCtrl::slotTextChanged( QLineEdit* pEdit )
{
for ( unsigned int i = 0; i != QTUTL_IP_SIZE; ++i )
{
if ( pEdit == m_pLineEdit[i] )
{
if ( pEdit->text().size() == getIPItemStr( UCHAR_MAX ).size() &&
pEdit->text().size() == pEdit->cursorPosition() )
{
// auto-move to next item
if ( i+1 != QTUTL_IP_SIZE )
{
m_pLineEdit[i+1]->setFocus();
m_pLineEdit[i+1]->selectAll();
}
}
}
}
}
bool IPCtrl::eventFilter(QObject *obj, QEvent *event)
{
bool bRes = baseClass::eventFilter(obj, event);
if ( event->type() == QEvent::KeyPress )
{
QKeyEvent* pEvent = dynamic_cast<QKeyEvent*>( event );
if ( pEvent )
{
for ( unsigned int i = 0; i != QTUTL_IP_SIZE; ++i )
{
QLineEdit* pEdit = m_pLineEdit[i];
if ( pEdit == obj )
{
switch ( pEvent->key() )
{
case Qt::Key_Left:
{
if ( pEdit->cursorPosition() == 0 )
{
// user wants to move to previous item
if ( i != 0 )
{
m_pLineEdit[i-1]->setFocus();
m_pLineEdit[i-1]->setCursorPosition( m_pLineEdit[i-1]->text().size() );
}
}
break;
}
case Qt::Key_Right:
{
if ( pEdit->text().isEmpty() ||
(pEdit->text().size() == pEdit->cursorPosition()) )
{
// user wants to move to next item
if ( i+1 != QTUTL_IP_SIZE )
{
m_pLineEdit[i+1]->setFocus();
m_pLineEdit[i+1]->setCursorPosition( 0 );
}
}
break;
}
default:
{
emit signalTextChanged( pEdit );
}
}
break;
}
}
}
}
return bRes;
}
Tugo 代码的一点改进...我无法评论所以...
ipctrl.h:
#pragma once
#include <QFrame>
#include <array>
/// Thanx to https://stackoverflow.com/a/11358560/8524139
class QLineEdit;
class IPCtrl : public QFrame
{
Q_OBJECT
enum
{
QTUTL_IP_SIZE = 4, // число октетов IP адресе
MAX_DIGITS = 3 // число символов в LineEdit
};
public:
IPCtrl(QWidget *parent = 0);
~IPCtrl();
virtual bool eventFilter(QObject *obj, QEvent *event);
std::array<quint8, QTUTL_IP_SIZE> getIP() const;
void setIP(std::array<quint8, QTUTL_IP_SIZE> ipAddr);
signals:
void signalTextChanged(QLineEdit *pEdit);
private:
std::array<QLineEdit *, QTUTL_IP_SIZE> m_pLineEdit;
void slotTextChanged(QLineEdit *pEdit);
void moveNextLineEdit(int i);
void movePrevLineEdit(int i);
};
ipctrl.cpp:
#include "ipctrl.h"
#include <QHBoxLayout>
#include <QIntValidator>
#include <QKeyEvent>
#include <QLabel>
#include <QLineEdit>
IPCtrl::IPCtrl(QWidget *parent) : QFrame(parent)
{
setFrameShape(QFrame::StyledPanel);
setFrameShadow(QFrame::Sunken);
QHBoxLayout *pLayout = new QHBoxLayout(this);
setLayout(pLayout);
pLayout->setContentsMargins(0, 0, 1, 0);
pLayout->setSpacing(0);
for (int i = 0; i != QTUTL_IP_SIZE; ++i)
{
if (i != 0)
{
QLabel *pDot = new QLabel(".", this);
pLayout->addWidget(pDot);
pLayout->setStretch(pLayout->count(), 0);
}
m_pLineEdit.at(i) = new QLineEdit(this);
QLineEdit *pEdit = m_pLineEdit.at(i);
pEdit->installEventFilter(this);
pLayout->addWidget(pEdit);
pLayout->setStretch(pLayout->count(), 1);
pEdit->setFrame(false);
pEdit->setAlignment(Qt::AlignCenter);
QFont font = pEdit->font();
font.setStyleHint(QFont::Monospace);
font.setFixedPitch(true);
pEdit->setFont(font);
QRegExp rx("^(0|[1-9]|[1-9][0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))$");
QValidator *validator = new QRegExpValidator(rx, pEdit);
pEdit->setValidator(validator);
}
setMaximumWidth(30 * QTUTL_IP_SIZE);
connect(this, &IPCtrl::signalTextChanged, this, &IPCtrl::slotTextChanged, Qt::QueuedConnection);
}
IPCtrl::~IPCtrl()
{
}
void IPCtrl::slotTextChanged(QLineEdit *pEdit)
{
for (unsigned int i = 0; i != QTUTL_IP_SIZE; ++i)
{
if (pEdit == m_pLineEdit.at(i))
{
if ((pEdit->text().size() == MAX_DIGITS && pEdit->text().size() == pEdit->cursorPosition())
|| (pEdit->text() == "0"))
{
// auto-move to next item
if (i + 1 != QTUTL_IP_SIZE)
{
m_pLineEdit.at(i + 1)->setFocus();
m_pLineEdit.at(i + 1)->selectAll();
}
}
}
}
}
bool IPCtrl::eventFilter(QObject *obj, QEvent *event)
{
bool bRes = QFrame::eventFilter(obj, event);
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *pEvent = dynamic_cast<QKeyEvent *>(event);
if (pEvent)
{
for (unsigned int i = 0; i != QTUTL_IP_SIZE; ++i)
{
QLineEdit *pEdit = m_pLineEdit[i];
if (pEdit == obj)
{
switch (pEvent->key())
{
case Qt::Key_Left:
if (pEdit->cursorPosition() == 0)
{
// user wants to move to previous item
movePrevLineEdit(i);
}
break;
case Qt::Key_Right:
if (pEdit->text().isEmpty() || (pEdit->text().size() == pEdit->cursorPosition()))
{
// user wants to move to next item
moveNextLineEdit(i);
}
break;
case Qt::Key_0:
if (pEdit->text().isEmpty() || pEdit->text() == "0")
{
pEdit->setText("0");
// user wants to move to next item
moveNextLineEdit(i);
}
emit signalTextChanged(pEdit);
break;
case Qt::Key_Backspace:
if (pEdit->text().isEmpty() || pEdit->cursorPosition() == 0)
{
// user wants to move to previous item
movePrevLineEdit(i);
}
break;
case Qt::Key_Comma:
case Qt::Key_Period:
moveNextLineEdit(i);
break;
default:
emit signalTextChanged(pEdit);
break;
}
}
}
}
}
return bRes;
}
std::array<quint8, IPCtrl::QTUTL_IP_SIZE> IPCtrl::getIP() const
{
std::array<quint8, QTUTL_IP_SIZE> ipAddr;
std::transform(m_pLineEdit.cbegin(), m_pLineEdit.cend(), ipAddr.begin(),
[](const QLineEdit *lineEdit) -> quint8 { return lineEdit->text().toUInt(); });
return ipAddr;
}
void IPCtrl::setIP(std::array<quint8, IPCtrl::QTUTL_IP_SIZE> ipAddr)
{
for (auto i = 0; i != QTUTL_IP_SIZE; ++i)
{
m_pLineEdit.at(i)->setText(QString::number(ipAddr.at(i)));
}
}
void IPCtrl::moveNextLineEdit(int i)
{
if (i + 1 != QTUTL_IP_SIZE)
{
m_pLineEdit.at(i + 1)->setFocus();
m_pLineEdit.at(i + 1)->setCursorPosition(0);
m_pLineEdit.at(i + 1)->selectAll();
}
}
void IPCtrl::movePrevLineEdit(int i)
{
if (i != 0)
{
m_pLineEdit.at(i - 1)->setFocus();
m_pLineEdit.at(i - 1)->setCursorPosition(m_pLineEdit[i - 1]->text().size());
// m_pLineEdit[i-1]->selectAll();
}
}
所有基于
QFrame
的解决方案都很棒,但它们迫使您声明自己的类。 QLineEdit
使用输入掩码不会阻止输入“999.999.999.999”,因此这是不可接受的。
这是一个使用 QLineEdit 验证器的易于实现且运行良好的实现:
QString ipRange = "(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])"
QRegularExpression ipRegex("^" + ipRange + "\\." + ipRange + "\\." + ipRange + "\\." + ipRange + "$")
QRegularExpressionValidator* ipValidator = new QRegularExpressionValidator(ipRegex, NULL);
QLineEdit *ipEdit = new QLineEdit();
ipEdit->setValidator(ipValidator);
注意“192.168.1”将被接受,您可以最后检查是否用“.”分割结果。最终得到 4 个非空字符串。那么您的 IP 地址应该是有效的。