QML은 런타임에 객체 간의 상호 작용을 동적으로 제어할 수 있는 강력한 기능을 제공합니다. 특히 Connections 요소와 Binding 요소를 사용하면 객체의 신호나 속성을 상황에 맞게 변경하여 더욱 유연하고 반응성 높은 UI를 구현할 수 있습니다.
1. 동적 연결 (Connecting Indirectly)
Connections 요소를 사용하면 특정 객체의 신호를 다른 객체의 슬롯(함수)에 연결할 수 있습니다. 런타임에 연결할 대상 객체(target)를 변경해야 하는 경우, Connections의 target 속성을 동적으로 수정하여 구현할 수 있습니다. 이는 여러 객체의 이벤트를 하나의 Connections 요소로 처리해야 할 때 유용합니다.
예제 코드:
Rectangle {
id: container
width: 600
height: 400
color: "white"
// 왼쪽 영역
Column {
anchors.top: parent.top
anchors.left: parent.left
spacing: 20
Rectangle {
width: 290
height: 50
color: "lightGray"
MouseArea {
anchors.fill: parent
onClicked: container.state = "leftActive"
}
Text {
anchors.centerIn: parent
font.pixelSize: 30
text: container.state === "leftActive" ? "Active" : "Inactive"
}
}
Rectangle {
id: leftRect
width: 290
height: 200
color: "green"
MouseArea {
anchors.fill: parent
onClicked: leftClickedAnim.start()
}
Text {
anchors.centerIn: parent
font.pixelSize: 30
color: "white"
text: "Left Click"
}
}
}
// 오른쪽 영역
Column {
anchors.top: parent.top
anchors.right: parent.right
spacing: 20
Rectangle {
width: 290
height: 50
color: "lightGray"
MouseArea {
anchors.fill: parent
onClicked: container.state = "rightActive"
}
Text {
anchors.centerIn: parent
font.pixelSize: 30
text: container.state === "rightActive" ? "Active" : "Inactive"
}
}
Rectangle {
id: rightRect
width: 290
height: 200
color: "blue"
MouseArea {
anchors.fill: parent
onClicked: rightClickedAnim.start()
}
Text {
anchors.centerIn: parent
font.pixelSize: 30
color: "white"
text: "Right Click"
}
}
}
// 상태 표시 텍스트
Text {
id: statusText
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: 50
font.pixelSize: 30
color: "red"
text: "Area Clicked!"
opacity: 0
}
// 왼쪽 클릭 애니메이션
SequentialAnimation {
id: leftClickedAnim
PropertyAction { target: leftRect; property: "color"; value: "white" }
ColorAnimation { target: leftRect; property: "color"; to: "green"; duration: 3000 }
}
// 오른쪽 클릭 애니메이션
SequentialAnimation {
id: rightClickedAnim
PropertyAction { target: rightRect; property: "color"; value: "white" }
ColorAnimation { target: rightRect; property: "color"; to: "blue"; duration: 3000 }
}
// 상태 텍스트 애니메이션
SequentialAnimation {
id: statusTextAnim
PropertyAction { target: statusText; property: "opacity"; value: 1 }
PropertyAnimation { target: statusText; property: "opacity"; to: 0; duration: 3000 }
}
Connections {
id: handlerConnections
// 초기 target은 오른쪽 마우스 영역으로 설정
target: rightMouseArea
onClicked: statusTextAnim.start()
}
states: [
State {
name: "leftActive"
StateChangeScript {
script: handlerConnections.target = leftMouseArea // 왼쪽 마우스 영역으로 target 변경
}
},
State {
name: "rightActive"
StateChangeScript {
script: handlerConnections.target = rightMouseArea // 오른쪽 마우스 영역으로 target 변경
}
}
]
// 초기 상태 설정
Component.onCompleted: {
container.state = "leftActive"
}
}
위 예제에서 Connections 요소는 처음에는 rightMouseArea를 target으로 설정합니다. 그러나 Component.onCompleted와 states를 통해 container의 상태가 변경될 때마다 handlerConnections.target이 동적으로 leftMouseArea 또는 rightMouseArea로 변경됩니다. 이를 통해 사용자가 어느 영역을 클릭하느냐에 따라 연결되는 신호의 대상이 달라지게 됩니다.
2. 동적 바인딩 (Binding Indirectly)
Binding 요소를 사용하면 한 객체의 속성 값을 다른 객체의 속성 값에 연결(바인딩)할 수 있습니다. 런타임에 바인딩할 대상 객체(target)나 속성(property)을 동적으로 결정해야 할 경우 Binding을 활용할 수 있습니다. 이는 UI의 상태나 로딩된 컴포넌트에 따라 다른 객체의 속성을 참조해야 할 때 유용합니다.
예제 코드:
Rectangle {
id: root
width: 600
height: 400
property int currentSpeed: 0 // 현재 속도 값
// 속도 변화 애니메이션
SequentialAnimation {
running: true
loops: Animation.Infinite
NumberAnimation { target: root; property: "currentSpeed"; to: 145; easing.type: Easing.InOutQuad; duration: 4000 }
NumberAnimation { target: root; property: "currentSpeed"; to: 10; easing.type: Easing.InOutQuad; duration: 2000 }
}
Loader {
id: contentLoader
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: controlsRect.top
onLoaded: {
// Loader가 컴포넌트를 로드한 후, 바인딩 대상(target)을 설정
speedBinder.target = contentLoader.item
}
}
Binding {
id: speedBinder
// target은 onLoaded 시점에 동적으로 설정됨
property: "speed" // 바인딩할 속성 이름
value: root.currentSpeed // root의 currentSpeed 값을 사용
}
// 하단 컨트롤 영역
Rectangle {
id: controlsRect
anchors.left: parent.left
anchors.bottom: parent.bottom
width: parent.width / 2
height: 100
color: "gray"
Text { anchors.centerIn: parent; text: "Analog" }
MouseArea {
anchors.fill: parent
onClicked: root.state = "analogView"
}
}
Rectangle {
anchors.right: parent.right
anchors.bottom: parent.bottom
width: parent.width / 2
height: 100
color: "gray"
Text { anchors.centerIn: parent; text: "Digital" }
MouseArea {
anchors.fill: parent
onClicked: root.state = "digitalView"
}
}
states: [
State {
name: "analogView"
PropertyChanges { target: controlsRect; color: "green" }
PropertyChanges { target: contentLoader; source: "AnalogDisplay.qml" } // AnalogDisplay.qml 로드
},
State {
name: "digitalView"
PropertyChanges { target: controlsRect; color: "green" } // 해당 컨트롤 영역 색상 변경
PropertyChanges { target: contentLoader; source: "DigitalDisplay.qml" } // DigitalDisplay.qml 로드
}
]
initial: "analogView" // 초기 상태 설정
}
이 예제에서는 Loader를 사용하여 AnalogDisplay.qml 또는 DigitalDisplay.qml을 동적으로 로드합니다. Loader의 onLoaded 신호 핸들러에서 speedBinder의 target을 contentLoader.item (즉, 로드된 QML 컴포넌트)으로 설정합니다. speedBinder는 root.currentSpeed 값을 로드된 컴포넌트의 speed 속성에 바인딩합니다. 이를 통해 로드되는 컴포넌트가 다르더라도 항상 root.currentSpeed 값에 따라 해당 컴포넌트의 speed 속성이 업데이트됩니다.
3. 바인딩 관리
바인딩은 속성 간의 연결을 유지하지만, 직접적인 할당(assignment)은 바인딩을 파괴합니다. 바인딩을 복원하려면 Qt.binding() 함수를 사용하거나 Binding 요소의 when 조건을 활용할 수 있습니다.
바인딩 파괴 및 복원 (직접 할당 vs Qt.binding):
// 직접 할당은 바인딩을 파괴합니다.
// item2.value = item1.value1 + 1
// Qt.binding()을 사용하면 바인딩을 복원하거나 새로 생성할 수 있습니다.
// item2.value = Qt.binding(function() { return item1.value1 + 1; })
바인딩 조건 제어 (Binding when):
property bool enableBinding: true
Binding {
target: item2
property: "value2"
value: item1.value1 + 1
when: enableBinding // enableBinding이 true일 때만 바인딩 활성화
}
// 버튼 클릭 시 바인딩 비활성화 및 값 직접 할당
Button {
onClicked: {
enableBinding = false // 바인딩 비활성화
item2.value = item1.value1 + 10 // 직접 값 할당 (바인딩 파괴)
}
}
// 버튼 클릭 시 바인딩 다시 활성화
Button {
onClicked: {
enableBinding = true // 바인딩 활성화 (다시 연결됨)
}
}
이러한 기법들을 활용하면 QML 애플리케이션에서 동적인 데이터 흐름과 UI 업데이트를 효과적으로 관리할 수 있습니다.