dongyin2885 2018-12-14 02:53
浏览 166
已采纳

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

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?

  • 写回答

1条回答 默认 最新

  • drnmslpz42661 2018-12-23 12:50
    关注

    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:

    Native Alert from GO/QT

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 关于#java#的问题:找一份能快速看完mooc视频的代码
  • ¥15 这种微信登录授权 谁可以做啊
  • ¥15 请问我该如何添加自己的数据去运行蚁群算法代码
  • ¥20 用HslCommunication 连接欧姆龙 plc有时会连接失败。报异常为“未知错误”
  • ¥15 网络设备配置与管理这个该怎么弄
  • ¥20 机器学习能否像多层线性模型一样处理嵌套数据
  • ¥20 西门子S7-Graph,S7-300,梯形图
  • ¥50 用易语言http 访问不了网页
  • ¥50 safari浏览器fetch提交数据后数据丢失问题
  • ¥15 matlab不知道怎么改,求解答!!