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

如何从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 ansys fluent计算闪退
  • ¥15 有关wireshark抓包的问题
  • ¥15 需要写计算过程,不要写代码,求解答,数据都在图上
  • ¥15 向数据表用newid方式插入GUID问题
  • ¥15 multisim电路设计
  • ¥20 用keil,写代码解决两个问题,用库函数
  • ¥50 ID中开关量采样信号通道、以及程序流程的设计
  • ¥15 U-Mamba/nnunetv2固定随机数种子
  • ¥15 vba使用jmail发送邮件正文里面怎么加图片
  • ¥15 vb6.0如何向数据库中添加自动生成的字段数据。