当我单击QPushButton
时,将打开一个QColorDialog
。我的问题是:
如何更改QWidget
的{{1}}中mouseMoveEvent
的颜色
QColorDialog
答案 0 :(得分:2)
不幸的是,QColorDialog不支持这种交互。标准对话框尝试使用系统的本机颜色选择器对话框,该对话框不提供任何API,除了返回选定的颜色外,“非本机”对话框仅包含私有方法和对象,这些方法和对象无法从PyQt中轻松访问。 / p>
我认为使用非本机对话框可以实现您所需的 ,但这非常困难,因为应该“浏览”对话框的子级,找到“ color Shower”小部件并然后安装事件过滤器以获取其mouseMoveEvents,然后使用grab()
或render()
获取小部件屏幕截图,将其转换为QImage并获取像素的颜色。但这并不安全,主要是因为作为“硬编码”对话框,其内容将来可能会更改;长话短说,跨Qt的不同版本(甚至次要版本)可能无法按预期运行。另外,由于花洒具有“十字光标”来显示当前颜色,因此,如果将鼠标悬停在该花洒上,则可能会得到十字光标颜色,从而使上述所有工作都无济于事。
前一段时间,我对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_())