简体   繁体   English

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

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

I'm trying to add Sparkle into my Qt ( binding for Go ) app to make it can be updated automatically. 我正在尝试将Sparkle添加到我的Qt( 绑定Go )应用程序中,以使其可以自动更新。

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 这是代码: 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. 作者指出的原因是NSAlert需要一个运行循环才能工作。

I found some docs: 我找到了一些文档:

So, as I understand, we have to instantiate NSApplication before creating a QApplication . 因此,据我所知,我们必须在创建QApplication之前实例化NSApplication

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

My Go's main function is something like this: 我的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()
}

Question: how can I start Go's main function from within the NSApplicationMain event loop? 问题:如何从NSApplicationMain事件循环中启动Go的主要功能?

Using QApplication together with a Runloop 将QApplication与Runloop一起使用

Regarding your question how to use your QApplication together with a NSRunloop: you are doing it already. 关于如何将QApplication与NSRunloop一起使用的问题:您已经这样做了。 Since you are using QApplication (and not QCoreApplication) you already have a Runloop running, 由于您使用的是QApplication(而不是QCoreApplication),因此您已经运行了Runloop,

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 http://code.qt.io/cgit/qt/qt.git/plain/src/gui/kernel/qeventdispatcher_mac.mmhttp://code.qt.io/cgit/qt/qt.git/plain /src/plugins/platforms/cocoa/qcocoaeventloopintegration.mm

Proof 证明

A NSTimer needs a run loop to work. NSTimer需要运行循环才能工作。 So we could add quick test with an existing example Qt app called 'widget' from the repository you referenced in your question. 因此,我们可以使用您在问题中引用的存储库中的现有示例Qt应用程序“widget”添加快速测试。

Adding a small Objective-C test class TimerRunloopTest with a C function wrapper that can be called from GO: 添加一个小的Objective-C测试类TimerRunloopTest和一个可以从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 counterpart timerrunlooptest.go GO对应的timerrunlooptest.go

package main

/*
#cgo LDFLAGS: -framework Foundation

void runTimerRunloopTest();
*/
import "C"

func runTimerRunloopTest() { C.runTimerRunloopTest() }

Change in main.go main.go的变化

At the end before app.Exec() add this line: 在app.Exec()之前的末尾添加以下行:

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 在macOS Console uitlity中,我们现在可以看到,显示了计时器滴答,证明运行循环正在运行

NSAlert NSAlert

Then you cited in your question, that NSAlert needs a run loop to work. 然后你在你的问题中引用了NSAlert需要一个运行循环来工作。 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: 所以我们可以修改timerrunlooptest.go来通知它,我们想再次连接Cocoa,不仅仅是基金会:

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: 然后我们可以将以下代码添加到TimerRunLoopTest的run方法中:

#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: GO / QT应用程序按预期显示本机警报:

来自GO / QT的本机警报

Mixing Qt with Native Code 将Qt与本机代码混合使用

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文档中的这一提示可能有用,也可能没用:

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). Qt的事件调度程序比Cocoa提供的更灵活,并允许用户旋转事件调度程序(并运行QEventLoop :: exec),而不必考虑是否在屏幕上显示模态对话框(这与Cocoa相比有所不同) 。 Therefore, we need to do extra management in Qt to handle this correctly, which unfortunately makes mixing native panels hard. 因此,我们需要在Qt中进行额外的管理才能正确处理这个问题,不幸的是,这使得混合原生面板变得困难。 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. 然后我们知道Qt在显示本机面板之前已经干净地更新了任何挂起的事件循环递归。

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

There is also a small code example for this. 还有一个小代码示例。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在Qt的主事件循环中使用std :: thread? - How to use std::thread with Qt's main event loop? 有人如何在不阻塞主事件循环的情况下从不同的线程访问在主事件循环中创建的 QObject? - How can someone access a QObject created in the main event loop from a different thread without blocking the main event loop? 如何在其他类的嵌套循环内进入主事件循环QCoreApplication来实现网络操作? - How to get to the main event loop QCoreApplication within nested loops of other classes for implementing networking operations? Qt:立即启动线程,不延迟主事件循环 - Qt: Immediately start thread, without delay of main event loop 如何将lambda函数排队到Qt的事件循环中? - How to queue lambda function into Qt's event loop? 在主事件循环之前如何创建一些对象? - How some objects are created before main event loop? 主类中的 Qt 调用函数来自 qlabel 单击事件 - Qt Call function in main class from qlabel click event 如何排除一组事件不被 QT 的事件循环处理? - How to exclude a set of events from being processed by QT's event loop? 本地QEventLoop-等待线程发出的信号-阻止处理来自主事件循环的事件 - Local QEventLoop - waiting for a signal from thread - prevent processing events from main event loop 从非Qt线程或ouside Qt主事件循环发出Qt信号,为4.5 - emit Qt signal from non Qt Thread or ouside Qt main event loop with at 4.5
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM