Ниже новыми называются члены семьи, информация о которых в базе данных отсутствует, старыми — для которых она присутствует.
Для того чтобы добавить нового члена семьи в базу данных, необходимо нажать на кнопку "Добавить" окна ).
(рис 7.1) Режим добавления данных
Добавим обработчик события нажатия на кнопку "Добавить" окна familyForm. Ниже приведено его определение.
clauses
onNewClick(_Source) = button::defaultAction:-
db:selectedPerson := core::none(),
familyForm := This,
Win = tabForm::display(getParent()),
Win:setText("Добавление"),
if Index = listbox_ctl:tryGetSelectedIndex() then
listbox_ctl:selectAt(Index, false)
end if.
При открытии формы ). Кроме этого, становятся видимыми кнопки, которые не используются в режиме просмотра. Это кнопки служат для добавления или удаления изображений.
Определим предикат getList/1, который формирует список мужчин или женщин, предикат fillList/2, который заполняет выпадающий список списком элементов и выделяет первый элемент, а также предикат getSex/1, который возращает пол по состоянию переключателя "женский". В список родителей добавляется пустой элемент, который используется в случае, когда сведения о родителе отсутствуют в базе данных.
constants
emptyItem = "< >".
predicates
getList: (string Sex) -> string* ItemList.
clauses
getList(Sex) = [Person:idLegend() ||
Person in db:personList, Person:sex = Sex].
predicates
fillList: (string* ItemList, listButton).
clauses
fillList(ItemList, ListCtl):-
ListCtl:addList(ItemList),
ListCtl:addAt(list::length(ItemList), emptyItem),
ListCtl:selectAt(0, true).
predicates
getSex: (radioButton::state Female) -> string Sex.
clauses
getSex(radioButton::checked) = dbrel::female:- !.
getSex(_) = dbrel::male.
Изменим определение предиката onShow страницы infoPage так, как показано ниже.
clauses
onShow(_Source, _Data):- % режим просмотра
some(Person) = db:selectedPerson,
!,
name_ctl:setText(Person:name),
surname_ctl:setText(Person:surname),
setSex(Person:sex),
setParent(Person:idfather, father_ctl),
setParent(Person:idmother, mother_ctl),
setSpouses(Person),
setPictures(Person),
setEnable(Person:status).
onShow(_Source, _Data):- % режим добавления
FemaleList = getList(dbrel::female),
fillList(FemaleList, mother_ctl),
MaleList = getList(dbrel::male),
fillList(MaleList, father_ctl),
spouses_ctl:clearAll(),
spouses_ctl:addList(FemaleList),
setEnable("добавление").
Если новый член семьи — женщина, то в потенциальный список супругов помещается список мужчин, а если мужчина — то женщин. В список супругов заносятся идентификаторы выделенных элементов потенциального списка супругов.
Добавим обработчик событий StateChangedListener для переключателя "мужской".
Ниже приведено определение предиката onMaleStateChanged. Список супругов обновляется при изменении состояния переключателя.
predicates
onMaleStateChanged : radioButton::stateChangedListener.
clauses
onMaleStateChanged(_Source, _OldState, NewState):-
spouses_ctl:clearAll(),
spouses_ctl:addList(getList(getSex(NewState))).
Для нового члена семьи можно записать в базу данных сразу несколько изображений. Эти изображения должны быть заранее подготовлены. Файлы, содержащие изображения, копируются в папку images, которая находится в директории Exe, если они не были размещены в ней заранее. Файлы, расположенные в одной и той же папке, могут добавляться списком. Добавление изображений производится с помощью кнопки "…". Для удаления изображения предназначена кнопка "-". Добавим обработчики событий нажатия на эти кнопки.
Ниже определяются предикаты onBrowseClick и getFileList/3. При нажатия кнопки "…" откроется окно "Открыть файл", с помощью которого можно выбрать сразу несколько файлов (изначально открывается папка images), используя клавиши Shift или Ctrl.
clauses
onBrowseClick(_Source) = button::defaultAction:-
StartPath = dbrel::imageFolder(),
FileName = vpiCommonDialogs::getFileName(
"*.bmp", ["Файл bmp", "*.bmp"], "Выберите файлы",
[dlgfn_multisel], StartPath, FileNameList),
!,
Path = fileName::getPath(FileName),
FileList = getFileList(FileNameList, Path, StartPath),
pictures := list::union(pictures, FileList),
n := list::length(pictures),
k := 0,
pictures == [FirstFile | _],
pictControl_ctl:drawPict(FirstFile),
del_ctl:setEnabled(true),
right_ctl:setEnabled(toBoolean(n > 1)),
left_ctl:setEnabled(false).
onBrowseClick(_Source) = button::defaultAction.
predicates
getFileList: (string*, string Path, string StartPath) -> string*.
clauses
getFileList(FileNameList, Path, StartPath) =
[FileName || FullName in FileNameList,
FileName = filename::getNameWithExtension(FullName),
if not(string::equalIgnoreCase(Path, StartPath)),
ImageFile = fileName::setPath(FullName, StartPath),
not(file::existFile(ImageFile))
then
file::copy(FullName, ImageFile)
end if].
При нажатии кнопки "-" текущее изображение, которое отображается в окне, удаляется из списка изображений. Ниже определяется предикат onDelClick.
clauses
onDelClick(_Source) = button::defaultAction:-
n > 0,
pictures := list::remove(pictures, list::nth(k, pictures)),
!,
n := n - 1,
k := if n > 0, k < n then k elseif n > 0 then n - 1 else 0 end if,
if n > 0 then
pictControl_ctl:drawPict(list::nth(k, pictures))
else
pictControl_ctl:clear()
end if,
right_ctl:setEnabled(toBoolean((n > 1, k < n - 1))),
left_ctl:setEnabled(toBoolean((n > 1, k > 0))),
del_ctl:setEnabled(toBoolean(n > 0)).
onDelClick(_Source) = button::defaultAction.
Для любого члена семьи, нового или старого, в базу данных можно добавить заметку (жизнеописание). Для этого нужно ввести текст на странице descrPage в текстовое поле или вставить его из файла. После этого текст необходимо сохранить в текстовом файле в папке descriptions, с помощью нажатия на кнопку "Сохранить". Затем нужно нажать на кнопку "Записать". Позднее потребуется сохранить изменения в базе данных (см. ниже).
В редакторе страницы descrPage добавим обработчик события нажатия на кнопку "Записать". Ниже определяется предикат onSaveInDbClick.
clauses
onSaveInDbClick(_Source) = button::defaultAction:-
some(Person) = db:selectedPerson,
filename <> "",
Text = string::trim(sciLexer_ctl:text),
Text <> "",
FileName = fileName::getNameWithExtension(filename),
not((P in db:personList, FileName in P:descriptions)),
!,
file::writeString(filename, Text, isUnicode),
Person:descriptions := [Filename | Person:descriptions],
db:addDescription(Person, Filename),
stdio::write("Описание добавлено в базу данных.\n").
onSaveInDbClick(_Source) = button::defaultAction.
Предикат addDescription/2 необходимо объявить в интерфейсе dbrel и определить в имплементации класса dbrel.
predicates
addDescription: (person, string).
Листинг 7.10. Определение предиката в имплементации класса dbrel
clauses
addDescription(Person, FileName):-
Person:status <> "добавление",
not(descr(Person:id, Filename)),
!,
assert(descr(Person:id, Filename)).
addDescription(_, _).
Сведения о новом члене семьи, помещенные на страницу информации окна ).
(рис 7.2) Добавление новой персоны
Добавим обработчик события нажатия на кнопку Ok. Его определение приведено ниже.
clauses
onOkClick(_Source) = button::defaultAction:-
Person = infoPage:getNewData(),
!,
familyForm:addPerson(Person).
onOkClick(_Source) = button::defaultAction.
После нажатия на кнопку Ok, создается объект класса person, в который записывается информация, считанная с первой страницы вкладок. Затем имя нового члена семьи сразу отображается в списке окна familyForm.
В интерфейс infoPage cледует добавить объявление предиката getNewData.
predicates
getNewData: () -> person determ.
Ниже приведено определение этого предиката. Новый объект класса person создается только в том случае, если заполнены поля "Имя" и "Фамилия".
clauses
getNewData() = Person:-
Name = getName(name_ctl),
Surname = getName(surname_ctl),
!,
Id = db:getNewId(),
Person = person::new(Id),
Person:name := Name,
Person:surname := Surname,
Person:sex := getSex(),
Person:idFather := getParent(father_ctl, Id),
Person:idMother := getParent(mother_ctl, Id),
Person:spouses := getSpouses(),
Person:pictures := pictures,
Person:status := "добавление",
db:personList := [Person | db:personList].
getNewData() = _:-
vpiCommonDialogs::error("Поля apos;Имяapos; и apos;Фамилияapos; "
"обязательны для заполнения"),
fail.
predicates
getName: (editControl) -> string determ.
clauses
getName(Ctl) = Name:-
Name = string::trim(Ctl:getText()),
Name <> "".
predicates
getId: (string Item) -> unsigned Id determ.
clauses
getId(Item) = Id:-
string::frontToken(Item, Tok, _),
Id = tryToTerm(unsigned, Tok).
predicates
getSex: () -> string.
clauses
getSex() = getSex(female_ctl:getRadioState()).
predicates
getParent: (listButton, unsigned IdPerson) -> unsigned IdParent.
clauses
getParent(Ctl, Id) = IdParent:-
Index = Ctl:tryGetSelectedIndex(),
Item = Ctl:getAt(Index),
IdParent = getId(Item),
Parent = db:getPerson(IdParent),
!,
Parent:children := [Id | Parent:children].
getParent(_Ctl, _Id) = 0.
predicates
getSpouses: () -> unsigned* IdList.
clauses
getSpouses() = [Id || Item in L, Id = getId(Item)]:-
L = spouses_ctl:getSelectedItems().
Объявим предикат addPerson в интерфейсе familyForm.
predicates
addPerson: (person).
Ниже приведено определение предиката addPerson в имплементации класса familyForm.
clauses
addPerson(Person):-
listbox_ctl:addAt(-1, legend(Person)).
Первым аргументом предиката addAt/2 является индекс элемента списка. Если вместо него указывается значение (– 1), то элемент либо добавляется в конец списка, в случае если его элементы не сортируются автоматически (автоматическая сортировка устанавливается по умолчанию — см. таблицу свойств для списка), либо он вставляется в список так, чтобы тот оставался упорядоченным.
Информация о новом члене семьи не записывается в базу данных автоматически. Операции записи и удаления этой информации выполняются с помощью кнопок окна familyForm.
Для того чтобы записать сведения о члене семьи в базу данных, следует выделить его имя в списке окна familyForm и затем нажать на кнопку "Сохранить". Если он имел статус "добавление", то информация о нем запишется в базу данных. Удаление информации о члене семьи, как уже записанной в базу данных, так и еще не записанной в нее, выполняется при нажатии на кнопку "Удалить". Добавим обработчики событий нажатия на эти кнопки.
Ниже приведено определение предиката onSaveClick.
clauses
onSaveClick(_Source) = button::defaultAction:-
Person = getSelectedPerson(),
"добавление" = Person:status,
!,
db:savePerson(Person),
status_ctl:setText(Person:status).
onSaveClick(_Source) = button::defaultAction.
Далее определяется предикат onDelClick.
clauses
onDelClick(_Source) = button::defaultAction:-
Person = getSelectedPerson(),
db:deletePerson(Person),
Index = listbox_ctl:tryGetSelectedIndex(),
!,
listbox_ctl:delete(Index),
status_ctl:setText(""),
pictControl_ctl:clear(),
view_ctl:setEnabled(false),
del_ctl:setEnabled(false).
onDelClick(_Source) = button::defaultAction.
Предикаты savePerson/1 и deletePerson/1 следует объявить в интерфейсе dbrel.
predicates
savePerson: (person).
deletePerson: (person).
Ниже приведено определение этих предикатов в имплементации класса dbrel. Предикат savePerson записывает информацию в базу данных. Предикат deletePerson удаляет информацию из базы данных, а также из объектов класса person.
clauses
savePerson(Person):-
assert(person(Person:id, Person:name, Person:surname,
Person:sex, Person:idfather, Person:idmother)),
list::forAll(Person:spouses, {
(I):- getSpouses(Person, I, IdH, IdW),
assert(spouse(IdH, IdW))}),
list::forAll(Person:pictures,
{(BmpFile):- assert(pict(Person:id, BmpFile))}),
list::forAll(Person:descriptions,
{(TxtFile):- assert(descr(Person:id, TxtFile))}),
Person:status := "просмотр".
predicates
getSpouses: (person, unsigned, unsigned [out], unsigned [out]).
clauses
getSpouses(Person, IdW, Person:id, IdW):-
male = Person:sex,
!.
getSpouses(Person, IdH, IdH, Person:id).
clauses
deletePerson(Person):-
removeFromDb(Person),
removeFromPersonList(Person).
predicates
removeFromDb: (person).
clauses
removeFromDb(Person):-
"добавление" = Person:status,
!.
removeFromDb(Person):-
retractAll(person(Person:id, _, _, _, _, _)),
retractAll(spouse(Person:id, _)),
retractAll(spouse(_, Person:id)),
retractAll(pict(Person:id, _)),
retractAll(descr(Person:id, _)).
predicates
removeFromPersonList: (person).
clauses
removeFromPersonList(Person):-
personList := list::remove(personList, Person),
P in personList,
P:spouses:= list::remove(P:spouses, Person:id),
P:children := list::remove(P:children, Person:id),
if P:idfather = Person:id then P:idfather := 0 end if,
if P:idmother = Person:id then P:idmother := 0 end if,
fail.
removeFromPersonList(_Person).
Текущее состояние базы данных не сохраняется автоматически в файле. Для этого пользователь должен ответить положительно на вопрос программы о сохранении изменений в базе данных (см. ниже). Добавим для формы familyForm обработчик событий DestroyListener. Его определение приведено ниже.
predicates
onDestroy : window::destroyListener.
clauses
onDestroy(_Source):-
0 = vpiCommonDialogs::ask("Сохранение изменений",
"Сохранить изменения в базе данных?", ["Да", "Нет"]),
!,
db:copy(),
db:save(),
stdio::writef("Изменения сохранены в базе данных.\n").
onDestroy(_Source).
После закрытия окна ).
(рис 7.3) Диалоговое окно Ask
Если пользователь нажимает кнопку "Да", то создается новый файл, в который копируется содержимое файла, хранящего факты базы данных до ее загрузки. Затем новое состояние базы данных сохраняется в старом файле. Предикат ask/3 возвращает номер выбранной кнопки. Кнопка "Да" имеет номер 0, кнопка "Нет" — номер 1 (см. код).
Предикаты copy и save следует объявить в интерфейсе dbrel.
predicates
copy: ().
save: ().
Ниже приведено определение предикатов в имплементации класса dbrel.
clauses
copy():-
Time = time::new(),
S = Time:formatDateTime("ddMMyy", "HHmmss"),
Name = fileName::getName(filename),
NewName = string::concat(Name, S),
NewFileName = fileName::setExtension(NewName, "dbp"),
file::copy(filename, NewFileName).
save():-
file::save(filename, rel).
Имя файла, в который копируется старое состояние базы данных, генерируется автоматически с помощью приписывания к имени файла, в котором хранится база данных, текущих даты и времени. Ему присваивается расширение dbp.
Упражнения
7.1. Добавьте проверку корректности перед добавлением родителей и супругов (они не должны быть родственниками новому члену семьи в текущем состоянию базы данных, родители не должны быть моложе детей и т. д.).
7.2. Добавьте режим редактирования сведений о персоне. Страница информации формы tabForm должна открываться так же, как в режиме добавления, но поля, содержащие имя, фамилию, пол и изображения должны быть уже заполнены. В списках родителей должны быть выделены строки с именами родителей персоны или пустые строки, если сведения о родителях отсутствуют в базу данных. В списке супругов должны быть выделены имена супругов. После нажатия кнопки Ok, обновленная информация о персоне должна быть записана в объект класса person.
7.3. Измените определение обработчика событий нажатия на кнопку "Сохранить" так, чтобы в режиме редактирования персоны (см. упр. 7.2) из базы данных была удалена старая информация об этом человеке и была записана новая информация. Добавьте соответствующие предикаты в класс dbrel.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.