Добрый день, при написании программы в Prologe появляется строчка getSpiral(_, _, _, _) = []. Но данный предикат нигде не описан и естественно программа выдае ошибку. Можно уточнить, что это за предикат и где и как его необходимо описать |
Создание формы с вкладками
Создание формы
Ниже создается форма с владками, которая предназначена для использования в двух режимах — режиме просмотра информации о члене семьи и в режиме добавления сведений о члене семьи, информация о котором отсутствует в базе данных. В режиме просмотра не допускается изменение данных, в режиме добавления все поля доступны для ввода данных. В первом случае форма содержит три вкладки, во втором две.
Создадим форму tabForm. В редакторе формы выберем пользовательский элемент управления (Custom Control) в окне Controls, затем в окне выбора класса для элемента управления укажем имя класса tabControl. Растянем этот элемент управления на все поле формы (рис. 5.1 рис. 5.1).
Для всех якорей привязки выберем значение True. Закроем редактор формы и выберем команду компиляции пакета tabForm.pack.
Форма с вкладками открывается при нажатии на кнопку "Открыть" окна familyForm. Она отображает информацию о выделенном в списке члене семьи, поэтому предварительно следует найти и запомнить указатель на объект класса person, в котором хранится информация об этом человеке.
Откроем редактор формы familyForm, выделим кнопку "Открыть", перейдем на вкладку Events окна свойств и добавим обработчик события нажатия на кнопку ClickResponder. Ниже приведено определение предиката onViewClick.
predicates onViewClick : button::clickResponder. clauses onViewClick(_Source) = button::defaultAction:- Person = getSelectedPerson(), !, db:selectedPerson := some(Person), familyForm := This, Form = tabForm::display(getParent()), Form:setText(Person:legend()). onViewClick(_Source) = button::defaultAction.Листинг 5.1. Определение предиката onViewClick
Предикат getParent в данном случае возвращает указатель на класса taskWindow. Вместо него можно использовать предикат applicationWindow::get/0, который всегда возвращает указатель на объект класса taskWindow.
Итак, при нажатии на кнопку "Открыть" сначала находится указатель на объект выделенного элемента списка, затем он запоминается, далее запоминается указатель на объект окна familyForm, после этого открывается форма с вкладкамии и в ее строку заголовка записывается имя выделенного члена семьи.
Далее создаются страницы вкладок.
Создание страницы информации
Выделим папку tabForm дерева проекта, с помощью команды меню New In New Package откроем диалоговое окно Create Project Item, выделим в нем элемент Control, в поле Name напишем название страницы: infoPage. Нажмем кнопку Create. В строке Title таблицы свойств введем текст закладки: Информация. Разместим на странице следующие элементы управления (рис. 5.2 рис. 5.2):
надписи (Static Text) "Имя", "Фамилия", "Отец", "Мать";
поля редактирования (Edit Control):
Name: name_ctl; Text: <пустое поле>; Name: surname_ctl; Text: <пустое поле>;
групповой блок (Group Box):
Representation: Fact Variable; Name: sex_ctl; Text: Пол;
далее нужно выделить групповой блок и разместить в нем переключатель (Radio Button):
Name: male_ctl; Text: мужской; RadioState: checked;
затем нужно опять выделить групповой блок и разместить в нем еще один переключатель:
Name: female_ctl; Text: женский;
два выпадающих списка (List Button):
Name: father_ctl; Rows: 3; Name: mother_ctl; Rows: 3;
надпись (Static Text):
Representation: Fact Variable; Name: spouseText_ctl; Text: Супруги;
Name: spouses_ctl; MultiSelect: True;
пользовательский элемент управления (Custom Control):
Class: pictControl (или imageControl для пользователей CE);
четыре кнопки (Push Button):
Name: left_ctl; Text: <; Enabled: False; Name: right_ctl; Text: >; Enabled: False; Name: del_ctl; Text: -; Enabled: False; Visible: False; TooltipText: tip("Удалить"); Name: browse_ctl; Text: …; Visible: False.
По умолчанию свойство Representation групповых блоков (Group Box) и надписей (Static Text) имеет значение Variable. Для группового блока "Пол" и надписи "Супруги" его значение следует изменить на Fact Variable (см. выше).
Переключатель (Radio Button) может находиться в двух состояниях — checked (включен) и unchecked (выключен). Из набора переключателей, объединенных в одну группу, может быть включен только один переключатель.
Первые две кнопки используются для просмотра изображений. Третья кнопка предназначена для удаления изображения, четвертая кнопка — для добавления изображений. В режиме просмотра видимыми являются только две кнопки, предназначенные для просмотра изображений. Свойству Visible остальных кнопок устанавливается значение False (по умолчанию оно равно True). Для третьей кнопки создана подсказка (tooltip), которая появляется при наведении курсора на кнопку (аналогичные подсказки можно сделать для других элементов управления).
Для отображения родителей используются выпадающие списки, так как в режиме добавления в них помещаются списки членов семьи, из которых затем выбираются родители.
Надпись "Супруги" и список (супругов) становятся невидимыми в режиме просмотра, если в базе данных отсутствуют сведения о супругах члена семьи. Надпись меняется на "Супруг" или "Супруга", если член семьи имел (или имеет) ровно одного супруга или супругу.
Далее нужно закрыть редактор окна.
Аналогично следует создать в отдельных пакетах еще две страницы вкладок — treePage (Title: Деревья) и descrPage (Title: Описание). Эти страницы будут заполнены позднее.
В интерфейсе tabForm следует объявить свойство familyForm. Оно используется для хранения указателя на объект окна, из которого открывается форма с вкладками (может быть открыто несколько таких окон).
properties familyForm : familyForm.Листинг 5.2. Объявление свойства в интерфейсе tabForm
Теперь необходимо изменить определение конструктора new/1 в имплементации класса tabForm. В нем создаются страницы вкладок.
clauses new(Parent):- formWindow::new(Parent), generatedInitialize(), % familyForm := familyForm::familyForm, Page1 = tabPage::new(), Tab1 = infoPage::new(Page1:getContainerControl()), Page1:setText(Tab1:getText()), tabControl_ctl:addPage(Page1), if some(_) = familyForm:db:selectedPerson then Page2 = tabPage::new(), Tab2 = treePage::new(Page2:getContainerControl()), Page2:setText(Tab2:getText()), tabControl_ctl:addPage(Page2) end if, Page3 = tabPage::new(), Tab3 = descrPage::new(Page3:getContainerControl()), Page3:setText(Tab3:getText()), tabControl_ctl:addPage(Page3), infoPage := Tab1. facts familyForm : familyForm. infoPage : infoPage.Листинг 5.3. Изменение определения конструктора
Страница с деревьями отображается только в режиме просмотра сведений.
Заполнение страницы информации
Заполним первую страницу вкладок. В интерфейсе infoPage следует объявить свойство, которое хранит указатель на объект базы данных, а также предикат updatePicture, который инициирует перерисовку изображения при изменении размеров формы.
properties db : dbrel. predicates updatePicture: ().Листинг 5.4. Объявления в интерфейсе infoPage
В имплементации infoPage следует внести изменения в определение конструктора new, объявить необходимые факты-переменные и определить предикат updatePicture.
clauses new():- userControlSupport::new(), generatedInitialize(), db := familyForm::familyForm:db. facts db : dbrel := erroneous. % указатель на объект БД pictures : string* := []. % имена файлов с изображениями n : positive := 0. % количество изображений k : positive := 0. % номер текущего изображения clauses updatePicture():- pictControl_ctl:invalidate().Листинг 5.5. Основные параметры
Теперь следует добавить обработчик события ShowListener. Когда открывается окно, заполняются поля страницы.
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).Листинг 5.6. Определение предиката onShow
Ниже приведено определение предикатов, которые размещают сведения в элементах управления.
predicates setSex: (string Sex). setCheckedState: (radioButton). clauses setSex(dbrel::male):- !, setCheckedState(male_ctl). setSex(_):- setCheckedState(female_ctl). setCheckedState(RadioCtl):- RadioCtl:setRadioState(radioButton::checked). predicates setParent: (unsigned Id, listButton). clauses setParent(ParentId, ListCtl):- Person = db:getPerson(ParentId), !, ListCtl:addAt(0, Person:legend()), ListCtl:selectAt(0, true). setParent(_ParentId, _ListCtl). predicates setSpouses: (person). clauses setSpouses(Person):- L = [S:legend() || I in Person:spouses, S = db:getPerson(I)], L <> [], !, spouses_ctl:clearAll(), spouses_ctl:addList(L), spouseText_ctl:setText(spouseText(Person, L)). setSpouses(_Person):- spouses_ctl:setVisible(false), spouseText_ctl:setText(""). predicates spouseText: (person, string*) -> string. clauses spouseText(_Person, SpouseList) = "Супруги":- [_, _| _] = SpouseList, !. spouseText(Person, _SpouseList) = "Супруга":- dbrel::male = Person:sex, !. spouseText(_Person, _SpouseList) = "Супруг". predicates setPictures: (person). clauses setPictures(Person):- pictures := Person:pictures, n := list::length(pictures), right_ctl:setEnabled(toBoolean(n > 1)), [FileName | _] = pictures, !, pictControl_ctl:drawPict(FileName). setPictures(_Person). predicates setEnable: (string Status). clauses setEnable("просмотр"):- !, name_ctl:setReadOnly(), surname_ctl:setReadOnly(), sex_ctl:setEnabled(false). setEnable(_):- remove_ctl:setVisible(true), browse_ctl:setVisible(true), del_ctl:setVisible(true).Листинг 5.7. Заполнение полей страницы информации
Если окно открывается в режиме просмотра, то сведения, которые в нем отображаются, изменять нельзя (см. определение предиката setEnable/1).
Как отмечалось выше, если сведения о супругах отсутствуют в базе данных, то список не отображается (рис. 5.3 рис. 5.3).
Просмотр изображений
Для члена семьи в базе данных может храниться несколько изображений (см. п. 4.1.1 "Приложение "Родственные отношения"" ). Просмотр изображений выполняется с помощью кнопок ">" (вперед) и "<" (назад). Включение и выключение этих кнопок определяется количеством изображений.
Добавим обработчики событий нажатия на кнопки left_ctl и right_ctl. Определение этих предикатов приведено ниже.
clauses onLeftClick(_Source) = button::defaultAction:- k > 0, !, k := k - 1, pictControl_ctl:drawPict(list::nth(k, pictures)), if 0 = k then left_ctl:setEnabled(false) end if, if n – 2 = k, n > 1 then right_ctl:setEnabled(true) end if. onLeftClick(_Source) = button::defaultAction.Листинг 5.8. Определение предиката onLeftClick
clauses onRightClick(_Source) = button::defaultAction:- k < n - 1, !, k := k + 1, pictControl_ctl:drawPict(list::nth(k, pictures)), if n - 1 = k then right_ctl:setEnabled(false) end if, if 1 = k, n > 1 then left_ctl:setEnabled(true) end if. onRightClick(_Source) = button::defaultAction.Листинг 5.9. Определение предиката onRightClick
Наконец, в редакторе формы tabForm следует добавить обработчик событий SizeListener и определить его так, как показано ниже.
clauses onSize(_Source):- infoPage:updatePicture().Листинг 5.10. Обновление изображения при изменении размеров
Текстовый редактор
Ниже приводится пример использования класса sciLexer для просмотра и редактирования заметок о членах семьи.
Вторая страница вкладок будет заполнена позднее (в главе 6 "Деревья. Сводная таблица" ). Приступим к заполнению третьей страницы (рис. 5.4 рис. 5.4).
Откроем редактор страницы descrPage и разместим следующие элементы управления (см. рис. 5.4 рис. 5.4):
пользовательский элемент управления (Class: sciLexer; Border: True);
три кнопки:
Name: open_ctl; Text: Открыть; Name: save_ctl; Text: Сохранить; Name: saveInDb_ctl; Text: Записать.
Закроем редактор формы.
Откроем файл descrPage.pro и изменим определение конструктора new так, как показано ниже. Кроме этого, объявим факты-переменные для хранения указателя на объект базы данных, имени файла и указания на систему кодирования символов в этом файле (Unicode или нет).
clauses new():- userControlSupport::new(), generatedInitialize(), % db := familyForm::familyForm:db, postAction({ :- sciLexer_ctl:wrapMode := sciLexer_native::sc_wrap_word }). facts db : dbrel. filename : string := "". isUnicode : boolean := false.Листинг 5.11. Изменение определения конструктора
Предикат postAction используется в данном случае для того, чтобы после выведения текста к нему была применена операция автоматического переноса слова на следующую строку, если оно не помещается в поле окна целиком.
Далее следует добавить в редакторе окна descrPage обработчик события ShowListener и обработчики нажатия на кнопки "Открыть" и "Сохранить".
Ниже приведено определение предиката onShow. Когда открывается страница "Описание", в ней отображается текст из файла, указанного в базе данных.
clauses onShow(_Source, _Data):- some(Person) = db:selectedPerson, [FileName | _] = Person:descriptions, FilePath = dbrel::descriptionsFolder(), FullName = fileName::setPath(FileName, FilePath), try Text = file::readString(FullName, _) catch Error do stdio::writef("Error %. Unable to read from %.\n", Error, FullName), fail end try, !, sciLexer_ctl:text := Text. onShow(_Source, _Data).Листинг 5.12. Определение предиката onShow
Свойство text текстового редактора хранит текущий текст.
Текстовый редактор обладает большим количеством свойств (см. в Help страницу Interface sciLexerBase). Например, установить свойство "Только для чтения" можно следующим образом:
sciLexer_ctl:readOnly := true.
При нажатии кнопки "Открыть" открывается окно "Открыть файл". Содержимое выбранного в нем текстового файла отображается в окне.
clauses onOpenClick(_Source) = button::defaultAction:- StartPath = dbrel::descriptionsFolder(), FileName = vpiCommonDialogs::getFileName("*.txt", ["Текстовый файл", "*.txt", " Document Word", "*.doc"], "Открыть файл", [], StartPath, _), !, Text = file::readString(FileName, Mode), isUnicode := Mode, filename := FileName, sciLexer_ctl:text := Text. onOpenClick(_Source) = button::defaultAction.Листинг 5.13. Определение предиката onOpenClick
При нажатии кнопки "Сохранить" открывается окно "Сохранить файл". Содержимое поля записывается в выбранный файл.
clauses onSaveClick(_Source) = button::defaultAction:- Text = string::trim(sciLexer_ctl:text), Text <> "", StartPath = dbrel::descriptionsFolder(), FileName = vpiCommonDialogs::getFileName("*.txt", ["Текстовый файл", "*.txt", "Document Word", "*.doc"], "Сохранить как", [dlgfn_save], StartPath, _), !, file::writeString(Filename, Text, isUnicode), filename := FileName. onSaveClick(_Source) = button::defaultAction.Листинг 5.14. Определение предиката onSaveClick
Считать текст можно также следующим образом:
Text = sciLexer_ctl:getText().
Предикат обработки события нажатия на кнопку "Запомнить " будет определен позднее.
Упражнения
5.1. Создайте окно, содержащее два списка и кнопку. В первом списке должны быть перечислены названия родственных отношений: "родитель ", "брат " и т. д. Второй список должен быть пуст. Кроме этого, следует создать кнопку на форме familyForm, при нажатии на которую должно открываться это окно. После того, как в первом списке будет выделено название родственного отношения, во втором списке должен отображаться список соответствующих родственников выделенного (в окне familyForm) члена семьи.
5.2. Создайте окно для поиска в базе данных с помощью регулярных выражений (см. класс regEx). Регулярное выражение вводится в поле редактирования. Поиск ведется в строках вида "<имя> <фамилия> ". Результаты должны выводиться в списке.