En esta página

Escribir módulos QML

Debe declarar un módulo QML utilizando la API de módulos QML de CMake para:

  • Generar archivos qmldir y *.qmltypes.
  • Registrar tipos C++ anotados con QML_ELEMENT.
  • Combinar archivos QML y tipos basados en C++ en el mismo módulo.
  • Invocar qmlcachegen en todos los archivos QML.
  • Utilizar las versiones precompiladas de los archivos QML dentro del módulo.
  • Proporcionar el módulo tanto en el sistema de archivos físico como en el de recursos.
  • Crear una biblioteca de respaldo y un plugin opcional. Enlazar la biblioteca de respaldo en la aplicación para evitar cargar el plugin en tiempo de ejecución.

Todas las acciones anteriores también se pueden configurar por separado. Para obtener más información, consulte CMake QML Module API.

Múltiples módulos QML en un binario

Puede añadir múltiples módulos QML en el mismo binario. Defina un objetivo CMake para cada módulo y, a continuación, vincule los objetivos al ejecutable. Si los objetivos adicionales son todas las bibliotecas estáticas, el resultado será un binario, que contiene múltiples módulos QML. En resumen puedes crear una aplicación como esta:

myProject
    | - CMakeLists.txt
    | - main.cpp
    | - main.qml
    | - onething.h
    | - onething.cpp
    | - ExtraModule
        | - CMakeLists.txt
        | - Extra.qml
        | - extrathing.h
        | - extrathing.cpp

Para empezar, supongamos que main.qml contiene una instanciación de Extra.qml:

import ExtraModule
Extra { ... }

El módulo extra tiene que ser una librería estática para que puedas enlazarlo con el programa principal. Por tanto, indícalo en ExtraModule/CMakeLists.txt:

# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

qt_add_library(extra_module STATIC)
qt_add_qml_module(extra_module
    URI "ExtraModule"
    VERSION 1.0
    QML_FILES
        Extra.qml
    SOURCES
        extrathing.cpp extrathing.h
    RESOURCE_PREFIX /
)

Esto genera dos objetivos: extra_module para la librería de respaldo, y extra_moduleplugin para el plugin. Siendo también una librería estática, el plugin no puede ser cargado en tiempo de ejecución.

En myProject/CMakeLists.txt necesitas especificar el módulo QML del que forman parte main.qml y cualquier tipo declarado en onething.h:

qt_add_executable(main_program main.cpp)

qt_add_qml_module(main_program
    VERSION 1.0
    URI myProject
    QML_FILES
        main.qml
    SOURCES
        onething.cpp onething.h

)

A partir de ahí, añades el subdirectorio para el módulo extra:

add_subdirectory(ExtraModule)

Para asegurarse de que la vinculación del módulo adicional funciona correctamente, es necesario:

  • Definir un símbolo en el módulo adicional.
  • Crear una referencia al símbolo desde el programa principal.

Los plugins QML contienen un símbolo que puedes utilizar para este propósito. Puede utilizar la macro Q_IMPORT_QML_PLUGIN para crear una referencia a este símbolo. Añade el siguiente código al main.cpp:

#include <QtQml/QQmlExtensionPlugin>
Q_IMPORT_QML_PLUGIN(ExtraModulePlugin)

ExtraModulePlugin es el nombre de la clase plugin generada. Está compuesto por el URI del módulo con Plugin añadido. Luego, en el CMakeLists.txt del programa principal, enlaza el plugin, no la librería de respaldo, en el programa principal:

target_link_libraries(main_program PRIVATE extra_moduleplugin)

Versiones

QML tiene un complejo sistema para asignar versiones a componentes y módulos. En la mayoría de los casos deberías ignorarlo por completo:

  1. No añadir nunca una versión a las sentencias import
  2. Nunca especificar ninguna versión en qt_add_qml_module
  3. Nunca usando QML_ADDED_IN_VERSION o QT_QML_SOURCE_VERSIONS
  4. No utilizar nunca Q_REVISION o el atributo REVISION() en Q_PROPERTY
  5. Evitar accesos no cualificados
  6. Uso generoso de espacios de nombres de importación

Lo ideal es que el versionado se gestione fuera del propio lenguaje. Puede, por ejemplo, mantener rutas de importación separadas para diferentes conjuntos de módulos QML. O puede usar un mecanismo de versionado proporcionado por su sistema operativo para instalar o desinstalar paquetes con módulos QML.

En algunos casos, los propios módulos QML de Qt pueden mostrar un comportamiento diferente, dependiendo de la versión que se importe. En particular, si se añade una propiedad a un componente QML, y su código contiene un acceso no cualificado a otra propiedad del mismo nombre, su código se romperá. En el siguiente ejemplo, el código se comportará de forma diferente dependiendo de la versión de Qt, porque la propiedad topLeftRadius fue añadida en Qt 6.7:

