PDA

View Full Version : QCompleter and fuzzy search



T4ng10r
19th December 2016, 14:31
Hi,
After reading this (https://blog.forrestthewoods.com/reverse-engineering-sublime-text-s-fuzzy-match-4cffeed33fdb#.qyjbfkkl1) article about fuzzy search I felt empowered to add to default QCompleter mentioned code.
If it wasn't for QCompleter and it's graphical represenation (i mean list view with suggested completion) I would remove it and use mentioned in link code.
How can I do it?

d_stranz
19th December 2016, 17:03
So, yes, the fuzzy matcher looks very cool.

Unfortunately, I do not think that fuzzy matching is compatible with the matching modes available with QCompleter. QCompleter uses a small subset of Qt::MatchFlags to tell it how to match a string from your complete list of possibilities (the QAbstractItemModel set as input).

The actual matching is done by a private class derived from the private base class QCompleterEngine, defined in the private header file qcompleter_p.h. Which derived completion engine class is instantiated depends on which MatchFlags you have specified. The only supported match flags are MatchStartsWith, MatchContains, and MatchEndsWith. None of the other flags are supported. All of the code to create and use the engine is deeply embedded in the implementation of QCompleter. There isn't any handy "setCompletionEngine" method that would let you specify a custom engine for matching the user's typing.

If QCompleter supported the MatchWildcard flag, then you could trick it into accepting your fuzzy matches as input: set the flag to MatchWildcard and use a QSortFilterProxyModel as the input to the QCompleter. Your QSortFilterProxyModel would use the fuzzy match to filter the entries in the complete model and present that to the QCompleter. With a wildcard to "match anything", then the entire content of the proxy model would be shown as possible completions.

So if you wanted to modify the Qt source code for QCompleter, you could probably implement support for Qt::MatchWildcard. This would be in the QUnsortedModelEngine private class (in qcompleter.cpp), in the QUnsortedModelEngine:: buildIndices() method. Move the "case Qt:: MatchWildcard" to above "case Qt:: MatchExactly" and simply implement it as:



case Qt::MatchWildcard:
break;


This would cause every string that was passed in to be accepted as a possible match to the current index in the model.

And, of course, you would have to rebuild your Qt distribution to include this new code...

T4ng10r
19th December 2016, 17:29
Currently I'm investigating QSortFilterProxyModel::filterAcceptsRow()(). Perhaps it could be answer.

d_stranz
19th December 2016, 17:39
Yes, that would be where you implement the fuzzy match. But unfortunately, there is no way to set QCompleter up to "accept anything from the model" mode. It puts an event filter on the widget (line edit, for example) that captures what the user is typing and passes that into its completion engine. So you would have to short-circuit that process so that keystrokes get sent to your fuzzy filter instead. And then you still have to implement wildcard mode inside of QCompleter - I don't see any way to implement this otherwise. The matching modes that it supports aren't enough to handle fuzzy matching.

T4ng10r
22nd December 2016, 08:02
Seems that I will have to reproduce QCompleter functionality by my own (internal model, listview below attached edit line, etc).

It would be nice to have setSearchFunction( function object) or useCustomSearch and provide search functionality in overridden customSearch function. This way user will have more control over what to search.
On the other hand - this is so unique and rather rare demand that I don't believe it would be implemented.