如何从NSApplication事件循环中启动Go的主要功能? [英] How to start Go's main function from within the NSApplication event loop?

查看:100
本文介绍了如何从NSApplication事件循环中启动Go的主要功能?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在我的Qt中添加



与本机代码混合Qt



尽管我们似乎能够以上述方式显示本机警报,但QT文档中存在以下提示可能有用也可能没有用:


Qt的事件分派器比Cocoa提供的灵活,并且允许用户旋转事件分派器(并运行QEventLoop :: exec),而不必考虑是否模态屏幕上显示对话框(与可可相比有所不同)。因此,我们需要在Qt中进行额外的管理才能正确处理此问题,不幸的是这使得混合本机面板变得困难。目前执行此操作的最佳方法是遵循以下模式,在该模式中,我们使用本地代码而不是直接调用将调用发布到函数中。然后我们知道Qt在显示本机面板之前已经干净地更新了所有未决的事件循环递归。


请参见 https://doc.qt.io/qt-5/macos- issue.html#using-native-cocoa-panels



还有一个小代码示例。


I'm trying to add Sparkle into my Qt (binding for Go) app to make it can be updated automatically.

Problem: there is no popup dialog when running the latest version

Here's the code: https://github.com/sparkle-project/Sparkle/blob/master/Sparkle/SUUIBasedUpdateDriver.m#L104

The reason as the author pointed out is NSAlert needs a run loop to work.

I found some docs:

So, as I understand, we have to instantiate NSApplication before creating a QApplication.

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

My Go's main function is something like this:

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

Question: how can I start Go's main function from within the NSApplicationMain event loop?

解决方案

Using QApplication together with a Runloop

Regarding your question how to use your QApplication together with a NSRunloop: you are doing it already. Since you are using QApplication (and not QCoreApplication) you already have a Runloop running,

see 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

Proof

A NSTimer needs a run loop to work. So we could add quick test with an existing example Qt app called 'widget' from the repository you referenced in your question.

Adding a small Objective-C test class TimerRunloopTest with a C function wrapper that can be called from GO:

#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 counterpart timerrunlooptest.go

package main

/*
#cgo LDFLAGS: -framework Foundation

void runTimerRunloopTest();
*/
import "C"

func runTimerRunloopTest() { C.runTimerRunloopTest() }

Change in main.go

At the end before app.Exec() add this line:

runTimerRunloopTest()

Build and Run it

Switch loggin on for our logging messages:

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

Afterwards build an run it:

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

Test

In the macOS Console uitlity we can now see, that the timer ticks are shown, proofing that a run-loop is running

NSAlert

Then you cited in your question, that NSAlert needs a run loop to work. We already proofed that we have one, but testing it explicitely makes sense.

So we can modify timerrunlooptest.go to inform it, that we want to link agains Cocoa also, not only Foundation:

package main

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

void runTimerRunloopTest();
*/
import "C"

func runTimerRunloopTest() { C.runTimerRunloopTest() }

Then we could add the following code to the run method of TimerRunLoopTest:

#import <Cocoa/Cocoa.h>

...


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

Result

After doing a

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

the native Alert is shown from the GO/QT application as expected:

Mixing Qt with Native Code

Although we seem to be able to display native alerts in the way described above, there is this hint in the QT documents that may or may not be useful:

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.

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

There is also a small code example for this.

这篇关于如何从NSApplication事件循环中启动Go的主要功能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