78、Jython GUI开发:从基础到实践

Jython GUI开发:从基础到实践

1. Jython GUI开发概述

在Jython中开发图形用户界面(GUI),主要的工具包是Java的抽象窗口工具包(AWT)和Swing类。这是因为Jython用Java编写,能很好地在Python语法中使用Java类。不过,AWT和Swing并非实现GUI的唯一选择,也有越来越多与其他工具包的绑定可供使用,但这里主要关注用Jython编写的AWT和Swing GUI。

2. Java与Jython GUI对比
  • Java GUI示例
    以下是一个简单的Java GUI代码示例:
// file SimpleJavaGUI.java 
import java.awt.*; 
import java.awt.event.*; 

class SimpleJavaGUI implements ActionListener {
    private Button mybutton = new Button("OK"); 
    private Label mylabel = new Label("A Java GUI", Label.CENTER); 
    public SimpleJavaGUI() {
        Frame top_frame = new Frame(); 
        Panel panel = new Panel(); 
        top_frame.setTitle("A Basic Jython GUI"); 
        top_frame.setBackground(Color.yellow); 
        top_frame.addWindowListener(
            new WindowAdapter() {
                public void windowClosing(windowEvent e) {
                    System.exit(0); 
                } 
            }); 
        mybutton.addActionListener(this); 
        panel.add(mylabel); 
        panel.add(mybutton); 
        top_frame.add(panel); 
        top_frame.pack(); 
        Dimension winSz = Toolkit.getDefaultToolkit().getScreenSize(); 
        top_frame.setLocation(winSz.width/2 - top__frame.getWidth()/2, 
                              winSz.height/2 - top__frame.height()/2); 
        top_frame.setVisible(true); 
    } 
    public static void main(String[] args)) {
        SimpleJavaGUI s = new SimpleJavaGUI(); 
    } 
    public void actionPerformed(ActionEvent event) {
        System.exit(0); 
    } 
} 

这个示例展示了如何使用AWT类、bean属性和bean事件来创建一个简单的GUI。

  • Jython GUI示例
    对应的Jython代码如下:
# File: simpleJythonGUI.py 
import java 
from java import awt 
# Make an exit function 
def exit(e): 
    java.lang.System.exit(0) 
#Make root frame and the panel it will contain 
top_frame = awt.Frame(title="A Basic Jython GUI", 
                      background = awt.Color.yellow, 
                      windowClosing=exit) 
panel = awt.Panel() 
#Add stuff to look at inside frame 
mylabel = awt.Label("A Jython GUI", awt.Label.CENTER) 
mybutton = awt.Button("OK", actionPerformed=exit) 
panel.add(mylabel) 
panel.add(mybutton) 
top_frame.add(panel) 
# pack and show 
top_frame.pack() 
# set location 
toolkit = awt.Toolkit.getDefaultToolkit() 
winSz = toolkit.screenSize 
top_frame.setLocation(winSz.width/2 - top__frame.size.width/2, 
                      winSz.height/2 - top__frame.height/2) 
top_frame.visible = 1 

与Java代码相比,Jython代码在语法上有明显不同,例如不需要类型声明、访问修饰符、分号和大括号。同时,Jython通过自动添加bean属性和事件作为类属性,使用赋值来处理属性和事件,简化了代码。

3. Bean属性和事件
  • Bean属性
    • 基本概念 :bean是遵循特定方法、属性和事件约定的类。bean的属性通过get和set方法操作,事件通过add和remove方法指定监听器对象。
    • Jython的处理方式 :Jython通过自动将bean属性和事件作为类属性,简化了操作。例如,Java中使用 bean.getName() 获取属性,在Jython中可以直接使用 bean.name ;Java中使用 bean.setName("new name") 设置属性,在Jython中只需 bean.name = "new name"
    • 属性类型和元组 :Jython会自动将分配给bean属性的元组解释为属性类型的构造函数。例如,将三元组分配给 background 属性会自动成为 java.awt.Color 类的构造函数值,分配给 bounds 属性会自动成为 Rectangle 类型。
  • Bean事件
    • Java的处理方式 :在Java GUI中处理事件,需要添加实现事件监听器接口的类,并使用 addEvent Listener 方法设置监听器。
    • Jython的处理方式 :Jython使用自动事件属性作为属性,允许将可调用对象分配给事件的bean名称。例如,实现 windowClosing 事件,只需将可调用对象分配给容器的 windowClosing 属性。
