Использование градиентной заливки в 2D-графике

Введение

2D-графике и классе QPainter, используемом для рисования в среде программирования Qt, написано немало статей. Использование этого класса не представляет особых трудностей для человека, немного знакомого с английским языком и программированием. Однако, в отличие от других стандартных C-библиотек, Qt предлагает программисту некоторые нестандартные решения в области 2D графики. Одним из таких элементов является градиентная заливка. О том, что это такое, как с ней работать и каковы ее возможности в области программирования элементов графического интерфейса, мне и хотелось бы рассказать в рамках этой статьи.

Что же такое градиент?

Ответ на этот вопрос несложно найти и в литературе, и на тематических сайтах. Приведу здесь такое определение градиента – «…альтернатива однородному заполнению цветом. Градиентная заливка основана на интерполяции цветов, обеспечивающей сглаженные переходы от одного цвета к другому». На практике это означает то, что у программиста появляется альтернатива стандартной сплошной заливке.

Qt предлагает три варианта градиентов: линейный, конический и радиальный. Все они представлены на рисунке ниже.

 

QLinearGradient – линейный градиент. Процесс интерполяции производится в пределах наклонной полосы, от одного ее края к другому.

QRadialGradient – радиальный градиент. Процесс интерполяции производится в пределах заданной окружности. Интерполяция производится окружностями увеличивающегося радиуса, начиная от заданной точки, которая может не совпадать с центром главной окружности.

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

Перед тем, как описать каждый из программных вариантов создания градиентов, я введу несколько общих терминов, которые необходимы для работы с ними. Все обозначения, используемые в тексте определений, взяты с рисунка описания видов градиентов в Qt.

Центральная точка – для радиального градиента – точка центра окружности, в которой будет произведена интерполяция. Для конического градиента – точка, вокруг которой будет вращаться луч интерполяции. В обоих случаях на рисунке обозначена как (xc, yc).

Радиус интерполяции – для радиального градиента – радиус окружности, в пределах которой будет производиться интерполяция цветов (обозначен r на рисунке).

Стартовый угол – для конического градиента – угол поворота стартового луча интерполяции против часовой стрелки относительно текущего положения оси X, выходящей из центральной точки градиента. Совпадает с конечным углом.

Стартовая точка – точка, от которой начинается интерполяция ((x1, y1) для линейного и радиального градиентов). Для конического градиента можно считать стартовым не точку, а луч, определяющий стартовый угол относительно оси X.

Конечная точка – точка, в направлении которой производится интерполяция ((x2, y2) для линейного градиента). В случае использования радиального или конического градиента понятие конечной точки не определено – конический градиент охватывает все 360 градусов координатной сетки, а для радиального градиента этот параметр заменяет радиус интерполяции.

Создание градиентов и функции для работы с ними

Программист при создании градиента может использовать 2 типа конструкторов градиента: пустой или с параметрами. Возможные варианты конструкторов градиента:

 

QLinearGradient:

В качестве параметров в конструктор передаются стартовая и конечная точки градиента формата точки Qt – QPointF или в глобальных координатах (x, y).

 

QRadialGradient:

Для радиального градиента конструктор с параметрами может быть 2-х видов. В качестве параметров могут передаваться центральная и стартовая точки градиента вместе с радиусом или только центральная точка (которая в этом случае считается стартовой, если иное не обозначено в последующем коде) и радиус. Как и в случае линейного градиента, координаты точек можно задавать 2 различными способами.

 

QConicalGradient:

Конструктор конического градиента с параметрами предполагает передачу центральной точки и стартового угла. Задать точку можно как глобальными координатами, так и через QPointF. Кроме задания параметров в стандартных конструкторах, программист может также использовать функции класса градиента. Список этих функций представлен ниже:

 

 

QLinearGradient:

Используются 2 переопределяемые функции – установки начальной (setStart) и конечной (setFinalStop) точек линейного градиента. Параметры в эти функции можно передавать как в глобальных координатах (x, y), так и в формате точки Qt – QPointF.

 

QRadialGradient:

Используются 2 переопределяемые функции – установки центральной (setCenter) и стартовой (setFocalPoint) точек радиального градиента. Аналогично линейному градиенту, параметры функции могут быть переданы в виде глобальных координат (x, y), так и в формате точки Qt – QPointF. Кроме этих двух функций введена функция установки радиуса интерполяции – setRadius.

 

QLinearGradient:

Используется 1 переопределяемая функция – установки центральной точки (setCenter). Параметры в эту функцию передаются аналогично другим Qt-градиентам – в глобальных координатах (x, y), или в формате точки Qt – QPointF. Также используется функция установки стартового угла – setAngle.

 

 

Помимо перечисленных выше функций, все классы градиентов Qt наследуют функции класса-родителя QGradient (более подробно о нем можно прочитать в Qt Assistant). Основной наследуемой функцией является setColorAt, с помощью которой программист может задать цвета, интерполируемые внутри градиента:

  • void setColorAt ( qreal position, const QColor & color )

Первый аргумент функции – position, и он может принимать любые дробные значения в диапазоне от 0 до 1. Смысл этого аргумента заключается в том, чтобы задать степень распространения цвета по области градиента от стартовой точки или угла. Чем ближе к 1 будет это число, тем медленнее будет падать яркость цвета при удалении от стартовой точки (угла), и тем ближе он приблизится к границе градиента.

Аргумент color – элемент QColor, то есть цвета, и может создаваться с помощью стандартной RGB-палитры. Для задания цвета в качестве второго параметра функции достаточно использовать неименованное создание объекта цвета QColor(x, y, z), где x – интенсивность красного цвета, y – зеленого и z – синего цветов. Более подробно о классе QColor можно прочитать в Qt Assistant.

Для того, чтобы при рисовании использовать градиент, нужно установить его как текущую кисть с помощью функции setBrush (функция является одним из стандартных методов класса QPainter, более подробно о ней можно прочитать в QtAssistant), а затем нарисовать требуемую фигуру.

Ниже представлен пример использования линейного градиента:

 

 

//создание дескриптора контекста рисования и линейного градиента
 
QPainter painter(this);
 
QLinearGradient gradient;
 
 
 
//выделение памяти для градиента в конструкторе без параметров
 
gradient = new QLinearGradient;
 
 
 
//установка стартовой и конечной точек градиента
 
gradient->setStart(60, 120);
 
gradient->setFinalStop(80, 120);
 
 
 
//установка цветов градиента
 
gradient->setColorAt(0.43, QColor(280,10,20));
 
gradient->setColorAt(1.0, QColor(30,0,0));
 
 
 
//выбор заливки созданным градиентом
 
painter.setBrush(*gradient);
 
 
 
//рисование прямоугольника, содержащего созданный градиент
 
painter.drawRect(60, 50, 20, 100);
 
 
 
//очистка памяти
 
delete gradient;

 

Использование градиентов

 

Как было сказано выше, градиент является альтернативой сплошной заливке. Соответственно, его можно использовать вместо нее для создания более реалистичной картинки и эффекта псевдо-трехмерности. Самым простым вариантом применения градиента является рисование нестандартных элементов интерфейса (например, кнопок не прямоугольной формы) – задача, которую встроенными средствами Qt решить нельзя. В качестве примеров применения градиента для создания элементов интерфейса приведены варианты создания круглой красной и треугольной зеленой кнопок.

 

Создание красной круглой кнопки

 

Создать круглую кнопку несложно при использовании одного радиального градиента, код ниже описывает один из вариантов такого создания.

 

 

//создание дескриптора контекста рисования
 
QPainter painter(this);
 
 
 
//закраска окна сплошным тёмным цветом
 
painter.setBrush(Qt::SolidPattern);
 
painter.setBrush(QColor(40,40,40));
 
 
 
/*закраска прямоугольной области, которое занимает текущее окно; в качестве аргументов
передаются ширина и высота текущего окна (стандартные методы класса QPainter)*/
 
painter.drawRect(0, 0, this->width(), this->height());
 
 
 
