- •Часть IV
- •Глава 25
- •224 Глава 25
- •226 Глава 25
- •Глава 26
- •230 ____Глава 26
- •232 Глава 26
- •234 Глава 26
- •236 Глава 26
- •238 Глава 26
- •240 Глава 26
- •242 Глава 26
- •Глава 27
- •Virtual void InitMainWindow();//Замещаем функцию InitMainWindow
- •246 Глава 27
- •248 Глава 27
- •250 Глава 27
- •252 . Глава27
- •Глава 28 Диалоговые окна
- •256 Глава 28
- •258 Глава 28
- •260 Глава 28
- •262 Глава 28
- •264 Глава 28
- •266 Глава 28
- •268 Глава 28
- •Глава 29
- •270 Глава 29
- •272 Глава 29
- •274 Глава 29
- •276 Глава 29
- •278 Глава 29
- •280 Глава 29
- •282 Глава 29
232 Глава 26
/*Класс приложения, производный от TApplication (ради InitMainWindow)*/
class MyApp:public TApplication{ public:
void InitMainWindow();//Замещаем функцию InitMainWindow };
/*Класс главного окна, производный от TFrameWindov (ряди Paint) */ class MyWindow:public TFrameWindow{ public:
MyWindow(TWindow*parent,char far*title):TFrameWindow(parent,title){ Attr.X=0;Attr.Y=0; Attr.W=245;Attr.H=200; }
void Paint(TDC&,bool,TRect&);//Переопределяем функцию Paint };
/*Замещенная функция InitMainWindow()*/ void MyApp::InitMainWindow(void){
SetMainWindow(new MyWindow(0,"Программа 26-2")); }
/*Замещенная функция Paint ()*/ void MyWindow::Paint(TDC&dc,bool,TRect&){ int i;//Переменная циклов
char ticks[10][2];//Массив цифр под осью X TPoint р; //Текущая координата для рисования рисок dc.Rectangle(border);//Рисуем рамку for(i=0;i<=9;i++){//В цикле по 10 точкам
p=graph.BottomLeft()+=i*dx;//Текущая координата верхних концов рисок dc.MoveTo(p); / / В цикле перемещаемся по верхним концам рисок dc.LineTo(p.OffsetBy(0,margins));//и рисуем риски вниз до рамки wsprintf(ticks[i],"%d",i);//Преобразуем цифры в символы dc.TextOut(p.OffsetBy(-3,margins+3),ticks[i]);//Выводим цифры под осью }
dc.MoveTo(graph.TopLeft());//Рисуем верхнюю
dc.LineTo(graph.TopLeft().OffsetBy(-margins,0));//горизонтальную риску dc.MoveTo(graph.BottomLeft ());//Рисуем нижнуюю
dc.LineTo(graph.BottomLeft().OffsetBy(-margins,0));//горизонтальную риску . dc.TextOut(graph.TopLeft().OffsetBy(-margins-25,-7),"100");//Выводим цифры dc.TextOut(graph.BottomLeftО.OffsetBy(-margins-10,-7),"0");/1'масштаба TPen pen1(TColor::LtBlue,2);//Создаем синее перо толщиной 2 пиксела dc.SelectObject(pen1);//и выбираем его в контекст устройства
dc.MoveTo(graph.BottomLeft().OffsetBy(0,-data[0]));//Перемещаемся к первой точке for(i=1;i<=9;i++)//В цикле по 9 точкам соединияем линиями точки графика
dc.LineTo(graph.BottomLeft().OffsetBy(i*dx,-data[i])); TPen pen2(TColor::LtMagenta);//Создаем фиолетовое перо dc.SelectObject(pen2);//и выбираем его в контекст устройства TBrush brush(TColor::LtMagenta);//Создаем фиолетовую кисть dc.SelectObject(brush);//и выбираем ее в контекст устройства for(i=0; i<=9;i++)//Рисуем залитые точки графика
dc.Ellipse(graph.BottomLeft().OffsetBy(i*dx-radius,-data[i]-radius),size); }
/*Главная функция приложения OwlMain*/ int OwlMain(int,char*[]){ return MyApp().Run(); }
В примере 26-2 в главное окно приложения выводится график значений, записанных в массиве целых чисел data. Для упрощения программы эти числа не считываются из файла данных на диске, как это было бы более естественно, а описаны непосредственно в программе; кроме того, не предусмотрено ни масштабирования чисел (считается, что они расположены в диапазоне от 0 до 100), ни изменения их количества. Точки графика соединены прямыми линиями и, кроме того, каждая точка отображается в виде залитого кружка.
Программа по своей структуре аналогична уже рассмотренным, если не считать вид функции InitMainWindow, в которой создается объект класса MyWindow и созданное окно объявляется главным окном приложения. Поскольку указатель на этот объект (в предыдущих примерах он назывался myWin) нигде больше в программе не используется, нет необходимости его создавать отдельным предложением программы. В варианте, использованном в данном примере
SetMainWindow(new MyWindow(0,"Программа 26-2"));
оператор new, выступающий в качестве аргумента функции SetMainWindow(), возвращает значение указателя на создаваемый объект класса MyWindow, и все работает правильно, хотя этот указатель и не присутствует в программе в явном виде.
Обработка сообщения WM_PAINT u интерфейс GDI 233
Для упрощения разработки программы геометрические характеристики изображения записаны в виде символических констант (для целочисленных констант описатель int можно опускать), смысл которых показан на рис. 26.4.
Рис. 26.4. Обозначения точек, линий и прямоугольников для программы 26-2.
В программе 26-2, в частности, демонстрируется использование классов OWL 5.0, служащих для определения размеров и координат точек, линий и прямоугольных областей. Объекты этих классов сами по себе не производят никаких видимых эффектов, однако их удобно использовать в функциях GDI, требующих задания геометрических характеристик. Класс TPoint задает координаты точки, класс TRect -координаты прямоугольника, а класс TSize включает в себя пару чисел int, которые можно использовать для задания каких-либо размеров или смещений. В настоящем примере с помощью объекта класса TSize описаны размеры квадрата, служащего для вывода на экран круглых точек.
Объекты классов положения и размеров описаны в примере 26-2 в начале программы, в области глобальных переменных. С таким же успехом их можно было описать непосредственно в функции Paint(), где только они и используются.
Функция Paint() в принципе выглядит точно так же, как и в традиционных программах и состоит из последовательности вызовов функций GDI, осуществляющих рисование элементов изображения - рамки, рисок, надписей, кружков и т.д. Все эти функции вызываются для объекта контекста устройства dc, созданного конструктором класса TPaintDC, как это было описано в предыдущем разделе. Использованные в программе функции (Rectangle(), MoveTo(), LineTo() и др.) являются инкапсулированными функциями API Windows и не нуждаются в пояснениях. В качестве аргументов этих функций можно указывать обычные координаты точек, как и в API Windows, однако использование объекты классов положения и размеров предоставляет более широкие возможности. При этом функции GDI позволяют указывать геометрические характеристики рисуемых фигур в разных вариантах. Так, для рисования прямоугольной рамки в программе использован вызов
dc.Rectangle(border); //Рисуем рамку
где border - объект класса TRect. С таким же успехом можно было указать координаты прямоугольника в виде двух объектов класса TPoint
dc.Rectangle(X0Y0,XmYm);
или четырьмя целыми числами.
Имея объект класса TRect, можно с. помощью функций-членов этого класса выполнять различные операции над прямоугольником: извлекать его геометрические характеристики (площадь, высоту и ширину, координаты сторон и углов), перемещать его в заданную точку, увеличивать или уменьшать, определять вхождение в него точки с заданными координатами и т.д. Например, использованные в программе функции BottomLeft() и TopLeft() возвращают координаты нижнего левого и верхнего левого углов рамки графика, а функция InflateBy() с отрицательными аргументами позволяет получить из прямоугольника-рамки border прямоугольник graph меньшего размера, описывающий область собственно графика (см. секцию описания объектов классов в начале программы)
С помощью функций-членов класса TPoint можно найти расстояние данной точки от начала координат окна, а также переместить точку в другое место или определить координаты точки, отстоящей от данной на заданное смещение по осям. Именно последняя функция (носящая имя OffsetBy()) несколько раз используется в настоящем примере. Например, в паре предложений
dc.MoveTo(graph.TopLeft()); //Рисуем верхнюю
dc.LineTo(graph.TopLeft().OffsetBy(-margins,0)); //горизонтальную риску