Source code for schrodinger.ui.sequencealignment.find_gui

import os
from past.utils import old_div

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

try:
    from schrodinger.infra.mm import mmfile_schrodinger_appdata_dir
except:
    mmfile_schrodinger_appdata_dir = None

try:
    from schrodinger.maestro import maestro
except:
    maestro = None
try:
    from schrodinger.protein.sequence import find_generalized_pattern
except:
    find_generalized_pattern = None

MSV_PATTERNS = None
FIND_COMBO_BOX = None
FIND_LINE_EDIT = None
FIND_TEXT_CHANGED = None
PATTERN_EDIT_DIALOG = None

DEAMIDATION_PATTERN = "Deamidation Site"
OXIDATION_PATTERN = "Oxidation Site"
GLYCOSYLATION_N_PATTERN = "Glycosylation Site"
PROTEOLYSIS_PATTERN = "Proteolysis Site"

DEAMIDATION_RGB = (255, 0, 0)
OXIDATION_RGB = (0, 255, 0)
PROTEOLYSIS_RGB = (0, 0, 255)
GLYCOSYLATION_N_RGB = (255, 0, 255)

pre_defined_pattern_colors = {
    DEAMIDATION_PATTERN: DEAMIDATION_RGB,
    GLYCOSYLATION_N_PATTERN: GLYCOSYLATION_N_RGB,
    PROTEOLYSIS_PATTERN: PROTEOLYSIS_RGB,
    OXIDATION_PATTERN: OXIDATION_RGB
}


