nucleic / enaml

Declarative User Interfaces for Python
http://enaml.readthedocs.io/en/latest/
Other
1.54k stars 131 forks source link

ComboBox entry fails to update? #389

Closed bredelings closed 2 years ago

bredelings commented 4 years ago

Hi, I'm new to enaml, and I can't figure out why a ComboBox entry fails to update until the entire ComboBox is recreated (I think) because a new entry is added.

Part of the code looks like this:

        ObjectCombo: combo:
            items << [index for index in range(len(model.smodels))]
            to_string = lambda index: model.get_substitution_model_name(index)
        Conditional:
            condition << combo.selected is not None
            SubstitutionModelView:
                 smodel := model.smodels[combo.selected]

It seems that the ComboBox names (generated by the to_string lambda) don't get updated when the function value changes. However, if we add a new item to the ComboBox, then the number of items changes and the updated string is used. Am I missing something here?

This is a reduced testcase, the real case involves field names not updating in a table until I add a new row.

bredelings commented 4 years ago
from enaml.core.api import Conditional, Looper

from atom.api import Atom, Str, Range, Bool, Value, Int, Tuple, observe, ContainerList

from enaml.widgets.api import (
    Window, Label, Field, Form, DateSelector, CheckBox, GroupBox, Container,
    PushButton, MainWindow, MenuBar, Notebook, Page, FileDialogEx, ToolBar, ActionGroup, Action, VGroup, ObjectCombo
)

from enaml.widgets.menu import Menu
from enaml.widgets.action import Action

class Partition(Atom):
    substitution_model = Int()

class SubstitutionModel(Atom):
    name = Str()

    model = Str()

class ATModel(Atom):
    """A collection of partitions that can share models

    """

    partitions = ContainerList(Partition)
    smodels = ContainerList(SubstitutionModel)

    def get_substitution_model_name(self,index):
        print(f'get substitution model name {index}')
        if self.smodels[index].name:
            print("using the name {}".format(self.smodels[index].name))
            return self.smodels[index].name
        else:
            print("no name: using the name S{}".format(index))
            return 'S{}'.format(index+1)

    # branch_lengths = BranchLengthModel()
    def add_partition(self):
        smodel = len(self.smodels)
        self.smodels.append(SubstitutionModel(model='tn93'))

        partition = Partition(substitution_model = smodel)
        self.partitions.append(partition)

    def remove_partition(self):
        if len(self.partitions) > 0:
            self.partitions.pop()

enamldef SubstitutionModelView(Form):
    attr smodel
    Label:
        text = 'Name'
    Field:
        text := smodel.name
    Label:
        text = 'Model'
    Field:
        text := smodel.model

enamldef Main(Window):
    attr model = ATModel()
    Container:
        PushButton:
            text = 'Add partition'
            clicked :: model.add_partition()
        PushButton:
            text = 'Remove partition'
            clicked :: model.remove_partition()
        Label:
            text = 'Substitution Models'
        ObjectCombo: combo:
            items << [index for index in range(len(model.smodels))]
            to_string = lambda index: model.get_substitution_model_name(index)
        Conditional:
            condition << combo.selected is not None
            SubstitutionModelView:
                 smodel := model.smodels[combo.selected]
MatthieuDartiailh commented 4 years ago

Enaml has no way to track that output of the function change because some complicated inner part of you model changed. To get this to work you need induced a change on a part of the model tracked by the view. I will try to elaborate later if it is not clear.

MatthieuDartiailh commented 2 years ago

Closing for lack of activity.

As mentioned above enaml cannot react to a change stemming from a change in behavior inside a function. Passing as arguments all the elements that should cause a recomputation is one way to avoid the issue.