En esta página

Uso de un archivo Designer UI en su aplicación C

Qt Widgets Los archivos Designer UI representan el árbol de widgets del formulario en formato XML. Los formularios pueden ser procesados:

  • En tiempo de compilación, lo que significa que los formularios son convertidos a código C++ que puede ser compilado.
  • En tiempo de ejecución, lo que significa que los formularios son procesados por la clase QUiLoader que construye dinámicamente el árbol de widgets mientras analiza el archivo XML.

Procesamiento de formularios en tiempo de compilación

Puedes crear componentes de interfaz de usuario con Qt Widgets Designer y utilizar las herramientas de compilación integradas de Qt, qmake y uic, para generar código para ellos cuando se compila la aplicación. El código generado contiene el objeto de interfaz de usuario del formulario. Es una estructura C++ que contiene:

  • Punteros a los widgets del formulario, diseños, elementos de diseño, grupos de botones y acciones.
  • Una función miembro llamada setupUi() para construir el árbol de widgets en el widget padre.
  • Una función miembro llamada retranslateUi() que maneja la traducción de las propiedades de cadena del formulario. Para más información, consulte Reaccionar a los cambios de idioma.

El código generado puede incluirse en tu aplicación y utilizarse directamente desde ella. Alternativamente, puedes utilizarlo para extender subclases de widgets estándar.

Un formulario procesado en tiempo de compilación puede utilizarse en su aplicación con uno de los siguientes enfoques:

  • Enfoque directo: se construye un widget para utilizarlo como marcador de posición del componente y se configura la interfaz de usuario dentro de él.
  • El Enfoque de Herencia Única: usted subclasifica la clase base del formulario (QWidget o QDialog, por ejemplo), e incluye una instancia privada del objeto de interfaz de usuario del formulario.
  • Enfoque deherencia múltiple: se subclasifican tanto la clase base del formulario como el objeto de interfaz de usuario del formulario. Esto permite que los widgets definidos en el formulario se utilicen directamente desde el ámbito de la subclase.

Para demostrarlo, crearemos una sencilla aplicación de formulario de calculadora. Está basada en el ejemplo original del Formulario Calculadora.

La aplicación consiste en un archivo fuente, main.cpp y un archivo UI.

El archivo calculatorform.ui diseñado con Qt Widgets Designer se muestra a continuación:

Captura de pantalla del editor de formularios mostrando el diseño de una calculadora

Cuando se utiliza CMake para construir el ejecutable, se requiere un archivo CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)
project(calculatorform LANGUAGES CXX)

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)

find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)

qt_add_executable(calculatorform
                  calculatorform.ui main.cpp)

set_target_properties(calculatorform PROPERTIES
    WIN32_EXECUTABLE TRUE
    MACOSX_BUNDLE TRUE
)

target_link_libraries(calculatorform  PUBLIC
    Qt::Core
    Qt::Gui
    Qt::Widgets
)

El formulario aparece entre los archivos fuente C++ en qt_add_executable(). La opción CMAKE_AUTOUIC indica a CMake que ejecute la herramienta uic para crear un archivo ui_calculatorform.h que pueda ser utilizado por los archivos fuente.

Cuando se utiliza qmake para construir el ejecutable, se requiere un archivo .pro:

TEMPLATE    = app
FORMS       = calculatorform.ui
SOURCES     = main.cpp

La característica especial de este archivo es la declaración FORMS que le dice a qmake qué archivos procesar con uic. En este caso, el archivo calculatorform.ui se utiliza para crear un archivo ui_calculatorform.h que puede ser utilizado por cualquier archivo listado en la declaración SOURCES.

Nota: Puede utilizar Qt Creator para crear el proyecto Calculator Form. Genera automáticamente el archivo main.cpp, UI, y un archivo de proyecto para la herramienta de compilación deseada, que usted puede modificar.

El enfoque directo

Para utilizar el enfoque directo, incluimos el archivo ui_calculatorform.h directamente en main.cpp:

#include "ui_calculatorform.h"

La función main crea el widget de la calculadora construyendo un QWidget estándar que utilizamos para alojar la interfaz de usuario descrita por el archivo calculatorform.ui.

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QWidget widget;
    Ui::CalculatorForm ui;
    ui.setupUi(&widget);

    widget.show();
    return app.exec();
}

En este caso, el Ui::CalculatorForm es un objeto de descripción de interfaz del archivo ui_calculatorform.h que configura todos los widgets del diálogo y las conexiones entre sus señales y ranuras.

El enfoque directo proporciona una forma rápida y fácil de utilizar componentes simples y autocontenidos en sus aplicaciones. Sin embargo, los componentes creados con Qt Widgets Designer a menudo requieren una estrecha integración con el resto del código de la aplicación. Por ejemplo, el código CalculatorForm proporcionado anteriormente compilará y se ejecutará, pero los objetos QSpinBox no interactuarán con el QLabel ya que necesitamos una ranura personalizada para llevar a cabo la operación de añadir y mostrar el resultado en el QLabel. Para lograr esto, necesitamos utilizar el enfoque de herencia única.