4. 名称优先级

Jython支持实例方法、类静态字段、bean属性和bean事件属性,可能会出现命名冲突。Jython通过分配优先级来解决冲突,优先级顺序如下:
1. 方法
2. 类静态属性
3. 事件属性
4. Bean属性

5. pawt包

Jython的pawt包为在Jython中使用AWT和Swing提供了一些便利模块,包括GridBag、colors、test和swing。
- GridBag
- 功能 pawt.GridBag 类是一个包装类,用于简化 java.awt.GridBagLayout java.awt.GridBagConstraints 的使用。
- 使用方法 :要使用 pawt.GridBag 类,需要将Java容器的实例提供给其构造函数。例如:

from java import awt 
import pawt 
top_frame = awt.Frame("GridBag frame") 
bag = pawt.GridBag(top_frame) 
- **示例代码**:以下是使用`pawt.GridBag`模块简化地址簿条目设置的示例:
from java import awt 
from java.awt.event import ActionListener 
from pawt import GridBag 
from java.lang import System 
frame = awt.Frame("GridBag Test", visible=1,size=(200,200), 
                   windowClosing=lambda e: System.exit(0)) 
pane=awt.Panel(background=(200,200,255)) 
frame.add(pane) 
bag = GridBag(pane, fill="HORIZONTAL") 
labelFactory = lambda x: awt.Label(x, background=awt.Color.yellow) 
title = awt.Label("Jython GridBag Address Book", awt.Label.CENTER) 
bag.addRow(title) 
category = awt.Choice() 
map(category.add, ["Family", "Friends", "Business"]) 
bag.add(labelFactory("Category: ")) 
bag.addRow(category, anchor="WEST", fill="NONE") 
name = awt.TextField(25) 
bag.add(labelFactory("Name: ")) 
bag.addRow(name) 
address = awt.TextField(35) 
bag.add(labelFactory("Address: ")) 
bag.addRow(address) 
city = awt.TextField(25) 
bag.add(labelFactory("City: ")) 
bag.add(city) 
state = awt.TextField(2) 
bag.add(labelFactory("State: ")) 
bag.add(state) 
zip = awt.TextField(5) 
bag.add(labelFactory("zip: ")) 
bag.addRow(zip) 
homephone_areacode = awt.TextField(3) 
homephone_prefix = awt.TextField(3) 
homephone_suffix = awt.TextField(4) 
cellphone_areacode = awt.TextField(3) 
cellphone_prefix = awt.TextField(3) 
cellphone_suffix = awt.TextField(4) 
frame.pack() 
  • colors
    • 功能 colors 模块包含许多命名颜色,方便用户使用。可以通过 dir(pawt.colors) 查看所有定义的颜色。
  • test
    • 功能 test 函数允许简单测试图形组件,无需使用 Frame Panel 即可查看组件效果。例如:
import java 
import pawt 
b = java.awt.Button("help", background=(212,144,100)) 
pawt.test(b) 
  • pawt.swing
    • 功能 pawt.swing 模块选择适合JDK的Swing库,并提供特定于Swing组件的测试函数。
    • 注意事项 :该模块是一个动态模块,在编译Jython时可能会出现问题。因此,在子类化Swing组件或编译Jython应用程序时,最好直接使用 javax.swing
6. 示例
  • 简单AWT图形
    以下是一个简单的Jython横幅示例:
# file: jythonBanner.py 
from java import awt 
import java 
class Banner(awt.Canvas): 
    def paint(self, g): 
        g.color = 204, 204, 204 
        g.fillRect(15, 15, self.width-30, self.height-30) 
        g.color = 102, 102, 153 
        g.font = "Helvetica Bold", awt.Font.BOLD, 28 
        message = ">>> Jython" 
        g.drawString(message, 
                     self.width/2 - len(message)*10,  #approx. center 
                     self.height/2 + 14)              #same here 
top_frame = awt.Frame("Jython Banner", size=(350, 150), 
                      windowClosing=lambda e: java.lang.System.exit(0)) 
top_frame.add( Banner() ) 
top_frame.visible = 1 

这个示例展示了如何使用Jython的自动bean属性和事件属性来创建一个简单的图形界面。

  • 添加事件
    以下是一个绘制极坐标图并处理鼠标事件的示例:
# file: PolarGraph.py 
import java 
from java.lang import System 
from java import awt 
from java.lang import Math 
class Main(awt.Frame): 
    def __init__(self): 
        self.background=awt.Color.gray 
        self.bounds=(50,50,400,400) 
        self.windowClosing=lambda e: System.exit(0) 
        self.r = awt.Label("r:          ") 
        self.theta = awt.Label("theta:          ") 
        infoPanel = awt.Panel() 
        infoPanel.add(self.r) 
        infoPanel.add(self.theta) 
        self.graph = PolarCanvas() 
        self.add("North", infoPanel) 
        self.add("Center", self.graph) 
        self.visible = 1 
        self.graph.mouseMoved = self.updateCoords 
    def updateCoords(self, e): 
        limit = self.graph.limit 
        width = self.graph.width 
        height = self.graph.height 
        x = (2 * e.x * limit)/width - limit 
        y = limit - (2 * e.y * limit)/height 
        r = Math.sqrt(x**2 + y**2) 
        if x == 0: 
            theta = 0 
        else: 
            theta = Math.atan(Math.abs(y)/Math.abs(x)) 
        if x < 0 and y > 0: 
            theta = Math.PI - theta 
        elif x < 0 and y <= 0: 
            theta = theta + Math.PI 
        elif x > 0 and y < 0: 
            theta = 2 * Math.PI - theta 
        self.r.text = "r: %0.2f" % r 
        self.theta.text = "theta: %0.2f" % theta 
class PolarCanvas(awt.Canvas): 
    def __init__(self, interval=100, limit=400): 
        self.background=awt.Color.white 
        self.mouseReleased=self.onClick 
        self.interval = interval 
        self.limit = limit 
        self.points = [] 
    def onClick(self, e): 
        x = (2 * e.x * self.limit)/self.width - self.limit 
        y = self.limit - (2 * e.y * self.limit)/self.height 
        self.points.append(awt.Point(x, y)) 
        self.repaint() 
    def paint(self, g): 
        rings = self.limit/self.interval 
        step = (self.width/(rings*2), self.height/(rings*2)) 
        # Draw rings 
        for x in range(1, rings + 1): 
            r = awt.Rectangle(x*step[0], x*step[1], 
                              step[0] **(rings-x)*2, 
                              step[1]*(rings-x)*2) 
            lambda x, y: max(y - x ** 20, 10) 
            g.color = (max(140-x*20,10), max(200-x*20,10), 
                       max(240-x*20,10)) 
            g.fillOval(r.x, r.y, r.width, r.height) 
            g.color = awt.Color.black 
            g.drawOval(r.x, r.y, r.width, r.height) 
            g.drawString(str((rings*self.interval)-(x*self.interval)), 
                         r.x - 8, self.height/2 + 12) 
        # draw center dot 
        g.fillOval(self.width/2-2, self.height/2-2, 4, 4) 
        # draw points 
        g.color = awt.Color.red 
        for p in self.points: 
            x = (p.x * self.width)/(2 * self.limit) + self.width/2 
            y = self.height/2 - (p.y * self.height)/(2 * self.limit) 
            g.fillOval(x, y, 4, 4) 