[docs]def get_pre_defined_pattern_colors(): """ Return the global dictionary containing colors (rgb values) corresponding to the pre-defined patterns. This function is used by `reactive_protein_residues_gui.py`. """ global pre_defined_pattern_colors return pre_defined_pattern_colors
def _set_pre_defined_pattern_color(pattern_type, color): """ Set the color (rgb values) for a pre-defined pattern. :param pattern_type: pattern type or name :type pattern_type: str :param color: rgb values of a color :type color: tuple(int, int, int) """ global pre_defined_pattern_colors if pattern_type not in pre_defined_pattern_colors: return pre_defined_pattern_colors[pattern_type] = color
[docs]def writeMSVPatterns(patterns): if mmfile_schrodinger_appdata_dir: settings_path = mmfile_schrodinger_appdata_dir() settings_path = settings_path + os.sep + "msv" if not os.path.exists(settings_path): os.makedirs(settings_path) try: settings_file = settings_path + os.sep + "patterns" file = open(settings_file, "w") for name, pattern, hotspot, color in patterns: r, g, b = color rs = ('0' + hex(r)[2:])[-2:] gs = ('0' + hex(g)[2:])[-2:] bs = ('0' + hex(b)[2:])[-2:] color_str = '#' + rs + gs + bs if hotspot == "None": file.write(name + "\n" + pattern + color_str + "\n") else: file.write(name + "\n" + pattern + ":" + hotspot + color_str + "\n") file.close() except: return False # Error return True
[docs]def readMSVPatterns(): global MSV_PATTERNS default_patterns = [ (DEAMIDATION_PATTERN, "x-[NQ]-[GASHMYD]", "2", DEAMIDATION_RGB), (GLYCOSYLATION_N_PATTERN, "N-{P}-[ST]-{P}", "1", GLYCOSYLATION_N_RGB), (PROTEOLYSIS_PATTERN, "x-D-x", "2", PROTEOLYSIS_RGB), (OXIDATION_PATTERN, "[HMCWY]", "1", OXIDATION_RGB) ] patterns = [] if mmfile_schrodinger_appdata_dir: settings_path = mmfile_schrodinger_appdata_dir() try: settings_file = os.path.join(settings_path, "msv", "patterns") if not os.path.isfile(settings_file): settings_file = os.path.join(settings_path, "msv_patterns") if not os.path.isfile(settings_file): raise RuntimeError("No settings file") fh = open(settings_file) lines = fh.readlines() fh.close() if not lines: raise RuntimeError("Settings file was empty") for index in range(old_div(len(lines), 2)): pattern = lines[2 * index + 1].rstrip() hotspot = "None" color = (255, 0, 0) if '#' in pattern: try: pos = pattern.find('#') color_str = pattern[pos + 1:] pattern = pattern[:pos] color = (int(color_str[0:2], 16), int(color_str[2:4], 16), int(color_str[4:6], 16)) except: color = (255, 0, 0) if ':' in pattern: try: pos = pattern.find(':') hotspot = pattern[pos + 1:] pattern = pattern[:pos] except: pass name = lines[2 * index].rstrip() patterns.append((name, pattern, hotspot, color)) _set_pre_defined_pattern_color(name, color) except RuntimeError as err: patterns = default_patterns writeMSVPatterns(patterns) MSV_PATTERNS = patterns return patterns
[docs]def findItemChanged(index): global MSV_PATTERNS global FIND_COMBO_BOX global FIND_LINE_EDIT global FIND_TEXT_CHANGED global PATTERN_EDIT_DIALOG index -= 2 if index < 0: return if index < len(MSV_PATTERNS): FIND_COMBO_BOX.setCurrentIndex(0) FIND_LINE_EDIT.setText(MSV_PATTERNS[index][1]) FIND_TEXT_CHANGED() else: FIND_COMBO_BOX.setCurrentIndex(0) PATTERN_EDIT_DIALOG.setPatterns(MSV_PATTERNS) PATTERN_EDIT_DIALOG.display()
[docs]class PatternEditDialog(QtWidgets.QDialog): """ This class implements simple pattern editor dialog. """
[docs] def __init__(self, parent): # Initialize base class. QtWidgets.QDialog.__init__(self, parent) self.setWindowTitle("Edit Patterns") self.ok_pressed = False self.okButton = QtWidgets.QPushButton("OK") self.cancelButton = QtWidgets.QPushButton("Cancel") self.newButton = QtWidgets.QPushButton("Add New Pattern") self.deleteButton = QtWidgets.QPushButton("Delete Selected Rows") self.spacer = QtWidgets.QWidget() self.spacer.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) self.buttonLayout = QtWidgets.QHBoxLayout() self.buttonLayout.addWidget(self.newButton) self.buttonLayout.addWidget(self.deleteButton) self.buttonLayout.addWidget(self.spacer) self.buttonLayout.addWidget(self.okButton) self.buttonLayout.addWidget(self.cancelButton) self.patternTable = QtWidgets.QTableWidget(0, 4) self.table_font = self.patternTable.font() self.table_font.setPointSize(10) self.patternTable.setFont(self.table_font) self.patternTable.setHorizontalHeaderLabels( ["Pattern Name", "Definition", "Hotspot", "Color"]) self.patternTable.resizeColumnsToContents() self.patternTable.horizontalHeader().setSectionResizeMode( 1, QtWidgets.QHeaderView.Stretch) self.patternTable.sortItems(1, QtCore.Qt.AscendingOrder) self.description = QtWidgets.QTextEdit(self) self.description.setReadOnly(True) self.description.setHtml( "<b>Double click a table cell to edit pattern name or definition.</b><br><br> \ <b>Pattern syntax</b><br><br> \ The patterns are defined using extended PROSITE syntax:<ul> \ <li> standard IUPAC one-letter codes are used for all amino acids \ <li> each element in a pattern is separated using '-' symbol \ <li> symbol 'x' is used for position where any amino acid is accepted \ <li> ambiguities are listed using the acceptable amino acids between \ square brackets, e.g. [ACT] means Ala, Cys or Thr \ <li>amino acids not accepted for a given position are indicated \ by listing them between curly brackets, e.g. {GP} means 'not Gly and not Pro' \ <li>repetition is indicated using parentheses, e.g. A(3) means Ala-Ala-Ala, \ x(2,4) means between 2 to 4 any residues \ <li>the following lowercase characters can be used as additional flags \ <ul> \ <li>'x': any amino acid \ <li>'a': acidic residue: [DE] \ <li>'b': basic residue: [KR] \ <li>'o': hydrophobic residue: [ACFILPWVY] \ <li>'p': aromatic residue: [WYF] \ <li>'s': solvent exposed residue \ <li>'h': helical residue \ <li>'e': extended residue \ <li>'f': flexible residue \ </ul></ul><br> \ <b>Pattern examples</b> \ <ul> \ <li> N-{P}-[ST] : Asn-X-Ser or Thr (X != Pro) \ <li> N[sf]-{P}[sf]-[ST][sf] : as above, but all residues flexible OR solvent exposed \ <li> Nsf-{P}sf-[ST]sf : as above, but all residues flexible AND solvent exposed \ <li> Ns{f} : Asn solvent exposed AND not in flexible region \ <li> N[s{f}] : Asn solvent exposed OR not in flexible region \ <li> [ab]{K}{s}f : acidic OR basic, with exception of Lys flexible AND not solvent exposed \ <li> Ahe : Ala helical AND extended - no match possible \ <li> A[he] : Ala helical OR extended \ <li> A{he} : Ala not helical nor extended \ <li> [ST] : Ser OR Thr \ <li> ST : Ser AND Thr - no match possible \ </ul>") self.layout = QtWidgets.QVBoxLayout(self) self.layout.addWidget(self.patternTable) self.layout.addWidget(self.description) self.layout.addLayout(self.buttonLayout) self.patternTable.resizeColumnToContents(1) self.patternTable.horizontalHeader().setStretchLastSection(False) self.patternTable.setWordWrap(False) self.patternTable.itemSelectionChanged.connect(self.selectionChanged) self.patternTable.cellDoubleClicked.connect(self.cellDoubleClicked) self.cancelButton.clicked.connect(self.cancelClicked) self.okButton.clicked.connect(self.okClicked) self.newButton.clicked.connect(self.addNewClicked) self.deleteButton.clicked.connect(self.deleteClicked) self.resize(self.patternTable.width() + 15, self.patternTable.height()) self.selecting = False
[docs] def selectionChanged(self): if self.selecting: return self.selecting = True for item in self.patternTable.selectedItems(): row = item.row() # Select the entire row. self.patternTable.setRangeSelected( QtWidgets.QTableWidgetSelectionRange( row, 0, row, self.patternTable.columnCount() - 1), True) self.selecting = False
[docs] def cellDoubleClicked(self, row, column): """ User double clicked a cell. """ global MSV_PATTERNS if column == 3: name, pattern, hotspot, color = MSV_PATTERNS[row] r, g, b = color color = QtWidgets.QColorDialog.getColor( initial=QtGui.QColor(r, g, b)) if color: color = (color.red(), color.green(), color.blue()) r, g, b = color _set_pre_defined_pattern_color(name, color) new_item = QtWidgets.QTableWidgetItem("") new_item.setData(QtCore.Qt.DecorationRole, QtGui.QColor(r, g, b)) new_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.patternTable.setItem(row, 3, new_item) MSV_PATTERNS[row] = (name, pattern, hotspot, color) writeMSVPatterns(MSV_PATTERNS)
[docs] def okClicked(self): """ OK button callback """ # Validate patterns valid = True for index in range(self.patternTable.rowCount()): name = str(self.patternTable.item(index, 0).text()) pattern = str(self.patternTable.item(index, 1).text()) hotspot = str(self.patternTable.item(index, 2).text()) self.patternTable.item(index, 1).setForeground(QtCore.Qt.black) if hotspot != "None": try: num = int(hotspot) except: self.patternTable.setRangeSelected( QtWidgets.QTableWidgetSelectionRange( index, 0, index, self.patternTable.columnCount() - 1), False) self.patternTable.item(index, 2).setForeground(QtCore.Qt.red) valid = False break if find_generalized_pattern(None, pattern, validate_pattern=True) != True: self.patternTable.setRangeSelected( QtWidgets.QTableWidgetSelectionRange( index, 0, index, self.patternTable.columnCount() - 1), False) self.patternTable.item(index, 1).setForeground(QtCore.Qt.red) valid = False break if not valid: QtWidgets.QMessageBox.critical( self, "Invalid Pattern Syntax", "Please correct the pattern marked red.") return self.hide() self.ok_pressed = True
[docs] def deleteClicked(self): """ Delete button callback """ rows = self.patternTable.selectionModel().selectedRows() while rows: self.patternTable.removeRow(rows[0].row()) rows = self.patternTable.selectionModel().selectedRows() pattern_list = self.createPatternList() updateFindComboBox(pattern_list)
[docs] def cancelClicked(self): """ Cancel button callback """ self.hide() self.ok_pressed = False
[docs] def addNewClicked(self): """ Add new callback """ row = self.patternTable.rowCount() self.patternTable.insertRow(row) new_item = QtWidgets.QTableWidgetItem("New Pattern") new_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable) self.patternTable.setItem(row, 0, new_item) new_item = QtWidgets.QTableWidgetItem("A-A-A") new_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable) self.patternTable.setItem(row, 1, new_item) new_item = QtWidgets.QTableWidgetItem("None") new_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable) self.patternTable.setItem(row, 2, new_item) new_item = QtWidgets.QTableWidgetItem("") new_item.setData(QtCore.Qt.DecorationRole, QtGui.QColor(255, 0, 0)) new_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.patternTable.setItem(row, 3, new_item) pattern_list = self.createPatternList() updateFindComboBox(pattern_list)
[docs] def setPatterns(self, pattern_list): self.patternTable.clear() self.patternTable.setRowCount(0) row = 0 self.patternTable.setHorizontalHeaderLabels( ["Pattern Name", "Definition", "Hotspot", "Color"]) for index, item in enumerate(pattern_list): name, pattern, hotspot, color = item self.patternTable.insertRow(row) new_item = QtWidgets.QTableWidgetItem(name) new_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable) self.patternTable.setItem(row, 0, new_item) new_item = QtWidgets.QTableWidgetItem(pattern) new_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable) self.patternTable.setItem(row, 1, new_item) new_item = QtWidgets.QTableWidgetItem(hotspot) new_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable) self.patternTable.setItem(row, 2, new_item) r, g, b = color new_item = QtWidgets.QTableWidgetItem("") new_item.setData(QtCore.Qt.DecorationRole, QtGui.QColor(r, g, b)) new_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.patternTable.setItem(row, 3, new_item) row += 1 self.patternTable.resizeColumnToContents(0)
[docs] def display(self): """ Brings up the modal dialog. Returns None if cancelled, or the modified FeaturePattern, if OK. """ self.exec() if self.ok_pressed: self.ok_pressed = False pattern_list = self.createPatternList() updateFindComboBox(pattern_list) return pattern_list # Cancel pressed: return MSV_PATTERNS
[docs] def createPatternList(self): global MSV_PATTERNS pattern_list = [] for index in range(self.patternTable.rowCount()): name = str(self.patternTable.item(index, 0).text()) pattern = str(self.patternTable.item(index, 1).text()) hotspot = str(self.patternTable.item(index, 2).text()) color = QtGui.QColor( self.patternTable.item(index, 3).data(QtCore.Qt.DecorationRole)) color = (color.red(), color.green(), color.blue()) pattern_list.append((name, pattern, hotspot, color)) MSV_PATTERNS = pattern_list writeMSVPatterns(pattern_list) return pattern_list
[docs]def updateFindComboBox(pattern_list): global FIND_COMBO_BOX if FIND_COMBO_BOX is None: return name_list = [x[0] for x in pattern_list] FIND_COMBO_BOX.clear() FIND_COMBO_BOX.addItem("Select Pattern...") FIND_COMBO_BOX.addItems(name_list) FIND_COMBO_BOX.addItem("Edit Patterns...") FIND_COMBO_BOX.insertSeparator(1) FIND_COMBO_BOX.insertSeparator(len(name_list) + 2)
[docs]def createFindToolbar(parent, find_text_changed_act, find_previous_act, find_next_act, clear_pattern_act=None): global FIND_COMBO_BOX global FIND_LINE_EDIT global FIND_TEXT_CHANGED global PATTERN_EDIT_DIALOG # Create Find Toolbar findToolBar = QtWidgets.QToolBar("Find Toolbar", parent) if clear_pattern_act: parent.clearPatternButton = QtWidgets.QToolButton() parent.clearPatternButton.setText('x') parent.clearPatternButton.clicked.connect(clear_pattern_act) parent.findLineEdit = QtWidgets.QLineEdit() FIND_LINE_EDIT = parent.findLineEdit FIND_TEXT_CHANGED = find_text_changed_act PATTERN_EDIT_DIALOG = PatternEditDialog(parent) parent.findLineEdit.returnPressed.connect(find_text_changed_act) parent.findLineEdit.setToolTip( "Type PROSITE pattern to search the sequences:\n\n" "[AIL] - find any occurence of A, I, or L\n" "{ED} - exclude all occurences of E and D\n" "a - acidic residue [DE]\n" "b - basic residue [KR]\n" "o - hydrophobic residue [WILYFVPCA]\n" "p - aromatic residue [WYF]\n" "s - solvent exposed residue\n" "h - helical residue\n" "e - extended residue\n" "x or . or ? - any amino acid") selectPattern = QtWidgets.QComboBox(parent) selectPattern.currentIndexChanged.connect(findItemChanged) FIND_COMBO_BOX = selectPattern MSV_PATTERNS = readMSVPatterns() updateFindComboBox(MSV_PATTERNS) label = QtWidgets.QLabel(" Find Pattern: ") findToolBar.addWidget(label) findToolBar.addWidget(parent.findLineEdit) if clear_pattern_act: findToolBar.addWidget(parent.clearPatternButton) findToolBar.addSeparator() findToolBar.addWidget(selectPattern) findToolBar.addSeparator() findToolBar.addAction(find_previous_act) findToolBar.addAction(find_next_act) FIND_LINE_EDIT.setText("") return findToolBar