質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.07%

PythonのGUIクラスを継承して、サブクラスでイベント等を追記したい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,172

sponge_yukari

score 12

こんにちわ
PythonのGUIに関する内容で質問です。

 やりたいこと

1.GUIに関する記述を行ったクラスを継承したサブクラスで、ボタン等の処理内容を書きたい
2.上記の条件に加えて、サブ画面側からボタンを押してメイン画面の表示を変えたい
・今回の内容は [サブ画面でボタンを押す ⇒ メイン画面のラベル内容を変える] であり、GUIの部品名で書くと
[TestWindow(サブウィンドウ)のpushButtonを押す ⇒ TestMainWindow(メインウィンドウ)のTestLabelとTestLabelinFrameのテキストが変化]です。
図

GUIはQtDesignerで作成後、pyファイルに変換しています。
GUIメンテナンスの手間を考えて、ほとんどこのファイルをいじらずに継承とオーバーライドでどうにか済ませたいです。

 作成したコード

#ファイル名:TestStartUp.py

#ファイル実行用
import sys
from PyQt5 import QtCore as Qc, QtGui as Qg, QtWidgets as Qw
from PyQt5.QtCore import Qt

from Controller.TestController import TestControll

if __name__ == '__main__':
    app = Qw.QApplication(sys.argv)
    TestControll().showWindow()
#ファイル名:TestController.py

#画面の起動・終了をまとめるクラス
import sys
from PyQt5 import QtCore as Qc, QtGui as Qg, QtWidgets as Qw
from PyQt5.QtCore import Qt
from .screen import TestMainWindow, TestWindow_or, TestWindow

class TestControll(Qw.QMainWindow):
    # 初期設定
    def __init__(self, parent=None):
        super().__init__(parent)
        print(self.__class__.__name__ + "のinit")

        self.ui = TestMainWindow.Ui_MainWindow()
        self.ui.setupUi(self)

        #サブウィンドウ
        self.test = Qw.QWidget()

        #1.サブウィンドウを上書きする
        self.test.ui = TestWindow_or.FormOverride()
        self.test.ui.eventMethod(self.test, """問題点 入れる値がわからない""")

        #2.サブウィンドウを上書きせずに表示する
        #self.test.ui = TestWindow.Ui_Form()
        #self.test.ui.setupUi(self.test)

    # 画面起動用
    def showWindow(self):
        print(self.__class__.__name__ + "起動")
        app = Qw.QApplication([])
        self.show()
        self.test.show()
        sys.exit(app.exec())
#ファイル名:TestWindow_or.py

#画面情報を継承してイベントを埋め込むためのクラス
from . import TestWindow

class FormOverride(TestWindow.Ui_Form) :
    def setupUi(self, From):
        super().setupUi(From)

    def eventMethod(self, Form, ui):
        self.setupUi(Form)
        #ここに上書き内容を記載
        self.pushButton.clicked.connect(ui.TestLabel.setToolTip("A:サブ画面でクリックされました。"))
        self.pushButton.clicked.connect(ui.TestLabelinFrame.setToolTip("B:サブ画面でクリックされました。"))

 ここからはQt Designerで作成・変換したソース(GUI、画面構成)

#ファイル名:TestMainWindw.py

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(510, 359)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.TestFrame = QtWidgets.QFrame(self.centralwidget)
        self.TestFrame.setGeometry(QtCore.QRect(20, 60, 321, 181))
        self.TestFrame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.TestFrame.setFrameShadow(QtWidgets.QFrame.Raised)
        self.TestFrame.setObjectName("TestFrame")
        self.TestLabelinFrame = QtWidgets.QLabel(self.TestFrame)
        self.TestLabelinFrame.setGeometry(QtCore.QRect(20, 20, 151, 51))
        self.TestLabelinFrame.setObjectName("TestLabelinFrame")
        self.TestLabel = QtWidgets.QLabel(self.centralwidget)
        self.TestLabel.setGeometry(QtCore.QRect(30, 10, 121, 31))
        self.TestLabel.setObjectName("TestLabel")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 510, 17))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.TestLabelinFrame.setText(_translate("MainWindow", "B"))
        self.TestLabel.setText(_translate("MainWindow", "A"))
#ファイル名:TestWindow.py

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(400, 300)
        self.pushButton = QtWidgets.QPushButton(Form)
        self.pushButton.setGeometry(QtCore.QRect(20, 20, 75, 23))
        self.pushButton.setObjectName("pushButton")

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.pushButton.setText(_translate("Form", "PushButton"))

 ファイル構成

TestStartUp.py
Controller
| ∟ screen
|  ∟TestMainWindow.py
|  ∟TestWindow.py
|  ∟TestWindow_or.py
∟TestController.py

 問題点

問題となっている箇所は、TestControllerファイルのTestControllクラス内のself.test.ui.eventMethod・・・
この部分の2つ目の引数に、このクラス内にあるUIの情報を渡してeventMethod内でリンクさせるようにしたいのですが、
'self.ui'では[TypeError: argument 1 has unexpected type 'NoneType']と出てしまい、画面が立ち上がりませんでした。

 試したこと

'TestController.py'ファイルの1.の内容2行ともコメントアウトして、その下の2.の内容を有効にしたところ画面は起動しました。
もちろん、イベント等入っていないので動作はありません。

 補足情報(FW/ツールのバージョンなど)

