피로곰's 모두의 프린터

모두의 프린터는 개인, 기업 상관 없이 누구나 무료로 사용가능한 프로그램입니다. 회원가입,카드결제등을 요구하지 않습니다! 광고를 통해 연결되는 사이트에서의 회원가입, 카드결제 피해를 보지 않도록 주의하세요!
반응형

이번에 모두의 PDF에 책갈피 기능을 추가하면서 PDF의 책갈피 기능이 Parent, Children 을 이용한 무한 계층구조 인지라 이런 형태를 다루는데 가장 적합한 UI요소는 아무래도 트리뷰(TreeView)라서 관련 기능을 walkmgr 에 추가해 봅니다.

MFC나 윈도 API를 다루던 시절에도 사실 트리뷰는 진짜 쓰기 싫어라 하던 놈중 하나인데요 가능하면 리스트컨트롤로 대충 땜빵해서 쓰곤 했는데 .. 계층구조를 리스트 컨트롤로 다루는건 매우 비효율적인지라 미루고 미루다 추가해 봅니다.

이런놈을 보통 트리뷰라고 하지요.. 우리가 가장 흔히 접하는 경우를 보자면

윈도 탐색기의 폴더구조를 들 수 있겠습니다. 대표적인 계층구조의 트리뷰를 사용하고 있는 놈입니다.

기존에 pirogom/walk 나 pirogom/walkmgr 을 쓰시던 분들은 go get -u 명령으로 업데이트 하시면 업데이트된 패키지를 사용 가능하실꺼구요.

walkmgr 의 트리뷰 관련 코드는

https://github.com/pirogom/walkmgr/blob/main/treeview.go

 

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

위 링크를 참고하시면 되겠습니다.

언제나 그렇듯이 walkmgr 의 사용 예제는 walkmgr_example 저장소에서 확인 가능하십니다.

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

이번에 추가되는 트리뷰와 관련된 코드는

https://github.com/pirogom/walkmgr_example/blob/master/treeview_test.go

 

GitHub - pirogom/walkmgr_example: walkmgr example project

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

github.com

위 코드를 참고하시면 되겠습니다.

우선 가장 기본적인 사용법을 설명해봅니다.

func TestTreeView(t *testing.T) {
	icon1, _ := walk.NewIconFromFile("./icon/icon1.ico")
	icon2, _ := walk.NewIconFromFile("./icon/icon2.ico")

	wm := walkmgr.NewWin("트리뷰 예제", 640, 480)

	treeView := wm.NewTreeView()
	item := treeView.AddItem("1", icon1)
	item2 := item.AddItem("1-1", icon2)
	item2.AddItem("1-1-1", icon1)
	item2.AddItem("1-1-2", icon2)
	item2.AddItem("1-1-3", icon1)
	item2.AddItem("1-1-4", icon2)
	item2.AddItem("1-1-5", icon1)
	item2.AddItem("1-1-6", icon2)

	item3 := item.AddItem("1-2", icon2)
	item3.AddItem("1-2-1", icon1)
	item3.AddItem("1-2-2", icon2)
	item3.AddItem("1-2-3", icon1)
	item3.AddItem("1-2-4", icon2)
	item3.AddItem("1-2-5", icon1)
	item3.AddItem("1-2-6", icon2)

	item4 := treeView.AddItem("2", icon1)
	item4.AddItem("2-1", icon2)

	treeView.UpdateItems()

	wm.Start()
}

제가 작업중인 walkmgr 이 대부분 그렇듯이 트리뷰가 배치될 코드상 위치에서 WalkUI의 NewTreeView() 메소드를 호출하면 해당 위치에 트리뷰 객체가 추가됩니다. Split이나 Composite, GroupBox같은 레이아웃요소로 감싸는 등의 행위는 똑같이 가능 합니다.

이 NewTreeView() 메소드의 리턴값으로 walkmgr.TreeView 객체를 반환하고 이 객체로 트리뷰에 아이템을 추가하고 관리할 수 있습니다.

	treeView := wm.NewTreeView()
	item := treeView.AddItem("1", icon1)
	item2 := item.AddItem("1-1", icon2)

wm.NewTreeView() 메소드로 트리뷰를 생성하고 리턴받은 treeView객체의 AddItem 메소드를 호출하면 트리뷰의 맨 상위 루트에 아이템이 추가됩니다. 이 AddItem 메소드는 추가된 아이템의 객체를 반환하고 이렇게 반환된 아이템 객체에 존재하는 AddItem 메소드 또한 해당 아이템의 하위에 새로 아이템을 추가를 해주게 됩니다.

그러니 위의 경우 

트리뷰 최상단 루트에 1이라는 이름의 루트 아이템을 추가하고 이 추가된 루트아이템 하부에 1-1 이라는 이름의 서브 아이템을 추가를 하게 되는겁니다.

이 AddItem 의 파라미터는 화면에 표시될 이름.. 타이틀이라 해야 하나요.. 보여질 문자열을 첫번째 인자로 두번째 인자는 표시될 아이콘 객체를 전달하면 되고 두번째 인자를 nil로 전달하면 아이콘 자리는 빈채로 구성됩니다. 아이콘이 없는것 보다는 있는게 좋아요..

	icon1, _ := walk.NewIconFromFile("./icon/icon1.ico")
	icon2, _ := walk.NewIconFromFile("./icon/icon2.ico")

