Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. Validate QLineEdit before changing focus
Forum Updated to NodeBB v4.3 + New Features

Validate QLineEdit before changing focus

Scheduled Pinned Locked Moved Solved Qt for Python
11 Posts 3 Posters 4.3k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • J Offline
    J Offline
    jdsantos1978
    wrote on last edited by
    #1

    Hello.
    I'm new to PyQt and I'm trying to figure out how can I validate the content of a QLineEdit before focus goes to the next widget.
    I've been working a little with QValidators and they are great to control the input, but that is not the kind of validation I need.
    Let me explain it with an example:

    1. A QLineEdit is expecting a numeric value with the Id of a client.
    2. The user inputs the Id and it is checked, for example, in a database:
    • If it's correct, the client's name is displayed in a QLabel.
    • If it isn't the user is warned and the focus stays in the QLineEdit until he inputs a correct Id.

    How can I achieve that behaviour?
    Thank's in advance.

    1 Reply Last reply
    0
    • J Offline
      J Offline
      jdsantos1978
      wrote on last edited by
      #11

      It's a workaround, but it's working for me just now.

      The idea is using two EventFilters :

      • A normal Filter.
      • A temporary Filter.

      The "normal" filter installs the temporary filter in widget that is going to be focused next when processing the FocusOut event. The only thing that the temporary filter does is to accept all the focus events and stop them.

      The temporary filter removes itself from the widget in the FocusOut event.

      This way, the "normal" filter can do internal and external validations before losing the focus.

      I'm doing some tests and it's working for me. I want to implement it inside a custum widget.

          class DYFieldEventFilter(QtCore.QObject):
      
              class TemporaryFilter(QtCore.QObject):
                  def __init__(self):
                      super(DYField.DYFieldEventFilter.TemporaryFilter, self).__init__()
      
                  def eventFilter(self, widget: 'QObject', event: 'QEvent') -> bool:
                      focus_events = {
                          QtCore.QEvent.Type.FocusAboutToChange: "FocusAboutToChange",
                          QtCore.QEvent.Type.FocusIn: "FocusIn",
                          QtCore.QEvent.Type.FocusOut: "FocusOut",
                      }
      
                      if event.type() in focus_events:
                          event.accept()
      
                          if event.type() == QtCore.QEvent.Type.FocusOut:
                              widget.removeEventFilter(self)
      
                          return True
      
                      return False
      
              def __init__(self, field):
                  super(DYField.DYFieldEventFilter, self).__init__()
                  self.field = field
      
              def eventFilter(self, widget: 'QObject', event: QtCore.QEvent) -> bool:
      
                  if event.type() == QtCore.QEvent.Type.FocusOut:
      
                      # Internal validation.
                      self.field.validate_field()
      
                      # External validation.
                      self.field.validate.emit()
      
                      if not self.field.valid():
                          # If the content of the widget is not valid, focus will be kept on it.
                          # To achieve that, when focusing out, we install a TemporaryEventFilter in the next focused
                          # widget to accept and stop all the Focus Events.
                          # That filter will be removed in the FocusOut event of the TemporaryEventFilter.
                          next_focused = QtWidgets.QApplication.focusWidget()
                          temp_filter = DYField.DYFieldEventFilter.TemporaryFilter()
                          if next_focused:
                              next_focused.installEventFilter(temp_filter)
      
                          event.accept()
      
                          widget.setFocus()
                          widget.selectAll()
      
                          return True
      
                  return False
      
      1 Reply Last reply
      0
      • JonBJ Online
        JonBJ Online
        JonB
        wrote on last edited by JonB
        #2
        • Can't you write your own QValidator::State QValidator::validate(QString &input, int &pos) const override to do what you want? So far as I know, if that returns "invalid" the focus stays in the edit widget. You can probably start from QValidator::State QIntValidator::validate(QString &input, int &pos) const to get the integer validation, and add your own logic to check the value from the database?

        • If, for whatever reason, you don't do it with a validator (e.g. you write your own code on editing finished), you can always put the focus back to the widget on failure in your own code?

        1 Reply Last reply
        2
        • J Offline
          J Offline
          jdsantos1978
          wrote on last edited by
          #3

          Hi JonB. Thanks for your response.
          As long as I know QValidator validates every single key stroke, and I want to validate only when the user has finished typing and the focus is going to the next Widget.
          I've trying writing a slot for the editingFinished signal and it works, but only if the field is edited. I mean, an empty field doesnt fire the editingFinished signal, or if I try something wrong, I can check that it's wrong and I send the focus back to the QLineEdit, but if the user doesn't change the text again, editingFinished wont be fired again.
          I thought that the correct way of handling this was with an EvenFilter capturing, for example, the FocusAboutToChange event, like this:

          class Filter(QtCore.QObject):
              def __init__(self):
                  super(Filter, self).__init__()
          
              def eventFilter(self, object: 'QObject', a1: 'QEvent') -> bool:
                  if a1.type() in [QtCore.QEvent.Type.FocusAboutToChange]:
                      if validation_is_wrong():
                          return True
          
                  return False
          

          and installing the event filter in the LineEdit:

          self._filter = Filter()
                  self.led_nombre_empresa = QtWidgets.QLineEdit()
                  self.led_nombre_empresa.installEventFilter(self._filter)
          

          But it doesn't work as I expected.

          JonBJ 1 Reply Last reply
          0
          • J jdsantos1978

            Hi JonB. Thanks for your response.
            As long as I know QValidator validates every single key stroke, and I want to validate only when the user has finished typing and the focus is going to the next Widget.
            I've trying writing a slot for the editingFinished signal and it works, but only if the field is edited. I mean, an empty field doesnt fire the editingFinished signal, or if I try something wrong, I can check that it's wrong and I send the focus back to the QLineEdit, but if the user doesn't change the text again, editingFinished wont be fired again.
            I thought that the correct way of handling this was with an EvenFilter capturing, for example, the FocusAboutToChange event, like this:

            class Filter(QtCore.QObject):
                def __init__(self):
                    super(Filter, self).__init__()
            
                def eventFilter(self, object: 'QObject', a1: 'QEvent') -> bool:
                    if a1.type() in [QtCore.QEvent.Type.FocusAboutToChange]:
                        if validation_is_wrong():
                            return True
            
                    return False
            

            and installing the event filter in the LineEdit:

            self._filter = Filter()
                    self.led_nombre_empresa = QtWidgets.QLineEdit()
                    self.led_nombre_empresa.installEventFilter(self._filter)
            

            But it doesn't work as I expected.

            JonBJ Online
            JonBJ Online
            JonB
            wrote on last edited by
            #4

            @jdsantos1978 said in Validate QLineEdit before changing focus:

            But it doesn't work as I expected.

            You should explain what you mean by that. Do you receive the focus event?

            1 Reply Last reply
            0
            • J Offline
              J Offline
              jdsantos1978
              wrote on last edited by
              #5

              I've written an small example.

              I shouldn't be able to type in the second QLineEdit until "123" is typed in the first one.

              Acording to doc, eventfilter should return True if i want the event to be stopped:

              from PyQt6 import QtWidgets, QtCore, QtGui
              import sys
              
              class MyWindow(QtWidgets.QWidget):
                  def __init__(self):
                      super(MyWindow, self).__init__()
              
                      self.__add_widgets()
                      self.setGeometry(300,200,300,200)
              
                  def __add_widgets(self):
                      self.edit_id = QtWidgets.QLineEdit()
                      self.lbl_name = QtWidgets.QLabel("Name goes here...")
                      self.edit_other = QtWidgets.QLineEdit("Cannot enter here until 123 in first QLineEdit")
              
                      vbox = QtWidgets.QVBoxLayout()
                      vbox.addWidget(self.edit_id)
                      vbox.addWidget(self.lbl_name)
                      vbox.addWidget(self.edit_other)
              
                      self.setLayout(vbox)
                      self.edit_id.installEventFilter(self)
              
                  def eventFilter(self, object: QtCore.QObject, event: QtCore.QEvent) -> bool:
                      if object == self.edit_id:
                          if event.type() == QtCore.QEvent.Type.FocusAboutToChange:
                              if not self.__validate_id():
                                  print ("Not good :(")
                                  self.edit_id.setFocus()
                                  return True
                              else:
                                  print ("Good :)")
                                  return False
              
                      return super().eventFilter(object, event)
              
              
                  def __validate_id(self) -> bool:
                      customer_id = self.edit_id.text()
                      if customer_id == "123":
                          self.lbl_name.setText("Pepito Pérez")
                          return True
                      else:
                          self.lbl_name.setText("Unknown Customer")
                          return False
              
              app = QtWidgets.QApplication(sys.argv)
              window = MyWindow()
              window.show()
              sys.exit(app.exec())
              
              
              1 Reply Last reply
              0
              • SGaistS Offline
                SGaistS Offline
                SGaist
                Lifetime Qt Champion
                wrote on last edited by SGaist
                #6

                Hi,

                One design question: why allow your user to access anything that requires that id to be valid ?

                It rather looks that there should be an explicit check like when you log into a database.

                Interested in AI ? www.idiap.ch
                Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                1 Reply Last reply
                0
                • J Offline
                  J Offline
                  jdsantos1978
                  wrote on last edited by
                  #7

                  Hi.

                  I know that most of the validation of a form can be done in a "Ok" Button, but think in the following example (it's a java app):

                  example.png

                  In all the "Cta." fields:

                  • I want the user to write a valid Id. That can be validated at the OK. Button.
                  • I want the user to write an Id (not empty). That can be done at the Ok Button.
                  • I want to give the user the posibility of creating a new "Cta." if the id he has entered doesn't exist in the database.

                  This last behaviour could be achieved handling the FocusOut signal but:

                  • I would probably have interferences handling FocusOut of all those fields.
                  • I wouldn't want any validation if the Search Button or Cancel Button where pressed.

                  It would be great to have a "validate" signal in the widgets before the focus signals, and a causes_validation (True / False) flag in the widgets to handle this.

                  Anyway, I've figured out a way of achieving this handling the Focus Events. I have to work it out a little and I'll post it here.

                  1 Reply Last reply
                  0
                  • SGaistS Offline
                    SGaistS Offline
                    SGaist
                    Lifetime Qt Champion
                    wrote on last edited by
                    #8

                    Maybe a custom QValidator ?

                    Interested in AI ? www.idiap.ch
                    Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                    1 Reply Last reply
                    0
                    • J Offline
                      J Offline
                      jdsantos1978
                      wrote on last edited by
                      #9

                      Thanks SGaist.

                      As I said before, QValidator validates every single stroke, and I want a final validation before passing the focus.

                      Probably the best approach is to write and intall an EvenFilter that handles the QtCore.QEvent.Type.FocusAboutToChange event:

                      "When the filter object’s eventFilter() implementation is called, it can accept or reject the event, and allow or deny further processing of the event. If all the event filters allow further processing of an event (by each returning false), the event is sent to the target object itself. If one of them stops processing (by returning true), the target and any later event filters do not get to see the event at all."

                      The problem is that although I stop the FocusAboutToChange event, even the FocusOut event in the widget, it doesn't stop firing those same events in the next Widget, and that's precisely what I want to avoid.

                      1 Reply Last reply
                      0
                      • SGaistS Offline
                        SGaistS Offline
                        SGaist
                        Lifetime Qt Champion
                        wrote on last edited by
                        #10

                        Can you show your event filter ?

                        Interested in AI ? www.idiap.ch
                        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                        1 Reply Last reply
                        0
                        • J Offline
                          J Offline
                          jdsantos1978
                          wrote on last edited by
                          #11

                          It's a workaround, but it's working for me just now.

                          The idea is using two EventFilters :

                          • A normal Filter.
                          • A temporary Filter.

                          The "normal" filter installs the temporary filter in widget that is going to be focused next when processing the FocusOut event. The only thing that the temporary filter does is to accept all the focus events and stop them.

                          The temporary filter removes itself from the widget in the FocusOut event.

                          This way, the "normal" filter can do internal and external validations before losing the focus.

                          I'm doing some tests and it's working for me. I want to implement it inside a custum widget.

                              class DYFieldEventFilter(QtCore.QObject):
                          
                                  class TemporaryFilter(QtCore.QObject):
                                      def __init__(self):
                                          super(DYField.DYFieldEventFilter.TemporaryFilter, self).__init__()
                          
                                      def eventFilter(self, widget: 'QObject', event: 'QEvent') -> bool:
                                          focus_events = {
                                              QtCore.QEvent.Type.FocusAboutToChange: "FocusAboutToChange",
                                              QtCore.QEvent.Type.FocusIn: "FocusIn",
                                              QtCore.QEvent.Type.FocusOut: "FocusOut",
                                          }
                          
                                          if event.type() in focus_events:
                                              event.accept()
                          
                                              if event.type() == QtCore.QEvent.Type.FocusOut:
                                                  widget.removeEventFilter(self)
                          
                                              return True
                          
                                          return False
                          
                                  def __init__(self, field):
                                      super(DYField.DYFieldEventFilter, self).__init__()
                                      self.field = field
                          
                                  def eventFilter(self, widget: 'QObject', event: QtCore.QEvent) -> bool:
                          
                                      if event.type() == QtCore.QEvent.Type.FocusOut:
                          
                                          # Internal validation.
                                          self.field.validate_field()
                          
                                          # External validation.
                                          self.field.validate.emit()
                          
                                          if not self.field.valid():
                                              # If the content of the widget is not valid, focus will be kept on it.
                                              # To achieve that, when focusing out, we install a TemporaryEventFilter in the next focused
                                              # widget to accept and stop all the Focus Events.
                                              # That filter will be removed in the FocusOut event of the TemporaryEventFilter.
                                              next_focused = QtWidgets.QApplication.focusWidget()
                                              temp_filter = DYField.DYFieldEventFilter.TemporaryFilter()
                                              if next_focused:
                                                  next_focused.installEventFilter(temp_filter)
                          
                                              event.accept()
                          
                                              widget.setFocus()
                                              widget.selectAll()
                          
                                              return True
                          
                                      return False
                          
                          1 Reply Last reply
                          0

                          • Login

                          • Login or register to search.
                          • First post
                            Last post
                          0
                          • Categories
                          • Recent
                          • Tags
                          • Popular
                          • Users
                          • Groups
                          • Search
                          • Get Qt Extensions
                          • Unsolved