if __name__ == '__main__': 
    app = Main() 

这个示例展示了如何使用Jython的自动事件属性来处理鼠标事件。

  • 图像显示
    以下是一个显示图像的示例:
# file jyimage.py 
from java.lang import System 
from java import awt 
class jyimage(awt.Frame): 
    def __init__(self, im): 
        self.im = awt.Toolkit.getDefaultToolkit().getImage(im) 
        self.bounds=100,100,200,200 
        self.title="Jython Image Display" 
        self.windowClosing=lambda e: System.exit(0) 
        mt = awt.MediaTracker(self) 
        mt.addImage(self.im, 0) 
        mt.waitForID(0) 
        self.addNotify() # add peer to get insets 
        i = self.insets 
        self.resize(self.im.width + i.left + i.right, 
                    self.im.height + i.top + i.bottom) 
    def paint(self, g): 
        g.drawImage(self.im, self.insets.left, self.insets.top, self) 
if __name__ == '__main__': 
    f = jyimage("jython-new-small.gif") 
    f.visible = 1 

这个示例展示了如何使用Jython显示图像,并处理图像加载和显示的细节。

  • 菜单和菜单事件
    以下是一个创建菜单并处理菜单事件的示例:
# file: jythonmenus.py 
from java import awt 
from java.awt import event 
from java.lang import System 
menus = [
   ('File', ['New', 'Open', 'Save', 'Saveas', 'Close']), 
   ('Edit', ['Copy', 'Cut', 'Paste']), 
] 
class MenuTest(awt.Frame): 
    def __init__(self): 
        bar = awt.MenuBar() 
        for menu, menuitems in menus: 
            menu = awt.Menu(menu) 
            for menuitem in menuitems: 
                method = getattr(self, 'on%s' % menuitem) 
                item = awt.MenuItem(menuitem, actionPerformed=method) 
                menu.add(item) 
            bar.add(menu) 
        self.menuBar = bar 
        self.windowClosing = lambda e: System.exit(0) 
        self.eventLabel = awt.Label("Event: ") 
        self.bounds = 100, 100, 200, 100 
        self.add(self.eventLabel) 
    def onNew(self, e): 
        self.eventLabel.text = "Event: onNew" 
    def onOpen(self, e): 
        self.eventLabel.text = "Event: onOpen" 
    def onSave(self, e): 
        self.eventLabel.text = "Event: onSave" 
    def onSaveas(self, e): 
        self.eventLabel.text = "Event: onSaveas" 
    def onClose(self, e): 
        self.eventLabel.text = "Event: onClose" 
        System.exit(0) 
    def onCopy(self, e): 
        self.eventLabel.text = "Event: onCopy" 
    def onCut(self, e): 
        self.eventLabel.text = "Event: onCut" 
    def onPaste(self, e): 
        self.eventLabel.text = "Event: onPaste" 
f = MenuTest() 
f.visible = 1 

这个示例展示了如何使用Jython创建菜单并处理菜单事件。

  • 拖放功能
    以下是一个实现拖放功能的示例:
