如何在QFileSystemModel中显示文件夹的大小

时间:2023-03-09 16:47:01
如何在QFileSystemModel中显示文件夹的大小

  在Qt里面,有一种Model/View框架,Model负责收集信息,View负责显示信息。QFileSystemModel可以读取文件大小,但是默认情况下不能读取文件夹大小。

  QFileSystemModel里面有一个size()函数,获取一个index对应的文件的大小。

 qint64 QFileSystemModel::size(const QModelIndex &index) const
{
Q_D(const QFileSystemModel);
if (!index.isValid())
return ;
return d->node(index)->size();
}

  这里的Q_D指针通过宏定义指向QFileSystemModel的私有类:QFileSystemPrivate。

     #define Q_D(Class) Class##Private * const d = d_func()

 写一个类MyQFileSystemInfo继承QFileSystemInfo,重写它的size()函数,对具体文件直接返回文件大小,而对文件夹则递归加和计算每个子文件夹的大小以得到它的大小。

  可惜,仍然还是不能在view中实时显示文件夹的大小,看来view中使用的size并不是model的size()函数。github的代码链接:https://github.com/1171597779/big_file_inspector

  在QFileSystemModelPrivate里面也有一个size()函数。

 QString QFileSystemModelPrivate::size(const QModelIndex &index) const
{
if (!index.isValid())
return QString();
const QFileSystemNode *n = node(index);
if (n->isDir()) {
#ifdef Q_OS_MAC
return QLatin1String("--");
#else
return QLatin1String("");
#endif
// Windows - ""
// OS X - "--"
// Konqueror - "4 KB"
// Nautilus - "9 items" (the number of children)
}
return size(n->size());
}

  上面函数的17行调用了重载size()函数:

 QString QFileSystemModelPrivate::size(qint64 bytes)
{
// According to the Si standard KB is 1000 bytes, KiB is 1024
// but on windows sizes are calculated by dividing by 1024 so we do what they do.
const qint64 kb = ;
const qint64 mb = * kb;
const qint64 gb = * mb;
const qint64 tb = * gb;
if (bytes >= tb)
return QFileSystemModel::tr("%1 TB").arg(QLocale().toString(qreal(bytes) / tb, 'f', ));
if (bytes >= gb)
return QFileSystemModel::tr("%1 GB").arg(QLocale().toString(qreal(bytes) / gb, 'f', ));
if (bytes >= mb)
return QFileSystemModel::tr("%1 MB").arg(QLocale().toString(qreal(bytes) / mb, 'f', ));
if (bytes >= kb)
return QFileSystemModel::tr("%1 KB").arg(QLocale().toString(bytes / kb));
return QFileSystemModel::tr("%1 bytes").arg(QLocale().toString(bytes));
}

  QFileSystemModel的头文件里声明了私有类QFileSystemPrivate,转到这个私有类的头文件里面去,在这个类里定义了另外一个类QFileSystemNode。在这个类的构造函数中,出现一个QExtendedInformation*指针类型的info实参。这个实参在QFileSystemPrivate类里面是一个公有成员,文件或者文件夹的大小就是通过下面的QFileSystemNode的内联函数实现的。

