Gestión del diseño
El sistema de maquetación de Qt proporciona una forma sencilla y potente de organizar automáticamente los widgets hijos dentro de un widget para garantizar que hacen un buen uso del espacio disponible.
Introducción
Qt incluye un conjunto de clases de gestión de disposición que se utilizan para describir cómo se disponen los widgets en la interfaz de usuario de una aplicación. Estos diseños automáticamente posicionan y redimensionan los widgets cuando la cantidad de espacio disponible para ellos cambia, asegurando que están ordenados de forma consistente y que la interfaz de usuario en su conjunto sigue siendo utilizable.
Todas las subclases de QWidget pueden utilizar layouts para gestionar sus hijos. La función QWidget::setLayout() aplica una disposición a un widget. Cuando se establece un layout en un widget de esta forma, se encarga de las siguientes tareas:
- Posicionamiento de los widgets hijos
- Tamaños por defecto sensibles para las ventanas
- Tamaños mínimos razonables para las ventanas
- Cambio de tamaño
- Actualizaciones automáticas cuando cambian los contenidos
- Tamaño de fuente, texto u otros contenidos de los widgets hijos
- Ocultar o mostrar un widget hijo
- Eliminación de widgets hijos
Clases de diseño de Qt
Las clases de diseño de Qt fueron diseñadas para código C++ escrito a mano, permitiendo especificar las medidas en píxeles para simplificar, por lo que son fáciles de entender y usar. El código generado para los formularios creados con Qt Widgets Designer también utiliza las clases de diseño. Qt Widgets El diseñador es útil cuando se experimenta con el diseño de un formulario, ya que evita el ciclo de compilación, enlace y ejecución que suele implicar el desarrollo de interfaces de usuario.
Alinea widgets hijo horizontal o verticalmente | |
Contenedor para organizar grupos de widgets de botones | |
Gestiona formularios de widgets de entrada y sus etiquetas asociadas | |
Representa un ancla entre dos elementos en un QGraphicsAnchorLayout | |
Disposición donde uno puede anclar widgets juntos en la Vista Gráfica | |
Dispone los widgets en una rejilla | |
Agrupa el marco de la caja con un título | |
Alinea los widgets horizontalmente | |
La clase base de los gestores de geometría | |
Elemento abstracto que manipula un QLayout | |
Atributo Layout que describe la política de redimensionamiento horizontal y vertical | |
Espacio en blanco en un layout | |
Pila de widgets donde sólo un widget es visible a la vez | |
Pila de widgets donde sólo un widget es visible a la vez | |
Alinea los widgets verticalmente | |
Elemento de diseño que representa un widget |
Diseños horizontal, vertical, de cuadrícula y de formulario
La forma más fácil de dar a tus widgets un buen diseño es utilizar los gestores de diseño incorporados: QHBoxLayout, QVBoxLayout, QGridLayout, y QFormLayout. Estas clases heredan de QLayout, que a su vez deriva de QObject (no de QWidget). Se encargan de la gestión de la geometría de un conjunto de widgets. Para crear diseños más complejos, puedes anidar gestores de diseño unos dentro de otros.
- Un QHBoxLayout dispone los widgets en una fila horizontal, de izquierda a derecha (o de derecha a izquierda para idiomas de derecha a izquierda).

- QVBoxLayout dispone los widgets en una columna vertical, de arriba abajo.

- QGridLayout dispone los widgets en una cuadrícula bidimensional. Los widgets pueden ocupar varias celdas.

- QFormLayout presenta los widgets en un estilo de campo de etiqueta descriptivo de 2 columnas.

