Qt Layouts para posicionar los QWidget

Dedicaremos este tutorial ha estudiar el posicionamiento de los widgets en la ventana de nuestra aplicación, en Qt disponemos de diversos modos para realizar esto, entre ellos: posicionamiento absoluto, con él debemos indicar la posición y dimensiones de cada uno de los controles que ubicaremos en la GUI, otra alternativa es usar los layout manager, ellos se encargará de posicionar y redimensionar los controles de manera automática.

Posicionamiento absoluto en Qt

Para utilizar el posicionamiento absoluto debemos usar el método setGeometry(x, y, ancho, alto) con él establecemos la posición (X, Y) del widget y sus dimensiones (ancho, alto), veamos un pequeño ejemplo con un QPushButton.

#include <QApplication>
#include <QWidget>
#include <QPushButton>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QWidget ventana;

    QPushButton btn("QPushButton \nposicion: (10, 10), dimension: (350, 50)", &ventana);
    btn.setGeometry(10, 10, 350, 50);

    ventana.setWindowTitle("Posicion absoluta");
    ventana.show();

    return a.exec();
}

Este ejemplo solo muestra un botón, puedes agregar más, solo recuerda indicar la posición y dimensiones para cada control que añadas.

Qt posicion absoluta

Este modelo de posicionamiento es bastante sencillo, pero tiene algunos inconvenientes:

  • Si cambiamos el tamaño de la ventana el widget no se adapta al nuevo tamaño.
  • Al cambiar la fuente puede que el texto no se visualice en su totalidad.
  • En una GUI compleja es bastante tedioso ubicar cada uno de los componentes.

Usando los Layout en Qt

Los layout son clases que nos ayudan a posicionar y organizar los elementos de la GUI, entre ellos tenemos: QHBoxLayout, QVBoxLayout, QFormLayout, y QGridLayout, veremos ejemplos del funcionamiento de cada uno de ellos, a diferencia del posicionamiento absoluto estas clases redimensionan sus elementos para adaptarlos al cambio de tamaño de la ventana.

Para la demostración de la mencionados componentes usaremos esta clase:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QWidget>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QFormLayout>
#include <QGridLayout>
#include <QPushButton>

class MainWindow : public QWidget
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
};

#endif // MAINWINDOW_H

QVBoxLayout

Organiza los elementos que contiene en forma vertical, de la siguiente manera:

MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
{
    // crear el layout vertical
    QVBoxLayout* vLayout = new QVBoxLayout(this);
    vLayout->setSpacing(1); // espacio de 1px entre cada elemento

    QPushButton* btn1 = new QPushButton("Btn 1", this);
    QPushButton* btn2 = new QPushButton("Btn 2", this);
    QPushButton* btn3 = new QPushButton("Btn 3", this);

    // agregar los elementos al layout vertical
    vLayout->addWidget(btn1);
    vLayout->addWidget(btn2);
    vLayout->addWidget(btn3);

    // establer el layout a utilizar por la ventana
    setLayout(vLayout);
}

QVBoxLayout de Qt Framework

Usamos el método setSpacing() para establecer la cantidad de espacio entre cada botón, para que la ventana utilice el layout que acabamos de crear debemos utilizar setLayout() para establecerlo.

QHBoxLayout

Esta clase trabaja de manera similar a la anterior, salvo que organiza sus elementos en forma horizontal, veamos:

MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
{
    // crear el layout vertical
    QHBoxLayout* vLayout = new QHBoxLayout(this);
    vLayout->setSpacing(10); // espacio de 10px entre cada elemento

    // lo demás es exactamente igual al anterior ...
}

QHBoxLayout en Qt

Para este ejemplo solo cambiamos la clase, si cambiamos el tamaño de la ventana veremos que los botones adaptan su tamaño a la ventana, pero solo horizontalmente, este es el comportamiento por defecto, lo cambiamos de esta manera:

btn1->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
btn2->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);

Mediante QSizePolicy::Fixed indicamos que el widget debe mantener su tamaño y con QSizePolicy::Expanding establecemos que se debe expandir, esto lo indicamos en el método setSizePolicy() donde el primer parámetro establece el comportamiento horizontal y el segundo el comportamiento vertical.

QFormLayout

Con él podemos crear formularios, como su nombre lo dice, disponemos de dos columnas la primera para incluir etiquetas de texto y la otra contiene el respectivo campo de entrada para el formulario, ejemplo:

MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
{
    QFormLayout* formLayout = new QFormLayout(this);

    QLineEdit *nombreEdit = new QLineEdit(this);
    QLineEdit *correoEdit = new QLineEdit(this);
    QLineEdit *edadEdit = new QLineEdit(this);

    // alinear texto a la derecha y centrado verticalmente
    formLayout->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);

    // añadir cada fila del formulario
    formLayout->addRow("Nombre:", nombreEdit);
    formLayout->addRow("Correo:", correoEdit);
    formLayout->addRow("Edad:", edadEdit);

    setLayout(formLayout);
}

