PyQt5 - QLineEdit正则表达式输入验证器

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

你可能感兴趣的:(PyQt5 - QLineEdit正则表达式输入验证器)