Qt における QStackedWidget を活用したマルチビュー界面の構築

QStackedWidget の概念と役割

Qt フレームワークにおいて、複数の子ウィジェットを同一の矩形領域に配置し、そのうち一つのみを可視状態にするコンテナクラスが QStackedWidget です。このウィジェットは、wizard(ウィザード)形式の手順画面や、タブ切り替えのような多視点アプリケーションを実装する際に頻繁に利用されます。内部では複数のページが重ねて管理されますが、ユーザーに見えるのは常に現在のインデックスに対応する単一のウィジェットのみです。

主な特徴は以下の通りです:

  • ページ管理: 複数の QWidget を子要素として登録し、スタック構造で保持します。
  • 可視制御: 特定の瞬間にアクティブなページは一つのみであり、他は自動的に隠蔽されます。
  • 遷移機制: インデックス指定またはウィジェットポインタを用いて、表示内容を瞬時に切り替え可能です。

主要な API とシグナル

ページ操作および状態監視に必要なメソッドとシグナルは以下の表にまとめられています。

メソッド / シグナル 機能説明
addWidget(QWidget *w) 新しいページをスタック末尾に追加し、そのインデックスを返します。
insertWidget(int index, QWidget *w) 指定されたインデックス位置にページを挿入します。
removeWidget(QWidget *w) スタックからウィジェットを削除します(オブジェクト自体は削除されないため、必要に応じて delete が必要です)。
currentIndex() / currentWidget() 現在表示中のページインデックス、またはウィジェットポインタを取得します。
setCurrentIndex(int index) インデックスを指定してページを切り替えます。
setCurrentWidget(QWidget *w) ウィジェットポインタを指定してページを切り替えます。
currentChanged(int index) シグナル: 表示ページが変更された際に発行されます。ページ遷移時の初期化処理などに利用します。

ページ切り替えを検知するためのシグナル接続例:

QObject::connect(m_stackView, &QStackedWidget::currentChanged, this, [](int idx) {
    qDebug() << "表示ページが変更されました。インデックス:" << idx;
});

実装パターン:ナビゲーション付き画面構成

ここでは、左側にナビゲーションリスト、右側にコンテンツ領域を持つ構成を例に、コードによる実装手順を示します。Qt Designer を使用せず、すべてプログラム上で構築するアプローチを採用します。

1. クラス設計(ヘッダーファイル)

メインウィンドウクラス StackedWindow を定義し、スタックウィジェットとナビゲーション用のボタンをメンバ変数として保持します。

#include <QMainWindow>
#include <QStackedWidget>
#include <QPushButton>
#include <QVBoxLayout>

class StackedWindow : public QMainWindow {
    Q_OBJECT
public:
    explicit StackedWindow(QWidget *parent = nullptr);

private slots:
    void onNavigateDashboard();
    void onNavigateSettings();
    void onNavigateProfile();

private:
    void setupInterface();
    QWidget* createDashboardView();
    QWidget* createSettingsView();
    QWidget* createProfileView();

    QStackedWidget *m_stackView;
    QPushButton *m_btnDashboard;
    QPushButton *m_btnSettings;
    QPushButton *m_btnProfile;
};

2. 界面構築とロジック(ソースファイル)

コンストラクタ内で UI 要素を初期化し、レイアウトを整えます。ボタンの配置には move() ではなくレイアウトマネージャを使用し、保守性を高めています。

#include "stackedwindow.h"
#include <QHBoxLayout>
#include <QWidget>

StackedWindow::StackedWindow(QWidget *parent) : QMainWindow(parent) {
    setupInterface();
}

void StackedWindow::setupInterface() {
    // コンテナの作成
    m_stackView = new QStackedWidget(this);
    setCentralWidget(m_stackView);

    // 各ページの生成と登録
    m_stackView->addWidget(createDashboardView());
    m_stackView->addWidget(createSettingsView());
    m_stackView->addWidget(createProfileView());

    // ナビゲーションボタンの作成
    m_btnDashboard = new QPushButton("Dashboard");
    m_btnSettings = new QPushButton("Settings");
    m_btnProfile = new QPushButton("Profile");

    // ボタン配置用レイアウト
    QHBoxLayout *navLayout = new QHBoxLayout();
    navLayout->addWidget(m_btnDashboard);
    navLayout->addWidget(m_btnSettings);
    navLayout->addWidget(m_btnProfile);
    navLayout->addStretch();

    // メインレイアウト(ボタン上部、スタック下部)
    QWidget *central = new QWidget();
    QVBoxLayout *mainLayout = new QVBoxLayout(central);
    mainLayout->addLayout(navLayout);
    mainLayout->addWidget(m_stackView);
    
    setCentralWidget(central);

    // シグナルとスロットの接続
    connect(m_btnDashboard, &QPushButton::clicked, this, &StackedWindow::onNavigateDashboard);
    connect(m_btnSettings, &QPushButton::clicked, this, &StackedWindow::onNavigateSettings);
    connect(m_btnProfile, &QPushButton::clicked, this, &StackedWindow::onNavigateProfile);
}

// 各ページの生成ヘルパー
QWidget* StackedWindow::createDashboardView() {
    QWidget *page = new QWidget();
    page->setStyleSheet("background-color: #E3F2FD;");
    return page;
}

QWidget* StackedWindow::createSettingsView() {
    QWidget *page = new QWidget();
    page->setStyleSheet("background-color: #E8F5E9;");
    return page;
}

QWidget* StackedWindow::createProfileView() {
    QWidget *page = new QWidget();
    page->setStyleSheet("background-color: #FFF3E0;");
    return page;
}

// ナビゲーション処理
void StackedWindow::onNavigateDashboard() { m_stackView->setCurrentIndex(0); }
void StackedWindow::onNavigateSettings() { m_stackView->setCurrentIndex(1); }
void StackedWindow::onNavigateProfile() { m_stackView->setCurrentIndex(2); }

3. エントリーポイント

アプリケーション起動用の main 関数です。

#include <QApplication>
#include "stackedwindow.h"

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    StackedWindow window;
    window.resize(640, 480);
    window.show();
    return app.exec();
}

応用:リストウィジェットとの連携

ボタンではなく QListWidget をサイドバーとして利用し、項目選択と同時にページを切り替える実装も一般的です。これにより、項目数が多い場合でも柔軟なナビゲーションが可能になります。

QListWidget *sidebar = new QListWidget();
sidebar->addItems({"Home", "Configuration", "User Info"});

// リストの選択変更をスタックのインデックス変更と直結
connect(sidebar, &QListWidget::currentRowChanged, m_stackView, &QStackedWidget::setCurrentIndex);

// レイアウトに追加(左側にサイドバー、右側にスタック)
QHBoxLayout *layout = new QHBoxLayout();
layout->addWidget(sidebar);
layout->addWidget(m_stackView);

動的なページ管理が必要な場合は、addWidget で返されるインデックスを保持し、必要に応じて removeWidget を呼び出してメモリ解放を行うことで、ランタイムでの界面構成変更に対応できます。

タグ: qt-framework QStackedWidget c-plus-plus gui-design widget-container

6月4日 17:55 投稿