7

Pythonでスクリプトを書いていますが、少し問題があります。

class LightDMUser(QObject):
  def __init__(self, user):
    super(LightDMUser, self).__init__()
    self.user = user

  @pyqtProperty(QVariant)
  def background(self):      return self.user.get_background()

  @pyqtProperty(QVariant)
  def display_name(self):    return self.user.get_display_name()

  @pyqtProperty(QVariant)
  def has_messages(self):    return self.user.get_has_messages()

  @pyqtProperty(QVariant)
  def home_directory(self):  return self.user.get_home_directory()

  @pyqtProperty(QVariant)
  def image(self):           return self.user.get_image()

  @pyqtProperty(QVariant)
  def language(self):        return self.user.get_language()

  @pyqtProperty(QVariant)
  def layout(self):          return self.user.get_layout()

  @pyqtProperty(QVariant)
  def layouts(self):         return self.user.get_layouts()

  @pyqtProperty(QVariant)
  def logged_in(self):       return self.user.get_logged_in()

  @pyqtProperty(QVariant)
  def name(self):            return self.user.get_name()

  @pyqtProperty(QVariant)
  def real_name(self):       return self.user.get_real_name()

  @pyqtProperty(QVariant)
  def session(self):         return self.user.get_session()

ご覧のとおり、このコードはひどく冗長です。私はそれを次のように凝縮してみました:

class LightDMUser(QObject):
  attributes = ['background', 'display_name', 'has_messages', 'home_directory', 'image', 'language', 'layout', 'layouts', 'logged_in', 'name', 'real_name', 'session']

  def __init__(self, user):
    super(LightDMUser, self).__init__()
    self.user = user

    for attribute in self.attributes:
      setattr(self, attribute, pyqtProperty(QVariant, getattr(self.user, 'get_' + attribute)))

ただし、PyQt4は、インスタンスではなく、クラス自体にクラスメソッドが存在することを想定しています。クラスに定義されていないため、ブロックsetattrからコードを移動することもできませんでした。そのため、どうすればよいかわかりません。__init__self

誰かがこのコードを凝縮する方法を見ることができますか?

4

6 に答える 6

5

それを行うには、クラスデコレータ、メタクラス、Mixinなどのいくつかの方法があります。

一般的なヘルパー関数:

def set_pyqtproperties(klass, properties, proxy='user'):
    def make_prop(prop):        
        def property_(self):
            return getattr(getattr(self, proxy), 'get_' + prop)
        property_.__name__ = prop
        return property_

    if isinstance(properties, basestring):
       properties = properties.split()
    for prop in properties:
         setattr(klass, prop, pyqtProperty(QVariant, make_prop(prop)))

クラスデコレータ

def set_properties(properties):
    def decorator(klass):
        set_pyqtproperties(klass, properties)
        return klass
    return decorator
使用法
@set_properties("display background")
class LightDMUser(QObject): pass

クラスデコレータがサポートされていない場合は、次のことを試すことができます。

class LightDMUser(QObject): 
    pass
LightDMUser = set_properties("display background")(LightDMUser)

メタクラス

def set_properties_meta(properties):
    def meta(name, bases, attrs):
        cls = type(name, bases, attrs)
        set_pyqtproperties(cls, properties)
        return cls
    return meta
使用法
class LightDMUser(QObject):
    __metaclass__ =  set_properties_meta("display background")

注:プロパティのリストをクラス属性として設定すると、同じメタクラスを再利用できます。

def MetaClass(name, bases, attrs):
    cls = type(name, bases, attrs)
    set_pyqtproperties(cls, attrs.get('properties', ''))
    return cls

class LightDMUser(QObject):
    properties = "display background"
    __metaclass__ = MetaClass

attrsまた、直接操作することもできます:の代わりattrs[name] = valueに呼び出す前に。type()setattr(cls, name, value)

上記は、を前提としていQObject.__class__ is typeます。

混入します

def properties_mixin(classname, properties):
    #note: create a new class by whatever means necessary
    # e.g., even using exec() as namedtuple does
    # http://hg.python.org/cpython/file/3.2/Lib/collections.py#l235

    # reuse class decorator here
    return set_properties(properties)(type(classname, (), {}))
使用法
PropertiesMixin = properties_mixin('PropertiesMixin', 'display background')
class LightDMUser(PropertiesMixin, QObject): pass

私はそれを試したことがありません。コードは、機能を実装するために必要となる可能性のあるコードの量と種類を示すためにここにあります。

于 2012-08-12T12:08:21.800 に答える
2

You could attach these methods from еру outside of the class definition:

class LightDMUser(QObject):

  def __init__(self, user):
    super(LightDMUser, self).__init__()
    self.user = user

The simplest way is to create a closure for each property, override its __name__ (just for case if @pyqtProperty needs it) and to bind it to the class:

for attribute in [
        'background',
        'display_name',
        'has_messages',
        'home_directory',
        'image',
        'language',
        'layout',
        'layouts',
        'logged_in',
        'name',
        'real_name',
        'session'
      ]:

  def delegating(self):
    return getattr(self.user, 'get_' + attribute)()

  delegating.__name__ = attribute
  delegating = pyqtProperty(QVariant)(delegating)

  setattr(LightDMUser, attribute, delegating)
于 2012-08-12T11:28:55.003 に答える
1

ループをクラスの外に移動し、各属性名を保持するクロージャーを作成すると、これが機能すると確信しています。

class LightDMUser(QObject):
    attributes = ['background', 'display_name', 'has_messages',
                  'home_directory', 'image', 'language', 'layout',
                  'layouts', 'logged_in', 'name', 'real_name', 'session']

    def __init__(self, user):
        super(LightDMUser, self).__init__()
        self.user = user

