SWT/JFace2008.11.01 15:08

SWT, JFace가 뭐여?

자, 이제 시작해볼까? 일단 SWT, JFace가 뭔지부터 찾아봤다.

머 간단히 요약하자면 SWT(Standard Widget Toolkit)는 Swing의 단점을 보완한 IBM에서 개발해서 기증한 eclipse의 Widget Toolkit 이란다.

가장 큰 장점으로 Swing은 모든 사용자 인터페이스의 그래픽 컴포넌트들을 직접 구현한다. 하지만 SWT는 자신이 굴러가는 OS의 자원을 이용한다. 그렇기 때문에 가볍고, 그래서 속도도 Swing에 비해 빠르다고 한다.(Swing 프로그래밍을 안해봐서 직접 체험해보지 않아 이렇게 표현했다.ㅎ~ 근데 최근 소식에 의하면 Swing도 제법 빨라졌다고 한다.)

여기에 JFace는 SWT 개발을 좀 더 쉽게 하기 위해 모델 기반으로 쪼겠다. 모델 중엔 MVC 모델이 젤루 유명하지? 이와 유사하게 쪼갠거 같다.

 

허접 SWT 만들어보기

SWT는 크게 Display, Shell, Widget 으로 구성된다. OS 위에 Display를 얹고(이건 단지 Display 객체를 생성하면 자동 얹어진다~), 그 Display 객체에다가 Shell 객체를 얹고, 그 Shell 객체에다가 Widget 객체를 얹는다. 머 쉽네~ 함 해보까?

(다행히 객체를 생성할 땐 new {class 명} 하는건 안다.ㅋㅋㅋ...)

 

Display display = new Display(); //<-- Display 객체를 생성하고(이럼 OS 위에 얹어진다) 

Shell shell = new Shell(display); //<-- display 객체에다가 Shell 객체를 얹고,      

Text helloText = new Text(shell, SWT.CENTER); //<-- shell 객체에다가 Text Widget을 얹는다.

 

근데 다른 건 다 얹고자 하는 객체만 파라미터로 받는데 Text 객체는 인자가 두개네? SWT.CENTER 는 뭐야?

class 구조를 가지고 유추해본다면 swt class의 멤버변수 쯤 되나보다. 권한은 public 으로 되어있으니깐 외부에서도 직접 접근할 수 있겠지? ^^

이건 대충 이렇게 하고 넘어가자.  

가만, 얹으기만 하면 다 되는게 아니자나? 아무리 얹으면 뭐해. 화면에 나와야지. 지금까진 단지 초기화만 했을 뿐이다.

 

shell.open(); //<-- 자! 열고!

 

근데 이게 끝난게 아니란다!! 헉! 열면 다 끝난거 아냐?

우리가 엑셀같은 프로그램을 쓸 때를 생각해보면 그제사 이해가 간다.

엑셀을 쓸 때 프로그램 열고 땡인가? 아니자너~ 거다가 글도 쓰고, 숫자도 넣고, 수식도 넣어서 계산도 하고, 차트도 넣고, 별별 짓을 다 한다.

그럼 그동안 이 엑셀프로그램은 내가 하는 짓을 다 받아줘서 원하는대로 해줘야 할 것 아닌가.ㅋㅋㅋ

 

그럼 이것도 마찬가지로 해야지. 자 내가 뭔 짓을 할지 기다리면서 그 짓들을 다 받아줘라~


while(!shell.isDisposed()){

if(!display.readAndDispatch()){

display.sleep();

}

}

 

위 코드를 분석해보자. 영어공부도 함 해보까? ^^;

dis·pose vt.
1 <군대·물건을> 배치하다, 배열하다 《for》
   《dispose+++》 dispose soldiers for the battle 병사를 전장에 배치하다

2 …할 마음이 내키게 하다 《to》(⇒ disposed 1a)
   《dispose++to do》 His advice disposed me to read it. 그의 조언으로 그것을 읽고 싶어졌다.

3 <사람에게> …의 경향을 갖게 하다, <사람에게> …의 영향을 받기 쉽게 하다 《to》(⇒ disposed 1b)
   《dispose+++》 His physique disposes him to backache. 그는 요통을 앓기 쉬운 체격이다.

4 <사무·문제 등을> 처리하다
━ vi.
1 처리하다, 처치[처분]하다
2 일의 추세[성패]를 결정하다
dispose of…을 처분하다, 처리하다, …의 결말을 짓다;팔아 버리다;해치우다, 죽이다:dispose of oneself 거취를 결정하다, 처신하다

 그럼 대충 해석해 보면 shell.isDisposed() 메소드는 무엇을 묻는 것이냐 하면 shell 객체가 일할 생각이 있냐 없냐, 또는 잘 배치되어 있냐를 묻는 것인데...

