Решение , предложенное eyllanes c, как всегда идеально (и умно).
Тем не менее, несколько месяцев go мне приходилось реализовывать свой собственный виджет выбора цвета, который создает виджет «цветная радуга» только из кода, и я хотел бы поделиться частью этого кода, так как я считаю, что он может представлять некоторый интерес, особенно в отношении создания «радуги» и обнаружения цвета.
Я отредактировал его, чтобы сделать его базовым c пригодным для использования виджетом "Выбор цвета".
from PyQt5 import QtCore, QtGui, QtWidgets
# a helper function that ensures that a value is between the given range
sanitize = lambda m, value, M: min(max(m, value), M)
class RgbPicker(QtWidgets.QLabel):
colorGrads = QtGui.QLinearGradient(0, 0, 1, 0)
colorGrads.setCoordinateMode(colorGrads.ObjectBoundingMode)
xRatio = 1. / 6
# the basic "rainbow" gradient
colorGrads.setColorAt(0, QtCore.Qt.red)
colorGrads.setColorAt(1, QtCore.Qt.red)
colorGrads.setColorAt(xRatio, QtCore.Qt.magenta)
colorGrads.setColorAt(xRatio * 2, QtCore.Qt.blue)
colorGrads.setColorAt(xRatio * 3, QtCore.Qt.cyan)
colorGrads.setColorAt(xRatio * 4, QtCore.Qt.green)
colorGrads.setColorAt(xRatio * 5, QtCore.Qt.yellow)
# the superimposed white component
maskGrad = QtGui.QLinearGradient(0, 0, 0, 1)
maskGrad.setCoordinateMode(maskGrad.ObjectBoundingMode)
maskGrad.setColorAt(0, QtCore.Qt.transparent)
maskGrad.setColorAt(1, QtCore.Qt.white)
# the pseudo arrow cursor
cursorPath = QtGui.QPainterPath()
cursorPath.moveTo(-10, 0)
cursorPath.lineTo(-4, 0)
cursorPath.moveTo(0, -10)
cursorPath.lineTo(0, -4)
cursorPath.moveTo(4, 0)
cursorPath.lineTo(10, 0)
cursorPath.moveTo(0, 4)
cursorPath.lineTo(0, 10)
cursorPen = QtGui.QPen(QtCore.Qt.black, 3)
colorChanged = QtCore.pyqtSignal(QtGui.QColor)
def __init__(self, color=None):
QtWidgets.QLabel.__init__(self)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
self.setFixedSize(220, 200)
# create a pixmap that shows the "rainbow" and draw its contents
pixmap = QtGui.QPixmap(self.size())
qp = QtGui.QPainter(pixmap)
qp.fillRect(pixmap.rect(), self.colorGrads)
qp.fillRect(pixmap.rect(), self.maskGrad)
qp.end()
self.setPixmap(pixmap)
self.image = pixmap.toImage()
# a multiplier, used when an arrow key is kept pressed
self.keyTimer = QtCore.QTimer()
self.keyTimer.setInterval(1000)
self.keyTimer.timeout.connect(lambda: setattr(self, 'keyMulti', self.keyMulti + 1))
self._cursorPos = QtCore.QPoint()
self._color = color
self.setColor(color)
@property
def color(self):
return self._color
@property
def cursorPos(self):
return self._cursorPos
@cursorPos.setter
def cursorPos(self, pos):
self._cursorPos = pos
self.update()
def sanitizePos(self, pos):
# sanitize the position within the color rainbow margins
return QtCore.QPoint(sanitize(0, pos.x(), self.width() - 1), sanitize(0, pos.y(), self.height() - 1))
def setColor(self, color):
h, s, v, a = color.getHsv()
# compute the coordinates based on hsv components
x = (360 - h) * (self.width() - 1) / 360.
y = (255 - s) * (self.height() - 1) / 255.
self.cursorPos = QtCore.QPoint(x, y)
def translateColorCursor(self, pos):
# return the color of the given pixel
return QtGui.QColor(self.image.pixel(pos))
def mousePressEvent(self, event):
pos = self.sanitizePos(event.pos())
self._color = self.translateColorCursor(pos)
self.colorChanged.emit(QtGui.QColor(self._color))
self.cursorPos = pos
def mouseMoveEvent(self, event):
pos = self.sanitizePos(event.pos())
self._color = self.translateColorCursor(pos)
self.colorChanged.emit(QtGui.QColor(self._color))
self.cursorPos = pos
def keyPressEvent(self, event):
deltaX = deltaY = 0
# emulation of the Qt internal color picker keyboard navigation
if event.modifiers() & QtCore.Qt.KeypadModifier:
if event.key() in (QtCore.Qt.Key_7, QtCore.Qt.Key_Home):
deltaX = deltaY = -1
elif event.key() in (QtCore.Qt.Key_8, QtCore.Qt.Key_Up):
deltaY = -1
elif event.key() in (QtCore.Qt.Key_9, QtCore.Qt.Key_PageUp):
deltaX = 1
deltaY = -1
elif event.key() in (QtCore.Qt.Key_4, QtCore.Qt.Key_Left):
deltaX = -1
elif event.key() in (QtCore.Qt.Key_6, QtCore.Qt.Key_Right):
deltaX = 1
elif event.key() in (QtCore.Qt.Key_1, QtCore.Qt.Key_End):
deltaX = -1
deltaY = 1
elif event.key() in (QtCore.Qt.Key_2, QtCore.Qt.Key_Down):
deltaY = 1
elif event.key() in (QtCore.Qt.Key_3, QtCore.Qt.Key_PageDown):
deltaX = deltaY = 1
elif event.key() == QtCore.Qt.Key_Left:
deltaX = -1
elif event.key() == QtCore.Qt.Key_Right:
deltaX = 1
elif event.key() == QtCore.Qt.Key_Up:
deltaY = -1
elif event.key() == QtCore.Qt.Key_Down:
deltaY = 1
elif event.key() == QtCore.Qt.Key_Home:
if event.modifiers() == QtCore.Qt.ShiftModifier:
deltaY = -1000
elif event.modifiers() == QtCore.Qt.ControlModifier:
deltaX = deltaY = -1000
else:
deltaX = -1000
elif event.key() == QtCore.Qt.Key_End:
if event.modifiers() == QtCore.Qt.ShiftModifier:
deltaY = 1000
elif event.modifiers() == QtCore.Qt.ControlModifier:
deltaX = deltaY = 1000
else:
deltaX = 1000
elif event.key() == QtCore.Qt.Key_PageUp and not event.modifiers() & QtCore.Qt.KeypadModifier:
deltaY = -10
elif event.key() == QtCore.Qt.Key_PageDown and not event.modifiers() & QtCore.Qt.KeypadModifier:
deltaY = +10
else:
return QtWidgets.QWidget.keyPressEvent(self, event)
if not event.isAutoRepeat():
self.keyTimer.start()
self.keyMulti = 1
if deltaX or deltaY:
multi = self.keyMulti
if event.modifiers() & QtCore.Qt.ShiftModifier:
multi *= 10
deltaX *= multi
deltaY *= multi
pos = self.sanitizePos(QtCore.QPoint(self.cursorPos.x() + deltaX, self.cursorPos.y() + deltaY))
self._color = self.translateColorCursor(pos)
self.colorChanged.emit(QtGui.QColor(self._color))
self.cursorPos = pos
def keyReleaseEvent(self, event):
if not event.isAutoRepeat():
self.keyTimer.stop()
QtWidgets.QWidget.keyReleaseEvent(self, event)
def paintEvent(self, event):
QtWidgets.QLabel.paintEvent(self, event)
qp = QtGui.QPainter(self)
qp.setPen(self.cursorPen)
# translate to the crosshair position and paint it
qp.translate(self.cursorPos)
qp.drawPath(self.cursorPath)
class BlackPicker(QtWidgets.QWidget):
# the "black" selector, which has a gradient based on the current selected
# color of the RgbPicker (selected color -> black)
_rect = QtCore.QRect(0, 8, 16, 200)
grad = QtGui.QLinearGradient(0, 0, 0, 1)
grad.setCoordinateMode(grad.ObjectBoundingMode)
grad.setColorAt(1, QtCore.Qt.black)
arrow = QtGui.QPainterPath()
arrow.lineTo(4, -4)
arrow.lineTo(4, 4)
arrow.closeSubpath()
_color = QtGui.QColor()
_black = -1
blackChanged = QtCore.pyqtSignal(float)
def __init__(self, color):
QtWidgets.QWidget.__init__(self)
self.color = QtGui.QColor(color)
self.setFixedSize(22, 216)
@property
def black(self):
return self._black
@black.setter
def black(self, black):
if black == self._black:
return
self._black = black
self.update()
self.blackChanged.emit(black)
@property
def color(self):
return self._color
@color.setter
def color(self, color):
if color == self._color:
return
self._color = QtGui.QColor(color)
self.grad.setColorAt(0, color)
self.black = color.getCmykF()[3]
def setWhiteColor(self, color):
self.grad.setColorAt(0, color)
self.update()
def setColor(self, color):
self.color = color
def mousePressEvent(self, event):
self.black = sanitize(0, event.pos().y() - self._rect.top(),
self._rect.height()) / 200.
def mouseMoveEvent(self, event):
self.black = sanitize(0, event.pos().y() - self._rect.top(),
self._rect.height()) / 200.
def wheelEvent(self, event):
if event.pixelDelta().y() < 0:
delta = .01
else:
delta = -.01
self.black = sanitize(0, self.black + delta, 1)
def paintEvent(self, event):
qp = QtGui.QPainter(self)
qp.setRenderHints(qp.Antialiasing)
qp.fillRect(self._rect, self.grad)
qp.translate(self._rect.right() + 2, self._rect.top() + self.black * self._rect.height())
qp.setBrush(QtCore.Qt.black)
qp.translate(.5, .5)
qp.drawPath(self.arrow)
class ColorPicker(QtWidgets.QWidget):
colorChanged = QtCore.pyqtSignal(QtGui.QColor)
def __init__(self, color=None, parent=None):
super().__init__(parent)
layout = QtWidgets.QGridLayout(self)
if not (isinstance(color, QtGui.QColor) and color.isValid()):
if isinstance(color, QtCore.Qt.GlobalColor):
color = QtGui.QColor(color)
else:
color = self.palette().color(QtGui.QPalette.WindowText)
self.rgbPicker = RgbPicker(color)
layout.addWidget(self.rgbPicker, 0, 0)
self.blackPicker = BlackPicker(color)
layout.addWidget(self.blackPicker, 0, 1)
self.colorWidget = QtWidgets.QWidget()
layout.addWidget(self.colorWidget, 1, 0, 1, 2)
self.colorWidget.setMinimumHeight(16)
self.colorWidget.setAutoFillBackground(True)
self.colorLabel = QtWidgets.QLabel()
layout.addWidget(self.colorLabel)
self.rgbPicker.colorChanged.connect(self.updateColor)
self.rgbPicker.colorChanged.connect(self.blackPicker.setWhiteColor)
self.blackPicker.blackChanged.connect(self.updateColor)
self.updateColor()
def updateColor(self):
color = self.rgbPicker.color
c, m, y, _, _ = color.getCmykF()
color.setCmykF(c, m, y, self.blackPicker.black)
palette = self.colorWidget.palette()
palette.setColor(palette.Window, color)
self.colorWidget.setPalette(palette)
r, g, b = color.getRgb()[:-1]
hexColor = '{:02X}{:02X}{:02X}'.format(r, g, b)
self.colorLabel.setText('R:{:03} G:{:03} B:{:03} - #{}'.format(
r, g, b, hexColor))
self.colorChanged.emit(color)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
picker = ColorPicker(QtGui.QColor(QtCore.Qt.white))
picker.show()
sys.exit(app.exec())