qml tableview에 checkbox를 넣어 사용하기

김준형·2024년 1월 11일
0

qt 5.12.x 기반으로 만들어진 것 입니다.
qml tableview에 전체선택할 수 있게 checkbox를 column에 추가 하고 각 row마다 checkbox를 넣어 사용 할 수 있게 만들었습니다.

CustomTableView.qml

import QtQuick 2.0
import QtQuick 2.12
import QtQuick.Controls 2.5
import QtQuick.Controls 1.5

TableView {
	id: custom_tableview
	clip: true
	focus: true
	frameVisible: true
	sortIndicatorOrder: Qt.DescendingOrder
	sortIndicatorColumn: 1
    selectionMode: SelectionMode.ExtendedSelection//shiftKey_event ? SelectionMode.ExtendedSelection : SelectionMode.SingleSelection
	alternatingRowColors: false

	property bool all_check: !model ? false : (custom_tableview.model.count > 0 ?
								custom_tableview.selection.count === custom_tableview.model.count : false)
	property bool shiftKey_event: false

    Component { //동적으로 tableview에 넣을 checkbox component
		id: checkBoxColumnComponent
		TableViewColumn {
			id: checkBoxTableViewColumn
			resizable: false
			movable: false
			delegate: Item {
				CheckBox {
					implicitWidth: 14
					implicitHeight: 14
					anchors.horizontalCenter: parent.horizontalCenter
					anchors.verticalCenter: parent.verticalCenter
					checked: model ? model.checked : false
				}

				MouseArea {
					id: mouseArea
					anchors.fill: parent
					acceptedButtons: Qt.LeftButton
					enabled: shiftKey_event ? false : true
					onClicked: {
						if(model) {
							if(!model.checked) {
								custom_tableview.selection.select(styleData.row)
							} else {
								custom_tableview.selection.deselect(styleData.row)
							}
						}
					}
				}
			}
		}
	}


    Component { //동적으로 tableview에 넣을 일반 text component
		id: columnComponent
		TableViewColumn {
			id: componentTableViewColumn
			width: 100
			delegate: Text {
				id: columnDelegateTxt
				text : {
				   if(styleData.value !== undefined) return styleData.value
				   else return ""
				}
				horizontalAlignment: Text.AlignLeft
				leftPadding: 10
				verticalAlignment: Text.AlignVCenter
				clip: true
				elide: Text.ElideRight
				onImplicitWidthChanged: {
					if(implicitWidth > componentTableViewColumn.width) {
						componentTableViewColumn.width = implicitWidth + 20
					}
				}

				ToolTip.visible: truncated && mouseArea.containsMouse ? true : false
				ToolTip.text: styleData.value
				MouseArea {
					id: mouseArea
					anchors.fill: parent
					hoverEnabled: true
					acceptedButtons: Qt.NoButton
				}
			}
		}
	}


    Component.onCompleted:  {
        //tableview column을 동적으로 만들게 하였고 제일 처음 column에는 checkbox를 이용한 column
		custom_tableview.addColumn(checkBoxColumnComponent.createObject(custom_tableview,
																		{"role": "checked",
																		"width": 50}))

		custom_tableview.addColumn(columnComponent.createObject(custom_tableview,
																{"role": "text",
																"title": "TEXT",
																"width": 150}))
	}

	onSortIndicatorColumnChanged: {
		if(custom_tableview.rowCount === 0) return ;
        if(custom_tableview.getColumn(sortIndicatorColumn).role === "checked") { //전채선택을 눌렀을때
			sortIndicatorColumn = ++sortIndicatorColumn
            if(all_check) { //전체선택되었던거라면
                custom_tableview.selection.clear() // 선택 초기화
			} else {
                custom_tableview.selection.selectAll() // 모두 선택
			}
		}
	}



    headerDelegate: Rectangle {
		border.width: 0.5
		border.color: "#d2d2d2"
		height: 25
		color: "#efefef"
		clip: true

		CheckBox {
			id: headerCheckBox
			implicitWidth: 14
			implicitHeight: 14
			anchors.horizontalCenter: parent.horizontalCenter
			anchors.verticalCenter: parent.verticalCenter
            visible: styleData.column === 0 ? true : false //column이 0번째라면 checkbox활성화
			checked: visible && all_check
		}

		Text {
			anchors.left: parent.left
			anchors.leftMargin: 8
			anchors.verticalCenter: parent.verticalCenter
			color: "#2d3150"
			text: styleData.value
			width: parent.width
			elide: Text.ElideRight
			visible: !headerCheckBox.visible
		}
	}

	rowDelegate: Rectangle {
		height: 35
        property var rowSelect: styleData.selected // row가 선택여부
		property int rowIdx: -1
		readonly property color selectColor: "#D2E4Ed"
		color: {
			var baseColor = styleData.row % 2 ? "#f6f6f6" : "#ffffff"
			return styleData.selected? selectColor : baseColor
		}
		onRowSelectChanged: {
            console.log(styleData.row)
			if(styleData.row === undefined) return ;
			var no
			if(styleData.row === -1) {
				no = custom_tableview.model.get(rowIdx).no
			} else {
				rowIdx = styleData.row
				no = custom_tableview.model.get(rowIdx).no
			}
            custom_tableview.model.setProperty(no, "checked", rowSelect)
		}

		Text {
			anchors.left: parent.left
			anchors.leftMargin: 8
			anchors.verticalCenter: parent.verticalCenter
			color: "#2d3150"
			elide: Text.ElideRight
			text: styleData.value === undefined ? "" : styleData.value
			width: parent.width
		}
	}
}

