Loading time of QFileSystemModel entries
-
Hi,
I have a custom file system dialog which uses QFileSystemModel and displays entries in QListView.
Since there are lots of network drives and other "slow" entries on target machines I would like to indicate to the user that something is going on by QApplication::setOverrideCursor(Qt::WaitCursor) / QApplication::restoreOverrideCursor().The problem is that I can't seem to find any reliable pair of signals I could connect to to call those functions.
First thought was rootPathChanged()/directoryLoaded() of the model, but the second signal is only emitted the first time model visits directory, so if I enter directory, enter another and then go back I'm stuck with the wait cursor because second signal doesn't fire again. The other thing is that it takes few seconds between directoryLoaded() and QListView actually refreshing the view to show those loaded directories.I also looked at rowsAboutToBeInserted()/rowsInserted(), but those fire multiple times in a single directory and there are long gaps when loading consecutive network drives.
The start of loading seems to work fine with rootPathChanged(), but I can't find a reliable way to tell when directory has been fully loaded.I'd be grateful for any ideas.
-
Hi,
Welcome to DevNet.Unfortunately the only way I found was to dig deep into Qt implementation and provide a custom signal for that myself, but since I couldn't modify Qt at that time for licencing reasons I abandoned the idea.
I was not very happy to remove the wait cursor but oh well, I don't work in that project anymore :P -
[quote author="Chris Kawa" date="1396606381"]Hi,
Welcome to DevNet.Unfortunately the only way I found was to dig deep into Qt implementation and provide a custom signal for that myself, but since I couldn't modify Qt at that time for licencing reasons I abandoned the idea.
I was not very happy to remove the wait cursor but oh well, I don't work in that project anymore :P[/quote]Thank you.
Heh, I see. Well, I can share the only idea I have so far (I just had it in a grocery store so I haven't actually tried it): to subclass QFileSystemModel and re-implements its "data(..)" method like this:
@QVariant QCustomFileSystemModel::data(const QModelIndex & index, int role = Qt::DisplayRole) const {
if (role != Qt::DisplayRole) return QFileSystemModel::data(index,role);emit signalStart();
QVariant data = QFileSystemModel::data(index,role);
emit signalFinish();
return data;
}@Probably I will have to do the same with QFileSystemModel::columnCount(...) and QFileSystemModel::rowCount(...)
-
I don't think this will work. This would emit start/finish before and after a new item is added. So if you connect cursor change to that it will blink like crazy until the whole directory is loaded. But it will also not show anything between the items. so it doesn't solve the issue at all.
The problem here is that the operation is asynchronous. Qt posts a request to the OS to enumerate the directory and registers a callback that is called whenever OS is ready to list another item. Qt thus doesn't know if there are more items to come or not. To fix this you would need to check these OS dependent enumeration mechanisms and check if they provide something like items count before you start to enumerate them. If it does then you can increase some counter in the mentioned callback and know when you're done.
My guess (although totally unchecked) is that some platforms don't provide such count and that's why Qt doesn't provide a signal like that.
Edit: A "link":http://blogs.msdn.com/b/oldnewthing/archive/2009/02/17/9426787.aspx to confirm that theory at least on Windows.
-
[quote author="Chris Kawa" date="1396612341"]I don't think this will work. This would emit start/finish before and after a new item is added. So if you connect cursor change to that it will blink like crazy until the whole directory is loaded. But it will also not show anything between the items. so it doesn't solve the issue at all.
[/quote]My problem is a little bit different and I still hope this will help :) Damn, this is going to be hard to explain.
I work with a Linux embeded device and I have a file browser application which also mounts and unmounts samba shares.
When a share becomes unavailable (for any possible reason) I'm having major freezes every time the model tries to access it. I can't go to all "low level"(although I've already tried playing with mounting options) so I just wanted to start a timer when the model starts retrieving file list from a remote dir and use its timeout signal as a sing that something went wrong (meaning that I should probably reset the model and lazy umount this share).So in my imagination what I proposed will just start/reset a timeout timer properly.
-
So this seems to work. Not great, as there still is a delay between clicking on expand button and the cursor being changed to hourglass.
It needs an ugly hack as QAbstractModel::data() is const.
@
// fsmodelenhanced.h
#ifndef FSMODELENHANCED_H
#define FSMODELENHANCED_H#include <QFileSystemModel>
class FsModelEnhanced : public QFileSystemModel
{
Q_OBJECTpublic:
explicit FsModelEnhanced(QObject *parent = 0);signals:
void fsmeLoadStart ();
void fsmeLoadEnd ();protected:
virtual void timerEvent ( QTimerEvent *event); QVariant data( const QModelIndex & index, int role = Qt::DisplayRole) const;
private:
void startDataRetrieveSequence ();
private:
int timer_id_;
};#endif // FSMODELENHANCED_H
@@
// fsmodelenhanced.cpp
#include "fsmodelenhanced.h"
#include <QTimerEvent>FsModelEnhanced::FsModelEnhanced(QObject *parent) :
QFileSystemModel(parent),
timer_id_(-1)
{
}void FsModelEnhanced::startDataRetrieveSequence ()
{
if (timer_id_ == -1) {
// this is the first item in the chain
emit fsmeLoadStart();
} else {
killTimer (timer_id_);
}// another half second wait timer_id_ = startTimer (100);
}
QVariant FsModelEnhanced::data (
const QModelIndex & index, int role) const
{
for (;;) {if (role != Qt::DisplayRole) break; if (!index.isValid ()) break; if (!index.column () != 0) break; // this is nasty ((FsModelEnhanced*)this)->startDataRetreieveSequence(); break; } return QFileSystemModel::data(index,role);
}
void FsModelEnhanced::timerEvent (QTimerEvent *event)
{
for (;;) {
if (timer_id_ == -1) break;
if (event->timerId () != timer_id_) break;
killTimer (timer_id_);
timer_id_ = -1;
emit fsmeLoadEnd();
return;
}
QFileSystemModel::timerEvent (event);
}
@@
// mainwindow.cpp
// ...
connect (model, SIGNAL(fsmeLoadStart()),
this, SLOT(fsmeLoadStart()));
connect (model, SIGNAL(fsmeLoadEnd()),
this, SLOT(fsmeLoadEnd()));
// ...void MainWindow::fsmeLoadStart()
{
ui->treeView->setCursor (Qt::WaitCursor);
}void MainWindow::fsmeLoadEnd()
{
ui->treeView->setCursor (Qt::ArrowCursor);
}@
-
So this seems to work. Not great, as there still is a delay between clicking on expand button and the cursor being changed to hourglass.
It needs an ugly hack as QAbstractModel::data() is const.
@
// fsmodelenhanced.h
#ifndef FSMODELENHANCED_H
#define FSMODELENHANCED_H#include <QFileSystemModel>
class FsModelEnhanced : public QFileSystemModel
{
Q_OBJECTpublic:
explicit FsModelEnhanced(QObject *parent = 0);signals:
void fsmeLoadStart ();
void fsmeLoadEnd ();protected:
virtual void timerEvent ( QTimerEvent *event); QVariant data( const QModelIndex & index, int role = Qt::DisplayRole) const;
private:
void startDataRetrieveSequence ();
private:
int timer_id_;
};#endif // FSMODELENHANCED_H
@@
// fsmodelenhanced.cpp
#include "fsmodelenhanced.h"
#include <QTimerEvent>FsModelEnhanced::FsModelEnhanced(QObject *parent) :
QFileSystemModel(parent),
timer_id_(-1)
{
}void FsModelEnhanced::startDataRetrieveSequence ()
{
if (timer_id_ == -1) {
// this is the first item in the chain
emit fsmeLoadStart();
} else {
killTimer (timer_id_);
}// another half second wait timer_id_ = startTimer (100);
}
QVariant FsModelEnhanced::data (
const QModelIndex & index, int role) const
{
for (;;) {if (role != Qt::DisplayRole) break; if (!index.isValid ()) break; if (!index.column () != 0) break; // this is nasty ((FsModelEnhanced*)this)->startDataRetreieveSequence(); break; } return QFileSystemModel::data(index,role);
}
void FsModelEnhanced::timerEvent (QTimerEvent *event)
{
for (;;) {
if (timer_id_ == -1) break;
if (event->timerId () != timer_id_) break;
killTimer (timer_id_);
timer_id_ = -1;
emit fsmeLoadEnd();
return;
}
QFileSystemModel::timerEvent (event);
}
@@
// mainwindow.cpp
// ...
connect (model, SIGNAL(fsmeLoadStart()),
this, SLOT(fsmeLoadStart()));
connect (model, SIGNAL(fsmeLoadEnd()),
this, SLOT(fsmeLoadEnd()));
// ...void MainWindow::fsmeLoadStart()
{
ui->treeView->setCursor (Qt::WaitCursor);
}void MainWindow::fsmeLoadEnd()
{
ui->treeView->setCursor (Qt::ArrowCursor);
}@