第 11 章:在 IDE 中使用 Jython

在本章中,我们将讨论使用两个最流行的集成开发环境 Eclipse 和 Netbeans 开发 Jython 应用程序。如今,有许多其他开发环境可用于 Python 和 Jython,但是,这两个可能是最流行的,并且包含最多的 Jython 特定工具。Eclipse 多年来一直有一个名为 PyDev 的插件,该插件为开发和维护 Python 和 Jython 应用程序提供了丰富的支持。Netbeans 从 6.5 及更高版本开始包含 Python 和 Jython 支持。Netbeans IDE 还为开发和维护 Python 和 Jython 应用程序提供了丰富的支持。

请注意,在本章中,我们将 Python/Jython 称为 Jython。除非另有说明,否则讨论的所有 IDE 选项都适用于 Python 和 Jython。为了可读性和一致性,除非某些功能专门针对 Python 或 Jython,否则我们不会在本章中同时提及 Python 和 Jython。另请注意,我们将按名称称呼讨论的插件,因此在 Netbeans 的情况下,该插件称为 *Netbeans Python Plugin*。此插件在所有情况下都适用于 Python 和 Jython。

Eclipse

当然,您需要在您的机器上安装 Eclipse 才能使用 Jython。本书撰写时最新版本是 Eclipse 3.5(也称为 Eclipse Galileo),建议您使用此版本来遵循本节。版本 3.2、3.3 和 3.4 也将起作用,尽管用户界面会有细微差别,这可能会在您遵循本节时造成混淆。

如果您在机器上没有安装 Eclipse,请访问 http://www.eclipse.org/downloads/ 并下载适用于 Java 开发人员的版本。

安装 PyDev

Eclipse 本身并不包含 Jython 支持。因此,我们将使用 PyDev,这是一个优秀的插件,它为 Python 语言添加了支持,并包括对 Jython 的专门支持。PyDev 的主页是 http://pydev.sourceforge.net/,但您无需手动下载和安装它。

要安装插件,请启动 Eclipse 并转到菜单 Help ‣ Install new Software…,在“Work with”输入框中输入 http://pydev.sourceforge.net/updates/ 并按回车键。片刻之后,您将在下面的较大框中看到 PyDev 的条目。只需选择它,单击“PyDev”左侧出现的复选框(请参考下面的图片),最后单击“Next”按钮。

PyDev Installation

之后,只需按照向导操作,接受许可协议,然后单击“Finish”按钮。

Eclipse 安装完插件后,会询问您是否要重新启动 IDE 以启用插件。由于这是推荐的选项,请执行此操作,在对话框中回答“Yes”。Eclipse 重新启动后,您将在 IDE 中享受完整的 Python 支持。

最小配置

在开始 PyDev 项目之前,您必须告诉 PyDev 哪些 Python 解释器可用。在这种情况下,解释器只是 Python 的某个实现的特定安装。在开始时,您通常只需要一个解释器,在本节中,我们将只使用 Jython 2.5.0。要配置它,请打开 Eclipse 首选项对话框(通过主菜单栏中的 Window ‣ Preferences)。在左侧面板顶部的文本框中,输入“Jython”。这将过滤掉大量 Eclipse(和 PyDev!)选项,并向我们展示一个简化的视图,您将在左侧看到“Interpreter - Jython”部分。

选择“Intepreter - Jython”部分后,您将在右侧顶部看到一个空的 Jython 解释器列表。我们显然需要修复它!因此,单击“New…”按钮,输入“Jython 2.5.0”作为“Interpreter Name”,单击“Browse…”按钮,并在您的 Jython 2.5.0 安装中找到 jython.jar

注意

即使这是我们将在本章中使用的唯一运行时,我建议您使用类似于此处提出的命名方案,在解释器名称中包含实现名称(例如:“Jython”)和完整版本(例如:“2.5.0”)。这将避免在将来添加新解释器时出现混淆和名称冲突。

选择 jython.jar 文件后,PyDev 将自动检测默认的、全局 sys.path 条目。PyDev 始终推断正确的值,因此除非您有非常特殊的需求,否则只需接受默认选择并单击“OK”。

如果一切顺利,您现在将在 Jython 解释器列表中看到一个条目,代表您刚刚输入的信息。它将类似于下面的图片(当然,您的文件系统路径会有所不同)

PyDev Configuration: Jython Interpreter

就这样。单击“OK”,您将准备好使用 Jython 进行开发,同时享受现代 IDE 提供的支持。

如果您好奇,您可能想探索“Preferences”窗口中“PyDev”部分下方的其他选项(清除我们用来快速转到 Jython 解释器配置的搜索过滤器后)。但在我的经验中,很少需要更改其他可用选项。

在接下来的部分中,我们将看看更重要的 PyDev 功能,以获得更愉快的学习体验并提高您的工作效率。

Hello PyDev!:创建项目和执行模块

当您看到本章中的第一个示例代码时,它可能看起来过于简单。事实上,这是一个非常愚蠢的例子。重点是将重点放在您将在 Eclipse IDE 中执行的任何基于 Python 的项目的生命周期中执行的基本步骤上,这些步骤将适用于简单和复杂的项目。因此,正如您可能猜到的,我们的第一个项目将是一个愚蠢的“Hello World”。让我们开始吧!

转到 File ‣ New ‣ Project…。您将看到一个可能很长的列表,其中包含您可以使用 Eclipse 创建的所有类型的项目。选择“PyDev Project”,位于“PyDev”组下(您也可以使用顶部的过滤器文本框,如果更快,只需输入“PyDev Project”)。

下一个对话框将询问您项目的属性。作为“Project name”,我们将使用“LearningPyDev”。在“Project contents”中,我们将选中“Use default”复选框,因此 PyDev 将在 Eclipse 工作区(即 Eclipse 项目的根路径)中创建一个与项目同名的目录。由于我们使用的是 Jython 2.5.0,我们将把“Project type”更改为“Jython”,并将“Grammar Version”更改为“2.5”。我们将保留“Interpreter”,它将默认为我们在 最小配置 部分中定义的 Jython 解释器。我们还将选中“Create default ‘src’ folder and add it to the pythonpath”选项,因为它是在 Eclipse 项目中的一种常见约定。

点击“完成”后,PyDev 将创建您的项目,该项目仅包含一个空的 src 目录和对所用解释器的引用。现在让我们创建我们的程序!

右键单击项目,然后选择 新建 ‣ PyDev 模块。将“包”留空,并将“名称”输入为“main”。PyDev 提供了一些模板来加快新模块的创建速度,但我们不会使用它们,因为我们的需求相当简单。因此,将“模板”留空,然后单击“完成”。

PyDev 将为您提供一个刚刚创建的 main.py 文件的编辑器。现在是实现我们程序的时候了。在编辑器中编写以下代码

if __name__ == "__main__":
    print "Hello PyDev!"

然后按 Ctrl + F11 运行此程序。从出现的对话框中选择“Jython 运行”,然后单击“确定”。程序将运行,文本“Hello PyDev!”将出现在 IDE 底部区域的控制台中。

如果您手动输入了程序,您可能已经注意到 IDE 知道在 Python 中以“:”结尾的行表示一个块的开始,并且会自动将您的光标放在下一行的适当缩进级别。看看如果您手动覆盖此决定并将打印语句放在 if 语句的相同缩进级别并保存文件会发生什么。IDE 将突出显示该行,标记错误。如果您将鼠标悬停在错误标记上,您将看到错误的解释,如图像所示