아래 코드를 보면

if(all_check) { //전체선택되었던거라면
                custom_tableview.selection.clear() // 선택 초기화
			} else {
                custom_tableview.selection.selectAll() // 모두 선택
			}

tableview row를 전체선택 및 전체선택 초기화가있는데 clear(), selectAll() 함수가 호출되게되면 아래 코드에 styleData.selected가 해당 row에 호출된다.

rowDelegate Rectangle {
	height: 35
    property var rowSelect: styleData.selected // row가 선택여부
}

하지만 데이터가 많으면 많을 수록 selectAll(), clear()를 하게 되면 양이많아서 많이 버벅이지 않을까 싶었는데
tableview 자체에서 내부적으로 caching처리를 하여서 일부의 row에만 signal이 발생하게되어 전혀 버벅이지않게된다.

솔직히 처음에는 for문을 model 갯수만큼 돌려 checkbox를 일일이 true,false를해서 많이 버벅였었다.ㅠ

for(var i =0 ; i < custom_tableview.model.count; ++i) {
  custom_tableview.model.setProperty(no, "checked", true)
}

이렇게되면 모든 tableview modal의 데이터가 바뀌어버리니 데이터가 많으면 많을수록 UI에 반영하는 시간등 더오래걸리게 되버린다. 그래서 for을 이용한 데이터 변경은 사용하지않는걸 추천한다.

main.qml

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Layouts 1.12

Window {
	visible: true
	width: 640
	height: 480
	title: qsTr("Hello World")

	Component.onCompleted: {
        for(var i = 0 ; i < 800000; ++i) {
			listModel.append({"checked": false, "no": i, "text": i})
		}
	}

	ListModel {
		id: listModel
	}

	CustomTableView {
		id: customTableView
		anchors.fill: parent
		model: listModel
	}
}

listmodel에 800000개의 데이터를 만들고
만들어진 데이터를 customtableview model을 넣어주어 데이터를 UI에 보여주게끔한다.

하지만 ui스레드에서 model에 append하고 tableview에 그리는데 시간이 다소 소요된다. 그 때 동안은 UI스레드가 멈춰있어 행걸린 것 처럼 보일 수 있다.
다음 편에는 listmodel에 데이터를 비동기로 넣어 UI스레드가 행걸리지 않게 만드는 예제를 올리겠습니다.

profile
안녕하세요 QT,QML 개발자입니다.

0개의 댓글