피로곰's 모두의 프린터

반응형

https://github.com/pirogom/walk

 

GitHub - pirogom/walk: Walk is a "Windows Application Library Kit" for the Go Programming Language. Clone from lxn/walk.

Walk is a "Windows Application Library Kit" for the Go Programming Language. Clone from lxn/walk. - GitHub - pirogom/walk: Walk is a "Windows Application Library Kit" for the Go...

github.com

https://github.com/pirogom/walkmgr

 

GitHub - pirogom/walkmgr: for easy window native gui programming on go. enhanced pirogom/walk.

for easy window native gui programming on go. enhanced pirogom/walk. - GitHub - pirogom/walkmgr: for easy window native gui programming on go. enhanced pirogom/walk.

github.com

https://github.com/pirogom/walkmgr_example

 

GitHub - pirogom/walkmgr_example: walkmgr example project

walkmgr example project . Contribute to pirogom/walkmgr_example development by creating an account on GitHub.

github.com

Go언어로 윈도API를 이용한 GUI 프로그래밍과 관련된 글을 최근에 계속 작성하고 있는데요..

기존 lxn/walk, win 등에서 개인적으로 수정할 부분들을 고쳐서 따로 제 깃헙에 클론한 패키지들을 계속 손봐서 업데이트 하고 있습니다.

그중 walk 로 gui 프로그래밍을 하면서 가장 복잡한 구조를 가지게 되는 것이 .. 리스트컨트롤.. 입니다.

기존에 글을 올린것에서 TableView 라고 하여 walk 에서 기본적으로 제공하는 리스트컨트롤을 이용하는 방법을 글을 쓴적이 있습니다만.

그 구조는 테이블을 하나 만들때마다 model 구조체를 만들고 실제 사용되는 자료형과 매칭시켜서 동작이 되다보니 매번 중복된 코드를 계속 양산해야 하는 불편함이 존재했습니다.

그래서 개인적으로 자주 쓰는 구조와 형태로 MFC의 리스트컨트롤과 비스무리한 구조를 만들어 봤습니다.

우선 그러기 위해서 기본적으로 컨트롤에서 다루는 컬럼의 데이터들은 전부 string 형으로 통일하였구요.. 그러지 않으면 interface{}로 .. 또다시 복잡한 구조가 되기 때문에 .. 리스트에 표시되는 것 외에 실질 데이터는 따로 구성해서 관리해야 할 순 있지만..

일반적으로 MFC나 API에서 리스트컨트롤을 다룰때에도 실제 값이 저장된 구조체나 클래스와 별개로 리스트컨트롤의 데이터는 따로 처리하기 마련이라 .. 제 입장으론 이러한 구조가 오히려 범용적으론 좋을거라 생각 합니다.

기존의 TableView 구조도 그대로 놔둔 상태이니 필요에 따라 선택해서 사용하시면 되겠습니다.

walkmgr_example 프로젝트를 보시면 list_control_test.go 라는 파일이 추가 되었습니다.

package main

import (
	"fmt"
	"testing"
	"time"

	"github.com/pirogom/walkmgr"
)

// 리스트 컨트롤, 미완성 .. 개발중
func TestListControl(t *testing.T) {
	wm := walkmgr.NewWin("리스트 컨트롤 테스트", 640, 480)

	lc := walkmgr.NewListControl(wm, &walkmgr.ListControlCfg{MultiSelect: true, CheckBox: true})

	lc.AddColumn("이름", 300, walkmgr.LISTCTRL_ALIGN_LEFT, false)
	lc.AddColumn("나이", 50, walkmgr.LISTCTRL_ALIGN_LEFT, false)
	lc.AddColumn("성별", 50, walkmgr.LISTCTRL_ALIGN_LEFT, false)
	lc.AddColumn("상태", 50, walkmgr.LISTCTRL_ALIGN_LEFT, false)
	lc.Create()

	wm.HComposite()
	wm.PushButton("값추가", func() {
		idx := lc.AddItemData(fmt.Sprintf("피로곰-%d", time.Now().Unix()))
		lc.SetItemData(idx, 1, "나이")
		lc.SetItemData(idx, 2, "성별")
		lc.SetItemData(idx, 3, "상태")
	})
	wm.End()

	wm.Start()
}

func TestListControlOrdering(t *testing.T) {
	wm := walkmgr.NewWin("리스트 컨트롤 테스트", 640, 480)

	lc := walkmgr.NewListControl(wm, &walkmgr.ListControlCfg{MultiSelect: true, CheckBox: true})

	lc.AddColumn("이름", 300, walkmgr.LISTCTRL_ALIGN_LEFT, true)
	lc.AddColumn("나이", 50, walkmgr.LISTCTRL_ALIGN_LEFT, true)
	lc.AddColumn("레벨", 50, walkmgr.LISTCTRL_ALIGN_LEFT, true)
	lc.Create()

	wm.Starting(func() {
		for i := 0; i < 10; i++ {
			nr := lc.AddItemData(fmt.Sprintf("피로곰-%d", i))
			lc.SetItemData(nr, 1, fmt.Sprintf("%d", i))
			lc.SetItemData(nr, 2, fmt.Sprintf("%d", 10-i))
		}
	})

	wm.Start()
}

