четверг, 11 ноября 2010 г.

QCocoaApplication

Чем мне нравиться Qt, так это возможностью создавать приложения с использованием родных платформозависимых компонентов. В приложении к Mac-у, это использование родных графических фреймворков Cocoa и Carbon. Впрочем любые другие фреймворки не проблема. В стандартной поставке Qt есть демо Demos/macmainwindow, в котором демонстрируется использование родных элементов управления Cocoa через QMacCocoaViewContainer и Carbon через HI API. На самом деле работа с Cocoa/Carbon это лишь частный случай смешения кода на Objective-C++ и Qt.

Благодаря тому, что Qt это обычный С++ и собирается он обычным GCC (любым компилятором на ваш вкус) мы, при небольшой сноровке и соблюдении нехитрых правил, можем использовать любой платформозависимый код на Objective-C и любые платформозависимые фреймворки. А значит можем сделать качественное приложение для MacOS X, интегрированное в систему по всем правилам приличия.

Правило первое. Весь Objective-C код должен быть в m (Objective-C) и mm (Objective-C++) файлах. GCC распознает язык содержимого именно по расширению файла, если ему встретится код на Objective-C в любом другом файле, то выдаст несколько тысяч ошибок. Не смертельно, но больно долго ждать, если пользуетесь QtCreator. Никакого Objective-C кода в заголовочных файлах!

Правило два. Следует из первого правила. Для удобства, всю работу с Objective-C сущностями надо заворачивать в С++ классы. Тогда проблем с заголовками не будет.

Правило три. Заворачивайте все включения платформозависимого кода в #ifdef Q_WS_MAC #endif. Поможет сэкономить кучу времени потом.

Правило четыре. Не забываем подключать нужные фреймворки в pro-файле.

LIBS += -framework Cocoa -framework Carbon

Правило пять. Если вы используете Cocoa, то для нормальной работы должны создать объект типа NSAutoreleasePool, который будет вести учет всех autorelease объектов. В справке Qt об этом написано. Зато не написано что пул должен существовать на протяжении всего времени использования объектов Cocoa. Пример из справки игнорирует это правило и использует пул лишь в конструкторе объектов, в результате чего гарантированы утечки памяти и куча эксепшенов в фоне. Для решения этой проблемы можно использовать наследника QApplication, который будет создавать пул в конструкторе и уничтожать в деструкторе.

qtcocoaapplication.h
#ifndef COCOAAPPLICATION_H
#define COCOAAPPLICATION_H

#import <QtCore>
#import <QtGui>

class QCocoaApplication : public QApplication
{
Q_OBJECT

public:
QCocoaApplication(int &argc, char **argv);
~QCocoaApplication();

};

#endif // COCOAAPPLICATION_H

qtcocoaapplication.mm
#import "qtcocoaapplication.h"

#import <QtCore>
#import <QtGui>

#import <Cocoa/Cocoa.h>

// just for safe
static QMutex mutex;
static int usedPool = 0;
static NSAutoreleasePool * pool = 0;

QCocoaApplication::QCocoaApplication(int &argc, char **argv) : QApplication(argc, argv)
{
mutex.lock();
if (!usedPool) pool = [[NSAutoreleasePool alloc] init];
usedPool++;
mutex.unlock();

};

QCocoaApplication::~QCocoaApplication()
{
mutex.lock();
--usedPool;
if (!usedPool) [pool release];
mutex.unlock();
};

а main.cpp сделать по такому шаблону
#include <QtGui/QApplication>

#ifdef Q_WS_MAC

#include "QtCocoa/qtcocoaapplication.h"
#include "macmainwindow.h"

int main(int argc, char **argv)
{
QCocoaApplication app(argc, argv);
// optional
//app.setQuitOnLastWindowClosed(false);

MacMainWindow mainWindow;
mainWindow.show();

return app.exec();
}

#else

int main(int argc, char **argv)
{
QApplication app(argc, argv);
QLabel label;
label.resize(300, 200);
label.setText(" This application is platform-specific and requires Mac OS X.");
label.show();
return app.exec();
}

#endif

Добавляем в главных окнах setUnifiedTitleAndToolBarOnMac(true) по-вкусу и пользуемся новыми возможностями.

Комментариев нет:

Отправить комментарий