如何使用MouseMoveEvent从QColorDialog更改颜色

时间:2019-08-16 13:50:10

标签: python pyqt5

当我单击QPushButton时,将打开一个QColorDialog。我的问题是: 如何更改QWidget的{​​{1}}中mouseMoveEvent的颜色

QColorDialog

1 个答案:

答案 0 :(得分:2)

tl; dr-不,你不能

不幸的是,QColorDialog不支持这种交互。标准对话框尝试使用系统的本机颜色选择器对话框,该对话框不提供任何API,除了返回选定的颜色外,“非本机”对话框仅包含私有方法和对象,这些方法和对象无法从PyQt中轻松访问。 / p>

我认为使用非本机对话框可以实现您所需的 ,但这非常困难,因为应该“浏览”对话框的子级,找到“ color Shower”小部件并然后安装事件过滤器以获取其mouseMoveEvents,然后使用grab()render()获取小部件屏幕截图,将其转换为QImage并获取像素的颜色。但这并不安全,主要是因为作为“硬编码”对话框,其内容将来可能会更改;长话短说,跨Qt的不同版本(甚至次要版本)可能无法按预期运行。另外,由于花洒具有“十字光标”来显示当前颜色,因此,如果将鼠标悬停在该花洒上,则可能会得到十字光标颜色,从而使上述所有工作都无济于事。

替代解决方案:创建自己的颜色选择器

screenshot of the custom color picker example

前一段时间,我对Qt提供的功能不满意,我创建了一个更复杂的颜色选择工具:我主要在Linux上工作(它没有 actual 本机颜色对话框),并且我我在Windows和MacOS上都已经看到的必需功能,以及Qt4版本的QColorDialog具有的其他功能,这些功能在Qt5的对应版本中却不存在(反之亦然)。
幸运的是,我发现其中的颜色喷漆是如何绘制的,以及如何使用鼠标获得颜色,因此我已经可以回收其中的一部分。请注意,在我的工具中,我还创建了一个“色轮”(中间带有渐变三角形的色环),但这有点复杂。
本示例将显示一个带有颜色选择器的小对话框,并自动将其颜色设置为当前颜色。移动鼠标会自动更新主窗口小部件的背景,但是只有在接受对话框(单击“确定”或按Enter / Return)的情况下,该控件才会应用。

import sys
from PyQt5 import QtCore, QtGui, QtWidgets

class RgbPicker(QtWidgets.QLabel):
    # create a vertical color gradient similar to the "Color Shower"
    # used in QColorDialog
    colorGrads = QtGui.QLinearGradient(0, 0, 1, 0)
    colorGrads.setCoordinateMode(colorGrads.ObjectBoundingMode)
    xRatio = 1. / 6
    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)

    # add a "mask" gradient to support gradients to lighter colors
    maskGrad = QtGui.QLinearGradient(0, 0, 0, 1)
    maskGrad.setCoordinateMode(maskGrad.ObjectBoundingMode)
    maskGrad.setColorAt(0, QtCore.Qt.transparent)
    maskGrad.setColorAt(1, QtCore.Qt.white)

    # create a cross cursor to show the selected color, if any
    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)
    showCursor = False
    cursorPos = QtCore.QPoint()

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setMouseTracking(True)
        self.setFixedSize(220, 200)
        # create a pixmap and paint it with the gradients
        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)
        # a QImage is required to get the color of a specific pixel
        self.image = pixmap.toImage()
        self.currentColor = QtGui.QColor()

    def setColor(self, color):
        self.currentColor = color
        # compute the cursor coordinates according to the color values;
        # this is based on Hue/Saturation/Value data of the color
        h, s, v, a = color.getHsv()
        x = (360 - h) * (self.width() - 1) / 360.
        y = (255 - s) * (self.height() - 1) / 255.
        self.cursorPos = QtCore.QPoint(x, y)
        self.showCursor = True
        self.update()

    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.LeftButton:
            # set the current color and emit the colorChanged signal
            self.currentColor = QtGui.QColor(self.image.pixel(event.pos()))
            self.cursorPos = event.pos()
            self.showCursor = True
            self.update()

    def mouseMoveEvent(self, event):
        if event.pos() in self.rect():
            color = QtGui.QColor(self.image.pixel(event.pos()))
            self.colorChanged.emit(color)
            if event.buttons() == QtCore.Qt.LeftButton:
                # if the left button is pressed, update the current color
                self.currentColor = color
                self.cursorPos = event.pos()
                self.update()

    def leaveEvent(self, event):
        # emit the previously selected color when leaving
        self.colorChanged.emit(self.currentColor)

    def paintEvent(self, event):
        # paint the "color shower"
        QtWidgets.QLabel.paintEvent(self, event)
        if self.showCursor:
            # paint the color "cursor"
            qp = QtGui.QPainter(self)
            qp.setPen(self.cursorPen)
            qp.translate(self.cursorPos)
            qp.drawPath(self.cursorPath)


class ColorPicker(QtWidgets.QDialog):
    colorChanged = QtCore.pyqtSignal(QtGui.QColor)
    def __init__(self, parent=None):
        super().__init__(parent)
        layout = QtWidgets.QVBoxLayout()
        self.setLayout(layout)

        self.rgbPicker = RgbPicker(self)
        layout.addWidget(self.rgbPicker)
        self.rgbPicker.colorChanged.connect(self.colorChanged)

        buttonBox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Cancel)
        layout.addWidget(buttonBox)
        buttonBox.accepted.connect(self.accept)
        buttonBox.rejected.connect(self.reject)

    def getColor(self, color=None):
        if isinstance(color, QtGui.QColor):
            self.rgbPicker.setColor(color)
        # return a color only if the dialog is accepted
        if self.exec_():
            return self.rgbPicker.currentColor

class MainWindow(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        # get the current background color, should we ignore the picker selection
        self.color = self.palette().color(QtGui.QPalette.Window)
        self.colorPicker = ColorPicker(self)
        self.colorPicker.colorChanged.connect(self.setcolorChanged)
        self.colorChooser = QtWidgets.QPushButton("ColorChooser", self)
        self.colorChooser.clicked.connect(self.onColorPicker)
        self.colorChooser.move(10, 10)

    def setcolorChanged(self, color):
        # set the stylesheet *only* for this class, not its children, otherwise
        # you'll set the background for both the button *and* the color picker
        self.setStyleSheet("MainWindow { background-color:%s;}" % color.name())

    def onColorPicker(self):
        color = self.colorPicker.getColor(self.color)
        # update the color only if the dialog is accepted: if the user presses
        # Esc, it will be ignored
        if color:
            print('ok', color.getRgb())
            self.color = color
        self.setcolorChanged(self.color)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())