如何在QFileSystemModel中顯示文件夾的大小

  在Qt裏面,有一種Model/View框架,Model負責收集信息,View負責顯示信息。QFileSystemModel能夠讀取文件大小,可是默認狀況下不能讀取文件夾大小。node

  QFileSystemModel裏面有一個size()函數,獲取一個index對應的文件的大小。git

1 qint64 QFileSystemModel::size(const QModelIndex &index) const
2 {
3     Q_D(const QFileSystemModel);
4     if (!index.isValid())
5         return 0;
6     return d->node(index)->size();
7 }

  這裏的Q_D指針經過宏定義指向QFileSystemModel的私有類:QFileSystemPrivate。github

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

     寫一個類MyQFileSystemInfo繼承QFileSystemInfo,重寫它的size()函數,對具體文件直接返回文件大小,而對文件夾則遞歸加和計算每一個子文件夾的大小以獲得它的大小。windows

  惋惜,仍然仍是不能在view中實時顯示文件夾的大小,看來view中使用的size並非model的size()函數。github的代碼連接:https://github.com/1171597779/big_file_inspector框架

  在QFileSystemModelPrivate裏面也有一個size()函數。函數

 1 QString QFileSystemModelPrivate::size(const QModelIndex &index) const
 2 {
 3     if (!index.isValid())
 4         return QString();
 5     const QFileSystemNode *n = node(index);
 6     if (n->isDir()) {
 7 #ifdef Q_OS_MAC
 8         return QLatin1String("--");
 9 #else
10         return QLatin1String("");
11 #endif
12     // Windows   - ""
13     // OS X      - "--"
14     // Konqueror - "4 KB"
15     // Nautilus  - "9 items" (the number of children)
16     }
17     return size(n->size());
18 }

  上面函數的17行調用了重載size()函數:ui

 1 QString QFileSystemModelPrivate::size(qint64 bytes)
 2 {
 3     // According to the Si standard KB is 1000 bytes, KiB is 1024
 4     // but on windows sizes are calculated by dividing by 1024 so we do what they do.
 5     const qint64 kb = 1024;
 6     const qint64 mb = 1024 * kb;
 7     const qint64 gb = 1024 * mb;
 8     const qint64 tb = 1024 * gb;
 9     if (bytes >= tb)
10         return QFileSystemModel::tr("%1 TB").arg(QLocale().toString(qreal(bytes) / tb, 'f', 3));
11     if (bytes >= gb)
12         return QFileSystemModel::tr("%1 GB").arg(QLocale().toString(qreal(bytes) / gb, 'f', 2));
13     if (bytes >= mb)
14         return QFileSystemModel::tr("%1 MB").arg(QLocale().toString(qreal(bytes) / mb, 'f', 1));
15     if (bytes >= kb)
16         return QFileSystemModel::tr("%1 KB").arg(QLocale().toString(bytes / kb));
17     return QFileSystemModel::tr("%1 bytes").arg(QLocale().toString(bytes));
18 }

  QFileSystemModel的頭文件裏聲明瞭私有類QFileSystemPrivate,轉到這個私有類的頭文件裏面去,在這個類裏定義了另一個類QFileSystemNode。在這個類的構造函數中,出現一個QExtendedInformation*指針類型的info實參。這個實參在QFileSystemPrivate類裏面是一個公有成員,文件或者文件夾的大小就是經過下面的QFileSystemNode的內聯函數實現的。spa

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

  QExtendedinformation類中存在一個QFileInfo類的成員mFileInfo,並經過size()方法來根據具體狀況獲取文件大小:指針

 1     qint64 size() const {
 2         qint64 size = -1;
 3         if (type() == QExtendedInformation::Dir)
 4             size = 0;
 5         if (type() == QExtendedInformation::File)
 6             size = mFileInfo.size();
 7         if (!mFileInfo.exists() && !mFileInfo.isSymLink())
 8             size = -1;
 9         return size;
10     }

  而QFileInfo的size()方法以下:code

 1 qint64 QFileInfo::size() const
 2 {
 3     Q_D(const QFileInfo);
 4     if (d->isDefaultConstructed)
 5         return 0;
 6     if (d->fileEngine == 0) {
 7         if (!d->cache_enabled || !d->metaData.hasFlags(QFileSystemMetaData::SizeAttribute))
 8             QFileSystemEngine::fillMetaData(d->fileEntry, d->metaData, QFileSystemMetaData::SizeAttribute);
 9         return d->metaData.size();
10     }
11     if (!d->getCachedFlag(QFileInfoPrivate::CachedSize)) {
12         d->setCachedFlag(QFileInfoPrivate::CachedSize);
13         d->fileSize = d->fileEngine->size();
14     }
15     return d->fileSize;
16 }

  問題來了,要計算一個文件夾的大小,須要很長的時間,不能用內聯函數,所以須要重載一下,放棄內聯函數的寫法。

  總結一下,QFileSystemModel裏面的size()調用QFileSystemModelPrivate的size(),而這個函數裏會使用的QFileSystemNode的內聯函數size(),這裏面又轉到QExtendedinformation裏面去,最後是QFileInfo的size()方法。經過重寫QFileSystemModel的size()函數並不能實現treeview中文件夾大小的實時顯示,須要找出treeview中何時調用size()函數。

  從QFileSystemModelPrivate類的init()函數能夠看到一些端倪:

 1 void QFileSystemModelPrivate::init()
 2 {
 3     Q_Q(QFileSystemModel);
 4     qRegisterMetaType<QVector<QPair<QString,QFileInfo> > >();
 5 #ifndef QT_NO_FILESYSTEMWATCHER
 6     q->connect(&fileInfoGatherer, SIGNAL(newListOfFiles(QString,QStringList)),
 7                q, SLOT(_q_directoryChanged(QString,QStringList)));
 8     q->connect(&fileInfoGatherer, SIGNAL(updates(QString,QVector<QPair<QString,QFileInfo> >)),
 9             q, SLOT(_q_fileSystemChanged(QString,QVector<QPair<QString,QFileInfo> >)));
10     q->connect(&fileInfoGatherer, SIGNAL(nameResolved(QString,QString)),
11             q, SLOT(_q_resolvedName(QString,QString)));
12     q->connect(&fileInfoGatherer, SIGNAL(directoryLoaded(QString)),
13                q, SIGNAL(directoryLoaded(QString)));
14 #endif // !QT_NO_FILESYSTEMWATCHER
15     q->connect(&delayedSortTimer, SIGNAL(timeout()), q, SLOT(_q_performDelayedSort()), Qt::QueuedConnection);
16 
17     roleNames.insertMulti(QFileSystemModel::FileIconRole, QByteArrayLiteral("fileIcon")); // == Qt::decoration
18     roleNames.insert(QFileSystemModel::FilePathRole, QByteArrayLiteral("filePath"));
19     roleNames.insert(QFileSystemModel::FileNameRole, QByteArrayLiteral("fileName"));
20     roleNames.insert(QFileSystemModel::FilePermissions, QByteArrayLiteral("filePermissions"));
21 }

  經過看到QFileSystemModelPrivate裏面的方法addNote()纔看到一些但願:

 1 QFileSystemModelPrivate::QFileSystemNode* QFileSystemModelPrivate::addNode(QFileSystemNode *parentNode, const QString &fileName, const QFileInfo& info)
 2 {
 3     // In the common case, itemLocation == count() so check there first
 4     QFileSystemModelPrivate::QFileSystemNode *node = new QFileSystemModelPrivate::QFileSystemNode(fileName, parentNode);
 5 #ifndef QT_NO_FILESYSTEMWATCHER
 6     node->populate(info);
 7 #else
 8     Q_UNUSED(info)
 9 #endif
10 #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
11     //The parentNode is "" so we are listing the drives
12     if (parentNode->fileName.isEmpty()) {
13         wchar_t name[MAX_PATH + 1];
14         //GetVolumeInformation requires to add trailing backslash
15         const QString nodeName = fileName + QLatin1String("\\");
16         BOOL success = ::GetVolumeInformation((wchar_t *)(nodeName.utf16()),
17                 name, MAX_PATH + 1, NULL, 0, NULL, NULL, 0);
18         if (success && name[0])
19             node->volumeName = QString::fromWCharArray(name);
20     }
21 #endif
22     Q_ASSERT(!parentNode->children.contains(fileName));
23     parentNode->children.insert(fileName, node);
24     return node;
25 }

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

1         void populate(const QExtendedInformation &fileInfo) {
2             if (!info)
3                 info = new QExtendedInformation(fileInfo.fileInfo());
4             (*info) = fileInfo;
5         }
相關文章
相關標籤/搜索