import sys
from past.utils import old_div
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtWidgets
from schrodinger.Qt.QtCore import QPointF
from schrodinger.Qt.QtCore import QRectF
from schrodinger.Qt.QtGui import QBrush
from schrodinger.Qt.QtGui import QColor
from schrodinger.Qt.QtGui import QLinearGradient
from schrodinger.Qt.QtGui import QPainter
from schrodinger.Qt.QtGui import QPen
from schrodinger.Qt.QtWidgets import QColorDialog
from schrodinger.Qt.QtWidgets import QComboBox
from schrodinger.Qt.QtWidgets import QGraphicsScene
from schrodinger.Qt.QtWidgets import QGraphicsView
from schrodinger.Qt.QtWidgets import QPushButton
from schrodinger.Qt.QtWidgets import QVBoxLayout
from schrodinger.Qt.QtWidgets import QWidget
[docs]class GradientSelectorView(QGraphicsView):
[docs]    def __init__(self):
        QGraphicsView.__init__(self)
        self._select = None
        self.rescale(self.width())
        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.setRenderHints(QPainter.Antialiasing)
        border = 8
        self.setScene(QGraphicsScene(-10000, -10000, 20000, 20000))
        self.fitInView(0 - border, 0 - border, self._scale + 2 * border,
                       0 + 2 * border, QtCore.Qt.KeepAspectRatio)
        self._select = GradientSelector(self)
        self.scene().addItem(self._select)
        self.scene().addItem(self._select._ghostColor)
        c1 = self.addColorStop(0., QColor(0, 0, 0))
        c2 = self.addColorStop(1., QColor(255, 0, 0))
        c1._fixed = True
        c2._fixed = True
        self._grabbedColor = None
        self.setMouseTracking(True) 
    gradientUpdated = QtCore.pyqtSignal()
    preciseGradientUpdated = QtCore.pyqtSignal()
[docs]    def setLimits(self, m, M):
        self._select.setLimits(m, M) 
[docs]    def setValues(self, values):
        self._select.setValues(values) 
[docs]    def colorAt(self, x):
        return self._select.colorAt(x) 
[docs]    def resizeEvent(self, event):
        self.rescale(event.size().width())
        border = 15
        self.fitInView(0 - border, 0 - border, self._scale + 2 * border,
                       0 + 2 * border, QtCore.Qt.KeepAspectRatio) 
[docs]    def rescale(self, width):
        self._scale = width
        if (self._select):
            for color in self._select._colors:
                color.moveTo(color.getX()) 
[docs]    def mouseReleaseEvent(self, event):
        if self._grabbedColor and self._mouseTravelledDistance < 1:
            color = QColorDialog.getColor(self._grabbedColor.getColor(), None,
                                          "Choose Color")
            self._grabbedColor.setColor(color)
        if self._grabbedColor:
            self.gradientUpdated.emit()
        self._grabbedColor = None
        self._grabbedColorDeleted = False 
[docs]    def grabbedColorMoveEvent(self, event):
        self._mouseTravelledDistance += (
            event.pos() - self._lastMousePress).manhattanLength()
        self._lastMousePress = event.pos()
        if self._grabbedColor._fixed:
            return
        scenePos = self.mapToScene(event.pos())
        x = old_div(scenePos.x(), self._scale)
        y = scenePos.y()
        if (y < 0 or y > 20):
            if not self._grabbedColorDeleted:
                self._select.removeColor(self._grabbedColor)
                self._grabbedColorDeleted = True
        else:
            if self._grabbedColorDeleted:
                self._select.addColor(self._grabbedColor)
                self._grabbedColorDeleted = False
        if (x > 0 and x < 1):
            self._grabbedColor.moveTo(x) 
[docs]    def mouseMoveEvent(self, event):
        if (self._grabbedColor):
            self.grabbedColorMoveEvent(event)
            return
        p = event.pos()
        sceneP = self.mapToScene(p)
        x = old_div(sceneP.x(), self._scale)
        y = sceneP.y()
        if (y < 0 or y > 20):
            self._select._ghostColor.hide()
            return
        item = self.scene().itemAt(sceneP)
        if (item and item != self._select._ghostColor):
            self._select._ghostColor.hide()
            return
        if (x > 0 and x < 1):
            self._select._ghostColor.show()
            self._select._ghostColor.moveTo(x)
            self._select._ghostColor.setColor(self._select.colorAt(x)) 
[docs]    def mousePressEvent(self, event):
        self._grabbedColor = None
        p = event.pos()
        sceneP = self.mapToScene(p)
        item = self.scene().itemAt(sceneP)
        if item and not item._ghost:
            self._grabbedColor = item
            self._lastMousePress = p
            self._mouseTravelledDistance = 0
            return
        if (self._select._ghostColor.isVisible()):
            self.addColorStop(self._select._ghostColor.getX(),
                              self._select._ghostColor.getColor())
            self._select._ghostColor.hide() 
