• Qt——设计颜色编辑选取对话框


    Qt中已经有一些封装好的对话框,比如QMessageBox、QColorDialog等,使用起来快捷方便,但缺点是我们无法为它们自定义样式,所以可能难以“融入”我们的项目。既然如此,那就自己做一个把。抱着这样的想法,我设计了一个颜色编辑选取对话框。

    设计界面时,我参考了photoshop的拾色器、windows的画图软件以及一个手绘控件软件mockup。

    最终完成的界面如下:

    它包括以下一些功能:

    • 选取预设的基本颜色
    • 添加自定义颜色方便下次选取
    • 从颜色拾取区域选择颜色
    • 预览当前颜色和新选择的颜色
    • 查看和编辑调整颜色的hsv、rgb和16进制值

     下面对一些重点难点作相关介绍:

    一、色调、饱和度、亮度

    从上图可以看到,右边的矩形是调整色调的区域,这部分亮度和饱和度都是100%,色调值由下往上递增,范围是0-360。

    左边的正方形区域是在某个色调值下,调整饱和度和亮度。亮度由下往上递增,饱和度由左往右递增。

    色调(Hue)、饱和度(Saturation)、亮度(Brightness或Value)三者决定一个颜色(注:这里不考虑透明度),也就是我们所说的hsv。

    同样地,一个颜色可以用hsv表示,也可以用RGB来表示。

    根据上面的原理,设计相关算法也就不难了。

    1.色调

    m_huePixmap = QPixmap(34, 270);
    m_huePixmap.fill(Qt::transparent);
    QPainter painter(&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);

    请原谅代码中一些费解的变量和值。

    在这里,我们使用的是QLinearGeadient渐变来画一个pixmap,由下往上,饱和度百分值由0增加到1.0。

    2.饱和度、亮度

    一开始我的想法是这个问题能不能像色调一样,也用渐变来解决,但是并没有想到合适的办法。

    于是只能遍历每个像素并设置颜色,一共遍历了256 * 256次!

    for (int i = 0; i <= 255; ++i)
    {
        uchar *colorUnit = m_svImg.scanLine(i);
        for (int j = 0; j <= 255; ++j)
        {
            color.setHsv(m_iHue, j, 255 - i);
            QRgb curRgb = color.rgb();
            colorUnit[j * 4] = qBlue(curRgb);
            colorUnit[j * 4 + 1] = qGreen(curRgb);
            colorUnit[j * 4 + 2] = qRed(curRgb);
            colorUnit[j * 4 + 3] = 255;
        }
    }

    QImage有一个函数setPixel,设置每一个像素点的Rgb值,但是该函数效率很低。根据qt给出的建议,使用scanLine()这个函数

    一开始我用的是QPixmap,但发现并没有设置像素颜色的函数,于是换成了QImage。

    当时还找了别人写的一些颜色编辑器demo,发现他们居然都用的setPixel,效率之低难以置信。于是硬着头皮在Qt文档中找答案,最终找到了scanLine,效率提高了不少。

    但是遍历256*256次总让人感觉不那么自在!

    ~

    ~

    ~

    直到有一天,灵光一现。在这个正方形区域中,同一垂直线上的色调值和饱和度值是一样的,同一水平线上的色调值和亮度值是一样的。那么从左往右是从白色#ffffff到当前色调区域所选颜色的渐变,由上往下可以是一个透明度从0到255的渐变。将二者结合起来,正好得到了我想要的。或许,这就是缘分吧:

    //1
    QLinearGradient svGradient(0, 0, m_iAreaWidth, 0);
    svGradient.setColorAt(1, newColor);
    svGradient.setColorAt(0, QColor("#ffffff"));
    
    //2
    QLinearGradient vGradient(0, 0, 0, m_iAreaWidth);
    vGradient.setColorAt(0, QColor(0, 0, 0, 0));
    vGradient.setColorAt(1, QColor(0, 0, 0, 255));
    
    //最后将两个Pixmap重合

    通过“肉眼”的观察,能明显感觉到效率提高了很多很多!

    二、逻辑问题

    另一大难点就是逻辑问题,界面中各个区域有一个改变,就可能引起其它所有区域的改变。

    比如:色调改变 - 饱和度亮度区域需要重绘 - 颜色预览区域新的颜色改变 - 16进制文本编辑框和RGB、HSV的spinbox都会随之改变

    正所谓“牵一发而动全身”,更复杂的是,这些控件是相互影响的!

    如果思考不周,将导致程序逻辑混乱、程序出现死循环或者多次循环影响效率等问题。

    我在写这个程序时主要用了3种方法避免混乱:

    1.通过信号槽连接传递变化

    2.调用其它类的公有函数

    3.设置“flag”,通过判断它的布尔值来决定是否调用函数。

    第2和第3都是为了防止信号槽传递时可能导致程序的混乱。

    三、关于QLineEdit的思考

    左边的lineEdit中展示的是颜色RGB值的16进制形式,范围000000-ffffff,可以通过正则表达式对使用者的输入进行限制。也可以加上inputMask,对格式进行更加严格的限制。然而限制太多反而会影响用户体验,需慎重考虑。

    我的做法如下:

    1.使用正则表达式控制输入范围。

    2.手动编辑lineEdit中的内容时,光标位于lineEdit中,捕获textEdited信号,改变颜色值。

    3.鼠标点击界面其它部分,使lineEdit失去焦点,然后捕获它的编辑结束editingFinished信号,使其中的文本恢复6位的格式。

    为了使鼠标点击界面其它部分时输入框失去焦点,需要设置主窗口的focus属性:

    setFocusPolicy(Qt::ClickFocus);
    

    在设计这部分时,我也参考了photoshop的方法,可以打开ps感受一下哦。

    源码放在了github上,请原谅其中一些蹩脚的代码,因为最初使用Qt4编写的,后来在Qt5上运行也就没怎么修改。

    点我进入github

  • 相关阅读:
    如何遍历对象,hasOwnProperty()方法,和 in 的区别【CordeWars实践】 Pete, the baker
    去除字符串内所有空格【在CodeWars中实践】The Hashtag Generator
    JS 去掉小数点
    "写出下列代码的执行结果"一直不会做?【JS Event Loop】
    【CodeWars】Large Factorials (计算阶乘)
    去掉数组或者字符串中相邻的重复数据
    纯CSS绘制三角形(各种角度)
    3.坐标系与轴心点
    2.blender的基本操作与动画案例挑战
    CF1470E Strange Permutation
  • 原文地址:https://www.cnblogs.com/hellovenus/p/qt_colordialog.html
Copyright © 2020-2023  润新知