суббота, 26 декабря 2009 г.

Пишем плагин для Qt Creator - часть 2

Этот пост- продолжение серии про написание плагинов для Qt Creator. Начало, про минимальный рабочий плагин, см. здесь. В этой части речь пойдет про то, как привязаться к меню Qt Creator.

Для подготовки этого поста я, как и раньше, использовал доку от VCreate Logic, только в этот раз материал переработан в гораздо большей степени.

Работа с меню

Нам нужно уметь делать несколько вещей:
  • добавлять пункт меню в сушествующее меню Qt Creator (например, Help)
  • добавлять меню в менюбар Qt Creator
  • добавлять пункт меню в новое меню
  • регистрировать новые меню и пункты меню
  • обрабатывать вызов меню и пункта меню
Для демонстрации возьмем плагин DoNothing, который мы начали создавать в части 1 этой серии. Меню и пункты меню будем создавать в методе DoNothing::initialize(), поскольку это часть инициализации плагина. Сначала создадим пункт меню About DoNothing в меню Help, перед пунктом About Plugins, затем создадим новое меню DoNothing c единственным пунктом About DoNothing:


bool DoNothingPlugin::initialize(const QStringList& args, QString *errMsg)
{
    // The initialize() method is called when Qt Creator wants the plugin to initialize itself. This function is ideally used to initialize the internal state of the plugin and register actions/objects with Qt Creator.
    // The function is called after all the dependencies of this plugin have been loaded.

    Q_UNUSED(args);
    Q_UNUSED(errMsg);

    createMenuItems();
    createMenus();

    // Return true signifying that the initialization was successful.
    // If the initialization was unsuccessful (for some wired reason); the errMsg string should be set to a human readable error message.
    return true;
}


Вся работа с меню строится на использовании классов QMenu, QMenuBar, QAction, и нескольких классов Core:
  • Core::ActionManager
  • Core::ActionContainer
  • Core::Command

ActionManager отвечает за регистрацию меню, пунктов меню и горячих клавиш.

ActionContainer представляет собой меню или менюбар. Экземпляры ActionContainer никогда не создаются напрямую, только с помощью ActionManager::createMenu(), ActionManager::createMenuBar(). Получить существующее меню можно через ActionManger, используя константы Core::Constants (M_FILE, M_FILE_NEW,... M_HELP).

Core::Command представляет такие действия как выбор пункта меню, нажатие кнопки или горячей клавиши. Экземпляры Core::Command также не создаются напрямую, вместо этого используются вызовы ActionManager::registerAction() для регистрации действия и получения команды. Объект Command представляет видимое для пользователя действие и его свойства.

Еще два слова об ActionManager::registerAction(). Этот метод регистрирует наши новые меню и пункты меню в ActionManager. Что, помимо прочих вещей, позволяет определять горячие клавиши на них (см. Tools->Options->Environment->Keyboard).



Поскольку все пункты меню являются QAction, их сигналы triggered(bool) или toggled(bool) связываются с нашим обработчиком посредством connect(). Подробности см. в коде.

Далее мы рассмотрим два метода- CreateMenuItems() и CreateMenus(). Полностью код плагина приведен в конце поста.


Добавляем пункт меню в сушествующее меню Qt Creator

Добавить пункт меню можно как в начало меню, так и в середину. Добиться первого можно с помощью вызова метода Core::ActionContainer::addAction(), а второго- с помощью вызова метода QMenu::insertAction().

insertAction() в качестве первого параметра получает пункт уже существующего меню (указатель на QAction), перед которым надо добавить новый пункт меню. Ну а новый пункт меню (опять же указатель на QAction) передается во втором параметре.

Приведенный внизу код содержит оба метода. Мы добавим два новых пункта в меню Help- About DoNothing и About DoNothing 2:




void DoNothingPlugin::createMenuItems()
{
    // Fetch the action manager
    Core::ActionManager* am = Core::ICore::instance()->actionManager();

    // Create a command for "About DoNothing"
    Core::Command* cmd = am->registerAction(new QAction(this), "DoNothingPlugin.AboutDoNothingItem", QList<int>() << Core::Constants::C_GLOBAL_ID);
    cmd->action()->setText("About DoNothing");

    // Add the command "Do Nothing" in the beginning of Help menu
    am->actionContainer(Core::Constants::M_HELP)->addAction(cmd);

    // Since menu-items are QActions, we can connect to their triggered(bool) or
    // toggled(bool) signal and respond to trigger/toggled events
    connect(cmd->action(), SIGNAL(triggered(bool)), this, SLOT(about()));

    // Create a command for "About DoNothing 2"
    Core::Command* cmd2 = am->registerAction(new QAction(this), "DoNothingPlugin.AboutDoNothing2Item", QList<int>() << Core::Constants::C_GLOBAL_ID);
    cmd2->action()->setText("About DoNothing 2");

    // Insert the "DoNothing 2" item before "About Plugins..."
    QMenu* helpMenu = am->actionContainer(Core::Constants::M_HELP)->menu();
    QAction* aboutPluginsAction = am->command(Core::Constants::ABOUT_PLUGINS)->action();
    helpMenu->insertAction(aboutPluginsAction, cmd2->action());

    // Connect the action
    connect(cmd2->action(), SIGNAL(triggered(bool)), this, SLOT(about()));
}



