이 페이지에서

키보드 포커스 Qt Quick

키를 누르거나 놓으면 키 이벤트가 생성되어 포커스가 설정된 Qt Quick Item 로 전달됩니다. 재사용 가능한 컴포넌트의 구축을 용이하게 하고 유동적인 사용자 인터페이스 특유의 일부 사례를 해결하기 위해, Qt Quick 항목은 Qt의 기존 키보드 포커스 모델에 범위 기반 확장을 추가합니다.

키 처리 개요

사용자가 키를 누르거나 놓으면 다음과 같은 과정이 발생합니다:

  1. Qt는 키 동작을 수신하고 키 이벤트를 생성합니다.
  2. QQuickWindow 가 애플리케이션의 focus window 인 경우, 키 이벤트가 해당 로 전달됩니다.
  3. 키 이벤트는 씬(scene)을 통해 포커스가 활성화된 항목( Item )으로 전달됩니다. 포커스가 활성화된 항목이 없는 경우, 키 이벤트는 무시됩니다.
  4. 활성 포커스를 가진 QQuickItem 가 키 이벤트를 수락하면 전파가 중지됩니다. 그렇지 않은 경우, 이벤트가 수락되거나 루트 항목에 도달할 때까지 이벤트는 해당 항목의 상위 항목으로 전송됩니다.

    다음 예제에서 포커스가 활성화된 ‘ Rectangle ’ 유형의 항목이 있고 ‘ A ’ 키가 눌리면, 이벤트는 더 이상 전파되지 않습니다. ‘ B ’ 키를 누르면 이벤트는 루트 항목까지 전파되어 무시됩니다.

    Rectangle {
        width: 100; height: 100
        focus: true
        Keys.onPressed: (event)=> {
            if (event.key == Qt.Key_A) {
                console.log('Key A was pressed');
                event.accepted = true;
            }
        }
    }
  5. 루트 Item 에 도달한 경우, 키 이벤트는 ignored 로 처리되며 일반적인 Qt 키 처리가 계속됩니다.

Keys attached property 및 KeyNavigation attached property 항목도 참조하십시오.

활성 포커스 항목 조회

Item 가 활성 포커스를 가지고 있는지 여부는 Item::activeFocus 속성을 통해 조회할 수 있습니다. 예를 들어, 여기에는 활성 포커스 유무에 따라 텍스트가 결정되는 Text 유형이 있습니다.

    Text {
        text: activeFocus ? "I have active focus!" : "I do not have active focus"
    }

포커스 획득 및 포커스 범위

Item 는 focus 속성을 true 로 설정하여 포커스를 요청합니다.

매우 간단한 경우에는 focus 속성을 설정하는 것만으로도 충분한 경우가 있습니다. qml 도구를 사용하여 다음 예제를 실행해 보면, keyHandler 유형이 활성 포커스를 가지고 있으며, A, B 또는 C 키를 누르면 텍스트가 적절하게 변경되는 것을 확인할 수 있습니다.

Rectangle {
    color: "lightsteelblue"; width: 240; height: 25
    Text { id: myText }
    Item {
        id: keyHandler
        focus: true
        Keys.onPressed: (event)=> {
            if (event.key == Qt.Key_A)
                myText.text = 'Key A was pressed'
            else if (event.key == Qt.Key_B)
                myText.text = 'Key B was pressed'
            else if (event.key == Qt.Key_C)
                myText.text = 'Key C was pressed'
        }
    }
}

C 키가 눌렸음을 나타내는 파란색 둥근 위젯

그러나 위 예제를 재사용 가능한 컴포넌트나 임포트된 컴포넌트로 사용할 경우, focus 속성을 단순히 사용하는 것만으로는 더 이상 충분하지 않습니다.

이를 시연하기 위해, 앞서 정의한 컴포넌트의 인스턴스 두 개를 생성하고 첫 번째 인스턴스에 포커스를 설정합니다. 목표는 A, B 또는 C 키가 눌렸을 때, 두 컴포넌트 중 첫 번째 컴포넌트가 이벤트를 수신하고 그에 따라 반응하도록 하는 것입니다.

MyWidget을 임포트하고 두 개의 인스턴스를 생성하는 코드는 다음과 같습니다:

//Window code that imports MyWidget
Rectangle {
    id: window
    color: "white"; width: 240; height: 150

    Column {
        anchors.centerIn: parent; spacing: 15

        MyWidget {
            focus: true             //set this MyWidget to receive the focus
            color: "lightblue"
        }
        MyWidget {
            color: "palegreen"
        }
    }
}

MyWidget 코드:

