Enaml - subscription to TraitsList attribute

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Enaml - subscription to TraitsList attribute

Colm Ryan
Hello folks,

We're converting over to Enaml but I had some problems getting the correct change notifiers to fire for a enamldef attribute which dynamically gets set to a TraitsList.  There seems to be code in enaml to get_iter and assign the list_items notifier name but I can't figure out how to get it called.  As it is, I have to reassign the entire list to get the subscription notifier called whereas I'd like in-place changes such as append/pop to all trigger a refresh.  There is a toy example below which sets up a ListControl using a Looper and then tries to add items to the list.  The list is updated but the Looper doesn't refresh the view.

Thanks,

Colm 


########### ListTest.enaml ############
----------------------------------------------------

from enaml.widgets.api import Window, Container, ListControl, ListItem, PushButton, Field
from enaml.layout.api import hbox, vbox, spacer
from enaml.core.api import Looper

from TestClasses import A

enamldef VContainer(Container):
    """ A container which arranges its contents vertically and which
    updates the contraints when the child widgets change.

    """
    constraints << [vbox(*(widgets + (spacer,)))]

enamldef ListTest(Container):
id: listView
attr myList 
ListControl:
Looper: listLooper:
iterable << myList
ListItem:
text := loop_item.name
editable = True
VContainer:
Looper:
iterable << myList
Field:
text := loop_item.name
PushButton:
text = "Add item..."
clicked :: 
print('Adding item to list of length {}'.format(len(listView.myList)))
listView.myList.append(A(name='newItem'))

enamldef ListTestView(Window):
id: main
attr myB
ListTest:
myList := main.myB.AList


################# TestClasses.py ######################
----------------------------------------------------------------------------------------
from traits.api import HasTraits, List, Str, on_trait_change

class A(HasTraits):
name = Str

class B(HasTraits):
AList = List(A)

@on_trait_change('AList_items')
def list_changed_printer(self):
print("The list was changed!")
##################### ListTest.py ##########################
import enaml
from enaml.stdlib.sessions import show_simple_view
from TestClasses import A,B


with enaml.imports():
from ListTestView import ListTestView

myB = B()
myB.AList.append(A(name='A1'))
myB.AList.append(A(name='A2'))

show_simple_view(ListTestView(myB=myB))

_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev
Reply | Threaded
Open this post in threaded view
|

Re: Enaml - subscription to TraitsList attribute

Chris Colbert
For this line:

iterable << mylist

The only dependency is on the mylist attribute. In order to indicate you care about the items, you need to iterate the list. So doing the following, while redundant, will trigger the 'get_iter' handler of the code tracer:

iterable << [item for item in mylist]

As an aside, your definition of VContainer is a bit redundant since what you have declared is the default behavior of a Container:


On Tue, Feb 26, 2013 at 7:49 AM, Colm Ryan <[hidden email]> wrote:
Hello folks,

We're converting over to Enaml but I had some problems getting the correct change notifiers to fire for a enamldef attribute which dynamically gets set to a TraitsList.  There seems to be code in enaml to get_iter and assign the list_items notifier name but I can't figure out how to get it called.  As it is, I have to reassign the entire list to get the subscription notifier called whereas I'd like in-place changes such as append/pop to all trigger a refresh.  There is a toy example below which sets up a ListControl using a Looper and then tries to add items to the list.  The list is updated but the Looper doesn't refresh the view.

Thanks,

Colm 


########### ListTest.enaml ############
----------------------------------------------------

from enaml.widgets.api import Window, Container, ListControl, ListItem, PushButton, Field
from enaml.layout.api import hbox, vbox, spacer
from enaml.core.api import Looper

from TestClasses import A

enamldef VContainer(Container):
    """ A container which arranges its contents vertically and which
    updates the contraints when the child widgets change.

    """
    constraints << [vbox(*(widgets + (spacer,)))]

enamldef ListTest(Container):
id: listView
attr myList 
ListControl:
Looper: listLooper:
iterable << myList
ListItem:
text := loop_item.name
editable = True
VContainer:
Looper:
iterable << myList
Field:
text := loop_item.name
PushButton:
text = "Add item..."
clicked :: 
print('Adding item to list of length {}'.format(len(listView.myList)))
listView.myList.append(A(name='newItem'))

