📜 ⬆️ ⬇️

Mix and shake slightly: 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 in MacOS.

I am sure that the question of Qt and Cocoa communication was asked by many, but in the absence of intelligible information on the Internet, they hardly achieved significant success.


Introduction

By Qt 4.5, Trolltech began using 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 capabilities provided by this version are more than enough.
Despite the fact that progress is going forward, there are still those who do not want (or cannot) use the new versions of the library. Yes, and after 4.5 remain those who have installed the assembly without the support of Cocoa. 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 not a trivial matter.
The QT documentation has almost no mention of use in conjunction with Objective-C, but I know that these files have the extensions .m and .mm and are connected by the OBJECTIVE_SOURCES directive, but more on that later.

As a bridge, we will have a file written in Objective-C ++, which allows you to simultaneously use C, C ++ and Objective-C. An unpleasant feature is that the class, depending on the implementation, can receive either 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
  1. Copy Source | Copy HTML class menuWrapperPrivate ; // Forward declaration @ interface menuWrapperProxy : NSObject { @ private menuWrapperPrivate * wrap; } - ( void ) emitSayHelloRequested; //, Obj-C - ( menuWrapperPrivate *) getQtProxy; // QObject . Qt . @end
  2. Copy Source | Copy HTML class menuWrapperPrivate ; // Forward declaration @ interface menuWrapperProxy : NSObject { @ private menuWrapperPrivate * wrap; } - ( void ) emitSayHelloRequested; //, Obj-C - ( menuWrapperPrivate *) getQtProxy; // QObject . Qt . @end
  3. Copy Source | Copy HTML class menuWrapperPrivate ; // Forward declaration @ interface menuWrapperProxy : NSObject { @ private menuWrapperPrivate * wrap; } - ( void ) emitSayHelloRequested; //, Obj-C - ( menuWrapperPrivate *) getQtProxy; // QObject . Qt . @end
  4. Copy Source | Copy HTML class menuWrapperPrivate ; // Forward declaration @ interface menuWrapperProxy : NSObject { @ private menuWrapperPrivate * wrap; } - ( void ) emitSayHelloRequested; //, Obj-C - ( menuWrapperPrivate *) getQtProxy; // QObject . Qt . @end
  5. Copy Source | Copy HTML class menuWrapperPrivate ; // Forward declaration @ interface menuWrapperProxy : NSObject { @ private menuWrapperPrivate * wrap; } - ( void ) emitSayHelloRequested; //, Obj-C - ( menuWrapperPrivate *) getQtProxy; // QObject . Qt . @end
  6. Copy Source | Copy HTML class menuWrapperPrivate ; // Forward declaration @ interface menuWrapperProxy : NSObject { @ private menuWrapperPrivate * wrap; } - ( void ) emitSayHelloRequested; //, Obj-C - ( menuWrapperPrivate *) getQtProxy; // QObject . Qt . @end
  7. Copy Source | Copy HTML class menuWrapperPrivate ; // Forward declaration @ interface menuWrapperProxy : NSObject { @ private menuWrapperPrivate * wrap; } - ( void ) emitSayHelloRequested; //, Obj-C - ( menuWrapperPrivate *) getQtProxy; // QObject . Qt . @end
  8. 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 does not provide almost any functions. The following is a description of the 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. };


Also nothing special, as you can see. The only class task is to transfer a call from Objective-C ++ to Qt (C ++).

Copy Source | Copy HTML
  1. @implementation menuWrapperProxy
  2. - (id) init
  3. {
  4. if ((self = [super init])) {
  5. wrap = new menuWrapperPrivate (); // C ++ initialization
  6. wrap-> menu = [[TrayMenu alloc] init]; // Objective-C initialization
  7. [NSApp setDelegate: wrap-> menu];
  8. [wrap-> menu setParent: self];
  9. }
  10. return self;
  11. }
  12. // message transmitter
  13. - ( void ) emitSayHelloRequested
  14. {
  15. wrap-> privateWrapperSlot (); // C ++ call
  16. }
  17. - ( menuWrapperPrivate *) getQtProxy
  18. {
  19. return wrap;
  20. }
  21. @end


The main line of this part is the initialization of the menu. The TrayMenu class is written entirely in Objective-C (with this I wanted to show that you can connect many sources with almost no modification). The extension must be given .mm, so that the compiler correctly determines the type of file (in principle, and so will determine, but ideologically correct)

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


Intentionally omitting the extra details of the script, to save space.

Copy Source | Copy HTML
  1. @implementation TrayMenu
  2. // The source of the callback itself.
  3. - ( void ) onMyRequest: (id) sender {
  4. [_parent emitSayHelloRequested];
  5. }
  6. - ( void ) actionQuit: (id) sender {
  7. [NSApp terminate: sender];
  8. }
  9. - (NSMenu *) createMenu {
  10. NSZone * menuZone = [NSMenu menuZone];
  11. NSMenu * menu = [[NSMenu allocWithZone: menuZone] init];
  12. NSMenuItem * menuItem;
  13. menuItem = [menu addItemWithTitle: @ " say hello"
  14. action: @selector (onMyRequest :)
  15. keyEquivalent: @ "" ];
  16. [menuItem setTarget: self];
  17. [menu addItem: [NSMenuItem separatorItem]];
  18. menuItem = [menu addItemWithTitle: @ "Quit"
  19. action: @selector (actionQuit :)
  20. keyEquivalent: @ "" ];
  21. [menuItem setToolTip: @ "Click to Quit this App" ];
  22. [menuItem setTarget: self];
  23. return menu;
  24. }
  25. // Overloaded function, called after resource loading
  26. - ( void ) applicationDidFinishLaunching: (NSNotification *) notification {
  27. NSMenu * menu = [self createMenu];
  28. _statusItem = [[[NSStatusBar systemStatusBar] // Get to the statusbar
  29. statusItemWithLength: NSVariableStatusItemLength] retain];
  30. [_statusItem setMenu: menu];
  31. [_statusItem setTitle: @ "Menu" ];
  32. [_statusItem setHighlightMode: YES];
  33. [_statusItem setToolTip: @ "Test Tray" ];
  34. [menu release];
  35. }
  36. - ( void ) setParent: (menuWrapperProxy *) parent
  37. {
  38. _parent = parent;
  39. }
  40. @end

The button of the menu on pressing generates an event, which through the shell and the proxy class enters the Qt application. The scheme is unlikely to be different in more complex cases. A good tone would be to give the file the .m extension.

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 ()));


Just right? A little Obj-C ++ and classes in different languages ​​understand each other.



Now we need to force the compiler to collect all this horror.
The documentation states that an indication should be added to the .pro file (or rather read the following):

OBJECTIVE_SOURCES + = file1.m file2.mm

The problem is that initially qmake does not know how to handle Obective-C files, but only Objective-C ++.
Long-term smoking of smart paperwork, dancing with a tambourine and a spear method helped me to come to the decision - add instructions:

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

Not sure if it’s 100% true, but it does allow you to build code and write on Objective-C ++ in any project file, regardless of QMake directives.

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

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 incomprehensible libraries. It is only necessary to correctly arrange the header files, which will not require more than half an hour.

PS Here I have laid out the whole project, which will simply be compiled.

Source: https://habr.com/ru/post/64822/


All Articles