inline qint64 size() const { if (info && !info->isDir()) return info->size(); return ; }

  QExtendedinformation类中存在一个QFileInfo类的成员mFileInfo,并通过size()方法来根据具体情况获取文件大小:

     qint64 size() const {
qint64 size = -;
if (type() == QExtendedInformation::Dir)
size = ;
if (type() == QExtendedInformation::File)
size = mFileInfo.size();
if (!mFileInfo.exists() && !mFileInfo.isSymLink())
size = -;
return size;
}

  而QFileInfo的size()方法如下:

 qint64 QFileInfo::size() const
{
Q_D(const QFileInfo);
if (d->isDefaultConstructed)
return ;
if (d->fileEngine == ) {
if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::SizeAttribute))
QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::SizeAttribute);
return d->metaData.size();
}
if (!d->getCachedFlag(QFileInfoPrivate::CachedSize)) {
d->setCachedFlag(QFileInfoPrivate::CachedSize);
d->fileSize = d->fileEngine->size();
}
return d->fileSize;
}

  问题来了,要计算一个文件夹的大小,需要很长的时间,不能用内联函数,因此需要重载一下,放弃内联函数的写法。

  总结一下,QFileSystemModel里面的size()调用QFileSystemModelPrivate的size(),而这个函数里会使用的QFileSystemNode的内联函数size(),这里面又转到QExtendedinformation里面去,最后是QFileInfo的size()方法。通过重写QFileSystemModel的size()函数并不能实现treeview中文件夹大小的实时显示,需要找出treeview中什么时候调用size()函数。

  从QFileSystemModelPrivate类的init()函数可以看到一些端倪:

 void QFileSystemModelPrivate::init()
{
Q_Q(QFileSystemModel);
qRegisterMetaType<QVector<QPair<QString,QFileInfo> > >();
#ifndef QT_NO_FILESYSTEMWATCHER
q->connect(&fileInfoGatherer, SIGNAL(newListOfFiles(QString,QStringList)),
q, SLOT(_q_directoryChanged(QString,QStringList)));
q->connect(&fileInfoGatherer, SIGNAL(updates(QString,QVector<QPair<QString,QFileInfo> >)),
q, SLOT(_q_fileSystemChanged(QString,QVector<QPair<QString,QFileInfo> >)));
q->connect(&fileInfoGatherer, SIGNAL(nameResolved(QString,QString)),
q, SLOT(_q_resolvedName(QString,QString)));
q->connect(&fileInfoGatherer, SIGNAL(directoryLoaded(QString)),
q, SIGNAL(directoryLoaded(QString)));
#endif // !QT_NO_FILESYSTEMWATCHER
q->connect(&delayedSortTimer, SIGNAL(timeout()), q, SLOT(_q_performDelayedSort()), Qt::QueuedConnection); roleNames.insertMulti(QFileSystemModel::FileIconRole, QByteArrayLiteral("fileIcon")); // == Qt::decoration
roleNames.insert(QFileSystemModel::FilePathRole, QByteArrayLiteral("filePath"));
roleNames.insert(QFileSystemModel::FileNameRole, QByteArrayLiteral("fileName"));
roleNames.insert(QFileSystemModel::FilePermissions, QByteArrayLiteral("filePermissions"));
}

  通过看到QFileSystemModelPrivate里面的方法addNote()才看到一些希望:

 QFileSystemModelPrivate::QFileSystemNode* QFileSystemModelPrivate::addNode(QFileSystemNode *parentNode, const QString &fileName, const QFileInfo& info)
{
// In the common case, itemLocation == count() so check there first
QFileSystemModelPrivate::QFileSystemNode *node = new QFileSystemModelPrivate::QFileSystemNode(fileName, parentNode);
#ifndef QT_NO_FILESYSTEMWATCHER
node->populate(info);
#else
Q_UNUSED(info)
#endif
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
//The parentNode is "" so we are listing the drives
if (parentNode->fileName.isEmpty()) {
wchar_t name[MAX_PATH + ];
//GetVolumeInformation requires to add trailing backslash
const QString nodeName = fileName + QLatin1String("\\");
BOOL success = ::GetVolumeInformation((wchar_t *)(nodeName.utf16()),
name, MAX_PATH + , NULL, , NULL, NULL, );
if (success && name[])
node->volumeName = QString::fromWCharArray(name);
}
#endif
Q_ASSERT(!parentNode->children.contains(fileName));
parentNode->children.insert(fileName, node);
return node;
}

  上面的node->populate(),这里面就提取了文件信息:

         void populate(const QExtendedInformation &fileInfo) {
if (!info)
info = new QExtendedInformation(fileInfo.fileInfo());
(*info) = fileInfo;
}