PyDev Highlighting a Simple Error

对于您犯的任何语法错误,都期望得到相同类型的反馈。这有助于避免因不断进行编辑-运行循环而最终发现更多细微的语法错误而产生的挫败感。

传递命令行参数和自定义执行

命令行参数可能看起来过时了,但实际上是一种非常简单有效的方式,可以让程序与外部交互。既然您已经学会了将 Jython 用作脚本语言,那么编写从命令行获取输入的脚本并不少见(请注意,对于无人值守执行,从命令行读取输入比从标准输入获取数据要方便得多,更不用说使用 GUI 了)。

正如您可能猜到的那样,我们将使我们的玩具程序接受一个命令行参数。该参数将代表要问候的用户的名字,以构建更个性化的解决方案。以下是我们的 main.py 应该是什么样子的

import sys
if __name__ == "__main__":
    if len(sys.argv) < 2:
        print "Sorry, I can't greet you if you don't say your name"
    else:
        print "Hello %s!" % sys.argv[1]

如果您再次按下 Ctrl + F11,您将在控制台中看到“对不起,我不能问候你……”消息。这是有道理的,因为您没有传递姓名。更不用说这是您的错,因为您也没有机会说出您的名字。

要指定命令行参数,请转到 运行 ‣ 运行配置… 菜单,您将在左侧的“Jython 运行”部分下找到一个名为“LearningPyDev main.py”的条目。它可能已经被选中,但如果没有,请手动选中它。然后,在对话框的主部分,您将找到自定义脚本执行方式的方法。您可以更改当前目录、向 JVM 传递特殊参数、更改要使用的解释器、设置环境变量等方面。我们只需要指定一个参数,因此让我们在“程序参数”框中键入“Bob”,然后单击“运行”按钮。

正如您所期望的那样,程序现在在控制台中打印“Hello Bob!”。请注意,您输入的值会被记住,也就是说,如果您现在按下 Ctrl + F11,程序将再次打印“Hello Bob!”。有些人可能会指出,这种行为使得测试此类程序非常尴尬,因为每次需要更改参数时都必须打开“运行配置”对话框。但是,如果我们真的想测试我们的程序(这是一个好主意),我们应该以正确的方式进行。我们很快就会研究这个问题,但首先让我们完成对基本 IDE 功能的介绍。

使用编辑器

让我们进一步扩展我们的示例代码,提供不同的方式来问候我们的用户,使用不同的语言。这次我们将使用 optparse 来处理参数。如果您想了解如何使用 optparse,请参考第 8 章。我们还将使用装饰器(在第 6 章中介绍)使扩展我们的程序以使用新的问候用户方式变得微不足道。因此,我们的小型 main.py 现在已经增长了一点

# -*- coding: utf-8 -*-
import sys
from optparse import OptionParser

greetings = dict(en=u'Hello %s!',
                 es=u'Hola %s!',
                 fr=u'Bonjour %s!',
                 pt=u'Alò %s!')

uis = {}
def register_ui(ui_name):
    def decorator(f):
        uis[ui_name] = f
        return f
    return decorator

def message(ui, msg):
    if ui in uis:
        uis[ui](msg)
    else:
        raise ValueError("No greeter named %s" % ui)

def list_uis():
    return uis.keys()

@register_ui('console')
def print_message(msg):
    print msg

@register_ui('window')
def show_message_as_window(msg):
    from javax.swing import JFrame, JLabel
    frame = JFrame(msg,
                   defaultCloseOperation=JFrame.EXIT_ON_CLOSE,
                   size=(100, 100),
                   visible=True)
    frame.contentPane.add(JLabel(msg))

if __name__ == "__main__":
    parser = OptionParser()
    parser.add_option('--ui', dest='ui', default='console',
                      help="Sets the UI to use to greet the user. One of: %s" %
                      ", ".join("'%s'" % ui for ui in list_uis()))
    parser.add_option('--lang', dest='lang', default='en',
                      help="Sets the language to use")
    options, args = parser.parse_args(sys.argv)
    if len(args) < 2:
        print "Sorry, I can't greet you if you don't say your name"
        sys.exit(1)

    if options.lang not in greetings:
        print "Sorry, I don't speak '%s'" % options.lang
        sys.exit(1)

    msg = greetings[options.lang] % args[1]

    try:
        message(options.ui, msg)
    except ValueError, e:
        print "Invalid UI name\n"
        print "Valid UIs:\n\n" + "\n".join(' * ' + ui for ui in list_uis())
        sys.exit(1)

花点时间在编辑器中玩玩这段代码。尝试按下 Ctrl + Space,这是自动代码补全的快捷键(在微软术语中也称为“智能感知”),在不同的位置使用。它将为导入语句提供补全(尝试在 import 标记之后或 OptionParser 标记中间完成该行),以及属性或方法访问(例如在 sys.exitparser.add_option 上,甚至在 JFrame.EXIT_ON_CLOSE 上,它访问的是一个 Java 类!)。它还提供有关方法参数的提示。

一般来说,每次你输入一个点,自动补全列表都会弹出,如果 IDE 对你刚刚输入的符号有足够的了解以提供帮助。但你也可以在任何时候调用帮助。例如,转到代码底部并输入 message(。假设你只是忘记了该函数参数的顺序。解决方案:按下 Ctrl + Space,PyDev 将使用函数的形式参数名称“完成”语句。

还可以尝试在关键字(如 def)上使用 Ctrl + Space。PyDev 将为你提供一些小模板,可以节省一些打字时间。你可以在 Eclipse 首选项窗口的 PyDev ‣ Editor ‣ Templates 部分(在 Window ‣ Preferences 主菜单中可用)自定义模板。

现在我们有了更多代码,包括一些导入、函数和全局变量,你可能已经注意到 IDE 窗口右侧的“大纲”面板显示了正在编辑的代码的树状结构视图,显示了这些功能。顺便说一下,它还显示了类。

别忘了运行代码!当然,在按下 Ctrl + F11 后,我们仍然在控制台上看到相同的无聊的“Hello Bob!”文本,这并不令人惊奇。但是,如果你将命令行参数(如最近通过“运行配置...”对话框看到的)编辑为以下内容:Bob --lang es --ui window,你将看到一个用西班牙语问候 Bob 的漂亮窗口。还可以看看如果你指定了一个不支持的 UI(例如,--ui speech)或不支持的语言会发生什么。我们甚至支持 --help!因此,我们有一个通用的、多语言的问候程序,它恰好也相当健壮且用户友好(对于命令行程序标准来说)。

此时,你可能已经厌倦了手动测试程序,在该对话框中编辑命令行参数。再加一个额外的部分,我们将进入一种更好的方法来使用 IDE 测试我们的程序。实际上,下一部分的内容将帮助我们找到解决方案。

结构简介:包、模块和导航

如果你喜欢简单,你可能会问(或者咒骂,取决于你的性格)为什么我过度设计了最后一个例子。对于同一个问题陈述,有更简单的(从更简洁和易懂的代码意义上来说)解决方案。但我需要扩展我们的玩具代码来探索 IDE 的另一个方面,这对一些人来说是使用 IDE 的一个重要原因:组织复杂的代码库。你不会希望我在这本书上放一个完整的宠物商店示例来达到这一点,对吧? ;-)

因此,让我们假设我引入的复杂性(主要是通过装饰器公开的 UI 注册表)是完全合理的,因为我们正在处理一个稍微复杂的问题。换句话说:让我们推断一下。

