PySideでcolorSliderGrpモドキを作る方法

takkun
どうも!たっくんです。
みなさんは、レッドブル派ですか?モンスター派ですか!?
僕は、レッドブル派です(゚∀゚)

今回は、今までのモドキシリーズをベースに、「colorSliderGrpモドキ」を作っていきたいと思います!スライダーで明るさを微調整できるのが、結構便利ですよね(*´ω`*)

まだご覧でない方は、是非今までのモドキシリーズもご覧ください!

PySideでintSliderGrpモドキを作る方法!!

2017.08.23

PySideでfloatSliderGrpモドキを作る方法!!

2017.08.24

colorSliderGrpの構造

Mayaコマンドで「colorSliderGrp」を作ってみると以下のようになります!

左側の白く塗りつぶされたところをクリックすると、カラーダイアログが表示されて好きな色を選ぶことができ、スライダーを動かすと明るさを変更することができますね!厄介なのは、白く塗りつぶされたボタンの所ですね、、、

今回は、「QPushButton」と「Style Sheet」を組み合わせて、なるべくローコストで作ってみたいと思います!

色のボタンを作る!

色のボタンは、他にも使えることがあるかもしれないので独立したクラスで作成します。これを後でスライダーと連動するようにしたいと思います(゚∀゚)

クラスの骨格

まずは、QPushButtonをカスタマイズして、色のボタンを作成したいと思います!ついでに、ボタンが盛り上がったような雰囲気はいらないのでフラットにしてしまいます。

from PySide import QtCore, QtGui

class ColorButton(QtGui.QPushButton):	
	def __init__(self, *args, **kwargs):
		super(ColorButton, self).__init__(*args, **kwargs)
		self.setFlat(True)
		self.__color = QtGui.QColor(255, 255, 255)# 1

QPushButtonは色を管理することができないので、PySideの「QColor」を使って、インスタンス変数に色の情報を保存しておきます。「QColor」を使うと簡単にHSVに変換したりできるので、listでかんりするより幾分楽になるかと思います。(1)

色を選択できるようにする

次は、このボタンをクリックされたら色を選択してもらうためのダイアログがでるようにします。PySideにある「QColorDialog」を使って済ませてしまいますが、Mayaのダイアログとは違います(´・ω・`)

from PySide import QtCore, QtGui

class ColorButton(QtGui.QPushButton):
	def __init__(self, *args, **kwargs):
		super(ColorButton, self).__init__(*args, **kwargs)
		self.setFlat(True)
		self.clicked.connect(self.showDialog)
		
		self.__color = QtGui.QColor(255, 255, 255)
		
	def showDialog(self):
		color = QtGui.QColorDialog.getColor(self.__color)# 1
		if not color.isValid():# 2
			return
		
		self.__color = color# 3

色のダイアログを出す時は、「QColorDialog」の「getColor」を使うと簡単に色を選んでもらうロジックが出来上がります。引数を指定することで初期の色を指定できるので、内部に保存しておいた色から変更できるようにします。(1)

ダイアログ系の場合は、何らかの理由で中断されたときの処理が必要ですね!今回は「isValid」で中断の有無を調べることができますので、「False」の場合ははじくようにしておきます。(2)

最後に、ダイアログで選ばれた色を、インスタンス変数に上書きしておきます。

ボタンの描画

このままでは、ただのフラットにしたボタンが表示されるだけです。。。選ばれた色が背景色になるように「Style Sheet」を使って描画をコントロールしたいと思います。

from PySide import QtCore, QtGui

class ColorButton(QtGui.QPushButton):	
	def __init__(self, *args, **kwargs):
		super(ColorButton, self).__init__(*args, **kwargs)
		self.setFlat(True)
		self.clicked.connect(self.showDialog)
		
		self.__color = QtGui.QColor(255, 255, 255)
		self.__update()# 3
		
	def showDialog(self):
		color = QtGui.QColorDialog.getColor(self.__color)
		if not color.isValid():
			return
		
		self.__color = color
		self.__update()# 3
		
	def __update(self):
		r, g, b, a = self.__color.getRgb()# 1
		self.setStyleSheet(
			'*{border:none;background:rgb(%s, %s, %s);}' % (r, g, b)
			)# 2

内部に保存した色情報は「QColor」になっているので、処理しやすいようにRGBAの情報を取り出します。

以下のCSSに、この情報を当てはめてStyle Sheetを完成させて適用します。(2)コードでは見やすさより、1行になるようにしました(*´ω`*)

*{
	border : none;
	background : rgb(R, G, B);
}

ボタンの描画するメソッドを、「__init__の最後」「色を選んだ後」に追加して、設定と描画が連動するようにします。(3)

シグナルの作成

色を変更した時のシグナルを用意して、他のウィジェットと連動しやすくします。

from PySide import QtCore, QtGui

class ColorButton(QtGui.QPushButton):
	colorChanged = QtCore.Signal(QtGui.QColor)# 1
	
	def __init__(self, *args, **kwargs):
		super(ColorButton, self).__init__(*args, **kwargs)
		self.setFlat(True)
		self.clicked.connect(self.showDialog)
		
		self.__color = QtGui.QColor(255, 255, 255)
		self.__update()
		
	def showDialog(self):
		color = QtGui.QColorDialog.getColor(self.__color)
		if not color.isValid():
			return
		
		self.__color = color
		self.__update()
		self.colorChanged.emit(self.__color)# 2
	
	def __update(self):
		r, g, b, a = self.__color.getRgb()
		self.setStyleSheet(
			'*{border:none;background:rgb(%s, %s, %s);}' % (r, g, b)
			)

シグナルの追加は、今までのモドキシリーズでもでてきましたね!今回は引数に「QColor」を指定しました。intやfloat以外にも、色々なモノをしていできるのでホント便利ですね!(1)

作成したシグナルを、「showDialog」でインスタンス変数を更新した後にemitするようにします。(2)

setterとgetterの用意

ここでは、最低限のsetterとgetterを用意しました。他にも必要に応じて追加すると、便利なクラスになると思います!setterで色を変更した場合は、「描画の更新」と「シグナルのemit」を忘れないようにします!

from PySide import QtCore, QtGui

class ColorButton(QtGui.QPushButton):
	colorChanged = QtCore.Signal(QtGui.QColor)
	
	def __init__(self, *args, **kwargs):
		super(ColorButton, self).__init__(*args, **kwargs)
		self.setFlat(True)
		self.clicked.connect(self.showDialog)
		
		self.__color = QtGui.QColor(255, 255, 255)
		self.__update()
		
	def showDialog(self):
		color = QtGui.QColorDialog.getColor(self.__color)
		if not color.isValid():
			return
		
		self.__color = color
		self.__update()
		self.colorChanged.emit(self.__color)
	
	def __update(self):
		r, g, b, a = self.__color.getRgb()
		self.setStyleSheet(
			'*{border:none;background:rgb(%s, %s, %s);}' % (r, g, b)
			)
	
	def color(self):
		return self.__color
		
	def setColor(self, color):
		self.__color = color
		self.__update()
		self.colorChanged.emit(self.__color)
	
	def rgba(self):
		return self.__color.getRgb()
	
	def setRgba(self, r, g, b, a=255):
		self.__color.setRgb( r, g, b, a )
		self.__update()
		self.colorChanged.emit(self.__color)
		
	def hsv(self):
		return self.__color.getHsv()
	
	def setHsv(self, h, s, v, a=255):
		self.__color.setHsv(h, s, v, a)
		self.__update()
		self.colorChanged.emit(self.__color)

ColorSliderを作る!

続いて、作成したColorButtonとQSliderを組み合わせて行きたいと思います!(`・ω・´)ゞ

クラスの骨格

基本的な部分は、いままでのシリーズと同じです!

from PySide import QtCore, QtGui

class ColorButton(QtGui.QPushButton):
	~中略~
	
class ColorSlider(QtGui.QWidget):
	def __init__(self, *args, **kwargs):
		super(ColorSlider, self).__init__(*args, **kwargs)
		layout = QtGui.QHBoxLayout(self)
		layout.setContentsMargins(0, 0, 0, 0)
		
		self.__button = ColorButton(self)
		self.__button.setMinimumWidth(70)# 1
		self.__button.setMinimumHeight(10)# 1
		layout.addWidget(self.__button)
		
		self.__slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
		self.__slider.setRange(0, 255)
		self.__slider.setValue(self.__button.color().value())# 2
		layout.addWidget(self.__slider)

