Перенос приложений с Fremantle на Harmattan

В этой статье рассматривается ряд наиболее важных шагов по переносу существующего Fremantle-приложения в Harmattan. Как и всегда, отзывы, комментарии и исправления приветствуются!

Установка нового SDK


SDK можно скачать по адресу http://qt.nokia.com/downloads.
Первое, что необходимо проверить, будет ли портируемое приложение компилироваться новым SDK.
 

Сборка приложения в Scratchbox и запуск его на устройстве или в QEMU


На следующем шаге необходимо проверить, компилируется ли приложение в Harmattan ARMEL Scratchbox. Если приложение компилируется, то можно протестировать его работу на мобильном устройстве (N950) или на эмуляторе (QEMU). Заметим, что в этом случае приложение будет выглядеть как Qt-приложение для персонального компьютера, поскольку эмулятор не содержит Harmattan-специфичных компонентов компонентов Qt. Данная возможность появится в финальной версии сборочного окружения Harmattan.
 

Иконки


Размер иконок в Harmattan составляет 80х80 в списке доступных приложений и 64х64 в инструменте установки и удаления программ.

Обратите внимание, что в экспериментальной версии сборочного окружения Harmattan размеры иконок устанавливаются такими же, какими они были в Maemo 5. Это будет исправлено в финальной версии SDK для Harmattan. До этого момента, чтобы получить иконки правильного размера, необходимо вручную изменить base64 encoded секцию в файле debian_harmattan/control.
 

Файлы .desktop


Измените строку Exec в .desktop-файле так, как показано ниже:
Exec=invoker --single-instance --type=d /opt/myapp/bin/myapp
Отдельное замечание для экспериментального сборочного окружения: если файл .desktop был изменен как указано выше, но это не произвело никакого эффекта, то можно попробовать вставить пробел между $(MAKE) и INSTALL_ROOT в файле debian/rules, как это описано здесь.

Обратите внимание, что файлы .desktop в Harmattan расположены в /usr/share/applications, а не в /usr/share/applications/hildon.

Флаги Harmattan


QMake определяет следующие флаги в pro-файлах:
MEEGO_VERSION_MAJOR     = 1
MEEGO_VERSION_MINOR     = 2
MEEGO_VERSION_PATCH     = 0
MEEGO_EDITION           = harmattan
Эти флаги можно использовать так:
contains(MEEGO_VERSION_MAJOR,1): ...
contains(MEEGO_EDITION,harmattan): ...
В C++ данные флаги определяются директивами #define в файле qplatformdefs.h (MEEGO_VERSION_MAJOR, MEEGO_VERSION_MINOR, MEEGO_VERSION_PATCH и MEEGO_EDITION_HARMATTAN).
 
Необходимо отметить, что данные флаги будут доступны только в финальной верси сборочного окружения Harmattan. Если вы используете экспериментальный релиз SDK, то вам придётся определять эти флаги вручную:
exists($$QMAKE_INCDIR_QT"/../qmsystem2/qmkeys.h"):!contains(MEEGO_EDITION,harmattan): {
  MEEGO_VERSION_MAJOR     = 1
  MEEGO_VERSION_MINOR     = 2
  MEEGO_VERSION_PATCH     = 0
  MEEGO_EDITION           = harmattan
  DEFINES += MEEGO_EDITION_HARMATTAN
}
Теперь можно заменить все директивы условной компиляции
#ifdef Q_WS_MAEMO_5
на
#if defined(Q_WS_MAEMO_5) || defined(MEEGO_EDITION_HARMATTAN)


Переписывание пользовательского интерфейса на QML


После запуска приложения вы обнаружите, что оно выглядит "некрасиво". Чтобы исправить это, необходимо переписать пользовательский интерфейс на QML (на данный момент это единственный способ).

Начало работы с QML

Чтобы получить представление о QML, можно создать проект QML через SDK:
File -> New Project -> Qt Quick Project -> Harmattan Application
Это позволит начать работу с QML-проектом и понять, как он устроен. Более подробную информацию о QML и о его компонентах можно посмотреть здесь:
Прежде всего, автор просто скопировал сгенерированные QML-файлы, настроил проектный файл, чтобы включить в него новые файлы и изменил метод main существующего приложения, чтобы запустить демо-приложение QML.

Обратите внимание, что по умолчанию, QML-файлы включаются в Harmattan-приложение в качестве ресурсов. Это может быть удобно, чтобы быстро начать работу. Однако, для упрощения и ускорения процесса разработки приложения может быть полезно разместить эти файлы в файловой системе.

Портирование QGV-компонентов на QML

