Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

VisualC_Studio / Lab / lb_07

.htm
Скачиваний:
7
Добавлен:
21.03.2015
Размер:
133.47 Кб
Скачать

Наложение на кнопку «иконки» Лабораторная работа № 7

 

Тема: Элементы управления: наборный счётчик. Изображение диаграммы ориентированного графа по заданным спискам смежности

 

            В этой лабораторной работе разработаем приложение, которое по данным спискам смежности ориентированного графа рисует его диаграмму.

 

Краткое напоминание

            Рассмотрим множество V={v1,v2,...,vn}, n³2, и множество E={e1,e2,...,em}, которое представляет собой некоторое бинарное отношение на множестве V (EÍV´V). Пара множеств V и E называется ориентированным графом (орграфом) F(V,E) с множеством вершин V и множеством ребер E.

            Ребро ориентированного графа называется дугой.  Для дуги ek=(vi, vj) вершина vi называется начальной, а vj - конечной. Иными словами, ребро ek выходит из вершины vi и заходит в вершину vj. Говорят, что дуга ek инцидентна вершинам vi и vj,  а вершины vi и vj инцидентны дуге ek. Вершины vi и vj называют смежными.

            Граф F(V,E) может быть изображен геометрически. Для этого некоторые n точек трехмерного пространства помечаются элементами множества вершин V и вершины vi и vj соединяются «стрелкой», если (vi, vj)ÎЕ. Геометрическое изображение графа будем называть диаграммой. Ребро, у которого концевые точки совпадают, называется петлей. Изображая петлю на диаграмме, стрелку можно не рисовать.

            Пример. Пусть V={3, 8, 24}.  Зададим на этом множестве отношение

E={(u,w)/ u - делитель w;  u,wÎV} = {(3,3), (3,24), (8,8), (8,24), (24,24)}.

            Диаграмма орграфа приведена на рис. 1, a.

            Рис. 1.

 

            Из приведенного определения следует, что в орграфе отсутствуют кратные, или параллельные, ребра, соединяющие одни и те же пары вершин. Однако иногда удобно снять указанные ограничения.

Граф, содержащий кратные ребра называется мультиграфом. Граф, содержащий петли и (или) кратные ребра, называется псевдографом.

            Списки смежности - это один из способов его задания псевдографа. Список Li вершины vi содержит те вершины, в которые из vi проведена дуга.

            Примеры.

Для орграфов, изображенных на рис. 1, списки смежности имеют вид:        

a) L3 : 3, 8, 24; L8 : 8, 24; L24 : 24.      b) L1 : 3;   L2: 1, 2, 3;  список L3 пуст.

 

В дальнейшем будем полагать, что речь идёт именно о графе, а не о псевдографе или мультиграфе. Кроме того, для простоты будем считать, что множество V имеет вид V = {1,2,3,...,n}, где n £ 9.

 

Создание нового проекта с помощью генератора кода AppWizard

Создайте новый проект с именем MyPr07:

File > New > Project.

В качестве типа проекта Project types выберите MFC, в качестве Templates – MFC Application, укажите в разделе Location (Местоположение) свой директорий, заполните имя проекта Name: MyPr07 и щелкните ОК.

Затем

перелистните первую страницу, выбрав кнопку Next:

на странице Application type выберите Single document (однооконное приложение);

перелистните следующие четыре страницы;

на странице Advanced Features проверьте, чтобы флажок ActiveX Controls был  установлен;

на странице Generated Classes для класса с именем CMyPr07View в качестве базового класса (Base class) выберите из списка CFormView. Нажмите кнопку Finish.

 

В свойствах проекта смените кодировку (см. лабораторную работу № 1).

 

Перейдите на вкладку ResourceView окна проектов (если она отсутствует на экране, её можно открыть через команду View Главного меню).

Раскройте списки MyPr07.rs и Dialog. Дважды щелкните левой кнопкой мыши идентификатор IDD_MYPR07_FORM, и, когда перед Вами откроется пустое диалоговое окно MyPr02.rs (IDD...Form-Dialog), снова, теперь уже правой кнопкой мыши, щелкните идентификатор IDD_MYPR07_FORM. В открывшемся контекстном меню выберите команду Properties (Свойства).  В окне свойств диалога замените язык (Language) на русский (если он не указан там по умолчанию).

 

 

«Перевод» Главного меню на русский язык

«Переведите» Главное меню на русский язык и отредактируйте панель инструментов  (этот процесс подробно описан в лабораторной работе № 6).

