1

考虑一个简单的外汇计算器应用程序的例子。

我可以使用以下方法定义我的模型traitlets

from traitlets import HasTraits, Float, observe, Enum
import math

class FXModel(HasTraits):
    domestic_qty = Float()
    foreign_qty = Float()
    fx_rate  = Float(float('nan')) # in units of domestic_qty/foreign_qty
    lock = Enum(['domestic', 'foreign'], default_value='domestic')
    _calculating = Enum([None, 'domestic', 'foreign'], default_value=None)

    def calc_foreign(self):
        if not math.isnan(self.fx_rate):
            self._calculating = 'foreign'
            self.foreign_qty = self.domestic_qty / self.fx_rate
            self._calculating = None

    def calc_domestic(self):
        if not math.isnan(self.fx_rate):
            self._calculating = 'domestic'
            self.domestic_qty = self.foreign_qty * self.fx_rate
            self._calculating = None

    @observe('domestic_qty')
    def on_domestic(self, change):
        if self._calculating is None:
            self.calc_foreign()

    @observe('foreign_qty')
    def on_foreign(self, change):
        if self._calculating is None:
            self.calc_domestic()

    @observe('fx_rate')
    def on_fxrate(self, change):
        if self.lock == 'domestic':
            self.calc_foreign()
        else:
            self.calc_domestic()

以及一个相应的简单的基于“打印”的视图:

class FXView:
    def __init__(self, model):
        self.model = model

    def show(self):
        print("""
        domestic_qty: {:.4g}
        foreign_qty:  {:.4g}
        fx_rate:      {:.4g}
        lock:         {}""".format(
            self.model.domestic_qty,
            self.model.foreign_qty,
            self.model.fx_rate,
            self.model.lock
        ))

以下是它的工作原理:

>> fx_model = FXModel(domestic_qty = 100., fx_rate = 200.)
>> fx_view = FXView(fx_model)
>> fx_view.show()
    domestic_qty: 100
    foreign_qty:  0.5
    fx_rate:      200
    lock:         domestic
>> fx_model.fx_rate = 195.
>> fx_view.show()
    domestic_qty: 100
    foreign_qty:  0.5128
    fx_rate:      195
    lock:         domestic

我还使用 ipywidgets 创建了一个视图:

import ipywidgets as widgets
domestic_label = widgets.Label("Domestic quantity")
domestic_field = widgets.FloatText()

foreign_label = widgets.Label("Foreign quantity")
foreign_field = widgets.FloatText()

fx_label = widgets.Label("Exchange rate (domestic/foreign)")
fx_field = widgets.FloatText()

lock_label = widgets.Label("If rates change, keep ")
lock_field = widgets.Dropdown(options=["domestic", "foreign"])
lock_label_post = widgets.Label('fixed')

ipyview = widgets.HBox([widgets.VBox([domestic_label, foreign_label, fx_label, lock_label]),
              widgets.VBox([domestic_field, foreign_field, fx_field, widgets.HBox([lock_field, lock_label_post])])])

它看起来真的很好:

在此处输入图像描述

我的问题是;我怎样才能将我的模型和我的 ipyview“绑定”在一起?我有一些经验,enaml可以通过运营商:=和朋友来实现。

这样做的最佳方法是什么ipywidgets

4

1 回答 1

4

好问题!我在 ipywidgets 问题中发布了答案:https ://github.com/jupyter-widgets/ipywidgets/issues/2296

所以这里是如何使用 ipywidget 视图制作自定义视图,使用帖子中的小部件。关键是将属性绑定到小部件值的链接调用。

import ipywidgets as widgets
from traitlets import link
from IPython.display import display

class FXWidgetView:
    def __init__(self, model):
        self.model = model
        self.domestic_label = widgets.Label("Domestic quantity")
        self.domestic_field = widgets.FloatText()

        self.foreign_label = widgets.Label("Foreign quantity")
        self.foreign_field = widgets.FloatText()

        self.fx_label = widgets.Label("Exchange rate (domestic/foreign)")
        self.fx_field = widgets.FloatText()

        self.lock_label = widgets.Label("If rates change, keep ")
        self.lock_field = widgets.Dropdown(options=["domestic", "foreign"])
        self.lock_label_post = widgets.Label('fixed')

        self.ipyview = widgets.HBox([widgets.VBox([self.domestic_label, self.foreign_label, self.fx_label, self.lock_label]),
                      widgets.VBox([self.domestic_field, self.foreign_field, self.fx_field, widgets.HBox([self.lock_field, self.lock_label_post])])])

        link((model, 'domestic_qty'), (self.domestic_field, 'value'))
        link((model, 'foreign_qty'), (self.foreign_field, 'value'))
        link((model, 'fx_rate'), (self.fx_field, 'value'))
        link((model, 'lock'), (self.lock_field, 'value'))

    def _ipython_display_(self):
        display(self.ipyview)

我要指出,Param 一直在提倡这种分离,@jbednar 指出 ipywidgets 也可以实现便利功能来支持这种模式。我认为这是一个好主意 - 拥有一些简单的便利函数,这些函数比采用 HasTraits 类的交互函数更上一层楼,对其进行自省,并为常见情况的不同特征提供默认小部件。

于 2019-01-17T18:46:20.243 回答