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

时间:2013-02-28 18:51:18

标签: qt

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

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

4 个答案:

答案 0 :(得分:9)

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

事实证明,较新版本的Qt(4.8.7+,包括5.x;我使用5.4.1)实现了我们想要添加的方法,如果方法已经存在,class_addMethod将失败。见this 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

我的QApplication子类构造函数包含

#ifdef Q_OS_MAC
    setupDockClickHandler();
#endif

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

#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

另见Apple documentation on the applicationShouldHandleReopen:hasVisibleWindows: method

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

LIBS += -framework CoreFoundation -framework Carbon -lobjc

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

答案 1 :(得分: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

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

#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

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

void MyApplictionClass::onClickOnDock()
{
  // do something... 
}

像魅力一样工作。

答案 2 :(得分:0)

从Qt5.4.0开始,您可以处理与点击停靠相关的QEvent:QEvent :: ApplicationActivate。

https://bugreports.qt.io/browse/QTBUG-10899

https://doc.qt.io/qt-5/qevent.html

答案 3 :(得分:0)

QEvent :: ApplicationActivate的问题在于它会在每次激活时发出 - 例如,即使你切换到Application Switcher上的应用程序。本机行为是仅在Dock图标单击时显示应用程序,而不是在通过cmd + tab切换时显示。

但是,有一个黑客至少对Qt 5.9.1有用。 Dock图标单击产生2个连续的QEvent :: ApplicationStateChangeEvent事件,同时cmd + tab - 仅产生一个。 因此,此代码将非常准确地发出Dock点击信号。 App类是从QApplication继承的应用程序类,也是自身的事件过滤器。

bool App::eventFilter(QObject* watched, QEvent* event)
{
#ifdef Q_OS_MACOS
    if (watched == this && event->type() == QEvent::ApplicationStateChange) {
        auto ev = static_cast<QApplicationStateChangeEvent*>(event);
        if (_prevAppState == Qt::ApplicationActive
                && ev->applicationState() == Qt::ApplicationActive) {
            emit clickedOnDock();
        }
        _prevAppState = ev->applicationState();
    }
#endif // Q_OS_MACOS
    return QApplication::eventFilter(watched, event);
}