关键是,我们知道我们绝大多数的项目不能仅仅局限于一个文件(即一个 Python 模块)。即使是我们非常愚蠢的示例也开始变得难以阅读。而且,当我们意识到我们需要多个模块时,我们也意识到我们需要将我们的模块分组在一个共同的伞下,以保持清晰,表明我们的模块共同形成一个连贯的东西,并降低与其他项目的名称冲突的可能性。因此,如第 7 章所述,Python 对这个问题的解决方案是模块和包。

我们的计划是按以下方式组织代码。所有内容都将放在 hello 包下。核心逻辑,包括语言支持,将放在包本身(即它的 __init__.py 文件中),每个 UI 将放在 hello 包下的独立模块中。 main.py 脚本将保留为命令行入口点。

右键单击项目并选择 新建 ‣ PyDev 包。输入“hello”作为“名称”,然后单击“完成”。PyDev 将创建包并打开其 __init.py__ 文件的编辑器。正如我所说,我们将把核心逻辑移到这个包中,因此这个文件将包含以下代码,从我们之前版本的代码中提取出来

# -*- coding: utf-8 -*-
greetings = dict(en=u'Hello %s!',
                 es=u'Hola %s!',
                 fr=u'Bonjour %s!',
                 pt=u'Alò %s!')

class LanguageNotSupportedException(ValueError):
    pass

class UINotSupportedExeption(ValueError):
    pass

uis = {}
def register_ui(ui_name):
    def decorator(f):
        uis[ui_name] = f
        return f
    return decorator

def message(ui, msg):
    '''
    Displays the message `msg` via the specified UI which has to be
    previously registered.
    '''
    if ui in uis:
        uis[ui](msg)
    else:
        raise UINotSupportedExeption(ui)

def list_uis():
    return uis.keys()

def greet(name, lang, ui):
    '''
    Greets the person called `name` using the language `lang` via the
    specified UI which has to be previously registered.
    '''
    if lang not in greetings:
        raise LanguageNotSupportedException(lang)
    message(ui, greetings[lang] % name)

请注意,我接受了模块化代码的想法,提供异常以在调用问候者时通知客户端问题,而不是直接在标准输出上打印消息。

现在我们将创建包含控制台 UI 的 hello.console 模块。右键单击项目,选择 新建 ‣ PyDev 模块,输入“hello”作为“包”和“console”作为“名称”。如果您右键单击包而不是项目,则可以避免输入包名称。单击“完成”并将 print_message 函数复制到那里

from hello import register_ui

@register_ui('console')
def print_message(msg):
    print msg

同样,在 hello 包中创建 window 模块,并将 show_message_as_window 的代码放在那里

from javax.swing import JFrame, JLabel
from hello import register_ui

@register_ui('window')
def show_message_as_window(msg):
    frame = JFrame(msg,
                   defaultCloseOperation=JFrame.EXIT_ON_CLOSE,
                   size=(100, 100),
                   visible=True)
    frame.contentPane.add(JLabel(msg))

最后,我们旧的 main.py 的代码略微重塑为以下内容

import sys
import hello, hello.console, hello.window
from optparse import OptionParser

def main(args):
    parser = OptionParser()
    parser.add_option('--ui', dest='ui', default='console',
                      help="Sets the UI to use to greet the user. One of: %s" %
                      ", ".join("'%s'" % ui for ui in list_uis()))
    parser.add_option('--lang', dest='lang', default='en',
                      help="Sets the language to use")
    options, args = parser.parse_args(args)
    if len(args) < 2:
        print "Sorry, I can't greet you if you don't say your name"
        return 1
    try:
        hello.greet(args[1], options.lang, options.ui)
    except hello.LanguageNotSupportedException:
        print "Sorry, I don't speak '%s'" % options.lang
        return 1
    except hello.UINotSupportedExeption:
        print "Invalid UI name\n"
        print "Valid UIs:\n\n" + "\n".join(' * ' + ui for ui in hello.list_uis())
        return 1
    return 0

if __name__ == "__main__":
    main(sys.argv)

提示

到目前为止,我们一直在使用 PyDev 的向导来创建新的模块和包。但是,正如您在第 7 章中看到的,模块只是位于 sys.path 上或包内的 .py 扩展名的文件,而包只是碰巧包含 __init__.py 文件的目录。因此,如果您不喜欢向导,您可能希望使用 新建 ‣ 文件 创建模块,使用 新建 ‣ 文件夹 创建包。

现在我们的代码已拆分为多个文件。在一个小型项目中,使用左侧项目树(称为“PyDev 包资源管理器”)浏览它并不困难,但您可以想象,在一个包含数十个文件的项目中,这将很困难。因此,我们将看到一些简化代码库导航的方法。

首先,假设您正在阅读 main.py 并想跳转到 hello.greet 函数的定义,该函数在第 17 行调用。而不是手动更改到该文件并扫描直到找到该函数,只需按 Ctrl 并单击 greet。PyDev 将自动将您移动到定义。也适用于大多数变量和模块(例如,在导入语句上尝试一下)。

另一种快速在文件之间跳转的好方法是使用 Ctrl + Shift + R,这是“打开资源”的快捷键。只需键入要跳转到的文件名(部分)即可,PyDev 将在您打开的项目的每个包和目录中搜索。

现在您有了很多文件,请注意,您不必一定打开并激活要运行的文件。对于您运行的每个脚本(使用您需要编辑程序然后按 Ctrl + F11 的过程),IDE 将记住该脚本是您感兴趣运行的脚本,并将将其添加到“运行历史记录”中。您可以在主菜单的 运行 -> 运行历史记录 下访问“运行历史记录”,或者在主工具栏中位于绿色“播放”图标旁边的下拉按钮中访问。在这两个地方,您都会找到您最近运行的程序,并且很多时候,使用此列表并选择要重新运行的脚本比跳转到编辑器中的脚本然后按 Ctrl + F11 更方便。

最后,IDE 在内部记录了您在文件之间“跳转”的历史记录,就像 Web 浏览器记录您访问的网页一样。与 Web 浏览器一样,您可以向前和向后浏览。为此,请使用工具栏上的相应按钮或默认快捷键,即 Ctrl + LeftCtrl + Right

测试

好的,现在是时候探索我们的测试代码选项了,而无需诉诸我们一直在做的繁琐的手动黑盒测试,即更改命令行参数并观察输出。

PyDev 支持从 IDE 运行 PyUnit 测试,因此我们将编写它们。让我们在 hello 包中创建一个名为 tests 的模块,其中包含以下代码

import unittest
import hello

class UIMock(object):
    def __init__(self):
        self.msgs = []
    def __call__(self, msg):
        self.msgs.append(msg)

class TestUIs(unittest.TestCase):
    def setUp(self):
        global hello
        hello = reload(hello)
        self.foo = UIMock()
        self.bar = UIMock()
        hello.register_ui('foo')(self.foo)
        hello.register_ui('bar')(self.bar)
        hello.message('foo', "message using the foo UI")
        hello.message('foo', "another message using foo")
        hello.message('bar', "message using the bar UI")

    def testBarMessages(self):
        self.assertEqual(["message using the bar UI"], self.bar.msgs)

    def testFooMessages(self):
        self.assertEqual(["message using the foo UI",
                          "another message using foo"],
                          self.foo.msgs)
    def testNonExistentUI(self):
        self.assertRaises(hello.UINotSupportedExeption,
                          hello.message, 'non-existent-ui', 'msg')

    def testListUIs(self):
        uis = hello.list_uis()
        self.assertEqual(2, len(uis))
        self.assert_('foo' in uis)
        self.assert_('bar' in uis)