import QtQuick

Item {
    // property you want to use
    property real topLeftRadius: 24

    Rectangle {

        // correct for Qt version < 6.7 but uses Rectangle's topLeftRadius in 6.7
        objectName: "top left radius:" + topLeftRadius
    }
}

La solución aquí es evitar el acceso no cualificado. qmllint se puede utilizar para encontrar este tipo de problemas. El siguiente ejemplo accede a la propiedad a la que realmente se refiere de una forma segura y cualificada:

import QtQuick

Item {
    id: root

    // property you want to use
    property real topLeftRadius: 24

    Rectangle {

        // never mixes up topLeftRadius with unrelated Rectangle's topLeftRadius
        objectName: "top left radius:" + root.topLeftRadius
    }
}

También puede evitar la incompatibilidad importando una versión específica de QtQuick:

// make sure Rectangle has no topLeftRadius property
import QtQuick 6.6

Item {
    property real topLeftRadius: 24
    Rectangle {
        objectName: "top left radius:" + topLeftRadius
    }
}

Otro problema que resuelve el versionado es el hecho de que los componentes QML importados por distintos módulos pueden hacerse sombra unos a otros. En el siguiente ejemplo, si MyModule introdujera un componente llamado Rectangle en una versión más reciente, el Rectangle creado por este documento ya no sería un QQuickRectangle, sino el nuevo Rectangle introducido por MyModule.

import QtQuick
import MyModule

Rectangle {
    // MyModule's Rectangle, not QtQuick's
}

Una buena forma de evitar el shadowing sería importar QtQuick y/o MyModule en espacios de nombres de tipo como se indica a continuación:

import QtQuick as QQ
import MyModule as MM

QQ.Rectangle {
   // QtQuick's Rectangle
}

Alternativamente, si importa MyModule con una versión fija, y el nuevo componente recibe una etiqueta de versión correcta a través de QML_ADDED_IN_VERSION o QT_QML_SOURCE_VERSIONS, también se evita el sombreado:

import QtQuick 6.6

// Types introduced after 1.0 are not available, like Rectangle for example
import MyModule 1.0

Rectangle {
    // QtQuick's Rectangle
}

Para que esto funcione, es necesario utilizar versiones en MyModule. Hay que tener en cuenta algunas cosas.

Si añades versiones, añádelas en todas partes

Necesitas añadir un atributo VERSION a qt_add_qml_module. La versión debe ser la más reciente proporcionada por tu módulo. Las versiones menores más antiguas de la misma versión mayor se registrarán automáticamente. Para versiones mayores más antiguas, véase más abajo.

Debes añadir QML_ADDED_IN_VERSION o QT_QML_SOURCE_VERSIONS a cada tipo que no haya sido introducido en la versión x.0 de tu módulo, donde x es la versión mayor actual.

Si olvida añadir una etiqueta de versión, el componente estará disponible en todas las versiones, haciendo ineficaz el versionado.

Sin embargo, no hay forma de añadir versiones a las propiedades, métodos y señales definidos en QML. La única forma de versionar documentos QML es añadir un nuevo documento con QT_QML_SOURCE_VERSIONS separado para cada cambio.

Las versiones no son transitivas

Si un componente de su módulo A importa otro módulo B e instancia un tipo de ese módulo como elemento raíz, entonces la versión de importación de B es relevante para las propiedades disponibles del componente resultante, independientemente de la versión de A que importe un usuario.

Considere un archivo TypeFromA.qml con la versión 2.6 en el módulo A:

import B 2.7

// Exposes TypeFromB 2.7, no matter what version of A is imported
TypeFromB { }

Consideremos ahora un usuario de TypeFromA:

import A 2.6

// This is TypeFromB 2.7.
TypeFromA { }

El usuario espera ver la versión 2.6 pero en realidad obtiene la versión 2.7 de la clase base TypeFromB.

Por lo tanto, para estar seguro, no sólo tienes que duplicar tus archivos QML y darles nuevas versiones cuando añadas propiedades tú mismo, sino también cuando bump versiones de módulos que importes.

El acceso cualificado no respeta el versionado

El versionado sólo afecta al acceso no cualificado a los miembros de un tipo o al propio tipo. En el ejemplo con topLeftRadius, si escribes this.topLeftRadius, la propiedad se resolverá si estás usando Qt 6.7, incluso si escribes import QtQuick 6.6.

Versiones y revisiones

Con QML_ADDED_IN_VERSION, y las variantes de dos argumentos de Q_REVISION y Q_PROPERTY's REVISION(), sólo puede declarar versiones que estén estrechamente acopladas a la revisión metaobject's como se expone en QMetaMethod::revision y QMetaProperty::revision. Esto significa que todos los tipos de tu jerarquía de tipos tienen que seguir el mismo esquema de versiones. Esto incluye cualquier tipo proporcionado por Qt del que heredes.

