Search code examples
qtpysideqabstractitemmodelqcompleter

looking for example for QCompleter with segmented completion / tree models


The PySide docs include this section on QCompleter with tree models:

PySide.QtGui.QCompleter can look for completions in tree models, assuming that any item (or sub-item or sub-sub-item) can be unambiguously represented as a string by specifying the path to the item. The completion is then performed one level at a time.

Let’s take the example of a user typing in a file system path. The model is a (hierarchical) PySide.QtGui.QFileSystemModel . The completion occurs for every element in the path. For example, if the current text is C:\Wind , PySide.QtGui.QCompleter might suggest Windows to complete the current path element. Similarly, if the current text is C:\Windows\Sy , PySide.QtGui.QCompleter might suggest System .

For this kind of completion to work, PySide.QtGui.QCompleter needs to be able to split the path into a list of strings that are matched at each level. For C:\Windows\Sy , it needs to be split as “C:”, “Windows” and “Sy”. The default implementation of PySide.QtGui.QCompleter.splitPath() , splits the PySide.QtGui.QCompleter.completionPrefix() using QDir.separator() if the model is a PySide.QtGui.QFileSystemModel .

To provide completions, PySide.QtGui.QCompleter needs to know the path from an index. This is provided by PySide.QtGui.QCompleter.pathFromIndex() . The default implementation of PySide.QtGui.QCompleter.pathFromIndex() , returns the data for the edit role for list models and the absolute file path if the mode is a PySide.QtGui.QFileSystemModel.

But I can't seem to find an example showing how to do this. Can anyone point me at an example I can use as a starting point? (In my investigation it looks like maybe the hard part is the tree model rather than the QCompleter)

It looks like you would need to provide these functions:

  • ability to split a string into segments (for the example given, C:\Windows\Sy to ['C:','Windows','Sy']
  • the ability to specify the list of items that include the last segment (e.g. all the items included in ['C:','Windows']

I found an example for the basic functionality of QCompleter and have been able to tweak the basics fine (see below), I just don't know how to go about implementing a tree model type application.

'''based on
http://codeprogress.com/python/libraries/pyqt/showPyQTExample.php?index=403&key=QCompleterQLineEdit'''

from PySide.QtGui import * 
from PySide.QtCore import * 
import sys

def main():    
    app     = QApplication(sys.argv)
    edit     = QLineEdit()
    strList     = '''
Germany;Russia;France;
french fries;frizzy hair;fennel;fuzzball
frayed;fickle;Frobozz;fear;framing;frames
Franco-American;Frames;fancy;fire;frozen yogurt
football;fnord;foul;fowl;foo;bar;baz;quux
family;Fozzie Bear;flinch;fizzy;famous;fellow
friend;fog;foil;far;flower;flour;Florida
'''.replace('\n',';').split(";")
    strList.sort(key=lambda s: s.lower())
    completer     = QCompleter(strList,edit)
    completer.setCaseSensitivity(Qt.CaseInsensitive)

    edit.setWindowTitle("PySide QLineEdit Auto Complete")    
    edit.setCompleter(completer)
    edit.show()

    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

Solution

  • I couldn't find a good example for what I wanted, but I figured out how to adapt the Qt TreeModel example to using a QCompleter:

    https://gist.github.com/jason-s/9dcef741288b6509d362

    enter image description here

    The QCompleter is the easy part, you just have to tell it how to split a path into segments, and then how to get from a particular entry in the model back to a path:

    class MyCompleter(QtGui.QCompleter):
        def splitPath(self, path):
            return path.split('/')
        def pathFromIndex(self, index):
            result = []
            while index.isValid():
                result = [self.model().data(index, QtCore.Qt.DisplayRole)] + result
                index = index.parent()
            r = '/'.join(result)
            return r
    

    Aside from that, you have to configure the QCompleter properly, telling it how to get from a model item to a text string. Here I set it up to use the DisplayRole and to use column 0.

    edit     = QtGui.QLineEdit()
    completer     = MyCompleter(edit)
    completer.setModel(model)
    completer.setCompletionColumn(0)
    completer.setCompletionRole(QtCore.Qt.DisplayRole)
    completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)