본문 바로가기

컴퓨터/IT·운영체제

파워빌더 - 실무에 유용한 팁모음3

[Tip41] 

Row와 Column의 Status


◈ DataWindow의 Row나 Column의 상태

   New!           (row만)

   NewModified!   (row만)

   NewModified!   (row와 column)

   DataModified!  (row와 column)


◈ row 검색

  row가 datawindow에 retrieve되면 NotModified!라는 row의 상태    가 되며 모든 column은 NotModified! 라는 column상태가 된다.


◈ retrieve row의 수정

   column이 사용자 엔트리에 의하던지 SetItem함수에 의해 변경되면    그 column의 상태가 DataModified!로 변경되고 그 column이 존재하는 Row상태는 DataModified!로 변경된다.


◈ Row삽입

   datawindow에 row를 삽입할때 row는 New!라는 상태를 가지며 row의 모든 column은 NotModified!라는 Column상태를 가진다. 삽입된 row안의 칼럼이 갱신된 후 사용자가 SetItem함수 또는 기본값으로 입력하던지 칼럼상태가 DataModified!로 변경될 때 그 Row의 상태는 NewModified!로 변경된다.


◈ sql문 생성

row의 상태

발생 명령문

NewModified!

Insert

DataModified!

Update


 ◈ DwSetItemStatus

   datawindow의 row나 column의 상태를 변경하기 위해서 사용하는     함수


◈ DwGetItemStatus

   datawindow의 row나 column의 현재상태를 알아보기 위해서 사용하    는 함수


[Tip42] 

Return Value들..


◈ sqlcode에서

     0     ok

    -1     error

   100     no data


◈ update에서

     1     update함수 사용

    -1     update함수 실패

           dberror event발생


◈ retrieve에서

     1     retrieve함수 성공

    -1     retrieve함수 실패

           dberror event발생

     0     no data

           dberror event발생하지 않음


◈ 4.0의 SetActionCode-->5.0의 return value

   SetActionCode(0) ----> return 0 ----> 계속 실행

   SetActionCode(1) ----> return 1 ----> 진행 멈춤

   SetActionCode(2) ----> return 2 ----> 현재의 요청을 무시하고

                                             다음의 요청을 실행   

   

[Tip43] 

파워빌더에서 전화걸기


Declare 밑의 Local External Function에 다음과 같이 Script한다.


Function int OpenComm (string lpComName, uint wInQueue, uint wOutQueue) Librar

y "user.exe"


Function int CloseComm (int nCid) Library "user.exe"

Function int WriteComm (int nCid, string lpBuf, int nsize) Library "user.exe"


Function int FlushComm (int nCid, int nQueue) Library "user.exe"


Button Script 에 아래와 같이 Scriptd한다.


int li_comid

int li_rc


String ls_port      // Poer No.

String ls_buffer    /* 전화 번호(에를 들어 Pre Dial 이 있다면

                       9,01410 이런식으로*/


li_comid = OpenComm(ls_port,128,128)

If li_comid < 0 Then

        If li_comid = -2 Then

             Messagebox("Port: "+ ls_port + " In - Use","Error")

        Else

             Messagebox("Port: " + ls_port, "Error")

        End If

        Return -1

End If


Flushcomm(li_comid,0)


Flushcomm(li_comid,1)

li_rc = WriteComm(li_comid, ls_buffer, len(ls_buffer))


MessageBox("Dialing " + Left(ls_buffer,len(ls_buffer) - 2),"Hit           Ok to hang up modem")

CloseComm(li_comid)

return 1


[Tip44] 

Null 값 핸들링시에


이번 Tip은 DataWindow Painter에서 NULL값을 처리하는 작은 트릭입니다. 일반적으로 '급여:' + String(salary) 라는 computed field를 사용할 수 있겠는데 만약 salary column의 값이 NULL인 경우 field또한 NULL값을 취하게 되어 아무런 값도 나타나지 않게 됩니다.

