Все ваши проблемы связаны с тем, что вы блокируете основной поток.
В какао или почти любой другой структуре графического интерфейса основной поток запускает цикл, ожидающий следующего события, вызывает обработчик события и повторяет до завершения.
Ваш обработчик событий, applicationDidFinishLaunching_
, никогда не возвращается. Это означает, что Cocoa никогда не сможет справиться со следующим событием. В конце концов, OS заметит, что вы не отвечаете и устанавливаете пляжный мяч.
С помощью Cocoa иногда он прокрадывается в некоторые другие события каждый раз, когда вы даете ему шанс, например, на звонки setTitle_
, и есть некоторые вещи, которые ОС может подделать, даже если вы не отвечаете, например, сохраняя перерисовку окна , поэтому не всегда очевидно, что ваше приложение не реагирует. Но это не значит, что вам не нужно решать проблему.
Существует несколько способов сделать это, но проще всего использовать фоновый поток. Затем applicationDidFinishLaunching_ может запускать фоновый поток, а затем немедленно возвращаться, позволяя основному потоку возвращаться к событиям обработки заданий.
Единственный сложный бит в том, что код, выполняющийся на потоках фона, не может выполнять вызовы для объектов пользовательского интерфейса. Итак, что вы с этим поделаете?
вот что performSelectorOnMainThread_withObject_waitUntilDone_
для.
Вот пример:
class MyApplicationAppDelegate(NSObject):
var = 1
def background_work(self):
global ngmail
while var == 1 :
ngmail2 = gmail();
if ngmail2 !=ngmail:
self.statusItem.setTitle_("loading")
self.statusItem.performSelectorOnMainThread_withObject_waitUntilDone_('setTitle:', ngmail2, False)
time.sleep(6)
def applicationDidFinishLaunching_(self, sender):
NSLog("Application did finish launching.")
self.statusItem = NSStatusBar.systemStatusBar().statusItemWithLength_(NSVariableStatusItemLength)
self.background_worker = threading.Thread(target=self.background_work)
self.background_worker.start()
Единственной хитростью является то, что вы должны использовать имя ObjC для селектора (setTitle:
), а не имя Python (setTitle_
).
Однако ваш код другой тонкий ошибка: var
на самом деле не синхронизированы, так что это возможно для Вас, чтобы изменить его значение в основном потоке, не фоновый поток никогда не заметил.
Кроме того, выполнение sleep(6)
означает, что для выхода из приложения потребуется 6 секунд, поскольку фоновый поток не получит код, который проверяет var
, пока он не закончит спать.
Вы можете исправить их оба, используя Condition
.
class MyApplicationAppDelegate(NSObject):
var = 1
condition = threading.Condition()
def background_work(self):
global ngmail
with condition:
while var == 1:
ngmail2 = gmail();
if ngmail2 != ngmail:
self.statusItem.performSelectorOnMainThread_withObject_waitUntilDone_('setTitle:', ngmail2, False)
condition.wait(6)
@classmethod
def shutdown_background_threads(cls):
with condition:
var = 0
condition.notify_all()
(я предполагаю, что вы использовали атрибут класса для var
вместо экземпляра атрибута нарочно, так что я также сделал условию атрибут класса и метод отключения метода класса.)
Большое спасибо для вашей помощи. Я пытаюсь написать код, который вы написали, но ничего не происходит. Это код, я использую: 'если __name__ == "__main__":. приложение = NSApplication.sharedApplication() делегат = MyApplicationAppDelegate.alloc() Init() app.setDelegate_ (делегат) AppHelper.runEventLoop() ' – mrw
Пожалуйста, не пытайтесь помещать код в комментарии. Это почти невозможно, потому что SO уничтожает форматирование. Или отредактируйте его в свой вопрос, или поместите его где-нибудь, как http://pastebin.com, и разместите ссылку здесь. И в идеале пост полный код, потому что половина времени, когда кто-то говорит, что «этот ответ не работает», оказывается, после 30 минут назад и вперед, чтобы они не копировали его должным образом или не помещали в неправильное место ... – abarnert
Хорошо. Извините, я новичок здесь: http://pastebin.com/Bi8i70FE – mrw