Source code for schrodinger.application.canvas.similaritygui
"""
Support for Canvas fingerprint similarity operations that use GUI components.
There are classes to perform similarity calculations and to support
graphical interfaces for similarity options.
Copyright Schrodinger, LLC. All rights reserved.
"""
# Contributors: Quentin McDonald
import schrodinger.ui.qt.swidgets as swidgets
from schrodinger.application.canvas import similarity
from schrodinger.Qt import QtWidgets
######################### Qt GUIs begin here #########################
[docs]class CanvasFingerprintSimilarityGUI(similarity.CanvasFingerprintSimilarity):
    """
    A subclass of the canvas fingerprint similarity manager which is to be
    used from a program with a TKInter interface. This class has methods
    for creating a component which displays all the similarity metrics
    options and takes care of managing the internal state
    """
[docs]    def getGUI(self):
        """
        Returns a GUI component which displays the similarity
        options
        """
        self.base_group = swidgets.SGroupBox("Similarity settings")
        # Set up the layouts
        self._metric_layout = swidgets.SHBoxLayout()
        self._tversky_layout = swidgets.SHBoxLayout()
        self.base_group.layout.addLayout(self._metric_layout)
        self.base_group.layout.addLayout(self._tversky_layout)
        # Now the Tversky parameters
        self._tv_alpha_label = QtWidgets.QLabel('Tversky alpha:',
                                                self.base_group)
        self._tv_alpha_edit = QtWidgets.QLineEdit(str(self._alpha))
        self._tv_alpha_edit.setMaximumWidth(60)
        self._tv_alpha_edit.setValidator(swidgets.SNonNegativeRealValidator())
        self._tv_beta_label = QtWidgets.QLabel('Tversky beta:', self.base_group)
        self._tv_beta_edit = QtWidgets.QLineEdit(str(self._beta))
        self._tv_beta_edit.setMaximumWidth(60)
        self._tv_beta_edit.setValidator(swidgets.SNonNegativeRealValidator())
        # Keep updated with the latest alpha and beta parameters
        self._tv_alpha_edit.textChanged[str].connect(self.update)
        self._tv_beta_edit.textChanged[str].connect(self.update)
        # Lay them out
        self._tversky_layout.addWidget(self._tv_alpha_label)
        self._tversky_layout.addWidget(self._tv_alpha_edit)
        self._tversky_layout.addWidget(self._tv_beta_label)
        self._tversky_layout.addWidget(self._tv_beta_edit)
        self._tversky_layout.addStretch()
        # The metric widgets
        self._metric_label = QtWidgets.QLabel('Similarity metric:',
                                              self.base_group)
        self.metric_combobox = swidgets.SComboBox(items=self.SIMILARITY_METRICS,
                                                  default_item='Tanimoto',
                                                  command=self.setMetricCB)
        # Lay the metric widgets out
        self._metric_layout.addWidget(self._metric_label)
        self._metric_layout.addWidget(self.metric_combobox)
        self._metric_layout.addStretch()
        return self.base_group 
[docs]    def setMetricCB(self, metric):
        """
        A callback for setting the similarity metric - takes care of
        enabling the alpha and beta boxes.  Also makes sure that the program is
        ready to calculate similarity via the chosen metric.
        :type metric: str
        :param metric: the name of the similarity metric to use
        """
        state = metric == "Tversky"
        self._tv_alpha_label.setEnabled(state)
        self._tv_alpha_edit.setEnabled(state)
        self._tv_beta_label.setEnabled(state)
        self._tv_beta_edit.setEnabled(state)
        self.setMetric(metric) 
[docs]    def update(self):
        """
        Update the internal state to reflect the GUI (used mainly
        for alpha and beta values
        """
        try:
            self._alpha = float(self._tv_alpha_edit.text())
        except ValueError:
            # Only happens for blank edits
            pass
        try:
            self._beta = float(self._tv_beta_edit.text())
        except ValueError:
            # Only happens for blank edits
            pass 
[docs]    def getCalculationGUI(self, command, msg=None):
        """
        Create the GUI section that has the calculate button
        :type command: callable object
        :param command: function to be called when the Calculate Similarity
            button is pressed.
        :type msg: str
        :param msg: The message that appears right above the button
        :rtype: swidgets.SGroupBox (QGroupBox object)
        :return: a groupbox with the calculation widgets
        """
        gbox = swidgets.SGroupBox('Similarity Calculation')
        if msg is None:
            msg = 'Similarity is calculated between the entries included in' + \
                    
' the\nWorkspace and the selected entries in the project'
        gbox.layout.addWidget(QtWidgets.QLabel(msg))
        if command:
            swidgets.SPushButton('Calculate Similarity',
                                 layout=gbox.layout,
                                 command=command)
        else:
            swidgets.SPushButton('Calculate Similarity', layout=gbox.layout)
        self.tab_checkbox = swidgets.SCheckBox(
            'Sort selected entries by similarity',
            layout=gbox.layout,
            checked=True)
        return gbox 
[docs]    def getTab(self, command=None, settings=True, calculation=True, msg=None):
        """
        Creates a tab that can be used in a QTabWidget for calculating
        similarity.  The tab has a CanvasFingerprintSimilarityGUI section and a
        Similarity Calculation section (set up here).
        :type command: callable object
        :param command: function to be called when the Calculate Similarity
            button is pressed.
        :type settings: bool
        :param settings: True if the settings section is included, False if not
        :type calculation: bool
        :param calculation: True if the calculation section is included,
            False if not
        :type msg: str
        :param msg: The message that appears right above the button in the
            Calculation section
        :rtype: QWidget
        :return: widget containing the clustering gui
        Usage: QTabWidget.addTab(fp_sim.getTab(doSimilarity))
        """
        widget = QtWidgets.QWidget()
        tab_layout = swidgets.SVBoxLayout(widget)
        if settings:
            tab_layout.addWidget(self.getGUI())
        if calculation:
            tab_layout.addWidget(self.getCalculationGUI(command, msg))
        tab_layout.addStretch()
        return widget 
[docs]    def sortEntryCheck(self):
        """
        Check if entries should be sorted or not (based on the toggle state of
        the Sort selected entries checkbox on the Similarity Tab.
        :rtype: bool
        :return: True if the Sort checkbox is checked, False if it is not or it
            doesn't exist
        """
        try:
            return self.tab_checkbox.isChecked()
        except AttributeError:
            return False