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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Qt

QtはGUIプログラムの開発で広く使われているクロスプラットフォーム開発のフレームワークです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

PyCharm

エディター・開発ツール

Q&A

解決済

2回答

7794閲覧

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

sponge_yukari

総合スコア12

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Qt

QtはGUIプログラムの開発で広く使われているクロスプラットフォーム開発のフレームワークです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

PyCharm

エディター・開発ツール

0グッド

0クリップ

投稿2018/08/21 08:46

編集2018/08/22 00:42

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

やりたいこと

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

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

作成したコード

python

1#ファイル名:TestStartUp.py 2 3#ファイル実行用 4import sys 5from PyQt5 import QtCore as Qc, QtGui as Qg, QtWidgets as Qw 6from PyQt5.QtCore import Qt 7 8from Controller.TestController import TestControll 9 10if __name__ == '__main__': 11 app = Qw.QApplication(sys.argv) 12 TestControll().showWindow()

python

1#ファイル名:TestController.py 2 3#画面の起動・終了をまとめるクラス 4import sys 5from PyQt5 import QtCore as Qc, QtGui as Qg, QtWidgets as Qw 6from PyQt5.QtCore import Qt 7from .screen import TestMainWindow, TestWindow_or, TestWindow 8 9class TestControll(Qw.QMainWindow): 10 # 初期設定 11 def __init__(self, parent=None): 12 super().__init__(parent) 13 print(self.__class__.__name__ + "のinit") 14 15 self.ui = TestMainWindow.Ui_MainWindow() 16 self.ui.setupUi(self) 17 18 #サブウィンドウ 19 self.test = Qw.QWidget() 20 21 #1.サブウィンドウを上書きする 22 self.test.ui = TestWindow_or.FormOverride() 23 self.test.ui.eventMethod(self.test, """問題点 入れる値がわからない""") 24 25 #2.サブウィンドウを上書きせずに表示する 26 #self.test.ui = TestWindow.Ui_Form() 27 #self.test.ui.setupUi(self.test) 28 29 # 画面起動用 30 def showWindow(self): 31 print(self.__class__.__name__ + "起動") 32 app = Qw.QApplication([]) 33 self.show() 34 self.test.show() 35 sys.exit(app.exec())

Python

1#ファイル名:TestWindow_or.py 2 3#画面情報を継承してイベントを埋め込むためのクラス 4from . import TestWindow 5 6class FormOverride(TestWindow.Ui_Form) : 7 def setupUi(self, From): 8 super().setupUi(From) 9 10 def eventMethod(self, Form, ui): 11 self.setupUi(Form) 12 #ここに上書き内容を記載 13 self.pushButton.clicked.connect(ui.TestLabel.setToolTip("A:サブ画面でクリックされました。")) 14 self.pushButton.clicked.connect(ui.TestLabelinFrame.setToolTip("B:サブ画面でクリックされました。"))

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

python

1#ファイル名:TestMainWindw.py 2 3from PyQt5 import QtCore, QtGui, QtWidgets 4 5class Ui_MainWindow(object): 6 def setupUi(self, MainWindow): 7 MainWindow.setObjectName("MainWindow") 8 MainWindow.resize(510, 359) 9 self.centralwidget = QtWidgets.QWidget(MainWindow) 10 self.centralwidget.setObjectName("centralwidget") 11 self.TestFrame = QtWidgets.QFrame(self.centralwidget) 12 self.TestFrame.setGeometry(QtCore.QRect(20, 60, 321, 181)) 13 self.TestFrame.setFrameShape(QtWidgets.QFrame.StyledPanel) 14 self.TestFrame.setFrameShadow(QtWidgets.QFrame.Raised) 15 self.TestFrame.setObjectName("TestFrame") 16 self.TestLabelinFrame = QtWidgets.QLabel(self.TestFrame) 17 self.TestLabelinFrame.setGeometry(QtCore.QRect(20, 20, 151, 51)) 18 self.TestLabelinFrame.setObjectName("TestLabelinFrame") 19 self.TestLabel = QtWidgets.QLabel(self.centralwidget) 20 self.TestLabel.setGeometry(QtCore.QRect(30, 10, 121, 31)) 21 self.TestLabel.setObjectName("TestLabel") 22 MainWindow.setCentralWidget(self.centralwidget) 23 self.menubar = QtWidgets.QMenuBar(MainWindow) 24 self.menubar.setGeometry(QtCore.QRect(0, 0, 510, 17)) 25 self.menubar.setObjectName("menubar") 26 MainWindow.setMenuBar(self.menubar) 27 self.statusbar = QtWidgets.QStatusBar(MainWindow) 28 self.statusbar.setObjectName("statusbar") 29 MainWindow.setStatusBar(self.statusbar) 30 31 self.retranslateUi(MainWindow) 32 QtCore.QMetaObject.connectSlotsByName(MainWindow) 33 34 def retranslateUi(self, MainWindow): 35 _translate = QtCore.QCoreApplication.translate 36 MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) 37 self.TestLabelinFrame.setText(_translate("MainWindow", "B")) 38 self.TestLabel.setText(_translate("MainWindow", "A"))

