我正在创建一个小脚本,用于检查我的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)有关。
谢谢!
答案 0 :(得分:0)
你所有的问题都来自你阻止主线程的事实。
在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
的类属性而不是实例属性,因此我同样使条件成为类属性,将关闭方法作为类方法。)