[docs]    def addColorStop(self, x, color):
        color = ColorSelector(x, color, self._select)
        self._select.addColor(color)
        return color  
[docs]class GradientSelector(QtWidgets.QGraphicsItem):
[docs]    def __init__(self, view):
        QtWidgets.QGraphicsItem.__init__(self)
        self.setPos(0, 0)
        self._colors = []
        self._view = view
        self._gradient = QLinearGradient(0, 0, self._view._scale, 0)
        self.recalculateGradient()
        self._ghostColor = ColorSelector(0.5, self.colorAt(0.5), self)
        self._ghostColor.setZValue(-1)
        self._ghostColor._ghost = True
        self._ghostColor.setOpacity(0.2)
        self._ghostColor.hide()
        self._grabbedColor = None
        self._grabbedColorDeleted = False
        self._shownValues = []
        self._barHeight = 10
        self.setLimits(0., 1.) 
[docs]    def setLimits(self, minim, maxim):
        self._min = minim
        self._max = maxim 
[docs]    def setValues(self, values):
        span = self._max - self._min
        self._shownValues = [old_div((x - self._min), span) for x in values]
        self.update() 
[docs]    def mousePressEvent(self, event):
        pass 
[docs]    def mouseReleaseEvent(self, event):
        pass 
[docs]    def addColor(self, color):
        self._colors.append(color)
        self._colors.sort(key=lambda col: col.getX())
        self._view.scene().addItem(color)
        self.recalculateGradient() 
[docs]    def removeColor(self, color):
        self._colors.remove(color)
        self._view.scene().removeItem(color)
        self.recalculateGradient() 
[docs]    def colorAt(self, x):
        lastColor = None
        if x <= 0.:
            return self._colors[0].getColor()
        elif x >= 1.:
            return self._colors[-1].getColor()
        for color in self._colors:
            if lastColor is None:
                lastColor = color
                continue
            newx = color.getX()
            if (newx > x):
                oldx = lastColor.getX()
                f = old_div((x - oldx), (newx - oldx))
                newColor = color.getColor()
                oldColor = lastColor.getColor()
                redF = oldColor.redF() * (1 - f) + newColor.redF() * f
                greenF = oldColor.greenF() * (1 - f) + newColor.greenF() * f
                blueF = oldColor.blueF() * (1 - f) + newColor.blueF() * f
                returnC = QColor()
                returnC.setRedF(redF)
                returnC.setGreenF(greenF)
                returnC.setBlueF(blueF)
                return returnC
            lastColor = color
        return QColor(0, 0, 0) 
[docs]    def recalculateGradient(self):
        width = 3
        self._gradient = QLinearGradient(0, 0, self._view._scale, 0)
        for color in self._colors:
            self._gradient.setColorAt(color.getX(), color.getColor())
        self._pen = QPen()
        self._pen.setCapStyle(QtCore.Qt.RoundCap)
        self._pen.setWidthF(width)
        self._pen.setBrush(QBrush(self._gradient))
        self.update()
        self._view.preciseGradientUpdated.emit() 
[docs]    def getGradient(self):
        return self._gradient 
[docs]    def paint(self, painter, options, widget=None):
        painter.save()
        painter.setPen(self._pen)
        painter.drawLine(0., 0., self._view._scale, 0.)
        for value in self._shownValues:
            painter.drawLine(value * self._view._scale, -self._barHeight,
                             value * self._view._scale, 0.)
        painter.restore() 
[docs]    def boundingRect(self):
        width = 3
        return QRectF(0 - width, 0 - width - self._barHeight,
                      self._view._scale + 2 * width,
                      0 + 2 * width + self._barHeight)  
[docs]class ColorSelector(QtWidgets.QGraphicsItem):
[docs]    def __init__(self, x, color, parent):
        self._ghost = False
        self._fixed = False
        QtWidgets.QGraphicsItem.__init__(self)
        self._color = color
        self._rad = 5
        self._parent = parent
        self.moveTo(x) 
[docs]    def moveTo(self, x):
        self._x = x
        self.setPos(x * self._parent._view._scale, 0.)
        self.update()
        if not self._ghost:
            self._parent.recalculateGradient() 
[docs]    def getColor(self):
        return self._color 
[docs]    def setColor(self, color):
        self._color = color
        self.update()
        if not self._ghost:
            self._parent.recalculateGradient() 
[docs]    def getX(self):
        return self._x 
[docs]    def paint(self, painter, options, widget=None):
        painter.save()
        painter.setPen(QColor(0, 0, 0))
        painter.setBrush(self._color)
        painter.drawEllipse(QPointF(0, self._rad + 5), self._rad, self._rad)
        painter.restore() 
[docs]    def boundingRect(self):
        return QRectF(-self._rad, 5, 2 * self._rad, 2 * self._rad)  
if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    view = PropertyWidget()
    view.show()
    sys.exit(app.exec_())