Оставьте в меню следующие пункты:

 

           

            У команды  «Сохранить списки смежности» поменяйте идентификатор на ID_FILE_SAVE_LIST, у команды «Помощь» - на ID_HELP.

 

Редактирование диалогового окна

            Откройте окно редактора ресурсов: MyPr07.rc - IDD_MYPR07_FORM... (вкладка Resource View > Dialog > IDD_MYPR07_FORM)

 

1. Чтобы Вам было удобнее работать с редактором ресурсов, можете, как описано в лабораторной работе № 2, растянуть окно диалога, включить режим сетки (кнопка Toggle Grid внизу на диалоговой панели инструментов), поменять шрифт и размер и т.д.

 

            2. Щелкнув левой кнопкой мыши надпись: TODO: Place form controls on this dialog, выделите ее, после чего удалите (клавиша Delete клавиатуры).

 

3. Установите в проект ActiveX-элемент управления FlexGrid (процесс установки подробно описан в лабораторной работе № 6).

 

            4. Поскольку всю левую половину формы в дальнейшем займёт рисунок, разместите в правой половине диалогового окна элементы управления.

 

4.1. Расположите в правой части формы сверху статический текст (Static Text), в свойствах которого поменяйте наименование Caption на «Количество вершин (от 2 до 9)».

4.2. Под текстом поместите поле редактирования (Edit Box), в свойствах которого поменяйте идентификатор на IDC_EDIT_N, а свойство Read-only (Только для чтения) положите равным True.

4.3. Справа от поля редактирования расположите наборный счётчик

.

            Наборный счётчик - это вариант полосы прокрутки, который часто используется совместно с полем ввода. Поле ввода, расположенное слева от наборного счётчика, при переключении между элементами клавишей Tab должно иметь номер, на единицу меньший, чем номер наборного счётчика. Такое поле ввода называют «приятелем» («buddy») наборного счётчика.

            Суть работы управляющего элемента Spin состоит в том, что пользователь, поместив курсор на счётчик и удерживая левую кнопку мыши, может увеличивать или уменьшать число в поле ввода.

            В свойствах наборного счётчика установите свойства Auto Buddy и Set Buddy Integer равными True.

            4.4. Ниже поместите ещё один статический текст, в свойствах которого поменяйте наименование Caption на «Списки смежности».

            4.5. Под этим статическим текстом расположите добавленный в пункте 3 элемент «Таблица» (Microsoft FlexGrid Control, version 6.0). В свойствах таблицы обнулите количество фиксированных строк (параметр Fixed Rows).

4.6. Наконец, под таблицей добавьте кнопку (Button). В её свойствах поменяйте идентификатор на IDC_BUTTON_PAINT, а наименование Caption - на «Нарисовать».

 

            Выберите из главного меню команду Format, затем команду Tab Order. Результат Вашей работы должен выглядеть примерно так:

 

 

Убедитесь, что номер «приятеля» на единицу меньше номера наборного счётчика. Если у Вас это не так, то исправьте свою ошибку.

 

5. Сохраните файл ресурсов MyPr07.rc на диске: вкладка ResourceView > Dialog > правой кнопкой мыши щелкнуть IDD_MYPR07_FORM и выбрать Save MyPr07.rc.

 

Добавление переменных класса CMyPr07View

1. Откройте файл файла MyPr07View.h. В самое начало объявления класса CMyPr07View, сразу после строк

class CMyPr07View : public CFormView

{

вставьте следующие строки:

 

enum {nMin_N=2};     // минимальное количество вершин

enum {nMax_N=9};    // максимальное количество вершин

enum {dR=10};           // радиус окружности, изображающей вершину (в пикселях)

 

int MyLists[nMax_N][nMax_N];         // матрица для хранения списков смежности

CPoint MyPoints[nMax_N];                // массив точек для хранения координат вершин

 

2. В раздел Implementation того же заголовочного файла MyPr07View.h вставьте объявление переменных:

 

private:

            int nCurrent_N;                       // фактическое количество вершин

