Recent Posts
Recent Comments
Link
11-17 00:00
Today
Total
관리 메뉴

삶 가운데 남긴 기록 AACII.TISTORY.COM

[SWT/JFace] 이벤트 처리 본문

DEV&OPS/Java

[SWT/JFace] 이벤트 처리

ALEPH.GEM 2024. 1. 21. 19:07

이벤트 처리는 이벤트를 전달하는 이벤트 클래스와 이벤트를 처리하는 리스너가 담당합니다.

JFace는 이런 이벤트 처리를 간단하게 해줍니다.

 

SWT 에서 이벤트 처리

SWT는 운영체제의 이벤트 큐를 사용합니다.

Application의 Display클래스는 해당 큐의 내용을 정렬하는데 readAndDispatch()와 msg 필드를 사용합니다.

msg필드는 운영체제의 메시지 큐에 대한 핸들로 동작합니다.

해당 이벤트를 최상위 Shell 객체로 보내고 Shell객체는 어떤 위젯이 해당 이벤트를 받을지 결정합니다.

Shell은 해당 위젯으로 이벤트를 보내고 해당 위젯은 이 정보를 리스너 라는 인터페이스로 전달합니다.

그러면 리스너는 이벤트 핸들러를 호출해 이벤트에 필요한 동작을 수행합니다.

 

Adapter 클래스

Adapter는 추상클래스로 Listener 인터페이스를 구현하고 필요한 각 메소드의 기본적인 구현을 제공합니다.

위젯을 리스너가 아닌 어댑터와 연결하면 이벤트를 처리할 메소드 코드만 작성하면 되기 때문에 편리합니다.

다만 Adapter는 리스너가 하나 이상의 멤버 메소드를 가질 때만 유용합니다.

 

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;

public class MouseKey extends Composite{
	Label output;
	public MouseKey(Composite parent){
		super(parent, SWT.NULL);
		
		Button typed = new Button(this, SWT.PUSH);
		typed.setText("Typed");
		typed.setLocation(2,10);
		typed.pack();
		//Adapter를 익명 리스너로 사용. 
		typed.addKeyListener(new KeyAdapter() {
			public void keyPressed(KeyEvent e) {
				keyHandler();
			}
		});
		
		Button untyped = new Button(this, SWT.PUSH);
		untyped.setText("Untyped");
		untyped.setLocation(80, 10);
		untyped.pack();
		//UntypedListener 인터페이스를 구현해서 이벤트 처리
		untyped.addListener(SWT.MouseEnter, UntypedListener);
		untyped.addListener(SWT.MouseExit, UntypedListener);
		
		output = new Label(this, SWT.SHADOW_OUT);
		output.setBounds(40, 70, 90, 40);
		output.setText("No Event");
		pack();
	}
	
	//key 이벤트를 처리
	void keyHandler() {
		output.setText("Key Event");
	}
	
	//마우스 이벤트를 처리
	Listener UntypedListener = new Listener() {
		@Override
		public void handleEvent(Event event) {
			switch (event.type) {
			case SWT.MouseEnter:
				output.setText("Mouse Enter");
				break;
			case SWT.MouseExit:
				output.setText("Mouse Exit");
				break;
			}
		}
	};
}

이 클래스를 CompViewer 클래스와 연결 시키면 실행 결과를 볼수 있습니다.

import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;

public class CompViewer extends ApplicationWindow{

	public CompViewer() {
		super(null);
	}
	
	protected Control createContents(Composite parent) {
		MouseKey mk = new MouseKey(parent);
		return parent;
	}

	public static void main(String[] args) {
		CompViewer cv = new CompViewer();
		cv.setBlockOnOpen(true);
		cv.open();
		Display.getCurrent().dispose();
	}

}

실행결과:

키보드를 아무거나 누르면 이벤트를 처리하는 것을 볼 수 있고, Untyped 버튼에 마우스를 가져가면 마우스 이벤트도 정상 처리 됨을 볼 수 있습니다.

 

JFace의 이벤트 처리

JFace 개발자는 리스너, 어댑터, 위젯이 필요 없다고 여기고, 대신 이벤트 처리를 액션과 컨트리뷰터로 처리합니다.

