Как вызвать wm paint
Прежде чем как продолжить чтение, снова запустите программу, приведенную в предыдущем разделе, и введите несколько символов. Затем минимизируйте и снова распахните окно. Вы увидите, что в восстановленном окне ничего не отображается. И в том случае, если окно перекрывается другим окном, а затем вновь становится активным, последний введенный символ не отображается. Причина этого проста: Windows, как правило, не запоминает содержимое окна (число окон зависит от приложений). Таким образом, все заботы по перерисовке содержимого окна возлагаются на Вашу программу.
Замечание. В силу различных технических причин при перемещении, а иногда и при изменении размеров окон их содержимое сохраняется и перерисовывается системой. Однако это не касается тех случаев, когда окно минимизируется или перекрывается другим окном, а затем восстанавливается в исходное состояние.
Прежде чем объяснять, как обрабатывается WM_PAINT, будет полезно рассказать, почему Windows не перерисовывает окно автоматически. В большинстве случаев перерисовать окно проще программе, нежели Windows, поскольку именно программа, а не Windows, должна «знать» о содержимом окна и способах его перерисовки. И хотя достоинства этого подхода являются спорными, в данном случае нужно просто принять его, поскольку не похоже, что он будет меняться.
// Обработка запроса на перерисовку окна
hdc=BeginPaint(hwnd,&paintstruct); // Получить DC
TextOut(hdc,1,1,str,strlen(str)); // Вывести буфер
EndPaint(hwnd, &paintstruct); // Освободить DC
HDC BeginPaint(HWND hwnd, LPPAINTSTRICT lpPS);
Второй параметр является указателем на структуру PAINTSTRUCT, которая определяется следующим образом:
typedef struct tagPAINTSTRUCT
HDC hdc; // Дескриптор DC
BOOL fErase; // Истина, если перерисовывается окно
RECT rcPaint; // Координаты области перерисовки
BOOL fRestore; // Зарезервировано
BOOL flncUpdate; // Зарезервировано
BYTE rgbReserved[32]; // Зарезервировано
Тип RECT – это структура, описывающая прямоугольную область:
typedef struct tagRECT
LONG left, top; // Верхний левый угол
LONG right, bottom; // Правый нижний угол
В структуре PAINTSTRUCT поле rcPaint задает координаты прямоугольной области в окне, которая должна быть перерисована. В данном случае нас не интересует содержимое структуры PAINTSTRUCT; можно предположить, что рабочая область окна будет перерисовываться целиком.
LRESULT CALLBACK WindowFunc(HWND, UINT,
char szWinName[]="МоеОкно"; // Имя класса окна
char str[80]="Пример"; // Буфер для строки вывода
int WINAPI WinMain(HINSTANCE hThisInst,
WNDCLASS wcl; // Определить класс окна
wcl.hInstance=hThisInst; // Дескриптор приложения
wcl.lpszClassName=szWinName; // Имя класса окна
wcl.lpfnWndProc=WindowFunc; // Функция окна
wcl.style=0; // Стиль по умолчанию
wcl.lpszMenuName=NULL; // Без меню
wcl.cbClsExtra=0; // Без дополнительной информации
(HBRUSH)GetStockObject(WHITE_BRUSH); //Белый фон
if(!RegisterClass(&wcl)) // Регистрируем класс окна
hwnd=CreateWindow(szWinName, // Создать окно
WS_OVERLAPPEDWINDOW, // Стиль окна
HWND_DESKTOP, // Нет родител. окна
hThisInst,// Дескриптор приложения
NULL); // Нет дополнит. аргументов
ShowWindow (hwnd, nWinMode); // Показать окно
UpdateWindow (hwnd); // и перерисовать
while(GetMessage(&msg,NULL,0,0)) // Запустить цикл
TranslateMessage(&msg); // Разреш. исп. клавиатуры
DispatchMessage (&msg); // Вернуть управл. Windows
// Следующая функция вызывается операционной
// системой Windows и получает в качестве
LRESULT CALLBACK WindowFunc(HWND hwnd,
case WM_CHAR: // Обработка нажатия клавиши
hdc=GetDC(hwnd); // Для получ. контекста устр-ва
TextOut(hdc, 1, 1, " ", 4); // Стереть символ
sprintf(str,"%c",(char)wParam); // Запись симв.
TextOut (hdc, 1, 1, str, strlen(str)); // Вывод
ReleaseDC (hwnd, hdc); // Освободить контекст
case WM_PAINT: // Перерисовка рабочей области
hdc=BeginPaint(hwnd,&paintstruct);// Получить DC
EndPaint(hwnd, &paintstruct); // Освободить DC
case WM_DESTROY: // Завершение программы
// данной функции, направляются на обработку
В нашем случае программа обработки WM_PAINT довольно простая, во многих же реальных программах она может быть весьма сложной, поскольку большинство окон содержат значительно больше отображаемой информации.
Зная механизм перерисовки содержимого окна, Вы должны всегда использовать его в своих программах. В реальных программах перерисовка информации производится, как правило, одним из трех способов.
Во-первых, программа может выводить информацию, получаемую в результате каких-либо вычислений. Это проще всего, когда не требуется взаимодействие с пользователем.
Во-вторых, в некоторых случаях Вам может понадобиться запоминать последовательность событий и «проигрывать» их при необходимости перерисовывать окно.
Наконец, программа может поддерживать виртуальный экран, который будет просто копироваться в окно при перерисовке. Это наиболее общий метод (и он реализован далее в этой книге). Выбор подхода полностью зависит от конкретного приложения. В некоторых примерах, приведенных в книге, способ перерисовки окна не определен, поскольку это требует увеличения программы и мешает концентрировать внимание на теме примера. Однако Ваши собственные программы должны будут перерисовывать свои окна для того, чтобы отвечать всем требованиям, предъявляемым к приложениям Windows.
Смотрите оконную функцию она в самом низу. Всё остальное шаблонный код по созданию окна.
НУ и чё это за отрисовка? Я создаю консольное приложение, наряду с окном показывается консоль. Я вожу окном туда-сюда по рабочему столу, а оно ни фига не перерисовывается! Как была изначальная надпись так и остаётся. Если окно скрыть чем-нибудь, а потом снова показать- надпись изменится, да. Но такая отрисовка мне даром не нужна. Мне надо чтобы на моих глазах явно отрисовывалось, как задумываю.
Спасибо, кто откликнется.
Тебе нужна бегущая строка? Тогда WM_PAINT не подойдет, как по мне. Ближе к вечеру постродаю с этим, если чё. i слишком быстро выходит за пределы строки, а старый текст нигде не стирается. А так всё более-менее.И UpdateWindow просто вызывает WndProc с WM_PAINT, если есть неперерисованные части, так что в обработчике WM_PAINT он точно не нужен. За исключением того, что WM_PAINT не отсылается окну, пока оно не изменит размеры или не перекроется другим окном. Посему при простом таскании окно обновляться не будет, пока не обработано WM_MOVING, с вызовом в его обработчике InvalidateRect. И UpdateWindow просто вызывает WndProc с WM_PAINT, если есть неперерисованные части, так что в обработчике WM_PAINT он точно не нужен.
поторопился с этим, согласен. Вызов UpdateWindow прост сводится к посылке WM_PAINT, как я понял.
что WM_PAINT не отсылается окну, пока оно не изменит размеры или не перекроется другим окномнеправда ваша. Если бы WM_PAINT не посылалсь окну, я бы спросил- почему WM_PAINT не отсылается. В том-то и дело, что отсылается. Вы потаскайте, потаскайте окно ПОВЕРХ других окон поинтенсивнее за рамку. Только проект пусть будет консольным. Очень неплохо видно счётчик обработчиков вызовов WM_PAINT
Короче всё решилось так:
Тупо вставил вызов InvalidateRect (hWnd, NULL, true); и всё на этом.Но на самом деле это фигня, я это сделал неосмысленнно, я угадал. Почему я вижу это решение корявым? А вот:
Понимаете, то есть я в обработчике WM_PAINT вызываю InvalidateRect, которая в свою очередь, отправляет WM_PAINT, в обработчике которого вызывается InvalidateRect, которая в свою очередь отправляет WM_PAINT, в обработчике которого вызывается.
Короче, как у меня стек не рушится- непонятно.
Понимаете, то есть я в обработчике WM_PAINT вызываю InvalidateRect, которая в свою очередь, отправляет WM_PAINT, в обработчике которого вызывается InvalidateRect, которая в свою очередь отправляет WM_PAINT, в обработчике которого вызывается. И даже если бы оно шло прямо в очередь, то тогда проблемы были бы с очередью, а не со стеком - тут же рекурсии нет. Вы потаскайте, потаскайте окно ПОВЕРХ других окон поинтенсивнее за рамку.И чего я вижу после двухминутного таскания, (если только не вылез за границы десктопа, соответственно, окно частично скрыто, и потом, когда оно заново отображается полностью, и его надо обновить)? А вот что: в консольном окне один-единственный ноль как был, так и есть.
Ну хорошо, пусть окну не посылается WM_PAINT когда оно не не посылается. Согласен.
Фактически вы научили меня как таскать окно, чтобы к нему не приходили WM_PAINT и как таскать окно, чтобы к нему приходили WM_PAINT. Спасибо, за науку, а как на счёт вопроса?
Итак, таскаем окно, чтобы к нему приходили WM_PAINT- просто заводя его за края декстопа. Ну вот, собсно и всё. Вопрос- как сделать чтобы при отрисовке оно отрисовалось. То есть я задумываю при каждой обработке WM_PAINT уменьшить строчку на один символ, а не получается. От чего ушли к тому и пришли.
Я нашёл какой-то там способ (выше описал), но хотелось бы всё же осмысленное что-нибудь.
Обществом преподавателей информатики замечено, что очень многие, при изучении нового языка программирования, прежде всего интересуются его графическими возможностями. Видимо, ещё с детства в нас не остыл интерес к красивым разноцветным кружочкам и овалам. Как вы и думали, API даёт в этом плане огромнейшую свободу, ибо всё, что знает Windows о рисовании, она знает от API.
Разноцветные геометрические фигуры, которые можно заливать любым цветом, эллипсы, окружности, прямоугольники, линии. Во введении я уже говорил про кисти и перья. Эта важнейшие особенности API дают нам возможность заливать фигуры не только сплошным покровом, а линии делать пунктирными и штрих-пунктирными.
С текстом мы вроде как разобрались, теперь переходим к нашим любимым графическим примитивам. Так как в этой главе мы будем изучать почти все графические функции, предлагаю сделать заготовочку, в которую можно примерять новые изученные функции.
case WM_PAINT :
hdc=BeginPaint(hWnd, &ps);
//здесь можно вставить какие-нибудь функции рисования
.
//обновляем окно
ValidateRect(hWnd, NULL);
//заканчиваем рисовать
EndPaint(hWnd, &ps);
break;
Графические функции GDI:
1. Вывод точки. SetPixel устанавливает заданный цвет в точке с указанными координатами:
COLORREF SetPixel(HDC hDC, int x, int y, COLORREF crColor);
Пример:
SetPixel(hDC, 10,10, RGB(0,0,0));
Функция GetPixel соответственно возвращает цвет в заданных координатах.
COLORREF Getpixel(hDC, int x, int y);
2. Рисование линий.
BOOL LineTo(hDC, int x, int y);
Функция рисует линию от текущей позиции до места, указанного в аргументах. Чтобы изменить тип линии (толщину, стиль)- меняется тип пера. Но об этом позже.
Так как в отличие от многих других подходов, в GDI нет функции рисования линии от одного указанного места до другого, её можно создать самому. Она будет соединять линией точки с координатами: x1,y1 и x2,y2.
BOOL Line(HDC hdc, int x1, int y1, int x2, int y2)
MoveToEx(hdc, x1, y1, NULL); //сделать текущими координаты x1, y1
return LineTo(hdc, x2, y2);
>
3. Дуга
BOOL Arc(hDC, int left, int top, int right, int bottom, int x1, int y1, int x2, int y2);
Первые четыре аргумента - левый верхний и правый нижний углы прямоугольника, в который вписан эллипс. Остальные значения - координаты точек, от которых будут проведены прямые к центру эллипса. В местах пересечения первой и второй прямой с радиусом эллипса, начинается и кончается дуга.
4. Прямоугольник. По умолчанию прозрачный, а вообще, тип его заливки определяется текущей кистью. По умолчанию она тоже прозрачная.
BOOL Rectangle(hDC, int left, int top, int right, int bottom); //аргументы - это коордианты левого верхнего и правого нижнего углов
5. Закруглённый прямоугольник. Его можно использовать, как импровизированную кнопку, если не лень возиться.
BOOL RoundRect(hDC, int left, int top, int right, int bottom, int width, int height);
Первые пять параметров совпадают с параметрами предыдущей фукнции. Далее width и height задают ширину и высоту эллипса, дуги которого ограничивают прямоугольник.
6. Кисти. Самое время познакомиться с кистями, так как фигуры, которые пойдут дальше выглядят лучше закрашенными. Мы уже немного затронули эту тему во вводной части. Теперь рассмотрим как задать свой стиль кисти. Как и setfillstyle() в DOS, кисть закрашивает какую-то область в какой-то цвет. В зависимости от кисти, она может делать это в полосочку, в клеточку, по диагонали.
Есть два способа объявить кисть. Первый - задать сплошную заливку, второй - указать стиль. Для этого существуют соответственно функции: CreateSoldBrush() и CreateHatchBrush().
Пример:
HBRUSH hBrush; //создаём объект-кисть
CreateSolidBrush(RGB(255,0,67)); //задаём сплошную кисть, закрашенную цветом RGB
SelectObject(hdc, hBrush); //делаем кисть активной
А вот как объявить не сплошную кисть:
CreateHatchBrush(int fnStyle, RGB(r,g,b));
Аргумент fnStyle принимает ряд константных значений:
HS_DIAGONAL - штрихует по диагонали
HS_CROSS - клеточка
HS_DIAGCROSS - диагональная сетка
HS_FDIAGONAL - по диагонали в другую сторону
HS_HORIZONTAL - горизонтальная "тельняшка"
HS_VERTICAL - вертикальный "забор"
HBRUSH hBrush1;
CreateHatchBrush(int fnStyle, RGB(r,g,b));
SelectObject(hdc, hBrush1); //делаем кисть активной
Ellipse(hdc, 100,100,200,300); //эллипс будет заштрихован
//обновляем окно
ValidateRect(hWnd, NULL);
//заканчиваем рисовать
EndPaint(hWnd, &ps);
break;
7. Перья. Они задают стиль линий, как и setlinestyle в DOS. Линия может быть жирной и тонкой, прерывистой и штрих-пунктирной. Всё предусмотрено. Это очень удобно для создания графиков функций, когда на график накладывается сетка, рисуются оси и выводится сама функция. Вы можете сказать, что это касается только математиков! Но почти любая фирма, что бы она не производила, иногда проводит презентации. На графике можно показать рост внешнего капитала, объём продаж и многое другое.
HPEN hPen; //Объявляется кисть
CreatePen(fnPenStyle, int width, RGB(r,g,b)); //Создаётся объект
SelectObject(hdc, hPen); //Объект делается текущим
fnStyle может принимать следующие значения:
PS_SOLD - сплошная
PS_DASH - состоящая из точек
PS_DOT - состоящая из тире
PS_DASHDOT - "точка-тире"
PS_DASHDOTDOT - "тире-точка-точка-тире"
PS_NULL - невидимая
PS_INSIDEFRAME - обводка замкнутых фигур
HPEN hPen1, hPen2, hPen3; //объявляем сразу три объекта-пера
hPen1=CreatePen(PS_DASHDOT, 1, RGB(0,0,255)); //создаём всё три
hPen2=CreatePen(PS_DASH, 1, RGB(255,0,255));
hPen3=CreatePen(PS_DOT, 1, RGB(0,128,256));
SelectObject(hdc, hPen1); //но в одним момент времени может быть только 1
Rectangle(hdc, 10,10,100,100); //рисуем фигуру соответствующим пером
SelectObject(hdc, hPen2); //меняем перо
Ellipse(hdc, 100,100,200,300); //рисуем другим пером
SelectObject(hdc, hPen3);
LineTo(hdc, 200,100);
//обновляем окно
ValidateRect(hWnd, NULL);
//заканчиваем рисовать
EndPaint(hWnd, &ps);
break;
Важно понять, что можно создавать хоть 10 перьев с помощью CreatePen, но применить в данный момент времени можно только 1 из них. Для этого и нужен SelectObject, чтобы окно поняло какую кисть в настоящий момент мы достаём из этюдника GDI.
6. Закрашенный прямоугольник
int FillRect(HDC hDC, CONST RECT *lprc, HBRUSH hbr);
lprc - закрашиваемый прямоугольник типа RECT.
hbr - кисть
Вот пример-фрагмент WM_PAINT:
RECT r; //объявляем экзмепляр структуры RECT - координаты прямоугольника.
r.left=100; //левый верхний угол
r.top=100;
r.right=200; //правый нижний
r.right=300;
//Заполняем прямоугольник
FillRect(hdc, &r, (HBRUSH)CreateSolidBrush(RGB(255,0,0)));
А вот и первый пример программы подоспел! Нарисуем что-то очень красивое - то, что мы уже умеем.
В этом примере мы освежим методы работы со шрифтами, поменяв стандартный системный шрифт на свой, затем нарисуем прямоугольник, заполненный красным цветом, зелёный эллипс и прямоугольник с закруглёнными краями жёлтого цвета. Естественно, что для того, чтобы их заполнить нам понадобятся кисти, а для синей рамки на жёлтом прямоугольнике понадобятся перья. Всё, что есть в этой программе вы уже умеете. Мы просто закрепляем теорию на практике.
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
char szProgName[]="Имя программы";
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
HWND hWnd;
MSG lpMsg;
WNDCLASS w;
w.lpszClassName=szProgName; //имя программы - объявлено выше
w.hInstance=hInstance; //идентификатор текущего приложения
w.lpfnWndProc=WndProc; //указатель на функцию окна
w.hCursor=LoadCursor(NULL, IDC_ARROW); //загружаем курсор
w.hIcon=0;
w.lpszMenuName=0;
w.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); //цвет фона окна
w.style=CS_HREDRAW|CS_VREDRAW;
w.cbClsExtra=0;
w.cbWndExtra=0;
//Если не удалось зарегистрировать класс окна - выходим
if(!RegisterClass(&w))
return 0;
//Создадим окно в памяти, заполнив аргументы CreateWindow
hWnd=CreateWindow(szProgName, //Имя программы
"Грфические возможности Win32 API", //Заголовок окна
WS_OVERLAPPEDWINDOW, //Стиль окна - перекрывающееся
100, //положение окна на экране по х
100, //положение по у
500, //ширина
400, //высота
(HWND)NULL, //идентификатор родительского окна
(HMENU)NULL, //идентификатор меню
(HINSTANCE)hInstance, //идентификатор экземпляра программы
(HINSTANCE)NULL); //отсутствие дополнительных параметров
//Выводим окно из памяти на экран
ShowWindow(hWnd, nCmdShow);
//Обновим содержимое окна
UpdateWindow(hWnd);
while(GetMessage(&lpMsg, NULL, 0, 0)) TranslateMessage(&lpMsg);
DispatchMessage(&lpMsg);
>
return(lpMsg.wParam);
>
//Функция окна
LRESULT CALLBACK WndProc(HWND hWnd, UINT messg,
WPARAM wParam, LPARAM lParam)
HDC hdc; //создаём контекст устройства
PAINTSTRUCT ps; //создаём экземпляр структуры графического вывода
LOGFONT lf;
HFONT hFont;
RECT r;
HBRUSH hBrush;
HPEN hPen;
//Создаём свой шрифт
strcpy(lf.lfFaceName,"Times New Roman"); //копируем в строку название шрифта
lf.lfHeight=20;
lf.lfItalic=1;
lf.lfStrikeOut=0;
lf.lfUnderline=0;
lf.lfWidth=10;
lf.lfWeight=40;
lf.lfCharSet=DEFAULT_CHARSET; //значение по умолчанию
lf.lfPitchAndFamily=DEFAULT_PITCH; //значения по умолчанию
lf.lfEscapement=0;
hFont = CreateFontIndirect(&lf);
SelectObject(hdc, hFont);
SetTextColor(hdc, RGB(0,0,255));
TextOut(hdc, 80,40, "Красота спасёт мир!!", 20);
//рисуем красный прямоугольник
r.top=100;
r.left=180;
r.right=400;
r.bottom=300;
FillRect(hdc, &r, HBRUSH(CreateSolidBrush(RGB(255,0,55))));
//рисуем зелёный эллипс
hBrush=CreateSolidBrush(RGB(10,200,100));
SelectObject(hdc, hBrush);
Ellipse(hdc, 20,100,200,200);
//рисуем закруглённый прямоугольник
hBrush=CreateSolidBrush(RGB(250,200,100));
SelectObject(hdc, hBrush);
hPen=CreatePen(2,2,RGB(0,0,255));
SelectObject(hdc, hPen);
RoundRect(hdc, 20, 250, 250, 350, 15, 15);
ValidateRect(hWnd, NULL);
EndPaint(hWnd, &ps);
break;
default:
return(DefWindowProc(hWnd, messg, wParam, lParam)); //освобождаем очередь приложения от нераспознаных
>
return 0;
>
Правда, красиво? Наконец-то из этой API нам удалось выжать что-то стоящее. Продолжаем обзор.
7. Прямоугольная рамка - как видите, существует немало функций для работы с прямоугольниками:
int FrameRect(HDC hDC, CONST RECT *lprc, HBRUSH hbr);
Применение аналогично предыдущей.
8. Инверсия значения цветов точек в заданной области
BOOL InvertRect(HDC hDC, CONST RECT *lprc);
9. Эллипс
BOOL Ellipse(HDC hdc, int x1, int y1, int x2, int y2);
координаты - это прямоугольник, в который вписывается эллипс
10. Хорда (сегмент эллипса) - параметры аналогичны Arc
BOOL Chord(HDC hDC, int left, int top, int right, int bottom, int x1, int y1, int x2, int y2); Функция соединяет хордой точки начала и конца дуги эллипса и закрашивает выделенный сегмент текущей кистью.
11. Сектор эллипса - аналог pieslice в DOS.
BOOL Pie(HDC hDC, int left, int top, int right, int bottom, int x1, int y1, int x2, int y2);
12. Многоугольник . Есть много функций рисования мноугольников. Мы рассмотрим две. Рисования от вершины к вершине и рисования отрезками:
PolyDraw оперирует вершинами:
POINT poly[8];
BYTE polytype[8];
poly[0].x=375; //координаты первой вершины
poly[0].y=375;
. //и так заполняем координаты всех восьми вершин
poly[7].x=400; //координаты восьмой вершины
poly[7].y =400;
. //другой массив содержит режим рисования
PolyDraw(hdc, poly, polytype, 8); //рисование многоугольника
Функция Polyline рисует набором отрезков:
POINT poly[4];
poly[0].x =10;
poly[0].y =30;
poly[1].x =30;
poly[1].y =30;
poly[2].x =30;
poly[2].y =60;
Polyline(hdc, poly , 4);
КАК вывести график функции?
Всё это были фрагменты графических функций. Теперь рассмотрим настоящий полноценный пример. Это будет простейший график функции.
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
//Процедура рисования линии
BOOL Line(HDC hdc, int x1, int y1, int x2, int y2);
char szProgName[]="Имя программы";
int i, xView, yView;
double y;
char Buf[2];
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
HWND hWnd;
MSG lpMsg;
WNDCLASS w;
w.lpszClassName=szProgName;
w.hInstance=hInstance;
w.lpfnWndProc=WndProc;
w.hCursor=LoadCursor(NULL, IDC_ARROW);
w.hIcon=0;
w.lpszMenuName=0;
w.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
w.style=CS_HREDRAW|CS_VREDRAW;
w.cbClsExtra=0;
w.cbWndExtra=0;
//Если не удалось зарегистрировать класс окна - выходим
if(!RegisterClass(&w))
return 0;
//Создадим окно в памяти, заполнив аргументы CreateWindow
hWnd=CreateWindow(szProgName,
"График функции",
WS_OVERLAPPEDWINDOW,
100,
100,
500,
400,
(HWND)NULL,
(HMENU)NULL,
(HINSTANCE)hInstance,
(HINSTANCE)NULL);
//Выводим окно из памяти на экран
ShowWindow(hWnd, nCmdShow);
//Обновим содержимое окна
UpdateWindow(hWnd);
case WM_SIZE:
xView=LOWORD(lParam);
yView=HIWORD(lParam);
//Рисуем оси координат
Line(hdc,0, 220,0,-220);//ось У
Line(hdc, -100,0,500,0);//ось Х
MoveToEx(hdc, 0,0,NULL); //перемещаемся в начало координат
//Создание красного пера
hPen=CreatePen(1,4,RGB(255,25,0));
SelectObject(hdc, hPen);
//синусоида
for(i=0; i<450; i++) y=180.0*(exp(-i*0.01))*sin(pi*i*(200.0/400.0)/180.0);
LineTo(hdc, i, (int)y);
>
//Делаем перо снова чёрным
hPen=CreatePen(1,1,RGB(0,0,0));
SelectObject(hdc, hPen);
//Наносим деления
for(i=-100; i<500; i+=100)
Line(hdc, i, 3,i,-3);
Line(hdc, -3, i,3,i);
itoa(i, Buf, 10);
TextOut(hdc, i-5, -5, Buf , strlen(Buf));
TextOut(hdc, -5, i, Buf , strlen(Buf));
>
ValidateRect(hWnd, NULL); //Обновляем экран
EndPaint(hWnd, &ps);
break;
default:
return(DefWindowProc(hWnd, messg, wParam, lParam)); //освобождаем очередь приложения от нераспознаных
>
return 0;
>
//Функция рисования линии
BOOL Line(HDC hdc, int x1, int y1, int x2, int y2)
MoveToEx(hdc, x1, y1, NULL); //сделать текущими координаты x1, y1
return LineTo(hdc, x2, y2); //нарисовать линию
>
Поскольку в данной программе большое внимание уделяется всяким украшательствам: делениям и надписям, обращу ваше внимание на главное - создавать графики совсем не сложно. И вот как это делается:
Затем определим область вывода. Мы хотим, чтобы при увеличении координаты по у график рос вверх, а не вниз.
SetViewportExtEx(hdc, xView, -yView, NULL);
Обратите внимание: yView указан со знаком -. Значит все координаты по у будут расти в обратную сторону - вверх.
Центр графика обычно где-нибудь посередине экрана. Координаты же увеличиваются из левого верхнего угла. Перенесём центр графика:
SetViewportOrgEx(hdc, xView/6, yView/2, NULL);
В точке, равной 1/6 максимального значения по х и 1/2 значения по этого значения по у будет центр.
Можно также задать длину осей - 500 и 500. Для этого применяется следующая функция:
SetWindowExtEx(hdc, 500,500, NULL);
Как вы уже знаете, окно имеет логические координаты и физические. Для того, чтобы логические координаты совпадали с физическими, а также, чтобы единица отложенная по х была равно единице отложенной по у, задаётся режим MM_ISOTROPIC. Его задаёт функция:
SetMapMode(hdc, MM_ISOTROPIC);
Вот, как будет выглядеть эта конструкция вцелом:
SetMapMode(hdc, MM_ISOTROPIC); //логические единицы отображаем, как физические
SetWindowExtEx(hdc, 500,500, NULL); //Длина осей
SetViewportExtEx(hdc, xView, -yView, NULL); //Определяем облась вывода
SetViewportOrgEx(hdc, xView/6, yView/2, NULL); //Начало координат
Дальше надо нарисовать оси. К нашей радости, точка 0, 0 сместилась на середину экрана, в левую его часть. Исходя из этого, рисуем оси, применяя самописную функцию Line:
Line(hdc,0, 220,0,-220);//ось У
Line(hdc, -100,0,500,0);//ось Х
MoveToEx(hdc, 0,0,NULL);
В выводе графика нет ничего примечательного. Переменная i меняется от 0 до 450. Подставляя i в формулу, мы получаем зависимость y от i. Рисуем линию до этой точки. Небольшие отрезки сольются в непрерывную линию.
for(i=0; i<450; i++) y=180.0*(exp(-i*0.01))*sin(pi*i*(200.0/400.0)/180.0);
LineTo(hdc, i, (int)y);
>
Иногда, требуется хранить значения физических величин несколько дней. Тогда график выполняется с длиннющей горизонтальной полосой прокрутки. При желании, можно вернуться и посмотреть были ли изменения, и если да, то какие.
Другой выход из положения - вести историю, записывая её в файл, а на экран выводя только показания последних пяти минут.
Задания:
1. Добавьте в приложения с графиком стрелочки и подписи к осям.
2. Нарисуйте снеговика известными средствами GDI. Напомню, что снеговик состоит из трёх непрозрачных эллипсов грязно-белого цвета, на груди у него должны быть пуговки, в руке - метла, а на голове - ведро.
3. Нарисуйте красивый паровозик, клубы из трубы которого будут выводиться в цикле в виде синих эллипсов
4. Нарисуйте кораблик с жёлтой палубой и красными бортами, используя Polyline или Polydraw. Кораблик покоится на синих волнах, которые нарисованы дугами разной толщины. Попробуйте вывести дуги в цикле.
5. Нарисуйте с помощью одних только линий домик с забором
BOOL InvalidateRect(HWND hwnd,
CONST RECT *lpRect,
// Каркасная программа для Windows, осуществляющая
LRESULT CALLBACK WindowFunc(HWND, UINT,
char szWinName[]="МоеОкно"; // Имя класса окна
char str[80]="Пример"; // Буфер для строки вывода
int X=1, Y=1; // Координаты строки на экране
int WINAPI WinMain(HINSTANCE hThisInst,
WNDCLASS wcl; // Определить класс окна
wcl.hInstance=hThisInst; // Дескриптор приложения
wcl.lpszClassName=szWinName; // Имя класса окна
wcl.lpfnWndProc=WindowFunc; // Функция окна
wcl.style=0; // Стиль по умолчанию
wcl.lpszMenuName=NULL; // Без меню
wcl.cbClsExtra=0; // Без дополнительной информации
(HBRUSH)GetStockObject(WHITE_BRUSH); //Белый фон
if(!RegisterClass(&wcl)) // Регистрируем класс окна
hwnd=CreateWindow(szWinName, // Создать окно
WS_OVERLAPPEDWINDOW, // Стиль окна
HWND_DESKTOP, // Нет родител. окна
hThisInst,// Дескриптор приложения
NULL); // Нет дополнит. аргументов
ShowWindow (hwnd, nWinMode); // Показать окно
UpdateWindow (hwnd); // и перерисовать
while(GetMessage(&msg,NULL,0,0)) // Запустить цикл
TranslateMessage(&msg); // Разреш. исп. клавиатуры
DispatchMessage (&msg); // Вернуть управл. Windows
// Следующая функция вызывается операционной
// системой Windows и получает в качестве
LRESULT CALLBACK WindowFunc(HWND hwnd,
case WM_CHAR: // Обработка нажатия клавиши
X=Y=1; // Ввод отобразить в левом верхнем углу
sprintf(str,"%c",(char)wParam); // Запись симв.
InvalidateRect(hwnd,NULL,1); // Перерисов. экран
case WM_PAINT: // Перерисовка рабочей области
hdc=BeginPaint(hwnd,&paintstruct);// Получить DC
EndPaint(hwnd, &paintstruct); // Освободить DC
case WM_RBUTTONDOWN: // Нажата правая кнопка мыши
strcpy(str,"Нажата ПРАВАЯ кнопка");
X = LOWORD(lParam); // В X и Y записываются
Y = HIWORD(lParam); // текущие координаты мыши
InvalidateRect(hwnd,NULL,1); // Перерисов. экран
case WM_LBUTTONDOWN: // Нажата левая кнопка мыши
strcpy(str,"Нажата ЛЕВАЯ кнопка");
X = LOWORD(lParam); // В X и Y записываются
Y = HIWORD(lParam); // текущие координаты мыши
InvalidateRect(hwnd,NULL,1); // Перерисов. экран
case WM_DESTROY: // Завершение программы
// данной функции, направляются на обработку
Как видите, когда весь вывод выполняется при обработке WM_PAINT, программа стала несколько меньше и в некотором смысле проще для понимания. Кроме того, как упоминалось в начале этого раздела, такая программа позволяет Windows определить наилучшее время для перерисовки окна.
Читайте также: