python - 如何使用 MouseMoveEvent 从 QColorDialog 更改颜色
问题描述
当我单击QPushButton
时,QColorDialog
会打开一个。我的问题是:我如何改变QWidget
inmouseMoveEvent
的颜色QColorDialog
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
import sys
class ColorPicker(QColorDialog):
def __init__(self):
super().__init__()
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.color = None
self.colorpicker = ColorPicker()
self.colorChooser = QPushButton("ColorChooser", self)
self.colorChooser.clicked.connect(self.onColorPicker)
self.colorChooser.move(10, 10)
def onColorPicker(self):
self.get_color = self.colorpicker.getColor()
self.setStyleSheet("background-color:%s;" % self.get_color.name())
if __name__ == "__main__":
app = QApplication(sys.argv)
mw = MainWindow()
mw.setFixedSize(400, 400)
mw.show()
sys.exit(app.exec_())
解决方案
tl;博士 - 不,你不能
不幸的是,QColorDialog 不支持这种交互。标准对话框尝试使用系统的本机颜色选择器对话框,它不提供任何 API,除了返回选定的颜色,而“非本机”对话框只有私有方法和对象,这些方法和对象不容易从 PyQt 访问。
我认为您需要的可以使用非本地对话框来实现,但这非常困难,因为应该“浏览”对话框的子项,找到“彩色淋浴”小部件,然后安装一个事件过滤器来获取它的 mouseMoveEvents,然后使用grab()
orrender()
获取小部件屏幕截图,将其转换为 QImage 并获取像素的颜色。但这并不安全,主要是因为它是一个“硬编码”对话框,它的内容将来可能会发生变化;长话短说,在 Qt 的不同版本(甚至是次要版本)中,这可能无法按预期工作。此外,由于彩色淋浴有一个“十字光标”来显示当前颜色,你可能会冒险,通过将鼠标悬停在它上面,你只会得到十字光标的颜色,
替代解决方案:创建自己的颜色选择器
前段时间我创建了一个更复杂的颜色选择工具,因为我对 Qt 提供的功能不满意:我主要在 Linux 上工作(它没有实际的原生颜色对话框),我需要在 Windows 和MacOS,加上 Qt4 版本的 QColorDialog 所具有的 Qt5 对应物(反之亦然)所缺少的其他东西。
幸运的是,我已经能够为此回收其中的一部分,因为我发现了彩色淋浴是如何绘制的以及如何使用鼠标获得颜色。请注意,在我的工具中,我还创建了一个“色轮”(中间带有渐变三角形的色环),但这有点复杂。
此示例将显示一个带有颜色选择器的小对话框,并自动将其颜色设置为当前颜色。移动鼠标会自动更新主窗口小部件的背景,但只有当对话框被接受时才会应用(通过单击 Ok 或按 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_())
推荐阅读
- vue.js - 如何使用 Vee-Validate 验证选择选项
- php - Wordpress 错误日志不显示特定错误的位置
- html - 影响子 div 的 Wordpress 混合混合模式
- firebase - 通过firebase功能将文件上传到firebase存储的问题
- webgl - 渲染到立方体贴图的非零 mip 级别
- php - PHP函数按年检索两个日期之间的天数
- java - Linux 和 Windows JDBC 时间戳精度的差异?
- javascript - 为什么Firebase在createUserWithEmailAndPassword之后获取uid时返回未定义
- ruby-on-rails - 通过字段组合在表中保留最后/最新记录
- wordpress - Woocommerce 更改购物车和结帐的网址