Custom Color Edit Pick Dialog

1. Introduction

There are already some encapsulated dialog boxes in Qt, such as QMessageBox, QColorDialog, etc., which are quick and convenient to use, but the disadvantage is that we cannot customize styles for them, so it may be difficult to “integrate” into our project. If so, make one yourself. With this in mind, I designed a color editing and selection dialog box.
When designing the interface, I refer to the color picker of photoshop, the drawing software of windows, and a hand-painted control software mockup.
The final interface is as follows:

Customize the color edit selection dialog box, which includes some of the following functions:

  • Choose a default base color
  • Add a custom color for easy selection next time
  • Select a color from the color picker area
  • Preview the current color and the newly selected color
  • View and edit hsv, rgb and hex values for adjusted colors

2. Basic color control – BasicColorItem

#include "BasicColorItem.h"

BasicColorItem::BasicColorItem(const QColor &c, QWidget *parent)
: QLabel(parent)
, m_bMouseIn(false)
{<!-- -->
setFixedSize(ITEM_EDGE_LENGTH, ITEM_EDGE_LENGTH);
    m_color = c;
    this->update();
}

// set the color
void BasicColorItem::setColor(const QColor &c)
{<!-- -->
    m_color = c;
    QImage itemImg = QImage(ITEM_EDGE_LENGTH, ITEM_EDGE_LENGTH, QImage::Format_RGB32);
    QPainter painter( &itemImg);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setCompositionMode(QPainter::CompositionMode_Source);
    painter. drawImage(0, 0, itemImg);
    painter.setPen(Qt::NoPen);
    painter. setBrush(c);
    painter. drawRect(0, 0, width(), height());
    painter. end();
    this->setPixmap(QPixmap::fromImage(itemImg));
    update();
}

// get the color
QColor BasicColorItem::getColor()
{<!-- -->
return m_color;
}

void BasicColorItem::paintEvent(QPaintEvent *event)
{<!-- -->
    Q_UNUSED(event);

    QPainter painter(this);
    painter.setCompositionMode(QPainter::CompositionMode_Source);
    if (m_bMouseIn)
        painter.setPen(QPen(Qt::white, 3));
    else
        painter.setPen(QPen(m_color, 0));
    painter.setBrush(m_color);
    painter.drawRect(1, 1, ITEM_EDGE_LENGTH - 3, ITEM_EDGE_LENGTH - 3);
}

void BasicColorItem::mousePressEvent(QMouseEvent *event)
{<!-- -->
    Q_UNUSED(event);

    emit sigItemClicked(m_color);
}

void BasicColorItem::enterEvent(QEvent *event)
{<!-- -->
    Q_UNUSED(event);

m_bMouseIn = true;
    this->update();
}

void BasicColorItem::leaveEvent(QEvent *event)
{<!-- -->
    Q_UNUSED(event);

m_bMouseIn = false;
    this->update();
}

The basic color control is used to display different basic colors. It inherits from QLabel and uses drawing events to draw its own color. When the mouse is moved in, it will display a white border effect.

3. Basic color area – BasicColorArea

#include "BasicColorArea.h"

