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

Qt——设计颜色编辑选取对话框的更多相关文章

  1. Qt——设计颜色编辑器

    Qt中有一些封装好的对话框,比如QMessageBox.QColorDialog等,用途很广,但缺点是样式难以改变,直接拿来用可能与我们自己的系统样式不搭配,显得很难看. 所以我在无聊的时候,就自己写 ...

  2. GTK 预置对话框 GtkDialog 文件/颜色/字体选取等 GtkFileSelection

    (GTK2) 文档链接 作用:打开一个预置的对话框,如文件选取对话框 GtkFileSelection 效果下图所示 ╰── GtkDialog ├── GtkAboutDialog ├── GtkC ...

  3. UI设计, 弹出对话框 设计(区分强调按钮和普通按钮,给用户一个 正向建议的强调按钮)

    在UI设计时,经常会需要 设计 弹出对话框,以下是个样式设计: 0.标准对话框 说明 Title space : 标题区 contents space : 内容区 function space: 功能 ...

  4. qt学习001之运行对话框

    使用QT实现Window下运行对话框 1.摆放控件 首先设置并摆放相应的对话框控件,并更改相应名称:        2.实现功能 1)在文本框中输入信息后,点击确定或回车可以运行系统中相应的程序: 点 ...

  5. QML-关于Qt.rgba()颜色无法正常显示问题

    GitHub:八至 作者:狐狸家的鱼 本文链接:关于Qt.rgba()颜色的正确写法 当在正常给color属性写颜色的时候,用十六进制能正常显示,但是用Qt.rgba()时,颜色无法正常显示出来. 按 ...

  6. qt creator中编辑Makefile的设置

    在qt creator中编辑Makefile时的Tab键总是不能识别,需要这样设置

  7. qt中不能编辑

    下载的qt代码不能编辑,原因是文本编码是GB2312,现在选择的是utf-8,把编码改过来就可以编辑了

  8. QT设计UI:QT模式对话框打开文件

    使用QT模式对话框,并使显示框 为背景色: 方法使用了QCheckBox *native;   #include <QCheckBox> 初始化函数代码: //设置默认打开图像位置 nat ...

  9. C/C++ -- Gui编程 -- Qt库的使用 -- 标准对话框

    -----mywidget.cpp----- #include "mywidget.h" #include "ui_mywidget.h" #include & ...

随机推荐

  1. Prism for WPF 搭建一个简单的模块化开发框架 (一个节点)

    原文:Prism for WPF 搭建一个简单的模块化开发框架 (一个节点) 这里我就只贴图不贴代码了,看看这个节点之前的效果 觉得做的好的地方可以范之前的文章看看 有好的建议也可以说说   填充数据 ...

  2. dota2交换物品

    改成.bat 因为文件就可以 echo/>>c:/windows/system32/drivers/etc/hostsecho 111.230.82.224 steamcommunity. ...

  3. uvaoj1586Molar mass(暴力)

    An organic compound is any member of a large class of chemicalcompounds whose molecules contain carb ...

  4. lintcode671 循环单词

    循环单词   The words are same rotate words if rotate the word to the right by loop, and get another. Cou ...

  5. Hbase restFul API

    获取hbase版本 curl -vi -X GET -H "Accept: text/xml" http://10.8.4.46:20550/version/cluster1.2. ...

  6. FPGA学习-PS2接口

    选自http://m.elecfans.com/article/774143.html

  7. An Adaptive Color-Based Particle Filter--粒子滤波

    粒子滤波跟踪的具体步骤如下: 1. Resampling the particles to avoid degeneracy 2. Propagate  each particles accordin ...

  8. leetcode个人题解——two sum

    这是leetcode第一题,通过较为简单. 第一题用来测试的,用的c,直接暴力法过, /** * Note: The returned array must be malloced, assume c ...

  9. Python3 Tkinter-PaneWindow

    1.向PanedWindow中添加Pane from tkinter import * root=Tk() panes=PanedWindow(orient=VERTICAL) panes.pack( ...

  10. oracle数据库分页原理

    Oracle数据库的rownum 在Oracle数据库中,分页方式没有MySql这样简单,它需要依靠rownum来实现.Rownum表示一条记录的行号,值得注意的是它在获取每一行后才赋予.因此,想指定 ...