2016/11/01(火) [n年前の日記]
#2 [python] PySideとQColorとHSL
PySideを使ってRGB指定もしくはHSL(色相、彩度、輝度)指定で色を選択できるかテスト。
こんな感じになった。
_convert_hsl.py
RGBとHSLの変換は、こんな感じでイケそうだなと。
こんな感じになった。
_convert_hsl.py
u"""
QSliderとQSpinBoxを1つのWidgetにまとめて並べてみるサンプル.
RGB/HSLのQSlider + QSpinBoxを用意して値を連動させる。結果を色で表示する。
Windows10 x64 + Python 2.7.11 + PySide 1.2.4
"""
import sys
from PySide.QtCore import * # NOQA
from PySide.QtGui import * # NOQA
class LabelSliderSpinBox(QWidget):
"""Slider and SpinBox."""
# Signalを用意
valueChanged = Signal(int)
def __init__(self, text, parent=None):
"""init."""
super(LabelSliderSpinBox, self).__init__(parent)
# self.setContentsMargins(0, 0, 0, 0)
hl = QHBoxLayout()
hl.setContentsMargins(0, 0, 0, 0)
# hl.setSpacing(0)
self.lbl = QLabel(text, self)
self.sld = QSlider(Qt.Horizontal, self)
self.spb = QSpinBox(self)
hl.addWidget(self.lbl)
hl.addWidget(self.sld)
hl.addWidget(self.spb)
self.setLayout(hl)
self.sld.valueChanged[int].connect(self.changed_slider_value)
self.spb.valueChanged[int].connect(self.changed_spinbox_value)
# 用意したシグナルとスロットを関連付ける
self.valueChanged[int].connect(self.changedValue)
def setText(self, text):
"""set QLabel text."""
self.lbl.setText(text)
def setRange(self, start_v, end_v):
"""set range QSlider and QSpinBox."""
self.sld.setRange(start_v, end_v)
self.spb.setRange(start_v, end_v)
def setValue(self, value):
"""set value to QSlider and QSpinBox."""
self.sld.setValue(value)
self.spb.setValue(value)
def value(self):
"""get value."""
return self.spb.value()
def changed_slider_value(self, n):
"""changed slider value."""
self.spb.setValue(n)
def changed_spinbox_value(self, n):
"""changed spinbox value."""
self.sld.setValue(n)
self.valueChanged.emit(n) # 値が変わったのでシグナルを発行
# スロットを用意する
@Slot(int)
def changedValue(self, value):
"""changed slider or spinbox value."""
pass
# print("value = %d" % value)
class MyWidget(QWidget):
"""My Widget."""
def __init__(self, *argv, **keywords):
"""init."""
super(MyWidget, self).__init__(*argv, **keywords)
self.setWindowTitle("Color Slider")
self.event_ignore = False
l = QVBoxLayout()
# color display button
btn = QPushButton()
btn.setFixedSize(64, 64)
# set button background-color
btn.setStyleSheet("QWidget { background-color: %s }"
% QColor(128, 128, 128).name())
l.addWidget(btn)
btn.clicked.connect(self.show_color_dialog)
self.btn = btn
# sliders
data = [
("R", 0, 255, 128), ("G", 0, 255, 128), ("B", 0, 255, 128),
("H", 0, 360, 180), ("S", 0, 255, 128), ("L", 0, 255, 128)]
self.sliders = []
for (i, d) in enumerate(data):
text, start_v, end_v, init_v = d
w = LabelSliderSpinBox(text, self)
w.setRange(start_v, end_v)
w.setValue(init_v)
l.addWidget(w)
self.sliders.append(w)
if i < 3:
w.valueChanged.connect(self.changed_rgb)
else:
w.valueChanged.connect(self.changed_hsl)
self.setLayout(l)
def update_rgb_disp(self, col):
"""update RGB disp button."""
# set background-color
self.btn.setStyleSheet("QWidget { background-color: %s }"
% col.name())
def set_sliders_rgb(self, col):
"""set RGB slider value."""
r, g, b = col.red(), col.green(), col.blue()
self.set_sliders(0, (r, g, b))
def set_sliders_hsl(self, col):
"""set HSL slider value."""
h, s, l = col.hslHue(), col.hslSaturation(), col.lightness()
self.set_sliders(3, (h, s, l))
def set_sliders(self, idx, v):
"""set 3 slider value."""
for i in range(3):
if self.sliders[idx + i].value() != v[i]:
self.sliders[idx + i].setValue(v[i])
def get_sliders(self, idx):
"""get 3 slider value."""
v = []
for i in range(3):
v.append(self.sliders[idx + i].value())
return tuple(v)
def changed_rgb(self, _):
"""changed RGB value."""
if not self.event_ignore:
self.event_ignore = True
r, g, b = self.get_sliders(0)
col = QColor(r, g, b)
self.set_sliders_hsl(col)
self.update_rgb_disp(col)
self.event_ignore = False
def changed_hsl(self, _):
"""changed HSL value."""
if not self.event_ignore:
self.event_ignore = True
h, s, l = self.get_sliders(3)
col = QColor()
col.setHsl(h, s, l)
self.set_sliders_rgb(col)
self.update_rgb_disp(col)
self.event_ignore = False
def show_color_dialog(self):
"""open QColorDialog."""
col = QColorDialog.getColor()
if col.isValid():
self.set_sliders_rgb(col)
def main():
"""Main."""
app = QApplication(sys.argv)
w = MyWidget()
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
RGBとHSLの変換は、こんな感じでイケそうだなと。
◎ RGBからHSLの変換について。 :
PySideには、
_QColor
という、色情報を管理できるクラスがあるのだけど。この QColor には、RGB値とHSL値の変換機能も含まれているようで、わざわざ自分で変換処理を書かなくても済んでしまった。ありがたや。
例えば…。col = QColor(r, g, b) で生成した後、以下のような記述でHSLが取り出せる。
HSLからRGBに変換することもできる。col = QColor() で生成した後、col.setHsl(h, s, l) でHSLを設定してやれば、以下のような記述でRGB値が取り出せる。
整数ではなく、小数点で得ることもできる模様。その場合、getRgbF() や getHslF() のように、最後にFがついた関数を呼ぶ。返り値は、0.0〜1.0、とドキュメントには書いてあるように見える。
そもそも Python 自体に、 _colorsys なるその手の処理をしてくれるモジュールがあるらしい。今回は使ってないけれど。Python は便利だなあ…。
例えば…。col = QColor(r, g, b) で生成した後、以下のような記述でHSLが取り出せる。
- h = col.hslHue() で色相。
- s = col.hslSaturation() で彩度。
- l = col.lightness() で輝度。
- あるいは、h, s, l, a = col.getHsl() でも取り出せるかもしれないが試してない。
HSLからRGBに変換することもできる。col = QColor() で生成した後、col.setHsl(h, s, l) でHSLを設定してやれば、以下のような記述でRGB値が取り出せる。
- r = col.red() で赤成分。
- g = col.green() で緑成分。
- b = col.blue() で青成分。
- あるいは、r, g, b, a = col.getRgb() でも取り出せるかもしれないがどうなんだろう。
整数ではなく、小数点で得ることもできる模様。その場合、getRgbF() や getHslF() のように、最後にFがついた関数を呼ぶ。返り値は、0.0〜1.0、とドキュメントには書いてあるように見える。
そもそも Python 自体に、 _colorsys なるその手の処理をしてくれるモジュールがあるらしい。今回は使ってないけれど。Python は便利だなあ…。
◎ ラベルとスライダーとスピンボックスをまとめた。 :
今回、管理をしやすくするために、QLabel(ラベル)と、QSlider(スライダー)と、QSpinBox(スピンボックス)をまとめたウィジェットを作って配置してみた。
QWidget に _QHBoxLayout(横に並べるレイアウト) を設定して、中に _QLabel 、 _QSlider 、 _QSpinBox を並べて管理、みたいな。
更に、「QSlider の値が変わったら QSpinBox も同じ値に変更」「QSpinBox の値が変わったら QSlider も同じ値に変更」という処理も入れた。これで、QSlider と QSpinBox が連動して動く。QSlider、QSpinBox には valueChanged というシグナル?が用意されていて、値が変わったらそのシグナルが発生することになっているので、ソレを利用した。
しかし、このままだと、QSlider もしくは QSpinBox の値が変わったことを親Widgetに伝える術が無くなるので(valueChanged シグナルを既に使っているので)、別途シグナルを設定して、QSlider もしくは QSpinBox の値が変わったらそのシグナルを発生するようにしてみたり。
QWidget に _QHBoxLayout(横に並べるレイアウト) を設定して、中に _QLabel 、 _QSlider 、 _QSpinBox を並べて管理、みたいな。
更に、「QSlider の値が変わったら QSpinBox も同じ値に変更」「QSpinBox の値が変わったら QSlider も同じ値に変更」という処理も入れた。これで、QSlider と QSpinBox が連動して動く。QSlider、QSpinBox には valueChanged というシグナル?が用意されていて、値が変わったらそのシグナルが発生することになっているので、ソレを利用した。
しかし、このままだと、QSlider もしくは QSpinBox の値が変わったことを親Widgetに伝える術が無くなるので(valueChanged シグナルを既に使っているので)、別途シグナルを設定して、QSlider もしくは QSpinBox の値が変わったらそのシグナルを発生するようにしてみたり。
class LabelSliderSpinBox(QWidget):
# Signalを用意
valueChanged = Signal(int)
def __init__(self, text, parent=None):
(中略)
# 用意したシグナルとスロットを関連付ける
self.valueChanged[int].connect(self.changedValue)
(中略)
def changed_spinbox_value(self, n):
self.sld.setValue(n)
self.valueChanged.emit(n) # 値が変わったのでシグナルを発行
# スロットを用意する
@Slot(int)
def changedValue(self, value):
print("value = %d" % value)
親Widget側では、「子Widget の持ってる値が変化したら valueChanged というシグナルを子Widgetが出してくれるはずだから、そのシグナルが来た時はこの関数を呼ぶ」的な設定をしてやればいい。
w.valueChanged.connect(self.changed_rgb)
◎ シグナルが出続ける問題にまた遭遇。 :
今回も、よく考えないでソースを書いてたら、無限ループのような状態になってしまった…。
- HSLのスライダーもしくはスピンボックスの値が、ユーザ操作で変更される。
- 「HSLの値が変わったぞ!」のイベント発生。
- HSLからRGBに変換して値を求め、RGBスライダーの値を変更。
- 「RGBの値が変わったぞ!」のイベント発生。
- RGBからHSLに変換して値を求め、HSLスライダーの値を変更。
- 2に戻る。無限ループ状態に陥る。
◎ ボタンの背景色を設定。 :
QPushButton への背景色を任意の色にすることもできるらしい。
ていうか。Qtってスタイルシートで見た目を設定できたとは。知らなかった…。
btn.setStyleSheet("QWidget { background-color: %s }"
% QColor(128, 128, 128).name())
QWidget もスタイルシートを持ってるらしいので、そこを弄れば目的が果たせるようで。ていうか。Qtってスタイルシートで見た目を設定できたとは。知らなかった…。
[ ツッコむ ]
以上です。
