0

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()
Hooked
  • 84,485
  • 43
  • 192
  • 261
user2175850
  • 193
  • 2
  • 13
  • Does anyone have any ideas for this? Thanks – user2175850 Nov 13 '14 at 18:15
  • The code presented here will still fail to present readable results even if the initial traits.Range issue is solved. It is best to reduce questions asked on this forum to individual issues. As far as I can see, you are asking first how to define a range by an associated trait within a single object defined in the class Simple. There appear to be more issues with the class Complex, which the stated question doesn't quite get to. I'll drop my answer regarding the Range trait 'ranged' below, but you may think about separating out the issues involved with "Complex" for another question. – OYRM Nov 17 '14 at 17:10

1 Answers1

1

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.

OYRM
  • 1,395
  • 10
  • 29
  • Thank you for this answer. So it seems that to change range of the Range trait you actually have to remove it and then add it again? I agree there were more questions here. My main confusion is 1).when can a trait be treated as the python type i.e. when is a trait.Int the same as an int, and 2) why do we define all the traits in the class definition rather than in the constructor __init__. I've used python classes a lot but I always define all my attributes in the constructor why is this different with Traits? I can ask this as a separate question if you prefer. Thanks – user2175850 Nov 20 '14 at 16:38
  • I've attempted to answer a "How" question, but the "Why" question is a whole different animal.I would prefer if that question be posted independently to give some of the enthought architectural gurus a swipe at it. I'll point you in the direction of my understanding in any case 1) has to do with how a Traits object is instantiated and the meta programming aspects of the traits api. You can read traits.trait_handlers.TraitType source and http://docs.enthought.com/traits/traits_user_manual/custom.html to get a better idea. – OYRM Nov 20 '14 at 17:33
  • As far as using the __init__ constructor, that's something you can opt to do if you like. It is not an uncommon programming idiom however to make class level declarations before the object level manipulations conducted in the instance code. Here is a discussion of dynamic Trait initialization http://docs.enthought.com/traits/traits_user_manual/advanced.html#dynamic-initialization – OYRM Nov 20 '14 at 17:37
  • @user2175850 I've found a better way to skin this cat. I didn't know that it existed, but it turns out that one can edit the ranged trait definition as follows : `ranged = Range(low='minimum', high='maximum')`, using the strings in the Range declaration to point at your Int traits will like them automatically – OYRM Nov 21 '14 at 17:10
  • Your question "when is a traits.api.Int a python int" does not really make sense. The answer is, never, because a traits.api.Int is the type of a trait, not a python value (unless that python value is a trait). Automagically traits are turned into python values following initialization, but not during definition. – aestrivex Dec 18 '14 at 17:22