意外的整数 RangeEditor

Unexpected RangeEditor for Integers

看来 RangeEditor 天生就期待浮动特征。以下示例脚本说明了该行为。有没有更简洁的方法来为整数特征指定 RangeEditor?

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 确实默认返回 float 值,除非您通过将 highlow 参数设置为 int 值来覆盖默认值. Range 特征的 lowhigh 参数不会传递给特征的默认编辑器 (see the source here),因此您必须单独设置它们。 (从源头上应该可以看出为什么使用 low_namehigh_name 确实 有效。)IIUC,这是从它的角度来看有意将特征解耦,但是如果您认为行为应该有所不同,欢迎您在 Traits 中提出问题进行讨论。

可能有助于阐明您的代码的一件事是将模型和视图的定义分开。在特征定义中指定编辑器很容易混淆 w.r.t。特征的参数与其视图。如果值的约束是 Exposure 模型固有的,请使用 Range,否则只需使用基础特征类型。单独定义视图可以让你的代码更清晰:哪些约束属于模型,哪些属于视图。另外,如果你想定义第二个具有不同约束的视图,这样做也没问题。

以下是我建议重写代码的方法:

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)