如何访问traits.api.Dict()下的字典?

时间:2013-12-12 04:23:02

标签: python traits traitsui

以下是shell失败的示例。

>>> from traits.api import Dict
>>> d=Dict()
>>> d['Foo']='BAR'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'Dict' object does not support item assignment

我一直在网上搜索,并没有指示如何使用Dict。

我正在尝试编写一个显示python字典内容的简单应用程序。这个链接(Defining view elements from dictionary elements in TraitsUI)是适度有用的,除了字典在某个poll_interval上更新,如果我在那里使用解决方案(在从HasTraits派生的类中包装一个普通的python dict),显示不会更新基础词典得到更新。

以下是我现在所拥有的相关部分。最后一个类几乎可以被忽略,我包含它的唯一原因是帮助理解我打算如何使用Dict。

pyNetObjDisplay.run_ext()从基类run()方法每次循环调用一次

class DictContainer(HasTraits):
    _dict = {}

    def __getattr__(self, key):
        return self._dict[key]  
    def __getitem__(self, key):
        return self._dict[key]
    def __setitem__(self, key, value):
        self._dict[key] = value
    def __delitem__(self, key, value):
        del self._dict[key]
    def __str__(self):
        return self._dict.__str__()
    def __repr__(self):
        return self._dict.__repr__()
    def has_key(self, key):
        return self._dict.has_key(key)


class displayWindow(HasTraits):
    _remote_data = Instance(DictContainer)
    _messages = Str('', desc='Field to display messages to the user.', label='Messages', multi_line=True)

    def __remote_data_default(self):
        tempDict = DictContainer()
        tempDict._dict = Dict
        #tempDict['FOO'] = 'BAR'
        sys.stderr.write('SETTING DEFAULT DICTIONARY:\t%s\n' % tempDict)
        return tempDict
    def __messages_default(self):
        tempStr = Str()
        tempStr = ''
        return tempStr
    def traits_view(self):
        return View(
            Item('object._remote_data', editor=ValueEditor()),
            Item('object._messages'),
            resizable=True
        )


