使用Python开发基本的Cocoa应用程序,避免使用Xcode及其附加内容
看起来如果我想创建一个非常基础的Cocoa应用程序,比如有个停靠图标之类的,我就必须使用Xcode和图形界面构建工具(配合PyObjC)。
我打算写的这个应用主要是处理算法和基本的输入输出,所以和苹果特有的东西关系不大。
简单来说,这个应用应该定期运行(比如每3分钟一次),通过AppleScript获取一些信息,并把HTML文件写到一个特定的目录里。我想给这个应用加一个停靠图标,主要是用来显示“状态”(例如,如果出现错误,停靠图标上会有一个红旗)。停靠图标的另一个好处是我可以让它在开机时自动运行。
另外,如果能简单地定义停靠图标的右键菜单(比如用Python的可调用列表),那就更好了。
我能否不使用Xcode或图形界面构建工具,而是仅仅用Emacs和Python来实现这些功能呢?
3 个回答
Chuck说得对,关于PyObjC的内容。
接下来,你可以了解一下这个NSApplication的方法,用来更改你的应用图标。
-(void)setApplicationIconImage:(NSImage *)anImage;
关于Dock菜单,你可以在应用的代理中实现以下内容。你可以通过编程的方式创建一个NSMenu,这样就不需要使用InterfaceBuilder了。
PyObjC 是 Mac OS X 10.5 和 10.6 自带的一个工具,跟你想要的东西非常接近。
首先,安装最新的 py2app,然后创建一个新文件夹,进入这个文件夹,在里面新建一个 HelloWorld.py
文件,内容可以是:
# generic Python imports
import datetime
import os
import sched
import sys
import tempfile
import threading
import time
# need PyObjC on sys.path...:
for d in sys.path:
if 'Extras' in d:
sys.path.append(d + '/PyObjC')
break
# objc-related imports
import objc
from Foundation import *
from AppKit import *
from PyObjCTools import AppHelper
# all stuff related to the repeating-action
thesched = sched.scheduler(time.time, time.sleep)
def tick(n, writer):
writer(n)
thesched.enter(20.0, 10, tick, (n+1, writer))
fd, name = tempfile.mkstemp('.txt', 'hello', '/tmp');
print 'writing %r' % name
f = os.fdopen(fd, 'w')
f.write(datetime.datetime.now().isoformat())
f.write('\n')
f.close()
def schedule(writer):
pool = NSAutoreleasePool.alloc().init()
thesched.enter(0.0, 10, tick, (1, writer))
thesched.run()
# normally you'd want pool.drain() here, but since this function never
# ends until end of program (thesched.run never returns since each tick
# schedules a new one) that pool.drain would never execute here;-).
# objc-related stuff
class TheDelegate(NSObject):
statusbar = None
state = 'idle'
def applicationDidFinishLaunching_(self, notification):
statusbar = NSStatusBar.systemStatusBar()
self.statusitem = statusbar.statusItemWithLength_(
NSVariableStatusItemLength)
self.statusitem.setHighlightMode_(1)
self.statusitem.setToolTip_('Example')
self.statusitem.setTitle_('Example')
self.menu = NSMenu.alloc().init()
menuitem = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(
'Quit', 'terminate:', '')
self.menu.addItem_(menuitem)
self.statusitem.setMenu_(self.menu)
def writer(self, s):
self.badge.setBadgeLabel_(str(s))
if __name__ == "__main__":
# prepare and set our delegate
app = NSApplication.sharedApplication()
delegate = TheDelegate.alloc().init()
app.setDelegate_(delegate)
delegate.badge = app.dockTile()
delegate.writer(0)
# on a separate thread, run the scheduler
t = threading.Thread(target=schedule, args=(delegate.writer,))
t.setDaemon(1)
t.start()
# let her rip!-)
AppHelper.runEventLoop()
当然,在你的实际代码中,你会每3分钟执行自己的操作(而不是像我这里每20秒写一个临时文件),进行自己的状态更新(而不是仅仅显示写入文件的数量),等等等等,但我希望这个例子能给你一个可行的起点。
接下来,在Terminal.App中进入这个源文件所在的目录,输入 py2applet --make-setup HelloWorld.py
,然后输入 python setup.py py2app -A -p PyObjC
。
现在你在子目录 dist
中会看到一个 HelloWorld.app
目录;打开 dist
,把图标拖到Dock上,这样就完成了(在你自己的机器上 -- 如果要分发到其他机器可能会因为 -A
标志而不工作,但我在没有这个标志的情况下构建时遇到了麻烦,可能是因为这个机器上有些安装不当的egg文件;-)。当然,你可能还想自定义你的图标等等。
这个过程并没有实现你问的“额外加分”功能 -- 这已经是很多代码了,我决定在这里停止(额外加分的功能可能需要另开一个问题)。另外,我也不太确定我这里做的所有步骤是否都是必要或有用的;文档对于如何在没有Xcode的情况下制作pyobjc .app的说明并不清晰,所以我从网上找到的示例代码和大量的试错中拼凑出了这个方案。希望这能对你有所帮助!-)