이렇게 해석한다면 생각이 있으면 true를, 일할 생각이 없이 이제 그만 둘란다 하면 false를 뱉어낼텐데... 그게 아닌가보다.

while(조건문) 문법에 따르면 {조건문}이 참(true)인 동안 계속 돈다는 것인데 거기에 not(!)을 붙였으니 shell.isDisposed() 가 false를 뱉어내는 동안 계속 돈다는 거다.

뭐 내 생각과는 반대인데 그래도 어쩌겠는가 이렇게 해야 shell이 죽지 않고 계속 화면에 떠있게 할 수 있다는데. ^^;

shell객체가 처리가 끝났냐, 일 다하고 죽었냐 하고 묻는 것이다. 아 수동태니깐 살해되었냐 군. ㅋㅋㅋ  그럼 말이 되네. shell 이 죽지 않았으면 while 문을 수행해라.

그 다음 일단 shell이 죽지 않고 돌고 있는 상태에서 display 객체에 조건을 주었다. display 객체는 OS와 통신하는 부분이다. OS바로 위에 붙어서 말이다.

display.readAndDispatch() 메소드는 ... 또 영어공부 함 해보까?

dis·patch vt.
1 <군대·특사 등을> 급파[특파]하다;<급보를> 발송하다 《to》
2 <일 등을> 재빨리 해치우다;신속히 처리하다;<식사를> 빨리 먹어치우다
3 죽이다(kill);<사형수 등을> 처형하다
━ n.
1 급파, 특파, 파견;급송, 발송, 발신; 속달편
2 급송 공문서;【신문】 급보, 특전
3 (처리 등의) 신속;날랜 처리;[U.C] 죽음에 의한 해결, 살해(killing)
   a happy dispatch 《익살》 할복 자살

4 (속달 화물) 운송 대리점
be mentioned in dispatches《영》 <군인의 이름이> 수훈(殊勳) 보고서에 오르다
with dispatch신속히, 재빠르게

(음. 앞으론 대충 뜻만 써야겠다. ^^;)

또 대충 해석해보면 readAndDispatch 라고 하면 지금 OS와 통신하고 있냐 하는 것으로 보면 되겠다. 그러니까 display 객체에게 묻는거지. "야~ 너 OS랑 이바구 까고 있냐?  이바구 안까고 있으면 display 객체에게 잠자코 있어라(sleep)" 라고 한다. 워워~

 

그럼 모든 초짜들이 반드시 넘어야 할 산 HelloWorld 를 짜보자~!!!

 

package com.swtjface.unit1; 

import org.eclipse.swt.*;

import org.eclipse.swt.widgets.*;

 

public class HelloWorld{

public static void main(String[] args){

Display display = new Display();

Shell shell = new Shell(display);

Text helloText = new Text(shell,SWT.CENTER);

helloText.setText("Hello World!");

shell.open();

 

while(!shell.isDisposed()){

if(!display.readAndDispatch()) display.sleep();

}

display.dispose();

}

}

 

자! 그럼 실행!

 

helloWorld1.gif

 

엥? 이게 뭐야? 덩그러니 창 하나만 떴다. 뭐가 문제인가?

책을 다시 보니 내가 짠 코드랑 틀린게 있네?

 

helloText.pack();

 

pack 이라 함은 팩을 싼다는 건데... 확인해보니 pack() 메소드는 구성 요소의 필요한 만큼만 공간을 사용하라는 것이다. 근데 머 필요하지 않는 공간도 그냥 쓰면 안되는건가? ^^;

어쨋든 pack()을 해야만 helloText 객체가 나타난다. 그럼 뭐 이왕 하는거 shell 도 pack()을 해서 자원절약에 동참해볼까?

 

package com.swtjface.unit1;

import org.eclipse.swt.*;

import org.eclipse.swt.widgets.*;

 

public class HelloWorld{

public static void main(String[] args){

Display display = new Display();

Shell shell = new Shell(display);

Text helloText = new Text(shell,SWT.CENTER);

helloText.setText("Hello World!");

helloText.pack();

shell.pack();

shell.open();

 

while(!shell.isDisposed()){

if(!display.readAndDispatch()) display.sleep();

}

 

display.dispose(); 

}

}

 

자! 그럼 다시 실행!

 

