Qt Creator 的查找对话框允许用户使用文本或者正则表达式进行搜索。点击“编辑-查找/替换-高级查找”即可打开查找对话框。
查找对话框:
在查找对话框中,“范围”和“文件模式”两项都是可以扩展的。也是就说,我们可以利用插件,向范围下拉框添加更多项目,每次都可以依据选择的下拉项不同,读取不同的配置文件。在 Qt Creator 中,范围下拉框中的每一项都叫做“查找过滤器(find filter)”。
Qt Creator 的 find 插件暴露出的接口是 Find::IFindFilter ,该接口在 src/plugins/find/ 中声明:
#ifndef IFINDFILTER_H#define IFINDFILTER_H#include "find_"#include ""QT_BEGIN_NAMESPACEclass QWidget;class QSettings;class QKeySequence;QT_END_NAMESPACEnamespace Find {class FIND_EXPORT IFindFilter : public QObject{ Q_OBJECTpublic: virtual ~IFindFilter() {} virtual QString id() const = 0; virtual QString displayName() const = 0; /// virtual bool isEnabled() const = 0; virtual bool canCancel() const = 0; virtual void cancel() = 0; virtual QKeySequence defaultShortcut() const; virtual bool isReplaceSupported() const { return false; } virtual FindFlags supportedFindFlags() const; virtual void findAll(const QString &txt, Find::FindFlags findFlags) = 0; virtual void replaceAll(const QString &txt, Find::FindFlags findFlags) { Q_UNUSED(txt) Q_UNUSED(findFlags) } virtual QWidget *createConfigWidget() { return 0; } virtual void writeSettings(QSettings *settings) { Q_UNUSED(settings) } virtual void readSettings(QSettings *settings) { Q_UNUSED(settings) }signals: void changed();};} // namespace Find#endif // IFINDFILTER_H
通过实现 IFindFilter 接口,然后将其实例添加到对象池,我们就可以注册一个新的文件过滤器,这意味着我们将会在前面提到的查找对话框的“范围”下拉框中找到新的一项。
假设我们需要一个自定义过滤器,用于在已打开工程中查找一个给定的头文件。那么,我们可以根据下面的几个步骤来理解如何编写这个查找过滤器。
首先,我们需要声明一个实现了 Find::IFindFilter 接口的类,也就是 HeaderFilter 。这个类定义如下:
#ifndef HEADERFILTER_H#define HEADERFILTER_H#include class HeaderFilterData;class HeaderFilter : public Find::IFindFilter{ Q_OBJECTpublic: HeaderFilter(); ~HeaderFilter(); QString id() const; QString displayName() const; bool isEnabled() const; bool canCancel() const; void cancel(); QKeySequence defaultShortcut() const; void findAll(const QString &txt, Find::FindFlags findFlags); QWidget *createConfigWidget();private: HeaderFilterData *d;};#endif // HEADERFILTER_H
构造函数和析构函数都是空白的。我们会在后面详细了解过 IFindFilter 的工作过程之后再来填入更多的代码。
struct HeaderFilterData{};HeaderFilter::HeaderFilter(){ d = new HeaderFilterData;}HeaderFilter::~HeaderFilter(){ delete d;}
id() 函数用于该 filter 的唯一标示符:
QString HeaderFilter::id() const{ return "HeaderFilter";}
displayName() 函数用于在范围下拉框中显示:
QString HeaderFilter::displayName() const{ return tr("Header Filter");}
isEnabled() 函数返回 bool 值,表示该过滤器是否可用。在这个例子中,我们希望在 Qt Creator 加载了项目之后,该过滤器可用,否则不可用。为了更进一步了解该函数的作用,我们必须了解 ProjectExplorer 命名空间。现在,我们简单的将其返回值设置为 true,等到学习过 ProjectExplorer 命名空间之后再对此函数作进一步完善。
【领 QT开发教程 学习资料, 点击下方链接莬费领取↓↓ ,先码住不迷路~】
点击这里:
bool HeaderFilter::isEnabled() const{ return true;}
canCancel() 和 cancel() 函数用于表示该过滤器能够被取消。这里我们也是用最简单的代码:
bool HeaderFilter::canCancel() const{ return true;}void HeaderFilter::cancel(){ // Do nothing}
defaultShortcut() 函数返回与此过滤器相关的快捷键。用户按下该快捷键之后,查找对话框将自动打开,并且该过滤器被选中。在本例中,我们返回一个非法快捷键:
QKeySequence HeaderFilter::defaultShortcut() const{ return QKeySequence();}
createConfigWidget() 函数返回一个用于配置的组件。这个组件将显示在查找对话框的最下方(注意本文开始的查找对话框图片中,最下方的“文件模式”的位置)。在本例中,我们不需要对 header filter 作任何配置,因此使用一个 label 显示即可:
QWidget *HeaderFilter::createConfigWidget(){ return (new QLabel("This is a header filter"));}
findAll() 函数用于实现真实的“查找”操作。我们需要了解 ProjectExplorer 、 TextEditor 、 Find 和 Core::Utils 这几个命名空间之后才能编写实际代码。所以现在,我们的 findAll() 函数还是空白的:
void HeaderFilter::findAll(const QString &text, Find::FindFlags findFlags){ // Do nothing}
HeaderFilterPlugin 代码同前面一样,我们只需修改 initialize() 函数:
3. 实现HeaderFilterPlugin代码HeaderFilterPlugin代码同前面一样,我们只需修改 initialize()函数:
将我们的代码编译之后,重启 Qt Creator。我们之前仅仅使用了 coreplugin 这个插件,而现在,我们需要使用 find。这需要修改 pro 文件:
QTC_SOURCE = E:/codelibs/QtCreator/_BUILD = E:/codelibs/QtCreator/build/TEMPLATE = libTARGET = HeaderFilterPluginIDE_SOURCE_TREE = $QTC_SOURCEIDE_BUILD_TREE = $QTC_BUILDPROVIDER = GalaxyWorldDESTDIR = $QTC_BUILD/lib/qtcreator/plugins/GalaxyWorldLIBS += -L$QTC_BUILD/lib/qtcreator/plugins/Nokiainclude($QTC_SOURCE/src/)include($QTC_SOURCE/src/plugins/coreplugin/)include($QTC_SOURCE/src/plugins/find/)HEADERS = = _FILES =
注意其中添加了 一行。
现在,我们可以在查找对话框中看到新增加的 Header Filter 一项。注意在范围下拉框中的显示以及对话框下方的配置组件。
现在,由于我们的 findAll() 函数没有实现,我们仅仅为对话框提供了一个没有“查找”功能的界面。在后面的章节中,我们将继续完成这个插件。
ProjectExplorer 命名空间中的类都是与 Qt Creator 中的工程管理相关的。该命名空间由 projectexplorer 插件提供,而 Qt Crteaor 所支持的工程类型也是由这个插件提供。例如,
cmakeprojectmanager 插件实现了 ProjectExplorer 命名空间中所定义的接口,支持的是 CMake 工程; qt4projectmanager 插件提供对 Qt 4 工程的支持; qmlprojectmanager 插件提供对 QML 工程的支持。下面列出的是 ProjectManager 命名空间中一些关键的类和接口:
类/接口 | 描述 |
ProjectExplorer::IProjectManager | 如果需要提供一种新的工程类型,必须实现此接口。该接口的实现帮助 Qt Creator 加载工程。 |
ProjectExplorer::Project | 该接口定义了一个工程的几个术语: 用于描述工程的文件( Core::IFile ); 用于描述工程是应用程序还是库的一个 bool 值; 构建、清理工程所需的步骤( ProjectExplorer:: BuildStep ); 运行工程所需的配置信息; 运行项目所需的环境; 工程浏览器面板的根节点; 构建工程时所需的 include 路径和宏。 |
ProjectManager::ProjectExplorerPlugin | 工程浏览器插件所需实现的 Core::IPlugin 接口。通过该类,我们可以: 访问所有打开的工程; 访问当前工程; 访问在工程浏览器面板中当前选择的节点(文件/文件夹) 访问构建管理器( ProjectManager::BuildManager ) |
使用 ProjectManager::ProjectExplorerPlugin ,我们可以获取 Qt Creator 的所有已打开的工程。下面的代码显示如何做到这一点:
#include #include // Catch hold of the plugin-managerExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance();// Look for the ProjectExplorerPlugin objectProjectExplorer::ProjectExplorerPlugin* projectExplorerPlugin = pm->getObject();// Fetch a list of all open projectsQListprojects = d->projectPlugin->session()->projects();
利用我们得到的 projects 列表,我们可以访问该工程文件以及工程中的其他文件(源代码文件、头文件、资源文件等)。于是,我们可以编写如下的代码:
Q_FOREACH(ProjectExplorer::Project* project, projects){ QString name = project->name(); Core::IFile* projectFile = project->file(); // Do something with the above. For example: qDebug("Project %s has project file as %s", qPrintable(name), qPrintable(projectFile->fileName()));}
上面的代码展示了如何获取工程文件(,.pro 文件等),但是并不能获取与工程关联的其他文件。而下面的代码则显示了如何从 projects 列表获取所有文件的文件名列表:
// Make a list of files in each projectQStringList files;Q_FOREACH(ProjectManager::Project* project, projects){ files += project->files(Project::AllFiles);}
我们的 HeaderFilter 应该在至少有一个工程打开的情况下才能够使用。为简单起见,我们前面将 isEnabled() 函数的返回值始终设置为 true。下面,我们来修改这段代码,以便满足我们的需求:
struct HeaderFilterData{ ProjectExplorer::ProjectExplorerPlugin* projectExplorer() { if(m_projectPlugin) { return m_projectPlugin; } ExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance(); m_projectPlugin = pm->getObject(); return m_projectPlugin; }private: ProjectExplorer::ProjectExplorerPlugin* m_projectPlugin;};// ...bool HeaderFilter::isEnabled() const{ return d->projectExplorer()->session()->projects().count() >0;}
从前面的内容中,我们已经了解到如何访问与打开的工程相关联的文件名。现在,我们就可以在这些文件中进行查找了。下面,我们开始一步步实现 HeaderFilter::findAll() 函数:
void HeaderFilter::findAll(const QString &text, Find::FindFlags findFlags){ // Fetch a list of all open projects QListprojects = d->projectExplorer()->session()->projects(); // Make a list of files in each project QStringList files; Q_FOREACH(ProjectExplorer::Project* project, projects) { files += project->files(ProjectExplorer::Project::AllFiles); } // Remove duplicates (); // Search for text in files // ...}
我们可以获知需要搜索的文件的数目:或许只有 1 个文件,也可能有 1000 或者更多的文件!因此,直接在 findAll() 函数中进行搜索不是一个好主意。如果 findAll() 函数执行时间过长,就会使得整个 Qt Creator 界面停止响应,直到搜索完成(通常,这是 GUI 程序设计中需要注意的一个问题:不要在 GUI 线程执行过于复杂的操作,以免拖慢 GUI 的响应)!
这种问题的解决方案是:
使用 QtConcurrent ,创建多个线程执行实际的搜索; 为 QtConcurrent 返回的 QFuture 初始化一个 QFutureWatcher ,以便在搜索结果生成之后发送 signals; 监听 QFutureWatcher 发出的信息,列出搜索结果。Qt Creator 已经为我们提供了函数 findInFiles() ,用于在一个文件列表中查找一个字符串,然后返回一个用于监视搜索结果的 QFuture 对象。这个函数在 src/libs/utils/ 中声明:
namespace Utils {// ...class QTCREATOR_UTILS_EXPORT FileSearchResult{public: FileSearchResult() {} FileSearchResult(QString fileName, int lineNumber, QString matchingLine, int matchStart, int matchLength, QStringList regexpCapturedTexts) : fileName(fileName), lineNumber(lineNumber), matchingLine(matchingLine), matchStart(matchStart), matchLength(matchLength), regexpCapturedTexts(regexpCapturedTexts) { } QString fileName; int lineNumber; QString matchingLine; int matchStart; int matchLength; QStringList regexpCapturedTexts;};typedef QListFileSearchResultList;QTCREATOR_UTILS_EXPORT QFuturefindInFiles( const QString &searchTerm, FileIterator *files, QTextDocument::FindFlags flags, QMapfileToContentsMap = QMap());QTCREATOR_UTILS_EXPORT QFuturefindInFilesRegExp( const QString &searchTerm, FileIterator *files, QTextDocument::FindFlags flags, QMapfileToContentsMap = QMap());// ...} // namespace Utils
好了,现在照着新的思路,继续完成 HeaderFilter::findAll() 函数吧:
struct HeaderFilterData{ QFutureWatcherwatcher; ProjectExplorer::ProjectExplorerPlugin* projectExplorer() { // ... }private: ProjectExplorer::ProjectExplorerPlugin* m_projectPlugin;};HeaderFilter::HeaderFilter(){ d = new HeaderFilterData; connect(&d->watcher, SIGNAL(resultReadyAt(int)), SLOT(displayResult(int)));}void HeaderFilter::findAll(const QString &text, Find::FindFlags findFlags){ // ... // Remove duplicates (); // Search for text in files // Variable to hold search results that will // come as the search task progresses QFuture searchResults; // Begin searching QString includeline = "#include <" + text + ">"; QListcodecs; codecs << QTextCodec::codecForLocale(); searchResults = Utils::findInFiles(includeline, new Utils::FileIterator(files, codecs), Find::textDocumentFlagsForFindFlags(findFlags)); // Let the watcher monitor the search results d->(searchResults);}void HeaderFilter::displayResult(int index){ // ...}
在 findAll() 的后续代码中,我们使用了 findInFiles() 函数,在后台开始了多个线程用于查找字符串。一旦搜索结果生成,根据我们的 connect, displayResult(int) 就会被调用。在这个槽函数中,我们需要将搜索结果展示出来。
find 插件提供了一个叫做 Find::SearchResultWindow 的对象,用于显示搜索结果:
我们当然希望搜索结果在这个 SearchResultWindow 中显示。为了达到这一目的,我们需要再次修改 HeaderFilter 的代码:
struct HeaderFilterData{ // ... // Method to search and return the search window Find::SearchResultWindow* searchResultWindow() { if(m_searchResultWindow) { return m_searchResultWindow; } ExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance(); m_searchResultWindow = pm->getObject(); return m_searchResultWindow; }private: // ... Find::SearchResultWindow *m_searchResultWindow;};// ...void HeaderFilter::findAll(const QString &text, Find::FindFlags findFlags){ // ... // Clear the current search results d->searchResultWindow()->clearContents(); // Begin searching QString includeline = "#include <" + text + ">"; // ...}void HeaderFilter::displayResult(int index){ Utils::FileSearchResultList results = d->().resultAt(index); Q_FOREACH(Utils::FileSearchResult result, results) { d->searchResultWindow()->addResult(, , , , ); }}
注意,我们使用了 addResult() 这个函数来向 SearchResultWindow 中添加新的结果项。使用这个函数可以让我们的搜索内容高亮显示,正如前面我们的截屏一样。
【领 QT开发教程 学习资料, 点击下方链接莬费领取↓↓ ,先码住不迷路~】
点击这里:
关键词: