1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
|
#------------------------------------------------------------------------------
# Copyright (c) 2015-2024, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
#------------------------------------------------------------------------------
""" An example which demonstrates how to observe a model signal.
This examples uses a model with a signal to notify listeners about in
place changes to a list. This pattern is interesting for the times when
a ContainerList would emit too many synchronous notifications. A common
example is reordering the elements in a list.
<< autodoc-me >>
"""
from atom.api import Atom, List, Signal, Str
from enaml.core.api import Looper
from enaml.widgets.api import Window, Form, PushButton, Field, Menu, Action
class Item(Atom):
"""Object to store in a list and display.
"""
text = Str()
class Model(Atom):
""" A model which manages a collection of values.
This model expose a list which should be considered read-only and
methods to manipulate it. The `values_changed` signal is emitted
when an in-place change occurs in the list.
"""
values = List(default=[Item(text='val')])
values_changed = Signal()
def add_value(self, index, val):
""" Add a value at a specified index.
"""
self.values.insert(index, val)
self.values_changed('add')
def move_value(self, old, new):
""" Move a value from one index to another.
"""
val = self.values.pop(old)
self.values.insert(new, val)
self.values_changed('moved')
def delete_value(self, index):
""" Delete a value.
"""
del self.values[index]
self.values_changed('deleted')
enamldef EditMenu(Menu):
""" A menu used to edit the content of the list.
The visible menu items will vary to show appropriate actions based
on the current model state.
"""
attr model: Model
attr index: int
Action:
text = 'Add before'
triggered :: model.add_value(index,
Item(text='item %d' % len(model.values)))
Action:
text = 'Add after'
triggered :: model.add_value(index + 1,
Item(text='item %d' % len(model.values)))
Action:
separator = True
Action:
visible = index != 0
text = 'Move up'
triggered :: model.move_value(index, index - 1)
Action:
visible = index != len(model.values) - 1
text = 'Move down'
triggered :: model.move_value(index, index + 1)
Action:
separator = True
Action:
visible = len(model.values) > 1
text = 'Delete'
triggered :: model.delete_value(index)
enamldef Main(Window):
""" The main window which displays the list contents as a form.
"""
attr model = Model()
attr _values = model.values[:]
# Subscribe to the model when the window initializes.
initialized :: model.observe('values_changed', on_changed)
func on_changed(kind):
self._values = model.values[:]
func open_menu(item):
EditMenu(model=model, index=_values.index(item)).popup()
Form:
# Note that a Looper expects to iterate over unique values. Passing
# duplicate values can lead to crashes.
Looper:
iterable << _values
PushButton:
text = '>'
constraints = [width == 15, height == 20]
font = 'bold 12pt Consolas'
clicked :: open_menu(loop.item)
Field:
read_only = True
text = loop.item.text
|