Source code for schrodinger.ui.qt.smatplotlib

"""
Contains Schrodinger helper widgets for matplotlib display in PyQt
"""
# Copyright Schrodinger, LLC. All rights reserved.

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.figure import Figure

from schrodinger.Qt import QtCore
from schrodinger.Qt import QtGui
from schrodinger.Qt import QtWidgets
from schrodinger.ui.qt import navtoolbar


class SmatplotlibToolbar(navtoolbar.NavToolbar):
    """
    Kept for backward compatibility - use navtoolbar.NavToolbar instead

    :deprecated: use schrodinger.ui.qt.navtoolbar.NavToolbar instead
    """


class SmatplotlibCanvas(FigureCanvasQTAgg):
    """
    Custom subclass of FigureCanvasQTAgg that does the following:
        - Allows for simultaneous definition of the Canvas, Figure and toolbar
            objects, and placing them in a layout
        - Fixes known bugs with the mouse events detecting modifier keys
    """

    def __init__(self,
                 width=5,
                 height=4,
                 dpi=100,
                 toolbar=True,
                 layout=None,
                 expanding=True,
                 **kwargs):
        """
        :type width: number
        :param width: width of the plot Figure in inches

        :type height: number
        :param height: height of the plot Figure in inches

        :type dpi: int
        :param dpi: resolution of the plot

        :type toolbar: boolean
        :param toolbar: True if the toolbar should be created, but the parent
            parameter must be supplied in this case

        :type layout: QLayout object
        :param layout: If layout is supplied, the toolbar and canvas will be
            placed in it.

        Extra keyword arguments are passed to the toolbar function
        """

        self.fig = Figure(figsize=(width, height), dpi=dpi)
        # self._control_down is used to capture when the control key is down
        self._control_down = False
        FigureCanvasQTAgg.__init__(self, self.fig)
        try:
            # PYTHON-2324
            import pyemf
        except ImportError:
            try:
                del self.filetypes['emf']
            except KeyError:
                pass
        if expanding:
            FigureCanvasQTAgg.setSizePolicy(self,
                                            QtWidgets.QSizePolicy.Expanding,
                                            QtWidgets.QSizePolicy.Expanding)
        FigureCanvasQTAgg.updateGeometry(self)

        # Do not allow shrinking below this size (fixes PYAPP-6020):
        self.setMinimumSize(100, 100)

        # Create the toolbar if asked for
        if toolbar:
            self.toolbar = navtoolbar.NavToolbar(self, self, **kwargs)
            self.toolbar.update()
            if layout is not None:
                layout.addWidget(self.toolbar)

        if layout is not None:
            layout.addWidget(self)

        # Custom handling of events
        self.installEventFilter(self)

    def eventFilter(self, obj, event):
        """
        The canvas needs to grab keyboard input in order to reliably tell when
        a modifier key (shift or control) is down when a mouse button is
        pressed/released.

        Some environments have trouble with passing the control key in events -
        a control-left-click is often mapped to a right-click.  Therefore we do
        our best to capture on our own when the control key is down.  Note that
        even this breaks for multiple control-clicks on Linux running under
        VMWare Fusion on Macs, because VMWare by default "lifts" the control
        key after the first click.

        :type obj: not used
        :param obj: not used

        :type event: QEvent
        :param event: the event object
        """

        # Grab and release the keyboard when the mouse enters or leaves the
        # canvas
        if event.type() == QtCore.QEvent.Enter:
            self.grabKeyboard()
        elif event.type() == QtCore.QEvent.Leave:
            self.releaseKeyboard()
        # Capture when the control key is pressed or raised.
        elif event.type() == QtCore.QEvent.KeyPress:
            key_event = QtGui.QKeyEvent(event)
            if key_event.key() == QtCore.Qt.Key_Control:
                self._control_down = True
        elif event.type() == QtCore.QEvent.KeyRelease:
            key_event = QtGui.QKeyEvent(event)
            if key_event.key() == QtCore.Qt.Key_Control:
                self._control_down = False
        return False

    def mousePressEvent(self, event):
        """
        Add in the control modifier key to the button_press_event

        :type event: QMouseEvent
        :param event: the QMouseEvent that triggered this handler

        self._key is matplotlib's way of storing the modifier keys
        """

        if not self._key and self._control_down:
            self._key = 'control'
        FigureCanvasQTAgg.mousePressEvent(self, event)

    def mouseReleaseEvent(self, event):
        """
        Add in the control modifier key to the button_release_event

        :type event: QMouseEvent
        :param event: the QMouseEvent that triggered this handler

        self._key is matplotlib's way of storing the modifier keys
        """

        if not self._key and self._control_down:
            self._key = 'control'
        FigureCanvasQTAgg.mouseReleaseEvent(self, event)