helloWorld2.gif

 

우헤~ 증말 pack 됐네? ㅋㅋㅋ

그럼 이제 SWT는 성공했구 JFace도 함 해보까?

 

허접 SWT/JFace 만들어보기

 JFace로도 위에 SWT로 만든 Hello World 를 똑같이 만들어본다.

그전에 JFace가 SWT와 뭐가 다른지 잠깐 알아보자.

JFace는 모델 기반 어뎁터(model-based adapters), 또는 Helper Classes 라고 한다. Helper Classes 라고 하면 머 도우미 클래스라고 생각하면 되겠지만 model 기반이라...흠. model이 뭐야? 이런건가?

model.jpg

오~~!!! 잠이 확 달아난다!!

하지만 이건 아닌것 같고.

영어사전을 뒤져보면 [모형, 설계도]로 나온다. 즉, 미리 모형을 만들어놓고 'adapter' 라고 하니깐 그걸 상속해서 변형하여 쓴다는 것이리라.

이런 model adapter classes는 크게 7가지가 있다.

  1. Viewer

  2. Action

  3. Contribution

  4. Image

  5. Font registry

  6. Dialog

  7. Wizard

그리고 SWT의 Shell 을 JFace는 대신 만들어주는 ApplicationWindow class 가 있다. 즉, Shell 을 직접 개발자가 컨트롤 할 필요없이 ApplicationWindow 객체가 기본적인 것은 다 컨트롤 해준다는 것이다. 근데 사실 아직까진 이 두개의 차이를 알지 못하겠다. 왜냐면 Shell을 직접 컨트롤 한게 그다지 복잡하지도 않았기 때문이다. 이건 앞으로 좀 더 복잡한 Shell 컨트롤을 해보게 되면 그 차이점을 알게 되리라 위안을 삼으면서 다시 JFace를 알아보자.

 

일단 HelloWorld 를 SWT 처럼 직접 정의하지 않고 JFace의 ApplicationWindow 를 상속받아서 만들어보자.

public class HelloWorld extends ApplicationWindow

 

그리고 ApplicationWindow 가 대신 만들어준다는 Shell을 만들기 위해선 ApplicationWindow 의 생성자를 그대로 사용하면 되겠지?

public HelloWorld(){

super(null);

}

 

위와 같이 하면 Display 객체와 Shell 객체가 생성된다는 것이다. 뭐 기껏해야 SWT의 두줄을 줄여준거지만 일단 믿고 가보자.

잠깐. 근데 아까 만든 SWT용 HelloWorld 를 좀 비스무리하게라도 해놔야 비교가 될 거 같다.

다음과 같이 SWT 의 HelloWorld를 바꿔봤다.

 

package com.swtjface.unit1;

 

import org.eclipse.swt.*;

import org.eclipse.swt.widgets.*;

 

public class HelloWorld{

 

Display display;

Shell shell;

 

public HelloWorld(){

display = new Display();

shell = new Shell(display);

}

 

public void createText(){

Text helloText = new Text(shell, SWT.CENTER);

helloText.setText("Hello World!");

helloText.pack();

shell.pack();

}

 

public static void main(String[] args){

helloWorld hw = new helloWorld();

hw.createText();

hw.shell.open();

 

while(!hw.shell.isDisposed()){

if(!hw.display.readAndDispatch()) hw.display.sleep();

}

 

hw.display.dispose(); 

}

}

 

자, 그럼 이제 다시 시작해볼까?

일단 생성자는 위에서 벌써 했구,

윈도우에 텍스트를 보이기 위한 부분인 createText() 메소드는 거의 변함이 없지만 단, JFace에선 ApplicationWindow에서 생성한 Shell에 표현되는 부분을 정의하는 메소드는 정해져 있다.

protect Control createContents(Composite parent){

Text helloText = new Text(parent, SWT.CENTER);

parent.pack();

return parent;

}

 

위와같이 상속받은 ApplicationWindow Class의 createContents 메소드를 재정의(overriding)하면 된다.

이 메소드는 parent 란 이름을 가진 Composite 를 만든 뒤 반환(return)한다. 그럼 ApplicationWindow 객체는 이를 받아 Shell에 보여주게 된다.

이 작업은 어디서 하냐구? 이거까지 알려면 ApplicationWindow Class 를 봐야 하는데 ... 너무 깊이 들어가진 말자. ㅋㅋㅋ

 

그럼 이제 main 메소드를 짜야지?

