【问题标题】:python and Cocoa: about statusbar scriptpython和Cocoa:关于状态栏脚本
【发布时间】:2013-07-02 20:41:48
【问题描述】:

我正在创建一个小脚本,用于检查我的 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)有关。

谢谢!

【问题讨论】:

    标签: python cocoa statusbar nsrunloop


    【解决方案1】:

    你所有的问题都源于你阻塞了主线程。

    在 Cocoa 或几乎任何其他 GUI 框架中,主线程运行一个循环,等待下一个事件,调用事件处理程序,然后重复直到退出。

    您的事件处理程序applicationDidFinishLaunching_ 永远不会返回。这意味着 Cocoa 永远无法处理下一个事件。最终,操作系统会注意到您没有响应并放置沙滩球。

    对于 Cocoa,有时它会在您每次给它机会时潜入其他事件中,例如在 setTitle_ 调用中,即使您没有响应,操作系统也可能会伪造一些东西,例如保留窗口重绘,因此您的应用程序没有响应并不总是很明显。但这并不意味着您不需要解决问题。

    有很多方法可以做到这一点,但最简单的可能是使用后台线程。然后,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()    
    

    唯一棘手的一点是您必须为选择器使用 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 使用类属性而不是实例属性,所以我同样将条件设为类属性,将关闭方法设为类方法。)

    【讨论】:

    • 非常感谢您的帮助。我一直在尝试您编写的代码,但没有任何反应。这是我正在使用的代码:if __name__ == "__main__": app = NSApplication.sharedApplication() delegate = MyApplicationAppDelegate.alloc().init() app.setDelegate_(delegate) AppHelper.runEventLoop()
    • 请不要尝试将代码放入 cmets。这几乎是不可能的,因为 SO 破坏了格式。将其编辑到您的问题中,或者将其放在pastebin.com 之类的地方并在此处发布链接。理想情况下发布完整的代码,因为有一半的时间有人说“这个答案不起作用”,经过 30 分钟的反复来回,他们没有正确复制它,或者把它放在错误的地方…
    • 是的,这正是问题所在。第二个示例仅显示了与第一个示例相比有所更改的内容,即与使用条件相关的部分。您仍然需要第一个示例中的代码。特别是,如果您没有某种 applicationDidFinishLaunching_ 方法,那么 Cocoa 就没有什么可以调用的,并且如果您没有创建和启动线程的代码,它就永远不会运行。您确实需要了解代码的作用,而不仅仅是盲目地复制和粘贴。
    • 另外,如果你不打算在适当的时候编写代码来调用shutdown_background_threads,那显然对你没有任何好处。
    • 我试图遵循你的一些代码。你给我的第一个例子有效,但是有很多错误,比如“objc [21227]:OC_PythonString 类的对象 0x7f930bcd6020 自动释放,没有适当的池 - 只是泄漏 - 在 objc_autoreleaseNoPool() 上中断以进行调试”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多