QT CREATOR 插件开发:添加新的工程类型

2023-10-27

Qt Creator 中,新的工程类型将出现在“文件 -> 新建”菜单项中;我们可以通过打开的选择工程类型的对话框来找到所需要的工程:

Qt Creator 新建工程

在本章中,我们将学习如何向上面所示的对话框中添加新的工程类型。

Core::IWizard接口

Qt Creator 提供了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);
};

可以看出,Qt Creator 支持一下类型的向导:

  • File
  • Class
  • Project

Core::IWizard就是实现以上向导所必须实现的接口。

简单实现Core::IWizard

下面,我们自己实现IWizard接口,来添加一个新的工程类型“Custom Project”,目的是让我们的新的工程类型能够显示在“新建工程”对话框中。

实现Core::IWizard接口

首先,新建一个类CustomProjectWizard,实现Core::IWizard:

#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

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

下面,我们将讨论每一个函数的实现。

函数kind()用于告诉 Qt Creator,我们实现的IWizard接口是哪种类型的。它的合法值就是前面所说的IWizard::WizardKind枚举。在我们的例子中,我们返回ProjectWizard:

Core::IWizard::WizardKind CustomProjectWizard::kind() const
{
    return IWizard::ProjectWizard;
}

函数icon()返回一个图标,这个图标代表该工程,将会出现在新建对话框列表的左侧。在我们的例子中,我们直接返回 Qt Creator 的图标:

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()函数将被调用。这个函数必须显示一个对话框,或者一个QWizard对象,用于向用户询问创建新的工程、文件或者类所必须的那些信息。在我们的示例中,这个函数仅仅显示一个对话框:

void CustomProjectWizard::runWizard(const QString &path, QWidget *parent)
{
    Q_UNUSED(path);
    Q_UNUSED(parent);

    QMessageBox::information(parent, "Custom Wizard Dialog", "Hi there!");
}

提供插件

至此我们已经完成自定义工程所需要的代码,下面就需要和前面一样,提供一个插件的封装:

bool CustomProjectWizardPlugin::initialize(const QStringList& args, QString *errMsg)
{
    Q_UNUSED(args);
    Q_UNUSED(errMsg);

    addAutoReleasedObject(new CustomProjectWizard);

    return true;
}

Qt Creator 自定义工程

 

预定义的IWizard实现:Core::BaseFileWizard

Qt Creator 已经提供了一个默认的IWizard接口的实现,也就是Core::BaseFileWizard类。这个类提供了
IWizard接口的所有函数的默认实现,并且增加了自己特有的虚函数。为了利用这个类,我们需要继承它,并且重写其中的一个或多个函数。

Core::GeneratedFile和Core::GeneratedFiles

通常说,一个新的向导(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 m_d;
};

typedef QList GeneratedFiles;

需要由Core::BaseFileWizard子类生成的文件由Core::GeneratedFile类对象表示。这个类包含了需要生成的文件的三个关键属性:

  1. 文件名(及绝对路径)
  2. 用于编辑文件所需要的编辑器类型,合法值为:CppEditor::Constants::CPPEDITOR_KINDGenericProjectManager::Constants::PROJECT_KINDGit::Constants:: GIT_COMMAND_LOG_EDITOR_KINDGit::Constants:: C_GIT_COMMAND_LOG_EDITOR
  3. 文件内容

假设我们需要生成如下内容的 C++ 文件:

#include <iostream>

int main()
{
    cout << "Hello World\n";

    return 0;
}

我们按照下述代码使用Core::GeneratedFile:

#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);

Item Model 类向导

假设我们希望提供一个新的类向导,来帮助我们自动生成一个数据项模型的文件。这个文件将基于如下几个数据:

  • 模型类名称;
  • 父类名称(可以是QAbstractItemModel、QAbstractListModel或者QAbstractTableModel中的一个);
  • 头文件名;
  • 源代码文件名。

下面,我们就来为 Qt Creator 提供这个“数据项模型”类向导。

第一步:设计类向导界面

