Jython GUI开发:从基础到实践
1. Jython GUI开发概述
在Jython中开发图形用户界面(GUI),主要工具包是Java的抽象窗口工具包(AWT)和Swing类。由于Jython是用Java编写的,并且擅长在Python语法中使用Java类,所以AWT和Swing是很自然的选择。不过,这并不意味着它们是实现GUI的唯一选择,越来越多与其他工具包的绑定也允许Java和Jython使用这些工具包。
2. Java与Jython GUI对比
编写Jython GUI与Java非常相似,大多数Java GUI可以轻松转换为Jython代码。以下是一个简单的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.getHeight()/2);
top_frame.setVisible(true);
}
public static void main(String[] args) {
SimpleJavaGUI s = new SimpleJavaGUI();
}
public void actionPerformed(ActionEvent event) {
System.exit(0);
}
}
对应的Jython代码如下:
# File: simpleJythonGUI.py
import java
from java import awt
def exit(e):
java.lang.System.exit(0)
top_frame = awt.Frame(title="A Basic Jython GUI",
background = awt.Color.yellow,
windowClosing=exit)
panel = awt.Panel()
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)
top_frame.pack()
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
从上述代码可以看出,Jython代码在语法上更简洁,不需要Java的类型声明、访问修饰符、分号和大括号。同时,Jython使用
awt
包前缀来实例化组件,并且可以自动进行类型转换。
3. Bean属性和事件
在介绍Jython自动Bean属性之前,先回顾一下Bean。Bean是遵循方法、属性和事件的适当约定的任何类。Bean的方法是指Bean中所有被指定为公共的方法,属性是通过get和set方法操作的数据对象,事件是通过add和remove方法指定的监听器对象。
Jython通过检查来确定Bean属性和事件,然后允许将这些属性和事件作为实例属性使用。例如,对于一个具有只读属性
name
的Bean:
public class A {
private String name;
public String getName() {
return name;
}
}
在Java中获取
name
属性的方式是:
import A;
bean = new A();
String name = bean.getName();
而在Jython中,可以直接将
name
属性作为属性访问:
import A
b = A()
name = b.name
对于设置属性,Java中使用
setName
方法:
import A;
A bean = new A();
bean.setName("new name");
在Jython中,只需进行属性赋值:
import A
bean = A()
bean.name = "new name"
这种方式同样适用于事件处理。在Jython中,可以通过属性赋值来处理事件,例如:
def wclose(e):
print "Processing the windowClosing Event"
e.getSource().dispose()
import java
f = java.awt.Frame("Jython AWT Example")
f.windowClosing = wclose
f.visible = 1
Jython的自动Bean属性和事件属性是一种快捷方式,结合关键字参数可以简化GUI开发的许多方面。
4. Bean属性的详细介绍
-
属性命名约定
:Bean属性遵循一定的命名约定,例如属性
extent对应的访问器和修改器方法是getExtent和setExtent。如果Java类有一个名为setAMultiWordName的方法,Jython会添加一个自动Bean属性aMultiWordName。 -
读写属性
:如果一个属性只定义了
get方法,则为只读属性;如果只定义了set方法,则为只写属性;同时定义get和set方法,则为读写属性。例如:
public class A {
private String name = "";
public String getName() {
System.out.println("Getting name");
return name;
}
public void setName(String s) {
name = s;
System.out.println("Setting name");
}
}
在Jython中测试:
import A
b = A()
b.name = "New name" # Setting name
print(b.name) # Getting name, New name
-
序列属性
:Bean属性也可以是序列。如果需要对序列属性进行完整的读写访问,会有四个访问方法:
setProperty([])、setProperty(int, Object)、getProperty()和getProperty(int)。但Jython的自动Bean属性只使用getProperty()和setProperty([])方法。以下是一个具有索引属性items的Java Bean示例:
// file: ItemBean.java
public class ItemBean {
private String[] data = {"A", "B", "C"};
public ItemBean() { ; }
public String[] getItems() {
return data;
}
public String getItems(int i) {
return data[i];
}
public void setItems(String[] items) {
data = items;
}
public void setItems(int index, String item) {
data[index] = item;
}
}
在Jython中测试:
import ItemBean
ib = ItemBean()
print(ib.items) # array(['A', 'B', 'C'], java.lang.String)
ib.items = ["1", "2", "3"]
5. Bean属性与元组
Jython会自动将分配给Bean属性的元组解释为正在设置的属性类型的构造函数。例如,分配给
background
属性的三元组会自动成为
java.awt.Color
类的构造函数值,分配给
bounds
属性的元组会自动成为
Rectangle
类型。以下是Java和Jython的对比示例:
// In Java (pseudo-code)
import java.awt.*;
Frame f = new Frame();
Label L = new Label("This is a Label");
L.setBackground( new Color(50, 160, 40));
L.setForeground( new Color(50, 255, 50));
L.setFont(new Font("Helvetica Bold", 18, 24));
f.add(L);
f.setVisible(true);
# In Jython
from java import awt
f = awt.Frame()
L = awt.Label("This is a Label", background=(50, 160, 40),
foreground=(50, 255, 50), font=("Helvetica Bold", 18, 24))
f.add(L)
f.visible = 1
6. Bean事件处理
在Java中,处理事件需要添加一个实现事件监听器接口的类。例如,处理
windowClosing
事件,需要使用
addWindowListener
方法添加一个实现
WindowListener
接口的类。而在Jython中,可以使用自动事件属性作为属性,将可调用对象分配给事件的Bean名称。例如:
def wclose(e):
print "Processing the windowClosing Event"
e.getSource().dispose()
def wopen(e):
print "Processing the windowOpened Event"
def wact(e):
print "Processing the windowActivated event"
import java
f = java.awt.Frame("Jython AWT Example")
f.windowClosing = wclose
f.windowOpened = wopen
f.windowActivated = wact
f.visible = 1
所有事件属性赋值也可以作为关键字参数包含在构造函数中:
f = java.awt.Frame("Jython AWT Example", windowOpened=wopen,
windowClosing=wclose, windowActivated=wact)
7. 名称优先级
Jython支持实例方法、类静态字段、Bean属性和Bean事件属性,这可能会导致命名冲突。Jython通过分配优先级来解决名称冲突,优先级顺序如下:
1. 方法
2. 类静态属性
3. 事件属性
4. Bean属性
8. pawt包
Jython的pawt包为在Jython中使用AWT和Swing提供了一些便利模块,包括
GridBag
、
colors
、
test
和
swing
。
-
GridBag
:
pawt.GridBag
类是一个包装类,用于帮助处理
java.awt.GridBagLayout
和
java.awt.GridBagConstraints
。它有两个方法:
add
和
addRow
。以下是使用
pawt.GridBag
类简化布局的示例:
from java import awt
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模块包含许多命名颜色,方便人们使用。可以通过以下方式查看所有定义的颜色:
import pawt
print(dir(pawt.colors))
-
test
:
test函数允许对图形组件进行简单测试。例如:
import java
import pawt
b = java.awt.Button("help", background=(212,144,100))
pawt.test(b)
该函数还可以接受一个大小参数:
from java.awt import Label
import pawt
L = Label("Test", background=(255,10,10), foreground=(10, 10, 100))
pawt.test(L, (200,140))
-
pawt.swing
:
pawt.swing模块有两个功能,一是为JDK选择合适的Swing库,二是有一个专门用于Swing组件的测试函数。例如:
import pawt
cb = pawt.swing.JCheckBox("Test Check Box")
myframe = pawt.swing.test(cb)
9. 示例
以下是一些使用AWT和Swing工具包的Jython示例:
-
简单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,
self.height/2 + 14)
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
- 添加事件 :实现一个极坐标图,显示鼠标位置的半径和角度:
# 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))
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)
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)
g.fillOval(self.width/2-2, self.height/2-2, 4, 4)
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()
- 显示图像 :在AWT框架中显示图像:
# 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()
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
- 拖放功能 :实现拖放列表:
# 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
- 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在GUI开发中通过自动Bean属性和事件属性,结合关键字参数,大大简化了代码编写,提高了开发效率。同时,pawt包提供的便利模块进一步增强了开发的便利性。无论是使用AWT还是Swing,Jython都能很好地发挥作用,为开发者提供了一种高效、简洁的GUI开发方式。
Jython GUI开发:从基础到实践(续)
10. 示例代码的流程图分析
为了更好地理解上述示例代码的执行流程,下面给出部分示例的mermaid格式流程图。
10.1 简单AWT图形(jythonBanner.py)流程图
graph TD;
A[开始] --> B[创建Banner类实例];
B --> C[创建Frame实例];
C --> D[将Banner实例添加到Frame中];
D --> E[设置Frame可见];
E --> F[触发paint方法];
F --> G[绘制背景矩形];
G --> H[设置字体和颜色];
H --> I[绘制字符串];
I --> J[结束];
10.2 显示图像(jyimage.py)流程图
graph TD;
A[开始] --> B[创建jyimage类实例];
B --> C[加载图像];
C --> D[设置Frame属性];
D --> E[使用MediaTracker等待图像加载完成];
E --> F[获取Frame的insets信息];
F --> G[调整Frame大小];
G --> H[触发paint方法];
H --> I[绘制图像];
I --> J[结束];
11. 示例代码的对比总结
| 示例类型 | Java实现复杂度 | Jython实现复杂度 | 主要优势 |
|---|---|---|---|
| 简单GUI | 需类型声明、访问修饰符等,代码冗长 | 语法简洁,自动类型转换 | 代码编写效率高 |
| Bean属性和事件处理 | 需显式调用get和set方法,事件处理需实现接口 | 可直接作为属性访问和赋值 | 代码简洁,易于理解 |
| 布局管理(GridBagLayout) | 需手动设置大量约束条件 | 可使用pawt.GridBag类简化操作 | 减少代码量,提高开发效率 |
| 事件处理 | 需实现事件监听器接口 | 可直接将可调用对象赋值给事件属性 | 代码简洁,灵活性高 |
12. 实际应用场景分析
Jython的GUI开发在以下实际场景中具有显著优势:
12.1 快速原型开发
由于Jython代码的简洁性和自动Bean属性、事件属性的特性,开发者可以快速搭建GUI原型。例如,在项目初期,开发者可以使用Jython快速实现一个简单的界面,展示基本功能,以便与团队成员或客户进行沟通和验证。
12.2 与Java系统集成
因为Jython是基于Java的,所以它可以很好地与现有的Java系统集成。在需要对Java系统进行界面扩展或修改时,使用Jython可以避免重新编写大量的Java代码,同时利用Python的灵活性和简洁性。
12.3 教育和学习
对于初学者来说,Jython的语法相对简单,易于理解。通过学习Jython的GUI开发,可以快速掌握GUI开发的基本概念和技术,同时了解Java和Python的结合使用。
13. 常见问题及解决方案
在Jython GUI开发过程中,可能会遇到以下常见问题及相应的解决方案:
13.1 类型转换问题
在Jython中,虽然有自动类型转换,但在某些情况下可能会出现类型不匹配的问题。例如,某些方法可能要求传入Java的特定类型参数。解决方案是明确进行类型转换,或者使用Jython提供的类型转换函数。
13.2 事件处理异常
如果事件处理函数出现异常,可能会导致程序崩溃。建议在事件处理函数中添加异常处理机制,例如使用
try-except
语句捕获异常并进行相应的处理。
13.3 布局管理问题
在使用复杂的布局管理器(如GridBagLayout)时,可能会出现组件显示位置不正确的问题。可以使用pawt包中的GridBag类简化布局管理,同时仔细检查布局约束条件。
14. 总结与展望
Jython在GUI开发中展现出了强大的优势,通过自动Bean属性、事件属性和关键字参数,大大简化了代码编写,提高了开发效率。pawt包的提供进一步增强了开发的便利性,使得开发者可以更加轻松地处理布局管理、颜色设置和组件测试等问题。
未来,随着Jython的不断发展和完善,相信它在GUI开发领域将会有更广泛的应用。同时,随着Python生态系统的不断壮大,Jython与其他Python库的结合也将为GUI开发带来更多的可能性。例如,结合数据可视化库可以实现更丰富的界面展示,结合机器学习库可以实现智能交互界面等。
总之,Jython为开发者提供了一种高效、简洁的GUI开发方式,无论是对于初学者还是有经验的开发者来说,都是一个值得尝试的选择。
超级会员免费看
55

被折叠的 条评论
为什么被折叠?