Rectangle {
    id: widget
    color: "lightsteelblue"; width: 175; height: 25; radius: 10; antialiasing: true
    Text { id: label; anchors.centerIn: parent}
    focus: true
    Keys.onPressed: (event)=> {
        if (event.key == Qt.Key_A)
            label.text = 'Key A was pressed'
        else if (event.key == Qt.Key_B)
            label.text = 'Key B was pressed'
        else if (event.key == Qt.Key_C)
            label.text = 'Key C was pressed'
    }
}

첫 번째 MyWidget 객체가 포커스를 갖도록 하려면, 해당 객체의 focus 속성을 true 로 설정해야 합니다. 하지만 코드를 실행해 보면 두 번째 위젯이 포커스를 받는 경우가 발생할 수 있습니다.

두 개의 위젯이 겹쳐져 있으며, 두 번째 녹색 위젯은 키 A가 눌렸음을 나타냅니다.

MyWidget 와 window 코드를 살펴보면 문제가 명확해집니다. focus 속성을 true 로 설정하는 유형이 세 가지나 있기 때문입니다. 두 개의 MyWidget는 focus 를 true 로 설정하고, window 컴포넌트도 포커스를 설정합니다. 결국, 키보드 포커스를 가질 수 있는 유형은 하나뿐이며, 시스템이 어느 유형이 포커스를 받을지 결정해야 합니다. QML은 어떤 요소의 속성이 먼저 초기화될지 보장하지 않기 때문에, 마지막으로 초기화된 MyWidget 가 초기 포커스를 받을 수도 있습니다.

이 문제는 가시성(visibility)에 기인합니다. MyWidget 컴포넌트는 포커스를 갖고 싶어 하지만, 임포트되거나 재사용될 때 포커스를 제어할 수 없습니다. 마찬가지로, window 컴포넌트도 임포트된 컴포넌트들이 포커스를 요청하고 있는지 알 수 있는 능력이 없습니다.

이 문제를 해결하기 위해 QML은 ‘포커스 범위(focus scope)’라는 개념을 도입했습니다. 기존 Qt 사용자에게 포커스 범위는 일종의 자동 포커스 프록시와 같습니다. 포커스 범위는 ` FocusScope ` 타입을 선언하여 생성됩니다.

다음 예제에서는 FocusScope 타입을 컴포넌트에 추가하고, 그 시각적 결과를 보여줍니다.

FocusScope {

    //FocusScope needs to bind to visual properties of the Rectangle
    property alias color: rectangle.color
    x: rectangle.x; y: rectangle.y
    width: rectangle.width; height: rectangle.height

    Rectangle {
        id: rectangle
        anchors.centerIn: parent
        color: "lightsteelblue"; width: 175; height: 25; radius: 10; antialiasing: true
        Text { id: label; anchors.centerIn: parent }
        focus: true
        Keys.onPressed: (event)=> {
            if (event.key == Qt.Key_A)
                label.text = 'Key A was pressed'
            else if (event.key == Qt.Key_B)
                label.text = 'Key B was pressed'
            else if (event.key == Qt.Key_C)
                label.text = 'Key C was pressed'
        }
    }
}

두 개의 위젯이 겹쳐져 있으며, 맨 위의 파란색 위젯은 키 B가 눌렸음을 나타냅니다.

개념적으로 포커스 스코프는 매우 간단합니다.

  • 각 포커스 범위 내에서 하나의 객체만 ` Item::focus `가 ` true`로 설정될 수 있습니다. 두 개 이상의 ` Item `에 ` focus ` 속성이 설정된 경우, ` focus `를 마지막으로 설정한 유형이 포커스를 갖게 되며 나머지는 포커스가 해제됩니다. 이는 포커스 범위가 없는 경우와 유사합니다.
  • 포커스 스코프가 활성 포커스를 받으면, ` focus `가 설정된 포함된 유형(있는 경우)도 활성 포커스를 받게 됩니다. 이 유형이 ` FocusScope`인 경우, 프록시 동작이 계속됩니다. 포커스 스코프와 하위 포커스 항목 모두 ` activeFocus ` 속성이 설정됩니다.

FocusScope 유형은 시각적 유형이 아니므로, FocusScope 의 부모 항목에 해당 자식 항목의 속성을 노출해야 한다는 점에 유의하십시오. 레이아웃 및 위치 지정 유형은 이러한 시각적 속성과 스타일 속성을 사용하여 레이아웃을 생성합니다. 이 예제에서 Column 유형은 FocusScope 에 자체적인 시각적 속성이 없기 때문에 두 위젯을 제대로 표시할 수 없습니다. MyWidget 컴포넌트는 rectangle 속성에 직접 바인딩하여, Column 유형이 FocusScope 의 자식 요소를 포함하는 레이아웃을 생성할 수 있도록 합니다.

