Source code for schrodinger.ui.sequencealignment.sequence_viewer_gui

"""
Additional dialogs and panels used by the sequence viewer.

Copyright Schrodinger, LLC. All rights reserved.
"""

# Contributors: Piotr Rotkiewicz

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

from . import maestro as my_maestro
from .fileio import load_fasta_file
from .sequence import Sequence

try:
    from schrodinger import maestro
except:
    maestro = None


[docs]class WeightColorsDialog(QtWidgets.QDialog): """ This class implements a remove redundancy dialog. """
[docs] def __init__(self, parent, sequence_group): # Initialize base class. QtWidgets.QDialog.__init__(self, parent) self.setWindowTitle("Adjust Color Range") self.sequence_group = sequence_group #: Dialog push buttons. self.button_ok = QtWidgets.QPushButton(self) self.button_ok.setText("OK") self.button_ok.setDefault(True) self.button_ok.clicked.connect(self.accept) self.button_reset = QtWidgets.QPushButton(self) self.button_reset.setText("Reset") self.button_reset.clicked.connect(self.resetSliders) self.button_layout = QtWidgets.QHBoxLayout() self.button_layout.addWidget(self.button_reset) self.button_layout.addWidget(self.button_ok) self.identity_label = QtWidgets.QLabel() self.min_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal) self.min_slider.setRange(0, 100.0) self.min_slider.setTickInterval(10) self.min_slider.setValue(parent.min_weight_identity) self.min_slider.setTickPosition(QtWidgets.QSlider.TicksBelow) self.min_slider.valueChanged.connect(self.minValueChanged) self.slider_layout = QtWidgets.QGridLayout() self.slider_layout.addWidget(QtWidgets.QLabel("Background Color"), 0, 0) self.slider_layout.addWidget(self.min_slider, 0, 1) self.max_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal) self.max_slider.setRange(0, 100.0) self.max_slider.setTickInterval(10) self.max_slider.setValue(parent.max_weight_identity) self.max_slider.setTickPosition(QtWidgets.QSlider.TicksBelow) self.max_slider.valueChanged.connect(self.maxValueChanged) self.slider_layout.addWidget(QtWidgets.QLabel("Full Color"), 1, 0) self.slider_layout.addWidget(self.max_slider, 1, 1) self.layout = QtWidgets.QVBoxLayout(self) self.layout.addWidget(self.identity_label) self.layout.addLayout(self.slider_layout) self.layout.addLayout(self.button_layout) # Update sliders. self.minValueChanged(self.min_slider.value()) self.maxValueChanged(self.max_slider.value()) self.sizePolicy().setVerticalPolicy(QtWidgets.QSizePolicy.Maximum) self.resize(600, 150)
[docs] def resetSliders(self): self.min_slider.setValue(20.0) self.max_slider.setValue(100.0) self.parent().updateView(update_colors=True)
[docs] def minValueChanged(self, value): if value >= self.parent().max_weight_identity: self.min_slider.setValue(self.parent().max_weight_identity - 1) return self.parent().min_weight_identity = value self.identity_label.setText("Sequence Identity Range: " + str(value) + "% - " + str(self.parent().max_weight_identity) + "%") self.parent().updateView(update_colors=True)
[docs] def maxValueChanged(self, value): if value <= self.parent().min_weight_identity: self.max_slider.setValue(self.parent().min_weight_identity + 1) return self.parent().max_weight_identity = value self.identity_label.setText("Sequence Identity Range: " + str(self.parent().min_weight_identity) + "% - " + str(value) + "%") self.parent().updateView(update_colors=True)
[docs]class SequenceEditorDialog(QtWidgets.QDialog):
[docs] def __init__(self, parent, sequence, sequence_group, fasta_editor=False): # Initialize base class. QtWidgets.QDialog.__init__(self, parent) self.setWindowTitle("Sequence Editor") self.resize(600, 300) self.layout = QtWidgets.QVBoxLayout(self) h_layout = QtWidgets.QHBoxLayout() self.name_label = QtWidgets.QLabel("Sequence Name:") self.name_editor = QtWidgets.QLineEdit() if sequence: self.name_editor.setText(sequence.name) h_layout.addWidget(self.name_label) h_layout.addWidget(self.name_editor) self.layout.addLayout(h_layout) self.sequence_label = QtWidgets.QLabel("Sequence:") self.layout.addWidget(self.sequence_label) self.sequence_editor = QtWidgets.QTextEdit() self.sequence_editor.setAcceptRichText(False) if sequence: self.sequence_editor.setText(sequence.toString()) self.layout.addWidget(self.sequence_editor) self.button_add = QtWidgets.QPushButton() self.button_add.setText("Append") self.button_replace = QtWidgets.QPushButton() self.button_replace.setText("Replace") self.button_cancel = QtWidgets.QPushButton() self.button_cancel.setText("Cancel") button_layout = QtWidgets.QHBoxLayout() button_layout.addWidget(self.button_add) button_layout.addWidget(self.button_replace) button_layout.addWidget(self.button_cancel) self.layout.addLayout(button_layout) self.sequence = sequence self.sequence_group = sequence_group self.button_add.clicked.connect(self.addSequence) self.button_replace.clicked.connect(self.replaceSequence) self.button_cancel.clicked.connect(self.reject) self.fasta_editor = fasta_editor if fasta_editor: self.name_editor.hide()
[docs] def addSequence(self): if self.fasta_editor: text = str(self.sequence_editor.toPlainText()) text = text.rsplit("\n") load_fasta_file(self.sequence_group, None, text) self.hide() else: self.sequence = Sequence() self.sequence.name = str(self.name_editor.text()) self.sequence.short_name = str(self.name_editor.text()) if len(self.sequence.short_name) > 1 and \ self.sequence.short_name[-2] == '_': self.sequence.chain_id = self.sequence.short_name[-1] self.sequence.appendResidues(self.sequence_editor.toPlainText()) self.sequence.sanitize() if self.sequence.length() > 0: self.sequence_group.sequences.append(self.sequence) self.hide()
[docs] def replaceSequence(self): self.sequence.name = str(self.name_editor.text()) self.sequence.short_name = self.sequence.name if len(self.sequence.short_name) > 1 and \ self.sequence.short_name[-2] == '_': self.sequence.chain_id = self.sequence.short_name[-1] self.sequence.residues = [] self.sequence.appendResidues(self.sequence_editor.toPlainText()) self.sequence.sanitize() self.hide()
[docs]class RemoveRedundancyDialog(QtWidgets.QDialog): """ This class implements a remove redundancy dialog. """
[docs] def __init__(self, parent, sequence_group): # Initialize base class. QtWidgets.QDialog.__init__(self, parent) self.setWindowTitle("Remove Redundant Sequences") self.sequence_group = sequence_group #: Dialog push button. self.button_remove = QtWidgets.QPushButton(self) self.button_remove.setText("Remove") self.button_remove.setDefault(True) self.button_remove.clicked.connect(self.accept) #: Dialog push button. self.button_cancel = QtWidgets.QPushButton(self) self.button_cancel.setText("Close") self.button_cancel.clicked.connect(self.reject) #: Button layout. self.button_layout = QtWidgets.QHBoxLayout() self.button_layout.addWidget(self.button_remove) self.button_layout.addWidget(self.button_cancel) self.redundancy_label = QtWidgets.QLabel() self.slider = QtWidgets.QSlider(QtCore.Qt.Horizontal) self.slider.setRange(0, 100) self.slider.setTickInterval(10) self.slider.setValue(100) self.slider.setTickPosition(QtWidgets.QSlider.TicksBelow) self.slider.valueChanged.connect(self.valueChanged) self.layout = QtWidgets.QVBoxLayout(self) self.layout.addWidget(self.redundancy_label) self.layout.addWidget(self.slider) self.layout.addLayout(self.button_layout) self.valueChanged(100) self.sizePolicy().setVerticalPolicy(QtWidgets.QSizePolicy.Maximum)
[docs] def valueChanged(self, value): self.redundancy_label.setText("Identity Threshold: " + str(value) + "%") self.sequence_group.selectRedundantSequences(value) self.parent().name_area.repaint()
[docs]class RemoteQueryDialog(QtWidgets.QDialog): """ This class implements a remote access query dialog. """
[docs] def __init__(self, parent): # Initialize base class. QtWidgets.QDialog.__init__(self, parent) self.parent = parent self.setWindowTitle("Attempting to Access Remote Server") self.setModal(True) self.resize(400, 200) #: Dialog push button. self.button_ok = QtWidgets.QPushButton(self) self.button_ok.setText("Continue") self.button_ok.clicked.connect(self.accept) #: Dialog push button. self.button_cancel = QtWidgets.QPushButton(self) self.button_cancel.setText("Cancel") self.button_cancel.setDefault(True) self.button_cancel.clicked.connect(self.reject) self.description = QtWidgets.QLabel( "Multiple Sequence Viewer is attempting to access a remote server.\n" "Would you like to continue?") self.never_ask = QtWidgets.QCheckBox("Do not ask this question again") #: Button layout. self.button_layout = QtWidgets.QHBoxLayout() self.button_layout.addWidget(self.button_ok) self.button_layout.addWidget(self.button_cancel) self.layout = QtWidgets.QVBoxLayout(self) self.layout.addWidget(self.description) self.layout.addWidget(self.never_ask) self.layout.addSpacing(20) self.layout.addLayout(self.button_layout)
[docs] def query(self, server=None): settings = QtCore.QSettings(QtCore.QSettings.IniFormat, QtCore.QSettings.UserScope, "multiple_sequence_viewer") if self.parent and hasattr(self.parent, "always_ask_action") and \ self.parent.always_ask_action: settings.setValue("viewer_ask_before_remote_access", self.parent.always_ask_action.isChecked()) always_ask = settings.value("viewer_ask_before_remote_access") if not always_ask: return True if self.exec_(): always_ask = not self.never_ask.isChecked() settings.setValue("viewer_ask_before_remote_access", always_ask) if self.parent and hasattr(self.parent, "always_ask_action") and \ self.parent.always_ask_action: self.parent.always_ask_action.setChecked(always_ask) return True else: return False
[docs]class RenumberResiduesDialog(QtWidgets.QDialog): """ This class implements 'Renumber Residues' dialog. """
[docs] def __init__(self, parent, sequence_group): # Initialize base class. QtWidgets.QDialog.__init__(self, parent) self.setWindowTitle("Renumber Residues") self.sequence_group = sequence_group self.simple_page = QtWidgets.QWidget(self) self.simple_page_layout = QtWidgets.QVBoxLayout() self.simple_page.setLayout(self.simple_page_layout) self.start_num_label = QtWidgets.QLabel("First position:") self.start_num_le = QtWidgets.QLineEdit("1", self) self.start_num_layout = QtWidgets.QHBoxLayout() self.start_num_layout.addWidget(self.start_num_label) self.start_num_layout.addWidget(self.start_num_le) self.start_num_layout.addStretch(100) self.inc_num_label = QtWidgets.QLabel("Increment:") self.inc_num_le = QtWidgets.QLineEdit("1", self) self.inc_num_layout = QtWidgets.QHBoxLayout() self.inc_num_layout.addWidget(self.inc_num_label) self.inc_num_layout.addWidget(self.inc_num_le) self.inc_num_layout.addStretch(100) self.preserve_icodes_cb = QtWidgets.QCheckBox(self) self.preserve_icodes_cb.setText("Preserve insertion codes") self.preserve_icodes_cb.setChecked(False) self.simple_page_layout.addLayout(self.start_num_layout) self.simple_page_layout.addLayout(self.inc_num_layout) self.simple_page_layout.addWidget(self.preserve_icodes_cb) self.simple_page_layout.addStretch(100) self.template_page = QtWidgets.QWidget(self) self.template_page_layout = QtWidgets.QVBoxLayout() self.template_page.setLayout(self.template_page_layout) self.schemeTable = QtWidgets.QTableWidget(0, 1) self.schemeTable.setAlternatingRowColors(True) self.table_font = self.schemeTable.font() self.table_font.setPointSize(10) self.schemeTable.setFont(self.table_font) self.schemeTable.setHorizontalHeaderLabels(["Numbering scheme"]) self.schemeTable.horizontalHeader().setStretchLastSection(True) self.schemeTable.resizeColumnsToContents() self.schemeTable.sortItems(1, QtCore.Qt.AscendingOrder) self.schemeTable.itemSelectionChanged.connect(self.schemeChanged) self.numbersTable = QtWidgets.QTableWidget(0, 3) self.numbersTable.setAlternatingRowColors(True) self.table_font = self.numbersTable.font() self.table_font.setPointSize(10) self.numbersTable.setFont(self.table_font) self.numbersTable.setHorizontalHeaderLabels( ["Residue number", "Insertion code", "Residue name"]) self.numbersTable.horizontalHeader().setStretchLastSection(True) self.numbersTable.resizeColumnsToContents() self.numbersTable.sortItems(1, QtCore.Qt.AscendingOrder) self.template_page_layout.addWidget(self.schemeTable) self.template_page_layout.addWidget(self.numbersTable) self.button_import = QtWidgets.QPushButton(self) self.button_import.setText("Import template...") self.button_import.clicked.connect(self.importTemplate) self.template_page_layout.addWidget(self.schemeTable) self.template_page_layout.addWidget(self.numbersTable) self.template_page_layout.addWidget(self.button_import) self.tabs = QtWidgets.QTabWidget(self) self.tabs.addTab(self.simple_page, "Simple") self.tabs.addTab(self.template_page, "Template-based") #: Dialog push buttons. self.button_ok = QtWidgets.QPushButton(self) self.button_ok.setText("Renumber") self.button_ok.setDefault(True) self.button_ok.clicked.connect(self.accept) self.button_reset = QtWidgets.QPushButton(self) self.button_reset.setText("Close") self.button_reset.clicked.connect(self.reject) #: "Help" push button. self.button_help = QtWidgets.QPushButton(self) self.button_help.setText("Help") self.button_help.clicked.connect(self.helpCB) self.apply_to_maestro_cb = QtWidgets.QCheckBox(self) self.apply_to_maestro_cb.setText("Renumber residues in Maestro entries") self.apply_to_maestro_cb.setChecked(True) self.button_layout = QtWidgets.QHBoxLayout() self.button_layout.addWidget(self.apply_to_maestro_cb) self.button_layout.addStretch(100) self.button_layout.addWidget(self.button_help) self.button_layout.addWidget(self.button_reset) self.button_layout.addWidget(self.button_ok) self.layout = QtWidgets.QVBoxLayout(self) self.layout.addWidget(self.tabs) self.layout.addLayout(self.button_layout) self.schemes = {}
[docs] def accept(self): start = int(self.start_num_le.text()) incr = int(self.inc_num_le.text()) none_selected = not self.sequence_group.hasSelectedSequences() for seq in self.sequence_group.sequences: if seq.isValidProtein() and (seq.selected or none_selected): seq.renumberResidues( start, incr, preserve_ins_codes=self.preserve_icodes_cb.isChecked()) if my_maestro and self.apply_to_maestro_cb.isChecked(): my_maestro.renumberMaestroEntry(self.parent(), seq) self.close() return True
[docs] def importTemplate(self): """ Import a PDB numbering template. """ self.file_import_dialog = QtWidgets.QFileDialog() self.file_import_dialog.setFileMode(QtWidgets.QFileDialog.ExistingFiles) self.file_import_dialog.setOption( QtWidgets.QFileDialog.DontUseNativeDialog) self.file_import_dialog.setViewMode(QtWidgets.QFileDialog.Detail) self.file_import_dialog.setWindowTitle("Import Sequences") self.file_import_dialog.setNameFilters( ["PDB (*.pdb *.ent)", "Any File (*.*)"]) self.file_import_dialog.exec_() scheme = [] self.numbersTable.clearContents() self.numbersTable.setRowCount(0) if self.file_import_dialog.result(): files = self.file_import_dialog.selectedFiles() if files: pdb_file = open(files[0], "r") if not pdb_file: return lines = pdb_file.readlines() pdb_file.close() prev_res_id = "" scheme_name = "Scheme " + str(self.schemeTable.rowCount() + 1) for line in lines: if line.startswith("HEAD"): scheme_name = line[62:67] + line[10:50] if not line.startswith("ATOM"): continue res_id = line[22:27] if res_id == prev_res_id: continue res_num = res_id[:-1] res_ins_code = res_id[-1] prev_res_id = res_id res_name = line[17:20] row = self.numbersTable.rowCount() self.numbersTable.insertRow(row) new_item = QtWidgets.QTableWidgetItem(res_num) new_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.numbersTable.setItem(row, 0, new_item) new_item = QtWidgets.QTableWidgetItem(res_ins_code) new_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.numbersTable.setItem(row, 1, new_item) new_item = QtWidgets.QTableWidgetItem(res_name) new_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.numbersTable.setItem(row, 2, new_item) scheme.append((res_num, res_ins_code, res_name)) if not scheme: return self.numbersTable.resizeColumnsToContents() self.numbersTable.horizontalHeader().setStretchLastSection(True) row = self.schemeTable.rowCount() self.schemeTable.insertRow(row) self.schemes[str(scheme_name)] = scheme new_item = QtWidgets.QTableWidgetItem(str(scheme_name)) new_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.schemeTable.setItem(row, 0, new_item)
[docs] def schemeChanged(self): selected = self.schemeTable.selectedItems() if selected: self.numbersTable.clearContents() self.numbersTable.setRowCount(0) key = str(selected[0].text()) if key in list(self.schemes): scheme = self.schemes[key] for item in scheme: res_num, res_ins_code, res_name = item row = self.numbersTable.rowCount() self.numbersTable.insertRow(row) new_item = QtWidgets.QTableWidgetItem(res_num) new_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.numbersTable.setItem(row, 0, new_item) new_item = QtWidgets.QTableWidgetItem(res_ins_code) new_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.numbersTable.setItem(row, 1, new_item) new_item = QtWidgets.QTableWidgetItem(res_name) new_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.numbersTable.setItem(row, 2, new_item)
[docs] def helpCB(self): """ Invokes a Homology Modeling help page. """ if maestro: maestro.command("helptopic TOOLS_MENU_MSV_RENUMBER_RESIDUES")