qml 멀티스레드WorkerScript를 이용하여 tableview에 데이터 넣기

김준형·2024년 1월 22일

이전 tableview에 checkbox를 넣어 사용할 수 있게 만들었습니다.
그 과정에서 데이터를 넣는데 데이터가 많아질수록 UI가 멈추었다가 UI에 데이터가 그려지는 현상이 있었습니다.

아래 예제코드를 한번 확인합니다.

main.qml

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls 1.4

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

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

	ListModel {
        id: listmodel
	}

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

    BusyIndicator {
        id: busyIndicator
        anchors.centerIn: parent
        running: false
    }
}

예제코드를 빌드하면 아래처럼 됩니다.
데이터 model을 동기적으로 추가하기

위 gif처럼 listmodel에 데이터를 800000개를 넣고 그 model을 tableview에 넣어 GUI에 그리는 과정인데 데이터가 많으니 UI가 움직이지 않고 멈춘것처럼 동작합니다. BusyIndicator도 동작하지 않고요
이유는 GUI스레드내에서 대량의 데이터를 추가하고 tableview 에 데이터를 그려내는데 시간이 오래걸려서 그런 것 입니다.

그러면 UI스레드가 아닌 qml에서 사용하는 멀티스레드인 WorkerScript를 이용하여 tableview에 데이터를 넣고 그리는 방식을 보여드리겠습니다.

아래 예제코드를 한번 확인합니다.

main.qml

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls 1.4

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

	Component.onCompleted: {
        busyIndicator.running = true
        workerScript.sendMessage({'listmodel': listmodel})
	}

	ListModel {
        id: listmodel
	}

    WorkerScript {
        id: workerScript
        source: "listModelWorkerScript.js"
        onMessage: {
            if(messageObject.finish) {
                busyIndicator.running = false
            }
        }
    }

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

    BusyIndicator {
        id: busyIndicator
        anchors.centerIn: parent
        running: false
    }
}

workerScript를 이용하면 새 스레드에서 작업을 실행합니다. 이는 기본 GUI 스레드가 차단되지 않도록 백그라운드에서 작업을 실행하는 데 유용합니다.

Component.onCompleted에 busyIndicator를 실행하고 workerScript에 메시지를 전달합니다 메시지안에는 listmodel값을 전달합니다

아래는 workerscript js 코드입니다.

listModelWorkerScript.js

WorkerScript.onMessage = function(message) {
    var listmodel = message.listmodel
    for(var i = 0 ; i < 800000; ++i) {
        listmodel.append({"checked": false, "no": i, "text": i})
    }

    listmodel.sync()
    WorkerScript.sendMessage({'finish': true})
}

메시지를 받은 listmodel에 800000 데이터를 넣습니다 그리고 listmodel에 sync를 호출하여 GUI 스레드에 있는 listmodel 데이터를 갱신해줍니다. workerScript에서 sync를 호출하지 않고 script가 끝난다면 listmodel은 갱신되지 않습니다.

 WorkerScript {
        id: workerScript
        source: "listModelWorkerScript.js"
        onMessage: {
            console.log(messageObject.finish)
            if(messageObject.finish) {
                busyIndicator.running = false
            }
        }
    }

script가 끝났을쯤 sendMessage method를 호출하여 onMessage signal을 받아 script가 끝났을 때 busyIndicator를 중지시킵니다.

WorkerScript를 이용한 데이터 model을 비동기적으로 추가하기

위 gif처럼 listmodel에 데이터를 800000개를 넣어도 UI는 멈추지않습니다. 이처럼 workerScript를 이용하여 새 스레드에서 작업을 하고 GUI에 데이터를 전달하는 방식으로 진행하면 GUI도 멈추지않고 유연하게 동작할 수 있습니다.

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

0개의 댓글