3

私は PyQt4 をかなり頻繁に使用してきました。いくつかのオブジェクトをオーバーロードして、機能を追加できるようにすることもあります。これは PyQt4 で正常に動作します。例:

from PyQt4 import QtGui
button = QtGui.QPushButton()
class MyPushButton(QtGui.QPushButton): pass
button.__class__ = MyPushButton

ただし、PyQt4 の代わりに PySide を使用するようにコードの一部を調整しようとしています。私の理解では、それらは同じ機能を持つべきです。PySide では、同じことはできません。

from PySide import QtGui
button = QtGui.QPushButton()
class MyPushButton(QtGui.QPushButton): pass
button.__class__ = MyPushButton

このエラーは次のとおりです。

TypeError: __class__ assignment: only for heap types

このエラーを回避するためにオブジェクトのクラスを変更する別の方法はありますか? 何が原因なのかよくわかりません。

注: pyuic によってコンパイルされたライブラリでオブジェクトが作成されるため、作成後にオブジェクトのクラスを変更する必要があります。また、私の PySide インストールには uic モジュールがありません。

4

2 に答える 2

3

PySide はShibokenを使用して、C++ クラスから Qt の CPython バインディングを生成します。などの Qt python クラスはすべてQPushButtonC++ で実装されているため、上書きできません__class__

>>> button = QPushButton()
>>> button.__class__ = MyButton
TypeError: __class__ assignment: only for heap types

Shiboken のドキュメントによると 、メソッドが仮想 (オーバーライド可能) である限り、既にインスタンス化されたオブジェクトのメソッドにモンキー パッチ (またはダック パンチ)を適用できます。

import types

def override_text(self):
    return 'overridden'

# Bind override_text() to button.
button.text = types.MethodType(override_text, button, QPushButton)

QPushButtonこれをさらに進めると、 as としてサブクラス化し、メソッドを インスタンスMyButtonに動的に注入できます。のサブクラスの作成は完全にオプションですが 、変更されたインスタンスに加えて の独自のインスタンスを作成できます。MyButtonQPushButtonMyButtonQPushButtonMyButtonQPushButton

MyButtonのサブクラスとして定義しましょうQPushButton

class MyButton(QPushButton):

    def text(self):
        # This will override QPushButton's text() method.
        print("inside MyButton.text()")
        return QPushButton.text(self)
  • 注: 親クラスのメソッドを呼び出す古いスタイルを使用する必要があります。 メソッドが注入されたとき、self は実際には aであり、 a ではないためsuper()、 a で失敗します。TypeErrorQPushButtonMyButton

または、さらに mixin アプローチを採用したい場合は、次を定義しましょうMyButtonOverrides

class MyButtonOverrides(object):

    def text(self):
        # This will override QPushButton's text() method.
        print("inside MyButtonOverrides.text()")
        return self.__class__.text(self)
  • 注:を直接 使用しないため、QPushButton.text()直接呼び出すことができます。self.__class__MyButtonOverrides

extend_instance()次に、オーバーライド メソッドをMyButton(またはMyButtonOverrides)からQPushButton インスタンスに注入するものを定義しましょう。

import inspect

def extend_instance(obj, cls):
    for name, attr in vars(cls).items():
        if inspect.isroutine(attr):
            # Bind instance, class and static methods to *obj*.
            setattr(obj, name, attr.__get__(obj, obj.__class__))

クラス メソッドを元のクラス (例: ) にバインドしたままにしたい場合はMyButton、次を使用します。

def extend_instance(obj, cls):
    for name, attr in vars(cls).items():
        if inspect.isroutine(attr):
            if isinstance(attr, classmethod):
                # Bind class methods to *cls*.
                setattr(obj, name, attr.__get__(cls, cls))
            else:
                # Bind instance and static methods to *obj*.
                setattr(obj, name, attr.__get__(obj, obj.__class__))

私のテストでは、これは Python 2.7 および 3.3 で動作しますが、2.6+ および 3+ でも動作するはずです。

最後に、ボタンを変更するには、 を使用しますextend_instance()

>>> button = QPushButton()
>>> extend_instance(button, MyButton)
>>> button.text()
inside MyButton.text()
u''
于 2014-02-20T18:15:43.127 に答える
1

モンキーパッチ__class__は完全に不要です。代わりに、Qt Designer で関連するウィジェットをプロモートして、サブクラスが自動的に使用されるようにする必要があります。

これを行うには、Qt Designer でウィジェットを右クリックし、[Promote to...] を選択します。ダイアログで、「昇格されたクラス名」をサブクラス (例: 「MyPushButton」) に設定し、「ヘッダー ファイル」をサブクラスを含むモジュールの Python インポート パス (例: 「myapp」または「myapp.gui」) に設定します。または何でも)。

[追加] をクリックしてから [プロモート] をクリックすると、[オブジェクト インスペクタ] ペインでクラスが [QPushButton] から [MyPushButton] に変更されます。

pyuicまたはを使用して ui モジュールを再生成するとpyside-uic、次のようなコードが含まれます。

class Ui_Window(object):
    def setupUi(self, Window):
        ...
        self.button = MyPushButton(Window)
        self.button.setObjectName("button")
        ...

from myapp import MyPushButton

したがって、プロモートされたボタンは のインスタンスとして作成されるためMyPushButton、ハッキングする必要はもうありません__class__

ウィジェットを昇格させるこの手法は、PyQt と PySide の両方で機能します。

于 2014-02-20T21:27:47.060 に答える