class TimeJumpDialog(QDialog): def __init__(self, parent: QWidget) -> None: super().__init__(parent) self.setWindowTitle("Select time...") self._label = QLabel("", self) self._time_edit = TimeEdit(self) self._radio_rel = QRadioButton("Relative", self) self._radio_abs = QRadioButton("Absolute", self) if relative_checked: self._radio_rel.setChecked(True) else: self._radio_abs.setChecked(True) strip = QDialogButtonBox(self) strip.addButton(strip.Ok) strip.addButton(strip.Cancel) if not show_radio: self._radio_abs.setHidden(True) self._radio_rel.setHidden(True) strip.accepted.connect(self.accept) strip.rejected.connect(self.reject) self._radio_rel.clicked.connect(self._on_radio_click) self._radio_abs.clicked.connect(self._on_radio_click) layout = QVBoxLayout(self) layout.addWidget(self._label) layout.addWidget(self._time_edit) layout.addWidget(self._radio_rel) layout.addWidget(self._radio_abs) layout.addWidget(strip) self._on_radio_click() self._time_edit.set_value(value) def _on_radio_click(self) -> None: is_relative = self._radio_rel.isChecked() if is_relative: self._label.setText(relative_label) else: self._label.setText(absolute_label) self._time_edit.set_allow_negative(is_relative) def value(self) -> tuple[int, bool]: return (self._time_edit.get_value(), self._radio_rel.isChecked())
class App(QMainWindow): def __init__(self, multithread: bool): super().__init__() self.title = "ProteoSushi" self.left = 10 self.top = 10 self.width = 640 self.height = 480 self.multithread = multithread self.centralWidget = QWidget() self.setCentralWidget(self.centralWidget) self.setWindowTitle("ProteoSushi") self.setWindowIcon(QIcon("ProteoSushi_icon.png")) self.species_id_dict = self.__make_species_dict() self.species_name_dict = { v: k for k, v in self.species_id_dict.items() } self.threadpool = QThreadPool() self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.file_chooser_label = QLabel("Types of ProteoSushi Files", self) self.maxquant_RB = QRadioButton("MaxQuant", self) self.maxquant_RB.toggled.connect(self.check_MQ_RB) self.maxquant_RB.setToolTip( "Use the txt output folder from the MaxQuant Search Engine") self.maxquant_button = QPushButton("MaxQuant txt Folder") self.maxquant_button.setHidden(True) self.maxquant_button.clicked.connect(self.onClickMQbutton) self.maxquant_filepath = QLabel("[Filepath]", self) self.maxquant_filepath.setHidden(True) self.mascot_RB = QRadioButton("Mascot", self) self.mascot_RB.toggled.connect(self.check_mascot_RB) self.mascot_RB.setToolTip( "Use the output from the Mascot Search Engine") self.mascot_button = QPushButton("Mascot File") self.mascot_button.setHidden(True) self.mascot_button.clicked.connect(self.onClickMascotButton) self.mascot_filepath = QLabel("[Filepath]", self) self.mascot_filepath.setHidden(True) self.generic_RB = QRadioButton("Generic", self) self.generic_RB.toggled.connect(self.check_generic_RB) self.generic_RB.setToolTip( "Use the output from any other search engine") self.generic_button = QPushButton("Generic File") self.generic_button.setHidden(True) self.generic_button.clicked.connect(self.onClickGenericButton) self.generic_filepath = QLabel("[Filepath]", self) self.generic_filepath.setHidden(True) self.search_engine_group = QButtonGroup() self.search_engine_group.addButton(self.maxquant_RB) self.search_engine_group.addButton(self.mascot_RB) self.search_engine_group.addButton(self.generic_RB) self.PTM_CBs = list() self.proteome_filepath_button = QPushButton("Uniprot Proteome FASTA") self.proteome_filepath_button.clicked.connect( self.on_click_proteome_button) self.proteome_filepath = QLabel("[Filepath]", self) self.output_filepath_button = QPushButton("Output Name and Location") self.output_filepath_button.clicked.connect( self.on_click_output_button) self.output_filepath = QLabel("[Filepath]", self) self.options_label = QLabel("Options", self) self.localization_checkbox = QCheckBox("Localization Threshold", self) self.localization_checkbox.stateChanged.connect( self.check_localization_checkbox) self.localization_checkbox.setHidden(True) self.localization_checkbox.setToolTip( "Set a threshold for the localization probability for all PTM sites" ) self.localization_edit = QLineEdit(self) self.localization_edit.setHidden(True) self.localization_edit.setToolTip( "Set a threshold for the localization probability for all PTM sites" ) self.target_checkbox = QCheckBox("Use Target Genes", self) self.target_checkbox.stateChanged.connect(self.checkTargetBox) self.target_checkbox.setToolTip( "Use a list of genes that will be prioritized given multiple matches" ) self.target_button = QPushButton("Target Gene List") self.target_button.setHidden(True) self.target_button.clicked.connect(self.onClickTargetButton) self.target_filepath = QLabel("[Filepath]", self) self.target_filepath.setHidden(True) self.quant_CB = QCheckBox("Use Quantitation Values", self) self.quant_CB.stateChanged.connect(self.check_quant_CB) self.quant_CB.setToolTip( "Whether to use the Intensity/Quantitation values from the PSMs") self.sum_RB = QRadioButton("Sum Peaks", self) self.sum_RB.setHidden(True) self.average_RB = QRadioButton("Average Peaks", self) self.average_RB.setHidden(True) self.quant_method_group = QButtonGroup() self.quant_method_group.addButton(self.sum_RB) self.quant_method_group.addButton(self.average_RB) self.uniprot_annot_CB = QCheckBox("Annotate with Uniprot", self) self.uniprot_annot_CB.setToolTip( "Whether to add in annotation from Uniprot, like subcellular location, secondary structure, etc." ) self.species_id_label = QLabel("Species ID", self) self.species_id_label.setToolTip( "The species ID for the species from the data provided (e.g. 9606)" ) self.species_id_edit = QLineEdit(self) self.species_id_edit.editingFinished.connect(self.update_species_name) self.species_id_edit.setToolTip( "The species ID for the species from the data provided (e.g. 9606)" ) self.species_name_label = QLabel("", self) self.max_missed_label = QLabel("Max Missed Cleavages", self) self.max_missed_label.setToolTip( "The maximum allowed missed cleavages for a given peptide") self.max_missed_edit = QLineEdit(self) self.max_missed_edit.setToolTip( "The maximum allowed missed cleavages for a given peptide") self.fdr_CB = QCheckBox("FDR Threshold", self) self.fdr_CB.stateChanged.connect(self.check_fdr_CB) self.fdr_CB.setToolTip( "[OPTIONAL] The threshold for pep_expect column for Mascot or PEP column for Maxquant.\nMust be between 0 and 1, but can be left blank." ) self.fdr_edit = QLineEdit(self) # TODO: Error check for a number self.fdr_edit.setHidden(True) self.fdr_edit.setToolTip( "[OPTIONAL] The threshold for pep_expect column for Mascot or PEP column for Maxquant.\nMust be between 0 and 1, but can be left blank." ) self.protease_label = QLabel("Protease used in sample digestion", self) self.protease_label.setToolTip( "The protease used to digest the sample\nExamples include: trypsin/p, trypsin!p, lys-c, asp-n, asp-ne, lys-n" ) self.protease_combo_box = QComboBox() self.protease_combo_box.setToolTip( "The protease used to digest the sample\nExamples include: trypsin/p, trypsin!p, lys-c, asp-n, asp-ne, lys-n" ) self.protease_combo_box.addItems( ["trypsin/p", "trypsin!p", "lys-c", "asp-n", "asp-ne", "lys-n"]) self.protease_combo_box.setEditable(True) self.run_button = QPushButton("Rollup!") self.run_button.setHidden(False) self.run_button.clicked.connect(self.onClickRunButton) self.createGridLayout() windowLayout = QVBoxLayout(self.centralWidget) windowLayout.addWidget(self.horizontalGroupBox) windowLayout.setAlignment(Qt.AlignTop) self.setLayout(windowLayout) self.show() def check_MQ_RB(self, state): if self.maxquant_RB.isChecked(): self.maxquant_filepath.setHidden(False) self.maxquant_button.setHidden(False) self.localization_checkbox.setHidden(False) if self.localization_checkbox.isChecked(): self.localization_edit.setHidden(False) else: self.maxquant_filepath.setHidden(True) self.maxquant_button.setHidden(True) self.localization_checkbox.setHidden(True) self.localization_edit.setHidden(True) def check_mascot_RB(self, state): if self.mascot_RB.isChecked(): self.mascot_filepath.setHidden(False) self.mascot_button.setHidden(False) else: self.mascot_filepath.setHidden(True) self.mascot_button.setHidden(True) def check_generic_RB(self, state): if self.generic_RB.isChecked(): self.generic_filepath.setHidden(False) self.generic_button.setHidden(False) self.localization_checkbox.setHidden(False) if self.localization_checkbox.isChecked(): self.localization_edit.setHidden(False) else: self.generic_filepath.setHidden(True) self.generic_button.setHidden(True) self.localization_checkbox.setHidden(True) self.localization_edit.setHidden(True) def checkTargetBox(self, state): if state == QtCore.Qt.Checked: self.target_filepath.setHidden(False) self.target_button.setHidden(False) else: self.target_filepath.setHidden(True) self.target_button.setHidden(True) def check_quant_CB(self, state): if state == QtCore.Qt.Checked: self.average_RB.setHidden(False) self.sum_RB.setHidden(False) else: self.average_RB.setHidden(True) self.sum_RB.setHidden(True) def check_fdr_CB(self, state): if state == QtCore.Qt.Checked: self.fdr_edit.setHidden(False) else: self.fdr_edit.setHidden(True) def check_localization_checkbox(self, state): if state == QtCore.Qt.Checked and (self.maxquant_RB.isChecked() or self.generic_RB.isChecked()): self.localization_edit.setHidden(False) else: self.localization_edit.setHidden(True) def update_species_name(self): if not self.species_id_edit.text(): self.species_name_label.setStyleSheet("background-color : white") self.species_name_label.setText("") elif self.species_id_edit.text() in self.species_id_dict: self.species_name_label.setStyleSheet("background-color : white") self.species_name_label.setText( self.species_id_dict[self.species_id_edit.text()][2:]) elif f"N={self.species_id_edit.text()}" in self.species_name_dict: self.species_name_label.setStyleSheet("background-color : white") self.species_name_label.setText(self.species_id_edit.text()) self.species_id_edit.setText( self.species_name_dict[f"N={self.species_id_edit.text()}"]) else: self.species_name_label.setText("Not a valid species ID") self.species_name_label.setStyleSheet("background-color : red") def checkRunButton(self, MQcheckState, MQfilepath: str, mascotCheckState, mascot_filepath: str, genericCheckState, generic_filepath: str): """Checks to see whether the run button is available. must have a search engine output and missed cleavages, etc Arguments: MQcheckState {QtCore.Qt.Checked} -- whether the box is checked MQfilepath {str} -- the filepath for maxquant output mascotCheckState {QtCore.Qt.Checked} -- whether the box is checked mascot_filepath {str} -- the filepath for maxquant output genericCheckState {QtCore.Qt.Checked} -- whether the box is checked generic_filepath {str} -- the filepath for maxquant output """ # TODO: update to require species, etc. if (((MQcheckState == QtCore.Qt.Checked and os.path.exists(MQfilepath)) or (mascotCheckState == QtCore.Qt.Checked and os.path.exists(mascot_filepath)) or (genericCheckState == QtCore.Qt.Checked and os.path.exists(generic_filepath))) and os.path.exists(self.proteome_filepath.text())): self.run_button.setHidden(False) else: self.run_button.setHidden(True) def __make_species_dict(self): species_id_dict = dict() with pkg_resources.open_text(lib, "spec_list_fixed.tsv") as spec_list: #with pkg_resources.open_text(__package__, "spec_list_fixed.tsv") as spec_list: for line in spec_list: split_line = line.strip().split('\t') species_id_dict[split_line[0]] = split_line[1] return species_id_dict def openCSVFileNameDialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getOpenFileName( self, "Choose the Search Engine Output", os.getcwd(), "CSV Files (*.csv);;All Files (*)", options=options) return fileName def openTXTFileNameDialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getOpenFileName( self, "Choose the Gene Name File", os.getcwd(), "TXT Files (*.txt);;All Files (*)", options=options) return fileName def open_directory_dialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog dir_name = str( QFileDialog.getExistingDirectory(self, "Select Directory", os.getcwd(), options=options)) return dir_name def openFASTAFileNameDialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getOpenFileName( self, "Choose the Proteome FASTA file", os.getcwd(), "FASTA Files (*.fasta);;All Files (*)", options=options) return fileName def write_output_dialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getSaveFileName( self, "Choose the name and location of the output", os.getcwd(), "CSV Files (*.csv);;All Files (*)", options=options) return fileName def convert_PTM_list(self, PTM_CBs: list) -> list: """changes the list of checkboxes into chosen ptms for analysis Arguments: PTM_CBs {list} -- a list of QCheckBoxes for each possible PTM Returns: list -- a list of strings of PTM names for analysis """ PTM_str_list = list() for ptm_cb in PTM_CBs: if ptm_cb.isChecked(): PTM_str_list.append(ptm_cb.text()) return PTM_str_list @pyqtSlot() def onClickMQbutton(self): self.statusBar().showMessage("Choose the MaxQuant output FOLDER") self.statusBar().setStyleSheet("background-color : white") filename = self.open_directory_dialog() #if self.maxquant_filepath.text() != "": if os.path.exists(filename): self.maxquant_filepath.setText(filename) missed_cleavages, protease, PTMs = parse_output( "maxquant", filename) self.max_missed_edit.setText(str(missed_cleavages)) self.protease_combo_box.setEditText(protease) #Remove the previous PTM checkboxes (if there were any) if self.PTM_CBs != []: for cb in self.PTM_CBs: self.layout.removeWidget(cb) cb.deleteLater() # Adds the new checkboxes to the list self.PTM_CBs = [] for ptm in PTMs: self.PTM_CBs.append(QCheckBox(ptm, self)) # Removes the label i = 0 if not self.layout.itemAtPosition(5, i) is None: label_to_remove = self.layout.itemAtPosition(5, i).widget() self.layout.removeWidget(label_to_remove) label_to_remove.deleteLater() # Inserts the new PTM checkboxes self.layout.addWidget(QLabel("PTMs for Current Analysis"), 5, i) for widget in self.PTM_CBs: self.layout.addWidget(widget, 6, i) i += 1 self.statusBar().showMessage("") @pyqtSlot() def onClickMascotButton(self): self.statusBar().showMessage("Choose the Mascot output file") self.statusBar().setStyleSheet("background-color : white") filename = self.openCSVFileNameDialog() #if self.mascot_filepath.text() != "": if os.path.exists(filename): self.mascot_filepath.setText(filename) missed_cleavages, protease, PTMs = parse_output("mascot", filename) if missed_cleavages == -5: self.statusBar().showMessage("Invalid Mascot file") self.statusBar().setStyleSheet("background-color : red") self.mascot_filepath.setText("") return self.max_missed_edit.setText(str(missed_cleavages)) self.protease_combo_box.setEditText(protease) #Remove the previous PTM checkboxes (if there were any) if self.PTM_CBs != []: for cb in self.PTM_CBs: self.layout.removeWidget(cb) cb.deleteLater() # Adds the new checkboxes to the list self.PTM_CBs = [] for ptm in PTMs: self.PTM_CBs.append(QCheckBox(ptm, self)) # Removes the label i = 0 if not self.layout.itemAtPosition(5, i) is None: label_to_remove = self.layout.itemAtPosition(5, i).widget() self.layout.removeWidget(label_to_remove) label_to_remove.deleteLater() # Inserts the new PTM checkboxes self.layout.addWidget(QLabel("PTMs for Current Analysis"), 5, i) for widget in self.PTM_CBs: self.layout.addWidget(widget, 6, i) i += 1 self.statusBar().showMessage("") @pyqtSlot() def onClickGenericButton(self): self.statusBar().showMessage("Choose the Search Engine output") self.statusBar().setStyleSheet("background-color : white") filename = self.openCSVFileNameDialog() #if self.generic_filepath.text() != "": if os.path.exists(filename): self.generic_filepath.setText(filename) missed_cleavages, protease, PTMs = parse_output( "generic", filename) if missed_cleavages == -3: self.statusBar().showMessage( "A sequence in the Peptide Modified Sequence column is missing PTMs" ) self.statusBar().setStyleSheet("background-color : red") return if missed_cleavages == -4: self.statusBar().showMessage("Invalid file") self.statusBar().setStyleSheet("background-color : red") self.generic_filepath.setText("") return #Remove the previous PTM checkboxes (if there were any) if self.PTM_CBs != []: for cb in self.PTM_CBs: self.layout.removeWidget(cb) cb.deleteLater() # Adds the new checkboxes to the list self.PTM_CBs = [] for ptm in PTMs: self.PTM_CBs.append(QCheckBox(ptm, self)) # Removes the label# This is where the MQ localization probability threshold will be i = 0 if not self.layout.itemAtPosition(5, i) is None: label_to_remove = self.layout.itemAtPosition(5, i).widget() self.layout.removeWidget(label_to_remove) label_to_remove.deleteLater() # Inserts the new PTM checkboxes self.layout.addWidget(QLabel("PTMs for Current Analysis"), 5, i) for widget in self.PTM_CBs: self.layout.addWidget(widget, 6, i) i += 1 self.statusBar().showMessage("") @pyqtSlot() def on_click_proteome_button(self): self.statusBar().showMessage("Choose the Uniprot Proteome FASTA file") self.statusBar().setStyleSheet("background-color : white") filename = self.openFASTAFileNameDialog() if os.path.exists(filename): # Parse the file to check it is valid and grab the species was_error, species_ID = parse_proteome(filename) if was_error: self.statusBar().showMessage( "ERROR: Invalid proteome FASTA file!") self.statusBar().setStyleSheet("background-color : red") else: self.proteome_filepath.setText(filename) if not species_ID is None: self.species_id_edit.setText(species_ID) self.update_species_name() self.statusBar().showMessage("") @pyqtSlot() def on_click_output_button(self): self.statusBar().showMessage( "Choose the ProteoSushi output file location") self.statusBar().setStyleSheet("background-color : white") filename = self.write_output_dialog() self.output_filepath.setText(filename) self.statusBar().showMessage("") @pyqtSlot() def onClickTargetButton(self): self.statusBar().showMessage("Choose the Target Gene file") self.statusBar().setStyleSheet("background-color : white") filename = self.openTXTFileNameDialog() if self.target_filepath.text() != "": self.target_filepath.setText(filename) self.statusBar().showMessage("") def __run_single_thread(self, fdr: float, combine_method, species_id, localization: float): """Runs the background on the same thread if there are problems""" # If the maxquant option was chosen, it sends that info to be run if self.maxquant_RB.isChecked() and os.path.exists( self.maxquant_filepath.text()): start_time = time() self.statusBar().showMessage("Analysis in Progress") self.statusBar().setStyleSheet("background-color : white") output = rollup( "maxquant", self.maxquant_filepath.text(), self.target_checkbox.isChecked( ), # Whether target will be used self.target_filepath.text(), int(self.max_missed_edit.text()), self.protease_combo_box.currentText(), fdr, self.quant_CB.isChecked(), self.convert_PTM_list(self.PTM_CBs), self.proteome_filepath.text(), combine_method, self.uniprot_annot_CB.isChecked(), species_id, self.output_filepath.text(), localization) # If there is a 502 proxy error (server side error) if self.uniprot_annot_CB.isChecked() and output == 502: self.statusBar().showMessage( "ERROR: Uniprot server error! Please try again later.") self.statusBar().setStyleSheet("background-color : red") return self.statusBar().showMessage("Analysis Complete!") self.statusBar().setStyleSheet("background-color : green") print("\033[92m {}\033[00m".format("\nAnalysis Complete!")) print("\033[96m {}\033[00m".format( f"Rollup took {time() - start_time} seconds")) #sys.exit() elif self.mascot_RB.isChecked() and os.path.exists( self.mascot_filepath.text()): start_time = time() self.statusBar().showMessage("Analysis in Progress") self.statusBar().setStyleSheet("background-color : white") output = rollup( "mascot", self.mascot_filepath.text(), self.target_checkbox.isChecked( ), # Whether target will be used self.target_filepath.text(), int(self.max_missed_edit.text()), self.protease_combo_box.currentText(), fdr, self.quant_CB.isChecked(), self.convert_PTM_list(self.PTM_CBs), self.proteome_filepath.text(), combine_method, self.uniprot_annot_CB.isChecked(), species_id, self.output_filepath.text(), None) # If the mascot file has no intensity values and user tried to analyze them if self.quant_CB.isChecked() and output == 2: self.statusBar().showMessage( "ERROR: Mascot file has no detectable intensity values!") self.statusBar().setStyleSheet("background-color : red") return # If there is a 502 proxy error (server side error) if self.uniprot_annot_CB.isChecked() and output == 502: self.statusBar().showMessage( "ERROR: Uniprot server error! Please try again later.") self.statusBar().setStyleSheet("background-color : red") return self.statusBar().showMessage("Analysis Complete!") self.statusBar().setStyleSheet("background-color : green") print("\033[92m {}\033[00m".format("\nAnalysis Complete!")) print("\033[96m {}\033[00m".format( f"Rollup took {time() - start_time} seconds")) #sys.exit() elif self.generic_RB.isChecked() and os.path.exists( self.generic_filepath.text()): start_time = time() self.statusBar().showMessage("Analysis in Progress") self.statusBar().setStyleSheet("background-color : white") output = rollup( "generic", self.generic_filepath.text(), self.target_checkbox.isChecked( ), # Whether target will be used self.target_filepath.text(), int(self.max_missed_edit.text()), self.protease_combo_box.currentText(), fdr, self.quant_CB.isChecked(), self.convert_PTM_list(self.PTM_CBs), self.proteome_filepath.text(), combine_method, self.uniprot_annot_CB.isChecked(), species_id, self.output_filepath.text(), localization) # If there is a 502 proxy error (server side error) if self.uniprot_annot_CB.isChecked() and (output == 502 or output == 4): self.statusBar().showMessage( "ERROR: Uniprot server error! Please try again later.") self.statusBar().setStyleSheet("background-color : red") return # If the generic file has no intensity values and user tried to analyze them if self.quant_CB.isChecked() and output == 2: self.statusBar().showMessage( "ERROR: Generic file has no detectable intensity values!") self.statusBar().setStyleSheet("background-color : red") return self.statusBar().showMessage("Analysis Complete!") self.statusBar().setStyleSheet("background-color : green") print("\033[92m {}\033[00m".format("\nAnalysis Complete!")) print("\033[96m {}\033[00m".format( f"Rollup took {time() - start_time} seconds")) #sys.exit() else: self.statusBar().showMessage( "ERROR: Missing Search Engine Output!") self.statusBar().setStyleSheet("background-color : red") def __run_async(self, fdr: float, combine_method, species_id, localization: float): """Function that runs the backend on a separate thread""" # If the maxquant option was chosen, it sends that info to be run if self.maxquant_RB.isChecked() and os.path.exists( self.maxquant_filepath.text()): start_time = time() self.statusBar().showMessage("Analysis in Progress") self.statusBar().setStyleSheet("background-color : white") output = rollup( "maxquant", self.maxquant_filepath.text(), self.target_checkbox.isChecked( ), # Whether target will be used self.target_filepath.text(), int(self.max_missed_edit.text()), self.protease_combo_box.currentText(), fdr, self.quant_CB.isChecked(), self.convert_PTM_list(self.PTM_CBs), self.proteome_filepath.text(), combine_method, self.uniprot_annot_CB.isChecked(), species_id, self.output_filepath.text(), localization) # If there is a 502 proxy error (server side error) if self.uniprot_annot_CB.isChecked() and output == 502: self.statusBar().showMessage( "ERROR: Uniprot server error! Please try again later.") self.statusBar().setStyleSheet("background-color : red") return self.statusBar().showMessage("Analysis Complete!") self.statusBar().setStyleSheet("background-color : green") print("\033[92m {}\033[00m".format("\nAnalysis Complete!")) print("\033[96m {}\033[00m".format( f"Rollup took {time() - start_time} seconds")) #sys.exit() elif self.mascot_RB.isChecked() and os.path.exists( self.mascot_filepath.text()): start_time = time() self.statusBar().showMessage("Analysis in Progress") self.statusBar().setStyleSheet("background-color : white") output = rollup( "mascot", self.mascot_filepath.text(), self.target_checkbox.isChecked( ), # Whether target will be used self.target_filepath.text(), int(self.max_missed_edit.text()), self.protease_combo_box.currentText(), fdr, self.quant_CB.isChecked(), self.convert_PTM_list(self.PTM_CBs), self.proteome_filepath.text(), combine_method, self.uniprot_annot_CB.isChecked(), species_id, self.output_filepath.text(), None) # If the mascot file has no intensity values and user tried to analyze them if self.quant_CB.isChecked() and output == 2: self.statusBar().showMessage( "ERROR: Mascot file has no detectable intensity values!") self.statusBar().setStyleSheet("background-color : red") return # If there is a 502 proxy error (server side error) if self.uniprot_annot_CB.isChecked() and output == 502: self.statusBar().showMessage( "ERROR: Uniprot server error! Please try again later.") self.statusBar().setStyleSheet("background-color : red") return self.statusBar().showMessage("Analysis Complete!") self.statusBar().setStyleSheet("background-color : green") print("\033[92m {}\033[00m".format("\nAnalysis Complete!")) print("\033[96m {}\033[00m".format( f"Rollup took {time() - start_time} seconds")) #sys.exit() elif self.generic_RB.isChecked() and os.path.exists( self.generic_filepath.text()): start_time = time() self.statusBar().showMessage("Analysis in Progress") self.statusBar().setStyleSheet("background-color : white") output = rollup( "generic", self.generic_filepath.text(), self.target_checkbox.isChecked( ), # Whether target will be used self.target_filepath.text(), int(self.max_missed_edit.text()), self.protease_combo_box.currentText(), fdr, self.quant_CB.isChecked(), self.convert_PTM_list(self.PTM_CBs), self.proteome_filepath.text(), combine_method, self.uniprot_annot_CB.isChecked(), species_id, self.output_filepath.text(), localization) # If there is a 502 proxy error (server side error) if self.uniprot_annot_CB.isChecked() and (output == 502 or output == 4): self.statusBar().showMessage( "ERROR: Uniprot server error! Please try again later.") self.statusBar().setStyleSheet("background-color : red") return # If the generic file has no intensity values and user tried to analyze them if self.quant_CB.isChecked() and output == 2: self.statusBar().showMessage( "ERROR: Generic file has no detectable intensity values!") self.statusBar().setStyleSheet("background-color : red") return self.statusBar().showMessage("Analysis Complete!") self.statusBar().setStyleSheet("background-color : green") print("\033[92m {}\033[00m".format("\nAnalysis Complete!")) print("\033[96m {}\033[00m".format( f"Rollup took {time() - start_time} seconds")) #sys.exit() else: self.statusBar().showMessage( "ERROR: Missing Search Engine Output!") self.statusBar().setStyleSheet("background-color : red") #sys.exit() def __cut_thread(self): """Called once the backend thread finishes""" # Enables the buttons again in case I get multiple analyses working self.run_button.setEnabled(True) self.proteome_filepath_button.setEnabled(True) self.output_filepath_button.setEnabled(True) self.maxquant_button.setEnabled(True) self.mascot_button.setEnabled(True) self.generic_button.setEnabled(True) #sys.exit() return @pyqtSlot() def onClickRunButton(self): if ((self.maxquant_RB.isChecked() and os.path.exists(self.maxquant_filepath.text())) or (self.mascot_RB.isChecked() and os.path.exists(self.mascot_filepath.text())) or (self.generic_RB.isChecked() and os.path.exists(self.generic_filepath.text()))): if not os.path.exists(self.proteome_filepath.text()): self.statusBar().showMessage( "ERROR: Missing Proteome FASTA file!") self.statusBar().setStyleSheet("background-color : red") return # If the user didn't choose a name or location for the file if self.output_filepath.text() == "": self.statusBar().showMessage( "ERROR: ProteoSushi output name and location not chosen!") self.statusBar().setStyleSheet("background-color : red") return if os.path.isdir(self.output_filepath.text()): self.output_filepath.setText( os.path.join(self.output_filepath.text(), "proteosushi_output.csv")) if self.output_filepath.text() == "[Filepath]": self.output_filepath.setText( os.path.join(os.getcwd(), "proteosushi_output.csv")) if self.output_filepath.text()[-4:] != ".csv": self.output_filepath.setText(self.output_filepath.text() + ".csv") try: # Check to see if the provided number of allowed missed cleavages is legal max_missed = int(self.max_missed_edit.text()) if max_missed < 0: self.statusBar().showMessage( "ERROR: Number of max allowed missed cleavages cannot be less than 0!" ) self.statusBar().setStyleSheet("background-color : red") return except ValueError: self.statusBar().showMessage( "ERROR: Invalid number for max allowed missed cleavages!") self.statusBar().setStyleSheet("background-color : red") return # Checks that the localization prob threshold is between 0 and 1 try: if self.localization_edit.text() != "": localization = float(self.localization_edit.text()) if localization < 0 or localization > 1: self.statusBar().showMessage( "ERROR: Localization threshold must be between 0 and 1!" ) self.statusBar().setStyleSheet( "background-color : red") return else: localization = None except ValueError: self.statusBar().showMessage( "ERROR: Localization threshold must be a number between 0 and 1!" ) self.statusBar().setStyleSheet("background-color : red") return try: # Checks to see if the provided FDR threshold is legal if self.fdr_edit.text() != "": fdr = float(self.fdr_edit.text()) if fdr < 0 or fdr > 1: self.statusBar().showMessage( "ERROR: FDR must be between 0 and 1!") self.statusBar().setStyleSheet( "background-color : red") return else: fdr = None except ValueError: self.statusBar().showMessage("ERROR: FDR must be a number") self.statusBar().setStyleSheet("background-color : red") return # If quantitation will be used, but the user has not selected a method to combine if self.quant_CB.isChecked() and not self.sum_RB.isChecked( ) and not self.average_RB.isChecked(): self.statusBar().showMessage( "ERROR: Must choose either Sum or Average for the quant method!" ) self.statusBar().setStyleSheet("background-color : red") return elif self.quant_CB.isChecked() and self.sum_RB.isChecked(): combine_method = "sum" elif self.quant_CB.isChecked() and self.average_RB.isChecked(): combine_method = "average" else: combine_method = "" # If a valid species ID has been entered in species_id = self.species_id_edit.text() if len(species_id) > 0 and not species_id in self.species_id_dict: self.statusBar().showMessage("ERROR: Not a valid species ID!") self.statusBar().setStyleSheet("background-color : red") return if self.protease_combo_box.currentText() in cleave_rules: # Disable all buttons before running to keep analysis confined self.run_button.setEnabled(False) self.proteome_filepath_button.setEnabled(False) self.output_filepath_button.setEnabled(False) self.maxquant_button.setEnabled(False) self.mascot_button.setEnabled(False) self.generic_button.setEnabled(False) if not self.multithread: self.__run_single_thread(fdr, combine_method, species_id, localization) else: # Starts a new thread for the backend analysis; causes seg fault currently print("\033[96m {}\033[00m".format("\nAnalysis Started!")) worker = Worker(self.__run_async, fdr, combine_method, species_id, localization) worker.signals.end.connect(self.__cut_thread) self.threadpool.start(worker) else: self.statusBar().showMessage( "ERROR: Protease provided is not supported!") self.statusBar().setStyleSheet("background-color : red") else: self.statusBar().showMessage( "ERROR: Missing Search Engine Output!") self.statusBar().setStyleSheet("background-color : red") def createGridLayout(self): self.horizontalGroupBox = QGroupBox("") self.layout = QGridLayout() self.layout.setRowStretch(0, 1) self.layout.setRowStretch(9, 1) row = 1 self.layout.addWidget(self.file_chooser_label, row, 0) row += 1 self.layout.addWidget(self.maxquant_RB, row, 0) self.layout.addWidget(self.maxquant_button, row, 1) self.layout.addWidget(self.maxquant_filepath, row, 2) row += 1 self.layout.addWidget(self.mascot_RB, row, 0) self.layout.addWidget(self.mascot_button, row, 1) self.layout.addWidget(self.mascot_filepath, row, 2) row += 1 self.layout.addWidget(self.generic_RB, row, 0) self.layout.addWidget(self.generic_button, row, 1) self.layout.addWidget(self.generic_filepath, row, 2) row += 1 # This is where the PTM label goes row += 1 # This is where the PTM checkboxes go row += 1 self.layout.addWidget(QLabel("Proteome File", self), row, 0) self.layout.addWidget(self.proteome_filepath_button, row, 1) self.layout.addWidget(self.proteome_filepath, row, 2) row += 1 self.layout.addWidget(self.species_id_label, row, 0) self.layout.addWidget(self.species_id_edit, row, 1) self.layout.addWidget(self.species_name_label, row, 2) row += 1 self.layout.addWidget(self.max_missed_label, row, 0) self.layout.addWidget(self.max_missed_edit, row, 1) row += 1 self.layout.addWidget(self.protease_label, row, 0) self.layout.addWidget(self.protease_combo_box, row, 1) row += 1 self.layout.addWidget(QLabel("Output Name & Location", self), row, 0) self.layout.addWidget(self.output_filepath_button, row, 1) self.layout.addWidget(self.output_filepath, row, 2) row += 1 self.layout.addWidget(self.options_label, row, 0) row += 1 self.layout.addWidget(self.localization_checkbox, row, 0) self.layout.addWidget(self.localization_edit, row, 1) row += 1 self.layout.addWidget(self.target_checkbox, row, 0) self.layout.addWidget(self.target_button, row, 1) self.layout.addWidget(self.target_filepath, row, 2) row += 1 self.layout.addWidget(self.quant_CB, row, 0) self.layout.addWidget(self.sum_RB, row, 1) self.layout.addWidget(self.average_RB, row, 2) row += 1 self.layout.addWidget(self.uniprot_annot_CB, row, 0) row += 1 self.layout.addWidget(self.fdr_CB, row, 0) self.layout.addWidget(self.fdr_edit, row, 1) row += 1 self.layout.addWidget(self.run_button, row, 0) self.layout.setAlignment(Qt.AlignTop) self.horizontalGroupBox.setAlignment(Qt.AlignTop) self.horizontalGroupBox.setLayout(self.layout)