# file: ListDnD.py 
from java import awt 
from java.awt import dnd 
from java.lang import System 
from java.awt import datatransfer 
class JythonDnD(awt.Frame, dnd.DragSourceListener, dnd.DragGestureListener): 
    def __init__(self): 
        self.title="Jython Drag -n- Drop Implementation" 
        self.bounds = 100, 100, 300, 200 
        self.windowClosing = lambda e: System.exit(0) 
        self.draglist = awt.List() 
        map(self.draglist.add, ["1","2","3","4","5"]) 
        self.droplist = droplist([]) 
        self.dropTarget = dnd.DropTarget(self, 
            dnd.DnDConstants.ACTION_COPY_OR_MOVE, self.droplist) 
        self.layout = awt.GridLayout(1, 2, 2, 2) 
        self.add(self.draglist) 
        self.add(self.droplist) 
        self.dragSource = dnd.DragSource() 
        self.recognize = self.dragSource.createDefaultDragGestureRecognizer(
                             self.draglist, 
                             dnd.DnDConstants.ACTION_COPY_OR_MOVE, 
                             self) 
    def dragGestureRecognized(self, e): 
        item = self.draglist.getSelectedItem() 
        e.startDrag(self.dragSource.DefaultCopyDrop, 
                    datatransfer.StringSelection(item), self) 
    def dragEnter(self, e): pass 
    def dragOver(self, e): pass 
    def dragExit(self, e): pass 
    def dragDropEnd(self, e): pass 
    def dropActionChanged(self, e): pass 

class droplist(awt.List, dnd.DropTargetListener): 
    def __init__(self, datalist): 
        map(self.add, datalist) 
        self.dropTarget = dnd.DropTarget(self, 3, self) 
    def drop(self, e): 
        transfer = e.getTransferable() 
        data = transfer.getTransferData(datatransfer.DataFlavor.stringFlavor) 
        self.add(data) 
        e.dropComplete(1) 
    def dragEnter(self, e): 
        e.acceptDrag(dnd.DnDConstants.ACTION_COPY_OR_MOVE) 
    def dragExit(self, e): pass 
    def dragOver(self, e): pass 
    def dropActionChanged(self, e): pass 
win = JythonDnD() 
win.visible=1 

这个示例展示了如何使用Jython实现拖放功能。

  • Swing示例
    以下是一个简单的Swing树显示示例:
# file: SwingTree.py 
from javax import swing 
from javax.swing import tree 
import sys 
top_frame = swing.JFrame("A Simple Tree in Jython and Swing", 
                         windowClosing=lambda e: sys.exit(0), 
                         background=(180,180,200), 
                         ) 
data = tree.DefaultMutableTreeNode("Root") 
data.add(tree.DefaultMutableTreeNode("a leaf")) 
childNode = tree.DefaultMutableTreeNode("a node") 
childNode.add(tree.DefaultMutableTreeNode("another leaf")) 
data.add(childNode) 
t = swing.JTree(data) 
t.putClientProperty("JTree.lineStyle", "Angled") 
top_frame.contentPane.add(t) 
top_frame.bounds = 100, 100, 200, 200 
top_frame.visible = 1 

这个示例展示了如何使用Jython创建Swing应用程序。

总结

通过以上示例可以看出,Jython在GUI开发中具有很多优势,如简化代码、自动处理bean属性和事件等。同时,pawt包提供的便利模块也进一步提高了开发效率。无论是使用AWT还是Swing,Jython都能帮助开发者更轻松地创建图形用户界面。

流程图

graph TD;
    A[开始] --> B[Jython GUI开发];
    B --> C[对比Java和Jython GUI];
    B --> D[Bean属性和事件];
    B --> E[pawt包];
    B --> F[示例];
    C --> C1[Java GUI示例];
    C --> C2[Jython GUI示例];
    D --> D1[Bean属性];
    D --> D2[Bean事件];
    D --> D3[名称优先级];
    E --> E1[GridBag];
    E --> E2[colors];
    E --> E3[test];
    E --> E4[pawt.swing];
    F --> F1[简单AWT图形];
    F --> F2[添加事件];
    F --> F3[图像显示];
    F --> F4[菜单和菜单事件];
    F --> F5[拖放功能];
    F --> F6[Swing示例];

表格

模块 功能
GridBag 简化 java.awt.GridBagLayout java.awt.GridBagConstraints 的使用
colors 包含许多命名颜色,方便使用
test 允许简单测试图形组件
pawt.swing 选择适合JDK的Swing库,并提供特定于Swing组件的测试函数

