Рабочий минимум информации для рисования по windows-форме на примере MFC.
Часть I. Введение в рисование с GDI в MFC
Осуществляется рисование проще всего библиотекой GDI. Для начала хочу сказать о событии OnPaint. Оно вызывается каждый раз, когда нужно перерисовать окно (перетаскивание по экрану, изменение размеров и т.п.), поэтому в конец этой функции удобно впихнуть необходимую "заготовку" (например, оси координат, как сделано у меня). Теперь подробнее о самом рисовании. Всем известно, что "координаты" отличаются от декартовых (Центр в левом верхнем углу, ось Y направлена ВНИЗ, а X ничем не отличается), что стоит учесть (сделать пересчет) при рисовании по координатам.
Простой пример: CDC *pDC = GetDC();
pDC->MoveTo(10,10);
pDC->LineTo(20,20);
ReleaseDC(pDC);
Первая строка: "захватываем контекст", грубо говоря, указатель на область, где можно что-то нарисовать. Вторая строка: переместить перо в точку (10,10). Третья строка: Провести линию (из исходной точки) в точку (20,20). Есть еще море функций с геометрическими фигурами, сплайнами и другой интересной ерундой....но нам это пока не обязательно. Четвертая точка освобождает ресурсы (сбрасываются кисти, указатель контекста и т.п.), рисунок остается!
Чтобы вывести текст:
pDC->TextOutA(10,10,"Some text");
где в начале указываются координаты, от которого начать рисовать (если указать только текст, то текст будет печататься там, где в данный момент стоит перо)
Чтобы сделать фон текста прозрачным, перед его выводом написать:
pDC->SetBkMode(TRANSPARENT);
Чтобы сменить ручку (да, точее всего это РУЧКА, а кистью тут принято закрашивать области (пока не нуждаемся) ):
CPen pen(PS_SOLID,3,RGB(255,0,0));
pDC->SelectObject(&pen);
Первая строка: выбрать тип ручки ( сейчас сплошная, задается макросами с префиксом PS), ее толщина (в пикселах) и цвет (в данном случае используется макрос RGB(красный, зеленый, синий)). Во второй строке мы применяем ручку. Мы можем их несколько заготовить и менять по надобности.
ЧАСТЬ II. О рисовании (считать продолжением прошлого поста о рисовании при помощи GDI)
Основой рисовании на форме является ее метод OnPaint(), т.е. перерисовка окна. Этот обработчик вызывается при таких явлениях как сворачивание/разворачивание окна, заслон другим окном при потере фокуса, изменении размера и т.п. Главный плюс метода - он вызывается сам при вышеназванных системных изменениях (нам не нужно отлавливать перерисовки, чтобы снова нанести свой рисунок) и то, что мы можем его инициировать в необходимом нам месте. Рисунок не становится объектом, поэтому после нанесения мы не можем на него как-то повлиять, только перерисовать окно, но тот рисунок не повторять (принцип-аналог хита 90х - розовая доска для рисования, когда чем нибудь давишь на пленку, она слипается образуя рисунок, который можно убрать проведя специальной рейкой внутри доски и разделив слои). Поэтому самый простой способ - создание глобального массива объектов с их параметрами, координатами и т.п. и отрисовывать их обходя массив в конце OnPaint().
ПРИМЕР: Создадим диалоговый проект MFC, назовем его Project_name =). Создадим в папке Headers files (панель Solution Explorer, где файлы проекта) заголовочный файл "Line.h". В него напишем такой класс:
//----------------------------------------
// Начало Line.h
using namespace std;
struct Line
{
int X1, Y1; // Координаты начала
int X2, Y2; // Координаты конца
Line() { // Конструктор по умолчанию
X1 = X2 = Y1 = Y2 = 0;
}
Line(int x1,int y1, int x2, int y2) { // Конструктор
X1 = x1; X2 = x2;
Y1 = y1; Y2 = y2;
}
};
Line* lines; // Глобальный массив для объектов наших линий.
int count; // Здесь будем хранить кол-во линий.
// Конец Line.h
//----------------------------------------
Теперь, нужно открыть файл Project_nameDlg.cpp и в самом начале где подключаются заголовочные файлы добавить #include "Line.h" :
#include "stdafx.h" #include "Project_name.h" #include "Project_nameDlg.h" #include "afxdialogex.h" // Сюда добавить #include "Line.h" .....
Пусть в обработчике "ОК" мы будем добавлять 4 разных линии. В графическом редакторе диалого дважды кликнем по кнопке "ОК", автоматические будет создан обработчик void CProject_nameDlg::OnBnClickedOk(). Пусть будет 5 косых линий, оформляем обработчик так:
void CProject_nameDlg::OnBnClickedOk() {
if (::lines == 0) { // Если массива еще нет (указатель на массив = 0)
::count = 5;
Line = new Line[::count]; // Создаем линии
for (int i=0; i<::count; ++i) {
::lines[i].X1 = i * 10;
::lines[i].Y1 = 0;
::lines[i].X2 = 0;
::lines[i].Y2 = i * 10;
}
RedrawWindow(); // Вызываем OnPaint()
}
}
Итак, у нас есть массив линий с их координатами, мы вызываем OnPaint, осталось научить OnPaint рисовать наши линии. Ищем в этом же файле метод void CProject_nameDlg::OnPaint() и в самом его конце пишем цикл рисования наших линий:
if (::lines) { // Если массив линий не пустой
CDC* dc = GetDC();
for (int i=0; i<::count; ++i) {
MoveTo(::lines[i].X1, ::lines[i].Y1); // Перемещаемся в начальную точку
dc->LineTo(::lines[i].X2, ::lines[i].Y2); // Ведем карандашом в конечную точку
}
}
Все теперь можно компилировать и смело жать "ОК". Координаты определять эмпирически, либо по линейкам в графическом редакторе окна диалога. Предлагаю попробовать самостоятельно написать обработчик для какой-нибудь кнопки, которая будет удалять одну линию из массива (и ::count--), снова вызывать RedrawWindows().
ЧАСТЬ III Анимация.
Разница от обыкновенного рисования только в том, что this->OnPaint() будет вызываться по таймеру и соответственным образом будет изменяться массив данных.
У нас есть обычный проект, к примеру от части II.Алгоритм добавления таймера в проект:
1) Добавить в класс обработчика (в Project_nameDlg.h) объявление функции-обработчика "тика" таймера:
afx_msg void OnTimer( UINT );
2) Добавить перехватываемое сообщение WM_TIMER в карту сообщений (в Project_nameDlg.cpp), тогда он будет примерно такого вида:
BEGIN_MESSAGE_MAP(CProject_nameDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDOK, &CProject_nameDlg::OnBnClickedOk)
ON_WM_TIMER() // Вот такой вот прототип
END_MESSAGE_MAP()
3) Теперь мы можем писать функцию-обработчик тика (теперь уже конкретное определение функции из п.1):
void CProject_nameDlg::OnTimer( UINT uTime) {
// Обработка, к примеру, смещение объекта кубика
// и вызов функции OnPaint
Box1.X += 10;
this->RedrawWindow();
}
4) Самое главное: запуск и остановка таймера.Запуск выполняется функцией:SetTimer(целочисленный_идентификатор_таймера,частота_тика_в_мс,NULL);
Пример:
SetTimer(1,1000,NULL); // Таймер с id = 1, "тикающий" каждую секунду
Остановка:
KillTimer(1);
Их вызовы можем укладывать в кнопки "старт анимация" и "стоп анимация"
Комментариев нет:
Отправить комментарий