Qtプログラミング:QTabWidgetのtabsClosableを使いこなすためのヒントとテクニック

2025-05-27

より詳しく説明すると以下のようになります。

  • 利用シーン
    タブを動的に追加・削除できるようなアプリケーションで、ユーザーが不要になったタブを自分で閉じたい場合に便利です。例えば、複数のドキュメントをタブで開くテキストエディタや、複数のウェブページをタブで表示するブラウザなどが考えられます。

  • 設定
    このプロパティの値は、以下のいずれかの方法で設定できます。

    • Qt Designer
      QtのGUIデザインツールであるQt Designerを使用している場合、QTabWidget を選択し、プロパティエディタの中で tabsClosable のチェックボックスをオンまたはオフにすることで設定できます。
    • プログラミング (C++またはPythonなど)
      コードの中で、QTabWidget オブジェクトの setTabsClosable(true) または setTabsClosable(false) というメソッドを呼び出すことで設定できます。
  • 機能
    tabsClosable プロパティが true (真) に設定されている場合、QTabWidget の各タブの横に閉じるボタンが表示されます。ユーザーはこのボタンをクリックすることで、対応するタブを閉じることができます。

  • tabsClosable (タブズ・クローザブル)
    これは QTabWidget クラスが持つプロパティ(設定)の名前です。「closable」は「閉じることができる」という意味ですね。

  • QTabWidget (キュウ・タブ・ウィジェット)
    これは、複数の「ページ」をタブで切り替えて表示するためのQtの基本的なGUIコンポーネントです。ウェブブラウザのタブや、多くのアプリケーションの設定画面などでよく見られます。



  1. 閉じるボタンが表示されない

    • 原因
      tabsClosable プロパティが false に設定されている可能性があります。
    • 解決策
      コード内で setTabsClosable(true) を呼び出しているか、Qt Designerで tabsClosable のチェックボックスがオンになっているかを確認してください。
    • 原因
      QTabWidget がレイアウトマネージャーによって適切に配置されていない場合、表示が崩れて閉じるボタンが見えないことがあります。
    • 解決策
      QTabWidget が適切なレイアウト(QVBoxLayout, QHBoxLayout, QGridLayout など)に追加されていることを確認し、必要に応じてレイアウトの設定(スペーサーの追加など)を見直してください。
  2. 閉じるボタンをクリックしてもタブが閉じられない

    • 原因
      tabCloseRequested シグナルに適切なスロット(処理関数)が接続されていない可能性があります。tabsClosabletrue に設定しただけでは、自動的にタブが閉じられるわけではありません。閉じるボタンがクリックされたときに、どのタブを閉じるかを指示する処理を自分で実装する必要があります。
    • 解決策
      QTabWidgettabCloseRequested(int index) シグナルを、タブを実際に閉じる処理を行うスロットに接続してください。スロット側では、引数として渡されるインデックス (index) を使って、removeTab(index) メソッドを呼び出すことでタブを閉じます。
    // 例 (C++)
    connect(tabWidget, &QTabWidget::tabCloseRequested, this, &MyWidget::closeTab);
    
    void MyWidget::closeTab(int index) {
        tabWidget->removeTab(index);
    }
    
    # 例 (Python - PyQt/PySide)
    self.tab_widget.tabCloseRequested.connect(self.close_tab)
    
    def close_tab(self, index):
        self.tab_widget.removeTab(index)
    
  3. 誤ったタブが閉じられる

    • 原因
      tabCloseRequested シグナルから渡されるインデックスを正しく処理していない可能性があります。タブの追加や削除の順序が変わると、インデックスも変わるため、常に正しいインデックスを使用するように注意が必要です。
    • 解決策
      tabCloseRequested シグナルから渡されるインデックスをそのまま removeTab() に渡すようにしてください。タブの識別にインデックス以外の情報(例えば、タブに関連付けられたデータ)を使用している場合は、その情報を基に正しいタブを特定してから閉じるように実装する必要があります。
  4. 最後のタブを閉じようとするとエラーが発生する、または予期しない動作をする

    • 原因
      アプリケーションの設計によっては、最後のタブを閉じさせたくない場合があります。しかし、tabCloseRequested シグナルを処理するスロット内で、最後のタブを閉じる処理を無条件に実行してしまうと、問題が発生する可能性があります。
    • 解決策
      tabCloseRequested スロット内で、現在のタブの数を確認し、最後のタブの場合は閉じる処理を行わないように条件分岐を追加してください。
    // 例 (C++)
    void MyWidget::closeTab(int index) {
        if (tabWidget->count() > 1) {
            tabWidget->removeTab(index);
        } else {
            // 最後のタブを閉じようとした場合の処理 (例: メッセージ表示、何もしないなど)
            qDebug() << "最後のタブは閉じられません。";
        }
    }
    
    # 例 (Python - PyQt/PySide)
    def close_tab(self, index):
        if self.tab_widget.count() > 1:
            self.tab_widget.removeTab(index)
        else:
            # 最後のタブを閉じようとした場合の処理
            print("最後のタブは閉じられません。")
    
  5. タブに独自のウィジェットを設定している場合に、閉じる処理でリソースリークが発生する

    • 原因
      removeTab() を呼び出すだけでは、タブに設定されていたカスタムウィジェットが適切に削除されない場合があります。特に、動的に割り当てたメモリやリソースが解放されないと、メモリリークにつながる可能性があります。
    • 解決策
      removeTab(index) を呼び出す前に、widget(index) メソッドでタブのウィジェットを取得し、deleteLater() を呼び出して安全に削除することを検討してください。
    // 例 (C++)
    void MyWidget::closeTab(int index) {
        QWidget* widgetToRemove = tabWidget->widget(index);
        tabWidget->removeTab(index);
        widgetToRemove->deleteLater();
    }
    
    # 例 (Python - PyQt/PySide)
    def close_tab(self, index):
        widget_to_remove = self.tab_widget.widget(index)
        self.tab_widget.removeTab(index)
        widget_to_remove.deleteLater()
    


