diff --git a/notebooks/VegaHeatmap.ipynb b/notebooks/VegaHeatmap.ipynb index 63a99757..f4e16b57 100644 --- a/notebooks/VegaHeatmap.ipynb +++ b/notebooks/VegaHeatmap.ipynb @@ -10,25 +10,10 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "downtown-toronto", "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "0620ef3a615c47b89511634a5bab0624", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(VegaWidget(), IntProgress(value=0, description='progress')))" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "import numpy as np\n", "import time\n", @@ -69,7 +54,8 @@ " # widget.update('data', remove='datum.x>=0')\n", " #print(\"displayed\", widget._displayed)\n", " widget._displayed = True\n", - " widget.update_dataframe('data', source, remove=\"true\")\n", + " widget.update_dataframe('data', source, remove=\"true\", \n", + " touch_mode=True, touch=True)\n", "\n", "await progressive_loop(widget)" ] @@ -84,25 +70,12 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "393a3fc4", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "2c3a03a90b554107bb871868609e1e02", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(VegaWidget(), IntProgress(value=0, description='progress'), Text(value='', description='Mean ti…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "metadata": { + "scrolled": false + }, + "outputs": [], "source": [ "import time\n", "import numpy as np\n", @@ -158,7 +131,8 @@ " value = hist\n", " widget._displayed = True\n", " start = time.time()\n", - " widget.update_array2d('data', value, ['x', 'y', 'z'], remove=\"true\")\n", + " widget.update_array2d('data', value, ['x', 'y', 'z'], remove=\"true\", \n", + " touch_mode=True, touch=True)\n", " times.append(time.time() - start)\n", " mean.value = f\"{np.mean(times)*1000:.3f}ms\"\n", " std.value = f\"{np.std(times)*1000:.3f}ms\"\n", @@ -191,7 +165,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.4" + "version": "3.9.5" } }, "nbformat": 4, diff --git a/vega/dataframes/numpy_adapter.py b/vega/dataframes/numpy_adapter.py index 2eaf1d2f..2d97d1b8 100644 --- a/vega/dataframes/numpy_adapter.py +++ b/vega/dataframes/numpy_adapter.py @@ -11,8 +11,9 @@ def __init__(self, source, *args, **kw): assert source is None or isinstance( source, dict ) # TODO: check values are ndarrays - source = {k:v.copy() for (k, v) in source.items()} super().__init__(source, *args, **kw) + if not self._touch_mode: + self._source = {k:v.copy() for (k, v) in self._source.items()} @property def columns(self): diff --git a/vega/dataframes/source_adapter.py b/vega/dataframes/source_adapter.py index 074cb2a1..db0b216f 100644 --- a/vega/dataframes/source_adapter.py +++ b/vega/dataframes/source_adapter.py @@ -2,10 +2,27 @@ class SourceAdapter(metaclass=ABCMeta): - def __init__(self, source, columns=None, compression=None): + def __init__(self, source, columns=None, compression=None, touch_mode=False): self._source = source self._columns = None self._compression = compression + self._touched = False + self._touch_mode = touch_mode + + @property + def is_touched(self): + ret = self._touched + self._touched = False + return ret + + def touch(self): + assert self._touch_mode + self._touched = True + + def _equals_no_touch_mode(self, other): + if self._touch_mode or other._touch_mode: + return True + return self.equals(other) @abstractproperty def columns(self): diff --git a/vega/dataframes/traitlets.py b/vega/dataframes/traitlets.py index cbeddd92..62c8975a 100644 --- a/vega/dataframes/traitlets.py +++ b/vega/dataframes/traitlets.py @@ -26,9 +26,10 @@ def set(self, obj, value): old_value = obj._trait_values.get(self.name, self.default_value) obj._trait_values[self.name] = new_value if ( - (old_value is None and new_value is not None) - or (old_value is Undefined and new_value is not Undefined) - or not old_value.equals(new_value) + (old_value is None and new_value is not None) + or (old_value is Undefined and new_value is not Undefined) + or (old_value.is_touched or new_value.is_touched) + or not old_value._equals_no_touch_mode(new_value) ): obj._notify_trait(self.name, old_value, new_value) diff --git a/vega/widget.py b/vega/widget.py index 0feae1ca..e07f9127 100644 --- a/vega/widget.py +++ b/vega/widget.py @@ -157,7 +157,8 @@ def update(self, key, remove=None, insert=None): else: self._pending_updates.append(update) - def update_dataframe(self, key, df, remove=None): + def update_dataframe(self, key, df, remove=None, + touch_mode=False, touch=False): """Update the chart data with a DataFrame. Updates are only reflected on the client, i.e., after re-displaying @@ -177,17 +178,20 @@ def update_dataframe(self, key, df, remove=None): property ``t < 5``. """ if isinstance(df, pd.DataFrame): - self._df = PandasAdapter(df) + self._df = PandasAdapter(df, touch_mode=touch_mode) else: assert isinstance(df, SourceAdapter) self._df = df + if touch: + self._df.touch() update = dict(key=key) if remove is not None: update['remove'] = remove update['insert'] = "@dataframe" self.send(dict(type="update", updates=[update])) - def update_array2d(self, key, arr, columns, remove=None): + def update_array2d(self, key, arr, columns, remove=None, + touch_mode=False, touch=False): """Update the chart data with a 2d array and their column names. Updates are only reflected on the client, i.e., after re-displaying @@ -217,10 +221,12 @@ def update_array2d(self, key, arr, columns, remove=None): """ if isinstance(arr, np.ndarray): fancy_col = ','.join(columns) - self._df = NumpyAdapter({fancy_col: arr}) + self._df = NumpyAdapter({fancy_col: arr}, touch_mode=touch_mode) else: assert isinstance(arr, SourceAdapter) - self._df = arr + self._df = arr + if touch: + self._df.touch() update = dict(key=key) if remove is not None: update['remove'] = remove