Поcобие_БЕЛОВ_Графический_интерфейс_API
.pdf121
SetBkColor(hdc, RGB(255-i*15,255-i*20,255-i*25));
Этим завершают процедуру установки атрибутов контекста отображения. Теперь можно вывести текст:
TextOut( hdc, 0, cyClient-30, szFont, strlen(szFont));
Шрифт hNFont больше не требуется. Поэтому в контекст выбираем предыдущий шрифт и удаляем шрифт hNFont:
SelectObject(hdc.hOFont); DeteteObject(hNFont);
Освобождают контекст отображения:
EndPaint(hwnd,&ps);
3.4.5. Определение метрик шрифта
Метрику шрифта, выбранного в контекст отображения hdc, определяют с помощью функции GetTextMetrics:
BOOL GetTextMetrics( HDC hdc, LPTEXTMETRIC lptm);
Параметр lptm указывает на структуру типа TEXTMETRIC, в которую необходимо записать метрики шрифта. Все размеры записываются в логических единицах контекста отображения hdc. При успешном выполнении функция возвращает ненулевое значение.Структура TEXTMETRIC предназначена для описания базовой информации о метриках шрифта и описана следующим образом:
typedef struct { LONG tmHeight; LONG tmAscent;
LONG tmDescent;
LONG tmInternalLeading; LONG tmExternalLeading; LONG tmAveCharWidth; LONG tmMaxCharWidth; LONG tmWeight;
LONG tmOverhang;
LONG tmDigitizedAspectX; LONG tmDigitizedAspectY; BCHAR tmFirstChar; BCHAR tmLastChar; BCHAR tmDefaultChar; BCHAR tmBreakChar; BYTE tmItalic;
BYTE tmUnderlined;
122
BYTE tmStruckOut; BYTE tmPitchAndFamily; BYTE tmCharSet;
}TEXTMETRIC;
Рассмотрим назначение полей этой структуры:
1)tmHeight – общая высота букв, равна tmAscent+tmDescent;
2)tmAscent – часть высоты букв от базовой линии с учетом таких элементов, как тильда в букве «И»;
3)tmDescent – часть высоты букв ниже базовой линии;
4)tmInternalLeading – высота таких выступающих элементов, как тильда в букве «Й», и может быть равна нулю;
5)tmExternalLeading – высота межстрочного интервала, рекомендуемая разработчиком шрифта, может быть приравнена нулю.
6)tmAveCharWidth – средняя ширина строчных букв, равна ширине латинской буквы «х»;
7)tmMaxCharWidth – ширина самой широкой буквы;
8)tmWeight – жирность шрифта, может находиться в пределах от 0 до 1000, как и для логического шрифта;
9)tmOverhang – величина изменения ширины символов при построении наклонного или полужирного шрифта из нормального шрифта;
10)tmDigitizedAspectX – разрешение устройства отображения по горизонтали;
11)tmDigitizedAspectY – разрешение устройства отображения по вертикали;
12)tmFirstChar – код первого символа в шрифте.
13)tmLastChar – код последнего символа в шрифте;
14)tmDefaultChar – код символа, заменяющего любой отсутствующий в шрифте символ;
15)tmBreakChar – код символа переноса слов с одной строки на другую при выравнивании текста;
16)tmItalic – означает наклонный шрифт;
17)tmUnderlined – означает подчеркнутый шрифт;
18)tmStruckOute – означает перечеркнутый шрифт;
19)tmPitchAndFamily – код семейства шрифта. Четыре младших бита этого кода комбинацией следующих констант определяют информацию о шаге и типе шрифта:
|
123 |
|
|
Константа |
Пояснение |
TMPF_FIXED_PITCH |
Если этот бит установлен, то шрифт с переменным |
|
шагом, иначе – с постоянным |
TMPF_VECTOR |
Если этот бит установлен, то шрифт векторного |
|
типа |
|
|
TMPF_TRUETYPE |
Если этот бит установлен, то шрифт типа TrueType |
TMPF_DEVICE |
Если этот бит установлен, шрифт определен |
|
устройством |
|
|
Четыре старших бита кода tmPitchAndFamily описывают семейство шрифта, так же как и для логического шрифта.
20) tmCharSet – код используемого набора символов, такой же, как и для логического шрифта.
Задача. В рабочей области окна вывести таблицу. В заголовке перечислить названия функций, а в строках таблицы отобразить значения этих функций. Причем вывод таблицы ограничить числом столбцов и строк, которые полностью помещаются в рабочей области.
Следующее приложение решает эту задачу. Листинг 3.7. Вывод таблицы в окно.
#include <windows.h> #include <math.h>
BOOL RegClass(WNDPROC, LPCTSTR, UINT); LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); HINSTANCE hInstance;
char szClass[ ] = "TableClass";
//Структура столбца данных typedef struct
{ |
char |
str[15]; //Поле имени столбца |
|
double |
val[50}; //Массив данных столбца |
} |
COLUMN; //Имя типа |
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{MSG msg; HWND hwnd;
::hInstance=hInstance;
if (!RegClass(WndProc, szClass, COLOR_WINDOW)) return FALSE;
124
hwnd = CreateWindow(szClass, "ТАБЛИЦА", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0,0, hInstance, NULL);
if (!hwnd) return FALSE; while(GetMessage(&msg, 0,0,0)) DispatchMessage(&msg);
return msg.wParam;
}
BOOL RegClass(WNDPROC Proc, LPCTSTR szName, UINT brBackground)
{WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW; wc.cbClsExtra=wc.cbWndExtra=0; wc.lpfnWndProc = Proc;
wc.hInstance = hInstance; wc.lpszClassName = szName;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(brBackground+1); wc.lpszMenuName = NULL;
return (RegisterClass(wc) != 0);
BOOL DrawLine( HDC hdc, int x0, int y0, int x, int y) { MoveToEx(hdc,x0,y0, NULL);
return LineTo(hdc, x, у);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam,
LPARAM lParam)
{//Описываем массив из 10 столбцов static COLUMN cols[10];
static int ex, cy, cxChar, cyChar; switch (msg)
{case WM_SIZE;
{cx=LOWORD(lParam);
cy=HIWORD(lParam); return 0;
125
}
case WM_CREATE:
{//Заполняем заголовки столбцов strcpy(cols[0].str,T); strcpy(cols[1].str,"10*sin(i/10.)"); strcpy(cols[2].str,"20*sin(i/20.)"); strcpy(cols[3].str,"30*sin(i/30.)"); strcpy(cols[4].str,"40*sin(i/40.)"); strcpy(cols[5].str,"50*sin(i/50.)"); strcpy(cols[6].str,"60*sin(i/60.)"); strcpy(cols[7].str,"70*sin(i/70.)"); strcpy(cols[8].str,"80*sin(i/80.)"); strcpy(cols[9].str/90*sin(i/90.)");
//Заполняем массивы данных столбцов for (int i=0; i<50; i++)
{//В первом столбце будут номера строк cols[0].val[i]=i;
//В остальных столбцах будут данные for(intj=1;j<10;j++)
cols[j].val[i]=10*sin(i/10./j);
}
//Определяем средние высоту и ширину символов
{TEXTMETRIC tm;
HDC hdc=GetDC(hwnd); //Определяем метрики текста
GetTextMetrics(hdc, &tm); ReleaseDC(hwnd.hdc);
//Число пикселей в высоте символа
cyChar = tm.tmHeight + tm.tmExtemaiLeading
//Число пикселей в средней ширине cxChar=tm.tmAveCharWidth+1;
}
return 0;
}
caseWM_PAINT:
{PAINTSTRUCT ps;
HDC hdc=BeginPaint(hwnd,&ps); char str[20];
126 //Установим начальные параметры для вывода
int left=cxChar, |
//Левый край |
|
top=cyChar/2 |
//Верх |
|
dx=cxChar, |
//Пробел по оси X |
|
dy=cyChar/4, |
//Пробел по оси Y |
|
hy=cyChar+dy+dy, |
//Высота для строки |
|
right=cx-cxChar, |
//Правый край |
bottom=cy-cyChar, //Низ
int bounds[10], //Массив ширин столбцов i=0,j=0; //Заполняем массив ширинами столбцов while (j<10)
{//Для j-ro столбца выбираем ширину bounds[j]=strlen(cols[j].str);
for (i=0; i<50; i++) { _gcvt( cols[j].val[i], 7, str ); int ss=strlen(str); if (bounds[j]<ss) bounds[j]=ss;
}
if (bounds[j]<=3) bounds[j]=4; if (bounds[j]>10) bounds[j]-=2; bounds[j] = cxChar*(bounds[j]);
}
//Определяем максимальное число столбцов, //которое помещается на ширине рабочей области int dd=left, maxcol=0;
while (maxcol<10)
{if (dd+bounds[maxcol]>right) break;
dd += bounds[maxcol]; maxcol++;
}
//right теперь указывает на правый край таблицы rignt=dd;
//Подгоняем ширину окна к числу столбцов
{RECT re; GetWindowRect(hwnd, &rc);
MoveWindow(hwnd, rc.left, rc.top,
//Изменяем только ширину окна
re. rig ht-rc. Ieft-(cx-right)+dx, rc.bottom-rc.top, TRUE);
}
127
//////НАЧАЛО ВЫВОДА ТАБЛИЦЫ//////
//Устанавливаем режим выравнивания
SetTextAlign(hdc, TA_RIGHT);
//////Начало вывода шапки таблицы//////
int y=top; //Текущая координата по оси Y //Горизонтальная линия на всю ширину
DrawLine(hdc,left,y,right,y);
//Заголовки столбцов
int x=left; //Текущая координата по оси X //Вертикальная линия слева от столбца
DrawLine(hdc, x, у, х, y+hy); for (j=0;j<maxcol; j++)
{//Устанавливаем х на правой границе столбца x+=boundsO];
TextOut(hdc,x-dx,y+dy, cols[j].str,strlen(cols[j].str));
//Вертикальная линия справа от столбца
DrawLine(hdc,x,y,x,y+hy);
}
//Горизонтальная линия на всю ширину y+=hy; DrawLine(hdc, left,y,right,y);
//////Конец вывода шапки таблицы//////
//////Начало вывода данных таблицы//////
1=0;//Счетчик номера строки данных while (i<50 && y<bottom)
{//Вертикальная линия слева от столбца x=left;
DrawLine(hdc,x,y,x,y+hy);
for (j=0;j<maxcol; j++) { x+=boundsO]; //Преобразуем целое число в строку if(j==0) itoa((int)cols[0].va1[i], str, 10);
//Преобразуем вещественное число в строку else _gcvt( cols[j].val[i], 7, str); TextOut(hdc,x-dx,y+dy,str,strlen(str)); //Вертикальная линия справа от столбца
DrawLine(hdc,x,y,x,y+hy);
}
//Горизонтальная линия на всю ширину y+=hy; DrawLine(hdc,left,y,right,y);
i++;
128
}
//////Конец вывода данных таблицы//////
EndPaint(hwnd,&ps); return 0;
}
case WM_DESTROY: { PostQuitMessage(0); return 0; }
}
return
DefWindowProc(hwnd, msg, wParam, lParam);
}
Рассмотрим основные шаги решения данной задачи.
|
А. Сначала создают в памяти таблицу. |
|
1. |
Описывают тип структуры столбца данных: |
|
typedef struct |
|
|
{ |
char str[15]; |
//Поле имени столбца |
|
double val[50]; |
//Массив данных столбца |
} |
COLUMN; |
//Имя типа |
2.Описывают массив cols из 10 столбцов этого типа: static COLUMN cols[10];
3.Заполняют этот массив некоторыми значениями. Сначала заполняются заголовки столбцов:
strcpy(cols[0].str,"i");
strcpy(cols[1].str,"10*sin(i/10.)");
…
strcpy(cols[9].str,”90*sin(i/90.)");
Затем заполняют массивы данных столбцов: for (int i=0; i<50; i++)
{ cols[0].val[i]=i; for(intj=1;j<10;j++)colsy].val[i]=10*sin(i/10./j);
}
При этом в первый столбец записывают номера строк, а в другие столбцы записывают значения функций.
Б. Определяют геометрические параметры вывода таблицы в рабочую область.
1. Определяют среднюю высоту и ширину символов установленного шрифта:
TEXTMETRIC tm; HDC hdc=GetDC(hwnd);
129 //Определяем метрики текста
GetTextMetrics(hdc, &tm); ReleaseDC(hwnd,hdc);
//Число пикселей в высоте символа
cyChar = tm.tmHeight + tm.tmExternalLeading; //Число пикселей в средней ширине cxChaFtm.tmAveCharWidth+1;
Метрики шрифта определяют на этапе создания окна, так как используют шрифт по умолчанию. Иначе метрики необходимо определять после выбора шрифта в контекст отображения.
2.Устанавливают границы вывода таблицы. Здесь left указьшает налевую, top – на верхнюю, right – на правую, bottom – на нижнююграницу таблицы в рабочей области. Параметр dx равен величине пробела по оси X, a dy – по оси Y, hy задает высоту строки таблицы.Также описывают переменные bounds[10], i и j.
3.Массив bounds заполняют значениями ширины столбцов:
while (j<10)
{bounds[j]=strlen(cols[j].str); for (i=0; i<50; i++)
{_gcvt( cols[j].val[i], 7, str); int ss=strlen(str);
if (bounds[j]<ss) bounds[j]=ss;
}
if (bounds[j]<=3) bounds[j]=4; if (bounds[j]>10) bounds[j]-=2; bounds[j] = cxChar*(bounds[j]); j++
}
Для j-го столбца вычисляют число символов заголовка: bounds[j]=strlen(cols[j].str);
Далее определяют максимально необходимое число символов для вывода в этот столбец:
_gcvt( cols[j].val[i], 7, str);
int ss=strlen(str); if (bounds[j]<ss) bounds[j]=ss;
После определения необходимого числа символов вносят незначительные коррекции. Так, в столбце выделяют место для вывода не менее четырех символов:
130 if (bounds[j]<=3) bounds[j]=4;
При большом (более 10) числе символов уменьшают число выделяемых позиций:
if(bounds[0]>10) bounds[j]-=2;
Вычисляют ширину столбца в пикселях: bounds[j] = cxChar*bounds[j];
4. Определяют максимальное число maxcol столбцов, которое помещается в установленных границах:
int dd=left, maxcol=0; while (maxcol<10)
{ if (dd+bounds[maxcol]>right) break; dd += bounds[maxcol]; maxcol++;
}
5.Значение right переносят на правую границу столбца maxcol: right=dd;
6.Уменьшают ширину окна так, чтобы она была достаточна для вывода только maxcol столбцов:
RECT re; GetWindowRect(hwnd, &rc); MoveWindow(hwnd, rc.left, rc.top, rc.right-rc.left-(cx-right)+dx, rc.bottom-rc.top, TRUE);
В. Выводят таблицу.
1.Устанавливают режим выравнивания по правой границе:
SetTextAlign(hdc, TA_RIGHT);
Обратите внимание на то, что в этом режиме горизонтальная координата вывода текста указывает на точку, где завершается вывод правого конца выводимой строки.
2.Рисуют заголовки столбцов таблицы.
В первую очередь рисуют горизонтальную линию на всю ширину таблицы (от левой границы – left до правой границы – right):
DrawLine(hdc,left,y,right,y);
Для рисования заголовков столбцов вспомогательную координату по оси X устанавливают на левой границе (x=left). После этого рисуют вертикальную линию на левой границе таблицы высотой hy:
DrawLine(hdc, x, у, х, y+hy);
Вывод заголовка j-ro столбца содержит следующие шаги: 1) устанавливают х на правой границе столбца: x+=bounds[j];