В приложении 29-2 демонстрируется методика создания приспособлений, используемых для ввода в программу конкретных значений настраиваемых переменных. В рассматриваемом примере в главном окне приложения создается квадратное дочернее окно-панель с серым фоном, в которое выводятся т.н. фигуры Лиссажу, получаемые при одновременном изменении х- и у- координат точки по синусоидальному закону. Если х- и у-координаты изменяются с одинаковой частотой, а сдвиг фаз между ними отсутствует, то фигура Лиссажу вырождается в прямую линию, наклоненную к осям под углом 45 градусов. При сдвиге фаз между колебаниями по осям, равным пи/2, кривая представляет собой правильную окружность.
Если же х- и у-частоты не совпадают, да еще между ними имеется сдвиг фаз, то образуются типичные кривые разнообразной формы, знакомые любому специалисту по электронике.
Для изменения соотношения частот колебаний по осям х и у используется приспособление-ползунок (класс TSlider), который в конкретном примере позволяет изменять соотношение частот от 1 до 10, а для задания Сдвига фаз между колебаниями - линейка прокрутки (класс TScrollBar), задающая сдвиг фаз от 0 до я с шагом 1/32 пи. Устанавливаемые с помощью приспособлений значения отношения частот и сдвига фаз отображаются в главном окне над соответствующими приспособлениями.
|
|
На рис. 29.3 изображен вид окна приложения с примером фигуры Лиссажу.
//Приложение 29-2. Дочернее окно, ползунок и линейка прокрутки
//Файл 29-2.h
#define ID_FREQUENCYSLIDER 100
#define ID_FREQUENCYTEXT 101
#define ID_FREQUENCYLEGEND 102
#define ID_PHASEBAR 103
#define ID_PHASETEXT 104
#define ID_PHASELEGEND 105
274 Глава 29
//Файл 29-2. rc
#include <owl\slider.rc> //Включение в наш файл изображения ползунка
//Файл 29-2.срр
#include <owl\framewin.h>
#include <owl\slider.h>
#include <owl\static.h>
#include <owl\slider.h>
#include <math.h>
#include "29-2.h"
const float PI=3.1415926; \//Число пи
int kf,ph;// Значения частоты и фазы, снимаемые с приспособлений
/*Класс приложения, производный от Tapplication*/
class MyApp:public TApplication{
public:
void InitMainWindow(); //Замещаем функцию InitMainWindow };
/*Класс дочернего окна-панели Panel, производный от класса Twindow*/ class Panel:public TWindow{ public:
Panel(TWindow* parent,char far* title);
void Paint(TDC&,bool,TRect&);/ /Замещаем функцию Paint для панели
}; Panel::Panel(TWindow* parent,char far* title):TWindow(parent,title){
Attr.Style=WS_CHILD|WS_VISIBLE|WS_BORDER;;
Attr.X=10;Attr.Y=10;Attr.W=220;Attr.H=240;
SetBkgndColor(TColor::LtGray);
}
/*Класс главного окна, производный от TframeWindow*/ class MyWindow:public TFrameWindow{ public;
MyWindow(TWindow*parent,char far*title);
void SetupWindow();// Замещаем функцию SetupWindow
TSlider* frequencySlider; //Указатель на объект -ползунок
TScrollBar* phaseBar; //Указатель на объект-линейку прокрутки
TStatic *frequencySliderValue, *phaseBarValue, *frequencyLegend, *phaseLegend;
void UpdateFrequency(UINT=0); //Функция настройки частоты
void UpdatePhase(UINT=0) ;//Функция настройки фазы
|
|
Panel* panel; //Указатель на объект-панель
DECLARE_RESPONSE_TABLE(MyWindow); //Объявляем таблицу откликов от приспособлений
}; DEFINE_RESPONSE_TABLE1(MyWindow,TFrameWindow) //Таблица откликов от приспособлений
EV_CHILD_NOTIFY^ALL_CODES(ID_FREQUENCYSLIDER,UpdateFrequency),
ЕV_СНILD_NOTIFY_ALL_CODES(ID_PHASEBAR,UpdatePhase), END_RESPONSE_TABLE; /*Функции-члены класса MyWindow*/ MyWindow::MyWindow(TWindow*parent,char far*title):TFrameWindow(parent,title){
panel=new Panel(this,NULL); //Создаем окно-панель без заголовка
frequencySlider=new THSlider(this,ID_FREQUENCYSLIDER, 250, 50,150,32); //Ползунок
phaseBar=new TScrollBar(this,ID_PHASEBAR,250,180,150,0,TRUE) ;//Линейка прокрутки
frequencySliderValue=new TStatic(this,-1,"",250,20,200,25);
frequencyLegend=new TStatic(this,-1,
"1 10,250,85,200,25);
phaseBarValue=new TStatic (this, -1,"", 250,150,200,25);
phaseLegend=new TStatic(this,-1,
"0 32",250,215,200,25);
} void MyWindow::UpdateFrequency(UINT notifyCode){
char str[50];
if(notifyCode==SB_THUMBTRACK||notifyCode==SB_ENDSCROLL) return;
else{
kf=frequencySlider->GetPosition(); wsprintf(str,"Отношение частот = %d",kf); frequencySliderValue->SetText (str);
panel->Invalidate(); }
} void MyWindow::UpdatePhase(UINT){
char str[50];
ph=phaseBar->GetPosition();
wsprintf(str,"Сдвиг фаз = PI/32 * %d",ph);
phaseBarValue->SetText(str);
Окна и их оформление 275
panel->Invalidate(); } /*3амещающая функция SetupWindow*/
void MyWindow::SetupWindow(){
TWindow::SetupWindow(); //Вызываем замещенную функцию SetupWindow
frequencySlider->SetRange(1,10); //Нижний и верхний пределы шкалы
freguencySlider->SetRuler(1,TRUE) ;//Шаг шкалы и фиксация
frequencySlider->SetPosition(1) ;//Начальное положение ручки
phaseBar->SetRange(0,32); //Нижний и верхний пределы шкалы
phaseBar->SetPosition(16); //Начальное положение ручки
UpdateFrequency();//
UpdatePhase();//
}
/*Функция Paint для окна-панели*/ void Panel::Paint(TDC&dc,bool,TRect&){
for(float i=0;i<2*PI*1000;i++) {//В цикле из 1000 шагов float x=sin(i/100)*100; //х-координата текущей точки float y=sin((i/100+PI/32*ph)*kf)*100;//у-координага текущей точки dc.Rectangle(x+110,y+110,x+112,y+112) ;11Квадратики вместо точек }
}
/*3амещающая функция InitWainWindow*/ void MyApp::InitMainWindow(void){
SetMainWindow(new MyWindow(0,"Программа 29-2"));
} /*Главная функция приложения OwlMain*/
int OwlMain(int,char*[]){
return MyApp{).Run();
}
Для создания дочернего окна-панели в программе объявляется класс Panel, производный от TWindow и включающий всего две функции - конструктор и замещающую функцию Paint(). В конструкторе класса устанавливается (с помощью структуры Attr) стиль окна - дочернее, видимое и без рамки, а также задаются его координаты и цвет фона. В функции Paint() в цикле из 1000 шагов вычисляются х- и у-координаты точек фигуры Лиссажу с учетом введенных пользователем значений отношения частот kf и сдвига фаз ph. Каждая точка рисуется в виде квадратика с размером сторон 2 пиксела. Число шагов в цикле и шаг изменения координат выбраны так, чтобы в графики состояли из достаточно большого числа точек. Рассмотренный алгоритм рисования двумерного графика использован по причине его наглядности и, строго говоря, не выдерживает никакой критики. По-настоящему следовало для каждой х-координаты с шагом в 1 пиксел вычислять соответствующую ей у-координату. Это устранило бы наложения и разрывы точек и позволило бы обойтись гораздо меньшим числом шагов в цикле, однако усложнило бы формулы для вычисления координат.
Главное окно приложения, представленное классом MyWindow, производным от TWindow, содержит в себе, помимо дочернего окна Panel, еще ряд управляющих элементов: горизонтальный ползунок, горизонтальную же линейку прокрутки и четыре статических элемента для вывода шкал и поясняющих надписей. Среди данных-членов класса MyWindow объявлены указатели на все эти элементы, а также две прикладные функции UpdateFrequency() и UpdatePhase(), служащие для съема данных с динамических элементов управления и модификации статических. Эти функции являются функциями отклика на сообщения, поступающие от элементов управления.
В таблицу откликов класса MyWindow включены всего два макроса EV_CHILD_NOYIFY_ALL_CODES, каждый из которых предназначен для вызова функции отклика для своего элемента управления при поступлении любого сообщения от этого (дочернего) элемента. Анализ приходящих сообщений, если он нужен, возлагается на функции отклика, при этом нотификационный код сообщения передается макросом в функцию отклика в качестве ее параметра.
|
|
В конструкторе класса MyWindow создаются объекты для окна-панели, двух динамических элементов управления классов THSlider и TScrollBar (см. рис. 28.5), а также для трех статических элементов класса TStatic. Для всех объектов указываются их идентификаторы и расположение в главном окне, а для статических элементов - выводимый текст. Ползунок и линейка прокрутки пока получают настройки по умолчанию, которые, скорее всего, не соответствуют тому, что нужно нам в данном конкретном случае.
Настройка динамических элементов управления осуществляется в замещающей функции SetupWin-dow(). Здесь прежде всего необходимо вызвать исходную, замещенную функция класса TWindow, которая создает все порожденные окна (в нашем случае - дочернее окно и элементы управления), для которых в конструкторе класса MyWindow мы создали OWL-объекты. Если позабыть вызвать замещенную функцию SetupWindow(), главное окно останется пустым. Далее вызовом соответствующих функций классов THSlider и TScrollBar устанавливаются требуемые настройки приспособлений - пределы и шаг шкал, а также начальные положения ручек. Наконец, прямым вызовом функций откликов UpdateFre-quency() и UpdatePhase() выполняется начальное снятие значений с элементов управления и формирование изображения в главном окне.
276 Глава 29
Функции откликов активизируются при выполнении пользователем каких-либо манипуляций с элементами управления. В них вызовом OWL-функции GetPosition() для соответствующего элемента снимается текущее положение его ручки, функцией wspintf() формируется текущее содержимое надписи, и функцией TStatic::SetText() новая надпись выводится в дочернее окно, соответствующее статическому объекту. Для того, чтобы изменения отобразились на экране, вызывается функция Invalidate() для главного окна.
Помимо описанных действий, общих для обеих функций отклика, при входе в функцию UpdateFre-quency() выполняется отбраковка двух кодов нотификации - SB_THUMBTRACK и SB_ENDSCROLL. В результате отбрасываются сообщения от ползунка, возникающие, когда пользователь тащит его ручку, а также сообщения, поступающие при отпускании клавиши мыши. Игнорирование этих сообщений заметно уменьшает мелькание экрана, который перерисовывается только в моменты окончательного выбора нового положения ручки ползунка.
|
|