使用 QRegExpValidator 的 QLineEdit 意外行为
QLineEdit unexpected behavior using QRegExpValidator
我正在使用 QLineEdit 小部件输入电子邮件地址,我设置了 QRegExpValidor 来验证输入。
验证器正在努力防止输入 QRegExp 中不允许的字符,但有趣的是,它允许输入中间输入,方法是按下回车键或触发“editingfinished”信号。
我验证了验证器的 return 是正确的(中等或可接受)。
检查 PyQt5 文档发现验证器的中间状态不会阻止焦点更改到另一个小部件。此外,它停止触发 editingFinished 和 returnedPressed 信号,因此用户可能会输入错误的地址,因为它部分地运行了 RegExp。
参考:https://doc.qt.io/qt-5/qlineedit.html#acceptableInput-prop
我能够通过从 QLineEdit 小部件中删除验证器来解决我的需求,并放置一个链接到 QLineEdit 小部件的 cursorPositionChanged 的方法“checkValidator”,当不可接受时丢弃最后输入的字符,并将焦点设置回QLineEdit 在验证中间时。它工作得很好,但是当焦点被重置时,其他小部件上的其他信号一次被触发一个。暂时,我通过检查发件人是否在方法开始时有焦点来处理这个问题(参见 lookForFile)
尽管我可以处理这个问题,但我非常感谢任何人向我解释使用 RegExpValidator 的正确方法,或者为什么重置焦点会突然触发其他信号。
def setUI(self):
self.setWindowTitle("EMail Settings")
self.setModal(True)
rx = QRegExp(r"[a-z0-9_%]+@[a-z0-9%_]+\.[a-z0-9%_]{3,3}")
lblAddress = QLabel("EMail Address")
self.lineAddress = QLineEdit(self)
self.mailValidator = QRegExpValidator(rx, self.lineAddress)
#self.lineAddress.setValidator(self.mailValidator)
self.lineAddress.cursorPositionChanged.connect(self.checkValidator)
self.lineAddress.returnPressed.connect(self.checkValidator)
lblPassword = QLabel("Password")
self.linePwd = QLineEdit()
self.linePwd.setEchoMode(QLineEdit.PasswordEchoOnEdit)
lblOauth2 = QLabel("Oauth2 Token")
self.lineOauth = QLineEdit()
pushOauth = QPushButton("...")
pushOauth.setObjectName("token")
pushOauth.clicked.connect(self.lookForFile)
pushOauth.setFixedWidth(30)
@pyqtSlot()
def checkValidator(self):
self.lineAddress.blockSignals(True)
v = self.mailValidator.validate(self.lineAddress.text(), len(self.lineAddress.text()))
if v[0] == 0:
self.lineAddress.setText(self.lineAddress.text()[:-1])
elif v[0] == 1:
self.lineAddress.setFocus()
elif v[0] == 2:
pass
print("validates", v)
self.lineAddress.blockSignals(False)
@pyqtSlot()
def lookForFile(self):
try:
if not self.sender().hasFocus():
return
baseDir = "C"
obj = self.sender()
if obj.objectName() == "Draft":
capt = "Email Draft"
baseDir = os.getcwd() + "\draft"
fileType = "Polo Management Email (*.pad)"
dialog = QFileDialog(self, directory=os.getcwd())
dialog.setFileMode(QFileDialog.Directory)
res = dialog.getExistingDirectory()
elif obj.objectName() == "token":
capt = "Gmail Outh2 token File"
fileType = "Gmail token Files (*.json)"
baseDir = self.lineOauth.text()
res = QFileDialog.getOpenFileName(self, caption=capt, directory=baseDir, filter=fileType)[0]
fileName = res
if obj.objectName() == "Draft":
self.lineDraft.setText(fileName)
elif obj.objectName() == "tokenFile":
self.lineOauth.setText(fileName)
except Exception as err:
print("settings: lookForFile", err.args)
希望用这个最小的可重现示例来回答@eyllanesc 和 Qmusicmante 的请求。我将正则表达式更改为一个简单的正则表达式,允许一串小写 a-z 后跟一个点和三个小写字符。
我想要的是验证器不允许用户输入错误的输入。该示例允许使用“xxxzb.ods”,但也允许使用“xxxzb”或“xxxzb.o”。
简而言之,不允许用户输入错误的内容。
这是我最小的可重现示例:
class CheckValidator(QDialog):
def __init__(self, parent=None):
super().__init__()
self.parent = parent
self.setUI()
def setUI(self):
self.setWindowTitle("EMail Settings")
self.setModal(True)
rx = QRegExp(r"[a-z]+\.[a-z]{3}")
lblAddress = QLabel("Check Line")
self.lineAddress = QLineEdit()
self.mailValidator = QRegExpValidator(rx, self.lineAddress)
self.lineAddress.setValidator(self.mailValidator)
self.lineAddress.cursorPositionChanged[int, int].connect(lambda
oldPos, newPos: self.printValidator(newPos))
lblCheck = QLabel("Check")
lineCheck = QLineEdit()
formLayout = QFormLayout()
formLayout.addRow(lblAddress, self.lineAddress)
formLayout.addRow(lblCheck, lineCheck)
self.setLayout(formLayout)
@pyqtSlot(int)
def printValidator(self, pos):
print(self.mailValidator.validate(self.lineAddress.text(), pos))
if __name__ == '__main__':
app = QApplication(sys.argv)
tst = CheckValidator()
tst.show()
app.exec()
我找到了一个解决方案,我 post 在这里为这种情况提供它可能对其他人有帮助。
首先,我从 QLineEdit 小部件中删除了 QRegExpValidator。原因是 QLineEdit 仅在验证器存在时 QRegExpValidator returns QValidator.Acceptable 触发 editingFinished (我们将需要它)。
然后我们设置一个由 QlineEdit 小部件的 'cursorPositionchanged' 信号触发的方法。在这个方法中,我们使用 QRegExpValidator 确定最后输入的字符是否有效。如果不是,我们将其删除。
最后,我设置了一个由 'editingFinished' 信号触发的方法,使用 RegEx exactMatch 函数来确定条目是否有效。如果不是,我们为用户提供清除条目或 return 到小部件以继续输入数据的选项。使用的正则表达式仅用于测试目的,有关电子邮件验证的更多信息,请查看@Musicamante 评论。
这是涉及的代码:
def setUI(self):
................
................
rx = QRegExp(r"[a-z0-9_%]+@[a-z0-9%_]+\.[a-z0-9%_]{3,3}")
lblAddress = QLabel("EMail Address")
self.lineAddress = QLineEdit(self)
self.mailValidator = QRegExpValidator(rx, self.lineAddress)
self.lineAddress.cursorPositionChanged[int, int].connect(lambda oldPos,
newPos: self.checkValidator(newPos))
self.lineAddress.editingFinished.connect(lambda : self.checkRegExp(rx))
@pyqtSlot(int)
def checkValidator(self, pos):
v = self.mailValidator.validate(self.lineAddress.text(), pos ))
if v[0] == 0:
self.lineAddress.setText(self.lineAddress.text()[:-1])
@pyqtSlot(QRegExp)
def checkRegExp(self, rx):
if not rx.exactMatch(self.lineAddress.text()) and self.lineAddress.text():
if QMessageBox.question(self, "Leave the Address field",
"The string entered is not a valid email address! \n"
"Do you want to clear the field?", QMessageBox.Yes|QMessageBox.No) ==
QMessageBox.Yes:
self.lineAddress.clear()
else:
self.lineAddress.setFocus()
我正在使用 QLineEdit 小部件输入电子邮件地址,我设置了 QRegExpValidor 来验证输入。
验证器正在努力防止输入 QRegExp 中不允许的字符,但有趣的是,它允许输入中间输入,方法是按下回车键或触发“editingfinished”信号。
我验证了验证器的 return 是正确的(中等或可接受)。
检查 PyQt5 文档发现验证器的中间状态不会阻止焦点更改到另一个小部件。此外,它停止触发 editingFinished 和 returnedPressed 信号,因此用户可能会输入错误的地址,因为它部分地运行了 RegExp。 参考:https://doc.qt.io/qt-5/qlineedit.html#acceptableInput-prop
我能够通过从 QLineEdit 小部件中删除验证器来解决我的需求,并放置一个链接到 QLineEdit 小部件的 cursorPositionChanged 的方法“checkValidator”,当不可接受时丢弃最后输入的字符,并将焦点设置回QLineEdit 在验证中间时。它工作得很好,但是当焦点被重置时,其他小部件上的其他信号一次被触发一个。暂时,我通过检查发件人是否在方法开始时有焦点来处理这个问题(参见 lookForFile)
尽管我可以处理这个问题,但我非常感谢任何人向我解释使用 RegExpValidator 的正确方法,或者为什么重置焦点会突然触发其他信号。
def setUI(self):
self.setWindowTitle("EMail Settings")
self.setModal(True)
rx = QRegExp(r"[a-z0-9_%]+@[a-z0-9%_]+\.[a-z0-9%_]{3,3}")
lblAddress = QLabel("EMail Address")
self.lineAddress = QLineEdit(self)
self.mailValidator = QRegExpValidator(rx, self.lineAddress)
#self.lineAddress.setValidator(self.mailValidator)
self.lineAddress.cursorPositionChanged.connect(self.checkValidator)
self.lineAddress.returnPressed.connect(self.checkValidator)
lblPassword = QLabel("Password")
self.linePwd = QLineEdit()
self.linePwd.setEchoMode(QLineEdit.PasswordEchoOnEdit)
lblOauth2 = QLabel("Oauth2 Token")
self.lineOauth = QLineEdit()
pushOauth = QPushButton("...")
pushOauth.setObjectName("token")
pushOauth.clicked.connect(self.lookForFile)
pushOauth.setFixedWidth(30)
@pyqtSlot()
def checkValidator(self):
self.lineAddress.blockSignals(True)
v = self.mailValidator.validate(self.lineAddress.text(), len(self.lineAddress.text()))
if v[0] == 0:
self.lineAddress.setText(self.lineAddress.text()[:-1])
elif v[0] == 1:
self.lineAddress.setFocus()
elif v[0] == 2:
pass
print("validates", v)
self.lineAddress.blockSignals(False)
@pyqtSlot()
def lookForFile(self):
try:
if not self.sender().hasFocus():
return
baseDir = "C"
obj = self.sender()
if obj.objectName() == "Draft":
capt = "Email Draft"
baseDir = os.getcwd() + "\draft"
fileType = "Polo Management Email (*.pad)"
dialog = QFileDialog(self, directory=os.getcwd())
dialog.setFileMode(QFileDialog.Directory)
res = dialog.getExistingDirectory()
elif obj.objectName() == "token":
capt = "Gmail Outh2 token File"
fileType = "Gmail token Files (*.json)"
baseDir = self.lineOauth.text()
res = QFileDialog.getOpenFileName(self, caption=capt, directory=baseDir, filter=fileType)[0]
fileName = res
if obj.objectName() == "Draft":
self.lineDraft.setText(fileName)
elif obj.objectName() == "tokenFile":
self.lineOauth.setText(fileName)
except Exception as err:
print("settings: lookForFile", err.args)
希望用这个最小的可重现示例来回答@eyllanesc 和 Qmusicmante 的请求。我将正则表达式更改为一个简单的正则表达式,允许一串小写 a-z 后跟一个点和三个小写字符。
我想要的是验证器不允许用户输入错误的输入。该示例允许使用“xxxzb.ods”,但也允许使用“xxxzb”或“xxxzb.o”。 简而言之,不允许用户输入错误的内容。
这是我最小的可重现示例:
class CheckValidator(QDialog):
def __init__(self, parent=None):
super().__init__()
self.parent = parent
self.setUI()
def setUI(self):
self.setWindowTitle("EMail Settings")
self.setModal(True)
rx = QRegExp(r"[a-z]+\.[a-z]{3}")
lblAddress = QLabel("Check Line")
self.lineAddress = QLineEdit()
self.mailValidator = QRegExpValidator(rx, self.lineAddress)
self.lineAddress.setValidator(self.mailValidator)
self.lineAddress.cursorPositionChanged[int, int].connect(lambda
oldPos, newPos: self.printValidator(newPos))
lblCheck = QLabel("Check")
lineCheck = QLineEdit()
formLayout = QFormLayout()
formLayout.addRow(lblAddress, self.lineAddress)
formLayout.addRow(lblCheck, lineCheck)
self.setLayout(formLayout)
@pyqtSlot(int)
def printValidator(self, pos):
print(self.mailValidator.validate(self.lineAddress.text(), pos))
if __name__ == '__main__':
app = QApplication(sys.argv)
tst = CheckValidator()
tst.show()
app.exec()
我找到了一个解决方案,我 post 在这里为这种情况提供它可能对其他人有帮助。 首先,我从 QLineEdit 小部件中删除了 QRegExpValidator。原因是 QLineEdit 仅在验证器存在时 QRegExpValidator returns QValidator.Acceptable 触发 editingFinished (我们将需要它)。
然后我们设置一个由 QlineEdit 小部件的 'cursorPositionchanged' 信号触发的方法。在这个方法中,我们使用 QRegExpValidator 确定最后输入的字符是否有效。如果不是,我们将其删除。
最后,我设置了一个由 'editingFinished' 信号触发的方法,使用 RegEx exactMatch 函数来确定条目是否有效。如果不是,我们为用户提供清除条目或 return 到小部件以继续输入数据的选项。使用的正则表达式仅用于测试目的,有关电子邮件验证的更多信息,请查看@Musicamante 评论。
这是涉及的代码:
def setUI(self):
................
................
rx = QRegExp(r"[a-z0-9_%]+@[a-z0-9%_]+\.[a-z0-9%_]{3,3}")
lblAddress = QLabel("EMail Address")
self.lineAddress = QLineEdit(self)
self.mailValidator = QRegExpValidator(rx, self.lineAddress)
self.lineAddress.cursorPositionChanged[int, int].connect(lambda oldPos,
newPos: self.checkValidator(newPos))
self.lineAddress.editingFinished.connect(lambda : self.checkRegExp(rx))
@pyqtSlot(int)
def checkValidator(self, pos):
v = self.mailValidator.validate(self.lineAddress.text(), pos ))
if v[0] == 0:
self.lineAddress.setText(self.lineAddress.text()[:-1])
@pyqtSlot(QRegExp)
def checkRegExp(self, rx):
if not rx.exactMatch(self.lineAddress.text()) and self.lineAddress.text():
if QMessageBox.question(self, "Leave the Address field",
"The string entered is not a valid email address! \n"
"Do you want to clear the field?", QMessageBox.Yes|QMessageBox.No) ==
QMessageBox.Yes:
self.lineAddress.clear()
else:
self.lineAddress.setFocus()