액션은 사용자가 GUI와 상호작용할 때 발생하는 것이며, 컨트리뷰터는 여러 종류의 액션을 받으면서도 단일한 액션을 발생시키는 것을 말합니다.

 

리스너 인터페이스는 이벤트를 시작하는 컴포넌트에 의존하게 되는데, 예를들어 마우스 이벤트를 받는 리스너는 다른 이벤트에 대해서는 사용할 수 없습니다.

JFace는 Action과 ActionContributionItem 클래스를 분리해서 제공합니다.

ActionContributionItem 이 위젯의 기능과 리스너 클래스를 결합합니다.

사용자가 상호작용을 할 때마다 연관된 Action 클래스를 발생시켜 이벤트를 처리합니다.

 

SWT 이벤트 모델 처럼 인터페이스 처리는 Display 클래스로 시작해서 운영체제의 이벤트 큐를 모니터링합니다.

하지만 Display 클래스가 Display의 Shell 객체를 포함하는 ApplicationWindow 정보를 전달합니다.

ApplicationWindow는 Action 클래스를 생성하여 원래 이벤트를 생성한 Contribution에 전달합니다.

Contribution은 단일 이벤트 핸들러이며 Action클래스의 run() 메소드를 호출합니다.

 

Action 클래스

 

이 예제는 ApplcationWindow의 상태 표시줄에 출력하는 기능을 수행합니다.

툴바 안에 이 클래스를 구현할 것이라 연결 시킬 이미지가 필요한데,

이클립스경로/plugins/org.eclipse.platform_~~~ 디렉토리에서 eclipse64.png 파일을 현재 프로젝트 폴더(패키지)로 복사하면 됩니다. 

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.StatusLineManager;
import org.eclipse.jface.resource.ImageDescriptor;

public class StatusAction extends Action{
	StatusLineManager statman;
	short triggercount = 0;
	
	public StatusAction(StatusLineManager sm) {
		//&은 이 문자가 단축키가 될것을 의미
		super("&Trigger@Ctrl+T", AS_PUSH_BUTTON);
		statman = sm;
		setToolTipText("액션 트리거");
		setImageDescriptor(ImageDescriptor.createFromFile(this.getClass(),"eclipse64.png"));
	}
	
	public void run() {
		triggercount++;
		statman.setMessage("상태 액션이 발생했습니다. count: "+triggercount);
	}
}

이 예제에서 StatusAction 클래스는 어떤 컴포넌트가 액션을 발생시키는지 모릅니다.

그래서 어떤 Control이 액션을 발생시키든 추가 메서드 작성 없이 StatusAction()을 사용할 수 있습니다.

run() 메서드가 유일한 이벤트 처리 루틴이라는 점이 SWT의 이벤트와 결합되는 핸들러가 여러개라는 점이 다른 점입니다.

run() 메서드가 이벤트 처리를 하지만 주요 작업은 생성자인 StatusAction()이 수행합니다.

만약 이 StatusAction 을 메뉴와 결합시키면 메뉴는 이벤트를 발생시킬 수 있게 되는 것입니다.

StatusAction을 발생시킬 때마다, run()이 실행됩니다. 

그 때마다 triggercount가 누적되고 메시지가 StatusLineManager 객체로 보내지게 됩니다.

 

ApplicationWindow에서 Contribution 구현

 

아래 예제는 ApplcationWindow에서 Contribution과 ContributionManager를 추가하는 방법에 대한 예제 입니다.

ActionContributionItem, MenuManager, ToolBarManager는 모두 StatusAction을 발생시키며, 이 Action은 메시지를 상태표시줄로 보냅니다.

package event;

import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.StatusLineManager;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;

public class Contributions extends ApplicationWindow{

	StatusLineManager slm = new StatusLineManager();
	//status_action 인스턴스 생성
	StatusAction status_action = new StatusAction(slm);
	//status_action 컨트리뷰션을 ActionContributionItem에 할당
	ActionContributionItem aci = new ActionContributionItem(status_action);	
	
	//생성자에서 Application Window에 자원을 추가
	public Contributions() {
		super(null);
		//상태표시줄 추가
		addStatusLine();
		//메뉴 추가
		addMenuBar();
		//툴바 추가
		addToolBar(SWT.FLAT | SWT.WRAP);
	}
	