首先,我们需要使用 Qt Designer 设计一个简单的界面,目的是向用户搜集我们上面提到的所需要的各个数据:

ModelClassPage UI

这个界面被命名为 ModelNamePage.ui。

第二步:实现类向导页面

现在,我们将这个 UI 界面导入 Qt 类,从而可以获取到界面数据。首先,我们需要设计一个结构体,来保存所需数据:

struct ModelClassParameters
{
    QString className;
    QString headerFile;
    QString sourceFile;
    QString baseClass;
    QString path;
};

下一步,我们声明表示向导页的类,用于将前面我们做出的 UI 类加载进来,以便用户能够使用我们设计的界面来输入需要的内容:

#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

构造函数和析构函数都是很简单的:

ModelNamePage::ModelNamePage(QWidget *parent) :
    QWizardPage(parent),
    ui(new Ui::ModelNamePage)
{
    ui->setupUi(this);
}

ModelNamePage::~ModelNamePage()
{
    delete ui;
}

函数setPath()用于将文件路径存储下来:

void ModelNamePage::setPath(const QString& path)
{
    this->path = path;
}

槽函数onClassNameChange()根据用户填写的类名,自动生成对应的头文件和源代码文件的名字:

void ModelNamePage::onClassNameChange(const QString &name)
{
    ui->headerEdit->setText(name + ".h");
    ui->implEdit->setText(name + ".cpp");
}

最后, 函数parameters()用于返回ModelClassParameters实例,实际就是返回用户填写的数据:

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;
}

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 WizardPageList;

    explicit BaseFileWizard(const BaseFileWizardParameters &parameters,
                            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接口,并且增加了几个新的函数:

  • createWizardDialog– 该函数必须被子类重写,用于提供一个供runWizard()函数显示的向导,其中parent参数应当作为该函数返回的QWizard的父类defaultPath参数应当作为生成的文件的默认路径extensionPages参数列出了应该被向导默认显示的所有页面
  • generateFiles– 该函数在用户点击了向导的“完成”按钮之后自动调用,该函数的实现必须按要求创建Core::GeneratedFile类的实例
  • postGenerateFiles– 该函数在generateFiles()返回之后被调用,子类可以通过覆盖该函数,做你需要做的任何事情

下面,我们使用BaseFileWizard来实现我们自己的向导:

#ifndef MODELCLASSWIZARD_H
#define MODELCLASSWIZARD_H

#include <coreplugin/basefilewizard.h>
#include <QMap>

class ModelClassWizard : public Core::BaseFileWizard
{
    Q_OBJECT
public:
    ModelClassWizard(const Core::BaseFileWizardParameters &parameters,
                     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& replacementMap) const;
};

#endif // MODELCLASSWIZARD_H

我们的构造函数和析构函数很简单:

ModelClassWizard::ModelClassWizard(const Core::BaseFileWizardParameters &parameters,
                                   QObject *parent)
    : Core::BaseFileWizard(parameters, parent)
{
}

ModelClassWizard::~ModelClassWizard()
{
}

函数createWizardDialog()创建一个很简单的QWizard,将我们前面写好的ModelNamePage作为其第一个页面,后面则添加其他默认页面:

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& 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;
}

这个函数将会实现这么一个功能:假设我们有一个文件 sample.txt:

#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

其中的 {{xyz}} 作为一种占位符。如果我们有如下代码片段:

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;
}

实现插件

同前面一样,我们的插件实现类也只是替换掉initialize()函数的内容:

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;
}

最后来测试一下我们的插件。

因为我们的向导是文件类型的,所以需要首先打开一个工程:

Open Project for Test

然后在工程名字上面点右键,添加新文件,可以看到我们的插件提供的文件类型:

Add Item Model New File

点击下方的“选择...”按钮,弹出我们设计的用户输入界面,填入 MyItemModel,可以看到下面的 Header 和 Implementation 都会自动修改:

Input Item Model Data

点击“下一步”,看看新的界面。这个页面就是系统默认添加的,还记得我们写的函数中最后那个循环吗?注意默认路径,Qt Creator 已经帮我们写好了:

Add Files to Project

点击完成,就可以看到,我们的文件已经生成好了!

结语

这是我们 《Qt Creator 插件开发》的最后一个章节。从我们以往的内容可以看出,Qt Creator 的插件开发并不是那么高不可攀,唯一的问题在于文档:Qt Creator 插件开发的文档一直写的不清不楚。虽然最新版本已经改善了很多,但是就学习而言,还是远远不够的。希望本文也能起到抛砖引玉的作用,最好能够让大家一起加入到 Qt Creator 的开发活动中来,一起完善这个 IDE。

感谢大家一直以来的支持!

本文福利,费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QT嵌入式开发,Quick模块等等)↓↓↓↓↓↓见下面↓↓文章底部点击费领取↓↓

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

QT CREATOR 插件开发:添加新的工程类型 的相关文章

  • 在 LINQ 查询中返回不带时间的日期

    我正在编写一个查询 我想计算按日期联系我们的呼叫中心的次数 看起来很简单 但由于联系日期字段是日期时间字段 我得到了时间 因此当我按联系日期 时间 分组时 每个联系日期实例的计数为 1 所以 我想只按日期分组 而不按时间分组 下面是我用来查
  • Signalr 在生产服务器中总是陷入长轮询

    当我在服务器中托管应用程序时 它会检查服务器端事件并始终回退到长轮询 服务器托管环境为Windows Server 2012 R1和IIS 7 5 无论如何 我们是否可以解决这个问题 https cloud githubuserconten
  • Func 方法参数的首选命名约定是什么?

    我承认这个问题是主观的 但我对社区的观点感兴趣 我有一个缓存类 它采用类型的缓存加载器函数Func
  • fgets() 和 Ctrl+D,三次才能结束?

    I don t understand why I need press Ctrl D for three times to send the EOF In addition if I press Enter then it only too
  • Cygwin 下使用 CMake 编译库

    我一直在尝试使用 CMake 来编译 TinyXML 作为一种迷你项目 尝试学习 CMake 作为补充 我试图将其编译成动态库并自行安装 以便它可以工作 到目前为止 我已经设法编译和安装它 但它编译成 dll 和 dll a 让它工作的唯一
  • 使用 Microsoft Graph API 订阅 Outlook 推送通知时出现 400 错误请求错误

    我正在尝试使用 Microsoft Graph API 创建订阅以通过推送通知获取 Outlook 电子邮件 mentions 我在用本文档 https learn microsoft com en us graph api subscri
  • C# 中可空类型是什么?

    当我们必须使用nullable输入 C net 任何人都可以举例说明 可空类型 何时使用可空类型 https web archive org web http broadcast oreilly com 2010 11 understand
  • 如何针对 Nancy 中的 Active Directory 进行身份验证?

    这是一篇过时的文章 但是http msdn microsoft com en us library ff650308 aspx paght000026 step3 http msdn microsoft com en us library
  • c 中的错误:声明隐藏了全局范围内的变量

    当我尝试编译以下代码时 我收到此错误消息 错误 声明隐藏了全局范围内的变量 无效迭代器 节点 根 我不明白我到底在哪里隐藏或隐藏了之前声明的全局变量 我怎样才能解决这个问题 typedef node typedef struct node
  • c# Asp.NET MVC 使用FileStreamResult下载excel文件

    我需要构建一个方法 它将接收模型 从中构建excel 构建和接收部分完成没有问题 然后使用内存流导出 让用户下载它 不将其保存在服务器上 我是 ASP NET 和 MVC 的新手 所以我找到了指南并将其构建为教程项目 public File
  • 按字典顺序对整数数组进行排序 C++

    我想按字典顺序对一个大整数数组 例如 100 万个元素 进行排序 Example input 100 21 22 99 1 927 sorted 1 100 21 22 927 99 我用最简单的方法做到了 将所有数字转换为字符串 非常昂贵
  • 在 ASP.Net Core 2.0 中导出到 Excel

    我曾经使用下面的代码在 ASP NET MVC 中将数据导出到 Excel Response AppendHeader content disposition attachment filename ExportedHtml xls Res
  • 使用向量的 merge_sort 在少于 9 个输入的情况下效果很好

    不知何故 我使用向量实现了合并排序 问题是 它可以在少于 9 个输入的情况下正常工作 但在有 9 个或更多输入的情况下 它会执行一些我不明白的操作 如下所示 Input 5 4 3 2 1 6 5 4 3 2 1 9 8 7 6 5 4 3
  • 如何在 Team Foundation 上强制发表有意义的签入评论?

    我有一个开发团队有一个坏习惯 他们写道poor签入评论 当我们必须在团队基础上查看文件的历史记录时 这使得它成为一场噩梦 我已经启用了变更集评论政策 这样他们甚至可以在签到时留下评论 否则他们不会 我们就团队的工作质量进行了一些讨论 他们很
  • 使用 LINQ 查找列表中特定类型的第一个元素

    使用 LINQ 和 C 在元素列表中查找特定类型的第一个项目的最短表示法是什么 var first yourCollection OfType
  • C 中的位移位

    如果与有符号整数对应的位模式右移 则 1 vacant bit will be filled by the sign bit 2 vacant bit will be filled by 0 3 The outcome is impleme
  • 已过时 - OpenCV 的错误模式

    我正在使用 OpenCV 1 进行一些图像处理 并且对 cvSetErrMode 函数 它是 CxCore 的一部分 感到困惑 OpenCV 具有三种错误模式 叶 调用错误处理程序后 程序终止 Parent 程序没有终止 但错误处理程序被调
  • 如何在内存中存储分子?

    我想将分子存储在内存中 这些可以是简单的分子 Methane CH4 C H bond length 108 7 pm H H angle 109 degrees But also more complex molecules like p
  • 方法参数内的变量赋值

    我刚刚发现 通过发现错误 你可以这样做 string s 3 int i int TryParse s hello out i returns false 使用赋值的返回值是否合法 Obviously i is but is this th
  • Bing 地图运行时错误 Windows 8.1

    当我运行带有 Bing Map 集成的 Windows 8 1 应用程序时 出现以下错误 Windows UI Xaml Markup XamlParseException 类型的异常 发生在 DistanceApp exe 中 但未在用户