public static void main(String[] args){

helloWorld hw = new helloWorld();

hw.setBlockOnOpen(true);

hw.open();

Display.getCurrent().dispose();

}

 

JFace가 SWT와 다른 점은 while문을 이용해서 생성한 Shell 객체가 죽었나 살았나 보고, 살아있는 동안 Display 객체보고 일안하면 쉬어라 라고 명령 내리는 부분이 결국 모든 SWT 프로그램에서 반복적으로, 그리고 필수적으로 들어가는 거니깐 하나의 메소드로 만들어놓고 그 메소드만 실행시키자는 것이다.

그 메소드가 바로 setBlockOnOpen(true) 다.

여기서 parameter 를 true로 줬는데 이건 "윈도우가 닫힐 때까진 계속 대기하고 있어라."이고,

만일 false로 준다면 "일 끝내고 바로 객체 자원을 다 돌려줘라."라는 것으로 결국 윈도우가 닫히게 된다.

근데 이렇게 짜질 않자나?

그냥 default를 true로 두지. 흠. 머 SDK 짜는 사람 맘이니깐 머. 이런 경우가 있나 보지 머.

마지막으로 Display 객체에 현재 열려있는 스레드, 즉 shell 객체들을 모두 dispose 하면 된다.

 

위에꺼 모두 이제 붙여보면 완전한 코드가 완성된다.

package com.swtjface.unit2;

 

import org.eclipse.jface.window.*;

import org.eclipse.swt.*;

import org.eclipse.swt.widgets.*;

 

public class helloWorld extends ApplicationWindow{

public helloWorld(){

super(null);

}

 

protected Control createContents(Composite parent){

Text helloText = new Text(parent, SWT.CENTER);

parent.pack();

return parent;

}

 

public static void main(String[] args){

helloWorld hw = new helloWorld();

hw.setBlockOnOpen(true);

hw.open();

Display.getCurrent().dispose();

}

}

 

26줄이 20줄로 줄었다. 그러니까 코딩줄수로 보면 23% 줄었다.

흠. 약 1/4이 줄었으니 꽤 줄은거 같다. ㅎㅎ

근데 JFace가 model 기반이라고 했는데 그 7가지 중 하나도 안쓴거 같은데?

근데 머 꼴랑 text 한줄꺼리도 안되는거 가지고 거창하게 model을 쓰네 마네 할 필요는 없을 거 같아서 너그러운 맘으로 넘어가준다.


 

 

이 글은 스프링노트에서 작성되었습니다.

신고
Posted by gildong0
ASP,Java,SQL2008.10.31 16:40
게시판의 페이징 쿼리를 연구(?)하던 중 희안한 현상을 발견했다.
다음의 두 쿼리를 보자.

1. SELECT TOP 5 * FROM
(SELECT TOP 10 * FROM tableA ORDER BY field1 ASC) AS A ORDER BY field1 DESC;

2. SELECT TOP 5 * FROM
(SELECT TOP 10 * FROM tableA) AS A ORDER BY field1 DESC;

field1은 PK로 INDEX가 ASC로 걸려있다.
어짜피 INDEX가 field1 ASC로 걸려있기 때문에 위 두개의 inner 쿼리 문은 같은 결과를 같는다.
그러나 이를 다시 DESC로 정렬하여 밑에서 5개를 가져오려고 할 때 문제가 발생한다.
2번 쿼리는 내부 쿼리 결과를 무시하고 다시 tableA 를 field1 DESC 하여 TOP 5 를 가져온다.

수행계획을 보면 차이점을 알 수 있다.


단순히 생각했을 때 inner 쿼리를 수행한 결과를 메모리에 저장하고 나서 그걸 다시 sort 하여 TOP 5를 가져오는 것이 아니네?
2번 쿼리를 보면 inner 쿼리의 내부 TOP을 수행한 뒤 바로 외부의 TOP을 수행했다. 
sort 연산은 수행하지 않았다.
둘다 select 연산은 맨 마지막에 한 번만 수행했다.

그럼 정렬 대상을 INDEX가 안걸려있는 field로 해보면 어떨까?

3. SELECT TOP 5 * FROM
(SELECT TOP 10 * FROM tableA) AS A ORDER BY field2 DESC;


하~ 이러니 sort 연산을 한다. 결과도 원하는대로 나온다.(당연하겠지만)
(※ 어? 근데 쿼리비용을 보면 3번이 더 적네?? 그래서 3번 쿼리를 실행하면서 1번쿼리도 동시에 실행시켜봤는데 비용은 같은 44.97%로 나왔다.ㅎㅎ)