	protected Control createContents(Composite parent) {
		//윈도우 타이틀 설정
		getShell().setText("Action/Contribution Example");
		//윈도우 크기 설정
		parent.setSize(800, 600);
		//ActionContributionItem을 호출해서 윈도우 안에 버튼을 생성
		aci.fill(parent);	//aci객체를 GUI에 넣어 눌릴때마다 statusEvent를 발생시킴	
		return parent;
	}
	
	//메뉴를 상단에 생성
	protected MenuManager createMenuManager() {
		MenuManager main_menu = new MenuManager(null);
		MenuManager action_menu = new MenuManager("Menu");
		main_menu.add(action_menu);
		//status_action 컨트리뷰션을 할당
		action_menu.add(status_action);
		return main_menu;	
	}
	
	//툴바 생성
	protected ToolBarManager createToolBarManager(int style) {
		ToolBarManager tool_bar_manager = new ToolBarManager(style);
		//status_action 컨트리뷰션을 할당
		tool_bar_manager.add(status_action);
		return tool_bar_manager;
	}
	
	protected StatusLineManager createStatusLineManager() {
		return slm;
	}

	public static void main(String[] args) {
		//윈도우 생성
		Contributions swin = new Contributions();
		//윈도우 열기
		swin.setBlockOnOpen(true);
		swin.open();
		//GUI 자원 처분
		Display.getCurrent().dispose();
	}

}

Contribution 연결 방법

위 예제에서 처럼

1. ContributionManager의 add() 사용. add()는 인자로 Action과 ActionContributionItem을 받는데 ContributionItem과 ContributionManager를 Action과 묵시적으로 연동할 수 있고, ActionContributionItem과 명시적으로 연동할 수 있음. 그러나 명시적 연동은 한 번만 수행되지만 묵시적 연동은 한 Action객체에 반복적으로 수행할 수 있음.

2.  ActionContributionItem의 fill() 사용. 인자로 SWT 위젯을 전달. 인자가 Composite 타입이면 해당 액션의 STYLE을 따름. 만약 인자가 SWT Menu 객체면 메뉴형태로, SWT ToolBar객체면 툴바 형태로 나타남. 

 

Ev_Composite.java 만들기
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;

public class Ev_Composite extends MouseKey {
	public Ev_Composite(Composite parent) {
		super(parent);
		Button launch = new Button(this, SWT.PUSH);
		launch.setText("실행");
		launch.setLocation(40, 120);
		launch.pack();
				
		launch.addMouseListener(new MouseAdapter() {
			public void mouseDown(MouseEvent e) {
				Contributions sw = new Contributions();
				sw.open();
			}
		});
	}
}

 

WidgetWindow.java 수정
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;

public class WidgetWindow extends ApplicationWindow {

	public WidgetWindow() {
		super(null);
	}
	
	protected Control createContents(Composite parent) {
		//탭폴더 생성
		TabFolder tf = new TabFolder(parent, SWT.NONE);
		
		TabItem ti = new TabItem(tf, SWT.NONE);
		ti.setText("탭1");
		ti.setControl(new Ev_Composite(tf));
				
		getShell().setText("위젯 윈도우");
		return parent;
	}

	public static void main(String[] args) {
		WidgetWindow wwin = new WidgetWindow();
		wwin.setBlockOnOpen(true);
		wwin.open();
		Display.getCurrent().dispose();
	}

}

 

 

요약

1. JFace가 메뉴, 툴바, 버튼에 대한 빠른 코딩이 가능한 반면 SWT는 키보드 작동 및 Shell과 테이블 같은 위젯과 관련한 이벤트에 필요합니다.

2. JFace가 오른쪽 클릭과 왼쪽 클릭을 구분해야 할 때는 쓸모가 없습니다.

 

따라서 효율적으로 코딩을 하려면 둘 다 사용하는 것이 좋다고 할 수 있습니다.

 

 

 

 

728x90

'DEV&OPS > Java' 카테고리의 다른 글

MyBatis 동적 쿼리의 기본  (0) 2024.05.08
Singleton Pattern 과 DeadLock  (21) 2024.01.21
[SWT/JFace] Composite 클래스  (1) 2024.01.21
JAVA 파일명으로 금지된 문자 검사하기  (1) 2024.01.20
자바 Thread dump  (0) 2024.01.20