QtDesigner 5.9.5
Python 3.6.5
PyQt5-5.11.2
PyCharm 2018.2.1

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

check解決した方法

0

以下のように変更することで解決しました。

#TestController.py
import sys
from PyQt5 import QtCore as Qc, QtGui as Qg, QtWidgets as Qw
from PyQt5.QtCore import Qt

from .screen import TestMainWindow, TestWindow, TestWindow_or, TestWindow_or2
from .screen.TestShare import textValues

class TestControll(Qw.QMainWindow):
    # 初期設定
    def __init__(self, parent=None):
        super().__init__(parent)
        print(self.__class__.__name__ + "のinit")

        self.ui = TestMainWindow.Ui_MainWindow()
        self.ui.setupUi(self)

        #方法1.画面構成だけ継承
        self.test = Qw.QWidget()
        self.test.__init__(None)
        self.test.ui = TestWindow_or.FormOverride()
        self.test.ui.setupUi(self.test)

        #画面継承側にあるイベントを設定する
        self.test.ui.eventMethod(self)

        #方法2.画面構成とWidgetを継承
        self.test2 = TestWindow_or2.FormOverride2()
        self.test2.eventMethod(self)

    #ラベルAを変える
    def labelChangeA(self) :
        self.ui.TestLabel.setText(textValues.textA)
        #textValues.textA = "A:サブ画面でクリックされました。"

    #ラベルBを変える
    def labelChangeB(self) :
        self.ui.TestLabelinFrame.setText(textValues.textB)
        #textValues.textB = "B:サブ画面でクリックされました。"

    # 画面起動用
    def showWindow(self):
        print(self.__class__.__name__ + "起動")
        app = Qw.QApplication([])
        self.show()
        self.test.show()
        self.test2.show()
        sys.exit(app.exec())
#TestWindow_or.py
from PyQt5.QtWidgets import QWidget
from . import TestWindow, MainForm
from .TestShare import textValues
from .. import TestController

class FormOverride(TestWindow.Ui_Form) :
    def __init__(self):
        print("FormOverrideのinit")

    def eventMethod(self, main):
        self.pushButton.clicked.connect(self.changeText)
        self.pushButton.clicked.connect(main.labelChangeA)

    def changeText(self):
        if textValues.textA == "A" :
            textValues.textA = "A:サブ画面でクリックされました。"
        else :
            textValues.textA = "A"

 追加ファイル

ついでに違う形の方法も見つけたので載せておきます。

#TestWindow_or2.py
from PyQt5.QtWidgets import QWidget
from . import TestWindow
from .TestShare import textValues

class FormOverride2(QWidget, TestWindow.Ui_Form):
    def __init__(self, parent=None):
        super(FormOverride2, self).__init__(parent)
        self.setupUi(self)

    def eventMethod(self, main):
        self.pushButton.clicked.connect(self.changeText)
        self.pushButton.clicked.connect(main.labelChangeB)

    def changeText(self):
        if textValues.textB == "B" :
            textValues.textB = "B:サブ画面でクリックされました。"
        else :
            textValues.textB = "B"

それとconnectの際に設定できる関数が()のものだけだったので、数値共有用のクラスを用意しました。

#TestShare.py
class textValues() :
    textA = "A"
    textB = "B"

ファイル構成

TestStartUp.py
Controller
| ∟ screen
|  ∟TestMainWindow.py
|  ∟TestWindow.py
|  ∟TestWindow_or.py
|  ∟TestWindow_or2.py
|  ∟TestShare.py
∟TestController.py

前の表記とは少し変わってしまって問題点がいまいちはっきりしませんでしたが、わかってる点で挙げてみると
・self.test = Qw.QWidget()と、self.test.init(None)はセットで存在しないとインスタンスができない(?)
そのまま進めると、ui.の処理ができない
・connectの際に指定する関数は()なしのもの(引数がselfのみ)でなくてはならない
・画面内容の変更は外部クラスでやらずに、変更を行うクラス内に処理を書く

引っかかったところはこんな感じでした。
別の方法もあるかもしれませんが、思いつかないのでこれでいきます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

エラー内容は、FormOverride::eventMethodの引数が少ないと言う指摘ですよね?
提示されたコードでは、Formとuiを必要としています。

"""問題点 入れる値がわからない""")
とありますが、画面構成や依存関係がわからないので、そもそもeventMethodにどのような引数が必要なのか指摘できませんが、処理をするのに必要なものを渡す必要があります。

self.test.ui.eventMethod(self.test, self.ui)
かな?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/08/22 09:21 編集

    わかりにくい部分があったので追記しました。
    >self.test.ui.eventMethod(self.test, self.ui)
    についてですが、これは本文中にある通り、これを流した結果エラーメッセージが出ております。
    私もこれ以外ないと思うのですがどうしてもエラーが取れないため行き詰っております。

    キャンセル

  • 2018/08/22 09:53

    なるほど、そのタイミングで与えたパラメータの内容を印字してみては?
    インスタンスは作ったつもりでうまく生成できていないとか、
    ウィンドウ用のメインループを回さないとウィンドウ系のデータがうまく設定されないとかありそうです。

    キャンセル

  • 2018/08/22 16:20

    printやデバッグで確認してみましたが、
    self.test = Qw.QWidget()
    self.test.ui = TestWindow_or.FormOverride()
    の段階で中身はあるように思えます。
    GUI部品の定義が親クラスにあるのが原因かもしれないので少し調べてみます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.07%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る