El enfoque de herencia única

Para utilizar el enfoque de herencia única, subclasificamos un widget Qt estándar e incluimos una instancia privada del objeto de interfaz de usuario del formulario. Esto puede tomar la forma de:

  • Una variable miembro
  • Una variable miembro puntero

Utilización de una variable miembro

En este enfoque, subclasificamos un widget Qt y configuramos la interfaz de usuario desde el constructor. Los componentes utilizados de esta forma exponen los widgets y diseños utilizados en el formulario a la subclase del widget Qt, y proporcionan un sistema estándar para realizar conexiones de señales y ranuras entre la interfaz de usuario y otros objetos de la aplicación. La estructura generada Ui::CalculatorForm es un miembro de la clase.

Este enfoque se utiliza en el ejemplo del formulario de calculadora.

Para asegurarnos de que podemos utilizar la interfaz de usuario, necesitamos incluir el fichero de cabecera que genera uic antes de referirnos a Ui::CalculatorForm:

#include "ui_calculatorform.h"

El archivo de proyecto debe ser actualizado para incluir calculatorform.h. Para CMake:

qt_add_executable(calculatorform
    calculatorform.cpp calculatorform.h calculatorform.ui
    main.cpp
)

En casos específicos, como el ejemplo siguiente, en el que la directiva include utiliza una ruta relativa, se puede utilizar qt_add_ui para generar el archivo ui_calculatorform.h en lugar de confiar en AUTOUIC.

Cuándo preferir qt_add_ui a AUTOUIC

#include "src/files/ui_calculatorform.h"
qt_add_ui(calculatorform SOURCES calculatorform.ui
          INCLUDE_PREFIX src/files)

Para qmake:

HEADERS     = calculatorform.h

La subclase se define de la siguiente manera:

class CalculatorForm : public QWidget
{
    Q_OBJECT

public:
    explicit CalculatorForm(QWidget *parent = nullptr);

private slots:
    void updateResult();

private:
    Ui::CalculatorForm ui;
};

La característica importante de la clase es el objeto privado ui que proporciona el código para configurar y gestionar la interfaz de usuario.

El constructor de la subclase construye y configura todos los widgets y diseños del diálogo simplemente llamando a la función setupUi() del objeto ui. Una vez hecho esto, es posible modificar la interfaz de usuario según sea necesario.

CalculatorForm::CalculatorForm(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);
    connect(ui.inputSpinBox1, &QSpinBox::valueChanged, this, &CalculatorForm::updateResult);
    connect(ui.inputSpinBox2, &QSpinBox::valueChanged, this, &CalculatorForm::updateResult);
}

Podemos conectar señales y ranuras en los widgets de la interfaz de usuario de la forma habitual añadiendo el prefijo on_<nombre de objeto> -. Para obtener más información, consulte widgets-and-dialogs-with-auto-connect.

Las ventajas de este método son el uso sencillo de la herencia para proporcionar una interfaz basada en QWidget y la encapsulación de las variables del widget de interfaz de usuario en el miembro de datos ui. Podemos utilizar este método para definir varias interfaces de usuario dentro del mismo widget, cada una de ellas contenida en su propio espacio de nombres, y superponerlas (o componerlas). Este método se puede utilizar para crear pestañas individuales a partir de formularios existentes, por ejemplo.

Uso de una variable miembro puntero

Alternativamente, la estructura Ui::CalculatorForm puede convertirse en un puntero miembro de la clase. La cabecera tendrá el siguiente aspecto:

namespace Ui {
    class CalculatorForm;
}

class CalculatorForm : public QWidget
...
virtual ~CalculatorForm();
...
private:
    Ui::CalculatorForm *ui;
...

El archivo fuente correspondiente tiene el siguiente aspecto:

#include "ui_calculatorform.h"

CalculatorForm::CalculatorForm(QWidget *parent) :
    QWidget(parent), ui(new Ui::CalculatorForm)
{
    ui->setupUi(this);
}

CalculatorForm::~CalculatorForm()
{
    delete ui;
}

La ventaja de este enfoque es que el objeto de interfaz de usuario puede ser declarado por adelantado, lo que significa que no tenemos que incluir el archivo ui_calculatorform.h generado en la cabecera. La forma puede entonces ser cambiada sin recompilar los archivos fuente dependientes. Esto es especialmente importante si la clase está sujeta a restricciones de compatibilidad binaria.

Por lo general, recomendamos este enfoque para bibliotecas y aplicaciones de gran tamaño. Para obtener más información, consulte Creación de bibliotecas compartidas.

