Валидация полей в интерфейсе на PyQt5

Предположим стоит такая задача:
Есть интерфейс на PyQt5 с двумя полями класса QLineEdit и кнопка QPushButton.
Как сделать, чтобы в эти поля пользователь мог добавить только цифры.
При добавлении любых других символов они бы туда не печатались.
И цифр там в итоге можно напечатать в первое поле только 7.
Восьмая цифра тоже уже не должна печататься в поле.
Во второе поле только 2 цифры.
И кнопка класса QPushButton становится активной только когда набраны в обеих полях соответственно 7 и 2 цифры.

Для выполнения задачи в PyQt5 мы будем использовать несколько элементов:

  1. Класс QLineEdit для текстового поля.
  2. Класс QPushButton для кнопки.
  3. Класс QIntValidator для ограничения ввода только цифрами.
  4. Сигналы и слоты для отслеживания изменений в текстовом поле и активации кнопки.

Начнем с создания основного интерфейса, затем добавим валидатор и логику активации кнопки.

Python
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLineEdit, QPushButton
from PyQt5.QtGui import QIntValidator

class MyApp(QWidget):
    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):
        layout = QVBoxLayout()

        # Создание первого текстового поля для 7 цифр
        self.line_edit_1 = QLineEdit(self)
        self.line_edit_1.setMaxLength(7)  # Ограничение длины ввода до 7 символов

        # Установка валидатора для ввода только цифр
        int_validator_1 = QIntValidator(0, 9999999, self)
        self.line_edit_1.setValidator(int_validator_1)
        
        # Подключение сигнала изменения текста к слоту
        self.line_edit_1.textChanged.connect(self.on_text_changed)

        # Создание второго текстового поля для 2 цифр
        self.line_edit_2 = QLineEdit(self)
        self.line_edit_2.setMaxLength(2)  # Ограничение длины ввода до 2 символов

        # Установка валидатора для ввода только цифр
        int_validator_2 = QIntValidator(0, 99, self)
        self.line_edit_2.setValidator(int_validator_2)
        
        # Подключение сигнала изменения текста к слоту
        self.line_edit_2.textChanged.connect(self.on_text_changed)

        # Создание кнопки и деактивация ее по умолчанию
        self.button = QPushButton('Submit', self)
        self.button.setEnabled(False)

        # Добавление элементов в макет
        layout.addWidget(self.line_edit_1)
        layout.addWidget(self.line_edit_2)
        layout.addWidget(self.button)

        self.setLayout(layout)
        self.setWindowTitle('Digit Input')
        self.show()

    def on_text_changed(self):
        # Проверка, что оба поля заполнены корректно
        is_line_edit_1_valid = len(self.line_edit_1.text()) == 7
        is_line_edit_2_valid = len(self.line_edit_2.text()) == 2

        # Активировать кнопку, если оба условия выполнены
        self.button.setEnabled(is_line_edit_1_valid and is_line_edit_2_valid)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MyApp()
    sys.exit(app.exec_())

Давайте разберем код:

  1. Создание интерфейса: Мы создаём основное окно с вертикальным макетом, в котором размещаются текстовое поле и кнопка.
  2. Валидатор: Используется QIntValidator для разрешения ввода только цифр. setMaxLength(7) устанавливает максимальную длину текста в 7 символов. Аналогично для второго поля.
  3. Сигналы и слоты: Подключаем сигнал textChanged текстового поля к слоту on_text_changed, который проверяет длину текста и активирует кнопку, если введено ровно 7 цифр.
  4. Активация кнопки: В методе on_text_changed мы проверяем длину текста и активируем кнопку, если оба поля содержат нужное количество цифр (7 и 2 соответственно).
А как валидировать поля на соответствие регулярному выражению?

Чтобы валидировать поля на соответствие регулярному выражению в PyQt5, можно использовать класс QRegExpValidator. Этот валидатор позволяет проверять вводимые данные на соответствие заданному регулярному выражению.

Вот пример кода, который демонстрирует, как использовать QRegExpValidator для валидации полей в нашем случае:

Python
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLineEdit, QPushButton
from PyQt5.QtGui import QRegExpValidator
from PyQt5.QtCore import QRegExp

