블로그 (Blog)/개발로그 (Devlogs)

PySide용 컬러 선택 위젯

티클러 2025. 6. 30. 15:53

PySide로 GUI를 작성 중에 필요해 만들어보았음.

from PySide6.QtWidgets import (
    QFrame, QPushButton, QColorDialog, QApplication, QStyleOptionButton, QStyle, QFrame
)
from PySide6.QtGui import (
    QPainter, QColor, QPen, QPaintEvent, QMouseEvent, QCursor, QPixmap
)
from PySide6.QtCore import (
    QRectF, QRect, QPointF, Qt, Signal, Slot, QStringListModel, QPoint, Qt
)

COLORSIZE = 20
ROWS = 5
SPACE = 4
MARGINY = 19
MARGINX = 4

class ColorKeyInfo:
    def __init__(self, color, rect):
        self.color = color
        self.rect = rect

class RColorPickerPopup(QFrame):
    selected = Signal(QColor)

    def __init__(self, parent=None):
        super().__init__(parent, Qt.Popup)
        self.setWindowFlags(Qt.Popup)
        #self.colors = QColor.colorNames()
        self.colors = [
            "#D6D6D6", "#A8A8A8", "#797979", "#464646", "#000000",
            "#5DDAFF", "#1ECCFF", "#1AADE0", "#1A90B9", "#147191",
            "#81B6FF", "#5898FF", "#3665EE", "#2D4FC9", "#1C3387",
            "#BA00FF", "#9E00F3", "#7600D8", "#4F009A", "#41007D",
            "#E500FF", "#D100FF", "#A600C4", "#8600A4", "#6A0081",
            "#F14F9A", "#EB0073", "#BC0058", "#9C004C", "#7B003D",
            "#FF7A74", "#FF4635", "#FF0000", "#E30000", "#AD0000",
            "#FBCD5F", "#FFAF00", "#FA7A00", "#DE5700", "#A74500",
            "#F5FF7D", "#F3FF38", "#F7FF00", "#C6C500", "#939600",
            "#96EE7D", "#6BE748", "#4DCE1D", "#41AD1C", "#328712"
        ]
        self.colorInfos = []
        self.hoverColor = None
        self.buttonKeyRect = QRectF()

        self.setFrameShape(QFrame.StyledPanel)
        self.setFrameShadow(QFrame.Plain)
        self.setMouseTracking(True)
        self.setStyleSheet(
            "QFrame{background-color: rgb(255, 255, 255);border-color: rgb(0, 0, 0);}"
        )

    def paintButton(self, painter, rc, text, hover):
        if not hover:
            painter.setPen(Qt.black)
            painter.fillRect(rc, Qt.lightGray)
        else:
            painter.setPen(Qt.blue)
            painter.fillRect(rc, Qt.yellow)

        painter.drawRect(rc)
        painter.drawText(rc, text, Qt.AlignHCenter | Qt.AlignVCenter)

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.drawRect(0, 0, self.width() - 1, self.height() - 1)
        painter.drawText(
            QRect(10, 1, self.width(), 19),
            self.tr("Standard"),
            Qt.AlignLeft | Qt.AlignVCenter
        )

        max_col = 0
        max_row = 0
        col = row = 0
        self.colorInfos.clear()
        for i, colorname in enumerate(self.colors):
            if row >= ROWS:
                row = 0
                col += 1
            rc = QRectF(
                col * COLORSIZE + SPACE + MARGINX,
                row * COLORSIZE + SPACE + MARGINY,
                COLORSIZE - SPACE,
                COLORSIZE - SPACE
            )
            color = QColor(colorname)
            painter.fillRect(rc, color)
            self.colorInfos.append(ColorKeyInfo(color, rc))
         
            row += 1
            
            if max_col < col:
                max_col = col
            if max_row < row:
                max_row = row
        
        width = max_col + 1
        height = max_row 

        width = width * COLORSIZE + SPACE + MARGINX + 4
        height = height * COLORSIZE + SPACE + MARGINY + 32
        self.resize(width, height)

        mousePos = self.mapFromGlobal(QCursor.pos())
        self.buttonKeyRect = QRectF(
            SPACE,
            (max_row) * COLORSIZE + SPACE + MARGINY + 5,
            self.width() - 2 * SPACE,
            20
        )
        self.paintButton(
            painter,
            self.buttonKeyRect,
            self.tr("More..."),
            self.buttonKeyRect.contains(mousePos)
        )

        # Draw hover rectangle
        for info in self.colorInfos:
            if info.rect.contains(mousePos):
                self.hoverColor = info.color
                rc = QRectF(info.rect)
                rc.setTop(rc.top() - 2)
                rc.setLeft(rc.left() - 2)
                rc.setBottom(rc.bottom() + 2)
                rc.setRight(rc.right() + 2)
                painter.setPen(QPen(Qt.blue, 2))
                painter.drawRect(rc)
                break

    def mouseMoveEvent(self, event: QMouseEvent):
        self.repaint()

    def mousePressEvent(self, event: QMouseEvent):
        if not self.rect().contains(event.pos()):
            self.close()
            return

        for info in self.colorInfos:
            if info.rect.contains(event.pos()):
                self.hoverColor = info.color
                self.selected.emit(self.hoverColor)
                self.close()
                return

        if self.buttonKeyRect.contains(event.position()):
            dialog = QColorDialog(self)
            if dialog.exec():
                self.hoverColor = dialog.selectedColor()
                self.selected.emit(self.hoverColor)
                self.close()

class RColorPicker(QPushButton):
    selected = Signal(QColor)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.popup = RColorPickerPopup(None)
        self.popup.setObjectName("popup")
        self.popup.setFrameShape(QFrame.StyledPanel)
        self.popup.setFrameShadow(QFrame.Plain)

        self.popup.selected.connect(self.on_popup_selected)

        self.setMinimumWidth(100)
        self._selectedColor = QColor(Qt.black)

    def paintEvent(self, event):
        painter = QPainter(self)
        rect = self.rect()

        # 버튼 스타일 그리기
        opt = QStyleOptionButton()
        opt.initFrom(self)
        opt.features = QStyleOptionButton.ButtonFeature.HasMenu
        self.style().drawControl(QStyle.CE_PushButtonBevel, opt, painter, self)

        # 색깔 박스(아이콘)
        color_rect = QRect(rect.left() + 8, rect.center().y() - 8, 16, 16)
        painter.setBrush(self._selectedColor)
        painter.setPen(Qt.NoPen)
        painter.drawRect(color_rect)

        # 텍스트
        r, g, b = self._selectedColor.red(), self._selectedColor.green(), self._selectedColor.blue()
        #rgb_text = f"{self._selectedColor.name()}  RGB({r}, {g}, {b})"
        rgb_text = f"{self._selectedColor.name()}"
        painter.setPen(self.palette().buttonText().color())
        text_rect = QRect(color_rect.right() + 8, rect.top(), rect.width() - color_rect.width() - 24, rect.height())
        painter.drawText(text_rect, Qt.AlignVCenter|Qt.AlignLeft, rgb_text)

    def mouseReleaseEvent(self, event: QMouseEvent):
        self.popup.move(self.mapToGlobal(QPoint(0, self.height())))
        self.popup.setWindowFlags(Qt.Popup)
        self.popup.show()

    @Slot(QColor)
    def on_popup_selected(self, color):
        self._selectedColor = color
        self.repaint()

 

아래는 QtPropertyBrowser를 PySide6로 포팅 후 장착한 모습.

 

아래는 QTreeWidget에 장착한 모습.