때문에 대개의 경우 IF문을 사용하지만 여기서는 String함수를 그대로 이용하는 방법을 소개합니다.

String함수의 mask는 number를 보기좋게 변환시켜주는 역할을 합니다.

mask는 number의 4가지 양태를 각각 지정할 수 있습니다.

즉 음수,양수,0,NULL 각각에 해당하는 mask를 semicolon으로 구분하여 별도로 지정합니다.

생략하는 경우 맨앞에 나타나는 mask로 처리됩니다.

'급여:' + string(salary, "#,##0; ; ; '(unknown)'")

위와같이 computed field를 사용하면 4번째 mask가 NULL인 경우에 적용됩니다. 즉 NULL인경우 '급여:(unknown)'이라고 나타나게 됩니다.

2번째,3번째는 생략되었기 때문에 1번째 mask 적용을 받게 됩니다.

이상의 설명은 String함수의 첫번째 Parameter data type이 number인 경우에 적용되는 팁이었습니다.


[Tip45] 

시스템환경을 체크해보자(cpu)


/* Declare */

Environment env

INTEGER     resp


/* Variable Setting */

resp = GetEnvironment(env)     //시스템 환경을 읽어오는 함수

       

CHOOSE  CASE  env.cputype      //CPU의 TYPE(파워빌더는 14                                        //개 인식)

 CASE  alpha!

  sle_1.text = 'Alpha'

 CASE  hppa!

  sle_1.text = 'HPPA'

 CASE  i286!

  sle_1.text = '286'

 CASE  i386!

  sle_1.text = '386'

 CASE  i486!

  sle_1.text = '486'

 CASE  m68000!

  sle_1.text = '68000'

 CASE  m68020!

  sle_1.text = '68020'

 CASE  m68030!

  sle_1.text = '68030'

 CASE  m68040!

  sle_1.text = '68040'

 CASE  mips!

  sle_1.text = 'MIPS'

 CASE  pentium!

  sle_1.text = 'Pentium'

 CASE  powerpc!

  sle_1.text = 'PowerPC'

 CASE  rs6000!

  sle_1.text = 'RS6000'

 CASE  sparc!

  sle_1.text = 'Sparc'

END CHOOSE




[Tip46] 

시스템환경을 체크해보자(os)


/* Declare */

Environment env

INTEGER     resp


/* Variable Setting */

resp = GetEnvironment(env)     //시스템 환경을 읽어오는 함수


CHOOSE  CASE env.OSType         //OS TYPE(파워빌더는 7개                                          //인식)

 CASE aix!

     sle_1.Text = 'AIX'

 CASE hpux!

     sle_1.Text = 'HPUX'

 CASE macintosh!

     sle_1.Text = 'MacIntosh'

 CASE osf1!

     sle_1.Text  = 'OSF1'

 CASE sol2!

     sle_1.Text  = 'Solaris 2'

 CASE Windows!

     sle_1.Text = 'Windows'

 CASE Windowsnt!

     sle_1.Text  = 'Windows NT'

END CHOOSE


sle_2.Text = Trim(Sle_1.Text) + ' ' + STRING(env.osmajorrevision) +    &

                                '.' + STRING(env.osminorrevision) +    &

                                '.' + STRING(env.osfixesrevision)


[Tip47] 

PowerBuilder의 버전은?


/* Declare */

Environment env

INTEGER     resp


/* Variable Setting */

resp = GetEnvironment(env)     //시스템 환경을 읽어오는 함수


CHOOSE CASE  env.pbtype      //현재 인스톨된 파워빌더의 type

       CASE  enterprise!

          sle_1.Text = 'Enterprise'

       CASE  desktop!

          sle_1.Text = 'Desktop'

END CHOOSE


IF env.win16 THEN

   sle_2.Text = Trim(sle_1.Text) + "/16"    + ' ' + &

                STRING(env.pbmajorrevision) + '.' + &

                STRING(env.pbminorrevision) + '.' + &

                STRING(env.pbfixesrevision)

