【PySide6】ウィジェットをクラス化したら全く反応しなくなったときの対処法

# はじめに
~~私向けのメモです。~~

PySide6でウィジェットを別のクラスにした際に発生した不具合の解決方法を記載しています。

ここではメニューバーをクラス化して分けた場合を記載しています。
テキストボックスやメッセージボックスなど、ほかのウィジェットでも応用可能です。

調べても文献が全く見当たらなかったので、試行錯誤して何とかした記録になります。
他の方々にも参考になれば幸いです。

# 環境

* Windows 11 Home
* Python 3.9.9

# 事象
例えば以下の図のようなメニューを作ってみました。
![000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/c0043538-3302-eb65-cafb-f7823a9f1261.png)

これをソースコードにするとこんな感じになります。

```py
# coding: shift-jis
import PySide6
from PySide6.QtGui import (QAction)
from PySide6.QtWidgets import (QApplication,
                               QMenuBar,
                               QWidget)
import os
import sys


class MainWindow(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        
        self.resize(300, 200)
        
        self.SetMenubar()
        
    # メニューバーを生成
    def SetMenubar(self):
        menubar = QMenuBar(self)
        
        # ファイル(F)
        fileMenu = menubar.addMenu("ファイル(&F)")
        
        # ファイル(F) → 新規(N)
        fileNew = QAction("新規(&N)", self)
        fileNew.setShortcut("Ctrl+N")
        fileNew.triggered.connect(self.FileNew)
        fileMenu.addAction(fileNew)
        
        # ファイル(F) → 終了(X)
        fileExit = QAction("終了(&X)", self)
        fileExit.setShortcut("Ctrl+Q")
        fileExit.triggered.connect(self.FileExit)
        fileMenu.addAction(fileExit)
        
    # ファイル(F) → 新規(N) をクリックしたとき
    def FileNew(self):
        # このメソッド名を表示するだけ
        print(sys._getframe().f_code.co_name)
        
    # ファイル(F) → 終了(X) をクリックしたとき
    def FileExit(self):
        # このメソッド名を表示するだけ
        print(sys._getframe().f_code.co_name)


if __name__ == "__main__":
    # 環境変数にPySide6を登録
    dirname = os.path.dirname(PySide6.__file__)
    pluginPath = os.path.join(dirname, 'plugins', 'platforms')
    os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = pluginPath
    
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())
```

このプログラムを実行すると以下のように、各メニュー項目をクリックしたらそれぞれの機能が動きました。
![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/cbc4eb9b-b025-9f52-b673-1e48361edb10.png)

ですがこのまま`MainWindow`クラスに次々とメニューを追加したら、クラスのコードが非常に長ったらしくなり見にくくなっちゃいます。

そこでメニュー項目は別のクラスとして`Menubar`に分けてみました。
そのソースコードがこちら。

```py
# coding: shift-jis
import PySide6
from PySide6.QtGui import (QAction)
from PySide6.QtWidgets import (QApplication,
                               QMenuBar,
                               QWidget)
import os
import sys


# メニューバーの処理が多くなったから、別クラスに分けた
class Menubar:
    def __init__(self, mainwindow, menubar):
        # ファイル(F)
        fileMenu = menubar.addMenu("ファイル(&F)")
        
        # ファイル(F) → 新規(N)
        fileNew = QAction("新規(&N)", mainwindow)
        fileNew.setShortcut("Ctrl+N")
        fileNew.triggered.connect(self.FileNew)
        fileMenu.addAction(fileNew)
        
        # ファイル(F) → 終了(X)
        fileExit = QAction("終了(&X)", mainwindow)
        fileExit.setShortcut("Ctrl+Q")
        fileExit.triggered.connect(self.FileExit)
        fileMenu.addAction(fileExit)
        
    # ファイル(F) → 新規(N) をクリックしたとき
    def FileNew(self):
        # このメソッド名を表示するだけ
        print(sys._getframe().f_code.co_name)
        
    # ファイル(F) → 終了(X) をクリックしたとき
    def FileExit(self):
        # このメソッド名を表示するだけ
        print(sys._getframe().f_code.co_name)


class MainWindow(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        
        self.resize(300, 200)
        
        # メニューバーを生成
        menubar = QMenuBar(self)
        Menubar(self, menubar)


if __name__ == "__main__":
    # 環境変数にPySide6を登録
    dirname = os.path.dirname(PySide6.__file__)
    pluginPath = os.path.join(dirname, 'plugins', 'platforms')
    os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = pluginPath
    
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())
```

