"""
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")