Qt Creator 最基本的功能是一个文本编辑器。在此基础之上,Qt Creator 还提供了编辑 UI 文件、QRC 文件、PRO/PRI 文件以及 EXE/DLL/SO 文件的功能。
从本节开始,我们将开始尝试开发一些实际的插件,理解如何为我们特有的文件格式提供编辑器。这里,我们选择 HTML 格式。我们的插件将使我们能够从本地文件系统中加载 HTML 文件,并且能够查看和编辑。下面就是我们的插件完成后的行为:
核心类和接口
为了支持新的编辑器类型,我们需要:
- 实现一个插件类(实现Core::IPlugin接口),暴露出一个“编辑器工厂”。前面我们已经介绍过如何创建插件,实现Core::IPlugin接口。
- 实现这个“编辑器工厂”,也就是Core::IEditorFactory接口。这个接口提供了帮助创建特定格式编辑器对象的函数。
- 实现编辑器,也就是Core::IEditor接口。这个接口提供了用于辅助编辑一种文件格式(例如 HTML、ODF 等)的函数。编辑器必须提供访问它所要显示或者编辑的文件的函数。
- 实现接口Core::IFile。该接口用于帮助数据加载或保存。
在后面的内容中,我们将依次了解上述各个接口的含义以及如何实现这些接口。
Core::IFile
Core::IFile接口将文件操作从用户界面抽象出来,提供用于加载和保存文件的虚函数(一般会以文件名作为参数)。我们可以将文件看做具有 mime-tyle、“modified” 和 “read-only” 等标记位的对象。Core::IFile接口在 src/plugins/coreplugin/ifile.h 中声明:
#ifndef IFILE_H
#define IFILE_H
#include "core_global.h"
#include <QtCore/QObject>
namespace Core {
class MimeType;
class CORE_EXPORT IFile : public QObject
{
Q_OBJECT
public:
// This enum must match the indexes of the reloadBehavior widget
// in generalsettings.ui
enum ReloadSetting {
AlwaysAsk = 0,
ReloadUnmodified = 1,
IgnoreAll = 2
};
enum Utf8BomSetting {
AlwaysAdd = 0,
OnlyKeep = 1,
AlwaysDelete = 2
};
enum ChangeTrigger {
TriggerInternal,
TriggerExternal
};
enum ChangeType {
TypeContents,
TypePermissions,
TypeRemoved
};
enum ReloadBehavior {
BehaviorAsk,
BehaviorSilent
};
enum ReloadFlag {
FlagReload,
FlagIgnore
};
IFile(QObject *parent = 0) : QObject(parent) {}
virtual ~IFile() {}
virtual bool save(const QString &fileName = QString()) = 0;
virtual QString fileName() const = 0;
virtual QString defaultPath() const = 0;
virtual QString suggestedFileName() const = 0;
virtual QString mimeType() const = 0;
virtual bool isModified() const = 0;
virtual bool isReadOnly() const = 0;
virtual bool isSaveAsAllowed() const = 0;
virtual ReloadBehavior reloadBehavior(ChangeTrigger state,
ChangeType type) const = 0;
virtual void reload(ReloadFlag flag, ChangeType type) = 0;
virtual void rename(const QString &newName) = 0;
virtual void checkPermissions() {}
signals:
void changed();
void aboutToReload();
void reloaded();
};
} // namespace Core
#endif // IFILE_H
【领QT开发教程学习资料,点击下方链接莬费领取↓↓,先码住不迷路~】
点击这里:「链接」
或许你会问:“我们已经有QFile,作为文件操作的一种抽象,为什么又要设计一个IFile呢?”原因如下:
- IFile关心的是以文件名为参数,将一个文件的内容加载进一个编辑器 editor 的行为,而QFile仅仅是将文件内容加载到一个QByteArray对象;
- IFile需要在用户在编辑器中修改了文件内容的时候发出modified()信号,需要注意的是,此时的文件修改并没有写入磁盘;而QFile只有在文件内容写入磁盘时才会发出bytesWritten()信号;
- IFile需要处理一个在磁盘被修改的文件如何重新加载到编辑器中,而QFile不需要处理这种问题
我们会在后面的章节中详细探讨如何实现Core::IFile接口。
Core::IEditor
Core::IEditor接口用于提供编辑不同类型文件的编辑器。这个接口位于 src/plugins/coreplugin/editormanager/ieditor.h 中:
#ifndef IEDITOR_H
#define IEDITOR_H
#include <coreplugin/core_global.h>
#include <coreplugin/icontext.h>
#include <QtCore/QMetaType>
namespace Core {
class IFile;
class CORE_EXPORT IEditor : public IContext
{
Q_OBJECT
public:
IEditor(QObject *parent = 0) : IContext(parent) {}
virtual ~IEditor() {}
virtual bool createNew(const QString &contents = QString()) = 0;
virtual bool open(const QString &fileName = QString()) = 0;
virtual IFile *file() = 0;
virtual QString id() const = 0;
virtual QString displayName() const = 0;
virtual void setDisplayName(const QString &title) = 0;
virtual bool duplicateSupported() const = 0;
virtual IEditor *duplicate(QWidget *parent) = 0;
virtual QByteArray saveState() const = 0;
virtual bool restoreState(const QByteArray &state) = 0;
virtual int currentLine() const { return 0; }
virtual int currentColumn() const { return 0; }
virtual void gotoLine(int line, int column = 0)
{ Q_UNUSED(line) Q_UNUSED(column) }
virtual bool isTemporary() const = 0;
virtual QWidget *toolBar() = 0;
virtual QString preferredModeType() const { return QString(); }
signals:
void changed();
};
} // namespace Core
Q_DECLARE_METATYPE(Core::IEditor*)
#endif // IEDITOR_H
Core::IEditor主要提供以下功能:
- 一个编辑器组件(由Core::IEditor::widget()函数返回)。Qt Creator 使用这个组件显示需要编辑的文件的内容;
- 实现Core::IFile接口的文件(由Core::IEditor::file()函数返回),Qt Creator 使用这个对象触发文件从磁盘加载以及保存到磁盘的操作;
- 一个自定义的工具条 toolbar,Qt Creator 会在该编辑器可用时自动加载该工具条;
- 在文件中光标的当前位置(Core::IEditor::currentLine()和Core::IEditor::currentColumn()函数)
- 需要显示在“打开文件”列表的名字。
我们可以通过下面的示意图来理解上面说的这几点:
Core::IEditorFactory
Core::IEditorFactory提供用于创建Core::IEditor实例的函数。Core::IEditor则可以支持 mime-type 文件的编辑。这个接口在 src/plugins/coreplugin/editormanager/ieditorfactory.h 中声明:
#ifndef IEDITORFACTORY_H
#define IEDITORFACTORY_H
#include <coreplugin/ifilefactory.h>
namespace Core {
class IEditor;
class CORE_EXPORT IEditorFactory : public Core::IFileFactory
{
Q_OBJECT
public:
IEditorFactory(QObject *parent = 0) : IFileFactory(parent) {}
virtual ~IEditorFactory() {}
virtual IEditor *createEditor(QWidget *parent) = 0;
};
} // namespace Core
#endif // IEDITORFACTORY_H
于创建该编辑器实例并将其返回。
Core::MimeDatabase
Core::MimeDatabase类用于保存 Qt Creator 所支持的所有的 mime-type;同时,这个类也可以判断给定文件的 mime-type。例如:
#include <coreplugin/mimedatabase.h>
Core::ICore* core = Core::ICore::instance();
Core::MimeDatabase* mdb = core->mimeDatabase();
Core::MimeType type1 = mdb->findByFile( QFileInfo("C:/Temp/sample.html") );
qDebug("File Type for sample.html = %s", qPrintable(type1.type()));
Core::MimeType type2 = mdb->findByFile( QFileInfo("C:/Temp/TextEdit/Main.cpp") );
qDebug("File Type for Main.cpp = %s", qPrintable(type2.type()));
Core::MimeType type3 = mdb->findByFile( QFileInfo("C:/Temp/TextEdit/TextEdit.pro") );
qDebug("File Type for TextEdit.pro = %s", qPrintable(type3.type()));
当我们运行上面代码时,我们会得到如下结果:
File Type for sample.html = text/plain
File Type for Main.cpp = text/x-c++src
File Type for TextEdit.pro = text/plain
Core::MimeDatabase利用文件后缀名、glob 模式以及“魔数”来判断给定的一个文件的 mime-type。不过此时,我们不去深究MimeDatabase是如何实现的,我们只需要知道可以获取文件的 mime-type 就好了。
正如前文所述,Core::IEditorFactory接口提供某一特定 mime-type 的编辑器(实现了Core::IEditor的类)的实例。那么,下面我们来解释一下,对于给定的文件名,Qt Creator 如何找到合适的Core::IEditorFactory的:
- 用户使用“文件->打开”命令选择一个文件;
- Qt Creator 使用Core::MimeDatabase识别选定文件的 mime-type;
- Qt Creator 遍历所有的Core::IEditorFactory实现,找到支持第 2 步所识别出的 mime-type 的 editor-factory;
- Qt Creator 请求该 editor factory 创建一个编辑器实例(Core::IEditor实现);
- 由Core::IEditor::widget()返回的组件显示在 Qt Creator 的主窗口;
- 调用Core::IEditor::open()函数,打开第 1 步选择的文件。
添加新的 mime-type
如果我们要添加新文件类型的编辑器,就需要向Core::MimeDatabase注册 mime-type。有很多种机制都可以实现注册新的 mime-type,这里我们使用最简单的方式:利用 XML 文件。
假设我们要注册 text/html mime-type,并且关联 *.html 文件名。我们需要创建一个 XML 文件,然后命名为 text-html-mimetype.xml。
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
<mime-type type="text/html">
<sub-class-of type="text/plain"/>
<comment>HTML File</comment>
<glob pattern="*.html"/>
</mime-type>
</mime-info>
我们利用这个 XML 文件,通过Core::MimeDatabase::addMimeTypes()函数注册新的 mime-type。所需代码示例如下:
Core::ICore* core = Core::ICore::instance();
Core::MimeDatabase* mdb = core->mimeDatabase();
QString errMsg;
bool success = mdb->addMimeTypes("text-html-mimetype.xml", errMsg);
一旦注册成功,Qt Creator 就会将所有 *.html 文件名映射成 text/plain mime-type。
【领QT开发教程学习资料,点击下方链接莬费领取↓↓,先码住不迷路~】
点击这里:「链接」
原文:https://www.devbean.net/2011/10/qtcreator-plugin-develop-8/
本文暂时没有评论,来添加一个吧(●'◡'●)