使用Python开发基本的Cocoa应用程序,避免使用Xcode及其附加内容

2 投票
3 回答
2761 浏览
提问于 2025-04-15 14:48

看起来如果我想创建一个非常基础的Cocoa应用程序,比如有个停靠图标之类的,我就必须使用Xcode和图形界面构建工具(配合PyObjC)。

我打算写的这个应用主要是处理算法和基本的输入输出,所以和苹果特有的东西关系不大。

简单来说,这个应用应该定期运行(比如每3分钟一次),通过AppleScript获取一些信息,并把HTML文件写到一个特定的目录里。我想给这个应用加一个停靠图标,主要是用来显示“状态”(例如,如果出现错误,停靠图标上会有一个红旗)。停靠图标的另一个好处是我可以让它在开机时自动运行。

另外,如果能简单地定义停靠图标的右键菜单(比如用Python的可调用列表),那就更好了。

我能否不使用Xcode或图形界面构建工具,而是仅仅用Emacs和Python来实现这些功能呢?

3 个回答

0

Chuck说得对,关于PyObjC的内容。

接下来,你可以了解一下这个NSApplication的方法,用来更改你的应用图标。

-(void)setApplicationIconImage:(NSImage *)anImage;

关于Dock菜单,你可以在应用的代理中实现以下内容。你可以通过编程的方式创建一个NSMenu,这样就不需要使用InterfaceBuilder了。

-(NSMenu *)applicationDockMenu:(NSApplication *)sender;

2

PyObjC 是 Mac OS X 10.5 和 10.6 自带的一个工具,跟你想要的东西非常接近。

9

首先,安装最新的 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的说明并不清晰,所以我从网上找到的示例代码和大量的试错中拼凑出了这个方案。希望这能对你有所帮助!-)

撰写回答