8

This is a two part question about input validation with one specific and another more general component.

The specific:

While researching the topic, I found THIS on Regular Expressions. I realize that the code in this post is using PyQt4. However I wanted to get this working with PyQt5, since I had already started my project with it. (Obviously blindly - I can only find C++ documentation for it)

This is what I tried:

# somewhere above:     
self.le_input = QtWidgets.QLineEdit()

# at some point validate_input gets called:
# more on that in the second part of this question

def validate_input(self):
    reg_ex = QtCore.QRegExp(""[0-9]+.?[0-9]{,2}"")
    input_validator = QtGui.QRegExpValidator(reg_ex, self.le_input.text())
    self.le_input.setValidator(input_validator)

When I run the code I get the following Error:

QRegExpValidator(parent: QObject = None): argument 1 has unexpected type 'QRegExp' QRegExpValidator(QRegExp, parent: QObject = None): argument 2 has unexpected type 'str'

Aren't these exactly the required arguments? Does anyone know how to get this working?


The general:

What is an effective way to implement live validation with PyQt in Python?

At the moment I would use:

self.le_input.textChanged.connect(self.validate_input)

This does work, but as soon as I try to connect two QtLineEdits, that affect each other, to the same slot, things stop working because "textChanged" gets called by both of them at the same time.

Use case: Two input fields: Amount before TAX and Amount after TAX - and whichever you enter automatically fills the other one while typing.

First validation, then calculation, then output to the other field.

Many thanks in advance! Any help is highly appreciated!

michaelh
  • 237
  • 1
  • 4
  • 8
  • I don't understand what you find puzzling about the error message. It very cleary tells you what the problem is. And if that is not clear enough, the Qt docs will spell it out for you. – ekhumoro Dec 11 '16 at 16:19
  • Yes, it tells me what it is NOT expecting. I do understand that very well. However I can't find what it IS expecting instead. Also, in the linked example and in all documentation I could find it, the arguments I used seem to be the right ones. – michaelh Dec 11 '16 at 16:34
  • It shows you the exact types of each argument, neither of which is a `str`. Also, the second argument is a keyword which gives you an even stronger hint. – ekhumoro Dec 11 '16 at 16:49

3 Answers3

18

You can set different validators for different QLineEdit objects.

QRegExpValidator has two constructors:

QRegExpValidator(parent: QObject = None)
QRegExpValidator(QRegExp, parent: QObject = None)

so, you should change your code to:

reg_ex = QRegExp("[0-9]+.?[0-9]{,2}")
input_validator = QRegExpValidator(reg_ex, self.le_input)
self.le_input.setValidator(input_validator)

Once you set validator for QLineEdit, there is no need to use

self.le_input.textChanged.connect(self.validate_input)

Just delete it. And that should work fine.

If you want to find documentation about PyQt5, you can simple use this in your console:

pydoc3 PyQt5.QtGui.QRegExpValidator

But pydoc can only show you little information, so you should use C++ version documation as well.

Finally, an example about this:

from PyQt5.Qt import QApplication
from PyQt5.QtCore import QRegExp
from PyQt5.QtGui import QRegExpValidator
from PyQt5.QtWidgets import QWidget, QLineEdit

import sys

class MyWidget(QWidget):
    def __init__(self, parent=None):
        super(QWidget, self).__init__(parent)
        self.le_input = QLineEdit(self)

        reg_ex = QRegExp("[0-9]+.?[0-9]{,2}")
        input_validator = QRegExpValidator(reg_ex, self.le_input)
        self.le_input.setValidator(input_validator)

if __name__ == '__main__':
    a = QApplication(sys.argv)

    w = MyWidget()
    w.show()

    a.exec()
September
  • 416
  • 4
  • 11
  • Thank you! That helped me to figure it out! Actually there was nothing wrong with my code. It was just in the wrong place. I had placed it inside a function. Perfect! Thanks! – michaelh Dec 11 '16 at 17:20
  • Any ideas on the last part of my question (which really was just an aside, but still would be interesting)? How could I do live calculations with the two fields and display the result in the other? – michaelh Dec 11 '16 at 17:22
  • You can simply use two slot functions. For example: You have a function A. In this function you should read input from one QLineEdit Q1, and put result into another QLineEdit Q2. Then just connect this function A to the textChanged signal of Q1. And do the same to Q2 – September Dec 11 '16 at 17:29
  • Won't the textChanged signal be sent every time the result is output to a LineEdit? - At least that is what I think is happening to me. So, it will go back and forth until something crashes as soon as you touch the thing. – michaelh Dec 11 '16 at 17:39
  • 1
    oh. You are right. It will happen. Then just use textEdit signal. This signal is emitted whenever the text is edited. Unlike textChanged(), this signal is not emitted when the text is changed programmatically, for example, by calling setText(). – September Dec 11 '16 at 17:44
  • 1
    textEdited() signal. Sorry for themistake. – September Dec 11 '16 at 17:46
0

Either methods seems to work well with input validation except for textChanged() signal when two lineEdits which affects each other when using the same slot function .However I have a comment on QRegExpValidator, it may scare the user to think their keyboard is not working since trying to type anything not allowed shows nothing(no interaction whatsoever) while using textChanged() signal can allow some interaction using some statements in the slot function. Any thoughts on my view above? How can I address this? @eyllanesc

I have made my conclusion by trying this code example shared above and many other examples I may not be able to share:

from PyQt5.Qt import QApplication
from PyQt5.QtCore import QRegExp
from PyQt5.QtGui import QRegExpValidator
from PyQt5.QtWidgets import QWidget, QLineEdit

import sys

class MyWidget(QWidget):
    def __init__(self, parent=None):
        super(QWidget, self).__init__(parent)
        self.le_input = QLineEdit(self)

        reg_ex = QRegExp("[0-9]+.?[0-9]{,2}")
        input_validator = QRegExpValidator(reg_ex, self.le_input)
        self.le_input.setValidator(input_validator)

if __name__ == '__main__':
    a = QApplication(sys.argv)

    w = MyWidget()
    w.show()

    a.exec()
kelvinmacharia254
  • 117
  • 1
  • 3
  • 12
0
# for PySide2
from PySide2 import QtCore, QtGui, QtWidgets

from PySide2.QtWidgets import *

from PySide2.QtGui import QFont, QColor, QRegExpValidator

from PySide2.QtCore import Qt, QRegExp


class MyWidget(QWidget):

    def __init__(self):

        super().__init__()


        def validator(valid):
            reg_ex = QRegExp(valid)
            vld = QRegExpValidator(reg_ex, self.le_input)
            return vld

        self.le_input = QLineEdit(self)
        val = "[0-9]+.?[0-9]{,2}"
        self.le_input.setValidator(validator(val))


a = QApplication([])

w = MyWidget()

w.show()

a.exec_()
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 25 '23 at 20:55