PyQt5 - QLineEdit正则表达式输入验证器
资料参考:
https://stackoverflow.com/questions/39202697/qt-qlineedit-input-validation
https://stackoverflow.com/questions/15829782/how-to-restrict-user-input-in-qlineedit-in-pyqt
前言
QLineEdit是PyQt中的一个常用的单行文本输入框,供用户在UI界面输入文本内容。
通常在程序的UI界面输入文本内容时,需要限定用户输入文本内容的格式。比如限制输入文本格式为科学计数法专用于计算,限制输入文本格式为整数专用于计数、序号命名等等。如果不使用输入验证器,当用户在UI界面中输入的内容不合规范时,每次运行程序时,如果不对输入的文本内容的格式进行检查,就极有可能造成程序运行崩溃失控。对QLineEdit控件使用正则表达式输入验证器,可以自动纠正用户输入不规范的内容,免去程序频繁地判断用户输入法的数据是否合法,简化代码的复杂程度。
对于QLineEdit正则表达式输入验证器这方面,记得当时被这个问题卡了一个多月,Google和百度能搜索到的网上的现成资料比较少或者说资料不全,只能在stackoverflow靠着零碎的解答,结合PyQt5的帮助文档,在一直不断尝试的努力下,终于完美解决了这个问题。现将完整代码发出来,仅供大家学习参考,减少这方面学习上的弯路。
实例演示
QLineEdit正则表达式输入验证器(类)
LineEditRegExpValidator.py
from PyQt5 import QtWidgets, QtCore, QtGui, Qt
import re
############## QLineEdit正则表达式输入验证器
class LineEditRegExpValidator(QtGui.QValidator):
'''
# 默认为科学计数法输入验证器
用法
SciNotValidator = LineEditRegExpValidator() # 创建一个QLineEdit正则表达式输入验证器的类,默认为科学计数法输入验证器
self.LineEdit1.setValidator(SciNotValidator) # 设置验证器(启用)
self.LineEdit1.installEventFilter(SciNotValidator) # QLineEdit清空内容且游标失焦时,自动填充上一次的字符串内容
self.LineEdit2.setValidator(SciNotValidator)
self.LineEdit2.installEventFilter(SciNotValidator)
self.LineEdit3.setValidator(SciNotValidator)
self.LineEdit3.installEventFilter(SciNotValidator)
Validator.validate() is abstract and must be overriddenValidator.validate() is abstract and must be overridden
'''
def __init__(
self,
# 编辑状态框输入结束允许的字符串
fullPatterns=[
r"[+|-]?[0-9]+\.?[0-9]*(?:[Ee][+|-]?[0-9]+)?",
r'[+|-]{0,1}nan', r'[+|-]{0,1}inf'
],
# 编辑状态框输入尚未结束允许的字符串
partialPatterns=[
r'[+|-]?[0-9]+\.?[0-9]*(?:[Ee][+|-]?)?',
r'-',
r'\+',
r'[+|-]{0,1}nan',
r'[+|-]{0,1}na',
r'[+|-]{0,1}n',
r'[+|-]{0,1}inf',
r'[+|-]{0,1}in',
r'[+|-]{0,1}i'
],
fixupString='1.0'
):
super(LineEditRegExpValidator, self).__init__()
self.fullPatterns = fullPatterns
self.partialPatterns = partialPatterns
self.fixupString = fixupString
# 实时监听文本框的改变
# 可能是键盘单个字符'n'输入, 也有可能是粘贴多个字符'nan'输入
def validate(self, string, pos) -> QtGui.QValidator.State: # string为编辑状态框中可见的字符串+输入字符/字符串
# 编辑过程结束,若返回True,将编辑状态框中的字符串填入LineEdit,若返回Flase则自动调用self.fixup方法,将fixup方法返回的字符串填入LineEdit
if self.acceptable_check(string):
#print(f'QtGui.QValidator.Acceptable:{QtGui.QValidator.Acceptable}')
return QtGui.QValidator.Acceptable, string, pos # QtGui.QValidator.Acceptable = 2;
# 编辑过程中允许出现的字符串
if self.intermediate_check(string):
#print(f'QtGui.QValidator.Intermediate:{QtGui.QValidator.Intermediate}')
return QtGui.QValidator.Intermediate, string, pos # QtGui.QValidator.State = 1;
# 编辑过程中不允许出现的字符串(本次输入的单个字符或字符串无效)
else:
#print(f'QtGui.QValidator.Invalid:{QtGui.QValidator.Invalid}')
return QtGui.QValidator.Invalid, string, pos
# 编辑状态框验证通过, 编辑状态框单个字输入符成功
def acceptable_check(self, string) -> bool:
True_ = 0
for fullPattern in self.fullPatterns:
if re.fullmatch(fullPattern, string):
True_ += 1
else:
continue
if True_ != 0:
return True
else:
return False
# 输入还未结束允许的字符串
def intermediate_check(self, string): #-> bool; string为编辑状态框中可见的字符串
"""
Checks if string makes a valid partial float, keeping in mind locale dependent decimal separators.
"""
if string == '':
return True
for partialPattern in self.partialPatterns:
if re.fullmatch(partialPattern, string):
return True
else:
pass
#
def eventFilter(self, lineEdit, event): # -> bool
# FocusIn event
# 每当fous in时,更新LineEditRegExpValidator的fixupString
# 输入验证器
'''
SciNotValidator = LineEditRegExpValidator()
self.LineEdit1.setValidator(SciNotValidator)
self.LineEdit1.installEventFilter(SciNotValidator)
'''
if event.type() == QtCore.QEvent.FocusIn:
# do custom stuff
#print('focus in')
# self.lineEdit_zhuansu.installEventFilter(SciNotValidator), 在本类中,widget是self.lineEdit,执行函数self.lineEdit.text(), 其它类不一定有text()方法
#lineEdit.selectAll()
QtCore.QTimer.singleShot(0, lineEdit.selectAll) # 0ms
self.fixupString = lineEdit.text()
#print(self.fixupString)
# return False so that the lineEdit will also handle the event
# otherwise it won't focus out
return False
else:
# we don't care about other events
return False
# 重写QValidator的fixup(str)方法。可以在切换焦点后,直接修改不合规则的字符串。参数str是经过validate()方法验证后的字符串;
def fixup(self, string) -> str:
"""
Fixes up input text to create a valid float. Puts an empty string on failure.
"""
print(string)
True_ = 0
for fullPattern in self.fullPatterns:
if re.fullmatch(fullPattern, string):
True_ += 1
else:
continue
if True_ != 0:
return string
else:
return self.fixupString
QLineEdit正则表达式输入验证器Delegate(类)
LineEditDelegate_Regx.py
from PyQt5 import QtWidgets, QtCore, QtGui, Qt
import re
############## QLineEdit正则表达式输入验证器
class LineEditRegExpValidator(QtGui.QValidator):
'''
# 默认为科学计数法输入验证器
用法
SciNotValidator = LineEditRegExpValidator() # 创建一个QLineEdit正则表达式输入验证器的类,默认为科学计数法输入验证器
self.LineEdit1.setValidator(SciNotValidator) # 设置验证器(启用)
self.LineEdit1.installEventFilter(SciNotValidator) # QLineEdit清空内容且游标失焦时,自动填充上一次的字符串内容
self.LineEdit2.setValidator(SciNotValidator)
self.LineEdit2.installEventFilter(SciNotValidator)
self.LineEdit3.setValidator(SciNotValidator)
self.LineEdit3.installEventFilter(SciNotValidator)
Validator.validate() is abstract and must be overriddenValidator.validate() is abstract and must be overridden
'''
def __init__(
self,
# 编辑状态框输入结束允许的字符串
fullPatterns=[
r"[+|-]?[0-9]+\.?[0-9]*(?:[Ee][+|-]?[0-9]+)?",
r'[+|-]{0,1}nan', r'[+|-]{0,1}inf'
],
# 编辑状态框输入尚未结束允许的字符串
partialPatterns=[
r'[+|-]?[0-9]+\.?[0-9]*(?:[Ee][+|-]?)?',
r'-',
r'\+',
r'[+|-]{0,1}nan',
r'[+|-]{0,1}na',
r'[+|-]{0,1}n',
r'[+|-]{0,1}inf',
r'[+|-]{0,1}in',
r'[+|-]{0,1}i'
],
fixupString='1.0'
):
super(LineEditRegExpValidator, self).__init__()
self.fullPatterns = fullPatterns
self.partialPatterns = partialPatterns
self.fixupString = fixupString
# 实时监听文本框的改变
# 可能是键盘单个字符'n'输入, 也有可能是粘贴多个字符'nan'输入
def validate(self, string, pos) -> QtGui.QValidator.State: # string为编辑状态框中可见的字符串+输入字符/字符串
# 编辑过程结束,若返回True,将编辑状态框中的字符串填入LineEdit,若返回Flase则自动调用self.fixup方法,将fixup方法返回的字符串填入LineEdit
if self.acceptable_check(string):
#print(f'QtGui.QValidator.Acceptable:{QtGui.QValidator.Acceptable}')
return QtGui.QValidator.Acceptable, string, pos # QtGui.QValidator.Acceptable = 2;
# 编辑过程中允许出现的字符串
if self.intermediate_check(string):
#print(f'QtGui.QValidator.Intermediate:{QtGui.QValidator.Intermediate}')
return QtGui.QValidator.Intermediate, string, pos # QtGui.QValidator.State = 1;
# 编辑过程中不允许出现的字符串(本次输入的单个字符或字符串无效)
else:
#print(f'QtGui.QValidator.Invalid:{QtGui.QValidator.Invalid}')
return QtGui.QValidator.Invalid, string, pos
# 编辑状态框验证通过, 编辑状态框单个字输入符成功
def acceptable_check(self, string) -> bool:
True_ = 0
for fullPattern in self.fullPatterns:
if re.fullmatch(fullPattern, string):
True_ += 1
else:
continue
if True_ != 0:
return True
else:
return False
# 输入还未结束允许的字符串
def intermediate_check(self, string): #-> bool; string为编辑状态框中可见的字符串
"""
Checks if string makes a valid partial float, keeping in mind locale dependent decimal separators.
"""
if string == '':
return True
for partialPattern in self.partialPatterns:
if re.fullmatch(partialPattern, string):
return True
else:
pass
#
def eventFilter(self, lineEdit, event): # -> bool
# FocusIn event
# 每当fous in时,更新LineEditRegExpValidator的fixupString
# 输入验证器
'''
SciNotValidator = LineEditRegExpValidator()
self.LineEdit1.setValidator(SciNotValidator)
self.LineEdit1.installEventFilter(SciNotValidator)
'''
if event.type() == QtCore.QEvent.FocusIn:
# do custom stuff
#print('focus in')
# self.lineEdit_zhuansu.installEventFilter(SciNotValidator), 在本类中,widget是self.lineEdit,执行函数self.lineEdit.text(), 其它类不一定有text()方法
#lineEdit.selectAll()
QtCore.QTimer.singleShot(0, lineEdit.selectAll) # 0ms
self.fixupString = lineEdit.text()
#print(self.fixupString)
# return False so that the lineEdit will also handle the event
# otherwise it won't focus out
return False
else:
# we don't care about other events
return False
# 重写QValidator的fixup(str)方法。可以在切换焦点后,直接修改不合规则的字符串。参数str是经过validate()方法验证后的字符串;
def fixup(self, string) -> str:
"""
Fixes up input text to create a valid float. Puts an empty string on failure.
"""
print(string)
True_ = 0
for fullPattern in self.fullPatterns:
if re.fullmatch(fullPattern, string):
True_ += 1
else:
continue
if True_ != 0:
return string
else:
return self.fixupString
UI界面
MainWindow_ui.py
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'MainWindow_ui.ui'
#
# Created by: PyQt5 UI code generator 5.14.2
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(813, 535)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.lineEdit_1 = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit_1.setGeometry(QtCore.QRect(30, 90, 241, 31))
self.lineEdit_1.setObjectName("lineEdit_1")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(30, 50, 261, 31))
self.label.setObjectName("label")
self.label_2 = QtWidgets.QLabel(self.centralwidget)
self.label_2.setGeometry(QtCore.QRect(30, 150, 211, 21))
self.label_2.setObjectName("label_2")
self.lineEdit_2 = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit_2.setGeometry(QtCore.QRect(30, 180, 241, 31))
self.lineEdit_2.setObjectName("lineEdit_2")
self.label_3 = QtWidgets.QLabel(self.centralwidget)
self.label_3.setGeometry(QtCore.QRect(30, 250, 211, 21))
self.label_3.setObjectName("label_3")
self.lineEdit_3 = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit_3.setGeometry(QtCore.QRect(30, 280, 241, 31))
self.lineEdit_3.setObjectName("lineEdit_3")
self.label_4 = QtWidgets.QLabel(self.centralwidget)
self.label_4.setGeometry(QtCore.QRect(30, 340, 211, 21))
self.label_4.setObjectName("label_4")
self.lineEdit_4 = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit_4.setGeometry(QtCore.QRect(30, 370, 241, 31))
self.lineEdit_4.setObjectName("lineEdit_4")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 813, 26))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "QLineEdit正则表达式输入验证器 - by 鱼子酱"))
self.lineEdit_1.setText(_translate("MainWindow", "a12^*+>"))
self.label.setText(_translate("MainWindow", "任意内容(无输入验证器)"))
self.label_2.setText(_translate("MainWindow", "科学计数法输入验证器"))
self.lineEdit_2.setText(_translate("MainWindow", "1.23e-3"))
self.label_3.setText(_translate("MainWindow", "整数(六位)输入验证器"))
self.lineEdit_3.setText(_translate("MainWindow", "123456"))
self.label_4.setText(_translate("MainWindow", "字母(六位)输入验证器"))
self.lineEdit_4.setText(_translate("MainWindow", "aBcDeF"))
主程序
Main.py
# coding:utf-8
# Python 3.8.x
from MainWindow_ui import Ui_MainWindow
from LineEditRegExpValidator import *
# PyQt5 - QLineEdit正则表达式输入验证器
# -- by 鱼子酱
class NewClassName(QtWidgets.QMainWindow, Ui_MainWindow):
'''
'''
def __init__(self, parent=None):
super(NewClassName, self).__init__(parent) #
self.setupUi(self) #
self.setFixedSize(self.width(), self.height())
self.setFocus(True)
# 创建输入验证器(默认为科学表达式输入验证器)
SciNotValidator = LineEditRegExpValidator()
self.lineEdit_2.setValidator(SciNotValidator)
# 当输入不合法或未完成时,游标移除编辑框时,编辑框返回上一次内容。
self.lineEdit_2.installEventFilter(SciNotValidator)
# 创建整数输入(六位)验证器
SciNotValidator = LineEditRegExpValidator(
fullPatterns=[
r'[0-9]{6}',
],
partialPatterns=[
'',
r'[0-9]{1,6}'
],
fixupString='123456'
)
self.lineEdit_3.setValidator(SciNotValidator)
self.lineEdit_3.installEventFilter(SciNotValidator)
# 创建字母(包含大小写,六位)输入验证器
SciNotValidator = LineEditRegExpValidator(
fullPatterns=[
r'[a-zA-Z]{6}',
],
partialPatterns=[
'',
r'[a-zA-Z]{1,6}'
],
fixupString='aBcDeF'
)
self.lineEdit_4.setValidator(SciNotValidator)
self.lineEdit_4.installEventFilter(SciNotValidator)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = NewClassName()
MainWindow.show()
app.exec_()
主页:https://www.zhihu.com/people/caviar126
本文地址:https://zhuanlan.zhihu.com/p/358497097