ipysheet icon indicating copy to clipboard operation
ipysheet copied to clipboard

observe triggers twice when doing a round trip

Open krey opened this issue 6 years ago • 3 comments

Related to https://github.com/QuantStack/ipysheet/issues/66

To illustrate, a simple app that formats floating point user input

import ipysheet
import ipywidgets as widgets

as_list = True

sheet = ipysheet.Sheet(rows=1, columns=1)
if as_list:
    sheet.cells = ipysheet.Cell(value=[""], row_start=0, row_end=0, column_start=0, column_end=0,
                                squeeze_column=True, squeeze_row=False),
else:
    sheet.cells = ipysheet.Cell(value="", row_start=0, row_end=0, column_start=0, column_end=0,
                                squeeze_column=True, squeeze_row=True),

output = widgets.Output()

updating = False

def update(change):
    global updating
    with output:
        if updating:
            print('internal trigger')
            print(change)
        else:
            print('external trigger')
            print(change)
            updating = True
            if as_list:
                sheet.cells[0].value = ['{:.4f}'.format(float(entry)) for entry in change.new]
            else:
                sheet.cells[0].value = '{:.4f}'.format(float(change.new))
            updating = False
            
sheet.cells[0].observe(update, names='value')

sheet.cells[0].value = ['-.4'] if as_list else '-.4'

This puts the following in the output widget:

external trigger
{'name': 'value', 'old': [''], 'new': ['-.4'], 'owner': Cell(choice=[], column_end=0, column_start=0, row_end=0, row_start=0, squeeze_row=False, value=['-.4']), 'type': 'change'}
internal trigger
{'name': 'value', 'old': ['-.4'], 'new': ['-0.4000'], 'owner': Cell(choice=[], column_end=0, column_start=0, row_end=0, row_start=0, squeeze_row=False, value=['-0.4000']), 'type': 'change'}
external trigger
{'name': 'value', 'old': ['-0.4000'], 'new': ['-.4'], 'owner': Cell(choice=[], column_end=0, column_start=0, row_end=0, row_start=0, squeeze_row=False, value=['-.4']), 'type': 'change'}
internal trigger
{'name': 'value', 'old': ['-.4'], 'new': ['-0.4000'], 'owner': Cell(choice=[], column_end=0, column_start=0, row_end=0, row_start=0, squeeze_row=False, value=['-0.4000']), 'type': 'change'}

This shows that there's an extra trait notification, trying to "update" the new value to the original input.

Setting as_list = False uses a scalar instead of a list and does not suffer from this problem

external trigger
{'name': 'value', 'old': '', 'new': '-.4', 'owner': Cell(choice=[], column_end=0, column_start=0, row_end=0, row_start=0, value='-.4'), 'type': 'change'}
internal trigger
{'name': 'value', 'old': '-.4', 'new': '-0.4000', 'owner': Cell(choice=[], column_end=0, column_start=0, row_end=0, row_start=0, value='-0.4000'), 'type': 'change'}

The double notification in the as_list = True case leads to flickering and I'm not sure how to work around it yet.

krey avatar Mar 14 '19 01:03 krey

That is really weird... I displayed the traceback using print_stack and it shows that the second external event has not the same origin. We should try to replicate it using a core widget and post an issue in ipywidgets.

In the meantime I can only try to help you with your use case. If what you want to do is format the float numbers in the cells you can do:

import ipysheet

sheet = ipysheet.sheet()
ipysheet.cell(0, 0, -0.4, numeric_format='0.000')
display(sheet)

martinRenou avatar Mar 14 '19 14:03 martinRenou

Thanks @martinRenou

numeric_format seems useful. I'd also like to replace non-numeric inputs with the empty string.

I tried to replicate the issue using core widgets (Select.options instead of Cell.value) but didn't succeed.

Let me know if you have any ideas

p.s. Do you think https://github.com/QuantStack/ipysheet/issues/66 is unrelated?

krey avatar Mar 14 '19 15:03 krey

I honestly have no clue. This is really internal behavior of traitlets and ipywidgets and I'm not really aware of it. But this 'old': ['-0.4000'], 'new': ['-.4'] thingy is really odd, I don't see why it should happen. I guess it's a bug.

The thing is that you are setting the value of the cell in the callback of an observe call, which is not really common use case. I guess what you would need is a traitlets validator instead?

martinRenou avatar Mar 14 '19 15:03 martinRenou