PyQt4で色を選択するウィジェット


ダイアログを表示するのもいいですが、常に表示していてちょっとそこから選ぶ方が便利なので、
そういうのを作ってみました。



自作のユーティリティ(myutil、qutil)を使っているところもありますが、
書き直すのが面倒なので。

#encoding:shift-jis
from __future__ import division, with_statement, print_function
from myutil import *
from qutil import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *

SELECTION_VALUES = set(["Bg", "Axis", "Grid", "Grid2"])

class ColorRect(QWidget):
    def __init__(self, color, parent=None):
        super(ColorRect, self).__init__(parent)
        self._color = color
        self.setMinimumSize(30, 30)
    
    def paintEvent(self, evt):
        painter = QPainter(self)
        painter.fillRect(self.rect(), self.color())
        painter.end()
        
    def sizeHint(self):
        return QSize(30, 30)
    
    def_qaccessor("color")

class ColorRect1(ColorRect):
    def __init__(self, *a, **kw):
        super(ColorRect1, self).__init__(*a, **kw)
        self._selection = set()
    
    def_qaccessor("selection")
    def setSelection(self, sel):
        sel = set(sel)
        assert sel <&#061; set(SELECTION_VALUES)
        self._selection &#061; sel
    
    def paintEvent(self, evt):
        super(ColorRect1, self).paintEvent(evt)
        
        painter &#061; QPainter(self)
        c &#061; int_to_qcolor(0xD0D0D0 ^ qcolor_to_int(self.color()))
        painter.setPen(QPen(c))
        
        r &#061; self.rect()
        c &#061; r.center()
        d &#061; {
            "Bg":   ("B",  r.x(), c.y()),
            "Axis" :("A",  c.x(), c.y()),
            "Grid" :("G",  r.x(), r.bottom()),
            "Grid2":("G2", c.x(), r.bottom()),
        }
        assert set(d) &#061;&#061; SELECTION_VALUES
        
        for t, (text, x, y) in d.iteritems():
            if t in self._selection:
                painter.drawText(QPoint(x + 2, y - 2), text)
        painter.end()
        
    def mousePressEvent(self, evt):
        b &#061; evt.button()
        ctrl  &#061; bool(evt.modifiers() & Qt.ControlModifier)
        shift &#061; bool(evt.modifiers() & Qt.ShiftModifier)
        
        mid &#061; b&#061;&#061;Qt.MidButton
        left&#061; b&#061;&#061;Qt.LeftButton
        right&#061;b&#061;&#061;Qt.RightButton
        
        c &#061; None
        if (mid and ctrl) or (left and ctrl):
            c &#061; "Grid"
        elif (mid and shift) or (left and shift):
            c &#061; "Grid2"
        elif left:
            c &#061; "Bg"
        elif right:
            c &#061; "Axis"
        else:
            return
        
        for r in self.parent()._rects:
            if c in r._selection:
                r._selection.remove(c)
        self._selection.add(c)
        self.parent().update()
        
        
class ColorSelector(QWidget):
    def __init__(self, parent&#061;None):
        super(ColorSelector, self).__init__(parent)
        
        layout &#061; QFormLayout()
        self._rects &#061; []
        for i in xrange(16):
            color &#061; QColor(i*16, (i*16+64) % 256, (i*16+128) % 256)
            self._rects.append(ColorRect1(color, parent&#061;self))
        for r in xrange(8):
            layout.addRow(self._rects[2*r], self._rects[2*r + 1])
        self.setLayout(layout)
        
        self._rects[0].setSelection(set(SELECTION_VALUES))
    
    for name in SELECTION_VALUES:
        execstr &#061; """
        def {lower}Color(self):
            for r in self._rects:
                if "{name}" in r.selection():
                    return r.color()
            assert 0, ("{name}", [r.selection() for r in self._rects])
        """.format(name&#061;name, lower&#061;name.lower())
        exec unindent(execstr)
    
def main():
    app &#061; QApplication(sys.argv)
    w &#061; QWidget()
    w.resize(300, 300)
    c &#061; ColorSelector(w)
    w.show()
    
    exit(app.exec_())
    
if __name__ &#061;&#061; "__main__":
    main()