一、QtCreator工程类型简介
1、QtCreator工程类型简介
QtCreator中,新的工程类型将出现在“文件 -> 新建”菜单项中,可以通过打开的选择工程类型的对话框来找到所需要的工程。
二、Core::IWizard接口
QtCreator提供了Core::IWizard接口,用于实现添加新的工程类型。Core::IWizard接口在src/plugins/coreplugin/dialogs/iwizard.h中声明:
class CORE_EXPORT IWizard
: public QObject
{
Q_OBJECT
public:
enum WizardKind {
FileWizard = 0x01,
ClassWizard = 0x02,
ProjectWizard = 0x04
};
Q_DECLARE_FLAGS(WizardKinds, WizardKind)
IWizard(QObject *parent = 0) : QObject(parent) {}
virtual ~IWizard() {}
virtual WizardKind kind() const = 0;
virtual QIcon icon() const = 0;
virtual QString description() const = 0;
virtual QString displayName() const = 0;
virtual QString id() const = 0;
virtual QString category() const = 0;
virtual QString displayCategory() const = 0;
virtual void runWizard(const QString &path, QWidget *parent) = 0;
// Utility to find all registered wizards
static QList allWizards();
// Utility to find all registered wizards of a certain kind
static QList wizardsOfKind(WizardKind kind);
};
QtCreator支持File、Class、Project类型的向导,要实现File、Class、Project类型向导就必须实现Core::IWizard。
三、创建新工程类型
1、实现Core::IWizard
实现IWizard接口,添加一个新的工程类型“Custom Project”,让新的工程类型能够显示在“新建工程”对话框中。
#ifndef CUSTOMPROJECTWIZARD_H
#define CUSTOMPROJECTWIZARD_H
#include <coreplugin/dialogs/iwizard.h>
class QIcon;
class QString;
class QWidget;
class CustomProjectWizard : public Core::IWizard
{
public:
CustomProjectWizard() { }
~CustomProjectWizard() { }
Core::IWizard::WizardKind kind() const;
QIcon icon() const;
QString description() const;
QString displayName() const;
QString id() const;
QString category() const;
QString displayCategory() const;
void runWizard(const QString &path, QWidget *parent);
};
#endif // CUSTOMPROJECTWIZARD_H
函数kind()用于告诉Qt Creator,实现的IWizard接口是哪种类型的,合法值是IWizard::WizardKind枚举。
Core::IWizard::WizardKind CustomProjectWizard::kind() const
{
return IWizard::ProjectWizard;
}
函数icon()返回一个图标,代表该工程,将会出现在新建对话框列表的左侧。
QIcon CustomProjectWizard::icon() const
{
return qApp->windowIcon();
}
函数description()、displayName()、category()、displayCategory()和id()用于返回新的工程类型的元数据,比如显示的名字、分类等。其中,以display开头的几个函数则用于在界面上显示的字符串,因此需要将其用tr()函数返回。
QString CustomProjectWizard::description() const
{
return "A custom project";
}
QString CustomProjectWizard::displayName() const
{
return tr("Custom Project");
}
QString CustomProjectWizard::id() const
{
return "CustomProjectID";
}
QString CustomProjectWizard::category() const
{
return "GalaxyWorld";
}
QString CustomProjectWizard::displayCategory() const
{
return tr("GalaxyWorld");
}
当用户选择了“CustomProject”分类的时候,runWizard()函数将被调用。runWizard()函数必须显示一个对话框,或者一个QWizard对象,用于向用户询问创建新的工程、文件或者类所必须的那些信息。
void CustomProjectWizard::runWizard(const QString &path, QWidget *parent)
{
Q_UNUSED(path);
Q_UNUSED(parent);
QMessageBox::information(parent, "Custom Wizard Dialog", "Hi there!");
}
2、插件实现
bool CustomProjectWizardPlugin::initialize(const QStringList& args, QString *errMsg)
{
Q_UNUSED(args);
Q_UNUSED(errMsg);
addAutoReleasedObject(new CustomProjectWizard);
return true;
}
3、运行结果
编译QtCreator
四、文件生成
QtCreator已经提供了一个默认的IWizard接口的实现,即Core::BaseFileWizard类。Core::BaseFileWizard类提供了IWizard接口的所有函数的默认实现,并且增加了自己特有的虚函数。为了利用Core::BaseFileWizard类,需要继承它,并且重写其中的一个或多个函数。
1、Core::GeneratedFile
一个新的向导(IWizard的实现)能够提供用户多种提示,并且最终生成一个或者多个文件。Core::GeneratedFile类帮助开发者对将要生成的文件进行抽象。在Core::BaseFileWizard的子类中,可以为每一个自动生成的文件创建Core::GeneratedFile类的实例。Core::GeneratedFile在 coreplugin/basefilewizard.h 中声明:
class CORE_EXPORT GeneratedFile
{
public:
enum Attribute { // Open this file in editor
OpenEditorAttribute = 0x01,
// Open project
OpenProjectAttribute = 0x02,
/* File is generated by external scripts, do not write out,
* see BaseFileWizard::writeFiles() */
CustomGeneratorAttribute = 0x4
};
Q_DECLARE_FLAGS(Attributes, Attribute)
GeneratedFile();
explicit GeneratedFile(const QString &path);
GeneratedFile(const GeneratedFile &);
GeneratedFile &operator=(const GeneratedFile &);
~GeneratedFile();
// Full path of the file should be created, or the suggested file name
QString path() const;
void setPath(const QString &p);
// Contents of the file (UTF8)
QString contents() const;
void setContents(const QString &c);
QByteArray binaryContents() const;
void setBinaryContents(const QByteArray &c);
// Defaults to false (Text file).
bool isBinary() const;
void setBinary(bool b);
// Id of editor to open the file with
QString editorId() const;
void setEditorId(const QString &k);
bool write(QString *errorMessage) const;
Attributes attributes() const;
void setAttributes(Attributes a);
private:
QSharedDataPointer<GeneratedFilePrivate> m_d;
};
typedef QList<GeneratedFile> GeneratedFiles;
2、文件生成
需要由Core::BaseFileWizard子类生成的文件由Core::GeneratedFile类对象表示。Core::GeneratedFile类包含了需要生成的文件的三个关键属性:
A、文件名(及绝对路径)
B、用于编辑文件所需要的编辑器类型,合法值为:
CppEditor::Constants::CPPEDITOR_KIND
GenericProjectManager::Constants::PROJECT_KIND
Git::Constants:: GIT_COMMAND_LOG_EDITOR_KIND
Git::Constants:: C_GIT_COMMAND_LOG_EDITOR
C、文件内容
如果需要生成如下内容的 C++ 文件:
#include <iostream>
int main()
{
cout << "Hello World\n";
return 0;
}
实现代码如下:
#include <coreplugin/basefilewizard.h>
#include <cppeditor/cppeditorconstants.h>
Core::GeneratedFile genFile("C:/Path/To/Source.cpp");
genFile.setEditorId(CppEditor::Constants::CPPEDITOR_ID);
genFile.setContents(
"#include <iostream>\n"
"\n"
"int main()\n"
"{\n"
" cout << \"Hello World\n\";\n"
" \n"
" return 0;\n"
"}"
);
genFile.write(NULL);
五、数据项模型创建
创建一个新的类向导,来帮助自动生成一个数据项模型的文件。文件将基于如下几个数据:
A、模型类名称;
B、父类名称(QAbstractItemModel、QAbstractListModel或者QAbstractTableModel中的一个);
C、头文件名;
D、源代码文件名。
1、类向导界面设计
创建数据结构存储信息
struct ModelClassParameters
{
QString className;
QString headerFile;
QString sourceFile;
QString baseClass;
QString path;
};
#ifndef MODELNAMEPAGE_H
#define MODELNAMEPAGE_H
#include <QWizardPage>
struct ModelClassParameters
{
QString className;
QString headerFile;
QString sourceFile;
QString baseClass;
QString path;
};
namespace Ui {
class ModelNamePage;
}
class ModelNamePage : public QWizardPage
{
Q_OBJECT
public:
explicit ModelNamePage(QWidget *parent = 0);
~ModelNamePage();
void setPath(const QString& path);
ModelClassParameters parameters() const;
private slots:
void onClassNameChange(const QString & name);
private:
Ui::ModelNamePage *ui;
QString path;
};
#endif // MODELNAMEPAGE_H
#include "ModelNamePage.h"
#include "ui_ModelNamePage.h"
ModelNamePage::ModelNamePage(QWidget *parent) :
QWizardPage(parent),
ui(new Ui::ModelNamePage)
{
ui->setupUi(this);
}
ModelNamePage::~ModelNamePage()
{
delete ui;
}
void ModelNamePage::setPath(const QString& path)
{
this->path = path;
}
void ModelNamePage::onClassNameChange(const QString &name)
{
ui->headerEdit->setText(name + ".h");
ui->implEdit->setText(name + ".cpp");
}
ModelClassParameters ModelNamePage::parameters() const
{
ModelClassParameters params;
params.className = ui->classNameEdit->text();
params.headerFile = ui->headerEdit->text();
params.sourceFile = ui->implEdit->text();
params.baseClass = ui->baseClassBox->currentText();
params.path = path;
return params;
}
2、Core::BaseFileWizard简介
类Core::BaseFileWizard在coreplugin/basefilewizard.h文件中声明。
class CORE_EXPORT BaseFileWizard : public IWizard
{
Q_DISABLE_COPY(BaseFileWizard)
Q_OBJECT
public:
virtual ~BaseFileWizard();
// IWizard
virtual WizardKind kind() const;
virtual QIcon icon() const;
virtual QString description() const;
virtual QString displayName() const;
virtual QString id() const;
virtual QString category() const;
virtual QString displayCategory() const;
virtual void runWizard(const QString &path, QWidget *parent);
// Build a file name, adding the extension unless baseName already has one
static QString buildFileName(const QString &path, const QString &baseName, const QString &extension);
// Sets some standard options on a QWizard
static void setupWizard(QWizard *);
// Read "shortTitle" dynamic property of the pageId and apply it as the title of corresponding progress item
static void applyExtensionPageShortTitle(Utils::Wizard *wizard, int pageId);
protected:
typedef QList<QWizardPage *> WizardPageList;
explicit BaseFileWizard(const BaseFileWizardParameters ¶meters, QObject *parent = 0);
// Overwrite to create the wizard dialog on the parent, adding
// the extension pages.
virtual QWizard *createWizardDialog(QWidget *parent,
const QString &defaultPath,
const WizardPageList &extensionPages) const = 0;
// Overwrite to query the parameters from the dialog and generate the files.
virtual GeneratedFiles generateFiles(const QWizard *w,
QString *errorMessage) const = 0;
/* Physically write files. Re-implement (calling the base implementation)
* to create files with CustomGeneratorAttribute set. */
virtual bool writeFiles(const GeneratedFiles &files, QString *errorMessage);
/* Overwrite to perform steps to be done after files are actually created.
* The default implementation opens editors with the newly generated files. */
virtual bool postGenerateFiles(const QWizard *w, const GeneratedFiles &l, QString *errorMessage);
// Utility that returns the preferred suffix for a mime type
static QString preferredSuffix(const QString &mimeType);
// Utility that performs an overwrite check on a set of files. It checks if
// the file exists, can be overwritten at all and prompts the user.
enum OverwriteResult { OverwriteOk, OverwriteError, OverwriteCanceled };
OverwriteResult promptOverwrite(const QStringList &files,
QString *errorMessage) const;
// Utility to open the editors for the files whose attribute is set accordingly.
static bool postGenerateOpenEditors(const GeneratedFiles &l, QString *errorMessage = 0);
private:
BaseFileWizardPrivate *m_d;
};
BaseFileWizard类实现了IWizard接口,并且增加了几个新的函数:
A、createWizardDialog– 函数必须被子类重写,用于提供一个供runWizard()函数显示的向导,其中parent参数应当作为该函数返回的QWizard的父类,defaultPath参数应当作为生成的文件的默认路径,extensionPages参数列出了应该被向导默认显示的所有页面
B、generateFiles– 函数在用户点击了向导的“完成”按钮之后自动调用,函数的实现必须按要求创建Core::GeneratedFile类的实例
C、postGenerateFiles– 函数在generateFiles()返回之后被调用,子类可以通过覆盖该函数。
3、自定义向导类实现
#ifndef MODELCLASSWIZARD_H
#define MODELCLASSWIZARD_H
#include <coreplugin/basefilewizard.h>
#include <QMap>
class ModelClassWizard : public Core::BaseFileWizard
{
Q_OBJECT
public:
ModelClassWizard(const Core::BaseFileWizardParameters ¶meters,
QObject *parent = 0);
~ModelClassWizard();
QWizard *createWizardDialog(QWidget *parent,
const QString &defaultPath,
const WizardPageList &extensionPages) const;
Core::GeneratedFiles generateFiles(const QWizard *w,
QString *errorMessage) const;
private:
QString readFile(const QString& fileName,
const QMap<QString,QString>& replacementMap) const;
};
#endif // MODELCLASSWIZARD_H
#include "ModelClassWizard.h"
#include "ModelNamePage.h"
#include "cppeditor/cppeditorconstants.h"
#include <QtGui>
ModelClassWizard::ModelClassWizard(const Core::BaseFileWizardParameters ¶meters,
QObject *parent)
: Core::BaseFileWizard(parameters, parent)
{
}
ModelClassWizard::~ModelClassWizard()
{
}
QWizard* ModelClassWizard::createWizardDialog(QWidget *parent,
const QString &defaultPath,
const WizardPageList &extensionPages) const
{
// Create a wizard
QWizard* wizard = new QWizard(parent);
wizard->setWindowTitle("Model Class Wizard");
// Make our page as first page
ModelNamePage* page = new ModelNamePage(wizard);
int pageId = wizard->addPage(page);
wizard->setProperty("_PageId_", pageId);
page->setPath(defaultPath);
// Now add the remaining pages
foreach (QWizardPage *p, extensionPages) {
wizard->addPage(p);
}
return wizard;
}
//函数readFile()读取文件,将其内容以字符串的形式返回。在返回字符串前,函数需要使用第二个参数提供的替换字符表修正这个字符串.
QString ModelClassWizard::readFile(const QString& fileName,
const QMap<QString,QString>& replacementMap) const
{
QFile file(fileName);
file.open(QFile::ReadOnly);
QString retStr = file.readAll();
QMap<QString,QString>::const_iterator it = replacementMap.begin();
QMap<QString,QString>::const_iterator end = replacementMap.end();
while(it != end) {
retStr.replace(it.key(), it.value());
++it;
}
return retStr;
}
Core::GeneratedFiles ModelClassWizard::generateFiles(const QWizard *w,
QString *errorMessage) const
{
Q_UNUSED(errorMessage);
Core::GeneratedFiles ret;
int pageId = w->property("_PageId_").toInt();
ModelNamePage* page = qobject_cast<ModelNamePage*>(w->page(pageId));
if(!page) {
return ret;
}
ModelClassParameters params = page->parameters();
QMap<QString,QString> replacementMap;
replacementMap["{{UPPER_CLASS_NAME}}"] = params.className.toUpper();
replacementMap["{{BASE_CLASS_NAME}}"] = params.baseClass;
replacementMap["{{CLASS_NAME}}"] = params.className;
replacementMap["{{CLASS_HEADER}}"] = QFileInfo(params.headerFile).fileName();
Core::GeneratedFile headerFile(params.path + "/" + params.headerFile);
headerFile.setEditorId(CppEditor::Constants::CPPEDITOR_ID);
Core::GeneratedFile sourceFile(params.path + "/" + params.sourceFile);
sourceFile.setEditorId(CppEditor::Constants::CPPEDITOR_ID);
if(params.baseClass == "QAbstractItemModel") {
headerFile.setContents(readFile(":/ModelClassWizard/ItemModelHeader", replacementMap));
sourceFile.setContents(readFile(":/ModelClassWizard/ItemModelSource", replacementMap));
} else if(params.baseClass == "QAbstractTableModel") {
headerFile.setContents(readFile(":/ModelClassWizard/TableModelHeader", replacementMap));
sourceFile.setContents(readFile(":/ModelClassWizard/TableModelSource", replacementMap));
} else if(params.baseClass == "QAbstractListModel") {
headerFile.setContents(readFile(":/ModelClassWizard/ListModelHeader", replacementMap));
sourceFile.setContents(readFile(":/ModelClassWizard/ListModelSource", replacementMap));
}
ret << headerFile << sourceFile;
return ret;
}
QString ModelClassWizard::readFile(const QString& fileName,
const QMap& replacementMap) const
{
QFile file(fileName);
file.open(QFile::ReadOnly);
QString retStr = file.readAll();
QMap::const_iterator it = replacementMap.begin();
QMap::const_iterator end = replacementMap.end();
while(it != end) {
retStr.replace(it.key(), it.value());
++it;
}
return retStr;
}
readFile函数额功能如下:
模板文件如下:
#ifndef {{UPPER_CLASS_NAME}}_H
#define {{UPPER_CLASS_NAME}}_H
#include <{{BASE_CLASS_NAME}}>
struct {{CLASS_NAME}}Data;
class {{CLASS_NAME}} : public {{BASE_CLASS_NAME}}
{
Q_OBJECT
public:
{{CLASS_NAME}}(QObject* parent=0);
~{{CLASS_NAME}}();
int rowCount(const QModelIndex& parent) const;
QVariant data(const QModelIndex& index, int role) const;
private:
{{CLASS_NAME}}Data* d;
};
#endif // {{UPPER_CLASS_NAME}}_H
模板文件中的{{xxx}} 作为一种占位符
执行如下代码:
QMap replacementMap;
replacementMap["{{UPPER_CLASS_NAME}}"] = "LIST_MODEL";
replacementMap["{{BASE_CLASS_NAME}}"] = "QAbstractListModel";
replacementMap["{{CLASS_NAME}}"] = "ListModel";
QString contents = readFile("Sample.txt", replacementTable);
得到的contents内容如下:
#ifndef LIST_MODEL_H
#define LIST_MODEL_H
#include <QAbstractListModel>
struct ListModelData;
class ListModel : public QAbstractListModel
{
Q_OBJECT
public:
ListModel(QObject* parent=0);
~ListModel();
int rowCount(const QModelIndex& parent) const;
QVariant data(const QModelIndex& index, int role) const;
private:
ListModelData* d;
};
#endif // LIST_MODEL_H
根据提供模板文件,然后使用用户输入的信息替换其中的占位符,来生成相应的文件。
generateFiles()函数创建了两个Core::GeneratedFile对象,并且在返回前赋予了正确的数据。
Core::GeneratedFiles ModelClassWizard::generateFiles(const QWizard *w,
QString *errorMessage) const
{
Q_UNUSED(errorMessage);
Core::GeneratedFiles ret;
int pageId = w->property("_PageId_").toInt();
ModelNamePage* page = qobject_cast(w->page(pageId));
if(!page) {
return ret;
}
ModelClassParameters params = page->parameters();
QMap replacementMap;
replacementMap["{{UPPER_CLASS_NAME}}"] = params.className.toUpper();
replacementMap["{{BASE_CLASS_NAME}}"] = params.baseClass;
replacementMap["{{CLASS_NAME}}"] = params.className;
replacementMap["{{CLASS_HEADER}}"] = QFileInfo(params.headerFile).fileName();
Core::GeneratedFile headerFile(params.path + "/" + params.headerFile);
headerFile.setEditorId(CppEditor::Constants::CPPEDITOR_ID);
Core::GeneratedFile sourceFile(params.path + "/" + params.sourceFile);
sourceFile.setEditorId(CppEditor::Constants::CPPEDITOR_ID);
if(params.baseClass == "QAbstractItemModel") {
headerFile.setContents(readFile(":/ModelClassWizard/ItemModelHeader",
replacementMap));
sourceFile.setContents(readFile(":/ModelClassWizard/ItemModelSource",
replacementMap));
} else if(params.baseClass == "QAbstractTableModel") {
headerFile.setContents(readFile(":/ModelClassWizard/TableModelHeader",
replacementMap));
sourceFile.setContents(readFile(":/ModelClassWizard/TableModelSource",
replacementMap));
} else if(params.baseClass == "QAbstractListModel") {
headerFile.setContents(readFile(":/ModelClassWizard/ListModelHeader",
replacementMap));
sourceFile.setContents(readFile(":/ModelClassWizard/ListModelSource",
replacementMap));
}
ret << headerFile << sourceFile;
return ret;
}
4、插件实现
bool CustomProjectWizardPlugin::initialize(const QStringList& args, QString *errMsg)
{
Q_UNUSED(args);
Q_UNUSED(errMsg);
Core::BaseFileWizardParameters params;
params.setKind(Core::IWizard::ClassWizard);
params.setIcon(qApp->windowIcon());
params.setDescription("Generates an item-model class");
params.setDisplayName("Item Model");
params.setCategory("GalaxyWorld");
params.setDisplayCategory(tr("GalaxyWorld"));
addAutoReleasedObject(new ModelClassWizard(params, this));
return true;
}