суббота, 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 сразу после закачки.

суббота, 28 ноября 2009 г.

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

На выходных решил посмотреть свежайший Qt Creator. Не тот, который 1.3-RC1 (т.е. 1.2.94), и не тот который можно найти в Qt Creator snapshots (1.3.0), а тот, который можно собрать из официального репозитория (1.3.80). Мимоходом замечу, что 1.3-RC1 отличается от свежака на 86 (!) ревизий. Такое впечатление, что за официальным 1.3 сразу выйдет beta 1.4.

Похоже, однако, что копают больше вглубь, чем вширь. Что видно сразу: в master branch добавили плагин для Mecurial. И все.

Однако, вернемся к предмету разговора. Итак, что нужно, чтобы получить 1.3.80? Нужно всего ничего:
1. Установить git.
2. Склонировать исходники Qt Creator из репозитория.
3. Собрать Qt Creator.

Ну и еще куча времени, потому как билд от 28.11.09 не особенно хочет работать под Windows. Об этом ниже.

1. Установка git.

Установка из исходников или бинарников для разных систем отлично описана, в том числе и по-русски, в книжке про git, см. главу 1.4. В главе 1.5 рассказано как нужно сконфигурировать git сразу после установки, т.е. задать имя пользователя и пр.

Вкратце, я делал вот так:

Под Ubuntu:
$ sudo apt-get install git-core

Под Windows нам нужен msysGit, живет он на Google Code. Не знаю, есть ли другие порты git'a для Windows, но этот сделан что-то уж чересчур сердито. Скачиваем и устанавливаем последнюю версию msysGit (на момент написания- 1.6.5.1) отсюда.

После установки нужно задать имя, мэйл, редактор и инструмент merge. Под Windows все делается все равно из-под bash, так что синтаксис одинаковый:
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com
$ git config --global core.editor notepad
$ git config --global merge.tool kdiff3

Проверка установок:
$ git config --list

2. Клонирование исходников из репозитория.

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

На будущее, если нам нужно достать последние обновления Qt Creator, делаем
$ git fetch

Отмечу, что fetch не делает merge автоматически. Если что-то меняли локально, надо делать pull.

3. Сборка Qt Creator.

Я собирал из-под Qt Creator 1.3-RC1 с Qt 4.6-RC1.

3.1. Под Ubuntu.

Под Ubuntu все оказалось просто. Ну, почти. :-) Запускаем Qt Creator 1.3-RC1, открываем проект /Projects/qt-creator/qtcreator.pro, выбираем Build->Set build configuration Release (или Debug), Build->Set run configuration qtcreator(bin).

Проект компилируется с восемью ворнингами, что совсем неплохо. Выходим из Qt Creator 1.3-RC1. Запускаем ./Projects/qt-creator/bin/qtcreator.bin и получаем сообщение о том что /plugins/Nokia/libQtScriptEditor.so: The shared library was not found.

Понятно, что что-то не так в зависимостях, и надо собрать плагины явно. Запускаем Qt Creator 1.3-RC1, открываем проект /Projects/qt-creator/src/libs/libs.pro, собираем. Не забываем про Build->Set build configuration Release (или Debug). Аналогично собираем /Projects/qt-creator/src/plugins/plugins.pro. Все! Закрываем Qt Creator 1.3-RC1 и запускаем только что собранный 1.3.80.

Я его толком не пользовал, но проекты в нем запускаются. Отладкой не занимался.

3.2. Под Windows.

Для Windows, если хотим использовать MSVC, надо скачать Qt, собранную под MSVC 2008. Ну, или пересобрать самим.

Запускаем Qt Creator 1.3-RC1, открываем проект d:/Projects/qt-creator/qtcreator.pro. Запускаем Build->Build Project. У меня сборка заняла 40 минут.

Запускаем 1.3.80, получаем вот такое сообщение:




В директорию qt-creator/bin надо скопировать вот эти Qt библиотеки:
QtCLucene4.dll
QtCore4.dll
QtDesigner4.dll
QtDesignerComponents4.dll
QtGui4.dll
QtHelp4.dll
QtNetwork4.dll
QtScript4.dll
QtSql4.dll
QtWebKit4.dll
QtXml4.dll
QtXmlPatterns4.dll

Копируем, запускаем по новой, получаем вот что:



Как с этим бороться я знаю- надо дебажить, смотреть, что же там такое вызывается. Но- в лом, да и времени нет. Скорее всего и так пофиксят не сегодня-завтра.

UPD: Все вылечилось добавлением Qt 4.6.0, собранной под MSVC, в переменную PATH, т.e.
PATH=...;d:\Qt\4.6.0\bin;