如您所见,测试涵盖了将消息分派到不同 UI 的功能。PyDev 的一个很好的功能是自动发现测试,因此您无需编写任何其他代码即可运行上述测试。只需右键单击 Package Explorer 中的 src 文件夹,然后选择 Run As ‣ Jython unit-test。您将在控制台上几乎立即看到测试的输出。

Finding files...
['/home/lsoto/eclipse3.5/workspace-jythonbook/LearningPyDev/src/'] ... done
Importing test modules ... done.

testBarMessages (hello.tests.TestUIs) ... ok
testFooMessages (hello.tests.TestUIs) ... ok
testListUIs (hello.tests.TestUIs) ... ok
testNonExistentUI (hello.tests.TestUIs) ... ok

----------------------------------------------------------------------
Ran 4 tests in 0.064s

OK

Python 的 unittest 不是 Python 世界中唯一的测试选项。一种方便的方法是使用 doctest 进行测试,这种测试比单元测试更像黑盒测试,但同样是自动化的。

注意

我们将在第 19 章中更详细地介绍测试工具,因此如果您感到过于迷茫,请查看该章。

doctest 的优点是它们看起来像与解释器的交互式会话,这使得它们非常易读且易于创建。我们将使用 doctest 测试我们的控制台模块。

首先,单击控制台工具栏最右边的按钮(您会认出它,它在左上角有一个加号,当您将鼠标悬停在其上时,它会有“打开控制台”的提示)。从菜单中选择“PyDev 控制台”。在下一个对话框中,回答“Jython 控制台”。完成此操作后,您将在 IDE 中获得一个嵌入式交互式解释器。

让我们开始使用解释器探索我们自己的代码

>>> from hello import console
>>> console.print_message("testing")
testing

我强烈建议您自己输入这两个命令。您会注意到代码完成也适用于交互式解释器!

回到主题,我们只是交互式地检查了我们的控制台模块按预期工作。最酷的事情是,我们可以将这段代码片段复制粘贴为 doctest,它将用于自动检查我们刚刚测试的行为在将来是否保持不变。

hello 包中创建一个名为 doctests 的模块,并将交互式控制台中的这三行粘贴进去,用三个引号将它们包围起来(因为它们毕竟不是语法上正确的 Python 代码)。在添加一些样板代码以使该文件可执行后,它将如下所示

"""
>>> from hello import console
>>> console.print_message("testing")
testing
"""

if __name__ == "__main__":
    import doctest
    doctest.testmod(verbose=True)

完成此操作后,您可以通过 Run ‣ Jython run 菜单运行此测试,同时 doctests.py 是编辑器中当前活动的文件。如果一切顺利,您将获得以下输出

Trying:
    from hello import console
Expecting nothing
ok
Trying:
    console.print_message("testing")
Expecting:
    testing
ok
1 items passed all tests:
   2 tests in __main__
2 tests in 1 items.
2 passed and 0 failed.
Test passed.

运行 doctest 后,您会注意到您的交互式控制台消失了,取而代之的是显示测试结果的输出控制台。要返回交互式控制台,请在控制台选项卡工具栏中查找控制台按钮,它正好位于您用来生成控制台的按钮的左侧。然后,在下拉菜单中选择“PyDev 控制台”,如下一张图片所示。

Bringing back the interactive console.

如您所见,您可以使用交互式控制台来玩弄您的代码,尝试想法并测试它们。之后,只需从同一个交互式控制台会话中复制粘贴文本,就可以进行简单的测试。特别值得注意的是,由于 Jython 代码可以非常轻松地访问 Java API,因此您也可以用这种方式测试用 Java 编写的类!

将 Java 库添加到项目

最后,我将向您展示如何将 Java 库集成到您的项目中。在几页之前测试命令行开关时,我暗示我们可以为我们的问候程序提供一个“语音”界面。毕竟,这听起来并不像个坏主意,因为(就像几乎所有方面一样)Java 世界拥有良好的库来解决这个问题。

我们将使用 FreeTTS 库,可以从 http://freetts.sourceforge.net/docs/index.php 下载。(您应该下载二进制版本)

下载 FreeTTS 后,您需要将存档解压缩到硬盘上的某个位置。然后,我们将从 FreeTTS 中导入一个 JAR 文件到我们的 PyDev 项目中。

右键单击项目并选择“导入...”。然后选择 常规 ‣ 文件系统 并浏览到您解压缩 FreeTTS 的目录并选择它。最后,展开左侧面板上的目录并选中 lib 子目录。请参考下图。

Importing ``freetts.jar`` into the PyDev Project

单击完成之后,您将看到该文件现在已成为项目的一部分。

提示

或者,根据您的操作系统,可以通过从文件管理器中复制文件夹并将其粘贴到项目中来执行相同的操作(通过菜单、键盘快捷键或拖放)。

现在,该文件已成为项目的一部分,但我们需要告诉 PyDev 该文件是一个 JAR 文件,应该添加到我们项目环境的 sys.path 中。为此,右键单击项目并选择“属性”。然后在对话框的左侧面板中选择“PyDev - PYTHONPATH”。然后单击“添加 zip/jar/egg”按钮,并在弹出的对话框右侧选择 lib/freetts.jar 文件。在两个对话框中都单击“确定”,您就可以从 Python 代码中使用此库了。

我们新的 hello.speech 模块的代码如下

from com.sun.speech.freetts import VoiceManager
from hello import register_ui

@register_ui('speech')
def speech_message(msg):
    voice = VoiceManager().getVoice("kevin16")
    voice.allocate()
    voice.speak(msg)
    voice.deallocate()

如果您在编辑器中使用代码,您会注意到 PyDev 还为引用我们正在使用的 Java 库的导入语句提供代码补全功能。

最后,我们将 main.py 的第二行从

import hello, hello.console, hello.window

更改为

import hello, hello.console, hello.window, hello.speech

以便也加载语音 UI。请随意打开扬声器并使用 --ui speech 选项让计算机向您和您的朋友问好!

就这样,我们简陋的问候程序终于进化成一个相当有趣、可移植的程序,具有语音合成功能。它仍然只是一个玩具,但它展示了使用 Jython 的强大功能、Java 的多样性以及 IDE 的帮助,您可以快速行动。

其他主题

我已经涵盖了大多数 PyDev 功能,但我遗漏了一些功能。在结束这个专门介绍 PyDev 的半章之前,我们将看看我们错过了什么。

调试

PyDev 为您的 Jython 代码提供了完整的调试功能。要尝试它,只需在编辑器的左侧边距上双击,在您的代码中设置一些断点,然后使用 F11 快捷键而不是 Ctrl + F11 启动程序。

调试器命中您的断点后,IDE 会要求您更改其透视图。这意味着它将更改为更适合调试活动的布局。对该对话框回答“是”,您将发现自己处于调试透视图中,它看起来像下图

PyDev's Jython Debugger.

简而言之,该透视图提供了调试器的典型元素:调用堆栈、调用堆栈每个帧的变量、断点列表,以及“单步进入”(F5)、“单步跳过”(F6)和“恢复执行”(F8)等功能。

完成调试会话后,您可以通过选择主 IDE 窗口右上角的“PyDev”(在调试透视图中,该按钮将显示“调试”按钮)返回到正常的编辑透视图。

重构

