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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM