Thoughts on how to implement FtpFileSystemModel
-
Hello.
I've been a few days thinking about how to implement something like QFileSystemModel, but for Ftp.
I have tried to hack something that inherits QFileSystem and has a private QFtp member, but I am getting nowhere (maybe I should just take some rest).
I'd like to know some general hints on how an experienced Qt programmer would go about this. Do you think that a QFileSystemModel derivative can do this with a fair amount of work?
Should I inherit QAbstractItemModel instead and implement my own from scratch? If so, I have a lot of learning to do before I can even think of that.
My main concern now is that it's not too clear for me how QFileSystemModel populates whatever structure it uses internally. I have tried playing around with data() and setData(), using also a QSqlTableModel to store temporarily all the data from QFtp::list(). But I am getting nowhere. I also have reimplemented the setRootPath() method, but nothing seems to work.
I have been looking around but haven't found anything about subclassing QFileSystemModel :(
Thanks beforehand for any idea.
PS: the ftp part works ok. I know how to handle that. But the data never reaches the model (let alone the view).
-
After some more research, I think that migrating to QAbstractItemModel is a good idea. I am getting some results. Even if it doesn't work, at least, the model is correctly storing the values (or so it seems as per what qDebug() says.
I am now using a QList<QTreeWidgetItem*>* as the storage engine. For no particular reason though.
The FtpFileSystemModel class features a QFtp *ftp; member. It also has some member functions that are relevant:
@ /* ftp member functions */
int connectToHost(const QString& host, quint16 port = 21); int login(const QString& user = QString(), const QString& password = QString()); QModelIndex setRootPath(const QString& newPath);@
Those mimic the methods from QFtp with the same name. slot_command_finished(int,bool) monitors all the commands. Once a "cd" command finishes with success QFtp::list() is automatically started, which in turn activates a slot called slot_list_info(QUrlInfo), up to now, pretty much standard stuff.
It's in slot_list_info() where all the magic should begin, I guess.
Right now, I do this:
@void FtpFileSystemModel::slot_list_info(QUrlInfo url_info)
{QTreeWidgetItem *item = new QTreeWidgetItem; int row = remote_file_list->count(); QModelIndex item_index = createIndex(row, 0); remote_file_list->append(item); QString file_name = url_info.name(); QString file_size = QString::number(url_info.size()); QString permissions = convert_perms_to_unix(url_info.permissions()); QString file_type; QFileIconProvider icon_provider; if(url_info.isDir()) { //item->setIcon(0, icon_provider.icon(QFileIconProvider::Folder)); file_type = QString(tr("Folder")); } else if(url_info.isFile()) { //item->setIcon(0, icon_provider.icon(QFileIconProvider::File)); QString suffix = QFileInfo(url_info.name()).suffix(); file_type = QString(tr("File")); if(!suffix.isEmpty()) { file_type = suffix.append(" ").append(file_type); } } else if(url_info.isSymLink()) { //item->setIcon(0, icon_provider.icon(QFileIconProvider::File)); file_type = QString(tr("Symlink")); } setData(index(row, 0, QModelIndex()), file_name); setData(index(row, 1, QModelIndex()), file_size); setData(index(row, 2, QModelIndex()), file_type); setData(index(row, 3, QModelIndex()), permissions);
}@
And, setData() is:
@bool FtpFileSystemModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
Q_UNUSED(role);qDebug() << Q_FUNC_INFO << QString("inserting value '%1' into %2, %3") .arg(value.toString()).arg(index.row()).arg(index.column()); QTreeWidgetItem *item = remote_file_list->at(index.row()); QString str = value.toString(); item->setText(index.column(), str); emit dataChanged(index, index); return true;
}@
To be frank, I believe that the problem is in my understanding of the QModelIndex class. I had a hard time with that while learning the QtSql related classes.
The output of that qDebug() clause in setData() seems to indicate that everything's fine.
@void FtpFileSystemModel::slot_command_finished(int, bool) ".-4096-Folder-755"
void FtpFileSystemModel::slot_command_finished(int, bool) "..-4096-Folder-555"
void FtpFileSystemModel::slot_command_finished(int, bool) ".gitignore-174-gitignore File-755"
void FtpFileSystemModel::slot_command_finished(int, bool) ".htaccess-5769-htaccess File-755"
........@
but I have a similar one in data(), and that one seems not to agree...@QVariant FtpFileSystemModel::data(const QModelIndex& index, int role) const
{
qDebug() << Q_FUNC_INFO;
// if(!index.isValid() || role != (Qt::DisplayRole|Qt::UserRole))
// {
// return QVariant();
// }QTreeWidgetItem *item = remote_file_list->at(index.row()); QString str = item->text(index.column()); qDebug() << Q_FUNC_INFO << QString("reading value '%1' from %2, %3") .arg(str).arg(index.row()).arg(index.column()); return item->text(index.column());
}@
@virtual QVariant FtpFileSystemModel::data(const QModelIndex&, int) const
virtual QVariant FtpFileSystemModel::data(const QModelIndex&, int) const "reading value '' from 0, 0"
virtual QVariant FtpFileSystemModel::data(const QModelIndex&, int) const
virtual QVariant FtpFileSystemModel::data(const QModelIndex&, int) const "reading value '' from 0, 1"
virtual QVariant FtpFileSystemModel::data(const QModelIndex&, int) const
virtual QVariant FtpFileSystemModel::data(const QModelIndex&, int) const "reading value '' from 0, 2"
virtual QVariant FtpFileSystemModel::data(const QModelIndex&, int) const
virtual QVariant FtpFileSystemModel::data(const QModelIndex&, int) const "reading value '' from 0, 3" @The index() method is this, please, don't laugh at me :P
@QModelIndex FtpFileSystemModel::index(const QString &path, int column) const
{
Q_UNUSED(column);for(int i = 0; i < remote_file_list->count(); i++) { int list_index = remote_file_list->indexOf(remote_file_list->at(i)); QModelIndex index = createIndex(list_index, 0, list_index); if(index.data().toString() == path) { return createIndex(index.row(), index.column()); } } return QModelIndex();
}
QModelIndex FtpFileSystemModel::index(int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(parent);QModelIndex index = createIndex(row, column, row); return index;
}@
I really have no idea how a QAbstractItemModel tracks the indexes, where it stores them, nor even what kind of mysterious creatures they are. And, truly, I haven't the slightest clue what createIndex() is used for.
The data is there, but for some reason, I can't get it back and it won't appear in my view.
Thanks for reading.
-
So, I have managed to get correct indexes to store the info using setData(). But for some reason, the indexes are incorrect when retrieving it using data().
@QVariant FtpFileSystemModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid() || role != (Qt::DisplayRole|Qt::UserRole))
{
return QVariant();
}QTreeWidgetItem *item = remote_file_list->at(index.row()); QString str = item->text(index.column()); qDebug() << Q_FUNC_INFO << QString("reading value '%1' from %2, %3") .arg(str).arg(index.row()).arg(index.column()); return item->text(index.column());
}
bool FtpFileSystemModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
Q_UNUSED(role);if(!index.isValid()) { return false; } qDebug() << Q_FUNC_INFO << QString("inserting value '%1' into %2, %3") .arg(value.toString()).arg(index.row()).arg(index.column()); QTreeWidgetItem *item = remote_file_list->at(index.row()); QString str = value.toString(); item->setText(index.column(), str); emit dataChanged(index, index); return true;
}
QModelIndex FtpFileSystemModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
{
return QModelIndex();
}QTreeWidgetItem *parent_item; if(!parent.isValid()) { parent_item = root_item; } else { parent_item = static_cast<QTreeWidgetItem*>(parent.internalPointer()); } //QTreeWidgetItem *child_item = parent_item->child(row); QTreeWidgetItem *child_item = remote_file_list->at(row); if(child_item) { return createIndex(row, column, child_item); } else { return QModelIndex(); }
}@
The data() method always return an empty QVariant(). Hence, the view remains empty.
I have debugged this step-by-step, and it seems that the data() method runs always after slot_list_info(). So, the data is already there. I have double checked it, by using a qDebug() in my slot_command_finished() and querying the model in a for loop. I can see that the data is there, and I am using the index to reach it.
I have no idea, though, how QAbstractItemModel calls data() and how it uses index() to call it. So, I guess my problem is there. The index in data() is always invalid for some reason. I guess I might need to reimplement some other method, but for now I have no clue what.
I'll post if I find anything. Thanks for reading :)
-
Uggh!
Shame on me. It turns out I had to emit a couple more signals at the right places.
@ emit layoutAboutToBeChanged();
remote_file_list->append(item);
emit layoutChanged();@This is from slot_list_info(QUrlInfo), which is what takes the info from QFtp and puts it into the model structures.
Finally, I can continue with this nonsense :lol:
I will keep you informed.
-
Hello, OP.
Did you finish this implementation?
-
Hello, OP.
Did you finish this implementation?
-
Sorry, but I had to put this one into pause for the time being. Real life distracted me with other issues and right now I have no time for development at all.
-
Sorry, but I had to put this one into pause for the time being. Real life distracted me with other issues and right now I have no time for development at all.