PyDev 还提供了一些基本的重构功能。其中一些功能仅限于 CPython,但另一些功能,如“提取方法”,在 Jython 项目中也能正常工作。我鼓励您尝试一下,看看它们是否适合您的工作方式。有时您可能更喜欢手动重构,因为任务往往不像在 Java(或任何其他没有类型推断的静态类型语言)中那样痛苦。另一方面,当 IDE 能够为您做正确的事情并避免一些机械工作时,您将更加高效。

(半)结论

PyDev 是 Eclipse 平台上一个非常成熟的插件,可以成为你工具箱中的重要元素。自动完成和建议在学习新 API(无论是 Python API 还是 Java API!)时非常有用,特别是与交互式控制台配合使用时。它也是将整个团队引入 Jython 或特定 Jython 项目的好方法,因为项目级别的配置可以通过正常的源代码控制系统共享。更不用说来自 Java 世界的程序员会发现自己在一个熟悉的环境中更加舒适。

对我来说,IDE 是我工具箱中一个有用的部分,并且在大型代码库和/或我还不完全理解的复杂代码上往往表现出色。强大的导航和重构功能是理解此类项目的关键,并且是未来应该不断改进的功能。

最后,PyDev 的调试功能非常出色,它将结束你使用 print 作为穷人调试器(说真的,我这样做了一段时间!)的日子。即使是掌握了 import pdb; pdb.set_trace() 艺术的更高级的 Python 用户也应该尝试一下。

现在,这是一个“半结论”,因为 PyDev 不是 Jython 可用的唯一 IDE。如果你已经在使用 Netbeans IDE,或者出于某种原因不喜欢 Eclipse 或 PyDev,请查看本章的其余部分,我们将介绍用于 Python 开发的 Netbeans 插件。

Netbeans

Netbeans 集成开发环境已经为 Java 社区服务了十多年。在此期间,该工具从最初的普通 Java 开发工具发展成为今天用于 Java 和其他语言的先进开发和测试环境。由于 Java 和 JavaEE 应用程序开发仍然是该工具不可或缺的一部分,因此 JRuby、Jython、Groovy 和 Scala 等其他语言也获得了在该工具中的地位。这些语言中的大多数都作为核心开发环境的插件得到支持,这使得 Netbeans 成为一个易于扩展的 IDE,因为它非常容易构建额外的功能以进行分发。Netbeans 中的 Python 支持最初是一个名为 nbPython 的小型插件,但它已发展成为一个功能齐全的 Python 开发环境,并且仍在不断发展。

Netbeans Python 支持为开发人员提供了所有预期的 IDE 功能,例如代码完成、颜色编码和轻松的运行时开发。它还包含一些用于调试应用程序等方面的不错的高级功能。

IDE 安装和配置

安装 Netbeans Python 开发环境的第一步是下载 Netbeans IDE 的当前版本。在撰写本文时,Netbeans 6.7 是最新的版本,实际上是刚发布的。你可以通过访问网站 http://www.netbeans.org 并点击下载链接来找到 IDE 下载。一旦你这样做,你将看到许多不同的下载选项。这些是 IDE 的变体,专注于为开发人员提供不同的功能,具体取决于他们将使用最多的功能。没有人想要一个笨重、占用内存的开发工具,它会极大地占用计算机。通过提供 IDE 的几种不同的配置,Netbeans 让你可以选择省略额外的功能,只安装对你的开发至关重要的部分。IDE 的不同版本包括 Java SE、Java、Ruby、C/C++、PHP 和 All。对于那些只对开发核心 Java 应用程序感兴趣的开发人员来说,Java SE 下载就足够了。同样,任何对其他语言感兴趣的人都可以下载特定于该语言的 IDE 配置。出于本书的目的,以及在我的日常开发中,我使用“All”选项,因为我喜欢所有选项都可用。但是,如果你只下载了 Java SE 或其他低配置版本,并且希望稍后添加更多功能,则可以使用其他选项来添加功能。

在撰写本文时,下载页面顶部附近还有一个 PythonEA 分发的链接。如果该链接或类似的 Python Netbeans 分发链接可用,则可以使用它来下载和安装 Netbeans IDE 的 Jython 特定功能。我绝对不建议采取这种方法,除非你计划只编写 Jython 应用程序。在我看来,Jython 开发人员社区中很大一部分人也会编写一些 Java 代码,甚至可能在他们的应用程序中集成 Java 和 Jython。如果是这种情况,你将希望使用 Netbeans 的 Java 特定功能。这就是我不建议 Jython 开发人员使用仅 Python 的分发的原因,但你可以选择。

现在您已经获得了 IDE,可以使用直观的 NetBeans 安装程序轻松地在任何环境中安装它。也许使用新 IDE 时最令人生畏的任务是根据您的需求对其进行配置。不过,对于 NetBeans 来说,情况并非如此,因为 Java 和 Python 的配置都非常简单。例如,如果您使用的是功能齐全的安装程序,您将可以使用 NetBeans 默认安装的 Glassfish 应用程序服务器。请注意,在安装后尽快更改管理员密码是一个明智的做法,以避免任何潜在的尴尬的安全问题。

当 IDE 首次打开时,您会看到一个主窗口,其中包含指向有关 NetBeans 功能的博客和文章的链接。您还可以使用标准菜单项,例如文件、编辑、工具等。在本章中,我们将专门介绍 Jython 功能的配置和使用,但是,在线和书籍格式中提供了非常有用的教程,用于介绍其他 NetBeans 功能。您应该注意的一点是,在初始安装中,除非您选择安装 *PythonEA* 发行版,否则 Python/Jython 开发工具尚未安装。假设您已安装完整的 NetBeans 发行版,您需要通过 NetBeans 插件中心添加 Python 插件。您需要转到 *工具* 菜单,然后打开 *插件* 子菜单。从那里,您应该选择 *可用插件* 选项卡并按类别排序。选择 *Python* 类别中的所有插件,然后安装。此选项将安装 Python 插件以及 Jython 发行版。您需要按照屏幕上的说明完成安装。

插件成功安装后,就该配置您的 Python 和 Jython 主目录了。为此,请转到 *工具* 菜单,然后打开 *Python 平台* 菜单,这将打开 Python/Jython 的平台管理器。在撰写本文时,与 Python 插件一起安装的默认 Jython 版本为 2.5b0+,即使 2.5.0 最终版已经发布。鉴于这种情况,请继续将您的 Jython 2.5.0 最终版安装添加为平台选项,并将其设为默认值。

Netbeans Python Platform Manager.

为此,请单击平台列表下方的 *新建* 按钮。您可以尝试选择 *自动检测* 选项,但我没有使用它来让 NetBeans 找到我的 Jython 2.5.0 最终版安装。如果您选择 *新建* 按钮,则会看到一个文件选择器窗口。您应该选择位于 <JYTHON_HOME>/bin 区域的 Jython 可执行文件,所有其他必要字段将自动填充正确的值。完成后,选择 *Python 平台管理器* 窗口底部的 *关闭* 按钮。现在,您已准备好开始在 NetBeans 中使用 Python 和 Jython 进行编程了。

高级 Python 选项

如果您进入 NetBeans 首选项窗口,您会发现一些更高级的选项,用于自定义您的 Python 插件。如果您转到 *编辑器* 选项卡,您可以为格式化、代码模板和提示设置 Python 特定选项。通过这样做,您可以完全自定义 NetBeans 显示代码的方式,并在使用 Jython 时提供帮助。您还可以选择通过选择 *字体和颜色* 选项卡来设置 Python 代码的不同字体和颜色。这仅仅是 NetBeans 可定制性的一个例子,因为您可以为每种语言类型设置不同的字体和颜色。

