Как запустить основную функцию Go из цикла событий NSApplication? - PullRequest
0 голосов
/ 14 декабря 2018

Я пытаюсь добавить Sparkle в мое приложение Qt ( привязка для Go ), чтобы оно могло обновляться автоматически.

Проблема: нет всплывающего диалогового окна при запуске последней версии

Вот код: https://github.com/sparkle-project/Sparkle/blob/master/Sparkle/SUUIBasedUpdateDriver.m#L104

Причина, как указал автор , заключается в NSAlert потребностицикл выполнения для работы.

Я нашел несколько документов:

Итак, какЯ понимаю, что нам нужно создать экземпляр NSApplication перед созданием QApplication.

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

Основная функция My 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()
}

Вопрос: как начатьОсновная функция Go из цикла событий NSApplicationMain?

1 Ответ

0 голосов
/ 23 декабря 2018

Использование QApplication вместе с Runloop

Относительно вашего вопроса, как использовать ваше QApplication вместе с NSRunloop: вы уже делаете это.Поскольку вы используете QApplication (а не QCoreApplication), у вас уже запущен Runloop,

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

Proof

Для работы NSTimer необходим цикл выполнения.Таким образом, мы могли бы добавить быстрый тест с существующим примером приложения Qt под названием 'widget' из репозитория, на который вы указали в своем вопросе.

Добавление небольшого класса Objective-C класса TimerRunloopTest с оберткой функции C, которую можно вызвать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 аналог 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 Console теперь мы можем видеть, что показываются отметки таймера, подтверждая, чтозапускается цикл выполнения

NSAlert

Тогда вы указали в своем вопросе, что для работы NSAlert нужен цикл выполнения.Мы уже доказали, что он у нас есть, но его проверка явно имеет смысл.

Таким образом, мы можем изменить timerrunlooptest.go, чтобы сообщить ему, что мы хотим связывать также с Cocoa, а не только с Foundation:

package main

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

void runTimerRunloopTest();
*/
import "C"

func runTimerRunloopTest() { C.runTimerRunloopTest() }

Затем мы можем добавить следующий код в метод runTimerRunLoopTest:

#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, как и ожидалось:

Native Alert from GO/QT

Смешивание Qt с собственным кодом

Хотя мы, похоже, можем отображать нативные предупреждения вкак описано выше, в документах QT есть эта подсказка, которая может или не может быть полезной:

Диспетчер событий Qt является более гибким, чем то, что предлагает Какао, и позволяет пользователю вращать диспетчер событий (и запуск QEventLoop :: exec) без необходимости думать о том, отображаются ли модальные диалоги на экране (что отличается от какао).Поэтому нам нужно сделать дополнительное управление в Qt для правильной обработки, что, к сожалению, затрудняет смешивание нативных панелей.На данный момент лучший способ сделать это - следовать приведенному ниже шаблону, где мы публикуем вызов функции с собственным кодом, а не вызываем ее напрямую.Затем мы знаем, что Qt корректно обновил все ожидающие рекурсии цикла событий перед отображением собственной панели.

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

Для этого также есть небольшой пример кода.

...