diff --git a/Docs/ChangeLog.md b/Docs/ChangeLog.md index 4bef56c..ca4ca32 100644 --- a/Docs/ChangeLog.md +++ b/Docs/ChangeLog.md @@ -1,3 +1,21 @@ +# Version: 1.107.8 +## Date: 2024-06-26 +### Changes: +- Change: +- 1.Use the heap as the default data structure to apply razor method to sum peptide intensity to protein intensity. +- 2.Changed the update message to a dialog window to show the update message to avoid the update message is too long to show in the message box. + +# Version: 1.107.7 +## Date: 2024-06-24 +### Changes: +- Change: Changed the method of summing peptiede intensity to protein intensity, changed the method "razor" to same as MaxQuant, and added a new method "rank". + +# Version: 1.107.6 +## Date: 2024-06-19 +### Changes: +- New: added an option in Settings to enable user to set the color of the theme (white or dark) of the HTML plot. + + # Version: 1.107.5 ## Date: 2024-06-18 ### Changes: diff --git a/Docs/MetaX_Cookbook.assets/settings_page2.png b/Docs/MetaX_Cookbook.assets/settings_page2.png index 2564529..e081c18 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.md b/Docs/MetaX_Cookbook.md index 341293e..eb1daf7 100644 --- a/Docs/MetaX_Cookbook.md +++ b/Docs/MetaX_Cookbook.md @@ -116,7 +116,7 @@ The **Database Updater** allows updating the database built by the **Database Bu ### 1. Results from MAG Workflow -The peptide results use Metagenome-assembled genomes (MAGs) as the reference database for protein searches, e.g., MetaLab-MAG and other databases like MGnify. +The peptide results use Metagenome-assembled genomes (MAGs) as the reference database for protein searches, e.g., MetaLab-MAG, MetaLab-DIA and other workflows wich using MAG databases like MGnify or customized MAGs Database. - Annotate the peptide to Operational Taxa-Functions (OTF) Table before analysis using the Peptide Annotator. @@ -227,9 +227,16 @@ The Data Overview provides basic information about your data, such as the number Click **Create Proteins Intensity Table** to sum peptides to proteins if the Protein column is in the original table. -- **Occam's Razor and Anti-Razor:** Methods available for inferring shared peptides. - 1. Build the rank of proteins. - 2. Choose the protein with a higher rank for the shared peptide. +- **Occam's Razor**, **Anti-Razor** and **Rank:** Methods available for inferring shared peptides. + - Razor: + 1. Build a minimal set of proteins to cover all peptides. + 2. For each peptide, choose the protein which has most peptides (if multiple proteins have the same number of peptides, share intensity to them). + - Anti-Razor: + - All proteins are shared the intensity of each peptide. + - Rank: + 1. Build the rank of proteins. + 2. Choose the protein with a higher rank for the shared peptide. + - **Methods to Build Protein Rank:** - unique_counts: Use the counts of proteins inferred by unique peptides. @@ -709,6 +716,7 @@ We can select **meta** **groups** or **samples** (default a - The yellow dots are taxa, and the grey dots are functions, the size of the dots presents the intensity - The red dots are the taxa we focused on - The green dots are the functions we focused on +- More parameters can be set in **Dev**->**Settings**->**Others** (e.g. Nodes Shape, color, Line Style) taxa_func_network diff --git a/utils/AnalyzerUtils/SumProteinIntensity.py b/utils/AnalyzerUtils/SumProteinIntensity.py index db35c6b..55bd907 100644 --- a/utils/AnalyzerUtils/SumProteinIntensity.py +++ b/utils/AnalyzerUtils/SumProteinIntensity.py @@ -1,40 +1,46 @@ # This file is used to sum the protein intensity for each sample -# Method: razor or anti-razor +# Method: razor, anti-razor or rank # By sample: True or False # Output: a dataframe with protein as index and sample as columns ############################################## # USAGE: # from utils.AnalyzerUtils.SumProteinIntensity import SumProteinIntensity # out = SumProteinIntensity(sw) -# df1 = out.sum_protein_intensity(method='razor', by_sample=False, rank_method='count') -# df2 = out.sum_protein_intensity(method='razor', by_sample=False, rank_method='shared') -# df3 = out.sum_protein_intensity(method='razor', by_sample=False, rank_method='unique') +# df0 = out.sum_protein_intensity(method='razor') +# df1 = out.sum_protein_intensity(method='rank', by_sample=False, rank_method='all_counts') +# df2 = out.sum_protein_intensity(method='rank', by_sample=False, rank_method='shared_intensity') +# df3 = out.sum_protein_intensity(method='rank', by_sample=False, rank_method='unique_counts') # df4 = out.sum_protein_intensity(method='anti-razor') ############################################## +from collections import defaultdict import pandas as pd +from tqdm import tqdm + class SumProteinIntensity: def __init__(self, taxa_func_analyzer): self.tfa = taxa_func_analyzer - self.res_intensity_dict = {} #store all sample to output - self.rank_dict = {} #store the rank of protein intensity for each sample temporarily - self.rank_method = None + self.res_intensity_dict = {} # store all sample to output + self.rank_dict = {} # store the rank of protein intensity for each sample temporarily + self.rank_method = None # only used for rank method self.extract_col_name = [self.tfa.peptide_col_name, self.tfa.protein_col_name] + self.tfa.sample_list - self.df = self.tfa.original_df.loc[:,self.extract_col_name] + self.df = self.tfa.original_df.loc[:, self.extract_col_name] self._init_dicts() + self.greedy_method = None # only used for razor method - def sum_protein_intensity(self, method='razor', by_sample=False, rank_method='unique_counts'): + def sum_protein_intensity(self, method='razor', by_sample=False, rank_method='unique_counts', greedy_method='heap'): - if method not in ['razor', 'anti-razor']: - raise ValueError('Method must in ["razor", "anti-razor"]') + if method not in ['razor', 'anti-razor', 'rank']: + raise ValueError('Method must in ["razor", "anti-razor", "rank"]') if rank_method not in ['shared_intensity', 'all_counts', 'unique_counts', 'unique_intensity']: raise ValueError('Rank method must in ["shared_intensity", "all_counts", "unique_counts", "unique_intensity"]') self.rank_method = rank_method + self.greedy_method = greedy_method - if method == 'razor': + if method == 'rank': print(f"\n-------------Start to sum protein intensity using method: [{method}] by_sample: [{by_sample}] rank_method: [{rank_method}]-------------") # make a dict to count the intensity of each protein, intensity sahred by peptides will be divided by the number of peptides if by_sample: @@ -42,7 +48,7 @@ def sum_protein_intensity(self, method='razor', by_sample=False, rank_method='un # update the dict for each sample print(f'Creating protein rank dict for [{sample}] by shared intensity', end='\r') self._update_protein_rank_dict(sample_name = sample, rank_method = rank_method) - self._sum_protein_razor(sample, by_sample) + self._sum_protein_rank(sample, by_sample) else: # without sample # only need to create the dict once @@ -51,8 +57,12 @@ def sum_protein_intensity(self, method='razor', by_sample=False, rank_method='un self._update_protein_rank_dict(sample_name = None, rank_method = rank_method) for sample in self.tfa.sample_list: - self._sum_protein_razor(sample, by_sample) - + self._sum_protein_rank(sample, by_sample) + elif method == 'razor': + print('start to sum protein intensity using method: [razor]') + # use Set Cover Problem to get the protein list, then sum the intensity + pep_to_protein = self._create_pep_to_protein_razor() + self._sum_protein_razor(pep_to_protein) elif method == 'anti-razor': print(f"\n-------------Start to sum protein intensity using method: [{method}] by_sample: [True] rank_method: [Shared]-------------") @@ -76,7 +86,119 @@ def sum_protein_intensity(self, method='razor', by_sample=False, rank_method='un return res_df + # razor method + def find_minimum_protein_set(self, peptides, protein_to_peptides): + protein_to_peptides_copy = protein_to_peptides.copy() + peptides_to_cover = set(peptides) + selected_proteins = set() + method = self.greedy_method + + if method == 'greedy': + print('Start creating protein dict for "Set Cover Problem" with Greedy Approximation Algorithm') + with tqdm(total=len(peptides_to_cover), desc="Covering peptides") as pbar: + while peptides_to_cover: + best_protein = None + peptides_covered_by_best = set() + for protein, covered_peptides in protein_to_peptides_copy.items(): + covered = peptides_to_cover & covered_peptides + if len(covered) > len(peptides_covered_by_best): + best_protein = protein + peptides_covered_by_best = covered + + if not best_protein: + break + + selected_proteins.add(best_protein) + peptides_to_cover -= peptides_covered_by_best + protein_to_peptides_copy.pop(best_protein) # remove the protein from the dict to speed up the process + pbar.update(len(peptides_covered_by_best)) + elif method == 'heap': + import heapq + print('Start creating protein dict for "Set Cover Problem" with Heap Optimization of Greedy Approximation Algorithm') + protein_coverage = {protein: covered_peptides & peptides_to_cover + for protein, covered_peptides in protein_to_peptides_copy.items()} + protein_heap = [(-len(covered), protein) for protein, covered in protein_coverage.items()] + heapq.heapify(protein_heap) + + with tqdm(total=len(peptides_to_cover), desc="Covering peptides") as pbar: + while peptides_to_cover: + while protein_heap: + max_covered, best_protein = heapq.heappop(protein_heap) + if best_protein in protein_coverage: + peptides_covered_by_best = protein_coverage.pop(best_protein) + break + + if not best_protein or not peptides_covered_by_best: + break + + selected_proteins.add(best_protein) + peptides_to_cover -= peptides_covered_by_best + pbar.update(len(peptides_covered_by_best)) + + # update other proteins' coverage + for protein in list(protein_coverage.keys()): + if protein_coverage[protein] & peptides_covered_by_best: + protein_coverage[protein] -= peptides_covered_by_best + heapq.heappush(protein_heap, (-len(protein_coverage[protein]), protein)) + if not protein_coverage[protein]: + del protein_coverage[protein] + else: + raise ValueError(f"Invalid greedy method: {method}. Must be ['greedy' or 'heap']") + + return selected_proteins + def _create_pep_to_protein_razor(self) -> dict: + """ + Create a dictionary mapping peptides to proteins based on a minimum protein set. + + Returns: + dict: A dictionary mapping peptides to proteins. + key: peptide + value: a list of proteins + """ + + df = self.df.loc[:, [self.tfa.peptide_col_name, self.tfa.protein_col_name]] + # Create a dictionary mapping proteins to peptides + protein_to_peptides = defaultdict(set) + peptides = set(df[self.tfa.peptide_col_name]) + + for _, row in tqdm(df.iterrows(), total=df.shape[0], desc="Creating protein to peptides mapping"): + sequence = row[self.tfa.peptide_col_name] + proteins = row[self.tfa.protein_col_name].split(';') + for protein in proteins: + protein_to_peptides[protein].add(sequence) + + mini_protein_set = self.find_minimum_protein_set(peptides, protein_to_peptides) + + # remove the proteins not in the mini_protein_set from the protein_to_peptides + filtered_protein_to_peptides = {protein: protein_to_peptides[protein] for protein in mini_protein_set} + # Assign each peptide to the protein that contains it with the highest peptide count + print('Assigning peptides to proteins') + peptide_to_protein = defaultdict(list) + for peptide in tqdm(peptides, desc="Assigning peptides to proteins"): + possible_proteins = [protein for protein, peps in filtered_protein_to_peptides.items() if peptide in peps] + if possible_proteins: + # 找到包含该肽最多的蛋白质 + max_protein_count = max(len(filtered_protein_to_peptides[protein]) for protein in possible_proteins) + best_proteins = [protein for protein in possible_proteins if len(filtered_protein_to_peptides[protein]) == max_protein_count] + peptide_to_protein[peptide].extend(best_proteins) + + return peptide_to_protein + + def _sum_protein_razor(self, peptide_to_protein: dict): + + for sample in tqdm(self.tfa.sample_list): + print(f'Assigning protein intensity for [{sample}]') + df = self.df.loc[:,[ self.tfa.peptide_col_name, sample]] + # create a dict to store the intensity of each peptide + df.set_index(self.tfa.peptide_col_name, inplace=True) + peptide_intensity_dict = df.to_dict()[sample] + for peptide, proteins in peptide_to_protein.items(): + intensity = peptide_intensity_dict[peptide] + self._update_output_dict(proteins, sample, intensity) + + + def _init_dicts(self): for sample in self.tfa.sample_list: self.res_intensity_dict[sample] = {} @@ -147,7 +269,7 @@ def _update_output_dict(self, protein_list: list, sample_name:str, intensity:flo self.res_intensity_dict[sample_name][protein] = intensity - def _sum_protein_razor(self, sample_name:str, by_sample=False): + def _sum_protein_rank(self, sample_name:str, by_sample=False): # print in one line print(f'Asigning protein intensity for [{sample_name}]', end='\r') df = self.df.loc[:,[ self.tfa.protein_col_name, sample_name]] @@ -180,7 +302,4 @@ def _sum_protein_anti_razor(self, sample_name:str): for row in df.itertuples(): proteins = row[1].split(';') intensity = row[2] - self._update_output_dict(proteins, sample_name, intensity) - - - + self._update_output_dict(proteins, sample_name, intensity) \ No newline at end of file diff --git a/utils/GUI.py b/utils/GUI.py index e7dfee7..77a1dfc 100644 --- a/utils/GUI.py +++ b/utils/GUI.py @@ -129,9 +129,6 @@ def __init__(self, MainWindow): # Check and load settings self.load_basic_Settings() - # set the default theme mode - self.theme = 'white' - #check update self.update_required = False self.check_update(manual_check_trigger=False) @@ -161,6 +158,9 @@ def __init__(self, MainWindow): self.add_theme_to_combobox() # ploting parameters + # set the default theme mode + self.html_theme = 'white' + self.heatmap_params_dict = {'linkage_method': 'average', 'distance_metric': 'euclidean'} self.tf_link_net_params_dict = {'taxa_shape': 'circle', 'func_shape': 'rect', @@ -650,12 +650,21 @@ def show_settings_window(self): self.settings_dialog.setModal(False) layout = QVBoxLayout(self.settings_dialog) self.settings_dialog.resize(900, 600) - - settings_widget = SettingsWidget(self.settings_dialog, self.update_branch, self.auto_check_update) + # General settings + settings_widget = SettingsWidget( + parent=self.settings_dialog, + update_branch=self.update_branch, + auto_check_update=self.auto_check_update, + QSettings=self.settings, + ) settings_widget.update_mode_changed.connect(self.on_update_mode_changed) settings_widget.auto_check_update_changed.connect(self.on_auto_check_update_changed) + # plotting parameters settings_widget.heatmap_params_dict_changed.connect(self.on_heatmap_params_changed) settings_widget.tf_link_net_params_dict_changed.connect(self.on_tf_link_net_params_changed) + settings_widget.html_theme_changed.connect(self.on_html_theme_changed) + # Other settings + settings_widget.protein_infer_method_changed.connect(self.on_protein_infer_method_changed) layout.addWidget(settings_widget) self.settings_dialog.setLayout(layout) @@ -682,6 +691,15 @@ def on_tf_link_net_params_changed(self, params_dict): self.tf_link_net_params_dict = params_dict print(f"Taxa-func link network params changed to: {params_dict}") + def on_html_theme_changed(self, theme): + self.html_theme = theme + print(f"HTML theme changed to: {theme}") + + def on_protein_infer_method_changed(self, method): + #save to settings + self.settings.setValue("protein_infer_greedy_mode", method) + print(f"Protein infering razor mode changed to: {method}") + ############### basic function End ############### @@ -722,9 +740,11 @@ def change_theme(self, theme, silent=False): self.show_message(f"Changing theme to {theme}...") # save the theme to settings self.settings.setValue("theme", theme) - # save the theme mode to GUI attribute (dark or light) - self.theme = 'dark' if 'dark' in theme else 'white' - print(f"Theme mode: {self.theme}") + + #! Deprecated, switch to manual change in Settings + ## save the theme mode to GUI attribute (dark or light) + # self.html_theme = 'dark' if 'dark' in theme else 'white' + # print(f"Theme mode: {self.html_theme}") # recover the .xml suffix theme = theme + '.xml' @@ -824,8 +844,8 @@ def change_theme(self, theme, silent=False): def change_event_checkBox_create_protein_table(self): if self.checkBox_create_protein_table.isChecked(): - self.checkBox_infrence_protein_by_sample.setEnabled(True) - self.comboBox_protein_ranking_method.setEnabled(True) + # self.checkBox_infrence_protein_by_sample.setEnabled(True) + # self.comboBox_protein_ranking_method.setEnabled(True) self.comboBox_method_of_protein_inference.setEnabled(True) else: self.comboBox_method_of_protein_inference.setEnabled(False) @@ -833,12 +853,12 @@ def change_event_checkBox_create_protein_table(self): self.comboBox_protein_ranking_method.setEnabled(False) def update_method_of_protein_inference(self): - if self.comboBox_method_of_protein_inference.currentText() == "anti-razor": + if self.comboBox_method_of_protein_inference.currentText() in ["razor", "anti-razor"]: # set checked self.checkBox_infrence_protein_by_sample.setChecked(True) self.checkBox_infrence_protein_by_sample.setEnabled(False) self.comboBox_protein_ranking_method.setEnabled(False) - else: + else: # method is ["rank"] self.checkBox_infrence_protein_by_sample.setEnabled(True) self.comboBox_protein_ranking_method.setEnabled(True) self.checkBox_infrence_protein_by_sample.setChecked(False) @@ -2471,7 +2491,8 @@ def set_multi_table(self, restore_taxafunc=False, saved_obj=None): sum_protein_params = { 'method': self.comboBox_method_of_protein_inference.currentText(), 'by_sample': self.checkBox_infrence_protein_by_sample.isChecked(), - 'rank_method' :self.comboBox_protein_ranking_method.currentText() + 'rank_method' :self.comboBox_protein_ranking_method.currentText(), + 'greedy_method': self.settings.value('protein_infer_greedy_mode', 'heap') } @@ -3396,7 +3417,7 @@ def plot_basic_list(self, plot_type='heatmap'): if reply == QMessageBox.No: return None self.show_message(f'Plotting {plot_type}...') - pic = BarPlot_js(self.tfa, theme=self.theme).plot_intensity_bar(df = df, width=width, height=height, + pic = BarPlot_js(self.tfa, theme=self.html_theme).plot_intensity_bar(df = df, width=width, height=height, title= '', rename_taxa=rename_taxa, show_legend=show_legend, font_size=font_size, rename_sample=rename_sample, plot_mean = plot_mean, @@ -3432,7 +3453,7 @@ def plot_basic_list(self, plot_type='heatmap'): else: title_new = '' subtitle = '' - pic = SankeyPlot(self.tfa, theme=self.theme).plot_intensity_sankey(df=df, width=width, height=height, + pic = SankeyPlot(self.tfa, theme=self.html_theme).plot_intensity_sankey(df=df, width=width, height=height, title=title_new, subtitle=subtitle, font_size=font_size, show_legend=self.checkBox_basic_bar_show_legend.isChecked()) self.save_and_show_js_plot(pic, title) @@ -3717,7 +3738,7 @@ def plot_trends_interactive_line(self): try: - pic = TrendsPlot_js(self.tfa, theme=self.theme).plot_trends_js( df=df, width=width, height= height, title=title, + pic = TrendsPlot_js(self.tfa, theme=self.html_theme).plot_trends_js( df=df, width=width, height= height, title=title, rename_taxa=rename_taxa, show_legend=show_legend, add_group_name = plot_samples, font_size=font_size) self.save_and_show_js_plot(pic, f'Cluster {cluster_num+1} of {table_name}') @@ -3792,7 +3813,7 @@ def save_and_show_js_plot(self, pic, title, width=None, height=None): pic.render(save_path) self.logger.write_log(f'html saved: {save_path}', 'i') - web = webDialog.WebDialog(save_path, None, theme=self.theme) + web = webDialog.WebDialog(save_path, None, theme=self.html_theme) if title: web.setWindowTitle(title) @@ -4036,7 +4057,7 @@ def get_title_by_table_name(self, table_name): return None self.show_message('PCA is running, please wait...') pic = PcaPlot_js(self.tfa, - theme=self.theme + theme=self.html_theme ).plot_pca_pyecharts_3d(df=df, title_name=title_name, show_label = show_label, rename_sample = rename_sample, width=width, height=height, font_size=font_size, legend_col_num=legend_col_num) @@ -4094,7 +4115,7 @@ def get_title_by_table_name(self, table_name): else: show_label = False - pic = SunburstPlot(theme=self.theme).create_sunburst_chart(taxa_df= taxa_df, width=width, height=height, + pic = SunburstPlot(theme=self.html_theme).create_sunburst_chart(taxa_df= taxa_df, width=width, height=height, title='Sunburst of Taxa', show_label=show_label, label_font_size = font_size) self.save_and_show_js_plot(pic, 'Sunburst of Taxa') @@ -4106,7 +4127,7 @@ def get_title_by_table_name(self, table_name): taxa_df = self.tfa.taxa_df[sample_list] - pic = TreeMapPlot(theme=self.theme).create_treemap_chart(taxa_df= taxa_df, width=width, height=height, + pic = TreeMapPlot(theme=self.html_theme).create_treemap_chart(taxa_df= taxa_df, width=width, height=height, show_sub_title = self.checkBox_pca_if_show_lable.isChecked(), font_size = font_size) self.save_and_show_js_plot(pic, 'Treemap of Taxa') @@ -4120,7 +4141,7 @@ def get_title_by_table_name(self, table_name): df = df[sample_list] title = 'Sankey of Taxa' if table_name == 'Taxa' else 'Sankey of Taxa-Functions' - pic = SankeyPlot(self.tfa, theme=self.theme).plot_intensity_sankey(df=df, width=width, height=height, + pic = SankeyPlot(self.tfa, theme=self.html_theme).plot_intensity_sankey(df=df, width=width, height=height, font_size = font_size, title='', subtitle='') self.save_and_show_js_plot(pic, title) @@ -4789,7 +4810,7 @@ def plot_deseq2_volcano(self): # VolcanoPlot().plot_volcano(df, padj = pvalue, log2fc = log2fc, title_name='2 groups', width=width, height=height) try: df = self.table_dict[table_name] - pic = VolcanoPlot(theme=self.theme).plot_volcano_js(df, pvalue = pvalue, p_type = p_type, + pic = VolcanoPlot(theme=self.html_theme).plot_volcano_js(df, pvalue = pvalue, p_type = p_type, log2fc_min = log2fc_min, log2fc_max=log2fc_max, title_name=title_name, font_size = font_size, width=width, height=height) @@ -4842,7 +4863,7 @@ def plot_co_expr_network(self): show_labels=show_labels, rename_taxa=rename_taxa, font_size=font_size, - theme=self.theme, + theme=self.html_theme, **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) @@ -4889,7 +4910,7 @@ def deseq2_plot_sankey(self): df = self.table_dict[table_name] title_name = f'{group1} vs {group2} of {table_name.split("(")[1].split(")")[0]}' - pic = SankeyPlot(self.tfa, theme=self.theme).plot_fc_sankey(df, width=width, height=height, pvalue=pvalue, p_type = p_type, + pic = SankeyPlot(self.tfa, theme=self.html_theme).plot_fc_sankey(df, width=width, height=height, pvalue=pvalue, p_type = p_type, log2fc_min=log2fc_min, log2fc_max=log2fc_max, title =title_name, font_size=font_size) self.save_and_show_js_plot(pic, f'Sankay plot {title_name}') @@ -5075,7 +5096,7 @@ def plot_network(self): show_labels=show_labels, rename_taxa=rename_taxa, font_size=font_size, - theme=self.theme, + theme=self.html_theme, **self.tf_link_net_params_dict ).plot_tflink_network(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, 'taxa-func link Network') @@ -5354,7 +5375,7 @@ def plot_tflink_bar(self): params['show_all_labels'] = show_all_labels self.show_message('Plotting bar plot, please wait...') - pic = BarPlot_js(self.tfa, theme=self.theme).plot_intensity_bar(**params) + pic = BarPlot_js(self.tfa, theme=self.html_theme).plot_intensity_bar(**params) self.save_and_show_js_plot(pic, 'Intensity Bar Plot') diff --git a/utils/MetaX_GUI/MainWindow.ui b/utils/MetaX_GUI/MainWindow.ui index 2377049..1323d1f 100644 --- a/utils/MetaX_GUI/MainWindow.ui +++ b/utils/MetaX_GUI/MainWindow.ui @@ -46,7 +46,7 @@ Qt::LeftToRight - 6 + 2 @@ -798,6 +798,11 @@ anti-razor + + + rank + + @@ -1315,7 +1320,7 @@ - 1 + 0 @@ -8026,7 +8031,7 @@ 0 0 1059 - 21 + 23 diff --git a/utils/MetaX_GUI/Setting.ui b/utils/MetaX_GUI/Setting.ui index 1f01903..5221958 100644 --- a/utils/MetaX_GUI/Setting.ui +++ b/utils/MetaX_GUI/Setting.ui @@ -17,7 +17,7 @@ - 1 + 2 @@ -25,7 +25,7 @@ 0 0 748 - 367 + 340 @@ -71,153 +71,247 @@ 0 0 748 - 367 + 340 - Others + Plotting - + - Taxa-Functions Link Network + HTML Global - - - - HeatMap - - - - - - - + + + + + + + 0 + 0 + + + + Line Width + + + + + + + 1 + + + 1.000000000000000 + + + 0.100000000000000 + + + 0.000000000000000 + + + + + + + The larger the value the greater the repulsion + + + Repulsion + + + + + + + Line opacity + + + + + + + + 0 + 0 + + + + Line Curve + + + + + + + Text Width + + + + + + + 100000 + + + 10 + + + 500 + + + + + + + 1 + + + 1.000000000000000 + + + 0.100000000000000 + + + 0.500000000000000 + + + + + + + Font Weight + + + + + - circle + bold - rect + normal - roundRect + bolder - triangle + lighter + + + + + + + 0 + 0 + + + + Label Postion + + + + + + + 9999 + + + 10 + + + 300 + + + + + + + + 0 + 0 + + + + 1 + + + 0.100000000000000 + + + 1.000000000000000 + + + 3.000000000000000 + + + + + - diamond + bottom - pin + top - arrow + right - - - - - - Taxa Shape - - - - - - bold + left - normal + inside - bolder + insideLeft - lighter + insideRight - - - - #374E55 - - - - - - - Repulsion - - - - - - - Focus Taxa Color - - - - - - Func Color - - - - - - - Line Curve + + + The gravitational factor towards the centre to which the node is subjected. The larger the value the closer the node is to the centre. - - - - - #9aa7b1 + Gravity - - - #DF8F44 - - - - - - - 1 - + 1.000000000000000 @@ -225,27 +319,43 @@ 0.100000000000000 - 0.000000000000000 + 0.200000000000000 - - - - 100000 - - - 10 - - - 500 + + + + + + + + Theme - - + + + + + white + + + + + dark + + + + + + + + + + - Font Weight + Taxa Shape @@ -256,6 +366,13 @@ + + + + #6A6599 + + + @@ -263,19 +380,10 @@ - - - - 1 - - - 1.000000000000000 - - - 0.100000000000000 - - - 0.500000000000000 + + + + #374E55 @@ -318,10 +426,10 @@ - - + + - Line Color + Focus Taxa Color @@ -332,139 +440,90 @@ - - - - Line opacity - - - - - - - #6A6599 - - - - - + + - Line Width - - - - - - - 1 - - - 0.100000000000000 - - - 1.000000000000000 - - - 3.000000000000000 + Function Shape - - + + - Function Shape + #DF8F44 - - + + - Label Postion + Func Color - - + + - bottom + circle - top + rect - right + roundRect - left + triangle - inside + diamond - insideLeft + pin - insideRight + arrow - - + + - Text Width - - - - - - - 9999 - - - 10 - - - 300 + Line Color - - + + - Gravity - - - - - - - 1.000000000000000 - - - 0.100000000000000 - - - 0.200000000000000 + #9aa7b1 - + + + + Taxa-Functions Link Network + + + + @@ -597,6 +656,59 @@ + + + + Network Global + + + + + + + HeatMap + + + + + + + + Others + + + + + + Protein Infer + + + + + + + + + + fast + + + + + normal + + + + + + + + Greedy Mode in Razor Method + + + + + diff --git a/utils/MetaX_GUI/Settings.py b/utils/MetaX_GUI/Settings.py index 3da8e40..165970c 100644 --- a/utils/MetaX_GUI/Settings.py +++ b/utils/MetaX_GUI/Settings.py @@ -7,8 +7,10 @@ class SettingsWidget(QWidget): auto_check_update_changed = pyqtSignal(bool) heatmap_params_dict_changed = pyqtSignal(dict) tf_link_net_params_dict_changed = pyqtSignal(dict) + html_theme_changed = pyqtSignal(str) + protein_infer_method_changed = pyqtSignal(str) - def __init__(self, parent=None, update_branch="main", auto_check_update=True): + def __init__(self, parent=None, update_branch="main", auto_check_update=True, QSettings=None): super().__init__(parent) self.update_mode = update_branch self.auto_check_update = auto_check_update @@ -19,7 +21,7 @@ def __init__(self, parent=None, update_branch="main", auto_check_update=True): self.ui = Ui_Settings() self.ui.setupUi(self) - self.init_ui(self.update_mode, self.auto_check_update) + self.init_ui(self.update_mode, self.auto_check_update, QSettings) # resize the window, 800 as default self.resize(800, 400) @@ -55,13 +57,26 @@ def __init__(self, parent=None, update_branch="main", auto_check_update=True): self.ui.spinBox_tf_link_net_text_width.valueChanged.connect(self.handle_tf_link_network_changed) self.ui.doubleSpinBox_tf_link_net_gravity.valueChanged.connect(self.handle_tf_link_network_changed) + # HTML theme + self.ui.comboBox_html_theme.currentTextChanged.connect(self.handle_html_theme_changed) - def init_ui(self, update_mode, auto_check_update): + # Protein inference method + self.ui.comboBox_protein_infer_greedy_mode.currentTextChanged.connect(self.handle_protein_infer_method_changed) + + + def init_ui(self, update_mode, auto_check_update, QSettings=None): if update_mode == "main": self.ui.radioButton_update_stable.setChecked(True) elif update_mode == "dev": self.ui.radioButton_update_beta.setChecked(True) self.ui.checkBox_auto_check_update.setChecked(auto_check_update) + + if QSettings: + method = QSettings.value('protein_infer_greedy_mode', 'fast') + selected_method = 'normal' if method == 'greedy' else 'fast' + print(f"Protein inference method: {method}") + self.ui.comboBox_protein_infer_greedy_mode.setCurrentText(selected_method) + def handle_checkbox_state_changed(self): self.auto_check_update = self.ui.checkBox_auto_check_update.isChecked() @@ -110,7 +125,18 @@ def handle_radio_button_toggled(self, checked): self.update_mode = "dev" self.update_mode_changed.emit(self.update_mode) - + def handle_html_theme_changed(self): + theme = self.ui.comboBox_html_theme.currentText() + self.html_theme_changed.emit(theme) + + def handle_protein_infer_method_changed(self): + protein_infer_greedy_mode = self.ui.comboBox_protein_infer_greedy_mode.currentText() + method = { + 'normal': 'greedy', + 'fast': 'heap', + } + self.protein_infer_method_changed.emit(method[protein_infer_greedy_mode]) + if __name__ == "__main__": import sys from PyQt5.QtWidgets import QApplication diff --git a/utils/MetaX_GUI/Ui_MainWindow.py b/utils/MetaX_GUI/Ui_MainWindow.py index 8f51c6b..af9d508 100644 --- a/utils/MetaX_GUI/Ui_MainWindow.py +++ b/utils/MetaX_GUI/Ui_MainWindow.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'c:\Users\Qing\OneDrive - University of Ottawa\code\TaxaFunc\MetaX\utils\MetaX_GUI\MainWindow.ui' +# Form implementation generated from reading ui file 'c:\Users\max\OneDrive - University of Ottawa\code\TaxaFunc\MetaX\utils\MetaX_GUI\MainWindow.ui' # # Created by: PyQt5 UI code generator 5.15.9 # @@ -438,6 +438,7 @@ def setupUi(self, metaX_main): self.comboBox_method_of_protein_inference.setObjectName("comboBox_method_of_protein_inference") self.comboBox_method_of_protein_inference.addItem("") self.comboBox_method_of_protein_inference.addItem("") + self.comboBox_method_of_protein_inference.addItem("") self.gridLayout_37.addWidget(self.comboBox_method_of_protein_inference, 0, 2, 1, 1) self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2.setObjectName("horizontalLayout_2") @@ -4208,7 +4209,7 @@ def setupUi(self, metaX_main): self.statusbar.setObjectName("statusbar") metaX_main.setStatusBar(self.statusbar) self.menuBar = QtWidgets.QMenuBar(metaX_main) - self.menuBar.setGeometry(QtCore.QRect(0, 0, 1059, 21)) + self.menuBar.setGeometry(QtCore.QRect(0, 0, 1059, 23)) self.menuBar.setObjectName("menuBar") self.menuTools = QtWidgets.QMenu(self.menuBar) self.menuTools.setObjectName("menuTools") @@ -4268,9 +4269,9 @@ def setupUi(self, metaX_main): self.retranslateUi(metaX_main) self.stackedWidget.setCurrentIndex(0) - self.tabWidget_TaxaFuncAnalyzer.setCurrentIndex(6) + self.tabWidget_TaxaFuncAnalyzer.setCurrentIndex(2) self.toolBox_2.setCurrentIndex(0) - self.tabWidget_4.setCurrentIndex(1) + self.tabWidget_4.setCurrentIndex(0) self.tabWidget_3.setCurrentIndex(0) self.tabWidget.setCurrentIndex(1) self.tabWidget_2.setCurrentIndex(1) @@ -4369,6 +4370,7 @@ def retranslateUi(self, metaX_main): self.checkBox_infrence_protein_by_sample.setText(_translate("metaX_main", "Inference by each Sample")) self.comboBox_method_of_protein_inference.setItemText(0, _translate("metaX_main", "razor")) self.comboBox_method_of_protein_inference.setItemText(1, _translate("metaX_main", "anti-razor")) + self.comboBox_method_of_protein_inference.setItemText(2, _translate("metaX_main", "rank")) self.label_136.setText(_translate("metaX_main", "Protein Ranking Method")) self.comboBox_protein_ranking_method.setItemText(0, _translate("metaX_main", "unique_counts")) self.comboBox_protein_ranking_method.setItemText(1, _translate("metaX_main", "all_counts")) diff --git a/utils/MetaX_GUI/Ui_Setting.py b/utils/MetaX_GUI/Ui_Setting.py index bde7ffc..395abc1 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, 748, 340)) self.page.setObjectName("page") self.gridLayout_3 = QtWidgets.QGridLayout(self.page) self.gridLayout_3.setObjectName("gridLayout_3") @@ -40,126 +40,96 @@ def setupUi(self, Settings): self.gridLayout_3.addLayout(self.gridLayout_2, 0, 0, 1, 1) self.toolBox.addItem(self.page, "") self.page_2 = QtWidgets.QWidget() - self.page_2.setGeometry(QtCore.QRect(0, 0, 748, 367)) + self.page_2.setGeometry(QtCore.QRect(0, 0, 748, 340)) self.page_2.setObjectName("page_2") self.gridLayout_4 = QtWidgets.QGridLayout(self.page_2) self.gridLayout_4.setObjectName("gridLayout_4") - self.label_4 = QtWidgets.QLabel(self.page_2) - self.label_4.setObjectName("label_4") - self.gridLayout_4.addWidget(self.label_4, 2, 0, 1, 1) - self.label = QtWidgets.QLabel(self.page_2) - self.label.setObjectName("label") - self.gridLayout_4.addWidget(self.label, 0, 0, 1, 1) - self.gridLayout_6 = QtWidgets.QGridLayout() - self.gridLayout_6.setObjectName("gridLayout_6") - self.comboBox_tf_link_net_taxa_sahpe = QtWidgets.QComboBox(self.page_2) - self.comboBox_tf_link_net_taxa_sahpe.setObjectName("comboBox_tf_link_net_taxa_sahpe") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.comboBox_tf_link_net_taxa_sahpe.addItem("") - self.gridLayout_6.addWidget(self.comboBox_tf_link_net_taxa_sahpe, 0, 1, 1, 1) - self.label_5 = QtWidgets.QLabel(self.page_2) - self.label_5.setObjectName("label_5") - self.gridLayout_6.addWidget(self.label_5, 0, 0, 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.lineEdit_tf_link_net_taxa_color = QtWidgets.QLineEdit(self.page_2) - self.lineEdit_tf_link_net_taxa_color.setObjectName("lineEdit_tf_link_net_taxa_color") - self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_taxa_color, 0, 3, 1, 1) - self.label_15 = QtWidgets.QLabel(self.page_2) - self.label_15.setObjectName("label_15") - self.gridLayout_6.addWidget(self.label_15, 2, 4, 1, 1) - self.label_9 = QtWidgets.QLabel(self.page_2) - self.label_9.setObjectName("label_9") - self.gridLayout_6.addWidget(self.label_9, 0, 4, 1, 1) - self.label_10 = QtWidgets.QLabel(self.page_2) - self.label_10.setObjectName("label_10") - self.gridLayout_6.addWidget(self.label_10, 1, 2, 1, 1) - self.label_14 = QtWidgets.QLabel(self.page_2) - self.label_14.setObjectName("label_14") - self.gridLayout_6.addWidget(self.label_14, 2, 2, 1, 1) - self.lineEdit_tf_link_net_line_color = QtWidgets.QLineEdit(self.page_2) - self.lineEdit_tf_link_net_line_color.setObjectName("lineEdit_tf_link_net_line_color") - self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_line_color, 3, 3, 1, 1) - self.lineEdit_tf_link_net_func_color = QtWidgets.QLineEdit(self.page_2) - self.lineEdit_tf_link_net_func_color.setObjectName("lineEdit_tf_link_net_func_color") - self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_func_color, 1, 3, 1, 1) + self.label_21 = QtWidgets.QLabel(self.page_2) + self.label_21.setObjectName("label_21") + self.gridLayout_4.addWidget(self.label_21, 2, 0, 1, 1) + self.gridLayout_7 = QtWidgets.QGridLayout() + self.gridLayout_7.setObjectName("gridLayout_7") + self.label_11 = QtWidgets.QLabel(self.page_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_11.sizePolicy().hasHeightForWidth()) + self.label_11.setSizePolicy(sizePolicy) + self.label_11.setObjectName("label_11") + self.gridLayout_7.addWidget(self.label_11, 0, 0, 1, 1) self.doubleSpinBox_tf_link_net_line_curve = QtWidgets.QDoubleSpinBox(self.page_2) 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.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.gridLayout_7.addWidget(self.doubleSpinBox_tf_link_net_line_curve, 0, 3, 1, 1) + self.label_15 = QtWidgets.QLabel(self.page_2) + self.label_15.setObjectName("label_15") + self.gridLayout_7.addWidget(self.label_15, 1, 0, 1, 1) + self.label_12 = QtWidgets.QLabel(self.page_2) + self.label_12.setObjectName("label_12") + self.gridLayout_7.addWidget(self.label_12, 0, 4, 1, 1) + self.label_14 = QtWidgets.QLabel(self.page_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_14.sizePolicy().hasHeightForWidth()) + self.label_14.setSizePolicy(sizePolicy) + self.label_14.setObjectName("label_14") + self.gridLayout_7.addWidget(self.label_14, 0, 2, 1, 1) + self.label_18 = QtWidgets.QLabel(self.page_2) + self.label_18.setObjectName("label_18") + self.gridLayout_7.addWidget(self.label_18, 2, 0, 1, 1) self.spinBox_tf_link_net_repulsion = QtWidgets.QSpinBox(self.page_2) 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.lineEdit_tf_link_net_func_focus_color = QtWidgets.QLineEdit(self.page_2) - self.lineEdit_tf_link_net_func_focus_color.setObjectName("lineEdit_tf_link_net_func_focus_color") - self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_func_focus_color, 1, 5, 1, 1) - self.label_8 = QtWidgets.QLabel(self.page_2) - self.label_8.setObjectName("label_8") - self.gridLayout_6.addWidget(self.label_8, 1, 4, 1, 1) + self.gridLayout_7.addWidget(self.spinBox_tf_link_net_repulsion, 1, 1, 1, 1) self.doubleSpinBox_tf_link_net_line_opacity = QtWidgets.QDoubleSpinBox(self.page_2) self.doubleSpinBox_tf_link_net_line_opacity.setDecimals(1) self.doubleSpinBox_tf_link_net_line_opacity.setMaximum(1.0) self.doubleSpinBox_tf_link_net_line_opacity.setSingleStep(0.1) self.doubleSpinBox_tf_link_net_line_opacity.setProperty("value", 0.5) self.doubleSpinBox_tf_link_net_line_opacity.setObjectName("doubleSpinBox_tf_link_net_line_opacity") - self.gridLayout_6.addWidget(self.doubleSpinBox_tf_link_net_line_opacity, 3, 1, 1, 1) - self.comboBox_tf_link_net_func_shape = QtWidgets.QComboBox(self.page_2) - self.comboBox_tf_link_net_func_shape.setObjectName("comboBox_tf_link_net_func_shape") - self.comboBox_tf_link_net_func_shape.addItem("") - self.comboBox_tf_link_net_func_shape.addItem("") - self.comboBox_tf_link_net_func_shape.addItem("") - self.comboBox_tf_link_net_func_shape.addItem("") - self.comboBox_tf_link_net_func_shape.addItem("") - self.comboBox_tf_link_net_func_shape.addItem("") - self.comboBox_tf_link_net_func_shape.addItem("") - self.gridLayout_6.addWidget(self.comboBox_tf_link_net_func_shape, 1, 1, 1, 1) - self.label_13 = QtWidgets.QLabel(self.page_2) - self.label_13.setObjectName("label_13") - self.gridLayout_6.addWidget(self.label_13, 3, 2, 1, 1) - self.label_7 = QtWidgets.QLabel(self.page_2) - self.label_7.setObjectName("label_7") - self.gridLayout_6.addWidget(self.label_7, 0, 2, 1, 1) - self.label_12 = QtWidgets.QLabel(self.page_2) - self.label_12.setObjectName("label_12") - self.gridLayout_6.addWidget(self.label_12, 3, 0, 1, 1) - self.lineEdit_tf_link_net_taxa_focus_color = QtWidgets.QLineEdit(self.page_2) - self.lineEdit_tf_link_net_taxa_focus_color.setObjectName("lineEdit_tf_link_net_taxa_focus_color") - self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_taxa_focus_color, 0, 5, 1, 1) - self.label_11 = QtWidgets.QLabel(self.page_2) - self.label_11.setObjectName("label_11") - self.gridLayout_6.addWidget(self.label_11, 2, 0, 1, 1) + self.gridLayout_7.addWidget(self.doubleSpinBox_tf_link_net_line_opacity, 0, 5, 1, 1) + self.label_16 = QtWidgets.QLabel(self.page_2) + self.label_16.setObjectName("label_16") + self.gridLayout_7.addWidget(self.label_16, 2, 2, 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_7.addWidget(self.comboBox_tf_link_net_font_weight, 2, 3, 1, 1) + self.label_17 = QtWidgets.QLabel(self.page_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_17.sizePolicy().hasHeightForWidth()) + self.label_17.setSizePolicy(sizePolicy) + self.label_17.setObjectName("label_17") + self.gridLayout_7.addWidget(self.label_17, 1, 4, 1, 1) + self.spinBox_tf_link_net_text_width = QtWidgets.QSpinBox(self.page_2) + self.spinBox_tf_link_net_text_width.setMaximum(9999) + self.spinBox_tf_link_net_text_width.setSingleStep(10) + self.spinBox_tf_link_net_text_width.setProperty("value", 300) + self.spinBox_tf_link_net_text_width.setObjectName("spinBox_tf_link_net_text_width") + self.gridLayout_7.addWidget(self.spinBox_tf_link_net_text_width, 2, 1, 1, 1) self.doubleSpinBox_tf_link_net_line_width = QtWidgets.QDoubleSpinBox(self.page_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.doubleSpinBox_tf_link_net_line_width.sizePolicy().hasHeightForWidth()) + self.doubleSpinBox_tf_link_net_line_width.setSizePolicy(sizePolicy) self.doubleSpinBox_tf_link_net_line_width.setDecimals(1) self.doubleSpinBox_tf_link_net_line_width.setMinimum(0.1) self.doubleSpinBox_tf_link_net_line_width.setSingleStep(1.0) self.doubleSpinBox_tf_link_net_line_width.setProperty("value", 3.0) self.doubleSpinBox_tf_link_net_line_width.setObjectName("doubleSpinBox_tf_link_net_line_width") - self.gridLayout_6.addWidget(self.doubleSpinBox_tf_link_net_line_width, 2, 1, 1, 1) - self.label_6 = QtWidgets.QLabel(self.page_2) - self.label_6.setObjectName("label_6") - self.gridLayout_6.addWidget(self.label_6, 1, 0, 1, 1) - self.label_17 = QtWidgets.QLabel(self.page_2) - self.label_17.setObjectName("label_17") - self.gridLayout_6.addWidget(self.label_17, 4, 0, 1, 1) + self.gridLayout_7.addWidget(self.doubleSpinBox_tf_link_net_line_width, 0, 1, 1, 1) self.comboBox_tf_link_net_label_position = QtWidgets.QComboBox(self.page_2) self.comboBox_tf_link_net_label_position.setObjectName("comboBox_tf_link_net_label_position") self.comboBox_tf_link_net_label_position.addItem("") @@ -169,26 +139,90 @@ def setupUi(self, Settings): self.comboBox_tf_link_net_label_position.addItem("") self.comboBox_tf_link_net_label_position.addItem("") self.comboBox_tf_link_net_label_position.addItem("") - self.gridLayout_6.addWidget(self.comboBox_tf_link_net_label_position, 4, 1, 1, 1) - self.label_18 = QtWidgets.QLabel(self.page_2) - self.label_18.setObjectName("label_18") - self.gridLayout_6.addWidget(self.label_18, 4, 2, 1, 1) - self.spinBox_tf_link_net_text_width = QtWidgets.QSpinBox(self.page_2) - self.spinBox_tf_link_net_text_width.setMaximum(9999) - self.spinBox_tf_link_net_text_width.setSingleStep(10) - self.spinBox_tf_link_net_text_width.setProperty("value", 300) - self.spinBox_tf_link_net_text_width.setObjectName("spinBox_tf_link_net_text_width") - self.gridLayout_6.addWidget(self.spinBox_tf_link_net_text_width, 4, 3, 1, 1) + self.gridLayout_7.addWidget(self.comboBox_tf_link_net_label_position, 1, 5, 1, 1) self.label_19 = QtWidgets.QLabel(self.page_2) self.label_19.setObjectName("label_19") - self.gridLayout_6.addWidget(self.label_19, 4, 4, 1, 1) + self.gridLayout_7.addWidget(self.label_19, 1, 2, 1, 1) self.doubleSpinBox_tf_link_net_gravity = QtWidgets.QDoubleSpinBox(self.page_2) self.doubleSpinBox_tf_link_net_gravity.setMaximum(1.0) self.doubleSpinBox_tf_link_net_gravity.setSingleStep(0.1) self.doubleSpinBox_tf_link_net_gravity.setProperty("value", 0.2) self.doubleSpinBox_tf_link_net_gravity.setObjectName("doubleSpinBox_tf_link_net_gravity") - self.gridLayout_6.addWidget(self.doubleSpinBox_tf_link_net_gravity, 4, 5, 1, 1) - self.gridLayout_4.addLayout(self.gridLayout_6, 2, 3, 1, 1) + self.gridLayout_7.addWidget(self.doubleSpinBox_tf_link_net_gravity, 1, 3, 1, 1) + self.gridLayout_4.addLayout(self.gridLayout_7, 3, 3, 1, 1) + self.gridLayout_5 = QtWidgets.QGridLayout() + self.gridLayout_5.setObjectName("gridLayout_5") + self.label_22 = QtWidgets.QLabel(self.page_2) + self.label_22.setObjectName("label_22") + self.gridLayout_5.addWidget(self.label_22, 0, 0, 1, 1) + self.comboBox_html_theme = QtWidgets.QComboBox(self.page_2) + self.comboBox_html_theme.setObjectName("comboBox_html_theme") + self.comboBox_html_theme.addItem("") + self.comboBox_html_theme.addItem("") + self.gridLayout_5.addWidget(self.comboBox_html_theme, 0, 1, 1, 1) + self.gridLayout_4.addLayout(self.gridLayout_5, 2, 3, 1, 1) + self.gridLayout_6 = QtWidgets.QGridLayout() + self.gridLayout_6.setObjectName("gridLayout_6") + self.label_5 = QtWidgets.QLabel(self.page_2) + self.label_5.setObjectName("label_5") + self.gridLayout_6.addWidget(self.label_5, 0, 0, 1, 1) + self.lineEdit_tf_link_net_func_focus_color = QtWidgets.QLineEdit(self.page_2) + self.lineEdit_tf_link_net_func_focus_color.setObjectName("lineEdit_tf_link_net_func_focus_color") + self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_func_focus_color, 1, 5, 1, 1) + self.lineEdit_tf_link_net_taxa_focus_color = QtWidgets.QLineEdit(self.page_2) + self.lineEdit_tf_link_net_taxa_focus_color.setObjectName("lineEdit_tf_link_net_taxa_focus_color") + self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_taxa_focus_color, 0, 5, 1, 1) + self.label_8 = QtWidgets.QLabel(self.page_2) + self.label_8.setObjectName("label_8") + self.gridLayout_6.addWidget(self.label_8, 1, 4, 1, 1) + self.lineEdit_tf_link_net_taxa_color = QtWidgets.QLineEdit(self.page_2) + self.lineEdit_tf_link_net_taxa_color.setObjectName("lineEdit_tf_link_net_taxa_color") + self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_taxa_color, 0, 3, 1, 1) + self.comboBox_tf_link_net_func_shape = QtWidgets.QComboBox(self.page_2) + self.comboBox_tf_link_net_func_shape.setObjectName("comboBox_tf_link_net_func_shape") + self.comboBox_tf_link_net_func_shape.addItem("") + self.comboBox_tf_link_net_func_shape.addItem("") + self.comboBox_tf_link_net_func_shape.addItem("") + self.comboBox_tf_link_net_func_shape.addItem("") + self.comboBox_tf_link_net_func_shape.addItem("") + self.comboBox_tf_link_net_func_shape.addItem("") + self.comboBox_tf_link_net_func_shape.addItem("") + self.gridLayout_6.addWidget(self.comboBox_tf_link_net_func_shape, 1, 1, 1, 1) + self.label_9 = QtWidgets.QLabel(self.page_2) + self.label_9.setObjectName("label_9") + self.gridLayout_6.addWidget(self.label_9, 0, 4, 1, 1) + self.label_7 = QtWidgets.QLabel(self.page_2) + self.label_7.setObjectName("label_7") + self.gridLayout_6.addWidget(self.label_7, 0, 2, 1, 1) + self.label_6 = QtWidgets.QLabel(self.page_2) + self.label_6.setObjectName("label_6") + self.gridLayout_6.addWidget(self.label_6, 1, 0, 1, 1) + self.lineEdit_tf_link_net_func_color = QtWidgets.QLineEdit(self.page_2) + self.lineEdit_tf_link_net_func_color.setObjectName("lineEdit_tf_link_net_func_color") + self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_func_color, 1, 3, 1, 1) + self.label_10 = QtWidgets.QLabel(self.page_2) + self.label_10.setObjectName("label_10") + self.gridLayout_6.addWidget(self.label_10, 1, 2, 1, 1) + self.comboBox_tf_link_net_taxa_sahpe = QtWidgets.QComboBox(self.page_2) + self.comboBox_tf_link_net_taxa_sahpe.setObjectName("comboBox_tf_link_net_taxa_sahpe") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.comboBox_tf_link_net_taxa_sahpe.addItem("") + self.gridLayout_6.addWidget(self.comboBox_tf_link_net_taxa_sahpe, 0, 1, 1, 1) + self.label_13 = QtWidgets.QLabel(self.page_2) + self.label_13.setObjectName("label_13") + self.gridLayout_6.addWidget(self.label_13, 2, 0, 1, 1) + self.lineEdit_tf_link_net_line_color = QtWidgets.QLineEdit(self.page_2) + self.lineEdit_tf_link_net_line_color.setObjectName("lineEdit_tf_link_net_line_color") + self.gridLayout_6.addWidget(self.lineEdit_tf_link_net_line_color, 2, 1, 1, 1) + self.gridLayout_4.addLayout(self.gridLayout_6, 4, 3, 1, 1) + self.label_4 = QtWidgets.QLabel(self.page_2) + self.label_4.setObjectName("label_4") + self.gridLayout_4.addWidget(self.label_4, 4, 0, 1, 1) self.gridLayout_8 = QtWidgets.QGridLayout() self.gridLayout_8.setObjectName("gridLayout_8") self.label_2 = QtWidgets.QLabel(self.page_2) @@ -232,12 +266,37 @@ def setupUi(self, Settings): self.comboBox_heatmap_linkage_metric.addItem("") self.comboBox_heatmap_linkage_metric.addItem("") self.gridLayout_8.addWidget(self.comboBox_heatmap_linkage_metric, 0, 3, 1, 1) - self.gridLayout_4.addLayout(self.gridLayout_8, 0, 3, 1, 1) + self.gridLayout_4.addLayout(self.gridLayout_8, 1, 3, 1, 1) + self.label_20 = QtWidgets.QLabel(self.page_2) + self.label_20.setObjectName("label_20") + self.gridLayout_4.addWidget(self.label_20, 3, 0, 1, 1) + self.label = QtWidgets.QLabel(self.page_2) + self.label.setObjectName("label") + self.gridLayout_4.addWidget(self.label, 1, 0, 1, 1) self.toolBox.addItem(self.page_2, "") + self.page_3 = QtWidgets.QWidget() + self.page_3.setObjectName("page_3") + self.gridLayout_11 = QtWidgets.QGridLayout(self.page_3) + self.gridLayout_11.setObjectName("gridLayout_11") + self.label_23 = QtWidgets.QLabel(self.page_3) + self.label_23.setObjectName("label_23") + self.gridLayout_11.addWidget(self.label_23, 1, 0, 1, 1) + self.gridLayout_10 = QtWidgets.QGridLayout() + self.gridLayout_10.setObjectName("gridLayout_10") + self.comboBox_protein_infer_greedy_mode = QtWidgets.QComboBox(self.page_3) + self.comboBox_protein_infer_greedy_mode.setObjectName("comboBox_protein_infer_greedy_mode") + self.comboBox_protein_infer_greedy_mode.addItem("") + self.comboBox_protein_infer_greedy_mode.addItem("") + self.gridLayout_10.addWidget(self.comboBox_protein_infer_greedy_mode, 0, 1, 1, 1) + self.label_24 = QtWidgets.QLabel(self.page_3) + self.label_24.setObjectName("label_24") + self.gridLayout_10.addWidget(self.label_24, 0, 0, 1, 1) + self.gridLayout_11.addLayout(self.gridLayout_10, 1, 1, 1, 1) + self.toolBox.addItem(self.page_3, "") self.gridLayout.addWidget(self.toolBox, 0, 0, 1, 1) self.retranslateUi(Settings) - self.toolBox.setCurrentIndex(1) + self.toolBox.setCurrentIndex(2) QtCore.QMetaObject.connectSlotsByName(Settings) def retranslateUi(self, Settings): @@ -247,30 +306,36 @@ def retranslateUi(self, Settings): self.radioButton_update_stable.setText(_translate("Settings", "Stable")) self.radioButton_update_beta.setText(_translate("Settings", "Beta")) self.toolBox.setItemText(self.toolBox.indexOf(self.page), _translate("Settings", "General")) - self.label_4.setText(_translate("Settings", "Taxa-Functions Link Network")) - self.label.setText(_translate("Settings", "HeatMap")) - self.comboBox_tf_link_net_taxa_sahpe.setItemText(0, _translate("Settings", "circle")) - self.comboBox_tf_link_net_taxa_sahpe.setItemText(1, _translate("Settings", "rect")) - self.comboBox_tf_link_net_taxa_sahpe.setItemText(2, _translate("Settings", "roundRect")) - self.comboBox_tf_link_net_taxa_sahpe.setItemText(3, _translate("Settings", "triangle")) - self.comboBox_tf_link_net_taxa_sahpe.setItemText(4, _translate("Settings", "diamond")) - self.comboBox_tf_link_net_taxa_sahpe.setItemText(5, _translate("Settings", "pin")) - self.comboBox_tf_link_net_taxa_sahpe.setItemText(6, _translate("Settings", "arrow")) - self.label_5.setText(_translate("Settings", "Taxa Shape")) + self.label_21.setText(_translate("Settings", "HTML Global")) + self.label_11.setText(_translate("Settings", "Line Width")) + self.label_15.setToolTip(_translate("Settings", "The larger the value the greater the repulsion")) + self.label_15.setText(_translate("Settings", "Repulsion")) + self.label_12.setText(_translate("Settings", "Line opacity")) + self.label_14.setText(_translate("Settings", "Line Curve")) + self.label_18.setText(_translate("Settings", "Text Width")) + 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.lineEdit_tf_link_net_taxa_color.setText(_translate("Settings", "#374E55")) - self.label_15.setText(_translate("Settings", "Repulsion")) - self.label_9.setText(_translate("Settings", "Focus Taxa Color")) - self.label_10.setText(_translate("Settings", "Func Color")) - self.label_14.setText(_translate("Settings", "Line Curve")) - self.lineEdit_tf_link_net_line_color.setText(_translate("Settings", "#9aa7b1")) - self.lineEdit_tf_link_net_func_color.setText(_translate("Settings", "#DF8F44")) - self.label_16.setText(_translate("Settings", "Font Weight")) + self.label_17.setText(_translate("Settings", "Label Postion")) + self.comboBox_tf_link_net_label_position.setItemText(0, _translate("Settings", "bottom")) + self.comboBox_tf_link_net_label_position.setItemText(1, _translate("Settings", "top")) + self.comboBox_tf_link_net_label_position.setItemText(2, _translate("Settings", "right")) + self.comboBox_tf_link_net_label_position.setItemText(3, _translate("Settings", "left")) + self.comboBox_tf_link_net_label_position.setItemText(4, _translate("Settings", "inside")) + self.comboBox_tf_link_net_label_position.setItemText(5, _translate("Settings", "insideLeft")) + self.comboBox_tf_link_net_label_position.setItemText(6, _translate("Settings", "insideRight")) + self.label_19.setToolTip(_translate("Settings", "The gravitational factor towards the centre to which the node is subjected. The larger the value the closer the node is to the centre.")) + self.label_19.setText(_translate("Settings", "Gravity")) + self.label_22.setText(_translate("Settings", "Theme")) + self.comboBox_html_theme.setItemText(0, _translate("Settings", "white")) + self.comboBox_html_theme.setItemText(1, _translate("Settings", "dark")) + self.label_5.setText(_translate("Settings", "Taxa Shape")) self.lineEdit_tf_link_net_func_focus_color.setText(_translate("Settings", "#B24745")) + self.lineEdit_tf_link_net_taxa_focus_color.setText(_translate("Settings", "#6A6599")) self.label_8.setText(_translate("Settings", "Focus Func Color")) + self.lineEdit_tf_link_net_taxa_color.setText(_translate("Settings", "#374E55")) self.comboBox_tf_link_net_func_shape.setItemText(0, _translate("Settings", "rect")) self.comboBox_tf_link_net_func_shape.setItemText(1, _translate("Settings", "circle")) self.comboBox_tf_link_net_func_shape.setItemText(2, _translate("Settings", "roundRect")) @@ -278,22 +343,21 @@ def retranslateUi(self, Settings): self.comboBox_tf_link_net_func_shape.setItemText(4, _translate("Settings", "diamond")) self.comboBox_tf_link_net_func_shape.setItemText(5, _translate("Settings", "pin")) self.comboBox_tf_link_net_func_shape.setItemText(6, _translate("Settings", "arrow")) - self.label_13.setText(_translate("Settings", "Line Color")) + self.label_9.setText(_translate("Settings", "Focus Taxa Color")) self.label_7.setText(_translate("Settings", "Taxa Color")) - self.label_12.setText(_translate("Settings", "Line opacity")) - self.lineEdit_tf_link_net_taxa_focus_color.setText(_translate("Settings", "#6A6599")) - self.label_11.setText(_translate("Settings", "Line Width")) self.label_6.setText(_translate("Settings", "Function Shape")) - self.label_17.setText(_translate("Settings", "Label Postion")) - self.comboBox_tf_link_net_label_position.setItemText(0, _translate("Settings", "bottom")) - self.comboBox_tf_link_net_label_position.setItemText(1, _translate("Settings", "top")) - self.comboBox_tf_link_net_label_position.setItemText(2, _translate("Settings", "right")) - self.comboBox_tf_link_net_label_position.setItemText(3, _translate("Settings", "left")) - self.comboBox_tf_link_net_label_position.setItemText(4, _translate("Settings", "inside")) - self.comboBox_tf_link_net_label_position.setItemText(5, _translate("Settings", "insideLeft")) - self.comboBox_tf_link_net_label_position.setItemText(6, _translate("Settings", "insideRight")) - self.label_18.setText(_translate("Settings", "Text Width")) - self.label_19.setText(_translate("Settings", "Gravity")) + self.lineEdit_tf_link_net_func_color.setText(_translate("Settings", "#DF8F44")) + self.label_10.setText(_translate("Settings", "Func Color")) + self.comboBox_tf_link_net_taxa_sahpe.setItemText(0, _translate("Settings", "circle")) + self.comboBox_tf_link_net_taxa_sahpe.setItemText(1, _translate("Settings", "rect")) + self.comboBox_tf_link_net_taxa_sahpe.setItemText(2, _translate("Settings", "roundRect")) + self.comboBox_tf_link_net_taxa_sahpe.setItemText(3, _translate("Settings", "triangle")) + self.comboBox_tf_link_net_taxa_sahpe.setItemText(4, _translate("Settings", "diamond")) + self.comboBox_tf_link_net_taxa_sahpe.setItemText(5, _translate("Settings", "pin")) + self.comboBox_tf_link_net_taxa_sahpe.setItemText(6, _translate("Settings", "arrow")) + self.label_13.setText(_translate("Settings", "Line Color")) + self.lineEdit_tf_link_net_line_color.setText(_translate("Settings", "#9aa7b1")) + self.label_4.setText(_translate("Settings", "Taxa-Functions Link Network")) 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")) @@ -314,4 +378,11 @@ def retranslateUi(self, Settings): self.comboBox_heatmap_linkage_metric.setItemText(6, _translate("Settings", "rogerstanimoto")) self.comboBox_heatmap_linkage_metric.setItemText(7, _translate("Settings", "russellrao")) self.comboBox_heatmap_linkage_metric.setItemText(8, _translate("Settings", "sqeuclidean")) - self.toolBox.setItemText(self.toolBox.indexOf(self.page_2), _translate("Settings", "Others")) + self.label_20.setText(_translate("Settings", "Network Global")) + self.label.setText(_translate("Settings", "HeatMap")) + self.toolBox.setItemText(self.toolBox.indexOf(self.page_2), _translate("Settings", "Plotting")) + self.label_23.setText(_translate("Settings", "Protein Infer")) + self.comboBox_protein_infer_greedy_mode.setItemText(0, _translate("Settings", "fast")) + self.comboBox_protein_infer_greedy_mode.setItemText(1, _translate("Settings", "normal")) + self.label_24.setText(_translate("Settings", "Greedy Mode in Razor Method")) + self.toolBox.setItemText(self.toolBox.indexOf(self.page_3), _translate("Settings", "Others")) diff --git a/utils/TaxaFuncAnalyzer.py b/utils/TaxaFuncAnalyzer.py index 9e3b4d4..21b9b60 100644 --- a/utils/TaxaFuncAnalyzer.py +++ b/utils/TaxaFuncAnalyzer.py @@ -479,9 +479,11 @@ def set_multi_tables(self, level: str = 's', func_threshold:float = 1.00, 'outlier_handle_by_group': None, 'processing_order': None}, peptide_num_threshold: dict = {'taxa': 1, 'func': 1, 'taxa_func': 1}, - sum_protein:bool = False, sum_protein_params: dict = { 'method': 'razor', + sum_protein:bool = False, sum_protein_params: dict = {'method': 'razor', 'by_sample': False, - 'rank_method': 'unique_counts'} + 'rank_method': 'unique_counts', + 'greedy_method': 'heap', + } ): """ Example Usage: diff --git a/utils/metax_updater.py b/utils/metax_updater.py index 09d4ece..acbac6e 100644 --- a/utils/metax_updater.py +++ b/utils/metax_updater.py @@ -200,27 +200,27 @@ def replace_metax_dir(self): shutil.move(os.path.join(root, file), os.path.join(metax_folder_path, file)) for dir in dirs: shutil.move(os.path.join(root, dir), os.path.join(metax_folder_path, dir)) + + return True def update_metax(self): - # ask if user want to update + # ask if user wants to update try: change_log_str = self.get_str() - except Exception as e: print(f"Read change log failed: {e}") change_log_str = "No change log." - + if self.current_api != self.remote_api: - QMessageBox.warning(self.MainWindow, "Update", f"MetaX new version is available with a new API.\n\nPlease download the new version manually.\n\n\ + self.display_message_in_text_browser("Update", f"MetaX new version is available with a new API.\n\nPlease download the new version manually.\n\n\ current version: {self.current_version}\nremote version: {self.remote_version}\n\nChange log:\n{change_log_str}") return - reply = QMessageBox.question(self.MainWindow, "Update", - f"MetaX new version is available. Do you want to update?\ - \ncurrent version: {self.current_version}\nremote version: {self.remote_version}\n\nChange log:\n{change_log_str}", - QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) + reply = self.display_message_in_text_browser("Update", f"MetaX new version is available. Do you want to update?\ + \ncurrent version: {self.current_version}\nremote version: {self.remote_version}\n\nChange log:\n{change_log_str}", + QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.Yes: self.metaXGUI.show_message("Updating MetaX...", "Updating...") # set update_required flag to True @@ -229,32 +229,69 @@ def update_metax(self): try: download_success = self.download_project_zip_and_unzip() - if download_success is False: + if not download_success: QMessageBox.warning(self.MainWindow, "Update", "Download failed. Please try again later or update manually.") return # replace the old MetaX folder with the new one replace_success = self.replace_metax_dir() - if replace_success is False: + if not replace_success: QMessageBox.warning(self.MainWindow, "Update", "An error occurred while replacing the MetaX directory. Please try again later or update manually.") return - + # check if the update is successful if self.check_update_status(): msg = f"MetaX has been updated to {self.remote_version}. Please restart MetaX." else: msg = f"Warning: MetaX update failed. Still in version {self.current_version}. Please try again later or update manually." - + QMessageBox.information(self.MainWindow, "Update", msg) # force close MetaX without triggering closeEvent QtWidgets.QApplication.quit() # close the QSplashScreen self.splash.finish(self.MainWindow) sys.exit() - - + except Exception as e: QMessageBox.warning(self.MainWindow, "Update", f'Update failed: {e}') + def display_message_in_text_browser(self, title, message, buttons=QMessageBox.NoButton, default_button=QMessageBox.NoButton): + dialog = QtWidgets.QDialog() + dialog.setWindowTitle(title) + layout = QtWidgets.QVBoxLayout(dialog) + + # set icon as parent's icon + dialog.setWindowIcon(self.MainWindow.windowIcon()) + + text_browser = QtWidgets.QTextBrowser() + text_browser.setText(message) + layout.addWidget(text_browser) + + # create button box + button_box = QtWidgets.QDialogButtonBox() + # create yes and no buttons + if buttons & QMessageBox.Yes: + yes_button = button_box.addButton(QtWidgets.QDialogButtonBox.Yes) + if default_button == QMessageBox.Yes: + yes_button.setDefault(True) + if buttons & QMessageBox.No: + no_button = button_box.addButton(QtWidgets.QDialogButtonBox.No) + if default_button == QMessageBox.No: + no_button.setDefault(True) + layout.addWidget(button_box) + + # connect signals + button_box.accepted.connect(dialog.accept) + button_box.rejected.connect(dialog.reject) + + dialog.setLayout(layout) + dialog.resize(500, 400) + + # show dialog and wait for user response + result = dialog.exec_() + if result == QtWidgets.QDialog.Accepted: + return QMessageBox.Yes + else: + return QMessageBox.No def check_update(self, show_message=False): print(f"Checking update from {self.branch} branch...") diff --git a/utils/version.py b/utils/version.py index fc032ef..7510d50 100644 --- a/utils/version.py +++ b/utils/version.py @@ -1,2 +1,2 @@ -__version__ = '1.107.5' +__version__ = '1.107.8' API_version = '1' \ No newline at end of file