Шаг 11.
Библиотека OWL.
Оконная графика. Хранение выводимых данных

    На этом шаге мы рассмотрим как можно сохранить изображение окна и восстановить его.

    Добавить функцию Paint() к оконному классу просто. Однако, как узнать, что именно нарисовать в окне, когда функция готова, получает сообщения WM_PAINT и настало время перерисовки? Это зависит от типа отображаемых в окне данных. К примеру, отслеживание текстовых данных сильно отличается от отслеживания картинок.

    Один способ восстановить содержимое окна - способ, пригодный для работы с данными любых типов, - это сохранение в памяти растрового изображения всей оконной области пользователя и затем пересылка его обратно в окно в момент, когда требуется перерисовка. Одной из возникающих при этом проблем является огромный объем памяти, который требуется для хранения растрового изображения всей области экрана. Как правило, удается найти метод "зарисовки", требующий гораздо меньше памяти.


    Замечание. Растровые изображения (Bitmaps) являются графическими изображениями, хранящимися таким способом, что каждый пиксел картинки кодируется одним или несколькими битами. Можно считать, что растровое изображение - это оцифрованный участок изображения. Чтобы отобразить его на экране, используются Windows-функции для работы с блоками битов; эти функции осуществляют обмен изображении между устройствами, обычно между экраном и памятью.

    В программах, приведенных на предыдущих шагах, необходимо отслеживать только положения точек, в которых пользователь нажимал на клавиши мыши. Для перерисовки окна вы просто выводите слово "Щелчок!" в каждой его точке. В следующем примере демонстрируется такой подход к перерисовке окна.

#include <owl\applicat.h>
#include <owl\framewin.h>
#include <owl\dc.h>

// Максимальное количество выводимых точек.
#define MAXPOINTS 100

//Класс приложения
class TApp: public TApplication
{
  public:
	 TApp():TApplication(){}
	 void InitMainWindow();
};

//Класс основного окна
class TWndw:public TFrameWindow
{
  protected:
	 TPoint *points[MAXPOINTS];
	 int count;
  public:
	 TWndw(TWindow *parent, const char far *title);
	 ~TWndw();
  protected:
	 void EvLButtonDown(UINT,TPoint &point);
	 BOOL CanClose();
	 void Paint(TDC&, BOOL, TRect&);
	 DECLARE_RESPONSE_TABLE (TWndw);
};

DEFINE_RESPONSE_TABLE1 (TWndw, TFrameWindow)
	EV_WM_LBUTTONDOWN,
END_RESPONSE_TABLE;

TWndw::TWndw(TWindow *parent, const char far *title):
		  TFrameWindow(parent,title)
{
	// Определить расположение и размеры окна.
	Attr.X=100;
	Attr.Y=100;
	Attr.W=400;
	Attr.H=300;
	// Инициализировать счетчик нажатий кнопки мыши.
	count = 0;
}

// TWndw::~TWndw()
//
// Это деструктор главного окна.
TWndw::~TWndw()
{
  // Удалить все объекты TPoint.
  for (int x=0; x<count; ++x)
	 delete points[x];
}

// TWndw::Paint()
//
// Эта функция, переопределяющая функцию Paint()
// класса TWindow, отвечает на сообщения WM_PAINT,
// посылаемые Windows окну при необходимости
// его перерисовки.
void TWndw::Paint(TDC &paintDC, BOOL, TRect&)
{
  // Вывести повторно "Щелчок!" в каждой
  // отслеженной точке,
  for (int x=0; x<count; ++x)
	 paintDC.TextOut(*points[x], "Щелчок!");
}

void TWndw::EvLButtonDown(UINT,TPoint &point)
{
  // Разрешить не более чем MAXPOINTS нажатий.
  if (count < MAXPOINTS)
  {
	 // Получить контекст устройства для
	 // области пользователя окна.
	 TDC *DC= new TClientDC(HWindow);
	 // Вывести текст в месте, указанном пользователем.
	 DC->TextOut(point,"Щелчок!");
	 // Удалить контекст устройства.
	 delete DC;
	 // Запомнить новую точку и увеличить значение счетчика.
	 points[count++] = new TPoint(point);
  }
}

BOOL TWndw::CanClose()
{
  int result=MessageBox("Вы действительно хотите закрыть окно?","Закрытие",
					 MB_YESNO | MB_ICONQUESTION);
  if (result==IDYES) return TRUE;
  else return FALSE;
}

void TApp::InitMainWindow()
{
  TFrameWindow *wndw=new TWndw(0,"Пример GDI-окна с сохранением изображения");
  SetMainWindow(wndw);
}

int OwlMain(int,char *[])
{
  return TApp().Run();
}
Текст этой программы можно взять здесь.

    В данной программе каждая точка, где была нажата кнопка мыши, запоминается в массиве. Этот массив, с именем points [], объявлен в классе TWndw:

