Создадим проект relatives (MDI). Подготовим текстовый файл так, как описано ниже, содержащий факты базы данных, и поместим его в папку Exe, а также bmp- и txt-файлы и разместим их в папках images и descriptions, которые должны быть созданы в директории Exe проекта (см. ниже).
Информация о членах семьи содержится во внутренней базе данных. Для ее хранения используется текстовый файл.
Создадим текстовый файл в директории Exe проекта, так чтобы он отображался в дереве проекта. Для этого выделим корень дерева проекта, с помощью команды (контекстного) меню New In New Package откроем диалоговое окно Create Project Item, выделим элемент Text File и заполним поля следующим образом:
Name: family;Parent Directory: Exe.Далее нужно нажать кнопку Create. В результате в директории Exe проекта будет создан файл family.txt. В него необходимо поместить факты базы данных, приведенные ниже.
clauses
person(1, "Иван", "Петров", "м", 0, 0).
person(2, "Анна", "Петрова", "ж", 0, 0).
person(3, "Мария", "Иванова", "ж", 1, 2).
person(4, "Павел", "Иванов", "м", 0, 3).
person(5, "Петр", "Иванов", "м", 0, 3).
person(6, "Елизавета", "Иванова", "ж", 0, 3).
person(7, "Степан", "Иванов", "м", 5, 0).
person(8, "Юлия", "Иванова", "ж", 0, 0).
spouse(1, 2).
spouse(4, 8).
pict(1, "ivan1.bmp").
pict(1, "ivan2.bmp").
pict(2, "anna.bmp").
pict(3, "maria1.bmp").
pict(3, "maria2.bmp").
pict(6, "elizabeth.bmp").
descr(3, "maria.txt").
Предикат ). Следует подготовить указанные bmp-файлы с изображениями, создать в директории ).
(рис 4.1) Папки Exe\descriptions и Exe\images
Предикат ).
Для каждого человека из базы данных создается объект класса person, в котором размещается вся информация об этом человеке.
Создадим класс person с интерфейсом person. В интерфейсе person следует объявить свойства, а также предикаты legend и idLegend, которые используются для формирования надписей в списках и деревьях.
properties
id : unsigned.
name : string.
surname : string.
sex : string.
idFather : unsigned.
idMother : unsigned.
children : unsigned*.
spouses : unsigned*.
pictures : string*.
descriptions : string*.
status : string.
predicates
legend: () -> string.
idLegend: () -> string.
В декларации класса person следует объявить конструктор.
constructors
new: (unsigned Id).
Ниже приведено определение свойств и предикатов.
facts
id : unsigned.
name : string := "".
surname : string := "".
sex : string := "м". % dbrel::male
idFather : unsigned := 0.
idMother : unsigned := 0.
children : unsigned* := [].
spouses : unsigned* := [].
pictures : string* := [].
descriptions : string* := [].
status : string := "просмотр".
clauses
new(Id):-
id := Id.
legend() = string::format("% %", name, surname).
idLegend() = string::format("%2. %", id, legend()).
Ниже создается класс dbrel, который обеспечивает взаимодействие с базой данных.
Создадим класс dbrel с интерфейсом dbrel. В интерфейсе dbrel следует объявить указанные ниже константы, свойства и предикаты.
constants
images = "images". % назв. папки с изобр.
descriptions = "descriptions". % назв. папки с заметками
constants
male = "м". % мужской пол
female = "ж". % женский пол
properties
personList : person*.
selectedPerson : optional{person}.
predicates
load: ().
getPerson: (unsigned) -> person determ.
getNewId: () -> unsigned.
Свойство personList используется для хранения указателей на объекты класса person. Свойство selectedPerson используется для хранения указателя на объект выделенного члена семьи. Домен optional{T} определен в классе core следующим образом:
domains
optional{T} = none(); some(T).
Предикат load загружает базу данных, предикат getPerson/1 по идентификатору возвращает указатель на объект класса person. Предикат getNewId возвращает идентификатор для нового человека, сведения о котором будут записываться в базу данных.
Следующее объявление свойства и предикатов необходимо поместить в декларацию класса dbrel.
properties
db : optional{dbrel}. % указатель на объект текущей БД
constructors
new: (string FileName).
predicates
imageFolder: () -> string.
descriptionsFolder: () -> string.
Свойство db используется для хранения указателя на объект текущей базы данных. Приложение предоставляет возможность одновременной работы с несколькими семьями. Конструктор new/1 создает по имени файла объект базы данных. Предикаты imageFolder и descriptionsFolder возвращают путь к папкам, содержащим изображения и описания.
В имплементации класса dbrel следует объявить базу данных, а также определить объявленные выше свойства и предикаты.
В раздел open имплементации класса следует добавить имя класса stdio.
open core, stdio
facts
filename : string.
personList : person* := [].
selectedPerson : optional{person} := none().
maxId : unsigned := 0.
clauses
new(FileName):-
filename := FileName.
class facts
db : optional{dbrel} := none().
facts - rel
person: (unsigned, string, string, string, unsigned, unsigned).
spouse: (unsigned IdHusband, unsigned IdWife).
pict: (unsigned Id, string BmpFileName).
descr: (unsigned Id, string TextFileName).
clauses
load():-
try file::consult(filename, rel)
catch Error do
writef("Error %. Unable to load the database from %\n",
Error, filename)
end try,
setMaxId(),
loadDb().
predicates
setMaxId: ().
clauses
setMaxId():-
IdList = [Id || person(Id, _, _, _, _, _)],
IdList <> [],
!,
maxId := list::maximum(IdList).
setMaxId().
predicates
loadDb: ().
clauses
loadDb():-
person(Id, Name, Surname, Sex, IdFather, IdMother),
Person = person::new(Id),
Person:name := Name,
Person:surname := Surname,
Person:sex := Sex,
Person:idFather := IdFather,
Person:idMother := IdMother,
Person:children := getChildren(Id, Person:sex),
Person:spouses := [I || spouse(Id, I); spouse(I, Id)],
Person:pictures := [BmpFile || pict(Id, BmpFile)],
Person:descriptions := [TxtFile || descr(Id, TxtFile)],
personList := [Person | personList],
fail.
loadDb().
predicates
getChildren: (unsigned Id, string Sex) -> unsigned* ChildrenList.
clauses
getChildren(Id, male) = [I || person(I, _, _, _, Id, _)]:- !.
getChildren(Id, _) = [I || person(I, _, _, _, _, Id)].
clauses
getPerson(Id) = Person:-
Person in personList,
Person:id = Id,
!.
clauses
getNewId() = maxId:-
maxId := maxId + 1.
clauses
imageFolder() = folder(images).
descriptionsFolder() = folder(descriptions).
class predicates
folder: (string Name) -> string Path.
clauses
folder(Name) = string::concat(Path, Folder):-
mainExe::getFileName(Path, _),
Folder = string::format(@"%\", Name).
Предикат loadDb для каждого члена семьи, информация о котором помещена в базу данных, создает объект класса person и записывает в него все сведения, имеющиеся о нем в базе данных. Предикат getFileName/2 возвращает путь к exe-файлу проекта.
Ниже создается окно просмотра общих сведений. Окно содержит список, в котором пересчисляются члены семьи, поле для просмотра изображений и ряд кнопок (рис. 4.2 рис 4.2).
(рис 4.2) База данных "Family"
Замечание.Пользователи версии Visual Prolog 7.x Commercial Edition могут не создавать окно pictControl, а вместо него использовать imageControl. Ниже показано, как это можно сделать.
Предварительно создадим окно pictControl, которое будет использоваться для отображения изображений (см. ниже).
Выделим корень дерева проекта, с помощью команды меню New in New Package откроем диалоговое окно Create Project Item, выделим в нем элемент Draw Control, в поле Name напишем название pictControl и нажмем кнопку Create. Затем закроем редактор окна.
В интерфейсе pictControl следует объявить предикаты drawPict/1 и clear. Первый предикат показывает изображение, второй очищает поле.
predicates
drawPict: (string FileName).
clear: ().
Определение объявленных предикатов в имплементации класса pictControl приведено ниже.
facts
pict : picture := erroneous.
pictRct : rct := erroneous.
class facts % база загруженных изображений
picture: (string FileName, picture, rct).
clauses
drawPict(FileName):-
picture(FileName, Picture, PictRect),
!,
pict := Picture,
pictRct := PictRect,
invalidate().
drawPict(FileName):-
Picture = loadPicture(FileName),
!,
pict := Picture,
vpi::pictGetSize(Picture, PictWidth, PictHeight, _),
pictRct := rct(0, 0, PictWidth, PictHeight),
assert(picture(FileName, pict, pictRct)),
invalidate().
drawPict(_FileName):-
clear().
predicates
loadPicture: (string FileName) -> picture determ.
clauses
loadPicture(FileName) = Picture:-
FullName = string::concat(dbrel::imageFolder(), FileName),
try Picture = vpi::pictLoad(FullName)
catch Error do
stdio::writef("Error %. Unable to load the picture from %\n",
Error, FullName),
fail
end try.
clauses
clear():-
pict := erroneous,
invalidate().
Далее для окна pictControl следует добавить обработчики событий PaintResponder, SizeListener и EraseBackgroundResponder.
Предикат onPaint либо отображает изображение, либо выводит надпись "No picture".
clauses
onPaint(_Source, Rectangle, GDI):-
not(isErroneous(pict)),
!,
GDI:pictDraw(pict, Rectangle, pictRct, rop_SrcCopy).
onPaint(_Source, Rectangle, GDI):-
GDI:clear(color_MediumTurquoise),
GDI:drawTextInRect(Rectangle, "NO PICTURE",
[dtext_center, dtext_singleline, dtext_vcenter]).
Предикаты onSize и onEraseBackground определяются так же, как и ранее.
clauses
onSize(_Source):-
invalidate().
clauses
onEraseBackground(_Source, _GDI) = noEraseBackground.
Создадим форму familyForm. В редакторе формы удалим из нее кнопки Cancel и Help. Ниже на форму добавляется ряд элементов управления и устанавливаются свойства для них с помощью окна свойств. Элемент управления следует выбрать на панели инструментов окна Controls и "перенести" на форму, а затем в таблице свойств изменить значения свойств, указанные ниже.
При выделении элемента управления в списке элементов, расположенном в верхней части окна свойств, или непосредственно на прототипе формы, в таблице свойств отображается список свойств этого элемента управления. Если на форме не выделен ни один из элементов управления или если в списке выделен элемент в верхнем списке окна свойств выделен элемент Form, поэтому в таблице свойств отображаются свойства окна.
(рис 4.3) Редактор формы familyForm
С помощью панели инструментов ):
cписок (List Box), свойству UseTabStop установим значение True;
надпись (Static Text), для которой установим следующие свойства:
Representation: Fact Variable; Name: status_ctl; Text:<пустое поле>
пользовательский элемент управления (), выберем имя класса pictControl (пользователи Commercial Edition вместо него могут указать imageControl), затем установим свойства:
Right Anchor: True; Bottom Anchor: True;
пять кнопок (Push Button)
Name: view_ctl; Text: Открыть; Enabled: False; Name: new_ctl; Text: Добавить; Name: save_ctl; Text: Сохранить; Enabled: False; Name: del_ctl; Text: Удалить; Enabled: False; Name: table_ctl; Text: Таблица
(рис 4.4) Окно выбора класса для элемента управления
Для всех новых кнопок следует установить следующие свойства:
Left Anchor: False; Top Anchor: False; Right Anchor: True; Bottom Anchor: True.
После этого следует закрыть редактор формы.
Свойство Anchor (якорь) определяет, привязан ли элемент к краю контейнера (формы). Если его значение равно True, то изменение размеров формы не влияет на расстояние от элемента до границы формы. В окне familyForm кнопки привязываются к правой и нижней границам формы, расстояние от них до указанных границ при изменении размеров формы остается неизменным. Элемент pictControl привязан ко всем границам формы, поэтому при изменении размеров формы будет изменяться и его размер.
Далее в интерфейсе familyForm необходимо объявить свойство, которое используется для хранения указателя на объект базы данных.
properties
db : dbrel.
В декларации класса familyForm следует объявить свойство, которое используется для !указателя на объект текущей формы (оно необходимо при одновременной работе с несколькими формами класса familyForm).
properties
familyForm : familyForm.
В имплементации класса familyForm определим объявленные свойства и добавим предикаты legend и getSelectedPerson. Предикат legend/1 используется для формирования элемента списка. Предикат getSelectedPerson/1 для выделенного элемента списка возвращает указатель на объект класса person.
facts
db : dbrel := erroneous.
class facts
familyForm : familyForm := erroneous.
predicates
legend: (person) -> string.
clauses
legend(Person) = string::format("%2. %-12\t%",
Person:id, Person:name, Person:surname).
predicates
getSelectedPerson: () -> person determ.
clauses
getSelectedPerson() = db:getPerson(Id):-
Index = listbox_ctl:tryGetSelectedIndex(),
Item = listbox_ctl:getAt(Index),
string::frontToken(Item, Tok, _),
Id = tryToTerm(unsigned, Tok).
Предикат tryGetSelectedIndex возвращает номер выделенного элемента списка (элементы нумеруются с нуля), предикат getAt/1 возвращает по номеру элемент списка.
Теперь в редакторе формы familyForm добавим обработчик событий ShowListener. При открытии окна формируется список членов семьи.
clauses
onShow(_Source, _Data):-
some(Db) = dbrel::db,
!,
db := Db,
listbox_ctl:setTabStops([22]),
listbox_ctl:addList([legend(Pers) || Pers in db:personList]).
onShow(_Source, _Data).
Далее добавим обработчик события выделения элемента списка SelectionChangedListener. Для этого в редакторе формы нужно выделить список, перейти на вкладку Events окна свойств и выбрать для обработчика события SelectionChangedListener элемент onListBoxSelectionChanged.
При выделении элемента списка (члена семьи) находится указатель на объект класса . Кроме этого, включаются (т. е. делаются доступными) кнопки "Открыть" и "Удалить", а также включается или выключается кнопка "Сохранить", в зависимости от статуса члена семьи ("просмотр" или "добавление"). Ниже приведено определение предиката.
predicates
onListboxSelectionChanged :
listControl::selectionChangedListener.
clauses
onListboxSelectionChanged(_Source):-
Person = getSelectedPerson(),
!,
if [FileName | _] = Person:pictures then
pictControl_ctl:drawPict(FileName)
else
pictControl_ctl:clear()
end if,
status_ctl:setText(Person:status),
view_ctl:setEnabled(true),
del_ctl:setEnabled(true),
save_ctl:setEnabled(toBoolean(Person:status <> "просмотр")).
onListboxSelectionChanged(_Source).
Замечание. Пользователи Commercial Edition, использующие imageControl, должны заменить строку pictControl_ctl:drawPict(FileName) следующим кодом:
FullName = string::concat(dbrel::imageFolder(), FileName),
imageControl_ctl:setImageFile(FullName)
Вместо pictControl_ctl:clear() следует написать
imageControl_ctl:setNoImage().
Сделаем включенным пункт меню ), добавим обработчик события вызова данной команды меню (см. п. 1.1.3 ) и определим его так, как показано ниже.
clauses
onFileNew(_Source, _MenuTag):-
mainExe::getFileName(StartPath, _Name),
FileName = vpiCommonDialogs::getFileName(
"*.txt", ["Текстовый файл", "*.txt"],
"Открыть базу данных", [], StartPath, _),
!,
Db = dbrel::new(FileName),
Db:load(),
dbrel::db := some(Db),
Form = familyForm::display(This),
familyForm::familyForm := Form,
Form:setText(fileName::getName(FileName)).
onFileNew(_Source, _MenuTag).
При выборе пункта меню ).
(рис 4.5) Окно "Открыть базу данных"
В нем пользователь должен выбрать текстовый файл, содержащий базу данных. После этого загружается база данных и открывается окно familyForm, в строку заголовка которого помещается имя файла. Кроме этого, запоминаются указатели на объекты базы данных и формы.
Упражнения
4.1. Подготовьте еще одну базу данных с информацией о какой-либо семье или династии. Текстовый файл, содержащий факты базы данных, поместите в директорию Exe проекта.
4.2. Создайте bmp-файлы с изображениями членов семьи (см. упр. 4.1) и текстовые файлы, содержащие их жизнеописания. Поместите их в папки Exe\images и Exe\descriptions, соответственно.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.