Qt: MdiAreaでQLayoutEngineを使ってウィンドウを配置する方法 〜showEvent() vs QLayoutEngine〜


QMdiAreaクラスは、複数の子ウィンドウを管理し、整列、表示、アクティブ化などの機能を提供する多重ドキュメントインターフェース(MDI)エリアウィジェットです。showEvent()関数は、QMdiAreaウィジェットが表示されたときに呼び出される仮想保護関数です。この関数は、ウィジェットの初期化と、子ウィンドウの配置などの処理に使用されます。

機能

showEvent()関数は以下の処理を行います。

  1. pendingRearrangementsリストに格納されている保留中の再配置要求を処理します。このリストには、子ウィンドウのサイズ変更、移動、アクティブ化などの操作に関する要求が含まれます。
  2. QMdiAreaウィジェットの現在のサイズと状態に基づいて、子ウィンドウを配置します。
  3. QMdiAreaウィジェットのツールバーとステータスバーを更新します。
  4. showEvent()関数は、QWidgetクラスの基底クラスから継承されたshowEvent()関数を呼び出して、標準的なウィジェット表示処理を実行します。

以下のコードは、showEvent()関数内で子ウィンドウをタイル表示する例です。

void QMdiArea::showEvent(QShowEvent *event)
{
    if (!pendingRearrangements.isEmpty()) {
        arrangePendingRearrangements();
    }

    tileSubWindows();

    QWidget::showEvent(event);
}

注意事項

showEvent()関数は、QMdiAreaウィジェットが表示されたときにのみ呼び出されます。ウィジェットが非表示になった場合は、この関数は呼び出されません。



タイル表示

この例では、showEvent() 関数内で tileSubWindows() 関数を呼び出して、子ウィンドウをタイル表示します。

void QMdiArea::showEvent(QShowEvent *event)
{
    if (!pendingRearrangements.isEmpty()) {
        arrangePendingRearrangements();
    }

    tileSubWindows();

    QWidget::showEvent(event);
}

カスケード表示

この例では、showEvent() 関数内で cascadeSubWindows() 関数を呼び出して、子ウィンドウをカスケード表示します。

void QMdiArea::showEvent(QShowEvent *event)
{
    if (!pendingRearrangements.isEmpty()) {
        arrangePendingRearrangements();
    }

    cascadeSubWindows();

    QWidget::showEvent(event);
}

カスタム配置

この例では、showEvent() 関数内で独自の配置ロジックを実装して、子ウィンドウを配置します。

void QMdiArea::showEvent(QShowEvent *event)
{
    if (!pendingRearrangements.isEmpty()) {
        arrangePendingRearrangements();
    }

    // カスタム配置ロジックを実装する
    for (QMdiSubWindow *subWindow : subWindows()) {
        // 子ウィンドウの位置とサイズを設定する
        subWindow->setPosition(QPoint(x, y));
        subWindow->setSize(QSize(width, height));

        x += subWindow->width();
        y += subWindow->height();
    }

    QWidget::showEvent(event);
}


しかし、showEvent() 関数はいくつかの制限があります。

  • 柔軟性: showEvent() 関数内で子ウィンドウを配置するには、pendingRearrangements リストを使用して、保留中の再配置要求を処理する必要があります。これは複雑で煩雑な場合があります。
  • タイミング: showEvent() 関数は、ウィジェットが表示されたときにのみ呼び出されます。ウィジェットが非表示になった場合は、この関数は呼び出されません。

これらの制限を克服するために、showEvent() 関数の代替方法として以下の方法が考えられます。

layout() 関数を使用する

QMdiArea ウィジェットは、QLayout クラスを継承しているため、layout() 関数を使用して子ウィンドウを配置することができます。layout() 関数は、ウィジェットが表示される前でも後でも呼び出すことができます。

QMdiArea *mdiArea = new QMdiArea;

// 子ウィンドウを作成する
QMdiSubWindow *subWindow1 = mdiArea->addWindow(new QWidget());
QMdiSubWindow *subWindow2 = mdiArea->addWindow(new QWidget());

// QHBoxLayout レイアウトを作成する
QHBoxLayout *layout = new QHBoxLayout;

// 子ウィンドウをレイアウトに追加する
layout->addWidget(subWindow1);
layout->addWidget(subWindow2);

// MdiArea にレイアウトを設定する
mdiArea->setLayout(layout);

カスタムシグナルスロット接続を使用する

QMdiArea ウィジェットは、windowActivated(), windowIconChanged(), windowMinimized(), windowMaximized(), windowClosed() などのシグナルを発行します。これらのシグナルをスロットに接続して、子ウィンドウの状態に応じて処理を行うことができます。

QMdiArea *mdiArea = new QMdiArea;

// 子ウィンドウを作成する
QMdiSubWindow *subWindow1 = mdiArea->addWindow(new QWidget());
QMdiSubWindow *subWindow2 = mdiArea->addWindow(new QWidget());

// windowActivated シグナルをスロットに接続する
connect(mdiArea, &QMdiArea::windowActivated, this, &MyClass::onWindowActivated);

void MyClass::onWindowActivated(QMdiSubWindow *subWindow)
{
    // アクティブになった子ウィンドウを処理する
    if (subWindow == subWindow1) {
        // サブウィンドウ 1 がアクティブになった場合の処理
    } else if (subWindow == subWindow2) {
        // サブウィンドウ 2 がアクティブになった場合の処理
    }
}

タイマーを使用する

QMdiArea ウィジェットが表示された後、一定時間後にタイマーを起動して、子ウィンドウを配置することができます。

QMdiArea *mdiArea = new QMdiArea;

// 子ウィンドウを作成する
QMdiSubWindow *subWindow1 = mdiArea->addWindow(new QWidget());
QMdiSubWindow *subWindow2 = mdiArea->addWindow(new QWidget());

// タイマーを作成する
QTimer *timer = new QTimer;

// タイマーがタイムアウトしたときにスロットを呼び出す
connect(timer, &QTimer::timeout, this, &MyClass::onTimeout);

// タイマーを 1 秒後に起動する
timer->start(1000);

void MyClass::onTimeout()
{
    // 子ウィンドウを配置する
    mdiArea->tileSubWindows();
}

QLayoutEngine を使用する

QMdiArea ウィジェットは、QLayoutEngine クラスを使用して子ウィンドウを配置することができます。QLayoutEngine クラスは、より柔軟な配置オプションを提供します。

QMdiArea *mdiArea = new QMdiArea;

// 子ウィンドウを作成する
QMdiSubWindow *subWindow1 = mdiArea->addWindow(new QWidget());
QMdiSubWindow *subWindow2 = mdiArea->addWindow(new QWidget());

// SpringLayout レイアウトエンジンを作成する
QSpringLayout *layout = new QSpringLayout;

// 子ウィンドウをレイアウトに追加する
layout->addWidget(subWindow1);
layout->addWidget(subWindow2);

// MdiArea にレイアウトエンジンを設定する