这篇文章中,探索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"