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&#40;&#41;)
    {
        //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.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.