ELSE

   sle_2.Text = Trim(sle_1.Text) + "/32"    + ' ' + &

                STRING(env.pbmajorrevision) + '.' + &

                STRING(env.pbminorrevision) + '.' + &

                STRING(env.pbfixesrevision)

END IF



[Tip48] 

Application의 환경


IF env.machinecode THEN               //어플리케이션의 실행코드

   sle_1.Text = "Machine Code"

ELSE

   sle_1.Text = "P-Code"

END IF


st_colors.text =string(env.numberofcolors)    //화면의 색상

st_height.text = string(env.screenheight)     //화면의 높이

st_width.text = string(env.screenwidth)       //화면의 너비


[Tip49] 

DataWindow에러 메시지의 변경


 DataWindow에서 값을 입력하다보면 'DataWinodw Error'라는 Title의 Message Box가 나타납니다.

 영어로 쓰여져있고 일반 End User들에게는 생소한 영문이라서 눈에 거슬렸는데 이 title을 원하시는 내용으로 변경하는 방법이 있습니다.

 DataWindow attribute에는 Message.Title이란 것이 있습니다.

 이부분을 수정하면 됩니다.

   dw_1.Modify("DataWindow.Mesage.Title = '입력오류'")


[Tip50] 

Dynamic DW Object and Compile...


 DataWindow control 이나 DataStore의 Object을 실행시에 Dynamic하게 변경시킬 수 있다.

 이런 프로그램은 실행파일을 만들때 PBR(PowerBuilder Resource) file에 DataWindow Object name을 기술해줘야 한다.

 DW Object을 dynamic하게 변동시키면 PowerBuilder에서는 문제없이 잘  실행되지만 실행파일을 만들어 보면 에러가 발생한다.

 다음과 같이 dynamic하게 하는 경우는 반드시 PBR file을 작성해야한다.


  dw_1.dataobject = 'd_employee_maint'


 PBR file이 필요한 이유는 DataWindow object 이름이 인용부호로 둘러싸여 있기 때문에 compiler가 실행파일 만들때 DataWindow object이라고 인식하지 못한다. 따라서 DataWindow Object으로 포함시키지 않게된다. 그렇기 때문에 반드시 PBR file에 다음과 같은 내용을 기술해주어야한다. employee.pbl(d_employee_maint) PBR file은 매모장같은 곳에서 작성하여 .pbr 확장자로 저장시키면된다.

그런다음에 실행파일을 만드는 Project painter에서 PBR file 이름을

포함시켜 주면 된다.


[Tip51] 

Powersoft사의 21세기 date


 PowerBuilder 5.0과 InfoMaker 5.0이외에도 다음과 같이 Powersoft사가 이전에 발표한 버전의 제품에서도 21세기를 대비한 Date가 지원된다. PowerBuilder 3.0 이상의 PowerBuilder 제품 InfoMaker 4.0 이상의 InfoMaker 제품이 PowerBuilder과 동일하게 지원한다.

PowerMaker 3.0과 PowerViewer 3.0에서는 PowerBuilder 3.0과 동일하게 Date를 지원한다.

Date형과 DateTime형의 데이터는  입력과 디스플레이 용도의 모든 형식으로 지원되며 Date의 진행도 적확히 파악되어 2000년 이상의 데이터까지도 표현 가능하다.

Date는 PowerBuilder내에서 Format과 Edit Mask 등 ,  여러 형식으로 표현할 수 있다.

특히 Date는 MM , DD , YY를 1에서 4까지의 자릿수를 이용하여 4자리 , 6자리 , 8자리등의 어떠한 조합으로도 구성할 수 있다.

이것은 개발자가 어플리케이션에 대해서 사전에 결정해야 할 사항이다.


* 2자리의 년도

  년도가 2자리로 표시되면 PowerBuilder와 InfoMaker는 다음과 같이

  세기를 선택한다

 