El enfoque de herencia múltiple

Los formularios creados con Qt Widgets Designer pueden subclasificarse junto con una clase estándar basada en QWidget. Este enfoque hace que todos los componentes de la interfaz de usuario definidos en el formulario sean directamente accesibles dentro del ámbito de la subclase, y permite que las conexiones de señales y ranuras se realicen de la forma habitual con la función connect().

Necesitamos incluir el fichero de cabecera que uic genera a partir del fichero calculatorform.ui, como sigue:

#include "ui_calculatorform.h"

La clase se define de forma similar a la utilizada en el enfoque de herencia única, salvo que esta vez heredamos tanto de QWidget como de Ui::CalculatorForm, como sigue:

class CalculatorForm : public QWidget, private Ui::CalculatorForm
{
    Q_OBJECT

public:
    explicit CalculatorForm(QWidget *parent = nullptr);

private slots:
    void on_inputSpinBox1_valueChanged(int value);
    void on_inputSpinBox2_valueChanged(int value);
};

Heredamos Ui::CalculatorForm de forma privada para asegurarnos de que los objetos de la interfaz de usuario son privados en nuestra subclase. También podemos heredarlo con las palabras clave public o protected de la misma forma que podríamos haber hecho ui público o protegido en el caso anterior.

El constructor de la subclase realiza muchas de las mismas tareas que el constructor utilizado en el ejemplo de herencia única:

CalculatorForm::CalculatorForm(QWidget *parent)
    : QWidget(parent)
{
    setupUi(this);
}

En este caso, se puede acceder a los widgets utilizados en la interfaz de usuario de la misma manera que a un widget creado en código a mano. Ya no necesitamos el prefijo ui para acceder a ellos.

Reaccionar a los cambios de idioma

Qt notifica a las aplicaciones si el idioma de la interfaz de usuario cambia enviando un evento del tipo QEvent::LanguageChange. Para llamar a la función miembro retranslateUi() del objeto de interfaz de usuario, reimplementamos QWidget::changeEvent() en la clase form, como sigue:

void CalculatorForm::changeEvent(QEvent *e)
{
    QWidget::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
   }
}

Procesamiento de formularios en tiempo de ejecución

Alternativamente, los formularios pueden ser procesados en tiempo de ejecución, produciendo interfaces de usuario generadas dinámicamente. Esto puede hacerse utilizando el módulo QtUiTools que proporciona la clase QUiLoader para manejar formularios creados con Qt Widgets Designer.

El enfoque UiTools

Para procesar formularios en tiempo de ejecución se necesita un archivo de recursos que contenga un archivo de interfaz de usuario. Además, la aplicación necesita ser configurada para utilizar el módulo QtUiTools. Esto se hace incluyendo las siguientes declaraciones en un archivo de proyecto CMake, asegurándose de que la aplicación se compila y enlaza adecuadamente.

find_package(Qt6 REQUIRED COMPONENTS Core Gui UiTools Widgets)
target_link_libraries(textfinder PUBLIC
    Qt::Core
    Qt::Gui
    Qt::UiTools
    Qt::Widgets
)

Para qmake:

QT += uitools

La clase QUiLoader proporciona un objeto cargador de formularios para construir la interfaz de usuario. Esta interfaz de usuario puede recuperarse desde cualquier QIODevice, por ejemplo, un objeto QFile, para obtener un formulario almacenado en el archivo de recursos de un proyecto. La función QUiLoader::load() construye el widget del formulario utilizando la descripción de la interfaz de usuario contenida en el archivo.

Las clases del módulo QtUiTools pueden incluirse utilizando la siguiente directiva:

#include <QtUiTools>

La función QUiLoader::load() se invoca como se muestra en este código del ejemplo Buscador de texto:

static QWidget *loadUiFile(QWidget *parent) { QFile file(u":/forms/textfinder.ui"_s); if (!file.open(QIODevice::Sólo lectura))        qFatal("Cannot open resource file");

   devolver QUiLoader().load(&archivo, padre); }

En una clase que utiliza QtUiTools para construir su interfaz de usuario en tiempo de ejecución, podemos localizar objetos en el formulario utilizando QObject::findChild(). Por ejemplo, en el siguiente código, localizamos algunos componentes basándonos en sus nombres de objeto y tipos de widget:

    ui_findButton = findChild<QPushButton*>("findButton");
    ui_textEdit = findChild<QTextEdit*>("textEdit");
    ui_lineEdit = findChild<QLineEdit*>("lineEdit");

Procesar formularios en tiempo de ejecución da al desarrollador la libertad de cambiar la interfaz de usuario de un programa, simplemente cambiando el archivo UI. Esto resulta útil a la hora de personalizar los programas para adaptarlos a las distintas necesidades de los usuarios, como iconos más grandes o un esquema de colores diferente para facilitar la accesibilidad.

