diff --git a/Docs/ChangeLog.md b/Docs/ChangeLog.md index 9ffc4f6..9fdf39e 100644 --- a/Docs/ChangeLog.md +++ b/Docs/ChangeLog.md @@ -1,3 +1,10 @@ +# Version: 1.107.2 +## Date: 2024-06-16 +### Changes: +- New: Added an option in 'Help' menu to open the online Tutorial page. +- Change: Co-expression Network plot: Use the corelation value as the weight of the edge, and improve the layout of the plot. +- Fix: Fixed the bug of when plot the Taxa-Functions Network, the shape setting was not work withotu focus list. + # Version: 1.107.1 ## Date: 2024-06-16 ### Changes: diff --git a/Docs/MetaX_Cookbook.assets/settings_page2.png b/Docs/MetaX_Cookbook.assets/settings_page2.png index abfd9b8..2564529 100644 Binary files a/Docs/MetaX_Cookbook.assets/settings_page2.png and b/Docs/MetaX_Cookbook.assets/settings_page2.png differ diff --git a/Docs/MetaX_Cookbook.assets/tf_link_net_2.png b/Docs/MetaX_Cookbook.assets/tf_link_net_2.png new file mode 100644 index 0000000..c51288c Binary files /dev/null and b/Docs/MetaX_Cookbook.assets/tf_link_net_2.png differ diff --git a/README.md b/README.md index fea6b74..ea3c64e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,15 @@ MetaX also features statistical modules and plotting tools for ana ## Taxa-Functions Linkage Linking Taxa and Functions in different levels of the hierarchy, and different functional categories. e.g., **Species-KO**, **Genus-CAZy**, **Phylum-EC**, etc. -![OTF](./Docs/MetaX_Cookbook.assets/tf_link_net.png) +- ![OTF](./Docs/MetaX_Cookbook.assets/tf_link_net.png) + + + +e.g. The **KEGG Pathways** linked to ***Roseburia hominis*** + +- ![tf_link_net_2](./Docs/MetaX_Cookbook.assets/tf_link_net_2.png) + + ## Download ### `Desktop Version(Recommended)`: diff --git a/utils/GUI.py b/utils/GUI.py index e30d897..7b8bdf9 100644 --- a/utils/GUI.py +++ b/utils/GUI.py @@ -163,8 +163,8 @@ def __init__(self, MainWindow): self.tf_link_net_params_dict = {'taxa_shape': 'circle', 'func_shape': 'rect', 'taxa_color': '#374E55','taxa_focus_color': '#6A6599', 'func_color': '#DF8F44', 'func_focus_color': '#B24745', - 'line_opacity': 0.5, 'line_width': 2, 'line_curve': 0.1, - 'line_color': '#9aa7b1', 'repulsion': 500 + 'line_opacity': 0.5, 'line_width': 2, 'line_curve': 0, + 'line_color': '#9aa7b1', 'repulsion': 500, 'font_weight': 'bold' } @@ -182,12 +182,14 @@ def __init__(self, MainWindow): self.actionAny_Table_Mode.setIcon(qta.icon('mdi.table')) self.actionCheck_Update.setIcon(qta.icon('mdi.update')) self.actionSettings.setIcon(qta.icon('mdi.cog')) + self.actionTutorial.setIcon(qta.icon('mdi6.book-open-page-variant-outline')) # set menu bar click event self.actionTaxaFuncAnalyzer.triggered.connect(self.swith_stack_page_analyzer) self.actionPeptide_to_TaxaFunc.triggered.connect(self.swith_stack_page_pep2taxafunc) self.actionDatabase_Builder.triggered.connect(self.swith_stack_page_dbuilder) self.actionDatabase_Update.triggered.connect(self.swith_stack_page_db_update) self.actionAbout.triggered.connect(self.show_about) + self.actionTutorial.triggered.connect(self.open_tutorial) self.actionRestore_Last_TaxaFunc.triggered.connect(lambda: self.run_restore_taxafunnc_obj_from_file(last=True)) self.actionRestore_From.triggered.connect(self.run_restore_taxafunnc_obj_from_file) self.actionSave_As.triggered.connect(lambda:self.save_metax_obj_to_file(save_path=None, no_message=False)) @@ -1381,7 +1383,15 @@ def check_update(self, show_message=False, manual_check_trigger=True): updater = Updater(MetaXGUI=self, version=__version__, splash=splash, show_message=show_message, branch=self.update_branch) updater.check_update(show_message=show_message) + def open_tutorial(self): + # use default browser to open the tutorial link + from PyQt5.QtGui import QDesktopServices + from PyQt5.QtCore import QUrl + url = QUrl("https://byemaxx.github.io/MetaX/") + QDesktopServices.openUrl(url) + + def show_about(self): dialog = QDialog(self.MainWindow) @@ -1406,6 +1416,7 @@ def show_about(self):

