IDL/Widget

Widget Programming 예제

이상우_IDL 2011. 11. 10. 18:47
728x90
IDL에서 할 수 있는 여러가지 일들 중 Widget Programming이라는 것이 있습니다. 이것은 IDL을 이용하여 일종의 GUI 어플리케이션을 만드는 작업입니다. 요즘같이 스마트폰이나 태블릿이 널리 활성화된 시대에 우리가 '앱(App)'이라는 용어를 많이 쓰는데, 이것과 비교하면 IDL로 앱을 만드는 작업이라고 보면 됩니다. 물론 이러한 작업은 IDL이 어느 정도 익숙한 중급이상의 사용자들이나 할 만한 작업이긴 합니다. 그리고 그냥 프로그램 한번 일사천리로 돌려서 결과만 얻으면 되는 경우가 일반적이기 때문에, 대다수의 IDLer들에게는 그다지 필요가 없을 수도 있는 방법론이기도 합니다. 하지만, IDL에서 할 수 있는 특정한 처리작업을 여러 사람들이 쉽게 할 수 있도록 공유하고자 할 경우, 반드시 이러한 작업이 필요할 수도 있습니다. 물론 이를 위해서는 공부를 어느 정도 해야 합니다.

 아실 idluser.org 웹사이트에서 '문서자료'라는 게시판의 28번 게시물에, 이러한 Widget Programming에 관한 전반적인 소개를 담은 PDF문서가 있습니다. 제가 몇 년전에 작성했던 문서라서 그다지 최신의 내용은 아니지만, 이러한 작업을 위한 기본적인 내용들은 들어가 있습니다. 혹시나 Widget Programming에 관심이 있으시다면 이 문서를 한번 읽어보시면 좋을 것 같아 일단 소개를 해봅니다.

오늘은 이러한 Widget Programming의 한 간단한 예제를 소개할까 합니다. 두 개의 숫자로 된 인자를 사용자가 입력하고 특정 버튼을 누르면 두 인자를 더하여 그 결과값을 보여주는 기능을 하는 앱을 만들어보고, 그러한 앱을 만들기 위한 프로그래밍이 어떻게 이루어지는지 간략하게 설명을 해보겠습니다. 먼저 오늘 만들어보고자 하는 앱의 모습은 다음 그림과 같습니다.


이 그림에서 보면, 먼저 텍스트창 두 개(Parameter A, B)가 있는데, 여기서는 연산의 대상이 될 두 개의 숫자값을 입력할 수 있습니다. 세번째에 있는 텍스트창은 앞의 두 숫자를 더한 결과값을 출력하는 역할을 합니다. 맨 아랫부분에 있는 두 버튼들 중 왼쪽 버튼은 계산을 수행하는 역할이고, 오른쪽 버튼은 앱을 종료하는 역할을 합니다. 이 앱을 만들기 위하여 작성된 IDL 프로그램은 메인루틴과 하나의 서브루틴으로 구성됩니다. 먼저 메인루틴의 내용을 보겠습니다.

PRO test_gui


tlb = WIDGET_BASE(/COLUMN, TITLE='My Calculation'); top-level-base widget

base1 = WIDGET_BASE(tlb, /ROW); 첫번째 base widget (인자 A)

param_A_label = WIDGET_LABEL(base1, VALUE='Enter Parameter A (only number)')

param_A = WIDGET_TEXT(base1, XSIZE=8, /EDITABLE, UVALUE='param_A')

base2 = WIDGET_BASE(tlb, /ROW); 두번째 base widget (인자 B)

param_B_label = WIDGET_LABEL(base2, VALUE='Enter Parameter A (only number)')

param_B = WIDGET_TEXT(base2, XSIZE=8, /EDITABLE, UVALUE='param_B')

base3 = WIDGET_BASE(tlb, /ROW); 세번째 base widget (결과)

result_label = WIDGET_LABEL(base3, VALUE='Result')

result = WIDGET_TEXT(base3, XSIZE=8)

base4 = WIDGET_BASE(tlb, /ROW); 네번째 base widget (두 버튼)

start_bttn = WIDGET_BUTTON(base4, VALUE='Calculation Start !', UVALUE='start')

exit_bttn = WIDGET_BUTTON(base4, VALUE='Exit', UVALUE='exit')


WIDGET_CONTROL, tlb, /REALIZE; GUI를 구현하여 가시화


info = {param_A:param_A, param_B:param_B, result:result}; 공유정보 구조체

WIDGET_CONTROL, tlb, SET_UVALUE=info; 공유정보 구조체를 tlb의 user value로 저장


XMANAGER, 'test_gui', tlb; 이벤트 대기 체제 구축


END


이 GUI는 크게 보면 세로방향(COLUMN 방향)으로 정렬된 총 4개의 base widget들로 구성되어 있고, 각 base widget은 내부적으로 label, text, button 등의 widget들을 갖고 있는데 이들은 가로방향(ROW)으로 정렬되어 있습니다. 그리고 서브루틴에서도 인식할 수 있는 공유정보를 구조체(structure)의 형태로 구성하고, 이 구조체를 top-level-base widget의 user value로 저장해놓았습니다. 그러면 나중에 서브루틴에서 이 구조체를 그대로 불러와서 사용할 수 있습니다.

