Source code for schrodinger.ui.sequencealignment.analyze_gui

import os

from schrodinger.Qt import QtCore
from schrodinger.Qt import QtWidgets

from .maestro import maestroCalculateMinimumDistance
from .maestro import maestroGetLigandAnnotations

ANALYZE_DIALOG = None


[docs]def analyzeBindingSiteDialog(parent=None): global ANALYZE_DIALOG if ANALYZE_DIALOG is None: ANALYZE_DIALOG = AnalyzeBindingSiteDialog(parent) return ANALYZE_DIALOG
[docs]class QTableWidgetNumericalItem(QtWidgets.QTableWidgetItem): """ QTableWidget for numerical items. """
[docs] def __init__(self): QtWidgets.QTableWidgetItem.__init__(self)
[docs] def data(self, role): if role == QtCore.Qt.DisplayRole: try: value = float(QtWidgets.QTableWidgetItem.data(self, role)) return "%g" % value except ValueError: return QtWidgets.QTableWidgetItem.data(self, role)
def __lt__(self, other): value1 = float( QtWidgets.QTableWidgetItem.data(self, QtCore.Qt.DisplayRole)) value2 = float( QtWidgets.QTableWidgetItem.data(other, QtCore.Qt.DisplayRole)) if value1 < value2: return True else: return False
[docs]class AnalyzeBindingSiteDialog(QtWidgets.QDialog): """ This class implements simple pattern editor dialog. """
[docs] def __init__(self, parent): # Initialize base class. QtWidgets.QDialog.__init__(self, parent) self.setWindowTitle("Analyze Binding Site") self.okButton = QtWidgets.QPushButton("Analyze") self.cancelButton = QtWidgets.QPushButton("Close") self.exportButton = QtWidgets.QPushButton( "Export results to CSV file...") self.viewer = parent self.group = None self.current_sequence = None self.spacer = QtWidgets.QWidget() self.spacer.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) self.min_range = QtWidgets.QSpinBox() self.min_range.setValue(3) self.min_range.setMinimum(0) self.min_range.setMaximum(100) self.min_range.setEnabled(True) self.min_label = QtWidgets.QLabel("Scan from:") self.min_label.setEnabled(True) self.max_range = QtWidgets.QSpinBox() self.max_range.setValue(10) self.max_range.setMinimum(0) self.max_range.setMaximum(100) self.max_range.setEnabled(True) self.max_label = QtWidgets.QLabel("to:") self.max_label.setEnabled(True) self.angstroms_label = QtWidgets.QLabel("angstroms") self.settingsLayout = QtWidgets.QHBoxLayout() self.settingsLayout.addWidget(self.min_label) self.settingsLayout.addWidget(self.min_range) self.settingsLayout.addWidget(self.max_label) self.settingsLayout.addWidget(self.max_range) self.settingsLayout.addWidget(self.angstroms_label) self.settingsLayout.addStretch() self.buttonLayout = QtWidgets.QHBoxLayout() self.buttonLayout.addWidget(self.exportButton) self.buttonLayout.addWidget(self.spacer) self.buttonLayout.addWidget(self.okButton) self.lowButtonLayout = QtWidgets.QHBoxLayout() self.lowButtonLayout.addStretch() self.lowButtonLayout.addWidget(self.cancelButton) self.resultsTable = QtWidgets.QTableWidget(0, 4) self.table_font = self.resultsTable.font() self.table_font.setPointSize(10) self.resultsTable.setFont(self.table_font) self.tableHeader = [ "Distance (angstroms)", "Identity %", "Similarity %", "Homology %" ] self.resultsTable.setHorizontalHeaderLabels(self.tableHeader) self.resultsTable.resizeColumnsToContents() self.resultsTable.sortItems(1, QtCore.Qt.AscendingOrder) self.layout = QtWidgets.QVBoxLayout(self) self.ligand_selector_layout = QtWidgets.QHBoxLayout() self.layout.addLayout(self.ligand_selector_layout) self.seq_label = QtWidgets.QLabel("Sequence:") self.ligand_selector_layout.addWidget(self.seq_label) self.seq_choice = QtWidgets.QComboBox(self) self.seq_choice.currentIndexChanged.connect(self.seqCB) self.ligand_selector_layout.addWidget(self.seq_choice) self.lig_label = QtWidgets.QLabel("Ligand:") self.ligand_selector_layout.addWidget(self.lig_label) self.ligand_choice = QtWidgets.QComboBox(self) self.ligand_choice.currentIndexChanged.connect(self.ligCB) self.ligand_selector_layout.addWidget(self.ligand_choice) self.ligand_selector_layout.addStretch() self.tab_view = QtWidgets.QTabWidget(self) self.tab_view.currentChanged.connect(self.currentChangedCB) self.layout.addWidget(self.tab_view) self.layout.addLayout(self.lowButtonLayout) self.conservation_page = QtWidgets.QWidget() self.conservation_page_layout = QtWidgets.QVBoxLayout( self.conservation_page) self.conservation_page.setLayout(self.conservation_page_layout) self.value_layout = QtWidgets.QHBoxLayout() self.value_layout.addWidget( QtWidgets.QLabel( "Sequence identity threshold in selected columns:")) self.value_layout.addStretch() self.value_box = QtWidgets.QLineEdit("100 %") self.value_box.setReadOnly(True) self.value_box.setAlignment(QtCore.Qt.AlignRight) self.value_box.setMaxLength(5) self.value_box.setFixedWidth(60) self.value_layout.addWidget(self.value_box) self.conservation_page_layout.addLayout(self.value_layout) self.similarity_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal, self) self.similarity_slider.setTickInterval(5) self.similarity_slider.setMinimum(0) self.similarity_slider.setMaximum(100) self.similarity_slider.setValue(100) self.similarity_slider.setTickPosition(QtWidgets.QSlider.TicksBelow) self.similarity_slider.valueChanged.connect(self.similaritySliderCB) self.conservation_page_layout.addWidget(self.similarity_slider) self.range_layout = QtWidgets.QHBoxLayout() self.range_layout.addWidget(QtWidgets.QLabel("0 %")) self.range_layout.addStretch() self.range_layout.addWidget(QtWidgets.QLabel("100 %")) self.conservation_page_layout.addLayout(self.range_layout) self.conservation_page_layout.addStretch() self.cons_button_layout = QtWidgets.QHBoxLayout() self.button_remove_similar = QtWidgets.QPushButton( "Remove Similar Sequences") self.button_remove_similar.clicked.connect( self.removeSimilarSequencesCB) self.button_remove_divergent = QtWidgets.QPushButton( "Remove Divergent Sequences") self.button_remove_divergent.clicked.connect( self.removeDivergentSequencesCB) self.cons_button_layout.addWidget(self.button_remove_similar) self.cons_button_layout.addWidget(self.button_remove_divergent) self.conservation_page_layout.addLayout(self.cons_button_layout) self.distance_page = QtWidgets.QWidget() self.distance_page_layout = QtWidgets.QVBoxLayout(self.distance_page) self.distance_page.setLayout(self.distance_page_layout) self.tab_view.addTab(self.conservation_page, "Conservation Analysis") self.tab_view.addTab(self.distance_page, "Distance Analysis") self.distance_page_layout.addWidget(self.resultsTable) self.distance_page_layout.addSpacing(5) self.distance_page_layout.addLayout(self.settingsLayout) self.distance_page_layout.addSpacing(5) self.distance_page_layout.addLayout(self.buttonLayout) self.resultsTable.horizontalHeader().setStretchLastSection(True) self.resultsTable.setWordWrap(False) self.cancelButton.clicked.connect(self.cancelClicked) self.okButton.clicked.connect(self.okClicked) self.exportButton.clicked.connect(self.exportClicked) self.resize(self.resultsTable.width() + 15, self.resultsTable.height()) self.save_dialog = None self.sequence = None self.seq_list = [] self.ligand_list = [] self.similarity_value = 100
[docs] def currentChangedCB(self, index): """ Current tab changed callback. """ self.updateLigand()
[docs] def ligCB(self, index): """ Ligand combo box callback. """ if not self.ligand_list or index >= len(self.ligand_list): return if self.viewer and self.ligand_list: self.viewer.contents_changed = True self.viewer.updateView() self.group.unselectAll() self.viewer.selectLigandContacts(ligand=self.ligand_list[index]) self.group.expandSelection() self.similaritySliderCB(self.similarity_value)
[docs] def seqCB(self, index): """ Sequence combo box callback. """ if not self.seq_list or index >= len(self.seq_list): return self.current_sequence = self.seq_list[index] self.updateLigand() self.similaritySliderCB(self.similarity_value)
[docs] def removeSimilarSequencesCB(self): """ Removes selected sequences. This function assumes similar sequences are selected in the group, so it deletes the selected sequences in the group and updates the dialog. """ if not self.group or not self.group.reference: return self.group.reference.selected = False if self.viewer: self.viewer.deleteSelectedSequences() self.update()
[docs] def removeDivergentSequencesCB(self): """ Removes de-selected sequences. This function assumes similar sequences are selected in the group, so it inverts the selection, deletes the selected sequences in the group and updates the dialog. """ if not self.group or not self.group.reference: return self.group.invertSequenceSelection() self.group.reference.selected = False if self.viewer: self.viewer.deleteSelectedSequences() self.update()
[docs] def similaritySliderCB(self, value): if not self.group: return self.value_box.setText(str(value) + " %") self.similarity_value = value self.group.unselectAllSequences() self.group.selectRedundantSequences(value, reference=self.current_sequence, columns=True) if self.viewer: self.viewer.updateView()
[docs] def setSequence(self, sequence_group, sequence): self.sequence = sequence self.group = sequence_group
[docs] def updateLigand(self): """ Updates ligand list in the dialog. Called when either group contents changes or current sequence changes. """ if not self.group or not self.current_sequence: return self.group.unselectAllSequences() self.group.unselectAll() self.current_sequence.selected = True ligand_list = maestroGetLigandAnnotations(self.current_sequence) self.ligand_list = ligand_list self.ligand_choice.clear() self.ligand_choice.setCurrentIndex(0) if not ligand_list: return for lig in ligand_list: self.ligand_choice.addItem(lig.short_name) if self.viewer and ligand_list: self.viewer.contents_changed = True self.viewer.updateView() self.viewer.selectLigandContacts(ligand=ligand_list[0]) self.group.expandSelection()
[docs] def update(self): """ View update event. """ self.seq_list = [] self.ligand_list = [] self.seq_choice.clear() self.seq_choice.setCurrentIndex(0) if not self.group: return for seq in self.group.sequences: if seq.isValidProtein(): self.seq_choice.addItem(seq.short_name + "_" + seq.chain_id) self.seq_list.append(seq) if not self.seq_list: return self.current_sequence = self.seq_list[0] self.updateLigand() self.similaritySliderCB(self.similarity_value)
[docs] def okClicked(self): """ OK button callback """ if not self.sequence: return distances = maestroCalculateMinimumDistance(self.sequence) self.resultsTable.clearContents() for row in range(self.resultsTable.rowCount()): self.resultsTable.removeRow(0) min = self.min_range.value() max = self.max_range.value() initial = [False] * len(self.sequence.residues) for idx, res in enumerate(self.sequence.residues): initial[idx] = res.selected for test in range(min, max + 1): for idx, res in enumerate(self.sequence.residues): if distances[idx] <= test * test: res.selected = True self.group.expandSelectionRef() id = 0.0 sim = 0.0 hom = 0.0 cnt = 0 for seq in self.group.sequences: if seq != self.group.reference and seq.isValidProtein(): id += seq.calcIdentity(self.sequence, self.group.consider_gaps, True) sim += self.group.reference.calcSimilarity( seq, self.group.consider_gaps, True) hom += self.group.reference.calcHomology( seq, self.group.consider_gaps, True) cnt += 1 if cnt == 0: break cnt = cnt * 0.01 # Convert to percents id /= cnt sim /= cnt hom /= cnt row = self.resultsTable.rowCount() self.resultsTable.insertRow(row) for col in range(4): new_item = QTableWidgetNumericalItem() new_item.setFont(self.table_font) new_item.setFlags(QtCore.Qt.ItemIsEnabled) if col == 0: new_item.setData(QtCore.Qt.DisplayRole, test) elif col == 1: new_item.setData(QtCore.Qt.DisplayRole, id) elif col == 2: new_item.setData(QtCore.Qt.DisplayRole, sim) elif col == 3: new_item.setData(QtCore.Qt.DisplayRole, hom) self.resultsTable.setItem(row, col, new_item) self.group.deselectAll() for idx, res in enumerate(self.sequence.residues): res.selected = initial[idx] self.resultsTable.resizeColumnsToContents() self.resultsTable.resizeColumnToContents(4)
[docs] def cancelClicked(self): """ Cancel button callback """ self.hide()
[docs] def exportClicked(self): """ Export button callback """ if self.resultsTable.rowCount() < 1: return False if not self.save_dialog: self.save_dialog = QtWidgets.QFileDialog() self.save_dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptSave) self.save_dialog.setOption( QtWidgets.QFileDialog.DontUseNativeDialog) self.save_dialog.setWindowTitle("Export Results") self.save_dialog.setViewMode(QtWidgets.QFileDialog.Detail) self.save_dialog.setNameFilters(["CSV File (*.csv)"]) self.save_dialog.exec_() if self.save_dialog.result(): file_names = self.save_dialog.selectedFiles() if len(file_names) < 1: return False file_name = file_names[0] if len(file_name) == 0: return False name, ext = os.path.splitext(str(file_name)) if (not ext) or (ext != ".csv"): file_name += ".csv" outfile = open(file_name, "w") outfile.write("Distance,Identity,Similarity,Homology\n") for row in range(self.resultsTable.rowCount()): line = "" for col in range(self.resultsTable.columnCount()): if col > 0: line += "," line += self.resultsTable.item(row, col).text() line += "\n" outfile.write(line) outfile.close()
[docs] def show(self): """ Show and update the dialog. """ self.update() QtWidgets.QDialog.show(self)