Aditional Information

For more information, please visit:

GitHub: The MetaX Project

+

Tutorial: MetaX Tutorial

iMeta: iMetaWiki Page

''' @@ -4836,6 +4847,7 @@ def plot_co_expr_network(self): show_labels=show_labels, rename_taxa=rename_taxa, font_size=font_size, + **self.tf_link_net_params_dict ).plot_co_expression_network(df_type= df_type, corr_method=corr_method, corr_threshold=corr_threshold, sample_list=sample_list, width=width, height=height, focus_list=focus_list, plot_list_only=plot_list_only) self.save_and_show_js_plot(pic, 'co-expression network') diff --git a/utils/MetaX_GUI/MainWindow.ui b/utils/MetaX_GUI/MainWindow.ui index d684bb0..44f8edc 100644 --- a/utils/MetaX_GUI/MainWindow.ui +++ b/utils/MetaX_GUI/MainWindow.ui @@ -7382,8 +7382,8 @@ 0 0 - 981 - 332 + 313 + 41 @@ -8038,6 +8038,7 @@ + @@ -8126,6 +8127,11 @@ Settings + + + Tutorial + + comboBox_taxa_level_to_stast diff --git a/utils/MetaX_GUI/Setting.ui b/utils/MetaX_GUI/Setting.ui index 7641ec1..3900e7e 100644 --- a/utils/MetaX_GUI/Setting.ui +++ b/utils/MetaX_GUI/Setting.ui @@ -24,8 +24,8 @@ 0 0 - 748 - 367 + 246 + 37 @@ -113,7 +113,7 @@ 0.100000000000000 - 0.100000000000000 + 0.000000000000000 @@ -335,7 +335,7 @@ - 10000 + 100000 10 @@ -345,6 +345,37 @@ + + + + Font Weight + + + + + + + + bold + + + + + normal + + + + + bolder + + + + + lighter + + + + diff --git a/utils/MetaX_GUI/Settings.py b/utils/MetaX_GUI/Settings.py index 7d31cbf..e1fde9c 100644 --- a/utils/MetaX_GUI/Settings.py +++ b/utils/MetaX_GUI/Settings.py @@ -50,7 +50,7 @@ def __init__(self, parent=None, update_branch="main", auto_check_update=True): self.ui.doubleSpinBox_tf_link_net_line_curve.valueChanged.connect(self.handle_tf_link_network_changed) self.ui.lineEdit_tf_link_net_line_color.textChanged.connect(self.handle_tf_link_network_changed) self.ui.spinBox_tf_link_net_repulsion.valueChanged.connect(self.handle_tf_link_network_changed) - + self.ui.comboBox_tf_link_net_font_weight.currentTextChanged.connect(self.handle_tf_link_network_changed) def init_ui(self, update_mode, auto_check_update): if update_mode == "main": @@ -88,7 +88,8 @@ def handle_tf_link_network_changed(self): "line_curve": self.ui.doubleSpinBox_tf_link_net_line_curve.value(), "line_color": self.ui.lineEdit_tf_link_net_line_color.text(), - 'repulsion': self.ui.spinBox_tf_link_net_repulsion.value() + 'repulsion': self.ui.spinBox_tf_link_net_repulsion.value(), + 'font_weight': self.ui.comboBox_tf_link_net_font_weight.currentText() } self.tf_link_net_params_dict_changed.emit(network_params_dict) diff --git a/utils/MetaX_GUI/Ui_MainWindow.py b/utils/MetaX_GUI/Ui_MainWindow.py index 24af019..cb04641 100644 --- a/utils/MetaX_GUI/Ui_MainWindow.py +++ b/utils/MetaX_GUI/Ui_MainWindow.py @@ -3884,7 +3884,7 @@ def setupUi(self, metaX_main): self.toolBox_metalab_res_anno = QtWidgets.QToolBox(self.tab_18) self.toolBox_metalab_res_anno.setObjectName("toolBox_metalab_res_anno") self.page_3 = QtWidgets.QWidget() - self.page_3.setGeometry(QtCore.QRect(0, 0, 981, 332)) + self.page_3.setGeometry(QtCore.QRect(0, 0, 313, 41)) self.page_3.setObjectName("page_3") self.gridLayout_45 = QtWidgets.QGridLayout(self.page_3) self.gridLayout_45.setObjectName("gridLayout_45") @@ -4243,12 +4243,15 @@ def setupUi(self, metaX_main): self.actionAny_Table_Mode.setObjectName("actionAny_Table_Mode") self.actionSettings = QtWidgets.QAction(metaX_main) self.actionSettings.setObjectName("actionSettings") + self.actionTutorial = QtWidgets.QAction(metaX_main) + self.actionTutorial.setObjectName("actionTutorial") self.menuTools.addAction(self.actionTaxaFuncAnalyzer) self.menuTools.addAction(self.actionPeptide_to_TaxaFunc) self.menuTools.addAction(self.actionDatabase_Builder) self.menuTools.addAction(self.actionDatabase_Update) self.menuHelp.addAction(self.actionAbout) self.menuHelp.addAction(self.actionCheck_Update) + self.menuHelp.addAction(self.actionTutorial) self.menuOthers.addAction(self.actionRestore_Last_TaxaFunc) self.menuOthers.addAction(self.actionRestore_From) self.menuOthers.addAction(self.actionSave_As) @@ -4963,3 +4966,4 @@ def retranslateUi(self, metaX_main): self.actionRestore_From.setText(_translate("metaX_main", "Restore From..")) self.actionAny_Table_Mode.setText(_translate("metaX_main", "Any Table Mode")) self.actionSettings.setText(_translate("metaX_main", "Settings")) + self.actionTutorial.setText(_translate("metaX_main", "Tutorial")) diff --git a/utils/MetaX_GUI/Ui_Setting.py b/utils/MetaX_GUI/Ui_Setting.py index 296a14b..b235db8 100644 --- a/utils/MetaX_GUI/Ui_Setting.py +++ b/utils/MetaX_GUI/Ui_Setting.py @@ -20,7 +20,7 @@ def setupUi(self, Settings): self.toolBox = QtWidgets.QToolBox(Settings) self.toolBox.setObjectName("toolBox") self.page = QtWidgets.QWidget() - self.page.setGeometry(QtCore.QRect(0, 0, 748, 367)) + self.page.setGeometry(QtCore.QRect(0, 0, 246, 37)) self.page.setObjectName("page") self.gridLayout_3 = QtWidgets.QGridLayout(self.page) self.gridLayout_3.setObjectName("gridLayout_3") @@ -59,7 +59,7 @@ def setupUi(self, Settings): self.doubleSpinBox_tf_link_net_line_curve.setDecimals(1) self.doubleSpinBox_tf_link_net_line_curve.setMaximum(1.0) self.doubleSpinBox_tf_link_net_line_curve.setSingleStep(0.1) - self.doubleSpinBox_tf_link_net_line_curve.setProperty("value", 0.1) + self.doubleSpinBox_tf_link_net_line_curve.setProperty("value", 0.0) self.doubleSpinBox_tf_link_net_line_curve.setObjectName("doubleSpinBox_tf_link_net_line_curve") self.gridLayout_6.addWidget(self.doubleSpinBox_tf_link_net_line_curve, 2, 3, 1, 1) self.comboBox_tf_link_net_taxa_sahpe = QtWidgets.QComboBox(self.page_2) @@ -142,11 +142,21 @@ def setupUi(self, Settings): self.label_15.setObjectName("label_15") self.gridLayout_6.addWidget(self.label_15, 2, 4, 1, 1) self.spinBox_tf_link_net_repulsion = QtWidgets.QSpinBox(self.page_2) - self.spinBox_tf_link_net_repulsion.setMaximum(10000) + self.spinBox_tf_link_net_repulsion.setMaximum(100000) self.spinBox_tf_link_net_repulsion.setSingleStep(10) self.spinBox_tf_link_net_repulsion.setProperty("value", 500) self.spinBox_tf_link_net_repulsion.setObjectName("spinBox_tf_link_net_repulsion") self.gridLayout_6.addWidget(self.spinBox_tf_link_net_repulsion, 2, 5, 1, 1) + self.label_16 = QtWidgets.QLabel(self.page_2) + self.label_16.setObjectName("label_16") + self.gridLayout_6.addWidget(self.label_16, 3, 4, 1, 1) + self.comboBox_tf_link_net_font_weight = QtWidgets.QComboBox(self.page_2) + self.comboBox_tf_link_net_font_weight.setObjectName("comboBox_tf_link_net_font_weight") + self.comboBox_tf_link_net_font_weight.addItem("") + self.comboBox_tf_link_net_font_weight.addItem("") + self.comboBox_tf_link_net_font_weight.addItem("") + self.comboBox_tf_link_net_font_weight.addItem("") + self.gridLayout_6.addWidget(self.comboBox_tf_link_net_font_weight, 3, 5, 1, 1) self.gridLayout_4.addLayout(self.gridLayout_6, 2, 3, 1, 1) self.gridLayout_8 = QtWidgets.QGridLayout() self.gridLayout_8.setObjectName("gridLayout_8") @@ -238,6 +248,11 @@ def retranslateUi(self, Settings): self.label_14.setText(_translate("Settings", "Line Curve")) self.lineEdit_tf_link_net_line_color.setText(_translate("Settings", "#9aa7b1")) self.label_15.setText(_translate("Settings", "Repulsion")) + self.label_16.setText(_translate("Settings", "Font Weight")) + self.comboBox_tf_link_net_font_weight.setItemText(0, _translate("Settings", "bold")) + self.comboBox_tf_link_net_font_weight.setItemText(1, _translate("Settings", "normal")) + self.comboBox_tf_link_net_font_weight.setItemText(2, _translate("Settings", "bolder")) + self.comboBox_tf_link_net_font_weight.setItemText(3, _translate("Settings", "lighter")) self.label_2.setText(_translate("Settings", "Linkage Method")) self.comboBox_heatmap_linkage_method.setCurrentText(_translate("Settings", "average")) self.comboBox_heatmap_linkage_method.setItemText(0, _translate("Settings", "average")) diff --git a/utils/TaxaFuncPloter/network_plot.py b/utils/TaxaFuncPloter/network_plot.py index de0f944..12506fa 100644 --- a/utils/TaxaFuncPloter/network_plot.py +++ b/utils/TaxaFuncPloter/network_plot.py @@ -16,13 +16,18 @@ def __init__(self, tfobj, line_width=1.5, line_curve=0.1, line_color="#9aa7b1", - repulsion=500 + repulsion=500, + co_network_focus_color="#B24745", + co_network_normal_color="#79af97", + font_weight="normal" ): self.tfa = tfobj self.show_labels = show_labels self.font_size = font_size + self.font_weight = font_weight + self.rename_taxa = rename_taxa self.taxa_shape = taxa_shape self.func_shape = func_shape @@ -38,6 +43,9 @@ def __init__(self, tfobj, self.repulsion = repulsion + self.co_network_focus_color = co_network_focus_color + self.co_network_normal_color = co_network_normal_color + def modify_focus_list(self, focus_list): new_focus_list = [] @@ -144,8 +152,8 @@ def normalize(value): return nodes, links, categories else: - nodes = [{"name": taxon, "category": 0, "symbolSize": normalize(taxa_sum[taxon]), "value": taxa_sum[taxon], "symbol": 'triangle'} for taxon in taxa] + \ - [{"name": function, "category": 1, "symbolSize": normalize(function_sum[function]), "value": function_sum[function], "symbol": 'circle'} for function in functions] + nodes = [{"name": taxon, "category": 0, "symbolSize": normalize(taxa_sum[taxon]), "value": taxa_sum[taxon], "symbol": self.taxa_shape} for taxon in taxa] + \ + [{"name": function, "category": 1, "symbolSize": normalize(function_sum[function]), "value": function_sum[function], "symbol": self.func_shape} for function in functions] links = [{"source": row["taxa"], "target": row["function"]} for _, row in df.iterrows()] categories = [ @@ -220,11 +228,12 @@ def plot_tflink_network(self, sample_list:list = None, width:int = 12, height:in color="auto", formatter="{b}", font_size=self.font_size, + font_weight=self.font_weight ), ) .set_global_opts( title_opts=opts.TitleOpts( - title=f"Taxa-Functions Network", + title= "Taxa-Functions Network", subtitle=f"{sample_list}" if sample_list else None, subtitle_textstyle_opts=opts.TextStyleOpts(font_size=10), ), @@ -263,7 +272,8 @@ def plot_tflink_network(self, sample_list:list = None, width:int = 12, height:in def plot_co_expression_network(self, df_type:str= 'taxa', corr_method:str = 'pearson', corr_threshold:float=0.5, sample_list:list = None, - width:int = 12, height:int = 8, focus_list:list = [], plot_list_only:bool = False): + width:int = 12, height:int = 8, focus_list:list = [], plot_list_only:bool = False, + ): from matplotlib import colormaps #check sample_list length if len(sample_list) < 2: @@ -303,8 +313,8 @@ def plot_co_expression_network(self, df_type:str= 'taxa', corr_method:str = 'pea max_node_size = node_sizes.max() min_node_size = node_sizes.min() - categories = [{"name": "Focused", "itemStyle": {"normal": {"color": "#ff0000"}}}, - {"name": "Normal", "itemStyle": {"normal": {"color": "#9AF10F"}}}] + categories = [{"name": "Focused", "itemStyle": {"normal": {"color": self.co_network_focus_color}}}, + {"name": "Normal", "itemStyle": {"normal": {"color": self.co_network_normal_color}}}] linked_nodes = set() if focus_list: @@ -326,16 +336,16 @@ def plot_co_expression_network(self, df_type:str= 'taxa', corr_method:str = 'pea if item in focus_list: # mark the focus nodes with a different color node_size = 50 - color = '#ff0000' + color = self.co_network_focus_color category = 0 # Focus category else: node_size = (node_sizes[item] - min_node_size) / (max_node_size - min_node_size) * 30 + 10 - color = colormaps.get_cmap('viridis')(node_size / 40) # normalize the node size to [0, 1] for the color map + color = colormaps.get_cmap('viridis_r')(node_size / 40) # normalize the node size to [0, 1] for the color map color = '#%02x%02x%02x' % (int(color[0]*255), int(color[1]*255), int(color[2]*255)) category = 1 # Normal category else: node_size = (node_sizes[item] - min_node_size) / (max_node_size - min_node_size) * 30 + 10 - color = colormaps.get_cmap('viridis')(node_size / 40) # normalize the node size to [0, 1] for the color map + color = colormaps.get_cmap('viridis_r')(node_size / 40) # normalize the node size to [0, 1] for the color map color = '#%02x%02x%02x' % (int(color[0]*255), int(color[1]*255), int(color[2]*255)) category = 1 # Normal category @@ -347,24 +357,24 @@ def plot_co_expression_network(self, df_type:str= 'taxa', corr_method:str = 'pea }) links = [] + # calculate the correlation between each pair of nodes, and create a link if the correlation is above a threshold + # the color of the link is determined by the correlation value for i in range(len(correlation_matrix)): for j in range(i+1, len(correlation_matrix)): correlation = correlation_matrix.iloc[i, j] # create a link if the correlation is above a threshold if correlation > corr_threshold: - color = colormaps.get_cmap('viridis')((correlation - corr_threshold) / corr_threshold) + color = colormaps.get_cmap('viridis')(1 - (correlation - corr_threshold) / corr_threshold) color = '#%02x%02x%02x' % (int(color[0]*255), int(color[1]*255), int(color[2]*255)) - links.append({"source": correlation_matrix.columns[i], "target": correlation_matrix.columns[j], "value": correlation, "lineStyle": {"color": color}}) + line_width = (correlation - corr_threshold) / (1 - corr_threshold) * self.line_width * 2 + links.append({"source": correlation_matrix.columns[i], "target": correlation_matrix.columns[j], "value": correlation, "lineStyle": {"color": color, "width": line_width}}) pic = ( Graph( init_opts=opts.InitOpts( width=f"{width*100}px", - height=f"{height*100}px", - animation_opts=opts.AnimationOpts( - animation_threshold=100, animation_easing="cubicOut" - ), + height=f"{height*100}px" ) ) .add( @@ -379,7 +389,12 @@ def plot_co_expression_network(self, df_type:str= 'taxa', corr_method:str = 'pea font_size=self.font_size, position="right", color="auto", - formatter="{b}" + formatter="{b}", + font_weight=self.font_weight + ), + linestyle_opts=opts.LineStyleOpts( + opacity=self.line_opacity, + curve=self.line_curve ), ) .set_global_opts( diff --git a/utils/version.py b/utils/version.py index 9c043c6..55702a1 100644 --- a/utils/version.py +++ b/utils/version.py @@ -1,2 +1,2 @@ -__version__ = '1.107.1' +__version__ = '1.107.2' API_version = '1' \ No newline at end of file