如何从 NSApplication 事件循环中启动 Go 的主要功能?

How to start Go's main function from within the NSApplication event loop?

我正在尝试添加 Sparkle into my Qt (binding for Go) 应用程序以使其可以自动更新。

问题:

代码如下:https://github.com/sparkle-project/Sparkle/blob/master/Sparkle/SUUIBasedUpdateDriver.m#L104

作者 pointed out 的原因是 NSAlert 需要 运行 循环才能工作。

我找到了一些文档:

因此,据我了解,我们必须在创建 QApplication.

之前实例化 NSApplication
void NSApplicationMain(int argc, char *argv[]) {
    [NSApplication sharedApplication];
    [NSBundle loadNibNamed:@"myMain" owner:NSApp];
    [NSApp run];
}

我的 Go 的主要功能是这样的:

func main() {
    widgets.NewQApplication(len(os.Args), os.Args)

    ...
    action := widgets.NewQMenuBar(nil).AddMenu2("").AddAction("Check for Updates...")
    // http://doc.qt.io/qt-5/qaction.html#MenuRole-enum
    action.SetMenuRole(widgets.QAction__ApplicationSpecificRole)
    action.ConnectTriggered(func(bool) { sparkle_checkUpdates() })
    ...

    widgets.QApplication_Exec()
}

问题:如何从 NSApplicationMain 事件循环中启动 Go 的主要功能?

将 QApplication 与 运行loop

一起使用

关于如何将 QApplication 与 NS运行loop 一起使用的问题:您已经这样做了。 由于您使用的是 QApplication(而不是 QCoreApplication),因此您已经有了一个 运行loop 运行ning,

http://code.qt.io/cgit/qt/qt.git/plain/src/gui/kernel/qeventdispatcher_mac.mm and http://code.qt.io/cgit/qt/qt.git/plain/src/plugins/platforms/cocoa/qcocoaeventloopintegration.mm

证明

NSTimer 需要一个 运行 循环才能工作。因此,我们可以使用您在问题中引用的存储库中名为 'widget' 的现有示例 Qt 应用程序添加快速测试。

添加一个小的 Objective-C 测试 class Timer运行loopTest 和一个可以从 GO 调用的 C 函数包装器:

#import <Foundation/Foundation.h>
#include <os/log.h>


@interface TimerRunloopTest : NSObject

- (void)run;

@end

void runTimerRunloopTest() {

    [[TimerRunloopTest new] run];

}


@implementation TimerRunloopTest

- (void)run {

    os_log_t log = os_log_create("widget.example", "RunloopTest");
    os_log(log, "setup happening at %f", NSDate.timeIntervalSinceReferenceDate);


    [NSTimer scheduledTimerWithTimeInterval:1.0
                                     target:self
                                   selector:@selector(timerTick:)
                                   userInfo:nil
                                    repeats:YES];
}

- (void)timerTick:(NSTimer *)timer {
    os_log_t log = os_log_create("widget.example", "RunloopTest");
    os_log(log, "timer tick %f", NSDate.timeIntervalSinceReferenceDate);
}

@end

GO对应timerrunlooptest.go

package main

/*
#cgo LDFLAGS: -framework Foundation

void runTimerRunloopTest();
*/
import "C"

func runTimerRunloopTest() { C.runTimerRunloopTest() }

变化 main.go

在 app.Exec() 之前的末尾添加这一行:

runTimerRunloopTest()

构建并运行它

为我们的日志消息切换登录:

sudo log config --subsystem widget.example --mode level:debug

然后构建一个 运行 它:

$(go env GOPATH)/bin/qtdeploy test desktop examples/basic/widgets

测试

在 macOS 控制台实用程序中,我们现在可以看到显示了计时器滴答声,证明 运行 循环是 运行ning

NSAlert

然后您在问题中引用了 NSAlert 需要一个 运行 循环才能工作。我们已经证明我们有一个,但是明确地测试它是有道理的。

所以我们可以修改 timerrunlooptest.go 来通知它,我们想要 link 再次 Cocoa,而不仅仅是 Foundation:

package main

/*
#cgo LDFLAGS: -framework Foundation
#cgo LDFLAGS: -framework Cocoa

void runTimerRunloopTest();
*/
import "C"

func runTimerRunloopTest() { C.runTimerRunloopTest() }

然后我们可以在Timer运行LoopTest的运行方法中加入如下代码:

#import <Cocoa/Cocoa.h>

...


NSAlert *alert = [[NSAlert alloc] init];
alert.messageText = @"Message";
alert.informativeText = @"Info";
[alert addButtonWithTitle:@"OK"];
[alert runModal];

结果

完成后

$(go env GOPATH)/bin/qtdeploy test desktop examples/basic/widgets

本机警报按预期从 GO/QT 应用程序中显示:

将 Qt 与本机代码混合

虽然我们似乎能够以上述方式显示本机警报,但 QT 文档中有此提示可能有用也可能没有用:

Qt's event dispatcher is more flexible than what Cocoa offers, and lets the user spin the event dispatcher (and running QEventLoop::exec) without having to think about whether or not modal dialogs are showing on screen (which is a difference compared to Cocoa). Therefore, we need to do extra management in Qt to handle this correctly, which unfortunately makes mixing native panels hard. The best way at the moment to do this, is to follow the pattern below, where we post the call to the function with native code rather than calling it directly. Then we know that Qt has cleanly updated any pending event loop recursions before the native panel is shown.

https://doc.qt.io/qt-5/macos-issues.html#using-native-cocoa-panels

还有一个小代码示例。