0

Gmailアカウントのメール数をチェックしてステータスバーに出力する小さなスクリプトを作成しています. 関数 gmail() は、新しい電子メールの数を返します。いくつか質問がありますが、最初にこれまでに書いたコードです (明らかに私は初心者です)。

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_("loading")" という行が必要なのですか? その行がないと、それ自体は更新されません。なぜだか本当にわかりません。

2) 正常に動作しますが、ステータス バーの数字に近づくと、糸車が表示されます。
その理由は、while を使用しているためだと思いますが、代わりに nsrunloop などを使用する必要があります。誰でもこれについてアドバイスできますか?

3) Mac をスリープ状態にして起動すると、スクリプトが動作しなくなります。解決策はありますか?おそらくこれは上記の質問 2) に関連しています。

ありがとう!

4

1 に答える 1

0

あなたの問題はすべて、メインスレッドをブロックしているという事実から来ています。

Cocoa や他のほぼすべての GUI フレームワークでは、メイン スレッドは次のイベントを待機するループを実行し、イベント ハンドラーを呼び出し、終了するまで繰り返します。

イベント ハンドラ はapplicationDidFinishLaunching_決して戻りません。これは、Cocoa が次のイベントを処理できないことを意味します。最終的に、OS はあなたが応答していないことに気づき、ビーチボールを準備します。

Cocoa では、呼び出しのように、機会を与えるたびに他のイベントに忍び込むことsetTitle_があります。また、ウィンドウを再描画し続けるなど、応答していなくても OS が偽造できるものがあるため、そうではありません。アプリが応答しないことは常に明らかです。しかし、それは問題を解決する必要がないという意味ではありません。

これを行うにはいくつかの方法がありますが、最も簡単なのはおそらくバックグラウンド スレッドを使用することです。次に、applicationDidFinishLaunching_ はバックグラウンド スレッドを開始してすぐに戻ることができるため、メイン スレッドはジョブ処理イベントに戻ることができます。

唯一の注意点は、バックグラウンド スレッドで実行されているコードが UI オブジェクトを呼び出せないことです。それで、あなたはそれについて何をしますか?

それ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()    

setTitle:唯一のトリッキーな点は、Python の名前 ( ) ではなく、ObjC の名前 ( ) をセレクターに使用する必要があることです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()

(意図的にインスタンス属性ではなく、クラス属性 for を使用していると思われるvarので、同様に条件をクラス属性に、シャットダウン メソッドをクラス メソッドにしました。)

于 2013-07-02T21:11:31.697 に答える