我正在用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
,所以我真的不知道该怎么做。
有人能看到一种方法来压缩这段代码吗?
答案 0 :(得分:5)
有很多方法可以做到:class decorator,metaclass,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
我没有尝试过任何一个。代码用于显示实现该功能所需的代码量和类型。
答案 1 :(得分:2)
您可以从类定义之外的地方附加这些方法:
class LightDMUser(QObject):
def __init__(self, user):
super(LightDMUser, self).__init__()
self.user = user
最简单的方法是为每个属性创建一个闭包,覆盖它的__name__
(仅适用于@pyqtProperty
需要它的情况)并将其绑定到类:
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)
答案 2 :(得分: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的实际类来测试它,但使用常规Python property
实例的更简单版本可以完美地运行。我也不确定这是个好主意,因为如果你还不熟悉它会很难弄清楚会发生什么。
答案 3 :(得分:1)
我不相信我喜欢这个,但这是一个可能的选择,不太难理解,并且不需要getattr
...以下可以使用有点像宏 - 但可能需要调整...(例如,从一个以getwith get开头的类定义中获取func,或者从现有对象中获取等...)还可以在其中添加 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()
当执行该程序时,您可以将主类编写为:
class LightDMUser(QObject, UserProperties):
def __init__(self, user):
super(LightDMUser, self).__init__()
self.user = user
答案 4 :(得分:1)
我为Python 3测试了下面的解决方案。它使用了元类关键字
# 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种实现
此致
答案 5 :(得分:0)
使用元类或类装饰器减少pyqtProperty的样板是很棘手的,但这是我们在这里工作的一个有用的起点。我认为,缺点是您不再使用@decorator语法,但在这种情况下,似乎更愿意将其转换为单行代码。
您可以将其设置为调用您的用户对象而不仅仅是self,或者您可以为LightDMUser实现自动调用self.user的自定义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))