for attribute in LightDMUser.attributes:
    closure = lambda self, attribute=attribute : getattr(self.user,
                                                         'get_' + attribute)()
    setattr(LightDMUser, attribute, pyqtProperty(QVariant, closure))

私はあなたが扱っている実際のQTベースのクラスでこれをテストしていませんが、通常のPythonpropertyインスタンスを使用するより単純なバージョンは完全に機能しました。また、これが良いアイデアかどうかもわかりません。まだ慣れていない場合、何が起こっているのかを理解するのはかなり難しいからです。

于 2012-08-12T10:56:57.737 に答える
1

私はこれが好きだとは確信していませんが、それは可能なオプションであり、理解するのはそれほど難しくなく、getattr'sの必要性を排除します...以下はマクロのように少し使用できますが、微調整が必​​要な場合があります。 。(たとえば、getで始まるクラス定義から、または既存のオブジェクトなどから関数を取得します...)また、そこにreprを追加して、ユーザーオブジェクトなどのプロパティとインターフェイスするためのサポートクラスであることを説明することもできます。 )。

def get_properties(name, funcs):
    get_text = """
class {name}(QObject):
""".format(name=name)
    for func in funcs:
        get_text += (
              "\n\t@pyqtProperty(QVariant)\n"
              "\tdef {func}(self): return self.user.get_{func}()\n"
              ).format(func=func)

    print get_text # this should be exec...

>>> get_properties('UserProperties', ['display', 'background'])

class UserProperties(QObject):

    @pyqtProperty(QVariant)
    def display(self): return self.user.get_display()

    @pyqtProperty(QVariant)
    def background(self): return self.user.get_background()

そのexecが実行されると、メインクラスを次のように記述できるようになります。

class LightDMUser(QObject, UserProperties):
    def __init__(self, user):
        super(LightDMUser, self).__init__()
        self.user = user
于 2012-08-12T11:10:13.593 に答える
1

Python 3について、以下のソリューションをテストしました。これは、metaclassキーワードを使用します。

# A bit of scaffolding

def pyqtProperty(cls, method):
    return method

class QObject:
    pass

class QVariant:
    pass

class User:
    def __init__(self, name="No Name"):
        self.name = name
    def get_background(self):
        return self.name
    def get_display_name(self):
        return self.name
    def get_has_messages(self):
        return self.name
    def get_home_directory(self):
        return self.name
    def get_image(self):
        return self.name
    def get_language(self):
        return self.name
    def get_layout(self):
        return self.name
    def get_layouts(self):
        return self.name
    def get_logged_in(self):
        return self.name
    def get_name(self):
        return self.name
    def get_real_name(self):
        return self.name
    def get_session(self):
        return self.name

# The Meta Class
class MetaLightDMUser(type):
    @classmethod
    def __prepare__(cls, name, baseClasses):
        classdict = {}
        for attribute in ['background', 'display_name', 'has_messages', 'home_directory', 'image', 'language', 'layout', 'layouts', 'logged_in', 'name', 'real_name', 'session']:
            classdict[attribute] = eval("lambda self: pyqtProperty(QVariant, getattr(self.user, 'get_" + attribute +"'))()")
        return classdict

    def __new__(cls, name, baseClasses, classdict):
        return type.__new__(cls, name, baseClasses, classdict)

# The class itself
class LightDMUser(QObject, metaclass = MetaLightDMUser): 
    def __init__(self, user):
        super(LightDMUser, self).__init__()
        self.user = user

あるいは、このようなclassdictエントリを作成することもできます

classdict[attribute] = lambda self, attr=attribute: pyqtProperty(QVariant, getattr(self.user, 'get_' + attr))()

しかし、それはattr引数を提示します。私たちと一緒eval()にこの議論をハードコア

同様に私達は使用することができたfunctools.partial

classdict[attribute] = functools.partial(lambda self, attr: pyqtProperty(QVariant, getattr(self.user, 'get_' + attr))(), attr=attribute)

ただし、呼び出しはである必要がありますu.method(u)。まさかu.method()

この呼び出しLightDMUser.method(u)は、3つの実装すべてで機能します

よろしく

于 2012-08-12T15:21:02.957 に答える
0

メタクラスまたはクラスデコレータを使用してpyqtPropertyの定型文を削減するのは難しいですが、これは、出発点として役立つ、ここで作業する必要があるものです。欠点は、@装飾構文を使用できなくなったことですが、この状況では、これを1行のコードにまとめた方が望ましいようです。

これは、selfだけでなく、ユーザーオブジェクトを呼び出すように設定することも、self.userを自動的に呼び出すLightDMUserのカスタムgetattr動作を実装することもできます。

from PyQt4.QtCore import pyqtProperty
from PyQt4.QtGui import QWidget, QColor
from functools import partial

def pyqtPropertyInit(name, default):
    def _getattrDefault(default, self, attrName):
        try:
            value = getattr(self, attrName)
        except AttributeError:
            setattr(self, attrName, default)
            return default
        return value
    ga = partial(_getattrDefault, default)
    return pyqtProperty(
        default.__class__,
        fget=(lambda s: ga(s, name)),
        fset=(lambda s, v: setattr(s, name, v)),
    )

class TestClass(QWidget):
    def __init__(self, *args, **kwargs):
        super(TestClass, self).__init__(*args, **kwargs)

    stdoutColor = pyqtPropertyInit('_stdoutColor', QColor(0, 0, 255))
    pyForegroundColor = pyqtPropertyInit('_pyForegroundColor', QColor(0, 0, 255))
于 2015-03-27T01:47:05.217 に答える