class TWndw:public TFrameWindow
{
  protected:
	 TPoint *points[MAXPOINTS];
	 int count;
  public:
	 TWndw(TWindow *parent, const char far *title);
	 ~TWndw();
  protected:
	 void EvLButtonDown(UINT,TPoint &point);
	 BOOL CanClose();
	 void Paint(TDC&, BOOL, TRect&);
	 DECLARE_RESPONSE_TABLE (TWndw);
};

DEFINE_RESPONSE_TABLE1 (TWndw, TFrameWindow)
	EV_WM_LBUTTONDOWN,
END_RESPONSE_TABLE;

    Массив points [] может хранить MAXPOINTS точек (константа, определенная в начале программы). Эти точки представлены в виде указателей на объект типа TPoint. TPoint - это класс OWL, позволяющий не только легко хранить двумерные координаты, но и выполнять над ними различные операции. В этот класс входят функции вычисления перемещений, а также перегруженные операторы сложения, вычитания и сравнивания точек.

    Помимо массива указателей на TPoint, этот класс содержит переменную count, отслеживающую число точек, хранящихся в points [] на данный момент. Класс TWndw включает также деструктор, необходимость которого скоро станет понятной.

    При получении сообщения WM_LBUTTONDOWN начинает работать функция EvLButtonDown():

void TWndw::EvLButtonDown(UINT,TPoint &point)
{
  // Разрешить не более чем MAXPOINTS нажатий.
  if (count < MAXPOINTS)
  {
	 // Получить контекст устройства для
	 // области пользователя окна.
	 TDC *DC= new TClientDC(HWindow);
	 // Вывести текст в месте, указанном пользователем.
	 DC->TextOut(point,"Щелчок!");
	 // Удалить контекст устройства.
	 delete DC;
	 // Запомнить новую точку и увеличить значение счетчика.
	 points[count++] = new TPoint(point);
  }
}

    Эта функция сначала проверяет count, дабы убедиться, что в массиве достаточно места для сохранения еще одной точки. Если места нет, никаких действий не производится. В противном случае функция получает контекст устройства для окна пользователя, выводит слово "Щелчок!" в точке, указанной пользователем, и удаляет DC. В конце функция создает новый объект TPoint, инициализируя его с координатами полученной точки, и сохраняет этот объект в массиве points [], увеличив при этом счетчик count.

    Новый объект TPoint можно создать несколькими способами. Конструкторы перечислены ниже:

   TPoint();
   TPoint(int _x,   int _y); 
   TPoint(const POINT point);   
   TPoint(const  SIZE  size); 
   TPoint(DWORD  dw);

    Первый конструктор создает неинициализированныи объект TPoint; второй инициализирует его координатами, передаваемыми через параметры и ; третий инициализирует его через структуру POINT; четвертый - через значения полей структуры SIZE; пятый задает поля х и у объекта как, соответственно, младшее и старшее слово переменной DWORD.

    Когда окно получает сообщение WM_PAINT, OWL передает его функции Paint() класса TWndw:

void TWndw::Paint(TDC &paintDC, BOOL, TRect&)
{
  // Вывести повторно "Щелчок!" в каждой
  // отслеженной точке,
  for (int x=0; x<count; ++x)
	 paintDC.TextOut(*points[x], "Щелчок!");
}

    Здесь работает цикл по всем точкам массива points [], и в каждой выводится "Щелчок!". Однако, поскольку points [] содержит указатели на объекты TPoint, то при обращении к TextOut() вы должны разыменовывать указатели.

    Обратите внимание на использование контекста устройства paintDC. Он не создается и не удаляется в рамках функции Paint(), поскольку эти действия поддерживаются OWL с помощью функции-члена EvPaint() класса TWnidow, которая создает объект TPaintDC. Конструктор объекта TPaintDC обеспечивает вызов Windows-функции BeginPaint(), а его деструктор - вызов Windows-функции EndPaint().


    Замечание. В любой Windows-программе перед перерисовкой окна должна быть вызвана API-функция BeginPaint(), передающая приложению ключ контекста устройства (DC) и структуру, содержащую информацию об окне, которое подлежит перерисойке. Кроме того, BeginPaint() задает для окна отсекающий прямоугольник. После перерисовки окна вы должны вызвать функцию EndPaint(), завершающую процесс. В ObjectWindows все это делается автоматически.

    Теперь, поскольку у нас есть объекты типа TPoint, хранящиеся в памяти, необходим способ их удаления при завершении пользователем работы. Лучшее место для такого рода чистки - это деструктор окна:

TWndw::~TWndw()
{
  // Удалить все объекты TPoint.
  for (int x=0; x<count; ++x)
	 delete points[x];
}

    Здесь деструктор организует цикл по элементам массива указателей, удаляя каждый объект TPoint, созданный ранее в программе. Переменная count гарантирует, что в цикле не окажется незаполненных ранее элементов массива. В качестве подстраховки можно добавить строку, проверяющую, равен ли указатель из массива NULL. Чтобы эта проверка сработала, конечно, необходимо проинициализировать все элементы массива значением NULL в самом начале программы. Лучшее место для этого - конструктор главного окна.

    На следующем шаге мы рассмотрим GDI-графику.




Предыдущий шаг Содержание Следующий шаг