En esta página

Enfoque del teclado en Qt Quick

Cuando se pulsa o suelta una tecla, se genera un evento de tecla que se envía a la dirección Qt Quick Item . Para facilitar la construcción de componentes reutilizables y abordar algunos de los casos exclusivos de las interfaces de usuario fluidas, los elementos de Qt Quick añaden una extensión basada en el ámbito al modelo tradicional de enfoque de teclado de Qt.

Visión general del manejo de teclas

Cuando el usuario pulsa o suelta una tecla, ocurre lo siguiente:

  1. Qt recibe la acción de la tecla y genera un evento de tecla.
  2. Si un QQuickWindow es el focus window de la aplicación, el evento de tecla es entregado a él.
  3. El evento clave es entregado por la escena al Item con foco activo. Si no hay ningún elemento con el foco activo, el evento clave se ignora.
  4. Si el QQuickItem con foco activo acepta el evento clave, la propagación se detiene. En caso contrario, el evento se envía al elemento padre hasta que se acepta el evento o se alcanza el elemento raíz.

    Si el tipo Rectangle del siguiente ejemplo tiene el foco activo y se pulsa la tecla A, el evento no se propagará más. Al pulsar la tecla B, el evento se propagará al elemento raíz y, por tanto, será ignorado.

    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. Si se alcanza la raíz Item, el evento de tecla es ignored y continúa el manejo normal de teclas Qt.

Véase también Keys attached property y KeyNavigation attached property.

Consulta del elemento de foco activo

Si un Item tiene o no el foco activo puede ser consultado a través de la propiedad Item::activeFocus. Por ejemplo, aquí tenemos un tipo Text cuyo texto viene determinado por si tiene o no el foco activo.

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

Adquisición de foco y ámbitos de foco

Un Item solicita el foco estableciendo la propiedad focus a true.

En casos muy sencillos, a veces basta con establecer la propiedad focus. Si ejecutamos el siguiente ejemplo con la herramienta qml, vemos que el tipo keyHandler tiene el foco activo y pulsando las teclas A, B, o C se modifica el texto adecuadamente.

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'
        }
    }
}

Sin embargo, si el ejemplo anterior se utilizara como componente reutilizable o importado, este simple uso de la propiedad focus ya no sería suficiente.

Para demostrarlo, creamos dos instancias de nuestro componente previamente definido y establecemos que la primera tenga el foco. La intención es que cuando se pulsen las teclas A, B, o C, el primero de los dos componentes reciba el evento y responda en consecuencia.

El código que importa y crea dos instancias de 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"
        }
    }
}

El código de 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'
    }
}

Queremos que el primer objeto MyWidget tenga el foco, por lo que establecemos su propiedad focus a true. Sin embargo, al ejecutar el código, puede ocurrir que el segundo widget reciba el foco.

Mirando el código de MyWidget y window, el problema es evidente - hay tres tipos que establecen la propiedad focus a true. Los dos MyWidgets establecen la focus a true y el componente window también establece el foco. En última instancia, sólo un tipo puede tener el foco del teclado, y el sistema tiene que decidir qué tipo recibe el foco. Dado que QML no garantiza qué elemento tendrá sus propiedades inicializadas primero, puede ocurrir que el último MyWidget obtenga el foco inicial.

Este problema se debe a la visibilidad. Al componente MyWidget le gustaría tener el foco, pero no puede controlarlo cuando se importa o se reutiliza. Del mismo modo, el componente window no tiene la capacidad de saber si sus componentes importados están solicitando el foco.

Para resolver este problema, QML introduce un concepto conocido como ámbito de foco. Para los usuarios de Qt, un ámbito de foco es como un proxy de foco automático. Un focus scope se crea declarando el tipo FocusScope.

En el siguiente ejemplo, se añade un tipo FocusScope al componente y se muestra el resultado visual.

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'
        }
    }
}

Conceptualmente, los ámbitos de enfoque son bastante sencillos.

  • Dentro de cada ámbito de foco un objeto puede tener Item::focus establecida en true. Si más de un Item tiene la propiedad focus establecida, el último tipo en establecer la focus tendrá el foco y los demás se desestablecen, de forma similar a cuando no hay ámbitos de foco.
  • Cuando un ámbito de foco recibe el foco activo, el tipo contenido con focus establecida (si existe) también recibe el foco activo. Si este tipo es también un FocusScope, el comportamiento de proxy continúa. Tanto el ámbito de enfoque como el elemento subenfocado tendrán la propiedad activeFocus establecida.

Tenga en cuenta que, dado que el tipo FocusScope no es un tipo visual, las propiedades de sus hijos deben exponerse al elemento padre del tipo FocusScope. Los tipos de diseño y posicionamiento utilizarán estas propiedades visuales y de estilo para crear el diseño. En nuestro ejemplo, el tipo Column no puede mostrar los dos widgets correctamente porque FocusScope carece de propiedades visuales propias. El componente MyWidget se vincula directamente a las propiedades de rectangle para permitir al tipo Column crear el diseño que contiene los hijos de FocusScope.

Hasta ahora, el ejemplo tiene el segundo componente seleccionado estáticamente. Ahora es trivial extender este componente para hacerlo clicable, y añadirlo a la aplicación original. Seguimos estableciendo uno de los widgets como enfocado por defecto. Ahora, al hacer clic en cualquiera de los dos MyClickableWidget se le da el foco y el otro widget pierde el foco.

El código que importa y crea dos instancias de 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"
        }
    }

}

El código de 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 } }
}

Cuando un QML Item cede explícitamente el foco (estableciendo su propiedad focus a false mientras tiene el foco activo), el sistema no selecciona automáticamente otro tipo para recibir el foco. Es decir, es posible que no haya ningún foco activo en ese momento.

Vea Qt Quick Ejemplos - Interacción de Teclas para una demostración de cómo mover el foco del teclado entre múltiples áreas usando tipos FocusScope.

Usos avanzados de los ámbitos de enfoque

Los Focus Scopes permiten particionar fácilmente el foco a la asignación. Varios elementos QML lo utilizan con este fin.

ListView, por ejemplo, es en sí mismo un ámbito de foco. Generalmente esto no se nota ya que ListView no suele tener hijos visuales añadidos manualmente. Al ser un ámbito de foco, ListView puede enfocar el elemento actual de la lista sin preocuparse de cómo afectará al resto de la aplicación. Esto permite al delegado del elemento actual reaccionar a la pulsación de teclas.

Este ejemplo artificial muestra cómo funciona. Al pulsar la tecla Return se imprimirá el nombre del elemento actual de la lista.

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)
                }
        }
    }
}

Aunque el ejemplo es sencillo, hay mucho que hacer entre bastidores. Cada vez que cambia el elemento actual, ListView establece la propiedad Item::focus del delegado. Como ListView es un focus scope, esto no afecta al resto de la aplicación. Sin embargo, si el propio ListView tiene foco activo, esto hace que el propio delegado reciba foco activo. En este ejemplo, el tipo raíz del delegado también es un ámbito de foco, que a su vez da foco activo al tipo TextInput que realmente realiza el trabajo de manejar la tecla Return.

Todas las clases de vista QML, como PathView y GridView, se comportan de forma similar para permitir la manipulación de teclas en sus respectivos delegados.

© 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.