구간 

PowerBuilder와 InfoMaker의 가정

00 과 49사이

첫 두자리를 20으로 가정

50 과 99사이

첫 두자리를 19으로 가정

만일 출생일 등의 1950년 이전의 데이터가 있다면 항상 그것을 4자리의 년도로 정의하여 PowerBuilder와 InfoMaker가 올바르게 인식할 수 있도록 유의해야 한다.

* Date의 유효 범위

  PowerBuilder와 InfoMaker는 1000 ~ 3000년의 년도를 지원한다.


* 윤년의 처리

  PowerBuilder와 InfoMaker가 윤년에 대해서 사용하는 알고리즘은 다음과 같다. 년도는 4로 나뉘어져야 하며 만약 100으로도 나뉘어진다면 400으로도 나뉘어져야 한다.

그러므로 1900년은 윤년이 아니고 2000년은 윤년이다.


* S-Designor의 경우

  S-Designor의 경우 Date의 처리는 상관없다.

  Date의 형식은 개발자가  Domain과 Data Element를 모두 정의하므 로 원하는 형태로 표시된다. 툴에 의해 취급되는 데이터는 없다.


[Tip52] 

DropDownListBox의 Reset


DropDownListBox 가 처음 선택하지 않은 상태로 하려면?

Allow Editing 이 가능할 경우에는 ddlb_1.text = "" 로 하면 초기화가 되지만 Allow Editing 이 불가능할 경우에는 ddlb_1.text = "" 로 해도 초기화가 되지 않는다. 이럴 경우에는 SelectItem() 함수를 사용한다.

ddlb_1.SelectItem(0) 하면 초기에 선택되지 않은 상태로 초기화 할 수 있다.


[Tip53] 

Column의 중복제거


 다음과 같이 자료가 display되는 경우 대분류의 중복 자료를 없애고자 한다.


  대분류     중분류     소분류

    1          12         23

    1          22         33

    1          23         43

    2          24         53

    2          25         63

    2          26         73


 이런 경우 대개 Rows의 Supress  Repeating Values를 사용하지만

다음과 같은 경우는 사용할수가 없다.


  대분류     중분류     소분류

               12         23

               22         33

    1          23         43

               24         53

               25         63

    2          26         73


이때는 Column Object의 Expressions의 Visible 속성에

다음과 같이 서술하면 된다.


if (a[0] = a[1] ,0,1)


물론 위의 경우도 Supress  Repeating Values를 사용하지 않고

if (a[0] = a[-1] ,0,1)이라 서술하면 된다.


[Tip54] 

DW list에서 현재 row의 표시


datawindow로 만든 리스트에서 현재 row를 표시하는 방법은 여러가지가 있지요. 가장 많이 쓰는 방법은 RowFocusChanged 이벤트에서


Selectrow(0,false)

SelectRow(GetRow(),true)


라고 적는 방법이 있죠.

위와 같이 하면 키보드의 위화살표,아래화살표로 리스트를 이동할 때

현재 row가 반전되서 나옵니다.

그런데 dw로 만든 list에서 중복선택할 경우는 현재 row를 표현한다는게

어렵지요. 선택한 row를 반전시켜야 하는데, 현재 row를 반전시킬 수도 없고 이럴때

dw_employee.SetRowFocusIndicator() 

를 사용하는 것입니다.

dw_employee.SetRowFocusIndicator(FocusRect!)

라고 미리 정해 놓으면 현재 row는 점선사각형으로 표현되죠.

그리고 선택한 row는 반전시키면 되고.


[Tip55]