//создание координат центральной точки радиального градиента и его радиуса
 
int x = 60, y = 60, r = 40;
 
 
 
//создание и объявление радиального градиента с центром в точке (х, у) радиуса 40
 
QRadialGradient* gradient;
 
gradient = new QRadialGradient(x, y, r);
 
 
 
//установка начальной точки
 
gradient->setFocalPoint(x - 20, y - 20);
 
 
 
//установка используемых цветов и их границ распространения
 
gradient->setColorAt(0.0, QColor(216,30,20));
 
gradient->setColorAt(0.9, QColor(168,24,17));
 
gradient->setColorAt(1, QColor(0,0,0));
 
 
 
//выбор закраски градиентом
 
painter.setBrush(*gradient);
 
 
 
//отрисовка окружности, целиком содержащей созданный градиент
 
painter.drawEllipse(x - r, y - r, r*2, r*2);
 
 
 
//очистка памяти, удаление градиента
 
delete gradient2;
 
 
 
//сдвиг центральной точки радиального градиента второй кнопки по оси y вниз
 
y += 100;
 
 
 
//объявление радиального градиента с центром в точке (х, у) радиуса 40
 
gradient2 = new QRadialGradient(x, y, 40);
 
 
 
//установка используемых цветов и их границ распространения
 
gradient2->setColorAt(0.02, QColor(216,30,20));
 
gradient2->setColorAt(0.8, QColor(168,24,17));
 
gradient2->setColorAt(1, QColor(0,0,0));
 
 
 
//выбор закраски градиентом
 
painter.setBrush(*gradient2);
 
 
 
//отрисовка окружности, целиком содержащей созданный градиент
 
painter.drawEllipse(x - r, y - r, r*2, r*2);
 
 
 
//очистка памяти, удаление градиента
 
delete gradient2;

 

 

После выполнения этого кода на экране будут нарисованы 2 кнопки. Сверху будет отображаться не нажатая, и выглядеть она будет следующим образом:

 

Под ней будет нарисована нажатая кнопка, и она будет иметь такой вид:

 

Создание зеленой треугольной кнопки

 

Создание многоугольных элементов интерфейса несколько сложнее, чем может показаться на первый взгляд. Первая проблема, которая встает перед программистом – даже треугольник является нестандартной фигурой, и создать его с помощью стандартной функции нельзя. Для создания таких элементов нужно использовать класс QPainterPath. Описание фигуры с помощью этого класса сводится к ограничению некоторой области линиями и ее закраске (более подробно этот класс описан в QtAssistant).

Теперь, когда треугольник создан, нужно применить к нему градиентную заливку. Здесь и появляется основная трудность – если использовать для отрисовки стороны треугольника линейный градиент, чтобы придать ей иллюзию трехмерности, то 2 оставшиеся окажутся закрашенными обычной сплошной заливкой (так как градиент закрашивает всю заданную область). Следовательно, чтобы все стороны были закрашены с использованием градиента, нужно треугольник разделить на сегменты. Самый простой вариант разбиения треугольника на 3 сегмента представлен на рисунке ниже:

 

Однако у этого разбиения есть один недостаток, который проявляется уже непосредственно при рисовании фигуры: он подходит только для случая, когда все стороны имеют одну и ту же степень интерполяции (подходит для нажатой кнопки). В противном случае сегменты в углах треугольника не будут стыковаться по цвету.

Пример рисования треугольника с различной степенью интерполяции цветов и разделением на треугольные сегменты:

 

Как видно из этой картинки, градиент левой стороны треугольника из-за своей ограниченности внутри треугольника в углах фигуры не прикрыл необходимую область, и образовались артефакты. Для рисования не нажатой кнопки нужно использовать другой вариант разбиения; в примере используются сегменты следующего вида:

 

Именно вследствие такого разбиения и возникают неочевидные на первый взгляд координаты при задании сегментов кнопки. Сами координаты этих точек смещения для стыковки градиентов зависят только от степени интерполяции и должны подбираться отдельно для каждого случая.

Стартовая и конечная точки направления интерполяции для каждого из полученных сегментов выбираются как на рисунке ниже, красная линия со стрелкой обозначает направление интерполяции:

 

Фактически, вектор направления интерполяции должен быть перпендикулярен стороне, к которой направлен, поэтому в коде координаты конечной точки взяты как координаты проведенной высоты. Для первого случая начальная и конечная точки выбраны следующим образом из-за того, что ширина сегмента очень мала, и на таком участке цвет не может быть хорошо интерполирован. Ниже приведен код, рисующий треугольную кнопку.

 

 

//создание дескриптора контекста рисования
 
QPainter painter(this);
 
//закраска окна сплошным тёмным цветом
 
painter.setBrush(Qt::SolidPattern);
 
painter.setBrush(QColor(40,40,40));
 
 
 
/*закраска прямоугольной области, которое занимает текущее окно; в качестве аргументов
передаются ширина и высота текущего окна (стандартные методы класса QPainter)*/
 
painter.drawRect(0, 0, this->width(), this->height());
 
 
 
//создание линейного градиента и объекта QPainterPath для задания сегментов кнопки
 
QPainterPath* path;
 
QLinearGradient* gradient;
 
 
 
//создание не нажатой кнопки
 
 
 
//создание координат углов треугольника, начиная с левой нижней по часовой стрелке
 
int x1 = 20, y1 = 100, x2 = x1 + 50, y2 = y1 - 80, x3 = x2 + 50, y3 = y1;
 
 
 
//создание коэффициента для определения координат конечной точки интерполяции
 
float k = 0.43;
 
 
 
//создаем не нажатую кнопку
 
//создание сегмента, содержащего правую сторону кнопки
 
path = new QPainterPath;
 
path->moveTo(x2, y2);
 
path->lineTo(x2 + 1, y2 + 6);
 
path->lineTo(x3 - 4, y3 - 2);
 
path->lineTo(x3, y3);
 
path->lineTo(x2, y2);
 
 
 
//создание градиента для правой стороны кнопки
 
gradient = new QLinearGradient(x1, y1, x2 + (x3 - x2)*k, \
    y2 + (y3 - y2)*k);
 
 
 
//установка используемых цветов и их границ распространения
 
gradient->setColorAt(0.95, QColor(14,120,20));
 
gradient->setColorAt(1.0, QColor(30,0,0));
 
 
 
//отрисовка сегмента кнопки
 
painter.fillPath(*path,QBrush(*gradient));
 
 
 
//очистка памяти, удаление градиента и фигуры сегмента
 
delete path; delete gradient;
 
 
 
//создание сегмента, содержащего нижнюю сторону кнопки
 
path = new QPainterPath;
 
path->moveTo(x1, y1);
 
path->lineTo(x1 + 7, y1 - 2);
 
path->lineTo(x3 - 4, y3 - 2);
 
path->lineTo(x3, y3);
 
path->lineTo(x1, y1);
 
 
 
//создание градиента для нижней стороны кнопки
 
gradient = new QLinearGradient(x2, y2, x2, y3);
 
 
 
 
 
//установка используемых цветов и их границ распространения
 
gradient->setColorAt(0.95, QColor(14,120,20));
 
gradient->setColorAt(1.0, QColor(30,0,0));
 
 
 
//отрисовка сегмента кнопки
 
painter.fillPath(*path,QBrush(*gradient));
 
 
 
//очистка памяти, удаление градиента и фигуры сегмента
 
delete path; delete gradient;
 
 
 
//создание сегмента, содержащего левую сторону и центральную часть кнопки
 
path = new QPainterPath;
 
path->moveTo(x1, y1);
 
path->lineTo(x2, y2);
 
path->lineTo(x2 + 1, y2 + 6);
 
path->lineTo(x3 - 4, y3 - 2);
 
path->lineTo(x1 + 7, y1 - 2);
 
path->lineTo(x1, y1);
 
 
 
//создание градиента для левой левой стороны и центральной части кнопки
 
gradient = new QLinearGradient(x3 - 3, y3 - 3, \
    x2 - (x2 - x1)*k, y2 + (y1 - y2)*k);
 
 
 
//установка используемых цветов и их границ распространения
 
gradient->setColorAt(0.86, QColor(14,120,20));
 
gradient->setColorAt(1.0, QColor(0,0,0));
 
 
 
//отрисовка сегмента кнопки
 
painter.fillPath(*path,QBrush(*gradient));
 
 
 
//очистка памяти, удаление градиента и фигуры сегмента
 
delete path; delete gradient;
 
 
 
//задание новых координат углов треугольника для нажатой кнопки
 
x1 = 20; y1 = 200; x2 = x1 + 50; \
y2 = y1 - 80; x3 = x2 + 50; y3 = y1;
 
 
 
//переназначение коэффициента для определения координат \
//конечной точки интерполяции
 
k = 0.503;
 
 
 
//создание нажатой кнопки
 
 
 
//создание сегмента, содержащего нижнюю сторону кнопки
 
path = new QPainterPath;
 
path->moveTo(x1, y1);
 
path->lineTo(x2, y2);
 
path->lineTo(x2, y2 + (int)(y1 - y2)*0.7);
 
path->lineTo(x1, y1);
 
 
 
//создание градиента для левой стороны кнопки
 
gradient = new QLinearGradient(x2, y2 + (y1 - y2)*0.7, \
x2 - (x2 - x1)*k, y2 + (y1 - y2)*k);
 
 
 
//установка используемых цветов и их границ распространения
 
gradient->setColorAt(0.8, QColor(14,120,20));
 
gradient->setColorAt(1.0, QColor(0,0,0));
 
 
 
 
 
//отрисовка сегмента кнопки
 
painter.fillPath(*path,QBrush(*gradient));
 
 
 
//очистка памяти, удаление градиента и фигуры сегмента
 
delete path; delete gradient;
 
 
 
//создание сегмента, содержащего нижнюю сторону кнопки
 
path = new QPainterPath;
 
path->moveTo(x1, y1);
 
path->lineTo(x2, y2 + (int)(y1 - y2)*0.7);
 
path->lineTo(x3, y3);
 
path->lineTo(x1, y1);
 
 
 
//создание градиента для нижней стороны кнопки
 
gradient = new QLinearGradient(x2, y2 + (y1 - y2)*0.7, x2, y1);
 
 
 
//установка используемых цветов и их границ распространения
 
gradient->setColorAt(0.8, QColor(14,120,20));
 
gradient->setColorAt(1.0, QColor(0,0,0));
 
 
 
//отрисовка сегмента кнопки
 
painter.fillPath(*path,QBrush(*gradient));
 
 
 
//очистка памяти, удаление градиента и фигуры сегмента
 
delete path; delete gradient;
 
 
 
//создание сегмента, содержащего правую сторону кнопки
 
path = new QPainterPath;
 
path->moveTo(x2, y2);
 
path->lineTo(x2, y2 + (int)(y1 - y2)*0.7);
 
path->lineTo(x3, y3);
 
path->lineTo(x2, y2);
 
 
 
//создание градиента для правой стороны кнопки
 
gradient = new QLinearGradient(x2, y2 + (y1 - y2)*0.7, \
    x2 + (x3 - x2)*k, y2 + (y3 - y2)*k);
 
 
 
//установка используемых цветов и их границ распространения
 
gradient->setColorAt(0.8, QColor(14,120,20));
 
gradient->setColorAt(1.0, QColor(0,0,0));
 
 
 
//отрисовка сегмента кнопки
 
painter.fillPath(*path,QBrush(*gradient));
 
 
 
//очистка памяти, удаление градиента и фигуры сегмента
 
delete path; delete gradient;//создание дескриптора контекста рисования
 
QPainter painter(this);
 
//закраска окна сплошным тёмным цветом
 
painter.setBrush(Qt::SolidPattern);
 
painter.setBrush(QColor(40,40,40));
 
 
 
/*закраска прямоугольной области, которое занимает текущее окно; в качестве аргументов
передаются ширина и высота текущего окна (стандартные методы класса QPainter)*/
 
painter.drawRect(0, 0, this->width(), this->height());
 
 
 
//создание линейного градиента и объекта QPainterPath для задания сегментов кнопки
 
QPainterPath* path;
 
QLinearGradient* gradient;
 
 
 
//создание не нажатой кнопки
 
 
 
//создание координат углов треугольника, \
//начиная с левой нижней по часовой стрелке
 
int x1 = 20, y1 = 100, x2 = x1 + 50, \
    y2 = y1 - 80, x3 = x2 + 50, y3 = y1;
 
 
 
//создание коэффициента для определения координат конечной точки интерполяции
 
float k = 0.43;
 
 
 
//создаем не нажатую кнопку
 
//создание сегмента, содержащего правую сторону кнопки
 
path = new QPainterPath;
 
path->moveTo(x2, y2);
 
path->lineTo(x2 + 1, y2 + 6);
 
path->lineTo(x3 - 4, y3 - 2);
 
path->lineTo(x3, y3);
 
path->lineTo(x2, y2);
 
 
 
//создание градиента для правой стороны кнопки
 
gradient = new QLinearGradient(x1, y1, \
    x2 + (x3 - x2)*k, y2 + (y3 - y2)*k);
 
 
 
//установка используемых цветов и их границ распространения
 
gradient->setColorAt(0.95, QColor(14,120,20));
 
gradient->setColorAt(1.0, QColor(30,0,0));
 
 
 
//отрисовка сегмента кнопки
 
painter.fillPath(*path,QBrush(*gradient));
 
 
 
//очистка памяти, удаление градиента и фигуры сегмента
 
delete path; delete gradient;
 
 
 
//создание сегмента, содержащего нижнюю сторону кнопки
 
path = new QPainterPath;
 
path->moveTo(x1, y1);
 
path->lineTo(x1 + 7, y1 - 2);
 
path->lineTo(x3 - 4, y3 - 2);
 
path->lineTo(x3, y3);
 
path->lineTo(x1, y1);
 
 
 
//создание градиента для нижней стороны кнопки
 
gradient = new QLinearGradient(x2, y2, x2, y3);
 
 
 
 
 
//установка используемых цветов и их границ распространения
 
gradient->setColorAt(0.95, QColor(14,120,20));
 
gradient->setColorAt(1.0, QColor(30,0,0));
 
 
 
//отрисовка сегмента кнопки
 
painter.fillPath(*path,QBrush(*gradient));
 
 
 
//очистка памяти, удаление градиента и фигуры сегмента
 
delete path; delete gradient;
 
 
 
//создание сегмента, содержащего левую сторону и центральную часть кнопки
 
path = new QPainterPath;
 
path->moveTo(x1, y1);
 
path->lineTo(x2, y2);
 
path->lineTo(x2 + 1, y2 + 6);
 
path->lineTo(x3 - 4, y3 - 2);
 
path->lineTo(x1 + 7, y1 - 2);
 
path->lineTo(x1, y1);
 
 
 
//создание градиента для левой левой стороны и центральной части кнопки
 
gradient = new QLinearGradient(x3 - 3, y3 - 3, \
x2 - (x2 - x1)*k, y2 + (y1 - y2)*k);
 
 
 
//установка используемых цветов и их границ распространения
 
gradient->setColorAt(0.86, QColor(14,120,20));
 
gradient->setColorAt(1.0, QColor(0,0,0));
 
 
 
//отрисовка сегмента кнопки
 
painter.fillPath(*path,QBrush(*gradient));
 
 
 
//очистка памяти, удаление градиента и фигуры сегмента
 
delete path; delete gradient;
 
 
 
//задание новых координат углов треугольника для нажатой кнопки
 
x1 = 20; y1 = 200; x2 = x1 + 50; y2 = y1 - 80; x3 = x2 + 50; y3 = y1;
 
 
 
//переназначение коэффициента для определения координат \
//конечной точки интерполяции
 
k = 0.503;
 
 
 
//создание нажатой кнопки
 
 
 
//создание сегмента, содержащего нижнюю сторону кнопки
 
path = new QPainterPath;
 
path->moveTo(x1, y1);
 
path->lineTo(x2, y2);
 
path->lineTo(x2, y2 + (int)(y1 - y2)*0.7);
 
path->lineTo(x1, y1);
 
 
 
//создание градиента для левой стороны кнопки
 
gradient = new QLinearGradient(x2, y2 + (y1 - y2)*0.7, \
    x2 - (x2 - x1)*k, y2 + (y1 - y2)*k);
 
 
 
//установка используемых цветов и их границ распространения
 
gradient->setColorAt(0.8, QColor(14,120,20));
 
gradient->setColorAt(1.0, QColor(0,0,0));
 
 
 
 
 
//отрисовка сегмента кнопки
 
painter.fillPath(*path,QBrush(*gradient));
 
 
 
//очистка памяти, удаление градиента и фигуры сегмента
 
delete path; delete gradient;
 
 
 
//создание сегмента, содержащего нижнюю сторону кнопки
 
path = new QPainterPath;
 
path->moveTo(x1, y1);
 
path->lineTo(x2, y2 + (int)(y1 - y2)*0.7);
 
path->lineTo(x3, y3);
 
path->lineTo(x1, y1);
 
 
 
//создание градиента для нижней стороны кнопки
 
gradient = new QLinearGradient(x2, y2 + (y1 - y2)*0.7, x2, y1);
 
 
 
//установка используемых цветов и их границ распространения
 
gradient->setColorAt(0.8, QColor(14,120,20));
 
gradient->setColorAt(1.0, QColor(0,0,0));
 
 
 
//отрисовка сегмента кнопки
 
painter.fillPath(*path,QBrush(*gradient));
 
 
 
//очистка памяти, удаление градиента и фигуры сегмента
 
delete path; delete gradient;
 
 
 
//создание сегмента, содержащего правую сторону кнопки
 
path = new QPainterPath;
 
path->moveTo(x2, y2);
 
path->lineTo(x2, y2 + (int)(y1 - y2)*0.7);
 
path->lineTo(x3, y3);
 
path->lineTo(x2, y2);
 
 
 
//создание градиента для правой стороны кнопки
 
gradient = new QLinearGradient(x2, y2 + (y1 - y2)*0.7, \
    x2 + (x3 - x2)*k, y2 + (y3 - y2)*k);
 
 
 
//установка используемых цветов и их границ распространения
 
gradient->setColorAt(0.8, QColor(14,120,20));
 
gradient->setColorAt(1.0, QColor(0,0,0));
 
 
 
//отрисовка сегмента кнопки
 
painter.fillPath(*path,QBrush(*gradient));
 
 
 
//очистка памяти, удаление градиента и фигуры сегмента
 
delete path; delete gradient;

 

Координаты сегментов в случае не нажатой кнопки выбираются без какого-то определенного алгоритма; конечная цель программиста – получить красивую картинку из сегментов. Формулы стартовой и конечной точек в конструкторах линейного градиента следуют из расчетов координат точки, принадлежащей отрезку, концы которого заданы. Параметр 0.7 берется из практических соображений – треугольник при использовании такого коэффициента делится на практически равные треугольные сегменты. Коэффициент k можно рассчитать из прямоугольных треугольников, образуемых линией интерполяции или подобрать вручную.

 

Описанному выше коду соответствует следующая картинка не нажатой кнопки:

 

Нажатая же будет изображена так:

 

Заключение

 

Процесс работы с градиентами не является чем-то сложным с точки зрения программирования, но сам процесс рисования с использованием градиентов может быть затратным с точки зрения времени, так как не всегда бывает просто сразу подобрать красивое сочетание цветов. В рамках этой статьи я постарался рассмотреть некоторые базовые методы работы с градиентами и применить их для конкретной цели – создания элементов пользовательского интерфейса. Вообще использование этого элемента не ограничивается рамками интерфейсов – закраска градиентом может быть использована и просто в компьютерной 2D графике, как альтернатива сплошной заливки.

Дмитрий Белов, Виталий Петров