pythonpyqtpyqt5qlineediteventfilter

How to Allow Spaces in Amount Field in PyQt5 QLineEdit While Restricting Other Non-Numeric Inputs


I am working on an expense manager application using PyQt5, and I need to allow spaces in the amount field while restricting other non-numeric inputs. My goal is to enable users to input multiple amounts separated by spaces. I have implemented an event filter to restrict inputs, but I am having trouble allowing spaces along with digits and decimal points.

Here is the relevant part of my code:

class ExpenseManager(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setup_ui()
        self.install_event_filters()

    def setup_ui(self):
        # Setup UI components (simplified for brevity)
        self.amount = QtWidgets.QLineEdit(self)
        self.itemName = QtWidgets.QLineEdit(self)
        self.date = QtWidgets.QDateEdit(self)
        self.note = QtWidgets.QTextEdit(self)
        self.accountDropdown = QtWidgets.QComboBox(self)
        self.statusBar = QtWidgets.QStatusBar(self)
        self.setStatusBar(self.statusBar)
        # Additional UI setup...

    def add_expense(self):
        amount_text = self.amount.text()
        item = self.itemName.text().upper()
        date = self.date.date().toString('yyyy-MM-dd')
        time = datetime.now().strftime("%H:%M:%S")
        note = self.note.toPlainText().upper()
        account = self.accountDropdown.currentText()

        if amount_text and item and account:
            amounts = amount_text.split()
            try:
                # Check if all amounts are valid numbers
                amounts = [float(amount) for amount in amounts]
                
                self.c.execute("SELECT id, name FROM accounts WHERE name = ?", (account,))
                account_row = self.c.fetchone()

                if account_row:
                    account_id = account_row[0]
                    account_on = account_row[1]

                    for amount in amounts:
                        self.c.execute("INSERT INTO expenses (amount, item, date, time, note, account_on, account_id) VALUES (?, ?, ?, ?, ?, ?, ?)", 
                                    (amount, item, date, time, note, account_on, account_id))
                    
                    self.conn.commit()
                    if self.completer_note:
                        self.completer_note.model().setStringList(self.get_notes())
                    self.amount.clear()
                    self.itemName.clear()
                    self.date.setDate(QtCore.QDate.currentDate())
                    self.note.clear()
                    self.statusBar.showMessage("Expense(s) added successfully", 3000)
                    self.amount.setFocus()
                else:
                    self.statusBar.showMessage("Selected account does not exist", 3000)
            except ValueError:
                self.statusBar.showMessage("Please enter valid amount(s)", 3000)
        else:
            self.statusBar.showMessage("Please fill all mandatory(*) fields", 3000)

        self.completer.model().setStringList(self.get_items())
        self.completer_note.model().setStringList(self.get_notes())  

    def install_event_filters(self):
        self.amount.installEventFilter(self)
        self.itemName.installEventFilter(self)
        self.note.installEventFilter(self)
        self.date.lineEdit().installEventFilter(self)

    def eventFilter(self, obj, event):
        if event.type() == QtCore.QEvent.KeyPress:
            if event.key() == QtCore.Qt.Key_Return or event.key() == QtCore.Qt.Key_Enter:
                if obj == self.date.lineEdit():
                    self.add_expense()
                    return True
                elif obj == self.note:
                    if event.modifiers() & QtCore.Qt.ControlModifier:
                        return False
                    else:
                        self.add_expense()
                        return True
                elif obj == self.itemName:
                    if event.key() == QtCore.Qt.Key_Return or event.key() == QtCore.Qt.Key_Enter:
                        if self.itemName.completer().popup().isVisible():
                            return False
                        else:
                            self.add_expense()
                            return True
            if isinstance(obj, QtWidgets.QLineEdit) or isinstance(obj, QtWidgets.QTextEdit):
                if event.text().isalpha():
                    obj.insert(event.text().upper())
                    return True
        return super().eventFilter(obj, event)

In the eventFilter I tried to add this below line but It didn't worked:

if event.text() == ' ' or event.text().isalnum():
    obj.insert(event.text())
    return True

With this code, I am attempting to allow digits, spaces, and decimal points in the amount field. However, the code is not working as expected. The amount field still does not accept spaces.

What changes should I make to ensure that the amount field allows spaces while restricting other non-numeric inputs?

Any help would be appreciated. Thank you!


Solution

  • I have created a custom validator which worked for my conditions that it should not have any characters or special characters and allow only numbers and space. This worked well for me. Below is the customer validator code

    class AmountValidator(QValidator):
        def validate(self, input, pos):
            # Remove spaces to check if the rest of the input is a valid number
            stripped_input = input.replace(" ", "")
            if stripped_input == "":
                return (QValidator.Intermediate, input, pos)
            try:
                float(stripped_input)
                return (QValidator.Acceptable, input, pos)
            except ValueError:
                return (QValidator.Invalid, input, pos)
    
        def fixup(self, input):
            return ''.join(filter(lambda x: x.isdigit() or x.isspace(), input))