目前支持获取焦点后指定默认选中地址段,键盘左右键和.键按下后会移动编辑的段并选中,与正常正则表达式校验不同的地方是,允许每一个段暂时为空,并且移动编辑段时,如果上一个编辑段为空,会进行恢复,保证ipv4地址格式的准确性。同时添加了大量的信号和槽来进行使用。
由于构造函数支持QHostAddress ,所以该类拥有一个方法支持直接获取文本框内的QHostAddress.
效果图如下。
关于版本问题,现有C++,Pyside6,不喜欢PyQt,Pyside6的函数定义也垃圾的很。
头文件 LLineEdit.h
class LHostAddressValidator : public QValidator
{
Q_OBJECT
public:
explicit LHostAddressValidator(QObject* parent = nullptr);
~LHostAddressValidator() override;
State validate(QString& input, int& pos) const override;
};
class LWIDGET_EXPORT LHostAddressEdit : public QLineEdit
{
Q_OBJECT
public:
explicit LHostAddressEdit(const QHostAddress& hostAddress, QWidget* parent = nullptr);
explicit LHostAddressEdit(const QString& text, QWidget* parent = nullptr);
explicit LHostAddressEdit(QWidget* parent = nullptr);
[[nodiscard]] QHostAddress hostAddress() const;
[[nodiscard]] int currentBlock() const;
[[nodiscard]] int defaultBlock() const;
signals:
void currentBlockChanged(int block);
void hostAddressChanged(const QHostAddress& hostAddress);
void hostAddressEditFinished(const QHostAddress& hostAddress);
public slots:
void setText(const QString& text);
void hostAddressEditFinish();
void nextBlock();
void setCurrentBlock(int block);
void setDefaultBlock(int block = 0);
void setHostAddress(const QHostAddress& hostAddress);
protected:
void focusOutEvent(QFocusEvent* event) override;
void focusInEvent(QFocusEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
void handleCursorPositionChanged(int oldPos, int newPos);
private:
QHostAddress _hostAddress;
int _currentblock{ 0 };
int _defaultblock{ 0 };
};
源文件 LLineEdit.cpp
LHostAddressValidator::LHostAddressValidator(QObject* parent)
:QValidator(parent)
{
}
LHostAddressValidator::~LHostAddressValidator() = default;
QValidator::State LHostAddressValidator::validate(QString& input, int& pos) const
{
QRegularExpression regularExpression("^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])?\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])?$");
QRegularExpressionMatch match = regularExpression.match(input);
if (match.hasMatch())
{
return QValidator::State::Acceptable;
}
return QValidator::State::Invalid;
}
LHostAddressEdit::LHostAddressEdit(const QHostAddress& hostAddress, QWidget* parent)
:QLineEdit(parent)
{
this->setValidator(new LHostAddressValidator(this));
connect(this, &LHostAddressEdit::cursorPositionChanged, this, &LHostAddressEdit::handleCursorPositionChanged);
connect(this, &LHostAddressEdit::selectionChanged, [=]()
{
if (this->selectedText() == ".")
this->setSelection(this->cursorPosition(), 0);
});
this->setHostAddress(hostAddress);
//connect(this, &QLineEdit::editingFinished, this, &LHostAddressEdit::hostAddressEditFinish);
}
LHostAddressEdit::LHostAddressEdit(const QString& text, QWidget* parent)
:LHostAddressEdit(QHostAddress(text), parent)
{
}
LHostAddressEdit::LHostAddressEdit(QWidget* parent)
:LHostAddressEdit(QHostAddress(), parent)
{
}
void LHostAddressEdit::setHostAddress(const QHostAddress& hostAddress)
{
if (hostAddress.isNull() || hostAddress.toString() == _hostAddress.toString())
{
if (this->text() != _hostAddress.toString())
QLineEdit::setText(_hostAddress.toString());
return;
}
_hostAddress = hostAddress;
QLineEdit::setText(_hostAddress.toString());
emit this->hostAddressChanged(_hostAddress);
}
QHostAddress LHostAddressEdit::hostAddress() const
{
return _hostAddress;
}
int LHostAddressEdit::currentBlock() const
{
return _currentblock;
}
int LHostAddressEdit::defaultBlock() const
{
return _defaultblock;
}
void LHostAddressEdit::setCurrentBlock(const int block)
{
if (block < 0 || block > 3)
return;
this->setHostAddress(QHostAddress(this->text()));
QStringList ipv4 = (this->text().split("."));
if (ipv4[_currentblock].length() == 0)
{
ipv4[_currentblock] = _hostAddress.toString().split(".").at(_currentblock);
QLineEdit::setText(ipv4.join("."));
}
_currentblock = block;
ipv4 = (this->text().split("."));
int beginPos = currentBlock();
for (int i = 0; i < _currentblock; ++i)
{
beginPos += ipv4[i].length();
}
if (this->hasFocus())
this->setSelection(beginPos, ipv4[_currentblock].length());
emit this->currentBlockChanged(_currentblock);
}
void LHostAddressEdit::setDefaultBlock(const int block)
{
if (block >= 0 && block < 4)
_defaultblock = block;
}
void LHostAddressEdit::setText(const QString& text)
{
QHostAddress hostAddress(text);
this->setHostAddress(hostAddress);
}
void LHostAddressEdit::hostAddressEditFinish()
{
this->setHostAddress(QHostAddress(this->text()));
emit this->hostAddressEditFinished(_hostAddress);
}
void LHostAddressEdit::focusOutEvent(QFocusEvent* event)
{
QLineEdit::focusOutEvent(event);
hostAddressEditFinish();
}
void LHostAddressEdit::focusInEvent(QFocusEvent* event)
{
QLineEdit::focusInEvent(event);
QTimer::singleShot(100, [=]() {this->setCurrentBlock(_defaultblock); });
}
void LHostAddressEdit::keyPressEvent(QKeyEvent* event)
{
if (this->hasSelectedText())
{
if (event->key() == Qt::Key_Left)
{
this->setCurrentBlock(_currentblock - 1);
return;
}
else if (event->key() == Qt::Key_Right)
{
this->setCurrentBlock(_currentblock + 1);
return;
}
}
QLineEdit::keyPressEvent(event);
if (event->key() == Qt::Key_Period)
{
this->nextBlock();
}
}
void LHostAddressEdit::handleCursorPositionChanged(int oldPos, int newPos)
{
if (this->hasSelectedText() && this->selectedText() == this->text())
return;
QStringList ipv4 = (this->text().split("."));
int pos = newPos;
int newBlock;
// 判断当前光标位置是否在第几个里面
if (pos <= ipv4[0].length())
newBlock = 0;
else if (pos <= ipv4[1].length() + ipv4[0].length() + 1)
newBlock = 1;
else if (pos <= ipv4[2].length() + ipv4[1].length() + ipv4[0].length() + 2)
newBlock = 2;
else if (pos <= ipv4[3].length() + ipv4[2].length() + ipv4[1].length() + ipv4[0].length() + 3)
newBlock = 3;
else
newBlock = 0;
if (newBlock != _currentblock)
this->setCurrentBlock(newBlock);
}
void LHostAddressEdit::nextBlock()
{
this->setCurrentBlock(_currentblock + 1);
}
Pyside6 LLineEdit.py
class LHostAddressValidator(QValidator):
def __init__(self, parent=None):
super().__init__(parent)
def validate(self, inputText: str, pos: int) -> QValidator.State:
regularExpression = QRegularExpression(
"^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])?\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])?$")
match: QRegularExpressionMatch = regularExpression.match(inputText)
if match.hasMatch():
return QValidator.State.Acceptable
return QValidator.State.Invalid
class LHostAddressLineEdit(QLineEdit):
currentBlockChanged = Signal(int)
hostAddressChanged = Signal(QHostAddress)
hostAddressEditFinished = Signal(QHostAddress)
@overload
def __init__(self, text: str = "", parent: QWidget | None = None) -> None:
...
@overload
def __init__(self, parent: QWidget | None = None) -> None:
...
@overload
def __init__(self, hostAddress: QHostAddress, parent: QWidget | None = None):
...
def __init__(self, *args, **kwargs):
self._hostAddress = QHostAddress()
self._currentblock = 0
self._defaultblock = 0
parent = None
hostAddress = QHostAddress()
if len(args) == 1:
parent = args[0]
elif len(args) == 2:
if isinstance(args[0], QHostAddress):
hostAddress = args[0]
else:
hostAddress = QHostAddress(args[0])
parent = args[1]
super().__init__(parent)
self.setValidator(LHostAddressValidator(self))
self.cursorPositionChanged.connect(self.handleCursorPositionChanged)
self.selectionChanged.connect(self.handleSelectionChanged)
self.setHostAddress(hostAddress)
def hostAddress(self) -> QHostAddress:
return self._hostAddress
def currentBlock(self) -> int:
return self._currentblock
def defaultBlock(self) -> int:
return self._defaultblock
@Slot(str)
def setText(self, text: str):
self.setHostAddress(QHostAddress(text))
@Slot()
def hostAddressEditFinish(self):
self.setHostAddress(QHostAddress(self.text()))
self.hostAddressEditFinished.emit(self._hostAddress)
@Slot()
def nextBlock(self):
self.setCurrentBlock(self._currentblock + 1)
@Slot(int)
def setCurrentBlock(self, block: int):
if 0 <= block <= 3:
self.setHostAddress(QHostAddress(self.text()))
ipv4: list = self.text().split(".")
if len(ipv4[self._currentblock]) == 0:
ipv4[self._currentblock] = len(self._hostAddress.toString().split(".")[self._currentblock])
super().setText(".".join(ipv4))
self._currentblock = block
beginPos = self._currentblock + sum([len(ipv4[i]) for i in range(self._currentblock)])
if self.hasFocus():
self.setSelection(beginPos, len(ipv4[self._currentblock]))
self.currentBlockChanged.emit(self._currentblock)
@Slot(int)
def setDefaultBlock(self, block: int):
if 0 <= block <= 3:
self._defaultblock = block
@Slot(QHostAddress)
def setHostAddress(self, hostAddress: QHostAddress):
if hostAddress.isNull() or hostAddress.toString() == self._hostAddress.toString():
if self.text() != self._hostAddress.toString():
super().setText(self._hostAddress.toString())
return
self._hostAddress = hostAddress
super().setText(self._hostAddress.toString())
self.hostAddressChanged.emit(self._hostAddress)
def focusOutEvent(self, event: QFocusEvent) -> None:
super().focusOutEvent(event)
self.hostAddressEditFinish()
def focusInEvent(self, event: QFocusEvent) -> None:
super().focusInEvent(event)
QTimer.singleShot(100, lambda: self.setCurrentBlock(self._defaultblock))
def keyPressEvent(self, event: QKeyEvent) -> None:
if self.hasSelectedText():
if event.key() == Qt.Key.Key_Left:
self.setCurrentBlock(self._currentblock - 1)
return
elif event.key() == Qt.Key.Key_Right:
self.setCurrentBlock(self._currentblock + 1)
return
super().keyPressEvent(event)
if event.key() == Qt.Key.Key_Period:
self.nextBlock()
def handleCursorPositionChanged(self, oldPosition: int, newPosition: int) -> None:
if self.hasSelectedText() and self.selectedText() == self.text():
return
ipv4 = self.text().split(".")
pos = newPosition
newBlock = 0
if pos <= len(ipv4[0]):
newBlock = 0
elif pos <= len(ipv4[0]) + len(ipv4[1]) + 1:
newBlock = 1
elif pos <= len(ipv4[0]) + len(ipv4[1]) + len(ipv4[2]) + 2:
newBlock = 2
elif pos <= len(ipv4[0]) + len(ipv4[1]) + len(ipv4[2]) + len(ipv4[3]) + 3:
newBlock = 3
else:
newBlock = 0
if newBlock != self._currentblock:
self.setCurrentBlock(newBlock)
def handleSelectionChanged(self) -> None:
if self.selectedText() == ".":
self.setSelection(self.cursorPosition(), 0)