I am trying to build a lineEdit that checks for non-ascii characters.
I tested the RegEx using regex101 and it worked as expected.[:ascii:]
seems to be a PCRE and should therefore work with the QtRegExp, right?
However, my QValidator subclass always returns QValidator.State.Invalid
(unless the lineEdit is empty when it returns QValidator.State.Intermediate
) even when the the lineEdit clearly contains ASCII chars only.
Here is the code for the validator:
class ASCIIValidator (QtGui.QRegExpValidator):
def __init__(self):
super(ASCIIValidator, self).__init__()
self.ASCII_REGEXP = QtCore.QRegExp()
self.ASCII_REGEXP.setPattern(r'^[[:ascii:]]+$')
self.ASCII_REGEXP.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.setRegExp(self.ASCII_REGEXP)
And here is my custom lineEdit:
class ULineEdit(QtWidgets.QLineEdit):
focusChange = QtCore.Signal(bool)
validated = QtCore.Signal(QtGui.QValidator.State)
"""Custom lineedit"""
def __init__(self,
defaultText: str = "",
validators: list = [],
completer: QtWidgets.QCompleter = None):
super(ULineEdit, self).__init__()
self.setText(defaultText)
if completer is not None and isinstance(completer, QtWidgets.QCompleter):
self.setCompleter(completer)
self.validators = validators
self.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
# Signals
self.textChanged.connect(self.validateText)
def addValidator(self, validator: QtGui.QValidator):
if isinstance(validator, QtGui.QValidator):
self.validators.append(validator)
return
elif isinstance(validator, list):
self.validators.extend(validator)
return
def validateText(self):
"""Check validators and set the style"""
if len(self.validators) > 0:
for val in self.validators:
testResult = val.validate(self.text(), 0)[0] #validate() returns a tuple
invalidTests = []
intermedTests = []
acceptedTests = []
print(testResult)
if testResult == QtGui.QValidator.Invalid:
invalidTests.append(testResult)
elif testResult == QtGui.QValidator.Intermediate:
intermedTests.append(testResult)
elif testResult == QtGui.QValidator.Acceptable:
acceptedTests.append(testResult)
if len(invalidTests) > 0:
self.setStyleSheet(INVALID_STYLESHEET)
self.validated.emit(QtGui.QValidator.Invalid)
return QtGui.QValidator.Invalid
if len(intermedTests) > 0:
self.setStyleSheet(LINEEDIT_STYLESHEET)
self.validated.emit(QtGui.QValidator.Intermediate)
return QtGui.QValidator.Intermediate
if len(acceptedTests) > 0:
self.setStyleSheet(VALID_STYLESHEET)
self.validated.emit(QtGui.QValidator.Acceptable)
return QtGui.QValidator.Acceptable
One option is to use the range of ASCII characters to validate whether or not it is as pointed out in this answer:
import sys
from PySide2 import QtCore, QtGui, QtWidgets
class ASCIIValidator(QtGui.QRegExpValidator):
def __init__(self, parent=None):
super().__init__(parent)
pattern = r"[^\x00-\x7F]+$"
regex = QtCore.QRegExp(pattern)
regex.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.setRegExp(regex)
class JoinValidator(QtGui.QValidator):
def __init__(self, parent=None):
super().__init__(parent)
self._validators = []
@property
def validators(self):
return self._validators
def add_validator(self, validator):
self.validators.append(validator)
def validate(self, _input, pos):
texts = []
positions = []
final_state = QtGui.QValidator.Acceptable
for validator in self.validators:
state, new_input, new_pos = validator.validate(_input, pos)
texts.append(new_input)
positions.append(new_pos)
if state == QtGui.QValidator.Invalid:
final_state = QtGui.QValidator.Invalid
elif (
state == QtGui.QValidator.Intermediate
and final_state != QtGui.QValidator.Invalid
):
final_state = QtGui.QValidator.Intermediate
new_pos = min(positions)
new_input = min(texts, key=len)
return final_state, new_input, new_pos
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.lineedit = QtWidgets.QLineEdit()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.lineedit)
self.validator = JoinValidator()
self.validator.add_validator(ASCIIValidator())
self.lineedit.textChanged.connect(self.handle_text_changed)
def handle_text_changed(self):
state, _, _ = self.validator.validate(
self.lineedit.text(), self.lineedit.cursorPosition()
)
print(state)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())