class MyApp(QWidget):
    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):
        layout = QVBoxLayout()

        # Создание первого текстового поля и установка валидатора на 7 цифр
        self.line_edit_1 = QLineEdit(self)
        reg_exp_1 = QRegExp(r"^\d{7}$")  # Регулярное выражение для 7 цифр
        validator_1 = QRegExpValidator(reg_exp_1, self.line_edit_1)
        self.line_edit_1.setValidator(validator_1)
        
        # Подключение сигнала изменения текста к слоту
        self.line_edit_1.textChanged.connect(self.on_text_changed)

        # Создание второго текстового поля и установка валидатора на 2 цифры
        self.line_edit_2 = QLineEdit(self)
        reg_exp_2 = QRegExp(r"^\d{2}$")  # Регулярное выражение для 2 цифр
        validator_2 = QRegExpValidator(reg_exp_2, self.line_edit_2)
        self.line_edit_2.setValidator(validator_2)
        
        # Подключение сигнала изменения текста к слоту
        self.line_edit_2.textChanged.connect(self.on_text_changed)

        # Создание кнопки и деактивация ее по умолчанию
        self.button = QPushButton('Submit', self)
        self.button.setEnabled(False)

        # Добавление элементов в макет
        layout.addWidget(self.line_edit_1)
        layout.addWidget(self.line_edit_2)
        layout.addWidget(self.button)

        self.setLayout(layout)
        self.setWindowTitle('Regex Validation Example')
        self.show()

    def on_text_changed(self):
        # Проверка валидности обоих полей
        state_1 = self.line_edit_1.hasAcceptableInput()
        state_2 = self.line_edit_2.hasAcceptableInput()

        # Активировать кнопку, если оба поля валидны
        self.button.setEnabled(state_1 and state_2)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MyApp()
    sys.exit(app.exec_())

В этом коде:

  1. Первое текстовое поле: Создаем поле QLineEdit и устанавливаем для него валидатор QRegExpValidator с регулярным выражением для проверки на 7 цифр (^\d{7}$).
  2. Второе текстовое поле: Аналогично создаем второе поле QLineEdit и устанавливаем валидатор QRegExpValidator с регулярным выражением для проверки на 2 цифры (^\d{2}$).
  3. Метод on_text_changed: Проверяет валидность обоих полей с помощью метода hasAcceptableInput и активирует кнопку только если оба поля валидны.
Теперь немного разнообразим условие задачи - в первом поле мы должны валидировать email, а во втором номер телефона.

Для email регулярное выражение может быть следующим: ^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$.

Разбор конструкции:

[a-zA-Z0-9_.+-] — это символьный класс, который означает «любой один символ из указанных в скобках». Вот, что конкретно здесь обозначают различные части:

  • a-z: любые строчные буквы латинского алфавита (от ‘a’ до ‘z’).
  • A-Z: любые заглавные буквы латинского алфавита (от ‘A’ до ‘Z’).
  • 0-9: любые цифры (от ‘0’ до ‘9’).
  • _: символ подчеркивания (нижнее подчеркивание).
  • .: точка.
  • +: знак плюс.
  • -: дефис (тире).

Когда вы видите [a-zA-Z0-9_.+-] в регулярном выражении, это означает, что в этом месте может стоять любой из указанных символов. Например, регулярное выражение ^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$ используется для валидации email и включает эту конструкцию для указания допустимых символов в разных частях email адреса.

Аналогично для номера телефона (например, 10-значный номер) регулярное выражение может быть следующим: ^\d{10}$.

Вот обновленный код:

Python
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLineEdit, QPushButton
from PyQt5.QtGui import QRegExpValidator
from PyQt5.QtCore import QRegExp

class MyApp(QWidget):
    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):
        layout = QVBoxLayout()

        # Создание текстового поля для email и установка валидатора
        self.line_edit_email = QLineEdit(self)
        
        # Регулярное выражение для email
        reg_exp_email = QRegExp(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$")  
        validator_email = QRegExpValidator(reg_exp_email, self.line_edit_email)
        self.line_edit_email.setValidator(validator_email)
        
        # Подключение сигнала изменения текста к слоту
        self.line_edit_email.textChanged.connect(self.on_text_changed)

        # Создание текстового поля для номера телефона и установка валидатора
        self.line_edit_phone = QLineEdit(self)
        
        # Регулярное выражение для 10-значного номера телефона
        reg_exp_phone = QRegExp(r"^\d{10}$")  
        validator_phone = QRegExpValidator(reg_exp_phone, self.line_edit_phone)
        self.line_edit_phone.setValidator(validator_phone)
        
        # Подключение сигнала изменения текста к слоту
        self.line_edit_phone.textChanged.connect(self.on_text_changed)

        # Создание кнопки и деактивация ее по умолчанию
        self.button = QPushButton('Submit', self)
        self.button.setEnabled(False)

        # Добавление элементов в макет
        layout.addWidget(self.line_edit_email)
        layout.addWidget(self.line_edit_phone)
        layout.addWidget(self.button)

        self.setLayout(layout)
        self.setWindowTitle('Regex Validation Example')
        self.show()

    def on_text_changed(self):
        # Проверка валидности обоих полей
        state_email = self.line_edit_email.hasAcceptableInput()
        state_phone = self.line_edit_phone.hasAcceptableInput()

        # Активировать кнопку, если оба поля валидны
        self.button.setEnabled(state_email and state_phone)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MyApp()
    sys.exit(app.exec_())