diff --git a/clans/clans/GUI/group_dialogs.py b/clans/clans/GUI/group_dialogs.py index 6fdb54e..7b1244e 100644 --- a/clans/clans/GUI/group_dialogs.py +++ b/clans/clans/GUI/group_dialogs.py @@ -915,11 +915,11 @@ def __init__(self, net_plot_object, dim_num, z_index_mode, color_by, group_by): self.delete_category_button = QPushButton("Delete") self.delete_category_button.released.connect(self.delete_category) - self.buttons_layout.addWidget(self.add_button) self.buttons_layout.addWidget(self.edit_button) self.buttons_layout.addWidget(self.move_up_button) self.buttons_layout.addWidget(self.move_down_button) self.buttons_layout.addWidget(self.delete_category_button) + self.buttons_layout.addWidget(self.add_button) self.buttons_layout.addStretch() self.main_layout.addLayout(self.buttons_layout) @@ -974,34 +974,24 @@ def add_category(self): item = QListWidgetItem(cfg.groups_by_categories[category_index]['name']) self.categories_list.insertItem(category_index+1, item) - is_changed_points_size = 0 - is_changed_names_size = 0 - is_changed_outline_color = 0 - is_changed_bold = 0 - is_changed_italic = 0 - if points_size != cfg.groups_by_categories[category_index]['nodes_size']: - is_changed_points_size = 1 - cfg.groups_by_categories[category_index]['nodes_size'] = points_size + cfg.groups_by_categories[category_index]['nodes_size'] = points_size if names_size != cfg.groups_by_categories[category_index]['text_size']: - is_changed_names_size = 1 - cfg.groups_by_categories[category_index]['text_size'] = names_size + cfg.groups_by_categories[category_index]['text_size'] = names_size if ColorArray(outline_color).hex != \ ColorArray(cfg.groups_by_categories[category_index]['nodes_outline_color']).hex: - is_changed_outline_color = 1 - cfg.groups_by_categories[category_index]['nodes_outline_color'] = outline_color + cfg.groups_by_categories[category_index]['nodes_outline_color'] = outline_color if is_bold != cfg.groups_by_categories[category_index]['is_bold']: - is_changed_bold = 1 - cfg.groups_by_categories[category_index]['is_bold'] = is_bold + cfg.groups_by_categories[category_index]['is_bold'] = is_bold if is_italic != cfg.groups_by_categories[category_index]['is_italic']: - is_changed_italic = 1 - cfg.groups_by_categories[category_index]['is_italic'] = is_italic + cfg.groups_by_categories[category_index]['is_italic'] = is_italic - cfg.groups_by_categories[category_index]['nodes_outline_width'] = outline_width + if outline_width != cfg.groups_by_categories[category_index]['nodes_outline_width']: + cfg.groups_by_categories[category_index]['nodes_outline_width'] = outline_width # Mark the new category's line self.categories_list.setCurrentRow(category_index-1) @@ -1092,6 +1082,14 @@ def edit_category(self): error_occurred(self.net_plot_object.hide_group_names, 'hide_group_names', err, error_msg) return + if self.z_index_mode == 'groups': + try: + self.net_plot_object.hide_scatter_by_groups() + except Exception as err: + error_msg = "An error occurred: cannot remove scatter by groups" + error_occurred(self.net_plot_object.hide_scatter_by_groups, 'hide_scatter_by_groups', err, + error_msg) + try: self.net_plot_object.update_group_by(self.dim_num, self.z_index_mode, self.color_by, self.group_by) except Exception as err: diff --git a/clans/clans/GUI/main_window.py b/clans/clans/GUI/main_window.py index 8e40333..d52989a 100644 --- a/clans/clans/GUI/main_window.py +++ b/clans/clans/GUI/main_window.py @@ -820,7 +820,6 @@ def receive_load_status(self, status): self.round_num_label.setStyleSheet("color: black;") self.mode_combo.setEnabled(True) self.select_all_button.setEnabled(True) - self.clear_selection_button.setEnabled(True) self.select_by_text_button.setEnabled(True) self.connections_button.setEnabled(True) self.hide_singeltons_button.setEnabled(True) @@ -1092,7 +1091,6 @@ def run_calc(self): self.mode_combo.setEnabled(False) self.selection_type_combo.setEnabled(False) self.select_all_button.setEnabled(False) - self.clear_selection_button.setEnabled(False) self.select_by_text_button.setEnabled(False) self.select_by_groups_button.setEnabled(False) self.edit_groups_button.setEnabled(False) @@ -1172,7 +1170,6 @@ def stopped_state(self, error): if self.is_subset_mode == 0: self.mode_combo.setEnabled(True) self.select_all_button.setEnabled(True) - self.clear_selection_button.setEnabled(True) self.select_by_text_button.setEnabled(True) @@ -1861,28 +1858,45 @@ def color_by_seq_length(self): self.color_by_combo.setCurrentText('Seq. length') self.color_by_combo.setEnabled(True) - else: - try: - gradient_colormap = colors.generate_colormap_gradient_2_colors(cfg.short_color, cfg.long_color) - except Exception as err: - error_msg = "An error occurred: cannot generate colormap" - error_occurred(colors.generate_colormap_gradient_2_colors, 'generate_colormap_gradient_2_colors', err, - error_msg) - return + # Produce the real colormap + try: + gradient_colormap = colors.generate_colormap_gradient_2_colors(cfg.short_color, cfg.long_color) + except Exception as err: + error_msg = "An error occurred: cannot generate colormap" + error_occurred(colors.generate_colormap_gradient_2_colors, 'generate_colormap_gradient_2_colors', err, + error_msg) + return - try: - self.network_plot.color_by_param(gradient_colormap, cfg.sequences_array['norm_seq_length'], - self.dim_num, self.z_indexing_mode, self.color_by, self.group_by) - except Exception as err: - error_msg = "An error occurred: cannot color the data by sequence-length" - error_occurred(self.network_plot.color_by_param, 'color_by_param', err, error_msg) - return + # Produce an opposite colormap just for the colorbar presentation (workaround a bug in the colorbar visual) + try: + opposite_gradient_colormap = colors.generate_colormap_gradient_2_colors(cfg.long_color, cfg.short_color) + except Exception as err: + error_msg = "An error occurred: cannot generate colormap" + error_occurred(colors.generate_colormap_gradient_2_colors, 'generate_colormap_gradient_2_colors', err, + error_msg) + return - try: - self.colorbar_plot.show_colorbar(gradient_colormap, cfg.sequences_array['seq_length'], 'Sequences length') - except Exception as err: - error_msg = "An error occurred: cannot display the colorbar" - error_occurred(self.colorbar_plot.show_colorbar, 'show_colorbar', err, error_msg) + try: + seq.normalize_seq_length() + except Exception as err: + error_msg = "An error occurred: cannot normalize the new range of sequence length" + error_occurred(seq.normalize_seq_length, 'normalize_seq_length', err, error_msg) + return + + try: + self.network_plot.color_by_param(gradient_colormap, cfg.sequences_array['norm_seq_length'], + self.dim_num, self.z_indexing_mode, self.color_by, self.group_by) + except Exception as err: + error_msg = "An error occurred: cannot color the data by sequence-length" + error_occurred(self.network_plot.color_by_param, 'color_by_param', err, error_msg) + return + + try: + self.colorbar_plot.show_colorbar(opposite_gradient_colormap, 'Sequences length', + cfg.run_params['min_seq_length'], cfg.run_params['max_seq_length']) + except Exception as err: + error_msg = "An error occurred: cannot display the colorbar" + error_occurred(self.colorbar_plot.show_colorbar, 'show_colorbar', err, error_msg) def open_color_by_length_dialog(self): @@ -1890,7 +1904,8 @@ def open_color_by_length_dialog(self): dlg = md.ColorByLengthDialog() if dlg.exec_(): - cfg.short_color, cfg.long_color = dlg.get_colors() + cfg.short_color, cfg.long_color, cfg.run_params['min_seq_length'], cfg.run_params['max_seq_length'] = \ + dlg.get_colors() self.color_by_seq_length() @@ -1967,6 +1982,7 @@ def color_by_user_param(self, param): min_param_color = cfg.sequences_numeric_params[param]['min_color'] max_param_color = cfg.sequences_numeric_params[param]['max_color'] + # Produce the real colormap try: gradient_colormap = colors.generate_colormap_gradient_2_colors(min_param_color, max_param_color) except Exception as err: @@ -1975,6 +1991,22 @@ def color_by_user_param(self, param): error_msg) return + # Produce an opposite colormap just for the colorbar presentation (workaround a bug in the colorbar visual) + try: + opposite_gradient_colormap = colors.generate_colormap_gradient_2_colors(max_param_color, min_param_color) + except Exception as err: + error_msg = "An error occurred: cannot generate colormap" + error_occurred(colors.generate_colormap_gradient_2_colors, 'generate_colormap_gradient_2_colors', err, + error_msg) + return + + try: + seq.normalize_numeric_param(param) + except Exception as err: + error_msg = "An error occurred: cannot normalize the new range of values" + error_occurred(seq.normalize_numeric_param, 'normalize_numeric_param', err, error_msg) + return + try: self.network_plot.color_by_param(gradient_colormap, cfg.sequences_numeric_params[param]['norm'], self.dim_num, self.z_indexing_mode, self.color_by, self.group_by) @@ -1984,7 +2016,8 @@ def color_by_user_param(self, param): return try: - self.colorbar_plot.show_colorbar(gradient_colormap, cfg.sequences_numeric_params[param]['raw'], param) + self.colorbar_plot.show_colorbar(opposite_gradient_colormap, param, cfg.sequences_numeric_params[param]['min_val'], + cfg.sequences_numeric_params[param]['max_val']) except Exception as err: error_msg = "An error occurred: cannot display the colorbar" error_occurred(self.colorbar_plot.show_colorbar, 'show_colorbar', err, error_msg) @@ -1995,7 +2028,7 @@ def open_color_by_param_dialog(self): dlg = md.ColorByParamDialog() if dlg.exec_(): - selected_param, added_params_list, min_param_color, max_param_color = dlg.get_param() + selected_param, added_params_list, min_param_color, max_param_color, min_val, max_val = dlg.get_param() if selected_param: @@ -2012,6 +2045,10 @@ def open_color_by_param_dialog(self): cfg.sequences_numeric_params[selected_param]['min_color'] = min_param_color cfg.sequences_numeric_params[selected_param]['max_color'] = max_param_color + # Update the values range of the selected parameter + cfg.sequences_numeric_params[selected_param]['min_val'] = min_val + cfg.sequences_numeric_params[selected_param]['max_val'] = max_val + self.color_by_user_param(selected_param) except Exception as err: @@ -2117,7 +2154,6 @@ def select_all(self): self.show_selected_names_button.setEnabled(True) self.open_selected_button.setEnabled(True) self.clear_selection_button.setEnabled(True) - self.inverse_selection_button.setEnabled(True) self.add_to_group_button.setEnabled(True) self.remove_selected_button.setEnabled(True) @@ -2253,6 +2289,7 @@ def manage_subset_presentation(self): self.mode_combo.setCurrentIndex(0) self.select_all_button.setEnabled(False) self.clear_selection_button.setEnabled(False) + self.inverse_selection_button.setEnabled(False) self.z_index_mode_combo.setCurrentIndex(0) self.z_index_mode_combo.setEnabled(False) self.z_index_mode_label.setStyleSheet("color: " + cfg.inactive_color + ";") @@ -2291,6 +2328,7 @@ def manage_subset_presentation(self): self.mode_combo.setEnabled(True) self.select_all_button.setEnabled(True) self.clear_selection_button.setEnabled(True) + self.inverse_selection_button.setEnabled(True) self.select_by_text_button.setEnabled(True) self.select_by_groups_button.setEnabled(True) diff --git a/clans/clans/GUI/metadata_dialogs.py b/clans/clans/GUI/metadata_dialogs.py index e07305b..3d88794 100644 --- a/clans/clans/GUI/metadata_dialogs.py +++ b/clans/clans/GUI/metadata_dialogs.py @@ -3,6 +3,7 @@ from PyQt5.QtCore import QThreadPool from vispy.color import ColorArray import numpy as np +import re import clans.config as cfg import clans.clans.io.io_gui as io import clans.clans.data.sequences as seq @@ -534,28 +535,26 @@ def __init__(self): self.main_layout = QVBoxLayout() self.layout = QGridLayout() - self.title = QLabel("Define the color range:") + self.title = QLabel("Define the colors and values range:") self.layout.addWidget(self.title, 0, 0, 1, 3) self.row_space = QLabel(" ") self.row_space.setFixedSize(150, 15) self.layout.addWidget(self.row_space, 1, 0, 1, 2) - self.short_label = QLabel("Short") - self.long_label = QLabel("Long") + self.short_label = QLabel("Min. length") + self.long_label = QLabel("Max. length") self.layout.addWidget(self.short_label, 2, 0) self.layout.addWidget(self.long_label, 2, 2) self.short_color = cfg.short_color self.short_color_button = QPushButton("Change") - self.short_color_button.setFixedSize(65, 28) self.short_color_button.setStyleSheet("background-color: " + self.short_color.hex[0]) self.short_color_button.pressed.connect(self.change_short_color) self.long_color = cfg.long_color self.long_color_button = QPushButton("Change") - self.long_color_button.setFixedSize(65, 28) self.long_color_button.setStyleSheet("background-color: " + self.long_color.hex[0]) self.long_color_button.pressed.connect(self.change_long_color) @@ -567,6 +566,23 @@ def __init__(self): self.layout.addWidget(self.switch_button, 3, 1) self.layout.addWidget(self.long_color_button, 3, 2) + self.min_length_widget = QLineEdit() + self.min_length_widget.setFixedSize(55, 20) + self.min_length = cfg.run_params['min_seq_length'] + self.min_length_widget.setText(str(self.min_length)) + + self.max_length = cfg.run_params['max_seq_length'] + self.max_length_widget = QLineEdit() + self.max_length_widget.setFixedSize(55, 20) + self.max_length_widget.setText(str(self.max_length)) + + self.layout.addWidget(self.min_length_widget, 4, 0) + self.layout.addWidget(self.max_length_widget, 4, 2) + + self.row_space2 = QLabel(" ") + self.row_space2.setFixedSize(150, 15) + self.layout.addWidget(self.row_space, 5, 0, 1, 2) + self.main_layout.addLayout(self.layout) # Add the OK/Cancel standard buttons @@ -616,7 +632,14 @@ def switch_colors(self): self.long_color_button.setStyleSheet("background-color: " + self.long_color.hex[0]) def get_colors(self): - return self.short_color, self.long_color + + if re.search("^\d+$", self.min_length_widget.text()): + self.min_length = int(self.min_length_widget.text()) + + if re.search("^\d+$", self.max_length_widget.text()): + self.max_length = int(self.max_length_widget.text()) + + return self.short_color, self.long_color, self.min_length, self.max_length class ColorByParamDialog(QDialog): @@ -663,7 +686,7 @@ def __init__(self): self.layout.addWidget(self.row_space, 5, 0, 1, 3) - self.color_range_label = QLabel("Define the color range:") + self.color_range_label = QLabel("Define the colors and values range:") self.colors_layout.addWidget(self.color_range_label, 0, 0, 1, 2) self.min_label = QLabel("Min. value") @@ -674,13 +697,13 @@ def __init__(self): self.min_color = cfg.min_param_color self.min_color_button = QPushButton("Change") - self.min_color_button.setFixedSize(65, 28) + #self.min_color_button.setFixedSize(65, 28) self.min_color_button.setStyleSheet("background-color: " + self.min_color.hex[0]) self.min_color_button.pressed.connect(self.change_min_color) self.max_color = cfg.max_param_color self.max_color_button = QPushButton("Change") - self.max_color_button.setFixedSize(65, 28) + #self.max_color_button.setFixedSize(65, 28) self.max_color_button.setStyleSheet("background-color: " + self.max_color.hex[0]) self.max_color_button.pressed.connect(self.change_max_color) @@ -692,11 +715,20 @@ def __init__(self): self.colors_layout.addWidget(self.switch_button, 2, 1) self.colors_layout.addWidget(self.max_color_button, 2, 2) - #self.layout.addLayout(self.colors_layout, 6, 0, 1, 2) + self.min_val_widget = QLineEdit() + self.min_val_widget.setFixedSize(55, 20) + self.min_val = 0 + + self.max_val_widget = QLineEdit() + self.max_val_widget.setFixedSize(55, 20) + self.max_val = 0 + + self.colors_layout.addWidget(self.min_val_widget, 3, 0) + self.colors_layout.addWidget(self.max_val_widget, 3, 2) + self.layout.addLayout(self.colors_layout, 6, 0) self.main_layout.addLayout(self.layout) - #self.main_layout.addLayout(self.colors_layout) # Add the OK/Cancel standard buttons self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) @@ -713,13 +745,18 @@ def __init__(self): for param in cfg.sequences_numeric_params: self.param_combo.addItem(param) - # Set the colors of the first parameter in the list + # Set the colors and values of the first parameter in the list if param_index == 0: self.min_color = cfg.sequences_numeric_params[param]['min_color'] self.min_color_button.setStyleSheet("background-color: " + self.min_color.hex[0]) self.max_color = cfg.sequences_numeric_params[param]['max_color'] self.max_color_button.setStyleSheet("background-color: " + self.max_color.hex[0]) + self.min_val = cfg.sequences_numeric_params[param]['min_val'] + self.max_val = cfg.sequences_numeric_params[param]['max_val'] + self.min_val_widget.setText(str(self.min_val)) + self.max_val_widget.setText(str(self.max_val)) + param_index += 1 self.message_label.hide() @@ -738,6 +775,8 @@ def __init__(self): self.min_color_button.hide() self.switch_button.hide() self.max_color_button.hide() + self.min_val_widget.hide() + self.max_val_widget.hide() def upload_file(self): try: @@ -785,6 +824,8 @@ def update_metadata(self, sequences_params_dict, error): self.min_color_button.show() self.switch_button.show() self.max_color_button.show() + self.min_val_widget.show() + self.max_val_widget.show() else: self.message_label.setText(error) @@ -837,11 +878,22 @@ def change_param(self): self.min_color_button.setStyleSheet("background-color: " + self.min_color.hex[0]) self.max_color = cfg.sequences_numeric_params[selected_param]['max_color'] self.max_color_button.setStyleSheet("background-color: " + self.max_color.hex[0]) + self.min_val = cfg.sequences_numeric_params[selected_param]['min_val'] + self.max_val = cfg.sequences_numeric_params[selected_param]['max_val'] + self.min_val_widget.setText(str(self.min_val)) + self.max_val_widget.setText(str(self.max_val)) def get_param(self): selected_param_name = self.param_combo.currentText() - return selected_param_name, self.added_params, self.min_color, self.max_color + + if re.search("^\d+\.?\d*(e-\d+)*$", self.min_val_widget.text()): + self.min_val = float(self.min_val_widget.text()) + + if re.search("^\d+\.?\d*(e-\d+)*$", self.max_val_widget.text()): + self.max_val = float(self.max_val_widget.text()) + + return selected_param_name, self.added_params, self.min_color, self.max_color, self.min_val, self.max_val diff --git a/clans/clans/GUI/windows.py b/clans/clans/GUI/windows.py index 4b7d8e7..bfb4576 100644 --- a/clans/clans/GUI/windows.py +++ b/clans/clans/GUI/windows.py @@ -631,6 +631,8 @@ def add_to_selected(self): # Enable all the controls that are related to selected items in the Main Window self.main_window_object.open_selected_button.setEnabled(True) + self.main_window_object.clear_selection_button.setEnabled(True) + self.main_window_object.inverse_selection_button.setEnabled(True) self.main_window_object.show_selected_names_button.setEnabled(True) self.main_window_object.add_to_group_button.setEnabled(True) self.main_window_object.remove_selected_button.setEnabled(True) @@ -686,6 +688,8 @@ def set_as_selected(self): # Enable all the controls that are related to selected items in the Main Window self.main_window_object.open_selected_button.setEnabled(True) + self.main_window_object.clear_selection_button.setEnabled(True) + self.main_window_object.inverse_selection_button.setEnabled(True) self.main_window_object.show_selected_names_button.setEnabled(True) self.main_window_object.add_to_group_button.setEnabled(True) self.main_window_object.remove_selected_button.setEnabled(True) @@ -859,6 +863,8 @@ def add_to_selected(self): # Enable all the controls that are related to selected items in the Main Window self.main_window_object.open_selected_button.setEnabled(True) + self.main_window_object.clear_selection_button.setEnabled(True) + self.main_window_object.inverse_selection_button.setEnabled(True) self.main_window_object.show_selected_names_button.setEnabled(True) self.main_window_object.add_to_group_button.setEnabled(True) self.main_window_object.remove_selected_button.setEnabled(True) @@ -914,6 +920,8 @@ def set_as_selected(self): # Enable all the controls that are related to selected items in the Main Window self.main_window_object.open_selected_button.setEnabled(True) + self.main_window_object.clear_selection_button.setEnabled(True) + self.main_window_object.inverse_selection_button.setEnabled(True) self.main_window_object.show_selected_names_button.setEnabled(True) self.main_window_object.add_to_group_button.setEnabled(True) self.main_window_object.remove_selected_button.setEnabled(True) diff --git a/clans/clans/data/sequences.py b/clans/clans/data/sequences.py index 11c340f..f725ac8 100644 --- a/clans/clans/data/sequences.py +++ b/clans/clans/data/sequences.py @@ -5,7 +5,7 @@ import random -#@profile +# @profile def create_sequences_array(sequences_list): # Resize the sequences array according to the input number of sequences np.resize(cfg.sequences_array, cfg.run_params['total_sequences_num']) @@ -14,7 +14,7 @@ def create_sequences_array(sequences_list): # seq_title, sequence, x_coordinate, y_coordinate, z_cordinate, in_group(initialized with -1) cfg.sequences_array = np.array(sequences_list, dtype=cfg.seq_dt) - #print("create_sequences_array: Sequences_array=\n" + str(cfg.sequences_array)) + # print("create_sequences_array: Sequences_array=\n" + str(cfg.sequences_array)) def init_groups_by_categories(): @@ -22,14 +22,29 @@ def init_groups_by_categories(): def add_seq_length_param(): - cfg.sequences_array['seq_length'] = np.char.str_len(cfg.sequences_array['sequence']) - min_seq_length = np.amin(cfg.sequences_array['seq_length']) - max_seq_length = np.amax(cfg.sequences_array['seq_length']) + cfg.run_params['min_seq_length'] = np.amin(cfg.sequences_array['seq_length']) + cfg.run_params['max_seq_length'] = np.amax(cfg.sequences_array['seq_length']) + + normalize_seq_length() + + +def normalize_seq_length(): + min_seq_length = cfg.run_params['min_seq_length'] + max_seq_length = cfg.run_params['max_seq_length'] if min_seq_length != max_seq_length: - cfg.sequences_array['norm_seq_length'] = (cfg.sequences_array['seq_length'] - min_seq_length) / \ + + # Set the sequences below the defined minimum to the minimal value + updated_seq_length = np.where(cfg.sequences_array['seq_length'] <= min_seq_length, + np.amin(cfg.sequences_array['seq_length']), cfg.sequences_array['seq_length']) + + # Set the sequences above the defined maximum to the maximal value + updated_seq_length = np.where(cfg.sequences_array['seq_length'] >= max_seq_length, + np.amax(cfg.sequences_array['seq_length']), cfg.sequences_array['seq_length']) + + cfg.sequences_array['norm_seq_length'] = (updated_seq_length - min_seq_length) / \ (max_seq_length - min_seq_length) else: @@ -40,7 +55,6 @@ def add_seq_length_param(): def add_numeric_params(params_dict): - added_params = [] for param_name in params_dict: @@ -59,6 +73,11 @@ def add_numeric_params(params_dict): cfg.sequences_numeric_params[param_name] = dict() cfg.sequences_numeric_params[param_name]['raw'] = np.array(values_list, dtype=float) + cfg.sequences_numeric_params[param_name]['min_val'] = \ + np.round(np.amin(cfg.sequences_numeric_params[param_name]['raw']), 2) + cfg.sequences_numeric_params[param_name]['max_val'] = \ + np.round(np.amax(cfg.sequences_numeric_params[param_name]['raw']), 2) + normalize_numeric_param(param_name) cfg.sequences_numeric_params[param_name]['min_color'] = cfg.min_param_color @@ -71,11 +90,15 @@ def add_numeric_params(params_dict): # Add numeric parameters that were saved in a full CLANS file (including colors) def add_saved_numeric_params(params_dict): - for param_name in params_dict: cfg.sequences_numeric_params[param_name] = dict() cfg.sequences_numeric_params[param_name]['raw'] = np.array(params_dict[param_name]['values'], dtype=float) + cfg.sequences_numeric_params[param_name]['min_val'] = \ + np.round(np.amin(cfg.sequences_numeric_params[param_name]['raw']), 2) + cfg.sequences_numeric_params[param_name]['max_val'] = \ + np.round(np.amax(cfg.sequences_numeric_params[param_name]['raw']), 2) + normalize_numeric_param(param_name) min_color_arr = params_dict[param_name]['min_color'].split(';') @@ -89,13 +112,22 @@ def add_saved_numeric_params(params_dict): def normalize_numeric_param(param_name): - - min_val = np.amin(cfg.sequences_numeric_params[param_name]['raw']) - max_val = np.amax(cfg.sequences_numeric_params[param_name]['raw']) + min_val = cfg.sequences_numeric_params[param_name]['min_val'] + max_val = cfg.sequences_numeric_params[param_name]['max_val'] if min_val != max_val: - cfg.sequences_numeric_params[param_name]['norm'] = (cfg.sequences_numeric_params[param_name]['raw'] - min_val) / \ - (max_val - min_val) + + # Set the sequences below the defined minimum to the minimal value + updated_values = np.where(cfg.sequences_numeric_params[param_name]['raw'] <= min_val, + np.amin(cfg.sequences_numeric_params[param_name]['raw']), + cfg.sequences_numeric_params[param_name]['raw']) + + # Set the sequences above the defined maximum to the maximal value + updated_values = np.where(cfg.sequences_numeric_params[param_name]['raw'] >= max_val, + np.amax(cfg.sequences_numeric_params[param_name]['raw']), + cfg.sequences_numeric_params[param_name]['raw']) + + cfg.sequences_numeric_params[param_name]['norm'] = (updated_values - min_val) / (max_val - min_val) else: if min_val == 0: @@ -106,7 +138,6 @@ def normalize_numeric_param(param_name): # Mode: full / subset def update_positions(xyz_coor, mode): - # Full data mode -> update 'normal' coordinates if mode == "full": cfg.sequences_array['x_coor'] = xyz_coor[0] @@ -155,6 +186,3 @@ def rollback_subset_positions(): cfg.sequences_array['x_coor_subset'][i] = cfg.sequences_array['x_coor'][i] cfg.sequences_array['y_coor_subset'][i] = cfg.sequences_array['y_coor'][i] cfg.sequences_array['z_coor_subset'][i] = cfg.sequences_array['z_coor'][i] - - - diff --git a/clans/clans/graphics/colorbar.py b/clans/clans/graphics/colorbar.py index 5865bcd..6981a2b 100644 --- a/clans/clans/graphics/colorbar.py +++ b/clans/clans/graphics/colorbar.py @@ -16,17 +16,16 @@ def __init__(self, view): self.colorbar = scene.visuals.ColorBar(cmap='cool', orientation='right', size=(60, 2)) self.colorbar.pos = (0, 20) self.colorbar.label = 'Sequences length' - # self.colorbar.set_gl_state('translucent', blend=True) - def show_colorbar(self, colormap, param_array, param_name): + def show_colorbar(self, colormap, param_name, min_val, max_val): self.colorbar.parent = None - min_param = np.round(np.amin(param_array), 2) - max_param = np.round(np.amax(param_array), 2) - # Generate and display a color-bar legend self.colorbar.cmap = colormap - self.colorbar.clim = (min_param, max_param) + + # Because of a bug in the colorbar visual (it is displayed upside down) - switch the order of the min/max values + #self.colorbar.clim = (min_val, max_val) + self.colorbar.clim = (max_val, min_val) self.colorbar.label = param_name diff --git a/clans/clans/graphics/network3d.py b/clans/clans/graphics/network3d.py index 0c941a4..2264094 100644 --- a/clans/clans/graphics/network3d.py +++ b/clans/clans/graphics/network3d.py @@ -1723,19 +1723,22 @@ def select_all(self, selection_type, dim_num, z_index_mode, color_by, group_by, def inverse_selection(self, dim_num_view, z_index_mode, color_by, group_by, is_show_group_names, group_names_display): + new_selected_points = [] + old_selected_points = [] + for seq_index in range(cfg.run_params['total_sequences_num']): # Select the non-selected if seq_index not in self.selected_points: self.selected_points[seq_index] = 1 cfg.sequences_array[seq_index]['in_subset'] = True - self.nodes_outline_color_array[seq_index] = self.selected_outline_color - self.nodes_size_array[seq_index] += 5 + new_selected_points.append(seq_index) # Deselect the selected else: del self.selected_points[seq_index] cfg.sequences_array[seq_index]['in_subset'] = False + old_selected_points.append(seq_index) if color_by == 'groups': @@ -1757,17 +1760,23 @@ def inverse_selection(self, dim_num_view, z_index_mode, color_by, group_by, is_s self.nodes_outline_color_array[seq_index] = self.nodes_outline_default_color self.nodes_size_array[seq_index] = self.nodes_size - # Hide the selected group names and initialize the selected group names visual - if is_show_group_names and group_names_display == 'selected': - self.hide_group_names() - self.selected_groups_text_visual = {} + # Unmark the old selection + self.unmark_selected_points(old_selected_points, 2, z_index_mode, color_by, group_by) - # Empty the selected_groups dictionaries - self.selected_groups = {} + # Mark the new selection + self.mark_selected_points(new_selected_points, z_index_mode, color_by, group_by) - self.update_sequences_names(dim_num_view) + # Hide the selected group names and initialize the selected group names visual + if is_show_group_names and group_names_display == 'selected': + self.hide_group_names() + self.selected_groups_text_visual = {} + + # Empty the selected_groups dictionaries + self.selected_groups = {} + + self.update_sequences_names(dim_num_view) - self.update_view(dim_num_view, color_by, group_by, z_index_mode) + #self.update_view(dim_num_view, color_by, group_by, z_index_mode) def select_subset(self, selected_dict, dim_num, z_index_mode, color_by, group_by): diff --git a/clans/clans/io/file_formats/tab_delimited_format.py b/clans/clans/io/file_formats/tab_delimited_format.py index d88d7f1..adf5a7b 100644 --- a/clans/clans/io/file_formats/tab_delimited_format.py +++ b/clans/clans/io/file_formats/tab_delimited_format.py @@ -281,7 +281,7 @@ def read_metadata(self, file_path): for i in range(1, col_num): value = row[i] - if not re.search("\d+\.?\d?", value): + if not re.search("\d+\.?\d*(e-\d+)*", value): error = "Invalid values: the values must be int/float numbers.\n" \ "Please correct the file and reload." return sequences_params_dict, error diff --git a/clans/config.py b/clans/config.py index b6d2876..0b8228e 100644 --- a/clans/config.py +++ b/clans/config.py @@ -3,7 +3,7 @@ import clans ## Defaults -version = "2.0.6" +version = "2.0.7" # i/o related parameters type_of_values = 'hsp' diff --git a/clans/manual/Manual.docx b/clans/manual/Manual.docx index f24c837..c624867 100644 Binary files a/clans/manual/Manual.docx and b/clans/manual/Manual.docx differ diff --git a/clans/manual/Manual.pdf b/clans/manual/Manual.pdf index 6a8a6a8..1ddc297 100644 Binary files a/clans/manual/Manual.pdf and b/clans/manual/Manual.pdf differ diff --git a/conda_build/meta.yaml b/conda_build/meta.yaml index 6965495..5974f05 100644 --- a/conda_build/meta.yaml +++ b/conda_build/meta.yaml @@ -1,5 +1,5 @@ {% set name = "clans" %} -{% set version = "2.0.6" %} +{% set version = "2.0.7" %} package: name: {{ name|lower }} @@ -8,7 +8,7 @@ package: source: git_tag: {{ version }} url: https://github.com/inbalpaz/CLANS/releases/download/{{ version }}/CLANS_{{ version }}.tar.gz - sha256: 0eab9c0af1a28f7efdcb4321c8a3f44b19f6ad6331d96f67e270e401e1b364ed + sha256: 084298dd001cb8c5759590b47b7da903f1ae098963fd9c640a8a7db31797c3d5 build: noarch: python diff --git a/conda_build_linux/meta.yaml b/conda_build_linux/meta.yaml index 19dd0c1..bcc9616 100644 --- a/conda_build_linux/meta.yaml +++ b/conda_build_linux/meta.yaml @@ -1,5 +1,5 @@ {% set name = "clans_linux" %} -{% set version = "2.0.6" %} +{% set version = "2.0.7" %} package: name: {{ name|lower }} @@ -8,7 +8,7 @@ package: source: git_tag: {{ version }} url: https://github.com/inbalpaz/CLANS/releases/download/{{ version }}/CLANS_{{ version }}.tar.gz - sha256: 0eab9c0af1a28f7efdcb4321c8a3f44b19f6ad6331d96f67e270e401e1b364ed + sha256: 084298dd001cb8c5759590b47b7da903f1ae098963fd9c640a8a7db31797c3d5 build: noarch: python