            int nCurrent_N_Paint;  // количество вершин на рисунке

CRect MyR;                            // прямоугольник с размерами рисунка

 

3. Начальные значения этих переменных определите в конструкторе CMyPr07View (файл MyPr07View.cpp), добавив в конец строки:

 

nCurrent_N=5;                        // определяем текущее количество вершин

nCurrent_N_Paint=0;  // количество вершин на рисунке равно 0, т.к. рисунка пока нет

// Положение верхнего левого угла и размеры рисунка задаём явно

MyR.top=10;   MyR.left=10; MyR.right=260; MyR.bottom=270;

 

4. Перейдите на вкладку Class View окна проектов и раскройте список MyPr07 classes. Щелкните правой кнопкой мыши CMyPr07View и в открывшемся контекстном меню выберите Add > Add Variable.

В результате перед Вами появилось одноименное окно.

Поставьте «галочку» слева от Control variable, объявив тем самым, что переменная связана с управляющим элементом.

В списке Control ID выберите идентификатор управления IDC_BUTTON_PAINT.

 В качестве имени переменной Variable name введите m_bPaint. Ничего больше не меняя, нажмите Finish.

Аналогичным образом добавьте переменную с именем m_strgrid, связанную с идентификатором таблицы IDC_MSFLEXGRID1.

 

Идентификатор

Тип переменной

Имя переменной

IDC_BUTTON_PAINT

Control

m_bPaint

IDC_MSFLEXGRID1

Control

m_strgrid

 

Начальная инициализация таблицы и наборного счётчика

1. В класс CMyPr07View добавим закрытую (private) функцию UpdateNameFlexGrid, которая будет менять размерность таблицы и нумеровать её строки.

Перейдите на вкладку ClassView окна проектов, раскройте список MyPr07 classes и щелкните правой кнопкой мыши строку CMyPr07View. В открывшемся контекстном меню выберите пункт Add > Add Function. Заполните одноимённое окно следующим образом:      

Return type: void;                    Function name: UpdateNameFlexGrid;

Parameter type: void;                         

 

Нажмите ОК. В заготовку (файл MyPr07View.cpp) внесите текст функции:

 

int i; CString str;

// Устанавливаем количество строк и столбцов таблицы

m_strgrid.put_Rows(nCurrent_N); m_strgrid.put_Cols(nCurrent_N+1);

// Устанавливаем ширину столбцов таблицы

for (i=0; i<=nCurrent_N; i++) {m_strgrid. put_ColWidth(i,340);}

// Очищаем таблицу

m_strgrid.Clear();

// Создаём заголовки строк таблицы

m_strgrid. put_Col(0); // Нулевой столбец

for (i=0; i<nCurrent_N; i++)

{

            m_strgrid. put_Row(i); str.Format("%d",i+1); m_strgrid. put_Text(str);

}

 

2. В том же файле MyPr07View.cpp в конец функции OnInitialUpdate добавьте строки:

 

// Создаем переменную pSpin, как указатель на элемент класса CSpinButtonCtrl

// и связываем эту переменную с  элементом управления,

// имеющим идентификатор IDC_SPIN1

CSpinButtonCtrl* pSpin=(CSpinButtonCtrl*) GetDlgItem(IDC_SPIN1);

// Устанавливаем диапазон значений наборного счётчика

pSpin->SetRange(nMin_N, nMax_N);

// Устанавливаем начальное положение

pSpin->SetPos(nCurrent_N);

 

UpdateNameFlexGrid();          // обновляем таблицу

 

Соберите приложение и запустите его на выполнение. Убедитесь, что первоначально в поле ввода указано значение «5», а таблица содержит пять строк и шесть столбцов, причём в ячейках нулевого столбца указаны номера строк. Пощёлкайте стрелки наборного счётчика; - значение в поле ввода изменяется от 2 до 9, как мы и хотели.

Заставим таблицу изменяться в зависимости от значения наборного счётчика.

 

Изменение размеров таблицы

Создадим процедуру - обработчик сообщения UDN_DELTAPOS, поступающего от наборного счётчика при изменении его значения (окно свойств наборного счётчика,  вкладка Message).

В текст функции OnDeltaposSpin1 сразу после строки

// TODO: Add your control notification handler code here

добавьте следующие строки:

 

int NewPos;    

// Новое положение определяем как сумму старого положения и приращения

NewPos = (pNMUpDown->iPos) + (pNMUpDown->iDelta);

if (NewPos>nMax_N)

{

            nCurrent_N=nMax_N;

}

else

{

            if (NewPos<nMin_N) {nCurrent_N=nMin_N;}

            else                                         {nCurrent_N=NewPos;}

}

 

UpdateNameFlexGrid();          // обновляем таблицу

 

Соберите приложение и запустите его на выполнение. Убедитесь, что размеры таблицы и нумерация строк меняются в зависимости от значения наборного счётчика.

Попробуйте внести какую-нибудь информацию в чистую ячейку таблицы. Увы, попытка оказалась неудачной!

 

Ввод списков смежности с клавиатуры

Чтобы разрешить пользователю редактировать содержимое таблицы, придётся создать специальную функцию. Пусть заодно эта функция контролирует входную информацию, разрешая вводить только цифры от 1 до nCurrent_N.

 

Создайте обработчик сообщения KeyPress (Нажата Клавиша) для идентификатора IDC_MSFLEXGRID1 (окно свойств таблицы,  вкладка Message).

В текст функции OnKeyPressMsflexgrid1 внесите строки:

 

int nCh; CString strCh, str;

nCh=*KeyAscii;                      // код введённого символа по Ascii

strCh=char(nCh);                    // сам введённый символ по Ascii

 

// Код "1" по Ascii равен 49, т.е. 1+48;

if ((nCh<1+48) || (nCh>nCurrent_N+48))

{

            str.Format("Допустимы значения от 1 до %d\n", nCurrent_N);

            AfxMessageBox(str);

}

else

{

            m_strgrid. put_Text(strCh);

}

 

Соберите приложение и запустите его на выполнение. Попробуйте на этот раз заполнить некоторые ячейки таблицы: теперь совсем другое дело!

 

Чтение списков смежности из входного файла

Предусмотрим и альтернативную возможность - заполнение таблицы информацией из входного файла.

Откройте и посмотрите файл VisualC_Part01\FilesForStud\inFile.txt. Здесь находятся списки смежности графа, диаграмма которого представлена на рис. 1, b. В первой строке расположена информация для пользователя. Признаком конца списка смежности служит точка с запятой, которая отделена пробелом от номера последней в списке вершины. Файл такой структуры мы и будем читать.

Скопируйте файл inFile.txt в свою папку MyPr07\MyPr07, т.е. туда, где находятся файлы с расширениями «.cpp» и «.h».

 

1. В начало файла MyPr07View.cpp, после всех операторов #include вставьте строки:

 

#include "fstream"

using namespace std;

 

Тем самым Вы подключили библиотеку файловых потоков для работы с входными и выходными файлами.

 

2. Создадим процедуру - обработчик команды «Открыть». Это будет обработчик сообщения COMMAND для идентификатора ID_FILE_OPEN.