Con qmlRegisterType y las funciones relacionadas puedes registrar cualquier mapeo entre revisiones de metaobjetos y versiones de tipos. Entonces tendrá que usar las formas de un solo argumento de Q_REVISION y el atributo REVISION de Q_PROPERTY. Sin embargo, esto puede llegar a ser bastante complejo y confuso y no se recomienda.

Exportación de varias versiones principales de un mismo módulo

qt_add_qml_module considera por defecto la versión principal indicada en su argumento VERSION, incluso si los tipos individuales declaran otras versiones en su versión específica añadida a través de QT_QML_SOURCE_VERSIONS o Q_REVISION. Si un módulo está disponible en más de una versión, también deberá decidir en qué versiones están disponibles los archivos QML individuales. Para declarar más versiones principales, puede utilizar la opción PAST_MAJOR_VERSIONS de qt_add_qml_module, así como la propiedad QT_QML_SOURCE_VERSIONS de los archivos QML individuales.

set_source_files_properties(Thing.qml
    PROPERTIES
        QT_QML_SOURCE_VERSIONS "1.4;2.0;3.0"
)

set_source_files_properties(OtherThing.qml
    PROPERTIES
        QT_QML_SOURCE_VERSIONS "2.2;3.0"
)

qt_add_qml_module(my_module
    URI MyModule
    VERSION 3.2
    PAST_MAJOR_VERSIONS
        1 2
    QML_FILES
        Thing.qml
        OtherThing.qml
        OneMoreThing.qml
    SOURCES
        everything.cpp everything.h
)

MyModule está disponible en las versiones principales 1, 2 y 3. La versión máxima disponible es la 3.2. Puede importar cualquier versión 1.x o 2.x con una x positiva. Para Thing.qml y OtherThing.qml hemos añadido información explícita sobre la versión. Thing.qml está disponible a partir de la versión 1.4, y OtherThing.qml está disponible a partir de la versión 2.2. También tienes que especificar las versiones posteriores en cada set_source_files_properties() porque puedes eliminar archivos QML de un módulo al aumentar la versión principal. No existe información explícita sobre la versión de OneMoreThing.qml. Esto significa que OneMoreThing.qml está disponible en todas las versiones mayores, desde la versión menor 0.

Con esta configuración, el código de registro generado registrará el módulo versions utilizando qmlRegisterModule() para cada una de las versiones principales. De este modo, se podrán importar todas las versiones.

Diseños de directorio personalizados

La forma más sencilla de estructurar módulos QML es mantenerlos en directorios nombrados por sus URI. Por ejemplo, un módulo Mi.Extra.Module viviría en un directorio Mi/Extra/Módulo relativo a la aplicación que lo utiliza. De esta manera, se pueden encontrar fácilmente en tiempo de ejecución y por cualquier herramienta.

En proyectos más complejos, esta convención puede ser demasiado restrictiva. Por ejemplo, es posible que desee agrupar todos los módulos QML en un solo lugar para evitar contaminar el directorio raíz del proyecto. O que quieras reutilizar un mismo módulo en varias aplicaciones. Para esos casos, se puede utilizar QT_QML_OUTPUT_DIRECTORY en combinación con RESOURCE_PREFIX e IMPORT_PATH.

Para recoger los módulos QML en un directorio de salida específico, por ejemplo, un subdirectorio "qml" en el directorio de construcción QT_QML_OUTPUT_DIRECTORY, establezca lo siguiente en el nivel superior CMakeLists.txt:

set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml)

Los directorios de salida de los módulos QML se mueven a la nueva ubicación. Asimismo, las invocaciones qmllint y qmlcachegen se adaptan automáticamente para utilizar el nuevo directorio de salida como ruta de importación. Como el nuevo directorio de salida no forma parte de la ruta de importación QML por defecto, hay que añadirlo explícitamente en tiempo de ejecución, para que se puedan encontrar los módulos QML.

Ahora que el sistema de archivos físico está solucionado, es posible que desee mover los módulos QML a un lugar diferente en el sistema de archivos de recursos. Para eso está la opción RESOURCE_PREFIX. Debe especificarla por separado en cada qt_add_qml_module. El módulo QML se colocará entonces bajo el prefijo especificado, con una ruta de destino generada a partir de la URI añadida. Por ejemplo, considere el siguiente módulo:

qt_add_qml_module(
    URI My.Great.Module
    VERSION 1.0
    RESOURCE_PREFIX /example.com/qml
    QML_FILES
        A.qml
        B.qml
)

