PDA

View Full Version : Searching in QListView



ct
23rd July 2007, 17:03
I just made my transition to Qt 4.3. Some of the features that I have seen are great, however, it's quite a jump from Qt 3.3 which I used to code in before.
Could anyone suggest me how could I search the QListView which has the model as QStringListModel for a regexp.
Actually, I want to produce the effect like that of the Qt Assistant side bar where you can search. Moreover I need to scroll in the listview to the item which I think I can achive with teh scrollTo() function.

Any help on using findchildren(QRegexp) to find the items in the QListView will be appreciated.
BTW, MVC is great :D

fullmetalcoder
23rd July 2007, 17:22
Actually, I want to produce the effect like that of the Qt Assistant side bar where you can search.
Then why don't you try to have a look at Qt Assistant sources? AFAIK the simplest way to do this, as you're using a real model and not an item-based stuff, is to rely on a QSortFilterProxyModel.

Michiel
23rd July 2007, 17:27
Like FMC said, you want QSortFilterProxyModel. Read the docs.

I don't think there's anything very smart going on there. It just walks through the list, filtering out those items that don't match.

ct
23rd July 2007, 18:24
I don't think there's anything very smart going on there. It just walks through the list, filtering out those items that don't match.


I did try the QSortFilterProxyModel just now. It is a bit slow though. I have a huge dictionary which needs to be searched. Amazingly though Qt Assistant's search is quite fast however I guess it is using some sort of searching algorithm. I wish there was an easier and efficient way.

marcel
23rd July 2007, 18:31
I did try the QSortFilterProxyModel just now. It is a bit slow though. I have a huge dictionary which needs to be searched. Amazingly though Qt Assistant's search is quite fast however I guess it is using some sort of searching algorithm. I wish there was an easier and efficient way.

What is the order of the items in the dictionary?
If they are already sorted, then you can write your own model and use a faster algorithm for sorted sets, like binary search.

Regards

Michiel
23rd July 2007, 18:41
But if you want to search for regex-patterns, binary search, which searches for prefixes only (or at least predetermined substrings), won't work.

ct
23rd July 2007, 19:58
QList<QString>::iterator i = qBinaryFind(dictionaryList.begin(), dictionaryList.end(),text);
listView->scrollTo(i,1);


I need to do something like this. The dictionaryList is a QStringList . It says in the manual that i is the position. I would like to move to the listView where it is the first match. How do I convert an iterator to QModelIndex ?

Also how do I create a List of items that matches with all the values in QStringList so that I could make a new StringList and set the proxy model to it and all the matched Strings are shown .

marcel
23rd July 2007, 21:40
QList<QString>::iterator i = qBinaryFind(dictionaryList.begin(), dictionaryList.end(),text);
listView->scrollTo(i,1);
I need to do something like this. The dictionaryList is a QStringList . It says in the manual that i is the position. I would like to move to the listView where it is the first match. How do I convert an iterator to QModelIndex ?
.
Like this:


int index = dictionaryList.indexOf( *i );
QModelIndex modelIndex(index, 1);


Regards

