3

I'm having trouble trying to dynamically initialize a Traits Range object.

The following code is a very simple example of what I'm trying to do, but sadly fails with the following type error:

TypeError: unsupported operand type(s) for -: 'int' and 'code'

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

class DynamicRange(HasTraits):
    """Dynamic initialisation of a Range object"""
    N = Int()
    R = Range(low=0, high='N')

    traits_view = View(Item('R'), Item('N'))

    def __init__(self, N):
        self.N = N         # initial parameter value for N

    def _N_default(self):
        return self.N

Robject = DynamicRange(N=10)
Robject.configure_traits()

The code works if I replace the definition of 'R' with: R = Range(low=0, high=10), but then of course I don't get the dynamic initialization of the Range's high parameter that I'm seeking.

All suggestions gratefully accepted.

* Edit after Jonathan's response *

Here's the simple solution to my question using Jonathan's helpful suggestion below:

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

class DynamicRange(HasTraits):
    """Dynamic initialisation of a Range object"""
    traits_view = View(Item('R'))

    def __init__(self, N):
        self.add_trait("R", Range(0, N))

Robject = DynamicRange(N=30)
Robject.configure_traits()
Jonathan March
  • 5,800
  • 2
  • 14
  • 16
dreme
  • 4,761
  • 3
  • 18
  • 20

2 Answers2

5

One method would be to define the trait not in the usual way, but within __init__, thus:

    self.add_trait("R", Range(0, N))

See http://docs.enthought.com/traits/traits_user_manual/advanced.html#per-object-trait-attributes

Jonathan March
  • 5,800
  • 2
  • 14
  • 16
3

You are experiencing a Traits/TraitsUI bug:

A work-around is to create another Trait for the low end:

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


class DynamicRange(HasTraits):
    """Dynamic initialisation of a Range object"""

    # Upper limit of R
    N = Int()

    # Lower limit of R (not exposed to the user)
    _zero = Int(value=0)

    R = Range(low='_zero', high='N')

    traits_view = View(Item('R'), Item('N'))


Robject = DynamicRange(N=10)
Robject.configure_traits()

Or use @JonathanMarch's solution. :)

Warren Weckesser
  • 110,654
  • 19
  • 194
  • 214
  • Thanks for the explanation of the essence of this issue, Warren. If you want the range to be truly dynamic (not just on initialization) then this is a much better solution - as Warren's code sample illustrates. – Jonathan March Feb 11 '15 at 21:44
  • Yes, thanks Warren, that is indeed a better solution as it allows updating within the class code. I hadn't realised that trait objects declared at the start of the class code could be initialised with passed parameters when the object is created. – dreme Feb 13 '15 at 01:12
  • Ah, but I see that passing parameters this way only works if you don't have an __init__ function in your class code – dreme Feb 13 '15 at 01:29
  • 1
    I didn't include an `__init__()` method because it didn't seem relevant to the issue. You can add an `__init__()` method, but be sure it includes a call to the `__init__()` method of the parent class. That is, if you have, say, `def __init__(self, **kw):` in the class `DynamicRange`, then include the line `super(DynamicRange, self).__init__(**kw)` in the method. – Warren Weckesser Feb 13 '15 at 03:20
  • @dreme -- Warren's comment here is worth study for a deeper understanding of how to use class inheritance. – Jonathan March Feb 15 '15 at 03:37
  • Thanks for that clarification Warren. I had seen that code with the super statement before, but hadn't really understood what it was for. – dreme Feb 15 '15 at 22:23