Добавляем новое меню в Qt Creator

Новое меню можно добававить либо в начало менюбара, либо где-нибудь в середину. Аналогично с пунктом меню, существуют методы Core::ActionContainer::addMenu() и QMenuBar::insertMenu().

isertMenu(), как и insertAction(), в качестве первого параметра получает указатель на QActon, который является меню, перед которым надо добавить наше новое меню. Как и раньше, указатель на новое передается во втором параметре.

Мы создадим два меню- в самом начале менюбара и перед меню Help:




void DoNothingPlugin::createMenus()
{
    // Fetch the action manager
    Core::ActionManager* am = Core::ICore::instance()->actionManager();

    // Create a DoNothing menu
    Core::ActionContainer* ac = am->createMenu("DoNothingPlugin.DoNothingMenu");
    ac->menu()->setTitle(tr("DoNothing"));

    // Create a command for "About DoNothing".
    Core::Command* cmd = am->registerAction(new QAction(this), "DoNothingPlugin.AboutDoNothing", QList<int>() << 0);
    cmd->action()->setText("About DoNothing");

    // Add DoNothing menu to the beginning of the menu bar
    am->actionContainer(Core::Constants::MENU_BAR)->addMenu(ac);

    // Add the "About DoNothing" action to the DoNothing menu
    ac->addAction(cmd);

    // Connect the action
    connect(cmd->action(), SIGNAL(triggered(bool)), this, SLOT(about()));

    // Create a DoNothing2 menu
    Core::ActionContainer* ac2 = am->createMenu("DoNothingPlugin.DoNothing2Menu");
    ac2->menu()->setTitle(tr("DoNothing2"));

    // Create a command for "About DoNothing 2".
    Core::Command* cmd2 = am->registerAction(new QAction(this), "DoNothingPlugin.AboutDoNothing2", QList<int>() << 0);
    cmd2->action()->setText("About DoNothing 2");

    // Insert the "DoNothing" menu between "Window" and "Help".
    QMenu* helpMenu = am->actionContainer(Core::Constants::M_HELP)->menu();
    QMenuBar* menuBar = am->actionContainer(Core::Constants::MENU_BAR)->menuBar();
    menuBar->insertMenu(helpMenu->menuAction(), ac2->menu());

    // Add the "About DoNothing 2" action to the DoNothing2 menu
    ac2->addAction(cmd2);

    // Connect the action
    connect(cmd2->action(), SIGNAL(triggered(bool)), this, SLOT(about()));
}



Код плагина

donothingplugin.h:


#ifndef DONOTHINGPLUGIN_H
#define DONOTHINGPLUGIN_H

#include <extensionsystem/iplugin.h>

class DoNothingPlugin : public ExtensionSystem::IPlugin
{
    Q_OBJECT

public:
    DoNothingPlugin();
    ~DoNothingPlugin();

    void extensionsInitialized();
    bool initialize(const QStringList & arguments, QString * errorString);
    void shutdown();

private:
    void createMenus();
    void createMenuItems();

private slots:
    void about();

};

#endif // DONOTHINGPLUGIN_H


donothingplugin.cpp


#include "donothingplugin.h"
#include <QtPlugin>
#include <QStringList>

#include <coreplugin/coreconstants.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/icore.h>
#include <QKeySequence>
#include <QtGui/QMessageBox>

DoNothingPlugin::DoNothingPlugin()
{
    // Do nothing
}

DoNothingPlugin::~DoNothingPlugin()
{
    // Do notning
}

bool DoNothingPlugin::initialize(const QStringList& args, QString *errMsg)
{
    // The initialize() method is called when Qt Creator wants the plugin to initialize itself. This function is ideally used to initialize the internal state of the plugin and register actions/objects with Qt Creator.
    // The function is called after all the dependencies of this plugin have been loaded.

    Q_UNUSED(args);
    Q_UNUSED(errMsg);

    createMenuItems();
    createMenus();

    // Return true signifying that the initialization was successful.
    // If the initialization was unsuccessful (for some wired reason); the errMsg string should be set to a human readable error message.
    return true;
}

void DoNothingPlugin::extensionsInitialized()
{
    // The extensionsInitialized() method is called after this plugin has been initialized (ie. after initialize() method has been called). This method is called on plugins that depend on this plugin first.

    // Do nothing
}

void DoNothingPlugin::shutdown()
{
    //The shutdown() method is called when the plugin is about to be unloaded.

    // Do nothing
}

void DoNothingPlugin::createMenuItems()
{
    // Fetch the action manager
    Core::ActionManager* am = Core::ICore::instance()->actionManager();

    // Create a command for "About DoNothing"
    Core::Command* cmd = am->registerAction(new QAction(this), "DoNothingPlugin.AboutDoNothingItem", QList<int>() << Core::Constants::C_GLOBAL_ID);
    cmd->action()->setText("About DoNothing");

    // Add the command "Do Nothing" in the beginning of Help menu
    am->actionContainer(Core::Constants::M_HELP)->addAction(cmd);

    // Since menu-items are QActions, we can connect to their triggered(bool) or
    // toggled(bool) signal and respond to trigger/toggled events
    connect(cmd->action(), SIGNAL(triggered(bool)), this, SLOT(about()));

    // Create a command for "About DoNothing 2"
    Core::Command* cmd2 = am->registerAction(new QAction(this), "DoNothingPlugin.AboutDoNothing2Item", QList<int>() << Core::Constants::C_GLOBAL_ID);
    cmd2->action()->setText("About DoNothing 2");

    // Insert the "DoNothing 2" item before "About Plugins..."
    QMenu* helpMenu = am->actionContainer(Core::Constants::M_HELP)->menu();
    QAction* aboutPluginsAction = am->command(Core::Constants::ABOUT_PLUGINS)->action();
    helpMenu->insertAction(aboutPluginsAction, cmd2->action());

    // Connect the action
    connect(cmd2->action(), SIGNAL(triggered(bool)), this, SLOT(about()));
}

void DoNothingPlugin::createMenus()
{
    // Fetch the action manager
    Core::ActionManager* am = Core::ICore::instance()->actionManager();

    // Create a DoNothing menu
    Core::ActionContainer* ac = am->createMenu("DoNothingPlugin.DoNothingMenu");
    ac->menu()->setTitle(tr("DoNothing"));

    // Create a command for "About DoNothing".
    Core::Command* cmd = am->registerAction(new QAction(this), "DoNothingPlugin.AboutDoNothing", QList<int>() << 0);
    cmd->action()->setText("About DoNothing");

    // Add DoNothing menu to the beginning of the menu bar
    am->actionContainer(Core::Constants::MENU_BAR)->addMenu(ac);

    // Add the "About DoNothing" action to the DoNothing menu
    ac->addAction(cmd);

    // Connect the action
    connect(cmd->action(), SIGNAL(triggered(bool)), this, SLOT(about()));

    // Create a DoNothing2 menu
    Core::ActionContainer* ac2 = am->createMenu("DoNothingPlugin.DoNothing2Menu");
    ac2->menu()->setTitle(tr("DoNothing2"));

    // Create a command for "About DoNothing 2".
    Core::Command* cmd2 = am->registerAction(new QAction(this), "DoNothingPlugin.AboutDoNothing2", QList<int>() << 0);
    cmd2->action()->setText("About DoNothing 2");

    // Insert the "DoNothing" menu between "Window" and "Help".
    QMenu* helpMenu = am->actionContainer(Core::Constants::M_HELP)->menu();
    QMenuBar* menuBar = am->actionContainer(Core::Constants::MENU_BAR)->menuBar();
    menuBar->insertMenu(helpMenu->menuAction(), ac2->menu());

    // Add the "About DoNothing 2" action to the DoNothing2 menu
    ac2->addAction(cmd2);

    // Connect the action
    connect(cmd2->action(), SIGNAL(triggered(bool)), this, SLOT(about()));
}

void DoNothingPlugin::about()
{
    QMessageBox::information(0, "About DoNothing Plugin", "Seriously dude, this plugin does nothing", QMessageBox::Ok);
}

// export the plugin class
Q_EXPORT_PLUGIN(DoNothingPlugin)



3 комментария:

  1. А как заставить записываться например в плагине редактора кода без диалогового окна ?

    ОтветитьУдалить
  2. Как горячие клавиши сразу устанавливать при регистрации действия в registerAction()? Не знаю пока. Нужно сырцы других плагинов посмотреть. Как найду, проапдейчу.

    ОтветитьУдалить
  3. В нужной команде определяем последовательность по умолчанию перед тем как добавить/вставить команду. Т.е. вот так:
    cmd->setDefaultKeySequence(QKeySequence(tr("Alt+N")));
    am->actionContainer(Core::Constants::M_HELP)->addAction(cmd);

    ОтветитьУдалить