암튼 이 결과 INDEX 가 걸려있는 field를 sort 할 때는 sort 연산이 이뤄지지 않는다는 걸 알았다. ASC든, DESC든.

그리고 innser 쿼리 결과를 재정렬하여 TOP 으로 가져오고자 할 경우 inner 쿼리에도 sort 를 걸어줘야 한다는 것도 알았다.
왜 그렇게 되는지는 정확히 알 수 없지만..ㅎㅎㅎ

신고
Posted by gildong0
SWT/JFace2008.10.22 17:33

뭘 가지고 SWT/JFace 프로그램을 만들지?

뭐 잡담은 그만하고 답은 eclipse 다. 나는 현재의 최신버전인 Ganymede 로 해봐야지. (정확히는 Ganymede RCP 버전)

근디 이놈의 eclipse는 3.3 버전부터인가 처음 설치하고 실행할 때마다 다음과 같은 오류가 뜬다.

 

ecilpseErr.gif

 

대처방법은?

구글신에게 물어보면 여러가지가 있다. 그 중 내가 적용해서 성공한 방법은 아래와 같다.

 

-Xmx512m   ☞  -Xmx256m

 

메모리를 256을 최대로 잡으면 되더라. 사실 잘 이해가 안된다. 많이 잡으면 많이 잡을 수록 eclipse 입장에선 더 나은 환경에서 돌 수 있을 텐데.

암튼 일단 Ganymede를 드디어 실행했다!

 

ganymede.gif

 

근데 그냥 eclipse 만 설치한다고 해서 SWT/JFace 프로그램을 짤 수 있는 건 아니었다.(암튼 인생 수월하지 않네. ㅡ.ㅡ)

 

eclipse에서 생성할 수 있는 Project 에는 기본적으로 SWT Project 란게 없다. 그냥 일반 Java Project 로 생성해야한다.

이러다보니 Java Project를 생성해 보면 Build Path 에 SWT 나 JFace 관련 Library 들은 안올라와 있으니 이걸 하나씩 찾아서 추가해줘야 한다. (Eclipse 는 Project 분류에 SWT/JFace Project를 추가해달라!)

 

  1. 생성한 Project 에서 오른쪽 마우스클릭으로 열린 메뉴 중 맨 밑에 있는 Properties 를 선택한다.

    projectProperties(1).gif

  2. 좌측 메뉴 중 Java Build Path 를 선택한 뒤 오른쪽에 Libraries 탭을 선택한다. (아래 화면 중 원래는 JRE System Library 하나 밖에 없다. 나머진 3번을 따라서 추가해줘야한다.)

    projectLibraries.gif

  3. 위 Libraries 들을 추가한다. 각 Library 위치 내용은 아래와 같다.

    Variable Entry Name Path (* %eclipse% : eclipse가 설치된 Root 폴더)
    BOOT_LIB %eclipse%/plugins/org.eclipse.core.boot_3.1.100.v20080218.jar
    COMMANDS_LIB %eclipse%/plugins/org.eclipse.core.commands_3.4.0.I20080509-2000.jar
    COMMON_LIB %eclipse%/plugins/org.eclipse.equinox.common_3.4.0.v20080421-2006.jar
    JFACE_LIB %eclipse%/plugins/org.eclipse.jface_3.4.1.M20080827-2000.jar
    RUNTIME_LIB %eclipse%/plugins/org.eclipse.core.runtime_3.4.0.v20080512.jar
    SWT_LIB %eclipse%/plugins/org.eclipse.swt.win32.win32.x86_3.4.1.v3449c.jar
    WORKBENCH_LIB %eclipse%/plugins/org.eclipse.ui.workbench_3.4.1.M20080827-0800a.jar

참, 위에서 각 JAR파일 이름에 먼 숫자들이 줄줄이 붙어 있는데 대충 알겠지만 버전을 의미하는 거다. 그러니 똑같은 거 없다고 땅 파지 말고 그 앞에 까지만 같은걸로 찾으면 된다. 그리고 eclipse RCP 버전을 깔면 기본적으로 위 JAR 파일들이 다 있지만 다른 버전을 깔면 없을 수 있다. 이럴 땐 지체말고 eclipse.org 에 방문해서 해당 파일을 다운받아다가 plugins 폴더 밑에 갖다 두면 된다.

 

자! 그럼 이제 프로그램 짤 준비는 다 된거 같으니 슬슬 기술 들어가 보까나~

이 글은 스프링노트에서 작성되었습니다.

신고
Posted by gildong0

티스토리 툴바