이 GUI에서 이벤트를 발생시킬 수 있는, 즉 사용자의 조작이 개입되는 구성요소들은 param_A, param_B, start_bttn, exit_bttn 등 총 4개입니다. 텍스트 형태의 widget인 param_A, param_B를 보면 /EDITABLE이라는 키워드가 들어가 있는데, 이것은 이 텍스트창에 사용자가 뭔가를 입력할 수 있다는 의미입니다. 이런 행위 자체가 이벤트가 됩니다. 참고로 result라는 텍스트 widget의 경우는 이 키워드가 들어가 있지 않습니다. 따라서 result라는 텍스트창에서는 사용자가 입력을 할 수 없고, 다만 거기 표시되는 문자열만 볼 수 있는 경우입니다. 두 개의 버튼들의 경우는 그 버튼을 누르는 동작 자체가 이벤트가 됩니다. 그리고 이 4개의 widget들을 보면 모두 UVALUE라는 키워드에 특정 문자값이 부여되어 있는 것이 보입니다. 이것은 각 widget에 대한 일종의 내부적인 인식표의 역할인데, 어떤 구성요소에서 발생한 이벤트인지를 서브루틴에서 구분하기 위한 역할을 합니다.

메인 루틴은 이 정도로 하고, 이제 서브루틴을 보겠습니다. 내용은 다음과 같습니다.

PRO test_gui_event, event


WIDGET_CONTROL, event.top, GET_UVALUE=info; top-level-base widget의 UVALUE 추출

WIDGET_CONTROL, event.id, GET_UVALUE=uval; 이벤트가 발생한 widget의 UVALUE 추출


CASE uval OF; 어떤 widget에서 발생한 이벤트냐를 구분하여 작업 수행

  'start' : BEGIN; 'Calculation Start !' 버튼을 누른 경우의 작업

      WIDGET_CONTROL, info.param_A, GET_VALUE=fw

      WIDGET_CONTROL, info.param_B, GET_VALUE=fh

      result = FLOAT(fw)+FLOAT(fh)

      result_str = STRTRIM(STRING(result, FORMAT='(F10.2)'), 2)

      WIDGET_CONTROL, info.result, SET_VALUE=result_str

  END

  'exit' : WIDGET_CONTROL, event.top, /DESTROY; 'Exit' 버튼을 누른 경우의 작업

  ELSE : RETURN; 그 외 이벤트에 대한 작업(아무것도 안하고 그냥 돌아감)

ENDCASE


END


GUI상에서 이벤트가 발생시 그 이벤트의 내용을 담은 구조체가 무조건 이 서브루틴으로 전달됩니다. 원래 메인루틴의 이름에 '_event'라는 문자가 붙은 이름의 서브루틴을 찾는 것이 기본설정입니다. 즉 여기서는 메인루틴의 이름이 'test_gui'이므로, 'test_gui_event'라는 이름의 서브루틴을 무조건 찾게 됩니다. 물론 각 widget에 대하여 EVENT_PRO라는 키워드로 다른 서브루틴을 지정해줄 경우에는, 그 widget에서 발생한 이벤트가 그 서브루틴으로 갑니다. 하지만 이벤트가 발생하는 widget임에도 불구하고, EVENT_PRO라는 키워드가 따로 지정이 안되어 있을 경우에는 무조건 이와 같은 이름의 루틴으로 이벤트 구조체가 전달된다는 점을 기억해둘 필요가 있습니다.


어쨌든 이 서브루틴의 맨 앞부분의 내용을 보면 먼저 event.top으로부터 UVALUE를 얻는다(Get)는 것인데, event.top은 이벤트가 발생된 widget의 최상위 레벨에 해당되는 widget을 의미하므로 바로 top-level-base widget이 됩니다. 그 다음 줄에서는 event.id로부터 UVALUE를 얻게 되는데, 여기서 event.id는 이벤트를 발생시킨 당사자 widget이 됩니다. 따라서 event.top으로부터 얻는 UVALUE는 앞서 메인루틴에서 정의했던 info라는 구조체가 될 것이고, event.id에서 얻는 UVALUE는 이벤트를 발생시키는 각 widget에 부여되어 있는 UVALUE가 됩니다. 이를 uval이라는 변수로 얻고, 이 uval이 뭐냐에 따라 해당 작업을 나눠서 수행하기 위하여 CASE라는 구문이 사용됩니다.


간단한 것 부터 보면, uval이 'exit'인 경우는 메인루틴에서 'Exit'라는 이름을 갖는 버튼에서 이벤트가 발생할 경우가 됩니다. 이 경우에는 GUI를 종료하겠다는 의미이므로 event.top를 /DESTROY한다는 내용이 들어간 것입니다. 그리고 uval이 'start'인 경우가 바로 우리가 원하는 연산을 수행하여 그 결과를 result라는 텍스트 widget에 표출하는 작업을 수행합니다. 이 작업의 내용을 보면 연산의 대상이 될 숫자값들을 param_A, param_B 두 텍스트 widget에서 얻고(문자형으로 얻어짐), 이 값을 실수형으로 변환하여 덧셈 연산을 수행한 다음, 그 결과값을 문자형으로 변환하여 result라는 텍스트 widget에 부여하는 순서로 진행됩니다.


대략 이와 같은 내용이 됩니다. 이 예제코드는 아래에 첨부합니다. 더 자세한 내용은 앞서 언급된 PDF문서의 내용을 참조하시길 바랍니다. 대략적으로 설명을 해보았지만, IDL로 앱을 만드는 하나의 예로서 소개를 해보았습니다.


test_gui.pro



test_gui.pro
0.0MB
LIST