Maya PySide2 / PySide チュートリアル 初学編 ⑧ – Signals & Slotsを作成する
このチュートリアルはSignals & Slotsを作成する方法について学んでいきます
前回のチュートリアルではSignals & Slotsとは何かという解説をしていきました
今回はもう少し踏み込んだ内容でQtCore.SignalとQtCore.Slotを使用して新しいシグナルを作成してみます
QtCore.Signal
シグナルはQtCore.Signal()クラスを使って定義でき、C++に対応するPythonの型かC++の型をパラメータとして渡すことができます
QtCore.Slot
スロットは QtCore.Slot()というデコレーターを使って割り当てたり、オーバーロードしたりします
シグネチャを定義するには、QtCore.Signal()クラスのように型を渡すだけです
本格的にカスタムするチュートリアルは後半のほうからしていくとして
手始めにQtの公式ドキュメントのコードがとても単純で分かりやすいのでそちらを参考にしていきます
そのままだといつも通りMayaが落ちてしまうので一部コードを書き替えています
import sys try: from PySide2.QtWidgets import * from PySide2.QtCore import * except ImportError: from PySide.QtGui import * from PySide.QtCore import *
どのコードにも上のコードを先頭に追加して実行してください
@Slot(str) def say_some_words(words): print(words) class Communicate(QObject): speak = Signal(str) someone = Communicate() someone.speak.connect(say_some_words) someone.speak.emit("Hello everybody!")
このコードはとても単純なものでemitの引数に入った文字列をプリントするというようなサンプルコードとなっています
@Slot(str) def say_some_words(words): print(words)
str型を受け取り、'saySomeWords'という名前を持つ新しいスロットを定義します
speak = Signal(str)
speakという新しいシグナルを定義しています
someone.speak.connect(say_some_words)
シグナルとスロットの接続
someone.speak.emit("Hello everybody!")
'speak' シグナルを発信
このコードは上のコードをもう少し拡張したもので2種類の型を扱えるようにしたものになります
@Slot(int) @Slot(str) def say_something(stuff): print(stuff) class Communicate(QObject): speak_number = Signal(int) speak_word = Signal(str) someone = Communicate() someone.speak_number.connect(say_something) someone.speak_word.connect(say_something) someone.speak_number.emit(10) someone.speak_word.emit("Hello everybody!")
@Slot(int) @Slot(str)
'int'または'str'を受け取り、'saySomething'を名前に持つ新しいスロットを定義します
speak_number = Signal(int) speak_word = Signal(str)
新しいシグナルを作成:speak_numberはint型を、speak_wordはstr型を扱います
someone.speak_number.connect(say_something) someone.speak_word.connect(say_something)
シグナルとスロットの接続
someone.speak_number.emit(10) someone.speak_word.emit("Hello everybody!")
'speak' シグナルを発信
複数の型が扱えるのはとても便利ですがもう少しすっきりとした書き方も可能です
@Slot(int) @Slot(str) def say_something(stuff): print(stuff) class Communicate(QObject): speak = Signal((int,), (str,)) someone = Communicate() someone.speak.connect(say_something) someone.speak[str].connect(say_something) someone.speak.emit(10) someone.speak[str].emit("Hello everybody!")
speak = Signal((int,), (str,))
ひとつ前のコードではspeak_number = Signal(int)
とspeak_word = Signal(str)
という風にそれぞれで宣言していましたが個々のコードではspeak = Signal((int,), (str,))
変数一つで宣言しています
型が一つの場合はSignal(Object)
であったり、Signal(QByteArray)
と書き方が単純ですが
複数の場合は書き方がSignal((), (str,), (int,))
になったりするため、少し特殊な書き方になりますがこういうものだと思ってもらえればいいかと思います
QPushButtonを拡張してみる
これまでの内容を基にQPushButtonを拡張してみます
このコードではPushbuttonをクリックすると事前にインプットしていた内容のコードを実行できます
class customPushButton(QPushButton): clickedEmitCode = Signal(str) def __init__(self, text="", parent=None, code=""): super(customPushButton, self).__init__(text, parent) self.code = code self.clicked.connect(self.click) self.clickedEmitCode[str].connect(self.emitText) @property def code(self): pass @code.setter def code(self, value): self.__code = value @code.getter def code(self): return self.__code def click(self): self.clickedEmitCode.emit(self.code) @Slot(str) def emitText(self, codeText): exec(codeText) class Example(QWidget): def __init__(self, parent=None): super(Example, self).__init__(parent) self.setGeometry(6500, 600, 400, 270) self.setWindowTitle("Signals & Slots") layout = QVBoxLayout(self) textList = { "polySphere": "cmds.polySphere()", "polyCube": "cmds.polyCube()", "polyCylinder": "cmds.polyCylinder()" } buttons = [] for count, text in enumerate(textList.keys()): buttons.append(customPushButton(text, self, code=textList)) layout.addWidget(buttons[count]) def main(): app = QApplication.instance() ex = Example() ex.show() sys.exit() app.exec_() main()
class customPushButton(QPushButton): clickedEmitCode = Signal(str)
customPushButtonにclickedEmitCodeというシグナルを用意します
def __init__(self, text="", parent=None, code=""): super(customPushButton, self).__init__(text, parent) self.code = code @property def code(self): pass @code.setter def code(self, value): self.__code = value @code.getter def code(self): return self.__code
codeというプロパティをデコレートします
これは実行したいコードを取得できるようにするためです
codeの設定は宣言時に行うようにしています
ほかにもPushButtonに設定するテキストなども準備しています
def __init__(self, text="", parent=None, code=""): super(customPushButton, self).__init__(text, parent) self.clicked.connect(self.click) self.clickedEmitCode[str].connect(self.emitText) def click(self): self.clickedEmitCode.emit(self.code) @Slot(str) def emitText(self, codeText): exec(codeText)
self.clicked.connect(self.click)でPushbuttonを実行した際にclickedGetTextが発信されるようにしています
clickedGetTextにはemitTextを宣言しておりこれは引数のcodeTextをexec()を使用して実行します
class Example(QWidget): def __init__(self, parent=None textList = { "polySphere": "cmds.polySphere()", "polyCube": "cmds.polyCube()", "polyCylinder": "cmds.polyCylinder()" }
textListにはkeyに使用したいテキスト情報valueにコード情報を設定しており、textListをforでループすることでcustomPushButtonを生成しています
今回のような例のような活用方法であれば、clickedで済むことが多いですが複数のウィジェットを組み合わせ、カスタムウィジェットなどを作成するときに便利ですので是非覚えてみてください
※ おまけ
こういった事例もあるそうです
Is the PySide Slot Decorator Necessary?
| シグナルをデコレーションされたPythonメソッドに接続することで、使用するメモリ量が減り、若干速くなるという利点もあります。
| マルチスレッド環境では、シグナルが間違ったスレッドに送られる可能性があるため、pyside Slotデコレーターを使用しないことが必須となる場合があります
Maya PySide2 / PySide チュートリアルのこのパートではSignals & Slotsを作成する方法を扱いました
ディスカッション
コメント一覧
まだ、コメントがありません