Казахстан |
Разработка полноценных 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); }