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_())