3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-12-28

はじめに

私向けのメモです。

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

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

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

環境

  • Windows 11 Home
  • Python 3.9.9

事象

例えば以下の図のようなメニューを作ってみました。
000.png

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

# 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

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

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

# 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

解決方法

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

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

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

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

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

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

# 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

● 「① 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

3
5
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?