Mix and shake lightly: Qt, Carbon and Cocoa
Qt can be considered the most common cross-platform library for developing user interfaces. Carbon (C ++) and Cocoa (Objective-C), in turn, are the main frameworks used by Apple, which means a harmonious appearance and the best integration with the system when using them. Despite attempts to create free clones, they are fully implemented only on MacOS.
I am sure that many people asked the question of Qt and Cocoa connection, but in the absence of clear information on the Internet, they are unlikely to have achieved significant success.
By Qt 4.5, Trolltech started using the Cocoa API, which allows building applications for 64-bit platforms. It is worth noting that Apple is gradually converting its applications from 32 to 64-bit, so the measure is very timely.
In most cases, the possibilities provided by this version are more than enough.
Despite the fact that progress is moving forward, there still remain those who do not want (or cannot) use the new versions of the library. And after 4.5 there are those who have installed the assembly without Cocoa support. In this case, the only solution is to use Carbon as a link.
I note that despite the apparent ease, the assembly of such a bridge is non-trivial.
In the QT documentation, there is practically no mention of use in conjunction with Objective-C, but I know that these files have the extensions .m and .mm and are connected with the OBJECTIVE_SOURCES directive, but more on that later.
As a bridge, we will have a file written in Objective-C ++, which allows us to use C, C ++ and Objective-C at the same time. An unpleasant feature is that the class, depending on the implementation, can either receive messages from Objective-C or provide slots for QT.
Schematically, it looks like this:
Since our class is a shell, it will not provide almost any functions. The following is a description of menuWrapperPrivate:
Also nothing special, as you see. The only task of the class is to transfer the call from Objective-C ++ to Qt (C ++).
The main line of this part is the initialization of the menu. The TrayMenu class is completely written in Objective-C (by this I wanted to show that you can connect many source codes with almost no modification). The extension must be given .mm, so that the compiler correctly determines the type of file (in principle, it will determine, but ideologically more correctly)
I deliberately omit the extra details of the script to save space.
By pressing the menu button, an event is generated that, through the shell and proxy class, enters the Qt application. The scheme is unlikely to differ in more complex cases. It will give a good tone to the file extension .m
Just right? Few Obj-C ++ and classes in different languages understand each other.
Now you need to get the compiler to collect all this horror.
The documentation says that you need to add an indication to the .pro file (or rather read the following):
OBJECTIVE_SOURCES + = file1.m file2.mm
The problem is that initially qmake is not able to handle Obective-C files, but only with Objective -C ++.
Long-term smoking of smart papers, dancing with a tambourine and the poking method helped me to come to a decision - add the instructions:
QMAKE_CXXFLAGS = -ObjC ++
QMAKE_CFLAGS = -ObjC ++.
I'm not sure that it is 100% true, but it allows you to compile code and write in Objective-C ++ in any project file, regardless of the QMake directives.
I hope the article will be useful to you in your projects. Unlike other examples on the Internet, the code is working, it contains an honest Objective-C class and does not require obscure libraries. You just need to correctly place the header files, which does not require more than half an hour.
PS Here I laid out the whole project, which will be quite simple to compile.
I am sure that many people asked the question of Qt and Cocoa connection, but in the absence of clear information on the Internet, they are unlikely to have achieved significant success.
Introduction
By Qt 4.5, Trolltech started using the Cocoa API, which allows building applications for 64-bit platforms. It is worth noting that Apple is gradually converting its applications from 32 to 64-bit, so the measure is very timely.
In most cases, the possibilities provided by this version are more than enough.
Despite the fact that progress is moving forward, there still remain those who do not want (or cannot) use the new versions of the library. And after 4.5 there are those who have installed the assembly without Cocoa support. In this case, the only solution is to use Carbon as a link.
I note that despite the apparent ease, the assembly of such a bridge is non-trivial.
In the QT documentation, there is practically no mention of use in conjunction with Objective-C, but I know that these files have the extensions .m and .mm and are connected with the OBJECTIVE_SOURCES directive, but more on that later.
As a bridge, we will have a file written in Objective-C ++, which allows us to use C, C ++ and Objective-C at the same time. An unpleasant feature is that the class, depending on the implementation, can either receive messages from Objective-C or provide slots for QT.
Schematically, it looks like this:
Source
Copy Source | Copy HTML- class menuWrapperPrivate; // Forward declaration
-
- @interface menuWrapperProxy : NSObject {
- @private
- menuWrapperPrivate * wrap;
- }
- - (void) emitSayHelloRequested; //используется, как прокси обратного вызова для Obj-C события
- - (menuWrapperPrivate *) getQtProxy; //предоставляет QObject для связывания. Вызывается из Qt класса.
- @end
Since our class is a shell, it will not provide almost any functions. The following is a description of menuWrapperPrivate:
Copy Source | Copy HTML- class menuWrapperPrivate : public QObject
- {
- Q_OBJECT
- public:
- TrayMenu * menu;
- signals:
- void sayHello();
- public slots:
- void privateWrapperSlot() { emit sayHello();};
- };
Also nothing special, as you see. The only task of the class is to transfer the call from Objective-C ++ to Qt (C ++).
Copy Source | Copy HTML- @implementation menuWrapperProxy
- - (id)init
- {
- if ((self = [super init])) {
- wrap = new menuWrapperPrivate(); //С++ инициализация
- wrap->menu = [[TrayMenu alloc] init]; //Objective-C инициализация
- [NSApp setDelegate: wrap->menu];
- [wrap->menu setParent: self];
- }
- return self;
- }
-
- //передатчик сообщения
- - (void) emitSayHelloRequested
- {
- wrap->privateWrapperSlot(); //С++ вызов
- }
-
- - (menuWrapperPrivate *) getQtProxy
- {
- return wrap;
- }
- @end
The main line of this part is the initialization of the menu. The TrayMenu class is completely written in Objective-C (by this I wanted to show that you can connect many source codes with almost no modification). The extension must be given .mm, so that the compiler correctly determines the type of file (in principle, it will determine, but ideologically more correctly)
Copy Source | Copy HTML- @interface TrayMenu : NSObject {
- @private
- NSStatusItem *_statusItem;
- menuWrapperProxy *_parent;
- }
- - (void) setParent: (menuWrapperProxy *)parent;
- @end
I deliberately omit the extra details of the script to save space.
Copy Source | Copy HTML- @implementation TrayMenu
- //Сам источник обратного вызова.
- - (void) onMyRequest:(id)sender {
- [_parent emitSayHelloRequested];
- }
-
- - (void) actionQuit:(id)sender {
- [NSApp terminate:sender];
- }
-
- - (NSMenu *) createMenu {
- NSZone *menuZone = [NSMenu menuZone];
- NSMenu *menu = [[NSMenu allocWithZone:menuZone] init];
- NSMenuItem *menuItem;
-
- menuItem = [menu addItemWithTitle:@"Say hello"
- action:@selector(onMyRequest:)
- keyEquivalent:@""];
- [menuItem setTarget:self];
-
- [menu addItem:[NSMenuItem separatorItem]];
-
- menuItem = [menu addItemWithTitle:@"Quit"
- action:@selector(actionQuit:)
- keyEquivalent:@""];
- [menuItem setToolTip:@"Click to Quit this App"];
- [menuItem setTarget:self];
-
- return menu;
- }
-
- //Overloaded функция, вызывается после окончания загрузки ресурсов
- - (void) applicationDidFinishLaunching:(NSNotification *)notification {
- NSMenu *menu = [self createMenu];
-
- _statusItem = [[[NSStatusBar systemStatusBar] //Доберемся до статусбара
- statusItemWithLength:NSVariableStatusItemLength] retain];
- [_statusItem setMenu:menu];
- [_statusItem setTitle: @"Menu"];
- [_statusItem setHighlightMode:YES];
- [_statusItem setToolTip:@"Test Tray"];
-
- [menu release];
- }
- - (void) setParent: (menuWrapperProxy *)parent
- {
- _parent = parent;
- }
- @end
-
By pressing the menu button, an event is generated that, through the shell and proxy class, enters the Qt application. The scheme is unlikely to differ in more complex cases. It will give a good tone to the file extension .m
Copy Source | Copy HTML- menuWrapperProxy * mwp = [[menuWrapperProxy alloc] init];
- menuWrapperPrivate * signalWrapper = [mwp getQtProxy];
- QMessageBox * box = new QMessageBox(0);
- connect(signalWrapper, SIGNAL(sayHello()), box, SLOT(exec()));
Just right? Few Obj-C ++ and classes in different languages understand each other.
Now you need to get the compiler to collect all this horror.
The documentation says that you need to add an indication to the .pro file (or rather read the following):
OBJECTIVE_SOURCES + = file1.m file2.mm
The problem is that initially qmake is not able to handle Obective-C files, but only with Objective -C ++.
Long-term smoking of smart papers, dancing with a tambourine and the poking method helped me to come to a decision - add the instructions:
QMAKE_CXXFLAGS = -ObjC ++
QMAKE_CFLAGS = -ObjC ++.
I'm not sure that it is 100% true, but it allows you to compile code and write in Objective-C ++ in any project file, regardless of the QMake directives.
Conclusion
I hope the article will be useful to you in your projects. Unlike other examples on the Internet, the code is working, it contains an honest Objective-C class and does not require obscure libraries. You just need to correctly place the header files, which does not require more than half an hour.
PS Here I laid out the whole project, which will be quite simple to compile.