Qt Creator 1.3.80 теперь запускается без каких-либо жалоб, проекты открываются и т.п. Осталось неясным, что же было сломано в Qt 4.6-RC1. К тому же, вот здесь обсуждалась сходная проблема со сборкой Qt Creator 1.3.0, правда не написано, как она была решена.

Заключение.

Понятно, что попробовать самое свежее хочется всегда. Надо только соразмерять затраты времени с возможным результатом. В случае с 1.3.80 я и не знаю, стоит ли сейчас прям так уж упираться. Тем более, что есть пара невыясненных вопросов.

Весь Qt Creator- это несколько больше, чем то, что можно собрать из репов. К примеру, нету jom. Кто знает, чего еще не хватает для нормальной работы, и чего нет в исходниках. Я погуглил в поисках описалова как делать ежедневные билды (1.3.0 на текущий момент), но безрезультатно. А в них ведь есть абсолютно все, что и RC1.

Также, билд может быть сломан, не на одной платформе, так на другой. Так что, кто знает, сколько времени на сборку уйдет, это ж лотерея.

Я напишу в Qt Creator мэйллист узнать что это за функция такая в Core.dll или что я там при сборке накосячил.

UPD: Мне ответили, что в переменной PATH надо указать путь к библиотеке, но он там и так уже был:
PATH=...;d:\Qt\4.6.0-rc1\;

Также, посоветовали поиграться с утилитой с названием depends_x86 или похожим, которая проверяет зависимости.

Проблема решилась сама собой после установки Qt 4.6.0 и добавлением ее в PATH (см. UPD выше).

среда, 18 ноября 2009 г.

Mercurial Plugin

Я уже писал про плагин для Perforce. И про то, что из всех систем управления версиями (дальше СУВ), Perforce мне понравился больше всего. Это считая только те, c которыми я работал по крайней мере несколько месяцев. Я их перепробовал несколько, помимо Perforce,- DBPS, Subversion и ClearCase. Все, как видно, централизованные. Самая ужасная- это, наверное, DBPS. Хотя, ClearCase тоже особо не радует. Сейчас всей конторой переходим с ClearCase на SVN. Почему не на Git/Bazaar/Mercurial- не знаю, инженеры решения не принимают и начальство перед ними не отчитывается. :)

