суббота, 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)



среда, 16 декабря 2009 г.

Еще блог по Qt Creator

Нашел еще блог по теме: Qt Creator Blog. Почитаю на досуге, вроде ничего так.

воскресенье, 13 декабря 2009 г.

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

Я тут нарыл неплохой документ по созданию плагинов с нуля. Он сильно экономит время- не надо лазить по хелпу и рыться в исходниках, пытаясь понять, с чего начать. Сразу скажу, что переводить эту доку на русский дословно я здесь не буду. Вместо этого кратко изложу, как я делал этот минимальный плагин, используя доку от VCreate Logic. Итак, часть 1-я.


Минимальный плагин

Этот плагин ничего полезного делать не будет, поэтому называется он DoNothing. Цель его создания- получить скелет, на который потом можно нарастить мясо. Вместе с тем, плагин должен успешно определяться по команде Help->About Plugins...

Что надо сделать:
  1. Скачать исходный код Qt Creator 1.3.
  2. Создать директорию для нового плагина.
  3. Создать файл проекта.
  4. Создать файл спецификации плагина.
  5. Создать файлы с исходным кодом.
  6. Собрать плагин и загрузить.

1. Исходный код Qt Creator 1.3

Скачиваем исходный код Qt Creator 1.3.0 и распаковываем, например, сюда: d:\Projects\qt-creator-1.3.0\.


2. Директория плагина

Директорию для исходников надо создать там же, где лежат исходники для остальных плагинов: d:\Projects\qt-creator-1.3.0\src\plugins\donothing

Далее все файлы будем создавать в этой директории. Соглашение об именах- я использую типичные для Qt Creator имена файлов и директорий. Регистр написания имен так же традиционен.


3. Файл проекта

Файл проекта назовем donothing.pro. Вот его содержимое:

TEMPLATE = lib
TARGET = DoNothing

include(../../qtcreatorplugin.pri)
include(../../plugins/coreplugin/coreplugin.pri)

HEADERS += donothingplugin.h
SOURCES += donothingplugin.cpp
OTHER_FILES += DoNothing.pluginspec

Этот файл:
  • говорит, что выходом будет библиотека (DoNothing.dll на Windows)
  • заставляет DoNothing читать установки из qtcreatorplugin.pri и coreplugin.pri
  • указывает .cpp и .h файлы с кодом плагина
  • добавляет в проект спецификацию плагина (не обязательно, но удобно)
Нам еще надо добавить наш плагин в проект всех плагинов. Добавляем вот эти строки в конец файла d:\Projects\qt-creator-1.3.0\src\plugins\plugins.pro:

SUBDIRS += plugin_DoNothing
plugin_DoNothing.subdir = donothing


4. Файл спецификации плагина

Файл спецификации назовем DoNothing.pluginspec. Туда надо записать:

<plugin name="DoNothing" version="0.0.1" compatVersion="1.3.0">
  <vendor>VCreate Logic Pvt. Ltd.</vendor>
  <copyright>(C) 2008-2009 VCreate Logic Pvt. Ltd.</copyright>
  <license>Do anything you want</license>
  <description>A plugin that does nothing</description>
  <url>http://www.vcreatelogic.com</url>
  <dependencyList>
    <dependency name="Core" version="1.3.0"/>
  </dependencyList>
</plugin>

Ключевых моментов тут два:
  • Необходимая версия Qt Creator- 1.3.0
  • Наш плагин зависит от плагина Core

5. Исходный код

Хедер файл назовем donothingplugin.h

Вот что в него надо записать:

#ifndef DONOTHINGPLUGIN_H
#define DONOTHINGPLUGIN_H

#include <extensionsystem/iplugin.h>

class DoNothingPlugin : public ExtensionSystem::IPlugin
{
public:
  DoNothingPlugin();
  ~DoNothingPlugin();

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

#endif // DONOTHINGPLUGIN_H

Наш плагин- это потомок IPlugin, который опрделяет интерфейс плагинов.

А вот и сишник (donothingplugin.cpp), который все объявленные методы и реализует:

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

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

    // Since our plugin really does nothing, we 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
}

// export the plugin class
Q_EXPORT_PLUGIN(DoNothingPlugin)

Комментарии в методах объясняют, для чего, собственно, эти методы нужны и когда они вызываются.


6. Сборка и использование плагина

Я собирал под Qt Creator 1.3. SDK можно взять здесь. Для Wiindows надо скачать сборку Qt 4.6 для MSVC.

Открываем наш плагин donothing.pro- File->Open File or Project. Build->Set Build Configuration: Release. И делаем Ctrl-B.

Если что не собирается, вот здесь я написал как собирать плагины для Qt Creator.

Копируем DoNothing.dll (libDoNothing.so в Linux) и DoNothing.pluginspec из d:\Projects\qt-creator-1.3.0\lib\qtcreator\plugins\Nokia\ в ту же директорию Nokia, но в Qt Creator 1.3.0. Перезапускаем Qt Creator.

По команде Help->About Plugins... видим наш новый плагин:




среда, 2 декабря 2009 г.

Если Qt Creator не ставится/не компилирует(ся)/...

Решил вот тут вот собрать всякие заморочки с установкой Qt Creator. На случай, если устанавливать надо будет на новый линух или еще куда. Буду пополнять список по мере поступления...

1. Первым делом установить g++. Иначе компиляция в MinGW не заработает. И не важно, что в Ubuntu gcc есть по умолчанию.
$ sudo apt-get install g++

2. Вот что рекомендуют поставить в инсталляторе Qt SDK 2010.02:

$ sudo apt-get install libglib2.0-dev libSM-dev libxrender-dev libfontconfig1-dev libxext-dev


3. Если во время компиляции чего-либо выскакивает ошибка
/usr/bin/ld: cannot find -lgobject-2.0
то надо установить libglib2.0-0 и libglib2.0-dev:
$ sudo apt-get install libglib2.0-0 libglib2.0-dev

4. Если компиляция плагина вылетает с ошибкой
/usr/bin/ld: cannot find -lAggregation
то надо билдить проект src/libs/libs.pro

5. Если компиляция библиотек вылетает с двумя ошибками (Qt Creator 2.0.1, при включенном shadow build):
cannot find -lAggregation
collect2: ld returned 1 exit status
то надо просто отключить shadow build.

6. Если компиляция библиотек вылетает с ошибкой (Qt Creator 2.0.1 и Qt 4.7 RC1)
[.moc/release-shared/moc_launcher.cpp] Segmentation fault
то надо отдельно построить библиотеку symbianutils.

7. Если сборка Qt Creator оканчивается кучей ошибок линкера LINK2019 с неразрешенными внешними ссылками на WebKit из проекта help.pro (Qt Creator 2.0.1, 2.1 и бинарники Qt Framework 4.7.1), например:
centralwidget.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __thiscall QWebView::print(class QPrinter *)const " (__imp_?print@QWebView@@QBEXPAVQPrinter@@@Z) referenced in function "public: void __thiscall Help::Internal::CentralWidget::print(void)" (?print@CentralWidget@Internal@Help@@QAEXXZ)
то для решения см. вот этот пост,

Сборка Qt из исходников

UPD по теме:

Для успешной установки нужно поставить:
  • Perl
  • библиотеку libxext-dev (понятно, только линукс)
  • git

Windows:


Для сборки Qt из исходников нужен Perl. Я установил ActiveState Perl. После установки надо перегрузить машину.

Установка git описана здесь.

После установке Perl и git, запускаем git bash и переходим в директорию, где будет создана директория qt. Затем клонируем исходники:
$ cd /Projects
$ git clone git://gitorious.org/qt/qt.git

Добавляем путь к bin в переменную среды PATH: d:\Projects\Qt\bin

Открываем VS2008 command prompt, переходим в директорию с исходниками qt и конфигурируем:
cd d:\projects\qt
configure -nomake examples -nomake demos -qt-sql-sqlite -no-qt3support -no-opengl -no-dbus -no-phonon -no-phonon-backend

После того, как configure закончился, делаем nmake.

Занял он на моей машине два часа, как и раньше. Так что компиляция примеров занимает 2 часа.


Ubuntu:


Достаем исходники из репозитория:
$ cd /home/qtcgeek/Projects
$ git clone git://gitorious.org/qt/qt.gi

Добавляем путь к bin в переменную PATH в .profile:
PATH=/home/qtcgeek/Projects/qt/bin:$PATH
export PATH

Под Ubuntu perl уже должен быть установлен. А вот библиотеки libxext-dev может и не быть. Ставим:
$ sudo apt-get install libxext-dev

Машину надо перегрузить, иначе линкер вылетит в segment fault. А может, и не вылетит.

Затем переходим в директорию с исходниками qt и конфигурируем:
$ cd /home/qtcgeek/Projects/qt
$ configure -nomake examples -nomake demos -qt-sql-sqlite -no-qt3support -no-opengl -no-phonon -no-phonon-backend

После того, как configure закончился, делаем make:
$ make

На лаптопе под убунту он занял 3.5 часа. Сравнить быстродействие Ubuntu/MinGW с Windows/MSVC не могу, каждая система живет на своей собственной машине. Если кто сравнивал, поделитесь инфой плиз.

Устанавливаем:
$ sudo make install

Если надо переконфигурировать, делаем make confclean, configure, затем make (а под Windows- nmake вместо make).

вторник, 1 декабря 2009 г.

Qt Creator 1.3 и Qt 4.6

Тролли официально выпустили Qt Creator 1.3 и Qt 4.6. Качаем отсюда.

Что нового заявлено в 1.3:
  • Начальная поддержка Symbian
  • Поддержка компиляции (MSVC) на многоядерных процессорах
  • Обновленная цепочка инструментов для MinGW 4.4
  • Рефакторинг кода

Полное описание добавлений/исправлений Qt Creator- тут.

UPD: Qt 4.6 можно скачать уже собранную- как под MinGW, так и под MSVC. Не надо больше, как раньше, пересобирать под MSVC сразу после закачки.