суббота, 6 августа 2011 г.

Qt Виджет выбора цвета (ColorPicker)

Виджет выбора цвета, ColorPicker
В процессе написания простого редактора картинок понадобилось написать виджет выбора цвета из палитры. Дизайнер нарисовал вот такую штуку:

Предполагается, что по клику на квадратик должен выбираться соответствующий цвет. Как такое реализовать с точки зрения программиста?
Под катом самое простое и короткое возможное решение.

Опыта у меня нет, и это, по сути, первый реальный проект. Возможно, что всё, что я тут напишу уже десять раз реализовывалось, но мне итоговое решение показалось интересным. И так подумаем.

Решение наивное: в Qt обязательно есть подходящий виджет. Наивное оно не, потому что его нет, есть, например, QColorDialog, а нет такого, какой нарисовал дизайнер. А ведь надо точь в точь.


Решение в лоб: каждый квадратик это виджет, который по клику сообщает свой цвет. Этот цвет надо для каждого квадратика вбить руками/ прочитать из файла/ внести другим нудным способом. Для такого вида палитры это просто, а если квадратиков в 2 раза больше?


В процессе обсуждения этого вопроса была в шутку брошена идея: «Ты ещё цвет под мышкой через PrintScreen определи». Printscreen конечно не вариант, но почему бы не пойти по такому пути? Развитие идеи: есть такой метод grabWidget(), который возвращает изображение виджета, по которому можно определить цвет точки, по которой кликнул пользователь.
В развёрнутом виде это выглядит так:

        1) Сам виджет это просто картинка, состоящая только из цветных квадратиков (фон будет мешать, его мы нарисуем в отдельном фоновом виджете). Полезно ещё наложить маску по этой же картинке, чтобы области между квадратиков не ловили события мыши.

        2) На событие mousePressEvent() делаем grabWidget одного пикселя под мышкой, передав параметром наш виджет и прямоугольник размерами 1x1, находящийся по координатам мыши (e->pos()). Важно: pos(), а не globalPos()
        3) Так как QPixmap не даёт функционала попиксельного доступа, приводим его к QImage.
        4) Изымаем единственный пиксель картинки и берём его цвет. Всё.

Итого в три строчки мы получили необходимый нам цвет.
/*ColorPalette.h*/
 
#ifndef COLORPALETTE_H
#define COLORPALETTE_H
 
#include <QWidget>
#include <QMouseEvent>
#include <QLabel>
 
class ColorPalette : public QLabel
{
    Q_OBJECT
public:
    explicit ColorPalette(QWidget *parent = 0);
 
signals:
    void Color(QColor color);
 
public slots:
 
protected:
    virtual void mousePressEvent(QMouseEvent *e);
 
};
 
#endif // COLORPALETTE_H
 

/*ColorPalette.cpp*/
 
#include "colorpalette.h"
#include <QImage>
#include <QDebug>
#include <QBitmap>
 
ColorPalette::ColorPalette(QWidget *parent) :
    QLabel(parent)
{
    setAutoFillBackground(true);
    QPalette pal;
    QPixmap pix(":/img/palette.png");
    setMask(pix.mask());
    pal.setBrush(backgroundRole(), QBrush(pix));
    setPalette(pal);
    resize(pix.size());
}
 
void ColorPalette::mousePressEvent(QMouseEvent *e)
{
    QRect onePixRect(e->pos(), QSize(1,1));
    QPixmap pix = QPixmap::grabWidget(this, onePixRect);
    QImage img = pix.toImage();
    QColor color(img.pixel(0, 0));
    emit Color(color);
}
 


Если посмотреть повнимательнее, то можно обнаружить, что такой способ хорошо подойдёт для любой палитры, даже непрерывной:

Это очень полезное свойство, ведь если дизайнеру захочеться «такое же, но без крыльев», то программисту надо всего-лишь заменить картинку.

Исходники: проект QtCreator.

2 комментария:

  1. Привет, как с тобой можно связаться?!
    Спасибо!

    ОтветитьУдалить
    Ответы
    1. Привет, лучше через почту russage at rambler dot ru. Там уже смогу написать скайп или другие контакты.

      Удалить