python

1#ファイル名:TestWindow.py 2 3from PyQt5 import QtCore, QtGui, QtWidgets 4 5class Ui_Form(object): 6 def setupUi(self, Form): 7 Form.setObjectName("Form") 8 Form.resize(400, 300) 9 self.pushButton = QtWidgets.QPushButton(Form) 10 self.pushButton.setGeometry(QtCore.QRect(20, 20, 75, 23)) 11 self.pushButton.setObjectName("pushButton") 12 13 self.retranslateUi(Form) 14 QtCore.QMetaObject.connectSlotsByName(Form) 15 16 def retranslateUi(self, Form): 17 _translate = QtCore.QCoreApplication.translate 18 Form.setWindowTitle(_translate("Form", "Form")) 19 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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

自己解決

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

Python

1#TestController.py 2import sys 3from PyQt5 import QtCore as Qc, QtGui as Qg, QtWidgets as Qw 4from PyQt5.QtCore import Qt 5 6from .screen import TestMainWindow, TestWindow, TestWindow_or, TestWindow_or2 7from .screen.TestShare import textValues 8 9class TestControll(Qw.QMainWindow): 10 # 初期設定 11 def __init__(self, parent=None): 12 super().__init__(parent) 13 print(self.__class__.__name__ + "のinit") 14 15 self.ui = TestMainWindow.Ui_MainWindow() 16 self.ui.setupUi(self) 17 18 #方法1.画面構成だけ継承 19 self.test = Qw.QWidget() 20 self.test.__init__(None) 21 self.test.ui = TestWindow_or.FormOverride() 22 self.test.ui.setupUi(self.test) 23 24 #画面継承側にあるイベントを設定する 25 self.test.ui.eventMethod(self) 26 27 #方法2.画面構成とWidgetを継承 28 self.test2 = TestWindow_or2.FormOverride2() 29 self.test2.eventMethod(self) 30 31 #ラベルAを変える 32 def labelChangeA(self) : 33 self.ui.TestLabel.setText(textValues.textA) 34 #textValues.textA = "A:サブ画面でクリックされました。" 35 36 #ラベルBを変える 37 def labelChangeB(self) : 38 self.ui.TestLabelinFrame.setText(textValues.textB) 39 #textValues.textB = "B:サブ画面でクリックされました。" 40 41 # 画面起動用 42 def showWindow(self): 43 print(self.__class__.__name__ + "起動") 44 app = Qw.QApplication([]) 45 self.show() 46 self.test.show() 47 self.test2.show() 48 sys.exit(app.exec())

Python

1#TestWindow_or.py 2from PyQt5.QtWidgets import QWidget 3from . import TestWindow, MainForm 4from .TestShare import textValues 5from .. import TestController 6 7class FormOverride(TestWindow.Ui_Form) : 8 def __init__(self): 9 print("FormOverrideのinit") 10 11 def eventMethod(self, main): 12 self.pushButton.clicked.connect(self.changeText) 13 self.pushButton.clicked.connect(main.labelChangeA) 14 15 def changeText(self): 16 if textValues.textA == "A" : 17 textValues.textA = "A:サブ画面でクリックされました。" 18 else : 19 textValues.textA = "A"

追加ファイル

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

Python

1#TestWindow_or2.py 2from PyQt5.QtWidgets import QWidget 3from . import TestWindow 4from .TestShare import textValues 5 6class FormOverride2(QWidget, TestWindow.Ui_Form): 7 def __init__(self, parent=None): 8 super(FormOverride2, self).__init__(parent) 9 self.setupUi(self) 10 11 def eventMethod(self, main): 12 self.pushButton.clicked.connect(self.changeText) 13 self.pushButton.clicked.connect(main.labelChangeB) 14 15 def changeText(self): 16 if textValues.textB == "B" : 17 textValues.textB = "B:サブ画面でクリックされました。" 18 else : 19 textValues.textB = "B"

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

Python

1#TestShare.py 2class textValues() : 3 textA = "A" 4 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のみ)でなくてはならない
・画面内容の変更は外部クラスでやらずに、変更を行うクラス内に処理を書く

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

投稿2018/08/29 06:54

sponge_yukari

総合スコア12

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

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

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

投稿2018/08/21 10:21

t_obara

総合スコア5488

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sponge_yukari

2018/08/22 00:23 編集

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

2018/08/22 00:53

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

2018/08/22 07:20

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問