基本的な例:閉じるボタンを有効にし、タブが閉じられるようにする

この例では、QTabWidget にいくつかのタブを追加し、tabsClosabletrue に設定して閉じるボタンを表示します。そして、tabCloseRequested シグナルを処理するスロットを接続し、実際にタブを閉じる処理を実装します。

C++ (Qt Widgets)

#include <QApplication>
#include <QMainWindow>
#include <QTabWidget>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
#include <QDebug>

class MainWindow : public QMainWindow {
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        centralWidget = new QWidget;
        setCentralWidget(centralWidget);

        layout = new QVBoxLayout(centralWidget);

        tabWidget = new QTabWidget;
        tabWidget->setTabsClosable(true); // 閉じるボタンを有効にする

        // タブを追加
        QWidget *tab1 = new QWidget;
        QLabel *label1 = new QLabel("最初のタブ");
        QVBoxLayout *layout1 = new QVBoxLayout(tab1);
        layout1->addWidget(label1);
        tabWidget->addTab(tab1, "タブ 1");

        QWidget *tab2 = new QWidget;
        QLabel *label2 = new QLabel("2番目のタブ");
        QVBoxLayout *layout2 = new QVBoxLayout(tab2);
        layout2->addWidget(label2);
        tabWidget->addTab(tab2, "タブ 2");

        layout->addWidget(tabWidget);

        // tabCloseRequested シグナルをスロットに接続
        connect(tabWidget, &QTabWidget::tabCloseRequested, this, &MainWindow::closeTab);
    }

private slots:
    void closeTab(int index) {
        qDebug() << "閉じようとしているタブのインデックス:" << index;
        tabWidget->removeTab(index);
    }

private:
    QWidget *centralWidget;
    QVBoxLayout *layout;
    QTabWidget *tabWidget;
};

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

#include "main.moc" // mocによるメタオブジェクトコードのインクルード

Python (PyQt)

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QTabWidget, QLabel, QVBoxLayout, QWidget
from PyQt5.QtCore import pyqtSlot

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        self.layout = QVBoxLayout(self.central_widget)

        self.tab_widget = QTabWidget()
        self.tab_widget.setTabsClosable(True) # 閉じるボタンを有効にする

        # タブを追加
        tab1 = QWidget()
        label1 = QLabel("最初のタブ")
        layout1 = QVBoxLayout(tab1)
        layout1.addWidget(label1)
        self.tab_widget.addTab(tab1, "タブ 1")

        tab2 = QWidget()
        label2 = QLabel("2番目のタブ")
        layout2 = QVBoxLayout(tab2)
        layout2.addWidget(label2)
        self.tab_widget.addTab(tab2, "タブ 2")

        self.layout.addWidget(self.tab_widget)

        # tabCloseRequested シグナルをスロットに接続
        self.tab_widget.tabCloseRequested.connect(self.close_tab)

    @pyqtSlot(int)
    def close_tab(self, index):
        print(f"閉じようとしているタブのインデックス: {index}")
        self.tab_widget.removeTab(index)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())