DW에서 like사용시 null컬럼 나오게


     table test 의 컬럼이

     empno,name,deptname,address,phone

     (사번,이름,부서명,주소,전화번호)

     라고 할때 datawindow를 디자인할때 주소로 검색할 경우

     retrieve argument를 r_address 라고 할때


     select empno,name,deptname,address,phone

     from test

     where address  = :r_address;


    로 정의한다.

    그런데, 전체를 검색하기 위해 r_address 의 값을 '%'로 주었을 경우

    address 컬럼이 null인 경우는 가져오지 못한다.

    이럴 경우 다음과 같이 한다.


     select empno,name,deptname,address,phone

     from test

     where ((address  = :r_address) or

           (address  is null and :r_address = '%'))


[Tip56] 

2000년 문제


 2000년 문제를 다루는 Tip으로서 그리 대단한건 아니지만 알면 도움될거 같아서 언급한다.

 PowerBuilder의 DataWindow에서 Data를 Retrieve한다음에 메뉴의 SaveAs에서 일반 Text file로 저장하면 PowerBuilder은 Date또는 DateTime의 날짜부분을 yy/mm/dd 형태로 저장한다.

이렇게 저장된 파일을 import받으면 년수가 50보다 작을때 20xx가된다.

즉 2000년을 기본으로 계산한다. 50보다 크면 19xx가된다.

하지만 이런식의 저장과 import를 막기위한 방법이 있다.

PowerBuilder이 사용하는 yy/mm/dd의 기본형은 Windows95에서 참조하여 사용한다. 따라서 Windows95의 날짜 기본형을 정정하면 해결된다. 정정하는 방법은 Windows95의 제어판에서 국가별 설정을 선택한다.

그러면 국가별 설정 등록 정보의 날짜를 선택하여 간단하게 표기의 형식을

yyyy/mm/dd로 수정해주면 된다. 그러면 항상 PowerBuilder에서 그 형식을 기본형으로 사용하게되어 2000년 형식에서 오는 문제를 해결할 수 있다.


[Tip57] 

DDDW(DropDownDataWindow) Trick


Parent DataWindow가 retrieve되는 시점에 DropDown DataWindow(dddw)의 모든 내용이 retrieve된다.

어떤 사람이 parent datawindow의 clicked event에서 dddw.retrieve()를 하면된다는 그러한  솔루션을 제공한적이 있었다. 그것이 틀리다고 볼수는 없다.하지만 문제가 있다.

clicked event는 user가 dropdown 화살표를 click하던 일반 text column을 click하던간에 event가 발생된다는 것이다.

만일 click하는 지점이 일반문자(dddw의 화살표 옆에 있는 문자말고...)라면 program은 dddw.retrieve()하지말아야 한다. 따라서 그 방법은 이들간의 click 지점이 어딘지를 구분해야하는 script가 필요하다.

GetObjectAtPointer()와 GetClickedColumn()함수는 dddw가 click되어졌는지 아닌지를 알려주진 못한다.

따라서 몇가지 내부적인 Hacking에 의해 올바른 event는 pbm_dwndropdown이라는 결론에 이르게 되었다. 그래서 solution은 다음과 같다.

user event ue_dddw_dropdown을 만들자(pbm_dwndropdown을 mapping) 그리고 다음과 같은 script를 작성한다.


1) DataWindowChild dwc

        dwGetChild(GetColumnName(), dwc)

        dwc.SetTransObject(SQLCA)

        dwc.retrieve()


위의 것은 기본적인 기능을 구현한 것이고 다음은 여러 개선사항을 첨가한 예다.


2) dwGetChild(GetColumnName(), dwc)

        // 만약 dwc.rowcount()>1 이라면 그러면 dddw는 이미 한번

        // drop down했다는 것이다. 따라서 귀찮게 다시 retrieve() 할          // 필요없다.

        // 또하나 주의할것이 있다. dddw는 하나의 단일 row 즉 blank          // 인 단일 row를 기본적으로 갖는다.


        if dwc.rowcount() > 1 then return


        dwc.SetTransObject(SQLCA)

        dwc.retrieve()

