您所有的问题都来自您阻塞主线程的事实。
在 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
实际上并没有同步,因此您可以在主线程中更改它的值,而不会注意到后台线程。
最重要的是,执行 asleep(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
而不是实例属性,所以我同样将条件设为类属性,将关闭方法设为类方法。)