Conexiones automáticas

Las conexiones entre señales y ranuras definidas para los formularios en tiempo de compilación o en tiempo de ejecución pueden configurarse manual o automáticamente, utilizando la capacidad de QMetaObject de establecer conexiones entre señales y ranuras con nombres adecuados.

Generalmente, en un QDialog, si queremos procesar la información introducida por el usuario antes de aceptarla, necesitamos conectar la señal clicked() del botón OK a una ranura personalizada en nuestro diálogo. Primero mostraremos un ejemplo de diálogo en el que la ranura se conecta a mano y luego lo compararemos con un diálogo que utiliza la conexión automática.

Un diálogo sin conexión automática

Definimos el diálogo de la misma forma que antes, pero ahora incluimos una ranura además del constructor:

class ImageDialog : public QDialog, private Ui::ImageDialog
{
    Q_OBJECT

public:
    explicit ImageDialog(QWidget *parent = nullptr);

private slots:
    void checkValues();
};

La ranura checkValues() se utilizará para validar los valores proporcionados por el usuario.

En el constructor del diálogo configuramos los widgets como antes, y conectamos la señal clicked() del botón Cancelar a la ranura reject() del diálogo. También desactivamos la propiedad autoDefault en ambos botones para asegurarnos de que el diálogo no interfiere con la forma en que el editor de línea maneja los eventos de tecla de retorno:

ImageDialog::ImageDialog(QWidget *parent)
    : QDialog(parent)
{
    setupUi(this);
    okButton->setAutoDefault(false);
    cancelButton->setAutoDefault(false);
    ...
    connect(okButton, &QAbstractButton::clicked, this, &ImageDialog::checkValues);
}

Conectamos la señal clicked() del botón OK a la ranura checkValues() del diálogo que implementamos como sigue:

void ImageDialog::checkValues()
{
    if (nameLineEdit->text().isEmpty()) {
        QMessageBox::information(this, tr("No Image Name"),
            tr("Please supply a name for the image."), QMessageBox::Cancel);
    } else {
        accept();
    }
}

Esta ranura personalizada hace lo mínimo necesario para asegurar que los datos introducidos por el usuario son válidos - sólo acepta la entrada si se ha dado un nombre a la imagen.

Widgets y diálogos con conexión automática

Aunque es fácil implementar un slot personalizado en el diálogo y conectarlo en el constructor, podríamos utilizar las facilidades de auto-conexión de QMetaObject para conectar la señal clicked() del botón OK a un slot en nuestra subclase. uic genera automáticamente código en la función setupUi() del diálogo para hacer esto, por lo que sólo necesitamos declarar e implementar un slot con un nombre que siga una convención estándar:

void on_<object name>_<signal name>(<signal parameters>);

Nota: Al renombrar los widgets en el formulario, los nombres de las ranuras deben adaptarse en consecuencia, lo que puede convertirse en un problema de mantenimiento. Por esta razón, recomendamos no usar esto en código nuevo.

Usando esta convención, podemos definir e implementar una ranura que responda a los clicks del ratón sobre el botón OK:

class ImageDialog : public QDialog, private Ui::ImageDialog
{
    Q_OBJECT

public:
    explicit ImageDialog(QWidget *parent = nullptr);

private slots:
    void on_okButton_clicked();
};

Otro ejemplo de conexión automática de señal y ranura sería el Buscador de Texto con su ranura on_findButton_clicked().

Utilizamos el sistema de QMetaObject para habilitar las conexiones de señales y ranuras:

    QMetaObject::connectSlotsByName(this);

Esto nos permite implementar la ranura, como se muestra a continuación:

void TextFinder::on_findButton_clicked()
{
    QString searchString = ui_lineEdit->text();
    QTextDocument *document = ui_textEdit->document();

    bool found = false;

    // undo previous change (if any)
    document->undo();

    if (searchString.isEmpty()) {
        QMessageBox::information(this, tr("Empty Search Field"),
                                 tr("The search field is empty. "
                                    "Please enter a word and click Find."));
    } else {
        QTextCursor highlightCursor(document);
        QTextCursor cursor(document);

        cursor.beginEditBlock();
    ...
        cursor.endEditBlock();

        if (found == false) {
            QMessageBox::information(this, tr("Word Not Found"),
                                     tr("Sorry, the word cannot be found."));
        }
    }
}

La conexión automática de señales y ranuras proporciona tanto una convención de nomenclatura estándar como una interfaz explícita con la que los diseñadores de widgets pueden trabajar. Al proporcionar el código fuente que implementa una interfaz determinada, los diseñadores de interfaces de usuario pueden comprobar que sus diseños funcionan realmente sin tener que escribir ellos mismos el código.

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