Существует много приложений, которые написаны с использованием Qt Graphics View framework (также известного как QGV). Поскольку QML также основан на этом фреймворке, то можно легко добавлять в QML-приложение графические элементы, ранее разработанные с помощью QGV. Для этого необходимо проделать следующие шаги:
  • Изменить субклассы QGraphicsItem и QGraphicsScene так, чтобы они стали наследниками QDeclarativeView
  • В субклассах QGraphicsItem
    • переопределить функцию paint() и разместить в ней код отображения элемента с помощью QPainter
    • добавить следующий вызов:
      setFlag(QGraphicsItem::ItemHasNoContents, false);
  • Использовать макрос Q_PROPERTY для всех объектов, к которым вы хотите получить доступ из QML
  • В main.cpp до загрузки QML-файлов зарегистрировать новый тип:
    qmlRegisterType<MemoryGameBoard>("MemoryGame", 1, 0, "MemoryGameBoard");
  • Для использования построенного элемента в QML необходимо импортировать модуль (имя модуля — это первый параметр в вызове qmlRegisterType выше).
См. также:

Перенос базовых частей пользовательского интерфейса в QML

Как только QML заработает, в скопированные файлы можно будет внести изменения, чтобы придать приложению желаемый внешний вид. В следующих ссылках приведено несколько примеров того, что может быть использовано в графическом интерфейсе на QML.

Подключение пользовательских виджетов в QML

Если приложение использует пользовательские виджеты, то они могут быть легко адаптированы для QML. Чтобы заставить существующие виджеты работать в QML-приложении, используются прокси.
 
Следующий код показывает, как быстро организовать такой прокси для пользовательского виджета AnalogMeterWidget:
class AnalogMeter : public VolumeMeter
{
   Q_OBJECT
public:
   AnalogMeter(QGraphicsItem* parent = 0)
       : QGraphicsProxyWidget(parent)
   {
       widget = new AnalogMeterWidget(0);
       setWidget(widget);
   }
private:
   AnalogMeterWidget *widget;
};
Прокси лучше всего зарегистрировать непосредственно в методе main(), например, так:
qmlRegisterType<AnalogMeter>("vumeter", 1, 0, "AnalogMeter");
В QML-файлах необходимо написать
import vumeter 1.0,
чтобы подключить свой код, и
AnalogMeter {
  id: vuMeter
}
для создания экземпляра своего класса.

Заметим, что при использовании описанного выше способа, QML-редактор будет выдавать сообщение об ошибке (неизвестные типы) при использовании операций импорта определённых модулей и виджетов, но это не будет влиять на функциональность приложения. Более подробно совместное использование C/C++ и QML изложено в следующем разделе.

Адаптеры, прокси, оболочки для существующего кода C/C++

Как описывалось выше, qmlRegisterType позволяет использовать собственные классы Qt в QML. Это позволяет с лёгкостью использовать существующий программный код, написанный на Qt, в QML. Вот несколько полезных ссылок:

Удаление устаревшего кода

Некоторые из существующих частей Fremantle-проекта не нужны в Harmattan (например, формы). Эти ненужные элементы не нужно удалять явно — их можно исключить из сборки в Harmattan путем изменения файла проекта, например, таким образом:
exists($$QMAKE_INCDIR_QT"/../qmsystem2/qmkeys.h"){
   DEFINES += Q_WS_MAEMO_6
   QT+= declarative
# As we use QML anyways we do not need any forms.
   FORMS =
}
Данный код определяет Q_WS_MAEMO_6, добавляет библиотеку declarative, необходимую для QML, и удаляет все формы при сборке под Harmattan. Используя данный подход, можно с лёгкостью собирать проект на всех поддерживаемых платформах (и на Fremantle, и на Harmattan).

Хранение QML в файловой системе, а не в ресурсах

Замечательная возможность QML заключается в том, что интерпретация кода осуществляется во время выполнения программы, поэтому изменение кода QML не требует компиляции. Эту возможность можно использовать для ускорения разработки приложения, размещая QML-файлы просто в файловой системе, где их удобно редактировать. В файле проекта это выглядит, например, так:
OTHER_FILES += qml/main.qml \
               qml/MainPage.qml
INSTALLS += target ... qml
target.path = /usr/bin
...
qml.path = /usr/share/vumeter/qml
qml.files += qml/main.qml \
       qml/MainPage.qml
В методе main можно сделать такой вызов:
view.setSource(QUrl("/usr/share/vumeter/qml/main.qml"));
вместо
view.setSource(QUrl("qrc:/qml/main.qml"));
чтобы загрузить QML-файл.

Создание пакетов для Harmattan

Создание пакетов для Harmattan аналогично созданию пакетов в Freemantle. Могут быть полезны следующие ссылки:
 

Оригинальный текст статьи: Porting Fremantle Applications to Harmattan
Перевод: Владимир Шабаршин
Редактирование: Надежда Лагутина, Илья Парамонов

Если вы заметили неточности в приведённом тексте статьи или у вас есть дополнения, пожалуйста, приводите их в комментариях к статье.