От нечего делать, решил таки образоваться в области распределенных СУВ. Нашел очень толковый пост о применимости git, Bazaar и Mercurial, особенно в домашних условиях. Погуглив и почитав обзоры и краткие руководства, быстро понял, что с теорией пора завязывать и надо попробовать что-то ручками. Решил начать с Mercurial (кстати, вот здесь неплохое описание базовых use case'ов), поставил ее пока только под Windows. Заработало все сразу, команды оказались простые- одно удовольствие. Порадовали hg serve и hg merge (с помощью KDiff3). Буду признателен, если кто поделится, каким инструментом для merge пользуется.

Стало интересно, а нельзя ли эту штуку прикрутить к Qt Creator. Он в плане плагинов для распределенных СУВ особо не блещет- "из коробки" поддерживается только Git. Гугление показало, что есть проект плагина для Mercurial за авторством Brian McGillion. Лежит здесь. Последний commit от 21 сентября этого года, так что проект, вроде бы, живой. Ну, плагины- это для меня самое интересное, что в Qt Creator есть, хочется в их устройстве разобраться. И код чужой посмотреть для этого полезно весьма.

Ну, пользуясь проторенной дорожкой, я стал собирать этот самый плагин под Qt Creator 1.2.92. Плагин оказался под 1.2.91, так что без жалоб компилятора не обошлось. Все вылечилось довольно просто, diff в конце этого поста.

Восле сборки и копирования длльки и манифеста в каталог плагинов, можно запускать Qt Creator. Плагин написан изначально под Ubuntu, так что в Options надо поменять команду "hg" на "hg.exe". Есть еще пара вещей, которые можно в коде поправить, а можно и забить. Брайану я написал, посмотрим, что он ответит. UPD: Брайан ответил, что поправит, и что проект был перенесен в основной репозиторий Qt Creator. Я проверил, текущая версия 1.3.80, так что скоро ждем.

Набор команд в плагине- это необходимый минимум: Annotate для текущего файла, Diff, Log, Revert и Status для текущего файла и проекта, Pull, Push, Update, Import, Incoming, Outgoing и, конечно, Commit. Все я пока не пробовал, но на первый взгляд- работает. Из того, чего не хватает- Merge с помощью внешнего инструмента и графа или таблицы коммитов. И еще тагов и веток. Так что есть простор для творчества.

В целом, вещь полезная. Если время будет (ага, разбежался), можно этот плагин превратить в плагин для Bazaar. Или подумать об общих классах для этих двух систем, чтобы не плодить копии.

Обещанный diff:

changeset: 1:b7b7b8d4d0c1
tag: tip
user: Qt Creator Geek
date: Tue Nov 17 23:30:46 2009 -0600
summary: Updated for Qt Creator 1.2.92.

diff --git a/Mercurial.pluginspec b/Mercurial.pluginspec
--- a/Mercurial.pluginspec
+++ b/Mercurial.pluginspec
@@ -1,4 +1,4 @@
-
+
Brian McGillion
(C) 2008-2009 Brian McGillion
@@ -19,9 +19,9 @@
Mercurial integration.
http://www.qtsoftware.com
-
-
-
-
+
+
+
+
diff --git a/mercurialcommitwidget.cpp b/mercurialcommitwidget.cpp
--- a/mercurialcommitwidget.cpp
+++ b/mercurialcommitwidget.cpp
@@ -84,7 +84,7 @@
MercurialCommitWidget::MercurialCommitWidget(QWidget *parent) :
- Core::Utils::SubmitEditorWidget(parent),
+ Utils::SubmitEditorWidget(parent),
mercurialCommitPanel(new QWidget)
{
mercurialCommitPanelUi.setupUi(mercurialCommitPanel);
diff --git a/mercurialcommitwidget.h b/mercurialcommitwidget.h
--- a/mercurialcommitwidget.h
+++ b/mercurialcommitwidget.h
@@ -12,7 +12,7 @@
Some extra fields have been added to the standard SubmitEditorWidget,
to help to conform to the commit style that is used by both git and Mercurial*/
-class MercurialCommitWidget : public Core::Utils::SubmitEditorWidget
+class MercurialCommitWidget : public Utils::SubmitEditorWidget
{
public:
diff --git a/mercurialjobrunner.cpp b/mercurialjobrunner.cpp
--- a/mercurialjobrunner.cpp
+++ b/mercurialjobrunner.cpp
@@ -11,6 +11,8 @@
#include
#include
+#include
+
using namespace Mercurial::Internal;
using namespace Mercurial;
@@ -155,18 +157,18 @@
}
if ((hgProcess.exitStatus() == QProcess::NormalExit) && (hgProcess.exitCode() == 0)) {
- QByteArray stdout = hgProcess.readAllStandardOutput();
+ QByteArray stdOut = hgProcess.readAllStandardOutput();
/*
* sometimes success means output is actually on error channel (stderr)
* e.g. "hg revert" outputs "no changes needed to 'file'" on stderr if file has not changed
* from revision specified
*/
- if (stdout == "")
- stdout = hgProcess.readAllStandardError();
- emit output(stdout);
+ if (stdOut == "")
+ stdOut = hgProcess.readAllStandardError();
+ emit output(stdOut);
} else {
- QByteArray stderr = hgProcess.readAllStandardError();
- emit error(stderr);
+ QByteArray stdErr = hgProcess.readAllStandardError();
+ emit error(stdErr);
}
hgProcess.close();
diff --git a/mercurialplugin.cpp b/mercurialplugin.cpp
--- a/mercurialplugin.cpp
+++ b/mercurialplugin.cpp
@@ -22,6 +22,9 @@
#include
+#include
+#include
+
#include
#include
@@ -216,33 +219,33 @@
{
Core::Command *command;
- annotateFile = new Core::Utils::ParameterAction(tr("Annotate Current File"), tr("Annotate \"%1\""), Core::Utils::ParameterAction::AlwaysEnabled, this);
+ annotateFile = new Utils::ParameterAction(tr("Annotate Current File"), tr("Annotate \"%1\""), Utils::ParameterAction::AlwaysEnabled, this);
command = actionManager->registerAction(annotateFile, Constants::ANNOTATE, context);
command->setAttribute(Core::Command::CA_UpdateText);
connect(annotateFile, SIGNAL(triggered()), this, SLOT(annotateCurrentFile()));
mercurialContainer->addAction(command);
- diffFile = new Core::Utils::ParameterAction(tr("Diff Current File"), tr("Diff \"%1\""), Core::Utils::ParameterAction::AlwaysEnabled, this);
+ diffFile = new Utils::ParameterAction(tr("Diff Current File"), tr("Diff \"%1\""), Utils::ParameterAction::AlwaysEnabled, this);
command = actionManager->registerAction(diffFile, Constants::DIFF, context);
command->setAttribute(Core::Command::CA_UpdateText);
command->setDefaultKeySequence(QKeySequence(tr(Constants::MENUKEY) + tr(Constants::MODIFIER) + "D"));
connect(diffFile, SIGNAL(triggered()), this, SLOT(diffCurrentFile()));
mercurialContainer->addAction(command);
- logFile = new Core::Utils::ParameterAction(tr("Log Current File"), tr("Log \"%1\""), Core::Utils::ParameterAction::AlwaysEnabled, this);
+ logFile = new Utils::ParameterAction(tr("Log Current File"), tr("Log \"%1\""), Utils::ParameterAction::AlwaysEnabled, this);
command = actionManager->registerAction(logFile, Constants::LOG, context);
command->setAttribute(Core::Command::CA_UpdateText);
command->setDefaultKeySequence(QKeySequence(tr(Constants::MENUKEY) + tr(Constants::MODIFIER) + "L"));
connect(logFile, SIGNAL(triggered()), this, SLOT(logCurrentFile()));
mercurialContainer->addAction(command);
- revertFile = new Core::Utils::ParameterAction(tr("Revert Current File"), tr("Revert \"%1\""), Core::Utils::ParameterAction::AlwaysEnabled, this);
+ revertFile = new Utils::ParameterAction(tr("Revert Current File"), tr("Revert \"%1\""), Utils::ParameterAction::AlwaysEnabled, this);
command = actionManager->registerAction(revertFile, Constants::REVERT, context);
command->setAttribute(Core::Command::CA_UpdateText);
connect(revertFile, SIGNAL(triggered()), this, SLOT(revertCurrentFile()));
mercurialContainer->addAction(command);
- statusFile = new Core::Utils::ParameterAction(tr("Status Current File"), tr("Status \"%1\""), Core::Utils::ParameterAction::AlwaysEnabled, this);
+ statusFile = new Utils::ParameterAction(tr("Status Current File"), tr("Status \"%1\""), Utils::ParameterAction::AlwaysEnabled, this);
command = actionManager->registerAction(statusFile, Constants::STATUS, context);
command->setAttribute(Core::Command::CA_UpdateText);
command->setDefaultKeySequence(QKeySequence(tr(Constants::MENUKEY) + tr(Constants::MODIFIER) + "S"));
diff --git a/mercurialplugin.h b/mercurialplugin.h
--- a/mercurialplugin.h
+++ b/mercurialplugin.h
@@ -20,11 +20,11 @@
class IVersionControl;
class IEditorFactory;
class IEditor;
+} // namespace Core
namespace Utils {
class ParameterAction;
} //namespace Utils
-} // namespace Core
namespace ProjectExplorer {
class ProjectExplorerPlugin;
@@ -137,12 +137,12 @@
QTemporaryFile *changeLog;
//Menu Items (file actions)
- Core::Utils::ParameterAction *annotateFile;
- Core::Utils::ParameterAction *diffFile;
- Core::Utils::ParameterAction *logFile;
- Core::Utils::ParameterAction *renameFile;
- Core::Utils::ParameterAction *revertFile;
- Core::Utils::ParameterAction *statusFile;
+ Utils::ParameterAction *annotateFile;
+ Utils::ParameterAction *diffFile;
+ Utils::ParameterAction *logFile;
+ Utils::ParameterAction *renameFile;
+ Utils::ParameterAction *revertFile;
+ Utils::ParameterAction *statusFile;
//submit editor actions
QAction *editorCommit;
diff --git a/optionspage.cpp b/optionspage.cpp
--- a/optionspage.cpp
+++ b/optionspage.cpp
@@ -12,7 +12,7 @@
QWidget(parent)
{
m_ui.setupUi(this);
- m_ui.commandChooser->setExpectedKind(Core::Utils::PathChooser::Command);
+ m_ui.commandChooser->setExpectedKind(Utils::PathChooser::Command);
m_ui.commandChooser->setPromptDialogTitle(tr("Mercurial Command"));
}
diff --git a/srcdestdialog.cpp b/srcdestdialog.cpp
--- a/srcdestdialog.cpp
+++ b/srcdestdialog.cpp
@@ -9,7 +9,7 @@
m_ui(new Ui::SrcDestDialog)
{
m_ui->setupUi(this);
- m_ui->localPathChooser->setExpectedKind(Core::Utils::PathChooser::Directory);
+ m_ui->localPathChooser->setExpectedKind(Utils::PathChooser::Directory);
}
SrcDestDialog::~SrcDestDialog()
@@ -17,7 +17,7 @@
delete m_ui;
}
-void SrcDestDialog::setPathChooserKind(Core::Utils::PathChooser::Kind kind)
+void SrcDestDialog::setPathChooserKind(Utils::PathChooser::Kind kind)
{
m_ui->localPathChooser->setExpectedKind(kind);
}
diff --git a/srcdestdialog.h b/srcdestdialog.h
--- a/srcdestdialog.h
+++ b/srcdestdialog.h
@@ -18,7 +18,7 @@
SrcDestDialog(QWidget *parent = 0);
~SrcDestDialog();
- void setPathChooserKind(Core::Utils::PathChooser::Kind kind);
+ void setPathChooserKind(Utils::PathChooser::Kind kind);
QString getRepositoryString();
protected: