Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
byemaxx committed Nov 16, 2024
2 parents 7122397 + 68d9de2 commit 05644c7
Show file tree
Hide file tree
Showing 17 changed files with 488 additions and 227 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
.vscode/
.idea/
.trunk/
local_tests/
.local_tests/
*.pyc
*.db
.gitignore
Expand Down
26 changes: 26 additions & 0 deletions Docs/ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
# Version: 1.118.2
## Date: 2024-11-8
### Changes:
- Change: changed the "Sum normalization" to "Percentages Scaling" in the data preprossing part to avoid the confusion with the normalization method.


# Version: 1.118.1
## Date: 2024-11-7
### Changes:
- New: added 'half_same_trends' optional to extract the table of each group has the same trend (all positive or all negative non-NA values) and at east 50% of the values are non-NA of the result of group-control in condition.
- Change: refined the layout of the setting window.


# Version: 1.118.0
## Date: 2024-11-7
### Changes:
- New: added a debug console to run any python code in the MetaX to modify the object (FOR DEVELOPER ONLY).
- Change: Load the some Setting from last profile when open the MetaX so that the user doesn't need to set the setting every time.
- Fix: Fixed the bug of when plot mean of heatmap while selcect the sub meta, the mean calculation was not correct.

# Version: 1.117.2
## Date: 2024-11-5
### Changes:
- Change: add a column of focus list to the taxa-function network table.


# Version: 1.117.1
## Date: 2024-11-5
### Changes:
Expand Down
56 changes: 41 additions & 15 deletions metax/gui/main_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
from metax.gui.metax_gui.extended_combo_box import ExtendedComboBox
from metax.gui.metax_gui.show_plt import ExportablePlotDialog
from metax.gui.metax_gui.input_window import InputWindow
from metax.gui.metax_gui.command_window import CommandWindow
from metax.gui.metax_gui.user_agreement_dialog import UserAgreementDialog
from metax.gui.metax_gui.settings_widget import SettingsWidget
from metax.gui.metax_gui.cmap_combo_box import CmapComboBox
Expand Down Expand Up @@ -129,6 +130,7 @@
from .metax_gui.extended_combo_box import ExtendedComboBox
from .metax_gui.show_plt import ExportablePlotDialog
from .metax_gui.input_window import InputWindow
from .metax_gui.command_window import CommandWindow
from .metax_gui.user_agreement_dialog import UserAgreementDialog
from .metax_gui.settings_widget import SettingsWidget
from .metax_gui.cmap_combo_box import CmapComboBox
Expand Down Expand Up @@ -234,6 +236,7 @@ def __init__(self, MainWindow):
self.actionSave_As.setIcon(qta.icon('mdi.content-save'))
self.actionExport_Log_File.setIcon(qta.icon('mdi.export'))
self.actionHide_Show_Console.setIcon(qta.icon('mdi.console'))
self.actionDebug_Console.setIcon(qta.icon('fa5b.dev'))
self.actionAny_Table_Mode.setIcon(qta.icon('mdi.table'))
self.actionCheck_Update.setIcon(qta.icon('mdi.update'))
self.actionSettings.setIcon(qta.icon('mdi.cog'))
Expand All @@ -251,6 +254,7 @@ def __init__(self, MainWindow):
self.actionExport_Log_File.triggered.connect(self.export_log_file)
self.console_visible = False
self.actionHide_Show_Console.triggered.connect(self.show_hide_console)
self.actionDebug_Console.triggered.connect(self.show_command_line_window)
self.actionAny_Table_Mode.triggered.connect(self.set_any_table_mode)
self.actionCheck_Update.triggered.connect(lambda: self.check_update(show_message=True, manual_check_trigger=True))
self.actionSettings.triggered.connect(self.show_settings_window)
Expand Down Expand Up @@ -713,6 +717,14 @@ def change_event_comboBox_condition_group(comboBox, group_name):


def show_settings_window(self):
def get_stat_mean_by_zero_dominant():
if hasattr(self, 'tfa.stat_mean_by_zero_dominant'):
return self.tfa.stat_mean_by_zero_dominant
elif self.settings.contains("stat_mean_by_zero_dominant") and self.settings.value("stat_mean_by_zero_dominant", type=bool):
return True
else:
return False

if self.settings_dialog is None:
self.settings_dialog = QDialog(self.MainWindow)
self.settings_dialog.setWindowTitle("Settings")
Expand All @@ -725,6 +737,7 @@ def show_settings_window(self):
parent=self.settings_dialog,
update_branch=self.update_branch,
auto_check_update=self.auto_check_update,
stat_mean_by_zero_dominant = get_stat_mean_by_zero_dominant(),
QSettings=self.settings,
)
settings_widget.update_mode_changed.connect(self.on_update_mode_changed)
Expand All @@ -741,7 +754,10 @@ def show_settings_window(self):
self.settings_dialog.setLayout(layout)

self.settings_dialog.show()


def show_command_line_window(self):
self.command_window = CommandWindow(self.MainWindow, main_gui=self)
self.command_window.show()

# handle the update mode changed from settings window
def on_update_mode_changed(self, mode):
Expand All @@ -768,11 +784,11 @@ def on_html_theme_changed(self, theme):

def on_stat_mean_by_zero_dominant_changed(self, mode):
# chcek if self.tfa exists
if not hasattr(self, 'tfa'):
if not hasattr(self.tfa, 'stat_mean_by_zero_dominant'):
print("Please load the data first.")
return

self.tfa.stat_mean_by_zero_dominant = mode
self.tfa.stat_mean_by_zero_dominant = mode
self.settings.setValue("stat_mean_by_zero_dominant", mode)
print(f"Stat mean by zero dominant changed to: {mode}")

def on_protein_infer_method_changed(self, method):
Expand Down Expand Up @@ -1798,8 +1814,11 @@ def run_after_set_multi_tables(self):

# add "protein" "Custom" to comboBoxs to plot
self.add_or_remove_protein_custom_label()


#set stat_mean_by_zero_dominant mode by QSettings
if self.settings.contains("stat_mean_by_zero_dominant"):
self.tfa.stat_mean_by_zero_dominant = self.settings.value("stat_mean_by_zero_dominant", type=bool)

# add tables to table dict
if self.table_dict == {}:
if self.tfa.any_df_mode:
Expand Down Expand Up @@ -2655,7 +2674,7 @@ def set_multi_table(self, restore_taxafunc=False, saved_obj=None):
"Standard Scaling (Z-Score)": "zscore",
"Min-Max Scaling": "minmax",
"Pareto Scaling": "pareto",
"Normalization by sum": "sum",
"Percentages Scaling": "percentage",
}
normalize_method = normalize_dict[normalize_method]
transform_method = transform_dict[transform_method]
Expand Down Expand Up @@ -3616,10 +3635,13 @@ def plot_basic_list(self, plot_type='heatmap'):
df = dft
else:
df = dft.loc[self.basic_heatmap_list]

# Done for creating the dataframe for the heatmap #

try:
if plot_type == 'heatmap':
df, sample_to_group_dict = self.tfa.BasicStats.get_df_by_mean_and_submeta(df = df,
sub_meta = sub_meta,
plot_mean = plot_mean)
if row_cluster or (scale =='row'):
df = self.delete_zero_rows(df)
if col_cluster or (scale =='col'):
Expand All @@ -3634,14 +3656,14 @@ def plot_basic_list(self, plot_type='heatmap'):
return
else:
pass

# plot heatmap
self.show_message(f'Plotting {plot_type}...')
HeatmapPlot(self.tfa, **self.heatmap_params_dict).plot_basic_heatmap(df=df, title=title, fig_size=(int(width), int(height)),
scale=scale, row_cluster=row_cluster, col_cluster=col_cluster,
cmap=cmap, rename_taxa=rename_taxa, font_size=font_size,
show_all_labels=show_all_labels, rename_sample=rename_sample,
plot_mean = plot_mean, sub_meta = sub_meta, return_type = 'fig')
show_all_labels=show_all_labels, return_type = 'fig',
sample_to_group_dict = sample_to_group_dict)


elif plot_type == 'bar':
Expand Down Expand Up @@ -5586,7 +5608,9 @@ def plot_tflink_heatmap(self, return_type = 'fig'):
QMessageBox.warning(self.MainWindow, 'Warning', 'No data!, please reselect!')
return None


df, sample_to_group_dict = self.tfa.BasicStats.get_df_by_mean_and_submeta(df = df,
sub_meta = sub_meta,
plot_mean = plot_mean)
if row_cluster or (scale == 'row'):
df = self.delete_zero_rows(df)