QFormLayout crea formularios

Usando el método setLabelAlignment() establecemos la alineación de la etiqueta de texto, con addRow() añadimos una fila al formulario, debemos indicar la etiqueta de la misma y el campo que captará el dato por parte del usuario, en este caso un QLineEdit que permite obtener un texto de entrada.

QGridLayout

Está diseñado para ubicar los elementos utilizando celdas, por lo que debemos indicar la fila y la columna a la que pertenece cada elemento que agregamos, para ello usamos addWidget(QWidget*, fila, columna) esta clase nos permite crear GUIs muy complejas ya que permite que un control ocupe una o más filas o columnas según nuestras necesidades.

MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
{
    QGridLayout *grid = new QGridLayout(this);
    grid->setSpacing(2);

    QLineEdit* edit = new QLineEdit(this);
    edit->setFixedHeight(50);
    edit->setFont(QFont("Consolas", 20));

    grid->addWidget(edit, 0, 0, 1, 4);

    QList<QString> values({
        "7", "8", "9", "/",
        "4", "5", "6", "*",
        "1", "2", "3", "-",
        "0", ".", "=", "+"
    });

    for (int i = 1, pos = 0; i <= 4; i++) {
        for (int j = 0; j < 4; j++) {
            QPushButton *btn = new QPushButton(values[pos++], this);
            btn->setFixedSize(80, 80);
            grid->addWidget(btn, i, j);
        }
    }

    setLayout(grid);
}

Qt uso de QGridLayout

Este es el código usado para agregar el control de texto, el mismo ocupa todas las columnas de la primera fila, usando addWidget(0, 0, 1, 4) indicamos primero la fila y columna en donde se ubicará, luego sigue la cantidad de filas y columnas que ocupará.

QLineEdit* edit = new QLineEdit(this);
edit->setFixedHeight(50);
edit->setFont(QFont("Consolas", 20));

grid->addWidget(edit, 0, 0, 1, 4);

Para agregar cada uno de los botones de la calculadora, solo indicamos la celda ya que cada botón ocupará solo una casilla.

for (int i = 1, pos = 0; i <= 4; i++) {                         
    for (int j = 0; j < 4; j++) {                               
        QPushButton *btn = new QPushButton(values[pos++], this);
        btn->setFixedSize(80, 80);                              
        grid->addWidget(btn, i, j);                             
    }                                                           
}

Combinar Layout en Qt

Para finalizar mostramos un ejemplo en donde se puede apreciar como combinar los distintos layout y configuraciones que hemos visto para lograr crear GUIs bastante complejas.

image

El contenedor principal es un QVBoxLayout el cual contiene un QFormLayout y un QHBoxLayout que contiene los dos botones que vemos en la parte inferior derecha de la aplicación.

MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
{
    QVBoxLayout* rootLayout = new QVBoxLayout(this);


    QHBoxLayout* btnLayout = new QHBoxLayout();

    QPushButton* btnAceptar = new QPushButton("Aceptar", this);
    QPushButton* btnCancelar = new QPushButton("Cancelar", this);

    btnAceptar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
    btnCancelar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);

    btnLayout->setSpacing(10);
    btnLayout->addStretch(1);
    btnLayout->addWidget(btnAceptar);
    btnLayout->addWidget(btnCancelar);


    QFormLayout* formLayout = new QFormLayout();

    QLineEdit *nombreEdit = new QLineEdit(this);
    QLineEdit *correoEdit = new QLineEdit(this);
    QLineEdit *edadEdit = new QLineEdit(this);

    QTextEdit *descEdit = new QTextEdit(this);
    descEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    descEdit->setPlaceholderText("escriba una breve descripcion...");

    formLayout->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);
    formLayout->addRow("Nombre:", nombreEdit);
    formLayout->addRow("Correo:", correoEdit);
    formLayout->addRow("Edad:", edadEdit);
    formLayout->addRow("Descripcion:", descEdit);

    rootLayout->addLayout(formLayout);
    rootLayout->addLayout(btnLayout);

    setLayout(rootLayout);
}

Para lograr que los dos botones se ubiquen a la derecha he usado el método btnLayout->addStretch(1) para ocupar el espacio a la izquierda de los botones.

Descargar ejemplo: qt-layout-básico.zip

Comentarios

Entradas populares de este blog

Conectar SQL Server con Java

Entrenar OpenCV en Detección de Objetos

Acceso a la webcam con OpenCV

JavaFx 8 Administrar ventanas

Analizador Léxico