随机推荐

  • jquery总结大纲

    一 选择器 基本选择器 什么是 id element class selector1 selector2 selectorN 答 根据给定的id匹配一个元素 用于搜索 通过id的属性给定值 案例 查找id为da3的元素 html代码 div
  • 超实用CSS技巧总结(1)——背景和边框

    文章内容来自CSS神书 CSS揭秘 献上膝盖就好 1 半透明边框 错误的写法 div background color white border 10px solid hsla 0 0 100 5 错误的原因很简单 因为默认情况下 背景是会
  • Base64编解码C源码

    文章目录 一 Base64是什么 二 示例源码 C语言接口 三 可打印字符表 一 Base64是什么 Base64是一种字节码编码方式 主要是基于64个可打印字符来表示二进制数据的方法 详细介绍请参考百度百科 百度百科介绍 二 示例源码 C
  • STMCubeMX+Proteus仿真DHT11(LCD1602显示)

    本文说明 开发工具 STM32CubeMX Keil5 开发芯片 STM32F103R6 采温模块 DHT11 数显屏幕 LCD1602 仿真软件 Proteus 8 12 项目目标 DHT11读取温湿度值 将信息显示在LCD1602上 T
  • 【Rust】002-基础语法:函数

    Rust 002 基础语法 函数 文章目录 Rust 002 基础语法 函数 一 概述 二 函数 1 基本函数 2 参数 3 即时补充 函数参数是 String 和 str 的区别 String str 对比 4 返回值 语句和表达式 语句
  • uniapp unipush 个推调试 通知消息 透传消息 的调试 踩坑指南

    前段时间分享了 unipush添加安卓手机的google厂商通道实现没有打开app也能收到推送消息也就是离线消息 今天分享一下调试的过程这是上次的文章地址https blog csdn net zhanghuanhuan1 article
  • 大鹏背景,大鹏为什么能邀请那么多明星?揭秘董成鹏的成名路

    大鹏到底是什么背景 为什么他自导自演的 屌丝男士 能请到那么多明星 为什么他参加 跑男 会和邓超等一众明星那么熟 为什么他自导自演的喜剧片 煎饼侠 能邀请袁珊珊 柳岩 郭采洁 尚格云顿 吴君如 邓超 小沈阳 宋小宝 岳云鹏等那么多明星 要了
  • 关于JAVA中的Class.cast方法

    今日看到了一片文章就是说的Class cast 方法 我感觉这篇文章有问题 所以写了一片文章来说明一下cast方法 我们来看一下JDK中这个方法的源码 SuppressWarnings unchecked public T cast Obj
  • Vue中一键批量注册全局组件

    文件目录如下 1 component文件夹中编写所有的公共组件 注意 之后一键注册的全局组件名就是每个公共组件 xxx vue 文件的文件名 xxx 2 plugins components js中批量注册组件 import Vue fro
  • vue3项目实战---知乎日报----个人中心

    目录 登录态校验 header组件 个人中心 修改个人信息 FormData FormData操作方法 登录态校验 通过 beforeEach 路由导航守卫实现 to 从哪来 from 去哪 next执行 通过to path 判断要去的页面
  • linux重启

    这本阿里P8撰写的算法笔记 再次推荐给大家 身边不少朋友学完这本书最后加入大厂 Github 疯传 史上最强悍 阿里大佬 LeetCode刷题手册 开放下载了 重启命令 1 reboot 2 shutdown r now 立刻重启 root
  • 数据字典标准与统一的重要性(码表&枚举值)

    在日常的软件开发当中 开发者经常会听到 公共代码 编码 码表 枚举值 这样的名词 对这些概念可能会有些混淆和认知不透彻 那么这篇文章会详细论述一下关于数据字典的相关概念 应用 标准与统一的重要性及其数据来源 关于 公共代码 编码 码表 枚举
  • VMware WorkStation安装CentOS7

    VMware WorkStation安装CentOS7 1 安装 设置bios 检查物理机虚拟化支持是否开启 需要进入到BIOS中设置 因各种电脑型号进入BIOS 方式不同 同学们自行查找对应品牌电脑如何进入BIOS 建议 先安装 如果安装
  • C语言 输出3X3的转置矩阵

    方法一 普通函数调用 include
  • GUN和GPL的大概意思

    广而告之 支持一下阿里云 阿里云ECS服务器 有幸运券了 欢迎大家来领取 https promotion aliyun com ntms act ambassador sharetouser html userCode 5uqvqirt p
  • VS code安装和使用技巧

    VS Code 是微软提供的一款轻量级但功能十分强大的编辑器 内置了对JavaScript TypeScript和Node js语言的支持 并且为其他语言如C C Python PHP等提供了丰富的扩展库和运行时 一 VS Code的安装
  • java nio 基础

    Java NIO 由以下三部分组成 Channels 通道部分 Buffers 数据载体部分 Selects 选择器部分 重点应用于网络开发 基于事件驱动类型 Channel 与Buffers 基本上 所有的NIO 都从Channel 开始
  • POP3 邮件接收 出现乱码了,希望哪位能帮忙看下啥问题!帮我改下,谢谢

    using System using System Collections Generic using System ComponentModel using System Data using System Drawing using S
  • tensorflow运行在gpu还是cpu

    tensorflow在电脑的gpu和cpu上均可运行 cpu 0 机器的 CPU device GPU 0 机器的 GPU 如果有一个 device GPU 1 机器的第二个 GPU 以此类推 当想要知道指令和张量在哪个设备上运行时 可以这
  • QT CREATOR 插件开发:添加新的工程类型

    Qt Creator 中 新的工程类型将出现在 文件 gt 新建 菜单项中 我们可以通过打开的选择工程类型的对话框来找到所需要的工程 在本章中 我们将学习如何向上面所示的对话框中添加新的工程类型 Core IWizard接口 Qt Crea