enamldef ListTestView(Window):
id: main
attr myB
ListTest:
myList := main.myB.AList


################# TestClasses.py ######################
----------------------------------------------------------------------------------------
from traits.api import HasTraits, List, Str, on_trait_change

class A(HasTraits):
name = Str

class B(HasTraits):
AList = List(A)

@on_trait_change('AList_items')
def list_changed_printer(self):
print("The list was changed!")
##################### ListTest.py ##########################
import enaml
from enaml.stdlib.sessions import show_simple_view
from TestClasses import A,B


with enaml.imports():
from ListTestView import ListTestView

myB = B()
myB.AList.append(A(name='A1'))
myB.AList.append(A(name='A2'))

show_simple_view(ListTestView(myB=myB))

_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev



_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev
Reply | Threaded
Open this post in threaded view
|

Re: Enaml - subscription to TraitsList attribute

Chris Colbert
As another note, its not really a good idea to use a Looper if you are going to append and pop from the list. The Looper has to destroy and rebuild all of the items each time the iterable changes. If you are doing this a lot, that can get expensive. In such cases, it can be more efficient to use an Include which offers more flexibility in managing the child objects.


On Tue, Feb 26, 2013 at 5:06 PM, Chris Colbert <[hidden email]> wrote:
For this line:

iterable << mylist

The only dependency is on the mylist attribute. In order to indicate you care about the items, you need to iterate the list. So doing the following, while redundant, will trigger the 'get_iter' handler of the code tracer:

iterable << [item for item in mylist]

As an aside, your definition of VContainer is a bit redundant since what you have declared is the default behavior of a Container:


On Tue, Feb 26, 2013 at 7:49 AM, Colm Ryan <[hidden email]> wrote:
Hello folks,

We're converting over to Enaml but I had some problems getting the correct change notifiers to fire for a enamldef attribute which dynamically gets set to a TraitsList.  There seems to be code in enaml to get_iter and assign the list_items notifier name but I can't figure out how to get it called.  As it is, I have to reassign the entire list to get the subscription notifier called whereas I'd like in-place changes such as append/pop to all trigger a refresh.  There is a toy example below which sets up a ListControl using a Looper and then tries to add items to the list.  The list is updated but the Looper doesn't refresh the view.

Thanks,

Colm 


########### ListTest.enaml ############
----------------------------------------------------

from enaml.widgets.api import Window, Container, ListControl, ListItem, PushButton, Field
from enaml.layout.api import hbox, vbox, spacer
from enaml.core.api import Looper

from TestClasses import A

enamldef VContainer(Container):
    """ A container which arranges its contents vertically and which
    updates the contraints when the child widgets change.

    """
    constraints << [vbox(*(widgets + (spacer,)))]

enamldef ListTest(Container):
id: listView
attr myList 
ListControl:
Looper: listLooper:
iterable << myList
ListItem:
text := loop_item.name
editable = True
VContainer:
Looper:
iterable << myList
Field:
text := loop_item.name
PushButton:
text = "Add item..."
clicked :: 
print('Adding item to list of length {}'.format(len(listView.myList)))
listView.myList.append(A(name='newItem'))

enamldef ListTestView(Window):
id: main
attr myB
ListTest:
myList := main.myB.AList


################# TestClasses.py ######################
----------------------------------------------------------------------------------------
from traits.api import HasTraits, List, Str, on_trait_change

class A(HasTraits):
name = Str

class B(HasTraits):
AList = List(A)

@on_trait_change('AList_items')
def list_changed_printer(self):
print("The list was changed!")
##################### ListTest.py ##########################
import enaml
from enaml.stdlib.sessions import show_simple_view
from TestClasses import A,B


with enaml.imports():
from ListTestView import ListTestView

myB = B()
myB.AList.append(A(name='A1'))
myB.AList.append(A(name='A2'))

show_simple_view(ListTestView(myB=myB))

_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev




_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev
Reply | Threaded
Open this post in threaded view
|

Re: Enaml - subscription to TraitsList attribute