如果您选择杂项选项卡,则可以将不同类型的文件添加到 Netbeans IDE 并将其与不同的 IDE 功能相关联。如果您查看文件的下拉菜单,您会发现扩展名为pypyc的文件与 Python 文件相关联。这确保了具有关联扩展名的文件将使用其指定的 Netbeans 功能。例如,如果我们想将扩展名为jy的 Jython 文件指定为 Python 文件,我们可以轻松地做到这一点,并将此扩展名与 Netbeans 中的 Python 文件相关联。一旦我们建立了这种关联,我们就可以创建扩展名为jy的文件,并在 Netbeans 中使用它们,就像它们是 Python 文件一样。最后,您可以更改一些基本选项,例如启用提示 Python 程序参数,以及从 Netbeans 首选项中的Python选项卡更改调试器端口和 shell 颜色。

一般 Jython 用法

如本章前面所述,使用 Netbeans Python 解决方案时,有多种选择。在创建新的 Jython 项目时,可以进行一些不同的选择。您可以选择创建Python 项目具有现有源代码的 Python 项目。这两种项目类型命名非常恰当,因为Python 项目将创建一个空项目,并且

创建后,很容易开发和维护应用程序和脚本。此外,您可以调试应用程序,并让 Netbeans 创建测试(如果您选择这样做)。您会注意到的第一个不错的功能之一是编辑器中的语法着色。还提供通过调试进行的测试

独立 Jython 应用程序

在本节中,我将讨论如何在 Netbeans 中开发独立的 Jython 应用程序。我们将使用我在本书其他地方使用过的标准HockeyRoster应用程序的变体。总的来说,在 Netbeans 中开发独立的 Jython 应用程序与开发独立的 Java 应用程序几乎没有区别。主要区别在于您将拥有不同的项目属性和其他选项,这些选项与创建 Jython 相关。显然,您将使用 Jython 源文件进行开发,以及 Python 插件提供的所有颜色编码和代码完成功能。

要开始,请使用文件菜单或 Netbeans 工具栏中的快捷方式创建一个新的 Python 项目。在本节中,将新项目命名为HockeyRoster。取消选中创建主文件选项,因为我们将手动执行此操作。创建项目后,通过右键单击(ctrl-单击)项目名称来探索您可用的选项。生成的菜单应允许您创建新文件、运行、调试或测试应用程序、构建 egg、使用代码覆盖率等。此时,您还可以通过选择以...方式查看 Python 包选项来更改 Netbeans 中 Python 包的视图。这将允许您选择以列表模式查看应用程序,这取决于您的偏好。您可以使用查找选项搜索代码,使用集成的 Netbeans Kenai 支持将其共享到 Kenai,查看本地文件历史记录,或将代码与版本控制系统一起使用。单击属性选项,将出现项目属性窗口。在项目属性窗口中,左侧列出了包括Python运行格式在内的选项。选项提供更改源位置或向项目添加新源位置的功能。此选项中的测试根文件夹部分允许您添加 Python 测试所在的目录,以便您可以将它们与项目一起使用。Python选项允许您更改 Python 平台,并将位置、JAR 和文件添加到 Python 路径。更改 Python 平台提供了一种方便的功能,可以测试您在 Jython 和 Python 上的程序,如果您想确保您的代码在每个平台上都能正常工作。运行选项提供添加或更改模块以及添加应用程序参数的功能。最后,格式选项允许您为 Netbeans 中的此特定项目指定不同的格式选项。这很棒,因为每个不同的项目都可以根据所选选项具有不同的彩色文本等。

此时,使用文件,然后新建下拉菜单,右键单击(cntrl-单击)项目,或使用工具栏图标来创建HockeyRoster应用程序的模块。从这里,您可以创建可执行模块、模块、空模块、Python 包或单元测试。选择创建可执行模块,并将主文件命名为HockeyRoster.py,并记住,在创建项目时,我们有能力让 IDE 为我们生成此文件,但我们选择拒绝。就我个人而言,我喜欢使用 Python 打包系统来组织我的项目。现在使用与创建文件相同的过程创建一些包,并将包命名为org。在第一个包中添加另一个包,并将其命名为jythonbook。创建后,将HockeyRoster.py模块拖到jythonbook包中以将其移动到适当位置。请注意,您还可以通过命名类似于org.jythonbook的包来同时创建多个包,这将创建两个生成的包。

HockeyRoster.py 主模块将是应用程序的实现模块,但我们仍然需要一个地方来存储每个球员的信息。为此,我们将创建一个名为 Player.py 的类对象容器。继续在同一个 jythonbook 包中创建一个名为 Player 的“空模块”。现在我们将为我们的项目编写 Player 类。为此,请删除 Netbeans 在 Player.py 模块中自动生成的代码,并键入以下内容。请注意,您可以通过更改 Python 应用程序的模板来更改生成新文件时创建的默认代码。

# Player.py
#
# Class container to hold player information


class Player:

    # Player attributes

    id = 0
    first = None
    last = None
    position = None
    goals = 0
    assists = 0


    def create(self, id, first, last, position):
        self.id = id
        self.first = first
        self.last = last
        self.position = position

    def set_goals(self, goals):
        self.goals = goals

    def add_goal(self):
        self.goals = goals + 1

    def get_goals(self):
        return self.goals

    def set_assists(self, assists):
        self.assists = assists

    def add_assist(self):
        self.assists = assists + 1

    def get_assists(self):
        return self.assists

首先要注意的是,Netbeans 会维护您的缩进级别。使用 SHIFT + TAB 键盘快捷键也很容易向后制表。使用默认环境设置,关键字应与其他代码具有不同的颜色(默认情况下为蓝色)。方法名称将以粗体显示,对 self 或变量的引用也将以不同的颜色显示。您应该注意到一些代码补全,主要是您键入方法名称然后右括号后自动放置的 self。其他细微的代码补全功能也有助于使我们的开发生活更轻松。如果您犯了错误,无论是缩进还是其他错误,您都会在错误附近看到红色下划线,以及编辑器左侧行号上的红色错误徽章。如果您将鼠标悬停在红色错误徽章或下划线上,Netbeans 会为您提供一些帮助来确定错误的原因。

现在我们已经编写了独立 Jython 应用程序中的第一个类,是时候看看实现代码了。HockeyRoster.py 模块是我们花名册应用程序的核心,因为它控制着对团队的操作。我们将使用 shelve 技术将我们的 Player 对象存储到花名册应用程序的磁盘中。从下面的代码中可以看出,这是一个非常基本的应用程序,与下一章中使用 Hibernate 持久性将找到的实现非常相似。

# HockeyRoster.py
#
# Implemenatation logic for the HockeyRoster application

# Import Player class from the Player module

from Player import Player
import shelve
import sys

# Define a list to hold each of te Player objects
playerList = []
factory = None

# Define shelve for storage to disk
playerData = None

# makeSelection()
#
# Creates a selector for our application.  The function prints output to the
# command line.  It then takes a parameter as keyboard input at the command line
# in order to choose our application option.

def makeSelection():
    validOptions = ['1','2','3','4','5']
    print "Please chose an option\n"

    selection = raw_input("Press 1 to add a player, 2 to print the roster, 3 to search for a player on the team, 4 to remove player, 5 to quit: ")
    if selection not in validOptions:
        print "Not a valid option, please try again\n"
        makeSelection()
    else:
        if selection == '1':
            addPlayer()
        elif selection == '2':
            printRoster()
        elif selection == '3':
            searchRoster()
        elif selection == '4':
            removePlayer()
        else:
            print "Thanks for using the HockeyRoster application."

