Nested HasTraits objects , how to Initialise properly

421 views Asked by At

I am trying to create a HasTraits objects which contains several other different instance of another HasTraits objects. However, I always seem to hit problems when I initialise my many HasTraits objects in the master object.

I've produced a simple example below that produces the error. Could someone explain the best way to do this? -I never know when I should use a traits.Instance(traits.Int) or just the traits.Int -How do I pass in initial values for the traits in the constructor? Whenever I do this I get errors like "type int required but found type traits.Int"

Thanks for your help

import enthought.traits.api as traits
import enthought.traits.ui.api as traitsui 

class Simple(traits.HasTraits):

    minimum = traits.Int()
    maximum= traits.Int()
    ranged = traits.Range(minimum, maximum)

    traits_view = traitsui.View(traitsui.Group(
    traitsui.Item('minimum'),
    traitsui.Item('maximum'),
    traitsui.Item('ranged')    
    ))

class Complex(traits.HasTraits):

    s1=Simple(minimum=1.0,maximum=5.0)
    s2=Simple(minimum=2.0,maximum=10.0)
    s3=Simple(minimum=traits.Int(1.0),maximum=traits.Int(5.0))

    traits_view = traitsui.View(traitsui.Group(
    traitsui.Item('s1'),
    traitsui.Item('s2')
    ))

c= Complex()
c.configure_traits()
1

There are 1 answers

5
OYRM On BEST ANSWER

I've tested your code with the same results, but as I think about this, I realize that there is an issue with the means by which you are using Range. Traits of the Range type must be defined with min and max values found within trait_handlers.RangeType which equates to (int, long, float). So, you'll have to define an initial range using those types, unless you want to replace traits.api.BaseRange, which I don't think is a smart move. So, you'll have to stick with these data types, and do a little more manual work if you'd like to tie changes to Simple.minimum and 'Simple.maximumto the bounds of yourSimple.ranged` trait.

Keeping in mind, you are not correct in stating that you're defining the Range values in the "Constructor". The definition of variables in the body of a class is not a constructor. Your ranged trait is being defined with invalid values because you're attempting to pass a Traits object and not an int, long, or float. Even if you were to convert the Int trait to an int value, keep in mind that you're working on the class definition at this point, and not the instance value as the class has yet to be instantiated at the point that you're defining the value of Ranged. With this in mind, I've added an _init_ method (read: Constructor) to the Simple class.

I have adapted the use of the add_trait method from the Traits UI Manual

from traits.api import HasTraits, Int, Range
from traitsui.api import View, Item

class Simple(HasTraits):
    minimum=Int()
    maximum=Int()
    ranged = Range(0, 0)

traits_view = View(
        Item('minimum'),
        Item('maximum'),
        Item('ranged'),
        resizable=True,width=600,height=400)

    def __init__(self, minimum=0, maximum=0, **traits):
        self.maximum = maximum
        self.minimum = minimum

        HasTraits.__init__(self, **traits)
        ranged = Range(self.minimum, self.maximum)
        self.remove_trait('ranged')
        self.add_trait('ranged', ranged)


    def _minimum_changed(self):
        self.remove_trait("ranged")
        ranged = Range(self.minimum, self.maximum)
        self.add_trait('ranged', ranged)


    def _maximum_changed(self):
        self.remove_trait("ranged")
        ranged = Range(self.minimum, self.maximum)
        self.add_trait('ranged', ranged)

c=Simple(minimum=1, maximum=5)
c.configure_traits()

Let me know if you have any follow up questions.

EDIT: I've learned since that this can be greatly simplified

from traits.api import HasTraits, Int, Range

class Simple(HasTraits):
    minimum=Int()
    maximum=Int()
    ranged = Range(low='minimum', high='maximum')

using the string values for low and high causes the api to link these traits in the background, baking in all that easy flavor. This is clearly the more concise idiom to adopt for this purpose.