Source code for schrodinger.ui.qt.matplot_widget
# Matplot widget that can be used in Qt Designer
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtWidgets
from schrodinger.ui.qt.structure2d import LabeledStructureToolTip
[docs]class MatplotCanvas(FigureCanvas):
    """ This is the class to represent FigureCanvas widget """
[docs]    def __init__(self):
        self.fig = Figure()
        self.ax = self.fig.add_subplot(111)
        FigureCanvas.__init__(self, self.fig)
        FigureCanvas.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding,
                                   QtWidgets.QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)
[docs]class MatplotWidget(QtWidgets.QWidget):
    """ This is a widget defined in Qt Designer """
[docs]    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        self.canvas = MatplotCanvas()
        self.vLayout = QtWidgets.QVBoxLayout()
        self.vLayout.addWidget(self.canvas)
        #self.vLayout.addWidget(self.ntb)  # matplot lib toolbar on/off
        self.setLayout(self.vLayout)
[docs]class StructureTooltipMixin(object):
    """
    A class that enables tooltip on matplotlib plots.
    Derived classes might need to re-define showToolTip(), findPoint()
    and addDataSet().
    """
    TOOLTIP_SHOW_DELAY = 300
    TOOLTIP_HIDE_DELAY = 0
[docs]    def __init__(self):
        self.tooltip = None
        self.tooltip_annotation = None
        # for project table use.
        self.tooltip_ds = None
        self.tooltip_eid = None
        self.tooltip_show_timer = QtCore.QTimer()
        self.tooltip_show_timer.setSingleShot(True)
        self.tooltip_show_timer.timeout.connect(self.showToolTip)
        self.tooltip_hide_timer = QtCore.QTimer()
        self.tooltip_hide_timer.setSingleShot(True)
        self.tooltip_hide_timer.timeout.connect(self.hideToolTip)
        self.data_sets = []
        self.st = None
        self.title = ""
[docs]    def showToolTip(self):
        """
        Constructs and displays the tooltip.
        Should be re-defined as needed in derived classes.
        """
        if self.st:
            self.tooltip = LabeledStructureToolTip(structure=self.st)
            if self.title:
                self.tooltip.addLabel(self.title)
        self.tooltip.show()
[docs]    def hideToolTip(self):
        """
        Hide and remove the current tooltip, stops the show timer if
        still running
        """
        if self.tooltip_show_timer.isActive():
            self.tooltip_show_timer.stop()
        if self.tooltip:
            self.tooltip.hide()
            self.tooltip = None
            self.tooltip_ds = None
            self.tooltip_eid = None
[docs]    def addDataSet(self, ds):
        """
        Adds data sets. The reason it is a list of data sets is primarily
        to work with projplot.PlotWindow. In the case of multiple sets,
        derived class should overwrite findPoint method.
        :param ds: Data set to add.
        :type ds: list of 2-element tuples. [(x1, y1), (x2, y2), ...]
            Derived class can use its own type of data sets.
        """
        self.data_sets.append(ds)
[docs]    def mouseMove(self, event):
        """
        A mouse move event handler used to pick points in the plot
        """
        if not event.inaxes:
            self.tooltip_hide_timer.start(self.TOOLTIP_HIDE_DELAY)
            return
        found_point = self.findPoint(event)
        if not found_point and self.tooltip:
            self.tooltip_hide_timer.start(self.TOOLTIP_HIDE_DELAY)
[docs]    def findPoint(self, event):
        """
        Takes care of all the stuff needed if the data point is found.
        Might need to redefine in derived classes.
        :param event: mouse event
        :type event: matplotlib.backend_bases.Event
        :return: Whether the point at the mouse event is found
        :rtype: bool
        """
        if not self.data_sets:
            return False
        if event.xdata is None or event.ydata is None:
            return False
        ds = self.data_sets[0]
        # ds is a list of 2-ele tuples [(x1, y1), (x2, y2)...]
        x_min = min([data[0] for data in ds])
        x_max = max([data[0] for data in ds])
        y_min = min([data[1] for data in ds])
        y_max = max([data[1] for data in ds])
        if (event.xdata >= x_min and event.xdata <= x_max and
                event.ydata >= y_min and event.ydata <= y_max):
            return True
        else:
            return False