Валидация поля QLineEdit с элементами обратной связи для пользователя. Неожиданное изменение высоты QLineEdit

Пытаюсь написать валидацю полей с элементом обратной связи для пользователя.

Кнопка Save активируется в случае, если все поля заполнены корректно.

В качестве обратной связи для пользователя использую изменение цвета border в фокусном QLineEdit.

По какой-то причине изменение цвета границ меняет высоту (height) поля, делая его слегка меньше.
Визуально заметно на поле changed_line, лечится методом setFixedHeight() (исправлено в поле fixed_line).

Но хотелось бы понять причину такого поведения и как "вылечить" глобально, не фиксируя высоту для каждого поля.
Причем размер возвращается к исходному значению при смене поля и начале ввода информации.

main.py

import sys

from PyQt5.QtGui import QRegExpValidator
from PyQt5.QtCore import pyqtSlot, QRegExp
from PyQt5.QtWidgets import *


class MainAppInterface(QMainWindow):

    def __init__(self):

        super().__init__()

        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)

        self.setMinimumHeight(300)
        self.setMinimumWidth(400)
        self.resize(400, 300)

        self.setWindowTitle("Test")

        self.__main_app_build()

    def __main_app_build(self):

        self.line = QLineEdit()
        self.line.setPlaceholderText("Default Line")

        changed_re = QRegExp("[a-zA-Z0-9_.-]+")
        self.changed_line = QLineEdit()
        self.changed_line.setPlaceholderText("Changed Line")
        self.changed_line.setValidator(QRegExpValidator(changed_re, self.changed_line))
        self.changed_line.textChanged.connect(self.__save_enable)

        fixed_re = QRegExp("^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$")
        self.fixed_line = QLineEdit()
        self.fixed_line.setPlaceholderText("Fixed Line (email field)")
        self.fixed_line.setValidator(QRegExpValidator(fixed_re, self.fixed_line))
        self.fixed_line.textChanged.connect(self.__save_enable)
        self.fixed_line.setFixedHeight(22)

        self.save_button = QPushButton("Save")
        self.save_button.setDisabled(True)

        g_layout = QGridLayout(self.centralWidget)
        g_layout.addWidget(self.line, 0, 1)
        g_layout.addWidget(self.changed_line, 1, 1)
        g_layout.addWidget(self.fixed_line, 2, 1)
        g_layout.addWidget(self.save_button, 3, 1)

    @ pyqtSlot()
    def __save_enable(self):

        if self.changed_line.hasAcceptableInput() and self.fixed_line.hasAcceptableInput():
            self.save_button.setDisabled(False)
        else:
            self.save_button.setDisabled(True)

        self.changed_line.setStyleSheet("""
            QLineEdit::focus {{
                border-radius: 2px;
                border: 1px outset;
                border-color: {color};
            }}
        """.format(color="#00ff00" if self.changed_line.hasAcceptableInput() else "#ff0000"))

        self.fixed_line.setStyleSheet("""
            QLineEdit::focus {{
                border-radius: 2px;
                border: 1px outset;
                border-color: {color};
            }}
        """.format(color="#00ff00" if self.fixed_line.hasAcceptableInput() else "#ff0000"))


if __name__ == '__main__':

    app = QApplication(sys.argv)
    app.setStyle("Fusion")
    ex = MainAppInterface()
    ex.show()
    sys.exit(app.exec_())

Была мысль выставить в классе:

    self.setStyleSheet("""
        QLineEdit::focus {
            height: 22px;
        
        }""")

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


Ответы (1 шт):

Автор решения: S. Nick

Я так понял, что такое поведение border: 1px outset;
Попрбуйте поиграться с размерами.

Обратите внимание, что я пока экспериментировал, добавил всем QLineEdit - objectName,
в любом случае, это не помешает, а также добавил метод func() для наблюдения происходящего.

import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtGui import QRegExpValidator
from PyQt5.QtCore import pyqtSlot, QRegExp
from PyQt5.QtWidgets import *


class MainAppInterface(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Test")
#        self.setMinimumHeight(300)
#        self.setMinimumWidth(400)
        self.resize(400, 300)
        
        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)
        self.__main_app_build()

    def __main_app_build(self):
        self.line = QLineEdit(objectName='line')
        self.line.setPlaceholderText("Default Line")

        changed_re = QRegExp("[a-zA-Z0-9_.-]+")
        self.changed_line = QLineEdit(objectName='changed_line')
        self.changed_line.setPlaceholderText("Changed Line")
        self.changed_line.setValidator(
            QRegExpValidator(changed_re, self.changed_line))
        self.changed_line.textChanged.connect(self.__save_enable)

        fixed_re = QRegExp("^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$")
        self.fixed_line = QLineEdit(objectName='fixed_line')
        self.fixed_line.setPlaceholderText("Fixed Line (email field)")
        self.fixed_line.setValidator(
            QRegExpValidator(fixed_re, self.fixed_line))
        self.fixed_line.textChanged.connect(self.__save_enable)
#        self.fixed_line.setFixedHeight(22)

        self.save_button = QPushButton("Save")
        self.save_button.setDisabled(True)

        g_layout = QGridLayout(self.centralWidget)
        g_layout.addWidget(self.line, 0, 1)
        g_layout.addWidget(self.changed_line, 1, 1)
        g_layout.addWidget(self.fixed_line, 2, 1)
        g_layout.addWidget(self.save_button, 3, 1)

# vvv для отладки         
        QtCore.QTimer.singleShot(20 , self.func)
    def func(self):
        print(f'line         = {self.line.size()}') 
        print(f'changed_line = {self.changed_line.size()}') 
        print(f'fixed_line   = {self.fixed_line.size()}\n') 
# ^^^ для отладки     
    
    @pyqtSlot(str)   # <-----vvvv 
    def __save_enable(self,  text):
        self.func()                                        # для отладки 

        if self.changed_line.hasAcceptableInput() and \
            self.fixed_line.hasAcceptableInput():
            self.save_button.setDisabled(False)
        else:
            self.save_button.setDisabled(True)

        self.changed_line.setStyleSheet("""
            #changed_line::focus {{
                border-color: {color};
            }}
        """.format(color="#00ff00" if self.changed_line.hasAcceptableInput() \
                    else "#ff0000"))

        self.fixed_line.setStyleSheet("""
            #fixed_line::focus {{
                border-color: {color};
            }}
        """.format(color="#00ff00" if self.fixed_line.hasAcceptableInput() \
                    else "#ff0000"))

        self.func()                                      # для отладки 
        
QSS = '''
QLineEdit {
    min-height: 22px;         /*  попробуйте изменить высоту */
    max-height: 22px;         /*  попробуйте изменить высоту */
    border-radius: 2px; 
    border: 1px outset;      
    border-color: #B8DFD8;    /*  попробуйте изменить color  */   
}
'''       
        

if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setStyleSheet(QSS)
    app.setStyle("Fusion")              # !!! 
    ex = MainAppInterface()
    ex.show()
    sys.exit(app.exec_())

введите сюда описание изображения

→ Ссылка