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ってスタイルシートで見た目を設定できたとは。知らなかった…。
[ ツッコむ ]
以上です。