Colm Ryan
Thanks for your quick replies Chris.  The list-comprehension worked perfectly.  I had started out with Include but ran into trouble with Stacks and StackItems and so switched to Looper to allow me to use the declarative style.  For example, how would I translate this declarative Looper to Include?

Stack:
    index << someThing
    Looper:
        StackItem:
            AView:
                myA = loop_item

My initial attempts were something like this which runs without complaint but doesn't show the stack view. 

Stack:
    index << someThing
  Include:
  objects << [StackItem(AView(myA=item)) for item in myList]



On Tue, Feb 26, 2013 at 7:11 PM, Chris Colbert <[hidden email]> wrote:
As another note, its not really a good idea to use a Looper if you are going to append and pop from the list. The Looper has to destroy and rebuild all of the items each time the iterable changes. If you are doing this a lot, that can get expensive. In such cases, it can be more efficient to use an Include which offers more flexibility in managing the child objects.


On Tue, Feb 26, 2013 at 5:06 PM, Chris Colbert <[hidden email]> wrote:
For this line:

iterable << mylist

The only dependency is on the mylist attribute. In order to indicate you care about the items, you need to iterate the list. So doing the following, while redundant, will trigger the 'get_iter' handler of the code tracer:

iterable << [item for item in mylist]

As an aside, your definition of VContainer is a bit redundant since what you have declared is the default behavior of a Container:


On Tue, Feb 26, 2013 at 7:49 AM, Colm Ryan <[hidden email]> wrote:
Hello folks,

We're converting over to Enaml but I had some problems getting the correct change notifiers to fire for a enamldef attribute which dynamically gets set to a TraitsList.  There seems to be code in enaml to get_iter and assign the list_items notifier name but I can't figure out how to get it called.  As it is, I have to reassign the entire list to get the subscription notifier called whereas I'd like in-place changes such as append/pop to all trigger a refresh.  There is a toy example below which sets up a ListControl using a Looper and then tries to add items to the list.  The list is updated but the Looper doesn't refresh the view.

Thanks,

Colm 


########### ListTest.enaml ############
----------------------------------------------------

from enaml.widgets.api import Window, Container, ListControl, ListItem, PushButton, Field
from enaml.layout.api import hbox, vbox, spacer
from enaml.core.api import Looper

from TestClasses import A

enamldef VContainer(Container):
    """ A container which arranges its contents vertically and which
    updates the contraints when the child widgets change.

    """
    constraints << [vbox(*(widgets + (spacer,)))]

enamldef ListTest(Container):
id: listView
attr myList 
ListControl:
Looper: listLooper:
iterable << myList
ListItem:
text := loop_item.name
editable = True
VContainer:
Looper:
iterable << myList
Field:
text := loop_item.name
PushButton:
text = "Add item..."
clicked :: 
print('Adding item to list of length {}'.format(len(listView.myList)))
listView.myList.append(A(name='newItem'))

enamldef ListTestView(Window):
id: main
attr myB
ListTest:
myList := main.myB.AList


################# TestClasses.py ######################
----------------------------------------------------------------------------------------
from traits.api import HasTraits, List, Str, on_trait_change

class A(HasTraits):
name = Str

class B(HasTraits):
AList = List(A)

@on_trait_change('AList_items')
def list_changed_printer(self):
print("The list was changed!")
##################### ListTest.py ##########################
import enaml
from enaml.stdlib.sessions import show_simple_view
from TestClasses import A,B


with enaml.imports():
from ListTestView import ListTestView

myB = B()
myB.AList.append(A(name='A1'))
myB.AList.append(A(name='A2'))

show_simple_view(ListTestView(myB=myB))

_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev




_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev



_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev
Reply | Threaded
Open this post in threaded view
|

Re: Enaml - subscription to TraitsList attribute

Chris Colbert
the problem with this line:

objects << [StackItem(AView(myA=item)) for item in myList]

is that you are using AView as the parent of StackItem. You need to invert the relationship:

enamldef MyItem(StackItem):
    attr some_item
    AView:
        myA = some_item

Stack:
   index << someThing
   Include:
       objects << [MyItem(some_item=item) for item in myList]

