1

I would like to use QLineEdit to get user input for values. I want to limit the input between a range so I should use QDoubleValidator. I would like it to work such that if they go over the allowed value, it sets the text to the top() value and if they go under then it sets it to the bottom() value.

I looked at using the textChanged, returnPressed, and inputRejected signals. The reason I am having trouble is that once I set the validator with the range, returnPressed will not enter the check_validator function which they mention here. Then, I thought maybe I could catch it with the input rejected signal but for some reason that doesn't seem to work either. Here is some code:

class LineEdit(QLineEdit):
    def __init__(self, text, parent=None):
        super(LineEdit, self).__init__(parent)
        self.validator = QDouble Validator()
        self.setValidator(self.validator)
        self.text = text
        self.textChanged.connect(self.new_text)
        self.returnPressed(self.check_validator)

    def new_text(self, text):
        self.ntext = text

    def check validator:
        try:
            if float(self.ntext) > self.validator.top():
                self.text = str(self.validator.top()
            if float(self.ntext) < self.validator.bottom():
                self.text = str(self.validator.bottom()
            else:self.text = self.ntext
            self.setText(self.text)
         except:
            mssg = QMessageBox.about(self, "Error", "Input can only be a number")
            mssg.exec()
            self.setText(self.text)

     def valRange(self, x1, x2):
         self.validator.setRange(x1, x2)

I also get an attribute error saying 'noneType' object has no attribute 'exec' when the window does pop up. I think I am missing a step on how to close that window properly.

Amanda.py
  • 113
  • 10
  • Is there a specific reason for which you're not using a simple QDoubleSpinBox? – musicamante Mar 01 '21 at 23:42
  • I didn't know about this class before, looks like it could have worked but the solution below worked so I am going to leave it. Thanks for the suggestion! – Amanda.py Mar 02 '21 at 00:55
  • I suggest you to read the [widget classes](https://doc.qt.io/qt-5/widget-classes.html#basic-widget-classes) documentation, it contains the full list of available widgets, including "variations" of base classes. I'd also suggest you to always try to use objects for what they're intended, unless very specific and peculiar requirements are needed. QLineEdit is intended for *string* input (even if numeric, like an IP address), while objects that should be treated as *actual* numbers should always use a QAbstractSpinBox subclass, as it provides a more suitable control. – musicamante Mar 02 '21 at 07:50

1 Answers1

2

You could reimplement keyPressEvent to catch return presses when hasAcceptableInput is false. There is also a drawback to overriding the text property, now any programatic calls to setText will not update the text of the QLineEdit. There's no reason to do it.

class LineEdit(QLineEdit):
    def __init__(self, *args, **kwargs):
        super(LineEdit, self).__init__(*args, **kwargs)
        self.validator = QDoubleValidator(0, 10, 4, notation=QDoubleValidator.StandardNotation)
        self.setValidator(self.validator)
        self.textChanged.connect(self.new_text)
        self.returnPressed.connect(self.check_validator)
        self.ntext = None

    def keyPressEvent(self, event):
        super().keyPressEvent(event)
        if event.key() == Qt.Key_Return and not self.hasAcceptableInput():
            self.check_validator()

    def new_text(self, text):
        if self.hasAcceptableInput():
            self.ntext = text

    def check_validator(self):
        try:
            if float(self.text()) > self.validator.top():
                self.setText(str(self.validator.top()))
            elif float(self.text()) < self.validator.bottom():
                self.setText(str(self.validator.bottom()))
        except:
            mssg = QMessageBox.about(self, "Error", "Input can only be a number")
            self.setText(self.ntext)
alec
  • 5,799
  • 1
  • 7
  • 20
  • Awesome, thank you. The only thing is that if the input is invalid, I would like for it to return to the previously acceptable value. – Amanda.py Mar 01 '21 at 22:54
  • It's also odd (but not really a problem...) that you can't type more than one character. Also, thank you for explaining that about setText. I was actually wondering about that. now the original values I put into the initialization of these actually shows up as well. – Amanda.py Mar 01 '21 at 23:10
  • I get why you had `self.ntext` now. I've edited the answer – alec Mar 01 '21 at 23:10
  • In the answer I put a range of 0-10, 4 decimal limit, and standard notation on the validator, you can't type more than one character?? – alec Mar 01 '21 at 23:12
  • I just mean that you cant type any regular letters, but that's ok it doesn't matter and it makes sense why you can't. – Amanda.py Mar 02 '21 at 00:54