It appears RangeEditor is hard-wired to expect a float trait. The following sample script illustrates the behavior. Is there a cleaner way to specify RangeEditor for an integer trait?
import sys
print (sys.version) # '3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:18:55) [MSC v.1900 64 bit (AMD64)]'
from traits import __version__
print (__version__) # '4.6.0.dev0'
from traits.api import HasTraits, Int, Range
from traitsui.api import Item, RangeEditor, View
class Exposure (HasTraits):
duration = Range (low=0.0, high=3600.0, exclude_low=True , exclude_high=False, editor=RangeEditor(mode='slider'))
min_count = Int ( 1)
max_count = Int (10000)
#<1>count = Range (low=1 , high=10000 , exclude_low=False, exclude_high=False, editor=RangeEditor(mode='slider'))
count = Range (low=1 , high=10000 , exclude_low=False, exclude_high=False, editor=RangeEditor(mode='slider', low_name='min_count', high_name='max_count'))
traits_view = View (Item('duration'), Item('count'), buttons=['OK', 'Cancel'])
exposure = Exposure (duration = 0.00032, count=1500)
exposure.configure_traits()
# Specifying `count` as per <1>, above, raises the following exception:
# traits.trait_errors.TraitError: The 'count' trait of an Exposure instance must be 1 <= a long integer <= 10000, but a value of 1.0 <class 'float'> was specified.
#
# A work-around is to declare min_count and max_count, and include them in the
# RangeEditor definition of `count`. Note, I am now duplicating `count` low and
# high limits in 2 places. Is there a better approach?
A RangeEditor
does indeed default to returning float
values, unless you override the default by setting the high
or low
parameters to int
values. The low
and high
parameters of a Range
trait are not passed to the trait's default editor (see the source here), so you have to set them separately. (It should be evident from the source why using low_name
and high_name
did work.) IIUC, this is a deliberate decoupling of the trait from its view, but you're welcome to open an issue in Traits for discussion if you think the behavior should be different.
One thing that may help clarify your code would be to separate the definitions of your model and view. Specifying the editors in your trait definitions makes it easy to get mixed up w.r.t. the parameters of the trait versus its view. If the constraints on the value are intrinsic to the Exposure
model, use a Range
, otherwise just use the underlying trait type. Defining the view separately makes your code clearer: which constraints belong to the model and which to the view. Also, if you want to define a second view with different constraints, it's no problem to do so.
Here's how I would propose re-writing your code:
from traits.api import HasTraits, Int, Range
from traitsui.api import Item, RangeEditor, View
class Exposure (HasTraits):
duration = Range (low=0.0, high=3600.0, exclude_low=True , exclude_high=False)
count = Int( 1500)
my_view = View(
Item('duration', editor=RangeEditor(mode='slider')),
Item('count', editor=RangeEditor(low=1, high=10000, mode='slider')),
buttons=['OK', 'Cancel']
)
exposure = Exposure (duration = 0.00032, count=1500)
exposure.configure_traits(view=my_view)