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. Sub-classing QlineEdit
Forum Updated to NodeBB v4.3 + New Features

Sub-classing QlineEdit

Scheduled Pinned Locked Moved Solved Qt for Python
4 Posts 3 Posters 1.0k 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.
  • B Offline
    B Offline
    bkvldr
    wrote on last edited by
    #1

    A while ago - in another thread - @JKSH suggested I could subclass QLineEdit for a specific purpose.

    I decided to try, but I can't quite get it to work. The code is posted below.
    In this case I don't use the model and mapper from Qt. I run a SQL query and use the value property to get my data into the object. That works.

    But when I type into the widget, the value does not change. The text property does change, but not value. This I don't understand.

    How does my typing change the object? To me, it looks like my class definition is ignored, and QLineEdit.setText() is used.

    from PyQt5 import QtWidgets,QtGui, QtCore
    import locale
    from PyQt5.QtCore import pyqtProperty
    #*******************************************************************************
    #
    #   A subclass of QLineEdit which can be used to display decimal numbers only.
    #   The numbers are displayed according to locale and always with a
    #   fixed number of decimals
    #   A validator is in place to ensure that only valid flots are entered by User
    #
    #*******************************************************************************
    class DblEdit (QtWidgets.QLineEdit):
        def __init__(self, parent):
            super().__init__(parent)
            self._decChar = QtCore.QLocale().decimalPoint()
            self._decimals = 2
            self._minimum = 0
            self._maximum = 9999
            self._dblVal = QtGui.QDoubleValidator(self)
            self._dblVal.setNotation(QtGui.QDoubleValidator.StandardNotation)
            self._dblVal.setRange(self._minimum, self._maximum, self._decimals)
            self.setValidator(self._dblVal)
            self._value = 0.0
    
    #-------------------------------------------------------------------------------
    #   Private version of setText
    #-------------------------------------------------------------------------------
        def _setText(self, text):
            super().setText(text)
    
    #-------------------------------------------------------------------------------
    #   Private method to pad text with 0 after decimals if necessary
    #-------------------------------------------------------------------------------
        def _fillDecimals(self, lacks):
            if lacks <=  0:
                return
            else:
                self._setText(self.text() + '0')
                self._fillDecimals(lacks-1)
    
    #-------------------------------------------------------------------------------
    #   setText not to be used
    #-------------------------------------------------------------------------------
        def setText(self, text):
            raise Exception('Method setText not available. Set value property')
    
    #-------------------------------------------------------------------------------
    #   getter for _value
    #-------------------------------------------------------------------------------
        @pyqtProperty(float,user=True)
        def value(self):
            print ('You are in value')
            return self._value
    
    #-------------------------------------------------------------------------------
    #   Setter for value should be used instead of setText .
    #   Supply a float as argument
    #-------------------------------------------------------------------------------
        @value.setter
        def value(self, d):
            if str(d).strip(' ')  == '' or d == None:
                self._value= None
                self._setText = ''
            else:
                self._value = round(float(d), self._decimals)
                self._setText(locale.str(self._value).strip())
                #find position of decimal characer
                pos = self.text().find(self._decChar,0,len(self.text()))
    
                if pos == -1:
                     self._setText(self.text()+self._decChar)
                     lacksDecimals = self._decimals
                else:
                    lacksDecimals = self._decimals - ( len(self.text())-(pos+1))
                self._fillDecimals(lacksDecimals)
            print ('You are in setValue', self.text(), self.value)
    
    #-------------------------------------------------------------------------------
    #   Method to specify how many decimals are wanted
    #-------------------------------------------------------------------------------
        def setDecimals(self, i):
            self._decimals = int(i)
    
    #-------------------------------------------------------------------------------
    #   Method to set range of values for validator
    #-------------------------------------------------------------------------------
        def setRange(self, min, max):
            self._minimum = min
            self._maximum = max
    
    #-------------------------------------------------------------------------------
    #  some getters
    #-------------------------------------------------------------------------------
        def decimals(self):
            return self._decimals
    
        def minimum(self):
            return self._minimum
    
        def maximum(self):
            return self._maximum
    
    JonBJ 1 Reply Last reply
    0
    • B bkvldr

      A while ago - in another thread - @JKSH suggested I could subclass QLineEdit for a specific purpose.

      I decided to try, but I can't quite get it to work. The code is posted below.
      In this case I don't use the model and mapper from Qt. I run a SQL query and use the value property to get my data into the object. That works.

      But when I type into the widget, the value does not change. The text property does change, but not value. This I don't understand.

      How does my typing change the object? To me, it looks like my class definition is ignored, and QLineEdit.setText() is used.

      from PyQt5 import QtWidgets,QtGui, QtCore
      import locale
      from PyQt5.QtCore import pyqtProperty
      #*******************************************************************************
      #
      #   A subclass of QLineEdit which can be used to display decimal numbers only.
      #   The numbers are displayed according to locale and always with a
      #   fixed number of decimals
      #   A validator is in place to ensure that only valid flots are entered by User
      #
      #*******************************************************************************
      class DblEdit (QtWidgets.QLineEdit):
          def __init__(self, parent):
              super().__init__(parent)
              self._decChar = QtCore.QLocale().decimalPoint()
              self._decimals = 2
              self._minimum = 0
              self._maximum = 9999
              self._dblVal = QtGui.QDoubleValidator(self)
              self._dblVal.setNotation(QtGui.QDoubleValidator.StandardNotation)
              self._dblVal.setRange(self._minimum, self._maximum, self._decimals)
              self.setValidator(self._dblVal)
              self._value = 0.0
      
      #-------------------------------------------------------------------------------
      #   Private version of setText
      #-------------------------------------------------------------------------------
          def _setText(self, text):
              super().setText(text)
      
      #-------------------------------------------------------------------------------
      #   Private method to pad text with 0 after decimals if necessary
      #-------------------------------------------------------------------------------
          def _fillDecimals(self, lacks):
              if lacks <=  0:
                  return
              else:
                  self._setText(self.text() + '0')
                  self._fillDecimals(lacks-1)
      
      #-------------------------------------------------------------------------------
      #   setText not to be used
      #-------------------------------------------------------------------------------
          def setText(self, text):
              raise Exception('Method setText not available. Set value property')
      
      #-------------------------------------------------------------------------------
      #   getter for _value
      #-------------------------------------------------------------------------------
          @pyqtProperty(float,user=True)
          def value(self):
              print ('You are in value')
              return self._value
      
      #-------------------------------------------------------------------------------
      #   Setter for value should be used instead of setText .
      #   Supply a float as argument
      #-------------------------------------------------------------------------------
          @value.setter
          def value(self, d):
              if str(d).strip(' ')  == '' or d == None:
                  self._value= None
                  self._setText = ''
              else:
                  self._value = round(float(d), self._decimals)
                  self._setText(locale.str(self._value).strip())
                  #find position of decimal characer
                  pos = self.text().find(self._decChar,0,len(self.text()))
      
                  if pos == -1:
                       self._setText(self.text()+self._decChar)
                       lacksDecimals = self._decimals
                  else:
                      lacksDecimals = self._decimals - ( len(self.text())-(pos+1))
                  self._fillDecimals(lacksDecimals)
              print ('You are in setValue', self.text(), self.value)
      
      #-------------------------------------------------------------------------------
      #   Method to specify how many decimals are wanted
      #-------------------------------------------------------------------------------
          def setDecimals(self, i):
              self._decimals = int(i)
      
      #-------------------------------------------------------------------------------
      #   Method to set range of values for validator
      #-------------------------------------------------------------------------------
          def setRange(self, min, max):
              self._minimum = min
              self._maximum = max
      
      #-------------------------------------------------------------------------------
      #  some getters
      #-------------------------------------------------------------------------------
          def decimals(self):
              return self._decimals
      
          def minimum(self):
              return self._minimum
      
          def maximum(self):
              return self._maximum
      
      JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by JonB
      #2

      @bkvldr
      I'm not sure, so please take my remark with a pinch of salt, but: internal Qt code will feel free to call QLineEdit::setText(). I am then unsure whether it will resolve to the base QLineEdit::setText(), or whether it will call your override which will then raise [I would put a print in there, just in case it's getting called form somewhere with exceptions trapped], but either way that does not sound your desired behaviour? I don't think you can afford to try to prevent setText() being called on your subclassed widget.

      if str(d).strip(' ') == '' or d == None: You're supposed to write that with d is None. Probably not relevant to your question. Why you choose to first str() it and then test for None I don't know, to me it would be cleaner to reverse those tests, but up to you :)

      1 Reply Last reply
      2
      • B Offline
        B Offline
        bkvldr
        wrote on last edited by
        #3

        @Denni-0

        I finally got some time to play with this. In keyPressEvent, I set self._value. This keeps self.text() and self._value in sync as I type. I am still not quite comfortable, because I don't know how my typing enters into my class.
        If you look at my original post, you'll see that it cannot be through the DblEdit.setText() .

        from PyQt5.QtCore      import pyqtSlot
        from PyQt5.QtGui       import QDoubleValidator
        from PyQt5.QtWidgets   import QApplication, QWidget, QLineEdit, QVBoxLayout
        import locale
        class DblEdit (QLineEdit):
            def __init__(self, Id):
                QLineEdit.__init__(self)
                self.Id = Id
                self.Decimals = 2
                self.Minimum = 0
                self.Maximum = 9999
                self._value = 0.0
                self.DblVal = QDoubleValidator(self)
                self.DblVal.setNotation(QDoubleValidator.StandardNotation)
                self.DblVal.setRange(self.Minimum, self.Maximum, self.Decimals)
                self.setValidator(self.DblVal)
        
            @pyqtSlot(object)
            def keyPressEvent(self, event):
                print('Captured Key Press Event in',self.Id,') ',event.text())
                QLineEdit.keyPressEvent(self, event)
                if self.text() == '':
                    self._value = 0.0
                else:
                    self._value = locale.atof(self.text())
        
        class MainWindow(QWidget):
            def __init__(self):
                QWidget.__init__(self)
                self.setWindowTitle('Muck With It')
                Top=300; Left=700; Width=250; Hight=75
                self.setGeometry(Left, Top, Width, Hight)
        
                self.DblOne = DblEdit(1)
                self.DblTwo = DblEdit(2)
        
                VBox = QVBoxLayout()
                VBox.addWidget(self.DblOne)
                VBox.addWidget(self.DblTwo)
                VBox.addStretch(1)
        
                self.setLayout(VBox)
        
        if __name__ == "__main__":
            MainEventHandler = QApplication([])
            locale.setlocale(locale.LC_ALL, '')
            application = MainWindow()
            application.show()
            MainEventHandler.exec()
        
        1 Reply Last reply
        0
        • B Offline
          B Offline
          bkvldr
          wrote on last edited by
          #4

          @Denni-0
          Is setting self._value in the keyPressEvent bade practise in any way?

          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