# addPlayer()
#
# Accepts keyboard input to add a player object to the roster list.  This function
# creates a new player object each time it is invoked and appends it to the list.
def addPlayer():
    addNew = 'Y'
    print "Add a player to the roster by providing the following information\n"
    while addNew.upper() == 'Y':
        first = raw_input("First Name: ")
        last = raw_input("Last Name: ")
        position = raw_input("Position: ")

        id = returnPlayerCount() + 1
        print id
        #set player and shelve
        player = Player(id, first, last, position)
        playerData[str(id)] = player


        print "Player successfully added to the roster\n"
        addNew = raw_input("Add another? (Y or N)")
    makeSelection()

# printRoster()
#
# Prints the contents of the list to the command line as a report
def printRoster():
    print "====================\n"
    print "Complete Team Roster\n"
    print "======================\n\n"
    playerList = returnPlayerList()
    for player in playerList.keys():
        print "%s %s - %s" % (playerList[player].first, playerList[player].last, playerList[player].position)
    print "\n"
    print "=== End of Roster ===\n"
    makeSelection()

# searchRoster()
#
# Takes input from the command line for a player's name to search within the
# roster list.  If the player is found in the list then an affirmative message
# is printed.  If not found, then a negative message is printed.
def searchRoster():
    index = 0
    found = False
    print "Enter a player name below to search the team\n"
    first = raw_input("First Name: ")
    last = raw_input("Last Name: ")
    position = None
    playerList = returnPlayerList()
    for playerKey in playerList.keys():
        player = playerList[playerKey]
        if player.first.upper() == first.upper() and player.last.upper() == last.upper():
            found = True
            position = player.position
    if found:
        print '%s %s is in the roster as %s' % (first, last, position)
    else:
        print '%s %s is not in the roster.' % (first, last)
    makeSelection()

def removePlayer():
    index = 0
    found = False
    print "Enter a player name below to remove them from the team roster\n"
    first = raw_input("First Name: ")
    last = raw_input("Last Name: ")
    position = None
    playerList = returnPlayerList()
    for playerKey in playerList.keys():
        player = playerList[playerKey]
        if player.first.upper() == first.upper() and player.last.upper() == last.upper():
            found = True
            foundPlayer = player
    if found:
        print '%s %s is in the roster as %s, are you sure you wish to remove?' % (foundPlayer.first, foundPlayer.last, foundPlayer.position)
        yesno = raw_input("Y or N")
        if yesno.upper() == 'Y':
                # remove player from shelve
                print 'The player has been removed from the roster', foundPlayer.id
                del(playerData[str(foundPlayer.id)])
        else:
            print 'The player will not be removed'
    else:
        print '%s %s is not in the roster.' % (first, last)
    makeSelection()

def returnPlayerList():
    playerList = playerData
    return playerList

def returnPlayerCount():
    return len(playerData.keys())


# main
#
# This is the application entry point.  It simply prints the applicaion title
# to the command line and then invokes the makeSelection() function.
if __name__ == "__main__":
    print sys.path
    print "Hockey Roster Application\n\n"
    playerData = shelve.open("players")
    makeSelection()

在本书的这一点上,代码应该相对容易理解。main 函数按预期启动进程,如您所见,它要么创建要么获取对存储花名册的 shelve 或字典的引用。一旦发生这种情况,处理就会转发到驱动程序的 makeSelection() 函数。这里要注意的重要一点是,使用 Netbeans 时,代码布局整齐,代码补全将帮助导入和完成各种代码块。要运行您的程序,您可以右键单击(CTRL+CLICK)项目,或者将项目设置为 Netbeans 中的主项目,并使用工具栏或下拉菜单。如果一切设置正确,您应该看到程序输出显示在 Netbeans 输出窗口中。您可以像使用终端一样与输出窗口进行交互。

Jython 和 Java 集成应用程序

为了避免重复 Jython 和 Java 在应用程序中可以混合使用的不同方式,本节将重点介绍如何在 Netbeans IDE 中进行混合使用。为了执行集成,可以采取多种方法,因此本节不会涵盖所有方法。但是,目标是为您提供一些在 Netbeans 中开发集成 Jython 和 Java 应用程序时使用的指南和示例。

在您的 Jython 应用程序中使用 JAR 或 Java 项目

在 Jython 应用程序中使用 Java 就是关于导入和确保您的类路径中有必要的 Java 类文件和/或 JAR 文件。为了成功实现此技术,您可以轻松确保 Netbeans 项目能够识别所有必要的文件。因此,本节的重点是使用 Python 项目属性来设置项目的 sys.path。要继续操作,请使用本节前面创建的 HockeyRoster Jython 项目。

假设我们希望向项目添加一些在名为 HockeyIntegration 的 Java 项目中实现的功能,我们正在 Netbeans 中编写该项目。此外,假设 HockeyIntegration Java 项目编译成一个 JAR 文件。为了在我们的 HockeyRoster 项目中使用此项目,您需要通过右键单击您的 Jython 项目并选择 属性选项来打开项目属性。窗口打开后,单击窗口左侧的 Python 菜单项。这将使您可以访问 sys.path,以便您可以添加其他 Python 模块、egg、Java 类、JAR 文件等。单击 添加按钮,然后遍历到您正在开发的 Java 应用程序的项目目录。到达那里后,进入 dist 目录并选择生成的 JAR 文件,然后单击 确定。您现在可以在 Jython 应用程序中使用 Java 项目的任何功能。

如果你有兴趣使用标准 Java 库中存在的 Java API,那么你已经处于非常好的状态。如你所知,Jython 自动提供对整个 Java 标准库的访问。你只需在你的 Jython 应用程序中导入你想要使用的 Java,然后开始使用,无需在 Netbeans 中进行任何特殊设置。在撰写本文时,Netbeans Python EA 还不支持对标准 Java 库的导入完成。但是,我怀疑此功能将在后续版本中添加。

在 Java 中使用 Jython

如果你有兴趣在你的 Java 应用程序中使用 Jython 或 Python 模块,Netbeans 使其变得容易。如第 10 章所述,从 Java 使用 Jython 的最常见方法是使用对象工厂模式。但是,还有其他方法可以做到这一点,例如使用clamp项目,该项目在撰写本文时尚未投入生产。为了便于本节的讨论,我们将讨论如何使用另一个 Netbeans Jython 项目以及其他 Jython 模块,从你的 Java 应用程序中使用对象工厂模式。

为了有效地演示从 Netbeans 中使用对象工厂模式,我们将使用PlyJy项目,该项目提供可以开箱即用的对象工厂实现。如果你还没有这样做,请访问Project Kenai网站,找到PlyJy项目并下载提供的 JAR。我们将在 Java 项目中使用 Netbeans 项目属性窗口将此 JAR 文件添加到我们的项目中。这样做将有效地减少手动编码任何对象工厂实现的要求,并且我们将能够直接在我们的项目中使用 Jython 类。

使用“新建->项目->Java 应用程序”选择创建一个名为ObjectFactoryExample的 Java 项目。完成此操作后,右键单击(CNTRL+CLICK)项目并选择属性。项目属性窗口出现后,单击左侧的选项。从那里,将你之前下载的PlyJy JAR 文件添加到你的项目类路径中。你还需要为你要使用的 Jython 的适当版本添加jython.jar文件。在本例中,我们将使用 Jython 2.5.0 版本。