class pyNetObjDisplay(pyNetObject.pyNetObjPubClient):
    '''A derived pyNetObjPubClient that stores remote data in a dictionary and displays it using traitsui.'''

    def __init__(self, hostname='localhost', port=54322, service='pyNetObject', poll_int=10.0):
        self._display = displayWindow()

        self.poll_int = poll_int
        super(pyNetObjDisplay, self).__init__(hostname, port, service)
        self._ui_running = False
        self._ui_pid = 0

        ### For Testing Only, REMOVE THESE LINES ###
        self.connect()
        self.ns_subscribe(service, 'FOO', poll_int)
        self.ns_subscribe(service, 'BAR', poll_int)
        self.ns_subscribe(service, 'BAZ', poll_int)
        ############################################

    def run_ext(self):
        if not self._ui_running:
            self._ui_running = True
            self._ui_pid = os.fork()
            if not self._ui_pid:
                time.sleep(1.25*self.poll_int)
                self._display.configure_traits()
        for ((service, namespace, key), value) in self._object_buffer:
            sys.stderr.write('TEST:\t' + str(self._display._remote_data) + '\n')
            if not self._display._remote_data.has_key(service):
                self._display._remote_data[service] = {}
            if not self._display._remote_data[service].has_key(namespace):
                #self._remote_data[service][namespace] = {}
                self._display._remote_data[service][namespace] = {}
            self._display._remote_data[service][namespace][key] = value

            msg = 'Got Published ((service, namespace, key), value) pair:\t((%s, %s, %s), %s)\n' % (service, namespace, key, value)
            sys.stderr.write(msg)
            self._display._messages += msg
            sys.stderr.write('REMOTE DATA:\n' + str(self._display._remote_data)
        self._object_buffer = []

2 个答案:

答案 0 :(得分:1)

我认为你的基本问题与生活在模型对象之外的特征的通知问题有关,而不是与“如何访问这些对象”本身有关[编辑:实际上这根本不是你的问题!但是,当我以我对以前遇到的问题的偏见心态阅读你的问题时,我认为你试图做的事情,无论如何我建议的解决方案仍然有效]。我最近遇到了这类问题,因为我决定如何设计我的程序(代码描述了一个GUI,它模块化地分离了它可以包含的非常复杂的数据集)。您可能已经找到了我的其他问题,因为您找到了第一个问题。

在远离GUI的复杂数据层次结构中存在大量数据并不是traitsui为您的应用程序考虑的设计,它会导致通知的各种问题。设计解决方案具有更扁平的设计,其中GUI信息更直接地集成到程序的不同部分中。

我认为通常可以使用各种变通方法(我在enabled_when listening outside model object中使用了一些不涉及字典的方法)。我不确定对你的词典问题最有设计友好的解决方案是什么,但有一件事是有效的并且不会干扰你的设计(但它仍然是一个“有点烦人”的解决方案)是让一切都在字典是HasTraits,因此将其标记为可听。像这样:

from traits.api import *
from traitsui.api import *
from traitsui.ui_editors.array_view_editor import ArrayViewEditor
import numpy as np

class DContainer(HasTraits):
    _dict=Dict
    def __getattr__(self, k):
        if k in self._dict:
            return self._dict[k]

class DItem(HasTraits):
    _item=Any

    def __init__(self,item):
        super(DItem,self).__init__()
        self._item=item
    def setitem(self,val):
        self._item=val
    def getitem(self):
        return self._item
    def traits_view(self):
        return View(Item('_item',editor=ArrayViewEditor()))

class LargeApplication(HasTraits):
  d=Instance(DContainer)
  stupid_listener=Any
  bn=Button('CLICKME')

  def _d_default(self):
    d=DContainer()
    d._dict={'a_stat':DItem(np.random.random((10,1))),
           'b_stat':DItem(np.random.random((10,10)))}
    return d

  def traits_view(self):
    v=View(
        Item('object.d.a_stat',editor=InstanceEditor(),style='custom'),
        Item('bn'),
        height=500,width=500)
    return v

  def _bn_fired(self):
    self.d.a_stat.setitem(np.random.random((10,1)))

LargeApplication().configure_traits()

答案 1 :(得分:0)

好的,我在这个问题中找到了答案(kindof):Traits List not reporting items added or removed

当将Dict或List对象作为属性包含在类中时,不应该这样做:

class Foo(HasTraits):
    def __init__(self):
        ### This will not work as expected!
        self.bar = Dict(desc='Description.', label='Name', value={})

而是这样做:

class Foo(HasTraits):
    def __init__(self):
        self.add_trait('bar', Dict(desc='Description.', label='Name', value={}) )

现在以下内容将起作用:

>>> f = Foo()
>>> f.bar['baz']='boo'
>>> f.bar['baz']
'boo'

不幸的是,由于某种原因,使用configure_traits()生成的GUI在基础数据更改时不会更新它的视图。以下是一些演示此问题的测试代码:

import os
import time
import sys
from traits.api import HasTraits, Str, Dict
from traitsui.api import View, Item, ValueEditor

class displayWindow(HasTraits):
    def __init__(self, **traits):
        super(displayWindow, self).__init__(**traits)
        self.add_trait('_remote_data', Dict(desc='Dictionary to store remote data in.', label='Data', value={}) )
        self.add_trait('_messages', Str(desc='Field to display messages to the user.', label='Messages', multi_line=True, value='') )

    def traits_view(self):
        return View(
            Item('object._remote_data', editor=ValueEditor()),
            Item('object._messages'),
            resizable=True
        )

class testObj(object):
    def __init__(self):
        super(testObj, self).__init__()
        self._display = displayWindow()
        self._ui_pid = 0
    def run(self):
        ### Run the GUI in the background
        self._ui_pid = os.fork()
        if not self._ui_pid:
            self._display.configure_traits()
        i = 0
        while True:
            self._display._remote_data[str(i)] = i
            msg = 'Added (key,value):\t("%s", %s)\n' % (str(i), i, )
            self._display._messages += msg
            sys.stderr.write(msg)
            time.sleep(5.0)
            i+=1
if __name__ == '__main__':
    f = testObj()
    f.run()