初識 Qt Creator 插件開發(fā)

本文通過幾個示例,來學(xué)習(xí)如何開發(fā) Qt Creator 插件。

首先,先確定幾個目錄的位置:

  • Qt 安裝目錄 - /usr/software/Qt5.13.1
  • Qt Creator 安裝目錄 - /data/qt-creator-opensource-src-4.10.1

目錄的位置不是固定的,可以按照自己的習(xí)慣選擇安裝時的目錄。

準(zhǔn)備工作

源碼下載

注意,安裝 qt 時,請最大安裝,因?yàn)榫幾g qt creator 需要依賴所有的 qt 庫,非最大安裝會出現(xiàn)編譯錯誤

首先,需要安裝 qt,建議下載離線安裝包,本示例使用 Qt 5.13.1
http://download.qt.io/official_releases/qt/5.13/5.13.1/qt-opensource-linux-x64-5.13.1.run
或者訪問下載目錄選擇需要的版本下載
http://download.qt.io/official_releases/qt/

還需下載 qt creator 源碼,本示例使用 Qt Creator 4.10.1
http://download.qt.io/official_releases/qtcreator/4.10/4.10.1/qt-creator-opensource-src-4.10.1.tar.gz
或者訪問下載目錄選擇需要的版本下載
http://download.qt.io/official_releases/qtcreator/

編譯

注意,編譯時間較長,請耐心等待

運(yùn)行一下命令執(zhí)行編譯

# 進(jìn)入 qt creator 安裝目錄
$ cd /data/qt-creator-opensource-src-4.10.1

# 運(yùn)行 qmake
$ /usr/software/Qt5.12.5/5.12.5/gcc_64/bin/qmake -r

# 開始編譯
$ make -j8

這里執(zhí)行 make -j8,表示使用8核運(yùn)行編譯,視編譯機(jī)器的cpu數(shù)決定 -jN 參數(shù)中N的值,最大與cpu核心數(shù)相等,可縮短編譯時間

編譯完成后會在 qt creator 安裝目錄中的 bin 目錄中生成可執(zhí)行文件,運(yùn)行 ./bin/qtcreator 啟動ide。

創(chuàng)建插件項(xiàng)目

在 Qt Creator 中選擇 文件 > 新建文件或項(xiàng)目 > Library > Qt Creator 插件,之后根據(jù)項(xiàng)目向?qū)瓿刹寮?xiàng)目的創(chuàng)建。

注意,向?qū)У谌?插件信息 頁面中的 Qt Creator源文件Qt Creator構(gòu)建 處需要選擇 Qt Creaotr 安裝目錄,這里選擇 /data/qt-creator-opensource-src-4.10.1

插件信息頁

項(xiàng)目創(chuàng)建完成后,目錄結(jié)構(gòu)如下:


項(xiàng)目結(jié)構(gòu)
目錄結(jié)構(gòu)

詳解

接下來將分析主要的項(xiàng)目文件

Demo1.json.in
該定義了插件的信息,插件編譯時會生成名為 Demo1.json 的文件。

這里需要注意一下,修改 Demo1.json.in 文件,將該文件的中字符串key和value前后的雙引號 " 替換成 \",否則插件可能無法運(yùn)行。

{
    \"Name\" : \"Demo1\",
    \"Version\" : \"0.0.1\",
    \"CompatVersion\" : \"0.0.1\",
    \"Vendor\" : \"abeir\",
    \"Copyright\" : \"(C) SyberOS\",
    \"License\" : \"Put your license information here\",
    \"Description\" : \"Put a short description of your plugin here\",
    \"Url\" : \"http://www.syberos.com\",
    $$dependencyList
}

demo1.pro

DEFINES += DEMO1_LIBRARY

SOURCES +=         demo1plugin.cpp

HEADERS +=         demo1plugin.h         demo1_global.h         demo1constants.h

isEmpty(IDE_SOURCE_TREE): IDE_SOURCE_TREE = $$(QTC_SOURCE)
isEmpty(IDE_SOURCE_TREE): IDE_SOURCE_TREE = "/data/qt-creator-opensource-src-4.10.1"

