Рабочий минимум информации для рисования по 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);
Их вызовы можем укладывать в кнопки "старт анимация" и "стоп анимация"
Комментариев нет:
Отправить комментарий