Python (PySide)

import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QTabWidget, QLabel, QVBoxLayout, QWidget
from PySide6.QtCore import Slot

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        self.layout = QVBoxLayout(self.central_widget)

        self.tab_widget = QTabWidget()
        self.tab_widget.setTabsClosable(True) # 閉じるボタンを有効にする

        # タブを追加
        tab1 = QWidget()
        label1 = QLabel("最初のタブ")
        layout1 = QVBoxLayout(tab1)
        layout1.addWidget(label1)
        self.tab_widget.addTab(tab1, "タブ 1")

        tab2 = QWidget()
        label2 = QLabel("2番目のタブ")
        layout2 = QVBoxLayout(tab2)
        layout2.addWidget(label2)
        self.tab_widget.addTab(tab2, "タブ 2")

        self.layout.addWidget(self.tab_widget)

        # tabCloseRequested シグナルをスロットに接続
        self.tab_widget.tabCloseRequested.connect(self.close_tab)

    @Slot(int)
    def close_tab(self, index):
        print(f"閉じようとしているタブのインデックス: {index}")
        self.tab_widget.removeTab(index)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec())

解説

  1. QTabWidget の作成と設定
    QTabWidget のインスタンスを作成し、setTabsClosable(true) を呼び出すことで、各タブに閉じるボタンが表示されるようになります。

  2. タブの追加
    addTab() メソッドを使って、QTabWidget に新しいタブを追加します。通常、タブには表示するウィジェットと、タブに表示するラベル(タイトル)を指定します。

  3. tabCloseRequested シグナルの接続
    QTabWidget が持つ tabCloseRequested(int index) シグナルを、タブを閉じる処理を行うスロット(ここでは closeTab メソッド)に connect() します。このシグナルは、タブの閉じるボタンがクリックされたときに発行されます。引数として、閉じようとしているタブのインデックスが渡されます。

より高度な例:タブを閉じる前に確認ダイアログを表示する

この例では、タブを閉じようとしたときに確認ダイアログを表示し、ユーザーに本当に閉じるかどうかを尋ねます。

C++ (Qt Widgets)

#include <QApplication>
#include <QMainWindow>
#include <QTabWidget>
#include <QLabel>
#include <QVBoxLayout>
#include <QWidget>
#include <QMessageBox>
#include <QDebug>

class MainWindow : public QMainWindow {
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        centralWidget = new QWidget;
        setCentralWidget(centralWidget);

        layout = new QVBoxLayout(centralWidget);

        tabWidget = new QTabWidget;
        tabWidget->setTabsClosable(true);

        // タブを追加 (省略)
        QWidget *tab1 = new QWidget;
        tabWidget->addTab(tab1, "タブ 1");
        QWidget *tab2 = new QWidget;
        tabWidget->addTab(tab2, "タブ 2");

        layout->addWidget(tabWidget);

        connect(tabWidget, &QTabWidget::tabCloseRequested, this, &MainWindow::closeTabRequest);
    }

private slots:
    void closeTabRequest(int index) {
        QMessageBox::StandardButton reply;
        reply = QMessageBox::question(this, "確認", "タブを閉じますか?",
                                      QMessageBox::Yes | QMessageBox::No);
        if (reply == QMessageBox::Yes) {
            qDebug() << "タブを閉じます:" << index;
            tabWidget->removeTab(index);
        } else {
            qDebug() << "タブのクローズをキャンセルしました。";
        }
    }

private:
    QWidget *centralWidget;
    QVBoxLayout *layout;
    QTabWidget *tabWidget;
};

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

#include "main.moc"

Python (PyQt)

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QTabWidget, QMessageBox, QWidget, QVBoxLayout
from PyQt5.QtCore import pyqtSlot

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        self.layout = QVBoxLayout(self.central_widget)

        self.tab_widget = QTabWidget()
        self.tab_widget.setTabsClosable(True)

        # タブを追加 (省略)
        tab1 = QWidget()
        self.tab_widget.addTab(tab1, "タブ 1")
        tab2 = QWidget()
        self.tab_widget.addTab(tab2, "タブ 2")

        self.layout.addWidget(self.tab_widget)

        self.tab_widget.tabCloseRequested.connect(self.close_tab_request)

    @pyqtSlot(int)
    def close_tab_request(self, index):
        reply = QMessageBox.question(self, "確認", "タブを閉じますか?",
                                     QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            print(f"タブを閉じます: {index}")
            self.tab_widget.removeTab(index)
        else:
            print("タブのクローズをキャンセルしました。")

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())