下一步是确保你想要使用的任何和所有 Jython 模块都在你的 CLASSPATH 中。这可以通过将它们作为常规代码模块添加到你的应用程序中的某个位置,然后进入项目属性窗口,并使用“添加 JAR/文件夹”按钮将该目录包含在部分中的“编译时库”列表中来轻松完成。虽然此步骤可能看起来没有必要,因为模块已经是项目的一部分,但必须执行此步骤才能将它们放入你的 CLASSPATH 中。成功将它们添加到 CLASSPATH 后,你就可以通过对象工厂模式开始使用它们。Netbeans 将无缝地使用你的应用程序中的模块,就好像所有代码都是用同一种语言编写的。

开发 Web 应用程序(Django 等)

截至撰写本文时,Netbeans 在框架方面对开发 Jython Web 应用程序的支持非常有限。使用 Jython 开发简单的 servlet 和/或 applet 很容易,只需创建一个常规的 Web 应用程序并进行相应的设置即可。但是,截至版本 6.7,无法在 Netbeans 中充分利用 Django 等框架。在 Django 插件成为 Netbeans 7 版本的一部分方面存在许多传言和讨论,但也许这将在本书的未来版本中介绍。在此期间,我们需要以 Netbeans 的当前形式使用它,而没有专门针对 Jython Web 开发的插件。虽然存在一些障碍,并且无法在工具中使任何框架完全正常工作,但有一些不错的技巧可以用来允许在 Netbeans 中执行的 Jython Web 开发。

为了在 Netbeans 中部署标准 Web 应用程序并使用 Jython servlet 和/或 applet,只需创建一个标准 Web 应用程序,然后以标准 servlet 或 applet 的方式编写 Jython 代码即可。由于没有插件支持这项工作,所以所有操作都是手动进行的。虽然没有向导帮助您编写 *web.xml* 配置,但我相信利用精细的代码补全和语义代码着色是一个不错的优势。由于没有向导来帮助我们,我们只会提到 Netbeans 通过利用 IDE 的功能,而不是抽象化编码并完成向导,从而使标准 Web Jython Web 开发变得更容易。

在 Netbeans 中使用 Django

如本节开头所述,如果您希望在 Netbeans 中使用标准框架开发 Jython Web 应用程序,这不是一项非常直接的任务。但是,通过一些额外的配置和一些手动操作,这很容易做到。在本节中,我将演示如何在不使用任何超出标准 Python 支持的 Netbeans 插件的情况下,使用 Netbeans 开发 Django 应用程序。您将看到,Jython 应用程序可以在 IDE 中运行、测试和验证,只需很少的工作。由于本节中有一些步骤可能难以直观地理解,如果您在阅读本文时没有使用 Netbeans,请使用提供的屏幕截图进行参考。

为了有效地创建和维护 Django 网站,您需要能够对 *manage.py* 执行命令。不幸的是,IDE 中没有内置的简单方法来做到这一点,因此我们必须使用终端或命令行以及 IDE 来完成这些操作。一旦我们在 Netbeans 中创建了项目并将其设置好,我们就可以在 Netbeans 中进行开发,您还可以设置项目的 *运行* 功能来启动 Django 服务器。

假设您已经在机器上设置并配置了 Django 以及 Django-Jython 项目,在 Netbeans 中使用 Django 项目的第一步实际上是创建项目。如果您正在使用已经创建的 Django 项目,则可以跳过此步骤,但如果不是,则需要转到终端或命令行,并使用 *django-admin.py* 创建项目。在本教程中,我们将我们的 Django 网站命名为 *NetbeansDjango*。

django-admin.py startproject NetbeansDjango

现在我们应该已经设置了默认的 Django 网站,并且可以将其导入 Netbeans。为此,请使用 *带有现有源代码的 Python 项目* 选项在 Netbeans 中启动一个新的 Python 项目,并确保将您的 Python 平台设置为 Jython 2.5.0,以便我们使用 Jython。点击 *下一步* 按钮后,我们可以向项目添加源代码。点击 *添加* 按钮,然后选择主项目文件夹,在本例中选择 *NetbeansDjango* 文件夹。这将添加我们的项目根目录作为应用程序的源代码根目录。反过来,它将我们的 Django 设置文件(如 *manage.py*)添加到我们的项目中。完成此操作后,您的项目应该类似于以下屏幕截图。

Sample Netbeans Django Project.

在下一步中,我们将配置 Netbeans 项目的 *运行* 选项,以便它为我们启动 Django Web 服务器。为此,请右键单击 (CNTRL+CLICK) 新创建的项目,然后转到 *属性*。从那里,在左侧菜单中选择 *Python* 选项,并将 Django 目录(包含 bin、conf、contrib、core 等文件)添加到您的路径中。在本教程中,我们还将使用 Postgresql 数据库,因此您还需要将 *postgresql.jar* 添加到您的 Python 路径中。

Netbeans Django Sample Project Path.

接下来,从左侧菜单中选择 *运行* 选项,并将 *manage.py* 添加为主模块,并将 *runserver* 添加为应用程序参数。这将实质上将 *运行* 项目选项连接到 Django 的 *manage.py*,以便它调用 Django Web 服务器启动。

Netbeans Django Run Options.

现在,我们已经准备好开始开发 Django 应用程序了。通过一些简单的设置和使用终端或命令行,我们就可以轻松地使用 Netbeans 开发 Django 项目。不过,这个过程中有一些小问题,需要注意的是,目前还没有真正集成的关闭 Web 服务器的方法,所以一旦启动,我们只能让它一直运行,或者通过系统进程管理器停止它。否则,您可以将不同的选项连接到 Netbeans 的 *运行* 项目命令,例如 *syncdb*,只需在项目属性中选择不同的应用程序参数即可。如果您使用这种方法,那么您可以像往常一样通过终端启动和停止 Django Web 服务器。我还发现,在运行 Django Web 服务器后,您必须手动删除生成的 *settings$.py.class* 文件,然后才能再次运行服务器,否则它会报错。

在 Netbeans 的未来版本中,特别是 Netbeans 7 版本,预计 Django 功能将被集成到 Python 支持中。届时,我们将再次研究如何在 Netbeans 中使用 Django。目前,这个方法有效,而且效果很好。您可以使用类似的方法在 Netbeans 中使用其他 Web 框架,例如 Pylons。

结论

与大多数其他编程语言一样,在开发 Jython 时,您有多种 IDE 选择。本章介绍了两种最常用的 Jython 应用程序开发 IDE 选项:Netbeans 和 Eclipse。Eclipse 为开发 Jython 应用程序(包括独立应用程序和基于 Web 的应用程序)提供了真正完整的 IDE 解决方案。加上 Eclipse 的 Django 插件,该 IDE 使您能够轻松地开始 Jython 开发,并管理现有项目。PyDev 正在不断开发中,并且一直在改进,添加新功能并简化现有功能。

截至本文撰写之时,Netbeans 的 Jython 支持仍处于早期开发阶段。许多主要功能,如代码补全和语法着色,已经到位。您可以开发 Jython 应用程序,包括 Jython 和 Java 集成以及基于 Web 的应用程序。在未来,Netbeans 的 Jython 支持将发展到包括更多功能,这些功能将在本书的未来版本中介绍。

在下一章中,我们将研究利用数据库开发一些应用程序。我们将介绍 zxJDBC API,并学习如何开发利用标准数据库事务的 Jython 应用程序。对象关系映射也以各种形式可用于 Jython,我们也将讨论其中许多选项。