Rock E Rolla
Rock E Rolla

Reputation: 39

PyQt5 - HSV gradient not RGB gradient

I am working on a color picker and I created a panel that mixes colors. the first part of the panel you can create tint, tone and shades of a color and the second part you can use 2 colors to mix.

enter image description here

However I was faced with a weird situation where my gradient representation on the widget does not reflect the actual colors it is calculating. Here you can see me using "GREEN" and "PINK" and the gradient is the same (RGB gradient?) I achieved this by calculating the interpolation the top bar with RGB color space and on the second bar interpolating in HSV, and this is the result they actually give.

enter image description here

this is my comparation of my gradient tests(upper) with an actual color mixer(below) on the painting program that hosts my code, and it really displays it in HSV.

enter image description here

How do I achieve this gradient transition representation on my widget?

Code test:

def paintEvent(self, event):
    green = QColor('#3c552c')
    pink = QColor('#d9bdcf')
    painter = QPainter(self)
    painter.setPen(QPen(Qt.black, 4, Qt.SolidLine))
    grad1 = QLinearGradient(20,20,190,20)
    grad1.setColorAt(0.0, green)
    grad1.setColorAt(1.0, pink)
    painter.setBrush(QBrush(grad1))
    painter.drawRect(10,10,200,200)

Code currently used:

def Mixer_Display(self):
    # Display Color with Tint, Tone, Shade
    mix_color_tint = str("background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgb(%f, %f, %f), stop:1 rgb(255, 255, 255));" % (self.color_n_red, self.color_n_green, self.color_n_blue))
    self.layout.color_tint.setStyleSheet(mix_color_tint)
    mix_color_tone = str("background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgb(%f, %f, %f), stop:1 rgb(127, 127, 127));" % (self.color_n_red, self.color_n_green, self.color_n_blue))
    self.layout.color_tone.setStyleSheet(mix_color_tone)
    mix_color_shade = str("background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgb(%f, %f, %f), stop:1 rgb(0, 0, 0));" % (self.color_n_red, self.color_n_green, self.color_n_blue))
    self.layout.color_shade.setStyleSheet(mix_color_shade)
    # Display Gradients
    mix_gradient_1 = str("background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgb(%f, %f, %f), stop:1 rgb(%f, %f, %f));" % (self.color_l1_red, self.color_l1_green, self.color_l1_blue, self.color_r1_red, self.color_r1_green, self.color_r1_blue))
    self.layout.gradient_1.setStyleSheet(mix_gradient_1)
    mix_gradient_2 = str("background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgb(%f, %f, %f), stop:1 rgb(%f, %f, %f));" % (self.color_l2_red, self.color_l2_green, self.color_l2_blue, self.color_r2_red, self.color_r2_green, self.color_r2_blue))
    self.layout.gradient_2.setStyleSheet(mix_gradient_2)
    mix_gradient_3 = str("background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgb(%f, %f, %f), stop:1 rgb(%f, %f, %f));" % (self.color_l3_red, self.color_l3_green, self.color_l3_blue, self.color_r3_red, self.color_r3_green, self.color_r3_blue))
    self.layout.gradient_3.setStyleSheet(mix_gradient_3)

Upvotes: 0

Views: 1074

Answers (2)

Rock E Rolla
Rock E Rolla

Reputation: 39

inspired on your answer I did something like this.

main:

# HSV Gradients
mix_hsv_g1 = self.style.HSV_Gradient(self.layout.hsv_g1.width(), self.color_hsv_l1, self.color_hsv_r1)
self.layout.hsv_g1.setStyleSheet(str(mix_hsv_g1))

module:

def HSV_Gradient(self, width, color_left, color_right):
    # Colors
    left = [color_left[3], color_left[4], color_left[5]]
    right = [color_right[3], color_right[4], color_right[5]]
    # Conditions
    cond1 = right[0] - left[0]
    cond2 = (left[0] + 360) - right[0]
    cond3 = right[2] - left[1]
    cond4 = right[2] - left[2]
    # Style String
    slider_gradient = "background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, \n "
    "stop:%s rgb(%s, %s, %s), " % (0.000, color_left[0], color_left[1], color_left[2])
    unit = 1 / width
    for i in range(width):
        # Stop
        stop = round((i * unit), 3)
        # HSV Calculation
        if cond1 <= cond2:
            hue = left[0] + (stop * cond1)
        else:
            hue = left[0] - (stop * cond2)
            if hue <= 0:
                hue = hue + 360
            else:
                pass
        hue = hue / 360
        sat = (left[1] + (stop * cond3)) / 100
        val = (left[2] + (stop * cond4)) / 100
        # HSV to RGB Conversion
        rgb = colorsys.hsv_to_rgb(hue, sat, val)
        red = round(rgb[0]*255,3)
        green = round(rgb[1]*255,3)
        blue = round(rgb[2]*255,3)
        # String
        slider_gradient += "stop:%s rgb(%s, %s, %s), \n " % (stop, red, green, blue)
    slider_gradient += "stop:%s rgb(%s, %s, %s) ) " % (1.000, color_right[0], color_right[1], color_right[2])
    # Return StyleSheet String
    return slider_gradient

Since I am using paint events to control a custom slider I thought in making a StyleSheet instead for the display since the calculation seems a bit long.

Result:

enter image description here

Upvotes: 0

Heike
Heike

Reputation: 24430

You could mimic the HSV gradient by adding extra colors to the gradient. It looks like the addon uses a linear interpolation between the hue, saturation, and value of the two colors, so you could do something like

from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QColor, QPainter, QBrush, QLinearGradient, QPen
import numpy as np


class HSVColorBar(QtWidgets.QFrame):
    def __init__(self, c0, c1, parent=None):
        super().__init__(parent)
        self.c0 = c0
        self.c1 = c1

    @staticmethod
    def color_interpolator(col0, col1, factor):
        h0 = col0.hsvHueF()
        h1 = col1.hsvHueF()
        h1 -= round(h1-h0)
        hue = (h0*(1-factor) + h1*factor) % 1
        sat = col0.hsvSaturationF() * (1 - factor) + col1.hsvSaturationF() * factor
        val = col0.valueF() * (1 - factor) + col1.valueF() * factor
        return QColor.fromHsvF(hue, sat, val)

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setPen(QPen(Qt.black, 4, Qt.SolidLine))
        grad1 = QLinearGradient(0, 0, event.rect().width(), 0)
        # add intermediate colors to mimic hue mixing
        for i in np.linspace(0, 1, 10):
            grad1.setColorAt(i, self.color_interpolator(self.c0, self.c1, i))
        painter.setBrush(QBrush(grad1))
        painter.drawRect(event.rect())

if __name__ == "__main__":
    app = QtWidgets.QApplication([])
    green = QColor('#3c552c')
    pink = QColor('#d9bdcf')
    w = HSVColorBar(pink, green)
    w.show()
    app.exec()

Screenshot screenshot

Upvotes: 1

Related Questions