func TestListControlItemDblClick(t *testing.T) {
	wm := walkmgr.NewWin("리스트 컨트롤 테스트", 640, 480)

	lc := walkmgr.NewListControl(wm, &walkmgr.ListControlCfg{MultiSelect: true, CheckBox: true})

	lc.AddColumn("이름", 300, walkmgr.LISTCTRL_ALIGN_LEFT, false)
	lc.AddColumn("나이", 50, walkmgr.LISTCTRL_ALIGN_LEFT, false)
	lc.AddColumn("레벨", 50, walkmgr.LISTCTRL_ALIGN_LEFT, false)
	lc.Create()

	// 더블클릭한 아이템의 정보를 출력
	lc.ItemDoubleClicked(func(idx int) {
		fmt.Println(lc.GetItemData(idx, 0))
		fmt.Println(lc.GetItemData(idx, 1))
		fmt.Println(lc.GetItemData(idx, 2))
	})

	wm.Starting(func() {
		for i := 0; i < 10; i++ {
			nr := lc.AddItemData(fmt.Sprintf("피로곰-%d", i))
			lc.SetItemData(nr, 1, fmt.Sprintf("%d", i))
			lc.SetItemData(nr, 2, fmt.Sprintf("%d", 10-i))
		}
	})

	wm.Start()
}

레이아웃 배치를 하는 구조는 버튼이나 라벨같은 놈들과는 약간 다릅니다. 우선 리스트컨트롤 객체를 생성을 먼저 한 뒤에 컬럼 정보등을 지정을 해야 하기 때문에..

기존에 WalkUI의 메소드들로 존재하던 PushButton이나 Label 같은 놈들과 달리 walkmgr 에서 NewListControl 이라는 메소드를 통해서 리스트컨트롤 객체를 생성합니다.

우선 리스트 컨트롤을 먼저 생성을 해야 하는데..

func NewListControl(wm *WalkUI, cfg *ListControlCfg) *ListControl

첫번째 인자인 wm 은 walkmgr.NewWin 등으로 생성한 WalkUI 객체를 전달하시면 됩니다.

ListControlCfg 는 몇가지 옵션사항을 포함한 구조체로 

type ListControlCfg struct {
	CheckBox    bool
	MultiSelect bool
}

CheckBox 는 리스트의 맨 좌측에 체크박스를 사용할 것이냐 여부를 true, false 로 지정하시면 됩니다. CheckBox 가 true 이면 체크박스를 사용하는 리스트컨트롤이 생성되고 false 이면 체크박스 없는 리스트가 생성됩니다.

MultiSelect 는 리스트의 각 row 그러니까 한번에 하나의 줄만 선택 가능한지 여러 줄을 선택 가능한지 여부를 true, false 로 지정하시면 됩니다.

이 cfg 값은 nil로 전달할경우 둘다 false 인 상태로 생성됩니다.

리스트컨트롤을 생성 하였으면 .. 컬럼 헤더를 설정하시면 됩니다.

	lc.AddColumn("이름", 300, walkmgr.LISTCTRL_ALIGN_LEFT, true)
	lc.AddColumn("나이", 50, walkmgr.LISTCTRL_ALIGN_LEFT, true)
	lc.AddColumn("레벨", 50, walkmgr.LISTCTRL_ALIGN_LEFT, true)

AddColumn 메소드를 사용하시면 되구요 

func (t *ListControl) AddColumn(title string, width int, align ListCtrlAlign, order bool)

title : 컬럼 헤더의 명칭입니다.

width: px 단위의 컬럼의 폭을 지정하시면 됩니다.

align : 컬럼 값을 좌,우,중앙 정렬여부를 지정하시면 됩니다. 

	LISTCTRL_ALIGN_RIGHT  ListCtrlAlign = 0
	LISTCTRL_ALIGN_LEFT   ListCtrlAlign = 1
	LISTCTRL_ALIGN_CENTER ListCtrlAlign = 2

이 3값중 하나를 지정하시면 됩니다.

order: 컬럼 헤더를 클릭시에 해당 컬럼을 기준으로 정렬(Sorting)을 해주는 기능을 쓸꺼냐 말꺼냐를 true, false 로 지정하시면 됩니다. true 로 활성화 하시면 컬럼헤더 클릭시에 ase, desc 로 토글되며 정렬됩니다.

컬럼 헤더의 지정이 끝나면 

lc.Create()

리스트 컨트롤을 생성하면 되겠습니다. 내부적으로 NewListControl 호출시에 전달한 WalkUI를 통해서 레이아웃에 추가하는 작업등은 이 단계에서 마무리 합니다.

			nr := lc.AddItemData(fmt.Sprintf("피로곰-%d", i))
			lc.SetItemData(nr, 1, fmt.Sprintf("%d", i))
			lc.SetItemData(nr, 2, fmt.Sprintf("%d", 10-i))

