How to tile widgets in a multiple document interface application

In Qt you can create multiple document interface (MDI) applications using a QMdiArea as the central widget. The QMdiArea widget serves as a window manager for the MDI windows. There are already functions available for tiling all the child windows into a tile pattern and for cascading them into a cascade pattern.

With MDI application it would sometimes be useful to tile the child windows also horizontally and vertically. The QMdiArea does not currently have an API to achieve this functionality but it’s quite simple to arrange the child windows in your application. We will next cover the steps needed to tile the child windows horizontally and vertically. The approach presented here is implemented on top of the MDI Example (http://qt-project.org/doc/qt-5.0/qtwidgets/mainwindows-mdi.html).

Let’s start by adding the actions in place so that the functionality can be triggered. To the MainWindow we will add one action for each function as private member variable:

QAction *tileVerticalAct;
QAction *tileHorizontalAct;

We will also add a private slot for each function:

void tileSubWindowsVertically();
void tileSubWindowsHorizontally();

Then we will initialize the actions and connect them to the right slots. We are going to add the following code to the createActions() method that already initializes actions for the application:

tileVerticalAct = new QAction(tr("Tile Vertically"), this);
tileVerticalAct->setStatusTip(tr("Tile the windows vertically"));
connect(tileVerticalAct, SIGNAL(triggered()), this, SLOT(tileSubWindowsVertically()));
tileHorizontalAct = new QAction(tr("Tile Horizontally"), this);
tileHorizontalAct->setStatusTip(tr("Tile the windows horizontally"));
connect(tileHorizontalAct, SIGNAL(triggered()), this, SLOT(tileSubWindowsHorizontally()));

And naturally we need to add the actions to the menu in order to launch the tiling. We can do this in the updateWindowMenu() where other actions are added to the menu too:

windowMenu->addAction(tileVerticalAct);
windowMenu->addAction(tileHorizontalAct);

Now we are ready to start tiling the child windows. We are going to take a closer look at the tileSubWindowsVertically() slot here. To begin with, we will start by checking that there are child windows in the MDI area to prevent us from trying to do modifications to non-existing windows.

if (mdiArea->subWindowList().isEmpty())
    return;

We are going to initialize a rectangle that defines the size of one child window. The width for a child window will naturally be the width of the MDI area. The height of a child window will be based on the height of the MDI area and the amount of child windows. Basically, we will just divide the MDI area height with the amount of child windows. The rectangle can then be used to set the geometry for each child widget in the MDI area.

QPoint position(0, 0);
foreach (QMdiSubWindow *window, mdiArea->subWindowList()) {
QRect rect(0, 0, mdiArea->width(),
mdiArea->height() / mdiArea->subWindowList().count());
window->setGeometry(rect);
window->move(position);
position.setY(position.y() + window->height());
}

While we are setting the geometry for the child widgets we also define the new position for them to have the windows tiled vertically. The child windows are ordered here based on the order of the child windows in the MDI area. By default, this order is the order in which the windows were inserted into the workspace. We will move the first window in the MDI area to the top left corner. While processing the child windows we will adjust the position so that each child window is position below the previously processed one. This is all we need to tile the child windows vertically.

Tiling the child windows horizontally will be almost the same; you only need to adjust the width and x-position of the window. The height of a child window will be the same as the height of the MDI area. The position of a child window will be on the right of the previous child.

void MainWindow::tileSubWindowsHorizontally()
{
    if (mdiArea->subWindowList().isEmpty())
        return;
    QPoint position(0, 0);
    foreach (QMdiSubWindow *window, mdiArea->subWindowList()) {
        QRect rect(0, 0, mdiArea->width() / mdiArea->subWindowList().count(),
mdiArea->height());
        window->setGeometry(rect);
        window->move(position);
        position.setX(position.x() + window->width());
    }
}

Not that hard or what do you think? If you want to give it a try you can get the source code for the example here: Source code for example application . Shall you have any questions related to this you can always contact us in the Qt Support team via the Customer Portal.


Blog Topics:

Comments