isEmpty(IDE_BUILD_TREE): IDE_BUILD_TREE = $$(QTC_BUILD)
isEmpty(IDE_BUILD_TREE): IDE_BUILD_TREE = "/data/qt-creator-opensource-src-4.10.1"

QTC_PLUGIN_NAME = Demo1
QTC_LIB_DEPENDS +=     # nothing here at this time

QTC_PLUGIN_DEPENDS +=     coreplugin

QTC_PLUGIN_RECOMMENDS +=     # optional plugin dependencies. nothing here at this time

include($$IDE_SOURCE_TREE/src/qtcreatorplugin.pri)

QTC_SOURCE 和 QTC_BUILD 變量指明 Qt Creator 的源碼目錄和構(gòu)建目錄。可以通過設(shè)置環(huán)境變量的方式修改至其他路徑。由于之前創(chuàng)建插件項(xiàng)目時,指定了源碼目錄和構(gòu)建目錄,在這里都指向了 /data/qt-creator-opensource-src-4.10.1
QTC_LIB_DEPENDS 指定依賴的庫。
QTC_PLUGIN_DEPENDS 指定依賴的其他插件。

demo1plugin.h

#ifndef DEMO1_H
#define DEMO1_H
#include "demo1_global.h"

#include <extensionsystem/iplugin.h>

namespace Demo1 {
namespace Internal {

class Demo1Plugin : public ExtensionSystem::IPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Demo1.json")

public:
    Demo1Plugin();
    ~Demo1Plugin() override;

    bool initialize(const QStringList &arguments, QString *errorString) override;
    void extensionsInitialized() override;
    ShutdownFlag aboutToShutdown() override;

private:
    void triggerAction();
};

} // namespace Internal
} // namespace Demo1


#endif // DEMO1_H

IPlugin 類是插件的基類,所有的插件都需要繼承該類,并實(shí)現(xiàn)其中的虛函數(shù)。
IPlugin 提供了一系列函數(shù),用于插件加載到一定階段時進(jìn)行回調(diào)。下面我們按照插件加載的順序介紹 IPlugin 提供的一些函數(shù)。

當(dāng)插件描述文件讀取、并且所有依賴都滿足之后,插件將開始進(jìn)行加載。這一步分為三個階段:

  1. 所有插件的庫文件按照依賴樹從根到葉子的順序進(jìn)行加載。
  2. 按照依賴樹從根到葉子的順序依次調(diào)用每個插件的 IPlugin::initialize() 函數(shù)。
  3. 按照依賴樹從葉子到根的順序依次調(diào)用每個插件的 IPlugin::extensionsInitialized() 函數(shù)。
virtual bool initialize(const QStringList &arguments, QString *errorString) = 0;

該函數(shù)會在插件加載完成,并且創(chuàng)建了插件對象之后調(diào)用。該函數(shù)返回值是bool類型,當(dāng)插件初始化成功,返回 true;否則,由 errorString 參數(shù)將錯誤信息返出。當(dāng)前插件的 initialize() 函數(shù)會在所有依賴的插件都調(diào)用了 initialize() 函數(shù)之后被調(diào)用 。如果插件需要共享一些對象,就應(yīng)該將這些共享對象放在這個函數(shù)中。

virtual void extensionsInitialized() = 0;

該函數(shù)在 initialize() 函數(shù)調(diào)用完畢、并且所依賴插件的 extensionsInitialized() 函數(shù)調(diào)用完畢之后被調(diào)用。當(dāng)運(yùn)行到這一階段時,插件所依賴的其它插件都已經(jīng)初始化完畢。這也暗示著,該插件所依賴的各個插件提供的可被共享的對象都已經(jīng)創(chuàng)建完畢,可以正常使用了。

virtual bool delayedInitialize() { return false; }