Python (PySide)

import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QTabWidget, QMessageBox, QWidget, QVBoxLayout
from PySide6.QtCore import Slot

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        self.layout = QVBoxLayout(self.central_widget)

        self.tab_widget = QTabWidget()
        self.tab_widget.setTabsClosable(True)

        # タブを追加 (省略)
        tab1 = QWidget()
        self.tab_widget.addTab(tab1, "タブ 1")
        tab2 = QWidget()
        self.tab_widget.addTab(tab2, "タブ 2")

        self.layout.addWidget(self.tab_widget)

        self.tab_widget.tabCloseRequested.connect(self.close_tab_request)

    @Slot(int)
    def close_tab_request(self, index):
        reply = QMessageBox.question(self, "確認", "タブを閉じますか?",
                                     QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            print(f"タブを閉じます: {index}")
            self.tab_widget.removeTab(index)
        else:
            print("タブのクローズをキャンセルしました。")

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec())

解説

  1. tabCloseRequested シグナルに接続するスロットを closeTabRequest に変更しました。

  2. ユーザーが「はい」を選択した場合のみ、removeTab(index) を呼び出してタブを閉じます。それ以外の場合は、タブは閉じられません。



独自の閉じるボタンをタブのウィジェット内に配置する

tabsClosable プロパティを使用せず、各タブのコンテンツとして表示されるウィジェット内に、独自の閉じるボタン(QPushButton など)を配置する方法です。

利点

  • 閉じるボタンの位置をタブバーではなく、タブの内容領域内に配置できる。
  • タブごとに異なる閉じる処理を実装できる。
  • 閉じるボタンのデザインや動作を完全にカスタマイズできる。

欠点

  • タブバーに標準の閉じるボタンが表示されないため、ユーザーによっては操作に戸惑う可能性がある。
  • 各タブのウィジェットに閉じるボタンを個別に追加し、処理を実装する必要があるため、手間がかかる。

C++ (Qt Widgets) の例

#include <QApplication>
#include <QMainWindow>
#include <QTabWidget>
#include <QLabel>
#include <QPushButton>
#include <QHBoxLayout>
#include <QWidget>
#include <QDebug>

class TabWidgetWithCloseButton : public QWidget {
public:
    TabWidgetWithCloseButton(const QString& title, QTabWidget* parentTabWidget)
        : QWidget(parentTabWidget), tabWidget(parentTabWidget), tabTitle(title) {
        QHBoxLayout* layout = new QHBoxLayout(this);
        QLabel* label = new QLabel("これは " + title + " の内容です。");
        QPushButton* closeButton = new QPushButton("閉じる");

        layout->addWidget(label);
        layout->addWidget(closeButton);

        connect(closeButton, &QPushButton::clicked, this, &TabWidgetWithCloseButton::closeTab);
    }

private slots:
    void closeTab() {
        int index = tabWidget->indexOf(this);
        if (index != -1) {
            qDebug() << tabTitle << " を閉じます。インデックス:" << index;
            tabWidget->removeTab(index);
            delete this; // 必要に応じてウィジェットを削除
        }
    }

private:
    QTabWidget* tabWidget;
    QString tabTitle;
};

class MainWindow : public QMainWindow {
public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        QTabWidget* tabWidget = new QTabWidget;

        tabWidget->addTab(new TabWidgetWithCloseButton("タブ A", tabWidget), "タブ A");
        tabWidget->addTab(new TabWidgetWithCloseButton("タブ B", tabWidget), "タブ B");

        setCentralWidget(tabWidget);
    }
};

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

#include "main.moc"

Python (PyQt/PySide) の例

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QTabWidget, QLabel, QPushButton, QHBoxLayout, QWidget
from PyQt5.QtCore import pyqtSlot

class TabWidgetWithCloseButton(QWidget):
    def __init__(self, title, parent_tab_widget):
        super().__init__()
        self.tab_widget = parent_tab_widget
        self.tab_title = title

        layout = QHBoxLayout(self)
        label = QLabel(f"これは {title} の内容です。")
        close_button = QPushButton("閉じる")

        layout.addWidget(label)
        layout.addWidget(close_button)

        close_button.clicked.connect(self.close_tab)

    @pyqtSlot()
    def close_tab(self):
        index = self.tab_widget.indexOf(self)
        if index != -1:
            print(f"{self.tab_title} を閉じます。インデックス: {index}")
            self.tab_widget.removeTab(index)
            self.deleteLater() # 必要に応じてウィジェットを削除

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        tab_widget = QTabWidget()

        tab_widget.addTab(TabWidgetWithCloseButton("タブ A", tab_widget), "タブ A")
        tab_widget.addTab(TabWidgetWithCloseButton("タブ B", tab_widget), "タブ B")

        self.setCentralWidget(tab_widget)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())