BasicColorArea::BasicColorArea(QWidget *parent)
: QWidget(parent)
{<!-- -->
    // initialize the grid layout
    m_pLayoutGrid = new QGridLayout(this);
    m_pLayoutGrid->setAlignment(Qt::AlignLeft | Qt::AlignTop);
    m_pLayoutGrid->setSpacing(2);
    m_pLayoutGrid->setMargin(2);
    m_pLayoutGrid->setGeometry(QRect(0,0,width(),height()));

    // list of colors
    QStringList colorList = {<!-- -->"0, 0, 0", "51, 51, 51", "102, 102, 102", "153, 153, 153", "204, 204, 204" , "221, 221, 221", "238, 238, 238", "255, 255, 255",
                             "207, 42, 39", "255, 153, 0", "255, 255, 0", "0, 158, 15", "0, 255, 255", "43, 120, 228", "153 , 0, 255", "255, 0, 255",
                             "234, 153, 153", "249, 203, 156", "255, 229, 153", "182, 215, 168", "162, 196, 201", "159, 197, 248", "180 , 167, 214", "213, 166, 189",
                             "224, 102, 102", "246, 178, 107", "255, 217, 102", "147, 196, 125", "118, 165, 175", "111, 168, 220", "142 , 124, 195", "194, 123, 160",
                             "204, 0, 0", "230, 145, 56", "241, 194, 50", "106, 168, 79", "69, 129, 142", "89, 126, 170", "103 , 78, 167", "166, 77, 121",
                             "153, 0, 0", "180, 95, 6", "119, 144, 0", "56, 118, 29", "19, 79, 92", "8, 83, 148", "52 , 28, 117", "116, 27, 71",
                             "102, 0, 0", "120, 63, 4", "127, 96, 0", "39, 78, 19", "12, 52, 61", "7, 55, 99", "32 , 18, 77", "71, 17, 48"};
    // traverse the list of color names
    QString color;
    QStringList strList;
    foreach(color,colorList) {<!-- -->
        strList = color.split(",");
        BasicColorItem *pItem11 = new BasicColorItem(QColor(strList.at(0).toInt(), strList.at(1).toInt(), strList.at(2).toInt()));
        connect(pItem11, SIGNAL(sigItemClicked(const QColor & amp;)), this, SIGNAL(sigColorItemSel(const QColor & amp;)));

        m_pLayoutGrid->addWidget(pItem11, m_pLayoutGrid->count()/8, m_pLayoutGrid->count()%8);
    }
}

All the basic color controls are placed in this area for selecting the preset basic colors, and the basic color controls are created by traversing the color list in the grid layout, which can be expanded or deleted by yourself.

4. Custom color area – CustomColorArea

#include "CustomColorArea.h"

CustomColorArea::CustomColorArea(QWidget *parent)
: QWidget(parent)
, m_iCurIndex(0)
{<!-- -->
    // initialize the grid layout
    m_pLayoutGrid = new QGridLayout(this);
    m_pLayoutGrid->setAlignment(Qt::AlignLeft | Qt::AlignTop);
    m_pLayoutGrid->setSpacing(2);
    m_pLayoutGrid->setMargin(2);
    m_pLayoutGrid->setGeometry(QRect(0,0,width(),height()));

    // Palette sub-interface layout
    QString color;
    QStringList strList;
    for(int i=0; i<16; i ++ ) {<!-- -->
        BasicColorItem *pItem11 = new BasicColorItem(Qt::white);
        connect(pItem11, SIGNAL(sigItemClicked(const QColor & amp;)), this, SIGNAL(sigColorItemSel(const QColor & amp;)));

        m_pLayoutGrid->addWidget(pItem11, m_pLayoutGrid->count()/8, m_pLayoutGrid->count()%8);

        m_mapIndexToItem.insert(i, pItem11);
    }
}

// set custom color
void CustomColorArea::setGivenColor(const QColor &c)
{<!-- -->
int iIndex = m_iCurIndex % 16;
    BasicColorItem *pCurItem = m_mapIndexToItem[iIndex];
pCurItem->setColor(c);
m_iCurIndex++;
}

// set custom item color
void CustomColorArea::setGivenItemColor(int iIndex, const QColor & amp;c)
{<!-- -->
BasicColorItem *pCurItem = m_mapIndexToItem[iIndex];
pCurItem->setColor(c);
}

The custom color area is used to add a custom color for the next selection, that is, 16 CustomColorArea controls are created in a grid layout. If you click Add to Custom Color, the new color is set to the basic color where m_iCurIndex is located controls.

5. Saturation (S) and Brightness (V) area – SVColorArea

#include "SVColorArea.h"

SVColorArea::SVColorArea(QWidget *parent)
: QWidget(parent)
, m_iHue(0)
, m_iSaturation(0)
, m_iBrightness(0)
, m_iAreaWidth(256)
{<!-- -->
    // initialize interface
    this->setFixedSize(256, 256);

    // Create a pixmap of a square area
createSVPixmap();
    // Create a pixmap with brightness gradient
createVPixmap();
    // Update the pixmap of the square area
    updateSVPixmap();
}

void SVColorArea::setHue(int h)
{<!-- -->
m_iHue = h;
updateSVPixmap();
update();
}

void SVColorArea::setSaturation(int s)
{<!-- -->
m_iSaturation = s;
update();
}

void SVColorArea::setBrightness(int v)
{<!-- -->
m_iBrightness = v;
update();
}

void SVColorArea::setHsv(int h, int s, int v)
{<!-- -->
m_iHue = h;
m_iSaturation = s;
m_iBrightness = v;
updateSVPixmap();
update();
}

void SVColorArea::setColor(const QColor &c)
{<!-- -->
m_iHue = c. hue();
m_iSaturation = c.saturation();
m_iBrightness = c. value();
updateSVPixmap();
    emit sigSvChanged(m_iHue, m_iSaturation, m_iBrightness);
update();
}

void SVColorArea::paintEvent(QPaintEvent *)
{<!-- -->
    // Draw a square color area
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.drawPixmap(0, 0, m_svPixmap);
painter.drawPixmap(0, 0, m_vPixmap);

    // Draw the circle of the color selection point (brightness > 128 is black; < 128 is white; avoid the problem that the black circle may not be displayed clearly on the black background)
painter.setPen(QPen(m_iBrightness > 128 ? Qt::black : Qt::white, 2));
QPointF selecPos = QPointF(m_iSaturation, 255 - m_iBrightness);
painter. drawEllipse(selecPos, 6, 6);
}

void SVColorArea::mousePressEvent(QMouseEvent *ev)
{<!-- -->
m_iSaturation = qBound(0, ev->x(), 255);
m_iBrightness = qBound(0, 255 - ev->y(), 255);
    emit sigSvChanged(m_iHue, m_iSaturation, m_iBrightness);
update();
}

void SVColorArea::mouseMoveEvent(QMouseEvent *ev)
{<!-- -->
m_iSaturation = qBound(0, ev->x(), 255);
m_iBrightness = qBound(0, 255 - ev->y(), 255);
    emit sigSvChanged(m_iHue, m_iSaturation, m_iBrightness);
update();
}

// Create a pixmap with brightness gradient
void SVColorArea::createVPixmap()
{<!-- -->
m_vPixmap = QPixmap(m_iAreaWidth, m_iAreaWidth);
m_vPixmap.fill(Qt::transparent);
QPainter painter( & amp; m_vPixmap);
painter.setRenderHint(QPainter::Antialiasing);
painter.setCompositionMode(QPainter::CompositionMode_Source);
QLinearGradient vGradient(0, 0, 0, m_iAreaWidth);
vGradient.setColorAt(0, QColor(0, 0, 0, 0));
vGradient.setColorAt(1, QColor(0, 0, 0, 255));
painter.setPen(Qt::NoPen);
painter.setBrush(QBrush(vGradient));
painter.drawRect(0, 0, m_iAreaWidth, m_iAreaWidth);
}

// Create a pixmap of a square area
void SVColorArea::createSVPixmap()
{<!-- -->
m_svPixmap = QPixmap(m_iAreaWidth, m_iAreaWidth);
m_svPixmap.fill(Qt::transparent);
}

// Update the pixmap of the square area
void SVColorArea::updateSVPixmap()
{<!-- -->
QColor newColor;
newColor.setHsv(m_iHue, 255, 255);
QPainter painter( & amp; m_svPixmap);
painter.setRenderHint(QPainter::Antialiasing);
QLinearGradient svGradient(0, 0, m_iAreaWidth, 0);
svGradient.setColorAt(1, newColor);
svGradient.setColorAt(0, QColor("#ffffff"));
painter.setPen(Qt::NoPen);
painter.setBrush(QBrush(svGradient));
painter.drawRect(0, 0, m_iAreaWidth, m_iAreaWidth);
}

// Slot function for hue change
void SVColorArea::slotHueChanged(int h)
{<!-- -->
m_iHue = h;
updateSVPixmap();
    emit sigSvChanged(m_iHue, m_iSaturation, m_iBrightness);
update();
}

The SVColorArea class and the following HColorArea class are relatively more complicated and have more details. They use the QLinearGeadient gradient to draw a pixmap

6. Hue area – HColorArea

#include "HColorArea.h"

HColorArea::HColorArea(QWidget *parent)
: QWidget(parent)
, m_hue(0.0)
, m_iHue(0)
, m_iColorHeight(256)
    , m_iColorWidth(25)
{<!-- -->
    this->setFixedSize(34, 270);

    // Create the hue pixmap
createHuePixmap();
}

