Qt Widgets:タブが移動した時に知りたい!QTabBar::tabMoved()のしくみと応用例


シグナルの引数

tabMoved() シグナルは、2 つの整数を引数として渡します。

  • to: 移動後のタブのインデックス
  • from: 移動前のタブのインデックス

シグナルの接続

tabMoved() シグナルをスロットに接続するには、QObject::connect() 関数を使用します。以下の例は、tabMoved() シグナルを mySlot() スロットに接続する方法を示しています。

connect(tabBar, &QTabBar::tabMoved, this, &MyClass::mySlot);

スロットの実装

mySlot() スロットは、fromto の引数を受け取ります。これらの引数を使用して、移動されたタブと新しい位置に関する情報を取得できます。以下の例は、移動されたタブのインデックスと新しい位置をログに記録する方法を示しています。

void MyClass::mySlot(int from, int to)
{
    qDebug() << "Tab" << from << "moved to" << to;
}

使用例

tabMoved() シグナルは、さまざまな目的に使用できます。たとえば、次のような用途に使用できます。

  • タブの移動を制限する
  • タブの移動履歴を記録する
  • タブの新しい位置に基づいてウィジェットを更新する
  • tabMoved() シグナルは、タブが削除された場合に発生しません。タブが削除された場合は、tabRemoved() シグナルが発生します。
  • tabMoved() シグナルは、タブがプログラムで移動された場合にのみ発生します。ユーザーがマウスでタブをドラッグした場合は、currentChanged() シグナルが発生します。
  • このシグナルは、デフォルトで有効になっています。無効にするには、setTabBarMovable(false) 関数を呼び出します。
  • このシグナルは、スレッドセーフではありません。スレッドからこのシグナルを接続および発信する場合は、適切な同期メカニズムを使用する必要があります。


#include <QApplication>
#include <QTabBar>

class MyWidget : public QWidget
{
public:
    MyWidget(QWidget *parent = nullptr);

private:
    QTabBar *tabBar;
};

MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
{
    tabBar = new QTabBar(this);
    tabBar->addTab("Tab 1");
    tabBar->addTab("Tab 2");
    tabBar->addTab("Tab 3");

    connect(tabBar, &QTabBar::tabMoved, this, &MyWidget::mySlot);
}

void MyWidget::mySlot(int from, int to)
{
    qDebug() << "Tab" << from << "moved to" << to;
}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MyWidget w;
    w.show();
    return app.exec();
}

このコードを実行すると、次の出力がコンソールに表示されます。

Tab 0 moved to 2

これは、Tab 1 が当初はインデックス 0 にありましたが、インデックス 2 に移動されたことを意味します。

以下のコードは、tabMoved() シグナルを使用して、移動されたタブに基づいてウィジェットを更新する方法を示しています。

#include <QApplication>
#include <QTabBar>
#include <QLabel>

class MyWidget : public QWidget
{
public:
    MyWidget(QWidget *parent = nullptr);

private:
    QTabBar *tabBar;
    QLabel *label;
};

MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
{
    tabBar = new QTabBar(this);
    tabBar->addTab("Tab 1");
    tabBar->addTab("Tab 2");
    tabBar->addTab("Tab 3");

    label = new QLabel(this);
    label->setText("Tab 1");

    connect(tabBar, &QTabBar::tabMoved, this, &MyWidget::mySlot);
}

void MyWidget::mySlot(int from, int to)
{
    label->setText(tabBar->tabText(to));
}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MyWidget w;
    w.show();
    return app.exec();
}

このコードを実行すると、Tab 1 をインデックス 2 に移動すると、label のテキストが Tab 3 に更新されます。



currentChanged() シグナルを使用する

currentChanged() シグナルは、現在のタブが変更されたときに発生するシグナルです。tabMoved() シグナルと異なり、このシグナルは、タブがプログラムで移動された場合だけでなく、ユーザーがマウスでタブをドラッグした場合にも発生します。

ただし、currentChanged() シグナルは、タブが移動されたかどうかを判断するのに役立ちません。新しい現在のタブが移動されたタブかどうかを確認するには、追加の処理を行う必要があります。

connect(tabBar, &QTabBar::currentChanged, this, &MyWidget::mySlot);

void MyWidget::mySlot(int index)
{
    if (index != previousIndex) {
        // タブが移動された可能性があります
        // 移動されたかどうかを確認するために追加の処理を行う
    }

    previousIndex = index;
}

tabBar->currentIndex() をポーリングする

tabBar->currentIndex() 関数は、現在のタブのインデックスを返します。この関数を定期的にポーリングすることで、タブが移動されたかどうかを検出できます。

ただし、この方法は CPU 負荷が高くなる可能性があります。特に、タブが頻繁に移動される場合は、ポーリング間隔を短くする必要があるためです。

void MyWidget::timerEvent(QTimerEvent *event)
{
    int currentIndex = tabBar->currentIndex();

    if (currentIndex != previousIndex) {
        // タブが移動されました
    }

    previousIndex = currentIndex;
}

カスタムシグナルを作成する

独自のシグナルを作成して、タブが移動されたときに発生するようにすることができます。この方法は、より柔軟な制御を提供しますが、追加のコーディングとメンテナンスが必要になります。

class MyTabBar : public QTabBar
{
public:
    signal void tabMoved(int from, int to);

protected:
    void mouseMoveEvent(QMouseEvent *event) override
    {
        int fromIndex = currentIndex();
        int toIndex = tabAt(event->pos());

        if (fromIndex != toIndex) {
            emit tabMoved(fromIndex, toIndex);
        }

        QTabBar::mouseMoveEvent(event);
    }
};

QObject::connect() 関数の "QueuedConnection" を使用する

QObject::connect() 関数の QueuedConnection 型を使用すると、シグナルがスレッドのイベントループにキューイングされるまで接続を延期できます。これにより、スレッドセーフな方法で tabMoved() シグナルを接続できます。

connect(tabBar, &QTabBar::tabMoved, this, &MyWidget::mySlot, Qt::QueuedConnection);

QTabBar::tabMoved() シグナルは、多くの場合、タブが移動されたときに発生するイベントを処理するための便利な方法ですが、常に最適なソリューションとは限りません。状況によっては、上記に示した代替方法を検討する必要があります。