Jython GUI开发:从基础到实践

7. 深入理解Jython GUI开发的技术细节
  • 自动类型转换
    在Jython中,存在自动类型转换机制。例如, setVisible 方法需要Java的 Boolean 参数,Jython会将 PyInteger 类型的1转换为 Boolean 类型的 true ,0转换为 false 。这意味着在Jython中可以将 visible 属性设置为1来显示组件,如 top_frame.visible = 1
  • 类的使用
    在Jython中,类不是必需的。虽然使用类可以更准确地翻译Java代码,但Jython允许不使用类来实现GUI。如 SimpleJythonGUI 示例中就没有使用类,但实现了与Java版本几乎相同的GUI。不过,由于AWT和Swing类的特性,复杂的无类GUI实现起来较为困难。
8. 不同场景下的Jython GUI开发技巧
  • 快速原型开发
    由于Jython可以轻松将Java GUI代码转换为Jython代码,因此在进行GUI原型开发时,使用Jython可以快速验证想法。开发人员可以先在Jython中进行原型设计,之后再根据需要将其转换为Java代码。例如,在开发一个简单的表单界面时,可以先使用Jython快速搭建界面,测试功能的可行性。
  • 与Java生态的集成
    Jython本身是基于Java的,能够很好地与Java生态集成。在开发GUI时,可以利用Java丰富的类库和资源。例如,在处理复杂的业务逻辑时,可以调用Java的类和方法。同时,Jython的自动bean属性和事件属性也能与Java的AWT和Swing类无缝配合。
9. 常见问题及解决方案
  • 命名冲突问题
    如前文所述,Jython支持多种属性类型,可能会出现命名冲突。当遇到命名冲突时,需要根据名称优先级来解决。例如,如果一个方法名与bean属性名冲突,Jython会优先使用方法。开发人员在编写代码时,应尽量避免命名冲突,或者在出现冲突时,根据优先级规则进行调整。
  • pawt.swing模块的使用问题
    pawt.swing 模块是一个动态模块,在编译Jython时可能会出现问题。如果需要对Jython应用程序进行编译,或者子类化Swing组件,建议直接使用 javax.swing ,避免使用 pawt.swing 模块。
10. 未来发展趋势
  • 更多工具包的支持
    随着技术的发展,可能会有更多与其他工具包的绑定出现,使得Jython在GUI开发中有更多的选择。这将进一步丰富Jython的应用场景,提高开发的灵活性。
  • 性能优化
    未来可能会对Jython的性能进行优化,特别是在处理大规模GUI应用时,提高响应速度和资源利用率。这将使得Jython在更复杂的项目中也能有出色的表现。
11. 总结与展望

Jython在GUI开发中展现出了独特的优势,通过简化代码、自动处理bean属性和事件等功能,大大提高了开发效率。pawt包的出现,更是为开发提供了便利。无论是初学者还是有经验的开发者,都能利用Jython快速创建出功能丰富的图形用户界面。

在未来的开发中,我们可以期待Jython在GUI领域有更多的创新和发展。同时,开发者也可以不断探索Jython的更多可能性,将其应用到更广泛的项目中。

流程图

graph TD;
    A[Jython GUI开发] --> B[技术细节];
    A --> C[开发技巧];
    A --> D[常见问题];
    A --> E[未来趋势];
    B --> B1[自动类型转换];
    B --> B2[类的使用];
    C --> C1[快速原型开发];
    C --> C2[与Java生态集成];
    D --> D1[命名冲突问题];
    D --> D2[pawt.swing模块问题];
    E --> E1[更多工具包支持];
    E --> E2[性能优化];

表格

问题 解决方案
命名冲突 根据名称优先级(方法 > 类静态属性 > 事件属性 > Bean属性)解决
pawt.swing模块编译问题 子类化Swing组件或编译Jython应用时,直接使用 javax.swing
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值