ct
24th July 2007, 03:40
error: no matching function for call to `QModelIndex::QModelIndex(int&, int)'

ct
24th July 2007, 04:51
I tried to look at the Assistant Code, and I see something .



class IndexListModel;


This model is doing the filter job. However, I can't find it's declaration besides this small forward declaration. Is it that the uic will generate this IndexListModel ? Also I notice that the listview is named indexList. Is is sometype of auto generation inside Qt or am I missing something totally ?

BTW, I am referring to following code in Qt Assistant


---------- HELPDIALOG.H
[45]class IndexListModel;
[142] IndexListModel *indexModel;

ct
24th July 2007, 19:06
Currently I am connecting the textChanged(QString)SIGNAL of QLineEdit to the setFilterFixedString(QString) slot of the QSortFilterProxyModel as suggested by the manual but I need something faster ... anyone ???

jpn
25th July 2007, 11:23
One simple trick to make it feel slightly faster is to emit a custom signal, let's say textDelayedChange(QString), after a certain delay when no changes have been made. Every change resets the timer and once it reaches the predefined delay, the signal is emitted. This way entering a string of 10 chars does not trigger 10 re-filterings but one (provided that the string is entered in a row and no delay exceeds in between strokes).

PS. As far as I remember, Qt Assistant performs search on a custom indexed dictionary.

ct
26th July 2007, 03:36
yes, that is just great. It's the each textChange() signal that is causing the trouble.


...
connect(lineEdit,SIGNAL(textChanged(QString)),this ,delayChange(QString)));
connect(this,SIGNAL(textChangeDelayed(QString)),pr oxyModel,SLOT(setFilterFixedString(QString)));
....

void delayChange(QString)
{
//reset timer;
timer = 0;
while(timer < 9999)
timer++;

if(timer > 9999)
emit textChangeDelayed(String);

}



Is this what you meant ? I am a bit confused about how this type of slots ar handled though. Do they all run in thread ? Wont' all the slots emit the textChangeDelayed() eventually ? How do you cause the delay.



PS. As far as I remember, Qt Assistant performs search on a custom indexed dictionary.


I was thinking about it too. I have around 22,000 words so eventually sorting is slow too. I have to keep it in a indexed file. So I might as well do something with the index of each word starting from a particular alphabet at the time of loading.
But, I like this delay thing, it is less work :D

jpn
26th July 2007, 10:38
You can't block the event loop of a GUI application with a busy loop. You should do it more or less like this:


QTime delay;
static const int DELAY = 200; // ms
...
connect(lineEdit,SIGNAL(textChanged(QString)),this ,delayChange()));
connect(this,SIGNAL(textChangeDelayed(QString)),pr oxyModel,SLOT(setFilterFixedString(QString)));
....

void delayChange()
{
delay.start();
QTimer::singleShot(DELAY, this, SLOT(informChange()));
}

void informChange()
{
if (delay.elapsed() >= DELAY)
emit textChangeDelayed(lineEdit->text());
else
QTimer::singleShot(DELAY - delay.elapsed(), this, SLOT(informChange()));
}

Michiel
26th July 2007, 11:37
I suggest the following code. Might be a little cleaner (assuming QLineEdit subclass):


class DelayLineEdit : public QLineEdit {
Q_OBJECT

public:
DelayLineEdit(QWidget * parent = 0) : QLineEdit(parent) {
_delay = new QTimer(this);
_delay->setInterval(200); // ms
_delay->setSingleShot(true);
connect(this, SIGNAL(textChanged(QString)), _delay, SLOT(start()));
connect(_delay, SIGNAL(timeout()), this, SLOT(informChange()));
}

private slots:
void informChange() {
emit textChangeDelayed(text());
}

signals:
void textChangeDelayed(QString);

private:
QTimer* _delay;
}

Of course, the informChange() slot is only necessary if you really want to add the new text to the signal. If an empty signal is enough, you can put that signal directly in the second connect of the constructor (signal chaining).

Edit: This code has now been tested. (Though I've changed some details from the code above.) I've uploaded an example project, ready to compile. Feel free to use the code.

ct
27th July 2007, 16:21
Thanks, it seems to quicken things up. I guess I will go dig up the manual more..there are so many new things in Qt 4.3.0.
Thanks again jpn and Michiel.

sharrychrist
27th July 2007, 16:38
Hi, this post is very informative; however I would like some specific information. If someone can help me then please send me a private message. Best Regards,

Michiel
27th July 2007, 17:20
Feel free to ask your questions right here. If we communicate by private message, no one else will benefit from it.

Michiel
27th July 2007, 17:51
It just occurred to me that a useful macro could be created that does the same thing without the need for subclasses or extra clutter in your code. It would be great if you could do something like this:

connect(object1, DELAYED_SIGNAL(textChanged(), 200), object2, SLOT(doSomething()));

The DELAYED_SIGNAL macro is probably possible. But I don't know enough about the internals of the signal/slot system to create it myself.

ct
28th July 2007, 14:54
Actually, I used jpn's method ...it worked like a charm without subclassing. Yours is nice too but I was lazy to subclass as I already had all the widgets laid out.
I am however thinking about using some sort of searching algorithm algorithm..as QListView takes time to load the items..especially when you hit "backspace" and there are no items in the LineEdit and all the items have to be loaded onto QListView. But even then I don't know if it is some other thing that might be causing this.
May be a PIII with 192mb RAM is not enough :o

Michiel
28th July 2007, 15:40
That's fine of course. But in Designer you can just promote your QLineEdits to the subclass. Already having them laid out doesn't matter.