OS X上的QT,如何检测单击应用程序Dock图标

Jas*_*enX 6 qt

我有一个开放的Qt Mac应用程序.我点击了应用程序图标

有没有办法在应用程序中获取此通知?

exs*_*ape 9

由于弃用警告(OS X 10.5之后)和类型错误,我无法得到正确编译的原始答案; 我更改了一些类型名称并将其编译,但代码仍然无效.

事实证明,较新版本的Qt(4.8.7+,包括5.x;我使用5.4.1)实现了我们想要添加class_addMethod的方法,如果方法已经存在则会失败.看到这个QTBUG.
注意:上面的错误报告包含一个稍微不同的解决方案(我自己解决了问题后发现了它).

一个对我有用的解决方案是检查方法是否存在.如果是,我们将其替换.如果没有,我们只需添加它.
我没有在较旧的Qt版本上测试此代码,但它使用OP逻辑,所以它应该工作.

这是我的代码.与在OP中的情况一样,所有代码都在QApplication子类的.cpp文件中.

#ifdef Q_OS_MAC
#include <objc/objc.h>
#include <objc/message.h>
void setupDockClickHandler();
bool dockClickHandler(id self,SEL _cmd,...);
#endif
Run Code Online (Sandbox Code Playgroud)

我的QApplication子类构造函数包含

#ifdef Q_OS_MAC
    setupDockClickHandler();
#endif
Run Code Online (Sandbox Code Playgroud)

最后,在同一个文件的某个地方(在我的情况下,在底部):

#ifdef Q_OS_MAC
void setupDockClickHandler() {
    Class cls = objc_getClass("NSApplication");
    objc_object *appInst = objc_msgSend((objc_object*)cls, sel_registerName("sharedApplication"));

    if(appInst != NULL) {
        objc_object* delegate = objc_msgSend(appInst, sel_registerName("delegate"));
        Class delClass = (Class)objc_msgSend(delegate,  sel_registerName("class"));
        SEL shouldHandle = sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:");
        if (class_getInstanceMethod(delClass, shouldHandle)) {
            if (class_replaceMethod(delClass, shouldHandle, (IMP)dockClickHandler, "B@:"))
                qDebug() << "Registered dock click handler (replaced original method)";
            else
                qWarning() << "Failed to replace method for dock click handler";
        }
        else {
            if (class_addMethod(delClass, shouldHandle, (IMP)dockClickHandler,"B@:"))
                qDebug() << "Registered dock click handler";
            else
                qWarning() << "Failed to register dock click handler";
        }
    }
}

bool dockClickHandler(id self,SEL _cmd,...) {
    Q_UNUSED(self)
    Q_UNUSED(_cmd)
    // Do something fun here!
    qDebug() << "Dock icon clicked!";

    // Return NO (false) to suppress the default OS X actions
    return false;
}
#endif
Run Code Online (Sandbox Code Playgroud)

另请参阅有关applicationShouldHandleReopen:hasVisibleWindows:方法Apple文档.

为了编译它,您还需要链接一些额外的框架.
使用qmake,我将以下内容添加到我的.pro文件中:

LIBS += -framework CoreFoundation -framework Carbon -lobjc
Run Code Online (Sandbox Code Playgroud)

如果您手动编译,那些标志当然正是您应该添加到c ++或clang ++命令行的标志.
这应该是所需要的一切.

  • 这是一个很好的答案,但顺便说一句,我认为不必避免使用目标C。qmake可以处理它,并且当包含在.mm文件中时,Qt头是有效的。这可以简单地用obj-C ++编写在`.mm`文件中,并且在没有显式调用objc运行时的情况下更具可读性。Mac上的Qt项目可以包含.m和.mm文件,您可以使用.mm文件中的Qt。 (2认同)

Jas*_*enX 5

这很疯狂,但我得到了它,没有任何Objective-C编码:

我派生了QApplication.在我的派生类的*.cpp部分,我把:

#ifdef Q_OS_MAC

#include <objc/objc.h>
#include <objc/message.h>

bool dockClickHandler(id self,SEL _cmd,...)
{
    Q_UNUSED(self)
    Q_UNUSED(_cmd)
   ((MyApplictionClass*)qApp)->onClickOnDock();
     return true;
}

#endif
Run Code Online (Sandbox Code Playgroud)

在我的派生应用程序类构造函数中我把:

#ifdef Q_OS_MAC

    objc_object* cls = objc_getClass("NSApplication");
    SEL sharedApplication = sel_registerName("sharedApplication");
    objc_object* appInst = objc_msgSend(cls,sharedApplication);

    if(appInst != NULL)
    {
        objc_object* delegate = objc_msgSend(appInst, sel_registerName("delegate"));
        objc_object* delClass = objc_msgSend(delegate,  sel_registerName("class"));
        const char* tst = class_getName(delClass->isa);
        bool test = class_addMethod((objc_class*)delClass, sel_registerName("applicationShouldHandleReopen:hasVisibleWindows:"), (IMP)dockClickHandler,"B@:");

        if (!test)
        {
            // failed to register handler...
        }
    }

#endif
Run Code Online (Sandbox Code Playgroud)

将这个简单的方法添加到我的应用程序类中(注意它是从我的答案顶部的处理程序中引用的)

void MyApplictionClass::onClickOnDock()
{
  // do something... 
}
Run Code Online (Sandbox Code Playgroud)

像魅力一样工作.