2013-07-02 1 views
0

Я создаю небольшой скрипт, который проверяет количество писем в моей учетной записи gmail и печатает их в строке состояния . Функция gmail() возвращает количество новых писем. У меня есть несколько вопросов, но сначала это код, который я написал до сих пор (ясно я новичок):python и Cocoa: о сценарии состояния.

class MyApplicationAppDelegate(NSObject): 

var = 1 

def applicationDidFinishLaunching_(self, sender): 
    NSLog("Application did finish launching.") 

    global ngmail 

    self.statusItem = NSStatusBar.systemStatusBar().statusItemWithLength_(NSVariableStatusItemLength) 

    while var == 1 : 
     ngmail2 = gmail(); 
     if ngmail2 !=ngmail: 
      self.statusItem.setTitle_("loading") 
      self.statusItem.setTitle_(ngmail2) 
      ngmail = ngmail2 
     time.sleep(6) 

1) Зачем мне нужна строка «self.statusItem.setTitle _ (» загрузка «)» ? без этой строки он не обновлялся бы сам. Я действительно не знаю, почему.

2) он работает так, как должен, но всякий раз, когда я приближаюсь к номеру в строке состояния, появляется вращающееся колесо.
Я думаю, причина в том, что я использую while, и вместо этого я должен использовать что-то вроде nsrunloop или что-то в этом роде. Может ли кто-нибудь посоветовать это?

3) Если я поставил свой макинтош спать, и я его разбудил, скрипт перестает работать. Любое решение? возможно, это связано с вопросом 2) выше.

Спасибо!

ответ

0

Все ваши проблемы связаны с тем, что вы блокируете основной поток.

В какао или почти любой другой структуре графического интерфейса основной поток запускает цикл, ожидающий следующего события, вызывает обработчик события и повторяет до завершения.

Ваш обработчик событий, 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 вместо экземпляра атрибута нарочно, так что я также сделал условию атрибут класса и метод отключения метода класса.)

+0

Большое спасибо для вашей помощи. Я пытаюсь написать код, который вы написали, но ничего не происходит. Это код, я использую: 'если __name__ == "__main__":. приложение = NSApplication.sharedApplication() делегат = MyApplicationAppDelegate.alloc() Init() app.setDelegate_ (делегат) AppHelper.runEventLoop() ' – mrw

+0

Пожалуйста, не пытайтесь помещать код в комментарии. Это почти невозможно, потому что SO уничтожает форматирование. Или отредактируйте его в свой вопрос, или поместите его где-нибудь, как http://pastebin.com, и разместите ссылку здесь. И в идеале пост полный код, потому что половина времени, когда кто-то говорит, что «этот ответ не работает», оказывается, после 30 минут назад и вперед, чтобы они не копировали его должным образом или не помещали в неправильное место ... – abarnert

+0

Хорошо. Извините, я новичок здесь: http://pastebin.com/Bi8i70FE – mrw

Смежные вопросы