void HColorArea::setHue(int h)
{<!-- -->
m_hue = (double)h / 360;
m_iHue = h;
update();
}

void HColorArea::paintEvent(QPaintEvent *)
{<!-- -->
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.drawPixmap(0, 0, m_huePixmap);

int iHeight = m_iColorHeight - m_hue * m_iColorHeight;
QPolygonF triangle;
triangle.append(QPointF(m_iColorWidth, iHeight + topMargin));
triangle.append(QPointF(width(), iHeight));
triangle.append(QPointF(width(), iHeight + 2 * topMargin - 1));
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::white);
painter. drawPolygon(triangle);
}

void HColorArea::mousePressEvent(QMouseEvent *ev)
{<!-- -->
double tempValue = 1 - (double)(ev->pos().y() - topMargin) / m_iColorHeight;
m_hue = qBound(0.0, tempValue, 1.0);
update();
m_iHue = m_hue * 360;
    emit sigHueChanged(m_iHue);
}

void HColorArea::mouseMoveEvent(QMouseEvent *ev)
{<!-- -->
double tempValue = 1 - (double)(ev->pos().y() - topMargin) / m_iColorHeight;
m_hue = qBound(0.0, tempValue, 1.0);
update();
m_iHue = m_hue * 360;
    emit sigHueChanged(m_iHue);
}

// Create the hue pixmap
void HColorArea::createHuePixmap()
{<!-- -->
m_huePixmap = QPixmap(34, 270);
m_huePixmap.fill(Qt::transparent);
QPainter painter( & amp; m_huePixmap);
painter.setRenderHint(QPainter::Antialiasing);
QLinearGradient hueGradient(0, m_iColorHeight, 0, 0);
    for (double i = 0; i < 1.0; i + = 1.0 / 16)
{<!-- -->
hueGradient.setColorAt(i, QColor::fromHsvF(i, 1, 1, 1));
}
hueGradient.setColorAt(1, QColor::fromHsvF(0, 1, 1, 1));
painter.setPen(Qt::NoPen);
painter.setBrush(QBrush(hueGradient));
painter.drawRect(0, topMargin, m_iColorWidth, m_iColorHeight);
}

Here the QLinearGeadient gradient is used to draw a pixmap, from bottom to top, the saturation percentage value increases from 0 to 1.0.

7. Color preview area – PreviewColorArea

#include "PreviewColorArea.h"

PreviewColorArea::PreviewColorArea(QWidget *parent)
: QWidget(parent)
, m_curColor(Qt::black)
, m_newColor(Qt::black)
{<!-- -->
    // initialize interface
    this->setFixedSize(166, 88);
}

// set the current color
void PreviewColorArea::setCurColor(const QColor &c)
{<!-- -->
m_curColor = c;
repaint();
}

// set new color
void PreviewColorArea::setNewColor(const QColor &c)
{<!-- -->
m_newColor = c;
update();
}

void PreviewColorArea::paintEvent(QPaintEvent *)
{<!-- -->
QStylePainter painter(this);
paint(painter, geometry());
}

void PreviewColorArea::resizeEvent(QResizeEvent *)
{<!-- -->
update();
}

void PreviewColorArea::paint(QPainter & amp;painter, QRect rect) const
{<!-- -->
int iMiddleWidth = rect. width() / 2;
int iHeight = rect. height();
painter.fillRect(0, 0, iMiddleWidth, iHeight, m_curColor);
painter.fillRect(iMiddleWidth, 0, iMiddleWidth, iHeight, m_newColor);
painter.setPen(QPen(Qt::black, 1));
painter. drawRect(0, 0, width() - 1, height() - 1);
}

void PreviewColorArea::slotSvChanged(int h, int s, int v)
{<!-- -->
m_newColor.setHsv(h, s, v);
update();
    emit sigSvChanged(h, s, v);
}

It is used to preview the current color and the newly selected color, the left half area draws the current color, and the right half area draws the new color. The current color refers to the color selected last time after clicking the OK button, and the new color refers to the color to be selected after clicking the basic color control.

8. Color selection interface – ColorSelWidget

#include "ColorSelWidget.h"

