PySideでのシグナル・スロットの正しい実装手順・動かない原因

PySide・PyQTでのイベント処理

ボタンだったらclickedシグナルが送られ、
それをconnectすればイベント処理できます。

今回はそういう標準的イベントではなく、

  • 独自にシグナル(SIGNAL)を送信したい
  • 独自にスロット(SLOT)を定義したい

そういう場面に遭遇しました。

少し落とし穴的なトラップもあったので、
PySideのシグナル・スロットの実装方法まとめます。

大前提はSIGNALをQObject継承クラスで定義すること

これはハマりがちなポイント1つめです。

「必ずQObject継承クラスでSIGNAL定義する」

普通のクラス内ではSignalは定義できません。

▼ これが分からないと悩み続けます。

  • 独自シグナルを○○クラスで定義しただろ…
  • それでそのシグナルを emit すると…
  • アレ?どうして発火してくれないの?

それはシグナル定義そのものが間違ってるからです。

QObject以外でSIGNAL定義だと no attribute エラーが発生

QObject以外でシグナル定義するのはダメ!

例えばダメなコード例を少し考えてみます。

▼ このようなコードは期待通りに動かない

▼ 次のようなエラーが発生した

単純にSignalを定義するのは間違い

純粋なクラスでSignal定義しても全く無意味です。

必ずQObject継承クラスでSIGNAL定義すべし

先ほどのコードを修正します。

▼ 修正後の正しいコード例

このようにシグナル・スロットを使うには、

「必ずQObjectを継承させること」

これはPySide・PyQtに限った話でもありません。

C++版QTでもこの仕様は同じです。要注意

任意に独自シグナル送信 ⇒ スロットで受け取る方法

今度はSignal送信とSlotの受け取り方について

こういうコードが分かりやすいと思います。

▼ Signal送信・Slot受け取りコード例

これがSIGNAL/SLOTのミニマムな実装例

重要な点は2つあります。

1.シグナル(SIGNAL)送信にはemit()を実行する

上記コードの次の部分です。

▼ こちらのコード

もし受け取り側(SLOT)が文字列型の引数を受け取りなら、この例のように引数付きで emit() を実行すればOKです。(SLOT関数については後述)

それで登録済のSLOT関数が実行されるだけ

2.スロット(SLOT)関数は明示的に@SLOTを付ける

上記コードの以下の部分がそれ

▼ こちらのコード

ただスロット関数を定義するではダメ

このように@QtCore.Slotデコレーターを使い、明示的にどういった引数を取って・どういった返り値を返すのか定義してあげます。

 

ちなみに返り値は result によって指定可能です。

▼ @Slotによる返り値指定のコード例

ここはあまり深く触れないことにします。
(自分もこの部分をよく理解してないため)

とりあえず@Slotデコレーターが必須ということ

最後にシグナル・スロットの要点をまとめ

少しまとまりのない内容だったかもしれない。

そこで重要な点だけをまとめたいと思います。

  • SIGNALはQObject継承クラスで定義する
  • SIGNAL送信にはemit()を使う
  • SLOT関数には明示的に@SLOTを付ける

これだけを抑えとけば問題ないはずです。