該函數(shù)會在 extensionsInitialized() 函數(shù)調(diào)用完成,并且所依賴插件的 delayedInitialize() 函數(shù)也調(diào)用完成之后才被調(diào)用。delayedInitialize() 函數(shù)會在程序運(yùn)行之后才被調(diào)用,并且距離程序啟動有幾個毫秒的間隔。為避免不必要的延遲,插件對該函數(shù)的實(shí)現(xiàn)應(yīng)該盡快返回。該函數(shù)的意義在于,有些插件可能需要進(jìn)行一些重要的啟動工作;這些工作雖然不必在啟動時直接完成,但也應(yīng)該在程序啟動之后的較短時間內(nèi)完成。該函數(shù)默認(rèn)返回false,即不需要延遲初始化。

virtual ShutdownFlag aboutToShutdown() { return SynchronousShutdown; }

signals:
    void asynchronousShutdownFinished();

aboutToShutdown() 函數(shù)應(yīng)該用于與其它插件斷開連接、隱藏所有 UI、優(yōu)化關(guān)閉操作,會以插件初始化的相反順序調(diào)用,即先調(diào)用當(dāng)前插件的 aboutToShutdown() 函數(shù),再依次調(diào)用依賴插件的。如果插件需要延遲真正的關(guān)閉,例如,需要等待外部進(jìn)程執(zhí)行完畢,以便自己完全關(guān)閉,則應(yīng)該返回 AsynchronousShutdown,這么做的話會進(jìn)入主事件循環(huán),等待所有返回了 AsynchronousShutdown 的插件都發(fā)出了 asynchronousShutdownFinished() 信號之后,再執(zhí)行相關(guān)操作。該函數(shù)默認(rèn)返回 SynchronousShutdown,即不等待其它插件關(guān)閉。

下面,我們再回過頭來看看 demo1plugin.h

Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Demo1.json")

Q_PLUGIN_METADATA 這個宏用于聲明插件的元數(shù)據(jù)。當(dāng)實(shí)例化插件對象時,這些元數(shù)據(jù)會作為該對象的一部分。這個宏需要聲明一個 IID 屬性,用于標(biāo)識對象實(shí)現(xiàn)的接口;還需要一個文件的引用FILE屬性,該文件包含了插件的元數(shù)據(jù)。
使用這個宏的類必須有無參數(shù)的構(gòu)造函數(shù)。在這里,Qt Creator 規(guī)定其插件的 IID 必須是 org.qt-project.Qt.QtCreatorPlugin。FILE指向了 Demo1.json 文件,但是項(xiàng)目里只有 Demo1.json.in,這是由于編譯時,會使用 Demo1.json.in 生成 Demo1.json 文件,我們可以在編譯目錄下找到該文件。

demo1plugin.cpp

#include "demo1plugin.h"
#include "demo1constants.h"

#include <coreplugin/icore.h>
#include <coreplugin/icontext.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/coreconstants.h>

#include <QAction>
#include <QMessageBox>
#include <QMainWindow>
#include <QMenu>

namespace Demo1 {
namespace Internal {

Demo1Plugin::Demo1Plugin()
{
}

Demo1Plugin::~Demo1Plugin()
{
}

bool Demo1Plugin::initialize(const QStringList &arguments, QString *errorString)
{
    Q_UNUSED(arguments)
    Q_UNUSED(errorString)

    auto action = new QAction(tr("Demo1 Action"), this);
    Core::Command *cmd = Core::ActionManager::registerAction(action, Constants::ACTION_ID,
                                                             Core::Context(Core::Constants::C_GLOBAL));
    cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Meta+A")));
    connect(action, &QAction::triggered, this, &Demo1Plugin::triggerAction);

    Core::ActionContainer *menu = Core::ActionManager::createMenu(Constants::MENU_ID);
    menu->menu()->setTitle(tr("Demo1"));
    menu->addAction(cmd);
    Core::ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu);