さぁ実行すっぞ！！と意気揚々とメニューをクリックしたら、何とメニュー項目をクリックしても何も反応がなくなってしまいました。

![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/7a0c0381-9969-67b2-9ff7-a779248b83af.png)


# 解決方法

:::note alert
コメント欄に @shiracamus さんより、ナイスなプログラムを書いていただいております。
正直私の書いたコードより参考になります。

みんな、私の書いた記事じゃなくて、私の記事のコメントを・・・見よう。
:::

<b>
[ナイスなプログラム](https://qiita.com/karakuri-t910/items/341ab2eac23043cf6e56#comment-b80ea2f3370dd0d7af6c)
</b>

この事象は２つの処理を追加するだけで解決します。

① `Menubar`クラスに`QMenuBar`を継承させる。
② `Menubar`クラスの`__init__`メソッド内で、親クラスが誰なのかを教える`super().__init__(parent)`を追加します。

追加したコードがこちらになります。

```py
# coding: shift-jis
import PySide6
from PySide6.QtGui import (QAction)
from PySide6.QtWidgets import (QApplication,
                               QMenuBar,
                               QWidget)
import os
import sys


# メニューバーの処理が多くなったから、別クラスに分けた
class Menubar(QMenuBar):  # ①メニューバーならQMenuBarを継承させる
    def __init__(self, mainwindow, menubar):
        # ②親クラスが誰なのかを教える
        super().__init__(mainwindow)
        
        # ファイル(F)
        fileMenu = menubar.addMenu("ファイル(&F)")
        
        # ファイル(F) → 新規(N)
        fileNew = QAction("新規(&N)", mainwindow)
        fileNew.setShortcut("Ctrl+N")
        fileNew.triggered.connect(self.FileNew)
        fileMenu.addAction(fileNew)
        
        # ファイル(F) → 終了(X)
        fileExit = QAction("終了(&X)", mainwindow)
        fileExit.setShortcut("Ctrl+Q")
        fileExit.triggered.connect(self.FileExit)
        fileMenu.addAction(fileExit)
        
    # ファイル(F) → 新規(N) をクリックしたとき
    def FileNew(self):
        # このメソッド名を表示するだけ
        print(sys._getframe().f_code.co_name)
        
    # ファイル(F) → 終了(X) をクリックしたとき
    def FileExit(self):
        # このメソッド名を表示するだけ
        print(sys._getframe().f_code.co_name)


class MainWindow(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        
        self.resize(300, 200)
        
        # メニューバーを生成
        menubar = QMenuBar(self)
        Menubar(self, menubar)


if __name__ == "__main__":
    # 環境変数にPySide6を登録
    dirname = os.path.dirname(PySide6.__file__)
    pluginPath = os.path.join(dirname, 'plugins', 'platforms')
    os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = pluginPath
    
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())
```

動かしてみると、ちゃんと動きました。
![003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614920/e435970c-6212-2115-c331-2b09b7789272.png)

:::note info
● 「① MenubarクラスにQMenuBarを継承させる。」についてちょっと補足

今回はメニューバーでしたが、テキストボックスならQTextEditを、メッセージボックスならQMessageBoxを継承させます。

(ex 1) QTextEditの場合

    class Textbox(QTextEdit):
        def __init__(self, rootObject):
            super().__init__(rootObject)
            ...(略)

(ex 2) QMessageBoxの場合

    class Messagebox(QMessageBox):
        def __init__(self, rootObject):
            super().__init__(rootObject)
            ...(略)
:::

# 参考

[Flow Layout Example](https://doc.qt.io/qtforpython/examples/example_widgets_layouts_flowlayout.html)