カスタムタブバーウィジェットを使用する

QTabWidget のタブバーを、独自のウィジェット(QWidget を継承)で置き換える方法です。これにより、タブの外観や動作、閉じるボタンの表示方法などを完全に制御できます。

利点

  • 標準のタブバーにはない独自の機能を追加できる。
  • タブバーのUI/UXを大幅にカスタマイズできる。

欠点

  • QTabWidget の標準のタブバーの機能(ドラッグ&ドロップによるタブの移動など)を再実装する必要がある場合がある。
  • タブバーの描画やイベント処理などを自分で実装する必要があるため、高度な知識と手間が必要。

この方法は比較的複雑になるため、ここでは概念的な説明に留めます。具体的な実装には、タブの位置計算、描画、マウスイベント処理などが含まれます。

コンテキストメニュー(右クリックメニュー)から閉じる機能を提供する

タブバーを右クリックした際に表示されるコンテキストメニューに「閉じる」などのアクションを追加する方法です。

利点

  • 右クリックという一般的な操作でタブを閉じることができる。
  • 画面上のスペースを節約できる。

欠点

  • 閉じる機能が視覚的にすぐに分からないため、ユーザーによっては発見しにくい可能性がある。

C++ (Qt Widgets) の例

#include <QApplication>
#include <QMainWindow>
#include <QTabWidget>
#include <QLabel>
#include <QVBoxLayout>
#include <QWidget>
#include <QMenu>
#include <QAction>
#include <QPoint>
#include <QDebug>

class MainWindow : public QMainWindow {
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) {
        centralWidget = new QWidget;
        setCentralWidget(centralWidget);

        layout = new QVBoxLayout(centralWidget);

        tabWidget = new QTabWidget;

        // タブを追加 (省略)
        tabWidget->addTab(new QLabel("内容 1"), "タブ 1");
        tabWidget->addTab(new QLabel("内容 2"), "タブ 2");

        tabWidget->setContextMenuPolicy(Qt::CustomContextMenu);
        connect(tabWidget, &QTabWidget::customContextMenuRequested, this, &MainWindow::showContextMenu);

        layout->addWidget(tabWidget);
    }

private slots:
    void showContextMenu(const QPoint& pos) {
        int index = tabWidget->tabBar()->tabAt(pos);
        if (index != -1) {
            QMenu contextMenu(this);
            QAction* closeTabAction = new QAction("閉じる", this);
            connect(closeTabAction, &QAction::triggered, [this, index]() {
                qDebug() << "タブを閉じます (コンテキストメニュー):" << index;
                tabWidget->removeTab(index);
            });
            contextMenu.addAction(closeTabAction);
            contextMenu.exec(tabWidget->tabBar()->mapToGlobal(pos));
        }
    }

private:
    QWidget *centralWidget;
    QVBoxLayout *layout;
    QTabWidget *tabWidget;
};

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

#include "main.moc"
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QTabWidget, QLabel, QVBoxLayout, QWidget, QMenu, QAction
from PyQt5.QtCore import pyqtSlot, Qt, QPoint

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)

        self.layout = QVBoxLayout(self.central_widget)

        self.tab_widget = QTabWidget()

        # タブを追加 (省略)
        self.tab_widget.addTab(QLabel("内容 1"), "タブ 1")
        self.tab_widget.addTab(QLabel("内容 2"), "タブ 2")

        self.tab_widget.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tab_widget.customContextMenuRequested.connect(self.show_context_menu)

        self.layout.addWidget(self.tab_widget)

    @pyqtSlot(QPoint)
    def show_context_menu(self, pos):
        index = self.tab_widget.tabBar().tabAt(pos)
        if index != -1:
            context_menu = QMenu(self)
            close_tab_action = QAction("閉じる", self)
            close_tab_action.triggered.connect(lambda: self.close_tab_from_menu(index))
            context_menu.addAction(close_tab_action)
            context_menu.exec_(self.tab_widget.tabBar().mapToGlobal(pos))

    @pyqtSlot(int)
    def close_tab_from_menu(self, index):
        print(f"タブを閉じます (コンテキストメニュー): {index}")
        self.tab_widget.removeTab(index)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())