Disposición de widgets en código
El siguiente código crea un QHBoxLayout que gestiona la geometría de cinco QPushButtons, como se muestra en la primera captura de pantalla anterior:
QWidget *window = new QWidget; QPushButton *button1 = new QPushButton("One"); QPushButton *button2 = new QPushButton("Two"); QPushButton *button3 = new QPushButton("Three"); QPushButton *button4 = new QPushButton("Four"); QPushButton *button5 = new QPushButton("Five"); QHBoxLayout *layout = new QHBoxLayout(window); layout->addWidget(button1); layout->addWidget(button2); layout->addWidget(button3); layout->addWidget(button4); layout->addWidget(button5); window->show();
El código para QVBoxLayout es idéntico, excepto la línea donde se crea el diseño. El código para QGridLayout es un poco diferente, porque necesitamos especificar la posición de fila y columna del widget hijo:
QWidget *window = new QWidget; QPushButton *button1 = new QPushButton("One"); QPushButton *button2 = new QPushButton("Two"); QPushButton *button3 = new QPushButton("Three"); QPushButton *button4 = new QPushButton("Four"); QPushButton *button5 = new QPushButton("Five"); QGridLayout *layout = new QGridLayout(window); layout->addWidget(button1, 0, 0); layout->addWidget(button2, 0, 1); layout->addWidget(button3, 1, 0, 1, 2); layout->addWidget(button4, 2, 0); layout->addWidget(button5, 2, 1); window->show();
El tercer QPushButton abarca 2 columnas. Esto es posible especificando 2 como quinto argumento de QGridLayout::addWidget().
QFormLayout añadirá dos widgets en una fila, comúnmente un QLabel y un QLineEdit para crear formularios. Añadir un QLabel y un QLineEdit en la misma fila establecerá el QLineEdit como compañero del QLabel. El siguiente código utilizará QFormLayout para colocar tres QPushButtons y su correspondiente QLineEdit en una fila.
QWidget *window = new QWidget; QPushButton *button1 = new QPushButton("One"); QLineEdit *lineEdit1 = new QLineEdit(); QPushButton *button2 = new QPushButton("Two"); QLineEdit *lineEdit2 = new QLineEdit(); QPushButton *button3 = new QPushButton("Three"); QLineEdit *lineEdit3 = new QLineEdit(); QFormLayout *layout = new QFormLayout(window); layout->addRow(button1, lineEdit1); layout->addRow(button2, lineEdit2); layout->addRow(button3, lineEdit3); window->show();
Consejos para el uso de diseños
Cuando utilices un layout, no necesitas pasar un padre cuando construyas los widgets hijos. El diseño reparentará automáticamente los widgets (utilizando QWidget::setParent()) para que sean hijos del widget en el que está instalado el diseño.
Nota: Los widgets de una maquetación son hijos del widget sobre el que está instalada la maquetación, no de la propia maquetación. Los widgets sólo pueden tener otros widgets como padre, no las maquetas.
Puede anidar maquetaciones utilizando addLayout() en una maquetación; la maquetación interior se convierte entonces en hija de la maquetación en la que se inserta.
Añadir widgets a un diseño
Al añadir widgets a una maqueta, el proceso de maquetación funciona del siguiente modo:
- A todos los widgets se les asignará inicialmente una cantidad de espacio de acuerdo con su QWidget::sizePolicy() y QWidget::sizeHint().
- Si alguno de los widgets tiene factores de estiramiento establecidos, con un valor mayor que cero, entonces se les asigna espacio en proporción a su factor de estiramiento (explicado más adelante).
- Si alguno de los widgets tiene factores de estiramiento establecidos en cero, sólo obtendrá más espacio si ningún otro widget lo desea. De estos, el espacio se asigna primero a los widgets con una política de tamaño Expanding.
- Cualquier widget al que se le asigne menos espacio que su tamaño mínimo (o sugerencia de tamaño mínimo si no se especifica un tamaño mínimo) se le asigna este tamaño mínimo que necesita. (Los widgets no tienen que tener un tamaño mínimo o una sugerencia de tamaño mínimo, en cuyo caso el factor de estiramiento es su factor determinante).
- A los widgets que tengan asignado más espacio que su tamaño máximo se les asigna el espacio de tamaño máximo que necesiten. (Los widgets no tienen que tener un tamaño máximo, en cuyo caso el factor de estiramiento es su factor determinante).
Factores de ampliación
Los widgets se crean normalmente sin ningún factor de estiramiento establecido. Cuando se colocan en un diseño, los widgets reciben una parte del espacio de acuerdo con su QWidget::sizePolicy() o su sugerencia de tamaño mínimo, lo que sea mayor. Los factores de estiramiento se utilizan para cambiar la cantidad de espacio que se da a los widgets en proporción entre sí.
Si tenemos tres widgets en QHBoxLayout sin factores de estiramiento, obtendremos un diseño como este:

Si aplicamos factores de estiramiento a cada widget, se distribuirán en proporción (pero nunca por debajo de su tamaño mínimo), por ejemplo

