在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 }