PDA

View Full Version : Customize QCompleter suggestions



tuli
6th September 2018, 11:39
Hey,

I have a simple model with two columns, which I hand to a QCompleter and attach to a QLineEdit.


completer = new QCompleter(model, this);
completer->setCaseSensitivity(Qt::CaseInsensitive);
completer->setCompletionRole(MyModel::DisplayRole);
completer->setCompletionColumn(0);

input = new QLineEdit(this);
input->setCompleter(completer);


This works well, but now I want the completions/suggestions shown by the completer to display not as "<column0>" but rather as "<column0> : <column1>".

I tried to introduce a custom DisplayRole (Qt::UserRole +1) in my model that always returns "<column0> : <column1>". Turns out this is not for the suggestions, but only for the text set into the linedit AFTER a completion-suggestion is accepted by the user! The suggestions
themselves are still only "<column0>"!

Next thing I tried is to introduce a fake third column in the model that returns the desired format, but is not actually part of the model. This outright doesnt work though.



How do I accomplish the desired behavior?

ChrisW67
8th September 2018, 09:37
If you want to display your two-column completion model as a table with two columns then you should be able to construct a QTableView and call QCompleter::setPopup() with the object to replace the default list view.

tuli
8th September 2018, 12:43
Good idea. This may be a stupid question, but what else do I have to do to set that up?

When I just sprinkle

completer->setPopup(new QTableView);

in there, it pops up the TableView correctly, but it's always empty. (But still it only pops up when suggestions should be shown.)

tuli
8th September 2018, 16:38
Ah, got it, QCompleter assumes the horizontalHeader() is hidden, otherwise the header will cover the content. Sadly, aesthetically this is not an option for my concrete case, the popup has very little space and the model has too many unneeded columns. No, it has to be a single text string.


I could add a "fake" column to my model that returns exactly the string I want the completer to use and then hide the columns by default... What do you think about this idea?

d_stranz
8th September 2018, 18:47
I think you should continue on with the QTableView popup idea and consider using a QSortFilterProxyModel between your model and that view to select only the two columns you wish for display in the table view popup. (QSortFilterProxyModel::filterAcceptsColumn())

You could try the fake column idea but I don't know if that will work.

tuli
9th September 2018, 00:39
Thanks for the input. I really like the idea. But it seems the interaction between QCompleter and QSortFilterModel is problematic:

Say the sourcemode is the full, original mode and I put a QSortFilterModel in between it and the tableview-popup.


I enter something, the completer popsup the tableview with suggestions. I accept the first suggestion.

At this point, completer->currentIndex() returns an index of (0,0). This seems to refer to the first suggestion that I accepted (?).
However, when I pass the (0,0) index to sortfiltermodel->data(index), then it returns the first entry of the underlying sourcemodel! (in other words, it returns data that had in this case been filtered out by the sortmodel!).



Unless I made a mistake somewhere, I dont see any way Qt lets you get ahold of the model-data the user selected, when using QSortFilterModel. :(



edit: Also it seems no matter how many suggestions there are and which one I accept, the completer->currentIndex().row() is always 0... :(

d_stranz
9th September 2018, 17:42
However, when I pass the (0,0) index to sortfiltermodel->data(index), then it returns the first entry of the underlying sourcemodel!

Probably because you didn't override the mapToSource() method when you wrote your QSFPM. That translates QModelIndex from the QSFPM into the correct QModelIndex of the source model. Look at the other map...() methods as well to see if you need to override any of them.

tuli
9th September 2018, 18:39
I figured it out, there are two quirks we have to consider when dealing with QCompleter



1. It uses its own internal model, that holds all the current suggestions. This can be retrieved with complerter->completionModel(). Any index QCompleter gives you is valid in this model only.


2. Once you accept a suggestion, completer->currentIndex() is not the index you accepted! However, it will still be a valid index(!) (it's the first index of the list of the suggestions, but not necessarely the one you accepted...). If you cancel the completer without accepting something, the index is invalid though.


So the solution was this:

Dont use completer->currentIndex() at all, instead connect a slot to the QCompleter::activated(const QModelIndex&) slot. THIS index will be the correct index you accepted; the index is valid for the completer->completionModel() only.


In this slot you can then either map the index to your sourcemodel, or retrieve data directly from the completionModel().