Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Solved Completion problem with QLineEdit and QItemDelegate

    General and Desktop
    1
    2
    871
    Loading More Posts
    • 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.
    • M
      Matthieu 0 last edited by

      Hello,

      I'm trying to implement the QCompleter example we can find in the widgets/tools/customcompleter sources.
      What I want is when I'm typing some text, if there is some matches, the first completion line available is selected so if I hit the return key (and only if I hit it), the completion is done.

      But in my code here, the line is never selected. I think my problem is located in the keyPressEvent. It is a minimal example, written in pyqt5.

      Any help is very appreciated :)

      import sys
      
      from PyQt5.QtCore import QAbstractTableModel, Qt, QVariant
      from PyQt5.QtWidgets import (QApplication, QCompleter, QItemDelegate,
                                   QLineEdit, QMainWindow, QTableView)
      
      
      class MyLineEdit(QLineEdit):
      
          def __init__(self, parent=None, completer=None):
              super().__init__(parent)
              if completer:
                  self.setCompleter(completer)
      
          def setCompleter(self, completer):
              if completer:
                  completer.setWidget(self)
                  completer.setCompletionMode(QCompleter.PopupCompletion)
                  completer.setCaseSensitivity(Qt.CaseInsensitive)
                  completer.setModelSorting(
                      QCompleter.CaseSensitivelySortedModel)
                  completer.setMaxVisibleItems(15)
                  completer.activated.connect(self.insertCompletion)
      
              super().setCompleter(completer)
      
          def insertCompletion(self, completion):
              completer = self.completer()
              if completer and completer.widget() == self:
                  completer.widget().setText(completion)
      
          def keyPressEvent(self, event):
      
              completer = self.completer()
      
              if event.key() in (Qt.Key_Return, Qt.Key_Enter,
                                 Qt.Key_Tab, Qt.Key_Backtab):
                  self.returnPressed.emit()
                  if completer and completer.popup().isHidden():
                      return
      
              super().keyPressEvent(event)
              input_text = self.text()
      
              if completer:
      
                  if not event.text():
                      completer.popup().hide()
                      return
      
                  if input_text and input_text != completer.completionPrefix():
                      completer.setCompletionPrefix(input_text)
                      completer.popup().setCurrentIndex(
                          completer.completionModel().index(0, 0))
      
      
      class MyDelegate(QItemDelegate):
      
          def __init__(self, parent):
              super().__init__(parent)
      
          def createEditor(self, parent, option, index):
              strings = ('tata', 'tete', 'titi', 'toto', 'tutu')
              completer = QCompleter(strings)
              editor = MyLineEdit(parent)
              editor.setCompleter(completer)
              editor.editingFinished.connect(self.commitAndCloseEditor)
              editor.returnPressed.connect(self.commitAndCloseEditor)
              return editor
      
          def commitAndCloseEditor(self):
              editor = self.sender()
              self.commitData.emit(editor)
              self.closeEditor.emit(editor)
      
          def setEditorData(self, editor, index):
              if editor:
                  editor.setText(index.model().data[0])
      
          def setModelData(self, editor, model, index):
              if editor:
                  model.setData(index, editor.text(), Qt.EditRole)
      
      
      class Model(QAbstractTableModel):
      
          def __init__(self):
              super().__init__()
              self.data = ['hello']
      
          def rowCount(self, parent=None):
              return 1
      
          def columnCount(self, parent=None):
              return 1
      
          def data(self, index, role):
              if not index.isValid():
                  return QVariant()
      
              if role in (Qt.DisplayRole, Qt.EditRole):
                  return self.data[0]
      
              return QVariant()
      
          def setData(self, index, value, role):
              if role == Qt.EditRole:
                  self.data[0] = value
      
                  top_left = self.index(0, 0)
                  bottom_right = self.index(
                      self.rowCount() + 1, self.columnCount())
                  self.dataChanged.emit(top_left, bottom_right,
                                        [Qt.DisplayRole])
                  return True
      
              return False
      
          def flags(self, index):
              return Qt.ItemIsEditable | super().flags(index)
      
      
      class MainWindow(QMainWindow):
      
          def __init__(self):
              super().__init__()
              self.model = Model()
              self.table = QTableView()
              self.table.setModel(self.model)
              delegate = MyDelegate(self.table)
              self.table.setItemDelegateForColumn(0, delegate)
      
          def initUI(self):
              self.show()
      
              self.setCentralWidget(self.table)
      
      
      if __name__ == '__main__':
          app = QApplication(sys.argv)
      
          mw = MainWindow()
          mw.initUI()
      
          sys.exit(app.exec_())
      
      

      Best regards

      1 Reply Last reply Reply Quote 0
      • M
        Matthieu 0 last edited by

        Finally found a solution. I used a QTextEdit, it does not work with QLineEdit, I don't know why. I still have a small problem, I can't close the completion popup when I hit the return key, so I must hit return key, twice. Not a big problem.

        import sys
        
        from PyQt5.QtCore import QAbstractTableModel, Qt, QVariant, pyqtSignal
        from PyQt5.QtGui import QTextCursor, QTextOption
        from PyQt5.QtWidgets import (QAbstractItemDelegate, QApplication, QCompleter,
                                     QItemDelegate, QMainWindow, QTableView, QTextEdit)
        
        
        class MyLineEdit(QTextEdit):
        
            returnPressed = pyqtSignal()
        
            def __init__(self, parent=None):
                super().__init__(parent)
                self.setAcceptRichText(False)
                self.setWordWrapMode(QTextOption.NoWrap)
                self.setUndoRedoEnabled(False)
                self.setTabChangesFocus(True)
                self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
                self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
                self.completer = None
                self.textChanged.connect(self.textHasChanged)
        
            def setCompleter(self, completer):
                if completer:
                    completer.setWidget(self)
                    completer.setCompletionMode(QCompleter.PopupCompletion)
                    completer.setCaseSensitivity(Qt.CaseInsensitive)
                    completer.setModelSorting(
                        QCompleter.CaseSensitivelySortedModel)
                    completer.setMaxVisibleItems(15)
                    completer.activated.connect(self.insertCompletion)
                    self.completer = completer
        
            def insertCompletion(self, completion):
                print('>>> insertCompletion')
                if self.completer and self.completer.widget() == self:
                    self.completer.widget().setPlainText(completion)
                    self.completer.widget().moveCursor(QTextCursor.EndOfLine)
                    self.completer.widget().ensureCursorVisible()
        
            def focusInEvent(self, event):
                print('>>> focusInEvent')
                if self.completer:
                    self.completer.setWidget(self)
                super().focusInEvent(event)
        
            def keyPressEvent(self, event):
                print('>>> keyPressEvent')
        
                if self.completer and self.completer.popup().isVisible():
        
                    if event.key() in (Qt.Key_Return, Qt.Key_Enter,
                                       Qt.Key_Tab, Qt.Key_Backtab, Qt.Key_Escape):
                        event.ignore()
                        return
                else:
                    if event.key() in(Qt.Key_Return, Qt.Key_Enter):
                        self.returnPressed.emit()
                        return
        
                super().keyPressEvent(event)
        
                if not self.toPlainText():
                    self.completer.popup().hide()
                    return
        
                self.completer.setCompletionPrefix(self.toPlainText())
                self.completer.popup().setCurrentIndex(
                    self.completer.completionModel().index(0, 0))
                self.completer.complete()
        
            def textHasChanged(self):
                # remove new lines and strip left blank characters
                self.blockSignals(True)
                cursor = self.textCursor()
                self.setPlainText(' '.join(self.toPlainText().splitlines()).lstrip())
                self.setTextCursor(cursor)
                self.ensureCursorVisible()
                self.blockSignals(False)
        
        
        class MyDelegate(QItemDelegate):
        
            def __init__(self, parent):
                super().__init__(parent)
        
            def createEditor(self, parent, option, index):
                strings = ('tata', 'tada', 'tadam', 'tete', 'titi', 'toto', 'tutu')
                completer = QCompleter(strings)
                editor = MyLineEdit(parent)
                editor.setCompleter(completer)
                editor.returnPressed.connect(self.commitAndCloseEditor)
                return editor
        
            def commitAndCloseEditor(self):
                print('>>> commitAndCloseEditor')
                editor = self.sender()
                self.commitData.emit(editor)
                self.closeEditor.emit(editor)
        
            def setEditorData(self, editor, index):
                if editor:
                    editor.setText(index.model().data[0])
                    editor.selectAll()
        
            def setModelData(self, editor, model, index):
                if editor:
                    model.setData(index, editor.toPlainText(), Qt.EditRole)
        
        
        class Model(QAbstractTableModel):
        
            def __init__(self):
                super().__init__()
                self.data = ['hello']
        
            def rowCount(self, parent=None):
                return 1
        
            def columnCount(self, parent=None):
                return 1
        
            def data(self, index, role):
                if not index.isValid():
                    return QVariant()
        
                if role in (Qt.DisplayRole, Qt.EditRole):
                    return self.data[0]
        
                return QVariant()
        
            def setData(self, index, value, role):
                if role == Qt.EditRole:
                    self.data[0] = value
        
                    top_left = self.index(0, 0)
                    bottom_right = self.index(
                        self.rowCount() + 1, self.columnCount())
                    self.dataChanged.emit(top_left, bottom_right,
                                          [Qt.DisplayRole])
                    return True
        
                return False
        
            def flags(self, index):
                return Qt.ItemIsEditable | super().flags(index)
        
        
        class MainWindow(QMainWindow):
        
            def __init__(self):
                super().__init__()
                self.model = Model()
                self.table = QTableView()
                self.table.setModel(self.model)
                delegate = MyDelegate(self.table)
                self.table.setItemDelegateForColumn(0, delegate)
        
            def initUI(self):
                self.show()
        
                self.setCentralWidget(self.table)
        
        
        if __name__ == '__main__':
            app = QApplication(sys.argv)
        
            mw = MainWindow()
            mw.initUI()
        
            sys.exit(app.exec_())
        
        1 Reply Last reply Reply Quote 1
        • First post
          Last post