Esto añadirá un directorio example.com/qml/My/Great/Module al sistema de archivos de recursos y colocará en él el módulo QML definido anteriormente. No es estrictamente necesario añadir el prefijo resource a la ruta de importación de QML, ya que el módulo puede encontrarse en el sistema de archivos físico. Sin embargo, generalmente es una buena idea añadir el prefijo resource a la ruta de importación QML porque la carga desde el sistema de archivos resource es más rápida que la carga desde el sistema de archivos físico para la mayoría de los módulos.

Si los módulos QML se van a utilizar en un proyecto más grande con varias rutas de importación, tendrá que realizar un paso adicional: Aunque añada las rutas de importación en tiempo de ejecución, las herramientas como qmllint no tienen acceso a ellas y podrían no encontrar las dependencias correctas. Utilice IMPORT_PATH para indicar a las herramientas las rutas adicionales que deben tener en cuenta. Por ejemplo

qt_add_qml_module(
    URI My.Dependent.Module
    VERSION 1.0
    QML_FILES
        C.qml
    IMPORT_PATH "/some/where/else"
)

Eliminación del acceso al sistema de archivos en tiempo de ejecución

Si todos los módulos QML se cargan siempre desde el sistema de archivos de recursos, puede desplegar la aplicación como un único binario.

Si la política QTP0001 se establece en NEW, el argumento RESOURCE_PREFIX para qt_add_qml_module() se establece por defecto en /qt/qml/, por lo tanto sus módulos se colocan en :/qt/qml/ en el sistema de archivos de recursos. Esto forma parte de la ruta de importación de QML por defecto, pero no es utilizada por Qt. Para que los módulos sean utilizados dentro de su aplicación, este es el lugar correcto.

Si en su lugar ha especificado un RESOURCE_PREFIX personalizado, tiene que añadir el prefijo del recurso personalizado a la Ruta de Importación QML. También puede añadir varios prefijos de recursos:

QQmlEngine qmlEngine;
qmlEngine.addImportPath(QStringLiteral(":/my/resource/prefix"));
qmlEngine.addImportPath(QStringLiteral(":/other/resource/prefix"));
// Use qmlEngine to load the main.qml file.

Esto puede ser necesario cuando se utilizan bibliotecas de terceros para evitar conflictos de nombres de módulos. Se desaconseja utilizar un prefijo de recurso personalizado en todos los demás casos.

La ruta :/qt-project.org/imports/ también forma parte de la ruta de importación QML predeterminada. Para los módulos que son muy reutilizados en diferentes proyectos o versiones de Qt, :/qt-project.org/imports/ es aceptable como prefijo de recursos. Sin embargo, los propios módulos QML de Qt se colocan ahí. Debes tener cuidado de no sobreescribirlos.

No añadas rutas de importación innecesarias. El motor QML podría encontrar sus módulos en el lugar equivocado. Esto puede desencadenar problemas que sólo pueden reproducirse en entornos específicos.

Integración de plugins QML personalizados

Si integra un image provider en el módulo QML, necesitará implementar el método QQmlEngineExtensionPlugin::initializeEngine(). Esto, a su vez, hace necesario escribir su propio plugin. Para soportar este caso de uso, se puede utilizar NO_GENERATE_PLUGIN_SOURCE.

Consideremos un módulo que proporciona su propia fuente de plugin:

qt_add_qml_module(imageproviderplugin
    VERSION 1.0
    URI "ImageProvider"
    PLUGIN_TARGET imageproviderplugin
    NO_PLUGIN_OPTIONAL
    NO_GENERATE_PLUGIN_SOURCE
    CLASS_NAME ImageProviderExtensionPlugin
    QML_FILES
        AAA.qml
        BBB.qml
    SOURCES
        moretypes.cpp moretypes.h
        myimageprovider.cpp myimageprovider.h
        plugin.cpp
)

Puedes declarar un proveedor de imágenes en myimageprovider.h, así:

class MyImageProvider : public QQuickImageProvider
{
    [...]
};

En plugin.cpp puede entonces definir el QQmlEngineExtensionPlugin:

#include <myimageprovider.h>
#include <QtQml/qqmlextensionplugin.h>

class ImageProviderExtensionPlugin : public QQmlEngineExtensionPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid)
public:
    void initializeEngine(QQmlEngine *engine, const char *uri) final
    {
        Q_UNUSED(uri);
        engine->addImageProvider("myimg", new MyImageProvider);
    }
};

Esto hará que el proveedor de imágenes esté disponible. Tanto el plugin como la librería de respaldo están en el mismo target de CMake imageproviderplugin. Esto se hace para que el enlazador no deje caer partes del módulo en varios escenarios.

Como el plugin crea un proveedor de imágenes, ya no tiene una función trivial initializeEngine. Por lo tanto, el plugin ya no es opcional.

Ver también Cambios en Qt Qml, Modernización de módulos QML, y Portar módulos QML a CMake.

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