However, this is effectively equivalent to the Looper construct, in that the old items will be destroyed and everything will be regenerated when myList changes. If this is infrequent, you'll be fine. But if you find that you are constantly changing the items in the list, it will be more performant to write a bit of manual object management as demonstrated in this example:


Where some of the columns manually append and pop from the Include object list. You could get the same effect using a helper class which builds the sub-items for you:

 objects << some_helper.items

The Include is smart enough to only destroy/build the items that have changed.


On Wed, Feb 27, 2013 at 12:56 PM, Colm Ryan <[hidden email]> wrote:
Thanks for your quick replies Chris.  The list-comprehension worked perfectly.  I had started out with Include but ran into trouble with Stacks and StackItems and so switched to Looper to allow me to use the declarative style.  For example, how would I translate this declarative Looper to Include?

Stack:
    index << someThing
    Looper:
        StackItem:
            AView:
                myA = loop_item

My initial attempts were something like this which runs without complaint but doesn't show the stack view. 

Stack:
    index << someThing
  Include:
  objects << [StackItem(AView(myA=item)) for item in myList]



On Tue, Feb 26, 2013 at 7:11 PM, Chris Colbert <[hidden email]> wrote:
As another note, its not really a good idea to use a Looper if you are going to append and pop from the list. The Looper has to destroy and rebuild all of the items each time the iterable changes. If you are doing this a lot, that can get expensive. In such cases, it can be more efficient to use an Include which offers more flexibility in managing the child objects.


On Tue, Feb 26, 2013 at 5:06 PM, Chris Colbert <[hidden email]> wrote:
For this line:

iterable << mylist

The only dependency is on the mylist attribute. In order to indicate you care about the items, you need to iterate the list. So doing the following, while redundant, will trigger the 'get_iter' handler of the code tracer:

iterable << [item for item in mylist]

As an aside, your definition of VContainer is a bit redundant since what you have declared is the default behavior of a Container:


On Tue, Feb 26, 2013 at 7:49 AM, Colm Ryan <[hidden email]> wrote:
Hello folks,

We're converting over to Enaml but I had some problems getting the correct change notifiers to fire for a enamldef attribute which dynamically gets set to a TraitsList.  There seems to be code in enaml to get_iter and assign the list_items notifier name but I can't figure out how to get it called.  As it is, I have to reassign the entire list to get the subscription notifier called whereas I'd like in-place changes such as append/pop to all trigger a refresh.  There is a toy example below which sets up a ListControl using a Looper and then tries to add items to the list.  The list is updated but the Looper doesn't refresh the view.

Thanks,

Colm 


########### ListTest.enaml ############
----------------------------------------------------

from enaml.widgets.api import Window, Container, ListControl, ListItem, PushButton, Field
from enaml.layout.api import hbox, vbox, spacer
from enaml.core.api import Looper

from TestClasses import A

enamldef VContainer(Container):
    """ A container which arranges its contents vertically and which
    updates the contraints when the child widgets change.

    """
    constraints << [vbox(*(widgets + (spacer,)))]

enamldef ListTest(Container):
id: listView
attr myList 
ListControl:
Looper: listLooper:
iterable << myList
ListItem:
text := loop_item.name
editable = True
VContainer:
Looper:
iterable << myList
Field:
text := loop_item.name
PushButton:
text = "Add item..."
clicked :: 
print('Adding item to list of length {}'.format(len(listView.myList)))
listView.myList.append(A(name='newItem'))

enamldef ListTestView(Window):
id: main
attr myB
ListTest:
myList := main.myB.AList


################# TestClasses.py ######################
----------------------------------------------------------------------------------------
from traits.api import HasTraits, List, Str, on_trait_change

class A(HasTraits):
name = Str

class B(HasTraits):
AList = List(A)

@on_trait_change('AList_items')
def list_changed_printer(self):
print("The list was changed!")
##################### ListTest.py ##########################
import enaml
from enaml.stdlib.sessions import show_simple_view
from TestClasses import A,B


with enaml.imports():
from ListTestView import ListTestView

myB = B()
myB.AList.append(A(name='A1'))
myB.AList.append(A(name='A2'))

show_simple_view(ListTestView(myB=myB))

_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev




_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev



_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev



_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev