How can I wrap a non-Traits model for use with Pyt

2019-05-20 04:25发布

I would like to wrap a non-Traits model class for use with Python Traits. My goal is to write a Traits-based UI to manipulate an "external" model class. The external model class has been generated by SWIG and so I cannot add enthought.traits.api.HasTraits as an ancestor (I think, though I may be wrong).

My current best attempt is

from enthought.traits.api import HasStrictTraits, Property, Instance

class ExternalModel():
    foo = 'foo'

class TraitsModel(HasStrictTraits):
    _e = Instance(ExternalModel)

    def __init__(self):
        self._e = ExternalModel()
        self.add_trait('foo', Property(lambda     :getattr(self._e,'foo'     ),
                                       lambda attr:setattr(self._e,'foo',attr)))

which causes the Traits-based class TraitsModel to have a mutable property which delegates to the contained non-Traits ExternalModel instance. However, TraitsModel.trait_names() doesn't report 'foo' as a recognized trait.

Any suggestions for how to have TraitsModel report a 'foo' trait which is linked to ExternalModel? enthought.traits.api.DelegatesTo seems to require the target be a Traits class (though I may not have found the right invocation and that is possible).

A more MVC-ish approach is probably to have a Traits-based view of my ExternalModel. I've been unable to figure out having a non-Traits model for a Traits-based view. Suggestions in that direction also greatly welcome.

Update I have figured out how to get HasTraits as the ExternalModel superclass using the approach at http://agentzlerich.blogspot.com/2011_05_01_archive.html and it seems to have been a complete waste of time. Apparently the SWIG voodoo and the Traits hoodoo do not jive. Wrapping ExternalModel within TraitsModel as this question asks seems the best route.

1条回答
对你真心纯属浪费
2楼-- · 2019-05-20 04:51
from enthought.traits.api import HasStrictTraits, Instance, Property

class ExternalModel(object):
    foo = 'foo'

class TraitsModel(HasStrictTraits):
    _e = Instance(ExternalModel, ExternalModel())

    def __init__(self):
        '''
        >>> wrapper = TraitsModel()
        >>> wrapper.foo
        'foo'
        >>> wrapper._e.foo = 'bar'
        >>> wrapper.foo
        'bar'
        >>> wrapper.trait_names()
        ['trait_added', '_e', 'foo', 'trait_modified']
        '''
        HasStrictTraits.__init__(self)
        for trait in (name for name in dir(self._e) if not name.startswith('__')):
            self.__class__.add_class_trait(
                trait,
                Property(
                    lambda:getattr(self._e, trait),
                    lambda attr:setattr(self._e, trait, attr)
                )
            )


if __name__ == '__main__':
    import doctest
    doctest.testmod()

A fairly robust solution is to use the add_class_trait of the HasTraits class, coupled with dir(self._e) to get the names of the class attributes of ExternalModel and a generator expression/list comprehension to filter the magic class method names (filter with an appropriate function would work better for wrapping a more complex class).

Also:

  • ExternalModel should inherit from object

  • __init__ should call HasStrictTraits.__init__ (or super(HasStrictTraits, self).__init__())

  • _e can also be created in the Instance trait declaration as the second argument using ExternalModel() or even (), or as a method of TraitsModel like:

    def __e_default(self): # note preceding underscore
        return ExternalModel()
    

Lastly, I have an slightly old copy of the Enthought APIs including Traits which can be very handy.

查看更多
登录 后发表回答