日本語 English

PySideの落とし穴

このWikiページでは、PySideを使ってプログラム開発を始めたときに陥りやすい、よくある落とし穴についてまとめています。PySideを使ってアプリを開発している最中に「オー!」体験を見つけたら、是非その体験をここに記録してください。

参照

QObjectはPythonのスコープから外れると削除されてしまいます。そのためオブジェクトの参照を保持するように注意する必要があります。

  • 他のオブジェクトの属性にして保持しましょう。例: self.window = QMainWindow()
  • コンストラクタに親となるQObjectを渡しましょう。こうすればそのオブジェクトは親によって保持されます。

動作しない例

  1. class MyStuff(object):
  2.     # ...
  3.     def animate_stuff(self, target):
  4.         animation = QtCore.QPropertyAnimation(target, 'rotation')
  5.         animation.setDuration(700)
  6.         animation.setEasingCurve(QtCore.QEasingCurve.OutSine)
  7.         animation.setEndValue(100)
  8.         animation.start(QtCore.QAbstractAnimation.DeleteWhenStopped)

これがうまくいかない理由はanimationがanimate_stuff()の終了時に削除されてしまい、動作をする機会が得られないからです。一般的にアニメーションを使う場合には、対象となるQObjectがある場合が多いので、アニメーションをそのオブジェクトの子にするとよいでしょう。これはコンストラクタの最後の引数に親オブジェクトを与えることによって行います。

  1. class MyStuff(object):
  2.     # ...
  3.     def animate_stuff(self, target):
  4.         animation = QtCore.QPropertyAnimation(target, 'rotation', target)
  5.         # ...

この方法ではanimationの参照がtargetによって保持されるのでうまくいきます。別の方法としてanimationをそのオブジェクトの属性にする方法もあります( “self.animation = …“を使います)。この方法ではオブジェクトが属性を持っている間はアニメーションへの参照が保持され続けます(事前に手動で削除しない限り)。属性の上書き(例えば、最初のアニメーションがまだ実行されている最中に、他のアニメーションを代入するなど)は、また元のようにオブジェクトへの参照が削除されて、結局オブジェクト自体が削除されてしまう原因になるのでご注意ください。

QtCore.QObject.sender()

シグナルの発行元オブジェクトを取得したい場合はQtCore.QObject.sender()を使用してください。ただし使用する前によく考えるべきです(詳しくはQObjectのAPIドキュメントをごらんください)。どうしても使用する必要があると判断した場合でも次のことに注意してください。この関数は静的メソッドとして呼び出すことはできませんし、呼び出せるのはQObjectのスロット内だけです。

つまり、 次のコードは動作しません

  1. def on_signal(*args):
  2.     print args
  3.     print QtCore.QObject.sender() # THIS DOES NOT WORK!
  4.  
  5. a.someSignal.connect(on_signal)

これを実現するには、次のように、スロットを持ったQObjectのサブクラスを作成して、そのオブジェクトのメンバー関数としてsender()にアクセスします。

  1. class Listener(QtCore.QObject):
  2.     @QtCore.Slot()
  3.     def on_signal(self, *args):
  4.         print args
  5.         print self.sender()
  6.  
  7. listener = Listener()
  8. a.someSignal.connect(listener.on_signal)

Categories: