[JAVA] 배워서 남주기 | 게시물 [67]건 | ||||||||
| |||||||||
|
Java/swing
- 자바 GUI 프로그래밍(Java GUI Programming) 2007.08.20
- 메뉴와 파일 다이얼로그 사용하기 2007.08.20
- JTree Tutorial 2007.08.20
- 이클립스용 GUI 빌더, Jigloo 시작하기 (한글) - 5 2007.07.15
- 이클립스용 GUI 빌더, Jigloo 시작하기 (한글) - 4 2007.07.13
- 이클립스용 GUI 빌더, Jigloo 시작하기 (한글) - 3 2007.07.13
- 이클립스용 GUI 빌더, Jigloo 시작하기 (한글) - 2 2007.07.13
- 이클립스용 GUI 빌더, Jigloo 시작하기 (한글) - 1 2007.07.13
자바 GUI 프로그래밍(Java GUI Programming)
메뉴와 파일 다이얼로그 사용하기
1 메뉴 사용하기
자바 Swing 에서 메뉴를 사용하기 위해서는 3개의 클래스가 필요하다
javax.swing.JMenu 클래스
javax.swing.JMenuBar 클래스
javax.swing.JMenuItem 클래스
JMenuBar 클래스는 JMenu 를 담아두는 클래스로 꼭 한개 필요하다
JMenu 클래스는 JMenuBar 에 추가되어 각종 메뉴를 구성하는데
예를 들자면 지금 사용하고 있는 Explorer 의 파일(F) 편집(E) 등등이 각각 하나의 메뉴이다
JMenuItem 은 메뉴에 추가되어 사용되는 클래스로 Explorer 의 파일(F) 메뉴 내의
새로 만들기, 열기 등등을 지칭한다..
사용법은 JMenuBar 를 생성하고 여기에 add() 메소드로 각각 생성된 JMenu 를 더하고
JMenu 의 add() 메소드로 각각의 JMenuItem 클래스를 더하면 된다.
마지막으로 setJMenuBar() 메서드를 통해 JFrame 클래스에 메뉴바를 더하면 된다
// 각종 필드 선언
private JMenu jFile;
private JMenuBar jMenuBar;
private JMenuItem jOpen;
// 각종 필드 생성
jMenuBar = new JMenuBar();
jFile = new JMenu("File");
jOpen = new JMenuItem("Open");
// 실제 더하기
jFile.add(jOpen);
jMenuBar.add(jFile);
setJMenuBar(jMenuBar);
이벤트는 메뉴 아이템 객체에 addActionListener(); 메서드를 통해서 주면 된다
이렇게 말이다..
2. 파일 다이얼로그 사용하기
javax.swing.JFileChooser 클래스를 사용하면 된다.
File file;
JFileChooser fc = new JFileChooser();
int returnVal = fc.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION)
{
file = fc.getSelectedFile();
}
위 소스 처럼 간단이 사용하면 되는데
선택된 파일 정보는 file 필드내에 저장되어 있다
궁금하다면 if 내에 System.out.println(file.getName()); 메서드를 확인하면 된다.
다이얼 로그는 아래 사진처럼 열리게 된다.
옵션으로 필터도 줄 수 있는데 자세한 사항은 스킵하자
그럼 위 두개를 사용하여 File 메뉴에 Open 을 클릭하면 파일 다이얼 로그가 열리게
만든 소스를 만들었다
제일 위부분 다운 받는 곳에서 다운 받아서 확인하자..
JTree Tutorial
1. Introduction: Custom Models and Renderers
The idea of custom data models and cell renderers was covered in detail in the Swing tutorial section on JList. JTree
is another component that commonly uses these techniques. This section will illustrate the basic use of JTree
, show how to respond to node selection events, give an example of a custom model (a tree that builds the children "on the fly") and show how replace the icons that appear at the tree nodes.
2. Simple JTree
The simplest and most common way to use JTree
is to create objects of type DefaultMutableTreeNode
to act as the nodes of the tree. Nodes that have no children will be displayed as leaves. You supply a value, known as the "user object", to the DefaultMutableTreeNode
constructor, to act as the value at each node. The toString
method of that user object is what is displayed for each node.
Once you have some nodes, you hook them together in a tree structure via parentNode.add(childNode)
. Finally, you pass the node to the JTree
constructor. Note that, since trees can change size based upon user input (expanding and collapsing nodes), trees are usually placed inside a JScrollPane
. For example, here is a very simple tree:
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
DefaultMutableTreeNode child1 = new DefaultMutableTreeNode("Child 1");
root.add(child1);
DefaultMutableTreeNode child2 = new DefaultMutableTreeNode("Child 2");
root.add(child2);
JTree tree = new JTree(root);
someWindow.add(new JScrollPane(tree));
For more complicated trees, it is sometimes tedious and hard to maintain if you hook everything together "by hand". So you may find it useful to first make a simple tree-like data structure, then build nodes and hook them together automatically from that data structure. Here's an example that uses nested arrays to define the data structure.
2.1 Simple JTree Example: Source Code (Download source code)
import java.awt.*;
import javax.swing.*;
import javax.swing.tree.*;
public class SimpleTree extends JFrame {
public static void main(String[] args) {
new SimpleTree();
}
public SimpleTree() {
super("Creating a Simple JTree");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();
Object[] hierarchy =
{ "javax.swing",
"javax.swing.border",
"javax.swing.colorchooser",
"javax.swing.event",
"javax.swing.filechooser",
new Object[] { "javax.swing.plaf",
"javax.swing.plaf.basic",
"javax.swing.plaf.metal",
"javax.swing.plaf.multi" },
"javax.swing.table",
new Object[] { "javax.swing.text",
new Object[] { "javax.swing.text.html",
"javax.swing.text.html.parser" },
"javax.swing.text.rtf" },
"javax.swing.tree",
"javax.swing.undo" };
DefaultMutableTreeNode root = processHierarchy(hierarchy);
JTree tree = new JTree(root);
content.add(new JScrollPane(tree), BorderLayout.CENTER);
setSize(275, 300);
setVisible(true);
}
/** Small routine that will make node out of the first entry
* in the array, then make nodes out of subsequent entries
* and make them child nodes of the first one. The process is
* repeated recursively for entries that are arrays.
*/
private DefaultMutableTreeNode processHierarchy(Object[] hierarchy) {
DefaultMutableTreeNode node =
new DefaultMutableTreeNode(hierarchy[0]);
DefaultMutableTreeNode child;
for(int i=1; i<hierarchy.length; i++) {
Object nodeSpecifier = hierarchy[i];
if (nodeSpecifier instanceof Object[]) // Ie node with children
child = processHierarchy((Object[])nodeSpecifier);
else
child = new DefaultMutableTreeNode(nodeSpecifier); // Ie Leaf
node.add(child);
}
return(node);
}
}
Note: also requires WindowUtilities.java and ExitListener.java, shown earlier.
2.2. Simple JTree Example: Initial Result
TreeSelectionListener
. The TreeSelectionListener
interface requires a single method; valueChanged
. You extract the currently selected node via tree.getLastSelectedPathComponent
, then casting that to your node type (usually DefaultMutableTreeNode
), then extracting the user object via getUserObject
. However, if all you want is the node label, you can just call toString
on the result of tree.getLastSelectedPathComponent
. Here's an example: import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;
public class SelectableTree extends JFrame
implements TreeSelectionListener {
public static void main(String[] args) {
new SelectableTree();
}
private JTree tree;
private JTextField currentSelectionField;
public SelectableTree() {
super("JTree Selections");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();
DefaultMutableTreeNode root =
new DefaultMutableTreeNode("Root");
DefaultMutableTreeNode child;
DefaultMutableTreeNode grandChild;
for(int childIndex=1; childIndex<4; childIndex++) {
child = new DefaultMutableTreeNode("Child " + childIndex);
root.add(child);
for(int grandChildIndex=1; grandChildIndex<4; grandChildIndex++) {
grandChild =
new DefaultMutableTreeNode("Grandchild " + childIndex +
"." + grandChildIndex);
child.add(grandChild);
}
}
tree = new JTree(root);
tree.addTreeSelectionListener(this);
content.add(new JScrollPane(tree), BorderLayout.CENTER);
currentSelectionField = new JTextField("Current Selection: NONE");
content.add(currentSelectionField, BorderLayout.SOUTH);
setSize(250, 275);
setVisible(true);
}
public void valueChanged(TreeSelectionEvent event) {
currentSelectionField.setText
("Current Selection: " +
tree.getLastSelectedPathComponent().toString());
}
}
A JTree
uses a TreeModel
to get its data. As with JList
, you can replace the model altogether, specifying how to extract data from the custom model. See the tutorial section on JList for an example of this general approach.
JTree
, however, the default TreeModel
uses a TreeNode
to store data associated with the tree, and it is more common to leave the TreeModel
unchanged and instead make a custom TreeNode
. The easiest approach for that is to start with DefaultMutableTreeNode
. One of the common cases where this is useful is when you don't want to explicitly lay out each node in the tree, but instead you have some sort of algorithm that describes the children of a given node, and you want to build the tree dynamically, only actually generating children for places that the user expands. For instance, in the following example the tree is potentially infinite, with each node describing a section in an outline. The root will be "1", the first-level children will be 1.1, 1.2, 1.3, etc., the second-level children will be 1.1.1, 1.1.2, etc., and so forth. The actual number of children of each node will be determined by a command-line argument to the program.JTree
dynamically is to observe that getChildCount
(a method in DefaultMutableTreeNode
) will be called before any of the children will actually be retrieved. So you keep a flag indicating whether children have ever been built. So you wait until getChildCount
is called, then, if the flag is false
, build the children and add them. To keep the tree from trying to count the children (and thus build the nodes) in order to determine which nodes are leaf nodes, override isLeaf
to always return false.import javax.swing.*;
public class DynamicTree extends JFrame {
public static void main(String[] args) {
int n = 5; // Number of children to give each node
if (args.length > 0)
try {
n = Integer.parseInt(args[0]);
} catch(NumberFormatException nfe) {
System.out.println("Can't parse number; using default of " + n);
}
new DynamicTree(n);
}
public DynamicTree(int n) {
super("Creating a Dynamic JTree");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();
JTree tree = new JTree(new OutlineNode(1, n));
content.add(new JScrollPane(tree), BorderLayout.CENTER);
setSize(300, 475);
setVisible(true);
}
}
import javax.swing.*;
import javax.swing.tree.*;
public class OutlineNode extends DefaultMutableTreeNode {
private boolean areChildrenDefined = false;
private int outlineNum;
private int numChildren;
public OutlineNode(int outlineNum, int numChildren) {
this.outlineNum = outlineNum;
this.numChildren = numChildren;
}
public boolean isLeaf() {
return(false);
}
public int getChildCount() {
if (!areChildrenDefined)
defineChildNodes();
return(super.getChildCount());
}
private void defineChildNodes() {
// You must set the flag before defining children if you
// use "add" for the new children. Otherwise you get an infinite
// recursive loop, since add results in a call to getChildCount.
// However, you could use "insert" in such a case.
areChildrenDefined = true;
for(int i=0; i<numChildren; i++)
add(new OutlineNode(i+1, numChildren));
}
public String toString() {
TreeNode parent = getParent();
if (parent == null)
return(String.valueOf(outlineNum));
else
return(parent.toString() + "." + outlineNum);
}
}
JTree
is little different than it was for a JList. See the tutorial section on JList for an example of this general approach. However, one very common task is to simply to change the three icons shown to indicate unexpanded internal (ie non-leaf) nodes, expanded internal nodes, and leaf nodes. This is quite simple -- just make a DefaultTreeCellRenderer
, call setOpenIcon
, setClosedIcon
, and setLeafIcon
either with the Icon
of interest (usually an ImageIcon
made from a small image file) or null to just turn off node icons. Then associate this cell renderer with the tree via setCellRenderer
.import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
/** JTree with missing or custom icons at the tree nodes.
* 1999 Marty Hall, http://www.apl.jhu.edu/~hall/java/
*/
public class CustomIcons extends JFrame {
public static void main(String[] args) {
new CustomIcons();
}
private Icon customOpenIcon = new ImageIcon("images/Circle_1.gif");
private Icon customClosedIcon = new ImageIcon("images/Circle_2.gif");
private Icon customLeafIcon = new ImageIcon("images/Circle_3.gif");
public CustomIcons() {
super("JTree Selections");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();
content.setLayout(new FlowLayout());
DefaultMutableTreeNode root =
new DefaultMutableTreeNode("Root");
DefaultMutableTreeNode child;
DefaultMutableTreeNode grandChild;
for(int childIndex=1; childIndex<4; childIndex++) {
child = new DefaultMutableTreeNode("Child " + childIndex);
root.add(child);
for(int grandChildIndex=1; grandChildIndex<4; grandChildIndex++) {
grandChild =
new DefaultMutableTreeNode("Grandchild " + childIndex +
"." + grandChildIndex);
child.add(grandChild);
}
}
JTree tree1 = new JTree(root);
tree1.expandRow(1); // Expand children to illustrate leaf icons
JScrollPane pane1 = new JScrollPane(tree1);
pane1.setBorder(BorderFactory.createTitledBorder("Standard Icons"));
content.add(pane1);
JTree tree2 = new JTree(root);
tree2.expandRow(2); // Expand children to illustrate leaf icons
DefaultTreeCellRenderer renderer2 = new DefaultTreeCellRenderer();
renderer2.setOpenIcon(null);
renderer2.setClosedIcon(null);
renderer2.setLeafIcon(null);
tree2.setCellRenderer(renderer2);
JScrollPane pane2 = new JScrollPane(tree2);
pane2.setBorder(BorderFactory.createTitledBorder("No Icons"));
content.add(pane2);
JTree tree3 = new JTree(root);
tree3.expandRow(3); // Expand children to illustrate leaf icons
DefaultTreeCellRenderer renderer3 = new DefaultTreeCellRenderer();
renderer3.setOpenIcon(customOpenIcon);
renderer3.setClosedIcon(customClosedIcon);
renderer3.setLeafIcon(customLeafIcon);
tree3.setCellRenderer(renderer3);
JScrollPane pane3 = new JScrollPane(tree3);
pane3.setBorder(BorderFactory.createTitledBorder("Custom Icons"));
content.add(pane3);
pack();
setVisible(true);
}
}
이클립스용 GUI 빌더, Jigloo 시작하기 (한글) - 5
요약
이번 튜토리얼에서는 Jigloo를 사용하여 기본적인 GUI를 생성하는 방법을 살펴보았다. 몇 가지 컨테이너와 컨트롤들을 사용해 봤다. 이벤트를 사용하여 각각의 컨트롤을 비즈니스 로직을 구현해 놓은 모델과 엮는 방법도 살펴보았다. 이제부터 할 수 있는 것이 많아졌을 것이다. Jigloo의 좀 더 어려운 기능들도 사용할 준비가 되었다. Jigloo를 사용하여 다음과 같은 일들을 할 수 있다.
- 폼에서 재사용 가능하고 분리된 컴포넌트들을 추출
- 이런 컴포넌트들을 다른 애플리케이션에서 사용할 수 있으며 심지어 비주얼 상속을 사용하여 확장(extend)할 수도 있다.
- 스윙 컴포넌트 추가
- 전체 애플리케이션을 스윙으로 변환
Jigloo 구조상 좀 더 다양한 특징을 제공하기 때문에 폼을 좀 더 복잡하게 만들 수 있다. 날짜 선택 컨트롤과 이런 데이터 요소를 위한 콤보 컨트롤을 사용할 수 있다.
애플리케이션에서 Jigloo 사용에 대해서는 참고자료에 있는 추가 문서들과 개념을 참조하기 바란다.
<출처 :https://www.ibm.com/developerworks/kr/library/tutorial/os-eclipse-jigloo/section5.html>
설명 | 이름 | 크기 | 다운로드 방식 |
---|---|---|---|
Part 4 source code | os-eclipse-jigloo.workflow.zip | 18KB | HTTP |
다운로드 방식에 대한 정보 |
교육
- Eclipse.org에서 이클립스 플랫폼에 대한 최신 정보를 얻을 수 있다.
- CloudGarden에서 Jigloo를 체크아웃할 수 있다.
- developerWorks의 "A gentle introduction to SWT and JFace" 연재 기사를 참조하여 SWT 애플리케이션을 만드는 방법을 배울 수 있다.
- "Migrate your Swing application to SWT"에서 기존 스윙 애플리케이션을 고성능 SWT 플랫폼으로 변경하는 방법을 배울 수 있다.
- "SWT, Swing or AWT: 나에게 맞는 것 찾기 (한글)"에서 SWT, 스윙, AWT의 장점과 단점을 살펴볼 수 있다.
- 이클립스 플러그인 시스템에 대한 기사에서 Jigloo와 같은 이클립스 플러그인을 만들 수 있는 방법을 배울 수 있다.
- "이클립스 RCP 응용 프로그램 커스터마이즈하기 (한글)"에서 SWT를 사용하여 이클립스 리치 클라이언트 애플리케이션을 만드는 방법을 익힐 수 있다.
- "Create native, cross-platform GUI applications, revisited"를 참조하여 리눅스에서 SWT를 사용한 GUI 애플리케이션을 개발하는 방법을 익힐 수 있다.
- 이클립스 SWT 위젯 레퍼런스 매뉴얼에서 다양한 SWT 위젯에 대해 알 수 있다.
- developerWorks의 "Introduction to Swing"을 참조하여 스윙에 입문할 수 있다.
- 이클립스 플랫폼에 대한 소개는 "Getting started with the Eclipse Platform"을 보라.
- "Eclipse 추천 도서 리스트 (한글)"를 확인하라.
- 한국 developerWorks에서 이클립스 기사를 검색해 보라.
- 한국 IBM developerWorks의 Eclipse 프로젝트 리소스 (한글)를 확인해 이클립스 기술을 향상시키라.
- 소프트웨어 개발자들의 흥미로운 인터뷰와 토론은 developerWorks 포드캐스트를 통해 들을 수 있다.
- developerWorks의 기술 행사와 웹 캐스트.
- IBM 오픈소스 개발자와 관련된 세계 곳곳에서 열리는 컨퍼런스, 전시회, 웹 캐스트와 기타 행사들을 확인하라.
- 한국 developerWorks의 오픈소스 존을 방문하여 다양한 정보, 도구, 프로젝트들을 사용하여 자신의 오픈소스 기술을 향상시킬 수 있으며 IBM 제품과 함께 사용할 수 있다.
제품 및 기술 얻기
- 이클립스 플랫폼 다운로드
- Jigloo 다운로드.
- IBM alphaWorks에서 최신 이클립스 기술을 확인할 수 있다.
- IBM 시험판 소프트웨어를 다운로드하거나 DVD로 받을 수 있다.
토론
- 이클립스 플랫폼 뉴스그룹에서 이클립스 관련 궁금증을 해결할 수 있을 것이다(링크를 클릭하면 기본 유즈넷 뉴스 리더 애플리케이션을 실행하고 eclipse.platform을 자동으로 열 것이다).
- The 이클립스 뉴스그룹에는 이클립스를 확장하고 사용하는 것에 관심 있는 사람들을 위한 많은 참고자료가 있다.
- 이클립스 플랫폼 SWT 메일링 리스트를 구독하여 최신 이클립스 소식을 얻을 수 있다.
- developerWorks 블로그와 developerWorks 커뮤니티에 참여할 수 있다.
<출처 :https://www.ibm.com/developerworks/kr/library/tutorial/os-eclipse-jigloo/section7.html>
이클립스용 GUI 빌더, Jigloo 시작하기 (한글) - 4
애플리케이션에서 사용할 데이터 모델 만들기
모델-뷰-컨트롤(MVC) 아키텍처에 익숙하다면 지금까지 만든 것이 애플리케이션의 뷰에 해당한다는 것을 알아차렸을 것이다. 이 부분이야 말로 Jigloo가 진가를 발휘하는 부분이다. 이제는 모델을 만들어야 한다. 다시 말하면 시스템에서 사용할 데이터를 가져오고 사용할 자바 코드를 만들어야 함을 의미한다. Jigloo는 여기서도 역시 빛을 발한다. Jigloo는 단순한 이클립스 플러그인으로 Jigloo를 사용할 때도 직접 이클립스의 강력한 기능들을 힘들이지 않고 바로 사용할 수 있다. 이클립스는 자바 코드 작성과 데이터를 다루는 데 매우 훌륭한 도구이기 때문에 Jigloo 역시 이런 작업을 하기에 적합하다.
언급했다시피, XML 형식으로 데이터를 저장하고 JAXB를 사용하여 XML을 읽고 쓰는 작업을 할 것이다. 따라서 스키마를 작성할 필요가 있다.
Listing 1. 워크플로우 XML 스키마
<?xml version="1.0" encoding="UTF-8"?> <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="org:developerworks:workflow" xmlns:dw="org:developerworks:workflow"> <element name="workflow" type="dw:workflow"/> <complexType name="workflow"> <sequence> <element name="user" type="dw:user" minOccurs="0" maxOccurs="unbounded"/> <element name="po" type="dw:purchaseOrder" minOccurs="0" maxOccurs="unbounded"/> </sequence> </complexType> <complexType name="user"> <sequence> <element name="username" type="string"/> <element name="role" type="dw:role"/> </sequence> <attribute name="id" type="integer" use="required"/> </complexType> <simpleType name="role"> <restriction base="string"> <enumeration value="worker"/> <enumeration value="manager"/> </restriction> </simpleType> <complexType name="purchaseOrder"> <sequence> <element name="priority" type="dw:priority"/> <element name="dateRequested" type="date"/> <element name="dateNeeded" type="date" minOccurs="0"/> <element name="itemName" type="string"/> <element name="itemDescription" type="string" minOccurs="0"/> <element name="quantityRequested" type="integer"/> <element name="url" type="anyURI" minOccurs="0"/> <element name="price" type="decimal"/> <element name="status" type="dw:orderStatus"/> <element name="submittedBy" type="integer"/> <element name="processedBy" type="integer" minOccurs="0"/> </sequence> <attribute name="id" type="integer" use="required"/> </complexType> <simpleType name="priority"> <restriction base="string"> <enumeration value="normal"/> <enumeration value="high"/> </restriction> </simpleType> <simpleType name="orderStatus"> <restriction base="string"> <enumeration value="pending"/> <enumeration value="approved"/> <enumeration value="rejected"/> </restriction> </simpleType> </schema> |
스키마에 해당하는 XML 파일에 바인딩할 자바 클래스를 만들기 위해 JAXB를 사용할 수 있다. 자바 6을 사용한다면 JAXB가 내장되어 있다. 자바 5를 사용한다면 썬에서 JAXB를 다운로드해야 한다. 여러분은 명령행 도구인 xjc를 사용하길 원할 수도 있다. 예를 들어 xjc workflow.xsd와 같이 사용할 수 있다. 이 명령어를 사용하여 workflow.xsd를 스키마 컴파일러가 파싱하고 클래스를 만들어낼 것이다. 그런 다음 클래스 파일들을 프로젝트에 복사하여 사용할 수 있다. 프로젝트에 XML 디렉터리를 만들고 그 안에 스키마 파일도 복사한다. 이제 예제로 사용할 XML 파일을 만들자.
Listing 2. 초기 XML 데이터
<?xml version="1.0" encoding="UTF-8"?> <dw:workflow xmlns:dw="org:developerworks:workflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="org:developerworks:workflow workflow.xsd "> <user id="0"> <username>homer</username> <role>worker</role> </user> <user id="1"> <username>bart</username> <role>manager</role> </user> <po id="0"> <priority>normal</priority> <dateRequested>2001-01-01</dateRequested> <dateNeeded>2001-01-01</dateNeeded> <itemName>stapler</itemName> <itemDescription>A great stapler</itemDescription> <quantityRequested>2</quantityRequested> <url>http://www.thinkgeek.com/homeoffice/gear/61b7/</url> <price>21.99</price> <status>pending</status> <submittedBy>0</submittedBy> </po> </dw:workflow> |
모든 요소들을 추가한 뒤 Package Explorer는 그림 26처럼 보일 것이다(필요하다면 JAXB jar 파일들을 자바 빌드 패스에 추가해야 한다).
그림 26. JAXB 클래스와 XML 파일이 추가된 Package explorer
|
Package Explorer에 몇 가지 파일들이 추가된 것을 확인할 수 있을 것이다. 바로 WorkflowDao와 XmlWorkflow다. WorkflowDao는 데이터를 사용할 때 필요로 하는 작업들을 정의한 인터페이스다(Listing 3).
Listing 3. WorkflowDao 인터페이스
package org.developerworks.workflow; import java.util.List; public interface WorkflowDao { public List<User> getUsers(); public List<PurchaseOrder> getAllOrders(); public List<PurchaseOrder> getAllPendingOrders(); public List<PurchaseOrder> getOrdersForUser(int userId); public void saveOrder(PurchaseOrder order); public void setOrderStatus(int orderId, OrderStatus status); } |
고전적인 데이터 접근 객체(Data Access Object) 패턴을 사용하고 있다. 간단하게 인터페이스를 정의하고 이 인터페이스에 대한 애플리케이션 코드를 작성한다. XML을 사용하여 JAXB 기반 구현을 할 것이다. 하지만 이 디자인을 사용하여 손쉽게 데이터베이스 기반 구현 같은 기타 구현으로 변경할 수 있다. 이 인터페이스의 구현체가 XmlWorkFlow다.
Listing 4. XmlWorkflow 인터페이스 구현
package org.developerworks.workflow; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; public class XmlWorkflow implements WorkflowDao { private static final String DATA_FILE = "data.xml"; private static XmlWorkflow instance; private Workflow workflow; private XmlWorkflow() { try { JAXBContext ctx = this.getContext(); Unmarshaller unm = ctx.createUnmarshaller(); File dataFile = this.getDataFile(); InputStream inputStream; if (dataFile.exists() && dataFile.length() > 0){ inputStream = new FileInputStream(dataFile); } else { inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("xml/"+DATA_FILE); } JAXBElement element = (JAXBElement) unm.unmarshal(inputStream); this.workflow = (Workflow) element.getValue(); } catch (JAXBException e) { e.printStackTrace(); throw new RuntimeException("Failed to read data file",e); } catch (FileNotFoundException e) { e.printStackTrace(); throw new RuntimeException("Could not open data file", e); } } public static XmlWorkflow getInstance(){ if (instance == null){ instance = new XmlWorkflow(); } return instance; } public List<PurchaseOrder> getAllOrders() { return this.workflow.getPo(); } public List<PurchaseOrder> getAllPendingOrders() { List<PurchaseOrder> allOrders = this.getAllOrders(); List<PurchaseOrder> pending = new ArrayList<PurchaseOrder>(); for (PurchaseOrder order : allOrders){ if (order.getStatus().equals(OrderStatus.PENDING)){ pending.add(order); } } return pending; } public List<PurchaseOrder> getOrdersForUser(int userId) { List<PurchaseOrder> allOrders = this.getAllOrders(); List<PurchaseOrder> userOrders = new ArrayList<PurchaseOrder>(); for (PurchaseOrder order : allOrders){ if (order.getSubmittedBy().intValue() == userId){ userOrders.add(order); } } return userOrders; } public List<User> getUsers() { return this.workflow.getUser(); } public void saveOrder(PurchaseOrder order) { int index = 0; for (PurchaseOrder po : this.workflow.getPo()){ if (po.getId().intValue() == order.getId().intValue()){ this.workflow.getPo().set(index, order); this.saveData(); return; } index++; } // add new order order.setId(new BigInteger(Integer.toString(this.workflow.getPo().size()))); this.workflow.getPo().add(order); this.saveData(); } public void setOrderStatus(int orderId, OrderStatus status) { for (PurchaseOrder po : this.workflow.getPo()){ if (po.getId().intValue() == orderId){ po.setStatus(status); this.saveData(); return; } } } private void saveData(){ File dataFile = this.getDataFile(); try { JAXBContext ctx = this.getContext(); Marshaller marshaller = ctx.createMarshaller(); FileOutputStream stream = new FileOutputStream(dataFile); marshaller.marshal(this.workflow, stream); } catch (JAXBException e) { e.printStackTrace(); throw new RuntimeException("Exception serializing data file",e); } catch (FileNotFoundException e) { e.printStackTrace(); throw new RuntimeException("Exception opening data file"); } } private File getDataFile() { String tempDir = System.getProperty("java.io.tmpdir"); File dataFile = new File(tempDir + File.separatorChar + DATA_FILE); return dataFile; } private JAXBContext getContext() throws JAXBException { JAXBContext ctx = JAXBContext.newInstance("org.developerworks.workflow"); return ctx; } public static void main(String[] args){ XmlWorkflow dao = XmlWorkflow.getInstance(); List<User> users = dao.getUsers(); assert(users.size() == 2); for (User user : users){ System.out.println("User: " + user.getUsername() + " ID:" + user.getId()); } List<PurchaseOrder> orders = dao.getAllOrders(); assert(orders.size() == 1); for (PurchaseOrder order : orders){ System.out.println("Order:" + order.getItemName() + " ID:" + order.getId() + " Status:" + order.getStatus()); } PurchaseOrder order = orders.get(0); order.setStatus(OrderStatus.APPROVED); order.setProcessedBy(new BigInteger("1")); dao.saveOrder(order); } } |
위에서 만들었던 예제 파일을 초기에 읽어 들이는 것을 확인할 수 있지만 시스템 임시 폴더에 data.xml로 변경사항을 저장한다. 데이터를 저장하기에 가장 안전한 장소는 아니지만 예제 애플리케이션에 사용하기에는 적절하다. 이 클래스 파일 안에 간단한 main 메서드가 있는 것 또한 볼 수 있다. JAXB가 동작하는 것을 간단하게 단위 테스트하기 위한 용도로 만들었다. 만약에 자바 5를 사용한다면 JAXB jar 파일을 프로젝트의 클래스패스에 추가해야 한다. 계속해서 작업하려면 그 파일들을 복사하여 프로젝트에 추가하거나 프로젝트 외부에 그 파일들이 위치한 장소를 참조할 수 있도록 해야 한다.
|
애플리케이션과 인터랙트하기 전에 모든 요소를 초기화해야 한다. 먼저 애플리케이션에서 사용할 모델 객체를 몇 개 선언해야 한다. Listing 5에 있는 코드를 추가하여 WorkflowMain
멤버 변수를 추가한다.
Listing 5. 모델 객체 선언
// Data Model Objects private java.util.List<User> users; private User user; // Service Object private WorkflowDao dao = XmlWorkflow.getInstance(); |
코드에 접근하려면 Workflow.java 파일을 마우스 오른쪽 클릭을 하고 Open With > Java Editor를 선택한다.
애플리케이션의 initGUI()
메서드 코드를 수정한다. 사용자 명단을 초기화하기 위해 private 메서드를 만들겠다.
Listing 6. 사용자 메서드 만들기
private void initUserList(){ this.users = dao.getUsers(); for (User u : users){ this.userListCombo.add(u.getUsername()); } } |
userListCombo
를 정의한 후 initGUI()
에서 이 메서드를 호출한다.
Listing 7. 사용자 메서드 호출
{ userListCombo = new Combo(this, SWT.NONE); userListCombo.setText("Users"); userListCombo.setBounds(28, 35, 105, 21); this.initUserList(); } |
|
뷰와 모델을 만들었다. 이제 그 둘을 엮어 보자. 컨트롤러가 필요하다. SWT(와 스윙)는 모든 UI 프레임워크에서 사용하는 간단한 기술을 사용한다. 바로 이벤트 주도 시스템이라는 것이다. 이벤트를 사용하여 언제 모델에 있는 작업을 호출하고 뷰를 수정할지 알려줄 수 있다.
이제 다시 비주얼 디자이너로 돌아가자. 모델과 엮기를 원하는 첫 번째 UI 이벤트는 사용자 콤보 리스트에서 사용자를 선택했을 때다. 콤보 컨트롤을 선택하고 GUI 속성 뷰에서 Event 탭으로 변경하면 그림 27에 보이는 것과 같은 화면을 확인할 수 있을 것이다.
그림 27. 콤보 컨트롤 이벤트에 접근하기
몇몇 리스너들을 콤보 컨트롤에서 볼 수 있다. SelectionListener
를 선택한다. 이 리스너는 콤보 컨트롤에서 무언가를 선택할 때마다 SelectionEvent
를 발생시킨다. 이 이벤트를 다룰 것을 익명 메서드로 그것을 인라인에서 다룰 수도 있고이 이벤트를 다룰 메서드를 정의할 수도 있다. 후자를 선택하겠다. 이렇게 하면 userListComboWidgetSelected
라는 메서드가 생성된다. 이벤트를 다루기 위한 코드는 Listing 8에 나와있다.
Listing 8. 사용자 콤보 리스트 선택 코드
private void userListComboWidgetSelected(SelectionEvent evt) { int index = this.userListCombo.getSelectionIndex(); if (index >= 0){ this.user = this.users.get(index); System.out.println("User selected="+this.user.getUsername()); purchaseOrderTable.removeAll(); java.util.List<PurchaseOrder> orders; boolean isManager = this.user.getRole().equals(Role.MANAGER); if (isManager){ orders = dao.getAllPendingOrders(); } else { orders = dao.getOrdersForUser(this.user.getId().intValue()); } this.approveButton.setVisible(isManager); this.rejectButton.setVisible(isManager); for (PurchaseOrder order : orders){ displayPurchaseOrder(order); } } } |
여기서 살펴봐야 할 것들이 많다. 먼저 사용자가 관리자인지 확인한다. 관리자가 아니면 사용자의 모든 구매 주문 목록을 보여준다. 관리자가 맞다면 대기중인 주문 목록만을 보여준다. 다음으로 관리자가 아니면 승인/취소 버튼이 보이지 않도록 한다. 마지막으로 purchaseOrderTable
에 있는 모든 주문 데이터들을 데이터 접근 객체를 사용해 가져와 보여준다.
이제 Approve 버튼에 이벤트를 추가한다. 비주얼 디자이너에 보이는 Approve 버튼을 선택하고 GUI 속성 창에 있는 이벤트 탭으로 이동한다. 버튼을 선택했을 때 실행되어야 하기 때문에 그림 28에 보이는 것처럼 여기서도 selection event를 사용할 것이다.
그림 28. Approve 버튼 선택 이벤트 설정
그리고 나서 이 이벤트를 다룰 코드를 추가한다.
Listing 9. Approve 버튼 선택 이벤트 코드
private void approveButtonWidgetSelected(SelectionEvent evt) { TableItem[] selected = this.purchaseOrderTable.getSelection(); if (selected != null){ for (TableItem item : selected){ this.dao.setOrderStatus(Integer.parseInt(item.getText(4)), OrderStatus.APPROVED); item.setText(3, OrderStatus.APPROVED.toString()); } } } |
reject 버튼도 이와 매우 비슷하게 할 수 있다. 선택 이벤트 리스너를 추가하고 비슷한 코드를 실행한다. 유일한 차이가 있다면 주문 상태를 APPROVED
가 아닌 REJECTED
로 바꾼다는 것이다.
Listing 10. Reject 버튼 코드
private void rejectButtonWidgetSelected(SelectionEvent evt) { TableItem[] selected = this.purchaseOrderTable.getSelection(); if (selected != null){ for (TableItem item : selected){ this.dao.setOrderStatus(Integer.parseInt(item.getText(4)), OrderStatus.REJECTED); item.setText(3, OrderStatus.REJECTED.toString()); } } } |
이제 남은 건 Submit 버튼이다. 이 버튼을 사용하여 새로운 구매 주문을 추가할 수 있어야 한다. 이 버튼 역시 다른 버튼들과 유사하다. 선택 이벤트에 대한 이벤트 핸들러를 그림 29처럼 추가한다.
그림 29. PO 버튼 선택 리스너 추가
이벤트를 다룰 코드를 추가한다.
Listing 11. PO 버튼 이벤트 핸들러 코드 추가
import java.math.BigDecimal; import java.math.BigInteger; import java.util.GregorianCalendar; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; ... private void addButtonWidgetSelected(SelectionEvent evt) { try { this.addPurchaseOrder(); } catch (Exception e) { throw new RuntimeException("Exception adding purchase order",e); } this.formItemText.clearSelection(); this.formPriceText.clearSelection(); this.formQuantityText.clearSelection(); } private void addPurchaseOrder() throws Exception{ String item = this.formItemText.getText(); String priceString = this.formPriceText.getText(); String quantityString = this.formQuantityText.getText(); BigDecimal price = new BigDecimal(priceString); BigInteger quantity = new BigInteger(quantityString); PurchaseOrder po = new PurchaseOrder(); int num = this.dao.getAllOrders().size(); String numString = Integer.toString(num); BigInteger newId = new BigInteger(numString); po.setId(newId); po.setItemName(item); po.setPrice(price); po.setQuantityRequested(quantity); po.setPriority(Priority.NORMAL); po.setStatus(OrderStatus.PENDING); po.setSubmittedBy(this.user.getId()); GregorianCalendar cal = (GregorianCalendar) GregorianCalendar.getInstance(); DatatypeFactory factory = DatatypeFactory.newInstance(); XMLGregorianCalendar now = factory.newXMLGregorianCalendar(cal); po.setDateRequested(now); this.dao.saveOrder(po); this.displayPurchaseOrder(po); } private void displayPurchaseOrder(PurchaseOrder order) { String[] row = new String[] {order.getItemName(), order.getPrice().toString(), order.getQuantityRequested().toString(), order.getStatus().toString(), order.getId().toString()}; TableItem tableItem = new TableItem(purchaseOrderTable,0); tableItem.setText(row); this.purchaseOrderTable.showItem(tableItem); } |
|
GUI를 테스트해야 할 시간이다. 그림 30처럼 클래스를 오른쪽 클릭을 한 후 Run As > SWT Application을 선택한다.
그림 30. 애플리케이션 실행
이렇게 하면 애플리케이션이 실행된다.
그림 31. 워크플로우 애플리케이션
기본 데이터로 대기중인 구매 주문을 넣어뒀기 때문에 사용자 bart를 선택한 뒤 구매 주문을 승인할 수 있다.
그림 32. 주문 승인
사용자를 homer로 변경한 뒤 구매 주문의 상태를 확인해 보자.
그림 33. 사용자가 자기 PO 보기
새로운 주문을 추가할 수도 있다.
그림 34. PO 추가
Add PO를 클릭하여 테이블에 구매 주문을 추가할 수 있다.
그림 35. 새 PO 추가
다시 bart로 변경하여 승인 또는 취소할 수 있다.
그림 36. 업데이트된 관리자 화면
이클립스용 GUI 빌더, Jigloo 시작하기 (한글) - 3
GUI 설정하기
시스템에 대한 기본적인 설계를 했기 때문에 이제는 코드를 작성할 차례다. GUI와 Jigloo 사용법에 집중하도록 하겠다.
워크플로우 애플리케이션 GUI 설계를 시작해 보자. 먼저, 워크플로우 애플리케이션에서 사용할 패키지를 만들자. Package Explorer 창에서 프로젝트를 마우스 오른쪽 클릭을 한 후 New > Package를 선택한다. 기본 패키지를 org.developerworks.workflow
로 하겠다. Finish를 클릭한다.
이제 이 패키지에 애플리케이션을 작성해보자. 패키지에서 오른쪽 클릭한 후 New > Other를 선택한다. 그럼 “Select a wizard” 화면을 볼 수 있을 것이다. 모든 Jigloo 마법사들이 이 화면에 있는 GUI Form 폴더에 있을 것이다. 그림 3과 같이 그 폴더를 열고 나서 SWT 폴더를 열고 SWT Main Application 옵션을 선택한다.
그림 3. 마법사 선택
Next를 선택하여 New SWTApp 마법사를 시작한다. 그림 4처럼 main 클래스를 WorkflowMain
으로 하겠다.
그림 4. 새 SWTApp 마법사
나머지는 그대로 두고 Finish를 클릭한다.
이제 여러분의 이클립스 워크스페이스가 그림 5처럼 보일 것이다.
그림 5. WorkflowMain을 보여주는 이클립스
여기서 주목해야 할 것들이 여러 가지 있다. 먼저 메인 창은 Jigloo에서 사용하는 커스텀 뷰를 보여준다. 이 뷰는 몇 개의 화면으로 나뉘어 있다. 맨 위에는 Jigloo 비주얼 디자이너가 있다. GUI에서 드래그 앤 드롭을 사용하여 만들 수 있기 때문에 이 화면을 가장 자주 사용하게 될 것이다. 이 뷰에 대해서는 좀 더 자세히 알아보겠다. 나뉘어져 있는 화면 맨 아래에는 나중에 자세히 살펴 볼 애플리케이션의 자바 코드가 있다. Jigloo가 어떻게 코드를 만들어 놨는지 살펴보길 바란다.
메인 창 바로 아래에는 GUI 속성 창이 있다. 이 뷰에서는 비주얼 다자이너 창에서 선택된 비주얼 컴포넌트의 속성들을 자세히 보여준다. 이 뷰 역시 자주 사용할 것이다. 마지막으로 Package Explorer 뷰에 com.cloudscape.resource
패키지가 만들어진 것을 볼 수 있다. 이 패키지는 자동으로 만들어진 SWTResourceManager
클래스를 포함하고 있다. 클래스의 이름이 암시하듯이 이 클래스는 글꼴, 색, 이미지와 같은 SWT 자원을 관리한다. Jigloo와 같은 비주얼 디자이너 도구를 사용해본 경험이 있다면, 이런 클래스를 본 적이 있을 것이다. 보통 이런 종류의 클래스는 절대로 수정하지 않도록 권하고 있다. 이 클래스를 수정할 경우 에러를 발생할 가능성이 많으며 아마도 수정하면 예외를 던지거나 수정을 가하더라도 아마 재정의될 것이다. 그러나 SWTResourceManager
의 경우에는 그렇지 않다. 이 클래스를 수정하여 Jigloo와 애플리케이션에서 사용할 기본 글꼴과 같은 이 클래스가 관리할 자원을 변경할 수 있다. 그리고 Jigloo가 그 변경사항들을 무시한 채 덮어쓰지도 않을 것이며 에러를 발생시키지도 않을 것이다.
비주얼 디자이너를 사용해 보자. 만약 이클립스를 사용하는 데 익숙하다면, 창 제목 막대를 더블 클릭하면 창을 최대화할 수 있음을 알고 있을 것이다. 그림 6처럼 WorkflowMain
창을 최대화하자.
그림 6. WorkflowMain 최대화
이렇게 함으로써 비주얼 디자이너로 작업할 때 좀 더 예측하기가 수월하며 오른쪽 아래에 있는 GUI 속성에 접근하기도 편리해진다. 또한 우상단에서 GUI를 계층적으로 보여준다. 현재는 매우 단순하지만 GUI에 컴포넌트를 추가하면 그것들이 계층(hierarchy)에 추가되는 것을 확인할 수 있다. 그뿐만 아니라 애플리케이션을 미리 보고 테스트할 수 있는 유용한 기능을 제공한다. 이제 비주얼 디자이너에 조금 익숙해졌을 것이다. 이것을 사용하여 애플리케이션을 만들어 보자.
|
스윙이나 SWT 애플리케이션을 개발해 본 적이 있다면, 다양한 종류의 레이아웃 관리자에 익숙할 것이다. 레이아웃 관리자는 자바 GUI 애플리케이션의 강력한 기능 중 하나다. GUI의 레이아웃을 구성할 때 다양한 종류의 알고리즘을 적용할 수 있다. 자바의 모토는 항상 “한 번 만들면 거의 어디서든 동작한다”였고 이러한 레이아웃 알고리즘 역시 이 모토와 일맥상통한다. 이것들을 사용하면 GUI 애플리케이션의 레이아웃이 GUI를 보여주는 창이나 화면에 놓이는 도형들의 크기를 고려하지 않아도 된다.
이런 레이아웃 알고리즘의 유일한 단점은 개발자들이 학습을 해야 한다는 것이며 특히 구성요소들을 절대 좌표에 위치시켜야 하는 플랫폼을 사용하던 개발자들에겐 더욱 심하다. 절대 좌표를 사용하면 컴포넌트들을 화면에서 원하는 위치에 놓을 수 있고 그럼 구성요소들은 그 곳에 놓인다. 이런 레이아웃의 문제점은 창의 크기가 자유롭게 변하는 경우 “깨짐” 현상이 나타난다는 것이다.
예제 애플리케이션은 매우 간단한 프로그램이기 때문에 절대 좌표 레이아웃을 사용할 것이다. 설정은 GUI 속성에서 할 수 있다. 그림 7처럼 Layout 탭을 클릭하고 Layout 값을 Absolute로 변경한다.
그림 7. 레이아웃 설정
다음으로 애플리케이션 크기를 좀 더 크게 해보자. 그렇게 하려면 메인 창의 오른쪽 아래 구석을 마우스로 클릭하여 잡은 다음에 원하는 크기만큼 잡아당겨 늘이면 된다.
이제 애플리케이션에 컴포넌트를 추가할 준비가 끝났다. 여러 사용자 계정 중에 로그인할 사용자 계정을 선택할 수 있어야 한다. 또한 로그인 화면을 만들어 사용자 이름과 비밀번호를 입력할 수 있어야 하지만 최대한 단순하게 만들자. drop-down 리스트를 사용하여 로그인할 사용자를 선택하자.
비주얼 디자이너 위에 툴바가 있고 그곳에서 여러 컴포넌트들을 선택하여 애플리케이션에 추가할 수 있다. 이것은 탭으로 되어 있는 툴바다. 컨트롤을 추가해야 되기 때문에 컨트롤 탭을 선택한다. 각각의 컨트롤은 직관적인 아이콘으로 보이고 있지만 각 아이콘 위에 마우스를 가져가면 이름을 확인할 수 있다. combo 컨트롤을 선택하고 메인 창 위에 놓는다. 그렇게 하면 그림 8처럼 속성 편집 창에 새로운 컨트롤에 대한 정보가 표시된다.
그림 8. 콤보 컨트롤용 속성 편집창
컨포넌트의 이름을 좀 더 직관적인 이름인 userListCombo
로 수정하고 Users의 기본 텍스트를 준다. OK를 클릭하면 애플리케이션에서 볼 수 있다.
이제 사용자를 선택하고 바꿀 수 있는 방법이 생겼다. 사용자를 선택했을 때 그 사용자들이 진짜로 원하는 것이 무엇일까? 작업자들은 그들이 입력했던 구매 주문 데이터와 주문 상태를 보고 싶을 것이다. 관리자들은 어떤 주문들이 승인 또는 취소를 기다리고 있는지 보고 싶을 것이다. 구매 주문을 보여주기 위한 테이블을 만들어 보자.
테이블을 추가하기 위해 툴바 탭에서 컨테이너 탭으로 바꾼다. 테이블처럼 보이는 아이콘을 찾아 선택한다. 테이블을 처음 선택하면 마우스를 사용하여 원하는 위치에 그것을 놓을 수 있다. 위에서 추가했던 콤보 리스트 바로 아래에 놓는다. 위치를 정하면 테이블 이름을 묻는 대화창이 나타날 것이다. 콤보 리스트의 이름으로 직관적인 purchaseOrderTable
을 입력하자. OK를 클릭하면 테이블이 비주얼 디자이너에 보일 것이다.
테이블 크기도 오른쪽 아래 코너를 잡아 좀 더 크게 만들 수 있다. 이 방법으로 좀 더 테이블을 크게 설정하자. 테이블 속성은 이클립스 오른쪽 아래에 있는 GUI 속성 뷰에 보일 것이다. Properties 탭을 클릭하고 속성 목록 중에서 Expert를 펼친다. 그림 9에 보이는 것처럼 체크박스를 클릭하여 lines-visible 속성을 true로 설정하자.
그림 9. 테이블 속성 편집
테이블에 칼럼 몇 개를 추가해 보자. 툴바에 있는 컨테이너 탭에서 이전에 선택했던 테이블 아이콘 바로 오른쪽에 있는 Add TableColumn to Table 아이콘을 선택한다. 이제 TableColumn을 purchaseOrderTable
에 내려놓으면 TableColumn
속성 편집기가 보일 것이다. 주문한 물건의 이름을 보여줄 것이 때문에 poItemNameColumn
이라는 적절한 이름으로 설정한다. 이 값은 테이블의 칼럼 이름으로 사용할 것이기 때문에 Item에 입력하고 OK를 클릭한다.
이것을 추가한 뒤에 비주얼 디자이너는 그림 10처럼 보일 것이다.
그림 10. 칼럼 하나를 추가한 후의 비주얼 편집기
그런데 지금, 테이블에서 칼럼 이름을 볼 수 없을지도 모른다. 그렇다면 이것은 테이블의 Expert 속성을 변경하여 보이게 할 수 있다. 테이블을 클릭하고 GUI 속성 창으로 다시 이동한다. Expert 섹션을 열고 headerVisible
속성을 Figure 11처럼 설정한다.
그림 11. 테이블 속성 편집
속성 값은 체크 박스를 사용하여 true나 false로 설정할 수 있다. true로 바꾸고 나면 테이블에서 header를 볼 수 있고 첫 번째 칼럼의 item 레이블을 볼 수 있을 것이다.
계속해서 같은 방법으로 네 개의 칼럼(Price, Quantity, Status, ID)를 추가하자. 모두 추가했으면 그림 12와 같이 보일 것이다.
그림 12. 테이블에 칼럼 전부 추가
필요한 주문을 전부 보여줄 수 있다. 여기에서 주문을 승인할지 취소할지 선택할 수 있는 방법이 필요하다. 그렇게 할 수 있도록 각각의 기능을 할 수 있는 버튼들을 추가하자. 버튼을 추가하려면 툴바에 있는 컨트롤 탭으로 이동하여 버튼 컨트롤 아이콘을 선택한다. 이 버튼 역시 원하는 위치에 놓을 수 있다. 테이블 바로 아래에 위치시킨다. 버튼을 위치시키면 버튼의 속성 편집창이 나타날 것이다. 첫 번째 버튼은 구매 주문을 승인하기 위한 버튼으로 사용할 것이다. 따라서 이름은 approveButton이고 텍스트 레이블은 Approve로 하자. OK를 클릭하면 비주얼 편집기에 새로 추가한 버튼이 보일 것이다.
간단하게 Reject 버튼을 애플리케이션에 추가할 것이다. 이 모든 과정을 마친 뒤 비주얼 디자이너는 그림 13처럼 보일 것이다.
그림 13. 버튼 둘 다 추가
추가한 두 개의 버튼이 줄이 맞지 않다는 것을 위 그림에서 알아차렸을 것이다. 맨 눈으로 그런 일을 하는 것은 쉽지 않지만 다행히도 Jigloo가 컴포넌트들의 줄을 맞추는 쉬운 방법을 제공한다. 간단하게 두 개의 컴포넌트를 선택하고(이 경우 버튼 두 개) 비주얼 디자이너 왼쪽에 있는 스타일링 툴바를 사용하면 된다. Align tops of selected elements 버튼을 사용하면 그림 14처럼 보일 것이다.
그림 14. 버튼 정렬
버튼들의 모양이 보기 좋게 자리를 잡았다. Jigloo를 사용하면 GUI 모양을 매우 전문적으로 보이게 하는 것이 쉽다.
이제 사용자를 선택할 수 있고 구매 주문 내역을 볼 수 있으며 구매 주문을 승인 또는 거절할 수 있다. 이제 남은 건 구매 주문을 추가하는 기능이다. 새로운 구매 주문을 만들기 위한 데이터를 입력할 때 사용할 폼을 만들 것이다. 폼에는 구매 주문 모델을 만드는 데 필요한 다양한 데이터 유형에 해당하는 여러 가지 컴포넌트과 관련되어 있다. 이 컴포넌트들을 그룹화해 만들어 보자. 모든 컴포넌트를 그룹으로 처리하는 것은 매우 유용하다. 예를 들어, 특정 사용자만 새로운 구매 주문을 추가할 수 있도록 할 수 있다. 그런 권한이 있는 사용자에게만 폼이 보이도록 설정할 수 있다. 이 때 모든 컴포넌트가 그룹으로 되어 있을 때 더 쉽게 할 수 있다.
그룹을 만들려면 Composite 컴포넌트를 애플리케이션에 추가한다. 툴바의 컨테이너 탭으로 이동하고 Figure 15처럼 Composite 아이콘을 선택한다.
그림 15. 툴바를 사용해 컴포지트 추가
원하는 위치에 놓는다. 오른쪽 공간이 많이 비어 있으므로 그곳에 놓자. 그림 16처럼 Composite 속성 편집 창이 나오는 것에 익숙해졌을 것이다.
그림 16. 컴포지트 속성 편집창
여기서 추가한 Composite이 하는 일의 의도에 적당한 이름인 itemForm
을 입력한다. 레이아웃으로 절대 좌표를 사용하는 것에 유의하자. Composite도 자신의 레이아웃 관리자를 사용할 수 있기 때문이다. OK를 클릭한 후 그림 17과 같이 비주얼 디자이너에 보일 것이다.
그림 17. 비주얼 디자이너의 컴포지트
오른쪽 아래 코너를 잡아 당겨 Composite의 크기를 조정할 수 있다. 폼 요소들이 모두 자리 잡을 수 있을 만큼 넉넉하게 크기를 확장시키자.
이제 폼을 디자인해 보자. 폼은 텍스트 박스와 레이블을 주로 사용한다. 툴바를 사용하여 이런 요소들을 추가한다. Control 탭으로 이동하여 레이블 아이콘을 선택한다. 예상했듯이 원하는 위치에 내려 놓을 수 있다. 폼 컨테이너 왼쪽 위에 놓도록 하자. 이번에는 레이블 컨트롤에 대한 속성 편집창이 그림 18처럼 나타날 것이다.
그림 18. 라벨 컨트롤 속성 편집창
이제 비주얼 디자이너에서 레이블을 볼 수 있을 것이다. 다시 오른쪽 아래 코너를 사용하여 원하는 크기로 조정하자.
레이블 바로 오른쪽에 텍스트 박스를 추가해 사용자가 구매 주문할 물건의 이름을 적을 수 있도록 만든다. 툴바에서 Text Control을 선택한다. 이제 원하는 위치에 놓으면 그림 19와 같은 속성 편집창이 나타난다.
그림 19. 텍스트 컨트롤 속성 편집창
formItemText
라는 이름을 주자. Text 값에는 아무것도 입력하지 않는다. 기본 값을 줄 수도 있지만 그럴 필요는 없다. 이제 비주얼 편집기에서 텍스트 박스를 볼 수 있고 크기를 조정할 수 있다.
계속해서 두 개의 필드 Price와 Quantity를 이와 같은 방법으로 추가한다. 각각 레이블과 텍스트 박스를 사용한다. 모두 추가하면 비주얼 디자이너는 그림 20처럼 보일 것이다.
그림 20. 가격 및 수량 필드 추가
Quantity의 기본 값을 1로 설정한 것에 주의하기 바란다. 구매 주문을 할 때 필요로 하는 기본적으로 필요로 하는 요소들이다. 이제 폼을 처리하기 위해 보내는 방법이 필요하다. 이런 기능을 할 버튼을 추가하자. 툴바에서 Button 컨트롤을 선택하여 추가하는 작업은 이전에 살펴보았다. 이번에는 다른 방법으로 해보자. Composite 안에서 마우스 오른쪽 버튼을 클릭하고 그림 21처럼 Add SWT Object > Button을 선택한다.
그림 21. 컨텍스트 메뉴를 사용해 버튼 추가
이제 많이 익숙한 속성 편집창이 뜰 것이다. 컴포넌트의 이름을 addButton으로 하고 텍스트는 Add PO로 한다. OK를 클릭하면 비주얼 디자이너에 그림 22처럼 새로운 버튼이 추가된 것을 볼 수 있다.
그림 22. 버튼 추가
이 방법과 툴바를 사용하는 방법의 가장 큰 차이는 위치를 조정하고 다른 컨트롤들과의 배열을 맞출 필요가 있다는 것이다. 이 방법을 사용하면 다른 배열 컨트롤을 사용할 수 있는 기회가 생긴다. 세 개의 텍스트 박스와 추가 버튼을 선택하고(shift 키를 사용해 마우스를 클릭하면 여러 컨트롤을 선택할 수 있다) 그림 23처럼 Space selected elements evenly vertically"를 클릭한다.
그림 23. 요소 간 위아래 간격을 똑같게 조정하기
다른 정렬 컨트롤러들도 자유롭게 사용할 수 있다. 모든 작업을 완료하면 그림 24처럼 보일 것이다.
그림 24. 최종 UI 디자인
|
이제 GUI를 보여주는 멋진 그림을 얻었다. 하지만 아직 어떤 데이터도 연결되지 않았기 때문에 기능적으로 동작하지는 않는다. 간단하게 추가할 수 있지만 그전에 Jigloo의 멋진 기능 중 하나인 미리보기를 사용하기에 적절한 시점이다. 창의 오른쪽 위에 있는 Preview를 클릭한다. 그림 25에서 볼 수 있듯이 GUI의 미리보기가 제공될 것이다.
그림 25. 워크플로우 GUI 미리보기
우리가 마지막으로 만들었던 애플리케이션의 모양과 정확히 일치한다. Windows® 애플리케이션과 모양이 똑같다는 것에 주목하기 바란다. 윈도우에서 SWT를 사용하고 있기 때문이다. 언급했다시피 SWT는 네이티브 위젯을 사용하기 때문이다.
<출처 :https://www.ibm.com/developerworks/kr/library/tutorial/os-eclipse-jigloo/section3.html>
이클립스용 GUI 빌더, Jigloo 시작하기 (한글) - 2
개요
이번 절에서는 Jigloo 플러그인을 설치하고 설정하기 전에 이것으로 무엇을 할 수 있는지 살펴보겠다.
는 비주얼 자바 GUI 빌더다. Jigloo는 CloudGarden에서 만든 이클립스(와 WebSphere® Studio) 플러그인이다. 상업적인 용도가 아니라면 공짜로 쓸 수 있다. CloudGarden에서 라이선스를 받아 상업적인 용도로 사용할 수 있다.
Jigloo는 스윙과 SWT GUI를 개발할 수 있는 고전적인 위지위그(WYSIWYG) 편집기다. 만약 자바로 데스크톱 애플리케이션을 개발하고 싶다면 Jigloo가 최고의 대안이 될 것이다. 하지만 그것뿐만이 아니다.
Jigloo는 라운드 트리핑(round-tripping)을 지원한다. 이 말은 코드 수정을 통해서만 GUI에 변화를 줄 수 있는 것이 아니라 GUI에 직접 변화를 주어 코드가 수정되는 것을 확인할 수 있다는 뜻이다. 기존에 만들어진 GUI를 변경하고 싶을 때도 Jigloo는 훌륭한 대안이 될 수 있다. 이미 존재하고 있는 애플리케이션을 분석하고 화면을 통해 수정할 수 있도록 도와준다. 그리고 Jigloo를 NetBeans나 JBuilder와 같은 IDE와도 함께 쓸 수 있다.
Jigloo는 스윙/SWT 호환을 염두에 두고 개발되었다. 각각의 기술이 제공하는 다양한 레이아웃 옵션을 지원한다. 새로운 UI를 만드는 것뿐만 아니라 Jigloo를 사용하여 스윙과 SWT를 서로 변환할 수도 있다. SWT_AWT 브리지(bridge)를 사용하여 스윙 컴포넌트를 SWT 애플리케이션에 포함시킬 수도 있다. 하지만 이번 튜토리얼에서 우리는 SWT GUI를 사용하는 워크플로우 애플리케이션을 개발하겠다.
Jigloo는 이클립스 플러그인이기 때문에 설치가 매우 쉽다. 만약 몇 년 간 이클립스를 사용해오고 있다면 플러그인을 다운로드해 이클립스 설치 디렉터리에 압축을 푸는 방법으로 플러그인을 설치해봤을 것이다. 하지만 최신 버전 이클립스를 사용하면 더 쉽게 설치할 수 있다. 이클립스 업데이트 관리자 기능이 바로 그것이다. 이 기능을 사용하려면 Help > Software Updates > Find and Install을 선택하면 된다. 그러면 Install/Update 대화창이 열린다. 이 때 “Search for new features to install” 옵션을 선택한다. 그리고 나서 Next를 클릭하면 Update Site 대화창으로 이동한다.
만약 다른 이클립스 플러그인들이 이미 설치되어 있다면 “Sites to include in search”에 이미 등록되어 있는 다른 사이트들이 보일 것이다. 이 때 목록에 보이는 모든 사이트들을 선택하지 않는다. 아래 보이는 New Update Site 대화창을 볼 수 있도록 New Remote Site를 클릭한다.
그림 1. 새 업데이트 사이트
이 때 중요한 것은 URL 필드에 http://cloudgarden1.com/update-site
를 입력하는 것이다. Name 필드에는 어떤 것이든 원하는 값을 입력할 수 있지만 Jigloo Update Site처럼 무슨 사이트인지 설명할 수 있는 내용이 좋을 것이다. OK를 클릭하면 Update Site 대화창으로 다시 이동하지만 이번에는 방금 입력한 업데이트 사이트가 목록에 포함되어 보일 것이다. Finish를 클릭하면 Search Results 대화창으로 이동한다. Search Results 대화창에서 Jigloo를 선택하고 Next를 클릭한다. 그럼 Feature License로 이동할 것이다.
이미 언급했던 것처럼 Jigloo는 무료로 사용할 수 있지만 상업적인 용도가 아닐 경우에만 그렇다. 만약 상용으로 사용할 것이라면 CloudGarden에서 전문가용 라이선스를 받아야 한다. 라이선스를 읽은 후 “I accept the terms in the license agreement”를 선택하여 라이선스에 동의한다. 그리고 나서 간단하게 Next를 클릭하면 Installation details 대화창으로 이동한다. Finish를 클릭하면 Feature Verification 대화창이 나온다. Install이나 Install All을 선택하면 된다. 그럼 이제 설치를 시작하게 된다. 이클립스는 CloudGarden에서 플러그인을 다운로드하고 설치할 것이다. 설치가 끝나면 이클립스를 재시작해야 설치 과정이 끝난다.
축하한다! 이제 Jigloo 설치가 끝났다. 다른 이클립스 플러그인들과 마찬가지로 전혀 어렵지 않았다. 이제 Jigloo를 시작할 준비가 끝났다. 간단한 설정부터 시작하자.
|
이클립스를 사용하여 새 자바 프로젝트를 생성한다. File > New > Project에서 Java Project를 클릭한다. Next를 클릭하여 New Project 창으로 이동한다.
이 튜토리얼에서 프로젝트의 이름은 “workflow”로 하겠다. 물론 여러분이 원하는 이름을 줄 수도 있다. 이름을 준 뒤 Finish를 클릭한다.
이미 언급했다시피, Jigloo를 사용해 스윙 또는 SWT GUI를 만들 수 있다. 이 튜토리얼에서 우리는 SWT GUI를 만들 것이다. 이 경우 약간의 추가 설정이 필요하다. SWT JAR 파일을 프로젝트의 클래스패스에 추가해야 한다. 이 작업을 하기 위해 프로젝트를 클릭하고 메뉴에서 File > Properties를 선택한다. 그럼 Project Properties 화면이 나타나고 여기서 왼쪽 네비게이션의 Java Build Path를 선택하여 Java Build Path 창으로 이동한다.
Libraries 탭을 클릭한 후 Add External JARs 버튼을 클릭한다. 그러면 file explorer 창이 뜬다. 아래 보이는 그림처럼 이클립스를 설치한 디렉터리인 $ECLIPSE_HOME의 하위 디렉터리인 $ECLIPSE_HOME/plug-ins로 이동한다.
그림 2. 이클립스 플러그인 디렉터리
여기서 org.eclipse.swt.X.X.X.jar를 볼 수 있을 것이다. X.X.X는 사용하는 플랫폼 종류와 설치한 이클립스 버전에 따라 다를 수 있다. Open을 클릭하고 Java Build Path 화면으로 이동하여 OK를 클릭한다.
기본적인 자바 프로젝트를 만들고 SWT 라이브러리를 클래스패스에 추가했다. 이제 Jigloo를 사용하여 워크플로우 애플리케이션 디자인과 개발을 시작할 준비가 끝났다.
예제는 매우 간단한 워크플로우 애플리케이션이다. 두 종류의 사용자가 있는데 작업자와 관리자다. 작업자는 구매 요청을 입력할 때 이 애플리케이션을 사용한다. 작업자들은 구매 주문에 필요한 정보를 입력할 것이다. 그리고 입력한 모든 주문의 상태를 볼 수도 있어야 한다. 각각의 구매 주문은 세 가지(대기, 승인, 취소) 상태 중 한 상태여야 한다. 관리자에는 이 애플리케이션을 약간 다르게 사용한다. 관리자는 구매 주문을 대기 상태와 함께 볼 수 있어야 한다. 관리자는 구매 주문을 승인 또는 취소할 수 있어야 한다.
간단한 애플리케이션이므로 로그인이나 로그아웃은 신경 쓰지 않겠다. 대신 시스템의 모든 사용자 명단을 보여주고 그 중에 로그인할 사용자를 선택할 수 있도록 하겠다. 진짜 워크플로우 시스템에서는 동시성(concurrency)이 관건이다. 이것도 역시 예제 애플리케이션을 여러 시스템에서 실행하지 않을 것이기 때문에 고려하지 않겠다. 실제 워크플로우 시스템은 워크플로우 데이터를 관계형 데이터베이스 같은 공유 저장소에 영속성을 유지한다. 하지만 여기서는 최대한 단순하게 하기 위해 데이터를 XML 파일을 사용하여 영속성을 유지한다. JAXB를 사용하여 XML 형태로 데이터를 저장하고 읽을 것이다.
자, 이제 GUI를 만들기 위해 실제 코드를 작성해 보자.
<출처 :https://www.ibm.com/developerworks/kr/library/tutorial/os-eclipse-jigloo/section2.html>
이클립스용 GUI 빌더, Jigloo 시작하기 (한글) - 1
SWT GUI를 사용한 워크플로우 애플리케이션 만들기
Jigloo는 자바(Java™) 플랫폼에서 실행되는 복잡한 그래픽 사용자 인터페이스(GUI)를 빠르게 만들 수 있도록 도와주는 이클립스 플러그인입니다. 이 플러그인을 사용하여 스윙(Swing) 기반 애플리케이션과 SWT(Standard Widget Toolkit) 기반 애플리케이션을 만들 수 있습니다. 사용하기 쉬운 비주얼 편집기이기 때문에 데스크톱용 애플리케이션 UI를 빨리 만들 수 있습니다. 이 튜토리얼에서는 간단한 워크플로우 애플리케이션을 만들고 그 UI를 만들기 위해 Jigloo를 사용합니다. Jigloo 사용이 얼마나 쉬운지 살펴볼 것이며 비주얼 상속과 같은 고급 기능도 살펴볼 것입니다. 마지막으로 애플리케이션을 테스트하고 다른 사람이 사용할 수 있도록 패키징하겠습니다.
시작하기 전에
이 튜토리얼의 목적은 데스크톱 애플리케이션을 만들기 원하는 자바 개발자들이 애플리케이션의 UI를 만들 때 Jigloo라는 이클립스 플러그인을 사용할 수 있도록 도와주는 것이다. 예제 애플리케이션은 XML, XML 스키마, JAXB와 자바 5 기능 중 어노테이션과 제네릭(Generic)를 사용한다.
자바는 리치 데스크톱 애플리케이션을 만드는 데 뛰어난 플랫폼이다. 자바가 1995년 데뷔했을 때 AWT(Abstract Window Toolkit)이 들어있었다. AWT는 데스크톱 애플리케이션을 빌드하기 위해 자바의 첫 번째 UI 라이브러리였다. 1998년에 나온 JDK 1.2에는 스윙이 들어있었다. 스윙은 훨씬 개선된 툴킷이었다. 그 이후로 스윙에는 많은 개선이 있었다. 스윙은 이제 강력한 UI 라이브러리로 서로 다른 많은 플랫폼에서 잘 동작한다. SWT는 경쟁 UI 툴킷으로 많은 이점을 제공한다. 이제 Jigloo로 스윙이나 SWT를 타깃으로 하는 UI를 빠르게 빌드할 수 있다. 심지어 스윙 구성요소를 포함하는 SWT 애플리케이션을 빌드할 수 있다. 그러나 그 내용은 이 튜토리얼의 범위 밖이다.
이번 튜토리얼에서는 Jigloo를 사용하여 간단한 워크플로우 애플리케이션을 개발하는 방법에 대해 살펴보겠다. 이클립스 플러그인 Jigloo를 사용하여 애플리케이션의 UI를 만들겠다. 그러고 나서 애플리케이션을 빌드 하고 테스트한 뒤 다른 사람들이 사용할 수 있도록 패키징할 것이다.
|
이벤트 핸들러와 데이터 바인딩과 같은 UI 프로그래밍 개념에 익숙하다면 도움이 되지만 반드시 필요한 것은 아니다. AWT/스윙 또는 SWT를 사용해본 경험이 있으면 좋겠지만 역시 반드시 필요한 것은 아니다.
- 이클립스 V3.2
- Jigloo가 이클립스 플러그인이기 때문에 당연히 이클립스가 필요하다. 애플리케이션이 SWT를 사용할 것이기 때문에 이클립스와 함께 SWT 라이브러리 또한 필요하다.
- Jigloo
- Jigloo는 이클립스 플러그인이다. 이번 튜토리얼에서는 이클립스의 강력한 업데이트 관리자를 사용하여 이클립스에서 직접 Jigloo를 설치하는 법을 살펴보겠다.
- JAXB V2.0
- 본 튜토리얼에서 데이터를 저장하기 위한 형식으로는 XML을, XML을 파싱하고 직렬화할 때는 JAXB를 사용할 것이다. 자바 6을 사용하고 있다면 JAXB가 내장되어 있기 때문에 별다른 작업이 필요하지 않지만 자바 5를 사용하고 있다면 썬(Sun)의 JAXB 2.0 RI(Reference Implementation)를 참고할 필요가 있다.
<출처 : https://www.ibm.com/developerworks/kr/library/tutorial/os-eclipse-jigloo/section1.html>