    return true;
}

void Demo1Plugin::extensionsInitialized()
{
}

ExtensionSystem::IPlugin::ShutdownFlag Demo1Plugin::aboutToShutdown()
{
    return SynchronousShutdown;
}

void Demo1Plugin::triggerAction()
{
    QMessageBox::information(Core::ICore::mainWindow(),
                             tr("Action Triggered"),
                             tr("This is an action from Demo1."));
}

} // namespace Internal
} // namespace Demo1

接下來分析一下 demo1plugin.cpp 都做了些什么。

auto action = new QAction(tr("Demo1 Action"), this);
Core::Command *cmd = Core::ActionManager::registerAction(action, Constants::ACTION_ID,
                                                             Core::Context(Core::Constants::C_GLOBAL));
cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Meta+A")));
connect(action, &QAction::triggered, this, &Demo1Plugin::triggerAction);

這段代碼定義了一個動作 Demo1 Action,并為該動作設(shè)置了快捷鍵 Ctrl+Alt+Meta+A,最后再將 triggered 信號綁定至本類中的 triggerAction 槽函數(shù)上。triggered 信號是在用戶觸發(fā)了動作,比如,單擊或使用快捷鍵時發(fā)送。

Core::ActionContainer *menu = Core::ActionManager::createMenu(Constants::MENU_ID);
menu->menu()->setTitle(tr("Demo1"));
menu->addAction(cmd);
Core::ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu);

這段代碼創(chuàng)建了一個菜單,并設(shè)置了菜單名為 Demo1,最后再將菜單添加到 Qt Creator 的 工具 菜單中。
Qt Creator 提供了一些主菜單允許我們將自己創(chuàng)建的菜單添加進(jìn)去:

  • Core::Constants::M_FILE - 文件
  • Core::Constants::M_EDIT - 編輯
  • Core::Constants::M_EDIT_ADVANCED - 編輯 > Advanced
  • Core::Constants::M_TOOLS - 工具
  • Core::Constants::M_TOOLS_EXTERNAL - 工具 > 外部
  • Core::Constants::M_WINDOW - 控件
  • Core::Constants::M_WINDOW_MODESTYLES - 控件 > Mode Selector Style
  • Core::Constants::M_WINDOW_VIEWS - 控件 > 視圖
  • Core::Constants::M_HELP - 幫助
void Demo1Plugin::triggerAction()
{
    QMessageBox::information(Core::ICore::mainWindow(),
                             tr("Action Triggered"),
                             tr("This is an action from Demo1."));
}

這段代碼是當(dāng)我們點(diǎn)擊 工具 > Demo1 > Demo1 Action 時,觸發(fā)一個彈出框。

最后,運(yùn)行 構(gòu)建項(xiàng)目,Demo1 插件將被編譯成名為 libDemo1.so 的文件并安裝至 /data/qt-creator-opensource-src-4.10.1/lib/qtcreator/plugins

運(yùn)行

$ /data/qt-creator-opensource-src-4.10.1/bin/qtcreator

啟動 Qt Creator 就可以在 工具 菜單中找到我們的插件。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.Qt概述 1.1 什么是Qt Qt是一個跨平臺的C++圖形用戶界面應(yīng)用程序框架。它為應(yīng)用程序開發(fā)者提供建立藝術(shù)...
    你的社交帳號昵閱讀 8,877評論 0 10
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,659評論 1 32
  • Qt是屬于一個跨平臺的GUI開發(fā)軟件,支持的平臺有Unix、Linux、Windows/WinCE、IOS等。 Q...
    一葉之界閱讀 8,411評論 0 17
  • 一池塘的荷花多少天的時候會開滿一半? 你以為是第15天,其實(shí)是在第29天的時候,當(dāng)?shù)?0天來臨時,滿池的荷花都開了...
    多則惑少則得閱讀 176評論 0 0
  • 又一年了,新年快樂哇!你會更好的!我會在未來中做到獨(dú)擋一面的!想做最最真誠的自己!趙鈳的歌曲《有人》聽的我感慨萬千...
    茯苓_(dá)fuling閱讀 61評論 0 1

友情鏈接更多精彩內(nèi)容