Source code for fast_conformation.gui.analysis_config

import sys
from dataclasses import dataclass
from typing import Callable
from pathlib import Path
import os
from PyQt5.QtWidgets import (
    QApplication, QScrollArea, QVBoxLayout, QWidget, QLabel, QStackedWidget, QCheckBox, QHBoxLayout, QMessageBox
)
from PyQt5.QtCore import QSize, Qt
from PyQt5.QtGui import QIcon
from fast_conformation.gui.icons import Icons
from fast_conformation.rmsf_plddt import run_rmsf_analysis
from fast_conformation.rmsd_mode1d import run_rmsd_analysis
from fast_conformation.rmsd_mode2d import run_2d_rmsd_analysis
from fast_conformation.pca_clustering import run_pca_analysis
from fast_conformation.save_traj import run_trajectory_saving
from fast_conformation.tmscore_mode1d import run_tmscore_analysis
from fast_conformation.tmscore_mode2d import run_2d_tmscore_analysis
from fast_conformation.gui.widget_base import AnalysisWidgetBase, merge_configs
from fast_conformation.gui.plot_widget import PlotWidget
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QFormLayout, QLineEdit, QPushButton, QFileDialog, QListWidget, QVBoxLayout
from PyQt5.QtCore import Qt
from dataclasses import dataclass
from typing import Callable


[docs] class AnalysisConfigWidget(QWidget): """ The AnalysisConfigWidget class is responsible for providing a user interface to configure various analysis options. It contains general analysis options and specific configurations for different types of analysis (RMSF, RMSD, TMScore, etc.). Attributes: job_manager: A manager object that handles job submissions and execution. """ def __init__(self, job_manager): """ Initialize the AnalysisConfigWidget with a job manager. Args: job_manager: The manager responsible for handling job execution. """ super().__init__() self.job_manager = job_manager # Main layout main_layout = QVBoxLayout() # Top part for general analysis options and icon grid top_layout = QHBoxLayout() self.setStyleSheet(""" QToolBar { background-color: #333333; color: white; padding: 10px; font-size: 16px; } QPushButton { background-color: #555555; color: white; border: none; padding: 8px 16px; border-radius: 4px; margin: 0 5px; font-size: 16px; } QPushButton:hover { background-color: #666666; } QPushButton:pressed { background-color: #777777; } """) # General analysis options self.general_analysis_widget = GeneralAnalysisWidget() top_layout.addWidget(self.general_analysis_widget) # Icon grid self.icon_grid = Icons(self) self.icon_grid.addItems(ANALYSIS_CATEGORIES) self.icon_grid.itemClicked.connect(self._on_item_clicked) top_layout.addWidget(self.icon_grid) main_layout.addLayout(top_layout) # Stacked widget for analysis configuration panels self.analysis_stack = QStackedWidget() main_layout.addWidget(self.analysis_stack) # Create a scroll area and set the main layout as its widget scroll_area = QScrollArea() scroll_area.setWidgetResizable(True) scroll_content = QWidget() scroll_content.setLayout(main_layout) scroll_area.setWidget(scroll_content) # Set the scroll area as the central widget layout = QVBoxLayout() layout.addWidget(scroll_area) self.setLayout(layout) self.setWindowTitle("Analysis Configurations") def _on_item_clicked(self, item): """ Handle the event when an item is clicked in the icon grid. It loads the corresponding analysis widget in the stacked widget. Args: item: The clicked item in the icon grid. """ name = item.text() getter = self.general_analysis_widget.get_general_options widget = ANALYSIS_CATEGORIES[name].widget(getter, self.job_manager) widget.setStyleSheet(""" QToolBar { background-color: #333333; color: white; padding: 10px; font-size: 16px; } QPushButton { background-color: #555555; color: white; border: none; padding: 8px 16px; border-radius: 4px; margin: 0 5px; font-size: 16px; } QPushButton:hover { background-color: #666666; } QPushButton:pressed { background-color: #777777; } """) self.analysis_stack.addWidget(widget) self.analysis_stack.setCurrentWidget(widget)
[docs] def show_plot(self, name, plot_widget): """ Display the plot for the given analysis. Args: name: The name of the analysis. plot_widget: The widget containing the plot to be displayed. """ parent=self.parentWidget().parentWidget().parentWidget().parentWidget() print(f'parent_widget {parent}') parent.show_plot(name, plot_widget)
[docs] @dataclass class AnalysisCategory: """ Data class to represent an analysis category. Attributes: widget: A callable that returns the widget associated with the analysis. tool_tip: A string that provides a description of the analysis category. """ widget: Callable tool_tip: str = ""
ANALYSIS_CATEGORIES = { "RMSF Analysis": AnalysisCategory( widget=lambda getter, job_manager: RMSFAnalysisWidget(getter, job_manager), tool_tip="RMSF analysis configurations", ), "RMSD Analysis": AnalysisCategory( widget=lambda getter, job_manager: RMSDAnalysisWidget(getter, job_manager), tool_tip="RMSD analysis configurations", ), "RMSD_2D": AnalysisCategory( widget=lambda getter, job_manager: RMSD2DWidget(getter, job_manager), tool_tip="RMSD_2D configurations", ), "TMSCORE": AnalysisCategory( widget=lambda getter, job_manager: TMSCOREWidget(getter, job_manager), tool_tip="TMSCORE configurations", ), "TMSCORE_2D": AnalysisCategory( widget=lambda getter, job_manager: TwoTMScoreWidget(getter, job_manager), tool_tip="TMSCORE_2D configurations", ), "PCA": AnalysisCategory( widget=lambda getter, job_manager: PCAWidget(getter, job_manager), tool_tip="PCA configurations", ), "Trajectory Saving": AnalysisCategory( widget=lambda getter, job_manager: TrajectorySavingWidget(getter, job_manager), tool_tip="Trajectory saving configurations", ), }
[docs] class GeneralAnalysisWidget(QWidget): """ GeneralAnalysisWidget provides the user interface for setting general analysis options like job name, output path, sequence pairs, and other configurations. Methods: clear_seq_pairs: Clears the list of sequence pairs. select_output_path: Opens a file dialog to select the output path directory. select_predictions_path: Opens a file dialog to select the predictions path directory. auto_detect_sequence_pairs: Automatically detects sequence pairs from the selected directory. get_general_options: Returns a dictionary of the current general analysis options. add_seq_pair: Adds a new sequence pair to the list. remove_seq_pair: Removes a sequence pair from the list. """ def __init__(self): """ Initialize the GeneralAnalysisWidget with input fields and layout. """ super().__init__() layout = QFormLayout() self.jobname_input = QLineEdit("abl_wt") self.output_path_input = QLineEdit("") self.output_path_button = QPushButton("Browse") self.predictions_path_input = QLineEdit("") self.predictions_path_button = QPushButton("Browse") self.engine_input = QLineEdit("alphafold2") self.align_range_input = QLineEdit("backbone") self.analysis_range_input = QLineEdit("backbone and resid 358-365") self.analysis_range_name_input = QLineEdit("aloop") self.ref1d_input = QLineEdit() self.starting_residue_input = QLineEdit("200") # Seq Pairs self.seq_pairs_layout = QVBoxLayout() self.add_seq_pair_button = QPushButton("Add Pair:") self.add_seq_pair_button.clicked.connect(lambda: self.add_seq_pair("", "")) self.seq_pairs = [] layout.addRow("Job Name:", self.jobname_input) output_path_layout = QHBoxLayout() output_path_layout.addWidget(self.output_path_input) output_path_layout.addWidget(self.output_path_button) layout.addRow("Output Path:", output_path_layout) instructions_label = QLabel("Select the path to AF2 predictions which contains subfolders for each subsampling condition.") instructions_label.setWordWrap(True) # Ensure the text wraps if it's too long layout.addRow(instructions_label) predictions_path_layout = QHBoxLayout() predictions_path_layout.addWidget(self.predictions_path_input) predictions_path_layout.addWidget(self.predictions_path_button) layout.addRow("Predictions Path:", predictions_path_layout) self.seq_pairs_layout.addWidget(self.add_seq_pair_button) layout.addRow("max_seq:extra_seq pairs:", self.seq_pairs_layout) layout.addRow("Engine:", self.engine_input) layout.addRow("Align Range:", self.align_range_input) layout.addRow("Analysis Range:", self.analysis_range_input) layout.addRow("Analysis Range Name:", self.analysis_range_name_input) layout.addRow("Ref1D:", self.ref1d_input) layout.addRow("Starting Residue:", self.starting_residue_input) self.output_path_button.clicked.connect(self.select_output_path) self.predictions_path_button.clicked.connect(self.select_predictions_path) self.setLayout(layout)
[docs] def clear_seq_pairs(self): """ Clears all sequence pairs from the layout. """ for i in range(len(self.seq_pairs) - 1, -1, -1): layout, seq_pair = self.seq_pairs.pop(i) while layout.count(): child = layout.takeAt(0) if child.widget(): child.widget().deleteLater() self.seq_pairs_layout.removeItem(layout) layout.deleteLater()
[docs] def select_output_path(self): """ Opens a file dialog to allow the user to select an output directory. """ directory = QFileDialog.getExistingDirectory(self, "Select Directory") if directory: self.output_path_input.setText(directory)
[docs] def select_predictions_path(self): """ Opens a file dialog to allow the user to select a directory containing AF2 predictions. """ directory = QFileDialog.getExistingDirectory(self, "Select Directory") if directory: self.predictions_path_input.setText(directory) self.auto_detect_sequence_pairs(directory)
[docs] def auto_detect_sequence_pairs(self, predictions_path): """ Automatically detects sequence pairs based on the structure of the predictions path directory. Args: predictions_path: The path to the directory containing predictions. """ try: self.clear_seq_pairs() for subdir in os.listdir(predictions_path): subdir_path = os.path.join(predictions_path, subdir) if os.path.isdir(subdir_path): parts = subdir.split('_') if len(parts) >= 3 and parts[-2].isdigit() and parts[-1].isdigit(): max_seq = parts[-2] extra_seq = parts[-1] self.add_seq_pair(max_seq, extra_seq) else: current_pair = subdir.split('_')[-2:] if all(part.isdigit() for part in current_pair): max_seq, extra_seq = current_pair self.add_seq_pair(max_seq, extra_seq) except Exception as e: QMessageBox.critical(self, "Error", f"Failed to auto-detect sequence pairs: {e}")
[docs] def get_general_options(self): """ Retrieves the current general options set in the widget. Returns: A dictionary containing the general options. """ return { "jobname": self.jobname_input.text(), "output_path": self.output_path_input.text(), "seq_pairs": [[int(seq_pair[0].text()), int(seq_pair[1].text())] for layout, seq_pair in self.seq_pairs], "predictions_path": self.predictions_path_input.text(), "engine": self.engine_input.text(), "align_range": self.align_range_input.text(), "analysis_range": self.analysis_range_input.text(), "analysis_range_name": self.analysis_range_name_input.text(), "ref1d": self.ref1d_input.text(), "starting_residue": self.starting_residue_input.text(), }
[docs] def add_seq_pair(self, seq1='', seq2=''): """ Adds a new sequence pair to the widget. Args: seq1: The first sequence in the pair. seq2: The second sequence in the pair. """ seq_pair_layout = QHBoxLayout() seq_pair = [] seq1_input = QLineEdit(seq1) seq1_input.setPlaceholderText("Sequence 1") seq2_input = QLineEdit(seq2) seq2_input.setPlaceholderText("Sequence 2") seq_pair.append(seq1_input) seq_pair.append(seq2_input) remove_button = QPushButton("Remove") remove_button.clicked.connect(lambda: self.remove_seq_pair(seq_pair_layout, seq_pair)) seq_pair_layout.addWidget(seq1_input) seq_pair_layout.addWidget(seq2_input) seq_pair_layout.addWidget(remove_button) self.seq_pairs_layout.insertLayout(self.seq_pairs_layout.count() - 1, seq_pair_layout) self.seq_pairs.append((seq_pair_layout, seq_pair))
[docs] def remove_seq_pair(self, layout, seq_pair): """ Removes a sequence pair from the widget. Args: layout: The layout containing the sequence pair widgets. seq_pair: The sequence pair to be removed. """ while layout.count(): child = layout.takeAt(0) if child.widget(): child.widget().deleteLater() self.seq_pairs_layout.removeItem(layout) self.seq_pairs.remove((layout, seq_pair)) layout.deleteLater()
[docs] class RMSFAnalysisWidget(AnalysisWidgetBase): """ Widget to configure and run RMSF (Root Mean Square Fluctuation) analysis. Methods: validate_inputs: Validates the inputs provided by the user. get_specific_options: Retrieves the specific options for RMSF analysis. run_specific_analysis: Runs the RMSF analysis with the given configuration. """ def __init__(self, general_options_getter, job_manager): """ Initialize the RMSFAnalysisWidget with general options getter and job manager. Args: general_options_getter: Callable to retrieve general analysis options. job_manager: The manager responsible for handling job execution. """ super().__init__(job_manager) self.general_options_getter = general_options_getter layout = QFormLayout() self.detect_mobile_checkbox = QCheckBox() self.detect_mobile_checkbox.setChecked(True) self.peak_width_input = QLineEdit("3") self.peak_prominence_input = QLineEdit("1") self.peak_height_input = QLineEdit("2") self.starting_residue_input = QLineEdit("200") layout.addRow("Detect Mobile:", self.detect_mobile_checkbox) layout.addRow("Peak Width:", self.peak_width_input) layout.addRow("Peak Prominence:", self.peak_prominence_input) layout.addRow("Peak Height:", self.peak_height_input) layout.addRow("Starting Residue:", self.starting_residue_input) self.layout_rmsf=layout self.run_button = QPushButton("Run") self.run_button.clicked.connect(self.run_analysis) layout.addWidget(self.run_button) self.setLayout(layout)
[docs] def validate_inputs(self): """ Validates the inputs provided by the user for RMSF analysis. Returns: A list of errors if any input is invalid, otherwise an empty list. """ errors = [] if not self.peak_width_input.text().isdigit(): errors.append("Peak Width must be a number.") if not self.peak_prominence_input.text().isdigit(): errors.append("Peak Prominence must be a number.") if not self.peak_height_input.text().isdigit(): errors.append("Peak Height must be a number.") if not self.starting_residue_input.text().isdigit(): errors.append("Starting Residue must be a number.") return errors
[docs] def get_specific_options(self): """ Retrieves the specific options for RMSF analysis. Returns: A dictionary containing the specific options for RMSF analysis. """ return { "detect_mobile": self.detect_mobile_checkbox.isChecked(), "peak_width": int(self.peak_width_input.text()), "peak_prominence": int(self.peak_prominence_input.text()), "peak_height": int(self.peak_height_input.text()), "starting_residue": int(self.starting_residue_input.text()) }
[docs] def run_specific_analysis(self, config): """ Runs the RMSF analysis with the given configuration. Args: config: The configuration options for the RMSF analysis. """ self.plot_widget = PlotWidget(self) parent=self.parentWidget().parentWidget().parentWidget().parentWidget().parentWidget() run_rmsf_analysis(config, self.plot_widget) parent.show_plot("RMSF Analysis", self.plot_widget)
[docs] class RMSDAnalysisWidget(AnalysisWidgetBase): """ Widget to configure and run RMSD (Root Mean Square Deviation) analysis. Methods: validate_inputs: Validates the inputs provided by the user. get_specific_options: Retrieves the specific options for RMSD analysis. run_specific_analysis: Runs the RMSD analysis with the given configuration. """ def __init__(self, general_options_getter, job_manager): """ Initialize the RMSDAnalysisWidget with general options getter and job manager. Args: general_options_getter: Callable to retrieve general analysis options. job_manager: The manager responsible for handling job execution. """ super().__init__(job_manager) self.general_options_getter = general_options_getter layout = QFormLayout() self.analysis_range_input = QLineEdit("backbone and resid 358-365") self.analysis_range_name_input = QLineEdit("aloop") layout.addRow("Analysis Range:", self.analysis_range_input) layout.addRow("Analysis Range Name:", self.analysis_range_name_input) self.run_button = QPushButton("Run") self.run_button.clicked.connect(self.run_analysis) layout.addWidget(self.run_button) self.setLayout(layout)
[docs] def validate_inputs(self): """ Validates the inputs provided by the user for RMSD analysis. Returns: A list of errors if any input is invalid, otherwise an empty list. """ errors = [] if not self.analysis_range_input.text(): errors.append("Analysis Range cannot be empty.") if not self.analysis_range_name_input.text(): errors.append("Analysis Range Name cannot be empty.") return errors
[docs] def get_specific_options(self): """ Retrieves the specific options for RMSD analysis. Returns: A dictionary containing the specific options for RMSD analysis. """ return { "analysis_range": self.analysis_range_input.text(), "analysis_range_name": self.analysis_range_name_input.text() }
[docs] def run_specific_analysis(self, config): """ Runs the RMSD analysis with the given configuration. Args: config: The configuration options for the RMSD analysis. """ self.plot_widget = PlotWidget(self) parent=self.parentWidget().parentWidget().parentWidget().parentWidget().parentWidget() run_rmsd_analysis(config, self.plot_widget) parent.show_plot("RMSD Analysis", self.plot_widget)
[docs] class RMSD2DWidget(AnalysisWidgetBase): """ Widget to configure and run 2D RMSD (Root Mean Square Deviation) analysis. Methods: validate_inputs: Validates the inputs provided by the user. get_specific_options: Retrieves the specific options for 2D RMSD analysis. run_specific_analysis: Runs the 2D RMSD analysis with the given configuration. """ def __init__(self, general_options_getter, job_manager): """ Initialize the RMSD2DWidget with general options getter and job manager. Args: general_options_getter: Callable to retrieve general analysis options. job_manager: The manager responsible for handling job execution. """ super().__init__(job_manager) self.general_options_getter = general_options_getter layout = QFormLayout() self.mode_results_input = QLineEdit() self.ref2d1_input = QLineEdit() self.ref2d2_input = QLineEdit() self.n_stdevs_input = QLineEdit("5") self.n_clusters_input = QLineEdit() layout.addRow("RMSD1D analysis results csv filepath:", self.mode_results_input) layout.addRow("Reference Structure 1 (Ref2D1):", self.ref2d1_input) layout.addRow("Reference Structure 2 (Ref2D2):", self.ref2d2_input) layout.addRow("Number of Standard Deviations (n_stdevs):", self.n_stdevs_input) layout.addRow("Number of Clusters (n_clusters):", self.n_clusters_input) self.run_button = QPushButton("Run") self.run_button.clicked.connect(self.run_analysis) layout.addWidget(self.run_button) self.setLayout(layout)
[docs] def validate_inputs(self): """ Validates the inputs provided by the user for 2D RMSD analysis. Returns: A list of errors if any input is invalid, otherwise an empty list. """ errors = [] if not self.n_stdevs_input.text().isdigit(): errors.append("Number of Standard Deviations (n_stdevs) must be a number.") return errors
[docs] def get_specific_options(self): """ Retrieves the specific options for 2D RMSD analysis. Returns: A dictionary containing the specific options for 2D RMSD analysis. """ return { "mode_results": self.mode_results_input.text(), "ref2d1": self.ref2d1_input.text(), "ref2d2": self.ref2d2_input.text(), "n_stdevs": self.n_stdevs_input.text(), "n_clusters": self.n_clusters_input.text() }
[docs] def run_specific_analysis(self, config): """ Runs the 2D RMSD analysis with the given configuration. Args: config: The configuration options for the 2D RMSD analysis. """ self.plot_widget = PlotWidget(self) parent=self.parentWidget().parentWidget().parentWidget().parentWidget().parentWidget() run_2d_rmsd_analysis(config, self.plot_widget) parent.show_plot("TMScore-2D Analysis", self.plot_widget)
[docs] class TMSCOREWidget(AnalysisWidgetBase): """ Widget to configure and run TMScore analysis. Methods: validate_inputs: Validates the inputs provided by the user. get_specific_options: Retrieves the specific options for TMScore analysis. run_specific_analysis: Runs the TMScore analysis with the given configuration. """ def __init__(self, general_options_getter, job_manager): """ Initialize the TMSCOREWidget with general options getter and job manager. Args: general_options_getter: Callable to retrieve general analysis options. job_manager: The manager responsible for handling job execution. """ super().__init__(job_manager) self.general_options_getter = general_options_getter layout = QFormLayout() self.slice_predictions_input = QLineEdit("backbone and resid 210-459") self.ref1_input = QLineEdit() layout.addRow("Slice Predictions:", self.slice_predictions_input) layout.addRow("Reference Structure (Ref1):", self.ref1_input) self.run_button = QPushButton("Run") self.run_button.clicked.connect(self.run_analysis) layout.addWidget(self.run_button) self.setLayout(layout)
[docs] def validate_inputs(self): """ Validates the inputs provided by the user for TMScore analysis. Returns: A list of errors if any input is invalid, otherwise an empty list. """ errors = [] return errors
[docs] def get_specific_options(self): """ Retrieves the specific options for TMScore analysis. Returns: A dictionary containing the specific options for TMScore analysis. """ return { "slice_predictions": self.slice_predictions_input.text(), "ref1": self.ref1_input.text() }
[docs] def run_specific_analysis(self, config): """ Runs the TMScore analysis with the given configuration. Args: config: The configuration options for the TMScore analysis. """ self.plot_widget = PlotWidget(self) parent=self.parentWidget().parentWidget().parentWidget().parentWidget().parentWidget() run_tmscore_analysis(config, self.plot_widget) parent.show_plot("TMScore Analysis", self.plot_widget)
[docs] class TwoTMScoreWidget(AnalysisWidgetBase): """ Widget to configure and run 2D TMScore analysis. Methods: validate_inputs: Validates the inputs provided by the user. get_specific_options: Retrieves the specific options for 2D TMScore analysis. run_specific_analysis: Runs the 2D TMScore analysis with the given configuration. """ def __init__(self, general_options_getter, job_manager): """ Initialize the TwoTMScoreWidget with general options getter and job manager. Args: general_options_getter: Callable to retrieve general analysis options. job_manager: The manager responsible for handling job execution. """ super().__init__(job_manager) self.general_options_getter = general_options_getter layout = QFormLayout() self.mode_results_input = QLineEdit() self.ref2d1_input = QLineEdit() self.ref2d2_input = QLineEdit() self.n_stdevs_input = QLineEdit("5") self.n_clusters_input = QLineEdit() layout.addRow("TMScore1D analysis results csv filepath:", self.mode_results_input) layout.addRow("Reference Structure 1 (Ref2D1):", self.ref2d1_input) layout.addRow("Reference Structure 2 (Ref2D2):", self.ref2d2_input) layout.addRow("Number of Standard Deviations (n_stdevs):", self.n_stdevs_input) layout.addRow("Number of Clusters (n_clusters):", self.n_clusters_input) self.run_button = QPushButton("Run") self.run_button.clicked.connect(self.run_analysis) layout.addWidget(self.run_button) self.setLayout(layout)
[docs] def validate_inputs(self): """ Validates the inputs provided by the user for 2D TMScore analysis. Returns: A list of errors if any input is invalid, otherwise an empty list. """ errors = [] if not self.mode_results_input.text(): errors.append("Mode Results cannot be empty.") if not self.n_stdevs_input.text().isdigit(): errors.append("Number of Standard Deviations (n_stdevs) must be a number.") return errors
[docs] def get_specific_options(self): """ Retrieves the specific options for 2D TMScore analysis. Returns: A dictionary containing the specific options for 2D TMScore analysis. """ return { "mode_results": self.mode_results_input.text(), "ref2d1": self.ref2d1_input.text(), "ref2d2": self.ref2d2_input.text(), "n_stdevs": self.n_stdevs_input.text(), "n_clusters": self.n_clusters_input.text() }
[docs] def run_specific_analysis(self, config): """ Runs the 2D TMScore analysis with the given configuration. Args: config: The configuration options for the 2D TMScore analysis. """ self.plot_widget = PlotWidget(self) parent=self.parentWidget().parentWidget().parentWidget().parentWidget().parentWidget() run_2d_tmscore_analysis(config, self.plot_widget) parent.show_plot("TMScore-2D Analysis", self.plot_widget)
[docs] class PCAWidget(AnalysisWidgetBase): """ Widget to configure and run PCA (Principal Component Analysis). Methods: validate_inputs: Validates the inputs provided by the user. get_specific_options: Retrieves the specific options for PCA analysis. run_specific_analysis: Runs the PCA analysis with the given configuration. """ def __init__(self, general_options_getter, job_manager): """ Initialize the PCAWidget with general options getter and job manager. Args: general_options_getter: Callable to retrieve general analysis options. job_manager: The manager responsible for handling job execution. """ super().__init__(job_manager) self.general_options_getter = general_options_getter layout = QFormLayout() self.align_range_input = QLineEdit("backbone") self.analysis_range_input = QLineEdit("backbone and resid 358-365") self.analysis_range_name_input = QLineEdit("aloop") self.n_pca_clusters_input = QLineEdit("3") layout.addRow("Align Range:", self.align_range_input) layout.addRow("Analysis Range:", self.analysis_range_input) layout.addRow("Analysis Range Name:", self.analysis_range_name_input) layout.addRow("Number of PCA Clusters:", self.n_pca_clusters_input) self.run_button = QPushButton("Run") self.run_button.clicked.connect(self.run_analysis) layout.addWidget(self.run_button) self.setLayout(layout)
[docs] def validate_inputs(self): """ Validates the inputs provided by the user for PCA analysis. Returns: A list of errors if any input is invalid, otherwise an empty list. """ errors = [] if not self.align_range_input.text(): errors.append("Align Range cannot be empty.") if not self.analysis_range_input.text(): errors.append("Analysis Range cannot be empty.") if not self.analysis_range_name_input.text(): errors.append("Analysis Range Name cannot be empty.") if not self.n_pca_clusters_input.text().isdigit(): errors.append("Number of PCA Clusters must be a number.") return errors
[docs] def get_specific_options(self): """ Retrieves the specific options for PCA analysis. Returns: A dictionary containing the specific options for PCA analysis. """ return { "align_range": self.align_range_input.text(), "analysis_range": self.analysis_range_input.text(), "analysis_range_name": self.analysis_range_name_input.text(), "n_pca_clusters": self.n_pca_clusters_input.text() }
[docs] def run_specific_analysis(self, config): """ Runs the PCA analysis with the given configuration. Args: config: The configuration options for the PCA analysis. """ self.plot_widget = PlotWidget(self) parent=self.parentWidget().parentWidget().parentWidget().parentWidget().parentWidget() run_pca_analysis(config, self.plot_widget) parent.show_plot("TMScore-2D Analysis", self.plot_widget)
[docs] class TrajectorySavingWidget(AnalysisWidgetBase): """ Widget to configure and run trajectory saving. Methods: validate_inputs: Validates the inputs provided by the user. get_specific_options: Retrieves the specific options for trajectory saving. run_specific_analysis: Runs the trajectory saving process with the given configuration. """ def __init__(self, general_options_getter, job_manager): """ Initialize the TrajectorySavingWidget with general options getter and job manager. Args: general_options_getter: Callable to retrieve general analysis options. job_manager: The manager responsible for handling job execution. """ super().__init__(job_manager) self.general_options_getter = general_options_getter layout = QFormLayout() self.analysis_range_input = QLineEdit() self.analysis_range_name_input = QLineEdit() self.reorder_input = QLineEdit("rmsd_1d") self.traj_format_input = QLineEdit("pdb") layout.addRow("Analysis Range:", self.analysis_range_input) layout.addRow("Analysis Range Name:", self.analysis_range_name_input) layout.addRow("Reorder By:", self.reorder_input) layout.addRow("Trajectory Format:", self.traj_format_input) self.run_button = QPushButton("Run") self.run_button.clicked.connect(self.run_analysis) layout.addWidget(self.run_button) self.setLayout(layout)
[docs] def validate_inputs(self): """ Validates the inputs provided by the user for trajectory saving. Returns: A list of errors if any input is invalid, otherwise an empty list. """ errors = [] if not self.analysis_range_input.text(): errors.append("Analysis Range cannot be empty.") if not self.analysis_range_name_input.text(): errors.append("Analysis Range Name cannot be empty.") if not self.reorder_input.text(): errors.append("Reorder By cannot be empty.") if not self.traj_format_input.text(): errors.append("Trajectory Format cannot be empty.") return errors
[docs] def get_specific_options(self): """ Retrieves the specific options for trajectory saving. Returns: A dictionary containing the specific options for trajectory saving. """ return { "analysis_range": self.analysis_range_input.text(), "analysis_range_name": self.analysis_range_name_input.text(), "reorder": self.reorder_input.text(), "traj_format": self.traj_format_input.text() }
[docs] def run_specific_analysis(self, config): """ Runs the trajectory saving process with the given configuration. Args: config: The configuration options for trajectory saving. """ run_trajectory_saving(config)