Смешать и слегка взболтать: Qt, Carbon и Cocoa

    Qt можно считать наиболее распространенной кроссплатформенной библиотекой для разработки пользовательских интерфейсов. Carbon (С++) и Cocoa (Objective-C), в свою очередь, являются основными фреймворками, используемыми Apple, что означает гармоничный внешний вид и наилучшую интеграцию с системой при их использовании. Несмотря на попытки создания свободных клонов, в полном объеме они реализованы только в MacOS.

    Уверен, что вопросом связи Qt и Cocoa задавались многие, но в условиях отсутствия внятной информации в интернете вряд-ли достигли значимых успехов.


    Вступление

    К версии Qt 4.5, Trolltech начала использовать Cocoa API, что позволяет собирать приложения для 64-битных платформ. Стоит заметить, что Apple постепенно переводит свои приложения с 32 в 64-битные, так что мера весьма своевременна.
    В большинстве случаев, возможностей, предоставляемых этой версией, более чем достаточно.
    Несмотря на то, что прогресс идет вперед, все еще остаются те, кто не хотят (или не могут) пользоваться новыми версиями библиотеки. Да и после 4.5 остаются те, у кого установлена сборка без поддержки Cocoa. В таком случае единственным решением является использование Carbon в качестве связующего звена.

    Замечу, что несмотря на кажущуюся легкость, сборка такого моста является делом нетривиальным.
    В документации QT практически нет упоминаний про использование в связке с Objective-C, но я то знаю, что эти файлы имеют расширения .m и .mm и подключаются директивой OBJECTIVE_SOURCES, но об этом далее.

    В качестве моста у нас будет выступать файл, написанный на Objective-C++, который позволяет одновременно использовать C, C++ и Objective-C. Неприятной особенностью является то, что класс, в зависимости от реализации, может принимать либо сообщения от Objective-C, либо предоставлять слоты для QT.

    Схематично это выглядит так:



    Исходный код

    Copy Source | Copy HTML
    1. class menuWrapperPrivate; // Forward declaration
    2.  
    3. @interface menuWrapperProxy : NSObject {
    4. @private
    5.     menuWrapperPrivate * wrap;
    6. }
    7. - (void) emitSayHelloRequested; //используется, как прокси обратного вызова для Obj-C события
    8. - (menuWrapperPrivate *) getQtProxy; //предоставляет QObject для связывания. Вызывается из Qt класса.
    9. @end


    Так как наш класс — оболочка, практически никаких функций он не предоставлет. Далее идет описание menuWrapperPrivate:

    Copy Source | Copy HTML
    1. class menuWrapperPrivate : public QObject
    2. {
    3.     Q_OBJECT
    4.     public:
    5.         TrayMenu * menu;
    6.     signals:
    7.         void sayHello();
    8.     public slots:
    9.         void privateWrapperSlot() { emit sayHello();};
    10. };


    Тоже ничего особенного, как видите. Единственная задача класса — передать вызов из Objective-C++ в Qt (C++).

    Copy Source | Copy HTML
    1. @implementation menuWrapperProxy
    2. - (id)init
    3. {
    4.     if ((self = [super init])) {
    5.         wrap = new menuWrapperPrivate(); //С++ инициализация
    6.         wrap->menu = [[TrayMenu alloc] init]; //Objective-C инициализация
    7.         [NSApp setDelegate: wrap->menu];
    8.         [wrap->menu setParent: self];
    9.     }
    10.     return self;
    11. }
    12.  
    13. //передатчик сообщения
    14. - (void) emitSayHelloRequested
    15. {
    16.     wrap->privateWrapperSlot(); //С++ вызов
    17. }
    18.  
    19. - (menuWrapperPrivate *) getQtProxy
    20. {
    21.     return wrap;
    22. }
    23. @end


    Главной строкой этой части является инициализация меню. TrayMenu класс полностью написан на Objective-C (этим я хотел показать, что можно подключить многие исходники практически без модификации). Расширение нужно дать .mm, чтоб компилятор правильно определил тип файла (в принципе, и так определит, но идеологически правильнее)

    Copy Source | Copy HTML
    1. @interface TrayMenu : NSObject {
    2. @private
    3.         NSStatusItem *_statusItem;
    4.         menuWrapperProxy *_parent;
    5. }
    6. - (void) setParent: (menuWrapperProxy *)parent;
    7. @end


    Намеренно опускаю лишние детали скрипта, для экономии места.

    Copy Source | Copy HTML
    1. @implementation TrayMenu
    2. //Сам источник обратного вызова.
    3. - (void) onMyRequest:(id)sender {
    4.         [_parent emitSayHelloRequested];
    5. }
    6.  
    7. - (void) actionQuit:(id)sender {
    8.     [NSApp terminate:sender];
    9. }
    10.  
    11. - (NSMenu *) createMenu {
    12.     NSZone *menuZone = [NSMenu menuZone];
    13.     NSMenu *menu = [[NSMenu allocWithZone:menuZone] init];
    14.     NSMenuItem *menuItem;
    15.  
    16.     menuItem = [menu addItemWithTitle:@"Say hello"
    17.                                action:@selector(onMyRequest:)
    18.                         keyEquivalent:@""];
    19.     [menuItem setTarget:self];
    20.  
    21.     [menu addItem:[NSMenuItem separatorItem]];
    22.  
    23.     menuItem = [menu addItemWithTitle:@"Quit"
    24.                                action:@selector(actionQuit:)
    25.                         keyEquivalent:@""];
    26.     [menuItem setToolTip:@"Click to Quit this App"];
    27.     [menuItem setTarget:self];
    28.  
    29.     return menu;
    30. }
    31.  
    32. //Overloaded функция, вызывается после окончания загрузки ресурсов
    33. - (void) applicationDidFinishLaunching:(NSNotification *)notification {
    34.     NSMenu *menu = [self createMenu];
    35.  
    36.     _statusItem = [[[NSStatusBar systemStatusBar] //Доберемся до статусбара
    37.                     statusItemWithLength:NSVariableStatusItemLength] retain];
    38.     [_statusItem setMenu:menu];
    39.             [_statusItem setTitle: @"Menu"];
    40.     [_statusItem setHighlightMode:YES];
    41.     [_statusItem setToolTip:@"Test Tray"];
    42.  
    43.     [menu release];
    44. }
    45. - (void) setParent: (menuWrapperProxy *)parent
    46. {
    47.     _parent = parent;
    48. }
    49. @end
    50.  

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

    Copy Source | Copy HTML
    1. menuWrapperProxy * mwp = [[menuWrapperProxy alloc] init];
    2. menuWrapperPrivate * signalWrapper = [mwp getQtProxy];
    3. QMessageBox * box = new QMessageBox(0);
    4. connect(signalWrapper, SIGNAL(sayHello()), box, SLOT(exec()));


    Просто, правда? Немного Obj-C++ и классы на разных языках понимают друг друга.



    Теперь нужно заставить компилятор собирать весь этот ужас.
    В документации сказано, что в .pro файл нужно добавлять указание (а лучше прочитать изложенное ниже):

    OBJECTIVE_SOURCES += file1.m file2.mm

    Проблема состоит в том, что изначально qmake не умеет обращаться с Obective-C файлами, а только с Objective-C++.
    Длительное курение умных бумажек, пляски с бубном и метод тыка помогли мне прийти к решению — добавить указания:

    QMAKE_CXXFLAGS = -ObjC++
    QMAKE_CFLAGS = -ObjC++.

    Не уверен, что 100% верно, но позволяет собирать код и писать на Objective-C++ в любом файле проекта независимо от директив QMake.

    http://img406.imageshack.us/img406/9150/18285056.jpg

    Заключение

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

    P.S. Вот здесь выложил весь проект, который будет достаточно просто откомпилировать.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 24

      –10
      добавил бы в карму плюс, но пока не могу :)
      статья полезная, спасибо.
      ждем от тебя большего. (новых статей)
        0
        Спасибо, буду стараться по мере возможностей.
          +1
          ZloeSabo — это типа злая сандалия? кстати, сначала прочитал ник как ZloeSalo :)
            0
            Да, что-то в этом роде. Все мои ники, как оказалось при регистрации, уже используются. Пришлось срочно придумывать.
        +7
        На Carbon смотреть не надо — Apple его слила.
          0
          Да, но вот, например, Cocoa меню есть обёртка над Carbon меню. Интересно, в Snow Leopard переписали или нет?
            +1
            она там есть, потому как меню это Finder, а он, на Carbon. В Барсе всё переписали на Cocoa
          –1
          всё, надо срочно брать себя в руки и начинать изучать Objective-C. Я не программер, но что то уж больно интересно и завараживающе :))
            0
            Мне кажется, что лучше начинать с Automator и написанием actions для него на Applescript. Собственно этого должно быть достаточно для решения большинства повседневных задач.
              –1
              для решения повседневных задач хватает уровня Automator и без знания AS, на крайняк есть гугл при решение нестандартных задач. На самом деле с тех пор как перешёл на мак, понял как много хорошего софта под него просто не существует! хорошего или просто удобного (давно слез с винды, но любовь к некоторым удобным программкам осталась до сих пор и к сожалению аналогов по удобству так до сих пор найти и не удалось).

              Вот к примеру есть отличная прога CoverScout, но вот объясните мне — почему нет такого же удобного инструмента для работа с mp3/lossles и другими форматами без привязки к тунцу? ну ведь не сложно же читать теги не из тунца, а из файлов напрямую, но… и это далеко не единственный пример, могу из привести с десяток, которые постоянно крутятся в голове. Ну и в моменты очередного «безумия» меня посещает мысль — что быстрее — научится самому O-C или дождаться когда кто то это наконец то реализует (ну не может же быть, что такие идеи посещают меня одного). Конечно не совсем с нуля учиться, с php работаю уже пару лет, считаю что до неплохого уровня дорос, да IT технологиями интересуюсь сколько себя помню (правда в ключе сисадминства), так что не вижу каких-либо преград, в принципе.

              конечно как вариант — подкинуть свои идеи кому-нить из разработчиков, тут всё-таки плюсов больше — и люди этим профессионально занимаются, и у самого есть опыт работы менеджером проектов, но что то как то не складывается :)
                +1
                Скинете список того, чего не хватает, на досуге? Поищу, может наберется информации на статью.
                Как показывает практика, обычным гуглением часто бывает не обойтись — на искомое натыкаешься или случайно, или по чьиму-то совету.
                  +1
                  чего не хватает. ну про работу с обложками — CoverScout уже выше написал. так же не могу найти чего столь же удобного как mp3tag виндовый (работа сразу с группой файлов, переименование, мозможность сделать теги из имени файла и переименовать файл по тегам и т.д.). Работа с сабами — массовая конвертация из одного формата в другой, смена кодировок и т.д. и т.п.
                  Никак не могу ничего приличного найти из области работы с медиа, по этому для этих целей рядом сидит винда (ну не только для этих, ещё поиграть и ХД позырить).
                  не хватает приличного файл-менеджера, возможностей файнедар мне не хватает (самое главное не хватает многооконности), пробовал PathFinder — куча ошибок постоянно при копировании, ещё пару каких то менеджеров — не удобно, громоздко, а толку мало

                  ну и ещё одна мысль, правда она уже несколько более глобально, нежели программинг под Mac OS — клиент-серверная утилита для синхронизации (нормальной) Windows-Mac OS-Linux-iPhone-WindowsMobile-WebOS (с клиентами и серверами под них, ну кроме мобильных платформ). Ведь многие используют дома различные системы, mac+windows уже давно не новость, windows+linux так же сильно распространено, а человеческих средств для синхронизации контакторв/заметок/почты/файлов — нет. есть конечно сервисы типа mobileme, но поддерживают они далеко не все платформы, да и не всегда есть необходимость делать это через интернет. идею развивать можно долго, но тут фронт работ уже достаточно огромен, да и сама схема работы тоже сложна, по этому сижу тихо и мечтаю :)

                  чего ещё очень сильно не хватает, так это адекватной утилиты для тестирования скорости работы HDD!!! то что есть — не обновлялось уже пару лет, да и найти _очень_ проблематично
                    –1
                    >Работа с сабами — массовая конвертация из одного формата в другой, смена кодировок<
                    По-моему это как раз та задача, которую можно решить через написание Automator Action

                    Про программы вы в целом верно подметили, однако стоит помнить, что Mac сообщество на фоне Win выглядит довольно скромно, и разработчиков, как мне кажется, сильно меньше.

                    >чего ещё очень сильно не хватает, так это адекватной утилиты для тестирования скорости работы HDD!<
                    Возможно стоит поискать утилиты из мира nix? Согласен, не mac way, но ведь никто не мешает написать тот же automator action с выводом всей информации в окошко.

                    Дерзайте! :)

                      0
                      да, это действительно не mac way. Если подходить таким образом, можно и под винду софт не писать — командная строка тоже много что умеет, но так же никто не поступает? не думаю что я единственный кому не хватает подобных программ, а значит это должно быть нечто большее нежели AS, или собирать что то из мира nix систем. В моём понимании — если программа востребована больше чем паре людей — это должен быть законченный продукт, а не поделка на коленке.
                        0
                        Вы правы в том, что использование командной строки ломает некоторый повседневный user experience mac пользователя. Я всего лишь указал, что такие вещи как конвертация и смена кодировок могут быть без особого труда реализованы в качестве action'а для Automator( и не обязательно на AS, можете использовать как Cocoa так и Shell script).

                        Однако, также необходимо принять(и даже усвоить), что решение задачи далеко не всегда состоит в написании полноценной программы. Путь мака — путь интеграции. Нужно всегда проверять, не будет ли написание расширения для существующего приложения более гибким решением, чем написание полноценного приложения.
                          0
                          ну я же нигде не говорю что не нужно вообще использовать AS. Да, какие то задачи можно решить и при помощи него, а какие то и при помощи консоли, но разговор как бы зашёл о разработке ПО в принципе, а не в конкретных оболочках/на конкретных языках.

                          По поводу концепции в принципе даже спорить не стану, я не разработчик и на данный момент, по крайней мере, очень далёк от этого, по этому готов принять на веру ваши слова, тем более что оно вполне логично и применимо к любой ОС, а не только маку

                          Но на самом деле я считаю, что если подходить к решению задачи достаточно серьёздно и ставить целью создание полноценного приложения — разработка полноценного приложения будет более удобна и функциональна. Если же стоит задачей решение 1-2 задач, то можно обойтись и более «лёгкими» средствами. В контексте перечисленного мной выше думаю что вполне можно продумать функционал, достаточный для полноценного, самодостаточного приложения. Даже первые 2 пункта можно объединить в один продукт (ну по сути эти приложения созданы для одной цели, по этому будет в принципе логично).
                          0
                          Кстати, по поводу mac way: сдается мне, что использование тунца для хранения и обработки музыки как раз таковым и является. Скорее всего ситуация окажется примерно такой: более менее приличные проги используют тунец, т.к. Apple предоставляет удобные средства для разработчиков, избавляющие от необходимости изобретать велосипед; большАя часть программ, не использующих api тунца, портированы с nix, ориентированы на командную строку, а если и имеют графический интерфейс, то он отличается падучестью т.к. сделан на коленке за вечер. (т.к., имхо, большая часть мак разработчиков привыкла работать только за деньги и всякие опенсорсные задумки проходят мимо) Что возвратит опять предыдущим постам — айтюнс не приемлим, а консоль — не mac way.
                            0
                            позволю не согласится
                            сколько под мак программ для работы с тегами? бесплатных, написанных именно под мак? десяток, не меньше. а аналогов CoverScout я не встречал в принципе — просто никто не делал (разработчик CoverScout делает софтину для работы с тегами, которая тоже завязана на тунце, и это единственная программа для работы с тегами, которая привязана к тунцу), так что думаю что дело здесь совсем не в этом. т.к. поток свитчеров в последнее время растёт практически в геометрической прогрессии, а работать с тунцом хотят далеко не все (сторонние плеера сейчас пользуются большой популярностью — cog, songbird, etc.), подобный софт становится всё нужнее и популярнее. Да и я не скажу что хорошего, бесплатного софта под мак нет, или его мало, на самом деле его не много меньше чем под винду (ну если сравнивать в процентном соотношении).

                            а по поводу того что разработчики хотят работать за деньги… да я думаю что любой разработчик хочет работать и за свою работу получать деньги, тут как бы без привязки к платформе или языку :)
                        0
                        Не знаю, что такое mp3tag, но по описанию похоже на EasyTag. Свободная лицензия, написано на GTK+, есть под макось.
                          0
                          кстати раз уж пошла разговор про теги — никто не знает программы которая бы устанавливала правильные теги по куску мп3? я имею ввиду с использованием какого-нибудь сервиса?
              0
              «котороый»
                0
                поправил

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

              Самое читаемое