色のボタンを、Mayaのような感じにするために、70px X 10px より小さくならないようにしておきます。別途、最大値も設定したほうが良いかもしれません。(1)

スライダーの初期値は、色ボタンに設定された明度になるようにします。自分で作成したsetterで「QColor」を受け取り「value()」で明度を取得しています。(2)

ボタンとスライダーの関連付け

いままでのシリーズと同様に、直接関連付けをするのではなく一度クッションをはさみます。

from PySide import QtCore, QtGui

class ColorButton(QtGui.QPushButton):
	~中略~
	
class ColorSlider(QtGui.QWidget):
	def __init__(self, *args, **kwargs):
		super(ColorSlider, self).__init__(*args, **kwargs)
		layout = QtGui.QHBoxLayout(self)
		layout.setContentsMargins(0, 0, 0, 0)
		
		self.__button = ColorButton(self)
		self.__button.setMinimumWidth(70)
		self.__button.setMinimumHeight(10)
		self.__button.colorChanged[QtGui.QColor].connect(
			self.colorChangedCallback
			)
		layout.addWidget(self.__button)
		
		self.__slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
		self.__slider.setRange(0, 255)
		self.__slider.setValue(self.__button.color().value())
		self.__slider.valueChanged[int].connect(self.colorChangedCallback)
		layout.addWidget(self.__slider)
		
	def colorChangedCallback(self, value):
		sender = self.sender()
		if sender == self.__button:# ボタンから変更された場合
			color = self.__button.color()
			self.__slider.blockSignals(True)
			self.__slider.setValue(color.value())
			self.__slider.blockSignals(False)
		
		elif sender == self.__slider:# スライダーから変更された場合
			hsv = self.__button.hsv()
			self.__button.blockSignals(True)
			self.__button.setHsv(hsv[0], hsv[1], value)# 1
			self.__button.blockSignals(False)

スライダーから色を変更した場合は、ボタンに設定された色のHSVを取得して明度だけスライダーの値に差し替えています。(1)もっとスマートにやりたい場合は、明度だけ変更できるsetterを用意したほうがいいかもしれません(*´ω`*)b

シグナルの作成

最後に、シグナルを作成してColorSliderの変更を通知できるようにしておきましょう!ここでも、シグナルの引数に「QColor」を指定しました。

from PySide import QtCore, QtGui

class ColorButton(QtGui.QPushButton):
	~中略~
	
class ColorSlider(QtGui.QWidget):
	colorChanged = QtCore.Signal(QtGui.QColor)#シグナルの作成
	
	def __init__(self, *args, **kwargs):
		super(ColorSlider, self).__init__(*args, **kwargs)
		layout = QtGui.QHBoxLayout(self)
		layout.setContentsMargins(0, 0, 0, 0)
		
		self.__button = ColorButton(self)
		self.__button.setMinimumWidth(70)
		self.__button.setMinimumHeight(10)
		self.__button.colorChanged[QtGui.QColor].connect(self.colorChangedCallback)
		layout.addWidget(self.__button)
		
		self.__slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
		self.__slider.setRange(0, 255)
		self.__slider.setValue(self.__button.color().value())
		self.__slider.valueChanged[int].connect(self.colorChangedCallback)
		layout.addWidget(self.__slider)
		
	def colorChangedCallback(self, value):
		sender = self.sender()
		if sender == self.__button:
			color = self.__button.color()
			self.__slider.blockSignals(True)
			self.__slider.setValue(color.value())
			self.__slider.blockSignals(False)
		
		elif sender == self.__slider:
			hsv = self.__button.hsv()
			self.__button.blockSignals(True)
			self.__button.setHsv(hsv[0], hsv[1], value)
			self.__button.blockSignals(False)
		
		self.colorChanged.emit(self.__button.color())# シグナルのエミット

以上、完成品はコチラ!

ColorButtonのように、setter、getterを作成すると、より使いやすいクラスになると思います!(`・ω・´)ゞ