            2.1. Откроем редактор ресурса «Главное меню»: ResourceView > Menu > дважды щелкнуть левой кнопкой мыши IDR_MAINFRAME.

            2.2. Добавим обработчик: щёлкните правой кнопкой мыши ячейку «Открыть» и в открывшемся контекстном меню выберите команду Add Event Handler (Добавить Событие).

 В окне Event Handler Wizard выберите класс CMyPr07View, убедитесь, что в списке Message type указано сообщение COMMAND и нажмите кнопку Add and Edit (Добавить и Редактировать).

В текст функции OnFileOpen внесите строки:

 

int i,j,k;

// Объявляем вспомогательный массив all_var;

// размерность - по максимальному количеству вершин

CString all_var[nMax_N];      

char lin[81];                             // новая строка

char node_new[3];                  // новая вершина

 

// Открываем стандартное окно диалога

CFileDialog dlg(TRUE,NULL,"*.txt",NULL,

            "Текстовые файлы (*.txt)|*.txt|Все файлы (*.*)|*.*|");       

if (dlg.DoModal()==IDOK)

{

            ifstream inFile;  // объявляем файловую переменную

            inFile.open(dlg.GetFileName());           // открываем файл

            if (!inFile)

            {

            AfxMessageBox("File open error");

                        exit(1);

            }

            inFile.getline(lin,sizeof(lin)); // пропускаем первую строку (строку с пояснениями)

            inFile >> nCurrent_N;  // читаем из файла количество вершин

            if ((nCurrent_N<2) || (nCurrent_N>9))

            {

                        AfxMessageBox("Неверное количество вершин!");

            }

            else

            {

                        UpdateNameFlexGrid(); // обновляем размерность и вид таблицы

                        for (i=0; i<nCurrent_N; i++)

                        {

                                   for (j=0; j<nMax_N; j++)        // очищаем массив all_var

                                   {

                                               all_var[j]="0";

                                   }

                                   k=0;

                                   inFile >> node_new;                // читаем из файла 1-й элемент в строке

                                  

                                   // Пока node_new не равно ";" - признаку окончания строки,

                                   //          формируем массив all_var

                                   while ( strcmp(node_new,";") != 0 )                

                                   {

                                               all_var[k]=node_new;

                                               k++;

                                               inFile >> node_new;    // читаем из файла следующий элемент

                                   }

                                   // Записываем информацию в таблицу

                                   for (j=1; j<=nCurrent_N; j++)

                                   {          // пропускаем 0-й элемент - номер списка

                                               m_strgrid.put_Row(i); m_strgrid.put_Col(j);

                                               if ( (atoi(all_var[j])>=1) && (atoi(all_var[j])<=nCurrent_N) )

                                               {          // неверные символы просто не заносим в таблицу

                                                           m_strgrid.put_Text(all_var[j]);

                                               }

                                   }

                        }

 

            }

            inFile.close();               // закрываем файл

}

 

Соберите приложение и запустите его на выполнение. Откройте файл inFile.txt. Убедитесь, что таблица заполняется верно.

 

Рисуем диаграмму орграфа