그래서 함수 상단에 icon1, icon2 파일을 미리 로드해두고 사용하도록 예제가 구성되어 있습니다.

	item := treeView.AddItem("1", icon1)
	item2 := item.AddItem("1-1", icon2)
	item2.AddItem("1-1-1", icon1)
	item2.AddItem("1-1-2", icon2)
	item2.AddItem("1-1-3", icon1)
	item2.AddItem("1-1-4", icon2)
	item2.AddItem("1-1-5", icon1)
	item2.AddItem("1-1-6", icon2)

최상단 루트에 1이라는 명칭으로 아이템을 추가하고 이 추가된 아이템에 1-1을 추가하고 1-1을 추가하고 리턴받은 item2에 1-1-1~1-1-6까지를 추가해 봤습니다.

위 코드의 결과가 이렇게 나오는 것이지요.

	item3 := item.AddItem("1-2", icon2)
	item3.AddItem("1-2-1", icon1)
	item3.AddItem("1-2-2", icon2)
	item3.AddItem("1-2-3", icon1)
	item3.AddItem("1-2-4", icon2)
	item3.AddItem("1-2-5", icon1)
	item3.AddItem("1-2-6", icon2)

	item4 := treeView.AddItem("2", icon1)
	item4.AddItem("2-1", icon2)

같은 개념으로 코드를 구성하면 아래와 같은 결과가 됩니다.

AddItem으로 값을 추가한 뒤에는 입력된 값이 실제 트리뷰 UI에 적용되도록 NewTreeView() 메소드로 리턴받은 트리뷰 객체의 UpdateItems() 메소드를 호출하셔야 적용됩니다.

	treeView.UpdateItems()

다음으로는 .. 동적으로 아이템을 추가하는 예제를 설명해 봅니다.

func TestTreeView2(t *testing.T) {
	var itemCnt int
	icon1, _ := walk.NewIconFromFile("./icon/icon1.ico")
	icon2, _ := walk.NewIconFromFile("./icon/icon2.ico")

	wm := walkmgr.NewWin("트리뷰 예제", 640, 480)

	treeView := wm.NewTreeView()
	treeView.AddItem("1", icon1)

	treeView.UpdateItems()

	wm.PushButton("루트 추가", func() {
		itemCnt++
		newItem := treeView.AddItem(fmt.Sprintf("추가됨%d", itemCnt), icon2)
		treeView.UpdateItems()
		treeView.SetCurrentItem(newItem)
	})

	wm.PushButton("서브 추가", func() {

		currItem := treeView.CurrentItem()

		if currItem == nil {
			walkmgr.MsgBox("트리뷰 아이템을 선택하세요.")
			return
		}

		itemCnt++
		newItem := currItem.AddItem(fmt.Sprintf("추가된 서브아이템%d", itemCnt), icon2)
		treeView.UpdateItems()
		treeView.SetCurrentItem(newItem)
		treeView.SetExpanded(currItem, true)

	})

	wm.Start()
}

루트 추가의 경우에는 하위로 추가되는게 아니기 때문에 그냥 NewTreeView()에서 리턴받은 객체의 AddItem 메소드를 호출하면 그만입니다. 추가후 UpdateItems()만 호출해주시면 추가된 사항이 바로 적용 됩니다.

	wm.PushButton("루트 추가", func() {
		itemCnt++
		newItem := treeView.AddItem(fmt.Sprintf("추가됨%d", itemCnt), icon2)
		treeView.UpdateItems()
		treeView.SetCurrentItem(newItem)
	})

SetCurrentItem 메소드는 파라미터로 전달된 트리뷰 아이템을 현재 선택된 아이템으로 만들어 줍니다. 아이템이 많은 경우 추가된 놈이 어디 있는지 알아보기 힘들수 있음으로 제 경우 보통 이런식으로 처리합니다.

	wm.PushButton("서브 추가", func() {

		currItem := treeView.CurrentItem()

		if currItem == nil {
			walkmgr.MsgBox("트리뷰 아이템을 선택하세요.")
			return
		}

		itemCnt++
		newItem := currItem.AddItem(fmt.Sprintf("추가된 서브아이템%d", itemCnt), icon2)
		treeView.UpdateItems()
		treeView.SetCurrentItem(newItem)
		treeView.SetExpanded(currItem, true)

	})

서브에 추가는 루트와 달리 특정 아이템의 서브 아이템으로 추가되는거라 선택된 현재 아이템 객체를 얻어와야 합니다. 이 짓을 해주는게 트리뷰 객체의 CurrentItem() 메소드 입니다. 현재 선택된 아이템이 없으면 이 메소드의 리턴값은 nil을 리턴합니다. 예외처리는 반드시 하셔야 겠구요.

루트에 추가시에는 트리뷰 객체에 직접 하면 되지만 서브 아이템으로 추가는 부모아이템이 있어야 함으로 현재 선택된 아이템을 부모로 서브로 생성하게 됩니다.

고로 CurrentItem 메소드로 리턴받은 아이템 객체의 AddItem 메소드로 추가를 하면 됩니다.

루트에 추가에선 안보이던 메소드가 하나 있습니다. SetExpanded .. 이놈은 첫번째 인자로 전달된 아이템의 서브 아이템들을 펼쳐놓을지 접어놓을지를 두번째 인자로 전달하시면 됩니다.

두번째 인자가 true 이면 펼쳐진 상태, false 이면 접힌상태가 되겠습니다.

트리뷰의 예는 좀더 심화 과정이 필요할것 같아서 이후에 한두개 정도 예제코드를 더 만들고 설명을 하도록 하겠습니다.

이상입니다.

반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band