3)위의 (1)과 동일한 방법

        dwc.retrieve(GetText())


        dddw의 SQL문장:

         select country_name from contry_lookup

         where country_name like :retrieve_argument + "%"


위의 것은 만약 user가 'ca'를 입력해서 dropdown 화살표를 click하면 국가이름의 목록이 아주 보기좋게 drop down될것이다.


    cambodia

    cameroon

    canada

      .

      .

하지만 france나 sussia같은 국가명은 retrieve되지 않는다. 왜냐하면 SQL문장의 like구문에 의해 ca로 시작하는 것만 retrieve하기 때문이다.

이건 retrieve량을 줄이게되어 메모리 절약등의 효과도 있게된다.

그런데 만일 dddw가 employee_name이고 department field가 있다고 생각한다면...

회사의 사원은 상당히 많다 따라서 department에 'Information Service'라고 입력한다. 그리고 employee_name field로 이동하여 dddw의 화살표를 click하면 dddw는 오직 Information Service에서 일하는 사원만을 retrieve하게된다.


        //(1)과 같은 방법

        dwc.retrieve(GetItemString(GetRow(), "department"))


        dddw의 SQL문장:

          select employee_name from employee_table

          where department = :retrieve_argument


여기까지의 설명에서 주 요점 사항으로 기억할것은 pbm_dwndropdown event를 이해하고 사용하는 것이다. 이건 undocumented event다.


[Tip58] DataWindow, refer to data


DataWindow, refer to data in a different row.

DataWindow에 product_id라는 column이 있다.

product_id값을 검사하여 product_id값이 변경되면 글자색도 변경하고자 한다. 이런 경우 현재 row의 product_id값과 다음 row의 product_id값을 비교할 수 있다면 간단해진다.

DataWindow에서 각각의 column은 마치 배열처럼 취급할 수 있다.

즉 row는 column의 첨자라고 볼 수 있다.

이러한 특징은 script 작성시에 많이 느낄 수 있을 것이다.

그러나 DataWindow field에선 좀 다르다.

현재 row는 0이고 그 다음 row는 -1, -2, -3으로 나간다.

그 전 row는 1,2,3,4.. 가 된다.

따라서 위의 문제가 아주 간단히 해결된다.


 if ( product_id = product_id[-1], rgb(0, 0, 0), rgb(255, 0, 0) )

이러한 식을 product_id 의 color property에 기술하면 간단히 해결된다.


[TIP 59]

DataWindow 리스트 멀티 Select 기법


윈도우즈의 화일관리자등을 보면 Ctrl+Click 의 경우 계속해서 선택하고

Shift+Click 의 경우 처음 클릭과 나중클릭의 자료를 전부 선택하는 기능을 DataWindow에 적용해 보았습니다. 먼저 Global 함수를 다음과 같이 만듭니다.

----------------------------------------------

함수명 : gf_multi_select

전달값 : a_dw (DataWindow type)

----------------------------------------------

long w_rownum,w_selrow,i

w_rownum = a_dw.GetClickedRow()

if w_rownum <= 0 then return

if keydown(keyshift!) then

   w_selrow = a_dw.GetSelectedRow(0)

 if w_selrow > 0 then

  for i = min(w_selrow,w_rownum) to max(w_selrow,w_rownum)

          a_dw.SelectRow(i,true)

  next

 else

   a_dw.SelectRow(w_rownum,true)

 end if

elseif keydown(KeyControl!) then

  if a_dw.isSelected(w_rownum) then

     a_dw.SelectRow(w_rownum,false)

  else

     a_dw.SelectRow(w_rownum,true)

  end if

else

  if a_dw.isSelected(w_rownum) then

     a_dw.SelectRow(0,false)

  else

     a_dw.SelectRow(0,false)

     a_dw.SelectRow(w_rownum,true)

  end if

end if


이 함수를 해당 DataWindow의 Click Event에서 부르면 됩니다.


dw_1의 click event


gf_multi_select(this)



출처 : 주호의파워빌더