            Как и в лабораторной работе № 5, мы будем создавать изображение в памяти ЭВМ, после чего выводить готовую картинку на экран.

 

            1. Откройте лабораторную работу № 5, найдите там раздел «Устраняем мелькание с помощью растрового изображения» и выполните действия, описанные в пунктах 1-4.

 

            2. В текст функции OnPaint надо внести только одно изменение: ранее мы объявили переменную MyR в заголовочном файле и определили её значение в конструкторе; поэтому закомментируйте (или удалите) строки:

 

CRect MyR;                            // Объявляем переменную MyR типа CRect (Прямоугольник)

dc.GetClipBox(&MyR);          // Загружаем в переменную MyR размеры окна приложения

 

Соберите приложение и запустите его на выполнение. Область, занятая рисунком, окрасилась белым цветом. Если Вас не устраивает положение рисунка, измените в конструкторе координаты верхнего (MyR.top) левого (MyR.left) угла. Размеры рисунка можно изменить, меняя значения MyR.right и MyR.bottom.

 

3. Создадим функцию-реакцию по нажатие кнопки «Нарисовать», как обработчик сообщения BN_CLICKED для идентификатора ID_BUTTON_PAINT (окно свойств кнопки, вкладка Message). В текст функции OnButtonPaint внесите строки:

 

int i,j,r; double phi, dPhi, pi;

// Считываем списки смежности из таблицы в матрицу MyLists

for (i=0; i<nCurrent_N; i++)

{

            for (j=0; j<nCurrent_N; j++)  

            {          // пропускаем 0-й элемент - номер списка

                        m_strgrid.put_Row(i); m_strgrid.put_Col(j+1);

                        MyLists[i][j]=atoi(m_strgrid.get_Text());

            }

}

// Формируем массив координат вершин на рисунке

r=MyR.Width() / 3;

pi=4*atan(1.0);

phi=0; dPhi=(360/nCurrent_N)*(pi/180); // в радианах

for (i=0; i<nCurrent_N; i++)

{

            MyPoints[i].x=int(r*cos(phi)); MyPoints[i].y=int(r*sin(phi));

            phi+=dPhi;

}

nCurrent_N_Paint=nCurrent_N;          // определяем количество вершин на рисунке

Invalidate();      // посылаем команду - перерисовать окно

 

Пояснения

3.1. Так как наш орграф имеет небольшое количество вершин, не обязательно для хранения его списков смежности формировать сложную ссылочную структуру. Можно хранить эти списки в двумерном массиве MyLists, который мы объявили ранее в заголовочном файле.

 

3.2. Окружности, изображающие на диаграмме вершины графа, будем располагать

по кругу, радиус r которого составляет одну треть от ширины рисунка. Приращение dj угла j определим, поделив 360 градусов на текущее количество вершин.

4. Чтобы подключить библиотеку математических функций, в начало файла MyPr07View.cpp добавьте строку:

 

#include "Math.h"

 

5. Создайте процедуру прорисовки, как обработчик сообщения OnDraw (окно свойств класса CMyPr07View, кнопка Overrides)

В текст функции OnDraw внесите строки:

 

int i,j, k; CString str;

CPoint My0;    // Новое начало координат

My0.x=MyR.Width()/2; My0.y=MyR.Height()/2;       // Точка My0 - середина рисунка

 

Соседние файлы в папке Lab