ColorSelWidget::ColorSelWidget(QWidget *parent) : QWidget(parent)
{<!-- -->
    // initialize interface
    this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
    this->setFixedSize(730, 420);
    this->setFocusPolicy(Qt::ClickFocus);
    this->setObjectName("ColorSelWidget");
    this->setStyleSheet("QWidget#ColorSelWidget{background: rgb(23,25,29);}");

    // title bar widget
    m_pLabTitle = new QLabel;
    m_pBtnClose = new QPushButton;
    m_pLabSplitLine = new QLabel;
    m_pLabTitle->setText("Edit Color");
    m_pLabTitle->setObjectName("LabTitle");
    m_pBtnClose->setObjectName("m_pCloseBtn");
    m_pLabSplitLine->setObjectName("LabSplitLine");
    m_pBtnClose->setFixedSize(30, 30);
    m_pLabSplitLine->setFixedSize(width(), 2);

    // Initialize the basic color interface
    m_pBasicColorArea = new BasicColorArea;
    // custom color area
    m_pCustomColorArea = new CustomColorArea;

    // Initialize the saturation (S) and brightness (V) areas
    m_pSVColorArea = new SVColorArea;
    // Initialize the hue area
    m_pHColorArea = new HColorArea;

    // Initialize the preview color interface
    m_pPreviewColorArea = new PreviewColorArea;
    m_pLabCurColor = new QLabel;
    m_pLabNewColor = new QLabel;
    m_pBtnOk = new QPushButton;
    m_pBtnCancle = new QPushButton;
    m_pLabCurColor->setText("current\
color");
    m_pLabNewColor->setText("New\
color");
    m_pBtnOk->setText("OK");
    m_pBtnCancle->setText("Cancel");
    m_pLabCurColor->setFixedSize(30, 40);
    m_pLabNewColor->setFixedSize(30, 40);
    m_pBtnOk->setFixedSize(62, 26);
    m_pBtnCancle->setFixedSize(62, 26);

    // Initialize the GroupBox
    m_pGroupBoxBasic = new QGroupBox;
    m_pGroupBoxCustom = new QGroupBox;
    m_pGroupBoxBasic->setTitle("Basic color");
    m_pGroupBoxCustom->setTitle("custom color");

    // Hue, Saturation, Brightness - label, counter controls
    m_pLabH = new QLabel;
    m_pSpinBoxH = new QSpinBox;
    m_pLabS = new QLabel;
    m_pSpinBoxS = new QSpinBox;
    m_pLabV = new QLabel;
    m_pSpinBoxV = new QSpinBox;
    m_pLabR = new QLabel;
    m_pSpinBoxR = new QSpinBox;
    m_pLabG = new QLabel;
    m_pSpinBoxG = new QSpinBox;
    m_pLabB = new QLabel;
    m_pSpinBoxB = new QSpinBox;
    m_pLabH->setText("Hue (H):");
    m_pLabS->setText("Saturation(S):");
    m_pLabV->setText("Brightness (V):");
    m_pLabR->setText("Red(R):");
    m_pLabG->setText("Green(G):");
    m_pLabB->setText("Blue(B):");
    m_pLabH->setFixedSize(66, 26);
    m_pLabS->setFixedSize(66, 26);
    m_pLabV->setFixedSize(66, 26);
    m_pLabR->setFixedSize(50, 26);
    m_pLabG->setFixedSize(50, 26);
    m_pLabB->setFixedSize(50, 26);

    m_pLabColor = new QLabel; // color text label
    m_pEditColor = new QLineEdit; // color edit box
    m_pBtnCustom = new QPushButton;
    m_pLabColor->setText("#");
    m_pEditColor->setText("000000");
    m_pBtnCustom->setText("Add to custom color");
    m_pBtnCustom->setFixedSize(128, 26);
    m_pEditColor->setFixedSize(72, 26);

    // regular expression applied to the color edit bar
    QRegExp rx("(\d?[a-f]?){0,6}");
    m_pEditColor->setValidator(new QRegExpValidator(rx, this));
    m_pEditColor->setText("000000");

    // title bar layout
    QHBoxLayout *pLayoutTitle = new QHBoxLayout;
    pLayoutTitle->addSpacing(6);
    pLayoutTitle->addWidget(m_pLabTitle);
    pLayoutTitle->addStretch();
    pLayoutTitle->addWidget(m_pBtnClose, 0, Qt::AlignTop);
    pLayoutTitle->addSpacing(4);
    pLayoutTitle->setMargin(0);

    // basic color layout
    QVBoxLayout *pLayoutBasic = new QVBoxLayout(m_pGroupBoxBasic);
    pLayoutBasic->addWidget(m_pBasicColorArea);
    // custom color layout
    QVBoxLayout *pLayoutCustom = new QVBoxLayout(m_pGroupBoxCustom);
    pLayoutCustom->addWidget(m_pCustomColorArea);

    // layout below
    QHBoxLayout *pLayoutDown = new QHBoxLayout;
    pLayoutDown->addStretch();
    pLayoutDown->addWidget(m_pLabColor);
    pLayoutDown->addSpacing(2);
    pLayoutDown->addWidget(m_pEditColor);
    pLayoutDown->addStretch();
    pLayoutDown->addWidget(m_pBtnCustom);
    pLayoutDown->addSpacing(0);
    pLayoutDown->setSpacing(0);
    pLayoutDown->setMargin(0);

    // left layout
    QVBoxLayout *pLayoutLeft = new QVBoxLayout;
    pLayoutLeft->addWidget(m_pGroupBoxBasic);
    pLayoutLeft->addSpacing(12);
    pLayoutLeft->addWidget(m_pGroupBoxCustom);
    pLayoutLeft->addSpacing(12);
    pLayoutLeft->addLayout(pLayoutDown);
    pLayoutLeft->setSpacing(0);
    pLayoutLeft->addStretch();
    pLayoutLeft->setMargin(0);

    // grid layout
    QGridLayout *m_pGridLayout = new QGridLayout;
    m_pGridLayout->addWidget(m_pLabH, 0, 0, Qt::AlignRight);
    m_pGridLayout->addWidget(m_pSpinBoxH, 0, 1);
    m_pGridLayout->addWidget(m_pLabS, 1, 0, Qt::AlignRight);
    m_pGridLayout->addWidget(m_pSpinBoxS, 1, 1);
    m_pGridLayout->addWidget(m_pLabV, 2, 0, Qt::AlignRight);
    m_pGridLayout->addWidget(m_pSpinBoxV, 2, 1);
    m_pGridLayout->addWidget(m_pLabR, 3, 0, Qt::AlignRight);
    m_pGridLayout->addWidget(m_pSpinBoxR, 3, 1);
    m_pGridLayout->addWidget(m_pLabG, 4, 0, Qt::AlignRight);
    m_pGridLayout->addWidget(m_pSpinBoxG,4,1);
    m_pGridLayout->addWidget(m_pLabB, 5, 0, Qt::AlignRight);
    m_pGridLayout->addWidget(m_pSpinBoxB, 5, 1);

    // color layout
    QHBoxLayout *pLayoutHue = new QHBoxLayout;
    pLayoutHue->addStretch();
    pLayoutHue->addWidget(m_pSVColorArea);
    pLayoutHue->addSpacing(10);
    pLayoutHue->addWidget(m_pHColorArea);
    pLayoutHue->addSpacing(10);
    pLayoutHue->addLayout(m_pGridLayout);
    pLayoutHue->addStretch();
    pLayoutHue->setSpacing(0);
    pLayoutHue->setMargin(0);

    // color preview layout
    QHBoxLayout *pLayoutPreview = new QHBoxLayout;
    pLayoutPreview->addSpacing(12);
    pLayoutPreview->addWidget(m_pLabCurColor);
    pLayoutPreview->addSpacing(8);
    pLayoutPreview->addWidget(m_pPreviewColorArea);
    pLayoutPreview->addSpacing(8);
    pLayoutPreview->addWidget(m_pLabNewColor);
    pLayoutPreview->addStretch();
    pLayoutPreview->addWidget(m_pBtnOk);
    pLayoutPreview->addSpacing(10);
    pLayoutPreview->addWidget(m_pBtnCancle);
    pLayoutPreview->addSpacing(4);
    pLayoutPreview->setSpacing(0);
    pLayoutPreview->setMargin(0);

    // intermediate layout
    QVBoxLayout *pLayoutCenter = new QVBoxLayout;
    pLayoutCenter->addStretch();
    pLayoutCenter->addLayout(pLayoutHue);
    pLayoutCenter->addLayout(pLayoutPreview);
    pLayoutCenter->addStretch();
    pLayoutCenter->setMargin(0);

    // layout
    QHBoxLayout *pLayoutMainR = new QHBoxLayout;
    pLayoutMainR->addSpacing(0);
    pLayoutMainR->addLayout(pLayoutLeft);
    pLayoutMainR->addSpacing(12);
    pLayoutMainR->addLayout(pLayoutCenter);
    pLayoutMainR->addSpacing(0);
    pLayoutCenter->setSpacing(0);
    pLayoutMainR->setContentsMargins(12, 0, 12, 12);

    // main layout
    QVBoxLayout *pLayoutMain = new QVBoxLayout(this);
    pLayoutMain->addSpacing(0);
    pLayoutMain->addLayout(pLayoutTitle);
    pLayoutMain->addWidget(m_pLabSplitLine);
    pLayoutMain->addLayout(pLayoutMain2);
    pLayoutMain->addStretch();
    pLayoutMain->setMargin(0);

    // Signal slot connection of each interface
    connect(m_pBasicColorArea, &BasicColorArea::sigColorItemSel, this, &ColorSelWidget::slotColorItemSel);
    connect(m_pCustomColorArea, &CustomColorArea::sigColorItemSel, this, &ColorSelWidget::slotColorItemSel);
    connect(m_pSVColorArea, &SVColorArea::sigSvChanged, m_pPreviewColorArea, &PreviewColorArea::slotSvChanged);
    connect(m_pPreviewColorArea, &PreviewColorArea::sigSvChanged, this, &ColorSelWidget::slotUpdateEditData);
    connect(m_pHColorArea, &HColorArea::sigHueChanged, m_pSVColorArea, &SVColorArea::slotHueChanged);

    // color edit box changes
    connect(m_pEditColor, &QLineEdit::textEdited, this, &ColorSelWidget::slotEditChanged);
    connect(m_pEditColor, &QLineEdit::editingFinished, this, &ColorSelWidget::slotEditFinished);

    // each button
    connect(m_pBtnCustom, &QPushButton::clicked, this, &ColorSelWidget::slotAddCustomColor);
    connect(m_pBtnOk, &QPushButton::clicked, this, &ColorSelWidget::slotOkBtnClicked);
    connect(m_pBtnCancle, &QPushButton::clicked, this, &ColorSelWidget::slotCancelBtnClicked);
    connect(m_pBtnClose, &QPushButton::clicked, this, &ColorSelWidget::slotCancelBtnClicked);

    // counter control
    connect(m_pSpinBoxH, SIGNAL(valueChanged(int)), this, SLOT(slotValueChangedH(int)));
    connect(m_pSpinBoxS, SIGNAL(valueChanged(int)), this, SLOT(slotValueChangedS(int)));
    connect(m_pSpinBoxV, SIGNAL(valueChanged(int)), this, SLOT(slotValueChangedV(int)));
    connect(m_pSpinBoxR, SIGNAL(valueChanged(int)), this, SLOT(slotValueChangedR(int)));
    connect(m_pSpinBoxG, SIGNAL(valueChanged(int)), this, SLOT(slotValueChangedG(int)));
    connect(m_pSpinBoxB, SIGNAL(valueChanged(int)), this, SLOT(slotValueChangedB(int)));
}

//......

In the color selection interface, place all the sub-interfaces of the above-mentioned area objects on this interface. The edit boxes such as hue and saturation on the right are also created directly on this interface, and there is no new sub-interface class for implementation. Because the code is relatively long, only the constructor code is displayed here, and other download source codes are viewed.
The focus is on the connection of each signal slot:

// Signal slot connection of each interface
connect(m_pBasicColorArea, &BasicColorArea::sigColorItemSel, this, &ColorSelWidget::slotColorItemSel);
connect(m_pCustomColorArea, &CustomColorArea::sigColorItemSel, this, &ColorSelWidget::slotColorItemSel);
connect(m_pSVColorArea, &SVColorArea::sigSvChanged, m_pPreviewColorArea, &PreviewColorArea::slotSvChanged);
connect(m_pPreviewColorArea, &PreviewColorArea::sigSvChanged, this, &ColorSelWidget::slotUpdateEditData);
connect(m_pHColorArea, &HColorArea::sigHueChanged, m_pSVColorArea, &SVColorArea::slotHueChanged);