Expand All @@ -5598,8 +5622,7 @@ def plot_tflink_heatmap(self, return_type = 'fig'):
fig_res = HeatmapPlot(self.tfa, **self.heatmap_params_dict).plot_basic_heatmap(df=df, title=title, fig_size=(int(width), int(height)),
scale=scale, row_cluster=row_cluster, col_cluster=col_cluster,
cmap=cmap, rename_taxa=rename_taxa, font_size=font_size, show_all_labels=show_all_labels,
rename_sample=rename_sample, sub_meta=sub_meta,
plot_mean=plot_mean, return_type = return_type
return_type = return_type, sample_to_group_dict = sample_to_group_dict
)

if return_type == 'table':
Expand Down Expand Up @@ -5632,8 +5655,11 @@ def delete_zero_columns(self, dataframe):
zero_columns = dataframe.columns[(dataframe == 0).all(axis=0)]
if not zero_columns.empty:
dataframe = dataframe.drop(zero_columns, axis=1)
# add group name to zero_columns
zero_columns = [f'{i} ({self.tfa.get_group_of_a_sample(i)})' for i in zero_columns]
# show the message with group name
try: # add group name to zero_columns if possible
zero_columns = [f'{i} ({self.tfa.get_group_of_a_sample(i)})' for i in zero_columns]
except Exception:
print('The column name is not a sample name, Skip adding group name to the column name!')
col_str = '\n'.join(zero_columns)
if len(zero_columns) > 10:
# use InputWindow to show the deleted rows
Expand Down
105 changes: 105 additions & 0 deletions metax/gui/metax_gui/command_window.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import sys
import io
from contextlib import redirect_stdout
from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit, QVBoxLayout, QWidget, QPushButton
from PyQt5.QtCore import Qt, QEvent
from PyQt5.QtGui import QTextCursor

class PlainTextEditor(QTextEdit):
def insertFromMimeData(self, source):
if source.hasText():
self.insertPlainText(source.text())

class OutputRedirector(io.StringIO):
def __init__(self, output_widget):
super().__init__()
self.output_widget = output_widget

def write(self, string):
super().write(string)
self.output_widget.append(string) # Append output to QTextEdit

def flush(self):
pass

class CommandWindow(QMainWindow):
def __init__(self, parent=None, main_gui=None):
super(CommandWindow, self).__init__(parent)
self.main_gui = main_gui # Ensure main_gui is properly handled if None
self.initUI()
self.local_context = {'metax': main_gui} if main_gui else {}
self.history = []
self.history_index = 0

def initUI(self):
self.setWindowTitle('Debug Console')
self.resize(900, 600)
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget)
layout = QVBoxLayout(self.central_widget)

self.output = QTextEdit()
self.output.setReadOnly(True)
layout.addWidget(self.output)

self.input = PlainTextEditor()
self.input.setFixedHeight(100)
layout.addWidget(self.input)

self.sendButton = QPushButton("Send")
self.sendButton.clicked.connect(self.process_command)
layout.addWidget(self.sendButton)

self.input.installEventFilter(self)

def process_command(self):
command = self.input.toPlainText().strip()
if command:
self.output.append(f"> {command}")
self.input.clear()
self.history.append(command)
self.history_index = len(self.history)

redirector = OutputRedirector(self.output) # 创建输出重定向器
with redirect_stdout(redirector): # 使用 redirect_stdout
try:
# 尝试作为表达式执行
result = eval(command, globals(), self.local_context)
if result is not None: # 如果有结果,显示它
print(result)
except SyntaxError:
# 如果表达式执行失败,尝试作为语句执行
try:
exec(command, globals(), self.local_context)
except Exception as e:
self.output.append(f"Error: {str(e)}")
except Exception as e:
self.output.append(f"Error: {str(e)}")

def eventFilter(self, source, event):
if source == self.input and event.type() == QEvent.KeyPress:
if event.key() == Qt.Key_Return:
if event.modifiers() & Qt.ShiftModifier:
self.input.insertPlainText('\n')
return True
else:
self.process_command()
return True
elif event.key() == Qt.Key_Up or event.key() == Qt.Key_Down:
if self.history_index > 0 and event.key() == Qt.Key_Up:
self.history_index -= 1
self.input.setText(self.history[self.history_index])
self.input.moveCursor(QTextCursor.End)
return True
elif self.history_index < len(self.history) - 1 and event.key() == Qt.Key_Down:
self.history_index += 1
self.input.setText(self.history[self.history_index])
self.input.moveCursor(QTextCursor.End)
return True
return super(CommandWindow, self).eventFilter(source, event)

if __name__ == "__main__":
app = QApplication(sys.argv)
win = CommandWindow()
win.show()
sys.exit(app.exec_())
Loading

0 comments on commit 05644c7

Please sign in to comment.