Source code for schrodinger.ui.qt.protein_health_viewer

import math
from past.utils import old_div

import schrodinger
import schrodinger.protein._reliability as structure_reliability
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtGui
from schrodinger.Qt import QtWidgets
from schrodinger.Qt.QtCore import Qt

from . import protein_health_viewer_ui

maestro = schrodinger.get_maestro()

SOURCE_INDEX_ROLE = Qt.UserRole + 1

COLORS = {
    'g': QtGui.QColor(51, 255, 51),
    'r': QtGui.QColor("red"),
    'w': QtGui.QColor("white"),
    'y': QtGui.QColor('yellow')
}


[docs]class BubbleTextItem(QtWidgets.QGraphicsTextItem): """ single-click reloads description and the table """
[docs] def __init__(self, master, index, text): self.master = master self.index = index if text is None: text = "" super(BubbleTextItem, self).__init__(text)
[docs] def mousePressEvent(self, event): """ Called when the user clicks in the graphics area on the left side of the panel - selects the set clicked by the user. """ self.master.selectSet(self.index)
[docs]class ProteinHealthViewer(QtWidgets.QWidget): """ Class representing the Protein Reliability Report, with the sets graphics view on the left, and set points table on the right. """
[docs] def __init__(self, parent=None, flags=0): super(ProteinHealthViewer, self).__init__(parent) self.ui = protein_health_viewer_ui.Ui_Form() self.ui.setupUi(self) self.setWindowTitle('Protein Reliability Report') # Index of the currently selected set: self.clickedIndex = 0 self.textFont = QtGui.QFont() self.textFont.setPointSize(10) self.textFont.setUnderline(True) # Will be shown only when "Steric Clashes" set is selected: self.ui.show_crystal_mate_clashes_cb.hide() self.ui.show_crystal_mate_clashes_cb.toggled.connect( self.showCrystalMateClashesToggled) self.ui.table.itemSelectionChanged.connect(self.tableSelected)
[docs] def showCrystalMateClashesToggled(self): """ Called when the "Show crystal mate clashes" checkbox is toggled. Re-populates the set points table (right side of the panel). """ self.populateTableAndSummary()
[docs] def selectSet(self, set_index): """ Select the set with the given index. """ font = self.font() font.setBold(True) self.setFont(font) if self.clickedIndex != set_index: # Make the current set label bold, and remove bold from previous # selection: font2 = self.textItems[self.clickedIndex].font() font2.setBold(False) self.textItems[self.clickedIndex].setFont(font2) self.clickedIndex = set_index self.populateTableAndSummary() self.refit()
[docs] def getCurrentSet(self): """ Return the currently selected set object. """ return self.ordered_sets[self.clickedIndex]
[docs] def loadResults(self, struct, firstTime, chain='All'): # Reloads the model every time chain ID is changed, otherwise # filter_data_by_chain changes the underlying data model = structure_reliability.ModelCheck(struct, do_calc=False) if chain != "All": model.filter_data_by_chain(chain) self.loadModel(model) # PANEL-10965 - If summary table results are being shown for some set, # update the table to show only the results for the current chain(s). if self.ui.table.isVisible(): self.selectSet(self.clickedIndex)
[docs] def loadModel(self, model): self.model = model self.textItems = [] self.bubbleItems = [] dist = 20 # text to center of each bubble textWidth = 140 textOption = QtGui.QTextOption() textOption.setAlignment(Qt.AlignCenter) textOption.setWrapMode(QtGui.QTextOption.WordWrap) # Set up the bubbles and texts based on title order bubbleHoriDist = 160 nrow = math.ceil(old_div(len(self.model.calc_sets), 4)) bubbleVerDist = int(old_div(550, nrow)) scene = QtWidgets.QGraphicsScene() # Order the sets in an order in which we want them to be displayed. # For now, it's the same order as the one they are created in - to # match other reports. See PANEL-9419. self.ordered_sets = self.model.calc_sets z_orders = self._bubble_z_orders() for b, s in enumerate(self.ordered_sets): title = s.title color = COLORS.get(s.color) if not color: raise ValueError('Invalid color: "%s"' % s.color) xpos = (b % 4) * bubbleHoriDist ypos = (old_div(b, 4)) * bubbleVerDist dia = math.sqrt(s.area) bubble = QtWidgets.QGraphicsEllipseItem(xpos - old_div(dia, 2), ypos - old_div(dia, 2), dia, dia) bubble.setBrush(color) bubble.setZValue(z_orders[s]) if color.red() == 255 and color.green() == 255: bubble.setToolTip("No data") text = BubbleTextItem(self, b, title) text.setFont(self.textFont) text.setTextWidth(textWidth) text.setPos( QtCore.QPointF(xpos - old_div(textWidth, 2), ypos + dist)) text.document().setDefaultTextOption(textOption) scene.addItem(bubble) scene.addItem(text) self.bubbleItems.append(bubble) self.textItems.append(text) self.ui.view.setScene(scene) scene.setSceneRect( QtCore.QRectF(old_div(-bubbleHoriDist, 2), old_div(-bubbleVerDist, 2), 670, 670))
def _bubble_z_orders(self): """ :return: a dictionary mapping the items in `ordered_sets` to a z-order so that the largest bubble has the lowest z-value and all being < -9 :rtype: Dict[schrodinger.protein.analysis.Report.data_set, z-order] """ ret = dict() sorted_data_sets = sorted(self.ordered_sets, key=lambda x: x.area) for idx, s in enumerate(sorted_data_sets, start=10): ret[s] = -idx return ret
[docs] def populateTableAndSummary(self): """ Populate the set table (on the right side of the panel) with the points in that set. """ a_set = self.getCurrentSet() # Show/hide the set-specific widgets: SC_set = (a_set.label == structure_reliability.STERIC_CLASHES) self.ui.show_crystal_mate_clashes_cb.setVisible(SC_set) # Make a list of point rows that should be shown in the table if SC_set and not self.ui.show_crystal_mate_clashes_cb.isChecked(): show_points = [] for point in a_set.bad_points: if '*' in point.descriptor: # skip clashes with crystal mates continue show_points.append(point) else: # In all other cases, show all points in the set: show_points = a_set.bad_points font = QtGui.QFont() font.setPointSize(10) self.ui.summary_label.setFont(font) self.ui.summary_label.setText(a_set.summary) tableWidget = self.ui.table # Readability tableWidget.clear() tableWidget.setColumnCount(0) tableWidget.setRowCount(0) tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) tableWidget.setSelectionMode( QtWidgets.QAbstractItemView.SingleSelection) if a_set.bad_points: # Show column headers only if the set contains data (even if all # of it is hidden when "Show" checkbox is not checked) tableWidget.setColumnCount(len(a_set.fields)) tableWidget.setHorizontalHeaderLabels(a_set.fields) tableWidget.setRowCount(len(show_points)) for row, point in enumerate(show_points): # Add the descriptor column: item = QtWidgets.QTableWidgetItem(point.descriptor) item.setFont(font) item.setTextAlignment(Qt.AlignCenter) item.setData(SOURCE_INDEX_ROLE, row) tableWidget.setItem(row, 0, item) # Add other columns, based on the values in the set: col = 1 for value in point.values: valueStr = self._valToString(value) item = QtWidgets.QTableWidgetItem(valueStr) item.setFont(font) item.setTextAlignment(Qt.AlignCenter) tableWidget.setItem(row, col, item) col = col + 1 tableWidget.resizeColumnsToContents() tableWidget.setSortingEnabled(True)
[docs] def tableSelected(self): if not maestro: return selectedSet = self.getCurrentSet() for item in self.ui.table.selectedItems(): if item.column() == 0: row = item.data(SOURCE_INDEX_ROLE) point = selectedSet.bad_points[row] atom_list = ",".join([str(iatom) for iatom in point.atoms]) ws_st = maestro.workspace_get(copy=False) # PANEL-10245 - fit won't zoom to invisible atoms so we make # all fit atoms temporarily visible for the Maestro command. fit_atoms = [ws_st.atom[anum] for anum in point.atoms] inviz_atoms = [atom for atom in fit_atoms if not atom.visible] for atom in inviz_atoms: atom.visible = True maestro.command("fit atom.num %s" % atom_list) maestro.command("workspaceselectionreplace atom.num %s" % atom_list) for atom in inviz_atoms: atom.visible = False return
def _valToString(self, value): valueStr = str(value) try: fval = float(value) except: pass else: if fval != 0 and abs(math.log10(abs(fval))) > 4: valueStr = "%.3e" % fval else: valueStr = "%.3f" % fval return valueStr
[docs] def refit(self): if maestro: maestro.command("fit all") maestro.command("workspaceselectionclear")
[docs] def saveImage(self, filename): format = QtGui.QImage.Format_ARGB32_Premultiplied scene = self.ui.view.scene() width = scene.width() height = scene.height() painter = QtGui.QPainter() img = QtGui.QImage(QtCore.QSize(width, height), format) painter.begin(img) painter.fillRect(0, 0, width, height, QtCore.Qt.white) scene.render(painter) painter.end() img.save(filename) scene = self.ui.view.scene()
[docs] def reset(self): scene = QtWidgets.QGraphicsScene() self.ui.view.setScene(scene) self.ui.summary_label.setText("Click on a text field to view details.") self.ui.table.clear() self.ui.table.setRowCount(0) self.ui.table.setColumnCount(0) self.ui.show_crystal_mate_clashes_cb.hide()