3.4. Traits: building interactive dialogs

作者Didrik Pinte

Traits项目允许您简单地将验证,初始化,委派,通知和图形用户界面添加到Python对象属性。

小费

在本教程中,我们将探讨Traits工具集,并了解如何大幅减少编写的样板代码数量,快速GUI应用程序开发以及了解Enthought Tool Suite其他部分的构思。

Traits和Entto工具套件是根据BSD样式许可证授权的开源项目。

目标受众

中级到高级Python程序员

要求

3.4.1. 简介

小费

Enthought工具套件可以构建用于数据分析,2D绘图和3D可视化的复杂应用程序框架。这些强大的,可重用的组件是在自由BSD样式许可证下发布的。

../../_images/ETS.jpg

Enthought工具套件的主要软件包有:

  • Traits - 基于组件的方法来构建我们的应用程序。
  • Kiva - 支持基于路径渲染,仿射变换,alpha混合等的2D原语。
  • 启用 - 基于对象的2D绘图画布。
  • Chaco - 用于构建复杂的交互式2D图的绘图工具包。
  • Mayavi - 基于VTK的科学数据的三维可视化。
  • Envisage - 用于构建可编写脚本和可扩展应用程序的应用程序插件框架

在本教程中,我们将关注Traits。

3.4.2. 示例

在本教程中,我们将使用一个基于水资源管理简单情况的示例。我们将尝试建模一个大坝和水库系统。水库和大坝确实有一组参数:

  • 名称
  • 储层的最小和最大容量[hm3]
  • 坝的高度和长度[m]
  • 集水区[km2]
  • 液压头[m]
  • 涡轮机功率[MW]
  • 最小和最大释放量[m3 / s]
  • 涡轮机的效率

储层具有已知的行为。一部分与基于释放的水的能量产生有关。用于近似水力发电厂的电力生产的简单公式是P = \rho hrgk,其中:

  • P是以瓦特为单位的功率,
  • \rho是水的密度(〜1000kg / m 3),
  • h是以米为单位的高度,
  • r是以立方米每秒为单位的流速,
  • g是由于9.8m / s2的重力加速度,
  • k是范围从0到1的效率系数。

小费

年发电量取决于可用的供水量。在一些安装中,水流速率可以在一年中以10:1的因子变化。

行为的第二部分是依赖于受控和不受控参数的存储状态:

警告

本教程中使用的数据不是真实的,甚至可能在现实中没有意义。

3.4.3. 什么是性状

trait是一个类型定义,可以用于普通的Python对象属性,赋予属性一些额外的特性:

  • 标准化:
    • 初始化
    • 验证
    • 推迟
  • 通知
  • 可视化
  • 文档

类可以自由地将基于trait的属性与正常的Python属性混合,或者可以选择允许在类中只使用固定或开放的trait属性。由类定义的特性属性由从类派生的任何子类自动继承。

创建traits类的常见方法是从HasTraits基类扩展并定义类traits:

from traits.api import HasTraits, Str, Float
class Reservoir(HasTraits):
name = Str
max_storage = Float

警告

对于Traits 3.x用户

如果使用Traits 3.x,你需要修改traits包的命名空间:

  • traits.api应该是enthought.traits.api
  • traitsui.api应该是enthought.traits.ui.api

使用像这样的traits类就像任何其他Python类一样简单。请注意,trait值使用关键字参数传递:

reservoir = Reservoir(name='Lac de Vouglans', max_storage=605)

3.4.3.1. 初始化

所有的traits都有一个默认值来初始化变量。例如,基本的python类型确实有以下trait等同:

特征 Python类型 内置默认值
Bool 布尔值
复杂 复数 0 + 0j
浮点数 浮点数 0.0
Int 平原整数 0
长整数 0L
Str ''
Unicode Unicode u“

一些其他预定义的trait类型确实存在:Array,Enum,Range,Event,Dict,List,Color,Set,Expression,Code,Callable,Type,Tuple等。

可以在代码中定义自定义默认值:

from traits.api import HasTraits, Str, Float
class Reservoir(HasTraits):
name = Str
max_storage = Float(100)
reservoir = Reservoir(name='Lac de Vouglans')

复杂初始化

当特性需要复杂的初始化时,可以实现_XXX_default魔法。当试图访问XXX性状时,它将被懒惰地调用。例如:

def _name_default(self):
""" Complex initialisation of the reservoir name. """
return 'Undefined'

3.4.3.2. 验证

当用户尝试设置其内容时,每个特征都会进行验证:

reservoir = Reservoir(name='Lac de Vouglans', max_storage=605)
reservoir.max_storage = '230'
---------------------------------------------------------------------------
TraitError Traceback (most recent call last)
.../scipy-lecture-notes/advanced/traits/<ipython-input-7-979bdff9974a> in <module>()
----> 1 reservoir.max_storage = '230'
.../traits/trait_handlers.pyc in error(self, object, name, value)
166 """
167 raise TraitError( object, name, self.full_info( object, name, value ),
--> 168 value )
169
170 def arg_error ( self, method, arg_num, object, name, value ):
TraitError: The 'max_storage' trait of a Reservoir instance must be a float, but a value of '23' <type 'str'> was specified.

3.4.3.3. 文档

实质上,所有的特征都提供了关于模型本身的文档。创建类的声明式方法使其具有自我描述性:

from traits.api import HasTraits, Str, Float
class Reservoir(HasTraits):
name = Str
max_storage = Float(100)

特征的desc元数据可用于提供关于特征的更具描述性的信息:

from traits.api import HasTraits, Str, Float
class Reservoir(HasTraits):
name = Str
max_storage = Float(100, desc='Maximal storage [hm3]')

让我们现在定义完整的水库类:

from traits.api import HasTraits, Str, Float, Range
class Reservoir(HasTraits):
name = Str
max_storage = Float(1e6, desc='Maximal storage [hm3]')
max_release = Float(10, desc='Maximal release [m3/s]')
head = Float(10, desc='Hydraulic head [m]')
efficiency = Range(0, 1.)
def energy_production(self, release):
''' Returns the energy production [Wh] for the given release [m3/s]
'''
power = 1000 * 9.81 * self.head * release * self.efficiency
return power * 3600
if __name__ == '__main__':
reservoir = Reservoir(
name = 'Project A',
max_storage = 30,
max_release = 100.0,
head = 60,
efficiency = 0.8
)
release = 80
print 'Releasing {} m3/s produces {} kWh'.format(
release, reservoir.energy_production(release)
)

3.4.3.4. 可视化:打开对话框

Traits库还知道用户界面,并且可以弹出Reservoir类的默认视图:

reservoir1 = Reservoir()
reservoir1.edit_traits()
../../_images/reservoir_default_view.png

TraitsUI简化了用户界面的创建方式。HasTraits类上的每个trait都有一个默认编辑器,它将管理trait在屏幕上的呈现方式(例如Range trait显示为滑块等)。)。

在与Traits声明式创建类的方式相同的地方,TraitsUI提供了一个声明性接口来构建用户界面代码:

from traits.api import HasTraits, Str, Float, Range
from traitsui.api import View
class Reservoir(HasTraits):
name = Str
max_storage = Float(1e6, desc='Maximal storage [hm3]')
max_release = Float(10, desc='Maximal release [m3/s]')
head = Float(10, desc='Hydraulic head [m]')
efficiency = Range(0, 1.)
traits_view = View(
'name', 'max_storage', 'max_release', 'head', 'efficiency',
title = 'Reservoir',
resizable = True,
)
def energy_production(self, release):
''' Returns the energy production [Wh] for the given release [m3/s]
'''
power = 1000 * 9.81 * self.head * release * self.efficiency
return power * 3600
if __name__ == '__main__':
reservoir = Reservoir(
name = 'Project A',
max_storage = 30,
max_release = 100.0,
head = 60,
efficiency = 0.8
)
reservoir.configure_traits()
../../_images/reservoir_view.png

3.4.3.5. 延迟

能够将trait的定义及其价值推迟到另一个对象是Traits的强大特征。

from traits.api import HasTraits, Instance, DelegatesTo, Float, Range
from reservoir import Reservoir
class ReservoirState(HasTraits):
"""Keeps track of the reservoir state given the initial storage.
"""
reservoir = Instance(Reservoir, ())
min_storage = Float
max_storage = DelegatesTo('reservoir')
min_release = Float
max_release = DelegatesTo('reservoir')
# state attributes
storage = Range(low='min_storage', high='max_storage')
# control attributes
inflows = Float(desc='Inflows [hm3]')
release = Range(low='min_release', high='max_release')
spillage = Float(desc='Spillage [hm3]')
def print_state(self):
print 'Storage\tRelease\tInflows\tSpillage'
str_format = '\t'.join(['{:7.2f}'for i in range(4)])
print str_format.format(self.storage, self.release, self.inflows,
self.spillage)
print '-' * 79
if __name__ == '__main__':
projectA = Reservoir(
name = 'Project A',
max_storage = 30,
max_release = 100.0,
hydraulic_head = 60,
efficiency = 0.8
)
state = ReservoirState(reservoir=projectA, storage=10)
state.release = 90
state.inflows = 0
state.print_state()
print 'How do we update the current storage ?'

特殊的trait允许使用魔术_xxxx_fired方法来管理事件和触发函数调用:

from traits.api import HasTraits, Instance, DelegatesTo, Float, Range, Event
from reservoir import Reservoir
class ReservoirState(HasTraits):
"""Keeps track of the reservoir state given the initial storage.
For the simplicity of the example, the release is considered in
hm3/timestep and not in m3/s.
"""
reservoir = Instance(Reservoir, ())
min_storage = Float
max_storage = DelegatesTo('reservoir')
min_release = Float
max_release = DelegatesTo('reservoir')
# state attributes
storage = Range(low='min_storage', high='max_storage')
# control attributes
inflows = Float(desc='Inflows [hm3]')
release = Range(low='min_release', high='max_release')
spillage = Float(desc='Spillage [hm3]')
update_storage = Event(desc='Updates the storage to the next time step')
def _update_storage_fired(self):
# update storage state
new_storage = self.storage - self.release + self.inflows
self.storage = min(new_storage, self.max_storage)
overflow = new_storage - self.max_storage
self.spillage = max(overflow, 0)
def print_state(self):
print 'Storage\tRelease\tInflows\tSpillage'
str_format = '\t'.join(['{:7.2f}'for i in range(4)])
print str_format.format(self.storage, self.release, self.inflows,
self.spillage)
print '-' * 79
if __name__ == '__main__':
projectA = Reservoir(
name = 'Project A',
max_storage = 30,
max_release = 5.0,
hydraulic_head = 60,
efficiency = 0.8
)
state = ReservoirState(reservoir=projectA, storage=15)
state.release = 5
state.inflows = 0
# release the maximum amount of water during 3 time steps
state.update_storage = True
state.print_state()
state.update_storage = True
state.print_state()
state.update_storage = True
state.print_state()

可以使用trait 属性自动实现对象之间的依赖关系。depends_on属性表示属性和其他属性之间的依赖关系。当其他traits更改时,属性将失效。同样,Traits使用魔术方法名称的属性:

  • _get_XXX用于XXX属性trait的getter
  • _set_XXX用于XXX属性trait的设置器
from traits.api import HasTraits, Instance, DelegatesTo, Float, Range
from traits.api import Property
from reservoir import Reservoir
class ReservoirState(HasTraits):
"""Keeps track of the reservoir state given the initial storage.
For the simplicity of the example, the release is considered in
hm3/timestep and not in m3/s.
"""
reservoir = Instance(Reservoir, ())
max_storage = DelegatesTo('reservoir')
min_release = Float
max_release = DelegatesTo('reservoir')
# state attributes
storage = Property(depends_on='inflows, release')
# control attributes
inflows = Float(desc='Inflows [hm3]')
release = Range(low='min_release', high='max_release')
spillage = Property(
desc='Spillage [hm3]', depends_on=['storage', 'inflows', 'release']
)
### Private traits.
_storage = Float
### Traits property implementation.
def _get_storage(self):
new_storage = self._storage - self.release + self.inflows
return min(new_storage, self.max_storage)
def _set_storage(self, storage_value):
self._storage = storage_value
def _get_spillage(self):
new_storage = self._storage - self.release + self.inflows
overflow = new_storage - self.max_storage
return max(overflow, 0)
def print_state(self):
print 'Storage\tRelease\tInflows\tSpillage'
str_format = '\t'.join(['{:7.2f}'for i in range(4)])
print str_format.format(self.storage, self.release, self.inflows,
self.spillage)
print '-' * 79
if __name__ == '__main__':
projectA = Reservoir(
name = 'Project A',
max_storage = 30,
max_release = 5,
hydraulic_head = 60,
efficiency = 0.8
)
state = ReservoirState(reservoir=projectA, storage=25)
state.release = 4
state.inflows = 0
state.print_state()

注意

缓存属性

当访问输入没有改变的属性时,大量计算或长时间运行的计算可能是一个问题。@cached_property装饰器可用于缓存值,并且只有在无效后才重新计算它们。

让我们用ReservoirState例子扩展TraitsUI的介绍:

from traits.api import HasTraits, Instance, DelegatesTo, Float, Range, Property
from traitsui.api import View, Item, Group, VGroup
from reservoir import Reservoir
class ReservoirState(HasTraits):
"""Keeps track of the reservoir state given the initial storage.
For the simplicity of the example, the release is considered in
hm3/timestep and not in m3/s.
"""
reservoir = Instance(Reservoir, ())
name = DelegatesTo('reservoir')
max_storage = DelegatesTo('reservoir')
max_release = DelegatesTo('reservoir')
min_release = Float
# state attributes
storage = Property(depends_on='inflows, release')
# control attributes
inflows = Float(desc='Inflows [hm3]')
release = Range(low='min_release', high='max_release')
spillage = Property(
desc='Spillage [hm3]', depends_on=['storage', 'inflows', 'release']
)
### Traits view
traits_view = View(
Group(
VGroup(Item('name'), Item('storage'), Item('spillage'),
label = 'State', style = 'readonly'
),
VGroup(Item('inflows'), Item('release'), label='Control'),
)
)
### Private traits.
_storage = Float
### Traits property implementation.
def _get_storage(self):
new_storage = self._storage - self.release + self.inflows
return min(new_storage, self.max_storage)
def _set_storage(self, storage_value):
self._storage = storage_value
def _get_spillage(self):
new_storage = self._storage - self.release + self.inflows
overflow = new_storage - self.max_storage
return max(overflow, 0)
def print_state(self):
print 'Storage\tRelease\tInflows\tSpillage'
str_format = '\t'.join(['{:7.2f}'for i in range(4)])
print str_format.format(self.storage, self.release, self.inflows,
self.spillage)
print '-' * 79
if __name__ == '__main__':
projectA = Reservoir(
name = 'Project A',
max_storage = 30,
max_release = 5,
hydraulic_head = 60,
efficiency = 0.8
)
state = ReservoirState(reservoir=projectA, storage=25)
state.release = 4
state.inflows = 0
state.print_state()
state.configure_traits()
../../_images/reservoir_state_view.png

一些用例需要用户在设置trait的值时中断委托机制。PrototypeFrom trait实现此行为。

from traits.api import HasTraits, Str, Float, Range, PrototypedFrom, Instance
class Turbine(HasTraits):
turbine_type = Str
power = Float(1.0, desc='Maximal power delivered by the turbine [Mw]')
class Reservoir(HasTraits):
name = Str
max_storage = Float(1e6, desc='Maximal storage [hm3]')
max_release = Float(10, desc='Maximal release [m3/s]')
head = Float(10, desc='Hydraulic head [m]')
efficiency = Range(0, 1.)
turbine = Instance(Turbine)
installed_capacity = PrototypedFrom('turbine', 'power')
if __name__ == '__main__':
turbine = Turbine(turbine_type='type1', power=5.0)
reservoir = Reservoir(
name = 'Project A',
max_storage = 30,
max_release = 100.0,
head = 60,
efficiency = 0.8,
turbine = turbine,
)
print 'installed capacity is initialised with turbine.power'
print reservoir.installed_capacity
print '-' * 15
print 'updating the turbine power updates the installed capacity'
turbine.power = 10
print reservoir.installed_capacity
print '-' * 15
print 'setting the installed capacity breaks the link between turbine.power'
print 'and the installed_capacity trait'
reservoir.installed_capacity = 8
print turbine.power, reservoir.installed_capacity

3.4.3.6. 通知

Traits实现了一个Listener模式。对于每个trait,静态和动态侦听器的列表可以被馈送回调。当特性改变时,所有的监听器都被调用。

静态侦听器使用_XXX_changed魔法方法来定义:

from traits.api import HasTraits, Instance, DelegatesTo, Float, Range
from reservoir import Reservoir
class ReservoirState(HasTraits):
"""Keeps track of the reservoir state given the initial storage.
"""
reservoir = Instance(Reservoir, ())
min_storage = Float
max_storage = DelegatesTo('reservoir')
min_release = Float
max_release = DelegatesTo('reservoir')
# state attributes
storage = Range(low='min_storage', high='max_storage')
# control attributes
inflows = Float(desc='Inflows [hm3]')
release = Range(low='min_release', high='max_release')
spillage = Float(desc='Spillage [hm3]')
def print_state(self):
print 'Storage\tRelease\tInflows\tSpillage'
str_format = '\t'.join(['{:7.2f}'for i in range(4)])
print str_format.format(self.storage, self.release, self.inflows,
self.spillage)
print '-' * 79
### Traits listeners ###########
def _release_changed(self, new):
"""When the release is higher than zero, warn all the inhabitants of
the valley.
"""
if new > 0:
print 'Warning, we are releasing {} hm3 of water'.format(new)
if __name__ == '__main__':
projectA = Reservoir(
name = 'Project A',
max_storage = 30,
max_release = 100.0,
hydraulic_head = 60,
efficiency = 0.8
)
state = ReservoirState(reservoir=projectA, storage=10)
state.release = 90
state.inflows = 0
state.print_state()

静态特征通知签名可以是:

  • def _release_changed(self):
    通过
  • def _release_changed(self,new):
    通过
  • def _release_changed(self,old,new):
    通过
  • def _release_changed(self,name,old,new
    通过

收听所有更改

要监听HasTraits类的所有更改,可以实现魔术_any_trait_changed方法。

在许多情况下,您不会提前知道需要激活什么类型的侦听器。Traits提供了使用动态侦听器即时注册侦听器的能力

from reservoir import Reservoir
from reservoir_state_property import ReservoirState
def wake_up_watchman_if_spillage(new_value):
if new_value > 0:
print 'Wake up watchman! Spilling {} hm3'.format(new_value)
if __name__ == '__main__':
projectA = Reservoir(
name = 'Project A',
max_storage = 30,
max_release = 100.0,
hydraulic_head = 60,
efficiency = 0.8
)
state = ReservoirState(reservoir=projectA, storage=10)
#register the dynamic listener
state.on_trait_change(wake_up_watchman_if_spillage, name='spillage')
state.release = 90
state.inflows = 0
state.print_state()
print 'Forcing spillage'
state.inflows = 100
state.release = 0
print 'Why do we have two executions of the callback ?'

动态特征通知签名与静态特征不同:

  • def wake_up_watchman():
    通过
  • def wake_up_watchman(新):
    通过
  • def wake_up_watchman(name,new):
    通过
  • def wake_up_watchman(object,name,new):
    通过
  • def wake_up_watchman(object,name,old,new):
    通过
删除动态侦听器可以通过以下方式完成:
  • 使用侦听器方法作为参数调用trait上的remove_trait_listener方法,
  • 调用带有listener方法的on_trait_change方法和关键字remove = True,
  • 删除保存侦听器的实例。

也可以使用on_trait_change装饰器将侦听器添加到类中:

from traits.api import HasTraits, Instance, DelegatesTo, Float, Range
from traits.api import Property, on_trait_change
from reservoir import Reservoir
class ReservoirState(HasTraits):
"""Keeps track of the reservoir state given the initial storage.
For the simplicity of the example, the release is considered in
hm3/timestep and not in m3/s.
"""
reservoir = Instance(Reservoir, ())
max_storage = DelegatesTo('reservoir')
min_release = Float
max_release = DelegatesTo('reservoir')
# state attributes
storage = Property(depends_on='inflows, release')
# control attributes
inflows = Float(desc='Inflows [hm3]')
release = Range(low='min_release', high='max_release')
spillage = Property(
desc='Spillage [hm3]', depends_on=['storage', 'inflows', 'release']
)
### Private traits. ##########
_storage = Float
### Traits property implementation.
def _get_storage(self):
new_storage = self._storage - self.release + self.inflows
return min(new_storage, self.max_storage)
def _set_storage(self, storage_value):
self._storage = storage_value
def _get_spillage(self):
new_storage = self._storage - self.release + self.inflows
overflow = new_storage - self.max_storage
return max(overflow, 0)
@on_trait_change('storage')
def print_state(self):
print 'Storage\tRelease\tInflows\tSpillage'
str_format = '\t'.join(['{:7.2f}'for i in range(4)])
print str_format.format(self.storage, self.release, self.inflows,
self.spillage)
print '-' * 79
if __name__ == '__main__':
projectA = Reservoir(
name = 'Project A',
max_storage = 30,
max_release = 5,
hydraulic_head = 60,
efficiency = 0.8
)
state = ReservoirState(reservoir=projectA, storage=25)
state.release = 4
state.inflows = 0

on_trait_change方法和装饰器支持的模式是强大的。读者应该看看HasTraits.on_trait_change的docstring的细节。

3.4.3.7. 一些更高级的特征

以下示例演示了枚举和列表特征的用法:

from traits.api import HasTraits, Str, Float, Range, Enum, List
from traitsui.api import View, Item
class IrrigationArea(HasTraits):
name = Str
surface = Float(desc='Surface [ha]')
crop = Enum('Alfalfa', 'Wheat', 'Cotton')
class Reservoir(HasTraits):
name = Str
max_storage = Float(1e6, desc='Maximal storage [hm3]')
max_release = Float(10, desc='Maximal release [m3/s]')
head = Float(10, desc='Hydraulic head [m]')
efficiency = Range(0, 1.)
irrigated_areas = List(IrrigationArea)
def energy_production(self, release):
''' Returns the energy production [Wh] for the given release [m3/s]
'''
power = 1000 * 9.81 * self.head * release * self.efficiency
return power * 3600
traits_view = View(
Item('name'),
Item('max_storage'),
Item('max_release'),
Item('head'),
Item('efficiency'),
Item('irrigated_areas'),
resizable = True
)
if __name__ == '__main__':
upper_block = IrrigationArea(name='Section C', surface=2000, crop='Wheat')
reservoir = Reservoir(
name='Project A',
max_storage=30,
max_release=100.0,
head=60,
efficiency=0.8,
irrigated_areas=[upper_block]
)
release = 80
print 'Releasing {} m3/s produces {} kWh'.format(
release, reservoir.energy_production(release)
)

特性监听器可以用于监听列表的内容的变化,例如跟踪链接到给定储层的总作物表面。

from traits.api import HasTraits, Str, Float, Range, Enum, List, Property
from traitsui.api import View, Item
class IrrigationArea(HasTraits):
name = Str
surface = Float(desc='Surface [ha]')
crop = Enum('Alfalfa', 'Wheat', 'Cotton')
class Reservoir(HasTraits):
name = Str
max_storage = Float(1e6, desc='Maximal storage [hm3]')
max_release = Float(10, desc='Maximal release [m3/s]')
head = Float(10, desc='Hydraulic head [m]')
efficiency = Range(0, 1.)
irrigated_areas = List(IrrigationArea)
total_crop_surface = Property(depends_on='irrigated_areas.surface')
def _get_total_crop_surface(self):
return sum([iarea.surface for iarea in self.irrigated_areas])
def energy_production(self, release):
''' Returns the energy production [Wh] for the given release [m3/s]
'''
power = 1000 * 9.81 * self.head * release * self.efficiency
return power * 3600
traits_view = View(
Item('name'),
Item('max_storage'),
Item('max_release'),
Item('head'),
Item('efficiency'),
Item('irrigated_areas'),
Item('total_crop_surface'),
resizable = True
)
if __name__ == '__main__':
upper_block = IrrigationArea(name='Section C', surface=2000, crop='Wheat')
reservoir = Reservoir(
name='Project A',
max_storage=30,
max_release=100.0,
head=60,
efficiency=0.8,
irrigated_areas=[upper_block],
)
release = 80
print 'Releasing {} m3/s produces {} kWh'.format(
release, reservoir.energy_production(release)
)

下一个示例显示了如何使用数组特性来提供特定的TraitsUI项目ChacoPlotItem:

import numpy as np
from traits.api import HasTraits, Array, Instance, Float, Property
from traits.api import DelegatesTo
from traitsui.api import View, Item, Group
from chaco.chaco_plot_editor import ChacoPlotItem
from reservoir import Reservoir
class ReservoirEvolution(HasTraits):
reservoir = Instance(Reservoir)
name = DelegatesTo('reservoir')
inflows = Array(dtype=np.float64, shape=(None))
releass = Array(dtype=np.float64, shape=(None))
initial_stock = Float
stock = Property(depends_on='inflows, releases, initial_stock')
month = Property(depends_on='stock')
### Traits view ###########
traits_view = View(
Item('name'),
Group(
ChacoPlotItem('month', 'stock', show_label=False),
),
width = 500,
resizable = True
)
### Traits properties #####
def _get_stock(self):
"""
fixme: should handle cases where we go over the max storage
"""
return self.initial_stock + (self.inflows - self.releases).cumsum()
def _get_month(self):
return np.arange(self.stock.size)
if __name__ == '__main__':
reservoir = Reservoir(
name = 'Project A',
max_storage = 30,
max_release = 100.0,
head = 60,
efficiency = 0.8
)
initial_stock = 10.
inflows_ts = np.array([6., 6, 4, 4, 1, 2, 0, 0, 3, 1, 5, 3])
releases_ts = np.array([4., 5, 3, 5, 3, 5, 5, 3, 2, 1, 3, 3])
view = ReservoirEvolution(
reservoir = reservoir,
inflows = inflows_ts,
releases = releases_ts
)
view.configure_traits()
../../_images/reservoir_evolution.png

也可以看看

参考文献