
- •Государственный комитет рф по высшему образованию
- •2002 Г. Содержание дипломного проекта:
- •1. Специальная часть. Разработка программного обеспечения «Хроника моего рода».
- •2. Технологическая часть. Технология разработки программных систем.
- •3. Организационно-экономическая часть. Расчет затрат на разработку программного продукта.
- •4. Производственно-экологическая часть. Анализ воздействия вредных факторов на оператора эвм.
- •Введение.
- •1.1 Техническое задание
- •1.2 Эскизный проект
- •1.2.1 Постановка задачи
- •1.2.1.1 Назначение системы
- •1.2.1.2 Общая структура системы
- •1.2.1.3 Требования к программному обеспечению комплекса
- •1.2.1.4 Требования к составным частям программного комплекса
- •1.2.2 Обзор аналогичных программных продуктов, имеющихся на рынке
- •1.2.3 Выбор языка программирования
- •1.3. Технический проект
- •1.3.3 Проектирование создания дерева и способов работы с ним
- •1.3.3.1 Методика построения генеалогического дерева
- •Pис.1.3.3.3 Схема алгоритма рекурсивной процедуры Potomok(Id, pos).
- •Рабочий проект
- •1.4.1 Общий вид программного продукта
- •Инсталляция программного комплекса
- •1.4.3 Защита программного продукта
- •1.4.4 Разработка программы
- •Тестирование надежности программного продукта
- •1.4.6 Зависимость размера файлов родословных и машинного времени построения генеалогического дерева от количества персон.
- •1.5 Выводы
1.4.4 Разработка программы
Разработанные алгоритмы, приведенные в техническом проекте, реализовывались модулями программы. Модули создавались в среде BorlandDelphi5 [1]. Взаимодействие модулей программы рассмотрено на рис. 1.4.1.2. Ниже некоторые модули описаны подробнее.
Алгоритм ввода, сохранения и изменения информации, а также выдача информации по конкретному узлу генеалогического дерева реализован модулем info1.
1. Модуль info1 – это модуль информации по конкретным узлам.
Переменные, объявленные в модуле:
FUpdating– идентификатор выбора стиля текста;
miMinInfoCounter– идентификатор минимума вводимой информации;
MotherChanged, FatherChanged, HusChanged, ChiChanged – идентификаторы нажатых кнопок;
UrlCommaCounter– идентификатор ввода информации.
Процедуры и функции модуля (кроме процедур обработки нажатий кнопок):
procedure ClearAll – очищает все поля формы fmMainInfo;
procedure SaveInfo(Id: integer)– сохраняет информацию о персоне в массиве персон;
function LoadFromArFoto(id: integer; name: string):string– загружает фото для просмотра, в соответствии с информацией из массиваArFoto;
function LoadFromArVideo(id: integer; name: string):string – аналогично для видео;
function LoadFromArAudio(id: integer; name: string):string – аналогично для аудио;
procedure AddName(var list: TListBox;var KolMedia: integer; Name,path: string;var mas: array of TMedia) – сохраняет информацию в медиа массивы;
procedure FindAllMedia - находит все медиа файлы, относящиеся к персоне;
procedure httpConnect(Sender: TObject)– производит запуск Интернет-броузера;
procedure changepic(path:string) – «подгоняет» фото под размеры окна;
procedure ConvertData(data: string; live: integer) – инвертирует данные о дате в нужный формат.
Модуль Pr1– модуль визуализации дерева. Также в этом модуле организована работа с мышью и масштабирование дерева.
Переменные, объявленные в модуле:
Width1, Height1– высота и ширина рисунка генеалогического дерева;
Kol_u– количество поколений в родословной;
sum: array [0..100] of integer– одномерный массив, в котором индекс соответствует номеру поколения, а элемент – это количество людей в этом поколении.
u_max, u_min – максимальный и минимальный номер поколения. Той персоне, с которой начали строить родословную присваивается нулевое поколение, следующему поколению на единицу больше. Соответственно меньшие поколения на единицу меньше, то есть они будут с отрицательным знаком, напр., -1, -2.
kx, ky –коэффициенты сжатия по оси Х иY.
SomeWife - идентификатор принимает значенияtrue(у персоны один супруг) иfalse(у персоны есть не один заключенный брак или дети вне брака).
Процедуры и функции модуля (кроме процедур обработки нажатий кнопок):
В алгоритм рисования дерева входят две основные процедуры CreateMas и DrawTree, в них еще несколько подпроцедур.
Процедура CreateMas: сначала устанавливаются идентификаторыkey иSomeWife. Второй идентификатор принимает значенияtrue(у персоны один супруг) иfalse(у персоны есть не один заключенный брак или дети вне брака).В начале работы алгоритма этому идентификатору присваивается значениеtrue. Далее вызывается функцияmax_z, которая подсчитывает общее количество поколений в родословной. Результат этой функции присваивается переменнойKol_u.Следующая процедураCreateArpok создает двумерный массив из одномерного массиваPersonAr. В получившемся массиве элементами одной строки являются люди одного поколения. Сортировка массива осуществляется процедуройSortArpok. Муж и жена в одной строке расположены рядом. ПроцедураFormXYформирует координаты узлов генеалогического дерева. Размеры рисунка дерева рассчитываются процедуройSizePanel. Для того, чтобы дерево находилось не в одной части экрана, если его размеры меньше размеров экрана монитора, его координаты корректируются таким образом, чтобы оно располагалось посередине. После проведенных действий массивpArpokсформирован должным образом.
Функция max_z: рассчитывает общее количество поколений в родословной. Здесь использован простой алгоритм нахождения максимума и минимума, когда на первом этапе за минимум берется заведомо большое числоmin1:= 1000, за максимум соответственно – маленькоеmax1:= -1000. Поиск минимума: первый элементPersonAr[k].pok сравнивается сmin1. Если он меньше, то он становитсяmin1, далее сравниваются остальные элементы. В итоге значениеmin1и есть меньшее поколение. Максимум находится аналогично. Общее количество поколений в родословнойmax_zесть разностьmax1и min1. Переменнымu_min присваивается значение меньшего поколения, переменнойu_max– значение старшего поколения.
Процедура CreateArpok:создает двумерный массивpArpokиз одномерного массиваPersonAr таким образом, чтобы в каждой строке массиваpArpokнаходились люди одного поколения. В начале формируется одномерный массивsum: array [0..100] of integer.В нем индексу соответствует номер поколения, а значению элемента массива с этим индексом соответствует количество людей в этом поколении. Далее в цикле поj(номер поколения) сначала для нулевого поколения заполняем нулевую строку массиваpArpok.Каждый элемент массиваPersonAr[k].pokсравнивается с номером поколенияj(сейчас равен 0). Те элементы, которые соответствуют этому поколению, заносятся наj-ую строку массиваpArpok.Одновременно с этим подсчитывается количество элементов на этой строке, если оно превосходит соответствующий этому поколению элемент массиваsum,то переходим к заполнению следующей строке-поколению.
procedure TfmTree.CreateArpok();
var s, i, j,k: integer;
begin
for
j:=u_min to u_max do
begin
s:=0;
for i:=1 to Kol-1 do
if PersonAr[i].pok=j then формирование одномерного
begin массива sum
s:=s+1;
end;
sum[j-u_min]:=s;
end;
for j:=0 to Kol_U do
begin
s:=0;
for k:=1 to Kol do
begin заполнение
if PersonAr[k].pok=j+u_min then массива pArpok
begin
if (PersonAr[k].Fam<>'') or (PersonAr[k].Id<>0) then
begin
pArpok[j,s]:= PersonAr[k];
s:=s+1;
end;
if s=sum[j] then break;
end;
end;
end;
Процедура SortArpok:сортирует массив pArpokтак, чтобы супруги в одном поколении размещались рядом. В этой процедуре так же используется массивsum, упоминавшийся в предыдущей процедуре. Проходимся по строкам массиваpArpok.Сначала первый элемент запоминаем, какP, следующий, какP1. Если супругP-го элемента естьP1, то ставим их вместе. (Перемещение элемента осуществляет процедураChange(lvl,p1,p2: integer)).Если нет, тоP1 присваиваем значение следующего элемента и сравниваем снова. Достигнут конец строки, тогдаPприсваивается значение второго элемента, если замены не произошло и третьего элемента, если произошла замена, и продолжается сравнение.
Procedure SortArpok();
var j,i,m,a: integer;
P,P1: TPerson;
begin
for j:=0 to Kol_u do
begin
m:=0;
while m<sum[j] do
begin
P:=pArpok[j,m];
a:=0;
for i:=m+1 to sum[j]-1 do
begin
P1:=pArpok[j,i];
if P1.Husbend=p.Id then
begin
Change(j,m+1,i);
a:=a+1;
break;
end;
end;
if a>0 then m:=m+2
else m:=m+1;
end;
end;
end;
Procedure Change(lvl,p1,p2: integer);
var buf: TPerson;
begin
buf:=pArpok[lvl,p1];
pArpok[lvl,p1]:=pArpok[lvl,p2];
pArpok[lvl,p2]:=buf;
end;
На вход процедуры Change(lvl,p1,p2: integer) подается номер уровня lvl,расположение элемента на этой строкеp1 и расположение второго элементаp2, который будет ставиться рядом.
Процедура FormXY: формирует координаты узлов генеалогического дерева. Присваиваются каждому человеку координатыxиy. (Именно на месте этих координат будет отображен данный человек.) Заметим, что ближайшие люди находятся на расстоянии 200 пикселей по осиXи 90 пикселей по осиY. В этой же процедуре происходит выравнивание общего вида дерева.
Процедура DrawTree: осуществляет процесс визуализации генеалогического дерева. В начале заливается цветом фона прямоугольник размером с рисунок дерева, который рассчитывался ранее. На этом фоне рисуются узлы дерева кружочками (персона женского пола) или квадратиками (персона мужского пола). Для этого проходим по всем элементам массива данных и сравниваем поле записи элементаpArpok[j,i].pol со значением «жен» и «муж». Чтобы нарисовать линию, связывающую двух супругов, выполняются два цикла. Проходимся по строкам массиваpArpok, запоминаем идентификатор супруга рассматриваемой персоны. Затем ищем супруга в данной строке и запоминаем индекс этого элемента. Теперь, узнав индексы требуемых элементов, рисуем соединяющую их линию при помощи процедурыLinkOneLayer(xn,y,xk: integer).Так проверяем каждый элемент. Если найденный супруг персоны уже проверялся, то проверяем следующие элементы.
for j:=0 to Kol_U do
begin
for i:=0 to sum[j]-1 do
begin
d:=pArpok[j,i].Husbend;
if PersonAr[d].pok <> pArpok[j,i].pok then goto l;
if ((d=0) or (d= pArpok[j,i-1].Id)) then goto l;
for k:=0 to sum[j]-1 do
if (pArpok[j,k].Id = d) then d1:= k;
LinkOneLayer(pArpok[j,i].xp, pArpok[j,i].yp,pArpok[j,d1].xp);
l: d:= -1;
end;
end;
Рисование линии-связи «родители-ребенок» осуществляется похожим образом, что и линия, связывающая супругов. Только здесь следует учитывать, что детей может быть несколько и ребенок имеет указатель на следующего ребенка. Рисование происходит с помощью процедуры LinkTwoLayer(xn,y, xk, xsb,yk: integer) .
for j:=1 to Kol_U do
begin
for i:=0 to sum[j]-1 do
begin
d:=pArpok[j,i].Husbend;
if ((d=0) or (d= pArpok[j,i-1].Id)) then goto m1;
for k:=0 to sum[j]-1 do
if (pArpok[j,k].Id = d) then d1:= k;
d2:= pArpok[j,i].Children;
if d2=0 then goto m1;
if d2<> PersonAr[d].Children then goto m1;
for k:=0 to sum[j-1]-1 do
if (pArpok[j-1,k].Id = d2) then d3:= k;
LinkTwoLayer(pArpok[j,i].xp, pArpok[j,i].yp,pArpok[j,d1].xp, pArpok[j-1,d3].xp, pArpok[j-1,d3].yp);
d4:= pArpok[j-1,d3]. Sist_Broth;
while d4<>0 do
begin
for k:=0 to sum[j-1]-1 do
if (pArpok[j-1,k].Id = d4) then d3:= k;
LinkTwoLayer(pArpok[j,i].xp, pArpok[j,i].yp,pArpok[j,d1].xp, pArpok[j-1,d3].xp, pArpok[j-1,d3].yp);
d4:= pArpok[j-1,d3]. Sist_Broth;
end;
m1: d:= -1;
end;
end;
В этой процедуре необходимо охватить все связи между людьми, например, разное количество браков или внебрачные дети. Для этого есть идентификатор SomeWife, принимает значениеfalseв этом случае. Тогда, когда вызывается процедураLinkOneLayer(xn,y,xk: integer)происходит проверка на это идентификатор. В рассматриваемом случае эта линия будет рисоваться ниже и пунктиром.
Если известен только один родитель ребенка, то это тоже учитывается, при этом используется процедура Link(xn,yn, xk,yk: integer).Если супруги находятся на разных поколениях, то рисование линии, связывающей их, осуществляет процедураWrongLink(xn,yn, xk,yk: integer).
for i:=0 to Kol do
begin
m:= PersonAr[i].MotherId;
f:= PersonAr[i].FatherId;
if (((PersonAr[i].MotherId <> 0) and (PersonAr[i].FatherId = 0)) or ((PersonAr[i].MotherId = 0) and (PersonAr[i].FatherId <> 0))) then
begin
if PersonAr[i].MotherId <> 0 then Link(PersonAr[m].xp, PersonAr[m].yp,PersonAr[i].xp,PersonAr[i].yp)
else Link(PersonAr[f].xp, PersonAr[f].yp,PersonAr[i].xp,PersonAr[i].yp);
end;
end;
for i:=0 to Kol do
begin
h:= PersonAr[i].husbend;
if ((PersonAr[i].pok <> PersonAr[h].pok) and (h<>0)) then WrongLink(PersonAr[i].xp, PersonAr[i].yp, PersonAr[h].xp, PersonAr[h].yp);
end;
Процедура Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer): при движении мышки по узлу дерева данная процедура обеспечивает высвечивание подсказки с ФИО персоны этого узла. Сравниваются координаты мышки и узла дерева.
Процедура Image1MouseDown(Sender:TObject;Button:TMouseButton;
Shift:TShiftState; X, Y: Integer): при нажатии мышки на узел дерева появляется форма «Информация о конкретном узле дерева». Сама процедура аналогичнаImage1MouseMove.
Процедура N16Click: обеспечивает уменьшение размеров дерева до размеров экрана монитора. Рассчитываются коэффициенты сжатия по оси Х иY,kxиky.
kx:= fmTree.Width/Image1.Width;
ky:= (fmTree.Height-70)/Image1.Height;
Изменяются координаты всех дерева путем умножения на коэффициенты имееющихся координат.
Процедура N17Click: возвращает реальный размер дерева.
ПроцедурыPointM (x,y:integer;Image1:TImage;l:integer) и PointF(x,y:integer;Image1:TImage; l: integer): рисуют узлы дерева, кружочек – это женщина, квадратик – мужчина.
Модуль SelFam – этот модуль реализует алгоритм выделения отдельной ветви дерева, описанный ранее в техническом проекте.
Этот алгоритм основан на рекурсивной процедуре Potomok(Id, pos).В качестве входных параметров в процедуреPotomok(Id,pos)номерId– тот, чьих потомков ищем,Pos– индекс в массивеMas – место, куда записывается очередной найденный потомок. Цель процедуры сформировать массивMas,в котором будут находиться только те, кто является потомком выбранного человека. На первое место массиваMasзаносится тот самый выбранный человек. Если у человека нет детей и сестер/братьев, то выходим из алгоритма.
Если есть сестры/братья, то проверяем одни ли у них родители. Родители одни – записываем найденного человека в массив Mas. После этого вызываем рекурсивно процедуруPotomok(Id,pos)уже для найденного человека. Так будет продолжаться, пока у найденных сестер/братьев будут находиться дети или их сестры /братья. Если таковых нет, то в массив заносится ребенок выбранного нами человека и вызывается процедура уже для него. Таким образом, в массивеMasокажутся все потомки выбранного пользователем человека. Можно рисовать. Но в рисовании есть тоже некоторые особенности, которые выполняет процедураDrawBranch.
function Potomok(id:integer; var pos:integer):boolean;
label l;
begin
if ((mas[pos].Children=0) and (mas[pos].Sist_Broth=0)) then
begin
roma:=true;
exit;
end;
while mas[pos].Sist_Broth<>0 do
begin
//if (((mas[pos].Husbend <> PersonAr[mas[pos].Sist_Broth].MotherId) and (mas[pos].Husbend = PersonAr[mas[pos].Sist_Broth].FatherId)) or ((mas[pos].Husbend = PersonAr[mas[pos].Sist_Broth].MotherId) and (mas[pos].Husbend <> PersonAr[mas[pos].Sist_Broth].FatherId)) or (mas[pos].Husbend=0)) then goto l;
if (((mas[pos].MotherId <> PersonAr[mas[pos].Sist_Broth].MotherId) and (mas[pos].FatherId = PersonAr[mas[pos].Sist_Broth].FatherId)) or ((mas[pos].MotherId = PersonAr[mas[pos].Sist_Broth].MotherId) and (mas[pos].FatherId <> PersonAr[mas[pos].Sist_Broth].FatherId)) {or (mas[pos].Husbend=0}) then goto l;
pos:=pos+1;
mas[pos]:=PersonAr[mas[pos-1].Sist_Broth];
Potomok(mas[pos].id,pos);
end;
l: pos:=pos+1;
mas[pos]:=PersonAr[PersonAr[id].Children];
Potomok(mas[pos].Id,pos);
end;
Процедура DrawBranch: осуществляет рисование ветви дерева в соответствии с массивомMas. Процедура выполняется аналогично процедуреDrawTree.
Модуль Combine– модуль объединения деревьев. Модуль реализует алгоритм объединения деревьев, описанный в техническом проекте.
Главной процедурой этого модуля является процедура ReadFromFile (fname: string).На начальном этапе дерево, с которым мы работаем, находится в массивеPersonAr. В массивPersonAr1считываем дерево, с которым будем объединять. Сравниваем два массива на предмет общих людей. Присваиваем флажкуIdединичку, если общие люди есть. Затем делаем проверку на этот флажок, если не единица, то показываем формуfmSugestс предупреждением «В деревьях нет общих людей». Затем предлагается выбрать другое для объединения дерево или выйти из задачи объединения.
Модуль files – это модуль, где описаны функции работы с файлами.
Модуль prgenealogia_begin – это главный модуль программы.
Модуль enterfam – это модуль информации по конкретным узлам.
Модуль gedcom – это модуль чтения информации изGedComфайла.
Модуль gedcom1 – это модуль записи информации вGedCom файл.
Модуль people – это модуль выбора родственников. В нем предлагается выбрать нужного человека из предложенного списка, либо добавить нового человека. Выбрать можно супруга, мать, отца и детей. Эти действия реализуются процедурамиShowFather, ShowMother, ShowChildren, ShowHusbend. Процедуры аналогичны друг другу. Ниже приведена одна из этих процедур:
procedure TfmChoice.ShowHusbend;
var i: integer;
begin
if PersonAr[tecid].pol='жен' then
cmbPol.Text:='муж' else cmbPol.Text:='жен';
for i:=1 to Kol do
if ((PersonAr[i].Husbend=tecid) and (PersonAr[i].Id<>tecId)) then
lbPeople.Items.Add(IntToStr(PersonAr[i].id)+' '+PersonAr[i].Fam+' '+PersonAr[i].Name+' '+PersonAr[i].PatronName);
for i:=1 to Kol do
if PersonAr[i].Id<>tecId then
cmbChoice.Items.Add(IntToStr(PersonAr[i].id)+' '+PersonAr[i].Fam+' '+PersonAr[i].Name+' '+PersonAr[i].PatronName);
end;
Модуль warningdel– это модуль предупреждения об удалении.
Модуль name – это модуль ввода названия медиа объекта.
Модуль husbend – это модуль вопроса о браке родителей.
Модуль poisk – это модуль информации по конкретным узлам.
Всю информацию по модулю – см. в первой части работы.
Модуль poisk1 – это модуль вывода ответа на вопрос пользователя.
Всю информацию по модулю – см. в первой части работы.
Модуль statistic – это модуль статистики по текущей родословной.
Всю информацию по модулю – см. в первой части работы.