这篇文章中,探索Qt中的类qfileinfogatherer类,先给出私有类头文件。我们先想一想要形成一个信息采集者,需要什么?需要一个线程,当文件信息发生变化的时候,作为一个槽来接收信号。
先预备一些小知识,关于QT_REQUIRE_CONFIG,
1 /**************************************************************************** 2 ** 3 ** Copyright (C) 2016 The Qt Company Ltd. 4 ** Contact: https://www.qt.io/licensing/ 5 ** 6 ** This file is part of the QtWidgets module of the Qt Toolkit. 7 ** 8 ** $QT_BEGIN_LICENSE:LGPL$ 9 ** Commercial License Usage 10 ** Licensees holding valid commercial Qt licenses may use this file in 11 ** accordance with the commercial license agreement provided with the 12 ** Software or, alternatively, in accordance with the terms contained in 13 ** a written agreement between you and The Qt Company. For licensing terms 14 ** and conditions see https://www.qt.io/terms-conditions. For further 15 ** information use the contact form at https://www.qt.io/contact-us. 16 ** 17 ** GNU Lesser General Public License Usage 18 ** Alternatively, this file may be used under the terms of the GNU Lesser 19 ** General Public License version 3 as published by the Free Software 20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the 21 ** packaging of this file. Please review the following information to 22 ** ensure the GNU Lesser General Public License version 3 requirements 23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 24 ** 25 ** GNU General Public License Usage 26 ** Alternatively, this file may be used under the terms of the GNU 27 ** General Public License version 2.0 or (at your option) the GNU General 28 ** Public license version 3 or any later version approved by the KDE Free 29 ** Qt Foundation. The licenses are as published by the Free Software 30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 31 ** included in the packaging of this file. Please review the following 32 ** information to ensure the GNU General Public License requirements will 33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and 34 ** https://www.gnu.org/licenses/gpl-3.0.html. 35 ** 36 ** $QT_END_LICENSE$ 37 ** 38 ****************************************************************************/ 39 40 #ifndef QFILEINFOGATHERER_H 41 #define QFILEINFOGATHERER_H 42 43 // 44 // W A R N I N G 45 // ------------- 46 // 47 // This file is not part of the Qt API. It exists purely as an 48 // implementation detail. This header file may change from version to 49 // version without notice, or even be removed. 50 // 51 // We mean it. 52 // 53 54 #include <QtWidgets/private/qtwidgetsglobal_p.h> 55 56 #include <qthread.h> 57 #include <qmutex.h> 58 #include <qwaitcondition.h> 59 #include <qfilesystemwatcher.h> 60 #include <qfileiconprovider.h> 61 #include <qpair.h> 62 #include <qstack.h> 63 #include <qdatetime.h> 64 #include <qdir.h> 65 #include <qelapsedtimer.h> 66 67 #include <private/qfilesystemengine_p.h> 68 69 QT_REQUIRE_CONFIG(filesystemmodel); 70 71 QT_BEGIN_NAMESPACE 72 73 class QExtendedInformation { 74 public: 75 enum Type { Dir, File, System }; 76 77 QExtendedInformation() {} 78 QExtendedInformation(const QFileInfo &info) : mFileInfo(info) {} 79 80 inline bool isDir() { return type() == Dir; } 81 inline bool isFile() { return type() == File; } 82 inline bool isSystem() { return type() == System; } 83 84 bool operator ==(const QExtendedInformation &fileInfo) const { 85 return mFileInfo == fileInfo.mFileInfo 86 && displayType == fileInfo.displayType 87 && permissions() == fileInfo.permissions(); 88 } 89 90 #ifndef QT_NO_FSFILEENGINE 91 bool isCaseSensitive() const { 92 return QFileSystemEngine::isCaseSensitive(); 93 } 94 #endif 95 96 QFile::Permissions permissions() const { 97 return mFileInfo.permissions(); 98 } 99 100 Type type() const { 101 if (mFileInfo.isDir()) { 102 return QExtendedInformation::Dir; 103 } 104 if (mFileInfo.isFile()) { 105 return QExtendedInformation::File; 106 } 107 if (!mFileInfo.exists() && mFileInfo.isSymLink()) { 108 return QExtendedInformation::System; 109 } 110 return QExtendedInformation::System; 111 } 112 113 bool isSymLink(bool ignoreNtfsSymLinks = false) const 114 { 115 if (ignoreNtfsSymLinks) { 116 #ifdef Q_OS_WIN 117 return !mFileInfo.suffix().compare(QLatin1String("lnk"), Qt::CaseInsensitive); 118 #endif 119 } 120 return mFileInfo.isSymLink(); 121 } 122 123 bool isHidden() const { 124 return mFileInfo.isHidden(); 125 } 126 127 QFileInfo fileInfo() const { 128 return mFileInfo; 129 } 130 131 QDateTime lastModified() const { 132 return mFileInfo.lastModified(); 133 } 134 135 qint64 size() const { 136 qint64 size = -1; 137 if (type() == QExtendedInformation::Dir) 138 size = 0; 139 if (type() == QExtendedInformation::File) 140 size = mFileInfo.size(); 141 if (!mFileInfo.exists() && !mFileInfo.isSymLink()) 142 size = -1; 143 return size; 144 } 145 146 QString displayType; 147 QIcon icon; 148 149 private : 150 QFileInfo mFileInfo; 151 }; 152 153 class QFileIconProvider; 154 155 class Q_AUTOTEST_EXPORT QFileInfoGatherer : public QThread 156 { 157 Q_OBJECT 158 159 Q_SIGNALS: 160 void updates(const QString &directory, const QVector<QPair<QString, QFileInfo> > &updates); 161 void newListOfFiles(const QString &directory, const QStringList &listOfFiles) const; 162 void nameResolved(const QString &fileName, const QString &resolvedName) const; 163 void directoryLoaded(const QString &path); 164 165 public: 166 explicit QFileInfoGatherer(QObject *parent = 0); 167 ~QFileInfoGatherer(); 168 169 // only callable from this->thread(): 170 void clear(); 171 void removePath(const QString &path); 172 QExtendedInformation getInfo(const QFileInfo &info) const; 173 QFileIconProvider *iconProvider() const; 174 bool resolveSymlinks() const; 175 176 public Q_SLOTS: 177 void list(const QString &directoryPath); 178 void fetchExtendedInformation(const QString &path, const QStringList &files); 179 void updateFile(const QString &path); 180 void setResolveSymlinks(bool enable); 181 void setIconProvider(QFileIconProvider *provider); 182 183 private Q_SLOTS: 184 void driveAdded(); 185 void driveRemoved(); 186 187 private: 188 void run() Q_DECL_OVERRIDE; 189 // called by run(): 190 void getFileInfos(const QString &path, const QStringList &files); 191 void fetch(const QFileInfo &info, QElapsedTimer &base, bool &firstTime, QVector<QPair<QString, QFileInfo> > &updatedFiles, const QString &path); 192 193 private: 194 mutable QMutex mutex; 195 // begin protected by mutex 196 QWaitCondition condition; 197 QStack<QString> path; 198 QStack<QStringList> files; 199 // end protected by mutex 200 QAtomicInt abort; 201 202 #ifndef QT_NO_FILESYSTEMWATCHER 203 QFileSystemWatcher *watcher; 204 #endif 205 #ifdef Q_OS_WIN 206 bool m_resolveSymlinks; // not accessed by run() 207 #endif 208 QFileIconProvider *m_iconProvider; // not accessed by run() 209 QFileIconProvider defaultProvider; 210 }; 211 212 QT_END_NAMESPACE 213 #endif // QFILEINFOGATHERER_H
接下来是它所对应的cpp文件:
1 /**************************************************************************** 2 ** 3 ** Copyright (C) 2016 The Qt Company Ltd. 4 ** Contact: https://www.qt.io/licensing/ 5 ** 6 ** This file is part of the QtWidgets module of the Qt Toolkit. 7 ** 8 ** $QT_BEGIN_LICENSE:LGPL$ 9 ** Commercial License Usage 10 ** Licensees holding valid commercial Qt licenses may use this file in 11 ** accordance with the commercial license agreement provided with the 12 ** Software or, alternatively, in accordance with the terms contained in 13 ** a written agreement between you and The Qt Company. For licensing terms 14 ** and conditions see https://www.qt.io/terms-conditions. For further 15 ** information use the contact form at https://www.qt.io/contact-us. 16 ** 17 ** GNU Lesser General Public License Usage 18 ** Alternatively, this file may be used under the terms of the GNU Lesser 19 ** General Public License version 3 as published by the Free Software 20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the 21 ** packaging of this file. Please review the following information to 22 ** ensure the GNU Lesser General Public License version 3 requirements 23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 24 ** 25 ** GNU General Public License Usage 26 ** Alternatively, this file may be used under the terms of the GNU 27 ** General Public License version 2.0 or (at your option) the GNU General 28 ** Public license version 3 or any later version approved by the KDE Free 29 ** Qt Foundation. The licenses are as published by the Free Software 30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 31 ** included in the packaging of this file. Please review the following 32 ** information to ensure the GNU General Public License requirements will 33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and 34 ** https://www.gnu.org/licenses/gpl-3.0.html. 35 ** 36 ** $QT_END_LICENSE$ 37 ** 38 ****************************************************************************/ 39 40 #include "qfileinfogatherer_p.h" 41 #include <qdebug.h> 42 #include <qdiriterator.h> 43 #ifndef Q_OS_WIN 44 # include <unistd.h> 45 # include <sys/types.h> 46 #endif 47 #if defined(Q_OS_VXWORKS) 48 # include "qplatformdefs.h" 49 #endif 50 51 QT_BEGIN_NAMESPACE 52 53 #ifdef QT_BUILD_INTERNAL 54 static QBasicAtomicInt fetchedRoot = Q_BASIC_ATOMIC_INITIALIZER(false); 55 Q_AUTOTEST_EXPORT void qt_test_resetFetchedRoot() 56 { 57 fetchedRoot.store(false); 58 } 59 60 Q_AUTOTEST_EXPORT bool qt_test_isFetchedRoot() 61 { 62 return fetchedRoot.load(); 63 } 64 #endif 65 66 static QString translateDriveName(const QFileInfo &drive) 67 { 68 QString driveName = drive.absoluteFilePath(); 69 #ifdef Q_OS_WIN 70 if (driveName.startsWith(QLatin1Char('/'))) // UNC host 71 return drive.fileName(); 72 if (driveName.endsWith(QLatin1Char('/'))) 73 driveName.chop(1); 74 #endif // Q_OS_WIN 75 return driveName; 76 } 77 78 /*! 79 Creates thread 80 */ 81 QFileInfoGatherer::QFileInfoGatherer(QObject *parent) 82 : QThread(parent), abort(false), 83 #ifndef QT_NO_FILESYSTEMWATCHER 84 watcher(0), 85 #endif 86 #ifdef Q_OS_WIN 87 m_resolveSymlinks(true), 88 #endif 89 m_iconProvider(&defaultProvider) 90 { 91 #ifndef QT_NO_FILESYSTEMWATCHER 92 watcher = new QFileSystemWatcher(this); 93 connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(list(QString))); 94 connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(updateFile(QString))); 95 96 # if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) 97 const QVariant listener = watcher->property("_q_driveListener"); 98 if (listener.canConvert<QObject *>()) { 99 if (QObject *driveListener = listener.value<QObject *>()) { 100 connect(driveListener, SIGNAL(driveAdded()), this, SLOT(driveAdded())); 101 connect(driveListener, SIGNAL(driveRemoved()), this, SLOT(driveRemoved())); 102 } 103 } 104 # endif // Q_OS_WIN && !Q_OS_WINRT 105 #endif 106 start(LowPriority); 107 } 108 109 /*! 110 Destroys thread 111 */ 112 QFileInfoGatherer::~QFileInfoGatherer() 113 { 114 abort.store(true); 115 QMutexLocker locker(&mutex); 116 condition.wakeAll(); 117 locker.unlock(); 118 wait(); 119 } 120 121 void QFileInfoGatherer::setResolveSymlinks(bool enable) 122 { 123 Q_UNUSED(enable); 124 #ifdef Q_OS_WIN 125 m_resolveSymlinks = enable; 126 #endif 127 } 128 129 void QFileInfoGatherer::driveAdded() 130 { 131 fetchExtendedInformation(QString(), QStringList()); 132 } 133 134 void QFileInfoGatherer::driveRemoved() 135 { 136 QStringList drives; 137 const QFileInfoList driveInfoList = QDir::drives(); 138 for (const QFileInfo &fi : driveInfoList) 139 drives.append(translateDriveName(fi)); 140 newListOfFiles(QString(), drives); 141 } 142 143 bool QFileInfoGatherer::resolveSymlinks() const 144 { 145 #ifdef Q_OS_WIN 146 return m_resolveSymlinks; 147 #else 148 return false; 149 #endif 150 } 151 152 void QFileInfoGatherer::setIconProvider(QFileIconProvider *provider) 153 { 154 m_iconProvider = provider; 155 } 156 157 QFileIconProvider *QFileInfoGatherer::iconProvider() const 158 { 159 return m_iconProvider; 160 } 161 162 /*! 163 Fetch extended information for all \a files in \a path 164 165 \sa updateFile(), update(), resolvedName() 166 */ 167 void QFileInfoGatherer::fetchExtendedInformation(const QString &path, const QStringList &files) 168 { 169 QMutexLocker locker(&mutex); 170 // See if we already have this dir/file in our queue 171 int loc = this->path.lastIndexOf(path); 172 while (loc > 0) { 173 if (this->files.at(loc) == files) { 174 return; 175 } 176 loc = this->path.lastIndexOf(path, loc - 1); 177 } 178 this->path.push(path); 179 this->files.push(files); 180 condition.wakeAll(); 181 182 #ifndef QT_NO_FILESYSTEMWATCHER 183 if (files.isEmpty() 184 && !path.isEmpty() 185 && !path.startsWith(QLatin1String("//")) /*don't watch UNC path*/) { 186 if (!watcher->directories().contains(path)) 187 watcher->addPath(path); 188 } 189 #endif 190 } 191 192 /*! 193 Fetch extended information for all \a filePath 194 195 \sa fetchExtendedInformation() 196 */ 197 void QFileInfoGatherer::updateFile(const QString &filePath) 198 { 199 QString dir = filePath.mid(0, filePath.lastIndexOf(QDir::separator())); 200 QString fileName = filePath.mid(dir.length() + 1); 201 fetchExtendedInformation(dir, QStringList(fileName)); 202 } 203 204 /* 205 List all files in \a directoryPath 206 207 \sa listed() 208 */ 209 void QFileInfoGatherer::clear() 210 { 211 #ifndef QT_NO_FILESYSTEMWATCHER 212 QMutexLocker locker(&mutex); 213 watcher->removePaths(watcher->files()); 214 watcher->removePaths(watcher->directories()); 215 #endif 216 } 217 218 /* 219 Remove a \a path from the watcher 220 221 \sa listed() 222 */ 223 void QFileInfoGatherer::removePath(const QString &path) 224 { 225 #ifndef QT_NO_FILESYSTEMWATCHER 226 QMutexLocker locker(&mutex); 227 watcher->removePath(path); 228 #else 229 Q_UNUSED(path); 230 #endif 231 } 232 233 /* 234 List all files in \a directoryPath 235 236 \sa listed() 237 */ 238 void QFileInfoGatherer::list(const QString &directoryPath) 239 { 240 fetchExtendedInformation(directoryPath, QStringList()); 241 } 242 243 /* 244 Until aborted wait to fetch a directory or files 245 */ 246 void QFileInfoGatherer::run() 247 { 248 forever { 249 QMutexLocker locker(&mutex); 250 while (!abort.load() && path.isEmpty()) 251 condition.wait(&mutex); 252 if (abort.load()) 253 return; 254 const QString thisPath = qAsConst(path).front(); 255 path.pop_front(); 256 const QStringList thisList = qAsConst(files).front(); 257 files.pop_front(); 258 locker.unlock(); 259 260 getFileInfos(thisPath, thisList); 261 } 262 } 263 264 QExtendedInformation QFileInfoGatherer::getInfo(const QFileInfo &fileInfo) const 265 { 266 QExtendedInformation info(fileInfo); 267 info.icon = m_iconProvider->icon(fileInfo); 268 info.displayType = m_iconProvider->type(fileInfo); 269 #ifndef QT_NO_FILESYSTEMWATCHER 270 // ### Not ready to listen all modifications 271 #if 0 272 // Enable the next two commented out lines to get updates when the file sizes change... 273 if (!fileInfo.exists() && !fileInfo.isSymLink()) { 274 info.size = -1; 275 //watcher->removePath(fileInfo.absoluteFilePath()); 276 } else { 277 if (!fileInfo.absoluteFilePath().isEmpty() && fileInfo.exists() && fileInfo.isReadable() 278 && !watcher->files().contains(fileInfo.absoluteFilePath())) { 279 //watcher->addPath(fileInfo.absoluteFilePath()); 280 } 281 } 282 #endif 283 #endif 284 285 #ifdef Q_OS_WIN 286 if (m_resolveSymlinks && info.isSymLink(/* ignoreNtfsSymLinks = */ true)) { 287 QFileInfo resolvedInfo(fileInfo.symLinkTarget()); 288 resolvedInfo = resolvedInfo.canonicalFilePath(); 289 if (resolvedInfo.exists()) { 290 emit nameResolved(fileInfo.filePath(), resolvedInfo.fileName()); 291 } 292 } 293 #endif 294 return info; 295 } 296 297 /* 298 Get specific file info's, batch the files so update when we have 100 299 items and every 200ms after that 300 */ 301 void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &files) 302 { 303 // List drives 304 if (path.isEmpty()) { 305 #ifdef QT_BUILD_INTERNAL 306 fetchedRoot.store(true); 307 #endif 308 QFileInfoList infoList; 309 if (files.isEmpty()) { 310 infoList = QDir::drives(); 311 } else { 312 infoList.reserve(files.count()); 313 for (const auto &file : files) 314 infoList << QFileInfo(file); 315 } 316 for (int i = infoList.count() - 1; i >= 0; --i) { 317 QString driveName = translateDriveName(infoList.at(i)); 318 QVector<QPair<QString,QFileInfo> > updatedFiles; 319 updatedFiles.append(QPair<QString,QFileInfo>(driveName, infoList.at(i))); 320 emit updates(path, updatedFiles); 321 } 322 return; 323 } 324 325 QElapsedTimer base; 326 base.start(); 327 QFileInfo fileInfo; 328 bool firstTime = true; 329 QVector<QPair<QString, QFileInfo> > updatedFiles; 330 QStringList filesToCheck = files; 331 332 QString itPath = QDir::fromNativeSeparators(files.isEmpty() ? path : QLatin1String("")); 333 QDirIterator dirIt(itPath, QDir::AllEntries | QDir::System | QDir::Hidden); 334 QStringList allFiles; 335 while (!abort.load() && dirIt.hasNext()) { 336 dirIt.next(); 337 fileInfo = dirIt.fileInfo(); 338 allFiles.append(fileInfo.fileName()); 339 fetch(fileInfo, base, firstTime, updatedFiles, path); 340 } 341 if (!allFiles.isEmpty()) 342 emit newListOfFiles(path, allFiles); 343 344 QStringList::const_iterator filesIt = filesToCheck.constBegin(); 345 while (!abort.load() && filesIt != filesToCheck.constEnd()) { 346 fileInfo.setFile(path + QDir::separator() + *filesIt); 347 ++filesIt; 348 fetch(fileInfo, base, firstTime, updatedFiles, path); 349 } 350 if (!updatedFiles.isEmpty()) 351 emit updates(path, updatedFiles); 352 emit directoryLoaded(path); 353 } 354 355 void QFileInfoGatherer::fetch(const QFileInfo &fileInfo, QElapsedTimer &base, bool &firstTime, QVector<QPair<QString, QFileInfo> > &updatedFiles, const QString &path) { 356 updatedFiles.append(QPair<QString, QFileInfo>(fileInfo.fileName(), fileInfo)); 357 QElapsedTimer current; 358 current.start(); 359 if ((firstTime && updatedFiles.count() > 100) || base.msecsTo(current) > 1000) { 360 emit updates(path, updatedFiles); 361 updatedFiles.clear(); 362 base = current; 363 firstTime = false; 364 } 365 } 366 367 QT_END_NAMESPACE 368 369 #include "moc_qfileinfogatherer_p.cpp"