From 9434655e56ad09adf9721fc2bd5953551638ff98 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 17 Jul 2024 16:15:19 +0530 Subject: [PATCH 01/21] add ConnectivityMatrixEditor widget --- tvbwidgets/api.py | 1 + .../ui/connectivity_matrix_editor_widget.py | 145 ++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 tvbwidgets/ui/connectivity_matrix_editor_widget.py diff --git a/tvbwidgets/api.py b/tvbwidgets/api.py index 89daded..aa1c242 100644 --- a/tvbwidgets/api.py +++ b/tvbwidgets/api.py @@ -7,6 +7,7 @@ from .ui.connectivity_ipy.connectivity_widget import ConnectivityWidget from .ui.connectivity_react.connectivity_widget import ConnectivityWidgetReact +from .ui.connectivity_matrix_editor_widget import ConnectivityMatrixEditor from .ui.dicom_widget import DicomWidget from .ui.phase_plane_widget import PhasePlaneWidget from .ui.spacetime_widget import SpaceTimeVisualizerWidget diff --git a/tvbwidgets/ui/connectivity_matrix_editor_widget.py b/tvbwidgets/ui/connectivity_matrix_editor_widget.py new file mode 100644 index 0000000..a7e5b38 --- /dev/null +++ b/tvbwidgets/ui/connectivity_matrix_editor_widget.py @@ -0,0 +1,145 @@ +import math +import ipycanvas as canvas +import ipywidgets as widgets +import matplotlib.colors as mcolors +from IPython.display import display + +class ConnectivityMatrixEditor(): + def __init__(self, connectivity): + self.connectivity = connectivity + self.weights = self.connectivity.weights + self.tract_lengths = self.connectivity.tract_lengths + self.cell_size = 20 + self.num_rows = int(len(self.connectivity.weights[0])/2) + self.num_cols = int(len(self.connectivity.weights[1])/2) + self._make_header() + self.tab = widgets.Tab() + self._get_quadrant_range(selection = 1) + self._prepare_matrices_tab() + + def _make_header(self): + options = ["Quadrant 1", "Quadrant 2", "Quadrant 3", "Quadrant 4"] + self.quadrants = widgets.Dropdown(options = options) + self.quadrants.observe(self._on_quadrant_select, names = ["value"]) + self.header = widgets.HBox() + self.cell_value = widgets.Text(description = "value") + self.button = widgets.Button(description = "Change") + self.button.on_click(lambda change :self.on_apply_change(change)) + self.cell_value.layout.visibility = "hidden" + self.button.layout.visibility = "hidden" + self.cell_value.layout.width = "200px" + self.button.layout.width ="80px" + self.header.children = [self.quadrants, self.cell_value, self.button] + + def _on_quadrant_select(self, change): + selection = int(change["new"][-1]) + self.weights_matrix.clear() + self.tract_lengths_matrix.clear() + self._get_quadrant_range(selection) + self._prepare_matrices_tab() + + def _get_quadrant_range(self, selection): + if selection == 1: + from_row = 0 + from_col = 0 + elif selection == 2: + from_row = int(self.weights.shape[0]/2) + from_col = 0 + elif selection == 3: + from_row = 0 + from_col = int(self.weights.shape[0]/2) + else: + from_row = int(self.weights.shape[0]/2) + from_col = int(self.weights.shape[0]/2) + self.from_row = from_row + self.from_col = from_col + + def _prepare_matrices_tab(self): + self.weights_matrix = self._prepare_matrix("weights") + self.tract_lengths_matrix = self._prepare_matrix("tract_lengths") + self.weights_matrix.on_mouse_down(lambda x, y: self.on_cell_clicked(x, y, "weights")) + self.tract_lengths_matrix.on_mouse_down(lambda x, y: self.on_cell_clicked(x, y, "tract_lengths")) + self.tab.children = [self.weights_matrix, self.tract_lengths_matrix] + self.tab.set_title(0, "weights") + self.tab.set_title(1, "tract_lengths") + + def _prepare_matrix(self, matrix_name): + cell_size = self.cell_size + rows = self.num_rows + cols = self.num_cols + matrix_full = canvas.MultiCanvas(4, width=1200, height= 1200) + + matrix = matrix_full[0] + row_header = matrix_full[1] + column_header = matrix_full[2] + color_bar = matrix_full[3] + + row_header.rotate(math.radians(-90)) + row_header.translate(-200 ,0) + with canvas.hold_canvas(matrix_full): + for i in range(rows): + column_header.font = "15px px sans serif" + column_header.stroke_rect(60, (i + 7) * 20, 100, 20) + column_header.fill_text(f"{self.connectivity.region_labels[self.from_col + i]}", 70, (i + 8) * 20, max_width = 100) + row_header.font = "15px px sans serif" + row_header.stroke_rect(60, (i + 8) * 20, 100, 20) + row_header.fill_text(f"{self.connectivity.region_labels[self.from_row + i]}", 70, (i + 9) * 20, max_width = 100) + + for j in range(cols): + matrix.fill_style = self._generate_color(i, j, matrix_name) + matrix.fill_rect((j + 8) * cell_size, (i + 7) * cell_size, cell_size, cell_size) + matrix.stroke_rect((j + 8) * cell_size, (i + 7) * cell_size, cell_size, cell_size) + matrix.stroke_style = "black" + matrix.line_width = 2 + + gradient = color_bar.create_linear_gradient(1000, 150, 1000, 700, + [(i/len(self.colors),self.colors[i]) for i in range(len(self.colors))]) + color_bar.fill_style = gradient + color_bar.fill_rect(1000, 150, 30, 500) + color_bar.fill_style = "black" + matrix_name = getattr(self, matrix_name) + for i in range(7): + color_bar.fill_text(f"-{int(matrix_name.max()) * i / 6}", 1030, i * 80 + 160) + + return matrix_full + + def _generate_color(self, i, j, matrix_name , value = None): + self.colors = ["#66797b", "#543146", "#5a1c5d", "#b468ab", "#6ade42", "#27913c", "#1c464a", + "#247663", "#38bcaa", "#a9e9ff", "#61cfff", "#37a5c1", "#e4e4e2", "#ff9f25", + "#fb5226"] + + color_scheme = mcolors.LinearSegmentedColormap.from_list('color_scheme', self.colors) + matrix = getattr(self, matrix_name) + norm = mcolors.Normalize(vmin=0, vmax=matrix.max()) + if not value: + value = matrix[int(self.from_row + i)][int(self.from_col + j)] + color = color_scheme(norm(value)) + color = f"rgba({color[0]*255:.0f}, {color[1]*255:.0f}, {color[2]*255:.0f}, {color[3]:.2f})" + return color + + def on_cell_clicked(self, x, y, matrix_name): + self.clicked_matrix = matrix_name + col = (x // self.cell_size) - 8 + row = (y // self.cell_size) - 7 + if row > -1 and row < self.num_rows and col > -1 and col < self.num_cols: + self.row = row + self.col = col + matrix = getattr(self, matrix_name) + value = matrix[int(self.from_row + self.row)][int(self.from_col + self.col)] + self.cell_value.value = f"{value}" + self.cell_value.layout.visibility = "visible" + self.button.layout.visibility = "visible" + + def on_apply_change(self, change): + matrix_name = self.clicked_matrix + "_matrix" + matrix = getattr(self, matrix_name) + self.cell_value.layout.visibility = "hidden" + self.button.layout.visibility = "hidden" + with canvas.hold_canvas(matrix[0]): + matrix[0].fill_style = self._generate_color(self.row, self.col, self.clicked_matrix, float(self.cell_value.value)) + matrix[0].fill_rect((self.col + 8) * self.cell_size, (self.row + 7) * self.cell_size, self.cell_size, self.cell_size) + matrix[0].stroke_rect((self.col + 8) * self.cell_size, (self.row + 7) * self.cell_size, self.cell_size, self.cell_size) + + def display(self): + display(self.header) + display(self.tab) \ No newline at end of file From 46d9e511c4bd069a5a86bc1499770865183a5588 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 17 Jul 2024 16:19:55 +0530 Subject: [PATCH 02/21] add notebook for ConnectivityMatrixEditor widget --- notebooks/ConnectivityMatrixEditor.ipynb | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 notebooks/ConnectivityMatrixEditor.ipynb diff --git a/notebooks/ConnectivityMatrixEditor.ipynb b/notebooks/ConnectivityMatrixEditor.ipynb new file mode 100644 index 0000000..e69de29 From bb924b9f013bb1f7b53727d2a06b82c36881e1d4 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 17 Jul 2024 16:33:31 +0530 Subject: [PATCH 03/21] add notebook for ConnectivityMatrixEditor widget --- notebooks/ConnectivityMatrixEditor.ipynb | 62 ++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/notebooks/ConnectivityMatrixEditor.ipynb b/notebooks/ConnectivityMatrixEditor.ipynb index e69de29..631e79c 100644 --- a/notebooks/ConnectivityMatrixEditor.ipynb +++ b/notebooks/ConnectivityMatrixEditor.ipynb @@ -0,0 +1,62 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from tvbwidgets.ui.connectivity_matrix_editor_widget import ConnectivityMatrixEditor\n", + "from tvb.datatypes.connectivity import Connectivity\n", + "conn = Connectivity().from_file() #defaults to connectivity_76.zip\n", + "conn.configure()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "wid = ConnectivityMatrixEditor(conn)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "wid.display()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tvb-wid", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From d4b6e70e1c1e88428683d3e89fea21fec4e63ace Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 22 Jul 2024 12:35:34 +0530 Subject: [PATCH 04/21] add option to save connectivity and view saved connectivities --- .../ui/connectivity_matrix_editor_widget.py | 174 +++++++++++++----- 1 file changed, 131 insertions(+), 43 deletions(-) diff --git a/tvbwidgets/ui/connectivity_matrix_editor_widget.py b/tvbwidgets/ui/connectivity_matrix_editor_widget.py index a7e5b38..35f2bf6 100644 --- a/tvbwidgets/ui/connectivity_matrix_editor_widget.py +++ b/tvbwidgets/ui/connectivity_matrix_editor_widget.py @@ -1,14 +1,17 @@ import math +import numpy as np import ipycanvas as canvas import ipywidgets as widgets import matplotlib.colors as mcolors from IPython.display import display +from tvb.datatypes.connectivity import Connectivity class ConnectivityMatrixEditor(): def __init__(self, connectivity): self.connectivity = connectivity self.weights = self.connectivity.weights self.tract_lengths = self.connectivity.tract_lengths + self.connectivities_history = [self.connectivity] self.cell_size = 20 self.num_rows = int(len(self.connectivity.weights[0])/2) self.num_cols = int(len(self.connectivity.weights[1])/2) @@ -16,12 +19,14 @@ def __init__(self, connectivity): self.tab = widgets.Tab() self._get_quadrant_range(selection = 1) self._prepare_matrices_tab() + self.new_connectivity = self._prepare_new_connectivity() + def _make_header(self): + self.header = widgets.HBox() options = ["Quadrant 1", "Quadrant 2", "Quadrant 3", "Quadrant 4"] self.quadrants = widgets.Dropdown(options = options) self.quadrants.observe(self._on_quadrant_select, names = ["value"]) - self.header = widgets.HBox() self.cell_value = widgets.Text(description = "value") self.button = widgets.Button(description = "Change") self.button.on_click(lambda change :self.on_apply_change(change)) @@ -29,14 +34,15 @@ def _make_header(self): self.button.layout.visibility = "hidden" self.cell_value.layout.width = "200px" self.button.layout.width ="80px" - self.header.children = [self.quadrants, self.cell_value, self.button] + self.save_button = widgets.Button(description = "Save", layout = widgets.Layout(margin='0 0 0 auto')) + self.save_button.on_click(self.on_click_save) + self.save_button.layout.width ="100px" + self.header.children = [self.quadrants, self.cell_value, self.button, self.save_button, self._get_history_dropdown()] def _on_quadrant_select(self, change): selection = int(change["new"][-1]) - self.weights_matrix.clear() - self.tract_lengths_matrix.clear() self._get_quadrant_range(selection) - self._prepare_matrices_tab() + self._update_matrices_view() def _get_quadrant_range(self, selection): if selection == 1: @@ -65,58 +71,60 @@ def _prepare_matrices_tab(self): def _prepare_matrix(self, matrix_name): cell_size = self.cell_size - rows = self.num_rows - cols = self.num_cols - matrix_full = canvas.MultiCanvas(4, width=1200, height= 1200) + matrix = getattr(self.connectivity, matrix_name) + matrix_full = canvas.MultiCanvas(5, width=1200, height= 1200) - matrix = matrix_full[0] + matrix_view = matrix_full[0] row_header = matrix_full[1] column_header = matrix_full[2] color_bar = matrix_full[3] + grid = matrix_full[4] row_header.rotate(math.radians(-90)) row_header.translate(-200 ,0) + with canvas.hold_canvas(matrix_full): - for i in range(rows): + x= np.tile(np.linspace(160, 900, self.num_cols), self.num_cols) + y= np.repeat(np.linspace(140, 881, self.num_rows), self.num_rows) + grid.stroke_rects(x, y, height = cell_size, width = cell_size) + matrix_view.fill_styled_rects(x, y, color = self._generate_color(value=matrix[self.from_row : self.from_row + 38, self.from_col : self.from_col + 38], matrix_name=matrix_name), height = cell_size - 1, width = cell_size -1) + + for i in range(self.num_rows): + grid.stroke_rect( (i + 8) * cell_size, 60, cell_size, 100) + grid.stroke_rect(60, (i + 7) * cell_size, 100, cell_size) column_header.font = "15px px sans serif" - column_header.stroke_rect(60, (i + 7) * 20, 100, 20) - column_header.fill_text(f"{self.connectivity.region_labels[self.from_col + i]}", 70, (i + 8) * 20, max_width = 100) + column_header.fill_text(f"{self.connectivity.region_labels[self.from_col + i]}", 70, (i + 8) * cell_size, max_width = 100) row_header.font = "15px px sans serif" - row_header.stroke_rect(60, (i + 8) * 20, 100, 20) - row_header.fill_text(f"{self.connectivity.region_labels[self.from_row + i]}", 70, (i + 9) * 20, max_width = 100) + row_header.fill_text(f"{self.connectivity.region_labels[self.from_row + i]}", 70, (i + 9) * cell_size, max_width = 100) - for j in range(cols): - matrix.fill_style = self._generate_color(i, j, matrix_name) - matrix.fill_rect((j + 8) * cell_size, (i + 7) * cell_size, cell_size, cell_size) - matrix.stroke_rect((j + 8) * cell_size, (i + 7) * cell_size, cell_size, cell_size) - matrix.stroke_style = "black" - matrix.line_width = 2 - - gradient = color_bar.create_linear_gradient(1000, 150, 1000, 700, + gradient = grid.create_linear_gradient(1000, 150, 1000, 700, [(i/len(self.colors),self.colors[i]) for i in range(len(self.colors))]) - color_bar.fill_style = gradient - color_bar.fill_rect(1000, 150, 30, 500) - color_bar.fill_style = "black" - matrix_name = getattr(self, matrix_name) + grid.fill_style = gradient + grid.fill_rect(1000, 150, 30, 500) + grid.fill_style = "black" for i in range(7): - color_bar.fill_text(f"-{int(matrix_name.max()) * i / 6}", 1030, i * 80 + 160) + color_bar.fill_text(f"-{int(matrix.max()) * i / 6}", 1030, i * 80 + 160) return matrix_full - def _generate_color(self, i, j, matrix_name , value = None): + def _generate_color(self, i=0, j=0, matrix_name=None , value = None): self.colors = ["#66797b", "#543146", "#5a1c5d", "#b468ab", "#6ade42", "#27913c", "#1c464a", "#247663", "#38bcaa", "#a9e9ff", "#61cfff", "#37a5c1", "#e4e4e2", "#ff9f25", "#fb5226"] color_scheme = mcolors.LinearSegmentedColormap.from_list('color_scheme', self.colors) - matrix = getattr(self, matrix_name) + matrix = getattr(self.connectivity, matrix_name) norm = mcolors.Normalize(vmin=0, vmax=matrix.max()) - if not value: - value = matrix[int(self.from_row + i)][int(self.from_col + j)] - color = color_scheme(norm(value)) - color = f"rgba({color[0]*255:.0f}, {color[1]*255:.0f}, {color[2]*255:.0f}, {color[3]:.2f})" - return color - + if not isinstance(value, np.ndarray): + if not value: + value = matrix[int(self.from_row + i)][int(self.from_col + j)] + color = color_scheme(norm(value)) + color = f"rgba({color[0]*255:.0f}, {color[1]*255:.0f}, {color[2]*255:.0f}, {color[3]:.2f})" + return color + colors = color_scheme(norm(value)) + colors = colors[:, :, :3] * 255 + return colors + def on_cell_clicked(self, x, y, matrix_name): self.clicked_matrix = matrix_name col = (x // self.cell_size) - 8 @@ -124,7 +132,7 @@ def on_cell_clicked(self, x, y, matrix_name): if row > -1 and row < self.num_rows and col > -1 and col < self.num_cols: self.row = row self.col = col - matrix = getattr(self, matrix_name) + matrix = getattr(self.connectivity, matrix_name) value = matrix[int(self.from_row + self.row)][int(self.from_col + self.col)] self.cell_value.value = f"{value}" self.cell_value.layout.visibility = "visible" @@ -132,14 +140,94 @@ def on_cell_clicked(self, x, y, matrix_name): def on_apply_change(self, change): matrix_name = self.clicked_matrix + "_matrix" - matrix = getattr(self, matrix_name) + matrix_ui = getattr(self, matrix_name) self.cell_value.layout.visibility = "hidden" self.button.layout.visibility = "hidden" - with canvas.hold_canvas(matrix[0]): - matrix[0].fill_style = self._generate_color(self.row, self.col, self.clicked_matrix, float(self.cell_value.value)) - matrix[0].fill_rect((self.col + 8) * self.cell_size, (self.row + 7) * self.cell_size, self.cell_size, self.cell_size) - matrix[0].stroke_rect((self.col + 8) * self.cell_size, (self.row + 7) * self.cell_size, self.cell_size, self.cell_size) - + with canvas.hold_canvas(matrix_ui[0]): + matrix_ui[0].fill_style = self._generate_color(self.row, self.col, self.clicked_matrix, float(self.cell_value.value)) + matrix_ui[0].fill_rect((self.col + 8) * self.cell_size, (self.row + 7) * self.cell_size, self.cell_size, self.cell_size) + matrix_ui[0].stroke_rect((self.col + 8) * self.cell_size, (self.row + 7) * self.cell_size, self.cell_size, self.cell_size) + matrix_name = self.clicked_matrix + matrix = getattr(self.new_connectivity, matrix_name) + matrix[int(self.row)][int(self.col)] = float(self.cell_value.value) + + def get_connectivity(self, gid=None): + if gid is None: + return self.connectivity + for conn in self.connectivities_history: + if conn.gid.hex == gid.hex: + return conn + + def on_click_save(self, change): + conn = self.new_connectivity + self.connectivities_history.append(conn) + self.connectivity = self.new_connectivity + self.header.children = list(self.header.children)[:-1] + [self._get_history_dropdown()] + self.new_connectivity = self._prepare_new_connectivity() + self._get_quadrant_range(selection=1) + self._update_matrices_view() + + def _prepare_new_connectivity(self): + self.new_connectivity = Connectivity() + self.new_connectivity.parent_connectivity = self.connectivity.gid.hex + self.new_connectivity.centres = self.connectivity.centres + self.new_connectivity.region_labels = self.connectivity.region_labels + self.new_connectivity.orientations = self.connectivity.orientations + self.new_connectivity.cortical = self.connectivity.cortical + self.new_connectivity.hemispheres = self.connectivity.hemispheres + self.new_connectivity.areas = self.connectivity.areas + self.new_connectivity.weights = self.connectivity.weights + self.new_connectivity.tract_lengths = self.connectivity.tract_lengths + self.new_connectivity.configure() + return self.new_connectivity + + + def _get_history_dropdown(self): + values = [(conn.gid.hex, conn) for conn in self.connectivities_history] + default = len(values) and values[-1][1] or None + + dropdown = widgets.Dropdown(options=values, + description='View history', + disabled=False, + value=default, + ) + + def on_connectivity_change(change): + self.connectivity = change["new"] + self._get_quadrant_range(selection=1) + self._update_matrices_view() + + dropdown.observe(on_connectivity_change, 'value') + return dropdown + + def _update_matrices_view(self): + matrices = ["weights", "tract_lengths"] + for matrix_name in matrices: + matrix_view = getattr(self, matrix_name + "_matrix") + matrix = getattr(self.connectivity, matrix_name) + matrix_view[0].clear() + matrix_view[1].clear() + matrix_view[2].clear() + matrix_view[3].clear() + x = np.tile(np.linspace(160, 900, self.num_cols), self.num_cols) + y = np.repeat(np.linspace(140, 881, self.num_rows), self.num_rows) + value = matrix[self.from_row:self.from_row + self.num_rows,self.from_col:self.from_col + self.num_cols] + matrix_view[0].fill_styled_rects(x, y, color = self._generate_color(value=value, matrix_name=matrix_name), height = self.cell_size - 1, width = self.cell_size - 1) + max_value = int(matrix.max()) + region_labels = self.connectivity.region_labels + for i in range(self.num_rows): + row_label = region_labels[self.from_row + i] + matrix_view[1].fill_text(row_label, 70, (i + 9) * self.cell_size, max_width=100) + + for i in range(self.num_cols): + col_label = region_labels[self.from_col + i] + matrix_view[2].fill_text(col_label, 70, (i + 8) * self.cell_size, max_width=100) + + for i in range(7): + value = f"-{max_value * i / 6}" + matrix_view[3].fill_text(value, 1030, i * 80 + 160) + def display(self): display(self.header) - display(self.tab) \ No newline at end of file + display(self.tab) + \ No newline at end of file From 830eb9f6718dac5f9c982239f03cbe86e8a04a77 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 22 Jul 2024 14:11:18 +0530 Subject: [PATCH 05/21] update Connectivity Matrix Editor widget notebook --- notebooks/ConnectivityMatrixEditor.ipynb | 91 ++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 7 deletions(-) diff --git a/notebooks/ConnectivityMatrixEditor.ipynb b/notebooks/ConnectivityMatrixEditor.ipynb index 631e79c..5baf2fa 100644 --- a/notebooks/ConnectivityMatrixEditor.ipynb +++ b/notebooks/ConnectivityMatrixEditor.ipynb @@ -2,9 +2,20 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "22-07-2024 01:30:49 - DEBUG - tvbwidgets - Package is not fully installed\n", + "22-07-2024 01:30:49 - DEBUG - tvbwidgets - Version read from the internal package.json file\n", + "22-07-2024 01:30:50 - INFO - tvbwidgets - Version: 2.0.3\n", + "2024-07-22 13:30:57,554 - WARNING - tvb.basic.readers - File 'hemispheres' not found in ZIP.\n" + ] + } + ], "source": [ "from tvbwidgets.ui.connectivity_matrix_editor_widget import ConnectivityMatrixEditor\n", "from tvb.datatypes.connectivity import Connectivity\n", @@ -14,7 +25,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -23,13 +34,79 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "440195fb849c46a291922f499d807750", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HBox(children=(Dropdown(options=('Quadrant 1', 'Quadrant 2', 'Quadrant 3', 'Quadrant 4'), value='Quadrant 1'),…" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9cdcea32c3cd42c79e842fed2ffce7a2", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Tab(children=(MultiCanvas(height=1200, width=1200), MultiCanvas(height=1200, width=1200)), selected_index=0, t…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "wid.display()" ] }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "

Connectivity

\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
value
Number of connections
1560
Number of regions
76
Undirected
False
areas
 [min, median, max] = [0, 2580.89, 10338.2] dtype = float64 shape = (76,)
tract_lengths
 [min, median, max] = [0, 71.6635, 153.486] dtype = float64 shape = (76, 76)
tract_lengths (connections)
 [min, median, max] = [0, 55.8574, 138.454] dtype = float64 shape = (1560,)
tract_lengths-non-zero
 [min, median, max] = [4.93328, 74.0646, 153.486] dtype = float64 shape = (5402,)
weights
 [min, median, max] = [0, 0, 3] dtype = float64 shape = (76, 76)
weights-non-zero
 [min, median, max] = [0.00462632, 2, 3] dtype = float64 shape = (1560,)
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "wid.get_connectivity()" + ] + }, { "cell_type": "code", "execution_count": null, @@ -40,7 +117,7 @@ ], "metadata": { "kernelspec": { - "display_name": "tvb-wid", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -58,5 +135,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } From 14c4be7be7557ba6f3b10d42a709f8e21cea7253 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 Jul 2024 16:48:25 +0530 Subject: [PATCH 06/21] add ipycanvas as a dependency in requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 6c557ce..89f1a01 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,3 +21,4 @@ pyunicore>=1.0.0 traitlets>=5.7.1 toml bokeh +ipycanvas From c04c29608cf0a67af108d1b03d9248d28d3e2455 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 25 Jul 2024 18:24:47 +0530 Subject: [PATCH 07/21] Fixes in connectivity matrix editor widget -Fixed hardcoded size of widget -Made all cells change color when larger value is inputted -Add border -Adjusted colorbar bounds -Bolden text font --- .../ui/connectivity_matrix_editor_widget.py | 120 +++++++++++------- 1 file changed, 76 insertions(+), 44 deletions(-) diff --git a/tvbwidgets/ui/connectivity_matrix_editor_widget.py b/tvbwidgets/ui/connectivity_matrix_editor_widget.py index 35f2bf6..2cc3845 100644 --- a/tvbwidgets/ui/connectivity_matrix_editor_widget.py +++ b/tvbwidgets/ui/connectivity_matrix_editor_widget.py @@ -5,39 +5,46 @@ import matplotlib.colors as mcolors from IPython.display import display from tvb.datatypes.connectivity import Connectivity +from tvbwidgets.ui.base_widget import TVBWidget -class ConnectivityMatrixEditor(): - def __init__(self, connectivity): +class ConnectivityMatrixEditor(TVBWidget): + def __init__(self, connectivity, size = 600): + self.size = size self.connectivity = connectivity self.weights = self.connectivity.weights self.tract_lengths = self.connectivity.tract_lengths self.connectivities_history = [self.connectivity] - self.cell_size = 20 self.num_rows = int(len(self.connectivity.weights[0])/2) self.num_cols = int(len(self.connectivity.weights[1])/2) + self.cell_size = self.size / (self.num_rows + 5) + + self.header = widgets.HBox(layout = self.DEFAULT_BORDER) self._make_header() - self.tab = widgets.Tab() + self.tab = widgets.Tab(layout = self.DEFAULT_BORDER) self._get_quadrant_range(selection = 1) self._prepare_matrices_tab() self.new_connectivity = self._prepare_new_connectivity() + def _make_header(self): - self.header = widgets.HBox() options = ["Quadrant 1", "Quadrant 2", "Quadrant 3", "Quadrant 4"] + self.quadrants = widgets.Dropdown(options = options) self.quadrants.observe(self._on_quadrant_select, names = ["value"]) - self.cell_value = widgets.Text(description = "value") - self.button = widgets.Button(description = "Change") - self.button.on_click(lambda change :self.on_apply_change(change)) - self.cell_value.layout.visibility = "hidden" - self.button.layout.visibility = "hidden" - self.cell_value.layout.width = "200px" - self.button.layout.width ="80px" - self.save_button = widgets.Button(description = "Save", layout = widgets.Layout(margin='0 0 0 auto')) + + self.cell_value = widgets.Text(description = "value", + layout = widgets.Layout(width = "200px", visibility = "hidden")) + + self.change_button = widgets.Button(description = "Change", + layout = widgets.Layout(width = "80px", visibility = "hidden")) + self.change_button.on_click(lambda change :self.on_apply_change(change)) + + self.save_button = widgets.Button(description = "Save", + layout = widgets.Layout(width = "100px", margin='0 0 0 auto')) self.save_button.on_click(self.on_click_save) - self.save_button.layout.width ="100px" - self.header.children = [self.quadrants, self.cell_value, self.button, self.save_button, self._get_history_dropdown()] + + self.header.children = [self.quadrants, self.cell_value, self.change_button, self.save_button, self._get_history_dropdown()] def _on_quadrant_select(self, change): selection = int(change["new"][-1]) @@ -57,14 +64,17 @@ def _get_quadrant_range(self, selection): else: from_row = int(self.weights.shape[0]/2) from_col = int(self.weights.shape[0]/2) + self.from_row = from_row self.from_col = from_col def _prepare_matrices_tab(self): self.weights_matrix = self._prepare_matrix("weights") self.tract_lengths_matrix = self._prepare_matrix("tract_lengths") + self.weights_matrix.on_mouse_down(lambda x, y: self.on_cell_clicked(x, y, "weights")) self.tract_lengths_matrix.on_mouse_down(lambda x, y: self.on_cell_clicked(x, y, "tract_lengths")) + self.tab.children = [self.weights_matrix, self.tract_lengths_matrix] self.tab.set_title(0, "weights") self.tab.set_title(1, "tract_lengths") @@ -72,7 +82,7 @@ def _prepare_matrices_tab(self): def _prepare_matrix(self, matrix_name): cell_size = self.cell_size matrix = getattr(self.connectivity, matrix_name) - matrix_full = canvas.MultiCanvas(5, width=1200, height= 1200) + matrix_full = canvas.MultiCanvas(5, width=self.size + 10*cell_size, height=self.size + 10*cell_size) matrix_view = matrix_full[0] row_header = matrix_full[1] @@ -80,30 +90,33 @@ def _prepare_matrix(self, matrix_name): color_bar = matrix_full[3] grid = matrix_full[4] + #rotate the row_header canvas so they appear vertical row_header.rotate(math.radians(-90)) - row_header.translate(-200 ,0) + row_header.translate(-cell_size * 10 ,0) with canvas.hold_canvas(matrix_full): - x= np.tile(np.linspace(160, 900, self.num_cols), self.num_cols) - y= np.repeat(np.linspace(140, 881, self.num_rows), self.num_rows) + x= np.tile(np.linspace(cell_size * 8, cell_size * 45, self.num_cols), self.num_cols) #x-coordinates of cells + y= np.repeat(np.linspace(cell_size * 7, cell_size * 44, self.num_rows), self.num_rows) #y-coordinates of cells grid.stroke_rects(x, y, height = cell_size, width = cell_size) - matrix_view.fill_styled_rects(x, y, color = self._generate_color(value=matrix[self.from_row : self.from_row + 38, self.from_col : self.from_col + 38], matrix_name=matrix_name), height = cell_size - 1, width = cell_size -1) + colors = self._generate_color(value=matrix[self.from_row : self.from_row + self.num_rows, self.from_col : self.from_col + self.num_cols], matrix_name=matrix_name) + matrix_view.fill_styled_rects(x, y, color = colors, height = cell_size - 1, width = cell_size -1) for i in range(self.num_rows): - grid.stroke_rect( (i + 8) * cell_size, 60, cell_size, 100) - grid.stroke_rect(60, (i + 7) * cell_size, 100, cell_size) - column_header.font = "15px px sans serif" - column_header.fill_text(f"{self.connectivity.region_labels[self.from_col + i]}", 70, (i + 8) * cell_size, max_width = 100) - row_header.font = "15px px sans serif" - row_header.fill_text(f"{self.connectivity.region_labels[self.from_row + i]}", 70, (i + 9) * cell_size, max_width = 100) - - gradient = grid.create_linear_gradient(1000, 150, 1000, 700, - [(i/len(self.colors),self.colors[i]) for i in range(len(self.colors))]) + grid.stroke_rect(cell_size * 3, (i + 7) * cell_size, cell_size * 5, cell_size) #grid for row headers + grid.stroke_rect( (i + 8) * cell_size, cell_size * 2, cell_size, cell_size * 5) #grid for column headers + row_header.font = f"bold {self.cell_size * 0.90}px px sans serif" + row_header.fill_text(f"{self.connectivity.region_labels[self.from_row + i]}", cell_size * 3.5, (i + 9) * cell_size, max_width = cell_size * 5) + column_header.font = f"bold {self.cell_size * 0.90}px px sans serif" + column_header.fill_text(f"{self.connectivity.region_labels[self.from_col + i]}", cell_size * 3.5, (i + 8) * cell_size, max_width = cell_size * 5) + + gradient = grid.create_linear_gradient(cell_size * 47, cell_size * 7.5, cell_size * 47 , cell_size * 35, + [(i/len(self.colors),self.colors[-i-1]) for i in range(len(self.colors))]) #color gradient for color-bar grid.fill_style = gradient - grid.fill_rect(1000, 150, 30, 500) + grid.fill_rect(cell_size * 47, cell_size * 7.5, cell_size , cell_size * 25) grid.fill_style = "black" + for i in range(7): - color_bar.fill_text(f"-{int(matrix.max()) * i / 6}", 1030, i * 80 + 160) + color_bar.fill_text(f"-{int(matrix.max()) * (6 - i) / 6}", cell_size * 48, i * cell_size * 4 + cell_size * 8) #labels for colorbar return matrix_full @@ -115,12 +128,15 @@ def _generate_color(self, i=0, j=0, matrix_name=None , value = None): color_scheme = mcolors.LinearSegmentedColormap.from_list('color_scheme', self.colors) matrix = getattr(self.connectivity, matrix_name) norm = mcolors.Normalize(vmin=0, vmax=matrix.max()) + if not isinstance(value, np.ndarray): if not value: value = matrix[int(self.from_row + i)][int(self.from_col + j)] + color = color_scheme(norm(value)) color = f"rgba({color[0]*255:.0f}, {color[1]*255:.0f}, {color[2]*255:.0f}, {color[3]:.2f})" return color + colors = color_scheme(norm(value)) colors = colors[:, :, :3] * 255 return colors @@ -129,27 +145,40 @@ def on_cell_clicked(self, x, y, matrix_name): self.clicked_matrix = matrix_name col = (x // self.cell_size) - 8 row = (y // self.cell_size) - 7 + if row > -1 and row < self.num_rows and col > -1 and col < self.num_cols: self.row = row self.col = col matrix = getattr(self.connectivity, matrix_name) value = matrix[int(self.from_row + self.row)][int(self.from_col + self.col)] self.cell_value.value = f"{value}" + self.cell_value.layout.visibility = "visible" - self.button.layout.visibility = "visible" + self.change_button.layout.visibility = "visible" def on_apply_change(self, change): matrix_name = self.clicked_matrix + "_matrix" matrix_ui = getattr(self, matrix_name) + value = float(self.cell_value.value) + self.cell_value.layout.visibility = "hidden" - self.button.layout.visibility = "hidden" + self.change_button.layout.visibility = "hidden" + with canvas.hold_canvas(matrix_ui[0]): - matrix_ui[0].fill_style = self._generate_color(self.row, self.col, self.clicked_matrix, float(self.cell_value.value)) + matrix_ui[0].fill_style = self._generate_color(self.row, self.col, self.clicked_matrix, value) matrix_ui[0].fill_rect((self.col + 8) * self.cell_size, (self.row + 7) * self.cell_size, self.cell_size, self.cell_size) matrix_ui[0].stroke_rect((self.col + 8) * self.cell_size, (self.row + 7) * self.cell_size, self.cell_size, self.cell_size) + matrix_name = self.clicked_matrix matrix = getattr(self.new_connectivity, matrix_name) - matrix[int(self.row)][int(self.col)] = float(self.cell_value.value) + matrix[int(self.row)][int(self.col)] = value + matrix_original = getattr(self.connectivity, matrix_name) + + if value > matrix_original.max() or value < matrix_original.min(): + temp = self.connectivity + self.connectivity = self.new_connectivity + self._update_matrices_view() + self.connectivity = temp def get_connectivity(self, gid=None): if gid is None: @@ -179,6 +208,7 @@ def _prepare_new_connectivity(self): self.new_connectivity.weights = self.connectivity.weights self.new_connectivity.tract_lengths = self.connectivity.tract_lengths self.new_connectivity.configure() + return self.new_connectivity @@ -209,25 +239,27 @@ def _update_matrices_view(self): matrix_view[1].clear() matrix_view[2].clear() matrix_view[3].clear() - x = np.tile(np.linspace(160, 900, self.num_cols), self.num_cols) - y = np.repeat(np.linspace(140, 881, self.num_rows), self.num_rows) - value = matrix[self.from_row:self.from_row + self.num_rows,self.from_col:self.from_col + self.num_cols] + + x= np.tile(np.linspace(self.cell_size * 8, self.cell_size * 45, self.num_cols), self.num_cols) + y= np.repeat(np.linspace(self.cell_size * 7, self.cell_size * 44, self.num_rows), self.num_rows) + value = matrix[self.from_row:self.from_row + self.num_rows, self.from_col:self.from_col + self.num_cols] matrix_view[0].fill_styled_rects(x, y, color = self._generate_color(value=value, matrix_name=matrix_name), height = self.cell_size - 1, width = self.cell_size - 1) + max_value = int(matrix.max()) region_labels = self.connectivity.region_labels + for i in range(self.num_rows): row_label = region_labels[self.from_row + i] - matrix_view[1].fill_text(row_label, 70, (i + 9) * self.cell_size, max_width=100) + matrix_view[1].fill_text(row_label, self.cell_size * 3.5, (i + 9) * self.cell_size, max_width = self.cell_size * 5) for i in range(self.num_cols): col_label = region_labels[self.from_col + i] - matrix_view[2].fill_text(col_label, 70, (i + 8) * self.cell_size, max_width=100) + matrix_view[2].fill_text(col_label, self.cell_size * 3.5, (i + 8) * self.cell_size, max_width = self.cell_size * 5) for i in range(7): - value = f"-{max_value * i / 6}" - matrix_view[3].fill_text(value, 1030, i * 80 + 160) + value = f"-{max_value * (6 - i) / 6}" + matrix_view[3].fill_text(value, self.cell_size * 48, i * self.cell_size * 4 + self.cell_size * 8) def display(self): display(self.header) - display(self.tab) - \ No newline at end of file + display(self.tab) \ No newline at end of file From cb6ec793d6f6a9f5d0326b3900a062773a4a9098 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 25 Jul 2024 18:50:33 +0530 Subject: [PATCH 08/21] add header and description of connectivity matrix editor widget --- notebooks/ConnectivityMatrixEditor.ipynb | 106 ++++-------------- .../ui/connectivity_matrix_editor_widget.py | 11 ++ 2 files changed, 33 insertions(+), 84 deletions(-) diff --git a/notebooks/ConnectivityMatrixEditor.ipynb b/notebooks/ConnectivityMatrixEditor.ipynb index 5baf2fa..cc6c6d9 100644 --- a/notebooks/ConnectivityMatrixEditor.ipynb +++ b/notebooks/ConnectivityMatrixEditor.ipynb @@ -1,110 +1,43 @@ { "cells": [ { - "cell_type": "code", - "execution_count": 1, + "cell_type": "markdown", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "22-07-2024 01:30:49 - DEBUG - tvbwidgets - Package is not fully installed\n", - "22-07-2024 01:30:49 - DEBUG - tvbwidgets - Version read from the internal package.json file\n", - "22-07-2024 01:30:50 - INFO - tvbwidgets - Version: 2.0.3\n", - "2024-07-22 13:30:57,554 - WARNING - tvb.basic.readers - File 'hemispheres' not found in ZIP.\n" - ] - } - ], "source": [ - "from tvbwidgets.ui.connectivity_matrix_editor_widget import ConnectivityMatrixEditor\n", - "from tvb.datatypes.connectivity import Connectivity\n", - "conn = Connectivity().from_file() #defaults to connectivity_76.zip\n", - "conn.configure()" + "# Connectivity Matrix Editor Widget\n", + "\n", + "The connectivity matrix editor lets you edit the connectivity weights or tract_lengths, save the new connectivity, and view saved connectivities. Only 1 quadrant is displayed at a time.

\n", + "This work has been produced as part of GSOC 2024." ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "wid = ConnectivityMatrixEditor(conn)" + "from tvbwidgets.ui.connectivity_matrix_editor_widget import ConnectivityMatrixEditor\n", + "from tvb.datatypes.connectivity import Connectivity\n", + "conn = Connectivity().from_file() #defaults to connectivity_76.zip\n", + "conn.configure()" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "440195fb849c46a291922f499d807750", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "HBox(children=(Dropdown(options=('Quadrant 1', 'Quadrant 2', 'Quadrant 3', 'Quadrant 4'), value='Quadrant 1'),…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "9cdcea32c3cd42c79e842fed2ffce7a2", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Tab(children=(MultiCanvas(height=1200, width=1200), MultiCanvas(height=1200, width=1200)), selected_index=0, t…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ - "wid.display()" + "wid = ConnectivityMatrixEditor(conn, size = 600)" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "

Connectivity

\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
value
Number of connections
1560
Number of regions
76
Undirected
False
areas
 [min, median, max] = [0, 2580.89, 10338.2] dtype = float64 shape = (76,)
tract_lengths
 [min, median, max] = [0, 71.6635, 153.486] dtype = float64 shape = (76, 76)
tract_lengths (connections)
 [min, median, max] = [0, 55.8574, 138.454] dtype = float64 shape = (1560,)
tract_lengths-non-zero
 [min, median, max] = [4.93328, 74.0646, 153.486] dtype = float64 shape = (5402,)
weights
 [min, median, max] = [0, 0, 3] dtype = float64 shape = (76, 76)
weights-non-zero
 [min, median, max] = [0.00462632, 2, 3] dtype = float64 shape = (1560,)
" - ], - "text/plain": [ - "" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "wid.get_connectivity()" + "wid.display()" ] }, { @@ -112,7 +45,12 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "# replace None in the method below with the gid of the connectivity you wish to export\n", + "# if no gid is provided it will export the connectivity currently viewed\n", + "\n", + "wid.get_connectivity(gid=None)" + ] } ], "metadata": { diff --git a/tvbwidgets/ui/connectivity_matrix_editor_widget.py b/tvbwidgets/ui/connectivity_matrix_editor_widget.py index 2cc3845..c8980bd 100644 --- a/tvbwidgets/ui/connectivity_matrix_editor_widget.py +++ b/tvbwidgets/ui/connectivity_matrix_editor_widget.py @@ -1,3 +1,14 @@ +# -*- coding: utf-8 -*- +# +# "TheVirtualBrain - Widgets" package +# +# (c) 2022-2024, TVB Widgets Team +# +""" +..moduleauthor:: Priya Yadav + +""" + import math import numpy as np import ipycanvas as canvas From d201f7e5e89b6790b0b5ac721d25598c036aed08 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 29 Jul 2024 09:55:13 +0530 Subject: [PATCH 09/21] add tests for connectivity matrix editor widget --- .../test_connectivity_matrix_editor_widget.py | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 tvbwidgets/tests/test_connectivity_matrix_editor_widget.py diff --git a/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py b/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py new file mode 100644 index 0000000..9334613 --- /dev/null +++ b/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py @@ -0,0 +1,122 @@ +# -*- coding: utf-8 -*- +# +# "TheVirtualBrain - Widgets" package +# +# (c) 2022-2024, TVB Widgets Team +# + +import math +import pytest +import numpy as np +import ipycanvas as canvas +import ipywidgets as widgets +from tvb.datatypes.connectivity import Connectivity +from tvbwidgets.ui.connectivity_matrix_editor_widget import ConnectivityMatrixEditor + +@pytest.fixture +def connectivity(): + conn = Connectivity.from_file() + conn.configure() + return conn + +@pytest.fixture +def wid(connectivity): + widget = ConnectivityMatrixEditor(connectivity) + return widget + +def test_display(wid): + wid.display() + assert isinstance(wid, ConnectivityMatrixEditor) + +def test_make_header(wid): + assert len(wid.header.children) == 5 + assert isinstance(wid.header.children[0], widgets.Dropdown) + assert isinstance(wid.header.children[1], widgets.Text) + assert isinstance(wid.header.children[2], widgets.Button) + assert isinstance(wid.header.children[3], widgets.Button) + assert isinstance(wid.header.children[4], widgets.Dropdown) + +def test_get_quadrant_range(wid): + wid._get_quadrant_range(selection = 1) + assert wid.from_row == 0 + assert wid.from_col == 0 + wid._get_quadrant_range(selection = 2) + assert wid.from_row == 38 + assert wid.from_col == 0 + wid._get_quadrant_range(selection = 3) + assert wid.from_row == 0 + assert wid.from_col == 38 + wid._get_quadrant_range(selection = 4) + assert wid.from_col == 38 + assert wid.from_row == 38 + +def test_prepare_matrices_tab(wid): + assert isinstance(wid.weights_matrix, canvas.canvas.MultiCanvas) + assert isinstance(wid.tract_lengths_matrix, canvas.canvas.MultiCanvas) + assert len(wid.tab.children) == 2 + assert wid.tab.get_title(0) == "weights" + assert wid.tab.get_title(1) == "tract_lengths" + +def test_prepare_matrix(wid): + assert math.isclose(wid.cell_size, 13.95, abs_tol=0.1) + assert math.isclose(wid.weights_matrix.width, 739, abs_tol=1) + assert math.isclose(wid.weights_matrix.height, 739, abs_tol=1) + assert math.isclose(wid.tract_lengths_matrix.width, 739, abs_tol=1) + assert math.isclose(wid.tract_lengths_matrix.height, 739, abs_tol=1) + assert isinstance(wid.weights_matrix[0], canvas.canvas.Canvas) + assert isinstance(wid.weights_matrix[1], canvas.canvas.Canvas) + assert isinstance(wid.weights_matrix[2], canvas.canvas.Canvas) + assert isinstance(wid.weights_matrix[3], canvas.canvas.Canvas) + assert isinstance(wid.weights_matrix[4], canvas.canvas.Canvas) + assert isinstance(wid.tract_lengths_matrix[0], canvas.canvas.Canvas) + assert isinstance(wid.tract_lengths_matrix[1], canvas.canvas.Canvas) + assert isinstance(wid.tract_lengths_matrix[2], canvas.canvas.Canvas) + assert isinstance(wid.tract_lengths_matrix[3], canvas.canvas.Canvas) + assert isinstance(wid.tract_lengths_matrix[4], canvas.canvas.Canvas) + +def test_generate_color_with_indices(wid): + color = wid._generate_color(i=20, j=38, matrix_name="weights") + assert color == 'rgba(102, 121, 123, 1.00)' + color = wid._generate_color(i=5, j=70, matrix_name="tract_lengths") + assert color == 'rgba(38, 124, 105, 1.00)' + +def test_generate_color_with_single_value(wid): + color = wid._generate_color(value = 2, matrix_name="weights") + assert color == 'rgba(145, 224, 255, 1.00)' + color = wid._generate_color(value = 3, matrix_name = "tract_lengths") + assert color == 'rgba(97, 101, 108, 1.00)' + +def test_generate_color_with_array_value(wid): + value = np.array([[1, 2], [2, 3]]) + colors = wid._generate_color(value = value, matrix_name="weights") + assert np.allclose(colors, [[[ 61.33333333, 170.66666667, 62. ], + [145. , 224.33333333, 255. ]], + [[145. , 224.33333333, 255. ], + [251. , 82. , 38. ]]]) + + colors = wid._generate_color(value = value, matrix_name="tract_lengths") + assert np.allclose(colors, [[[101.01176471, 117.04705882, 120.09019608], + [ 99.03529412, 109.14117647, 114.27058824]], + [[ 99.03529412, 109.14117647, 114.27058824], + [ 97.05882353, 101.23529412, 108.45098039]]]) + +def test_get_connectivity(wid): + conn = wid.get_connectivity() + assert isinstance(conn, Connectivity) + assert conn == wid.connectivity + +def test_on_click_save(wid): + wid.save_button.click() + assert len(wid.connectivities_history) == 2 + +def test_prepare_new_connectivity(wid): + conn = wid._prepare_new_connectivity() + assert isinstance(conn, Connectivity) + assert wid.new_connectivity.parent_connectivity == wid.connectivity.gid.hex + assert wid.new_connectivity.centres.all() == wid.connectivity.centres.all() + assert wid.new_connectivity.orientations.all() == wid.connectivity.orientations.all() + assert wid.new_connectivity.cortical.all() == wid.connectivity.cortical.all() + assert wid.new_connectivity.hemispheres.all() == wid.connectivity.hemispheres.all() + assert wid.new_connectivity.areas.all() == wid.connectivity.areas.all() + assert wid.new_connectivity.weights.all() == wid.connectivity.weights.all() + assert wid.new_connectivity.tract_lengths.all() == wid.connectivity.tract_lengths.all() \ No newline at end of file From 1288568ca3ecba374042d04e2bb47faac9e66faa Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Jul 2024 11:42:38 +0530 Subject: [PATCH 10/21] Fix incorrect cell modification due to scrolling; round colorbar labels. --- .../ui/connectivity_matrix_editor_widget.py | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/tvbwidgets/ui/connectivity_matrix_editor_widget.py b/tvbwidgets/ui/connectivity_matrix_editor_widget.py index c8980bd..005e995 100644 --- a/tvbwidgets/ui/connectivity_matrix_editor_widget.py +++ b/tvbwidgets/ui/connectivity_matrix_editor_widget.py @@ -36,8 +36,6 @@ def __init__(self, connectivity, size = 600): self._prepare_matrices_tab() self.new_connectivity = self._prepare_new_connectivity() - - def _make_header(self): options = ["Quadrant 1", "Quadrant 2", "Quadrant 3", "Quadrant 4"] @@ -84,7 +82,10 @@ def _prepare_matrices_tab(self): self.tract_lengths_matrix = self._prepare_matrix("tract_lengths") self.weights_matrix.on_mouse_down(lambda x, y: self.on_cell_clicked(x, y, "weights")) + self.weights_matrix.on_mouse_move(self.set_mouse_position) + self.tract_lengths_matrix.on_mouse_down(lambda x, y: self.on_cell_clicked(x, y, "tract_lengths")) + self.tract_lengths_matrix.on_mouse_move(self.set_mouse_position) self.tab.children = [self.weights_matrix, self.tract_lengths_matrix] self.tab.set_title(0, "weights") @@ -127,7 +128,7 @@ def _prepare_matrix(self, matrix_name): grid.fill_style = "black" for i in range(7): - color_bar.fill_text(f"-{int(matrix.max()) * (6 - i) / 6}", cell_size * 48, i * cell_size * 4 + cell_size * 8) #labels for colorbar + color_bar.fill_text(f"-{round(int(matrix.max()) * (6 - i) / 6, 2)}", cell_size * 48, i * cell_size * 4 + cell_size * 8) #labels for colorbar return matrix_full @@ -151,11 +152,16 @@ def _generate_color(self, i=0, j=0, matrix_name=None , value = None): colors = color_scheme(norm(value)) colors = colors[:, :, :3] * 255 return colors + + def set_mouse_position(self, x, y): + self.x_coord = x + self.y_coord = y def on_cell_clicked(self, x, y, matrix_name): self.clicked_matrix = matrix_name - col = (x // self.cell_size) - 8 - row = (y // self.cell_size) - 7 + x_coord, y_coord = self.x_coord , self.y_coord + col = (x_coord // self.cell_size) - 8 + row = (y_coord // self.cell_size) - 7 if row > -1 and row < self.num_rows and col > -1 and col < self.num_cols: self.row = row @@ -221,8 +227,7 @@ def _prepare_new_connectivity(self): self.new_connectivity.configure() return self.new_connectivity - - + def _get_history_dropdown(self): values = [(conn.gid.hex, conn) for conn in self.connectivities_history] default = len(values) and values[-1][1] or None @@ -268,7 +273,7 @@ def _update_matrices_view(self): matrix_view[2].fill_text(col_label, self.cell_size * 3.5, (i + 8) * self.cell_size, max_width = self.cell_size * 5) for i in range(7): - value = f"-{max_value * (6 - i) / 6}" + value = f"-{round(max_value * (6 - i) / 6, 2)}" matrix_view[3].fill_text(value, self.cell_size * 48, i * self.cell_size * 4 + self.cell_size * 8) def display(self): From dbd9c293c6c00ca009e3ba3b52c37342932dca85 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 6 Aug 2024 14:32:19 +0530 Subject: [PATCH 11/21] Fixed incorrect color display issues in the widget --- .../ui/connectivity_matrix_editor_widget.py | 70 +++++++++++-------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/tvbwidgets/ui/connectivity_matrix_editor_widget.py b/tvbwidgets/ui/connectivity_matrix_editor_widget.py index 005e995..98993be 100644 --- a/tvbwidgets/ui/connectivity_matrix_editor_widget.py +++ b/tvbwidgets/ui/connectivity_matrix_editor_widget.py @@ -28,13 +28,14 @@ def __init__(self, connectivity, size = 600): self.num_rows = int(len(self.connectivity.weights[0])/2) self.num_cols = int(len(self.connectivity.weights[1])/2) self.cell_size = self.size / (self.num_rows + 5) - + + self.new_connectivity = self._prepare_new_connectivity() + self.is_matrix_saved = False self.header = widgets.HBox(layout = self.DEFAULT_BORDER) self._make_header() self.tab = widgets.Tab(layout = self.DEFAULT_BORDER) self._get_quadrant_range(selection = 1) self._prepare_matrices_tab() - self.new_connectivity = self._prepare_new_connectivity() def _make_header(self): options = ["Quadrant 1", "Quadrant 2", "Quadrant 3", "Quadrant 4"] @@ -56,9 +57,15 @@ def _make_header(self): self.header.children = [self.quadrants, self.cell_value, self.change_button, self.save_button, self._get_history_dropdown()] def _on_quadrant_select(self, change): + self.cell_value.layout.visibility = "hidden" + self.change_button.layout.visibility = "hidden" + + connectivity = self.connectivity if self.is_matrix_saved else self.new_connectivity selection = int(change["new"][-1]) self._get_quadrant_range(selection) - self._update_matrices_view() + self._update_matrices_view(connectivity) + + def _get_quadrant_range(self, selection): if selection == 1: @@ -110,7 +117,7 @@ def _prepare_matrix(self, matrix_name): x= np.tile(np.linspace(cell_size * 8, cell_size * 45, self.num_cols), self.num_cols) #x-coordinates of cells y= np.repeat(np.linspace(cell_size * 7, cell_size * 44, self.num_rows), self.num_rows) #y-coordinates of cells grid.stroke_rects(x, y, height = cell_size, width = cell_size) - colors = self._generate_color(value=matrix[self.from_row : self.from_row + self.num_rows, self.from_col : self.from_col + self.num_cols], matrix_name=matrix_name) + colors = self._generate_color(self.connectivity, value=matrix[self.from_row : self.from_row + self.num_rows, self.from_col : self.from_col + self.num_cols], matrix_name=matrix_name) matrix_view.fill_styled_rects(x, y, color = colors, height = cell_size - 1, width = cell_size -1) for i in range(self.num_rows): @@ -132,13 +139,13 @@ def _prepare_matrix(self, matrix_name): return matrix_full - def _generate_color(self, i=0, j=0, matrix_name=None , value = None): + def _generate_color(self, connectivity, i=0, j=0, matrix_name=None , value = None): self.colors = ["#66797b", "#543146", "#5a1c5d", "#b468ab", "#6ade42", "#27913c", "#1c464a", "#247663", "#38bcaa", "#a9e9ff", "#61cfff", "#37a5c1", "#e4e4e2", "#ff9f25", "#fb5226"] color_scheme = mcolors.LinearSegmentedColormap.from_list('color_scheme', self.colors) - matrix = getattr(self.connectivity, matrix_name) + matrix = getattr(connectivity, matrix_name) norm = mcolors.Normalize(vmin=0, vmax=matrix.max()) if not isinstance(value, np.ndarray): @@ -166,7 +173,8 @@ def on_cell_clicked(self, x, y, matrix_name): if row > -1 and row < self.num_rows and col > -1 and col < self.num_cols: self.row = row self.col = col - matrix = getattr(self.connectivity, matrix_name) + connectivity = self.connectivity if self.is_matrix_saved else self.new_connectivity + matrix = getattr(connectivity, matrix_name) value = matrix[int(self.from_row + self.row)][int(self.from_col + self.col)] self.cell_value.value = f"{value}" @@ -174,29 +182,28 @@ def on_cell_clicked(self, x, y, matrix_name): self.change_button.layout.visibility = "visible" def on_apply_change(self, change): + if self.is_matrix_saved == True: + self.is_matrix_saved = False matrix_name = self.clicked_matrix + "_matrix" matrix_ui = getattr(self, matrix_name) value = float(self.cell_value.value) + matrix_name = self.clicked_matrix + matrix = getattr(self.new_connectivity, matrix_name) + max_val = matrix.max() + matrix[self.from_row + int(self.row)][self.from_col + int(self.col)] = value + if max_val != matrix.max(): + self._update_matrices_view(self.new_connectivity) + + self.cell_value.layout.visibility = "hidden" self.change_button.layout.visibility = "hidden" with canvas.hold_canvas(matrix_ui[0]): - matrix_ui[0].fill_style = self._generate_color(self.row, self.col, self.clicked_matrix, value) + matrix_ui[0].fill_style = self._generate_color(self.new_connectivity, self.row, self.col, self.clicked_matrix, value) matrix_ui[0].fill_rect((self.col + 8) * self.cell_size, (self.row + 7) * self.cell_size, self.cell_size, self.cell_size) matrix_ui[0].stroke_rect((self.col + 8) * self.cell_size, (self.row + 7) * self.cell_size, self.cell_size, self.cell_size) - matrix_name = self.clicked_matrix - matrix = getattr(self.new_connectivity, matrix_name) - matrix[int(self.row)][int(self.col)] = value - matrix_original = getattr(self.connectivity, matrix_name) - - if value > matrix_original.max() or value < matrix_original.min(): - temp = self.connectivity - self.connectivity = self.new_connectivity - self._update_matrices_view() - self.connectivity = temp - def get_connectivity(self, gid=None): if gid is None: return self.connectivity @@ -205,13 +212,16 @@ def get_connectivity(self, gid=None): return conn def on_click_save(self, change): + self.cell_value.layout.visibility = "hidden" + self.change_button.layout.visibility = "hidden" + conn = self.new_connectivity - self.connectivities_history.append(conn) - self.connectivity = self.new_connectivity + self.connectivities_history.insert(0, conn) self.header.children = list(self.header.children)[:-1] + [self._get_history_dropdown()] self.new_connectivity = self._prepare_new_connectivity() - self._get_quadrant_range(selection=1) - self._update_matrices_view() + self.is_matrix_saved = True + self._update_matrices_view(self.connectivity) + def _prepare_new_connectivity(self): self.new_connectivity = Connectivity() @@ -239,18 +249,22 @@ def _get_history_dropdown(self): ) def on_connectivity_change(change): + self.cell_value.layout.visibility = "hidden" + self.change_button.layout.visibility = "hidden" + + self.is_matrix_saved = True self.connectivity = change["new"] - self._get_quadrant_range(selection=1) - self._update_matrices_view() + self.new_connectivity = self._prepare_new_connectivity() + self._update_matrices_view(self.connectivity) dropdown.observe(on_connectivity_change, 'value') return dropdown - def _update_matrices_view(self): + def _update_matrices_view(self, connectivity): matrices = ["weights", "tract_lengths"] for matrix_name in matrices: matrix_view = getattr(self, matrix_name + "_matrix") - matrix = getattr(self.connectivity, matrix_name) + matrix = getattr(connectivity, matrix_name) matrix_view[0].clear() matrix_view[1].clear() matrix_view[2].clear() @@ -259,7 +273,7 @@ def _update_matrices_view(self): x= np.tile(np.linspace(self.cell_size * 8, self.cell_size * 45, self.num_cols), self.num_cols) y= np.repeat(np.linspace(self.cell_size * 7, self.cell_size * 44, self.num_rows), self.num_rows) value = matrix[self.from_row:self.from_row + self.num_rows, self.from_col:self.from_col + self.num_cols] - matrix_view[0].fill_styled_rects(x, y, color = self._generate_color(value=value, matrix_name=matrix_name), height = self.cell_size - 1, width = self.cell_size - 1) + matrix_view[0].fill_styled_rects(x, y, color = self._generate_color(connectivity, value=value, matrix_name=matrix_name), height = self.cell_size - 1, width = self.cell_size - 1) max_value = int(matrix.max()) region_labels = self.connectivity.region_labels From 3fd772da8e1672a2ddc7eb6ee6558a6b8f2c1229 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 7 Aug 2024 17:52:28 +0530 Subject: [PATCH 12/21] Fix: adjusted widget to handle different connectivities --- .../ui/connectivity_matrix_editor_widget.py | 93 ++++++++++--------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/tvbwidgets/ui/connectivity_matrix_editor_widget.py b/tvbwidgets/ui/connectivity_matrix_editor_widget.py index 98993be..dff9783 100644 --- a/tvbwidgets/ui/connectivity_matrix_editor_widget.py +++ b/tvbwidgets/ui/connectivity_matrix_editor_widget.py @@ -27,7 +27,6 @@ def __init__(self, connectivity, size = 600): self.connectivities_history = [self.connectivity] self.num_rows = int(len(self.connectivity.weights[0])/2) self.num_cols = int(len(self.connectivity.weights[1])/2) - self.cell_size = self.size / (self.num_rows + 5) self.new_connectivity = self._prepare_new_connectivity() self.is_matrix_saved = False @@ -65,8 +64,6 @@ def _on_quadrant_select(self, change): self._get_quadrant_range(selection) self._update_matrices_view(connectivity) - - def _get_quadrant_range(self, selection): if selection == 1: from_row = 0 @@ -99,9 +96,8 @@ def _prepare_matrices_tab(self): self.tab.set_title(1, "tract_lengths") def _prepare_matrix(self, matrix_name): - cell_size = self.cell_size matrix = getattr(self.connectivity, matrix_name) - matrix_full = canvas.MultiCanvas(5, width=self.size + 10*cell_size, height=self.size + 10*cell_size) + matrix_full = canvas.MultiCanvas(5, width=self.size * 1.5, height=self.size * 1.2) matrix_view = matrix_full[0] row_header = matrix_full[1] @@ -111,31 +107,35 @@ def _prepare_matrix(self, matrix_name): #rotate the row_header canvas so they appear vertical row_header.rotate(math.radians(-90)) - row_header.translate(-cell_size * 10 ,0) + row_header.translate(-self.size * 0.2 ,0) with canvas.hold_canvas(matrix_full): - x= np.tile(np.linspace(cell_size * 8, cell_size * 45, self.num_cols), self.num_cols) #x-coordinates of cells - y= np.repeat(np.linspace(cell_size * 7, cell_size * 44, self.num_rows), self.num_rows) #y-coordinates of cells - grid.stroke_rects(x, y, height = cell_size, width = cell_size) + self.cell_x= np.tile(np.linspace(self.size * 0.2, self.size, self.num_cols), self.num_cols) #x-coordinates of cells + self.cell_y= np.repeat(np.linspace(self.size * 0.2, self.size, self.num_rows), self.num_rows) #y-coordinates of cells + self.cell_size = self.cell_x[1] - self.cell_x[0] + grid.stroke_rects(self.cell_x, self.cell_y, height = self.cell_size, width = self.cell_size) colors = self._generate_color(self.connectivity, value=matrix[self.from_row : self.from_row + self.num_rows, self.from_col : self.from_col + self.num_cols], matrix_name=matrix_name) - matrix_view.fill_styled_rects(x, y, color = colors, height = cell_size - 1, width = cell_size -1) + matrix_view.fill_styled_rects(self.cell_x, self.cell_y, color = colors, height = self.cell_size , width = self.cell_size) - for i in range(self.num_rows): - grid.stroke_rect(cell_size * 3, (i + 7) * cell_size, cell_size * 5, cell_size) #grid for row headers - grid.stroke_rect( (i + 8) * cell_size, cell_size * 2, cell_size, cell_size * 5) #grid for column headers + y = np.linspace(self.size * 0.2, self.size, self.num_cols) + x = 0 + grid.stroke_rects(y, x, height = self.size * 0.2, width = self.cell_size) + grid.stroke_rects(x, y, height = self.cell_size, width = self.size * 0.2) + + for i in range(len(y)): row_header.font = f"bold {self.cell_size * 0.90}px px sans serif" - row_header.fill_text(f"{self.connectivity.region_labels[self.from_row + i]}", cell_size * 3.5, (i + 9) * cell_size, max_width = cell_size * 5) + row_header.fill_text(f"{self.connectivity.region_labels[self.from_row + i]}", x + 10, y[i] + self.cell_size, max_width = self.cell_size * 5) column_header.font = f"bold {self.cell_size * 0.90}px px sans serif" - column_header.fill_text(f"{self.connectivity.region_labels[self.from_col + i]}", cell_size * 3.5, (i + 8) * cell_size, max_width = cell_size * 5) + column_header.fill_text(f"{self.connectivity.region_labels[self.from_col + i]}", x + 10, y[i] + self.cell_size, max_width = self.cell_size * 5) - gradient = grid.create_linear_gradient(cell_size * 47, cell_size * 7.5, cell_size * 47 , cell_size * 35, - [(i/len(self.colors),self.colors[-i-1]) for i in range(len(self.colors))]) #color gradient for color-bar + gradient = grid.create_linear_gradient(self.size * 1.1, self.size * 0.2, self.size + 120, self.size, + [(i/len(self.colors),self.colors[-i-1]) for i in range(len(self.colors))]) #color gradient for color-bar grid.fill_style = gradient - grid.fill_rect(cell_size * 47, cell_size * 7.5, cell_size , cell_size * 25) + grid.fill_rect(self.size * 1.1, self.size * 0.2, 20 , self.size * 0.8) grid.fill_style = "black" for i in range(7): - color_bar.fill_text(f"-{round(int(matrix.max()) * (6 - i) / 6, 2)}", cell_size * 48, i * cell_size * 4 + cell_size * 8) #labels for colorbar + color_bar.fill_text(f"--{round(matrix.max() * (6 - i) / 6, 2)}", self.size * 1.1 + 20, self.size * 0.8 / 6.1 * i+ self.size * 0.21) #labels for colorbar return matrix_full @@ -167,8 +167,8 @@ def set_mouse_position(self, x, y): def on_cell_clicked(self, x, y, matrix_name): self.clicked_matrix = matrix_name x_coord, y_coord = self.x_coord , self.y_coord - col = (x_coord // self.cell_size) - 8 - row = (y_coord // self.cell_size) - 7 + col = ((x_coord - self.size * 0.2) // self.cell_size) + row = ((y_coord - self.size * 0.2) // self.cell_size) if row > -1 and row < self.num_rows and col > -1 and col < self.num_cols: self.row = row @@ -201,8 +201,8 @@ def on_apply_change(self, change): with canvas.hold_canvas(matrix_ui[0]): matrix_ui[0].fill_style = self._generate_color(self.new_connectivity, self.row, self.col, self.clicked_matrix, value) - matrix_ui[0].fill_rect((self.col + 8) * self.cell_size, (self.row + 7) * self.cell_size, self.cell_size, self.cell_size) - matrix_ui[0].stroke_rect((self.col + 8) * self.cell_size, (self.row + 7) * self.cell_size, self.cell_size, self.cell_size) + matrix_ui[0].fill_rect(self.size * 0.2 + self.col * self.cell_size, self.size * 0.2 + self.row * self.cell_size, self.cell_size, self.cell_size) + matrix_ui[0].stroke_rect(self.size * 0.2 + self.col * self.cell_size, self.size * 0.2 + self.row * self.cell_size, self.cell_size, self.cell_size) def get_connectivity(self, gid=None): if gid is None: @@ -265,30 +265,31 @@ def _update_matrices_view(self, connectivity): for matrix_name in matrices: matrix_view = getattr(self, matrix_name + "_matrix") matrix = getattr(connectivity, matrix_name) - matrix_view[0].clear() - matrix_view[1].clear() - matrix_view[2].clear() - matrix_view[3].clear() - - x= np.tile(np.linspace(self.cell_size * 8, self.cell_size * 45, self.num_cols), self.num_cols) - y= np.repeat(np.linspace(self.cell_size * 7, self.cell_size * 44, self.num_rows), self.num_rows) - value = matrix[self.from_row:self.from_row + self.num_rows, self.from_col:self.from_col + self.num_cols] - matrix_view[0].fill_styled_rects(x, y, color = self._generate_color(connectivity, value=value, matrix_name=matrix_name), height = self.cell_size - 1, width = self.cell_size - 1) - - max_value = int(matrix.max()) - region_labels = self.connectivity.region_labels - - for i in range(self.num_rows): - row_label = region_labels[self.from_row + i] - matrix_view[1].fill_text(row_label, self.cell_size * 3.5, (i + 9) * self.cell_size, max_width = self.cell_size * 5) + with canvas.hold_canvas(matrix_view): + matrix_view[0].clear() + matrix_view[1].clear() + matrix_view[2].clear() + matrix_view[3].clear() + + value = matrix[self.from_row:self.from_row + self.num_rows, self.from_col:self.from_col + self.num_cols] + matrix_view[0].fill_styled_rects(self.cell_x, self.cell_y, color = self._generate_color(connectivity, value=value, matrix_name=matrix_name), height = self.cell_size, width = self.cell_size) - for i in range(self.num_cols): - col_label = region_labels[self.from_col + i] - matrix_view[2].fill_text(col_label, self.cell_size * 3.5, (i + 8) * self.cell_size, max_width = self.cell_size * 5) - - for i in range(7): - value = f"-{round(max_value * (6 - i) / 6, 2)}" - matrix_view[3].fill_text(value, self.cell_size * 48, i * self.cell_size * 4 + self.cell_size * 8) + max_value = matrix.max() + region_labels = self.connectivity.region_labels + + y = np.linspace(self.size * 0.2, self.size, self.num_cols) + x = 0 + for i in range(self.num_rows): + row_label = region_labels[self.from_row + i] + matrix_view[1].fill_text(row_label, x + 10, y[i] + self.cell_size, max_width = self.cell_size * 5) + + for i in range(self.num_cols): + col_label = region_labels[self.from_col + i] + matrix_view[2].fill_text(col_label, x + 10, y[i] + self.cell_size, max_width = self.cell_size * 5) + + for i in range(7): + value = f"-{round(max_value * (6 - i) / 6, 2)}" + matrix_view[3].fill_text(value, self.size * 1.1 + 20, (self.size * 0.8 / 6.1) * i + self.size * 0.21) #labels for colorbar def display(self): display(self.header) From f4a5db0b3a05482807ec115f29f2512385e42119 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 8 Aug 2024 16:17:10 +0530 Subject: [PATCH 13/21] Refactor and minor fixes --- .../ui/connectivity_matrix_editor_widget.py | 174 ++++++++++-------- 1 file changed, 96 insertions(+), 78 deletions(-) diff --git a/tvbwidgets/ui/connectivity_matrix_editor_widget.py b/tvbwidgets/ui/connectivity_matrix_editor_widget.py index dff9783..d81a867 100644 --- a/tvbwidgets/ui/connectivity_matrix_editor_widget.py +++ b/tvbwidgets/ui/connectivity_matrix_editor_widget.py @@ -21,15 +21,13 @@ class ConnectivityMatrixEditor(TVBWidget): def __init__(self, connectivity, size = 600): self.size = size + self.layout_offset = self.size * 0.2 self.connectivity = connectivity - self.weights = self.connectivity.weights - self.tract_lengths = self.connectivity.tract_lengths self.connectivities_history = [self.connectivity] - self.num_rows = int(len(self.connectivity.weights[0])/2) - self.num_cols = int(len(self.connectivity.weights[1])/2) - + self.num_rows = int(len(self.connectivity.weights[0]) / 2) #num_cols will be equal to num_rows + + self.is_connectivity_being_edited = True self.new_connectivity = self._prepare_new_connectivity() - self.is_matrix_saved = False self.header = widgets.HBox(layout = self.DEFAULT_BORDER) self._make_header() self.tab = widgets.Tab(layout = self.DEFAULT_BORDER) @@ -59,25 +57,29 @@ def _on_quadrant_select(self, change): self.cell_value.layout.visibility = "hidden" self.change_button.layout.visibility = "hidden" - connectivity = self.connectivity if self.is_matrix_saved else self.new_connectivity selection = int(change["new"][-1]) + connectivity = self.new_connectivity if self.is_connectivity_being_edited else self.connectivity + self._get_quadrant_range(selection) self._update_matrices_view(connectivity) def _get_quadrant_range(self, selection): + middle_val = int(self.connectivity.weights.shape[0] / 2) + if selection == 1: from_row = 0 from_col = 0 elif selection == 2: - from_row = int(self.weights.shape[0]/2) + from_row = middle_val from_col = 0 elif selection == 3: from_row = 0 - from_col = int(self.weights.shape[0]/2) + from_col = middle_val else: - from_row = int(self.weights.shape[0]/2) - from_col = int(self.weights.shape[0]/2) + from_row = middle_val + from_col = middle_val + #indexing starts from this row and col self.from_row = from_row self.from_col = from_col @@ -96,7 +98,7 @@ def _prepare_matrices_tab(self): self.tab.set_title(1, "tract_lengths") def _prepare_matrix(self, matrix_name): - matrix = getattr(self.connectivity, matrix_name) + matrix = getattr(self.connectivity, matrix_name) #either weights or tracts matrix data matrix_full = canvas.MultiCanvas(5, width=self.size * 1.5, height=self.size * 1.2) matrix_view = matrix_full[0] @@ -107,46 +109,53 @@ def _prepare_matrix(self, matrix_name): #rotate the row_header canvas so they appear vertical row_header.rotate(math.radians(-90)) - row_header.translate(-self.size * 0.2 ,0) + row_header.translate(-self.layout_offset ,0) with canvas.hold_canvas(matrix_full): - self.cell_x= np.tile(np.linspace(self.size * 0.2, self.size, self.num_cols), self.num_cols) #x-coordinates of cells - self.cell_y= np.repeat(np.linspace(self.size * 0.2, self.size, self.num_rows), self.num_rows) #y-coordinates of cells + self.cell_x = np.tile(np.linspace(self.layout_offset, self.size, self.num_rows), self.num_rows) #x-coordinates of cells + self.cell_y = np.repeat(np.linspace(self.layout_offset, self.size, self.num_rows), self.num_rows) #y-coordinates of cells self.cell_size = self.cell_x[1] - self.cell_x[0] + grid.stroke_rects(self.cell_x, self.cell_y, height = self.cell_size, width = self.cell_size) - colors = self._generate_color(self.connectivity, value=matrix[self.from_row : self.from_row + self.num_rows, self.from_col : self.from_col + self.num_cols], matrix_name=matrix_name) + value = matrix[self.from_row : self.from_row + self.num_rows, self.from_col : self.from_col + self.num_rows] + colors = self._generate_color(self.connectivity, value = value, matrix_name = matrix_name) matrix_view.fill_styled_rects(self.cell_x, self.cell_y, color = colors, height = self.cell_size , width = self.cell_size) - y = np.linspace(self.size * 0.2, self.size, self.num_cols) x = 0 - grid.stroke_rects(y, x, height = self.size * 0.2, width = self.cell_size) - grid.stroke_rects(x, y, height = self.cell_size, width = self.size * 0.2) - - for i in range(len(y)): - row_header.font = f"bold {self.cell_size * 0.90}px px sans serif" - row_header.fill_text(f"{self.connectivity.region_labels[self.from_row + i]}", x + 10, y[i] + self.cell_size, max_width = self.cell_size * 5) - column_header.font = f"bold {self.cell_size * 0.90}px px sans serif" - column_header.fill_text(f"{self.connectivity.region_labels[self.from_col + i]}", x + 10, y[i] + self.cell_size, max_width = self.cell_size * 5) + y = np.linspace(self.layout_offset, self.size, self.num_rows) + grid.stroke_rects(y, x, height = self.layout_offset, width = self.cell_size) #grid for row headers + grid.stroke_rects(x, y, height = self.cell_size, width = self.layout_offset) #grid for column headers + + for i in range(self.num_rows): + row_header.font = f"bold {self.cell_size}px px sans serif" + row_header_text = f"{self.connectivity.region_labels[self.from_row + i]}" + row_header.fill_text(row_header_text, x + 10, y[i] + self.cell_size, max_width = self.layout_offset * 0.9) + + column_header.font = f"bold {self.cell_size}px px sans serif" + column_header_text = f"{self.connectivity.region_labels[self.from_col + i]}" + column_header.fill_text(column_header_text, x + 10, y[i] + self.cell_size, max_width = self.layout_offset * 0.9) - gradient = grid.create_linear_gradient(self.size * 1.1, self.size * 0.2, self.size + 120, self.size, + self.colorbar_x = self.size * 1.1 + gradient = grid.create_linear_gradient(self.colorbar_x, self.layout_offset, self.size * 1.2, self.size, [(i/len(self.colors),self.colors[-i-1]) for i in range(len(self.colors))]) #color gradient for color-bar grid.fill_style = gradient - grid.fill_rect(self.size * 1.1, self.size * 0.2, 20 , self.size * 0.8) + grid.fill_rect(self.colorbar_x, self.layout_offset, 20 , self.size - self.layout_offset) grid.fill_style = "black" for i in range(7): - color_bar.fill_text(f"--{round(matrix.max() * (6 - i) / 6, 2)}", self.size * 1.1 + 20, self.size * 0.8 / 6.1 * i+ self.size * 0.21) #labels for colorbar + label_text = f"--{round(matrix.max() * (6 - i) / 6, 2)}" + color_bar.fill_text(label_text, self.colorbar_x + 20, self.size * 0.8 / 6.1 * i + self.layout_offset + 5) #labels for colorbar return matrix_full - def _generate_color(self, connectivity, i=0, j=0, matrix_name=None , value = None): + def _generate_color(self, connectivity, i = 0, j = 0, matrix_name = None , value = None): self.colors = ["#66797b", "#543146", "#5a1c5d", "#b468ab", "#6ade42", "#27913c", "#1c464a", "#247663", "#38bcaa", "#a9e9ff", "#61cfff", "#37a5c1", "#e4e4e2", "#ff9f25", "#fb5226"] color_scheme = mcolors.LinearSegmentedColormap.from_list('color_scheme', self.colors) matrix = getattr(connectivity, matrix_name) - norm = mcolors.Normalize(vmin=0, vmax=matrix.max()) + norm = mcolors.Normalize(vmin = 0, vmax = matrix.max()) if not isinstance(value, np.ndarray): if not value: @@ -167,13 +176,13 @@ def set_mouse_position(self, x, y): def on_cell_clicked(self, x, y, matrix_name): self.clicked_matrix = matrix_name x_coord, y_coord = self.x_coord , self.y_coord - col = ((x_coord - self.size * 0.2) // self.cell_size) - row = ((y_coord - self.size * 0.2) // self.cell_size) + col = ((x_coord - self.layout_offset) // self.cell_size) + row = ((y_coord - self.layout_offset) // self.cell_size) - if row > -1 and row < self.num_rows and col > -1 and col < self.num_cols: + if row > -1 and row < self.num_rows and col > -1 and col < self.num_rows: self.row = row self.col = col - connectivity = self.connectivity if self.is_matrix_saved else self.new_connectivity + connectivity = self.new_connectivity if self.is_connectivity_being_edited else self.connectivity matrix = getattr(connectivity, matrix_name) value = matrix[int(self.from_row + self.row)][int(self.from_col + self.col)] self.cell_value.value = f"{value}" @@ -182,29 +191,35 @@ def on_cell_clicked(self, x, y, matrix_name): self.change_button.layout.visibility = "visible" def on_apply_change(self, change): - if self.is_matrix_saved == True: - self.is_matrix_saved = False + self.is_connectivity_being_edited = True + matrix_name = self.clicked_matrix + "_matrix" matrix_ui = getattr(self, matrix_name) - value = float(self.cell_value.value) - - matrix_name = self.clicked_matrix - matrix = getattr(self.new_connectivity, matrix_name) - max_val = matrix.max() - matrix[self.from_row + int(self.row)][self.from_col + int(self.col)] = value - if max_val != matrix.max(): - self._update_matrices_view(self.new_connectivity) + try: + value = float(self.cell_value.value) + except: + value = None + if value: + matrix_name = self.clicked_matrix + matrix = getattr(self.new_connectivity, matrix_name) + max_val = matrix.max() + matrix[self.from_row + int(self.row)][self.from_col + int(self.col)] = value + if max_val != matrix.max(): + self._update_matrices_view(self.new_connectivity) - self.cell_value.layout.visibility = "hidden" - self.change_button.layout.visibility = "hidden" + self.cell_value.layout.visibility = "hidden" + self.change_button.layout.visibility = "hidden" - with canvas.hold_canvas(matrix_ui[0]): - matrix_ui[0].fill_style = self._generate_color(self.new_connectivity, self.row, self.col, self.clicked_matrix, value) - matrix_ui[0].fill_rect(self.size * 0.2 + self.col * self.cell_size, self.size * 0.2 + self.row * self.cell_size, self.cell_size, self.cell_size) - matrix_ui[0].stroke_rect(self.size * 0.2 + self.col * self.cell_size, self.size * 0.2 + self.row * self.cell_size, self.cell_size, self.cell_size) + x = self.layout_offset + self.col * self.cell_size + y = self.layout_offset + self.row * self.cell_size - def get_connectivity(self, gid=None): + with canvas.hold_canvas(matrix_ui[0]): + matrix_ui[0].fill_style = self._generate_color(self.new_connectivity, self.row, self.col, self.clicked_matrix, value) + matrix_ui[0].fill_rect(x, y, self.cell_size, self.cell_size) + matrix_ui[0].stroke_rect(x, y, self.cell_size, self.cell_size) + + def get_connectivity(self, gid = None): if gid is None: return self.connectivity for conn in self.connectivities_history: @@ -218,41 +233,42 @@ def on_click_save(self, change): conn = self.new_connectivity self.connectivities_history.insert(0, conn) self.header.children = list(self.header.children)[:-1] + [self._get_history_dropdown()] + self.new_connectivity = self._prepare_new_connectivity() - self.is_matrix_saved = True + self.is_connectivity_being_edited = False self._update_matrices_view(self.connectivity) def _prepare_new_connectivity(self): - self.new_connectivity = Connectivity() - self.new_connectivity.parent_connectivity = self.connectivity.gid.hex - self.new_connectivity.centres = self.connectivity.centres - self.new_connectivity.region_labels = self.connectivity.region_labels - self.new_connectivity.orientations = self.connectivity.orientations - self.new_connectivity.cortical = self.connectivity.cortical - self.new_connectivity.hemispheres = self.connectivity.hemispheres - self.new_connectivity.areas = self.connectivity.areas - self.new_connectivity.weights = self.connectivity.weights - self.new_connectivity.tract_lengths = self.connectivity.tract_lengths - self.new_connectivity.configure() + new_connectivity = Connectivity() + new_connectivity.parent_connectivity = self.connectivity.gid.hex + new_connectivity.centres = self.connectivity.centres + new_connectivity.region_labels = self.connectivity.region_labels + new_connectivity.orientations = self.connectivity.orientations + new_connectivity.cortical = self.connectivity.cortical + new_connectivity.hemispheres = self.connectivity.hemispheres + new_connectivity.areas = self.connectivity.areas + new_connectivity.weights = self.connectivity.weights + new_connectivity.tract_lengths = self.connectivity.tract_lengths + new_connectivity.configure() - return self.new_connectivity + return new_connectivity def _get_history_dropdown(self): values = [(conn.gid.hex, conn) for conn in self.connectivities_history] - default = len(values) and values[-1][1] or None + default = values[values.index((self.connectivity.gid.hex, self.connectivity))][1] - dropdown = widgets.Dropdown(options=values, - description='View history', - disabled=False, - value=default, + dropdown = widgets.Dropdown(options = values, + description = 'View history', + disabled = False, + value = default, ) def on_connectivity_change(change): self.cell_value.layout.visibility = "hidden" self.change_button.layout.visibility = "hidden" - self.is_matrix_saved = True + self.is_connectivity_being_edited = False self.connectivity = change["new"] self.new_connectivity = self._prepare_new_connectivity() self._update_matrices_view(self.connectivity) @@ -265,31 +281,33 @@ def _update_matrices_view(self, connectivity): for matrix_name in matrices: matrix_view = getattr(self, matrix_name + "_matrix") matrix = getattr(connectivity, matrix_name) + with canvas.hold_canvas(matrix_view): matrix_view[0].clear() matrix_view[1].clear() matrix_view[2].clear() matrix_view[3].clear() - value = matrix[self.from_row:self.from_row + self.num_rows, self.from_col:self.from_col + self.num_cols] - matrix_view[0].fill_styled_rects(self.cell_x, self.cell_y, color = self._generate_color(connectivity, value=value, matrix_name=matrix_name), height = self.cell_size, width = self.cell_size) + value = matrix[self.from_row : self.from_row + self.num_rows, self.from_col : self.from_col + self.num_rows] + color = self._generate_color(connectivity, value = value, matrix_name = matrix_name) + matrix_view[0].fill_styled_rects(self.cell_x, self.cell_y, color = color, height = self.cell_size, width = self.cell_size) max_value = matrix.max() region_labels = self.connectivity.region_labels - y = np.linspace(self.size * 0.2, self.size, self.num_cols) x = 0 + y = np.linspace(self.layout_offset, self.size, self.num_rows) for i in range(self.num_rows): row_label = region_labels[self.from_row + i] - matrix_view[1].fill_text(row_label, x + 10, y[i] + self.cell_size, max_width = self.cell_size * 5) + matrix_view[1].fill_text(row_label, x + 10, y[i] + self.cell_size, max_width = self.layout_offset * 0.9) - for i in range(self.num_cols): + for i in range(self.num_rows): col_label = region_labels[self.from_col + i] - matrix_view[2].fill_text(col_label, x + 10, y[i] + self.cell_size, max_width = self.cell_size * 5) + matrix_view[2].fill_text(col_label, x + 10, y[i] + self.cell_size, max_width = self.layout_offset * 0.9) for i in range(7): - value = f"-{round(max_value * (6 - i) / 6, 2)}" - matrix_view[3].fill_text(value, self.size * 1.1 + 20, (self.size * 0.8 / 6.1) * i + self.size * 0.21) #labels for colorbar + value = f"--{round(max_value * (6 - i) / 6, 2)}" + matrix_view[3].fill_text(value, self.colorbar_x + 20, ((self.size - self.layout_offset) / 6.1) * i + self.layout_offset + 5) #labels for colorbar def display(self): display(self.header) From 0471d8c235d74f049fbc7a47879deb84f0fd41a0 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 8 Aug 2024 16:20:49 +0530 Subject: [PATCH 14/21] Update tests according to code changes --- .../test_connectivity_matrix_editor_widget.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py b/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py index 9334613..0fa7e5c 100644 --- a/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py +++ b/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py @@ -58,11 +58,11 @@ def test_prepare_matrices_tab(wid): assert wid.tab.get_title(1) == "tract_lengths" def test_prepare_matrix(wid): - assert math.isclose(wid.cell_size, 13.95, abs_tol=0.1) - assert math.isclose(wid.weights_matrix.width, 739, abs_tol=1) - assert math.isclose(wid.weights_matrix.height, 739, abs_tol=1) - assert math.isclose(wid.tract_lengths_matrix.width, 739, abs_tol=1) - assert math.isclose(wid.tract_lengths_matrix.height, 739, abs_tol=1) + assert math.isclose(wid.cell_size, 12.97, abs_tol=0.1) + assert math.isclose(wid.weights_matrix.width, 900, abs_tol=1) + assert math.isclose(wid.weights_matrix.height, 720, abs_tol=1) + assert math.isclose(wid.tract_lengths_matrix.width, 900, abs_tol=1) + assert math.isclose(wid.tract_lengths_matrix.height, 720, abs_tol=1) assert isinstance(wid.weights_matrix[0], canvas.canvas.Canvas) assert isinstance(wid.weights_matrix[1], canvas.canvas.Canvas) assert isinstance(wid.weights_matrix[2], canvas.canvas.Canvas) @@ -74,27 +74,27 @@ def test_prepare_matrix(wid): assert isinstance(wid.tract_lengths_matrix[3], canvas.canvas.Canvas) assert isinstance(wid.tract_lengths_matrix[4], canvas.canvas.Canvas) -def test_generate_color_with_indices(wid): - color = wid._generate_color(i=20, j=38, matrix_name="weights") +def test_generate_color_with_indices(wid, connectivity): + color = wid._generate_color(connectivity, i=20, j=38, matrix_name="weights") assert color == 'rgba(102, 121, 123, 1.00)' - color = wid._generate_color(i=5, j=70, matrix_name="tract_lengths") + color = wid._generate_color(connectivity, i=5, j=70, matrix_name="tract_lengths") assert color == 'rgba(38, 124, 105, 1.00)' -def test_generate_color_with_single_value(wid): - color = wid._generate_color(value = 2, matrix_name="weights") +def test_generate_color_with_single_value(wid, connectivity): + color = wid._generate_color(connectivity, value = 2, matrix_name="weights") assert color == 'rgba(145, 224, 255, 1.00)' - color = wid._generate_color(value = 3, matrix_name = "tract_lengths") + color = wid._generate_color(connectivity, value = 3, matrix_name = "tract_lengths") assert color == 'rgba(97, 101, 108, 1.00)' -def test_generate_color_with_array_value(wid): +def test_generate_color_with_array_value(wid, connectivity): value = np.array([[1, 2], [2, 3]]) - colors = wid._generate_color(value = value, matrix_name="weights") + colors = wid._generate_color(connectivity, value = value, matrix_name="weights") assert np.allclose(colors, [[[ 61.33333333, 170.66666667, 62. ], [145. , 224.33333333, 255. ]], [[145. , 224.33333333, 255. ], [251. , 82. , 38. ]]]) - colors = wid._generate_color(value = value, matrix_name="tract_lengths") + colors = wid._generate_color(connectivity, value = value, matrix_name="tract_lengths") assert np.allclose(colors, [[[101.01176471, 117.04705882, 120.09019608], [ 99.03529412, 109.14117647, 114.27058824]], [[ 99.03529412, 109.14117647, 114.27058824], From a96cf368a4107b43042d64f81c6779a7b5047be0 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 20 Aug 2024 20:47:02 +0530 Subject: [PATCH 15/21] fix error: changing value to 0 does not work --- tvbwidgets/ui/connectivity_matrix_editor_widget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvbwidgets/ui/connectivity_matrix_editor_widget.py b/tvbwidgets/ui/connectivity_matrix_editor_widget.py index d81a867..774e19b 100644 --- a/tvbwidgets/ui/connectivity_matrix_editor_widget.py +++ b/tvbwidgets/ui/connectivity_matrix_editor_widget.py @@ -200,7 +200,7 @@ def on_apply_change(self, change): except: value = None - if value: + if value is not None: matrix_name = self.clicked_matrix matrix = getattr(self.new_connectivity, matrix_name) max_val = matrix.max() From c6b2856be3f0154d4a9226647cb25bb16446e185 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 22 Aug 2024 10:55:39 +0530 Subject: [PATCH 16/21] highlight the selected cell --- .../test_connectivity_matrix_editor_widget.py | 2 ++ .../ui/connectivity_matrix_editor_widget.py | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py b/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py index 0fa7e5c..cc6dab1 100644 --- a/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py +++ b/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py @@ -68,11 +68,13 @@ def test_prepare_matrix(wid): assert isinstance(wid.weights_matrix[2], canvas.canvas.Canvas) assert isinstance(wid.weights_matrix[3], canvas.canvas.Canvas) assert isinstance(wid.weights_matrix[4], canvas.canvas.Canvas) + assert isinstance(wid.weights_matrix[5], canvas.canvas.Canvas) assert isinstance(wid.tract_lengths_matrix[0], canvas.canvas.Canvas) assert isinstance(wid.tract_lengths_matrix[1], canvas.canvas.Canvas) assert isinstance(wid.tract_lengths_matrix[2], canvas.canvas.Canvas) assert isinstance(wid.tract_lengths_matrix[3], canvas.canvas.Canvas) assert isinstance(wid.tract_lengths_matrix[4], canvas.canvas.Canvas) + assert isinstance(wid.tract_lengths_matrix[5], canvas.canvas.Canvas) def test_generate_color_with_indices(wid, connectivity): color = wid._generate_color(connectivity, i=20, j=38, matrix_name="weights") diff --git a/tvbwidgets/ui/connectivity_matrix_editor_widget.py b/tvbwidgets/ui/connectivity_matrix_editor_widget.py index 774e19b..68f6843 100644 --- a/tvbwidgets/ui/connectivity_matrix_editor_widget.py +++ b/tvbwidgets/ui/connectivity_matrix_editor_widget.py @@ -99,13 +99,14 @@ def _prepare_matrices_tab(self): def _prepare_matrix(self, matrix_name): matrix = getattr(self.connectivity, matrix_name) #either weights or tracts matrix data - matrix_full = canvas.MultiCanvas(5, width=self.size * 1.5, height=self.size * 1.2) + matrix_full = canvas.MultiCanvas(6, width=self.size * 1.5, height=self.size * 1.2) matrix_view = matrix_full[0] row_header = matrix_full[1] column_header = matrix_full[2] color_bar = matrix_full[3] grid = matrix_full[4] + # sixth canvas is for displaying a grid around selected cell #rotate the row_header canvas so they appear vertical row_header.rotate(math.radians(-90)) @@ -189,6 +190,19 @@ def on_cell_clicked(self, x, y, matrix_name): self.cell_value.layout.visibility = "visible" self.change_button.layout.visibility = "visible" + + matrix_ = self.clicked_matrix + "_matrix" + matrix_ui = getattr(self, matrix_) + + x = self.layout_offset + self.col * self.cell_size + y = self.layout_offset + self.row * self.cell_size + + with canvas.hold_canvas(matrix_ui[5]): + matrix_ui[5].clear() + matrix_ui[5].line_width = 2 + matrix_ui[5].stroke_style = "white" + matrix_ui[5].stroke_rect(x, y, self.cell_size, self.cell_size) + def on_apply_change(self, change): self.is_connectivity_being_edited = True @@ -219,6 +233,8 @@ def on_apply_change(self, change): matrix_ui[0].fill_rect(x, y, self.cell_size, self.cell_size) matrix_ui[0].stroke_rect(x, y, self.cell_size, self.cell_size) + matrix_ui[5].clear() + def get_connectivity(self, gid = None): if gid is None: return self.connectivity @@ -287,6 +303,7 @@ def _update_matrices_view(self, connectivity): matrix_view[1].clear() matrix_view[2].clear() matrix_view[3].clear() + matrix_view[5].clear() value = matrix[self.from_row : self.from_row + self.num_rows, self.from_col : self.from_col + self.num_rows] color = self._generate_color(connectivity, value = value, matrix_name = matrix_name) From 1fcb114d9c0781d666fc66f1f33d06256ee6f7ea Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 22 Aug 2024 11:25:31 +0530 Subject: [PATCH 17/21] add method to get gid of saved connectivity --- notebooks/ConnectivityMatrixEditor.ipynb | 10 ++++++++++ .../tests/test_connectivity_matrix_editor_widget.py | 8 +++++++- tvbwidgets/ui/connectivity_matrix_editor_widget.py | 8 +++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/notebooks/ConnectivityMatrixEditor.ipynb b/notebooks/ConnectivityMatrixEditor.ipynb index cc6c6d9..f434bc7 100644 --- a/notebooks/ConnectivityMatrixEditor.ipynb +++ b/notebooks/ConnectivityMatrixEditor.ipynb @@ -40,6 +40,16 @@ "wid.display()" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# to get the gid of all the saved connectivities\n", + "wid.saved_connectivities()" + ] + }, { "cell_type": "code", "execution_count": null, diff --git a/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py b/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py index cc6dab1..8525bcd 100644 --- a/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py +++ b/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py @@ -101,7 +101,13 @@ def test_generate_color_with_array_value(wid, connectivity): [ 99.03529412, 109.14117647, 114.27058824]], [[ 99.03529412, 109.14117647, 114.27058824], [ 97.05882353, 101.23529412, 108.45098039]]]) - + +def test_saved_connectivities(wid): + conn_list = wid.saved_connectivities() + assert conn_list is not None + assert isinstance(conn_list, list) + assert isinstance(conn_list[0], str) + def test_get_connectivity(wid): conn = wid.get_connectivity() assert isinstance(conn, Connectivity) diff --git a/tvbwidgets/ui/connectivity_matrix_editor_widget.py b/tvbwidgets/ui/connectivity_matrix_editor_widget.py index 68f6843..5f7d5bf 100644 --- a/tvbwidgets/ui/connectivity_matrix_editor_widget.py +++ b/tvbwidgets/ui/connectivity_matrix_editor_widget.py @@ -235,11 +235,17 @@ def on_apply_change(self, change): matrix_ui[5].clear() + def saved_connectivities(self): + conn_list = [] + for conn in self.connectivities_history: + conn_list.append(conn.gid.hex) + return conn_list + def get_connectivity(self, gid = None): if gid is None: return self.connectivity for conn in self.connectivities_history: - if conn.gid.hex == gid.hex: + if conn.gid.hex == gid: return conn def on_click_save(self, change): From 75b525a99d9b947123da5efc76096bc9098b7be5 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 22 Aug 2024 13:52:09 +0530 Subject: [PATCH 18/21] Make widget scrollable and adjust its size according to connectivity --- notebooks/ConnectivityMatrixEditor.ipynb | 2 +- .../test_connectivity_matrix_editor_widget.py | 10 +++---- .../ui/connectivity_matrix_editor_widget.py | 28 ++++++++++++++++--- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/notebooks/ConnectivityMatrixEditor.ipynb b/notebooks/ConnectivityMatrixEditor.ipynb index f434bc7..54baea5 100644 --- a/notebooks/ConnectivityMatrixEditor.ipynb +++ b/notebooks/ConnectivityMatrixEditor.ipynb @@ -28,7 +28,7 @@ "metadata": {}, "outputs": [], "source": [ - "wid = ConnectivityMatrixEditor(conn, size = 600)" + "wid = ConnectivityMatrixEditor(conn, size = None)" ] }, { diff --git a/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py b/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py index 8525bcd..ccf7899 100644 --- a/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py +++ b/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py @@ -58,11 +58,11 @@ def test_prepare_matrices_tab(wid): assert wid.tab.get_title(1) == "tract_lengths" def test_prepare_matrix(wid): - assert math.isclose(wid.cell_size, 12.97, abs_tol=0.1) - assert math.isclose(wid.weights_matrix.width, 900, abs_tol=1) - assert math.isclose(wid.weights_matrix.height, 720, abs_tol=1) - assert math.isclose(wid.tract_lengths_matrix.width, 900, abs_tol=1) - assert math.isclose(wid.tract_lengths_matrix.height, 720, abs_tol=1) + assert math.isclose(wid.cell_size, 16.43, abs_tol=0.1) + assert math.isclose(wid.weights_matrix.width, 1140, abs_tol=1) + assert math.isclose(wid.weights_matrix.height, 912, abs_tol=1) + assert math.isclose(wid.tract_lengths_matrix.width, 1140, abs_tol=1) + assert math.isclose(wid.tract_lengths_matrix.height, 912, abs_tol=1) assert isinstance(wid.weights_matrix[0], canvas.canvas.Canvas) assert isinstance(wid.weights_matrix[1], canvas.canvas.Canvas) assert isinstance(wid.weights_matrix[2], canvas.canvas.Canvas) diff --git a/tvbwidgets/ui/connectivity_matrix_editor_widget.py b/tvbwidgets/ui/connectivity_matrix_editor_widget.py index 5f7d5bf..6c24c33 100644 --- a/tvbwidgets/ui/connectivity_matrix_editor_widget.py +++ b/tvbwidgets/ui/connectivity_matrix_editor_widget.py @@ -19,12 +19,14 @@ from tvbwidgets.ui.base_widget import TVBWidget class ConnectivityMatrixEditor(TVBWidget): - def __init__(self, connectivity, size = 600): - self.size = size - self.layout_offset = self.size * 0.2 + def __init__(self, connectivity, size = None): self.connectivity = connectivity self.connectivities_history = [self.connectivity] self.num_rows = int(len(self.connectivity.weights[0]) / 2) #num_cols will be equal to num_rows + if size is None: + size = self.num_rows * 20 + self.size = size + self.layout_offset = self.size * 0.2 self.is_connectivity_being_edited = True self.new_connectivity = self._prepare_new_connectivity() @@ -93,7 +95,25 @@ def _prepare_matrices_tab(self): self.tract_lengths_matrix.on_mouse_down(lambda x, y: self.on_cell_clicked(x, y, "tract_lengths")) self.tract_lengths_matrix.on_mouse_move(self.set_mouse_position) - self.tab.children = [self.weights_matrix, self.tract_lengths_matrix] + out1 = widgets.Output() + out2 = widgets.Output() + + with out1: + display(self.weights_matrix) + + with out2: + display(self.tract_lengths_matrix) + + container1 = widgets.Box([out1], layout = widgets.Layout( + width = '1200px', + height = '400px', + overflow_x = 'auto', + overflow_y = 'auto', + )) + + container2 = widgets.Box([out2], layout = container1.layout) + + self.tab.children = [container1, container2] self.tab.set_title(0, "weights") self.tab.set_title(1, "tract_lengths") From bd2340d06c085a32ada36ebe7fa383422bf869c2 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 22 Aug 2024 15:35:59 +0530 Subject: [PATCH 19/21] Increase height of widget, display newly saved connectivity on clicking save --- tvbwidgets/ui/connectivity_matrix_editor_widget.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tvbwidgets/ui/connectivity_matrix_editor_widget.py b/tvbwidgets/ui/connectivity_matrix_editor_widget.py index 6c24c33..4365189 100644 --- a/tvbwidgets/ui/connectivity_matrix_editor_widget.py +++ b/tvbwidgets/ui/connectivity_matrix_editor_widget.py @@ -106,7 +106,7 @@ def _prepare_matrices_tab(self): container1 = widgets.Box([out1], layout = widgets.Layout( width = '1200px', - height = '400px', + height = '600px', overflow_x = 'auto', overflow_y = 'auto', )) @@ -274,13 +274,13 @@ def on_click_save(self, change): conn = self.new_connectivity self.connectivities_history.insert(0, conn) + self.connectivity = conn self.header.children = list(self.header.children)[:-1] + [self._get_history_dropdown()] self.new_connectivity = self._prepare_new_connectivity() self.is_connectivity_being_edited = False self._update_matrices_view(self.connectivity) - def _prepare_new_connectivity(self): new_connectivity = Connectivity() new_connectivity.parent_connectivity = self.connectivity.gid.hex From 266532dc94bbdb18b69957d6473d8070852d03e6 Mon Sep 17 00:00:00 2001 From: "RIO\\teodora.misan" Date: Mon, 9 Sep 2024 12:08:36 +0300 Subject: [PATCH 20/21] WID247-2: refactor code --- .../ui/connectivity_matrix_editor_widget.py | 200 ++++++++++-------- 1 file changed, 107 insertions(+), 93 deletions(-) diff --git a/tvbwidgets/ui/connectivity_matrix_editor_widget.py b/tvbwidgets/ui/connectivity_matrix_editor_widget.py index 4365189..e60b91c 100644 --- a/tvbwidgets/ui/connectivity_matrix_editor_widget.py +++ b/tvbwidgets/ui/connectivity_matrix_editor_widget.py @@ -18,11 +18,13 @@ from tvb.datatypes.connectivity import Connectivity from tvbwidgets.ui.base_widget import TVBWidget + class ConnectivityMatrixEditor(TVBWidget): - def __init__(self, connectivity, size = None): + def __init__(self, connectivity, size=None, **kwargs): + super().__init__(**kwargs) self.connectivity = connectivity - self.connectivities_history = [self.connectivity] - self.num_rows = int(len(self.connectivity.weights[0]) / 2) #num_cols will be equal to num_rows + self.connectivity_history_list = [self.connectivity] + self.num_rows = int(len(self.connectivity.weights[0]) / 2) # num_cols will be equal to num_rows if size is None: size = self.num_rows * 20 self.size = size @@ -30,35 +32,36 @@ def __init__(self, connectivity, size = None): self.is_connectivity_being_edited = True self.new_connectivity = self._prepare_new_connectivity() - self.header = widgets.HBox(layout = self.DEFAULT_BORDER) + self.header = widgets.HBox(layout=self.DEFAULT_BORDER) self._make_header() - self.tab = widgets.Tab(layout = self.DEFAULT_BORDER) - self._get_quadrant_range(selection = 1) + self.tab = widgets.Tab(layout=self.DEFAULT_BORDER) + self._get_quadrant_range(selection=1) self._prepare_matrices_tab() - + def _make_header(self): options = ["Quadrant 1", "Quadrant 2", "Quadrant 3", "Quadrant 4"] - - self.quadrants = widgets.Dropdown(options = options) - self.quadrants.observe(self._on_quadrant_select, names = ["value"]) - - self.cell_value = widgets.Text(description = "value", - layout = widgets.Layout(width = "200px", visibility = "hidden")) - - self.change_button = widgets.Button(description = "Change", - layout = widgets.Layout(width = "80px", visibility = "hidden")) - self.change_button.on_click(lambda change :self.on_apply_change(change)) - - self.save_button = widgets.Button(description = "Save", - layout = widgets.Layout(width = "100px", margin='0 0 0 auto')) + + self.quadrants = widgets.Dropdown(options=options) + self.quadrants.observe(self._on_quadrant_select, names=["value"]) + + self.cell_value = widgets.Text(description="value", + layout=widgets.Layout(width="200px", visibility="hidden")) + + self.change_button = widgets.Button(description="Change", + layout=widgets.Layout(width="80px", visibility="hidden")) + self.change_button.on_click(lambda change: self.on_apply_change(change)) + + self.save_button = widgets.Button(description="Save", + layout=widgets.Layout(width="100px", margin='0 0 0 auto')) self.save_button.on_click(self.on_click_save) - self.header.children = [self.quadrants, self.cell_value, self.change_button, self.save_button, self._get_history_dropdown()] + self.header.children = [self.quadrants, self.cell_value, self.change_button, self.save_button, + self._get_history_dropdown()] def _on_quadrant_select(self, change): self.cell_value.layout.visibility = "hidden" self.change_button.layout.visibility = "hidden" - + selection = int(change["new"][-1]) connectivity = self.new_connectivity if self.is_connectivity_being_edited else self.connectivity @@ -81,44 +84,44 @@ def _get_quadrant_range(self, selection): from_row = middle_val from_col = middle_val - #indexing starts from this row and col + # indexing starts from this row and col self.from_row = from_row self.from_col = from_col def _prepare_matrices_tab(self): self.weights_matrix = self._prepare_matrix("weights") - self.tract_lengths_matrix = self._prepare_matrix("tract_lengths") + self.tract_lengths_matrix = self._prepare_matrix("tract_lengths") self.weights_matrix.on_mouse_down(lambda x, y: self.on_cell_clicked(x, y, "weights")) self.weights_matrix.on_mouse_move(self.set_mouse_position) - + self.tract_lengths_matrix.on_mouse_down(lambda x, y: self.on_cell_clicked(x, y, "tract_lengths")) self.tract_lengths_matrix.on_mouse_move(self.set_mouse_position) - + out1 = widgets.Output() out2 = widgets.Output() with out1: display(self.weights_matrix) - + with out2: display(self.tract_lengths_matrix) - container1 = widgets.Box([out1], layout = widgets.Layout( - width = '1200px', - height = '600px', - overflow_x = 'auto', - overflow_y = 'auto', + container1 = widgets.Box([out1], layout=widgets.Layout( + width='1200px', + height='600px', + overflow_x='auto', + overflow_y='auto', )) - container2 = widgets.Box([out2], layout = container1.layout) + container2 = widgets.Box([out2], layout=container1.layout) self.tab.children = [container1, container2] self.tab.set_title(0, "weights") self.tab.set_title(1, "tract_lengths") def _prepare_matrix(self, matrix_name): - matrix = getattr(self.connectivity, matrix_name) #either weights or tracts matrix data + matrix = getattr(self.connectivity, matrix_name) matrix_full = canvas.MultiCanvas(6, width=self.size * 1.5, height=self.size * 1.2) matrix_view = matrix_full[0] @@ -128,79 +131,85 @@ def _prepare_matrix(self, matrix_name): grid = matrix_full[4] # sixth canvas is for displaying a grid around selected cell - #rotate the row_header canvas so they appear vertical + # rotate the row_header canvas so they appear vertical row_header.rotate(math.radians(-90)) - row_header.translate(-self.layout_offset ,0) + row_header.translate(-self.layout_offset, 0) with canvas.hold_canvas(matrix_full): - self.cell_x = np.tile(np.linspace(self.layout_offset, self.size, self.num_rows), self.num_rows) #x-coordinates of cells - self.cell_y = np.repeat(np.linspace(self.layout_offset, self.size, self.num_rows), self.num_rows) #y-coordinates of cells + self.cell_x = np.tile(np.linspace(self.layout_offset, self.size, self.num_rows), + self.num_rows) # x-coordinates of cells + self.cell_y = np.repeat(np.linspace(self.layout_offset, self.size, self.num_rows), + self.num_rows) # y-coordinates of cells self.cell_size = self.cell_x[1] - self.cell_x[0] - grid.stroke_rects(self.cell_x, self.cell_y, height = self.cell_size, width = self.cell_size) - value = matrix[self.from_row : self.from_row + self.num_rows, self.from_col : self.from_col + self.num_rows] - colors = self._generate_color(self.connectivity, value = value, matrix_name = matrix_name) - matrix_view.fill_styled_rects(self.cell_x, self.cell_y, color = colors, height = self.cell_size , width = self.cell_size) + grid.stroke_rects(self.cell_x, self.cell_y, height=self.cell_size, width=self.cell_size) + value = matrix[self.from_row: self.from_row + self.num_rows, self.from_col: self.from_col + self.num_rows] + colors = self._generate_color(self.connectivity, value=value, matrix_name=matrix_name) + matrix_view.fill_styled_rects(self.cell_x, self.cell_y, color=colors, height=self.cell_size, + width=self.cell_size) x = 0 y = np.linspace(self.layout_offset, self.size, self.num_rows) - grid.stroke_rects(y, x, height = self.layout_offset, width = self.cell_size) #grid for row headers - grid.stroke_rects(x, y, height = self.cell_size, width = self.layout_offset) #grid for column headers + grid.stroke_rects(y, x, height=self.layout_offset, width=self.cell_size) # grid for row headers + grid.stroke_rects(x, y, height=self.cell_size, width=self.layout_offset) # grid for column headers for i in range(self.num_rows): row_header.font = f"bold {self.cell_size}px px sans serif" row_header_text = f"{self.connectivity.region_labels[self.from_row + i]}" - row_header.fill_text(row_header_text, x + 10, y[i] + self.cell_size, max_width = self.layout_offset * 0.9) - + row_header.fill_text(row_header_text, x + 10, y[i] + self.cell_size, max_width=self.layout_offset * 0.9) + column_header.font = f"bold {self.cell_size}px px sans serif" column_header_text = f"{self.connectivity.region_labels[self.from_col + i]}" - column_header.fill_text(column_header_text, x + 10, y[i] + self.cell_size, max_width = self.layout_offset * 0.9) + column_header.fill_text(column_header_text, x + 10, y[i] + self.cell_size, + max_width=self.layout_offset * 0.9) self.colorbar_x = self.size * 1.1 gradient = grid.create_linear_gradient(self.colorbar_x, self.layout_offset, self.size * 1.2, self.size, - [(i/len(self.colors),self.colors[-i-1]) for i in range(len(self.colors))]) #color gradient for color-bar + [(i / len(self.colors), self.colors[-i - 1]) for i in + range(len(self.colors))]) # color gradient for color-bar grid.fill_style = gradient - grid.fill_rect(self.colorbar_x, self.layout_offset, 20 , self.size - self.layout_offset) + grid.fill_rect(self.colorbar_x, self.layout_offset, 20, self.size - self.layout_offset) grid.fill_style = "black" for i in range(7): label_text = f"--{round(matrix.max() * (6 - i) / 6, 2)}" - color_bar.fill_text(label_text, self.colorbar_x + 20, self.size * 0.8 / 6.1 * i + self.layout_offset + 5) #labels for colorbar - + color_bar.fill_text(label_text, self.colorbar_x + 20, + self.size * 0.8 / 6.1 * i + self.layout_offset + 5) # labels for colorbar + return matrix_full - def _generate_color(self, connectivity, i = 0, j = 0, matrix_name = None , value = None): - self.colors = ["#66797b", "#543146", "#5a1c5d", "#b468ab", "#6ade42", "#27913c", "#1c464a", - "#247663", "#38bcaa", "#a9e9ff", "#61cfff", "#37a5c1", "#e4e4e2", "#ff9f25", - "#fb5226"] + def _generate_color(self, connectivity, i=0, j=0, matrix_name=None, value=None): + self.colors = ["#66797b", "#543146", "#5a1c5d", "#b468ab", "#6ade42", "#27913c", "#1c464a", + "#247663", "#38bcaa", "#a9e9ff", "#61cfff", "#37a5c1", "#e4e4e2", "#ff9f25", + "#fb5226"] color_scheme = mcolors.LinearSegmentedColormap.from_list('color_scheme', self.colors) matrix = getattr(connectivity, matrix_name) - norm = mcolors.Normalize(vmin = 0, vmax = matrix.max()) + norm = mcolors.Normalize(vmin=0, vmax=matrix.max()) if not isinstance(value, np.ndarray): - if not value: + if not value: value = matrix[int(self.from_row + i)][int(self.from_col + j)] color = color_scheme(norm(value)) - color = f"rgba({color[0]*255:.0f}, {color[1]*255:.0f}, {color[2]*255:.0f}, {color[3]:.2f})" + color = f"rgba({color[0] * 255:.0f}, {color[1] * 255:.0f}, {color[2] * 255:.0f}, {color[3]:.2f})" return color - + colors = color_scheme(norm(value)) colors = colors[:, :, :3] * 255 return colors - + def set_mouse_position(self, x, y): self.x_coord = x self.y_coord = y - + def on_cell_clicked(self, x, y, matrix_name): self.clicked_matrix = matrix_name - x_coord, y_coord = self.x_coord , self.y_coord - col = ((x_coord - self.layout_offset) // self.cell_size) + x_coord, y_coord = self.x_coord, self.y_coord + col = ((x_coord - self.layout_offset) // self.cell_size) row = ((y_coord - self.layout_offset) // self.cell_size) - if row > -1 and row < self.num_rows and col > -1 and col < self.num_rows: + if -1 < row < self.num_rows and -1 < col < self.num_rows: self.row = row self.col = col connectivity = self.new_connectivity if self.is_connectivity_being_edited else self.connectivity @@ -223,7 +232,6 @@ def on_cell_clicked(self, x, y, matrix_name): matrix_ui[5].stroke_style = "white" matrix_ui[5].stroke_rect(x, y, self.cell_size, self.cell_size) - def on_apply_change(self, change): self.is_connectivity_being_edited = True @@ -233,14 +241,14 @@ def on_apply_change(self, change): value = float(self.cell_value.value) except: value = None - + if value is not None: - matrix_name = self.clicked_matrix + matrix_name = self.clicked_matrix matrix = getattr(self.new_connectivity, matrix_name) max_val = matrix.max() matrix[self.from_row + int(self.row)][self.from_col + int(self.col)] = value if max_val != matrix.max(): - self._update_matrices_view(self.new_connectivity) + self._update_matrices_view(self.new_connectivity) self.cell_value.layout.visibility = "hidden" self.change_button.layout.visibility = "hidden" @@ -249,7 +257,8 @@ def on_apply_change(self, change): y = self.layout_offset + self.row * self.cell_size with canvas.hold_canvas(matrix_ui[0]): - matrix_ui[0].fill_style = self._generate_color(self.new_connectivity, self.row, self.col, self.clicked_matrix, value) + matrix_ui[0].fill_style = self._generate_color(self.new_connectivity, self.row, self.col, + self.clicked_matrix, value) matrix_ui[0].fill_rect(x, y, self.cell_size, self.cell_size) matrix_ui[0].stroke_rect(x, y, self.cell_size, self.cell_size) @@ -257,14 +266,14 @@ def on_apply_change(self, change): def saved_connectivities(self): conn_list = [] - for conn in self.connectivities_history: + for conn in self.connectivity_history_list: conn_list.append(conn.gid.hex) - return conn_list + return conn_list - def get_connectivity(self, gid = None): + def get_connectivity(self, gid=None): if gid is None: return self.connectivity - for conn in self.connectivities_history: + for conn in self.connectivity_history_list: if conn.gid.hex == gid: return conn @@ -273,14 +282,14 @@ def on_click_save(self, change): self.change_button.layout.visibility = "hidden" conn = self.new_connectivity - self.connectivities_history.insert(0, conn) + self.connectivity_history_list.insert(0, conn) self.connectivity = conn self.header.children = list(self.header.children)[:-1] + [self._get_history_dropdown()] self.new_connectivity = self._prepare_new_connectivity() self.is_connectivity_being_edited = False self._update_matrices_view(self.connectivity) - + def _prepare_new_connectivity(self): new_connectivity = Connectivity() new_connectivity.parent_connectivity = self.connectivity.gid.hex @@ -293,18 +302,18 @@ def _prepare_new_connectivity(self): new_connectivity.weights = self.connectivity.weights new_connectivity.tract_lengths = self.connectivity.tract_lengths new_connectivity.configure() - + return new_connectivity - + def _get_history_dropdown(self): - values = [(conn.gid.hex, conn) for conn in self.connectivities_history] + values = [(conn.gid.hex, conn) for conn in self.connectivity_history_list] default = values[values.index((self.connectivity.gid.hex, self.connectivity))][1] - dropdown = widgets.Dropdown(options = values, - description = 'View history', - disabled = False, - value = default, - ) + dropdown = widgets.Dropdown(options=values, + description='View history', + disabled=False, + value=default, + ) def on_connectivity_change(change): self.cell_value.layout.visibility = "hidden" @@ -317,7 +326,7 @@ def on_connectivity_change(change): dropdown.observe(on_connectivity_change, 'value') return dropdown - + def _update_matrices_view(self, connectivity): matrices = ["weights", "tract_lengths"] for matrix_name in matrices: @@ -331,27 +340,32 @@ def _update_matrices_view(self, connectivity): matrix_view[3].clear() matrix_view[5].clear() - value = matrix[self.from_row : self.from_row + self.num_rows, self.from_col : self.from_col + self.num_rows] - color = self._generate_color(connectivity, value = value, matrix_name = matrix_name) - matrix_view[0].fill_styled_rects(self.cell_x, self.cell_y, color = color, height = self.cell_size, width = self.cell_size) - + value = matrix[self.from_row: self.from_row + self.num_rows, + self.from_col: self.from_col + self.num_rows] + color = self._generate_color(connectivity, value=value, matrix_name=matrix_name) + matrix_view[0].fill_styled_rects(self.cell_x, self.cell_y, color=color, height=self.cell_size, + width=self.cell_size) + max_value = matrix.max() region_labels = self.connectivity.region_labels - x = 0 + x = 0 y = np.linspace(self.layout_offset, self.size, self.num_rows) for i in range(self.num_rows): row_label = region_labels[self.from_row + i] - matrix_view[1].fill_text(row_label, x + 10, y[i] + self.cell_size, max_width = self.layout_offset * 0.9) + matrix_view[1].fill_text(row_label, x + 10, y[i] + self.cell_size, + max_width=self.layout_offset * 0.9) for i in range(self.num_rows): col_label = region_labels[self.from_col + i] - matrix_view[2].fill_text(col_label, x + 10, y[i] + self.cell_size, max_width = self.layout_offset * 0.9) + matrix_view[2].fill_text(col_label, x + 10, y[i] + self.cell_size, + max_width=self.layout_offset * 0.9) for i in range(7): value = f"--{round(max_value * (6 - i) / 6, 2)}" - matrix_view[3].fill_text(value, self.colorbar_x + 20, ((self.size - self.layout_offset) / 6.1) * i + self.layout_offset + 5) #labels for colorbar + matrix_view[3].fill_text(value, self.colorbar_x + 20, ((self.size - self.layout_offset) / 6.1) * i + + self.layout_offset + 5) # labels for colorbar def display(self): display(self.header) - display(self.tab) \ No newline at end of file + display(self.tab) From fe9318b3b82296fce85b45ef731d43a4958db3cb Mon Sep 17 00:00:00 2001 From: "RIO\\teodora.misan" Date: Mon, 9 Sep 2024 12:17:44 +0300 Subject: [PATCH 21/21] WID-247-2: resolve test failure after renaming params --- tvbwidgets/tests/test_connectivity_matrix_editor_widget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py b/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py index ccf7899..7ef6b71 100644 --- a/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py +++ b/tvbwidgets/tests/test_connectivity_matrix_editor_widget.py @@ -115,7 +115,7 @@ def test_get_connectivity(wid): def test_on_click_save(wid): wid.save_button.click() - assert len(wid.connectivities_history) == 2 + assert len(wid.connectivity_history_list) == 2 def test_prepare_new_connectivity(wid): conn = wid._prepare_new_connectivity()