| Казахстан |
Разработка полноценных Windows-приложений
3. Ввод начальных данных
Рассмотрим два способа ввода начальных данных: ввод точных данных с клавиатуры и визуальное редактирование таблицы узлов посредством мыши. Первый подход реализован с помощью создания и подключения к приложению диалогового окна с необходимыми элементами управления, а второй с помощью обработки двойного нажатия левой кнопки мыши. Также необходимо организовать хранение информации об узлах. Ввод данных с клавиатуры. Создадим ресурс диалогового окна с помощью редактора ресурсов. В диалоговом окне должны присутствовать 2 текстовых поля ввода (координаты x и y), 4 кнопки (Add, Delete, Ok, Cancel) и список для отображения таблицы узлов. После создания ресурса, добавим класс диалогового окна на его базе. Создадим подпункт меню Add/Delete Initial Points в пункте Tools и аналогичную кнопку на панели управления, при нажатии на которые будет появляться диалоговое окно. Прототип класса диалогового окна для ввода узлов:
#pragma once
#include "afxwin.h"
// CAddPointDlg dialog
class CAddPointDlg : public CDialog
{
DECLARE_DYNAMIC(CAddPointDlg)
public:
CAddPointDlg(CWnd* pParent = NULL); // standard constructor
virtual ~CAddPointDlg();
// Dialog Data
enum { IDD = IDD_ADDPOINT_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
DECLARE_MESSAGE_MAP()
public: //переменные связанные с элементами управления добавлены с помощью мастера
CListBox PointList; //Переменная связанная со списком
double ValX; //Переменная связанная с первым текстовым полем
double ValY; //Переменная связанная со вторым текстовым полем
vector<SDPoint> ListedPointsVec; //Вектор узлов интерполирования (добавлена вручную)
public: //Обработчики и переопределенные виртуальные функции добавлены с помощью мастера
afx_msg void OnBnClickedButtonAdd(); //Обработчик кнопки Add
virtual BOOL OnInitDialog();
afx_msg void OnBnClickedButtonDelete(); //Обработчик кнопки Delete
};Обработчик кнопки Add на диалоговом окне:
void CAddPointDlg::OnBnClickedButtonAdd()
{
CString str; //Вспомогательная строка для вывода узла в список
SDPoint temp; //Вспомогательная точка
UpdateData(true); //Обмен данными
temp.x = ValX; //Инициализация вспомогательной точки
temp.y = ValY;
for(size_t i = 0; i < ListedPointsVec.size(); i++) //Перебор всех узлов интерполяции
if(ListedPointsVec[i].x == ValX && ListedPointsVec[i].y == ValY) //Если один из узлов совпал с вводимым то...
return; //выход из функции
ListedPointsVec.push_back(temp); //Добавление нового узла в вектор (проверка сделана, одинаковых нет)
str.Format("(%f;%f)",ValX,ValY); //Формирование строки для добавления в список
PointList.AddString(str); //Добавление строки в конец списка
}Функция для инициализации переменных диалогового окна:
BOOL CAddPointDlg::OnInitDialog()
{
CDialog::OnInitDialog();
CString str; //Вспомогательная строка для формирования списка
for(size_t i = 0; i < ListedPointsVec.size(); i++) //Перебор по всему вектору узлов
{
str.Format("(%f;%f)",ListedPointsVec[i].x,ListedPointsVec[i].y); //Формирование строки
PointList.AddString(str); //Добавление ее в список
}
return TRUE;
}Обработчик кнопки Delete на диалоговом окне:
void CAddPointDlg::OnBnClickedButtonDelete()
{
int n = PointList.GetCurSel(); //Получение номера выделенной строки списка
if(n == LB_ERR) return; //Если ничего не выделено - выход
if(n == ListedPointsVec.size()) //Если выделена последняя строка, то...
ListedPointsVec.pop_back(); //удаление последнего элемента из таблицы
else //иначе...
{
for(size_t i = n; i < ListedPointsVec.size()-1; i++) //перебор узлов начиная с n-ого и до конца
{
//Смещение узлов в векторе
ListedPointsVec[i].x = ListedPointsVec[i+1].x;
ListedPointsVec[i].y = ListedPointsVec[i+1].y;
}
ListedPointsVec.pop_back(); //Удаление последнего элемента
}
PointList.DeleteString(n); //Удаление ненужней строки списка
}Для корректной работы последней функции, в списке должна быть отключена сортировка. Когда диалоговый класс реализован, необходимо подключить его к программе. Для этого используем соответствующий пункт меню и кнопку на панели управления - Add/Delete Initial Points. Также необходимо передать введенные данные в основное хранилище информации (объект класса документа). Обработчик пункта меню Add/Delete Initial Points и связанной с ним кнопки на панели инструментов:
void CGpView::OnToolsAddpoint()
{
CAddPointDlg dlg; //Создаем экземпляр класса диалогового
//Стандартная процедура получения указателя на документ и его проверки на //корректность
CGpDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
pol_ready = false; //Сигнализируем о том что построенный полином устарел (сейчас будет меняться таблица)
Invalidate(); //Перерисовываем
dlg.ListedPointsVec = pDoc->InitPoints; //переписываем копию узлов в диалоговую переменную
switch(dlg.DoModal()) //Ждем реультата работы диалога
{
case -1: AfxMessageBox("AddPoiintDialog creation error"); return; //Если диалог не запустился
case IDOK: pDoc->InitPoints = dlg.ListedPointsVec; Invalidate(); break; //Если нажали Ok то переписываем
новую таблицу в документ и перерисовываем
case IDCANCEL: break; //Если Cancel ничего не делаем
}
}Этот обработчик осуществляет связь между механизмом изменения (диалоговое окно) узлов и механизмом их хранения (объект документа). Прежде чем переходить к реализации визуального редактирования, рассмотрим механизм хранения узлов и интерполяционного полинома. Объект документа полностью соответствует требованиям хранилища необходимых программе данных. Прототип класса документа:
// GpDoc.h : interface of the CGpDoc class
//
#pragma once
#include "TPolinom.h"
class CGpDoc : public CDocument
{
protected: // create from serialization only
CGpDoc();
DECLARE_DYNCREATE(CGpDoc)
// Attributes
public:
// Operations
public:
// Overrides
public:
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar);
// Implementation
public:
virtual ~CGpDoc();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// Generated message map functions
protected:
DECLARE_MESSAGE_MAP()
public: //Необходимые переменные
vector<SDPoint> InitPoints; //Таблица узлов реализована с помощью вектора
вещественных точек плоскости
TPolinom<double> InterPol; //Интерполяционный полином
bool pol_ready; //Флаг сигнализирующий о том что полином построен
public:
bool BuildPolinom(void); //Функция для построения полинома
afx_msg void OnToolsBuilddrawpolinom();
};Важным для данного пункта является объект InitPoints класса vector SDPoint . В него записываются и хранятся на протяжении работы программы узлы интерполирования.
Визуальное редактирование таблицы узлов. Создадим подпункт Mark initial points в меню и соответствующую кнопку, которые будут переводить программу в режим ожидания редактирования (двойного клика по левой кнопке мыши) и выводить из него. Добавим обработчик сообщения WM_LBTNDBLCLICK Обработчик нажатия пункта меню Mark initial points и связанной с ним кнопки на панели инструментов:
void CGpView::OnToolsMarkinitialpoints()
{
parallel_shift = false; //Если в режиме ожидания переноса то выход из него
mark_points = !mark_points; //Вход или выход в(из) режима визуального редактирования узлов
}Обработчик изменения внешнего вида пункта меню Mark initial points и связанной с ним кнопки на панели инструментов:
void CGpView::OnUpdateToolsMarkinitialpoints(CCmdUI *pCmdUI)//Обрабатываем изменение внешнего вида кнопки (пункта меню)
{
pCmdUI->SetCheck(mark_points); //Смена статуса (активна или не активна) в зависимости от флага
}Обработчик двойного клика по левой кнопке мыши:
void CGpView::OnLButtonDblClk(UINT nFlags, CPoint point)
{
if(mark_points) //Если в режиме визуального редактирования, то...
{
//Получение указателя на документ
CGpDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
SDPoint help = SysToLog(point); //Вспомогательная переменная для проверки (вводится ли уже существующий узел)
for(size_t i = 0; i < pDoc->InitPoints.size(); i++) //Перебор по всем узлам и проверка...
if(help.x == pDoc->InitPoints[i].x && help.y == pDoc->InitPoints[i].y) //Совпадает с вводимым хотя бы оди узел таблицы
return; //Если да, то выход из функции без добавления вводимого узла в таблицу
pDoc->InitPoints.push_back(help); //Добавление вводимого узла в таблицу (проверка сделана, повторов нет)
Invalidate(); //Обновление клиентской области окна
}
CView::OnLButtonDblClk(nFlags, point);
}