1

I'm reading up on how to make my QAbstractTableModel editable, and it looks pretty straightforward.

But how do I set up an editable cell to use a QCompleter? I take it somehow I have to tell the QTableView to use a QLineEdit widget? How can I do this?


edit: hmm, I guess it has something with QTableView.setItemDelegateForColumn() but I don't know anything about delegates or how to use them.


edit: I tried RobbieE's solution, got something that sort of works but it gets the geometry of the popup combo box wrong and crashes Python when I press Enter.

class CompleterDelegate(QtGui.QStyledItemDelegate):
    def __init__(self, parent=None, completerSetupFunction=None):
        super(CompleterDelegate, self).__init__(parent)
        self._completerSetupFunction = completerSetupFunction
    def createEditor(self, parent, option, index):
        return QtGui.QLineEdit(parent)
    def setEditorData(self, editor, index):
        super(CompleterDelegate, self).setEditorData(editor, index)
        self._completerSetupFunction(editor, index)

My _completerSetupFunction looks something like this:

def setupFunc(editor, index):
    completer = MyCompleter(editor)
    completer.setCompletionColumn(0)
    completer.setCompletionRole(QtCore.Qt.DisplayRole)
    completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)    
    editor.setCompleter(completer)
    completer.setModel(myAbstractItemModel)
Jason S
  • 184,598
  • 164
  • 608
  • 970
  • If you want to create your own editor widgets, you must set the position and geometry using the data provided in the `option` parameter. – RobbieE Jul 27 '14 at 21:45

2 Answers2

3

Create a subclass of QStyledItemDelegate

All you need to do is reimplement the setEditorData function, check that the editor widget is a QLineEdit and then set the completer.

Please, excuse that I don't know Python but this is how it would be done in C++. Hopefully, translating to Python will be easy.

class MyDelegate : public QStyledItemDelegate{
     public:
         void setEditorData(QWidget *editor, QModelIndex const &index){
             
             // call the superclass' function so that the editor widget gets the correct data
             QStyledItemDelegate::setEditorData(editor, index);

             // Check that the editor passed in is a QLineEdit. 
             QLineEdit *lineEdit = qobject_cast<QLineEdit*>(editor);

             if (lineEdit != nullptr){

                 // add whatever completer is needed, making sure that the editor is the parent QObject so it gets deleted along with the editor
                 lineEdit.setComplete(new MyCompleter(editor));
             }
         }
}; 
m7913d
  • 10,244
  • 7
  • 28
  • 56
RobbieE
  • 4,280
  • 3
  • 22
  • 36
  • thanks. What if it isn't a QLineEdit? (or is that always the default for QTableView?) – Jason S Jul 25 '14 at 12:32
  • The approach works.. except that QTableView's default editor isn't a QLineEdit, it's just some anonymous QWidget. I can override `createEditor()` but it has some weird side-effects (one of which is crashing when I press return :-( – Jason S Jul 25 '14 at 19:41
  • The type of widget created for your editor is dependent on the type returned by the call to the `data()` function of your model, using the `Qt::EditRole`. If you look at the documentation of [`QItemEditorFactory`](http://srinikom.github.io/pyside-docs/PySide/QtGui/QItemEditorFactory.html), you'll see the default widgets for the most common data types. `QLineEdit` is the default if the `QVariant` was created from `QString` – RobbieE Jul 27 '14 at 21:32
  • Aha, I figured it out. You should not be applying the completer in setEditorData, but rather in the place where the editor is created. – Jason S Aug 25 '14 at 21:34
2

Per RobbieE's suggestion, I subclassed QStyledItemDelegate. But the correct place to apply the completer is when the editor is created, not setEditorData.

class CompleterDelegate(QtGui.QStyledItemDelegate):
    def __init__(self, parent=None, completerSetupFunction=None):
        super(CompleterDelegate, self).__init__(parent)
        self._completerSetupFunction = completerSetupFunction
    def createEditor(self, parent, option, index):
        editor = QtGui.QLineEdit(parent)
        self._completerSetupFunction(editor, index)
        return editor

and then I use a completerSetupFunction that basically looks like this:

def _completerSetupFunction(editor, index):
    print "completer setup: editor=%s, index=%s" % (editor, index)
    completer = QtGui.QCompleter(base_items, editor)
    completer.setCompletionColumn(0)
    completer.setCompletionRole(QtCore.Qt.EditRole)
    completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
    try:    
        editor.setCompleter(completer)            
    except:
        pass

Here's a complete example as a github gist.

Jason S
  • 184,598
  • 164
  • 608
  • 970