Widgets personalizados en diseños
Cuando creas tu propia clase de widget, también debes comunicar sus propiedades de diseño. Si el widget usa uno de los layouts de Qt, esto ya está solucionado. Si el widget no tiene ningún widget hijo, o utiliza un diseño manual, puedes cambiar el comportamiento del widget utilizando alguno o todos los mecanismos siguientes:
- Reimplemente QWidget::sizeHint() para devolver el tamaño preferido del widget.
- Reimplementar QWidget::minimumSizeHint() para devolver el tamaño más pequeño que puede tener el widget.
- Llamar a QWidget::setSizePolicy() para especificar los requisitos de espacio del widget.
Llame a QWidget::updateGeometry() siempre que cambie la sugerencia de tamaño, la sugerencia de tamaño mínimo o la política de tamaño. Esto provocará un recálculo del diseño. Varias llamadas consecutivas a QWidget::updateGeometry() sólo provocarán un recálculo del diseño.
Si la altura preferida de su widget depende de su anchura real (por ejemplo, una etiqueta con separación automática de palabras), establezca la bandera height-for-width en size policy del widget y vuelva a implementar QWidget::heightForWidth().
Incluso si implementas QWidget::heightForWidth(), sigue siendo una buena idea proporcionar un sizeHint() razonable.
Para más información sobre la implementación de estas funciones, consulte el artículo de Qt Quarterly Trading Height for Width.
Problemas de diseño
El uso de texto enriquecido en un widget de etiqueta puede introducir algunos problemas en el diseño de su widget padre. Los problemas ocurren debido a la forma en que el texto enriquecido es manejado por los gestores de diseño de Qt cuando la etiqueta se envuelve con palabras.
En ciertos casos el diseño padre se pone en modo QLayout::FreeResize, lo que significa que no adaptará el diseño de sus contenidos para que quepan en ventanas de tamaño pequeño, o incluso evitará que el usuario haga la ventana demasiado pequeña para ser utilizable. Esto puede solucionarse subclasificando los widgets problemáticos e implementando las funciones sizeHint() y minimumSizeHint() adecuadas.
En algunos casos, es relevante cuando se añade un diseño a un widget. Cuando se establece el widget de un QDockWidget o un QScrollArea (con QDockWidget::setWidget() y QScrollArea::setWidget()), el diseño ya debe haber sido establecido en el widget. De lo contrario, el widget no será visible.
Disposición manual
Si estás haciendo una maquetación especial única, también puedes hacer un widget personalizado como se ha descrito anteriormente. Reimplemente QWidget::resizeEvent() para calcular la distribución de tamaños requerida y llame a setGeometry() en cada hijo.
El widget recibirá un evento de tipo QEvent::LayoutRequest cuando la distribución necesite ser recalculada. Reimplemente QWidget::event() para manejar los eventos QEvent::LayoutRequest.
Cómo escribir un gestor de diseño personalizado
Una alternativa al diseño manual es escribir tu propio gestor de diseño subclasificando QLayout. El ejemplo Flow Layout muestra cómo hacerlo.
Aquí presentamos un ejemplo en detalle. La clase CardLayout está inspirada en el gestor de disposición Java del mismo nombre. Dispone los elementos (widgets o diseños anidados) unos encima de otros, cada elemento desplazado por QLayout::spacing().
Para escribir tu propia clase layout, debes definir lo siguiente:
- Una estructura de datos para almacenar los elementos manejados por el layout. Cada elemento es un QLayoutItem. En este ejemplo utilizaremos un QList.
- addItem(), cómo añadir elementos al layout.
- setGeometry(), cómo realizar la maquetación.
- sizeHint(), el tamaño preferido del layout.
- itemAt(), cómo iterar sobre el layout.
- takeAt(), cómo eliminar elementos de la maqueta.
En la mayoría de los casos, también implementará minimumSize().
El archivo de cabecera (card.h)
#ifndef CARD_H #define CARD_H #include <QtWidgets> #include <QList> class CardLayout : public QLayout { public: CardLayout(int spacing): QLayout() { setSpacing(spacing); } CardLayout(int spacing, QWidget *parent): QLayout(parent) { setSpacing(spacing); } ~CardLayout(); void addItem(QLayoutItem *item) override; QSize sizeHint() const override; QSize minimumSize() const override; int count() const override; QLayoutItem *itemAt(int) const override; QLayoutItem *takeAt(int) override; void setGeometry(const QRect &rect) override; private: QList<QLayoutItem *> m_items; }; #endif
El archivo de implementación (card.cpp)
//#include "card.h"Primero definimos count() para obtener el número de elementos de la lista.
int CardLayout::count() const { // QList::size() returns the number of QLayoutItems in m_items return m_items.size(); }
Luego definimos dos funciones que iteran sobre el layout: itemAt() y takeAt(). Estas funciones son utilizadas internamente por el sistema de diseño para gestionar la eliminación de widgets. También están disponibles para los programadores de aplicaciones.
itemAt() devuelve el elemento en el índice dado. takeAt() elimina el elemento en el índice dado y lo devuelve. En este caso usamos el índice de la lista como índice de diseño. En otros casos en los que tengamos una estructura de datos más compleja, puede que tengamos que dedicar más esfuerzo a definir un orden lineal para los elementos.
QLayoutItem *CardLayout::itemAt(int idx) const { // QList::value() performs index checking, and returns nullptr if we are // outside the valid range return m_items.value(idx); } QLayoutItem *CardLayout::takeAt(int idx) { // QList::take does not do index checking return idx >= 0 && idx < m_items.size() ? m_items.takeAt(idx) : 0; }
addItem() implementa la estrategia de colocación por defecto para los elementos de diseño. Esta función debe ser implementada. Es usada por QLayout::add(), por el constructor QLayout que toma un layout como padre. Si tu layout tiene opciones de colocación avanzadas que requieren parámetros, debes proporcionar funciones de acceso extra como las sobrecargas de row y column spanning de QGridLayout::addItem(), QGridLayout::addWidget(), y QGridLayout::addLayout().
void CardLayout::addItem(QLayoutItem *item) { m_items.append(item); }
El layout asume la responsabilidad de los elementos añadidos. Dado que QLayoutItem no hereda de QObject, debemos eliminar los elementos manualmente. En el destructor, eliminamos cada elemento de la lista utilizando takeAt(), y luego lo borramos.
CardLayout::~CardLayout() { QLayoutItem *item; while ((item = takeAt(0))) delete item; }
La función setGeometry() realiza realmente el diseño. El rectángulo suministrado como argumento no incluye margin(). Si es relevante, utiliza spacing() como distancia entre elementos.
void CardLayout::setGeometry(const QRect &r) { QLayout::setGeometry(r); if (m_items.size() == 0) return; int w = r.width() - (m_items.count() - 1) * spacing(); int h = r.height() - (m_items.count() - 1) * spacing(); int i = 0; while (i < m_items.size()) { QLayoutItem *o = m_items.at(i); QRect geom(r.x() + i * spacing(), r.y() + i * spacing(), w, h); o->setGeometry(geom); ++i; } }
sizeHint() y minimumSize() son normalmente muy similares en su implementación. Los tamaños devueltos por ambas funciones deben incluir spacing(), pero no margin().
QSize CardLayout::sizeHint() const { QSize s(0, 0); int n = m_items.count(); if (n > 0) s = QSize(100, 70); //start with a nice default size int i = 0; while (i < n) { QLayoutItem *o = m_items.at(i); s = s.expandedTo(o->sizeHint()); ++i; } return s + n * QSize(spacing(), spacing()); } QSize CardLayout::minimumSize() const { QSize s(0, 0); int n = m_items.count(); int i = 0; while (i < n) { QLayoutItem *o = m_items.at(i); s = s.expandedTo(o->minimumSize()); ++i; } return s + n * QSize(spacing(), spacing()); }
Notas adicionales
- Este diseño personalizado no maneja la altura para la anchura.
- Ignoramos QLayoutItem::isEmpty(); esto significa que la maquetación tratará los widgets ocultos como visibles.
- Para maquetaciones complejas, la velocidad puede incrementarse en gran medida almacenando en caché los valores calculados. En ese caso, implementa QLayoutItem::invalidate() para marcar que los datos almacenados en caché están sucios.
- Llamar a QLayoutItem::sizeHint(), etc. puede resultar caro. Por lo tanto, debe almacenar el valor en una variable local si lo necesita de nuevo más tarde dentro de la misma función.
- No debe llamar a QLayoutItem::setGeometry() dos veces sobre el mismo elemento en la misma función. Esta llamada puede ser muy costosa si el elemento tiene varios widgets hijos, porque el gestor de diseño debe hacer un diseño completo cada vez. En su lugar, calcula la geometría y luego establécela. (Esto no sólo se aplica a los layouts, deberías hacer lo mismo si implementas tu propio resizeEvent(), por ejemplo).
Ejemplos de Layouts
Muchos ejemplos de Qt Widgets ya utilizan layouts, sin embargo, existen varios ejemplos para mostrar varios layouts.
El ejemplo muestra como usar señales y ranuras para implementar la funcionalidad de un widget calculadora, y como usar QGridLayout para colocar widgets hijos en una rejilla. | |
El ejemplo Calendar Widget muestra el uso de QCalendarWidget. | |
Muestra como organizar los widgets para diferentes tamaños de ventana. | |
Muestra como funcionan los modos de composición en QPainter. | |
El ejemplo Menus demuestra como se pueden utilizar los menús en una aplicación de ventana principal. | |
El ejemplo Simple Tree Model muestra cómo utilizar un modelo jerárquico con las clases de vista estándar de Qt. |
© 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.