지금까지 예제에서는 두 번째 컴포넌트가 정적으로 선택된 상태였습니다. 이제 이 컴포넌트를 확장하여 클릭 가능하게 만들고, 원래 애플리케이션에 추가하는 것은 매우 간단합니다. 여전히 위젯 중 하나를 기본적으로 포커스 상태로 설정합니다. 이제 두 MyClickableWidget 중 하나를 클릭하면 해당 위젯이 포커스를 얻고, 다른 위젯은 포커스를 잃게 됩니다.

두 개의 MyClickableWidget 인스턴스를 임포트하고 생성하는 코드는 다음과 같습니다.

Rectangle {
    id: window

    color: "white"; width: 240; height: 150

    Column {
        anchors.centerIn: parent; spacing: 15

        MyClickableWidget {
            focus: true             //set this MyWidget to receive the focus
            color: "lightblue"
        }
        MyClickableWidget {
            color: "palegreen"
        }
    }

}

MyClickableWidget 코드:

FocusScope {

    id: scope

    //FocusScope needs to bind to visual properties of the children
    property alias color: rectangle.color
    x: rectangle.x; y: rectangle.y
    width: rectangle.width; height: rectangle.height

    Rectangle {
        id: rectangle
        anchors.centerIn: parent
        color: "lightsteelblue"; width: 175; height: 25; radius: 10; antialiasing: true
        Text { id: label; anchors.centerIn: parent }
        focus: true
        Keys.onPressed: (event)=> {
            if (event.key == Qt.Key_A)
                label.text = 'Key A was pressed'
            else if (event.key == Qt.Key_B)
                label.text = 'Key B was pressed'
            else if (event.key == Qt.Key_C)
                label.text = 'Key C was pressed'
        }
    }
    MouseArea { anchors.fill: parent; onClicked: { scope.focus = true } }
}

각각 마지막으로 누른 키를 표시하는, 클릭 가능한 위젯 두 개

QML Item 가 포커스를 명시적으로 해제할 때(포커스가 활성화된 상태에서 focus 속성을 false 로 설정함으로써), 시스템은 포커스를 받을 다른 유형을 자동으로 선택하지 않습니다. 즉, 현재 활성화된 포커스가 전혀 없을 수도 있습니다.

FocusScope 유형을 사용하여 여러 영역 간에 키보드 포커스를 이동하는 데모는 ‘Qt Quick 예제 - 키 상호작용’을 참조하십시오.

포커스 범위의 고급 사용법

포커스 스코프를 사용하면 포커스 할당을 쉽게 분할할 수 있습니다. 여러 QML 항목이 이러한 목적으로 이를 사용합니다.

ListView예를 들어, `ListItem` 자체는 포커스 스코프입니다. 일반적으로 ` ListView `에는 수동으로 추가된 시각적 자식 요소가 없기 때문에 이 점은 눈에 띄지 않습니다. 포커스 스코프로서 ` ListView `은 애플리케이션의 나머지 부분에 미치는 영향을 걱정할 필요 없이 현재 리스트 항목에 포커스를 줄 수 있습니다. 이를 통해 현재 항목의 델리게이트가 키 입력에 반응할 수 있습니다.

이 인위적인 예제는 그 작동 방식을 보여줍니다. Return 키를 누르면 현재 목록 항목의 이름이 출력됩니다.

Rectangle {
    color: "lightsteelblue"; width: 100; height: 50

    ListView {
        anchors.fill: parent
        focus: true

        model: ListModel {
            ListElement { name: "Bob" }
            ListElement { name: "John" }
            ListElement { name: "Michael" }
        }

        delegate: FocusScope {
                width: childrenRect.width; height: childrenRect.height
                x:childrenRect.x; y: childrenRect.y
                TextInput {
                    focus: true
                    text: name
                    Keys.onReturnPressed: console.log(name)
                }
        }
    }
}

편집 가능한 이름 세 개가 포함된 목록: Bob, John, Michael

예제는 간단해 보이지만, 내부적으로는 많은 작업이 이루어집니다. 현재 항목이 변경될 때마다 ` ListView `는 델리게이트의 ` Item::focus ` 속성을 설정합니다. ` ListView `는 포커스 범위이므로, 이 작업은 애플리케이션의 나머지 부분에 영향을 미치지 않습니다. 하지만 ` ListView ` 자체가 활성 포커스를 가지고 있다면, 이로 인해 델리게이트 자체도 활성 포커스를 받게 됩니다. 이 예제에서 델리게이트의 루트 유형(root type) 또한 포커스 범위이므로, 이는 Return 키를 실제로 처리하는 TextInput 유형에 활성 포커스를 부여합니다.

PathView 및 GridView 와 같은 모든 QML 뷰 클래스는 각 델리게이트에서 키 처리가 가능하도록 유사한 방식으로 동작합니다.

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.