리스트에 값을 추가하는 방법은 AddItemData, SetItemData 메소드를 사용하시면 됩니다. 기본적으로 한 줄을 추가하려 할때는 제일 처음에 AddItemData로 새로운 줄을 추가하시고 그 뒤에 존재하는 줄의 컬럼 값을 변경하는 식으로 동작합니다.

그래서 AddItemData 로 새로운 줄을 추가하면 추가된 줄의 인덱스 번호가 리턴됩니다. 이 인덱스 번호가 row 번호가 되겠습니다. 

SetItemData 메소드의 인자가 row, col, value 이니 AddItemData의 리턴값으로 받은 row 값을 첫번째 인자로하고 컬럼 인덱스를 지정한뒤 값을 입력하면 되겠습니다.

기본적으로 모두의 PDF의 리스트 컨트롤서 동작하는 여러 기능들은 대부분 포함할 예정인데.. 현재 포함된 기능은 체크박스가 아닌 부분을 더블클릭 하더라도 체크가 된다던지 ..

마우스 휠을 굴리면 선택된 row 가 위아래로 변경된다던지, 스페이스바 키를 통해서 선택된 아이템(row)들의 체크를 토글시킨다던지 하는 등의 짓들은 기본적으로 포함되었습니다.

그 외에 현재 내장된 메소드들에 대한 간단한 설명을 해보도록 하겠습니다.

func (t *ListControl) GetItemCount() int

리스트 상의 아이템(rows)수를 리턴합니다.

func (t *ListControl) GetItemData(row, col int) string

row, col 값을 기준으로 해당 컬럼의 값을 리턴합니다.

func (t *ListControl) SetItemData(row, col int, value string)

row,col 값을 기준으로 해당 컬럼의 값을 변경합니다.

func (t *ListControl) RemoveAll()

리스트의 모든 데이터를 삭제합니다.

func (t *ListControl) UpdateItem(row int) error

지정한 row의 화면 표시를 갱신합니다.

func (t *ListControl) UpdateAll()

모든 줄의 화면 표시를 갱신합니다.

func (t *ListControl) DeleteItem(row int)

지정한 row만 삭제합니다.

func (t *ListControl) AddItemData(value string) int

새로운 줄을 추가합니다. 이때 추가된 줄의 row 인덱스를 리턴합니다.

func (t *ListControl) SelectedItem() *ListControlItem

현재 선택된 아이템(row, 줄)의 데이터를 리턴합니다. 여러 아이템이 선택된 경우 마지막 선택된 한놈의 데이터를 리턴합니다. 이 ListControlItem 구조체 내에는 values []string 으로 string 슬라이스가 있습니다. 이 슬라이스에 컬럼 인덱스로 데이터가 저장되어 있습니다.

func (t *ListControl) SelectedItemIndex() int

현재 선택된 아이템의 row 인덱스를 리턴합니다.

func (t *ListControl) AllSelectedItem() []ListControlItem

선택된 모든 아이템을 []ListControlItem 슬라이스에 담아 리턴합니다.

func (t *ListControl) AllSelectedItemIndex() []int

선택된 모든 아이템의 row 인덱스를 []int 슬라이스에 담아 리턴합니다.

func (t *ListControl) SelectedItemCount() int

선택된 아이템의 갯수를 리턴합니다.

func (t *ListControl) CheckedAll(checked bool)

전체 아이템을 체크상태를 변경합니다. checked 가 ture 면 모두 체크가 되고 false 면 모두 체크가 해제됩니다.

func (t *ListControl) CheckedCount() int

체크된 아이템의 갯수를 리턴합니다.

func (t *ListControl) CheckedIndexs() []int

체크된 아이템의 row 인덱스를 []int 슬라이스에 담아 리턴합니다.

func (t *ListControl) ItemDoubleClicked(afterFunc ItemDoubleClickedFunc)

특정 아이템(줄,row)를 더블클릭 했을때 발생하는 이벤트 콜백 함수를 지정하는 메소드입니다. 이 메소드로 지정된 함수가 아이템 더블클릭시 호출됩니다.

type ItemDoubleClickedFunc func(idx int)
type CurrentIndexChangedFunc func(idx int)
type KeyDownFunc func(key walk.Key)

콜백 함수의 형태는 위와 같습니다.

func (t *ListControl) CurrIndexChanged(afterFunc CurrentIndexChangedFunc)

선택된 아이템의 인덱스가 변경될경우 이벤트 콜백을 받을 함수를 지정하는 메소드입니다. 위아래 화살표키나 마우스 클릭으로 선택한 아이템이 변경될경우 호출됩니다.

func (t *ListControl) KeyDown(afterFunc KeyDownFunc)

키보드 입력에 대한 이벤트 콜백을 받을 함수를 지정하는 메소드입니다. 

기타 추가되는 기능은 계속 .. 글로 작성하겠습니다.

이상입니다.

반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band