🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Python

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

Q&A

解決済

2回答

3454閲覧

Qthread で呼び出したクラス内で別class で定義した変数を使いたい

hachimitu

総合スコア36

Python

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

1グッド

0クリップ

投稿2021/01/30 10:07

#やりたいこと
Qthread で呼び出したProcess_code_text_class内で別class AK003で定義した変数を使いたい
具体的には"Executeボタンが押されたらテキスト内の文字列をprintfで出力したい"

#問題
以下コードだとテキストに文字列を入力しても値が出力されない。
![イメージ説明

python

1# -*- coding: utf-8 -*- 2""" 3Created on Tue Jun 16 19:37:35 2020 4プログラムmemo 5note : Qt desinerでMainWindowでguiを作成したのでUi_Formと異なる 6note: importした.pyファイル上のオブジェクトを呼び出すときはクラス名を付けること 7@author: asahi 8""" 9import os #pathからファイル名取得するときよう 10import sys #システムモジュール 11 12from PyQt5.QtCore import QThread, QTimer, QEventLoop # 追加 13from PyQt5 import QtWidgets,QtCore 14from PyQt5.QtWidgets import QMainWindow 15from PyQt5.QtWidgets import QMessageBox 16 17from PyQt5 import QtCore, QtGui, QtWidgets 18 19class Ui_MainWindow(object): 20 def setupUi(self, MainWindow): 21 MainWindow.setObjectName("MainWindow") 22 MainWindow.resize(300, 300) 23 self.centralwidget = QtWidgets.QWidget(MainWindow) 24 self.centralwidget.setObjectName("centralwidget") 25 self.frame = QtWidgets.QFrame(self.centralwidget) 26 self.frame.setGeometry(QtCore.QRect(10, 0, 261, 221)) 27 self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) 28 self.frame.setFrameShadow(QtWidgets.QFrame.Raised) 29 self.frame.setObjectName("frame") 30 self.plainTextEdit_code_text = QtWidgets.QPlainTextEdit(self.frame) 31 self.plainTextEdit_code_text.setGeometry(QtCore.QRect(20, 60, 231, 151)) 32 self.plainTextEdit_code_text.setObjectName("plainTextEdit_code_text") 33 self.label_2 = QtWidgets.QLabel(self.frame) 34 self.label_2.setGeometry(QtCore.QRect(20, 40, 71, 16)) 35 font = QtGui.QFont() 36 font.setFamily("Segoe UI") 37 self.label_2.setFont(font) 38 self.label_2.setObjectName("label_2") 39 self.pushButton__execute = QtWidgets.QPushButton(self.frame) 40 self.pushButton__execute.setGeometry(QtCore.QRect(140, 10, 111, 23)) 41 font = QtGui.QFont() 42 font.setFamily("Segoe UI") 43 self.pushButton__execute.setFont(font) 44 self.pushButton__execute.setObjectName("pushButton__execute") 45 self.frame_4 = QtWidgets.QFrame(self.frame) 46 self.frame_4.setGeometry(QtCore.QRect(10, 290, 471, 291)) 47 self.frame_4.setFrameShape(QtWidgets.QFrame.StyledPanel) 48 self.frame_4.setFrameShadow(QtWidgets.QFrame.Raised) 49 self.frame_4.setObjectName("frame_4") 50 self.pushButton_read_picture_2 = QtWidgets.QPushButton(self.frame_4) 51 self.pushButton_read_picture_2.setGeometry(QtCore.QRect(20, 10, 111, 23)) 52 font = QtGui.QFont() 53 MainWindow.setCentralWidget(self.centralwidget) 54 self.menubar = QtWidgets.QMenuBar(MainWindow) 55 self.menubar.setGeometry(QtCore.QRect(0, 0, 508, 21)) 56 self.menubar.setObjectName("menubar") 57 self.menuFile = QtWidgets.QMenu(self.menubar) 58 self.menuFile.setObjectName("menuFile") 59 MainWindow.setMenuBar(self.menubar) 60 self.statusbar = QtWidgets.QStatusBar(MainWindow) 61 self.statusbar.setObjectName("statusbar") 62 MainWindow.setStatusBar(self.statusbar) 63 self.menubar.addAction(self.menuFile.menuAction()) 64 65 self.retranslateUi(MainWindow) 66 self.pushButton__execute.clicked.connect(MainWindow.click_execute) 67 QtCore.QMetaObject.connectSlotsByName(MainWindow) 68 69 def retranslateUi(self, MainWindow): 70 _translate = QtCore.QCoreApplication.translate 71 MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) 72 self.pushButton__execute.setText(_translate("MainWindow", "Execute")) 73 74class AK003(QMainWindow,Ui_MainWindow): 75 def __init__(self,parent=None): 76 super(AK003, self).__init__(parent) 77 self.ui = Ui_MainWindow() 78 self.ui.setupUi(self) 79 80 81 def click_execute(self): 82 self.process_code_text = Process_code_text_class() 83 self.process_code_text.start() 84 85 86 87 88#コード実行用サブクラス 89class Process_code_text_class(QThread): 90 def __init__(self,parent=None): 91 super(Process_code_text_class, self).__init__(parent) 92 93 #self.ui = Ui_MainWindow() 94 #self.ui.setupUi(self) 95 self.ak003 = AK003() 96 97 98 99 def run(self): 100 #code_text解析処理 101 #code textの文字列取得 102 code = self.ak003.ui.plainTextEdit_code_text.toPlainText() 103 print(code) 104 105 106 107#メイン関数 108if __name__ == '__main__': 109 app = QtWidgets.QApplication(sys.argv) 110 window = AK003() 111 window.show() 112 sys.exit(app.exec_()) 113

#質問

上記問題を解決するためにアドバイスいただけますでしょうか。

teamikl👍を押しています

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

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

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

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

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

guest

回答2

0

ベストアンサー

別の回答では誤誘導してしまったかもしれないので、改めて投稿します。

「別スレッドで」、他クラスの変数を使うには、
クラス間での変数の参照という、コード表記上の変数のスコープの問題の他に、
スレッド間でのデータの受け渡しという実行時環境の観点が必要で、

回答としては、

Qt では、シグナル&スロットの仕組みが、スレッド間のデータ受け渡しに使えます。

以下は具体的な実装手順。前の回答と繰り返しになりますが、

  • 別スレッドでのシグナル&スロットを稼働させるには、イベントループが必要で
  • 別スレッドでのイベントループを使う為には、

 QThread の使い方を、継承ではなくmoveToThreadを用いた方法にする必要がある。

  • 追加で、スレッド内で実行したいコードも、場合によっては(時間がかかる場合は)

 イベントループに配慮した形に書き換える必要があります。

python

1import sys 2import logging 3 4from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot 5from PyQt5.QtWidgets import ( 6 QApplication, 7 QWidget, 8 QLineEdit, 9 QPlainTextEdit, 10 QPushButton, 11 QGridLayout, 12 ) 13 14 15class Worker(QObject): 16 17 done = pyqtSignal(str) 18 19 @pyqtSlot(str) 20 def execute(self, text): 21 # シグナル経由で呼び出すと別スレッドで実行されますが、 22 # シグナル&スロット機構を使う場合は、イベントループを使う必要があり、 23 # スレッドのイベントループを正常に稼働させるためには、 24 # 個々の関数(スロット)は即時に終了しなければなりません。 25 # 26 # 時間のかかる処理を行いたい場合は、 27 # 毎回新しいスレッドを生成するか、この中で更に別スレッドを使います。 28 29 logging.info("execute: %s", text) 30 31 self.done.emit(text) 32 33 34class View(QWidget): 35 36 sendMessage = pyqtSignal(str) 37 38 def __init__(self, parent=None): 39 super().__init__(parent) 40 lineEdit = QLineEdit(self) 41 button = QPushButton("Execute", self) 42 textEdit = QPlainTextEdit(self) 43 44 lineEdit.returnPressed.connect(self._onClicked) 45 button.clicked.connect(self._onClicked) 46 47 layout = QGridLayout(self) 48 layout.addWidget(lineEdit, 0, 0) 49 layout.addWidget(button, 0, 1) 50 layout.addWidget(textEdit, 1, 0, 1, 2) 51 52 self._button = button 53 self._textEdit = textEdit 54 self._lineEdit = lineEdit 55 56 def _onClicked(self): 57 text = self._lineEdit.text() 58 self.sendMessage.emit(text) 59 self._lineEdit.clear() 60 61 @pyqtSlot(str) 62 def addResultText(self, text): 63 logging.info("addResultText: %s", text) 64 self._textEdit.appendPlainText(text) 65 66 67def main(): 68 app = QApplication(sys.argv) 69 view = View() 70 71 # スレッド開始 72 thread = QThread(app) 73 worker = Worker() 74 worker.moveToThread(thread) 75 thread.start() 76 77 # view: メインスレッド, worker: 別スレッド 78 view.sendMessage.connect(worker.execute) 79 worker.done.connect(view.addResultText) 80 81 # XXX: シグナル&スロットを使わない呼び出し。 82 # 以下はメインスレッドで実行されるのがログで確認できます。 83 # worker.execute("TEST") 84 85 86 # プログラム終了時にエラーが出ないように、スレッドの後始末 87 # ※ QThread の使い方によって後始末の方法が異なるので注意 88 # QThread.quit は、スレッドのイベントループを終了します。 89 app.aboutToQuit.connect(thread.quit) 90 91 view.show() 92 sys.exit(app.exec_()) 93 94 95if __name__ == '__main__': 96 logging.basicConfig( 97 level=logging.DEBUG, 98 # ログにスレッド名を表示するための書式設定 99 # 100 # ※ 制限: QThread のスレッド名は Dummy-1 のように表示されますが、 101 # 今回は、メインスレッドかそうでないかを区別できれば充分です。 102 format="[%(threadName)-10s][%(levelname)-8s] %(message)s", 103 ) 104 main()

※ 元のコードはボタンを押したときにスレッド生成してましたが、
このサンプルでは、スレッドはアプリケーションの起動時~終了時まで
一つのスレッドのみを使ってます。
理由: 別スレッド側でシグナル&スロットの通知を受け取るイベントループを立ち上げておく必要があるため。
この辺は、状況次第で様々な使い方があります。


スレッドを跨いだ変数の利用について、

このコードでは logging の関数が該当しますが、
(メインスレッドでも別スレッド側でも区別なく使われている)

logging モジュールはスレッドセーフに設計されていて、
ドキュメント内にもスレッドセーフだと明言されてます。

Qt のオブジェクトでは、QThread のメソッドは大体スレッドセーフですが、
他のウィジェット等(QPlainTextEdit等)大半は、スレッドセーフでは有りません。

マルチスレッドで安全な設計にしたい場合、一般的には、
GUIの操作はメインスレッドで行い、その他の操作を別スレッドで。
必要なデータのみをスレッド間で受け渡しすることになります。

投稿2021/01/31 11:04

編集2021/01/31 11:45
teamikl

総合スコア8715

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

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

hachimitu

2021/02/07 09:18

私のやりたかったことができました! そして、threadに対しても理解を深めることができました。本当にありがとうございました。 次は"サブスレッド処理をキーボード入力の割り込みで終了する"ということにチャレンジします。
guest

0

class AK003で定義した変数を使いたい

変数のスコープの問題だけなら、
別スレッドで実行される関数に引数で渡すことはできますが、(コード例追記)

python

1 2 def click_execute(self): 3 self.process_code_text = Process_code_text_class(self) # (1) AK003()のインスタンスを渡す 4 self.process_code_text.start() 5 6#コード実行用サブクラス 7class Process_code_text_class(QThread): 8 def __init__(self,parent=None): 9 super(Process_code_text_class, self).__init__(parent) 10 11 self.ak003 = parent # (2) 12

スレッドを跨いで UI に入力された変化する値等を参照するのは、
マルチスレッドにおいては安全な操作では有りません。

前提として、
GUI の生成は別スレッドではなく、全てメインスレッドで行ってください。
GUIのクリックイベントや描画等は、
メインスレッドのイベントループ app.exec_() 内で行われます。

その上で、解決策と問題点:

  • スレッド間のデータの受け渡しには signal/slot を使います。
  • 但し、QThread を継承した利用方法では、スレッド間のシグナル&スロットの実行に制限があります。
  • QThread 間でsignal/slot を処理するイベントループを動かすためには、

 QThread を継承するのではなく、moveToThread を使った QThread の利用方法を調べてみてください。

投稿2021/01/30 11:20

編集2021/01/30 12:08
teamikl

総合スコア8715

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

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

hachimitu

2021/01/31 02:28

ありがとうございます! かなり助かりました。追記してもらったコードで進めていきたいと思います。 どうしても、無理な場合はmoveToThread方法で試します 追加で質問したいのですが、 以下部分がいまいちピンときていません self.ak003 = parent # (2) ---以下コード毎の認識があいまいでして。。 self.process_code_text = Process_code_text_class(self) ⇒AK003のインスタンスを引数にしたProcess_code_text_classをインスタンス化している class Process_code_text_class(QThread): def __init__(self,parent=None): ⇒Process_code_text_classで親としてQThreadを継承してparentには何もいれてない? super(Process_code_text_class, self).__init__(parent) ⇒QThreadの初期化内容を実施 self.ak003 = parent # (2) ⇒parentには何がはいっている?
teamikl

2021/01/31 04:29

> def __init__(self,parent=None): ⇒Process_code_text_classで親としてQThreadを継承してparentには何もいれてない? デフォルト引数なので、引数がなにもない時 parent は None になります。 今回は引数を渡すので None にはなりません。 ⇒Process_code_text_classで親としてQThreadを継承してparentには何もいれてない? 「親」が2つの文脈で使われてるので、混同されないように。 「親としてQThreadを継承して」は「クラス間の関係」 親クラスですが、 変数の parent は「オブジェクト間の関係」の親子です。 最初のコード(質問に掲載のコード)では parentは None でした。 一応、そのオブジェクトの生存期間が異なります。 (parentが指定されてると、親オブジェクトが破棄された時、 子オブジェクトも破棄されます) ⇒QThreadの初期化内容を実施 特に問題有りません。 > self.ak003 = parent # (2) ⇒parentには何がはいっている? 呼び出し部分は Process_code_text_class(self) なので AK003クラス内の self は、AK003() のインスタンスです。 ---- > 追記してもらったコードで進めていきたいと思います。 > どうしても、無理な場合はmoveToThread方法で試します - 変数のスコープの問題 - マルチスレッドでの問題 があり、質問の内容自体は、変数のスコープに関してですが、 後者の問題は実際にスレッドを使う以上は避けられないので、 その方針で進めるのは、不安が残ります。すぐに問題に当たりそう。 マルチスレッドで問題があると、デバッグが大変になるので (タイミング次第で問題が起こったり起こらなかったりする、  クラッシュや応答なしが頻発する原因となりやすい) できるだけ早期の対応をお勧めします。
teamikl

2021/01/31 06:51

スレッド間でエラーになる具体例を挙げたほうが良かったかな… 例えば、QPlainTextEdit で、 別スレッド側での toPlainText 呼出は、 一見、大丈夫 (そうに見えるだけで状況次第では問題) ですが、 別スレッド側での setPlainText 呼出は、 明確に、スレッドに関するエラーになります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問