. Никита Культин Санкт-Петербург «БХВ-Петербург» 2011
Никита Культин Санкт-Петербург «БХВ-Петербург» 2011

Никита Культин Санкт-Петербург «БХВ-Петербург» 2011

3 Оглавление ПРЕДИСЛОВИЕ. 1 ЧАСТЬ I. DELPHI XE. 3 ГЛАВА 1. СРЕДА РАЗРАБОТКИ DELPHI XE. 5 Установка. 5 Первое знакомство. 6 ГЛАВА 2. ПЕРВЫЙ ПРОЕКТ Начало работы Форма Компоненты Событие Процедура обработки события Редактор кода Система подсказок Шаблоны кода Справочная информация Сохранение проекта Структура проекта Компиляция Ошибки Предупреждения и подсказки Запуск программы Исключения Обработка исключения Внесение изменений Настройка приложения Установка приложения на другой компьютер. 50

4 IV Оглавление ГЛАВА 3. КОМПОНЕНТЫ Базовые компоненты Label Edit Button CheckBox RadioButton ComboBox ListBox Memo Timer Panel ControlBar SpeedButton StatusBar UpDown ProgressBar Image MainMenu OpenDialog SaveDialog Компоненты Vista TaskDialog FileOpenDialog и FileSaveDialog ЧАСТЬ II. ПРАКТИКУМ ПРОГРАММИРОВАНИЯ ГЛАВА 4. ГРАФИКА Графическая поверхность Карандаш и кисть Графические примитивы Текст Линия Ломаная линия Прямоугольник Многоугольник (полигон) Окружность и эллипс Дуга Сектор Точка

5 Оглавление V Битовые образы Мультипликация Движение Взаимодействие с пользователем Использование битовых образов ГЛАВА 5. МУЛЬТИМЕДИА Функция PlaySound Компонент MediaPlayer Воспроизведение MIDI Проигрыватель Audio CD Просмотр видеороликов Компонент Animate ГЛАВА 6. БАЗЫ ДАННЫХ База данных и СУБД Локальные и удаленные базы данных Структура базы данных Механизмы доступа к данным Компоненты доступа к данным Создание базы данных База данных Microsoft Access Доступ к данным Отображение данных Выбор информации из базы данных Работа с базой данных в режиме формы Загрузка строки соединения из INI-файла База данных Blackfish SQL Доступ к серверу Создание базы данных Доступ к базе данных Права пользователей База данных "Книги" Развертывание приложения работы с базой данных Blackfish SQL Server ГЛАВА 7. КОМПОНЕНТ ПРОГРАММИСТА Модуль компонента Тестирование модуля компонента Пакет компонентов Создание пакета компонентов

6 VI Оглавление Компиляция пакета компонентов Установка пакета компонентов Тестирование компонента Установка программы на другой компьютер Распространение компонента ГЛАВА 8. СПРАВОЧНАЯ ИНФОРМАЦИЯ Справочная система HTML Help Подготовка справочной информации Microsoft HTML Help Workshop Файл проекта Оглавление Идентификаторы разделов Компиляция Отображение справочной информации ГЛАВА 9. СОЗДАНИЕ УСТАНОВОЧНОГО ДИСКА Утилита InstallAware Новый проект Общая информация Программа и ее разработчик Требования к системе Компоненты Архитектура Возможности Файлы Ярлыки Интерфейс Диалоги Информация о программе и лицензионное соглашение Образ установочного диска ГЛАВА 10. ПРИМЕРЫ ПРОГРАММ Экзаменатор Требования к программе Файл теста Форма приложения Отображение иллюстрации Доступ к файлу теста Текст программы Запуск программы

7 Оглавление VII Сапер Правила и представление данных Форма Игровое поле Начало игры Игра Справочная информация Информация о программе Текст программы MP3-плеер Форма Регулятор громкости Перемещение окна Текст программы ПРИЛОЖЕНИЯ ПРИЛОЖЕНИЕ 1. СПРАВОЧНИК Форма Базовые компоненты Label Edit Button Memo RadioButton CheckBox ListBox ComboBox StringGrid Image Timer SpeedButton UpDown OpenDialog SaveDialog Animate MediaPlayer Компоненты доступа/манипулирования данными ADOConnection ADOTable ADODataSet

8 VIII Оглавление ADOQuery DataSource DBEdit, DBMemo, DBText DBGrid DBNavigator Графика PaintBox Canvas Pen Brush Цвет Функции Функции ввода и вывода Математические функции Функции преобразования Функции манипулирования датами и временем События Исключения ПРИЛОЖЕНИЕ 2. СОДЕРЖИМОЕ КОМПАКТ-ДИСКА ПРЕДМЕТНЫЙ УКАЗАТЕЛЬ

9 Предисловие Среда разработки Delphi является одним из популярнейших инструментов разработки прикладных программ. Она поддерживает так называемую быструю разработку, основанную на технологии визуального проектирования и событийного программирования, суть которой состоит в том, что среда разработки берет на себя большую часть рутины, оставляя программисту работу по созданию диалоговых окон (визуальное проектирование) и процедур обработки событий (событийное программирование). Производительность программиста при этом просто фантастическая! Delphi это среда быстрой разработки приложений (RAD-среда, от Rapid Application Development быстрая разработка приложений) на языке Delphi, в основе которого лежит хорошо знакомый многим программистам язык Pascal. Изначально, вплоть до седьмой версии, Delphi была ориентирована на разработку Win32-приложений. После того как Microsoft стала продвигать технологию.net, появилась Delphi 8 for The Microsoft.NET Framework среда разработки.net-приложений. Следующие версии Delphi выпускались в двух вариантах: для разработки Win32- и.net-приложений. Теперь программистам стала доступна очередная версия Delphi Embarcadero Delphi XE. Embarcadero новое имя выделенного из Borland подразделения (изначально оно называлось CodeGear), отвечающего за инструменты разработки приложений. Delphi XE существует в трех вариантах: Professional, Enterprise и Architect. Каждый комплект включает набор средств и компонентов, обеспечивающих разработку высокоэффективных приложений различного назначения, в том числе работы с базами данных InterBase, Blackfish SQL, Firebird, MySQL, Microsoft SQL Server, Oracle и др. Чем выше уровень пакета, тем больше возможностей он предоставляет программисту. Так, например, в Enterprise и Architect есть компоненты, позволяющие работать с удаленным сервером Blackfish SQL, а в Professional только с локальным. Среда Delphi XE доступна как отдельный инструмент разработки, а также как элемент Embarcadero RAD Studio XE. Delphi XE может работать в среде операционных систем Microsoft Windows XP Home или Professional (SP2 или SP3), Microsoft Windows Vista SP2, Microsoft Windows Server 2003 (SP1) или 2008, а также в Microsoft Windows 7. Особых требований, по современным меркам, к ресурсам компьютера среда не предъявляет: процессор должен быть класса Intel Pentium (или совместимый) с частотой 1,4 ГГц (рекомендуется 2 ГГц и выше), 1 Гбайт оперативной памяти (рекомендуется 2 Гбайт и больше), 3,75 Гбайт свободного места на жестком диске (в том числе 750 Мбайт для Microsoft.NET Framework и Microsoft.NET SDK).

10 2 Предисловие Книга, которую вы держите в руках, это не описание среды разработки или языка программирования, это руководство по программированию в Delphi XE, разработке Win32-приложений. В ней представлена технология визуального проектирования и событийного программирования, подробно рассмотрен процесс создания программы, показано назначение базовых компонентов, описан процесс создания справочной системы и образа установочного диска, уделено внимание вопросам программирования графики, мультимедиа, разработки программ работы с базами данных. Цель книги научить программировать, создавать программы различного назначения: от простых однооконных приложений до программ работы с базами данных. Следует обратить внимание, что хотя книга ориентирована на читателя, обладающего начальными знаниями и опытом в программировании, она вполне доступна и начинающим. Научиться программировать можно, только программируя, решая конкретные задачи. Поэтому чтобы получить максимальную пользу от книги, вы должны работать с ней активно. Изучайте листинги, старайтесь понять, как работают программы. Не бойтесь экспериментировать совершенствуйте программы, вносите в них изменения. Чем больше вы сделаете самостоятельно, тем большему научитесь!

11 ЧАСТЬ I DELPHI XE В данной части книги приведено краткое описание среды разработки Delphi XE; на примере программы "Конвертер" показан процесс разработки приложения; приведены описание и примеры использования базовых компонентов.

12 ГЛАВА 1 Среда разработки Delphi XE Установка Основным вариантом поставки RAD Studio является "установка с сервера (Download)": при покупке программист получает серийный номер и ссылку на программу активизации установки, а все необходимые для установки файлы загружаются с сервера Embarcadero Technologies. При желании программист может за отдельную плату заказать DVD-диск и в дальнейшем использовать его, например, для установки отсутствующих компонентов. Чтобы установить Delphi, надо с сайта компании Embarcadero Technologies загрузить пакет установки, представляющий собой ZIP-архив, распаковать его во временный каталог и запустить установщик (файл install_radstudio.exe). Delphi XE является.net-приложением. Поэтому установка начинается с проверки наличия на компьютере разработчика Microsoft.NET Framework 3.5 SP1 Redistributable Package, Microsoft Visual J# version 2.0 Redistributable Package, Microsoft Data Access Components (MDAC) 2.8, Microsoft Core XML Services (MSXML) 6.0 и Language Pack for Microsoft.NET Framework 2.0. Если какой-либо из перечисленных компонентов отсутствует, то он устанавливается. После этого начинается установка Delphi. Процесс установки обычный. Сначала на экране появляется окно лицензионного соглашения, затем окно Select Features, в котором программист может выбрать необходимые для работы компоненты (точнее, отказаться от установки ненужных). По умолчанию на компьютер устанавливаются все доступные компоненты и, если на жестком диске достаточно свободного места, то в окне Select Features лучше ничего не трогать. По окончании установки необходимо выполнить активацию продукта ввести в окне активации серийный номер. Серийный номер передается на сервер регистрации, который в ответ пересылает на компьютер программиста файл активации. На этом процесс установки можно считать законченным.

13 6 Часть I. Delphi XE Первое знакомство Чтобы запустить Delphi XE, надо сделать щелчок на кнопке Пуск и в меню Все программы выбрать команду Embarcadero RAD Studio XE Delphi XE. Затем, чтобы начать работу над новым проектом, надо в меню File выбрать команду New VCL Forms Application Delphi. Окно среды Delphi XE в начале работы над новым проектом приведено на рис В заголовке окна отображается имя проекта, над которым в данный момент работает программист. В верхней части окна находится строка меню и область отображения панелей инструментов. Рис Окно среды Delphi XE в начале работы над новым проектом Центральную часть окна среды Delphi XE занимает окно конструктора (дизайнера) формы (рис. 1.2). В нем находится форма заготовка окна приложения (окно программы во время его разработки принято называть формой). За окном конструктора формы находится окно редактора кода (рис. 1.3). Доступ к окну редактора кода можно получить, сделав щелчок кнопкой мыши на ярлыке Code или нажав клавишу <F12> (повторное нажатие <F12> или щелчок кнопкой мыши на ярлыке Design активизирует конструктор формы).

14 Глава 1. Среда разработки Delphi XE 7 Рис Окно конструктора формы Слева от окна дизайнера формы находится окно Object Inspector (рис. 1.4). Вкладка Properties этого окна используется для редактирования значений свойств объектов. Свойство (property) это характеристика объекта (формы, командной кнопки, поля редактирования и т. д.). Свойства определяют вид объекта, его положение и поведение. Например, свойство Caption формы определяет текст, который отображается в ее заголовке, а свойства Width и Height ее размеры (ширину и высоту). Справа от названия свойств указаны их значения. Свойства по функциональному признаку объединены в группы (названия групп выделены цветом). Так, например, свойства, определяющие внешний вид объекта, объединены в группу Visual. Программист может изменить способ отображения свойств, выбрав в контекстном меню вкладки Properties команду Arrange by Name (В алфавитном порядке) или Arrange by Category (По категориям). На вкладке Events окна Object Inspector перечислены события, которые может воспринимать объект.

15 8 Часть I. Delphi XE Рис Окно редактора кода На вкладках палитры компонентов (окно Tool Palette) находятся компоненты (рис. 1.5). Компонент это элемент пользовательского интерфейса или объект, реализующий некоторую функциональность. Например, на вкладке Standard находятся компоненты, обеспечивающие взаимодействие с пользователем (Label поле отображения текста; Edit поле редактирования; Button командная кнопка и др.), а на вкладке dbgo компоненты доступа к базам данных. Компоненты, обеспечивающие взаимодействие с пользователем, объединены в так называемую VCL-библиотеку (Visual Components Library). Поэтому приложения, использующие эти компоненты, называются VCL-приложениями (VCL Forms Application). Вспомните, чтобы начать работу над новым проектом (новой программой), надо в меню File выбрать команду New VCL Forms Application.

16 Глава 1. Среда разработки Delphi XE 9 Рис В окне Object Inspector на вкладке Properties перечислены свойства объекта, а на вкладке Events события, на которые объект может реагировать Рис Вкладка Standard содержит компоненты, обеспечивающие взаимодействие пользователя c программой В окне Project Manager (рис. 1.6) отображается структура проекта, над которым в данный момент идет работа.

17 10 Часть I. Delphi XE Рис В окне Project Manager отображается структура проекта Если какое-либо из перечисленных окон на экране отсутствует, то для того чтобы его увидеть, надо в меню View выбрать соответствующую команду.

18 ГЛАВА 2 Первый проект Процесс разработки программы в Delphi рассмотрим на примере создадим приложение (так принято называть прикладную программу), с помощью которого можно пересчитать цену из долларов в рубли (рис. 2.1). Рис Окно программы "Конвертер" Начало работы Чтобы начать работу над новым приложением, нужно в меню File выбрать команду New VCL Forms Application - Delphi. Форма Работа над приложением начинается с создания стартовой формы главного окна программы. Сначала нужно установить требуемые значения формы, затем поместить на форму необходимые компоненты (поля ввода информации, командные кнопки, поля отображения текста и др.).

19 12 Часть I. Delphi XE Настройка формы (а также компонентов) осуществляется путем изменения значений свойств. Свойства объекта (формы, компонента) определяют его вид и поведение. Например, свойство Caption определяет текст заголовка окна, а свойство Position положение окна в момент появления на экране. Основные свойства формы (объекта TForm) приведены в табл Таблица 2.1. Свойства формы (объекта TForm) Свойство Name Caption Width Height Position Top Left BorderStyle BorderIcons Icon Color Описание Имя (идентификатор) формы. Используется для доступа к форме, ее свойствам и методам, а также для доступа к компонентам формы Текст заголовка Ширина формы Высота формы Положение окна в момент первого его появления на экране (pocenterscreen в центре экрана; poownerformcenter в центре родительского окна; podesigned положение окна определяют значения свойств Top и Left) Расстояние от верхней границы формы до верхней границы экрана Расстояние от левой границы формы до левой границы экрана Вид границы. Граница может быть обычной (bssizeable), тонкой (bssingle) или вообще отсутствовать (bsnone). Если у окна обычная граница, то во время работы программы пользователь может с помощью мыши изменить размер окна. Изменить размер окна с тонкой границей нельзя. Если граница отсутствует, то на экран во время работы программы будет выведено окно без заголовка. Положение и размер такого окна во время работы программы изменить нельзя Кнопки управления окном. Значение свойства определяет, какие кнопки управления окном будут доступны пользователю во время работы программы. Значение свойства задается путем присвоения значений уточняющим свойствам bisystemmenu, biminimaze, bimaximaze и bihelp. Свойство bisystemmenu определяет доступность кнопки системного меню (значок в заголовке окна), biminimaze кнопки Свернуть, bimaximaze кнопки Развернуть, bihelp кнопки вывода справочной информации Значок в заголовке диалогового окна, обозначающий кнопку вывода системного меню Цвет фона. Цвет можно задать, указав название цвета или привязку к текущей цветовой схеме операционной системы. Во втором случае цвет определяется текущей цветовой схемой, выбранным компонентом привязки и меняется при изменении цветовой схемы операционной системы

20 Глава 2. Первый проект 13 Таблица 2.1 (окончание) Свойство Font Описание Шрифт. Шрифт, используемый по умолчанию компонентами, находящимися на поверхности формы. Изменение свойства Font формы приводит к автоматическому изменению свойства Font компонента, располагающегося на поверхности формы. То есть компоненты наследуют свойство Font от формы (имеется возможность запретить наследование) Для изменения значений свойств объектов используется вкладка Properties окна Object Inspector. В левой колонке этой вкладки перечислены свойства объекта, выбранного в данный момент, в правой указаны значения свойств. Имя выбранного объекта отображается в верхней части окна Object Inspector. На вкладке Properties свойства объединены в группы по функциональному признаку (названия групп выделены цветом). Например, группа Visual содержит свойства, определяющие вид объекта (для формы заголовок, цвет фона, вид границы), а группа Layout свойства, определяющие положение объекта (для формы координаты левого верхнего угла). Некоторые свойства, например Width и Height, отображаются в нескольких группах (Visual и Layout). Программист может изменить способ отображения свойств в окне Object Inspector. Например, чтобы свойства отображались в алфавитном порядке, в контекстном меню вкладки Properties надо выбрать команду Arrange by Name. Чтобы в заголовке формы вместо Form1 появилось название программы текст Конвертер, следует изменить значение свойства Caption. Чтобы это сделать, надо в окне Object Inspector щелкнуть левой кнопкой мыши в строке свойства (в результате будет выделено текущее значение свойства и появится курсор), ввести текст Конвертер и нажать клавишу <Enter> (рис. 2.2). Аналогичным образом можно установить значения свойств Height и Width, которые определяют высоту и ширину формы. Размер формы, а также размер других компонентов, задают в пикселах (точках). Свойствам Height и Width надо присвоить значения 215 и 366 соответственно. Размер формы можно изменить и с помощью мыши, точно так же, как и любого окна, т. е. путем перемещения границы. По окончании перемещения границы значения свойств Height и Width будут соответствовать установленному размеру формы. Положение окна на экране в момент его первого появления соответствует положению формы, заданному во время разработки программы. Положение можно задать, установив значение свойств Top (отступ от верхней границы экрана) и Left (отступ от левой границы экрана) или задав значение свойства Position. При выборе некоторых свойств, например BorderStyle, справа от текущего значения свойства появляется значок раскрывающегося списка. Очевидно, что значение таких свойств можно задать путем выбора из списка (рис. 2.3).

21 14 Часть I. Delphi XE Рис Изменение значения свойства Caption путем ввода нового значения Рис Установка значения свойства путем выбора из списка Некоторые свойства являются сложными, т. е. их значение определяется совокупностью значений других (уточняющих) свойств. Например, свойство BorderIcons определяет кнопки управления окном, которые будут доступны во время работы программы. Значения этого свойства определяются совокупностью значений свойств bisystemmenu, biminimize, bimaximize и bihelp, каждое из которых, в свою очередь, определяет наличие соответствующей командной кнопки в заголовке окна во время работы программы. Перед именами сложных свойств стоит значок "+", в результате щелчка на котором раскрывается список уточняющих свойств (рис. 2.4). Значение уточняющего свойства можно задать обычным образом (ввести значение в поле редактирования или выбрать в списке). В результате выбора некоторых свойств, например Font, в поле значения свойства отображается кнопка, на которой изображены три точки. Это значит, что задать значение свойства можно в дополнительном диалоговом окне, которое появится в результате щелчка на этой кнопке. Например, значение свойства Font можно задать путем ввода значений уточняющих свойств (Name, Size, Style и др.), а можно воспользоваться стандартным диалоговым окном Шрифт, которое появится в результате щелчка на кнопке с тремя точками (рис. 2.5). В табл. 2.2 приведены значения свойств стартовой формы программы "Конвертер". Значения остальных свойств формы оставлены без изменения и поэтому в таблице не представлены. Обратите внимание, в именах некоторых свойств есть точка. Это значит, что это значение уточняющего свойства.

22 Глава 2. Первый проект 15 Рис Изменение значения уточняющего свойства Рис Чтобы задать свойства шрифта, щелкните на кнопке с тремя точками Свойство Значение Комментарий Таблица 2.2. Значения свойств стартовой формы Caption Конвертер Height 215 Width 366 BorderStyle bssingle Тонкая граница формы. Во время работы программы пользователь не сможет изменить размер окна путем захвата и перемещения его границы Position podesktopcenter Окно программы появится в центре рабочего стола BorderIcons. bimaximize Font.Name False Tahoma В заголовке окна не отображать кнопку Развернуть Font.Size 10 После того как будут установлены значения свойств формы, она должна выглядеть так, как показано на рис Теперь на форму надо добавить компоненты.

23 16 Часть I. Delphi XE Рис Форма после изменения значений ее свойств Компоненты Поля редактирования, поля отображения текста, командные кнопки, списки, переключатели и другие элементы, обеспечивающие взаимодействие программы с пользователем, называют компонентами пользовательского интерфейса. Компоненты, которые программист может использовать в процессе разработки программы, находятся в палитре компонентов (Tool Palette). На вкладках Standard, Additional и Win32 располагаются часто используемые компоненты пользовательского интерфейса. Программа "Конвертер" для пересчета цены из долларов в рубли должна получить от пользователя цену в долларах и курс. Для ввода данных с клавиатуры предназначен компонент Edit. Поэтому на форму разрабатываемого приложения нужно поместить два компонента Edit. Чтобы на форму добавить компонент Edit, надо: 1. В палитре компонентов (окно Tool Palette) раскрыть вкладку Standard. Сделать щелчок на значке компонента Edit (рис. 2.7). Здесь следует обратить внимание, что в палитре компонентов, рядом со значком указывается тип компонента, а не его название. 2. Установить указатель мыши в ту точку формы, в которой должен быть левый верхний угол компонента. 3. Сделать щелчок левой кнопкой мыши. В результате на форме появляется поле редактирования компонент Edit (рис. 2.8). Каждому добавленному компоненту среда разработки присваивает имя, которое состоит из названия компонента и его порядкового номера. Например, первый компонент Edit получает имя Edit1, второй Edit2. Программист путем изменения значения свойства Name может поменять имя компонента. Однако в простых программах имена компонентов, как правило, не изменяют.

24 Глава 2. Первый проект 17 Рис Выбор компонента в палитре (компонент Edit поле редактирования) Рис Результат добавления на форму компонента Edit Основные свойства компонента Edit приведены в табл Таблица 2.3. Свойства компонента Edit (объект типа TEdit) Свойство Name Text Left Top Height Описание Имя компонента. Используется для доступа к компоненту и его свойствам Текст, который находится в поле редактирования Расстояние от левой границы компонента до левой границы формы Расстояние от верхней границы компонента до верхней границы формы Высота компонента

25 18 Часть I. Delphi XE Таблица 2.3 (окончание) Свойство Width Font ParentFont MaxLength TabOrder Описание Ширина компонента Шрифт, используемый для отображения текста в поле компонента Признак наследования шрифта от формы. Если значение свойства равно True, то для отображения текста в поле компонента используется шрифт формы Количество символов, которое можно ввести в поле редактирования. Если значение свойства равно нулю, ограничения на количество символов нет Определяет порядок перемещения фокуса (курсора) с одного элемента управления на другой в результате нажатия клавиши <Tab> На рис. 2.9 приведен вид формы после добавления двух полей редактирования. Один из компонентов выбран (выделен) помечен маленькими кружками. Свойства выбранного компонента отображаются в окне Object Inspector. Чтобы увидеть и, если надо, изменить свойства другого компонента, нужно этот компонент выбрать щелкнуть левой кнопкой мыши на изображении компонента или выбрать его имя в раскрывающемся списке, который находится в верхней части окна Object Inspector (рис. 2.10). Компонент, свойства которого надо изменить, можно выбрать и в окне Structure (рис. 2.11). Рис Форма с двумя компонентами Значения свойств, определяющих размер и положение компонента на поверхности формы, можно изменить с помощью мыши. Чтобы изменить положение компонента, необходимо установить курсор мыши на его изображение, нажать левую кнопку мыши и, удерживая ее нажатой, переместить компонент в нужную точку формы. Во время перемещения компонента (рис. 2.12) отображаются текущие значения координат левого верхнего угла компонента (значения свойств Left и Top).

26 Глава 2. Первый проект 19 Рис Выбор компонента в окне Object Inspector Рис Выбор компонента в окне Structure Рис Отображение значений свойств Left и Тор при изменении положения компонента Для того чтобы изменить размер компонента, необходимо сделать щелчок на его изображении (в результате чего компонент будет выделен), установить указатель мыши на один из маркеров, помечающих границу компонента, нажать левую кнопку мыши и, удерживая ее нажатой, изменить положение границы компонента. Во время изменения размера компонента отображаются его текущие размеры: ширина и высота (значения свойств Width и Height) (рис. 2.13). В табл. 2.4 приведены значения свойств компонентов Edit1 и Edit2 (прочерк показывает, что значением свойства Text является пустая строка). Значения остальных свойств компонентов Edit оставлены без изменения, и поэтому в таблице не показаны. Компонент Edit1 предназначен для ввода курса, Edit2 цены. Так как значения свойства Font компонентов Edit не указаны явно, то во время работы программы текст в полях редактирования будет отображаться шрифтом, заданным

27 20 Часть I. Delphi XE для формы. Компонент Edit, как и другие компоненты, наследует значение свойства Font от своего родителя объекта, на поверхности которого он находится. Поэтому если изменить значение свойства Font формы, автоматически изменится значение свойства Font компонентов, находящихся на форме. Если требуется, чтобы текст в поле компонента отображался другим шрифтом, нужно явно задать значение свойства Font этого компонента. Чтобы запретить автоматическое изменение значения свойства Font компонента при изменении свойства Font формы, надо свойству ParentFont компонента присвоить значение False. Рис Отображение значений свойств Width и Height при изменении размера компонента Форма программы "Конвертер" после настройки компонентов Edit приведена на рис Рис Форма после настройки компонентов Edit Таблица 2.4. Значения свойств компонентов Edit Компонент Свойство Значение Edit1 Top 64 Left 22 Text - TabOder 0

28 Глава 2. Первый проект 21 Таблица 2.4 (окончание) Компонент Свойство Значение Edit2 Top 52 Left 22 Text - TabOder 1 Отображение текста на поверхности формы обеспечивает компонент Label. В рассматриваемой программе текст отображается слева от полей редактирования (информация о назначении полей ввода). Результат расчета также отображается в окне программы. Поэтому в форму надо добавить три компонента Label (рис. 2.15). Добавляются компоненты Label на форму точно так же, как и поля редактирования (компонент Edit). Основные свойства компонента Label перечислены в табл Рис Компонент Label поле отображения текста Таблица 2.5. Свойства компонента Label Свойство Name Caption Описание Имя компонента. Используется для доступа к компоненту Отображаемый текст

29 22 Часть I. Delphi XE Таблица 2.5 (окончание) Свойство Font ParentFont AutoSize Left Top Height Width WordWrap Описание Шрифт, используемый для отображения текста Признак наследования характеристик шрифта от объекта (формы), на котором компонент находится Признак автоматического изменения размера компонента при изменении текста, отображаемого в поле компонента Расстояние от левой границы поля вывода до левой границы формы Расстояние от верхней границы поля вывода до верхней границы формы Высота поля вывода Ширина поля вывода Признак того, что слова, которые не помещаются в текущей строке, автоматически переносятся на следующую строку (значение свойства AutoSize должно быть False) Если поле компонента Label должно содержать несколько строк текста, то перед тем как изменить значение свойства Caption, сначала надо присвоить свойству AutoSize значение False, а свойству WordWrap True. Затем нужно установить требуемый размер компонента (с помощью мыши или вводом значений свойств Width и Height) и только после этого ввести значение свойства Caption. На форму разрабатываемого приложения надо добавить три компонента Label. В полях Label1 и Label2 отображается информация о назначении полей ввода, поле Label3 используется для вывода результата расчета. Значения свойств компонентов Label приведены в табл Таблица 2.6. Значения свойств компонентов Label Компонент Свойство Значение Label1 Left 20 Top 30 Caption Курс Label2 Left 20 Top 60 Caption Цена

30 Глава 2. Первый проект 23 Таблица 2.6 (окончание) Компонент Свойство Значение Label3 Left 16 Top 152 AutoSize False Width 297 Height 24 Caption - После настройки компонентов Label форма разрабатываемого приложения должна выглядеть так, как показано на рис Рис Вид формы после настройки полей отображения текста Последнее, что надо сделать на этапе создания формы, добавить на форму две командные кнопки: Пересчет и Завершить. Назначение этих кнопок очевидно. Командная кнопка, компонент Button, добавляется на форму точно так же, как и другие компоненты. Значок компонента Button находится на вкладке Standard (рис. 2.17). Основные свойства компонента Button приведены в табл Таблица 2.7. Свойства компонента Button Свойство Name Caption Enabled Описание Имя компонента. Используется для доступа к компоненту и его свойствам Текст на кнопке Признак доступности кнопки. Кнопка доступна (программа реагирует на ее нажатие), если значение свойства равно True, и не доступна, если значение свойства равно False

31 24 Часть I. Delphi XE Таблица 2.7 (окончание) Свойство Left Top Height Width TabOrder Описание Расстояние от левой границы кнопки до левой границы формы Расстояние от верхней границы кнопки до верхней границы формы Высота кнопки Ширина кнопки Определяет порядок перемещения фокуса (курсора) с одного элемента управления на другой в результате нажатия клавиши <Tab> Рис Командная кнопка компонент Button После того как на форму будут добавлены кнопки, нужно выполнить их настройку. Значения свойств компонентов Button приведены в табл. 2.8, окончательный вид формы показан на рис Таблица 2.8. Значения свойств компонентов Button Свойство Компонент Button1 Button2 Left Top Width 75 75

32 Глава 2. Первый проект 25 Таблица 2.8 (окончание) Свойство Компонент Button1 Button2 Height Caption Пересчет Завершить Рис Окончательный вид формы программы "Конвертер" Завершив работу по созданию формы, можно приступить к программированию созданию процедур обработки событий. Событие Вид созданной формы подсказывает, как работает программа. Очевидно, что пользователь должен ввести в поля редактирования исходные данные и сделать щелчок на кнопке Пересчет. Щелчок на изображении командной кнопки это пример того, что называется событием. Событие (event) это то, что происходит во время работы программы. У каждого события есть имя. Например, щелчок кнопкой мыши это событие Click, двойной щелчок мышью событие DblClick. В табл. 2.9 приведены некоторые события, возникающие в результате действий пользователя. Таблица 2.9. События Событие Click DblClick Описание Щелчок кнопкой мыши Двойной щелчек кнопкой мыши

33 26 Часть I. Delphi XE Таблица 2.9 (окончание) Событие MouseDown MouseUp MouseMove KeyPress KeyDown KeyUp Create Paint Enter Exit Описание Нажатие кнопки мыши Отпускание нажатой кнопки мыши Перемещение указателя мыши Нажатие клавиши клавиатуры Нажатие клавиши клавиатуры. События KeyDown и KeyPress это чередующиеся, повторяющиеся события, которые происходят до тех пор, пока не будет отпущена удерживаемая клавиша (в этот момент происходит событие KeyUp) Отпускание нажатой клавиши клавиатуры Создание объекта (формы, элемента управления). Процедура обработки этого события обычно используется для инициализации переменных, выполнения подготовительных действий Событие происходит при появлении окна на экране в начале работы программы, после появления части окна, которая, например, была закрыта другим окном Получение элементом управления фокуса Потеря элементом управления фокуса Следует понимать, что одни и те же действия, но выполненные над различными объектами, вызывают разные события. Например, щелчок (событие Click) на кнопке Пересчет и щелчок на кнопке Завершить это два разных события. Процедура обработки события Реакцией на событие должно быть какое-либо действие. В Delphi реакция на событие реализуется как процедура обработки события. Таким образом, для того чтобы программа выполняла некоторую работу в ответ на действия пользователя, программист должен написать процедуру обработки соответствующего события. Методику создания процедуры обработки события рассмотрим на примере обработки события Click, которое возникает в результате щелчка на кнопке Пересчет. Чтобы приступить к созданию процедуры обработки события, сначала надо выбрать компонент, для которого создается процедура обработки события. Для этого в окне конструктора формы надо сделать щелчок левой кнопкой мыши на нужном компоненте (компонент можно выбрать также в раскрывающемся списке, который находится в верхней части окна Object Inspector). Затем в окне Object Inspector нужно открыть вкладку Events.

34 Глава 2. Первый проект 27 В левой колонке вкладки Events (рис. 2.19) перечислены события, которые может воспринимать выбранный компонент. Строго говоря, на вкладке Events указаны не события, а свойства, значением которых являются имена процедур обработки соответствующих событий. Так, например, значением свойства OnClick является имя процедуры обработки события Click. Рис На вкладке Events перечислены события, которые может воспринимать компонент Для того чтобы создать процедуру обработки события, нужно на вкладке Evens выбрать событие (сделать щелчок мышью на его имени), ввести имя процедуры обработки события в ставшее доступным поле редактирования (рис. 2.20) и нажать клавишу <Enter>. В результате этих действий в текст программы (в модуль формы) будет добавлена процедура обработки события и станет доступным окно редактора кода (рис. 2.21), в котором можно набирать инструкции процедуры обработки события. Следует обратить внимание на то, что формируемое средой разработки полное имя процедуры обработки события состоит из двух частей. Первая часть идентифицирует форму, вторая представляет собой непосредственно имя процедуры. Согласно принятому соглашению имя процедуры обработки должно содержать информацию о компоненте, реагирующем на событие, и событии. Например, Button1Click это имя процедуры обработки события Click на компоненте Button1. Существует и другой способ создания процедуры обработки события. Сначала на вкладке Events надо выбрать событие, процедуру обработки которого необходимо создать, затем сделать двойной щелчок левой кнопкой мыши в поле редактирования, которое находится справа от имени события. В результате имя процедуры обработки события сформирует Delphi (имя процедуры обработки события образу-

35 28 Часть I. Delphi XE ется путем объединения имени компонента, для которого создается процедура обработки события, и имени события, например Button1Click). Рис Рядом с именем события надо ввести имя процедуры обработки события Рис Шаблон процедуры обработки события В листинге 2.1 приведен текст процедуры обработки события Click на кнопке Пересчет. Обратите внимание на то, как представлена программа. Ее общий вид соответствует тому, как она выглядит в окне редактора кода: ключевые слова выделены полужирным, комментарии курсивом (выделение выполняет редактор кода). Кроме того, инструкции программы набраны с отступами в соответствии с правилами хорошего стиля программирования.

36 Глава 2. Первый проект 29 Листинг 2.1. Обработка события Click на кнопке Пересчет procedure TForm1.Button1Click(Sender: TObject); var usd: real; // цена в долларах k: real; // курс rub: real; // цена в рублях // получить данные из полей редактирования k := StrToFloat(Edit1.Text); usd := StrToFloat(Edit2.Text); // пересчитать цену из долларов в рубли rub := usd * k; // вывести результат расчета в поле Label3 Label3.Caption := FloatToStrF(usd,ffGeneral, 6,2) + ' $ = ' + FloatToStrF(rub,ffCurrency,6,2); Процедура TForm1.Button1Click пересчитывает цену из долларов в рубли и выводит результат в поле Label3. Исходные данные (курс и цена в долларах) вводятся из полей редактирования Edit1 и Edit2 путем обращения к свойству Text. Значением свойства Text является строка, которая находится в поле редактирования. Свойство Text строкового типа, поэтому для преобразования строки в число используется функция StrToFloat. Следует обратить внимание, что функция StrToFloat возвращает результат только в том случае, если строка, переданная функции в качестве параметра, действительно является изображением числа в правильном формате, что предполагает использование запятой (при стандартной для России настройке операционной системы) в качестве разделителя целой и дробной частей числа. Вычисленное значение цены в долларах выводится в поле Label3 путем присваивания значения свойству Caption. Для преобразования числа в строку (свойство Caption строкового типа) используется функция FloatToStrF. У функции FloatToStr четыре параметра: первый значение, которое надо преобразовать в строку; второй формат (ffgeneral обычное число, ffcurrency финансовый); третий и четвертый задают соответственно общее количество цифр и количество цифр дробной части. ЗАМЕЧАНИЕ Для преобразования строки в целое число и целого числа в строку используйте соответственно методы-функции StrToInt и IntToStr.

37 30 Часть I. Delphi XE В листинге 2.2 приведена процедура обработки события Click на кнопке Завершить (создается она точно так же, как и процедура обработки события Click для кнопки Пересчет). В результате щелчка на кнопке Завершить программа должна завершить работу. Чтобы это произошло, надо закрыть окно программы (форму). Делает это метод Close. Листинг 2.2. Обработка события Click на кнопке Завершить procedure TForm1.Button2Click(Sender: TObject); Form1.Close; // закрыть окно Редактор кода В процессе набора текста программы редактор кода автоматически выделяет элементы программы: полужирным ключевые слова языка программирования, цветом константы, курсивом комментарии. Это делает текст программы выразительным, облегчает восприятие ее структуры. Кроме того, редактор "на лету" проверяет программу на наличие синтаксических ошибок и в случае обнаружения ошибки подчеркивает ее красной волнистой линией (сообщение об обнаруженной ошибке отображается в окне Structure). В процессе разработки программы часто возникает необходимость переключения между окном редактора кода и окном конструктора формы. Выбрать нужное окно можно щелчком на ярлыке Code (редактор кода) или Design (редактор формы). Для переключения между этими окнами удобно использовать клавишу <F12>. Система подсказок Во время набора текста программы редактор кода автоматически выводит подсказки. Например, после набора имени функции редактор кода выводит список ее параметров. Параметр, который в данный момент вводит программист, в подсказке выделяется полужирным шрифтом. Например, если набрать слово StrToFloat, которое является именем функции преобразования строки в дробное число, и открывающую скобку, то на экране появится окно, в котором будут перечислены параметры функции (рис. 2.22). Редактор также выводит список свойств и методов текущего объекта и позволяет выбрать в нем нужное свойство или метод. Например, если в окне редактора кода набрать Edit1 (имя компонента, поля редактирования) и точку, то на экране появится список свойств и методов класса TEdit (рис. 2.23). Программисту остается только выбрать в списке нужное свойство или метод (быстро перейти к нужному

38 Глава 2. Первый проект 31 элементу списка можно, нажав клавишу, соответствующую первому символу этого элемента), и нажать клавишу <Enter>. Рис Пример подсказки список параметров функции Рис Редактор кода автоматически выводит список свойств и методов текущего объекта

39 32 Часть I. Delphi XE Шаблоны кода В процессе набора текста программы можно использовать шаблоны кода (Code Templates). Шаблон кода это инструкция программы, записанная в общем виде. Например, простейший шаблон инструкции if выглядит так: if then Редактор кода предоставляет программисту большой набор шаблонов: объявления классов, функций, инструкций выбора (if, switch), циклов (for, while). Для некоторых инструкций, например if и while, есть несколько вариантов шаблонов. Часто используемые шаблоны кода редактор вставляет в текст программы автоматически, как только программист наберет ключевое слово. Например, если в окне редактора кода набрать if и пробел, то в текст будет вставлен шаблон if then, причем курсор останется после if (после ввода условия, чтобы установить курсор после then, надо нажать клавишу <Enter> или <Tab>). Все доступные шаблоны кода отображаются в окне Templates (рис. 2.24). Чтобы вставить шаблон в текст программы, надо сделать щелчок на имени шаблона. Рис В окне Templates отображаются шаблоны кода Обратите внимание, что по умолчанию окно шаблонов не отображается, и чтобы оно появилось на экране, надо в меню View выбрать команду Templates. Доступ к списку шаблонов (рис. 2.25) можно получить, нажав комбинацию клавиш <Ctrl>+<J>. Выбрать шаблон можно обычным образом (прокручивая список) или ввести его имя (например, шаблон if then называется if, а шаблон if

40 Глава 2. Первый проект 33 then end ifb). Выбрав в списке шаблон, надо нажать клавишу <Enter>, шаблон будет вставлен в текст программы. Рис Чтобы получить доступ к списку шаблонов, нажмите клавиши <Ctrl>+<J> Справочная информация Во время набора программы можно получить справку о конструкции языка, типе данных, процедуре или функции. Быстро получить доступ к нужному разделу справочной информации можно, набрав в окне редактора кода ключевое слово и нажав клавишу <F1>. Например, чтобы получить справку о функции FloatToStrF, надо в окне редактора кода набрать ее имя и нажать клавишу <F1>. Иногда описанный способ доступа к справочной информации не дает результата (например, если неправильно указано имя функции). В этом случае надо в меню Help выбрать команду Delphi Help, затем в появившемся окне отображения справочной информации выбрать вкладку Index и в поле Look for ввести ключевое слово (или несколько первых букв слова). В результате будет сформирован список разделов, связанных с введенным словом. Сохранение проекта Проект это набор файлов, используя которые, компилятор создает exe-файл. В простейшем случае проект образуют: файл описания проекта (dproj-файл), главный модуль (dpr-файл), файл описания формы (dfm-файл), модуль формы (pasфайл) и файл ресурсов (res-файл).

41 34 Часть I. Delphi XE Рис Сохранение модуля формы Рис Сохранение проекта

42 Глава 2. Первый проект 35 Чтобы сохранить проект, нужно в меню File выбрать команду Save Project As. Если проект еще ни разу не был сохранен, то сначала на экране появляется окно Save Unit As. В этом окне (рис. 2.26) надо выбрать папку, предназначенную для проектов Delphi (по умолчанию это C:\Users\UserName\Documents\RAD Studio\Projects, где UserName имя пользователя в системе), создать в ней папку для сохраняемого проекта, открыть созданную папку и в поле Имя файла ввести имя модуля (обычно в качестве имени модуля указывают имя формы, например MainForm) и нажать кнопку Сохранить. Затем в появившемся окне Save Project As (рис. 2.27) надо ввести имя проекта. Обратите внимание на то, что имя проекта определяет имя exe-файла, который будет создан компилятором. Структура проекта Как уже говорилось, проект представляет собой совокупность файлов, которые используются компилятором для генерации выполняемого файла. Основу проекта образуют файл главного модуля (dpr-файл) и один или несколько модулей (для каждой формы Delphi создает отдельный модуль pas-файл). Помимо файла проекта и модулей в процессе компиляции используются: файл ресурсов (res-файл), файлы описания форм (отдельный dfm-файл для каждой формы), а также файл конфигурации (cfg-файл). Общая информация о проекте хранится в dproj-файле. Файл главного модуля (чтобы его увидеть, надо в меню Project выбрать команду View Source) содержит инструкции, обеспечивающие инициализацию приложения. В качестве примера в листинге 2.3 приведен главный модуль приложения "Конвертер". Листинг 2.3. Главный модуль приложения "Конвертер" (converter.dpr) program converter; uses Forms, Unit1 in 'Unit1.pas' ; Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end.

43 36 Часть I. Delphi XE Начинается файл проекта директивой program, в которой указывается имя приложения. Далее, за директивой uses, следуют имена модулей, необходимых для создания выполняемого файла: библиотечного модуля Forms и модуля unit1 стартовой формы. Строка это директива компилятору подключить файл ресурсов. В файле ресурсов находится значок приложения. Звездочка показывает, что имя файла ресурсов такое же, как и у файла проекта. Инструкции между и end обеспечивают инициализацию приложения (создание объекта Application), создание стартовой формы (объекта Form1) и запуск приложения (метод Run). Модули содержат объявления типов, констант, переменных, процедур и функций. В качестве примера в листинге 2.4 приведен модуль стартовой формы программы "Конвертер". Этот модуль содержит объявление класса TForm1. Обратите внимание, что инструкция Application.CreateForm(TForm1, Form1), которая находится в файле проекта (см. листинг 2.3), создает объект класса TForm1, т. е. стартовую форму приложения "Конвертер". Листинг 2.4. Модуль стартовой формы программы "Конвертер" (unit1.pas) unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(tform) Edit1: TEdit; Edit2: TEdit; Label1: TLabel; Label2: TLabel; Label3: TLabel; Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private < Private declarations >public

44 Глава 2. Первый проект 37 var Form1: TForm1; implementation procedure TForm1.Button1Click(Sender: TObject); var usd: real; // цена в долларах k: real; // курс rub: real; // цена в рублях // получить данные из полей редактирования k := StrToFloat(Edit1.Text); usd := StrToFloat(Edit2.Text); // пересчитать цену из долларов в рубли rub := usd * k; // вывести результат расчета в поле Label3 Label3.Caption := FloatToStr(usd) + ' $ = ' + FloatToStr(rub) + ' руб.'; procedure TForm1.Button2Click(Sender: TObject); Form1.Close; // закрыть окно end. Начинается модуль словом unit, за которым указано имя модуля. Именно это имя упоминается в списке используемых модулей в инструкции uses файла проекта. Модуль состоит из следующих разделов: интерфейса, реализации и инициализации. В разделе интерфейса (начинается словом interface) находятся объявления класса TForm1 и объекта Form1. Таким образом, модуль, который использует модуль unit1, может создать форму Form1 (окно Конвертер). Раздел реализации открывается словом implementation. Директива указывает компилятору, что описание формы, сформированное Delphi в процессе

45 38 Часть I. Delphi XE ее создания, находится в файле unit1.dfm. Далее следует описание методов класса TForm1 (процедуры Button1Click и Button2Click являются методами). Следует отметить, что модуль проекта (dpr-файл), файл описания формы (dfmфайл), файл ресурсов (res-файл), а также значительное количество инструкций модуля формы (pas-файл) формирует Delphi. Компиляция Процесс преобразования исходной программы в выполняемую называется компиляцией и состоит из двух этапов: непосредственно компиляции и компоновки. На этапе компиляции выполняется перевод исходной программы (модулей) в некоторое внутреннее представление. На этапе компоновки выполняется сборка (построение) программы. Процесс компиляции активизируется в результате выбора в меню Project команды Compile, а также путем запуска программы из среды разработки (команда Run или Run Without Debugging меню Run), если с момента последней компиляции в программу были внесены изменения. Следует обратить внимание, что перед тем как первый раз выполнить компиляцию новой программы, необходимо сохранить проект. Процесс и результат компиляции отражаются в окне Compile. Если в программе синтаксических ошибок нет, то по завершении компиляции в поле Compiling отображается слово Done (рис. 2.28). Рис Результат компиляции (в программе ошибок нет) Если в программе есть ошибки и (или) неточности, то в поле Compiling выводится сообщение There are errors (рис. 2.29) и отображается информация о количестве синтаксических (поле Errors) и семантических (поле Warnings) ошибок, а также о количестве неточностей (поле Hints). Сами же сообщения об ошибках, предупреждения и подсказки отображаются в окне Messages (рис. 2.30). Кроме того, после того как окно Compile будет закрыто, в редакторе кода будет выделена строка, в которой находится первая (от начала файла) обнаруженная ошибка.

46 Глава 2. Первый проект 39 Рис Результат компиляции (в программе есть ошибки) Рис Сообщения об обнаруженных ошибках Ошибки Компилятор генерирует выполняемую программу (exe-файл) только в том случае, если в исходной программе нет синтаксических ошибок. Если в программе есть ошибки, то программист должен их устранить. Процесс устранения ошибок носит итерационный характер. Обычно сначала устраняются наиболее очевидные ошибки, например объявляются необъявленные переменные. После очередного внесения изменений в текст программы выполняется повторная компиляция. В табл приведены сообщения компилятора о типичных ошибках. Таблица Сообщения компилятора об ошибках Сообщение Undeclared identifier (Необъявленный идентификатор) Вероятная причина Используется переменная, не объявленная в разделе var. Ошибка при записи имени переменной. Например, в программе объявлена переменная Sum, а в тексте программы написано: Suma Unterminated string (Незавершенная строка) При записи строковой константы, например сообщения, не поставлена завершающая кавычка

47 40 Часть I. Delphi XE Таблица 2.10 (окончание) Сообщение Incompatible types (Несовместимые типы) Вероятная причина В инструкции присваивания тип выражения не соответствует или не может быть приведен к типу переменной, получающей значение выражения. Тип фактического параметра процедуры или функции не соответствует или не может быть приведен к типу формального параметра Missing operator or semicolon (Отсутствует оператор или точка с запятой) Could not create output file (Невозможно создать exe-файл) После инструкции не поставлена точка с запятой Программа, над которой идет работа, запущена (командой Run Run Without Debugging), поэтому существующий exe-файл нельзя заменить новым Следует обратить внимание, что компилятор не всегда может точно локализовать ошибку. Поэтому, анализируя фрагмент кода, который помечен компилятором как содержащий ошибку, надо обращать внимание и на текст, который находится в предыдущих строках. Предупреждения и подсказки При обнаружении в программе неточностей, которые не являются ошибками, компилятор выводит подсказки (Hints) и предупреждения (Warnings). Например, часто выводимой подсказкой является сообщение об объявленной, но не используемой переменной: Variable. is declared but never used in. Действительно, зачем объявлять переменную и не использовать ее? В табл приведены предупреждения и подсказки компилятора о типичных неточностях в программе. Таблица Предупреждения и подсказки компилятора Сообщение Variable. is declared but never used in. Variable. might not have been initialized. (Вероятно, используется неинициализированная переменная) Причина Переменная объявлена, но не используется В программе нет инструкции, которая присваивает переменной начальное значение

48 Глава 2. Первый проект 41 Запуск программы Пробный запуск программы можно выполнить непосредственно из Delphi, не завершая работу со средой разработки. Для этого в меню Run надо выбрать команду Run или Run Without Debugging. Можно также сделать щелчок на кнопке Run (рис. 2.31) или нажать клавишу <F9>. Рис Чтобы запустить программу, сделайте щелчок на кнопке Run Команда Run запускает программу в режиме отладки. Команда Run Without Debugging запускает программу в обычном режиме, даже в том случае, если в ней есть информация, необходимая для отладки (заданы точки останова, указаны переменные, значения которых надо контролировать). Следует обратить внимание, что процесс запуска программы командой Run Without Debugging происходит быстрее. Исключения Ошибки, возникающие во время работы программы, называют исключениями. В большинстве случаев причиной исключений являются неверные данные. Например, если в поле Курс программы "Конвертер" ввести строку и сделать щелчок на кнопке Пересчет, то на экране появится сообщение (рис. 2.32): '30.09' is not valid floating point value (30.09 не является дробным числом). Рис Пример сообщения о возникновении ошибки исключения Причина возникновения исключения описанной ошибки в следующем. Преобразование строки, введенной в поле редактирования, в число выполняет функция StrToFloat. Эта функция работает правильно, если ее параметром является строковое представление дробного числа, что при стандартной для России настройке

49 42 Часть I. Delphi XE Windows предполагает использование в качестве десятичного разделителя запятой. В рассматриваемом примере строка не является строковым представлением дробного числа, поэтому и возникает исключение. Если программа запущена из среды разработки в режиме отладки (команда Run из меню Run), то при возникновении исключения выполнение программы приостанавливается и на экране появляется окно Debugger Exception Notification, в котором, помимо сообщения об ошибке, указывается тип исключения (рис. 2.33). Щелчок на кнопке Break приостанавливает выполнение программы в реальном времени и переводит ее в режим выполнения по шагам (при этом в окне редактора кода выделяется инструкция, при выполнении которой произошла ошибка). Щелчок на кнопке Continue запускает программу с той точки, в которой была приостановлена ее работа. Рис Пример сообщения о возникновении исключения Для того чтобы остановить программу, во время работы которой возникло исключение, надо в меню Run выбрать команду Program Reset. Обработка исключения Обработку исключений берет на себя автоматически добавляемый в выполняемую программу код, который обеспечивает вывод сообщения об ошибке и завершение процедуры, при выполнении которой возникло исключение. Вместе с тем программист может поместить в программу код, который выполнит обработку исключения. Инструкция обработки исключения в общем виде выглядит так: try // здесь инструкции, при выполнении которых может // произойти исключение except on Исключение do // здесь инструкции обработки исключения

50 Глава 2. Первый проект 43 Ключевое слово try указывает, что далее следуют инструкции, при выполнении которых возможно возникновение исключений, и что обработку этих исключений берет на себя программа. Слово except обозначает начало секции обработки исключений. После слова on указывается исключение, обработку которого берет на себя программа, а после do инструкции, обеспечивающие обработку исключения. Нужно обратить внимание, что инструкции, следующие за той, при выполнении которой возникло исключение, после обработки исключения не выполняются. В табл перечислены часто возникающие исключения и указаны причины, которые могут привести к их возникновению. Таблица Типичные исключения Тип исключения EConvertError EZeroDivide EFOpenError EInOutError EOLEException Возникает При выполнении преобразования строки в число, если преобразуемая величина не может быть приведена к требуемому виду. Чаще всего возникает при преобразовании строки в дробное число, если в качестве разделителя целой и дробной частей указан неверный символ Деление на ноль. При выполнении операции деления, если делитель равен нулю (если и делитель, и делимое равны нулю, то возникает исключение EInvalidOp) При обращении к файлу, например при попытке загрузить файл иллюстрации с помощью метода LoadFromFile. Наиболее частой причиной является отсутствие требуемого файла или, в случае использования сменного диска, отсутствие диска в накопителе При обращении к файлу, например при попытке открыть для чтения несуществующий файл При выполнении операций с базой данных, например при попытке открыть несуществующую базу данных, если для доступа к базе данных используются ADO-компоненты (чтобы иметь возможность обработки этого исключения, в директиву uses надо добавить ссылку на модуль ComObj) В качестве примера использования инструкции try в листинге 2.5 приведена процедура обработки события Click на кнопке Пересчет окна программы "Конвертер". При возникновении исключения EConvertError программа определяет причину (незаполненное поле или неверный формат данных) и выводит соответствующее сообщение (рис. 2.34). Листинг 2.5. Щелчок на кнопке Пересчет (c обработкой исключения) procedure TForm1.Button1Click(Sender: TObject); var usd: real; // цена в долларах

51 44 Часть I. Delphi XE k: real; // курс rub: real; // цена в рублях try // получить данные из полей редактирования k := StrToFloat(Edit1.Text); usd := StrToFloat(Edit2.Text); // пересчитать цену из долларов в рубли rub := usd * k; // вывести результат расчета в поле Label4 Label3.Caption := FloatToStr(usd) + '$ = ' + FloatToStr(rub) + 'руб.'; except if (Length(Edit1.Text) = 0) or (Length(Edit2.Text)=0) then MessageDlg('Надо ввести данные в оба поля', mtwarning,[mbok],0) else MessageDlg('При вводе дробных чисел используйте запятую', mterror,[mbok],0); Рис Пример сообщения об ошибке (диалог MessageDlg) В приведенной процедуре обработки события Click для вывода сообщения о неверных данных используется функция MessageDld, инструкция вызова которой в общем виде выглядит так: r := MessageDlg(Сообщение, Тип, Кнопки, РазделСправки) где: Сообщение текст сообщения; Тип тип сообщения. Сообщение может быть информационным (mtinformation), предупреждающим (mtwarning) или сообщением об ошибке (mterror). Каждому типу сообщения соответствует значок (табл. 2.13); Кнопки список кнопок, отображаемых в окне сообщения (табл. 2.14);

52 Глава 2. Первый проект 45 РазделСправки идентификатор раздела справочной информации, который появится на экране, если пользователь нажмет клавишу <F1>. Если вывод справки не предусмотрен, то в качестве параметра следует указать ноль. Значение, возвращаемое функцией MessageDlg (табл. 2.15), позволяет определить, какая кнопка была нажата пользователем для завершения диалога. Если в окне сообщения отображается одна кнопка (очевидно, что в этом случае не нужно проверять, какую кнопку нажал пользователь), то функцию MessageDld можно вызвать как процедуру. Сообщение Тип сообщения Значок Таблица Сообщения Warning (Внимание) Error (Ошибка) mtwarning mterror Information (Информация) mtinformation Confirmation (Подтверждение) mtconfirmation Таблица Идентификаторы кнопок Идентификатор mbyes mbno mbok mbcancel mbhelp Кнопка Yes No OK Cancel Help Таблица Значения функции MessageDlg Значение mryes mrok mrno mrcancel Диалог завершен нажатием кнопки Yes Ok No Cancel

53 46 Часть I. Delphi XE Для вывода сообщения вместо функции MessageDlg можно использовать процедуру ShowMessage. Эта процедура выводит окно с сообщением и кнопкой OK. На рис приведен пример окна сообщения результат выполнения инструкции ShowMessage('Надо ввести данные в оба поля'). Рис Сообщение, выведенное процедурой ShowMessage Обратите внимание, что в заголовке окна отображается имя приложения, указанное на вкладке Application окна Project Options (окно становится доступным в результате выбора в меню Project команды Options) или, если имя приложения не задано, имя проекта. Внесение изменений Программу "Конвертер" можно усовершенствовать. Например, сделать так, чтобы в поля редактирования пользователь мог ввести только правильную информацию, и чтобы в результате нажатия клавиши <Enter> в поле Курс курсор переходил в поле Цена, а при нажатии этой же клавиши в поле Цена становилась активной кнопка Пересчет. Можно сделать так, чтобы кнопка Пересчет была доступной только в том случае, если данные есть в обоих полях редактирования. Чтобы внести изменения в программу, нужно открыть соответствующий проект. Сделать это можно обычным способом, выбрав в меню File команду Open Project. Можно выбрать нужный проект из списка проектов, над которыми в последнее время работал программист. Этот список становится доступным в результате выбора в меню File команды Reopen. Чтобы программа "Конвертер" работала так, как было описано ранее, надо создать процедуры обработки событий KeyPress и Change для полей редактирования (компонентов Edit1 и Edit2). Процедура обработки события KeyPress (для каждого компонента своя) обеспечивает фильтрацию вводимых пользователем символов. Она проверяет символ нажатой клавиши (символ передается в процедуру обработки события через параметр Key) и, если символ "запрещен", заменяет его на так называемый нуль-символ (в результате запрещенный символ в поле редактирования не отображается). Процедура обработки события Change (событие возникает, если текст, находящийся в поле редактирования, изменился, например в результате нажатия какой-либо клавиши в поле редактирования) управляет доступностью кнопки Пересчет. Она проверяет, есть ли данные в полях редактирования, и, если в ка-

54 Глава 2. Первый проект 47 ком-либо из полей данных нет, присваивает свойству Enabled кнопки Button1 значение False и тем самым делает кнопку недоступной. Следует обратить внимание, что действие, которое надо выполнить, если изменилось содержимое поля Edit1, ничем не отличается от действия, которое надо выполнить, если изменилось содержимое поля Edit2. Поэтому обработку события Change для обоих компонентов выполняет одна процедура. Чтобы одна процедура могла обрабатывать события разных компонентов, сначала надо создать процедуру обработки события для одного компонента, а затем указать имя этой процедуры в качестве имени процедуры обработки события другого компонента. В листинге 2.6 приведен модуль главной формы усовершенствованной программы "Конвертер". Обратите внимание, что в процедуре обработки события Click кнопки Пересчет нет инструкций обработки исключений. Исключения не могут возникнуть, т. к. пользователь просто не сможет ввести в поля редактирования неверные данные. Листинг 2.6. Модуль главной формы программы "Конвертер" (usd2rub.pas) // нажатие клавиши в поле Курс procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char); case Key of '0'..'9', #8: ; // цифры и <Backspace> < Обработку десятичного разделителя сделаем "интеллектуальной". Заменим разделитель (точку или запятую) символом DecimalSeparator, который должен использоваться при записи дробных чисел >'.',',': Key := DecimalSeparator; // проверим, введен ли уже в поле Edit десятичный разделитель if pos(decimalseparator,edit1.text) <> 0 then Key := #0; #13: Edit2.SetFocus; // <Enter> курсор в поле Edit2 else Key := #0; // остальные символы запрещены // нажатие клавиши в поле Цена procedure TForm1.Edit2KeyPress(Sender: TObject; var Key: Char);

55 48 Часть I. Delphi XE case Key of '0'..'9', #8: ; // цифры и <Backspace> '.',',': Key := DecimalSeparator; // проверим, введен ли уже в поле Edit десятичный разделитель if pos(decimalseparator,edit1.text) <> 0 then Key := #0; #13: Button1.SetFocus; // сделать активной кнопку Пересчет else Key := Char(0); // остальные символы запрещены // текст, находящийся в поле редактирования, изменился procedure TForm1.EditChange(Sender: TObject); // проверим, есть ли данные в полях редактирования if (Length(Edit1.Text) = 0) or (Length(Edit1.Text) = 0) then Button1.Enabled := False // кнопка Пересчет недоступна else Button1.Enabled := True; // кнопка Пересчет доступна // щелчок на кнопке Пересчет procedure TForm1.Button1Click(Sender: TObject); var usd: real; // цена в долларах k: real; // курс rub: real; // цена в рублях // получить данные из полей редактирования k := StrToFloat(Edit1.Text); usd := StrToFloat(Edit2.Text); // пересчитать цену из долларов в рубли rub := usd * k; // вывести результат расчета в поле Label3 Label3.Caption := FloatToStrF(usd, ffnumber, 6,2) + ' $ = ' + FloatToStrF(rub,ffCurrency,6,2);

56 Глава 2. Первый проект 49 // щелчок на кнопке Завершить procedure TForm1.Button2Click(Sender: TObject); Form1.Close; // закрыть окно Настройка приложения После того как приложение будет отлажено, можно выполнить его окончательную настройку: задать название программы и значок, который будет ее изображать в папке, на рабочем столе и на панели задач (во время работы программы). Настройка приложения выполняется в окне Project Options (рис. 2.36), которое становится доступным в результате выбора в меню Project команды Options. Рис Название программы надо ввести в поле Title, затем щелкнуть на кнопке Load Icon и выбрать значок Название программы (его надо ввести в поле Title) отображается во время ее работы на панели задач, а также в заголовках окон сообщений, выводимых функцией ShowMessage (если название приложения не задано, то на панели задач и в заголовках окон сообщений отображается имя выполняемого файла).

57 50 Часть I. Delphi XE Значок приложения изображает exe-файл программы в папке, на рабочем столе и в меню команд. Чтобы задать значок приложения, надо щелкнуть на кнопке Load Icon и, используя стандартное окно просмотра папок, найти подходящий значок (файл с расширением ico). Программист может создать для своего приложения уникальный значок. Сделать это можно, например, с помощью утилиты Borland Image Editor, но, к сожалению, в комплект поставки Delphi XE она не входит. Установка приложения на другой компьютер Приложение, созданное в Delphi XE, можно перенести на другой компьютер, например, с помощью флэш-накопителя. Однако следует обратить внимание на то, что по умолчанию программа компилируется в режиме использования динамических библиотек (runtime packages) и поэтому она будет работать на другом компьютере только в том случае, если на нем есть необходимые библиотеки. Таким образом, помимо самой программы (exe-файла) на компьютер пользователя надо установить динамические библиотеки, необходимые для работы программы. Это как минимум так называемая библиотека времени выполнения (Runtime Library, файл rtl150.bpl) и библиотека визуальных компонентов (Visual Components Library, файл vcl150.bpl). Указанные файлы следует поместить в папку приложения или, если библиотеки будут использоваться несколькими программами, в папку C:\Windows\System32. ЗАМЕЧАНИЕ Имена библиотек, используемых программами, созданными в Delphi, состоят из двух частей. Первая часть имени, например rtl или vcl, несет информацию о назначении библиотеки (rtl Runtime Library, vcl Visual Components Library). Вторая часть имени содержит информацию о версии библиотеки. Например, 150 это библиотека Delphi XE, 140 Delphi 2010, 70 Delphi 7. Следует обратить внимание на то, что весь необходимый для работы программы код можно включить в exe-файл (в этом случае файлы динамических библиотек не нужны). Чтобы это сделать, надо, перед тем как выполнить компиляцию, в окне Project Options (оно становится доступным в результате выбора в меню Project команды Options) развернуть раздел Packages и сбросить флажок Build with runtime packages (рис. 2.37). Профессиональный подход к разработке программного обеспечения предполагает, что программист создает не только приложение, но и программу, обеспечивающую установку приложения на компьютер пользователя (установщик). Создать установщик можно, например, с помощью утилиты InstallAware (см. главу 9).

58 Глава 2. Первый проект 51 Рис Чтобы программа не использовала динамические библиотеки, надо сбросить флажок Build with runtime packages

59 ГЛАВА 3 Компоненты В этой главе на конкретных примерах демонстрируется назначение основных (базовых) компонентов, а также компонентов для Windows Vista. Базовые компоненты К базовым можно отнести компоненты, обеспечивающие взаимодействие с пользователем (Edit, Label, Button и др.), отображение иллюстраций (Image), стандартных диалоговых окон (SaveDialog, OpenDialog) и некоторые другие, например Timer. Базовые компоненты находятся на вкладках Standard, Additional, Win32, System и Dialogs. Label Компонент Label, его значок (рис. 3.1) находится на вкладке Standard, предназначен для отображения текста. Чтобы задать текст, отображаемый в поле компонента, надо присвоить значение свойству Caption. Сделать это можно как при разработке формы, так и во время работы программы. Свойства компонента приведены в табл Рис Значок компонента Label Таблица 3.1. Свойства компонента Label Свойство Name Caption Left Описание Имя (идентификатор) компонента Отображаемый текст Расстояние от левой границы поля вывода до левой границы формы

60 Глава 3. Компоненты 53 Таблица 3.1 (окончание) Свойство Top Height Width AutoSize WordWrap Alignment Font ParentFont Color Transparent Visible Описание Расстояние от верхней границы поля вывода до верхней границы формы Высота поля вывода Ширина поля вывода Признак того, что размер поля определяется его содержимым Признак того, что слова, которые не помещаются в текущей строке, автоматически переносятся на следующую строку (значение свойства AutoSize должно быть False) Задает способ выравнивания текста внутри поля. Текст может быть выровнен по левому краю (taleftjustify), по центру (tacenter) или по правому краю (tarightjustify) Шрифт, используемый для отображения текста. Уточняющие свойства определяют шрифт (Name), размер (Size), стиль (Style) и цвет символов (Color) Признак наследования компонентом характеристик шрифта формы, на которой находится компонент. Если значение свойства равно True, то текст выводится шрифтом, установленным для формы Цвет фона области вывода текста Управляет отображением фона области вывода текста. Значение True делает область вывода текста прозрачной (область вывода не закрашивается цветом, заданным свойством Color) Позволяет скрыть текст (False) или сделать его видимым (True) Возможности компонента Label демонстрирует программа "Доход" (ее форма приведена на рис. 3.2, а текст процедуры обработки события Click на кнопке Ok в листинге 3.1). Рис Форма программы "Доход"

61 54 Часть I. Delphi XE Она показывает, как во время работы программы изменить цвет текста, отображаемого в поле компонента, как вывести в поле компонента значение переменной, а также как разбить отображаемый текст на строки. Программа вычисляет доход по вкладу. Если пользователь оставит какое-либо из полей незаполненным, то в результате щелчка на кнопке Ok в поле компонента Label3 красным цветом отображается сообщение об ошибке. Если все поля формы заполнены, то в поле компонента Label3 в две строки выводится результат расчета. Разбиение текста на строки обеспечивает символ "новая строка" (его код равен 10). Добавить нужный символ в формируемую строку можно с помощью функции Chr. Вместо функции Chr можно указать код символа, поставив перед значением "решетку" (#). Именно этот способ и используется в рассматриваемой программе. Значения свойств компонентов Label приведены в табл Таблица 3.2. Значения свойств компонентов Label Компонент Свойство Значение Label1 Caption Сумма (руб.) Label2 Caption Срок (мес.) Label3 Caption AutoSize WordWrap False True Width 233 Height 57 Листинг 3.1. Обработка события Click на кнопке Ok // щелчок на кнопке Ok procedure TForm1.Button1Click(Sender: TObject); var sum, pr: real; // сумма, процентная ставка srok: integer; // срок вклада, месяцев dohod, sum2 : real; // доход и сумма в конце срока вклада if (Length(Edit1.Text) = 0) or (Length(Edit2.Text) = 0) then Label3.Font.Color := clmaroon; // темно-красный Label3.Caption := 'Надо заполнить все поля формы'; end

62 Глава 3. Компоненты 55 else sum := StrToFloat(Edit1.Text); srok := StrToInt(Edit2.Text); // определить процентную ставку case srok of 1..3: pr := 9.5; 4..6: pr := 11; 7..12: pr := 12.5; : pr := 14; else pr:=18; dohod := sum * (pr / 100/ 12) * srok; sum2 := sum + dohod; Label3.Font.Color := clnavy; Label3.Caption := 'Сумма: ' + FloatToStrF(sum,ffCurrency,6,2) + #10 + 'Процент (годовых):' + FloatToStrF(pr,ffNumber,2,2) + #10 + 'Доход: ' + FloatToStrF(dohod, ffcurrency,6,2) + #10 + 'Сумма в конце срока: ' + FloatToStrF(sum2,ffCurrency,6,2); Edit Компонент Edit, его значок (рис. 3.3) находится на вкладке Standard, обеспечивает ввод и редактирование текста. Свойства компонента приведены в табл Рис Значок компонента Edit Таблица 3.3. Свойства компонента Edit Свойство Name Text Left Описание Имя (идентификатор) компонента Текст, находящийся в поле ввода и редактирования Расстояние от левой границы компонента до левой границы формы

63 56 Часть I. Delphi XE Таблица 3.3 (окончание) Свойство Top Height Width Font ParentFont Enabled Visible Описание Расстояние от верхней границы компонента до верхней границы формы Высота Ширина Шрифт, используемый для отображения текста Признак наследования компонентом характеристик шрифта формы, на которой находится компонент. Если значение свойства равно True, то при изменении свойства Font формы автоматически меняется значение свойства Font компонента Используется для ограничения возможности изменить текст в поле редактирования. Если значение свойства равно False, то текст в поле редактирования изменить нельзя Позволяет скрыть компонент (False) или сделать его видимым (True) В поле компонента Edit отображаются все символы, которые пользователь набирает на клавиатуре. Вместе с тем программист может, создав процедуру обработки события KeyPress, запретить ввод (отображение) некоторых символов. Использование компонента Edit для ввода данных различного типа демонстрирует программа "Компонент Edit" (ее форма приведена на рис. 3.4, а текст в листинге 3.2). Программа спроектирована таким образом, что в режиме ввода текста в поле редактирования можно ввести любой символ, в режиме ввода целого числа только цифры и знак " " (если это первый символ). В режиме ввода дробного числа кроме цифр и знака " " в поле компонента можно ввести символразделитель (запятую или точку, в зависимости от настройки операционной системы). Рис Форма программы "Компонент Edit" Листинг 3.2. Компонент Edit // клавиша нажата в поле "Текст" procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);

64 Глава 3. Компоненты 57 if key = #13 // клавиша <Enter> then Edit2.SetFocus; // переместить курсор в поле Edit2 // клавиша нажата в поле "Целое число" procedure TForm1.Edit2KeyPress(Sender: TObject; var Key: Char); case Key of '0'..'9', #8: ; // цифры и <Backspace> #13: Edit3.SetFocus; // <Enter> переместить курсор в поле Edit3 '-': if Length(Edit2.Text) <> 0 then Key := #0; else Key := #0; // остальные символы не отображать // клавиша нажата в поле "Дробное число" procedure TForm1.Edit3KeyPress(Sender: TObject; var Key: Char); case Key of '0'..'9', #8: ; // цифры и <Backspace> '.',',': // DecimalSeparator глобальная переменная, в которой // находится символ "десятичный разделитель" Key := DecimalSeparator; if pos(decimalseparator,edit3.text) <> 0 then Key := #0; '-': if Length(Edit3.Text) <> 0 then Key := #0; else Key := #0; // остальные символы не отображать Button Компонент Button, его значок (рис. 3.5) находится на вкладке Standard, представляет собой командную кнопку. Свойства компонента приведены в табл Рис Значок компонента Button

65 58 Часть I. Delphi XE Таблица 3.4. Свойства компонента Button Свойство Name Caption Left Top Height Width Enabled Visible Hint ShowHint Описание Имя (идентификатор) компонента Текст на кнопке Расстояние от левой границы кнопки до левой границы формы Расстояние от верхней границы кнопки до верхней границы формы Высота кнопки Ширина кнопки Признак доступности кнопки. Если значение свойства равно True, то кнопка доступна. Если значение свойства равно False, то кнопка не доступна (в результате щелчка на кнопке событие Click не возникает) Позволяет скрыть кнопку (False) или сделать ее видимой (True) Подсказка текст, который появляется рядом с указателем мыши при позиционировании указателя на командной кнопке (для того чтобы текст появился, значение свойства ShowHint должно быть True) Разрешает (True) или запрещает (False) отображение подсказки при позиционировании указателя на кнопке Использование компонента Button демонстрирует программа "Версты километры" (ее форма приведена на рис. 3.6, а текст в листинге 3.3). Программа пересчитывает расстояние из километров в версты. Расчет и отображение результата выполняет процедура обработки события Click на кнопке OK. Следует обратить внимание, что кнопка OK доступна только в том случае, если в поле редактирования находятся данные (хотя бы одна цифра). Управляет доступностью кнопки процедура обработки события Change компонента Edit1. Процедура контролирует количество символов, которое находится в поле редактирования, и, если в поле нет ни одной цифры, присваивает значение False свойству Enabled и тем самым делает кнопку недоступной. В процессе создания формы свойству Enabled кнопки надо присвоить значение False. Рис Форма программы "Версты километры"

66 Глава 3. Компоненты 59 Листинг 3.3. Версты километры // текст в поле редактирования изменен procedure TForm1.Edit1Change(Sender: TObject); if Length(Edit1.Text) = 0 then Button1.Enabled := False else Button1.Enabled := True; // щелчок на кнопке OK procedure TForm1.Button1Click(Sender: TObject); var km: real; // расстояние в километрах v: real; // расстояние в верстах km := StrToFloat(Edit1.Text); v := km / ; Label2.Caption := Edit1.Text + ' км это - ' + FloatToStrF(v,ffFixed,7,2) + ' верст'; // нажатие клавиши в поле редактирования procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char); case Key of '0'..'9', #8: ; // цифры и <Backspace> '.',',': // DecimalSeparator глобальная переменная, в которой // находится символ "десятичный разделитель" Key := DecimalSeparator; if pos(decimalseparator,edit1.text) <> 0 then Key := #0; #13: Button1.SetFocus; else Key := #0; // остальные символы не отображать

67 60 Часть I. Delphi XE CheckBox Компонент CheckBox, его значок (рис. 3.7) находится на вкладке Standard, представляет собой флажок, который может находиться в одном из двух состояний: выбранном или невыбранном. Часто вместо "выбранный" говорят "установленный", а вместо "невыбранный" "сброшенный". Рядом с флажком обычно находится поясняющий текст. Свойства компонента CheckBox приведены в табл Рис Значок компонента CheckBox Таблица 3.5. Свойства компонента CheckBox Свойство Name Caption Checked State AllowGrayed Left Top Height Width Font ParentFont Описание Имя (идентификатор) компонента Комментарий (текст, который находится справа от флажка) Состояние флажка: если флажок установлен (в квадратике есть "галочка"), то значение Checked равно True; если флажок сброшен (нет "галочки"), то значение Checked равно False Состояние флажка. В отличие от свойства Checked, позволяет различать установленное, сброшенное и промежуточное состояния. Состояние флажка определяет одна из констант: cbchecked (установлен), cbunchecked (сброшен), cbgrayed (серый, неопределенное состояние) Свойство определяет, может ли флажок находиться в промежуточном состоянии: если значение AllowGrayed равно False, то флажок может быть только установленным или сброшенным; если значение AllowGrayed равно True, то допустимо промежуточное состояние, когда флажок и не установлен, и не сброшен. Если компонент находится в промежуточном состоянии, то он окрашен в серый (gray) цвет Расстояние от левой границы флажка до левой границы формы Расстояние от верхней границы флажка до верхней границы формы Высота поля вывода поясняющего текста Ширина поля вывода поясняющего текста Шрифт, используемый для отображения поясняющего текста Признак наследования характеристик шрифта родительской формы Использование компонентов CheckBox демонстрирует программа "Комплектация" (ее форма приведена на рис. 3.8, а текст в листинге 3.4). Программа позволяет посчитать цену автомобиля в комплектации, выбранной пользователем. Значения свойств компонентов CheckBox приведены в табл. 3.6.

68 Глава 3. Компоненты 61 Рис Форма программы "Комплектация" Таблица 3.6. Значения свойств компонентов CheckBox Компонент Свойство Значение CheckBox1 Caption защита картера Checked False CheckBox2 Caption легкосплавные диски Checked False CheckBox3 Caption шипованные шины Checked False CheckBox4 Caption коврики Checked False Листинг 3.4. Комплектация procedure TForm1.Button1Click(Sender: TObject); var cena: real; // цена в базовой комплектации dop: real; // сумма за доп. оборудование discount: real; // скидка total: real; // общая сумма st: string; // сообщение результат расчета

69 62 Часть I. Delphi XE cena := ; dop := 0; if (CheckBox1.Checked) then // защита картера dop := dop ; if (CheckBox2.Checked) then // зимние шины dop := dop ; if (CheckBox3.Checked) then // литые диски dop := dop ; if (CheckBox4.Checked) then // коврики dop := dop ; total := cena + dop; st := 'Цена в выбранной комплектации: ' + FloatToStrF(total, ffcurrency, 6,2); if (dop <> 0) then st := st + #10+ 'В том числе доп. оборудование: ' + FloatToStrF(dop, ffcurrency, 6,2); if ((CheckBox1.Checked) and (CheckBox2.Checked) and (CheckBox3.Checked) and (CheckBox4.Checked)) then // скидка предоставляется, если выбраны все опции discount := dop * 0.1; total := total - discount; st := st + #10 + 'Скидка на доп. оборудование (10%): ' + FloatToStrF(discount, ffcurrency, 6,2) + #10 + 'Итого: ' + FloatToStrF(total, ffcurrency, 6,2); Label2.Caption := st;

70 Глава 3. Компоненты 63 RadioButton Компонент RadioButton, его значок (рис. 3.9) находится на вкладке Standard, представляет собой кнопку (переключатель), состояние которой зависит от состояния других компонентов RadioButton, находящихся на форме. Обычно компоненты RadioButton объединяют в группу (достигается это путем размещения нескольких компонентов в поле компонента GroupBox). В каждый момент времени только одна из кнопок группы может находиться в выбранном состоянии (возможна ситуация, когда ни одна из кнопок не выбрана). Состояние кнопок, принадлежащих одной группе, не зависит от состояния кнопок, принадлежащих другой группе. Свойства компонента RadioButton приведены в табл Рис Значок компонента RadioButton Таблица 3.7. Свойства компонента RadioButton Свойство Name Caption Описание Имя (идентификатор) компонента Комментарий (текст, который находится справа от кнопки) Checked Состояние. Определяет вид кнопки: True кнопка выбрана, False кнопка не выбрана (выбрана другая кнопка группы) Left Top Height Width Font ParentFont Расстояние от левой границы флажка до левой границы формы Расстояние от верхней границы флажка до верхней границы формы Высота поля вывода поясняющего текста Ширина поля вывода поясняющего текста Шрифт, используемый для отображения поясняющего текста Признак наследования характеристик шрифта родительской формы Использование компонента RadioButton демонстрирует программа "Фото" (ее форма приведена на рис. 3.10, а текст в листинге 3.5). Программа вычисляет стоимость печати фотографий. Значения свойств компонентов RadioButton приведены в табл Таблица 3.8. Значения свойств компонентов RadioButton Компонент Свойство Значение RadioButton1 Caption 9 х 12 Checked True

71 64 Часть I. Delphi XE Компонент Свойство Значение RadioButton2 Caption 10 х 15 Таблица 3.8 (окончание) Checked False RadioButton3 Caption 18 х 24 Checked False Рис Форма программы "Фото" Листинг 3.5. Фото // щелчок на кнопке OK procedure TForm1.Button1Click(Sender: TObject); var cena: real; // цена 1 шт. kol: integer; // количество sum: real; // сумма // определить цену одной фотографии if RadioButton1.Checked then // 9x12 cena := 3.50; if RadioButton2.Checked then // 10x15 cena := 4.50;

72 Глава 3. Компоненты 65 if RadioButton3.Checked then // 18x24 cena := 14; kol := StrToInt(Edit1.Text); sum := cena * kol; Label2.Caption := 'Цена: ' + FloatToStrF(cena,ffCurrency,3,2)+ #10 + 'Кол-во: ' + IntToStr(kol) + #10 + 'Сумма заказа: ' + FloatToStrF(sum,ffCurrency,3,2); // изменилось содержимое поля редактирования procedure TForm1.Edit1Change(Sender: TObject); // если количество фотографий не задано (поле Edit1 пустое), // сделаем кнопку OK недоступной if Length(Edit1.Text) = 0 then // в поле нет текста Button1.Enabled := False else // текст в поле есть Button1.Enabled := True; // нажатие клавиши в поле Edit1 procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char); // по условию задачи в поле "Количество" // можно ввести только целое число case Key of '0'..'9', #8: ; // цифры и <Backspace> #13: Button1.SetFocus; // <Enter> переместить фокус на Button1 else Key := #0; // остальные символы не отображать ComboBox Компонент ComboBox, его значок (рис. 3.11) находится на вкладке Standard, представляет собой комбинацию поля редактирования и списка, что дает возмож-

73 66 Часть I. Delphi XE ность ввести данные путем набора на клавиатуре или выбором из списка. Свойства компонента приведены в табл Рис Значок компонента ComboBox Таблица 3.9. Свойства компонента ComboBox Свойство Name Style Text Items Count ItemIndex Sorted DropDownCount Left Top Height Width Font ParentFont Описание Имя (идентификатор) компонента Вид компонента: csdropdown поле ввода и раскрывающийся список (данные можно ввести в поле редактирования или выбрать в списке); csdropdownlist только раскрывающийся список; cssimple только поле редактирования Текст, находящийся в поле ввода/редактирования Элементы списка массив строк Количество элементов списка Номер элемента, выбранного в списке (элементы нумеруются с нуля). Если ни один из элементов списка не выбран, то значение свойства равно 1 Признак необходимости автоматической сортировки (True) списка после добавления очередного элемента Количество элементов, отображаемых в раскрытом списке. Если количество элементов списка больше DropDownCount, то появляется вертикальная полоса прокрутки Расстояние от левой границы компонента до левой границы формы Расстояние от верхней границы компонента до верхней границы формы Высота компонента (поля ввода/редактирования) Ширина компонента Шрифт, используемый для отображения элементов списка Признак наследования свойств шрифта родительской формы Список, отображаемый в поле компонента, можно сформировать во время создания формы или во время работы программы. Чтобы сформировать список во время создания формы, надо выбрать свойство Items, щелкнуть на находящейся в поле значения свойства кнопке и в окне String List Editor ввести элементы списка (рис. 3.12).

74 Глава 3. Компоненты 67 Рис Формирование списка компонента во время разработки формы Чтобы сформировать список во время работы программы (добавить в список элемент), надо применить метод Add к свойству Items. Например: ComboBox1.Items.Add('алюминий'); ComboBox1.Items.Add('пластик'); ComboBox1.Items.Add('текстиль'); Использование компонента ComboBox демонстрирует программа "Жалюзи" (ее форма приведена на рис. 3.13). Материал, из которого должны быть сделаны жалюзи, выбирается в списке ComboBox. Настройку компонента ComboBox выполняет конструктор формы. Процедура обработки события Click на командной кнопке Button1 и процедура обработки события Create формы приведены в листинге 3.6. Рис Форма программы "Жалюзи"

75 68 Часть I. Delphi XE Листинг 3.6. Жалюзи // щелчок на кнопке OK procedure TForm1.Button1Click(Sender: TObject); var w,h: real; // ширина, высота (см) s: real; // площадь (кв. м) c: real; // цена за 1 кв. м summ: real; // сумма st: string; // сообщение if (Length(Edit1.Text) = 0) // не задана ширина or (Length(Edit1.Text) = 0) // не задана высота or (ComboBox1.ItemIndex = -1) // не выбран материал then ShowMessage('Надо задать ширину, высоту и выбрать материал'); exit; // вычислить площадь w := StrToFloat(Edit1.Text); h := StrToFloat(Edit2.Text); s := w * h / 10000; // определить цену // ItemIndex номер элемента, выбранного в списке case ComboBox1.ItemIndex of 0: c := 700; // алюминий 1: c := 450; // пластик 2: c := 300; // текстиль // расчет summ := s * c; st := 'Размер: ' + Edit1.Text + 'x' + Edit2.Text + ' см' + #10 + 'Материал: ' + ComboBox1.Text + #10 + 'Сумма: ' + FloatToStrF(summ, ffcurrency, 6,2); Label4.Caption := st;

76 Глава 3. Компоненты 69 // конструктор формы procedure TForm1.FormCreate(Sender: TObject); ComboBox1.Style := csdropdownlist; ComboBox1.Items.Add('алюминий'); ComboBox1.Items.Add('пластик'); ComboBox1.Items.Add('текстиль'); ListBox Компонент ListBox, его значок (рис. 3.14) находится на вкладке Standard, представляет собой список, в котором можно выбрать нужный элемент. Свойства компонента приведены в табл Рис Значок компонента ListBox Таблица Свойства компонента ListBox Свойство Name Items Count Sorted ItemIndex Left Top Height Width Font ParentFont Описание Имя (идентификатор) компонента Элементы списка Количество элементов списка Признак необходимости автоматической сортировки (True) списка после добавления очередного элемента Номер выбранного элемента (элементы списка нумеруются с нуля). Если в списке ни один из элементов не выбран, то значение свойства равно 1 Расстояние от левой границы списка до левой границы формы Расстояние от верхней границы списка до верхней границы формы Высота поля списка Ширина поля списка Шрифт, используемый для отображения элементов списка Признак наследования свойств шрифта родительской формы Список, отображаемый в поле компонента, можно сформировать при создании формы или во время работы программы. Чтобы сформировать список во время создания формы, надо выбрать свойство Items, щелкнуть на находящейся в поле

77 70 Часть I. Delphi XE значения свойства кнопке и в окне String List Editor ввести элементы списка. Формирование списка во время работы программы обеспечивает метод Add свойства Items. Использование компонента ListBox демонстрирует программа "Просмотр иллюстраций" (ее окно приведено на рис. 3.15, форма представлена на рис. 3.16, а текст в листинге 3.7). Программа позволяет просмотреть фотографии (JPEGфайлы). Рис Окно программы "Просмотр иллюстраций" Компонент ListBox используется для выбора фотографии. Заполняет список компонента ListBox процедура FillListBox (ее объявление надо поместить в секцию Protected объявления формы). В начале работы программы процедуру FillListBox вызывает процедура обработки события Create формы. Процедура обработки события Click на кнопке Папка отображает стандартное окно Обзор папок (отображение диалога обеспечивает процедура SelectDirectory), затем вызывает функцию FillListBox. Фотография отображается в поле компонента Image (см. разд. "Image" далее в этой главе). Для того чтобы иллюстрация отображалась без искажения, свойству AutoSize компонента Image надо присвоить значение False, а свойству Proportional True. Отображение выбранной в списке иллюстрации осуществляет процедура обработки события Click, которое происходит в результате щелчка на элементе списка или перемещения указателя текущего элемента списка с помощью клавиш управления курсором. Следует обратить внимание, что в директиву uses модуля формы надо добавить ссылки на модули FileCtrl и Jpeg.

78 Глава 3. Компоненты 71 Рис Форма программы "Просмотр иллюстраций" Листинг 3.7. Просмотр иллюстраций unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls, FileCtrl, Jpeg; type TForm1 = class(tform) ListBox1: TListBox; Button1: TButton; Image1: TImage; procedure ListBox1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); private Path: string; // папка, которую выбрал пользователь Procedure FillListBox; public

79 72 Часть I. Delphi XE var Form1: TForm1; implementation // заполняет список компонента ListBox (формирует список JPEG-файлов) procedure TForm1.FillListBox; var SearchRec: TSearchRec; // результат поиска файла r: integer; r := FindFirst(Path + '*.jpg', faanyfile, SearchRec); if r = 0 then // в каталоге Path есть по крайней мере один JPEG-файл ListBox1.Items.Clear; ListBox1.Items.Add(SearchRec.Name); while 0 = FindNext(SearchRec) do ListBox1.Items.Add(SearchRec.Name); ListBox1.ItemIndex := 0; Image1.Picture.LoadFromFile(Path + ListBox1.Items[ListBox1.ItemIndex]); end else // в выбранном каталоге нет илюстраций ListBox1.Items.Clear; // очистить список Image1.Picture.Bitmap.FreeImage; // конструктор формы procedure TForm1.FormCreate(Sender: TObject); FillListBox;

80 Глава 3. Компоненты 73 // щелчок в поле компонента ListBox procedure TForm1.ListBox1Click(Sender: TObject); var Filename: string; FileName := Path + ListBox1.Items[ListBox1.ItemIndex]; Image1.Picture.LoadFromFile(Filename); // щелчок на кнопке "Папка" procedure TForm1.Button1Click(Sender: TObject); if SelectDirectory('Выберите каталог', '',Path) then Path := Path + '\'; Form1.Caption := 'Прсмотр иллюстраций - ' + Path; FillListBox; end. Memo Компонент Memo, его значок (рис. 3.17) находится на вкладке Standard, представляет собой элемент редактирования текста, который может состоять из нескольких строк. Свойства компонента приведены в табл Рис Значок компонента Memo Таблица Свойства компонента Memo Свойство Name Text Lines Left Top Описание Имя (идентификатор) компонента Текст, находящийся в поле Memo. Рассматривается как единое целое Массив строк, соответствующий содержимому поля. Доступ к строке осуществляется по номеру. Строки нумеруются с нуля Расстояние от левой границы поля до левой границы формы Расстояние от верхней границы поля до верхней границы формы

81 74 Часть I. Delphi XE Таблица 3.11 (окончание) Свойство Width Height Font ParentFont ReadOnly ScrollBars Modified Описание Ширина поля Высота поля Шрифт, используемый для отображения вводимого текста Признак наследования свойств шрифта родительской формы Разрешает (False) или запрещает (True) редактирование текста, находящегося в поле редактирования Задает отображаемые полосы прокрутки (ssvertical только вертикальная; sshorizontal только горизонтальная; ssboth вертикальная и горизонтальная; ssnone полосы прокрутки не отображать) Индикатор: True текст изменен Использование компонента Memo для отображения и редактирования текста, загруженного из файла, демонстрирует программа "Просмотр/редактирование" (ее форма представлена на рис. 3.18). Текст процедур обработки событий Activate и CloseQuery приведен в листинге 3.8. Загрузку текста из файла kurs.txt выполняет процедура обработки события Activate формы. Процедура обработки события CloseQuery, которое возникает в результате щелчка на кнопке Закрыть, проверяет, внесены ли в текст какие-либо изменения, и, если текст изменен (значение свойства Modified равно True), предлагает сохранить его (сообщение выводит функция MessageDlg). Рис Форма программы "Просмотр/редактирование" Листинг 3.8. Просмотр/редактирование const TEXTFILE = 'kurs.txt';

82 Глава 3. Компоненты 75 procedure TForm1.FormActivate(Sender: TObject); // загрузить в компонент Memo1 текст из файла try Memo1.Lines.LoadFromFile(TEXTFILE); Form1.Caption := 'Просмотр/редактирование - ' + TEXTFILE except on e: EFOpenError do MessageDlg('Нет файла ' + TEXTFILE, mterror,[mbyes],0); Memo1.Enabled := False; // пользователь сделал щелчок на кнопке "Закрыть" procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean); var r: integer; if Memo1.Modified then // содержимое поля редактирования изменено r := MessageDlg('Файл изменен. Сохранить изменения?', mtwarning,[mbyes,mbno,mbcancel],0); case r of mryes: // записать текст в файл Memo1.Lines.SaveToFile(TEXTFILE); mrcancel: // продолжить работу с программой CanClose := False; Timer Компонент Timer, его значок (рис. 3.19) находится на вкладке System, генерирует последовательность событий Timer. Рис Значок компонента Timer

83 76 Часть I. Delphi XE Компонент является невизуальным, т. е. во время работы программы не отображается на форме. Свойства компонента приведены в табл Таблица Свойства компонента Timer Свойство Interval Enabled Описание Период генерации события Timer. Задается в миллисекундах Разрешает (True) или запрещает (False) генерацию события Timer Рис Форма программы "Секундомер" Использование компонента Timer демонстрирует программа "Секундомер" (ее форма приведена на рис. 3.20, а текст в листинге 3.9). В процессе создания формы свойству Interval компонента Timer1 надо присвоить значение 500. Кнопка Button1 предназначена как для запуска секундомера, так и для его остановки. В начале работы программы значение свойства Enabled компонента Timer1 равно False, поэтому таймер не генерирует события Timer. Процедура обработки события Click на кнопке Button1 проверяет состояние таймера и, если таймер не работает, присваивает свойству Enabled таймера значение True и тем самым запускает его. Процедура обработки события Timer, которое возникает с периодом 0,5 с, инвертирует значение свойства Visible компонента Label2 (в результате чего двоеточие мигает) и, если двоеточие отображается, увеличивает счетчик времени, а также выводит в поле компонентов Label1 и Label3 значения счетчиков минут и секунд. Следует обратить внимание, что объявления счетчиков минут и секунд (переменных s и m) надо поместить в секцию private объявления формы. Если секундомер работает, то щелчок на кнопке Button1 останавливает секундомер. Листинг 3.9. Секундомер // щелчок на кнопке "Старт/Стоп" procedure TForm1.Button1Click(Sender: TObject); if Timer1.Enabled then // секундомер работает

84 Глава 3. Компоненты 77 Timer1.Enabled := False; // остановить таймер Button1.Caption := 'Старт'; Button2.Enabled := True; end else // секундомер остановлен Timer1.Enabled := True; // пустить таймер Button1.Caption := 'Стоп'; Button2.Enabled := False; // сигнал таймера (событие Timer) procedure TForm1.Timer1Timer(Sender: TObject); // таймер генерирует событие Timer каждые 0,5 с // показать/скрыть двоеточие Label2.Visible := not Label2.Visible; if not Label2.Visible then exit; // в эту точку попадаем каждую секунду if s = 59 then inc(m); Label1.Caption := IntToStr(m); s := 0; end else inc(s); // отобразить секунды if s < 10 then Label3.Caption := '0'+ IntToStr(s) else Label3.Caption := IntToStr(s);

85 78 Часть I. Delphi XE // щелчок на кнопке "Сброс" procedure TForm1.Button2Click(Sender: TObject); s := 0; m := 0; Label1.Caption := '00'; Label3.Caption := '00'; Panel Компонент Panel, его значок (рис. 3.21) находится на вкладке Standard, представляет собой панель, на поверхность которой можно поместить другие компоненты. Обычно панель используют для привязки компонентов к границе окна (при изменении размера окна компоненты, которые находятся на панели, не меняют своего положения относительно границы окна, к которой "привязана" панель). Некоторые свойства компонента Panel приведены в табл Рис Значок компонента Panel Таблица Свойства компонента Panel Свойство Align BevelOuter Enabled Описание Определяет границу формы, к которой привязана (прикреплена) панель. Панель может быть прикреплена к левой (alleft), правой (alright), верхней (altop) или нижней (albottom) границе Внешняя "фаска" панели. Если значение свойства равно bvnone, то фаска не отображается и поверхность панели находится на одном уровне с поверхностью формы. Если значение свойства равно bvlowered, то поверхность панели утоплена; если bvraised, то поверхность панели приподнята над поверхностью формы Свойство позволяет сделать недоступными (False) все компоненты, которые находятся на панели На рис приведено окно программы "Просмотр иллюстраций" (см. разд. "Image" далее в этой главе), в котором кнопки управления (компоненты SpeedButton) размещены на панели. Панель привязана к нижней границе окна (значение свойства Align равно albottom), поэтому даже в том случае, если пользователь развернет окно программы на весь экран, панель и, следовательно, кнопки все равно будут находиться в нижней части окна.

86 Глава 3. Компоненты 79 Рис Программа "Просмотр иллюстраций" ControlBar Компонент ControlBar, его значок (рис. 3.23) находится на вкладке Additional, представляет собой так называемую панель инструментов, на поверхность которой можно поместить другие компоненты. Обычно панель инструментов находится в верхней части окна, а на ее поверхности размещены командные кнопки. Некоторые свойства компонента TControlBar приведены в табл Рис Значок компонента ControlBar Таблица Свойства компонента ControlBar Свойство Align AutoSize DrawingStyle Описание Определяет границу формы, к которой привязана (прикреплена) панель. Панель может быть прикреплена к левой (alleft), правой (alright), верхней (altop) или нижней (albottom) границе Если значение свойства равно True, то высота панели инструментов автоматически устанавливается равной высоте командных кнопок, находящихся на панели Способ закраски панели: градиент (dsgradient) или сплошная закраска одним цветом (dsnormal).

87 80 Часть I. Delphi XE Таблица Свойства компонента ControlBar Свойство GradientDirection Описание Направление градиента закраски: gdvertical по вертикали; gdhorizontal по горизонтали. Цвета градиентной закраски определяют свойства GradientStartColor и GradientEndColor SpeedButton Компонент SpeedButton, его значок (рис. 3.24) находится на вкладке Additional, представляет собой командную кнопку, на которой находится картинка. Обычно компоненты SpeedButton размещают на поверхности панели (компонента Panel или TollBar). Свойства компонента SpeedButton приведены в табл Рис Значок компонента SpeedButton Таблица Значения свойств компонента SpeedButton Свойство Name Glyph NumGlyphs Flat GroupIndex Down AllowAllUp Left Top Описание Имя (идентификатор) компонента Битовый образ, в котором находятся картинки для каждого из возможных состояний кнопки (доступна, недоступна, нажата, зафиксирована) Количество картинок в битовом образе Glyph Определяет вид кнопки (наличие границы). Если значение свойства равно True, то граница кнопки появляется только при позиционировании указателя мыши на кнопке Идентификатор группы кнопок. Кнопки, имеющие одинаковый идентификатор группы, работают подобно переключателям (RadioButton): нажатие одной из кнопок группы вызывает срабатывание других кнопок этой группы. Чтобы кнопку можно было зафиксировать, значение свойства GroupIndex не должно быть равно 0 Идентификатор состояния кнопки. Изменить значение свойства можно, если значение свойства GroupIndex не равно 0 Свойство определяет возможность отжать кнопку. Если кнопка нажата и значение свойства равно True, то кнопку можно отжать Расстояние от левой границы кнопки до левой границы формы Расстояние от верхней границы кнопки до верхней границы формы

88 Глава 3. Компоненты 81 Таблица 3.15 (окончание) Свойство Height Width Enabled Visible Hint ShowHint Описание Высота кнопки Ширина кнопки Признак доступности кнопки. Если значение свойства равно True, то кнопка доступна. Если значение свойства равно False, то кнопка не доступна Позволяет скрыть кнопку (False) или сделать ее видимой (True) Подсказка текст, который появляется рядом с указателем мыши при позиционировании указателя на командной кнопке (для того чтобы текст появился, значение свойства ShowHint должно быть True) Разрешает (True) или запрещает (False) отображение подсказки при позиционировании указателя на кнопке Свойство Glyph представляет собой битовый образ, в котором находятся картинки для каждого из возможных состояний кнопки (доступна, недоступна, нажата, зафиксирована). Структура битового образа для компонента SpeedButton приведена на рис Доступна Нажата Недоступна Зафиксирована Рис Структура битового образа кнопки SpeedButton Чтобы задать битовый образ, надо в окне Object Inspector выбрать свойство Glyph, сделать щелчок на кнопке с тремя точками, в окне Picture Editor щелкнуть на кнопке Load и в окне Load Picture выбрать BMP-файл, в котором находится битовый образ. В качестве примера на рис приведен битовый образ для кнопки Дальше. Рис Битовый образ для кнопки Дальше

89 82 Часть I. Delphi XE Следует обратить внимание, что левый нижний пиксел картинки задает "прозрачный" цвет элементы рисунка, окрашенные этим цветом, на поверхности кнопки не отображаются. Также необходимо отметить, что BMP-файл, из которого был загружен битовый образ во время разработки формы, во время работы программы не нужен. На рис приведена форма программы "Просмотр иллюстраций", в которой для выбора папки, перехода к следующей и возврата к предыдущей иллюстрациям используются кнопки SpeedButton. Обратите внимание, что сначала на форму нужно поместить компонент Panel и настроить его (присвоить значение свойству Align). После этого на поверхность панели нужно поместить командные кнопки. Компонент Image настраивается последним. Значения свойств компонентов приведены в табл Рис Форма программы "Просмотр иллюстраций" Таблица Значения свойств компонентов Компонент Свойство Значение Panel1 Align albottom Height 32 SpeedButton1 Width 49 Height 22 Flat True Glyph

90 Глава 3. Компоненты 83 Таблица 3.16 (окончание) Компонент Свойство Значение SpeedButton1 NumGlyphs 1 Hint ShowHint Выбор папки True SpeedButton2 Width 49 Height 22 Flat True Glyph NumGlyphs 2 Hint ShowHint Следующая True SpeedButton3 Width 49 Height 22 Flat True Glyph NumGlyphs 2 Hint ShowHint Предыдущая True Image1 Proportional True Align AlignWithMargins alclient True Margins.Bottom 3 Margins.Left 3 Margins.Right 3 Margins.Top 3 StatusBar Компонент StatusBar, его значок (рис. 3.28) находится на вкладке Win32, представляет собой область вывода служебной информации (область состояния). Обычно в области состояния находится несколько панелей.

91 84 Часть I. Delphi XE Свойства компонента StatusBar приведены в табл Рис Значок компонента StatusBar Таблица Свойства компонента StatusBar Свойство Panels SimpleText SimplePanel Описание Коллекция объектов типа TStatusPanel (см. табл. 3.18), каждый из которых представляет собой панель, отображаемую в области состояния Текст, который отображается в поле компонента, если значение свойства SimplePanel равно True Тип компонента. Если значение свойства равно True, то в поле компонента отображается текст, заданный значением свойства SimpleText. Если значение свойства равно False, в поле компонента отображаются панели Таблица Свойства объекта TStatusPanel Свойство Техt Width Описание Текст, отображаемый на панели Ширина панели Чтобы добавить в область состояния панель, надо в окне Object Inspector выбрать свойство Panels, щелчком на кнопке с тремя точками, которая находится в области значения свойства, раскрыть окно Editing и в этом окне щелкнуть на кнопке Add New (рис. 3.29). Рис Чтобы добавить в область состояния панель, надо сделать щелчок на кнопке Add New

92 Глава 3. Компоненты 85 UpDown Компонент UpDown, его значок (рис. 3.30) находится на вкладке Win32, представляет собой две кнопки, нажимая которые можно изменить значение внутренней переменной-счетчика. Обычно компонент UpDown используется в связке с компонентом Edit, что позволяет ввести значение в поле редактирования обычным образом или изменить содержимое поля редактирования с помощью кнопок компонента UpDown. Свойства компонента приведены в табл Рис Значок компонента UpDown Таблица Свойства компонента UpDown Свойство Position Min Max Increment Associate Orientation Описание Счетчик. Значение свойства изменяется в результате щелчка на кнопке Up (увеличивается) или Down (уменьшается). Диапазон изменения определяют свойства Min и Max, величину изменения свойство Increment Нижняя граница диапазона изменения свойства Position Верхняя граница диапазона изменения свойства Position Величина, на которую изменяется значение свойства Position в результате щелчка на одной из кнопок компонента Определяет компонент (например, Edit или Label), используемый в качестве индикатора значения свойства Position. Если в качестве индикатора используется компонент Edit, то при изменении содержимого поля редактирования автоматически меняется значение свойства Position Задает ориентацию кнопок компонента. Кнопки могут быть ориентированы вертикально (udvertical) или горизонтально (udhorizontal) Использование компонента UpDown демонстрирует программа "Будильник" (ее форма показана на рис. 3.31, значения свойств компонентов приведены в табл. 3.21, текст программы в листинге 3.10). Основную работу выполняет процедура обработки события Timer, которое с периодом 1 с генерирует таймер. Процедура сравнивает текущее время (значение функции Now) со временем, на которое установлен будильник (значение переменной AlarmTime). Сравнение выполняет функция CompareTime. Чтобы эта функция, а также функции HourOf и MinuteOf были доступны, в директиву uses надо поместить ссылку на модуль DataUtils. Воспроизведение звукового сигнала обеспечивает функция PlaySound. В качестве ее первого параметра указан стандартный звуковой сигнал

93 86 Часть I. Delphi XE (файлы, в которых находятся звуки, используемые операционной системой, размещаются в каталоге c:\windows\media). Рис Форма программы "Будильник" Таблица Значения свойств компонентов Компонент Свойство Значение UpDown1 Min 0 Max 23 Associate Edit1 UpDown2 Min 0 Max 59 Associate Edit2 Листинг Будильник uses DateUtils, mmsystem; var AlarmTime: TDateTime; // время сигнала procedure TForm1.FormCreate(Sender: TObject); // для доступа к MinuteOf и HourOf в директиву // uses надо добавить ссылку на DateUtils UpDown1.Position := HourOf(Now); UpDown2.Position := MinuteOf(Now);

94 Глава 3. Компоненты 87 // щелчок на кнопке OK procedure TForm1.Button1Click(Sender: TObject); var h,m: word; // час, минуты Timer1.Enabled := False; h := StrToInt(Edit1.Text); m := StrToInt(Edit2.Text); AlarmTime := EncodeTime(h,m,0,0); Timer1.Enabled := True; // пуск таймера Form1.Hide; // свернуть окно программы // сигнал таймера procedure TForm1.Timer1Timer(Sender: TObject); < Функция CompareTime позволяет сравнить два значения типа TTime: A B CompareTime(A,B) 13:40 14: :40 14: :40 14:40 1 >if CompareTime(Now,AlarmTime) >= 0 then Timer1.Enabled := False; if CheckBox1.Checked then PlaySound('notify.wav',0,SND_ASYNC); ShowMessage(FormatDateTime(' hh:nn - ', Now) + Edit3.Text); Form1.Show; // отобразить (развернуть) окно // нажатие клавиши в поле редактирования "Минут" procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char); case Key of #8: ; // <BackSpace> '0'..'9': if Length(Edit1.Text) = 2 then Key := #0;

95 88 Часть I. Delphi XE else Key := #0; // нажатие клавиши в поле редактирования "Секунд" procedure TForm1.Edit2KeyPress(Sender: TObject; var Key: Char); case Key of #8: ; // <Backspace> '0'..'9': if Length(Edit1.Text) = 2 then Key := #0; else Key := #0; ProgressBar Компонент ProgressBar, его значок (рис. 3.32) находится на вкладке Win32, представляет собой индикатор, который обычно используется для наглядного представления протекания процесса, например обработки (копирования) файлов или загрузки информации из сети. Свойства компонента приведены в табл Рис Значок компонента ProgressBar Таблица Свойства компонента ProgressBar Свойство Position Min Max Step Smooth Описание Значение, отображаемое в поле компонента в виде прямоугольника, ширина которого пропорциональна значению свойства Position Нижняя граница диапазона допустимого значения свойства Position Верхняя граница диапазона допустимого значения свойства Position Шаг изменения значения свойства Position, если для изменения значения свойства Position используется метод StepIt Определяет вид индикатора. Если значение свойства равно False, то полоса делится на сегменты

96 Глава 3. Компоненты 89 Использование компонента ProgressBar демонстрирует программа "Угадай число" (ее форма приведена на рис. 3.33, а текст в листинге 3.11). Компонент применяется в качестве индикатора времени, оставшегося на решение поставленной задачи. Следует обратить внимание, что размер компонента подобран так, чтобы в поле компонента отображалось 30 сегментов. Значения свойств компонента ProgressBar приведены в табл Рис Форма программы "Угадай число" Таблица Значения свойств компонента ProgressBar Свойство Значение Mim 0 Max 30 Position 30 Step 1 Smoth False Листинг Угадай число // начало работы программы procedure TForm1.FormCreate(Sender: TObject); Randomize; sn := Random(100) + 1; rem := 30; Label2.Caption := 'Осталось: 30 сек'; Timer1.Interval := 1000; Timer1.Enabled := True;

97 90 Часть I. Delphi XE // сигнал таймера procedure TForm1.Timer1Timer(Sender: TObject); rem := rem - 1; ProgressBar1.StepIt; // изменить значение Position Label2.Caption := 'Осталось: ' + IntToStr(rem) + ' сек'; if rem = 0 then Timer1.Enabled := False; Edit1.Enabled := False; ShowMessage('Вы не справились с поставленной задачей.' + #10 + 'Компьютер "задумал" число ' + IntToStr(sn)); // нажатие клавиши в поле редактирования procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char); var igrok: integer; // вариант игрока Label4.Caption := ''; case Key of '0'..'9': if Length(Edit1.Text) = 3 then Key := #0; // цифры #8: ; // <Backspace> #13: // <Enter> p := p + 1; igrok := StrToInt(Edit1.Text); Label3.Caption := 'Попыток: ' + IntToStr(p); if igrok = sn then // число угадано Timer1.Enabled := False; ShowMessage('Вы справились с поставленной задачей' + #10 + 'за ' + IntToStr(30 - rem) + 'сек.'); Edit1.Enabled := False; end else if igrok < sn then Label4.Caption := 'больше'

98 Глава 3. Компоненты 91 else else Key := #0; Label4.Caption := 'меньше'; Image Компонент Image, его значок (рис. 3.34) находится на вкладке Additional, обеспечивает отображение графики (иллюстраций, фотографий, рисунков). Свойства компонента приведены в табл Рис Значок компонента Image Таблица Свойства компонента Image Свойство Picture Width, Height AutoSize Stretch Proportional Center Align Описание Иллюстрация, которая отображается в поле компонента Размер компонента. Если размер компонента меньше размера иллюстрации, а значения свойств AutoSize, Stretch и Proportional равны False, то отображается часть иллюстрации Признак автоматического изменения размера компонента в соответствии с реальным размером иллюстрации Признак автоматического масштабирования (сжатия или растяжения) иллюстрации в соответствии с реальным размером компонента. Если размер компонента не пропорционален размеру иллюстрации, то иллюстрация будет искажена Признак автоматического масштабирования картинки без искажения. Чтобы масштабирование было выполнено, значение данного свойства должно быть True, а свойства AutoSize False Признак определяет расположение картинки в поле компонента по горизонтали, если ширина картинки меньше ширины поля компонента. Если значение свойства равно True, то картинка располагается в центре поля компонента Задает границу формы, к которой "привязан" компонент. Если значение свойства равно alclient, то размер компонента устанавливается равным размеру "клиентской" (внутренней) области формы, причем, если во время работы программы будет изменен размер формы, автоматически будет изменен и размер компонента

99 92 Часть I. Delphi XE Таблица 3.24 (окончание) Свойство AlignWithMargins Margins Canvas Описание Признак того, что для вычисления положения компонента на поверхности объекта, к которому компонент "привязан", следует использовать значения, заданные свойством Margins. Позволяет установить отступ от границы объекта, к которой компонент привязан Задает расстояния от границы компонента до соответствующей границы объекта (формы), к которой компонент привязан Поверхность компонента Картинку, отображаемую в поле компонента Image, можно задать как во время разработки формы, так и загрузить из файла во время работы программы. Если картинка задана во время разработки формы, то файл, из которого она была загружена, во время работы программы не нужен (копия картинки помещается в файл ресурса программы). Загрузку картинки из файла во время работы программы обеспечивает метод LoadFromFile свойства Picture. Необходимо отметить: для того чтобы во время работы программы в поле компонента можно было загрузить иллюстрацию из JPEG-файла, в директиву uses модуля формы надо добавить ссылку на модуль Jpeg. Использование компонента Image для просмотра фотографий демонстрирует программа "Просмотр иллюстраций" (ее форма приведена на рис. 3.35). Рис Форма программы "Просмотр иллюстраций"

100 Глава 3. Компоненты 93 Необходимо отметить, что во время создания формы сначала нужно настроить компонент Panel (см. разд. "Panel" ранее в этой главе) (присвоить значение albottom свойству Align), а затем компонент Image (значения свойств приведены в табл. 3.25). В листинге 3.12 приведены процедуры обработки событий Click на кнопках SpeedButton. Процедура обработки события Click на SpeedButton1 отображает стандартное окно Выбор папки и формирует список иллюстраций. Для хранения списка иллюстраций в программе используется список строк Pictures (объект типа TStringList). Его объявление надо поместить в секцию private объявления формы. Создает объект Pictures конструктор формы процедура обработки события Create. Таблица Значения свойств компонента Image Свойство Align AlignWithMargins Значение alclient True Margins.Bottom 3 Margins.Left 3 Margins.Right 3 Margins.Top 3 Листинг Просмотр иллюстраций unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, ExtCtrls, FileCtrl, Jpeg; type TForm1 = class(tform) Panel1: TPanel; Image1: TImage; SpeedButton2: TSpeedButton; SpeedButton1: TSpeedButton; SpeedButton3: TSpeedButton; procedure FormCreate(Sender: TObject); procedure SpeedButton1Click(Sender: TObject);

101 94 Часть I. Delphi XE procedure SpeedButton3Click(Sender: TObject); procedure SpeedButton2Click(Sender: TObject); procedure FormResize(Sender: TObject); private apath: string; // каталог, который выбрал пользователь asearchrec : TSearchRec; // информация о файле Pictures : TStringList; // список иллюстраций n: integer; // номер отображаемой иллюстрации public < Public declarations >var Form1: TForm1; implementation // конструктор формы procedure TForm1.FormCreate(Sender: TObject); Pictures := TStringList.Create; SpeedButton2.Enabled := False; SpeedButton3.Enabled := False; // щелчок на кнопке "Папка" procedure TForm1.SpeedButton1Click(Sender: TObject); var r: integer; if SelectDirectory('Выберите папку', '',apath) then apath := apath + '\'; Form1.Caption := 'Просмотр иллюстраций - ' + apath; // сформировать список иллюстраций r := FindFirst(aPath+'*.jpg',faAnyFile,aSearchRec); if r = 0 then

102 Глава 3. Компоненты 95 // в указанном каталоге есть JPEG-файл Pictures.Clear; // очистить список иллюстраций Pictures.Add(aSearchRec.Name); // добавить имя файла // в список иллюстраций // получить имена остальных JPEG-файлов repeat r := FindNext(aSearchRec); // получить имя следующего файла if r = 0 then Pictures.Add(aSearchRec.Name); until (r <> 0); if Pictures.Count > 1 then SpeedButton2.Enabled := True; // отобразить иллюстрацию n := 0; // номер отображаемой иллюстрации try Form1.Image1.Picture.LoadFromFile(aPath + Pictures[n]); Form1.Caption := apath + Pictures[n]; except on EInvalidGraphic do Form1.Image1.Picture.Graphic := nil; if Pictures.Count = 1 then SpeedButton2.Enabled := False; end else // в выбранном каталоге нет JPEG-файлов SpeedButton2.Enabled := False; SpeedButton3.Enabled := False; Form1.Image1.Picture.Graphic := nil; // вывод следующей картинки procedure TForm1.SpeedButton2Click(Sender: TObject); // вывести картинку n := n+1;

103 96 Часть I. Delphi XE try Form1.Image1.Picture.LoadFromFile(aPath + Pictures[n]); Form1.Caption := apath + Pictures[n]; except on EInvalidGraphic do Form1.Image1.Picture.Graphic := nil; if n = Pictures.Count-1 then SpeedButton2.Enabled := False; // если кнопка "Предыдущая" не доступна, сделать ее доступной if (n > 0) and SpeedButton3.Enabled = False then SpeedButton3.Enabled := True; // вывод предыдущей картинки procedure TForm1.SpeedButton3Click(Sender: TObject); // вывести картинку n := n-1; try Form1.Image1.Picture.LoadFromFile(aPath + Pictures[n]); Form1.Caption := apath + Pictures[n]; except on EInvalidGraphic do Form1.Image1.Picture.Graphic := nil; if n = 0 then SpeedButton3.Enabled := False; // если кнопка "Следующая" не доступна, // сделать ее доступной if (n < Pictures.Count) and SpeedButton2.Enabled = False then SpeedButton2.Enabled := True; // изменился размер окна procedure TForm1.FormResize(Sender: TObject);

104 Глава 3. Компоненты 97 // изменить положение командных кнопок // кнопка "Назад" SpeedButton3.Left := Round(Panel1.Width/2)-SpeedButton3.Width - 5; // кнопка "Вперед" SpeedButton2.Left := Round(Panel1.Width/2) + 5; end. MainMenu Компонент MainMenu, его значок (рис. 3.36) находится на вкладке Standard, представляет собой строку главного меню. Рис Значок компонента MainMenu После того как значок компонента будет помещен на форму, его нужно настроить. Сначала надо определить структуру меню. Для этого необходимо двойным щелчком на значке компонента раскрыть окно редактора меню. В начале работы над новым меню в окне редактора меню находится одинединственный прямоугольник, который изображает новый элемент меню (свойства элемента меню отображаются в окне Object Inspector). Сначала в поле значения свойства Caption нужно ввести название меню, например Файл, и нажать клавишу <Enter>. В результате чего в меню будет добавлен элемент (создан объект типа TMenuItem), а в окне редактора меню появятся два прямоугольника: снизу и справа от выбранного элемента меню. Следует обратить внимание, что по умолчанию редактор меню присваивает каждому созданному элементу меню имя, которое состоит из буквы N и порядкового номера элемента. Так, первый элемент меню получает имя N1, второй N2 и т. д. Чтобы добавить в созданное меню команду, надо выбрать прямоугольник, который находится снизу, и в поле значения свойства Caption ввести название команды, например Открыть. Чтобы добавить раздел меню, надо выбрать тот прямоугольник, который находится справа, и в поле значения свойства Caption ввести название раздела меню, например Справка. В качестве примера на рис приведено окно редактора меню, в котором отображается меню программы MEdit. После того как структура меню будет определена, можно выполнить его окончательную настройку. Каждый элемент меню представляет собой объект типа TMenuItem (свойства объекта приведены в табл. 3.26).

105 98 Часть I. Delphi XE Рис Окно редактора меню (настройка компонента MainMenu) Таблица Свойства объекта TMenuItem Свойство Name Caption Bitmap Enabled ShortCut Описание Идентификатор элемента меню Название элемента меню или команды Картинка, которая отображается слева от названия элемента меню Признак доступности элемента меню (True элемент доступен, False недоступен) Функциональная клавиша или комбинация клавиш, например <Ctrl>+<Z>, с помощью которой можно быстро выбрать элемент меню Рис Форма программы Medit

106 Глава 3. Компоненты 99 Использование компонента MainMenu демонстрирует программа MEdit (ее форма показана на рис. 3.38, а значения свойств элементов меню приведены в табл. 3.27). После того как меню будет создано и настроено, надо для каждого элемента меню задать действие, которое должно быть выполнено в результате выбора этого элемента меню. Чтобы это сделать, надо в окне Object Inspector выбрать объект MenuItem, соответствующий нужному элементу меню, и далее обычным образом создать процедуру обработки события Click. Процедуры обработки событий приведены в листинге Таблица Значения свойств компонентов Компонент (объект) Свойство Значение N1 Name N1 Caption Файл N2 Name N2 Caption Новый Bitmap N3 Name N3 Caption Открыть. Bitmap N4 Name N4 Caption Сохранить как. Bitmap N5 Name N5 Caption Выход N6 Name N6 Caption Справка N7 Name N7 Caption Enabled О программе False Листинг MEdit простой редактор текста // команда Файл >> Новый procedure TForm1.N2Click(Sender: TObject); var r: integer; // идентификатор кнопки

107 100 Часть I. Delphi XE if Memo1.Modified then r := MessageDlg('Текст был изменен. Создать новый документ' + #10 +'без сохранения изменений в текущем?', mtwarning,[mbyes,mbno],0,mbno); if r = mryes then Memo1.Clear; end else Memo1.Clear; // команда Файл >> Открыть procedure TForm1.N3Click(Sender: TObject); var r: integer; // идентификатор кнопки if Memo1.Modified then r := MessageDlg('Текст был изменен. Открыть новый файл' + #10 + 'без сохранения изменений?', mtwarning,[mbyes,mbno],0,mbno); if r = mrno then exit; // продолжить работу // отобразить окно "Открыть" if OpenDialog1.Execute then // пользователь выбрал файл Memo1.Lines.LoadFromFile(OpenDialog1.FileName); FileName := OpenDialog1.FileName; Form1.Caption := 'MEdit - ' + FileName; // команда Файл >> Сохранить как procedure TForm1.N4Click(Sender: TObject); SaveDialog1.FileName := FileName;

108 Глава 3. Компоненты 101 if SaveDialog1.Execute then // сохранить текст в файле Memo1.Lines.SaveToFile(FileName); // команда Файл >> Выход procedure TForm1.N5Click(Sender: TObject); var r: integer; // идентификатор кнопки if Memo1.Modified then r := MessageDlg('Текст был изменен. Завершить работу' + #10 + 'программы без сохранения изменений?', mtwarning,[mbyes,mbno],0,mbno); if r = mryes then Close; // завершить работу программы // щелчок на кнопке "Закрыть" procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean); var r: integer; // идентификатор кнопки if Memo1.Modified then r := MessageDlg('Текст был изменен. Завершить работу' + #10 + 'программы без сохранения изменений?', mtwarning,[mbyes,mbno],0,mbno); if r = mrno then CanClose := False; // не завершать работу программы OpenDialog Компонент OpenDialog, его значок (рис. 3.39) находится на вкладке Dialogs, представляет собой диалог Открыть. Свойства компонента приведены в табл Отображение диалога обеспечивает метод Execute, значение которого

109 102 Часть I. Delphi XE позволяет определить, щелчком на какой кнопке, Открыть или Отмена, пользователь закрыл диалог. Рис Значок компонента OpenDialog Таблица Свойства компонента OpenDialog Свойство Title Filter FilterIndex InitialDir FileNane Описание Текст в заголовке окна. Если значение свойства не указано, то в заголовке отображается текст Открыть Свойство задает список фильтров имен файлов. В списке файлов отображаются только те файлы, имена которых соответствуют выбранному (текущему) фильтру. Во время отображения диалога пользователь может выбрать фильтр в списке Тип файлов. Каждый фильтр задается строкой вида описание маска, например Текст *.txt Если в списке Filter несколько элементов (например, Текст *.txt Все файлы *.*), то значение свойства задает фильтр, который используется в момент появления диалога на экране Каталог, содержимое которого отображается при появлении диалога на экране. Если значение свойства не указано, то в окне диалога отображается содержимое папки Мои документы Имя файла, выбранного пользователем Использование компонента OpenDialog для выбора файла, содержимое которого надо отобразить в поле компонента Memo, демонстрирует программа "Просмотр текста" (ее форма приведена на рис. 3.40). В меню Файл находятся два элемента: Открыть (идентификатор N2) и Выход (идентификатор N3). Отображение окна Открыть файл и загрузку выбранного файла обеспечивает процедура обработки события Click на элементе меню N2 (листинг 3.14). Значения свойств компонентов программы приведены в табл Компонент Свойство Значение OpenDialog1 Title Открыть файл Таблица Значения свойств компонентов Filter Текст *.txt Все файлы *.* Memo1 Align alclient ReadOnly True

110 Глава 3. Компоненты 103 Рис Форма программы "Просмотр текста" Листинг Просмотр текста // команда Файл >> Открыть procedure TForm1.N2Click(Sender: TObject); if OpenDialog1.Execute then // пользователь выбрал файл Memo1.Lines.LoadFromFile(OpenDialog1.FileName); // команда Файл >> Выход procedure TForm1.N3Click(Sender: TObject); Close; SaveDialog Компонент SaveDialog, его значок (рис. 3.41) находится на вкладке Dialogs, представляет собой диалог Сохранить. Свойства компонента приведены в табл Отображение диалога обеспечивает метод Execute, значение которого позволяет определить, щелчком на какой кнопке, Сохранить или Отмена, пользователь закрыл диалог. Рис Значок компонента SaveDialog

111 104 Часть I. Delphi XE Таблица Свойства компонента SaveDialog Свойство Title Filter FilterIndex InitialDir FileNane DefaultExt Описание Текст в заголовке окна. Если значение свойства не указано, то в заголовке отображается текст Сохранить как Свойство задает список фильтров имен файлов. В списке файлов отображаются только те файлы, имена которых соответствуют выбранному (текущему) фильтру. Во время отображения диалога пользователь может выбрать фильтр в списке Тип файлов. Каждый фильтр задается строкой вида описание маска, например Текст *.txt Если в списке Filter несколько элементов (например, Текст *.txt Все файлы *.*), то значение свойства задает фильтр, который используется в момент появления диалога на экране Каталог, содержимое которого отображается при появлении диалога на экране. Если значение свойства не указано, то в окне диалога отображается содержимое папки Мои документы Имя файла, введенное пользователем в поле Имя файла Расширение, которое будет добавлено к имени файла, если в поле Имя файла пользователь не задаст расширение файла В качестве примера использования диалога SaveDialog в листинге 3.15 приведена процедура обработки события CloseQuery главного окна простого редактора текста (форма представлена на рис. 3.42, значения свойств компонента SaveDialog в табл. 3.31). Рис Форма простого редактора текста Таблица Значения свойств компонента SaveDialog Свойство DefaultExt Filter Значение txt Текст *.txt

112 Глава 3. Компоненты 105 Листинг Обработка события CloseQuery // запрос на завершение работы с программой (попытка закрыть окно) procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean); var r: integer; // идентификатор кнопки, нажатой пользователем // в окне MessageDlg if Memo1.Modified then r := MessageDlg('Текст изменен. Сохранить изменения?', mtwarning,[mbyes,mbno,mbcancel],0); case r of mryes: // записать текст в файл if OpenDialog1.FileName <> '' then // имя файла задано (редактируется загруженный файл) Memo1.Lines.SaveToFile(OpenDialog1.FileName) else // имя файла не задано, отобразить SaveDialog if SaveDialog1.Execute then // пользователь задал имя файла Memo1.Lines.SaveToFile(SaveDialog1.FileName) else // не задано имя файла, // продолжить работу с программой CanClose := False; mrcancel: // продолжить работу с программой CanClose := False; Компоненты Vista Одной из отличительных черт Windows Vista и, соответственно, Windows 7 от предыдущих версий Windows является новый дизайн и существенно расширенная

113 106 Часть I. Delphi XE функциональность диалоговых окон. Окна диалогов Открыть и Сохранить, а также окна информационных сообщений в Windows Vista выглядят несколько иначе, чем в предыдущих версиях Windows. Для того чтобы в полной мере использовать возможности Vista, чтобы диалоги Открыть, Сохранить и окна сообщений в программе, работающей в Windows Vista, отображались в стиле Vista, вместо компонентов OpenDialog, SaveDialog и функции MessageDlg следует использовать компоненты FileOpenDialog, FileSaveDialog и TaskDialogs (рис. 3.43). Рис Компоненты Vista Dialogs Следует обратить внимание, что отображение диалогов обеспечивает операционная система. Поэтому при попытке отобразить Vista-диалог в программе, работающей в Windows предыдущей версии, например в Windows XP или Windows 2000, возникает ошибка. Чтобы предотвратить возникновение ошибки при попытке отобразить Vista-диалог в случае, если программа запущена не из Vista, в код необходимо добавить инструкции, которые будут проверять версию операционной системы и активизировать отображение диалога, соответствующего версии операционной системы. TaskDialog Компонент TaskDialog предназначен для отображения окон сообщений (рис. 3.44). Помимо текста и стандартных командных кнопок (Да, Нет, Отменить и др.) в окне диалога могут отображаться определенные программистом кнопки, переключатели и другие элементы управления. Кроме этого в тексте сообщений могут быть ссылки, например на разделы справочной информации. Свойства компонента TaskDialog приведены в табл Рис Пример сообщения

114 Глава 3. Компоненты 107 Таблица Свойства компонента TaskDialog Свойство Caption Title MainIcon CustomMainIcon Text CommonButtons ExpandedText ExpandButtonCaption FooterText FooterIcon Buttons RadioButtons ProgressBar ProgressBar.Position ProgressBar.Min Описание Текст в заголовке окна сообщения (обычно название программы, которая вывела окно сообщения) Заголовок сообщения Значок, отображаемый слева от заголовка сообщения. Обычно это один из стандартных значков: Information (tdiinformation), Warning (tdiwarning) или Error (tdierror). Если значение свойства равно tdinone, то отображается значок, заданный значением свойства CustemMainIcon Значок, отображаемый слева от заголовка сообщения. Появляется, если значение свойства MainIcon равно tdinone Основной текст сообщения Стандартные кнопки, отображаемые в окне диалога. Уточняющие свойства tcbok, tcbyes, tcbno, tcbcancel, tcbretry и tcbclose определяют, какие кнопки должны отображаться Поясняющий текст. Отображается в результате щелчка на кнопке ExpandButton Текст, отображаемый рядом с кнопкой ExpandButton (по умолчанию Дополнительно) Дополнительный текст, отображаемый в нижней части окна диалога (нижний колонтитул). Если значение свойства задано, то перед текстом отображается компонент CheckBox Значок, который отображается в области вывода нижнего колонтитула Дополнительные, определенные программистом, командные кнопки Дополнительные, определенные программистом, переключатели Индикатор протекания процесса Значение, отображаемое в поле индикатора в виде закрашенной области Нижняя граница области допустимого значения свойства Position

115 108 Часть I. Delphi XE Таблица 3.32 (продолжение) Свойство ProgressBar.Max ProgressBar.State ProgressBar.MarqueeSpeed Flags.tfEnableHyperlinks Flags.tfUseHiconMain Flags.tfExpandFooterArea Flags.tfUseHiconFooter Описание Верхняя граница области допустимого значения свойства Position Характеристика процесса, для отображения которого используется индикатор (pbsnormal нормальное протекание, pbspaused процесс приостановлен, pbserror процесс завершен с ошибкой) Скорость перемещения маркера внутри полосы (закрашенной области) индикатора Разрешает/запрещает использование HTMLтега <A> в тексте сообщений (основного, пояснительного, дополнительного). Если значение свойства равно True, то вместо текста тега отображается соответствующая ссылка Разрешает/запрещает отображение значка, заданного значением свойства MainIcon. Если значение свойства равно True, то перед текстом заголовка отображается значок, заданный свойством MainIcon, в противном случае свойством CustomMainIcon Управляет отображением области нижнего колонтитула. Если значение свойства равно False, то область нижнего колонтитула не отображается Разрешает/запрещает отображение значка, заданного значением свойства FooterIcon. Если значение свойства равно True, то в области нижнего колонтитула, перед компонентом CheckBox отображается значок, заданный свойством FooterIcon, в противном случае значок не отображается Flags.tfVerificationFlagChecked Определяет состояние компонента CheckBox, отображаемого в области нижнего колонтитула Flags.tfShowProgressBar Flags.tfShowMarqueeProgressBar Разрешает/запрещает отображение индикатора состояния процесса. Если значение свойства равно True, то индикатор отображается, в противном случае нет Разрешает/запрещает отображение маркера на индикаторе состояния процесса. Если значение свойства равно True, то маркер отображается, в противном случае нет

116 Глава 3. Компоненты 109 Таблица 3.32 (окончание) Свойство Flags.tfNoDefaultRadioButton ModalDesult RadioButton.ID Описание Управляет отображением состояния переключателей. Если значение свойства равно True, то при появлении диалога ни один из переключателей не является выбранным (пользователь должен сделать выбор) Идентификатор командной кнопки, щелчком на которой пользователь закрыл окно диалога: mrok, mryes, mrno, mrcancel, mrretry или mrclose. Если окно закрыто в результате щелчка на определенной пользователем командной кнопке, то по умолчанию значение свойства для первой кнопки равно 100, для второй 101 и т. д. Идентификатор переключателя, выбранного пользователем Значения свойств компонента TaskDialog устанавливаются в окне Object Inspector обычным образом. Чтобы увидеть, как будет выглядеть диалог во время работы программы, надо сделать двойной щелчок левой кнопкой мыши на находящемся на форме значке компонента или выбрать из контекстного меню компонента команду Test Dialog. Отображение диалога TaskDialog обеспечивает метод-функция Execute. Этот метод возвращает значение False, если пользователь завершил диалог щелчком на кнопке Close или Закрыть. Если окно закрыто щелчком на какой-либо другой кнопке, значение метода равно True. При этом свойство ModalResult содержит идентификатор нажатой кнопки. Как было сказано ранее, при попытке отобразить диалог TaskDialog не в Windows Vista возникает исключение. Поэтому, перед тем как активизировать процесс отображения диалога, необходимо проверить версию операционной системы. Код, позволяющий проверить версии ОС: // определить версию ОС OSVersionInfo.dwOSVersionInfoSize := sizeof(tosversioninfo); GetVersionEx(OSVersionInfo); if OSVersionInfo.dwMajorVersion >= 6 then // OC Vista end else // не Vista

117 110 Часть I. Delphi XE Использование диалога TaskDialog для вывода сообщения о неверных данных демонстрирует программа "Доход по вкладу" (ее окно показано на рис. 3.45, значения свойств компонента TaskDialog в табл. 3.33, процедура обработки события Click на кнопке Ok в листинге 3.16). Следует обратить внимание, что программа спроектирована так, что она будет работать как в Windows Vista, так и в Windows предыдущих версий. В Vista вывод сообщений обеспечивает диалог TaskDialog, в ОС предыдущих версий функция MessageDlg. Пример сообщения приведен на рис Рис Форма программы "Доход по вкладу" Таблица Значения свойств компонента TaskDialog Свойство Caption Title MainIcon Text CommonButtons Значение Доход по вкладу Ошибка tdierror Неверные исходные данные tcbok Листинг Щелчок на кнопке Ok // щелчок на кнопке Ok procedure TForm1.Button1Click(Sender: TObject); var summ: real; // сумма вклада period: integer; // срок вклада (дней) percent: real; // процентная ставка (годовых)

118 Глава 3. Компоненты 111 profit: real; total: real; ermsg: string; // доход // сумма в конце срока вклада // сообщение об ошибке OSVersionInfo: TOSVersionInfo; // версия ОС summ := StrToFloat(Edit1.Text); period := StrToInt(Edit2.Text); // проверить исходные данные ermsg := ''; if summ < 5000 then ermsg := 'Сумма вклада должна быть не менее 5000 руб.' + #10; if (period < 6) or (period > 24) then ermsg := ermsg + 'Срок вклада должен быть от 6 до 24 мес.'; if ermsg <> '' then Label3.Caption := ''; // определить версию ОС OSVersionInfo.dwOSVersionInfoSize := sizeof(tosversioninfo); GetVersionEx(OSVersionInfo); if OSVersionInfo.dwMajorVersion >= 6 then // OC Vista TaskDialog1.ExpandedText := ermsg; TaskDialog1.Execute; end else // не Vista MessageDlg(ermsg,mtError,[mbOk],0); end else // определить процентную ставку case period of 1..3: percent := 9.5; 4..6: percent := 11; 7..12: percent := 12.5; else percent:=14.5;

119 112 Часть I. Delphi XE profit := summ * (percent/100/ 12) * period; total := summ + profit; Label3.Caption := 'Сумма: ' + FloatToStrF(summ,ffCurrency,6,2) + #10 + 'Срок: ' + IntToStr(period) + ' мес.' + #10 + 'Процентная ставка: ' + FloatToStrF(percent,ffgeneral,6,2) + '%' + #10 + ' ' + #10 + 'Доход: ' + FloatToStrF(profit,ffCurrency,6,2) + #10 + 'Сумма в конце срока: ' + FloatToStrF(total,ffCurrency,6,2); Рис Пример сообщения об ошибке FileOpenDialog и FileSaveDialog Компоненты FileOpenDialog и FileSaveDialog обеспечивают отображение диалогов Открыть и Сохранить соответственно. Свойства компонентов приведены в табл и Таблица Свойства компонента FileOpenDialog Свойство Title DefaultFolder DefaultExtension Описание Текст в заголовке окна. Если значение свойства не задано, то в заголовке отображается текст Открыть Папка, содержимое которой отображается в окне диалога при появлении его на экране. Если значение свойства не установлено, то в окне диалога отображается содержимое папки Мои документы Расширение, которое добавляется к имени файла, введенному в поле Имя файла, в случае если пользователь явно не указал расширение

120 Глава 3. Компоненты 113 Таблица 3.34 (окончание) Свойство FileTypes FileTypeIndex FileNane FileNameLabel OkButtonLabel FavoriteLinks Options fdonochangedir fdofilemustexist fdopathmustexist fdonotaddtorecent fdohiddepinnedplaces Описание Свойство задает список фильтров имен файлов. В окне диалога отображаются только те файлы, имена которых соответствуют текущему фильтру. Список формируется путем добавления элементов в коллекцию FileTypes с последующей установкой значений свойств DisplayName и FileMask. Свойство DisplayName задает имя фильтра (например, Все файлы), а свойство FileMask фильтр (например, *.*) Задает текущий фильтр Имя выбранного файла Текст, который отображается перед полем ввода. Если значение свойства не задано, то перед полем ввода отображается текст Имя файла Текст, который отображается на кнопке OK. Если значение свойства не задано, то на кнопке отображается текст Открыть Список имен каталогов, который добавляется в список Избранные ссылки, отображаемый в окне диалога Свойство представляет собой совокупность уточняющих свойств и позволяет выполнить "тонкую" настройку диалога Запрещает (True) сменить текущий (открыть другой) каталог Активизирует (True) режим проверки существования файла, имя которого пользователь ввел в поле редактирования Активизирует (True) режим проверки существования каталога, имя которого пользователь ввел в поле редактирования Управляет записью в список Недавние документы имен файлов, выбранных пользователем в окне диалога. Если значение свойства True, то имя файла в списке Недавние документы не фиксируется Управляет отображением избранных ссылок. Если значение свойства True, то отображаются только ссылки, определенные программистом (свойство FavoriteLinks), другие ссылки, например Документы, Рабочий стол и пр., не отображаются Таблица Свойства компонента FileSaveDialog Свойство Title Описание Текст в заголовке окна. Если значение свойства не задано, то отображается текст Сохранить как

121 114 Часть I. Delphi XE Таблица 3.35 (продолжение) Свойство FileNameLabel FileNane OkButtonLabel DefaultFolder DefaultExtension FileTypes FileTypeIndex FavoriteLinks Options fdonochangedir fdofilemustexist fdopathmustexist fdonotaddtorecent Описание Текст, который отображается перед полем ввода имени файла. Если значение свойства не задано, то перед полем ввода имени файла отображается текст Имя файла Имя файла, введенное пользователем в поле редактирования Имя файла Текст, который отображается на кнопке OK. Если значение свойства не задано, то на кнопке отображается текст Сохранить Папка, содержимое которой отображается в окне диалога при появлении его на экране. Если значение свойства не установлено, то в окне диалога отображается содержимое папки Мои документы Расширение, которое добавляется к имени файла, введенному в поле Имя файла, в случае если пользователь явно не указал расширение Свойство задает список фильтров имен файлов. В списке файлов отображаются только те файлы, имена которых соответствуют текущему фильтру. Список формируется путем добавления элементов в коллекцию FileTypes с последующей установкой значений свойств DisplayName и FileMask. Свойство DisplayName задает имя фильтра (например, Все файлы), а свойство FileMask фильтр (например, *.*) Задает текущий фильтр Список имен каталогов, который добавляется в список Избранные ссылки, отображаемый в окне диалога Свойство представляет собой совокупность уточняющих свойств и позволяет выполнить "тонкую" настройку диалога Запрещает (True) сменить текущий (открыть другой) каталог Активизирует (True) режим проверки существования файла, имя которого пользователь ввел в поле редактирования Активизирует (True) режим проверки существования каталога, имя которого пользователь ввел в поле редактирования Управляет записью в список Недавние документы имен файлов, выбранных пользователем в окне диалога. Если значение свойства True, то имя файла в списке Недавние документы не фиксируется

122 Глава 3. Компоненты 115 Таблица 3.35 (окончание) Свойство fdohiddepinnedplaces Описание Управляет отображением избранных ссылок. Если значение свойства True, то отображаются только ссылки, определенные программистом (свойство FavoriteLinks), другие ссылки, например Документы, Рабочий стол и пр., не отображаются Отображение диалогов FileOpenDialog и FileSaveDialog обеспечивает методфункция Execute. Значение, возвращаемое методом, позволяет определить, щелчком на какой кнопке, Открыть (для диалога FileOpenDialog), Сохранить (для диалога FileSaveDialog) или Отмена, пользователь закрыл окно диалога. Использование компонентов FileOpenDialog и FileSaveDialog демонстрирует программа NkEdit (редактор текста), ее форма приведена на рис. 3.47, а текст в листинге В начале своей работы программа проверяет ОС (делает это процедура обработки события Activate), и если операционная система не Vista, завершает работу. Рис Форма программы NkEdit Листинг Модуль формы NkEdit unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Menus, StdCtrls;

123 116 Часть I. Delphi XE type TForm1 = class(tform) MainMenu1: TMainMenu; N1: TMenuItem; N2: TMenuItem; N3: TMenuItem; N4: TMenuItem; TaskDialog1: TTaskDialog; Memo1: TMemo; FileSaveDialog1: TFileSaveDialog; FileOpenDialog1: TFileOpenDialog; N5: TMenuItem; TaskDialog2: TTaskDialog; procedure FormCreate(Sender: TObject); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure N4Click(Sender: TObject); procedure N3Click(Sender: TObject); procedure N2Click(Sender: TObject); procedure N5Click(Sender: TObject); procedure FormActivate(Sender: TObject); private < Private declarations >afile: string; // файл, в котором надо сохранить документ function ToFile(var afile: string):boolean; public < Public declarations >var Form1: TForm1; implementation // конструктор формы procedure TForm1.FormCreate(Sender: TObject); //Form1.Width := 470; //Form1.Height := 520;

124 Глава 3. Компоненты 117 // команда Файл >> Открыть procedure TForm1.N2Click(Sender: TObject); var r: boolean; // true текст записан в файл r := true; if Memo1.Modified then // сохранить текст, находящийся в поле редактирования TaskDialog1.Title := 'Текст изменен'; if afile = '' then TaskDialog1.Text := 'Сохранить набранный текст в файле?' else TaskDialog1.Text := 'Сохранить измененный текст в файле?'; TaskDialog1.Execute; // запрос о необходимости сохранения текста case TaskDialog1.ModalResult of mryes: r := ToFile(aFile); if r then Memo1.Modified := false; mrno: r := true; mrcancel: r := false; // открыть файл if r and FileOpenDialog1.Execute then afile := FileOpenDialog1.FileName; Memo1.Lines.LoadFromFile(aFile); Form1.Caption := 'NkEdit - ' + afile; // команда Файл >> Сохранить procedure TForm1.N3Click(Sender: TObject);

125 118 Часть I. Delphi XE if ToFile(aFile) then Form1.Caption := 'NkEdit - ' + afile; // команда Файл >> Выход procedure TForm1.N4Click(Sender: TObject); Form1.Close; // команда "О программе" procedure TForm1.N5Click(Sender: TObject); TaskDialog2.Execute; // попытка закрыть окно программы procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean); if Memo1.Modified // текст изменен then TaskDialog1.Title := 'Завершение работы'; if afile = '' then TaskDialog1.Text := 'Сохранить набранный текст в файле?' else TaskDialog1.Text := 'Сохранить измененный текст в файле?'; TaskDialog1.Execute; // какую кнопку нажал пользователь? case TaskDialog1.ModalResult of mryes: if ToFile(aFile) then CanClose := true; mrno: CanClose := true; mrcancel: CanClose := false; end else CanClose := true; // можно закрыть окно программы // записать в файл текст, находящийся в поле Memo1 // возвращает False, если пользователь отменил операцию

126 Глава 3. Компоненты 119 function TForm1.ToFile(var afile: string): boolean; var r: boolean; r := false; if afile <> '' then // имя файла задано, сохранить Memo1.Lines.SaveToFile(aFile); r := true; end else // отобразить диалог "Сохранить" if FileSaveDialog1.Execute then // пользователь задал имя файла afile := FileSaveDialog1.FileName; Memo1.Lines.SaveToFile(aFile); Memo1.Modified := false; r := true; end else r:= false; // пользователь не ввел имя файла ToFile := r; procedure TForm1.FormActivate(Sender: TObject); var OSVersionInfo: TOSVersionInfo; // определить версию ОС OSVersionInfo.dwOSVersionInfoSize := sizeof(tosversioninfo); GetVersionEx(OSVersionInfo); if OSVersionInfo.dwMajorVersion < 6 then MessageDlg('Программа требует Windows Vista или выше', mterror, [mbok],0); Form1.Close; end end.

127 ЧАСТЬ II ПРАКТИКУМ ПРОГРАММИРОВАНИЯ Эта часть книги посвящена практике программированию графики, мультимедиа, разработке программ работы с базами данных. В ней также рассматриваются вопросы создания справочной системы и программы-установщика.

128 ГЛАВА 4 Графика В этой главе рассказывается, что надо сделать, чтобы на поверхности формы появилась картинка, сформированная из графических элементов (например, график или диаграмма), иллюстрация, созданная в графическом редакторе, или фотография. Также вы познакомитесь с принципами реализации анимации, узнаете, как "оживить" картинку. Графическая поверхность Отображение графики обеспечивают компоненты Image и PaintBox. Компонент Image обычно используется для отображения иллюстраций (в том числе и фотографий), загружаемых из файла, компонент PaintBox в качестве поверхности, на которой графика формируется из отдельных элементов во время работы программы. Рис Координаты точек графической поверхности

129 124 Часть II. Практикум программирования Графика, отображаемая в поле компонента PaintBox, формируется на его графической поверхности. Графическая поверхность представляет собой совокупность отдельных точек (пикселов), каждая из которых может быть окрашена каким-либо одним цветом. Положение пиксела на графической поверхности характеризуется горизонтальной (X) и вертикальной (Y) координатами. Координаты отсчитываются от левого верхнего угла и возрастают сверху вниз и слева направо (рис. 4.1). Левый верхний пиксел имеет координаты (0, 0). Доступ к графической поверхности компонента обеспечивает свойство Canvas (canvas с англ. "поверхность", "холст для рисования"), представляющее собой объект типа TCanvas. Рисование графических примитивов (линий, прямоугольников, окружностей и т. д.) обеспечивают методы класса TCanvas (табл. 4.1). Свойства класса TCanvas (табл. 4.2) определяют вид линий (цвет, толщину, стиль) и областей (цвет и стиль закраски). Таблица 4.1. Методы класса TCanvas Метод LineTo(x,y) Действие Рисует линию из текущей точки в точку с указанными координатами (перемещение указателя текущей точки в нужную обеспечивает метод MoveTo). Цвет линии определяет свойство Pen.Color Rectangle(x1,y1,x2,y2) Рисует прямоугольник. Параметры x1, y1 и x2, y2 задают координаты находящихся на одной диагонали углов прямоугольника. Цвет границы прямоугольника определяет значение свойства Pen.Color, цвет закраски области свойство Brush.Color RoundRect(x1,y1,x2,y2,x3,y3) Рисует прямоугольник со скругленными углами. Параметры x1, y1 и x2, y2 задают координаты находящихся на одной диагонали углов прямоугольника, параметры x3, y3 радиус скругления. Цвет границы прямоугольника определяет значение свойства Pen.Color, цвет закраски области свойство Brush.Color Ellipse(x1,y1,x2,y2) Рисует эллипс (окружность). Параметры x1, y1, x2, y2 задают координаты углов прямоугольника, внутри которого вычерчивается эллипс (окружность, если прямоугольник является квадратом). Цвет границы определяет значение свойства Pen.Color, цвет закраски области свойство Brush.Color Arc(x1,y1,x2,y2,x3,y3,x4,y4) Рисует дугу. Параметры x1, y1, x2 и y2 задают эллипс, частью которого является дуга, параметры x3, y3, x4 и y4 начальную и конечную точки дуги. Цвет дуги определяет свойство Pen.Color

130 Глава 4. Графика 125 Таблица 4.1 (окончание) Метод Действие Pie(x1,y1,x2,y2,x3,y3,x4,y4) Рисует сектор. Параметры x1, y1, x2 и y2 задают эллипс, частью которого является сектор, параметры x3, y3, x4 и y4 границы сектора. Цвет границы сектора определяет свойство Pen.Color, цвет закраски сектора свойство Brush.Color FillRect(aRect) FrameRect(aRect) Polyline(points,n) Рисует закрашенный прямоугольник. Параметр arect (тип TRect) определяет положение и размер прямоугольника. Цвет закраски области определяет свойство Brush.Color Рисует контур прямоугольника. Параметр arect (тип TRect) определяет положение и размер прямоугольника. Цвет контура определяет свойство Brush.Color Рисует ломаную линию. Points массив типа TPoint. Каждый элемент массива представляет собой запись, поля x и y которой содержат координаты точки перегиба ломаной. n количество звеньев ломаной. Метод Polyline вычерчивает ломаную линию, последовательно соединяя прямыми точки, координаты которых находятся в массиве: первую со второй, вторую с третьей, третью с четвертой и т. д. Таблица 4.2. Свойства класса TCanvas Свойство Pen PenPos Brush Font Описание Карандаш. Определяет цвет, стиль и толщину линии, которую рисует, например, метод LineTo Положение (координаты) карандаша Кисть. Определяет цвет и стиль закраски области, например прямоугольника, который рисует метод Rectangle Шрифт. Определяет шрифт, который используется для вывода текста, например методом TextOut Необходимо обратить внимание, что у формы также есть свойство Canvas, т. е. программа может вывести графику не только в поле компонента PaintBox, но и непосредственно на поверхность формы. Методы вывода графических примитивов рассматривают свойство Canvas как поверхность, на которой они могут рисовать.

131 126 Часть II. Практикум программирования Следует обратить внимание на важный момент. Графику на поверхности формы или компонента PaintBox должна формировать процедура обработки события Paint. Это событие возникает в момент запуска программы, когда окно появляется на экране первый раз, а также всякий раз, когда окно (или его часть) появляется на экране снова, например после того как пользователь сдвинет другое окно, частично или полностью перекрывающее окно программы, или развернет окно программы, если оно было свернуто. Карандаш и кисть Вид графического элемента определяют свойства Pen (Карандаш) и Brush (Кисть) той поверхности (Canvas), на которой рисует метод. Карандаш и кисть, являясь свойствами объекта Canvas, представляют собой объекты Pen и Brush. Свойства объекта Pen (табл. 4.3) определяют цвет, толщину и тип линии, свойства объекта Brush (табл. 4.4) цвет и стиль закраски областей. Таблица 4.3. Свойства объекта Pen (Карандаш) Свойство Color Width Style Описание Цвет линии Толщина линии (задается в пикселах) Вид линии: pssolid сплошная; psdash пунктирная, длинные штрихи; psdot пунктирная, короткие штрихи; psdashdot пунктирная, чередование длинного и короткого штрихов; psdashdotdot пунктирная, чередование одного длинного и двух коротких штрихов; psclear линия не отображается (используется, если не надо изображать границу области, например прямоугольника) Таблица 4.4. Свойства объекта Brush (Кисть) Свойство Color Style Описание Цвет закраски замкнутой области Стиль закраски области: bssolid сплошная заливка; штриховка: bshorizontal горизонтальная; bsvertical вертикальная; bsfdiagonal диагональная с наклоном линий вперед; bsbdiagonal диагональная с наклоном линий назад; bscross в клетку; bsdiagcross диагональная клетка Чтобы задать цвет карандаша или кисти, надо присвоить значение соответственно свойству Pen.Color или Brush.Color. В табл. 4.5 приведены именованные константы, которые можно использовать, чтобы задать цвет.

132 Глава 4. Графика 127 Таблица 4.5. Константы TColor Цвет Константа Цвет Константа ClAqua Бирюзовый clnavy Темно-синий clblack Черный clolive Оливковый ClBlue Синий clpurple Фиолетовый ClFuchsia Ярко-розовый ClRed Красный clgreen Зеленый ClSilver Серебристый ClLime Салатный clteal Зелено-голубой clmaroon Каштановый ClWhite Белый Если необходимо установить цвет, отличный от стандартного, то в свойство Color надо записать его RGB-код (как известно, любой цвет можно получить путем смешивания в разных пропорциях красной, зеленой и синей красок). Получить код цвета можно, обратившись к функции RGB, указав в качестве параметров долю красной, зеленой и синей составляющей. Например, значение RGB(56,176,222) это код цвета "осеннее небо". Графические примитивы Картинку, чертеж или схему можно рассматривать как совокупность графических примитивов: точек, линий, окружностей, дуг, а также букв (текста). Вычерчивание графических примитивов на графической поверхности, например компонента PaintBox, выполняют соответствующие методы класса TCanvas. Инструкция, обеспечивающая вычерчивание графического элемента, в общем виде выглядит так: Объект.Canvas.Mетод(Параметры) Объект определяет объект, на поверхности которого нужно нарисовать графический элемент. В качестве объекта обычно указывается компонент PaintBox. Метод это имя метода, который обеспечивает рисование нужного графического элемента. Параметры, в большинстве случаев, определяют положение графического элемента на графической поверхности и его размер. Например, в результате выполнения инструкции PaintBox1.Canvas.Rectangle(10,20,60,40) в поле компонента PaintBox1 будет нарисован прямоугольник шириной 50 и высотой 20 пикселов, левый верхний угол которого будет находиться в точке (10, 20).

133 128 Часть II. Практикум программирования При записи инструкций, обеспечивающих вывод графики, удобно использовать инструкцию with, которая позволяет сократить количество набираемого кода. Например, вместо: // итальянский флаг PaintBox1.Canvas.Brush.Color := clgreen; PaintBox1.Canvas.Rectangle(20,20,46,70); PaintBox1.Canvas.Brush.Color := clwhite; PaintBox1.Canvas.Rectangle(45,20,71,70); PaintBox1.Canvas.Brush.Color := clred; PaintBox1.Canvas.Rectangle(70,20,96,70); PaintBox1.Canvas.Brush.Style := bsclear; PaintBox1.Canvas.Font.Name := 'Tahoma'; PaintBox1.Canvas.Font.Size := 10; x := 20 + (75 - PaintBox1.Canvas.TextWidth('Италия')) div 2; PaintBox1.Canvas.TextOut(x,70 + Font.Size,'Италия'); можно написать: with PaintBox1.Canvas do // итальянский флаг Brush.Color := clgreen; Rectangle(20,20,46,70); Brush.Color := clwhite; Rectangle(45,20,71,70); Brush.Color := clred; Rectangle(70,20,96,70); Brush.Style := bsclear; Font.Name := 'Tahoma'; Font.Size := 10; x := 20 + (75 - TextWidth('Италия')) div 2; TextOut(x,70 + Font.Size,'Италия'); Текст Вывод строки текста на графическую поверхность объекта обеспечивает метод TextOut. Инструкция вызова метода TextOut в общем виде выглядит следующим образом: Объект.Canvas.TextOut(x,y,Текст)

134 Глава 4. Графика 129 Параметры x и y определяют координаты точки графической поверхности, от которой выполняется вывод текста (рис. 4.2). Необходимо обратить внимание на то, что область вывода текста закрашивается текущим цветом кисти, а после вывода текста карандаш автоматически перемещается в правый верхний угол области вывода текста. Также следует обратить внимание на то, что размер области вывода текста зависит от количества символов (длины текста) и характеристик шрифта, который используется для отображения текста. Рис Координаты области вывода текста Шрифт, используемый для отображения текста, определяет свойство Font графической поверхности, на которую текст выводится. Свойство Font представляет собой объект типа TFont. В табл. 4.6 перечислены свойства класса TFont. Таблица 4.6. Свойства класса TFont Свойство Name Size Style Color Определяет Шрифт, который используется для отображения текста. В качестве значения следует брать название шрифта, например Arial Размер шрифта Стиль начертания символов. Задается с помощью констант: fsbold (полужирный), fsitalic (курсив), fsunderline (подчеркнутый), fsstrikeout (перечеркнутый). Свойство Style является множеством, что позволяет комбинировать необходимые стили. Например, инструкция, которая устанавливает стиль "полужирный курсив", выглядит так: Font.Style := [fsbold,fsitalic] Цвет символов. В качестве значения можно использовать константу типа TColor

135 130 Часть II. Практикум программирования При выводе текста весьма полезны функции (методы класса TCanvas) TextWidth и TextHeight, которые позволяют определить размер (соответственно ширину и высоту) области вывода текста. Обоим этим методам в качестве параметра передается строка, которую предполагается вывести на графическую поверхность методом TextOut. Использование метода TextOut для вывода текста на поверхность формы демонстрирует следующий фрагмент кода (листинг 4.1). Приведенная процедура обработки события Paint загружает из файла фоновый рисунок, выводит его и затем в центре окна выводит текст (рис. 4.3). Следует обратить внимание, что у текста есть тень. Эффект тени достигается вследствие того, что текст сначала выводится белым цветом, затем, со смещением на один пиксел влево и вниз черным. Рис Вывод текста на графическую поверхность Листинг 4.1. Вывод текста на графическую поверхность type TForm1 = class(tform) PaintBox1: TPaintBox; procedure PaintBox1Paint(Sender: TObject); procedure FormCreate(Sender: TObject); private < Private declarations >back: TBitmap; // фоновый рисунок public < Public declarations >var Form1: TForm1;

136 Глава 4. Графика 131 implementation uses DateUtils; // для доступа к MonthOf var month: array[1..12] of string = ('января','февраля','марта','апреля', 'мая','июня','июля','августа', 'сентября','октября','ноября','декабря'); // конструктор формы procedure TForm1.FormCreate(Sender: TObject); // загрузить фоновый рисунок back := TBitmap.Create; back.loadfromfile('skylight.bmp'); // установить размер формы в соответствии с размером фонового рисунка Form1.ClientWidth := back.width; Form1.ClientHeight := back.height; procedure TForm1.PaintBox1Paint(Sender: TObject); var st: string; // текст x,y: integer; // точка, от которой выводится текст w,h: integer; // размер области отображения текста arect: TRect; // область вывода нескольких строк текста with PaintBox1.Canvas do // отобразить фоновый рисунок Draw(0,0,back); // Разместим текст в центре окна (по горизонтали). // Размер области вывода зависит от шрифта, // который используется для отображения текста // Чтобы область вывода текста не была видна, // кисть должна быть "прозрачной" Brush.Style := bsclear; // "прозрачная" кисть

137 132 Часть II. Практикум программирования st := 'Delphi XE'; Font.Name := 'Tahoma'; Font.Size := 16; Font.Color := clblack; w := TextWidth(st); h := TextHeight(st); x := Round((PaintBox1.Width - w) / 2); y := 50; TextOut(x,y,st); y := y+ h; Font.Size := 12; Font.Color := clblack; //Font.Style := [fsitalic]; st := FormatDateTime('Сегодня d ',now) + month[monthof(now)] + FormatDateTime(', dddd',now); w := TextWidth(st); x := Round((PaintBox1.Width - w) / 2); y := y + Round(Font.Size / 2); TextOut(x,y,st); Часто требуется вывести какой-либо текст после сообщения, длина которого во время разработки программы неизвестна. В этом случае необходимо знать координаты правой границы области выведенного текста. Координаты правой границы текста, показанного методом TextOut, можно получить, обратившись к свойству PenPos. Следующий фрагмент кода демонстрирует возможность вывода строки текста с помощью двух инструкций TextOut. with PaintBox1.Canvas do TextOut(10,10,'Embarcadero '); TextOut(PenPos.x, PenPos.y, 'Delphi XE');

138 Глава 4. Графика 133 Линия Метод LineTo рисует линию из точки, в которой в данный момент находится карандаш, в точку, координаты которой указаны в инструкции вызова метода. Инструкция вызова метода в общем виде выглядит так: Объект.Canvas.LineTo(x,y) Цвет, стиль и толщину линии определяют соответственно свойства Pen.Color, Pen.Style и Pen.Width графической поверхности, на которой метод рисует. Начальную точку линии можно задать, переместив карандаш в нужную точку. Сделать это можно с помощью метода MoveTo или присвоив значение свойству PenPos. Следует обратить внимание, что после того как линия будет нарисована, карандаш будет находиться в точке ее конца. В качестве примера в листинге 4.2 приведен фрагмент кода, который демонстрирует различные виды линий (рис. 4.4). Необходимо обратить внимание на то, что пунктиром можно нарисовать только линию толщиной в один пиксел. Рис Вид линии определяет значение свойства Pen.Style Листинг 4.2. Стиль линии var st: array[1..5] of string = ( 'pssolid - сплошная', 'psdash - штриховая', 'psdot - пунктирная, короткие штрихи', 'psdashdot - штрих-пунктирная', 'psdashdotdot - штрих, два пунктира'); procedure TForm1.PaintBox1Paint(Sender: TObject); const

139 134 Часть II. Практикум программирования L = 150; // длина линии var x,y: integer; // точка конца линии i: integer; Canvas.MoveTo(10,20); Canvas.Pen.Width := 1; for i:=1 to 5 do case i of 1: Canvas.Pen.Style := pssolid; 2: Canvas.Pen.Style := psdash; 3: Canvas.Pen.Style := psdot; 4: Canvas.Pen.Style := psdashdot; 5: Canvas.Pen.Style := psdashdotdot; Canvas.LineTo(Canvas.PenPos.X + L, Canvas.PenPos.Y); Canvas.TextOut(Canvas.PenPos.X+10, Canvas.PenPos.Y-7, st[i]); Canvas.MoveTo(10,Canvas.PenPos.Y + 20); Canvas.Pen.Style := pssolid; Canvas.Pen.Color := clnavy; for i:=1 to 3 do Canvas.Pen.Width := i; Canvas.MoveTo(10,Canvas.PenPos.Y + 20); Canvas.LineTo(Canvas.PenPos.X + L, Canvas.PenPos.Y); Canvas.TextOut(Canvas.PenPos.X+10, Canvas.PenPos.Y-7, 'Pen.Width='+ IntToStr(i)); Следующая программа, ее текст приведен в листинге 4.3, строит в поле компонента PaintBox график изменения курса доллара (рис. 4.5). Данные загружаются из файла (листинг 4.4). Следует обратить внимание на то, что разница между минимальным и максимальным значениями ряда данных, отображаемых на графике, незначительна, поэтому график строится в отклонениях от минимального значения. Координата Y точек графической поверхности возрастает сверху вниз, а на графике снизу вверх. Поэтому координата Y точки отсчитывается вверх от нижней границы компонента PaintBox.

140 Глава 4. Графика 135 Рис График Листинг 4.3. График var kurs: array[1..15] of real; // массив данных nrec: integer; // количество чисел, прочитанных из файла procedure TForm1.FormCreate(Sender: TObject); var f: TextFile; i: integer; //size: integer; // размер массива (кол-во элементов) kurs // sizeof(kurs) - кол-во байтов, занимаемых массивом // sizeof(real) - кол-во байтов, занимаемых числом real //size := Round(sizeof(kurs) / sizeof(real)); AssignFile(f,'kurs.txt'); try reset(f); // открыть для чтения i:=0; while (NOT EOF(f)) AND (i < 15) do i := i +1; readln(f,kurs[i]);

141 136 Часть II. Практикум программирования nrec := i; except on EInOutError do //MessageDlg('Нет файла данных kurs.txt', mterror,[mbok],0); procedure TForm1.PaintBox1Paint(Sender: TObject); var n: integer ; // количество точек x,y: integer; // координаты точки dx: integer; // шаг по X min,max: integer; // индекс минимального и максимального элемента m: real; // масштаб i: integer; st: string; if nrec=0 then PaintBox1.Canvas.Font.Size := 12; PaintBox1.Canvas.TextOut(10,10,'Ошибка! Нет файла данных (kurs.txt)'); // данные из файла не прочитаны exit; // выход из процедуры // строим график with PaintBox1.Canvas do // заголовок Font.Name := 'Tahoma'; Font.Size := 10; x := Round((PaintBox1.Width - TextWidth('Курс доллара')) /2); Brush.Style := bsclear; TextOut(x,10,'Курс доллара'); // определить кол-во элементов массива //n := Round(sizeof(kurs) / sizeof(real)); n:=nrec; // найти минимальное и максимальное значение ряда данных min := 1; // пусть первый элемент минимальный

142 Глава 4. Графика 137 max := 1; // пусть первый элемент максимальный for i := 1 to n do if (kurs[i] < kurs[min]) then min := i; if (kurs[i] > kurs[max]) then max := i; < Если разница между минимальным и максимальным значениями незначительна, то график получается ненаглядным. В этом случае можно построить не абсолютные значения, а отклонения от минимального значения ряда. >Font.Size := 9; Pen.Width := 1; Pen.Color := clnavy; dx:= Round((PaintBox1.Width - 40) / (n-1)); // Вычислим масштаб. // Для построения графика будем использовать не всю область // компонента, а ее нижнюю часть; верхняя область высотой // 60 пикселов используется для отображения заголовка m := (PaintBox1.Height - 60) / (kurs[max] - kurs[min]); x := 10; for i := 1 to n do y := PaintBox1.Height - Round((kurs[i] - kurs[min]) * m)-10; // поставить точку // Rectangle(x-2,y-2,x+2,y+2); Ellipse(x-2,y-2,x+2,y+2); if (i <> 1) then LineTo(x,y); // подпись данных if ((i = 1) or (kurs[i] <> kurs[i-1])) then st := FloatToStrF(kurs[i],ffGeneral,5,2); Brush.Style := bsclear; // область вывода текста - прозрачная TextOut(x,y-20,st);

143 138 Часть II. Практикум программирования < Так как метод TextOut меняет положение точки (карандаша), из которой рисует метод LineTo, то после вывода текста надо переместить указатель (карандаш) в точку (x,y). >MoveTo(x,y); x := x + dx; Листинг 4.4. Файл данных программы "График" (kurs.txt) Ломаная линия Метод Polyline чертит ломаную линию. Инструкция вызова метода в общем виде выглядит так: Объект.Canvas.Polyline(p) В качестве параметров методу передается массив типа TPoint, элементы которого содержат координаты узловых точек линии. Метод Polyline вычерчивает ломаную линию, последовательно соединяя точки, координаты которых находятся в массиве: первую со второй, вторую с третьей, третью с четвертой и т. д. Цвет, стиль и толщину линии определяют соответственно свойства Pen.Color, Pen.Style и Pen.Width той поверхности, на которой метод чертит. Следующий фрагмент кода рисует треугольный флажок (рис. 4.6), который можно рассматривать как ломаную, состоящую из трех звеньев (номера точек ломаной соответствуют индексам элементов массива).

144 Глава 4. Графика 139 Рис Пример ломаной линии procedure TForm1.PaintBox1Paint(Sender: TObject); var p: array[1..4] of TPoint; // координаты будем отсчитывать от верхней точки древка p[2].x := 10; P[2].Y := 10; p[3].x := p[2].x + 48; P[3].Y := p[2].y + 16; p[4].x := p[2].x; P[4].Y := p[2].y + 32; p[1].x := p[2].x; P[1].Y := p[2].y + 52; PaintBox1.Canvas.Polyline(p); В приведенном примере базовой точкой является верхняя точка древка (от нее отсчитываются координаты остальных точек), а начальной точкой ломаной точка, соответствующая нижней точке древка. Метод Polyline можно использовать для вычерчивания замкнутых контуров. Для этого надо, чтобы первый и последний элементы массива содержали координаты одной и той же точки. Прямоугольник Метод Rectangle вычерчивает прямоугольник. Инструкция вызова метода в общем виде выглядит так: Объект.Canvas.Rectangle(x1,y1,x2,y2) Параметры x1, y1 и x2, y2 задают координаты углов прямоугольника. Цвет, вид и ширину линии контура определяют соответственно значения свойств Pen.Color, Pen.Width и Pen.Style, а цвет и стиль заливки внутренней области значения свойств Brush.Color и Brush.Style той поверхности, на которой метод рисует.

145 140 Часть II. Практикум программирования В качестве примера в листинге 4.5 приведена процедура обработки события Paint, которая на поверхности формы рисует итальянский и французский флаги (рис. 4.7). Рис Метод Rectangle рисует прямоугольник Листинг 4.5. Французский и итальянский флаги (метод Rectangle) procedure TForm1.FormPaint(Sender: TObject); var x: integer; with Form1.Canvas do // итальянский флаг Brush.Color := clgreen; Rectangle(20,20,46,70); Brush.Color := clwhite; Rectangle(45,20,71,70); Brush.Color := clred; Rectangle(70,20,96,70); Brush.Style := bsclear; Font.Name := 'Tahoma'; Font.Size := 10; x := 20 + (75 - TextWidth('Италия')) div 2; TextOut(x,70 + Font.Size,'Италия'); // Французский флаг. // Чтобы не было контура вокруг полос, цвет контура // должен совпадать с цветом заливки Brush.Color := clblue; Pen.Color := clblue;

146 Глава 4. Графика 141 Rectangle(140,20,166,70); Brush.Color := clwhite; Pen.Color := clwhite; Rectangle(165,20,191,70); Pen.Color := clred; Brush.Color := clred; Rectangle(190,20,216,70); // контур флага Pen.Color := clblack; Brush.Style := bsclear; // "прозрачная" кисть Rectangle(140,20,216,70); Brush.Style := bsclear; Pen.Color := clblack; x := (75 - TextWidth('Франция')) div 2; TextOut(x, 70+Font.Size,'Франция'); В инструкции вызова метода Rectangle в качестве параметра метода можно указать структуру типа TRect. Поля структуры содержат координаты двух расположенных на одной диагонали углов прямоугольной области. Задать положение области можно, записав значения в поля Left, Top, Right и Bottom или в поля Top- Lef и BottomRight. Также для инициализации полей структуры можно использовать функцию Rect. Далее приведен фрагмент кода, который демонстрирует использование структуры TRect в качестве параметра метода Rectangle. procedure TForm1.PaintBox1Paint(Sender: TObject); var arect: TRect; p1,p2: TPoint; with PaintBox1.Canvas do arect.left := 10; arect.top := 20; arect.right := 30; arect.bottom := 40; Rectangle(aRect); p1.x := 50; p1.y := 20; p2.x := 70; p2.y := 40;

147 142 Часть II. Практикум программирования arect.topleft := p1; arect.bottomright := p2; Rectangle(aRect); Если нужно нарисовать только контур прямоугольника или закрашенный прямоугольник, цвет границы которого совпадает с цветом закраски, то вместо метода Rectangle можно применить соответственно метод FrameRect или FillRect. Необходимо обратить внимание на то, что для рисования контура метод FrameRect использует кисть, а не карандаш. Использование методов FillRect и FrameRect демонстрирует следующая программа (листинг 4.6). Листинг 4.6. Методы FillRect и FrameRect procedure TForm1.PaintBox1Paint(Sender: TObject); var r: TRect; x: integer; with PaintBox1.Canvas do // французский флаг рисуем методами FillRect и FrameRect r := Rect(140,20,165,70); Brush.Color := clblue; FillRect(r); r.left := 165; r.right := 190; Brush.Color := clwhite; FillRect(r); Brush.Color := clred; r.left := 190; r.right := 215; FillRect(r); // контур Brush.Color := clblack; r.left := 140; r.right := 215; FrameRect(r); Font.Name := 'Tahoma'; Font.Size := 10;

148 Глава 4. Графика 143 Brush.Style := bsclear; Pen.Color := clblack; x := (75 - TextWidth('Франция')) div 2; TextOut(x, 70+Font.Size,'Франция'); Метод RoundRect вычерчивает прямоугольник со скругленными углами. Инструкция вызова метода RoundRec в общем виде выглядит так: Объект.Canvas.RoundRect(x1,y1,x2,y2,x3,y3) Параметры x1, y1, x2, y2 определяют положение углов прямоугольника, а параметры x3 и y3 размер эллипса, одна четверть которого используется для вычерчивания скругленного угла (рис. 4.8). (x1,y1) x3 y3 (x2,y2) Рис Метод RoundRec вычерчивает прямоугольник со скругленными углами Многоугольник (полигон) Метод Polygon вычерчивает полигон (многоугольник). Инструкция вызова метода в общем виде выглядит так: Объект.Canvas.Polygon(p) где p массив записей типа TPoint, который содержит координаты вершин многоугольника (p[i].x и p[i].y координаты X и Y i-ой вершины полигона). Метод Polygon чертит полигон, соединяя прямыми линиями точки, координаты которых находятся в массиве: первую точку со второй, вторую с третьей, третью с четвертой и т. д. Цвет, вид и ширину линии контура определяют соответственно значения свойств Pen.Color, Pen.Width и Pen.Style, а цвет и стиль заливки внутренней области значения свойств Brush.Color и Brush.Style той поверхности, на которой метод рисует. В качестве примера в листинге 4.7 приведена программа, которая в виде полигона показывает динамику изменения курса доллара (рис. 4.9). Данные загружаются из файла.

149 144 Часть II. Практикум программирования Рис Полигон Листинг 4.7. Диаграмма (полигон) var kurs: array[1..15] of real; // массив данных min,max: integer; // мин. и макс. значения ряда данных nrec: integer; // количество чисел, прочитанных из файла p: array[0..16] of TPoint; // координаты точек полигона // p[0] левый нижний угол полигона, // p[1]..p[15] углы, изображающие данные // p[16] правый нижний угол полигона, procedure TForm1.FormCreate(Sender: TObject); var f: TextFile; i: integer; AssignFile(f,'kurs.txt'); try reset(f); // открыть для чтения i:=0; while (NOT EOF(f)) AND (i < 15) do i := i +1;

150 Глава 4. Графика 145 readln(f,kurs[i]); CloseFile(f); // закрыть файл nrec := i; // найти минимальное и максимальное значения ряда данных min := 1; // пусть первый элемент минимальный max := 1; // пусть первый элемент максимальный for i := 1 to nrec do if (kurs[i] < kurs[min]) then min := i; if (kurs[i] > kurs[max]) then max := i; except on EInOutError do //MessageDlg('Нет файла данных kurs.txt', mterror,[mbok],0); procedure TForm1.PaintBox1Paint(Sender: TObject); var x,y: integer; // координаты точки dx: integer; // шаг по X m: real; // масштаб i: integer; st: string; if nrec=0 then PaintBox1.Canvas.Font.Size := 12; PaintBox1.Canvas.TextOut(10,10,'Ошибка! Нет файла данных (kurs.txt)'); // данные из файла не прочитаны exit; // выход из процедуры // строим график with PaintBox1.Canvas do

151 146 Часть II. Практикум программирования // заголовок Font.Name := 'Tahoma'; Font.Size := 12; x := Round((PaintBox1.Width - TextWidth('Курс доллара')) /2); Brush.Style := bsclear; TextOut(x,5,'Курс доллара'); // *** вычислим координаты углов полигона *** dx:= Round((PaintBox1.Width - 40) / (nrec-1)); // Вычислим масштаб. // Для построения графика будем использовать не всю область // компонента PaintBox, а ее нижнюю часть. Верхняя область // высотой 60 пикселов используется для отображения заголовка m := (PaintBox1.Height - 60) / (kurs[max] - kurs[min]); x := 10; // левый нижний угол полигона p[0].x := x; p[0].y := PaintBox1.Height-1; for i := 1 to nrec do // вычислить координату Y y := PaintBox1.Height - Round((kurs[i] - kurs[min]) * m)-10; // записать координаты точки в массив p p[i].x := x; p[i].y := y; x := x + dx; // правый нижний угол полигона p[16].x := x; p[16].y := PaintBox1.Height-1; // *** рисуем полигон *** // цвет закраски PaintBox1.Canvas.Brush.Color := clteal;

152 Глава 4. Графика 147 // цвет и ширина контура PaintBox1.Canvas.Pen.Color := clyellow; PaintBox1.Canvas.Pen.Width := 2; // полигон PaintBox1.Canvas.Polygon(p); // подписи данных Font.Size := 10; Brush.Style := bsclear; // область вывода текста прозрачная for i := 1 to nrec do if ((i = 1) or (kurs[i] <> kurs[i-1])) then st := FloatToStrF(kurs[i],ffGeneral,5,2); TextOut(p[i].X,p[i].Y-20,st); Окружность и эллипс Нарисовать эллипс или окружность можно с помощью метода Ellipse. Инструкция вызова метода в общем виде выглядит следующим образом: Объект.Canvas.Ellipse(x1,y1,x2,y2) Параметры x1, y1, x2, y2 определяют координаты прямоугольника, внутри которого вычерчивается эллипс или, если прямоугольник является квадратом, окружность (рис. 4.10). Цвет, вид и ширину линии эллипса определяют соответственно значения свойств Pen.Color, Pen.Width и Pen.Style, а цвет и стиль заливки внутренней области значения свойств Brush.Color и Brush.Style той поверхности, на которой метод рисует. (x1,y1) (x1,y1) (x2,y2) (x2,y2) Рис Значения параметров метода Ellipse определяют вид геометрической фигуры Вместо четырех параметров координат диагональных углов прямоугольника, методу Ellipse можно передать один объект типа TRect.

153 148 Часть II. Практикум программирования Дуга Метод Arc рисует дугу часть эллипса. Инструкция вызова метода в общем виде выглядит так: Объект.Canvas.Arc(x1,y1,x2,y2,x3,y3,x4,y4) Параметры x1, y1, x2, y2 определяют эллипс, частью которого является дуга; x3 и y3 начальную, а x4 и y4 конечную точку дуги. Начальная (конечная) точка дуги это точка пересечения границы эллипса и прямой, проведенной из центра эллипса в точку с координатами x3 и y3 (x4, y4). Метод Arc вычерчивает дугу против часовой стрелки от начальной точки к конечной (рис. 4.11). Цвет, вид и ширину дуги определяют соответственно значения свойств Pen.Color, Pen.Width и Pen.Style той поверхности, на которой метод рисует. (x1,y1) (x3,y3) (x1,y1) (x4,y4) (x4,y4) (x3,y3) (x2,y2) (x2,y2) Рис Значения параметров метода Arc определяют дугу как часть эллипса Сектор Метод Pie рисует сектор эллипса. Инструкция вызова метода в общем виде выглядит следующим образом: Объект.Canvas.Pie(x1,y1,x2,y2,x3,y3,x4,y4) Параметры x1, y1, x2, y2 определяют эллипс, частью которого является сектор; x3, y3, x4 и y4 прямые границы сектора. Начальная точка границ совпадает с центром эллипса. Сектор вырезается против часовой стрелки от прямой, заданной точкой с координатами (x3, y3), к прямой, заданной точкой с координатами (x4, y4) (рис. 4.12). (x1,y1) (x3,y3) (x1,y1) (x4,y4) (x4,y4) (x3,y3) (x2,y2) (x2,y2) Рис Значения параметров метода Pie определяют сектор как часть эллипса (окружности)

154 Глава 4. Графика 149 Использование метода Pie демонстрирует программа "Круговая диаграмма" (ее окно приведено на рис. 4.13). В окне программы отображается круговая диаграмма. Текст процедуры приведен в листинге 4.8. Строит диаграмму процедура обработки события Paint. Процедура preparate выполняет предварительную обработку данных: сортирует данные по возрастанию и вычисляет долю каждой категории в общей сумме. Загрузку данных из файла (листинг 4.9) выполняет процедура обработки события Create формы. Рис Круговая диаграмма Листинг 4.8. Круговая диаграмма const R = 80; // радиус диаграммы HB = 6; // количество категорий данных var // исходные данные Title: string; data: array[1..hb] of real; percent: array[1..hb] of real; // доля категории в общей сумме (процент) // подписи данных dtitle: array[1..hb] of string; // цвет для каждой категории cl: array[1..hb] of TColor = (cllime, clblue, clpurple, clskyblue, clyellow, clmoneygreen); dataok: boolean; // True данные загружены успешно

155 150 Часть II. Практикум программирования // обработка исходных данных: сортировка по возрастанию // и вычисление процента procedure preparation; var i,j: integer; // буферные переменные, используемые в процессе сортировки // массивов data, dtitle и cl bd: real; bt: string; bc: TColor; sum: real; // сортировка исходных данных методом "пузырька" for i := 1 to HB-1 do for j := 1 to HB-1 do if (data[j+1] < data[j]) then // обменять местами j-й и j+1-й элементы массива bd := data[j]; data[j] := data[j+1]; data[j+1] := bd; bt := dtitle[j]; dtitle[j] := dtitle[j+1]; dtitle[j+1] := bt; bc := cl[j]; cl[j] := cl[j+1]; cl[j+1] := bc; // обработка данных вычисление доли каждой категории в общей сумме sum := 0; for i := 1 to HB do sum := sum + data[i]; for i := 1 to HB do percent[i] := (data[i] / sum) * 100;

156 Глава 4. Графика 151 procedure TForm1.FormCreate(Sender: TObject); var f: TextFile; i: integer; dataok := False; try AssignFile(f,'data.txt'); reset(f); readln(f,title); // заголовок диаграммы i:=0; // читать данные из файла while NOT EOF(f) do i:=i+1; readln(f,dtitle[i]); // категория readln(f,data[i]); // значение CloseFile(f); dataok := True; preparation; except on e: EInOutError do //dataok:= False; // процедура обработки события Paint рисует диаграмму procedure TForm1.PaintBox1Paint(Sender: TObject); var x0,y0: integer; // центр сектора (круга) x1,y1,x2,y2: integer; // координаты области, в которую вписан круг, // из которого вырезается сектор x3,y3: integer; // координата точки начала дуги x4,y4: integer; // координата точки конца дуги a1,a2: integer; // угол между OX и прямыми, ограничивающими сектор x,y: integer; dy: integer; i: integer;

157 152 Часть II. Практикум программирования if NOT dataok then // данные не загружены PaintBox1.Canvas.TextOut(10,10,'Ошибка! Нет файла данных (data.txt)'); exit; with PaintBox1.Canvas do // заголовок Font.Name := 'Tahoma'; Font.Size := 14; x := (Width - TextWidth(Title)) div 2; Brush.Style := bsclear; TextOut(x,10,Title); // круговая диаграмма x1:= 20 ; y1 := 50; x2 := x1 + 2*R; y2 := y1 + 2*R; x0 := x1 + R; y0 := y1 + R; a1 := 0; // первый сектор откладываем от оси OX for i := 1 to HB do < Из-за ошибок округления возможна ситуация, когда между первым и последним секторами будет небольшой промежуток или последний сектор перекроет первый. Чтобы этого не было, зададим, что граница последнего сектора совпадает с прямой OX. >if (i <> HB) then // 100% градусов; 1% - 3,6 градуса a2 := Round(a * percent[i]) else a2 := 359;

158 Глава 4. Графика 153 // координата точки начала дуги x3 := x0 + Round(R * cos(a2 * PI / 180)); y3 := y0 + Round(R * sin(a2 * PI / 180)); // координата точки конца дуги x4 := x0 + Round(R * cos(a1 * PI / 180)); y4 := y0 + Round(R * sin(a1 * PI / 180)); // если сектор меньше 6 градусов, границу сектора не рисуем if (abs(a1-a2) <= 6) then Pen.Style := psclear else Pen.Style := pssolid; Brush.Color := cl[i]; Pie(x1,y1,x2,y2,x3,y3,x4,y4); a1 := a2; // следующий сектор рисуем от начала текущего // легенда Font.Size := 10; dy := TextHeight('a'); x := x2 + 20; y := y1; for i := HB downto 1 do Brush.Color := cl[i]; Rectangle(x,y,x+40,y+dy); Brush.Style := bsclear; TextOut(x+50, y, dtitle[i]+', '+FloatToStrF(percent[i],ffGeneral,2,2)+'%'); y := y + dy + 10; Листинг 4.9. Файл данных (data.txt) Иcточники энергии Другие 0.5

159 154 Часть II. Практикум программирования Гидроэлектростанции 2.5 Атомные электростанции 7 Газ 23 Уголь 24 Нефть 40 Точка Свойство Pixels объекта Canvas представляет собой двумерный массив типа TСolor, который содержит информацию о цвете точек графической поверхности. Значением элемента массива Pixels является код цвета точки (значение типа TColor). Первый индекс элемента массива определяет горизонтальную координату точки, второй вертикальную. Размер массива Pixels соответствует размеру графической поверхности. Элемент Pixels[0,0] соответствует левой верхней точке графической поверхности, элемент Pixels[Canvas.Width-1,Canvas.Height-1] правой нижней. Присвоив значение элементу массива Pixels, можно изменить цвет точки графической поверхности, т. е. "нарисовать" или "стереть" точку. Например, инструкция PaintBox1.Canvas.Pixels[10,10] := clred; окрашивает точку поверхности в красный цвет. Битовые образы Для формирования сложных изображений используют битовые образы. Битовый образ это небольшая картинка, которая находится в памяти компьютера. Битовый образ можно загрузить из bmp-файла или из ресурса, а также сформировать путем копирования из другого битового образа или с графической поверхности. Картинку битового образа (иногда говорят просто "битовый образ") можно подготовить в графическом редакторе или, если предполагается, что битовый образ будет загружен из ресурса программы, с помощью редактора ресурсов, например Borland Resource Workshop. Файл ресурсов можно создать и с помощью утилиты Image Editor. Битовый образ это объект типа TBitmap. Некоторые свойства класса TBitmap приведены в табл. 4.7.

160 Глава 4. Графика 155 Таблица 4.7. Свойства класса TBitmap Свойство Height, Width Empty Transparent TransparentColor Canvas Описание Размер (ширина, высота) битового образа. Значения свойств соответствуют размеру загруженной из файла (метод LoadFromFile) или ресурса (метод LoadFromResourceID или LoadFromResourceName) картинки Признак того, что картинка в битовый образ не загружена (True) Устанавливает (True) режим использования "прозрачного" цвета. При выводе битового образа методом Draw элементы картинки, цвет которых совпадает с цветом TransparentColor, не выводятся. По умолчанию значение TransparentColor определяет цвет левого нижнего пиксела Задает прозрачный цвет. Элементы картинки, окрашенные этим цветом, методом Draw не выводятся Поверхность битового образа, на которой можно рисовать точно так же, как на поверхности формы или компонента Image Загрузку битового образа из файла обеспечивает метод LoadFromFile, которому в качестве параметра передается имя bmp-файла. Например, следующий фрагмент кода обеспечивает создание и загрузку битового образа из файла plane.bmp. bm := TBitmap.Create; // создать объект TBitmap bm.loadfromfile('plane.bmp'); // загрузить картинку В результате выполнения приведенного фрагмента битовый образ bm представляет собой изображение самолета (предполагается, что в файле plane.bmp находится изображение самолета, а не что-либо другое). После того как битовый образ сформирован (загружен из файла или из ресурса), его можно вывести, например, на графическую поверхность формы или компонента PaintBox. Отображение битового образа обеспечивает метод Draw объекта Canvas. В качестве параметров метода указывают координаты точки, от которой надо вывести битовый образ, и сам битовый образ. Например, инструкция Form1.Canvas.Draw(10,20,bm); выводит на поверхность формы битовый образ bm изображение самолета. Если свойству Transparent битового образа присвоить значение True, то фрагменты битового образа, цвет которых совпадает с цветом его левой нижней точки, не будут выведены. Такой прием используется для создания эффекта прозрачного фона. "Прозрачный" цвет можно задать напрямую, присвоив значение свойству Transparent.Color. Загрузку и отображение битовых образов демонстрирует следующая программа (ее окно приведено на рис. 4.14).

161 156 Часть II. Практикум программирования Рис Изображения неба и самолета битовые образы, загруженные из файлов После запуска программы в ее окне появляется изображение самолета, летящего в облаках. И небо, и самолет это битовые образы, загруженные из файлов. Загрузку битовых образов выполняет процедура обработки события Create формы, отображение процедура обработки события Paint. Объявление битовых образов следует поместить в секцию private объявления типа формы. Процедуры обработки событий и объявление типа формы приведены в листинге Листинг Загрузка и отображение битовых образов type TForm1 = class(tform) procedure FormPaint(Sender: TObject); procedure FormCreate(Sender: TObject); private // битовые образы Sky: TBitmap; // небо Plane: TBitmap; // самолет public < Public declarations >// конструктор формы procedure TForm1.FormCreate(Sender: TObject); // создать два объекта TBitmap и загрузить в них картинки Sky := TBitmap.Create; Sky.LoadFromFile('sky.bmp'); Plane := TBitmap.Create; Plane.LoadFromFile('plane.bmp');

162 Глава 4. Графика 157 Plane.Transparent := True; // прозрачный фон // обработка события Paint procedure TForm1.FormPaint(Sender: TObject); Canvas.Draw(0,0,Sky); // фон небо Canvas.Draw(120,50,Plane); // объект самолет Битовый образ можно использовать для формирования фонового рисунка по принципу кафельной плитки (рис. 4.15). а б Рис Фоновый рисунок (а) и битовый образ (б), из которого он составлен Как сформировать фоновый рисунок путем многократного вывода битового образа на поверхность формы, показывает программа "Фоновый рисунок" (листинг 4.11). Формирование рисунка (многократный вывод образа на поверхность формы) выполняет процедура обработки события Paint формы, загрузку битовых образов конструктор формы. Листинг Фоновый рисунок type TForm1 = class(tform) procedure FormPaint(Sender: TObject); procedure FormCreate(Sender: TObject); private bm: TBitmap; // "плитка" public

163 158 Часть II. Практикум программирования var Form1: TForm1; implementation // конструктор формы procedure TForm1.FormCreate(Sender: TObject); bm := TBitmap.Create; // создать битовый образ try // загрузить картинку bm.loadfromfile('back.bmp'); finally // обработка события Paint procedure TForm1.FormPaint(Sender: TObject); var x,y: integer; // координаты точки вывода битового образа if bm.empty then exit; // битовый образ не загружен x:=0; y :=0; repeat // горизонтальный ряд repeat Canvas.Draw(x,y,bm); x := x + bm.width; until (x > ClientWidth); // к следующему ряду x := 0; y := y + bm.height; until (y > ClientHeight);

164 Глава 4. Графика 159 Мультипликация Под мультипликацией обычно понимается рисованное изображение, элементы которого движутся. Изображение объекта можно формировать из графических примитивов во время работы программы или использовать заранее подготовленные битовые образы. Первый подход требует значительных вычислительных ресурсов. Второй подход менее ресурсоемок, поэтому именно он широко используется разработчиками компьютерных игр. Движение Реализовать эффект движения объекта не сложно: сначала нужно вывести изображение объекта на графическую поверхность (например, компонента PaintBox), затем через некоторое время стереть и снова вывести, но уже на небольшом расстоянии от его первоначального положения. Подбором времени между выводом и удалением изображения, а также расстояния между новым и предыдущим положением объекта, можно добиться того, что у наблюдателя будет складываться впечатление, что объект равномерно движется. Описанный метод реализации анимации демонстрирует программа "Движение объекта", в ее окне "плывет" корабль (рис. 4.16). Рис В окне программы "плывет" кораблик Объект (кораблик) на поверхности формы рисует процедура Ship. В качестве параметров она получает координаты базовой точки объекта. Базовая точка (x 0, y 0 ) определяет положение графического объекта в целом, от нее отсчитываются координаты графических примитивов, образующих объект (рис. 4.17). Следует обратить внимание на то, что координаты графических примитивов лучше отсчитывать не в пикселах, а в приращениях. Такой подход позволяет легко масштабировать изображение чтобы изменить размер объекта, достаточно соответствующим образом изменить шаг сетки.

165 160 Часть II. Практикум программирования (x d, y 0 3d) (x 0, y 0 2d) (x 0, y 0 ) (x d, y 0 2d) (x d, y 0 ) Рис Базовая точка (x 0, y 0) определяет положение объекта На рис приведена форма программы. Компонент Timer обеспечивает генерацию события Timer, процедура обработки которого выполняет основную работу: стирает изображение объекта и рисует его на новом месте. Настройку компонента Timer обеспечивает процедура обработки события Create формы. Эта же процедура задает размер и исходное положение корабля, а также скорость его движения (скорость определяется периодом возникновения события Timer и величиной приращения координаты Х). Рис Форма программы В листинге 4.12 приведена процедура Ship и представлены процедуры обработки события Timer и события Create. Листинг Движение объекта var d: integer; // размер объекта (коэф. масштабирования)

166 Глава 4. Графика 161 x,y: integer; // координаты объекта (базовой точки) dx: integer; // приращение координаты X procedure Ship(x,y: integer; d: integer); var // корпус и надстройку будем рисовать с помощью метода Polygon p1: array[1..7] of TPoint; // координаты точек корпуса p2: array[1..8] of TPoint; // координаты точек надстройки pc,bc: TColor; // текущий цвет карандаша и кисти with Form1.Canvas do // сохраним текущий цвет карандаша и кисти pc := Pen.Color; bc := Brush.Color; // установим нужный цвет карандаша и кисти Pen.Color := clblack; Brush.Color := clwhite; // корпус p1[1].x := x; p1[1].y := y; p1[2].x := x; p1[2].y := y-2*d; p1[3].x := x+10*d; p1[3].y := y-2*d; p1[4].x := x+11*d; p1[4].y := y-3*d; p1[5].x := x+17*d; p1[5].y :=y-3*d; p1[6].x := x+14*d; p1[6].y :=y; p1[7].x := x; p1[7].y :=y; Polygon(p1); // надстройка p2[1].x := x+3*d; p2[1].y := y-2*d; p2[2].x := x+4*d; p2[2].y := y-3*d; p2[3].x := x+4*d; p2[3].y := y-4*d; p2[4].x := x+13*d; p2[4].y := y-4*d; p2[5].x := x+13*d; p2[5].y := y-3*d; p2[6].x := x+11*d; p2[6].y := y-3*d; p2[7].x := x+10*d; p2[7].y := y-2*d; p2[8].x := x+3*d; p2[8].y := y-2*d; Polygon(p2);

167 162 Часть II. Практикум программирования MoveTo(x+5*d,y-3*d); LineTo(x+9*d,y-3*d); // капитанский мостик Rectangle(x+8*d,y-4*d+1,x+11*d,y-5*d); // труба Rectangle(x+7*d,y-4*d+1,x+8*d+1,y-6*d); // иллюминаторы Ellipse(x+11*d,y-2*d,x+12*d,y-1*d); Ellipse(x+13*d,y-2*d,x+14*d,y-1*d); // мачта MoveTo(x+10*d,y-5*d); LineTo(x+10*d,y-9*d); // оснастка Pen.Color := clwhite; MoveTo(x+17*d,y-3*d); LineTo(x+10*d,y-9*d); LineTo(x,y-2*d); // восстановим цвет карандаша и кисти Pen.Color := pc; Brush.Color := bc; procedure TForm1.Timer1Timer(Sender: TObject); // стереть объект Form1.Canvas.Brush.Color := Form1.Color; Form1.Canvas.FillRect(rect(x-1,y+1,x+17*d,y-9*d)); // вычислить координаты if x < Form1.ClientWidth then x := x +1 else x := -70; // нарисовать объект на новом месте Ship(x,y,d);

168 Глава 4. Графика 163 // конструктор procedure TForm1.FormCreate(Sender: TObject); // размер объекта d := 3; // начальное положение объекта x:= -50; y:= 90; // значения Timer1.Interval и dx определяют скорость движения объекта Timer1.Interval := 20; dx := 3; Взаимодействие с пользователем Программист может позволить пользователю управлять движением объектов в окне программы. Следующая программа показывает, как это сделать. Программа "ПВО" представляет собой игру, цель которой уничтожить самолеты противника (рис. 4.19). В начале игры в окне программы отображаются два объекта: летящий самолет и пусковая установка. Игрок, нажав клавишу <Пробел>, может запустить ракету, которая, если момент запуска выбран правильно, собьет самолет. Также, с помощью клавиш перемещения курсора, игрок может сместить пусковую установку влево или вправо. Текст программы приведен в листинге Рис Игра "ПВО" Основную работу выполняет процедура обработки события Timer, которая рисует цель (самолет), пушку (пусковую установку) и снаряд. Сначала она сравнивает

169 164 Часть II. Практикум программирования координаты самолета и снаряда. Если координаты самолета и снаряда совпадают, процедура стирает самолет, увеличивает счетчик попаданий и завершает работу. Если снаряд не долетел до самолета, процедура перерисовывает самолет на новом месте. Если ракета запущена (в этом случае значение переменной dy равно 1), то процедура рисует ее. Далее процедура проверяет, надо ли перерисовать на новом месте пусковую установку. Если игрок удерживает клавишу перемещения курсора, то процедура перерисовывает пусковую установку со смещением относительно ее текущего положения. Запуск ракеты обеспечивает процедура обработки события KeyPress. Она, если нажата клавиша <Пробел>, и если ракета не запущена, записывает в переменную dy минус единицу (в результате процедура обработки события Timer рисует ракету). Нажатие клавиш перемещения курсора обрабатывает процедура события KeyDown (это событие генерируется до тех пор, пока пользователь удерживает клавишу). Процедура, в зависимости от того, какую клавишу удерживает игрок, задает направление перемещения установки (если значение dx не равно нулю, процедура обработки события Paint рисует установку на новом месте). Если пользователь отпустит клавишу, то возникает событие KeyUp, процедура обработки которого записывает в переменную dx ноль, в результате установка перестает двигаться. Листинг Игра "ПВО" (управление движением объекта) var // координаты объектов us: TPoint; // установка (пушка) sn: TPoint; // снаряд pl: TPoint; // самолет dy: integer; // приращение координаты Y снаряда dx: integer; // приращение координаты X установки n: integer; // количество выстрелов m: integer; // количество попаданий // информация procedure info; var st: string; Form1.Canvas.Brush.Color := Form1.Color; st := 'Выстрелов: ' + IntToStr(n);

170 Глава 4. Графика 165 Form1.Canvas.TextOut(10,10,st); st := 'Попаданий: ' + IntToStr(m); Form1.Canvas.TextOut(10,25,st); // конструктор формы procedure TForm1.FormCreate(Sender: TObject); // исходное положение установки us.x := ClientWidth div 2-25; us.y := ClientHeight - 20; // исходное положение самолета pl.x := 0; pl.y := 50; Canvas.Pen.Color := Form1.Color; // клавиша нажата procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char); // следующую ракету можно пустить, если предыдущая улетела if (key = ' ') and (dy = 0) then // пуск ракеты sn.x := us.x + 25; sn.y := ClientHeight - 30; dy := -1; n:= n+1; Info; // клавиша удерживается procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); case key of VK_LEFT : dx := -1;

171 166 Часть II. Практикум программирования VK_RIGHT: dx := 1; // клавиша отпущена procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); if (key = VK_LEFT) or (key = VK_RIGHT) then dx := 0; // сигнал таймера procedure TForm1.Timer1Timer(Sender: TObject); // сравнить координаты самолета и снаряда if (pl.x > sn.x -5) and (pl.x < sn.x + 5) and (pl.y < sn.y +5) and (pl.y > sn.y -5) then // попадание, стереть самолет Canvas.Brush.Color := Form1.Color; Canvas.Rectangle(pl.X-5, pl.y-5, pl.x +10, pl.y+10); pl.x := -20; sn.x := us.x + 25; dy:=0; m := m +1; // количество попаданий info; // отобразить информацию pl.y := 50 + random(20); exit; // стереть самолет Canvas.Brush.Color := Form1.Color; Canvas.Rectangle(pl.X, pl.y, pl.x +7, pl.y+5); if pl.x < ClientWidth then pl.x := pl.x + 1 else pl.x := - 20;

172 Глава 4. Графика 167 pl.y := 50 + random(20); // нарисовать самолет на новом месте Canvas.Brush.Color := clnavy; Canvas.Rectangle(pl.X, pl.y, pl.x +7, pl.y+5); // снаряд if dy < 0 then // снаряд летит // стереть снаряд Canvas.Brush.Color := Form1.Color; Canvas.Rectangle(sn.X, sn.y, sn.x +4, sn.y+7); if sn.y > 0 then sn.y := sn.y - 1; // нарисовать снаряд на новом месте Canvas.Brush.Color := clblack; Canvas.Rectangle(sn.X, sn.y, sn.x +4, sn.y+7); end else // снаряд долетел до верхней границы окна dy := 0; // установка if ((dx < 0) and (us.x > 0)) or ((dx > 0) and (us.x < ClientWidth - 50))then // dx <> 0 игрок удерживает клавишу // "курсор вправо" или "курсор влево" Canvas.Brush.Color := Form1.Color; Canvas.Rectangle(us.X, us.y, us.x +50, us.y+7); us.x := us.x + dx; // нарисовать установку на новом месте Canvas.Brush.Color := clblack; Canvas.Rectangle(us.X, us.y, us.x +50, us.y+7);

173 168 Часть II. Практикум программирования procedure TForm1.FormPaint(Sender: TObject); info; Canvas.Brush.Color := clblack; Canvas.Rectangle(us.X, us.y, us.x +50, us.y+7); Использование битовых образов В предыдущих примерах изображение объектов формировалось из графических примитивов. Недостаток такого способа очевиден: чтобы сформировать более или менее реалистичную картинку, необходимо обеспечить отображение большого количества графических примитивов, что существенно увеличивает размер кода, снижает скорость работы программы (именно поэтому разработчики компьютерных игр используют специальные библиотеки). Теперь на примере программы "Полет в облаках" рассмотрим, как можно существенно улучшить графику благодаря использованию битовых образов. Как и в предыдущих программах, эффект движения (полет самолета) достигается вследствие периодической перерисовки объекта с некоторым смещением относительно его прежнего положения. Перед выводом картинки в новой точке предыдущее изображение должно быть удалено. Удалить изображение объекта можно путем перерисовки всей фоновой картинки или только той ее части, которая перекрыта объектом. В рассматриваемой программе используется второй способ. Форма программы "Полет в облаках" приведена на рис. 4.20, текст в листинге Рис Форма программы "Полет в облаках" Листинг Полет в облаках type TForm1 = class(tform)

174 Глава 4. Графика 169 Timer1: TTimer; procedure FormPaint(Sender: TObject); procedure Timer1Timer(Sender: TObject); procedure FormCreate(Sender: TObject); private back: TBitmap; // фон plane: TBitmap; // объект x,y: integer; // координаты объекта public < Public declarations >var Form1: TForm1; implementation // конструктор procedure TForm1.FormCreate(Sender: TObject); try back := TBitmap.Create; back.loadfromfile('sky.bmp'); // установить размер формы в соответствии с размером фонового рисунка Form1.ClientWidth := back.width; Form1.ClientHeight := back.height; plane := TBitmap.Create; plane.loadfromfile('plane.bmp'); plane.transparent := True; // исходное положение объекта x := -30; y := 70; Timer1.Interval := 25; finally

175 170 Часть II. Практикум программирования // сигнал таймера procedure TForm1.Timer1Timer(Sender: TObject); var r: TRect; // область, в которой находится объект r := Rect(x,y,x+plane.Width,y+plane.Height); Canvas.CopyRect(r,back.Canvas,r); // стереть объект (восстановить фон) x := x + 2; Canvas.Draw(x,y,plane); if x > Form1.Width + plane.width + 10 then x := -20; procedure TForm1.FormPaint(Sender: TObject); Canvas.Draw(0,0,back); Canvas.Draw(x,y,plane); Конструктор (процедура обработки события Create) загружает битовые образы (фон и изображение объекта), устанавливает размер формы в соответствии с размером фонового рисунка и задает начальное положение объекта. Следует обратить внимание на то, что начальное значение переменной x, которая определяет положение левой верхней точки области вывода изображения объекта отрицательное число, по модулю больше ширины битового образа объекта. Поэтому в начале работы программы самолет не виден. С каждым сигналом таймера значение координаты x увеличивается, и на экране появляется та часть битового образа, координаты которой больше нуля. Таким образом, у наблюдателя создается впечатление, что самолет вылетает из-за левой границы окна. Основную работу (перерисовку объекта) выполняет процедура обработки сигнала таймера (события Timer). Сначала она стирает изображение объекта (восстанавливает ''испорченную" часть фона), затем выводит изображение объекта на новом месте. Восстановление фона выполняется путем копирования фрагмента битового образа фона в ту область графической поверхности, в которой в данный момент находится объект (рис. 4.21). Копирование фрагмента обеспечивает метод CopyRect. Процедура обработки события Paint обеспечивает отображение фона в начале работы программы, а также всякий раз после того, как окно программы появляется на экране. Запустив программу "Полет в облаках", можно заметить, что изображение самолета мерцает. Это объясняется тем, что глаз успевает заметить, как самолет исчез и появился снова. Чтобы устранить мерцание, надо чтобы самолет не исчезал, а смещался. Добиться этого можно, если формировать изображение не на поверхно-

176 Глава 4. Графика 171 сти формы, а на невидимой для пользователя графической поверхности, и затем выводить готовое изображение на поверхность формы. Рисунок 4.22 поясняет процесс формирования изображения. Сначала фрагмент фона копируется на рабочую поверхность (шаг 1), затем накладывается изображение объекта (шаг 2), после чего сформированное изображение выводится в нужную точку видимой графической поверхности (шаг 3). Приведенная в листинге 4.15 программа демонстрирует реализацию описанного метода формирования изображения. Рис Восстановление фона перед отрисовкой объекта на новом месте обеспечивает метод CopyRect Рис Формирование и отображение кадра Листинг Формирование изображения на невидимой поверхности type TForm1 = class(tform) Timer1: TTimer;

177 172 Часть II. Практикум программирования procedure FormPaint(Sender: TObject); procedure Timer1Timer(Sender: TObject); procedure FormCreate(Sender: TObject); private back: TBitmap; // фон plane: TBitmap; // объект (самолет) frame: TBitmap; // кадр = область фона + объект x,y: integer; // координаты объекта public < Public declarations >var Form1: TForm1; implementation // конструктор procedure TForm1.FormCreate(Sender: TObject); try back := TBitmap.Create; back.loadfromfile('sky.bmp'); // установить размер формы в соответствии с размером фонового рисунка Form1.ClientWidth := back.width; Form1.ClientHeight := back.height; plane := TBitmap.Create; plane.loadfromfile('plane.bmp'); plane.transparent := True; frame := TBitmap.Create; frame.loadfromfile('plane.bmp'); // исходное положение объекта x := 20; y := 70;

178 Глава 4. Графика 173 Timer1.Enabled := True; finally // сигнал таймера procedure TForm1.Timer1Timer(Sender: TObject); var source :TRect; // область, откуда надо скопировать фон dest: Trect; // область рабочей поверхности, куда надо скопировать фон x := x + 2; source := Rect(x,y, x+frame.width, y+frame.height); dest := Rect(0,0,frame.Width,frame.Height); // копировать фрагмент фона frame.canvas.copyrect(dest, back.canvas, source); // наложить объект frame.canvas.draw(0,0,plane); // отобразить кадр Canvas.Draw(x,y,frame); if x > Form1.Width + plane.width + 10 then x := -20; procedure TForm1.FormPaint(Sender: TObject); Canvas.Draw(0,0,back); Canvas.Draw(x,y,plane);

179 ГЛАВА 5 Мультимедиа Большинство современных программ являются мультимедийными, что подразумевает использование возможности компьютера отображать графику, воспроизводить видео, анимацию, музыку. Типичными примерами мультимедийных программ являются игры и обучающие программы. Функция PlaySound Для реализации звуковых эффектов, например в играх, весьма удобна функция PlaySound. Она позволяет проиграть звуковой фрагмент, находящийся в wavфайле. Инструкция вызова функции PlaySound в общем виде выглядит так: PlaySound(WAV-файл, 0, Режим) Параметр WAV-файл задает звуковой файл, параметр Режим режим воспроизведения: синхронный или асинхронный. Если задан синхронный режим воспроизведения, то функция PlaySound возвращает управление программе сразу после того, как будет инициирован процесс воспроизведения звука. Если задан асинхронный режим, то программа, вызвавшая функцию PlaySound, продолжит работу только после того, как завершится воспроизведение звукового файла. В качестве значения параметра Режим можно указать именованную константу SND_SYNC (синхронный режим) или SND_ASYNC (асинхронный режим). Например, инструкция PlaySound('ringin.wav',0,SND_ASYNC); активизирует процесс воспроизведения файла ringin.wav. Для того чтобы функция PlaySound стала доступной, в директиву uses надо включить ссылку на модуль mmsystem. В качестве примера использования функции PlaySound в листинге 5.1 приведен фрагмент программы "Будильник" процедура обработки сигнала таймера. Когда наступает время, на которое установлен будильник, на экране появляется окно с сообщением. Появление окна сопровождается звуком ringin.wav. Так как задан асинхронный режим воспроизведения, то окно появляется сразу после начала воспроизведения звукового файла.

180 Глава 5. Мультимедиа 175 Листинг 5.1. Использование функции PlaySound // сигнал таймера procedure TForm1.Timer1Timer(Sender: TObject); if CompareTime(Now,AlarmTime) >= 0 then Timer1.Enabled := False; if CheckBox1.Checked then // звуковой сигнал PlaySound('ringin.wav',0,SND_ASYNC); ShowMessage(FormatDateTime(' hh:nn - ', Now) + Edit3.Text); Form1.Show; // отобразить (развернуть) окно Следует обратить внимание на то, что если в имени wav-файла путь не указан, то функция PlaySound сначала будет искать звуковой файл в текущем каталоге (в каталоге, из которого запущена программа), затем в каталоге C:\Windows\Media. Если ни в одном из этих каталогов нужного файла нет, то будет воспроизведен так называемый стандартный звук (задается в настройках Windows). Программист может запретить воспроизведение стандартного звука. Для этого в качестве параметра Режим надо указать константу SND_NODEFAULT. Компонент MediaPlayer Компонент MediaPlayer обеспечивает воспроизведение звуковых файлов различных форматов (WAV, MIDI, MP3), компакт-дисков, видеороликов (AVI) и сопровождаемой звуком анимации. Значок компонента MediaPlayer (рис. 5.1) находится на вкладке System. Рис Значок компонента MediaPlayer Внешне компонент MediaPlayer представляет собой группу кнопок (рис. 5.2), подобных тем, которые можно видеть на аудио- или видеоплеере. Назначение этих кнопок пояснено в табл Свойства компонента MediaPlayer, доступные во время разработки формы, приведены в табл Рис Компонент MediaPlayer

181 176 Часть II. Практикум программирования Кнопка Обозначение Действие Таблица 5.1. Кнопки компонента MediaPlayer Воспроизведение btplay Воспроизведение звука или видео Пауза btpause Приостановка воспроизведения Стоп btstop Остановка воспроизведения Следующий btnext Переход к следующему кадру Предыдущий btprev Переход к предыдущему кадру Шаг btstep Переход к следующему звуковому фрагменту, например к следующему треку (композиции) на CD Назад btback Переход к предыдущему звуковому фрагменту, например к предыдущей песне на CD Запись btrecord Активизирует процесс записи Открыть bteject Открывает CD-дисковод компьютера Таблица 5.2. Свойства компонента MediaPlayer Свойство Name DeviceType FileName AutoOpen Display VisibleButtons Описание Имя компонента. Используется для доступа к свойствам компонента и управления работой плеера Тип устройства. Определяет конкретное устройство, которое представляет собой компонент MediaPlayer. Тип устройства задается именованной константой: dtautoselect тип устройства определяется автоматически по расширению файла; dtwaveaudio проигрыватель звука; dtavivideo видеопроигрыватель; dtcdaudio CD-проигрыватель Имя файла, в котором находится воспроизводимый звуковой фрагмент или видеоролик Признак автоматической загрузки сразу после запуска программы, файла видеоролика или звукового фрагмента Определяет компонент, поверхность которого используется в качестве экрана для воспроизведения видеоролика (обычно в качестве экрана для отображения видео используют компонент Panel) Составное свойство. Определяет видимые кнопки компонента

182 Глава 5. Мультимедиа 177 Помимо свойств, доступных в процессе разработки формы, компонент MediaPlayer предоставляет свойства, доступные во время работы программы (табл. 5.3), которые позволяют получить информацию о состоянии медиаплеера, воспроизводимом файле или треке Audio CD. Следует обратить внимание, что значения свойств, содержащих информацию о длительности, могут быть представлены в различных форматах. Наиболее универсальным форматом является формат tfmilliseconds, в котором длительность выражается в миллисекундах. Некоторые устройства поддерживают несколько форматов. Например, если MediaPlayer используется для воспроизведения CD, то информация о воспроизводимом треке может быть представлена в формате tftmsf (Track, Minute, Second, Frame трек, минута, секунда, кадр). Для преобразования миллисекунд в минуты и секунды надо воспользоваться известными соотношениями. Если значение свойства представлено в формате tftmsf, то для преобразования можно использовать функции MCI_TMSF_TRACK, MCI_TMSF_SECOND и MCI_TMSF_MINUTE. Таблица 5.3. Свойства компонента MediaPlayer, доступные во время работы программы Свойство Length Tracks TrackLength Position TimeFormat Mode Display DisplayRect Описание Длина (время, необходимое для воспроизведения) открытого файла (например, WAV или AVI) или всех треков Audio CD Количество треков на открытом устройстве (количество композиций на Audio CD) Длина (длительность) треков. Свойство представляет собой массив, каждый элемент которого содержит информацию о длине трека (времени воспроизведения) Позиция (время от начала) в процессе воспроизведения трека Формат представления значений свойств Length, TrackLength и Position. Наиболее универсальным является формат tfmilliseconds. Если медиаплеер представляет собой CDпроигрыватель, то удобно использовать формат tftmsf Состояние устройства воспроизведения. Устройство может быть в состоянии воспроизведения (mpplaying). Процесс воспроизведения может быть остановлен (mpstopped) или приостановлен (mppaused). Устройство может быть не готово к работе (mpnotready) или в устройстве (CD-дисководе) может отсутствовать носитель (mpopen) Экран поверхность, на которой отображается клип. Если значение свойства не задано, то клип отображается в отдельном, создаваемом во время работы программы окне Размер и положение области отображения клипа на поверхности экрана

183 178 Часть II. Практикум программирования Компонент MediaPlayer предоставляет методы (табл. 5.4), используя которые можно управлять работой медиаплеера из программы так, как будто это делает пользователь. Таблица 5.4. Методы компонента MediaPlayer Метод Play Stop Pause Next Previous Step Back Действие Активизирует процесс воспроизведения. Действие метода аналогично щелчку на кнопке Play Останавливает процесс воспроизведения Приостанавливает процесс воспроизведения Переход к следующему треку, например к следующей композиции на Audio CD Переход к предыдущему треку, например к следующей композиции на Audio CD Переход к следующему кадру Переход к предыдущему кадру Использование компонента MediaPlayer для воспроизведения MP3-файлов демонстрирует программа "Простой MP3-плеер" (ее форма приведена на рис. 5.3). Значения свойств компонента MediaPlayer приведены в табл. 5.5, модуль формы в листинге 5.2. Рис Окно программы "Простой MP3-плеер"

184 Глава 5. Мультимедиа 179 Таблица 5.5. Значения свойств компонента MediaPlayer Свойство DeviceType VisibleButtons.btPlay VisibleButtons.btPause VisibleButtons.btStop VisibleButtons.btNext VisibleButtons.btPrev VisibleButtons.btStep VisibleButtons.btBack VisibleButtons.btRecord VisibleButtons.btEject Значение dtautoselect True False True False False False False False False Листинг 5.2. Простой MP3-плееер unit smp3m; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, ExtCtrls, MPlayer, ComCtrls, MMSYSTEM, FileCtrl; // эти ссылки вставлены вручную type TForm1 = class(tform) ListBox1: TListBox; MediaPlayer1: TMediaPlayer; Label1: TLabel; Button1: TButton; procedure FormCreate(Sender: TObject); procedure ListBox1Click(Sender: TObject); procedure MediaPlayer1Click(Sender: TObject; Button: TMPBtnType; var DoDefault: Boolean); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure Button1Click(Sender: TObject);

185 180 Часть II. Практикум программирования private < Private declarations >procedure PlayList(Path: string); // формирует список MP3-файлов procedure SelectFolder(Root:String); // выбор папки public < Public declarations >var Form1: TForm1; implementation var SoundPath: string[255]; // путь к MP3-файлам // формирует список MP3-файлов procedure TForm1.PlayList(Path: string); var lpbuf: PChar; // указатель на nul-terminated строку swindir: string[128]; // обычная Паскаль-строка SearchRec: TSearchRec; // структура SearchRec содержит информацию // о файле, удовлетворяющем условию поиска ListBox1.Clear; Label1.Caption := ''; // сформировать список MP3-файлов if FindFirst(Path + '*.mp3', faanyfile, SearchRec) =0 then // В каталоге есть файл с расширением mp3. // Добавим имя этого файла в список ListBox1.Items.Add(SearchRec.Name); // пока в каталоге есть другие файлы с расширением mp3 while (FindNext(SearchRec) = 0) do ListBox1.Items.Add(SearchRec.Name);

186 Глава 5. Мультимедиа 181 ListBox1.ItemIndex := 0; procedure TForm1.FormCreate(Sender: TObject); PlayList(''); // список файлов, находящихся в каталоге программы ListBox1.ItemIndex := 0; Label1.Caption:=ListBox1.Items[ListBox1.itemIndex]; MediaPlayer1.FileName := SoundPath + ListBox1.Items[ListBox1.itemIndex]; MediaPlayer1.Open; // щелчок на кнопке медиаплеера procedure TForm1.MediaPlayer1Click(Sender: TObject; Button: TMPBtnType; var DoDefault: Boolean); case Button of btplay: // нажата кнопка Play if ListBox1.Items.Count <> 0 then MediaPlayer1.FileName := SoundPath + ListBox1.Items[ListBox1.itemIndex]; MediaPlayer1.Open; MediaPlayer1.Play; Dodefault:=False; // щелчок на названии композиции произведения procedure TForm1.ListBox1Click(Sender: TObject); if MediaPlayer1.Mode = mpplaying then // остановить воспроизведение MediaPlayer1.Stop;

187 182 Часть II. Практикум программирования // вывести в поле Label1 имя выбранного файла Label1.Caption:=ListBox1.Items[ListBox1.itemIndex]; // выбрать папку и сформировать список mp3-файлов procedure Tform1.SelectFolder(Root:String); var pwroot : PWideChar; Dir: string; // Root корневой каталог GetMem(pwRoot, (Length(Root)+1) * 2); pwroot := StringToWideChar(Root,pwRoot,MAX_PATH*2); if SelectDirectory('Укажите папку, в которой находятся MP3-файлы', pwroot, Dir) then // пользователь выбрал папку Dir := Dir+'\'; // каталог, в котором находятся MP3-файлы, выбран SoundPath := Dir; // сформировать список MP3-файлов, // находящихся в папке, указанной пользователем PlayList(SoundPath); end else // в окне "Выбор папки" пользователь нажал кнопку "Отмена" Dir :=''; // щелчок на кнопке "Папка" procedure TForm1.Button1Click(Sender: TObject); if MediaPlayer1.Mode = mpplaying then MediaPlayer1.Stop; SelectFolder(SoundPath);

188 Глава 5. Мультимедиа 183 // завершение работы программы procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); try MediaPlayer1.Close; finally end. Приведенная программа показывает, что работой медиаплеера можно управлять не только с помощью командных кнопок, но и программно. Так, например, щелчок на названии композиции в списке ListBox (обработку этого события выполняет процедура ListBox1Click) путем вызова метода Stop останавливает воспроизведение текущей композиции (в режиме воспроизведения значения свойства Mode равно mpplaying). Следует обратить внимание на то, что в конце работы программы, использующей компонент MediaPlayer, медиаплеер надо "выключить" вызвать метод Close (см. процедуру FormClose). Воспроизведение MIDI В компьютерных играх, в обучающих и других программах, а также в качестве звуковых сигналов мобильных телефонов широко используется "электронная", или MIDI-, музыка. MIDI это сокращение названия интерфейса (Musical Instrument Digital Interface), способа подключения электронных музыкальных инструментов к компьютеру для записи звука. В компьютере MIDI-музыка хранится в MIDI-файлах. Рис Форма программы "Угадай число" (воспроизведение MIDI) Компонент MediaPlayer позволяет проигрывать MIDI-файлы. В качестве примера рассмотрим программу "Угадай число", в которой компонент MediaPlayer

189 184 Часть II. Практикум программирования обеспечивает воспроизведение фоновой мелодии. MIDI-музыка начинает звучать сразу после запуска программы. Форма программы "Угадай число" приведена на рис. 5.4, текст в листинге 5.3. Настройка компонента MediaPlayer выполняется программно. Листинг 5.3. Угадай число (воспроизведение MIDI) var comp: integer; // секретное число rem: integer = 60; // остаток времени на выполнение задания n: integer = 0; // сделано попыток // начало работы программы procedure TForm1.FormCreate(Sender: TObject); var k: integer; // номер мелодии // управление плеером осуществляет программа, // поэтому сделаем его кнопки невидимыми MediaPlayer1.Visible := False; Randomize; k := Random(3) +1; case k of 1: MediaPlayer1.FileName := 'pinkpanter.mid'; 2: MediaPlayer1.FileName := 'pulp_fiction.mid'; 3: MediaPlayer1.FileName := 'mission_impossible.mid'; // Если имя файла указано неверно, то при попытке загрузить // файл и активизировать процесс воспроизведения // возникает исключение EMCIDeviceError try MediaPlayer1.Open; MediaPlayer1.Play; except on e: exception do // ShowMessage(e.Message); // обработка исключения заключается в игнорировании ошибки

190 Глава 5. Мультимедиа 185 // сгенерировать число Randomize; comp := (Random(8)+1) * 10 + Random(9); // сигнал от медиаплеера procedure TForm1.MediaPlayer1Notify(Sender: TObject); < Если плеер воспроизводит файл и значение свойства Notify равно True (метод Play присваивает свойству Notify значение True), то в момент окончания воспроизведения возникает событие Notyfy. >if Timer1.Enabled then // Длительность мелодии меньше времени, отведенного // на решение задачи. Проиграть еще раз MediaPlayer1.Play; // нажатие клавиши в поле редактирования procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char); case Key of '0'..'9': if Length(Edit1.Text) = 2 then Key := #0; #13: ItsOk; // проверить, угадал ли игрок число #8: ; else Key := #0; // проверяет, угадал ли игрок число procedure TForm1.ItsOk; var igrok : integer; n := n +1; igrok := StrToInt(Edit1.Text); if igrok = comp then // игрок угадал число Edit1.Enabled := False; Button1.Enabled := False;

191 186 Часть II. Практикум программирования Timer1.Enabled := False; MediaPlayer1.Stop; Label2.Caption := 'ПРАВИЛЬНО!'; ShowMessage('Поздравляю! Вы справились с поставленной задачей за '+ IntToStr(60 - rem) + ' сек.'); end else if igrok < comp then Label2.Caption := 'БОЛЬШЕ' else Label2.Caption := 'МЕНЬШЕ'; StatusBar1.Panels[1].Text := 'Попыток: '+ IntToStr(n); Edit1.SetFocus; // установить курсор в поле ввода // щелчок на кнопке Ok procedure TForm1.Button1Click(Sender: TObject); ItsOk; // проверить, угадал ли игрок число // изменился текст в поле ввода procedure TForm1.Edit1Change(Sender: TObject); if Length(Edit1.Text) = 2 then Button1.Enabled := True else Button1.Enabled := False; // сигнал таймера procedure TForm1.Timer1Timer(Sender: TObject); rem := rem -1; StatusBar1.Panels[0].Text := 'Осталось: ' + IntToStr(rem) + ' сек.'; if rem = 0 then // время, отведенное на решение задачи, истекло Edit1.Enabled := False;

192 Глава 5. Мультимедиа 187 Button1.Enabled := False; Timer1.Enabled := False; MediaPlayer1.Stop; Label2.Caption := 'Вы не справились с задачей!'; // завершение работы программы procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); Timer1.Enabled := False; if MediaPlayer1.Mode = mpplaying then MediaPlayer1.Stop; MediaPlayer1.Close; Настройку плеера выполняет процедура обработки события Create формы. Она же активизирует процесс воспроизведения музыки. Следует обратить внимание, что мелодия воспроизводится "по кругу" до тех пор, пока игрок не угадает число или не истечет время, отведенное на решение задачи. Процесс повторного воспроизведения музыки активизирует процедура обработки события Notify медиаплеера. Это событие возникает всякий раз, когда по какой-либо причине состояние плеера меняется, например, в момент завершения воспроизведения файла. Процедура обработки события Click на кнопке Ok также управляет работой плеера. Она, если игрок угадал число, останавливает процесс воспроизведения музыки. Также необходимо обратить внимание на процедуру обработки события Close формы. Она проверяет состояние плеера и, если плеер воспроизводит музыку, останавливает его. Проигрыватель Audio CD Следующий пример показывает, как на основе компонента MediaPlayer можно создать вполне приличный проигрыватель компакт-дисков. Форма программы приведена на рис. 5.5, значения свойств компонетов в табл Помимо компонентов, которые показаны на рисунке, на форме есть компонент MediaPlayer и еще две кнопки SpeedButton. Кнопки SpeedButton1 SpeedButton3 используются для управления работой плеера, а кнопки SpeedButton4 и SpeedButton5 хранят картинки (битовые образы) Play и Stop. Во время работы программы, в момент активизации процесса воспроизведения, битовый образ кнопки SpeedButton5 копируется в битовый образ кнопки SpeedButton2 (в результате на кнопке появ-

193 188 Часть II. Практикум программирования ляется значок Stop). Если процесс воспроизведения активен, то в момент щелчка на кнопке SpeedButton2 битовый образ кнопки SpeedButton4 копируется в битовый образ кнопки SpeedButton2 (в результате на кнопке появляется значок Play). Рис Форма программы Compact Disc Player Таблица 5.6. Значение свойств компонентов Компонент Свойство Значение MediaPlayer DeviceType dtcdaudio SpeedButton1 NumGlyphs 2 Glyph Flat Enabled True False SpeedButton2 NumGlyphs 2 Glyph Flat Enabled True False SpeedButton3 NumGlyphs 2 Glyph Flat Enabled True False SpeedButton4 NumGlyphs 2 Glyph SpeedButton5 NumGlyphs 2 Glyph Timer1 Interval 500

194 Глава 5. Мультимедиа 189 Компонент Timer используется для организации цикла опроса состояния медиаплеера. Во время воспроизведения Audio CD функция обработки события Timer, которое генерирует таймер, выводит на индикатор (в поля компонентов Label1 и Label2) номер воспроизводимого трека, его длительность и время воспроизведения. Вид окна программы сразу после ее запуска, когда в CD-дисководе находится Audio CD, приведен на рис Если в дисководе диска нет или если диск не звуковой, то на индикаторе отображается сообщение Вставьте Audio CD. Щелчок на кнопке Play (SpeedButton2) активизирует процесс воспроизведения. Во время воспроизведения на индикаторе отражается информация о воспроизводимом треке (рис. 5.7). а б Рис В начале работы на индикаторе выводится информация о времени воспроизведения CD (а) или сообщение о необходимости вставить в дисковод Audio CD (б) Рис Во время воспроизведения на индикаторе отображается информация о воспроизводимом треке Текст программы приведен в листинге 5.4. Следует обратить внимание на событие Notify, которое может генерировать MediaPlayer. Это событие возникает в момент изменения состояния плеера (например, в момент активизации процесса воспроизведения) при условии, что значение свойства Notify равно True. В рассматриваемой программе событие Notify используется для обнаружения факта открытия CD-дисковода пользователем. Листинг 5.4. Проигрыватель компакт-дисков unit CDp_; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,

195 190 Часть II. Практикум программирования Forms, Dialogs, StdCtrls, Buttons, MPlayer, ExtCtrls, MMSYSTEM; // обеспечивает управление работой медиаплеера type TForm1 = class(tform) MediaPlayer: TMediaPlayer; Timer1: TTimer; Label1: TLabel; Label2: TLabel; Shape1: TShape; SpeedButton1: TSpeedButton; // кнопка "Предыдущий трек" SpeedButton3: TSpeedButton; // кнопка Play/Stop SpeedButton2: TSpeedButton; // кнопка "Следующий трек" SpeedButton4: TSpeedButton; // хранит картинку Play SpeedButton5: TSpeedButton; // хранит картинку Stop procedure SpeedButton2Click(Sender: TObject); procedure SpeedButton3Click(Sender: TObject); procedure SpeedButton1Click(Sender: TObject); procedure FormActivate(Sender: TObject); procedure MediaPlayerNotify(Sender: TObject); procedure Timer1Timer(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private procedure TrackInfo; // выводит информацию о треке public < Public declarations >var Form1: TForm1; implementation var Track: integer =0; // воспроизводимый трек // выводит информацию о треке procedure TForm1.TrackInfo;

196 Глава 5. Мультимедиа 191 var ms: longint; // длительность трека в миллисекундах min,sec: byte; // длительность трека: минут, секунд MediaPlayer.TimeFormat := tfmilliseconds; ms := MediaPlayer.TrackLength[Track]; MediaPlayer.TimeFormat := tftmsf; ms := ms div 1000; min := ms div 60; sec := ms mod 60; Label1.Caption := Format('Трек %d. Длительность %d:%.2d',[track, min,sec]); // начало работы программы procedure TForm1.FormActivate(Sender: TObject); MediaPlayer.Open; < Проверить, есть ли в дисководе диск Audio CD. Если в дисководе есть диск, то свойство Mode = mpstopped. Если диска нет или дисковод открыт, то Mode = mpopen. Тип диска можно определить по количеству треков. Если диск CD-ROM, то на нем один трек, на аудиодиске количество треков больше одного. >if (MediaPlayer.Mode = mpstopped) and (MediaPlayer.Tracks > 1) then MediaPlayer.Notify := True else Timer1.Enabled := True; // сигнал от MediaPlayer procedure TForm1.MediaPlayerNotify(Sender: TObject); case MediaPlayer.Mode of mpopen: // пользователь открыл дисковод SpeedButton2.Tag := 0; SpeedButton2.Enabled := False;

197 192 Часть II. Практикум программирования SpeedButton2.Enabled := False; Timer1.Enabled := True; Label2.Caption := '00:00'; MediaPlayer.Notify := True; // щелчок на кнопке "Предыдущий трек" procedure TForm1.SpeedButton1Click(Sender: TObject); MediaPlayer.Previous; // в начало текущего трека MediaPlayer.Previous; // в начало предыдущего трека if MCI_TMSF_TRACK(MediaPlayer.Position) = 1 then SpeedButton1.Enabled := False; if not SpeedButton3.Enabled then SpeedButton3.Enabled := True; // щелчок на кнопке Play/Stop procedure TForm1.SpeedButton2Click(Sender: TObject); var trk: integer; // номер трека if SpeedButton2.Tag = 0 then // щелчок на кнопке Play MediaPlayer.Play; MediaPlayer.TimeFormat := tftmsf; trk := MCI_TMSF_TRACK(MediaPlayer.Position); if trk > 1 then SpeedButton1.Enabled := True; if trk < MediaPlayer.Tracks then SpeedButton3.Enabled := True; MediaPlayer.Notify := False; Timer1.Enabled :=True; SpeedButton2.Tag := 1; // на кнопке SpeedButton5 картинка Stop SpeedButton2.Glyph := SpeedButton5.Glyph; end

198 Глава 5. Мультимедиа 193 else // щелчок на кнопке Stop SpeedButton1.Enabled := False; SpeedButton3.Enabled := False; MediaPlayer.Notify := True; MediaPlayer.Stop; Timer1.Enabled := False; SpeedButton2.Tag := 0; // на кнопке SpeedButton4 картинка Play SpeedButton2.Glyph := SpeedButton4.Glyph; // щелчок на кнопке "Следующий трек" procedure TForm1.SpeedButton3Click(Sender: TObject); MediaPlayer.Next; // если перешли к последнему треку, то кнопку // Next сделать недоступной if MCI_TMSF_TRACK(MediaPlayer.Position) = MediaPlayer.Tracks then SpeedButton3.Enabled := False; if not SpeedButton1.Enabled then SpeedButton1.Enabled := True; // сигнал таймера procedure TForm1.Timer1Timer(Sender: TObject); var trk, // трек ms: longint; // время звучания в миллисекундах min, sec: byte; // время case MediaPlayer.Mode of mpplaying: // режим воспроизведения // вывести номер трека и время воспроизведения trk := MCI_TMSF_TRACK(MediaPlayer.Position); min := MCI_TMSF_MINUTE(MediaPlayer.Position); sec := MCI_TMSF_SECOND(MediaPlayer.Position);

199 194 Часть II. Практикум программирования Label2.Caption := Format('%d:%.2d',[min,sec]); if trk <> Track then // начало воспроизведения нового трека Track := trk; TrackInfo; if Track = 2 then SpeedButton2.Enabled := True; if Track = MediaPlayer.Tracks then SpeedButton3.Enabled := False; mpstopped: // Если дисковод открыт или в нем нет // Audio CD, то Mode = mpopen if (MediaPlayer.Tracks > 1) then // В дисководе Audio CD. Вывести информацию о диске Timer1.Enabled := False; MediaPlayer.Open; MediaPlayer.Notify := True; trk := MCI_TMSF_TRACK(MediaPlayer.Tracks); MediaPlayer.TimeFormat := tfmilliseconds; ms := MediaPlayer.Length; ms := ms div 1000; min := ms div 60; sec := ms mod 60; SpeedButton2.Enabled := True; // кнопка Play Label1.Caption := Format('Треков: %d. Время звучания: %d:%.2d', [trk,min,sec]); Label1.Visible := True;

200 Глава 5. Мультимедиа 195 mpopen: // дисковод открыт или в дисководе нет аудиодиска Label1.Caption := 'Вставьте Audio CD'; Label1.Visible := not Label1.Visible; // завершение работы программы procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); MediaPlayer.Stop; end. Просмотр видеороликов Компонент MediaPlayer позволяет просматривать видеоролики и сопровождаемую звуком анимацию. В качестве примера использования компонента для решения этой задачи рассмотрим программу Video Player (рис. 5.8), с помощью которой можно просмотреть небольшой ролик или анимацию. Форма программы приведена на рис. 5.9, значения свойств компонентов в табл Рис Окно программы Video Player

201 196 Часть II. Практикум программирования Рис Форма программы Video Player Таблица 5.7. Значения свойств компонентов Компонент Свойство Значение MediaPlayer DeviceType dtautoselect Visible False SpeedButton1 NumGlyphs 4 Glyph Flat Enabled True True SpeedButton2 NumGlyphs 4 Glyph Flat Enabled True False GroupIndex 1 Timer1 Interval 100 Enabled False Компонент OpenDialog1 обеспечивает отображение стандартного диалогового окна Открыть файл для выбора файла. Окно Открыть файл становится доступ-

202 Глава 5. Мультимедиа 197 ным во время работы программы в результате щелчка на кнопке Eject (SpeedButton1). Следует обратить внимание, что для управления процессом воспроизведения кнопки компонента MediaPlayer1 не используются, поэтому свойству Visible компонента MediaPlayer присвоено значение False. Также необходимо обратить внимание на свойство GroupIndex кнопки SpeedButon1. Его значение равно единице, поэтому после щелчка кнопка остается в зафиксированном, нажатом, состоянии и на ее поверхности появляется значок Stop (значение свойства NumGlyhts равно четырем, это значит, что в битовом образе есть картинка для нажатого состояния). Компонент Timer обеспечивает обновление информации на индикаторе: процедура обработки сигнала таймера (события Timer) выводит в поле Label2 информацию о времени воспроизведения клипа. Тест программы приведен в листинге 5.5. Листинг 5.5. Video Player procedure TForm1.FormCreate(Sender: TObject); MediaPlayer.Display := Form1; // Это можно сделать во время создания формы. Но на всякий случай. SpeedButton1.GroupIndex := 1; SpeedButton1.AllowAllUp := True; // возвращает размер изображения AVI-файла procedure GetFrameSize(f: string; var w,h: integer); var fst: TFileStream; // структура заголовка AVI-файла header: record RIFF: array[1..4] of uchar; // 'RIFF' nu1: array[1..5] of LongInt; // не используется (в данном случае) AVIH: array[1..4] of uchar; // 'avih' nu2: array[1..9] of LongInt; // не используется (в данном случае) // размер кадра Width: LongInt; Height: LongInt; fst := TFileStream.Create(f,fmOpenRead);

203 198 Часть II. Практикум программирования fst.read(header,sizeof(header)); w := header.width; h := header.height; fst.destroy; // щелчок на кнопке Eject выбор файла procedure TForm1.SpeedButton2Click(Sender: TObject); var top,left: integer; // левый верхний угол "экрана" width,height: integer; // размер экрана mw,mh: integer; // максимально возможный размер экрана kh,kw: real; // коэф-ты масштабирования по h и w k: real; // коэф-т масштабирования ms: longint; // время воспроизведения (миллисекунд) min,sec: integer; // время воспроизведения (минут, секунд) OpenDialog.Title := 'Выбор клипа'; if not OpenDialog.Execute then exit; if MediaPlayer.FileName = OpenDialog.FileName then // при попытке открыть файл, который уже открыт, возникает ошибка exit; // Пользователь выбрал файл. // Определим размер и положение "экрана" (области на // поверхности формы), на котором будет выведен клип GetFrameSize(OpenDialog.FileName,width,height); mh:=speedbutton1.top - 10; mw:=form1.clientwidth; if mh > height then kh :=1 else kh := mh/height;

204 Глава 5. Мультимедиа 199 if mw > width then kw :=1 else kw := mw/width; if kw < kh then k := kw else k := kh; // здесь масштаб определен // определим размер экрана и его положение width := Round(width * k); height := Round(height * k); left := (Form1.ClientWidth - width) div 2; top := 10; // открыть файл MediaPlayer.FileName := OpenDialog.FileName; MediaPlayer.Open; MediaPlayer.DisplayRect := Rect(left,top,width,height); SpeedButton1.Enabled := True; // кнопка Play // вывести информацию о времени воспроизведения MediaPlayer.TimeFormat := tfmilliseconds; ms := MediaPlayer.Length; min := ms div 1000 div 60; sec := ms div 1000 mod 60; Label1.Caption := Format('Длительность: %d:%.2d',[min,sec]); Label1.Font.Color := clwindowtext; Label2.Font.Color := clwindowtext; Label3.Font.Color := clwindowtext; // активизировать процесс воспроизведения SpeedButton1.Down := True; // "нажать" Play MediaPlayer.Play; Timer1.Enabled := True;

205 200 Часть II. Практикум программирования // щелчок на кнопке Play/Stop procedure TForm1.SpeedButton1Click(Sender: TObject); if SpeedButton1.Down then // начать воспроизведение MediaPlayer.Play; SpeedButton1.Hint := 'Stop'; SpeedButton2.Enabled := False; end else // остановить воспроизведение MediaPlayer.Stop; SpeedButton1.Hint := 'Play'; SpeedButton2.Enabled := True; Timer1.Enabled := False; // сигнал таймера procedure TForm1.Timer1Timer(Sender: TObject); var ms: longint; min,sec: integer; // время воспроизведения ms := MediaPlayer.Position; min := ms div 1000 div 60; sec := ms div 1000 mod 60; Label2.Caption := Format('%d:%.2d',[min,sec]); // сигнал от плеера procedure TForm1.MediaPlayerNotify(Sender: TObject); if (MediaPlayer.Mode = mpstopped) and SpeedButton1.Down then SpeedButton1.Down := False; // "отжать" кнопку Play SpeedButton2.Enabled := True; // кнопка Eject Timer1.Enabled := False;

206 Глава 5. Мультимедиа 201 В качестве экрана, на котором осуществляется воспроизведение видеороликов, используется поверхность формы. Поэтому установить значение свойства Display компонента MediaPlayer1 во время разработки формы нельзя. Кроме того, размер экрана должен быть равен или пропорционален размеру кадров ролика. Значение свойства Display устанавливает функция обработки события Create для формы, а размер и положение экрана на форме функция обработки события Click на кнопке Eject (SpeedButton1). Размер экрана устанавливается максимально возможным и таким, чтобы ролик воспроизводился без искажения (высота и ширина экрана пропорциональны высоте и ширине кадров). Размер кадров ролика возвращает функция GetFrameSize, которая извлекает нужную информацию из заголовка файла. Компонент Animate Компонент Animate, его значок (рис. 5.10) находится на вкладке Win32, позволяет воспроизвести простую, не сопровождаемую звуком AVI-анимацию. Свойства компонента Animate перечислены в табл Рис Значок компонента Animate Таблица 5.8. Свойства компонента Animate Свойство FileName FrameWidth FrameHeight FrameCount AutoSize Center StartFrame StopFrame Active Color Описание Имя AVI-файла, в котором находится анимация Ширина кадра Высота кадра Количество кадров анимации Признак автоматического изменения размера компонента в соответствии с размером кадров анимации Признак центрирования кадров анимации в поле компонента. Если значение свойства равно True и размер компонента больше размера кадров (AutoSize=False), кадры анимации располагаются в центре поля компонента Номер кадра, с которого начинается отображение анимации Номер кадра, на котором заканчивается отображение анимации Признак активности процесса отображения анимации Цвет фона компонента (цвет "экрана"), на котором воспроизводится анимация

207 202 Часть II. Практикум программирования Таблица 5.8 (окончание) Свойство Transparent Repetitions CommonAVI Описание Режим использования "прозрачного" цвета при отображении анимации Количество повторов отображения анимации. Если значение свойства равно нулю, анимация воспроизводится непрерывно Определяет стандартную анимацию, которая отображается в поле компонента (avicopy копирование файла; avideletefile удаление файла; avireciclefile перемещение файла в корзину) Нужно еще раз обратить внимание, что компонент Animate предназначен для воспроизведения AVI-файлов, которые содержат только анимацию. При попытке открыть файл, в котором находится сопровождаемая звуком анимация, возникает исключение. Использование компонента Animate для отображения анимации демонстрирует следующая программа (ее окно приведено на рис. 5.11, а текст в листинге 5.6). В момент появления окна на экране в поле компонента Animate отображается последний кадр анимации изображение Дельфийского храма. Щелчок на кнопке Show активизирует процесс отображения анимации. Непосредственно воспроизведение анимации инициирует метод Play, параметры которого задают начальный и конечный кадры фрагмента анимации, который надо воспроизвести, и число повторов отображения анимации. Начальный и конечный кадры, а также количество повторов можно задать, присвоив значения свойствам StartFrame, StopFrame и Repetitions соответственно. В этом случае, чтобы активизировать процесс воспроизведения анимации, необходимо свойству Activate присвоить значение True. Рис Отображение анимации в поле компонента Animate Листинг 5.6. Отображение AVI-анимации // конструктор формы procedure TForm1.FormCreate(Sender: TObject);

208 Глава 5. Мультимедиа 203 const fn = 'Delphi_2.avi'; var st: string; try Animate1.FileName := fn; st := 'Файл: ' + fn + #13+ Format('Размер кадра: %dх%d Количество кадров: %d', [Animate1.Width, Animate1.Height, Animate1.FrameCount]); Label2.Caption := st; except on e: Exception do Label1.Caption := 'Ошибка загрузки файла ' + fn + #13 + 'Файл недоступен или содержит анимацию, ' + 'которая сопровождается звуком.'; Button1.Enabled := False; // обработка события Paint procedure TForm1.FormPaint(Sender: TObject); // отобразить последний кадр анимации Animate1.StartFrame := Animate1.FrameCount; // щелчок на кнопке Show procedure TForm1.Button1Click(Sender: TObject); // активизировать воспроизведение анимации // с первого по последний кадр Animate1.Play(1, Animate1.FrameCount,1);

209 ГЛАВА 6 Базы данных Delphi предоставляет программисту набор компонентов, используя которые он может создать программу работы практически c любой базой данных: от Microsoft Access до Microsoft SQL Server и Oracle. База данных и СУБД База данных это файл или совокупность файлов определенной структуры, в которых находится информация. Программная система, обеспечивающая работу с базой данных, называется системой управления базой данных (СУБД). СУБД позволяет создать базу данных, наполнить ее информацией, решить задачи просмотра (отображения), поиска, архивирования и др. Типичным примером СУБД является Microsoft Access. Локальные и удаленные базы данных В зависимости от расположения данных и приложения, обеспечивающего работу (доступ) с ними, различают локальные и удаленные базы данных. В локальной базе данных файлы данных, как правило, находятся на диске того компьютера, на котором работает программа манипулирования данными. Локальные базы данных не обеспечивают одновременный доступ к информации нескольким пользователям. Несомненным достоинством локальной базы данных является высокая скорость доступа к информации. Microsoft Access это типичная локальная база данных. В удаленных базах данных файлы данных размещают на отдельном, доступном по сети, компьютере (сервере). Программы, обеспечивающие работу с удаленными базами данных, строят по технологии "клиент-сервер". Программа-клиент, работающая на компьютере пользователя, обеспечивает доступ к данным (прием команд от пользователя, передачу их серверу, получение и отображение данных). Серверная часть (сервер), работающая на удаленном компьютере, принимает запросы (команды) от клиента, выполняет их и пересылает данные клиенту. Программа, работающая на удаленном компьютере, проектируется так, чтобы обеспе-

210 Глава 6. Базы данных 205 чить одновременный доступ к базе данных многим пользователям. В большинстве случаев в качестве серверной части используется стандартный сервер баз данных, например Borland InterBase, Microsoft SQL Server, MySQL, Oracle и т. п. Таким образом, разработка программы работы с удаленной базой данных в большинстве случаев сводится к разработке программы-клиента. Структура базы данных База данных (в широком смысле) это набор однородной, как правило, упорядоченной по некоторому критерию информации. На практике наиболее широко используются так называемые реляционные базы данных (от англ. relation отношение, таблица). Реляционная база данных это совокупность связанных таблиц данных. Так, например, базу данных Projects (Проекты) можно представить как совокупность таблиц Projects (Проекты), Tasks (Задачи) и Resources (Ресурсы), а базу данных Contacts (Контакты) однойединственной таблицей Contacts (Контакты). Доступ к таблице осуществляется по имени. Строки таблиц данных называются записями. Они содержат информацию об объектах базы данных. Например, строка таблицы Tasks (Задачи) базы данных Projects (Проекты) может содержать название задачи, дату, когда должна быть начата работа, и идентификатор ресурса, который назначен на выполнение задачи. Доступ к записям осуществляется по номеру. Записи состоят из полей (поле ячейка в строке таблицы). Поля содержат информацию о характеристиках объекта. Доступ к полю осуществляется по имени. Например, поля записей таблицы Tasks могут содержать: идентификатор задачи (поле TaskID), название задачи (поле Title), идентификатор проекта, частью которого является задача (поле ProjID), дату, когда работа по выполнению задачи должна быть начата (поле Start), информацию о состоянии задачи (поле Status) и идентификатор ресурса, который назначен на выполнение задачи (поле ResID). При представлении данных в табличной форме имена полей указывают в заголовке (в первой строке) таблицы. Физически база данных представляет собой файл или совокупность файлов, в которых находятся таблицы. Например, в Microsoft Access все таблицы, образующие базу данных, хранятся в одном файле с расширением mdb. Механизмы доступа к данным Существует довольно много механизмов (технологий) доступа к данным (BDE, ADO, dbexpress и др.). Технология BDE, основой которой является процессор баз данных Borland Database Engine (BDE), представляющий собой набор библиотек, драйверов и утилит, обеспечивающих работу практически с любой из существующих баз

211 206 Часть II. Практикум программирования данных. Однако существенным ее недостатком является трудоемкость процесса развертывания приложений, созданных на ее основе: помимо самого приложения на компьютер пользователя необходимо установить BDE. Технология ADO (ActiveX Data Object) разработана Microsoft как универсальный механизм доступа к базам данных. Ее несомненное достоинство гибкость, возможность доступа к различным источникам данных. Технология dbexpress разработана Borland как эффективный механизм доступа к удаленным базам данных. Компоненты доступа к данным Компоненты, обеспечивающие работу с базами данных, находятся на вкладках dbgo, dbexpress, InterBase и BDE. Компоненты вкладки dbgo для доступа к данным используют технологию ADO. Компоненты dbexpress обеспечивают так называемый однонаправленный (unidirectional) доступ удаленным базам данных на основе разработанной Borland технологии dbexpress. Вкладка InterBase содержит компоненты работы с базами данных InterBase. Компоненты вкладки BDE для доступа к данным используют процессор баз данных Borland Database Engine. Следует обратить внимание на то, что компоненты доступа к данным напрямую с базами данных не взаимодействуют, доступ к базе данных (серверу) обеспечивают соответствующие драйверы. На компьютер разработчика драйверы доступа к базам данных устанавливаются в процессе установки Delphi. На вкладках DataControls и DataAccess находятся компоненты, обеспечивающие хранение данных во время работы программы (ClientDataSet) и их отображение (DBGrid, DBText, DBEdit, DBMemo и др.). Создание базы данных Программы работы с базами данных обычно работают с существующими файлами данных и, как правило, не предоставляют пользователю возможность создать базу данных. Поэтому, перед тем как приступить к разработке программы работы с базой данных, необходимо как минимум создать базу данных с помощью соответствующей СУБД. База данных Microsoft Access Процесс разработки программы работы с базой данных Microsoft Access рассмотрим на примере. Создадим программу, обеспечивающую работу с базой данных "Контакты". Для доступа к базе данных будем использовать технологию ADO.

212 Глава 6. Базы данных 207 Перед тем как приступить непосредственно к работе над программой, необходимо с помощью Microsoft Access создать базу данных "Контакты" (файл contacts.mdb), состоящую из одной-единственной таблицы contacts (табл. 6.1). Файл базы данных следует поместить, например, в папку D:\Database. Также в папке Database надо создать папку Images (в ней будем хранить иллюстрации). Таблица 6.1. Таблица contacts базы данных "Контакты" Поле Тип Размер Описание cid Автоувеличение Уникальный идентификатор name Текстовый 50 Имя phone Текстовый 30 Телефон Текстовый 30 Адрес эл. почты img Текстовый 30 Файл иллюстрации, например фотографии Здесь надо обратить внимание на следующее. Microsoft Access это СУБД, приложение, обеспечивающее работу с базами данных. Для доступа к данным Microsoft Access использует так называемое ядро баз данных Microsoft Jet. Доступ к данным Доступ к данным (источнику данных) при использовании технологии ADO обеспечивают компоненты ADOConnection, ADODataSet, ADOTable и ADOQuery, значки которых находятся на вкладке dbgo (рис. 6.1). Рис Компоненты вкладки dbgo обеспечивают доступ к данным Компонент ADOConnection обеспечивает соединение с базой данных (источником данных). Компонент ADODataSet представляет собой данные, полученные от источника данных, в результате выполнения SQL-запроса. Компонент ADOTable также представляет собой данные, полученные из базы данных, но в отличие от компонента ADODataSet, который может быть заполнен информацией из разных таблиц, компонент ADOTable представляет данные, полученные из одной таблицы. Компонент ADOQuery представляет собой данные, полученные из базы данных в результате выполнения SQL-команды.

213 208 Часть II. Практикум программирования Для связи между данными, в качестве которых может выступать компонент ADODataSet, ADOTable или ADOQuery, и компонентом, обеспечивающим отображение данных, например DBGrid, используется компонент DataSource. Механизм взаимодействия компонентов, обеспечивающих доступ к данным и их отображение, показан на рис Сервер (база данных) ADOConnection ADODataSet (ADOTable, ADOQuery) Компоненты, обеспечивающие доступ к данным DataSource DBGrid (DBEdit, DBText) Компоненты, обеспечивающие отображение данных Рис Взаимодействие компонентов, обеспечивающих доступ к данным и их отображение Форма программы работы с базой данных "Контакты" приведена на рис Рис Форма программы работы с базой данных "Контакты"

214 Глава 6. Базы данных 209 Сначала на форму надо поместить компонент ADOConnection, затем ADODataSet, DataSource и DBGrid. Компоненты рекомендуется добавлять в том порядке, в котором они перечислены, и сразу настраивать (см. далее). Следует отметить, что компоненты ADOConnection, ADODataSet, DataSource во время работы программы на форме не отображаются (такие компоненты называют невизуальными, или невидимыми), поэтому их можно поместить в любое место формы. Компонент ADOConnection, его свойства приведены в табл. 6.2, обеспечивает соединение с базой данных. Таблица 6.2. Свойства компонента ADOConnection Свойство ConnectionString LoginPrompt Mode Connected Описание Строка соединения. Содержит информацию, необходимую для подключения к базе данных Признак необходимости в момент подключения к базе данных запросить у пользователя имя и пароль. Если значение свойства равно False, то окно Login в момент подключения к базе данных не отображается Режим соединения. Соединение с базой данных может быть открыто для чтения (cmread), записи (cmwrite), чтения/записи (cmreadwrite) Признак того, что соединение установлено Настраивается компонент ADOConnection следующим образом. Сначала надо сделать щелчок на кнопке с тремя точками, которая находится в строке свойства ConnectionString, затем в появившемся окне нажать кнопку Build. В результате откроется окно Свойства связи с данными, на вкладке Поставщик данных которого нужно выбрать тип источника данных (для базы данных Microsoft Access это Microsoft Jet OLE DB Provider) и щелкнуть на кнопке Далее (рис. 6.4). Затем на вкладке Подключение (рис. 6.5) надо задать базу данных щелкнуть на кнопке просмотра (. ) и в открывшемся окне выбрать файл базы данных. Если для доступа к базе данных необходимы пароль и идентификатор пользователя, то их надо указать (по умолчанию к базе данных, созданной в Microsoft Access, доступ есть у пользователя Admin, но пароль для доступа не нужен). После этого можно сделать щелчок на кнопке Проверить подключение, убедиться, что соединение с базой данных настроено правильно, и щелчком на кнопке OK закрыть окно Свойства связи с данными. После этого, если для доступа к базе данных пароль не нужен, необходимо присвоить значение False свойству LoginPrompt. Значения свойств компонента ADOConnection1 приведены в табл. 6.3.

215 210 Часть II. Практикум программирования Рис Настройка соединения с базой данных (шаг 1) Рис Настройка соединения с базой данных (шаг 2)

216 Глава 6. Базы данных 211 Таблица 6.3. Значения свойств компонента ADOConnection1 Свойство ConnectionString LoginPrompt Connected Значение Provider=Microsoft.Jet.OLEDB.4.0; Data Source=D:\Database\contacts.mdb; Persist Security Info=False False False После того как будет настроен компонент ADOConnection, можно приступить к настройке компонента ADODataSet. Компонент ADODataSet (набор данных) хранит данные, полученные из базы данных. Свойства компонента ADODataSet приведены в табл Таблица 6.4. Свойства компонента ADODataSet Свойство Connection CommandText Parameters Filter Filtered Activate Описание Ссылка на компонент (ADOConnection), который обеспечивает соединение с источником (базой) данных Команда, которая направляется серверу Параметры команды Фильтр. Позволяет отобрать записи, удовлетворяющие критерию отбора Признак использования фильтра Открывает или делает недоступным набор данных В базе данных PhonesBook.mdb информация хранится в таблице Phones. Для того чтобы информация из этой таблицы попала в компонент ADODataSet, в свойство CommandText нужно записать SQL-команду, обеспечивающую выбор необходимой информации. Выбор информации из таблицы базы данных обеспечивает команда SELECT. В простейшем случае, когда надо получить всю информацию, которая находится в таблице, в качестве параметров команды SELECT нужно указать таблицу, имена полей и, возможно, поле, по содержимому которого данные должны быть упорядочены. Например, SQL-команда, обеспечивающая чтение данных из таблицы contacts, выглядит так: SELECT * FROM contacts ORDER BY name Значения свойств компонента ADODataSet приведены в табл. 6.5.

217 212 Часть II. Практикум программирования Таблица 6.5. Значения свойств компонента ADODataSet Свойство Connection CommandText Activate Значение ADOConnection1 SELECT * FROM contacts ORDER BY name False Завершив настройку компонента ADODataSet, можно приступить к настройке компонента DataSource задать значение свойства DataSet, определяющего набор данных, связь с которым обеспечивает компонент (табл. 6.6). Таблица 6.6. Значения свойств компонента DataSource1 Свойство DataSet Значение ADODataSet1 Отображение данных Пользователь может работать с базой данных в режиме таблицы или в режиме формы. В режиме таблицы информация отображается в виде таблицы, что позволяет видеть одновременно несколько записей. Этот режим обычно используется для просмотра информации. В режиме формы отображается одна запись. Обычно данный режим применяется для ввода и редактирования информации. Часто эти два режима комбинируют. Краткая информация (содержимое некоторых ключевых полей) выводится в табличной форме, а при необходимости видеть содержимое всех полей выполняется переключение в режим формы. Отображение данных в форме таблицы обеспечивает компонент DBGrid (рис. 6.6). Свойства компонента (табл. 6.7) определяют вид таблицы и действия, которые могут быть выполнены над данными во время работы программы. Рис Значок компонента DBGrid Таблица 6.7. Свойства компонента DBGrid Свойство DataSource Columns BorderStyle Описание Ссылка на источник данных (например, ADODataSet) Отображаемая информация (столбцы) Вид границы вокруг компонента

218 Глава 6. Базы данных 213 Таблица 6.7 (окончание) Свойство Options.dgEditing Описание Разрешает (True) изменение, добавление и удаление данных. Чтобы активизировать режим редактирования записи, надо нажать клавишу <F2>; чтобы добавить запись <Insert>; чтобы удалить запись <Ctrl>+<Del> или <Del>, если значение свойства Options.dgConfirmDelete равно False Options.dgConfirmDelete Необходимость подтверждения удаления записи. Если значение свойства равно True, то, чтобы удалить запись, пользователь должен нажать комбинацию клавиш <Ctrl>+<Del> и подтвердить выполнение операции удаления щелчком на кнопке OK в появившемся окне Confirm. Если значение свойства равно False, то текущая запись будет удалена в результате нажатия клавиши <Del> Options.dgTitles Options.dgIndicator Options.dgColumnResize Options.dgColLines Options.dgRowLines Разрешает вывод строки заголовка столбцов Разрешает (True) отображение колонки индикатора. Во время работы с базой данных текущая запись помечается в колонке индикатора треугольником, новая запись звездочкой, редактируемая специальным значком Разрешает (True) менять во время работы программы ширину колонок таблицы Разрешает (True) выводить линии, разделяющие колонки таблицы Разрешает (True) выводить линии, разделяющие строки таблицы Свойство Columns компонента DBGrid представляет собой коллекцию (массив) объектов типа TColumn. Свойства объекта TColumn (табл. 6.8) определяют информацию, которая отображается в колонке. Таблица 6.8. Свойства объекта TColumn Свойство FieldName Width Font Color Alignment Описание Поле, содержимое которого отображается в колонке Ширина колонки в пикселах Шрифт, используемый для отображения текста в ячейках колонки Цвет фона Способ выравнивания текста в ячейках колонки. Текст может быть выровнен по левому краю (taleftjustify), по центру (tacenter) или по правому краю (tarightjustify)

219 214 Часть II. Практикум программирования Таблица 6.8 (окончание) Свойство Title.Caption Описание Заголовок колонки. По умолчанию в заголовке отображается имя поля Title.Alignment Способ выравнивания заголовка. Заголовок может быть выровнен по левому краю (taleftjustify), по центру (tacenter) или по правому краю (tarightjustify) Title.Color Title.Font Цвет фона заголовка колонки Шрифт заголовка колонки Настройка компонента DBGrid выполняется следующим образом. Сначала в коллекцию Columns надо добавить столько элементов, сколько столбцов данных необходимо отобразить в поле компонента DBGrid. Для этого следует раскрыть окно редактора коллекции щелкнуть на кнопке с тремя точками, которая находится в поле значения свойства Columns, или из контекстного меню, которое появляется в результате щелчка правой кнопкой мыши в поле компонента, выбрать команду Columns Editor. В окне редактора коллекции (рис. 6.7) надо сделать щелчок на кнопке Add New. В результате в коллекцию Columns будет добавлен новый элемент объект TColumns. Добавив нужное количество элементов в коллекцию Columns, можно приступить к их настройке. В простейшем случае для каждой колонки достаточно установить значение свойств FieldName и Title.Caption. В табл. 6.9 приведены значения свойств компонента DBGrid1, а на рис. 6.8 вид формы после его настройки. Рис Чтобы добавить элемент в коллекцию Columns, надо сделать щелчок на кнопке Add New Таблица 6.9. Значения свойств компонента DBGrid1 Свойство Значение Font.Name Tahoma Font.Size 9

220 Глава 6. Базы данных 215 Таблица 6.9. Значения свойств компонента DBGrid1 Свойство Значение Columns[0].FieldName Name Columns[0].Width 190 Columns[0].Title.Caption Имя Columns[0].Title.Font.Style fsbold Columns[1].FieldName phone Columns[1].Width 100 Columns[1].Title.Caption Телефон Columns[1].Title.Font.Style fsbold Columns[2].FieldName Columns[2].Title.Caption Columns[2].Width 100 Columns[2].Title.Font.Style fsbold Рис Вид формы после настройки компонента DBGrid Следующее, что надо сделать, создать процедуры обработки событий Activate и Close формы (листинг 6.1). Процедура обработки события Activate должна открыть базу данных, события Close сохранить изменения, сделанные пользователем. Здесь нужно обратить внимание, что все изменения, сделанные пользователем, автоматически фиксируются в базе данных (в файле) в момент перехода к следующей записи. Однако если пользователь, не завершив ввод или редактирование данных, закроет окно программы, данные последней редактируемой записи не бу-

221 216 Часть II. Практикум программирования дут записаны в файл. Поэтому, перед тем как завершить работу программы, надо проверить, не редактирует ли пользователь запись, и если редактирует (в этом случае значение свойства EditorMode компонента DBGrid равно True), то сохранить редактируемую запись в базе данных. Листинг 6.1. База данных "Контакты" // начало работы программы procedure TForm1.FormActivate(Sender: TObject); try ADOConnection1.Open; ADODataSet1.Active := True; except on e:exception do DBGrid1.Enabled := False; MessageDlg('Нет файла D:\Database\contacts.mdb', mterror,[mbok],0); // завершение работы программы procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); if DBGrid1.EditorMode then // пользователь не завершил редактирование // записать редактируемую запись ADODataset1.UpdateBatch(arCurrent); Выбор информации из базы данных При работе с базой данных пользователя, как правило, интересует не все ее содержимое, а некоторая конкретная информация. В простейшем случае найти нужные сведения можно, просмотрев таблицу. Однако такой способ поиска неудобен и малоэффективен. Выбрать нужную информацию из базы данных можно, направив серверу SQLкоманду SELECT или, если информация уже загружена из базы данных, активизировав фильтр.

222 Глава 6. Базы данных 217 SQL-запрос Чтобы выбрать из базы данных только нужные записи, надо направить серверу SQL-команду SELECT, указав в качестве параметра критерий отбора записей. В общем виде SQL-команда SELECT, обеспечивающая выборку данных из базы данных (таблицы), выглядит так: SELECT СписокПолей FROM Таблица WHERE (Критерий) ORDER BY СписокПолей Параметр Таблица задает таблицу базы данных, из которой надо выбрать (получить) данные. Параметр СписокПолей, указанный после слова SELECT, задает поля, содержимое которых надо получить (если необходимы данные из всех полей, то вместо списка полей можно указать "звездочку"). Параметр Критерий задает критерий (условие) отбора записей. Параметр СписокПолей, указанный после ORDER BY, задает поля, по содержимому которых будут упорядочены записи таблицы, сформированной в результате выполнения команды. Например, команда SELECT name, phone FROM contacts WHERE name = 'Культин Н.Б.' обеспечивает выборку из таблицы contacts записи, у которой в поле name находится текст Культин Н.Б.. В критерии запроса (при сравнении строк) вместо конкретного значения можно указать шаблон. Например, шаблон Ива% обозначает все строки, которые начинаются с Ива, а шаблон %Ива% все строки, в которых есть подстрока Ива. При использовании шаблонов вместо оператора = надо использовать оператор LIKE. Например, запрос SELECT * FROM contacts WHERE name LIKE 'Ку%' выберет из таблицы contacts только те записи, в поле name которых находится текст, начинающийся с Ку. Вместо оператора LIKE можно использовать оператор CONTAINING (Содержит). Например, приведенный ранее запрос, целью которого является вывод списка людей, фамилии которых начинаются с Ку, при использовании оператора CONTAINING будет выглядеть так: SELECT * FROM contacts WHERE name CONTAINING 'Ку' Использование SQL-запроса для поиска информации в базе данных демонстрирует следующая программа (ее форма приведена на рис. 6.9). Рассматриваемая программа является многооконным приложением (структура проекта, в том числе и список форм, отображается в окне Project Manager). В главном окне отображается список абонентов (весь или результат поиска). Окно Найти (рис. 6.10), которое становится доступным в результате щелчка в главном окне на соответствующей кнопке, используется для ввода имени абонента, телефон которого нужно найти в базе данных. Главная форма создается автоматически в момент начала работы над новым проектом. Чтобы создать форму Найти, необходимо в меню File выбрать команду New Form - Delphi. После того как форма Запрос будет настроена (рис. 6.11, табл и 6.11), ее надо сохранить в каталоге проекта выбрать в меню File команду Save.

223 218 Часть II. Практикум программирования Рис Форма программы работы с базой данных "Контакты" Рис Окно Найти Рис Форма Найти Здесь следует обратить внимание на свойство ModalResult кнопки Button1. По умолчанию его значение равно mrnone. В данном случае значение свойства ModalResult равно mrok, поэтому во время работы программы в результате щелчка на кнопке OK окно будет закрыто, причем процедура, которая активизирует процесс отображения окна, получит информацию о том, щелчком на какой кнопке пользователь закрыл окно (на кнопке OK или на системной кнопке Закрыть).

224 Глава 6. Базы данных 219 Таблица Значения свойств формы Найти (FindForm) Свойство Name BorderStyle BorderIcons.Minimize BorderIcons.Maximize Position Значение Form2 bssingle False False pomainformcenter Таблица Значения свойств кнопки OK (Button1) формы Найти Свойство Enabled ModalResult Значение False mrok Завершив настройку главной формы и формы Найти, можно приступить к созданию процедур обработки событий. Процедуры обработки событий главной формы приведены в листинге 6.2, формы Найти в листинге 6.3. Следует обратить внимание, что в модуль главной формы надо добавить ссылку на модуль формы Найти директиву uses FindForm. Необходимо также обратить внимание, что в директиве uses указывается имя модуля формы, которое совпадает с именем файла формы (но без расширения). Листинг 6.2. Модуль главной формы (MainForm.pas) uses FindForm; // ссылка на модуль формы "Найти" // начало работы программы procedure TForm1.FormActivate(Sender: TObject); try ADOConnection1.Open; ADODataSet1.Open; except on e:exception do DBGrid1.Enabled := False;

225 220 Часть II. Практикум программирования MessageDlg('Нет файла D:\Database\contacts.mdb', mterror,[mbok],0); // щелчок на кнопке "Запрос" procedure TForm1.Button1Click(Sender: TObject); Form2.ShowModal; // отобразить форму Найти if Form2.ModalResult = mrok then // пользователь ввел критерий запроса и нажал кнопку OK ADODataset1.Close; ADODataset1.CommandText := 'SELECT * FROM contacts WHERE ' + 'name Like ''%' + Form2.Edit1.Text + '%'''; ADODataset1.Open; // щелчок на кнопке "Все записи" procedure TForm1.Button2Click(Sender: TObject); ADODataset1.Close; ADODataset1.CommandText := 'SELECT * FROM contacts ORDER BY name'; ADODataset1.Open; // завершение работы программы procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); if DBGrid1.EditorMode then // пользователь не завершил редактирование // записать редактируемую запись ADODataset1.UpdateBatch(arCurrent); Листинг 6.3. Модуль формы Найти (FindForn.pas) < Это окно отображается как модальный диалог. Чтобы в результате щелчка на кнопке OK окно закрылось,

226 Глава 6. Базы данных 221 и вновь стало доступным главное окно, свойству ModalResult кнопки Button1 надо присвоить значение mrok. > // форма появилась на экране procedure TForm2.FormActivate(Sender: TObject); Edit1.Clear; Edit1.SetFocus; // установить курсор в поле Edit1 // изменился текст в поле редактирования procedure TForm2.Edit1Change(Sender: TObject); if Length(Edit1.Text) > 0 then Button1.Enabled := True else Button1.Enabled := False; // нажатие клавиши в поле редактирования procedure TForm2.Edit1KeyPress(Sender: TObject; var Key: Char); if (key = #13) and (Length(Edit1.Text) > 0) then Button1.SetFocus; Фильтр Часто нужная информация уже есть в загруженной таблице. В этом случае, для того чтобы ее найти (скрыть ненужную в данный момент информацию), следует воспользоваться механизмом фильтрации записей. Фильтр это условие отбора записей. Возможностью фильтрации обладают компоненты ADODataset, ADOQuery и ADOTable. Для того чтобы фильтрация была выполнена, в свойство Filter надо записать условие отбора записей и активизировать процесс фильтрации присвоить значение True свойству Filtered (чтобы отменить действие фильтра, свойству Filtered надо присвоить значение False). Следует обратить внимание, что фильтр воздействует на набор данных, сформированный в результате выполнения команды SELECT. Принципиальное отличие механизма фильтрации от выборки записей командой SELECT состоит в том, что фильтр воздействует на записи, загруженные из базы данных, и скрывает записи, не удовлетворяющие критерию запроса, в то время как команда SELECT загружает из базы данных записи, удовлетворяющие критерию запроса.

227 222 Часть II. Практикум программирования В качестве примера использования фильтра в листинге 6.4 приведены процедуры обработки событий Click для кнопок Найти и Все записи программы работы с базой данных "Записная книжка". Листинг 6.4. Щелчок на кнопке Найти (использование фильтра) // щелчок на кнопке "Найти" procedure TForm1.Button1Click(Sender: TObject); Form2.ShowModal; // отобразить форму "Найти" if Form2.ModalResult = mrok then // пользователь ввел критерий запроса и нажал кнопку OK // фильтр ADODataSet1.Filtered := False; ADODataSet1.Filter :='name Like ''%' + Form2.Edit1.Text + '%'''; ADODataSet1.Filtered := True; if ADODataSet1.RecordCount < 0 then // в базе данных нет записей, удовлетворяющих критерию запроса ADODataSet1.Filtered := False; ShowMessage('В БД нет записей, удовлетворяющих критерию запроса.'); // щелчок на кнопке "Все записи" procedure TForm1.Button2Click(Sender: TObject); ADODataSet1.Filtered := False; Работа с базой данных в режиме формы На практике используются два режима отображения данных: таблица и форма. В режиме таблицы в окне программы отображается таблица, что позволяет видеть несколько записей одновременно. Обычно этот режим используется для просмотра записей. Отображение данных в режиме таблицы обеспечивает компонент DBGrid. Если в таблице, содержимое которой отображается в поле компонента DBGrid, много колонок, то пользователь, как правило, не может видеть все столбцы одновременно, и, для того чтобы увидеть нужную информацию, он вынужден ме-

228 Глава 6. Базы данных 223 нять ширину столбцов или прокручивать содержимое поля компонента по горизонтали, что не совсем удобно. В режиме формы в окне программы отображается только одна запись, что позволяет одновременно видеть содержимое всех полей записи. Обычно режим формы используется для ввода информации в базу данных, а также для просмотра записей, состоящих из большого количества полей. Часто режим формы и режим таблицы комбинируют. Компоненты, обеспечивающие просмотр и редактирование полей (рис. 6.12), находятся на вкладке Data Controls. На практике чаще всего используются компоненты DBEdit и DBMemo. Они являются аналогами компонентов Edit и Мемо, ориентированными на работу с базами данных. Свойства компонентов DBEdit и DBMemo, обеспечивающие работу с базой данных, приведены в табл Рис Компоненты DBEdit и DBMemo обеспечивают редактирование полей записей базы данных, компонент DBNavigator навигацию по БД Таблица Свойства компонентов DBEdit и DBMemo Свойство DataSource DataField Описание Источник данных Поле записи БД, содержимое которого отображается в поле компонента В качестве примера рассмотрим программу, которая обеспечивает работу с базой "Контакты", но уже в режиме формы. Рис Форма программы работы с базой данных "Контакты" (режим формы)

229 224 Часть II. Практикум программирования Форма программы работы с БД "Контакты" приведена на рис Компоненты DBEdit обеспечивают отображение полей текущей записи, компонент Image1 отображение иллюстрации, имя файла которой находится в поле Image. Соединение с базой данных осуществляет компонент ADOConnection1, а доступ к данным, находящимся в таблице contacts, компонент ADODataSet1. Значения свойств этих компонентов приведены в табл Компонент Свойство Значение Таблица Значения свойств компонентов ADOConnection1 ConnectionString Provider=Microsoft.Jet.OLEDB.4.0; Data Source=D:\Database\contacts.mdb; Persist Security Info=False LoginPrompt False ADODataSet1 Connection ADOConnection1 CommandText SELECT * FROM contacts DataSource1 DataSet ADOTable1 DBEdit1 DataSource DataSource1 DataField AutoSelect ReadOnly name False True DBEdit2 DataSource DataSource1 DataField AutoSelect ReadOnly phone False True DBEdit3 DataSource DataSource1 DataField AutoSelect ReadOnly False True Image1 Proportional True Enabled False В форме программы отображается одна запись базы данных (эта запись называется текущей). Компонент DBNavigator, его свойства приведены в табл. 6.14, обеспечивает перемещение указателя текущей записи к следующей, предыдущей, первой или последней записи, а также выполнение других операций в результате

230 Глава 6. Базы данных 225 щелчка на соответствующей кнопке (табл. 6.15). Следует обратить внимание на свойство VisibleButtons. Оно позволяет скрыть некоторые кнопки компонента DBNavigator и тем самым запретить выполнение соответствующих операций над файлом данных. Например, присвоив значение False свойству VisibleButtons.nbDelete, можно скрыть кнопку nbdelete и тем самым запретить удаление записей. Значения свойств компонента DBNavigator1 приведены в табл Таблица Свойства компонента DBNavigator Свойство DataSource VisibleButtons Определяет Источник данных. В качестве источника данных может выступать, например, компонент ADODataSet, ADOTable или ADOQuery Кнопки, которые отображаются в поле компонента. Скрыв некоторые кнопки, можно запретить выполнение соответствующих действий Кнопка Обозначение Действие Таблица Кнопки компонента DBNavigator К первой nbfirst Указатель текущей записи перемещается к первой записи файла данных К предыдущей nbprior Указатель текущей записи перемещается к предыдущей записи файла данных К следующей nbnext Указатель текущей записи перемещается к следующей записи файла данных К последней nblast Указатель текущей записи перемещается к последней записи файла данных Добавить nbinsert В файл данных добавляется новая запись Удалить nbdelete Удаляется текущая запись файла данных Редактирование nbedit Устанавливает режим редактирования текущей записи Сохранить nbpost Изменения, внесенные в текущую запись, записываются в файл данных Отменить Cancel Отменяет внесенные в текущую запись изменения Обновить nbrefresh Записывает внесенные изменения в файл

231 226 Часть II. Практикум программирования Таблица Значения свойств компонента DBNavigator1 Свойство DataSource VisibleButtons.bnRefresh Значение DataSource1 False Модуль формы программы работы с базой данных приведен в листинге 6.5. Процедура обработки события AfterScroll для компонента ADODataSet1, которое возникает после того, как указатель текущей записи будет перемещен к другой записи (следующей или предыдущей, в зависимости от того, какую кнопку компонента DBNavigator нажал пользователь) инициирует процесс отображения иллюстрации. Отображение иллюстрации обеспечивает процедура ShowImage, которой в качестве параметра передается содержимое поля Image (или пустая строка, если поле пустое). Процедура ShowImage выводит иллюстрацию или, если поле img текущей записи пустое, картинку nobody.jpg. Информация в поля записи name, phone и вводится обычным образом путем заполнения полей Имя, Телефон и . Чтобы ввести информацию в поле img (задать имя файла иллюстрации), надо сделать щелчок в поле компонента Image1. В результате открывается диалог Выбор изображения (компонент Open- Dialog), в котором пользователь может выбрать иллюстрацию. Если иллюстрация выбрана, то имя файла иллюстрации записывается в поле Image текущей записи БД, а сам файл копируется в каталог Images. Листинг 6.5. Программа работы с базой данных "Контакты" (режим формы) unit MainForm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, DB, ADODB, Grids, DBGrids, ExtCtrls, DBCtrls, StdCtrls, ComCtrls, Mask, ExtDlgs; type TForm1 = class(tform) ADOConnection1: TADOConnection; ADODataSet1: TADODataSet; DataSource1: TDataSource; Label1: TLabel; Label2: TLabel;

232 Глава 6. Базы данных 227 DBNavigator1: TDBNavigator; StatusBar1: TStatusBar; DBEdit1: TDBEdit; DBEdit2: TDBEdit; DBEdit3: TDBEdit; Image1: TImage; OpenDialog1: TOpenDialog; procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure FormActivate(Sender: TObject); procedure ADODataSet1AfterScroll(DataSet: TDataSet); procedure Image1Click(Sender: TObject); procedure DBNavigator1Click(Sender: TObject; Button: TNavigateBtn); private < Private declarations >apath: string; procedure ShowImage(img: string);// отображает картинку в поле Image1 public < Public declarations >var Form1: TForm1; implementation uses Jpeg, IniFiles, StrUtils; // начало работы программы procedure TForm1.FormActivate(Sender: TObject); apath :='d:\database\'; try ADOConnection1.Open; ADODataSet1.Open; StatusBar1.Panels[0].Text := ' Запись: 1'; except on e:exception do DBEdit1.Enabled := False;

233 228 Часть II. Практикум программирования DBEdit2.Enabled := False; DBNavigator1.Enabled := False; MessageDlg('Ошибка доступа к файлу БД: '+ apath + 'NoteBook.mdb', mterror, [mbok], 0); // событие возникает после перехода к другой записи procedure TForm1.ADODataSet1AfterScroll(DataSet: TDataSet); var img: string; if ADODataSet1.RecNo <> -1 then StatusBar1.Panels[0].Text := ' Запись: ' + IntToStr(ADODataSet1.RecNo); if ADODataSet1.FieldValues['img'] <> Null then img := ADODataSet1.FieldValues['img'] else img := ''; ShowImage(img); end else StatusBar1.Panels[0].Text := ' Новая запись' // отображает иллюстрацию procedure TForm1.ShowImage(img: string); if img = '' then img := 'nobody.jpg'; try Image1.Picture.LoadFromFile(aPath+'images\'+img); finally // щелчок на кнопке компонента DBNavigator procedure TForm1.DBNavigator1Click(Sender: TObject; Button: TNavigateBtn);

234 Глава 6. Базы данных 229 case Button of nbinsert, nbdelete, nbedit: DBEdit1.ReadOnly := False; DBEdit2.ReadOnly := False; DBEdit3.ReadOnly := False; Image1.Enabled := True; if Button = nbinsert then ShowImage('nobody.jpg'); end ; nbpost, nbcancel: DBEdit1.ReadOnly := True; DBEdit2.ReadOnly := True; DBEdit3.ReadOnly := True; Image1.Enabled := False; end ; // щелчок в поле компонента Image (выбор картинки) procedure TForm1.Image1Click(Sender: TObject); var nfilename: string; OpenDialog1.FileName := '*.jpg'; if OpenDialog1.Execute then // пользователь выбрал изображение nfilename := ExtractFileName(OpenDialog1.FileName); CopyFile(PChar(OpenDialog1.FileName), PChar(aPath + 'images\'+ nfilename), false); ShowImage(nFileName); ADODataSet1.FieldValues['img'] := nfilename;

235 230 Часть II. Практикум программирования // завершение работы программы procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); if ADODataSet1.State = dsedit then // пользователь не завершил // редактирование // записать редактируемую запись ADODataset1.UpdateBatch(arCurrent); Загрузка строки соединения из INI-файла В программе работы с базой данных "Контакты" для соединения с источником данных (БД) используется компонент ADOConnection, свойство ConnectionString которого явно задает имя файла базы данных и путь к нему. Поэтому при установке программы работы с базой данных на другой компьютер необходимо, чтобы каталог базы данных находился на том же диске, что и на компьютере программиста, что не всегда удобно, а иногда и невыполнимо. Избежать подобных проблем можно, если строку соединения загружать из INI-файла в начале работы программы. Задачу загрузки строки соединения из файла или ее формирования с учетом реального размещения базы данных можно возложить на процедуру обработки события BeforeConnect компонента ADOConnection, которое происходит непосредственно перед подключением к базе данных. Приведенная в листинге 6.6 процедура показывает, как это можно сделать. В данном примере из INI-файла (листинг 6.7) загружается не строка соединения, а только имя папки, в которой находится файл базы данных, после чего строка соединения формируется путем замены ее фрагмента. Листинг 6.6. Обработка события BeforeConnection procedure TForm1.ADOConnection1BeforeConnect(Sender: TObject); var p1,p2: integer; IniFile: TIniFile; fn: string; // имя INI-файла st: string; // строка соединения // загрузить строку соединения из INI-файла // INI-файл должен находиться в том же каталоге, что и EXE-файл

236 Глава 6. Базы данных 231 // программы работы с БД, и его имя совпадает с именем EXE-файла p1 := Pos('.exe', Application.ExeName); fn := Copy(Application.ExeName, 1, p1-1) + '.ini'; IniFile := TIniFile.Create(fn); // прочитаем из INI-файла // имя папки, в которой должен находиться файл БД // ключ apath находится в секции data apath := IniFile.ReadString('data','aPath',''); if apath = '' then MessageDlg('Нет файла: '+ fn,mterror,[mbok],0); st := ADOConnection1.ConnectionString; p1 := Pos('Data Source',st); p2 := PosEx(';',st,p1); Delete(st,p1,p2-p1); Insert('Data Source='+ apath+ 'notebook.mdb',st,p1); ADOConnection1.ConnectionString := st; Листинг 6.7. INI-файл [data] apath=d:\database\ База данных Blackfish SQL Сервер Blackfish SQL Server представляет собой компактный, высокопроизводительный сервер баз данных. Но он, в отличие, например, от InterBase, Microsoft SQL Server или MySQL, практически не требует администрирования, что делает его использование в информационных системах среднего уровня сложности наилучшим решением. На компьютер разработчика сервер Blackfish SQL Server устанавливается как служба автоматически вместе с другими компонентами среды Delphi. Запускается сервер также автоматически, при каждом включении компьютера. Чтобы убедиться, что сервер запущен, надо раскрыть окно Службы (рис. 6.14) открыть Панель управления, сделать щелчок на значке Администрирование и затем (в появившемся окне Администрирование) на значке Службы.

237 232 Часть II. Практикум программирования Рис Окно Службы Доступ к серверу Доступ к серверу Blackfish SQL Server есть только у администратора системы. Только администратор (по умолчанию, после установки сервера, имя администратора sysdba, пароль masterkey) может создать базу данных и открыть ее для других пользователей. Здесь следует обратить внимание на то, что в Blackfish SQL Server пользователи баз данных не регистрируются на сервере так, как это делается, например, в InterBase или в Microsoft SQL Server. Вся информация о пользователях базы данных и их полномочиях хранится не в системной базе данных на сервере, а в самой базе данных. Создание базы данных Задачу создания базы Blackfish SQL Server рассмотрим на примере. Создадим базу данных "Книги". ЗАМЕЧАНИЕ Перед тем как приступить к разработке программы работы с базой данных Blackfish SQL Server, рекомендуется выполнить настройку сервера в файле конфигурации BSQLServer.exe.config (он находится в каталоге Program Files\Embarcadero\RAD Studio\

238 Глава 6. Базы данных \bin) указать каталог, предназначенный для файлов баз данных. Имя каталога надо указать в качестве значения ключа DataDirectory. Сделать это можно с помощью утилиты Database Explorer. Чтобы получить доступ к утилите, надо в окне Project Manager открыть вкладку Data Explorer. Следует обратить внимание, что Database Explorer можно запустить и из операционной системы (файл Program Files\Embarcadero\RAD Studio\8.0\bin\DataExplore.exe). Процесс создания БД Blackfish SQL Server состоит из двух шагов. Сначала надо создать соединение, затем через созданное соединение направить серверу команды, обеспечивающие создание базы данных (CREATE DATABASE) и таблиц в ней (CREATE TABLE). Следует обратить внимание, что создать базу данных может только администратор сервера. Чтобы создать соединение, надо в окне Data Explorer сделать щелчок правой кнопкой мыши в строке BLACKFISHSQL, в появившемся списке выбрать команду Add New Connection (рис. 6.15) и в появившемся окне Add New Connection ввести имя соединения. Рис Создание соединения с базой данных Blackfish SQL ЗАМЕЧАНИЕ Информация о всех созданных соединениях находится в файле С:\Documents and Settings\All Users\Документы\RAD Studio\dbExpress\8.0\dbxconnections.ini. После того как соединение будет создано, следует раскрыть список соединений BLACKFISHSQL, сделать щелчок правой кнопкой мыши на имени только что созданного соединения и в появившемся списке выбрать команду Modify Connection (рис. 6.16).

239 234 Часть II. Практикум программирования Рис Начало настройки соединения Рис Окно Modify Connection Далее в окне Modify Connection (рис. 6.17) в поле Server Name следует ввести сетевое имя компьютера, на котором установлен Blackfish SQL Server (вместо имени компьютера можно указать localhost), в поле Database Name имя базы данных (если перед именем базы данных указать макрос DataDirectory, то база данных будет создана в каталоге, имя которого указано в файле bsqlserver.exe.config в качестве параметра ключа DataDirectory), и сделать щелчок на кнопке Advanced. Затем в открывшемся окне Advanced Properties (рис. 6.18)

240 Глава 6. Базы данных 235 свойству create следует присвоить значение True. Далее надо нажать кнопку OK. В результате этих действий будет создана база данных Blackfish SQL файлы books.jds, books_loga_ и books_loga_anchor. Убедиться, что база создана, можно, нажав кнопку Test Connection во вновь ставшем доступном окне Modify Connection. После того как база данных будет создана, можно приступить к созданию таблиц и наполнению их информацией. Делается это путем направления серверу соответствующих SQL-команд. Рис Параметры соединения Чтобы направить серверу SQL-команду, обеспечивающую, например, создание таблицы в базе данных, надо раскрыть список соединений, сделать щелчок правой кнопкой мыши на имени нужного соединения и в появившемся списке выбрать команду SQL Window (рис. 6.19). В результате этого откроется страница Data Explorer (рис. 6.20), в нижней части которой можно набирать SQL-команды. Например, чтобы создать в базе данных таблицу books, серверу надо направить команду CREATE TABLE books (title CHAR(60) NOT NULL, author CHAR(30))

241 236 Часть II. Практикум программирования Рис Команда SQL Window Рис Страница Data Explorer Процесс выполнения команды активизируется щелчком на кнопке Execute SQL (рис. 6.21). Рис Кнопка Execute SQL

242 Глава 6. Базы данных 237 Доступ к базе данных Изначально доступ к базе данных есть только у администратора сервера. Чтобы другой пользователь тоже мог работать с базой данных, администратор (или другой обладающий правами администратора пользователь) должен зарегистрировать этого нового пользователя создать учетную запись. Регистрацию (создание учетной записи) пользователя базы данных обеспечивает команда CREATE USER UserID PASSWORD "password" где UserID и password идентификатор и пароль пользователя. Например, команда CREATE guest PASSWORD "guest" создает пользователя (учетную запись пользователя) guest, который для доступа к базе данных должен использовать пароль guest. Права пользователей Пользователи, имеющие доступ к базе данных, могут обладать разными правами. Например, одному пользователю может быть разрешено только просматривать записи, другому просматривать и изменять, а третьему просматривать, добавлять и изменять. Определить права доступа пользователя можно как на уровне доступа к базе данных в целом, так и на уровне доступа к отдельным таблицам. Определить права пользователя можно, направив серверу команду GRANT privilege TO UserID где privilege список прав (табл. 6.17), UserID идентификатор пользователя. Таблица Права пользователя на уровне базы данных Идентификатор STARTUP WRITE CREATE DROP RENAME ADMINISTRATOR Права Может открыть базу данных Может записывать информацию в базу данных Может создавать новые таблицы Может удалять таблицы Может переименовывать таблицы Может открыть доступ к базе данных другим пользователям (создать пользователя), может изменить права существующих пользователей, может выполнять другие действия (WRITE, CREATE, DROP, RENAME), а также шифровать базы данных

243 238 Часть II. Практикум программирования Например, команда GRANT STARTUP TO platon открывает базу данных пользователю platon для просмотра, а команда GRANT STARTUP, WRITE TO danila предоставляет пользователю danila право записи в базу данных. Команду GRANT можно использовать для определения прав пользователя для доступа к таблицам базы данных. В этом случае команда выглядит так: GRANT privileges ON table TO UserID где privileges список прав (SQL-команд, которые пользователь может направить серверу); table таблица базы данных; UserID идентификатор пользователя. Например, команды, определяющие полномочия доступа пользователей platon и danila к таблице books, могут быть такими: GRANT SELECT ON books TO platon GRANT SELECT, INSER, UPDATE ON books TO danila Как видно, пользователь platon может только просматривать таблицу books, а пользователь danila просматривать, добавлять и редактировать информацию. Если нескольким пользователям надо предоставить одинаковые права, то вместо того, чтобы определять права для каждого пользователя, можно определить (создать) роль, указать права для этой роли и затем назначить роль пользователям. Команда, обеспечивающая создание роли, в общем виде выглядит так: CREATE ROLE RoleID где RoleID идентификатор роли. Пример: CREATE ROLE operator Команда определения полномочий роли идентична команде определения полномочий пользователя. Например, команда GRANT SELECT, INSER, UPDATE ON books TO operator определяет полномочия роли operator. После того как роль определена, ее можно назначить конкретному пользователю. Команда назначения роли пользователю в общем виде выглядит так: GRANT RoleID TO UserID Например, команда GRANT operator TO larisa предоставляет пользователю larisa полномочия в соответствии с ролью operator. В каждой базе данных по умолчанию определена роль PUBLIC. Эта роль обычно используется для обеспечения доступа к таблицам базы данных в режиме "только просмотр". Особенность этой роли заключается в том, что она автоматически назначается пользователям, полномочия которых явно (с помощью команды GRANT) не заданы. Следует обратить внимание на то, что полномочия роли PUBLIC по умолчанию не определены, их надо указывать явно.

244 Глава 6. Базы данных 239 Например, команда GRANT SELECT ON books TO PUBLIC открывает таблицу books для просмотра всем пользователям базы данных, права которых не указаны явно. База данных "Книги" Процесс создания приложения работы с базой данных Blackfish SQL рассмотрим на примере создадим программу, обеспечивающую работу с базой данных "Книги". Сначала надо создать базу данных (books), поместить в нее таблицу books, записать в таблицу данные, открыть доступ к базе данных пользователю guest и определить его полномочия. Характеристики полей таблицы books приведены в табл. 6.18, SQL-команды, обеспечивающие выполнение описанной ранее задачи, в листинге 6.8. Таблица Поля таблицы books Поле Тип Примечание id INT AUTOINCREMENT Автоувеличение title VARCHAR(60) Обязательное поле author VARCHAR(25) Листинг 6.8. Создание таблицы и определение полномочий пользователя guest CREATE TABLE books(id INT AUTOINCREMENT, title VARCHAR(60) NOT NULL, author VARCHAR(30)); INSERT INTO books(title,author) VALUES('Delphi в задачах и примерах', 'Культин Н.Б.'); INSERT INTO books(title,author) VALUES('Microsoft Visual C# в задачах и примерах', 'Культин Н.Б.'); INSERT INTO books(title,author) VALUES('Microsoft Visual C++ в задачах и примерах', 'Культин Н.Б.'); CREATE USER guest PASSWORD 'guest'; GRANT SELECT ON books TO public Доступ к базе данных Blackfish SQL Server обеспечивают компоненты SQLConnection и SQLDataset, находящиеся на вкладке dbexpress (рис. 6.22). Следует обратить внимание, что архитектура DBEXPRESS поддерживает только так называемый однонаправленный (unidirectional) доступ к данным. Это значит, что указатель текущей записи может перемещаться лишь в одном направлении только к следующей записи. Именно поэтому для просмотра данных в режиме таблицы исполь-

245 240 Часть II. Практикум программирования зовать компонент DataGrid напрямую нельзя: при попытке переместить указатель текущей строки (записи) в предыдущую строку возникает исключение. Чтобы обеспечить возможность просмотра данных в режиме таблицы, в форму приложения необходимо добавить компонент ClientDataSet (он находится на вкладке Data Access), который обеспечит буферизацию данных и возможность перемещения указателя текущей записи "назад" (к предыдущей записи). Рис Компоненты dbexpress Форма программы работы с базой данных "Книги" приведена на рис Компонент SQLConnection обеспечивает соединение с базой данных, компонент SQLDataSet хранит информацию, полученную в результате выполнения запроса (команды SELECT). Компонент ClientDataSet используется в качестве буфера, он хранит данные (копию), полученные из базы, и обеспечивает возможность просмотра данных с помощью компонента DataGrid. Связь между компонентами SQLDataSet и ClientDataSet осуществляет компонент DataSetProvider, между компонентами ClientDataSet и DBGrid компонент DataSource. Рис Форма программы работы с базой данных "Книги" Значения свойств компонентов приведены в табл Необходимо обратить внимание, что значения следует устанавливать в том порядке, в котором они приведены в таблице.

246 Глава 6. Базы данных 241 Таблица Значения свойств компонентов Компонент Свойство Значение SQLConnection1 Name SQLConnection1 ConnectionName DriverName LoginPrompt LoadParamsOnConnect books BLACKFISHSQL False False SQLDataSet1 Name SQLDataSet1 SQLConnection CommandText SQLConnection1 SELECT * FROM books DataSetProvider1 Name DataSetProvider1 DataSet SQLDataSet1 ClientDataSet11 Name ClientDataSet11 ProviderName DataSetProvider1 DataSource1 Name DataSource1 DataSet ClientDataSet1 DataGrid1 Name DataGrid1 DataSource ReadOnly Columns[0].FieldName Columns[0].Title.Caption DataSource1 True author Автор Columns[0].Title.Width 100 Columns[0].Title.Font.Style fsbold Columns[1].FieldName Columns[1].Title.Caption title Название Columns[1].Width 350 Columns[0].Title.Font.Style fsbold

247 242 Часть II. Практикум программирования Процедуры обработки событий приведены в листинге 6.9. Листинг 6.9. Процедуры обработки событий uses DBXCOmmon; procedure TForm1.FormActivate(Sender: TObject); try SQLConnection1.Open; ClientDataset1.Open; StatusBar1.SimpleText := 'Записей: ' + IntToStr(SQLDataset1.RecordCount); except on e:tdbxerror do ShowMessage(e.Message); procedure TForm1.Button1Click(Sender: TObject); var cmd: string; cmd := 'SELECT * FROM books WHERE UPPER(title) LIKE UPPER(''%'+ Edit1.Text + '%'')'; < При попытке изменить значение свойства CommandText открытого набора данных возникает исключение. Поэтому перед тем как изменить значение свойства CommandText, закроем набор данных. >SQLConnection1.Close; ClientDataset1.close; SQLDataset1.CommandText := cmd; SQLConnection1.Open; ClientDataset1.Open; if SQLDataset1.RecordCount <> 0 then StatusBar1.SimpleText := 'Записей: ' + IntToStr(SQLDataset1.RecordCount)

248 Глава 6. Базы данных 243 else StatusBar1.SimpleText := 'В БД нет записей, удовлетворяющих критерию запроса'; procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char); if Key = Chr(VK_RETURN) then Button1.SetFocus; Как было сказано, информация о соединениях, созданных программистом, находится в файле dbxconnections.ini, из которого она при настройке компонента SQLConnection копируется в свойство Params. Если вы предполагаете, что программа работы с базой данных будет устанавливаться на другие компьютеры, то свойству LoadParamsOnConnect компонента SQLConnection следует присвоить значение True. В этом случае параметры соединения будут загружаться из файла dbxconnections.ini при каждом открытии соединения. Такое решение позволяет выполнять настройку программы работы с базой данных путем внесения изменений в файл конфигурации dbxconnections.ini. Параметры соединения отображаются в окне Value List editor (рис. 6.24), которое открывается в результате щелчка на находящейся в поле значения свойства Params кнопке с тремя точками. Рис Параметры соединения с БД Blackfish SQL Server

249 244 Часть II. Практикум программирования Развертывание приложения работы с базой данных Blackfish SQL Server Установка и настройка сервера Сервер Blackfish SQL Server является.net-приложением. Поэтому, перед тем как приступить к непосредственной установке Blackfish SQL Server, необходимо убедиться, что на компьютере, на который устанавливается сервер, есть Microsoft.NET Framework (на компьютер программиста Microsoft.NET Framework устанавливается автоматически, вместе с Delphi). Следует обратить внимание, что платформа Microsoft.NET Framework используется многими современными программами, и вполне вероятно, что она уже есть на компьютере пользователя. Microsoft.NET Framework также гарантированно есть на компьютере, если на нем установлена ОС Windows Vista или Windows 7. Сначала на диск компьютера (лучше в отдельный каталог) надо скопировать файлы BSQLServer.exe, Borland.Data.Blackfish.LocalClient.dll, BSQLServer.exe.config, а также файл лицензии (с расширением slip). После этого следует определить каталог, предназначенный для файлов баз данных. Имя этого каталога надо указать в файле конфигурации сервера (BSQLServer.exe.config) в качестве значения ключа blackfisfsql.datadirectory, например: <add key="blackfishsql.datadirectory" value="d:\database\" /> Следует обратить внимание, что если значение ключа blackfisfsql.datadirectory задано, то клиент для доступа к базе данных вместо абсолютного пути может указать относительный (для чего в строке Database файла параметров соединения с базой данных путь к базе данных нужно заменить макросом DataDirectory ). После этого надо открыть окно командной строки и набрать команду Каталог\BSQLServer.exe install где Каталог имя каталога, в котором находятся файлы Blackfish SQL Server. Эта команда обеспечивает установку Blackfish SQL Server как службы. Теперь при каждом включении сервера (компьютера) Blackfish SQL Server будет запускаться автоматически. ЗАМЕЧАНИЕ При установке службы Blackfish SQL Server в Windows Vista окно командной строки надо открыть от имени администратора. Следует обратить внимание на то, что по умолчанию брандмауэр Windows блокирует доступ клиента (программы работы с базой данных) к серверу. Поэтому после установки Blackfish SQL Server необходимо в список исключений брандмауэра добавить ссылку на BSQLServer (рис. 6.25).

250 Глава 6. Базы данных 245 Рис Настройка брандмауэра Установка программы работы с базой данных Процесс установки программы работы с базой данных Blackfish SQL Server на компьютер пользователя состоит из двух шагов. Сначала на диск компьютера пользователя (лучше в отдельный каталог) надо скопировать файлы, образующие программу работы с базой данных (список файлов программы работы с базой данных "Книги" приведен в табл. 6.20). Таблица Файлы программы работы с базой данных "Книги" Файл bf_books.exe midas.dll Комментарий Программа работы с базой данных "Книги" Библиотека, реализующая функциональность компонента ClientDataSet

251 246 Часть II. Практикум программирования Таблица 6.20 (окончание) Файл dbxconnections.ini Комментарий Файл параметров соединения Books (копия файла с компьютера разработчика). Заметим, что информацию о соединениях, не используемых программой, из файла dbxconnections.ini рекомендуется удалить dbxdrivers.ini Файл параметров драйвера базы данных Затем надо внести изменения в файл параметров соединения dbxconnections.ini указать имя компьютера, на котором работает сервер Blackfish SQL Server. Если сервер и программа работы с базой данных работают на одном компьютере, то в качестве значения параметра HostName надо указать localhost. Если программа работы с базой данных и сервер работают на разных компьютерах, то в качестве значения параметра HostName надо указать сетевое имя компьютера, на котором работает сервер. Кроме этого в файл C:\Windows\System32\drivers\ets\hosts рабочей станции надо записать имя и IP-адрес сервера (узнать IP-адрес сервера можно запустив на сервере утилиту ipconfig.exe). В качестве примера в листинге 6.10 приведен фрагмент файла dbxconnections.ini, а в листинге 6.11 файла hosts. Обратите внимание, что в файле конфигурации указан макрос DataDirectory, поэтому в файле bsqlserver.exe.config должно быть указано значение ключа blackfisfsql.datadirectory. Листинг Параметры соединения с базой данных "Книги" (dbxconnections.ini) [Books] drivername=blackfishsql User_Name=guest port=2508 create=false readonlydb=false HostName=danila Database= DataDirectory Books Листинг Файл hosts # (C) Корпорация Майкрософт (Microsoft Corp.), # # Это образец файла hosts, используемый Microsoft TCP/IP для Windows. # # Этот файл содержит сопоставления IP-адресов именам узлов. # Каждый элемент должен располагаться в отдельной строке.

252 Глава 6. Базы данных 247 # IP-адрес должен находиться в первом столбце, за ним должно следовать # соответствующее имя. # IP-адрес и имя узла должны разделяться хотя бы одним пробелом. # # Кроме того, в некоторых строках могут быть вставлены комментарии # (такие, как эта строка), они должны следовать за именем узла # и отделяться от него символом '#'. # # Например: # # rhino.acme.com # исходный сервер # x.acme.com # узел клиента x localhost danila

253 ГЛАВА 7 Компонент программиста Программист может создать собственный компонент, поместить его на одну из вкладок палитры компонентов и использовать при разработке приложений точно так же, как и другие компоненты Delphi. Новый компонент проще всего можно создать путем расширения возможностей уже существующего компонента. Например, компонент, обеспечивающий ввод (редактирование) числа, можно создать на основе компонента Edit, предназначенного для ввода и редактирования текста. Процесс создания компонента рассмотрим на примере. Создадим компонент (назовем его NkEdit), который внешне ничем не будет отличаться от стандартного поля редактирования (компонента Edit), но в его поле можно будет ввести только число. Приступая к разработке нового компонента, следует четко сформулировать его назначение. Затем необходимо определить, какой из существующих компонентов наиболее близок по своему назначению и функциональным возможностям к компоненту, который надо создать. Именно этот компонент следует выбрать в качестве базового. Так как компонент NkEdit предназначен для ввода и редактирования числа (строки символов, содержащей только цифры и, возможно, десятичный разделитель), то в качестве базы для нового компонента NkEdit выберем компонент Edit. Очевидно, что у нового компонента должны быть и новые свойства (табл. 7.1), обеспечивающие решение поставленной задачи. Свойство Тип Описание Таблица 7.1. Свойства компонента NkEdit MaxLenInt Integer Максимальное количество цифр целого числа или количество цифр целой части дробного числа, которое можно ввести в поле редактирования. По умолчанию значение свойства равно 6 MaxLenFrac Integer Максимальное количество цифр дробной части числа, которое можно ввести в поле редактирования. По умолчанию значение свойства равно 2

254 Глава 7. Компонент программиста 249 Свойство Тип Описание Таблица 7.1 (окончание) OnlyPositive Boolean Если значение свойства равно True, то в поле редактирования можно ввести только положительное число. По умолчанию значение свойства равно False Value Single Значение, соответствующее строке, введенной в поле редактирования NextControl TWinControl Элемент управления (компонент), на который перемещается фокус в результате нажатия в поле редактирования клавиши <Enter> Модуль компонента ЗАМЕЧАНИЕ Перед тем как приступить к работе по созданию нового компонента, рекомендуется создать новый каталог для модуля компонента. Рис Выбор базового класса для нового компонента

255 250 Часть II. Практикум программирования Чтобы начать работу над новым компонентом, надо в меню Components выбрать команду New VCL Component. Затем в появившемся окне New VCL Component следует выбрать базовый класс для создаваемого компонента. Так как компонент NkEdit разрабатывается на основе компонента Edit, то в качестве базового класса надо указать класс TEdit. В следующем окне (рис. 7.2), которое становится доступным в результате щелчка на кнопке Next, надо задать имя класса (поле Class Name) компонента, вкладку палитры компонентов, на которую будет помещен значок компонента (поле Palette Page), и имя модуля (поле Unit name) создаваемого компонента. Рис В поле Unit name надо ввести имя модуля создаваемого компонента Согласно принятому в Delphi соглашению имя класса (типа) должно начинаться с буквы "T". Поэтому в поле Class Name надо ввести TNkEdit. По умолчанию компоненты, созданные программистом, устанавливаются на вкладку Samples. Если в поле Palette Page ввести имя еще не существующей вкладки, то в процессе установки компонента вкладка с указанным именем будет создана. После того как будут заданы класс компонента, страница палитры компонентов и имя модуля, надо нажать кнопку Next. Далее, в следующем окне, надо выбрать переключатель Create Unit (Создать модуль) и сделать щелчок на кнопке Finish. В результате описанных действий будет создан модуль пакета компонентов (листинг 7.1).

256 Глава 7. Компонент программиста 251 Листинг 7.1. Шаблон модуля компонентов (NkCtrls.pas) unit NkCtrls; interface uses SysUtils, Classes, Controls, StdCtrls; type TNkEdit = class(tedit) private < Private declarations >protected < Protected declarations >public < Public declarations >published < Published declarations >procedure Register; implementation procedure Register; RegisterComponents('kultin', [TNkEdit]); end. В созданный Delphi модуль компонента нужно добавить объявления свойств, полей, процедур и функций, обеспечивающих доступ к полям. Если на какое-либо событие компонент должен реагировать не так, как базовый, то в объявление класса нужно поместить объявление соответствующей процедуры обработки события. В листинге 7.2 приведен модуль компонента NkEdit после внесения всех необходимых дополнений. Листинг 7.2. Модуль компонентов (NkCtrls.pas) unit NkCtrls; interface uses SysUtils, Classes, Controls, StdCtrls;

257 252 Часть II. Практикум программирования type TNkEdit = class(tedit) private FOnlyPositive: Boolean; // True в поле компонента можно ввести // только положительное число FMaxLenInt: Integer; // допустимое количество цифр целой части FMaxLenFrac: Integer; // допустимое количество цифр дробной части FNextControl: TWinControl; // компонент, на который перемещается // фокус в результате нажатия <Enter> procedure SetfValue(value: single); function GetfValue: single; protected // процедура обработки события KeyPress procedure KeyPress(var Key: char); override; public // конструктор constructor Create(AOwner: TComponent); override; published // объявления свойств property OnlyPositive: boolean read FOnlyPositive write FOnlyPositive default False; property MaxLenInt: integer read FMaxLenInt write FMaxLenInt default 6; property MaxLenFrac: integer read FMaxLenFrac write FMaxLenFrac default 6; property Value: single read GetfValue write SetfValue;

258 Глава 7. Компонент программиста 253 property NextControl: TWinControl read FNextControl write FNextControl default Nil; procedure Register; implementation procedure Register; RegisterComponents('Kultin_2010', [TNkEdit]); // конструктор constructor TNkEdit.Create(AOwner: TComponent); // вызвать конструктор базового класса inherited Create(AOwner); // чтобы в поле компонента не отображалось его имя ControlStyle := ControlStyle - [cssetcaption]; // конструктор имеет прямой доступ к полям FMaxLenInt := 6; FMaxLenFrac := 2; FOnlyPositive := False; // свойство Text, унаследованное от базового класса Text := '0'; // устанавливает значение свойства Value procedure TNkedit.SetfValue(value: Single); Text := FloatToStrF(value,ffFixed,self.FMaxLenInt,self.MaxLenFrac); // возвращает значение свойства Value function TNkEdit.GetfValue : single;

259 254 Часть II. Практикум программирования try GetfValue := StrToFloat(Text); except on e: EConvertError do GetfValue := 0; Text := '0'; // нажатие клавиши в поле редактирования компонента NkEdit procedure TNkEdit.KeyPress(var Key: Char); var n : integer; // количество цифр p : integer; // позиция десятичного разделителя case Key of '0'..'9': p := Pos(DecimalSeparator,self.Text); if (p = 0) or (self.selstart < p) then // цифра целой части // (SelStart позиция курсора в поле редактирования) if p = 0 then n := Length(self.Text) else n := p -1; if (n > 0) and (self.text[1] = '-') then n := n-1; if n >= self.maxlenint then Key := #0; end else // цифра дробной части n := Length(self.Text) - p; if n >= self.maxlenfrac then Key := #0;

260 Глава 7. Компонент программиста 255 #8,#9: ; #13: if NextControl <> Nil then NextControl.SetFocus; ',','.': Key := DecimalSeparator; if (Pos(DecimalSeparator,self.Text) <> 0) or (self.maxlenfrac = 0) then Key := #0; '-': if (FOnlyPositive) or (self.selstart > 0) or (Pos('-',self.Text) = 1) then Key := #0; else // остальные символы не отображать key:= #0; // вызов процедуры обработки события KeyPress базового класса inherited KeyPress(Key); end. В секции private класса TNkEdit объявлены поля FOnlyPositive, FMaxLenInt и FMaxLenFrac (имена полей, согласно принятому в Delphi соглашению, начинаются с буквы F, от англ. Field поле). Поля FOnlyPositive, FMaxLenInt, FMaxLenFrac и FNextContol хранят характеристики компонента: FOnlyPositive вид числа (положительное или отрицательное), которое можно ввести в поле редактирования; FMaxLenInt максимально допустимое количество цифр целой части числа; FMax- LenFrac максимально допустимое количество цифр дробной части числа; FnextControl имя компонента, на который будет перемещен фокус в результате нажатия в поле редактирования клавиши <Enter>. Следует обратить внимание, что поля объявлены в секции private, поэтому у программ, которые будут использовать компонент NkEdit, доступа к ним не будет. В секции published объявлен конструктор. Объявление constructor Create(AOwner: TComponent); override; показывает, что конструктор класса NkEdit замещает конструктор базового класса (override подменить).

261 256 Часть II. Практикум программирования Свойства объявлены в секции published, поэтому они будут отображаться в окне Object Inspector. В объявлении свойства указывается тип значения свойства, а также поле, которое хранит значение свойства, или функция и процедура, которые обеспечивают доступ к полю. Например, объявление property OnlyPositive: boolean read FOnlyPositive write FOnlyPositive default False; показывает, что значением свойства OnlyPositive является содержимое поля FOnlyPositive, что свойство доступно как для чтения, так и для записи (значение свойства можно изменить во время работы программы), а также то, что по умолчанию значение свойства равно False. Объявление свойства Value выглядит иначе: property Value: single read GetfValue write SetfValue; Следует обратить внимание, что после read указано имя функции, а после write процедуры. Это значит, что значением свойства Value является значение функции GetValue, а устанавливает значение свойства Value процедура SetValue. Сами же функция GetValue и процедура SetValue объявлены в секции private, поэтому программе, которая будет использовать компонент NkEdit, они, как и поля, доступны не будут. Процедура KeyPress обрабатывает соответствующее событие и обеспечивает фильтрацию символов, которые можно ввести в поле компонента NkEdit. В объявлении процедуры указана директива override. Это значит, что она подменяет соответствующую процедуру базового компонента. В разделе implementation находятся процедуры и функции, объявленные в объявлении класса TNkEdit. Конструктор создает компонент (инструкция inherited Create(AOwner) вызывает конструктор базового класса) и настраивает его. Процедура TNkEdit.KeyPress обеспечивает обработку события KeyPress. В качестве параметра эта процедура получает символ, соответствующий нажатой клавише. Если символ допустимый, то процедура "ничего не делает", а просто вызывает процедуру KeyPress базового класса (в результате чего символ появляется в поле редактирования). Если символ недопустимый, то он заменяется символом "ноль", после чего также вызывается функция KeyPress базового класса. Следует обратить внимание на процедуру Register. Она обеспечивает регистрацию типа TNkEdit в среде разработки. После того как в модуль будут внесены все необходимые дополнения, модуль компонента надо сохранить (имя файла модуля должно совпадать с именем модуля, указанным в директиве unit). Необходимо обратить внимание, что модуль ком-

262 Глава 7. Компонент программиста 257 понента не является самостоятельной программой, поэтому выполнить его компиляцию нельзя (команда Project Compile недоступна). Тестирование модуля компонента После того как будет создан модуль компонента, необходимо убедиться, что компонент работает правильно. Для этого надо создать приложение, которое будет использовать созданный компонент. Так как компонент NkEdit пока еще не включен ни в один из пакетов компонентов, то его значка нет в палитре компонентов и, следовательно, поместить созданный компонент на форму привычным способом нельзя. Поэтому тестируемый компонент придется создать в коде поместить в текст программы инструкции, обеспечивающие создание и настройку компонента. Приложение, обеспечивающее тестирование модуля компонента, создается следующим образом. Сначала надо активизировать процесс создания нового приложения, настроить форму и сохранить проект в том каталоге, в котором находится модуль тестируемого компонента. Рис Форма программы тестирования модуля компонента NkEdit (поля ввода данных будут созданы во время работы программы) Форма программы тестирования модуля компонента NkEdit приведена на рис Она содержит три компонента Label и командную кнопку. Полей редактирования, предназначенных для ввода исходных данных, на форме нет. Они (два компонента NkEdit) будут созданы во время работы программы. После того как проект будет сохранен, в модуль формы программы тестирования надо внести следующие дополнения (листинг 7.3): 1. В директиву uses добавить имя модуля тестируемого компонента NkCtrls. 2. В раздел public объявления формы поместить объявление двух компонентов (объектов) NkEdit. 3. Создать процедуру обработки события Create и поместить в нее инструкции, обеспечивающие создание и настройку тестируемых компонентов.

263 258 Часть II. Практикум программирования Листинг 7.3. Тестирование модуля компонента NkEdit unit NkEditTest; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, // ссылка на модуль компонента NkCtrls; type TForm1 = class(tform) Label1: TLabel; Label2: TLabel; Button1: TButton; Label3: TLabel; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); private < Private declarations >public // тестируемые компоненты NkEdit1 : TNkEdit; NkEdit2 : TNkEdit; var Form1: TForm1; implementation // обработка события Create procedure TForm1.FormCreate(Sender: TObject); // По умолчанию в поле компонента NkEdit можно ввести положительное // или отрицательное дробное число. Изменив значения свойств // OnlyPositive, MaxLenInt и MaxLenFrac, // можно выполнить "тонкую" настройку компонента

264 Глава 7. Компонент программиста 259 < Внимание! Присвоить значение свойству NextControl компонента NkEdit1 можно только после создания компонента NkEdit2. В противном случае значение NextControl будет Nil. >// создать NkEdit1 NkEdit1 := TNkEdit.Create(self); NkEdit1.Parent := self; // создать NkEdit2 NkEdit2 := TNkEdit.Create(self); NkEdit2.Parent := self; // настроить NkEdit1 поле "Цена" NkEdit1.OnlyPositive := True; NkEdit1.NextControl := NkEdit2; // значения свойств MaxLenInt и MaxLenFrac оставим без изменения // свойства Top, Left, TabOrder унаследованы от TEdit NkEdit1.Top := 20; NkEdit1.Left := 72; NkEdit1.TabOrder := 0; // настроить NkEdit2 поле "Курс" NkEdit2.NextControl := Button1; NkEdit2.OnlyPositive := True; NkEdit2.MaxLenInt := 2; NkEdit2.MaxLenFrac := 2; NkEdit2.Top := 50; NkEdit2.Left := 72; NkEdit2.TabOrder := 1; // щелчок на кнопке OK procedure TForm1.Button1Click(Sender: TObject); var usd: real; // цена в долларах k: real; // курс rub: real; // цена в рублях usd := NkEdit1.Value;

265 260 Часть II. Практикум программирования k := NkEdit2.Value; rub := usd * k; Label3.Caption := NkEdit1.Text + '$ = ' + FloatToStrF(rub,ffCurrency,6,2); end. Тестируемые компоненты создает и настраивает процедура обработки события Create формы. Создается компонент методом Create класса TNkEdit. Настройка компонента выполняется путем изменений его свойств. Изменить можно как значения уникальных свойств компонента NkEdit, так и значения свойств, унаследованных от "родителя", компонента Edit. Необходимо обратить внимание на идентификатор self. Внутри процедуры обработки события он обозначает объект, событие которого обрабатывает процедура, т. е. форму. Идентификатор self передается конструктору объекта в качестве параметра, и тем самым выполняется привязка созданного объекта к форме. Следует обратить внимание, что свойству Parent вновь созданного компонента обязательно надо присвоить значение self. Если этого не сделать, то созданный компонент на форме не появится. На рис. 7.4 приведено окно программы тестирования модуля компонента NkEdit. Рис Тестирование модуля компонента: поля ввода данных компоненты NkEdit Пакет компонентов Для того чтобы значок компонента появился на одной из страниц палитры компонентов, его надо поместить в пакет (Package) компонентов. Пакет компонентов это динамическая библиотека, в которой находится код, реализующий функциональность компонентов. Различают пакеты времени разработки (Designtime package) и пакеты времени выполнения (Runtime package). Пакеты времени разработки используются средой разработки, пакеты времени выполнения программами, использующими компоненты. Кроме пакетов указанных типов существуют универсальные пакеты (Design-

266 Глава 7. Компонент программиста 261 time and runtime package), которые могут использоваться как средой разработки, так и приложениями. Программист может поместить свой компонент в один из уже существующих пакетов или создать новый пакет и затем поместить в него свой компонент. Создание пакета компонентов Как было сказано, для того чтобы компонент был доступен разработчику, он должен находиться в пакете компонентов. Компонент программиста можно добавить в один из существующих пакетов или в новый, специально созданный, пакет. Рассмотрим, как создать пакет компонентов и поместить в него компонент. Чтобы создать пакет, надо в меню File выбрать команду New Package - Delphi. В результате чего будет создан новый модуль пакет (чтобы увидеть модуль, надо в меню Project выбрать команду View Source). Созданный пакет рекомендуется сразу сохранить в том каталоге, где находится модуль компонента, который предполагается поместить в пакет. Для этого надо в меню File выбрать команду Save Project и указать в качестве имени проекта предполагаемое имя пакета (например, NkPackage). После того как пакет (проект создания пакета) будет сохранен, в него надо добавить модуль компонента в меню Project выбрать команду Add to Project и в появившемся окне указать модуль компонента. В результате этого в списке Contains структуры пакета, которая отображается в окне Project Manager, появится имя добавленного модуля (рис. 7.5). Рис Структура пакета отображается в окне Project Manager

267 262 Часть II. Практикум программирования По умолчанию для изображения созданного программистом компонента на странице палитры компонентов используется значок компонента родительского класса. Если вы хотите, чтобы созданный компонент выглядел иначе, то надо создать файл ресурсов и поместить в него битовый образ (размер пиксела), который будет изображать компонент в палитре. В файл ресурсов можно поместить три битовых образа: 16 16, и Битовый образ должен иметь имя, совпадающее с именем типа компонента. У битовых образов и должны быть соответственно суффиксы 16 и 32. Создать файл ресурсов можно, например, с помощью утилиты Image Editor (рис. 7.6). После того как файл ресурсов будет создан, в файл проекта (он открывается командой Project View Source) создания пакета компонентов надо добавить ссылку (директива ) на этот файл (листинг 7.4). Рис Файл ресурсов пакета Листинг 7.4. Файл проекта package NkPackage;

268 Глава 7. Компонент программиста 263 requires rtl, vcl; contains NkCtrls in 'NkCtrls.pas'; end. Компиляция пакета компонентов Перед тем как выполнить компиляцию пакета, рекомендуется задать имя пакета. Его надо ввести в поле в окне Project Options в разделе Description (рис. 7.7). После того как пакет будет создан и в него будут помещены модули компонентов, можно выполнить компиляцию пакета выбрать в меню Project команду Compile. Результатом компиляции является пакет (bpl-файл) и компилированный модуль пакета (dcp-файл). Пакет (он создается в папке C:\Users\Public\Documents\RAD Studio\8.0\Bpl) необходим для компиляции и выполнения приложений в режиме использования run-

269 264 Часть II. Практикум программирования time-пакетов (Build with runtime packages). Компилированный модуль пакета (он создается в папке C:\Users\Public\Documents\RAD Studio\8.0\Dcp) необходим для компиляции приложений, использующих компоненты пакета, в режиме включения всего кода в exe-файл (Without runtime packages). Рис Имя пакета надо ввести в поле Description Установка пакета компонентов Для того чтобы значок компонента, находящегося в пакете, появился в палитре компонентов, пакет надо установить. Чтобы установить пакет, надо в меню Component выбрать команду Install Packages, в появившемся окне Install Packages нажать кнопку Add, затем в следующем окне открыть папку, в которой находится пакет, и выбрать bpl-файл. В результате описанных действий пакет будет установлен (рис. 7.8). Следует обратить внимание на то, что палитра компонентов отображается только в режиме конструирования формы. Поэтому, чтобы убедиться, что компонент, который находится в установленном пакете, действительно установлен и стал доступен, надо активизировать процесс создания нового приложения VCL Forms. Если процесс установки был выполнен верно, в палитре компонентов на вкладке, которая указана в процедуре регистрации компонента, должен появиться значок компонента (рис. 7.9).

270 Глава 7. Компонент программиста 265 Рис Пакет nk_package установлен Рис Значок компонента NkEdit отображается на вкладке Kultin_2010

271 266 Часть II. Практикум программирования ЗАМЕЧАНИЕ Возможна ситуация, когда после установки компонента и последующей активизации процесса создания нового приложения (после перезагрузки Delphi) вместо уникального значка компонента (или значка компонента базового класса) отображается значок "компонент". Причина этого в том, что битовые образы значков кэшируются в реестре (раздел HKEY_CURRENT_USER\Software\CodeGear\BDS\8.0\Palette\Cashe) и загружаются оттуда. Если по какой-либо причине при первой установке пакета (в процессе отладки) значок не был создан правильно, то при последующих загрузках Delphi будет загружаться именно этот, неправильный значок, даже в том случае, если ошибка устранена (сразу после установки пакета отображается правильный значок). Для устранения описанной ситуации в командной строке запуска Delphi надо указать параметр nocache. Для компиляции приложения, использующего компоненты пакета, необходим модуль компонентов (dcu-файл). Если предполагается, что компонент будет использоваться несколькими приложениями, то модуль компонента рекомендуется поместить в общедоступную папку, например в C:\Users\Public\Documents\RAD Studio\8.0\Dcu (ее надо создать), и указать ее имя в списке папок, в которых компилятор выполняет поиск кода. Имя папки надо ввести в окне Search path (рис. 7.10), которое становится доступным в результате активизации процесса коррекции списка Search path параметров компилятора (вкладка Delphi Compiler окна Project Options). Рис Список папок, в которых компилятор ищет код, необходимый в процессе компиляции

272 Глава 7. Компонент программиста 267 Тестирование компонента После того как значок компонента NkEdit появится в палитре компонентов, необходимо проверить поведение компонента во время разработки приложения (работоспособность компонента была проверена раньше, когда компонент добавлялся на форму приложения динамически, во время работы программы). Можно считать, что компонент работает правильно, если во время разработки приложения удалось поместить компонент на форму и, используя Object Inspector, установить значения свойств, причем как новых, так и унаследованных от родительского класса. Работоспособность компонента NkEdit можно проверить, использовав его, например, в программе "Доход". Форма приложения приведена на рис После того как на форму будут добавлены компоненты, следует, используя Object Inspector (рис. 7.12), выполнить их настройку (табл. 7.2). После чего можно приступить к созданию процедур обработки событий. Рис Форма программы "Доход" Таблица 7.2. Значения свойств компонентов NkEdit Компонент Свойство Значение NkEdit1 MaxLenInt 6 MaxLenFrac 2 NextControl OnlyPositive NkEdit2 True NkEdit2 MaxLenInt 2 MaxLenFrac 0 NextControl OnlyPositive Button1 True

273 268 Часть II. Практикум программирования Рис Значения свойств MaxLenFrac, MaxLenInt, NextControl, OnlyPositive и Value компонента NkEdit можно задать в окне Object Inspector В листинге 7.5 приведен модуль приложения "Доход". Здесь надо обратить внимание на следующее. В программе нет кода, обеспечивающего фильтрацию символов, вводимых в поля редактирования. Тем не менее во время работы программы пользователь сможет ввести в поля редактирования только положительные числа, причем в поле Срок можно ввести только целое число. В программе не используются функции StrToFloat и StrToInt. Числа, соответствующие строкам, находящимся в полях редактирования, получаются путем обращения к свойствам Value соответствующих компонентов NkEdit. Очевидно, что использование в программе компонента NkEdit вместо стандартного Edit освобождает программиста от рутины, сокращает размер кода, делает его более понятным. Листинг 7.5. Доход по вкладу (тест компонента NkEdit) unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, NkCtrls;

274 Глава 7. Компонент программиста 269 type TForm1 = class(tform) NkEdit1: TNkEdit; NkEdit2: TNkEdit; Button1: TButton; Label1: TLabel; Label2: TLabel; Label3: TLabel; procedure Button1Click(Sender: TObject); private < Private declarations >public < Public declarations >var Form1: TForm1; implementation procedure TForm1.Button1Click(Sender: TObject); var sum: real; // сумма period: integer; // срок percent: real; profit: real; // процентов в год // доход sum := NkEdit1.Value; period := Round(NkEdit2.Value); case period of 1..3: percent := 8; 4..6: percent := 9.5; 7..12: percent := 10.5; : percent := 14; else percent := 17;

275 270 Часть II. Практикум программирования profit := sum * (percent/100/12*period); Label3.Caption := 'Cумма: ' + FloatToStrF(sum,ffCurrency,6,2) + #13 + 'Cрок: ' + IntToStr(period) + 'мес.' + #13 + 'Процент: ' + FloatToStrF(percent,ffFixed,2,2) + '%' +#13 + 'Доход: ' + FloatToStrF(profit,ffCurrency,6,2); end. Установка программы на другой компьютер Программе, созданной в Delphi XE, необходимы как минимум две библиотеки: rtl150.bpl и vcl150.bpl (согласно принятому соглашению после имени библиотеки указывается номер ее версии, соответствующий номеру версии среды разработки). На компьютер разработчика эти библиотеки устанавливаются в процессе инсталляции Delphi. Если программа, использующая компонент, созданный программистом, скомпилирована в режиме использование пакетов времени выполнения (Build with runtime packages), то помимо стандартных библиотек для ее работы необходим пакет (bpl-файл), в котором находится компонент. На компьютере пользователя библиотеки, необходимые для работы приложения, можно поместить в каталог С:\Windows\system32 или в тот каталог, в котором находится exe-файл программы (программа ищет необходимые ей библиотеки сначала в том каталоге, из которого она запущена, а затем в каталоге system32). Следует обратить внимание на то, что программист может существенно облегчить себе жизнь, если включит весь код, необходимый для работы программы, в exe-файл. Для этого, перед тем как выполнить компиляцию, надо на вкладке Packages окна Project Options, которое становится доступным в результате выбора в меню Project команды Options, сбросить флажок Build with runtime packages. Распространение компонента Созданный компонент можно передать коллегам-программистам для того, чтобы они могли использовать его в своих разработках. В установочный комплект надо включить bpl- и dcp-файлы пакета, dcu-файлы модулей компонентов и инструкцию по установке. Если пакет распространяется на коммерческой основе, то рекомендуется создать установщик.

276 ГЛАВА 8 Справочная информация Разработчик программы должен позаботиться о том, чтобы пользователю было удобно работать с программой, чтобы при возникновении какой-либо проблемы он мог быстро получить рекомендацию по ее устранению, ответ на возникший вопрос. Решить обозначенную задачу можно, создав справочную систему, обеспечивающую доступ к справочной информации. Справочная система HTML Help В настоящее время в большинстве прикладных программ справочная информация отображается в формате HTML Help по запросу пользователя (в результате нажатия клавиши <F1>, выбора соответствующей команды в меню Справка или нажатия кнопки Справка в диалоговом окне) в отдельном окне. Как правило, это окно разделено на две части: в левой части отображается список разделов справочной информации (оглавление), в правой содержимое выбранного раздела (рис. 8.1). Рис Пример справочной информации в формате HTML Help

277 272 Часть II. Практикум программирования Основой справочной системы формата HTML Help являются компилированные HTML-документы (chm-файлы) совокупность отдельных HTML-страниц справочной информации. Помимо HTML-страниц chm-файл обычно содержит список названий разделов справочной информации (оглавление) и предметный указатель (индекс). Отображение справочной информации формата HTML Help обеспечивает системная утилита hh.exe. Таким образом, задача создания справочной системы приложения сводится к задаче создания CHM-файла. Создать chm-файл можно с помощью утилиты Microsoft HTML Help Workshop. Сначала надо подготовить справочную информацию (поместив информацию каждого раздела в отдельный HTML-файл), затем выполнить компиляцию. Процесс преобразования исходной справочной информации в chm-файл представлен на рис Справочная информация (htm-файлы) Файл проекта (hlp-файл) Файл контента (hhc-файл) CHM-компилятор (Microsoft HTML Help Workshop) Компилированный файл справочной информации (chm-файл) Рис Процесс создания chm-файла Подготовка справочной информации Исходным "материалом" для chm-компилятора является справочная информация, представленная в виде набора HTML-файлов. Создать файлы справочной информации в HTML-формате можно с помощью любого HTML-редактора, в том числе и входящего в состав Delphi. В простейшем случае всю справочную информацию можно поместить в один HTML-файл. Однако если для навигации по справочной системе предполагается использовать вкладку Содержание, на которой будут перечислены разделы справочной информации, то информацию каждого раздела нужно поместить в отдельный HTML-файл. Процесс подготовки справочной информации рассмотрим на примере программы "Чистый дисконтированный доход". Пусть справочная информация будет состоять из страниц Чистый дисконтированный доход, Финансовые результаты, Финансовые затраты, Ставка дисконтирования и О программе. Следовательно, необходимо создать пять HTML-документов. Чтобы в Delphi начать работу над новым HTML-документом, надо в меню File выбрать команду New Other Web Documents HTML Page. В результа-

278 Глава 8. Справочная информация 273 те откроется вкладка Design HTML-редактора (рис. 8.3), в которой можно набирать текст. Рис Окно HTML-редактора (режим Design) В верхней части окна HTML-редактора находится панель инструментов, используя которую можно задать стиль оформления абзаца, в том числе указать, что абзац является заголовком или элементом списка, выбрать шрифт, задать цвет символов, изменить стиль оформления текста (полужирный, курсив). Заголовок страницы (раздела) справочной информации рекомендуется оформить одним из стилей Заголовок. На страницу можно поместить картинку (рекомендуется заранее поместить файл картинки в каталог проекта справочной информации). Чтобы это сделать, нужно установить курсор в ту точку HTML-документа, в которую надо вставить картинку, сделать щелчок на кнопке Insert Image и в поле Image Source появившегося окна ввести имя файла иллюстрации. После того как HTML-текст раздела справочной информации будет набран, документ надо сохранить в каталоге проекта справочной системы выбрать в меню File команду Save и задать имя файла. Имя рекомендуется задать так, чтобы в нем присутствовал номер раздела. Например, текст раздела Чистый дисконтированный доход следует сохранить в файле npv_01.htm, раздела Финансовые результаты в файле npv_02.htm и т. д.

279 274 Часть II. Практикум программирования Рис Пример HTML-страницы справочной информации После того как справочная информация (отдельные HTML-страницы) будет подготовлена, можно приступить к созданию chm-файла. Microsoft HTML Help Workshop Как было сказано, создать справочную систему формата HTML Help можно с помощью Microsoft HTML Help Workshop. Чтобы создать справочную систему (или, в простейшем случае, chm-файл) надо создать файлы проекта, контента и индекса, а затем выполнить компиляцию. Файл проекта Работа над новым chm-файлом начинается с создания файла проекта. Сначала в меню File надо выбрать команду New Project, затем в окне New Project задать имя файла проекта (файл проекта надо создать в том каталоге, в котором находятся HTML-файлы страниц справочной информации). Следует обратить внимание, что по умолчанию имя chm-файла, который будет создан в процессе компиляции, совпадает с именем файла проекта. В качестве примера на рис. 8.5 приведено окно HTML Help Workshop в начале работы над проектом справочной системы программы "Доход по вкладу". После того как файл проекта будет создан, надо сформировать список файлов, в которых находится справочная информация (предполагается, что все необходимые HTML-файлы находятся в одном каталоге, в том же, в котором сохранен файл проекта).

280 Глава 8. Справочная информация 275 Рис Окно HTML Help Workshop в начале работы над новым проектом Чтобы указать файлы, в которых находится справочная информация, надо: 1. Щелкнуть на кнопке Add topic files. 2. В появившемся окне Topic Files щелкнуть на кнопке Add. 3. В стандартном окне Открыть выбрать HTML-файлы, в которых находится справочная информация (нажать клавишу <Ctrl> и, удерживая ее нажатой, щелкнуть на именах нужных файлов). Рис Формирование списка файлов, в которых находится справочная информация

281 276 Часть II. Практикум программирования В результате описанных действий в окне Topic Files появится список файлов (рис. 8.6), в которых находится справочная информация, а после щелчка на кнопке OK в файл проекта (его содержимое отображается на вкладке Project) будет добавлен раздел FILES, в котором будут перечислены HTML-файлы, содержащие справочную информацию (рис. 8.7). Рис В разделе FILES перечислены файлы, в которых находится справочная информация Рис В поле Title надо ввести заголовок окна справочной информации, а в поле Default file имя файла главной страницы

282 Глава 8. Справочная информация 277 Следующее, что нужно сделать, задать главную (стартовую) страницу и заголовок окна справочной информации. Заголовок окна и имя файла главной страницы надо ввести соответственно в поля Title и Default file вкладки General окна Options (рис. 8.8), которое становится доступным в результате щелчка на кнопке Change project options (см. рис. 8.5). Оглавление Если для навигации по справочной системе предполагается использовать вкладку Оглавление, то надо создать файл контента. Чтобы это сделать, нужно щелкнуть на ярлыке вкладки Contents, подтвердить создание нового файла контента (Create a new contents file) и задать его имя (в качестве имени файла контента рекомендуется указать имя проекта). В результате выполнения описанных действий в каталоге проекта будет создан файл контента (hhc-файл) и станет доступной вкладка Contents (рис. 8.9). Оглавление справочной информации формируется путем добавления на вкладку Contents ссылок на страницы справочной информации. Рис Вкладка Contents Чтобы на вкладку Contents добавить ссылку на страницу справочной информации, надо: 1. Щелкнуть на кнопке Insert a page.

283 278 Часть II. Практикум программирования 2. В поле Entry title вкладки General открывшегося окна Table of Contents Entry ввести название раздела справочной информации и щелкнуть на кнопке Add (рис. 8.10). Рис В поле Entry title надо ввести название раздела и щелкнуть на кнопке Add Рис В списке HTML titles надо выбрать HTML-страницу

284 Глава 8. Справочная информация 279 В результате станет доступным окно Path or URL (рис. 8.11), в списке HTML titles которого будут перечислены названия HTML-документов, включенных в проект (если в HTML-файле нет тега <Title>, то вместо названия документа отображается имя файла). 3. В списке HTML titles окна Path or URL выбрать HTML-документ, который надо отобразить в окне справочной информации в результате выбора ссылки на страницу (названия раздела справочной информации). 4. Щелкнуть на кнопке OK. Указанную последовательность действий надо выполнить для каждого раздела справочной информации. В качестве примера на рис приведено окно HTML Help Workshop, вкладка Contents которого содержит оглавление справочной информации программы "Доход по вкладу". Рис Вкладка Contents содержит оглавление справочной информации Идентификаторы разделов Обычно когда пользователь, нажав клавишу <F1>, запрашивает справочную информацию, в окне справочной системы сразу отображается нужный раздел (страница). Такой режим отображения справочной информации называется контекстно-зависимым. Для того чтобы программист мог реализовать контекстнозависимый режим отображения справочной информации, необходимо каждой странице назначить идентификатор.

285 280 Часть II. Практикум программирования Инструкция назначения идентификатора в общем виде выглядит так: #define Страница Идентификатор; комментарий Например, инструкция #define npv_01 1; Чистый дисконтированный доход объявляет идентификатор для страницы npv_01.htm. Следует обратить внимание, что при объявлении идентификатора расширение файла не указывается. Инструкции объявления идентификаторов следует поместить в отдельный файл. В качестве примера в листинге 8.1 показан файл объявления идентификаторов разделов справочной информации программы "Доход по вкладу". Следует обратить внимание, что файл с расширением h это обычный текстовый файл. Листинг 8.1. Объявление идентификаторов разделов справочной информации (profit.h) #define npv_01 1; Чистый дисконтированный доход #define npv_02 2; Финансовые результаты #define npv_03 3; Финансовые затраты #define npv_04 4; Ставка дисконтирования #define npv_05 5; О программе Рис Раздел MAP содержит ссылку на файл идентификаторов разделов справочной информации

286 Глава 8. Справочная информация 281 После того как h-файл будет подготовлен (его следует сохранить в каталоге проекта справочной системы), надо: 1. Выбрать вкладку Project и сделать щелчок на кнопке HTML Help API Information. 2. Выбрать вкладку Map и щелкнуть на кнопке Header file. 3. В появившемся окне Include file ввести имя h-файла и щелкнуть на кнопке OK. В результате описанных действий в файл проекта будет добавлен раздел MAP (рис. 8.13), в котором будет ссылка (директива #include) на файл объявления идентификаторов разделов справочной информации. Компиляция После того как будут определены файлы, в которых находится справочная информация, и подготовлена информация для формирования вкладки Содержание (создан файл контента), можно выполнить компиляцию. Исходной информацией для CHM-компилятора HTML Help Workshop являются файл проекта (hpp-файл), файл контента (hhc-файл) и файлы справочной информации (HTML-файлы). Результатом компиляции является файл справочной информации (chm-файл). Чтобы выполнить компиляцию, надо в меню File выбрать команду Compile и затем в появившемся окне Create a compiled file сделать щелчок на кнопке Compile (рис. 8.14). Рис Чтобы создать chm-файл, надо щелкнуть на кнопке Compile В результате в каталоге проекта будет создан chm-файл справочной информации. Чтобы убедиться, что файл справочной информации создан правильно, надо в меню View выбрать команду Compiled Help File. На экране должно появиться окно справочной информации.

287 282 Часть II. Практикум программирования Отображение справочной информации Отображение справочной информации формата HTML Help осуществляет утилита hh.exe, которая по отношению к приложению является внешней программой. Запуск внешних программ обеспечивает функция WinExec. В качестве параметров функции WinExec надо указать команду запуска программы и режим отображения окна, в котором программа должна работать. Команда запуска записывается точно так же, как если бы она набиралась в окне Выполнить или в окне командной строки операционной системы. Например, инструкция запуска утилиты hh.exe с целью отображения справочной информации, которая находится в файле npv.chm, выглядит так: WinExec('hh.exe npv.chm', SW_RESTORE); В результате выполнения приведенной команды на экране появится окно справочной информации, в котором будет отображено содержимое первого раздела. Если необходимо отобразить конкретный раздел справочной информации, то в команде запуска утилиты hh.exe надо указать параметр -mapid и идентификатор нужного раздела. Например, в результате выполнения инструкции WinExec('hh.exe mapid 3 npv.chm', SW_RESTORE); на экране появится окно справочной информации, в котором будет отображена информация раздела Финансовые затраты (см. листинг 8.1). Отображение справочной информации демонстрирует программа "Чистый дисконтированный доход" (ее форма приведена на рис. 8.15). Во время работы с программой пользователь может получить справку, сделав щелчок на кнопке Справка. Процедура обработки события Click на кнопке Справка, а также процедура обработки события Close формы приведены в листинге Рис Справочная информация отображается в результате щелчка на кнопке Справка

288 Глава 8. Справочная информация 283 Листинг 8.2. Отображение справочной информации unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Keyboard; type TForm1 = class(tform) Label1: TLabel; Label2: TLabel; Label3: TLabel; Edit1: TEdit; Edit2: TEdit; Edit3: TEdit; Button1: TButton; Button2: TButton; Label4: TLabel; procedure Button2Click(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure Button1Click(Sender: TObject); private < Private declarations >public < Public declarations >var Form1: TForm1; implementation // щелчок на кнопке "Расчет" procedure TForm1.Button1Click(Sender: TObject); var p: real; // поступления от продаж

289 284 Часть II. Практикум программирования r: real; // расходы d: real; // ставка дисконтирования npv: real; // чистый дисконтированный доход try p := StrToFloat(Edit1.Text); r := StrToFloat(Edit2.Text); d := StrToFloat(Edit3.Text); npv := (p - r) / (1.0 + d); Label4.Caption := 'Чистый дисконтированный доход (NPV): ' + FloatToStrF(npv,ffCurrency,6,2); except on e:exception do MessageDlg('Ошибка исходных данных: ' + e.message, mterror,[mbok],0); // щелчок на кнопке "Справка" procedure TForm1.Button2Click(Sender: TObject); var h : HWND; // идентификатор (дескриптор) окна h := FindWindow('HH Parent','Чистый дисконтированный доход'); if h = 0 then WinExec('hh.exe npv.chm',sw_restore) else ShowWindow(h,SW_RESTORE); Windows.SetForegroundWindow(h); // щелчок на кнопке "Закрыть окно" procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

290 Глава 8. Справочная информация 285 var end. h : HWND; h := FindWindow('HH Parent','Чистый дисконтированный доход'); if h <> 0 then // открыто окно справочной информации SendMessage (h,wm_close,0,0); Необходимо обратить внимание на следующее. Утилита hh.exe является внешней программой. Поэтому, если не предпринимать никаких усилий, и в процедуре обработки события Click на кнопке Справка указать только инструкцию запуска утилиты hh.exe, то каждый щелчок на кнопке Справка будет запускать новый экземпляр утилиты hh.exe (открывать новое окно справочной информации). Чтобы избежать ситуации, когда одновременно будут открыты несколько окон, в которых отображается одна и та же справочная информация, в процедуру обработки события Click добавлен код, запускающий утилиту hh.exe только в том случае, если она еще не запущена. Проверяет, запущена ли утилита hh.exe или нет, функция FindWindow (если программа запущена, то существует окно, в котором программа работает). В качестве параметров функции FindWindow передается тип окна (в результате запуска утилиты hh.exe создается окно класса HH Parent) и текст, который должен быть в заголовке окна (в заголовке окна справочной информации программы "Доход по вкладу" отображается текст Доход по вкладу). Если окно справочной информации не найдено (в этом случае значение функции FindWindow равно нулю), то функция WinExec запускает утилиту hh.exe. Если программа отображения справочной информации уже запущена (окно программы существует), то функция ShowWindow делает окно активным, а функция SetForegroundWindow перемещает его на передний план. Процедура обработки события Close формы также использует функцию Find- Window, чтобы проверить, открыто окно справочной информации или нет. Если окно открыто, то функция SendMessage направляет ему сообщение (команду) WM_CLOSE (Закрыть). В результате этого окно справочной информации закрывается. После чего программа завершает работу.

291 ГЛАВА 9 Создание установочного диска Перенести программу, созданную в Delphi, на другой компьютер можно, например, с помощью "флэшки", просто скопировав файлы программы на диск компьютера пользователя. При этом следует понимать, что программа, которая работает на компьютере разработчика, у пользователя может и не запуститься, например, из-за отсутствия нужных ей библиотек или файлов данных. Задачу установки и настройки приложения можно возложить на пользователя, передав ему все файлы, образующие программу, и инструкцию по установке. Однако лучше следовать принятой практике, когда установку прикладной программы на компьютер пользователя выполняет специальная программа-установщик. Создать установщик можно точно так же, как и любое другое приложение. Однако лучше воспользоваться одной из специальных утилит, обеспечивающих решение этой задачи, например InstallAware. Утилита InstallAware Утилита InstallAware является одним из современных инструментов создания инсталляционных программ. Она позволяет создать программу установки (самораспаковывающийся архив, образ установочного диска), обеспечивающую не только копирование файлов на компьютер пользователя, но и настройку системы. Программа установки, созданная с помощью InstallAware, имеет хорошо знакомый большинству пользователей и интуитивно понятный интерфейс. В комплект поставки RAD Studio XE утилита InstallAware включена в версии Express. На компьютер программиста она устанавливается путем выбора соответствующей команды в стартовом окне установщика RAD Studio. Новый проект Процесс создания установщика в InstallAware рассмотрим на примере создадим программу установки игры "Сапер".

292 Глава 9. Создание установочного диска 287 Перед тем как приступить к непосредственной работе в InstallAware, надо составить список файлов, которые необходимо перенести на компьютер пользователя (табл. 9.1). Следует обратить внимание, что в данном случае предполагается, что весь код программы находится в exe-файле, т. е. программа скомпилирована в режиме "без использования пакетов". Таблица 9.1. Файлы, которые нужно установить на компьютер пользователя Файл Описание Куда устанавливать saper.exe Программа Program Files\Saper saper.chm Файл справочной информации Program Files\Saper readme.txt Краткая информация о программе Program Files\Saper Запускается InstallAware обычным образом выбором в меню Пуск команды InstallAware Install InstallAware Express. Рис Начало работы над новым проектом Чтобы начать работу по созданию программы установки, нужно в меню File выбрать команду New Win32 Setup, в появившемся окне New Project (рис. 9.1)

293 288 Часть II. Практикум программирования ввести название проекта (поле Project Name) и указать каталог (поле Project Folder), в который следует поместить файлы проекта создания программы установки (в рассматриваемом примере каталог проекта будет создан в каталоге приложения, для которого создается программа установки). Следует обратить внимание на флажок Create project in subfolder. Если он установлен (а по умолчанию это так), то в результате щелчка на кнопке OK в каталоге, имя которого указано в поле Project Folder, будет создана папка проекта, а в ней файл проекта создания программы установки (с расширением mpr). Окно утилиты InstallAware в начале работы над новым проектом приведено на рис В левой части окна в виде списка представлены этапы и шаги процесса создания программы установки, в центральной отображаются действия (шаги), относящиеся к выбранному в данный момент этапу. Рис Окно утилиты InstallAware в начале работы над проектом Чтобы создать программу установки, надо последовательно заполнить формы, которые отображаются в результате выбора названия шага (Application Information, Setup Architecture, User Interface, Advanced Options), и активизировать процесс создания программы установки.

294 Глава 9. Создание установочного диска 289 Общая информация В формы, которые появляются в результате выбора соответствующих команд в группе Application Information, надо ввести общую информацию об устанавливаемой программе. Программа и ее разработчик В форме Project Properties (рис. 9.3) в поля Product Name и Product Version надо ввести соответственно название устанавливаемой программы и номер версии, в поле Manufacturer имя разработчика программы. В поле Target Folder надо ввести имя папки компьютера пользователя, в которую надо установить приложение. Так, по умолчанию в этом поле находится шаблон $PROGRAMFILES$\$TITLE$, то приложение будет установлено в папку с именем, совпадающим с именем приложения (шаблон $TITLE$ заменяется текстом, находящимся в поле Product Name), которая будет создана в папке Program Files (шаблон $PROGRAMFILES$). Рис Форма Project Properties

295 290 Часть II. Практикум программирования В поля следующей формы (рис. 9.4), которая становится доступной в результате выбора команды Summary Information, надо ввести информацию, которая будет отображаться в окне программы установки. Рис Форма Summary Information В поля формы Add-Remove Information надо ввести информацию, которая должна отображаться в окне Поддержка (это окно появляется в результате выбора в окне Установка и удаление программ операционной системы ссылки Сведения о поддержке). В форме надо указать производителя (поставщика) программного продукта, контакт, обеспечивающий поддержку и сопровождение, ссылки на ресурсы поддержки и обновления. Если производитель программы не обеспечивает сопровождение и поддержку программы через Интернет, то соответствующие поля следует оставить незаполненными (удалить значения, указанные по умолчанию). Требования к системе Если устанавливаемое приложение предъявляет особые требования к аппаратуре (объем оперативной памяти, разрешение экрана, количество отображаемых цветов) и к программному обеспечению компьютера (версия операционной системы, Internet Explorer, IIS), то их надо указать путем выбора соответствующих значений в форме Application Requirements. Компоненты В форме Application Runtimes надо указать компоненты (приложения и динамические библиотеки), которые необходимы для работы устанавливаемой программы. Например, приложению работы с базой данных требуется соответствующий сервер баз данных.

296 Глава 9. Создание установочного диска 291 Архитектура Команды группы Setup Architecture позволяют определить архитектуру устанавливаемого приложения: описать его элементы, составить список файлов, которые надо установить на компьютер пользователя, описать ярлыки, которые надо создать. Возможности Даже простое приложение можно представить как совокупность нескольких компонентов. Например, программа "Сапер" это непосредственно программа и справочная информация. Возможности пользователя при работе с программой определяются составом установленных компонентов. Например, если на компьютер пользователя установлены файлы справочной системы, то у пользователя есть возможность получить доступ к справочной информации, а если файлы справочной информации не установлены, то такой возможности нет. Форма Features позволяет разделить функциональные возможности программы (компоненты, которые могут устанавливаться по отдельности). Разделение компонентов программы на несколько групп (возможностей) по функциональному признаку позволяет организовать многовариантную, в том числе и определяемую пользователем, установку программы. Рис Форма Features

297 292 Часть II. Практикум программирования По умолчанию определена только одна группа (один элемент в списке All Features), что предполагает один вариант установки. Чтобы пользователь мог выбрать устанавливаемые компоненты, надо создать несколько групп и для каждой группы указать компоненты (файлы), реализующие соответствующую функциональность. Чтобы создать группу, надо открыть форму Features (рис. 9.5), сделать щелчок на кнопке New, ввести название группы и ее описание. Файлы Следующее, что надо сделать, указать файлы, которые необходимо установить на компьютер пользователя. Делается это в форме Files. Сначала в левом верхнем списке надо выбрать папку, в которой находятся файлы приложения (в рассматриваемом примере C:\Users\Никита\Documents\RAD Studio\Projects\Saper). Затем в левом нижнем списке надо указать каталог компьютера пользователя, в который надо поместить файлы приложения. Следует обратить внимание на то, что в этом списке указаны не имена, а псевдонимы каталогов. Рис Форма Files Например, псевдоним $PROGRAMFILES$ обозначает каталог Program Files, а псевдоним $TARGETDIR$ каталог устанавливаемого приложения (каталог, за-

298 Глава 9. Создание установочного диска 293 данный в форме Project Properties, см. рис. 9.3). После выбора каталога-приемника следует указать файлы, которые надо установить в выбранный каталог. Выбор файлов выполняется в правом верхнем списке. После выбора файла (или группы файлов) надо нажать кнопку Add File(s). Вид формы Files после выбора файлов, которые должны быть перенесены на компьютер пользователя в процессе установки программы "Сапер", приведен на рис Если предполагается, что во время установки пользователь сможет выбрать нужные ему функциональные возможности устанавливаемой программы, то необходимо установить переключатель Filter Files by Feature, в ставшем доступном списке выбрать возможность (см. предыдущий раздел) и указать файлы, определяющие эту возможность. Ярлыки Запустить программу, установленную на компьютере, можно выбором команды в меню программ, которое становится доступным в результате щелчка на кнопке Пуск, или щелчком на находящемся на рабочем столе значке (ярлыке) программы. Указать, куда следует поместить ярлык, обеспечивающий запуск программы или выполнение другого действия, например отображение справочной информации, можно в форме Shortcuts (рис. 9.7). Рис Форма Shortcuts

299 294 Часть II. Практикум программирования Чтобы создать ярлык, обеспечивающий запуск программы, надо в форме Shortcuts сделать щелчок на кнопке New и в поля появившегося окна New Shortcut (рис. 9.8) ввести информацию, необходимую для создания ярлыка. Рис Окно New Shortcut В поле Shortcuts Target надо ввести имя файла, который будет открыт в результате щелчка на ярлыке. В группе Shortcuts Placement надо указать расположение ярлыка (In the application Start Menu folder в меню программы; On the Desktop на рабочем столе; On the Quick Launch toolbar на панели быстрого запуска). В поле Text надо ввести название ярлыка, а в поле Description текст подсказки (она будет отображаться при позиционировании указателя мыши на ярлыке). Рис Выбор файла

300 Глава 9. Создание установочного диска 295 Чтобы в поле ввести имя файла, нажмите кнопку Browse и в появившемся окне (рис. 9.9) выберите файл. Обратите внимание, что в окне отображаются не каталоги компьютера программиста, а каталоги компьютера пользователя. Интерфейс В процессе установки программы пользователю, как правило, предоставляется возможность выбрать, куда надо установить программу, а также устанавливаемые компоненты. Взаимодействие программы установки с пользователем обеспечивают соответствующие диалоги. Команды группы User Interface позволяют задать диалоги, которые будут отображаться в процессе подготовки к установке, во время и по окончании установки приложения. Диалоги В форме Dialogs (рис. 9.10) перечислены диалоги, которые обеспечивают взаимодействие программы установки с пользователем. Назначение часто используемых диалогов приведено в табл Рис Вкладка Dialogs

301 296 Часть II. Практикум программирования Таблица 9.2. Диалоги процесса установки Диалог welcome licensecheck readme registration setuptype componentstree destination startmenu startinstallation progress finish Назначение Вывод информационного сообщения Отображение лицензионного соглашения. Позволяет прервать процесс установки программы в случае несогласия пользователя с условиями лицензионного соглашения Отображение информации об устанавливаемой программе Запрашивает информацию о пользователе (имя, название организации) Предоставляет пользователю возможность выбрать вариант установки (Typical обычная установка, Minimal минимальная установка, Custom выборочная установка) Предоставляет пользователю возможность выбора устанавливаемых компонентов при выборочной (Custom) установке Предоставляет пользователю возможность изменения заданного по умолчанию каталога, в который будет установлено приложение Позволяет пользователю задать имя меню, которое будет создано в списке Программы и в которое будет помещен ярлык, обеспечивающий, например, запуск программы Вывод информации, введенной пользователем на предыдущих шагах, с целью ее проверки перед началом непосредственной установки Отображает процесс установки (показывает процент выполненной работы по установке программы) Информирует о завершении установки. Позволяет запустить установленную программу и (или) активизировать отображение ReadMe-файла Чтобы диалог появился на экране во время работы инсталляционной программы, необходимо отметить соответствующий флажок. В нашем случае (мы создаем программу установки игры "Сапер") надо выбрать диалоги welcome, destination, startinstallation, progress и finish. Следует обратить внимание на кнопку Change Theme, которая позволяет выбрать стиль оформления диалоговых окон. Информация о программе и лицензионное соглашение Форма EULA and ReadMe (рис. 9.11) позволяет задать текст лицензионного соглашения (EULA) и информацию о программе (ReadMe), которые будут отобра-

302 Глава 9. Создание установочного диска 297 жаться в соответствующих окнах. Лицензионное соглашение и информацию о программе можно набрать непосредственно в области редактирования вкладки или загрузить из файла. В первом случае, после того как текст будет набран, надо сделать щелчок на кнопке Save, во втором, для выбора файла, на кнопке Load. Рис Вкладка EULA and ReadMe Образ установочного диска По умолчанию InstallAware создает образ установочного диска совокупность файлов и каталогов. Для экономии места на носителе или, например, для обеспечения удобства передачи (распространения) программы через сеть (локальную или Интернет) вместо обычного образа установочного диска можно создать образ установочного диска в виде самораспаковывающегося архива (Compressed Single Self- Installing EXE). Тип образа установочного диска (несжатый или самораспаковывающийся архив) можно задать в форме Build Settings (рис. 9.12). Чтобы активизировать процесс образа установочного диска, надо в меню Project выбрать команду Build или сделать щелчок на кнопке Build current project (рис. 9.13).

303 298 Часть II. Практикум программирования Рис Вкладка Build Settings Рис Кнопка Build current project После того как процесс создания образа установочного диска будет завершен, нужно проверить, как работает программа установки в меню Run выбрать команду Run (начнется процесс установки программы и на экране появится окно Welcome). По завершении процесса установки программы следует проверить, создан ли ярлык (в папке меню Пуск или на рабочем столе), обеспечивающий запуск приложения, щелчком на ярлыке запустить программу и убедиться, что она работает. Далее следует проверить, что установленную программу можно удалить с компьютера. Чтобы это сделать, надо через Панель управления открыть окно Программы и компоненты, найти установленную программу в списке программ и сделать щелчок на кнопке Удалить. Если программа установки на компьютере разработчика работает правильно (обеспечивает установку и удаление приложения), то необходимо проверить, как она работает на другом компьютере. Для этого надо создать установочный диск записать содержимое каталога Проект\Release\Uncompressed или Проект\Release\Single, если был выбран вариант создания упакованного образа установочного диска, на CD, DVD или "флэшку". Затем следует установить программу на компьютер пользователя с диска. Если установка будет выполнена успешно, то работу по созданию программы установки можно считать законченной.

304 ГЛАВА 10 Примеры программ Экзаменатор Тестирование широко применяется для оценки уровня знаний в учебных заведениях, при приеме на работу, для оценки квалификации персонала и т. п. Тест это последовательность вопросов, на которые испытуемый должен ответить, выбрав правильный ответ из нескольких предложенных вариантов. Оценка выставляется в зависимости от количества правильных ответов. Рис Задача испытуемого выбрать правильный ответ Рассмотрим программу "Экзаменатор" (рис. 10.1), которая позволяет автоматизировать процесс тестирования.

305 300 Часть II. Практикум программирования Требования к программе В результате анализа используемых на практике методик тестирования были сформулированы следующие требования к программе: программа должна быть универсальной, у пользователя должна быть возможность самостоятельной, без участия программиста, подготовки теста; программа должна работать с тестом произвольной длины, т. е. не должно быть ограничения на количество вопросов в тесте; ответ на вопрос должен осуществляться путем выбора одного ответа из нескольких (не более четырех) вариантов; в программе должна быть заблокирована возможность возврата к предыдущему вопросу. Если вопрос предложен, то на него должен быть дан ответ; результат тестирования должен быть отнесен к одному из четырех уровней, например: "отлично", "хорошо", "удовлетворительно" или "плохо"; вопрос может сопровождаться иллюстрацией. Файл теста Универсальность программы тестирования обеспечивается тем, что вопросы находятся в текстовом файле, имя которого указывается в команде запуска программы. Помимо вопросов (здесь и далее: вопрос это вопрос и несколько вариантов ответа) в файле теста находится информация, необходимая для выставления оценки. Файл теста состоит из: заголовка; раздела оценок; раздела вопросов. Заголовок состоит из двух абзацев: первый абзац название теста (отображается в заголовке окна программы тестирования), второй вводная информация, например о назначении теста и критерии выставления оценки. Вот пример заголовка: История Санкт-Петербурга Сейчас Вам будут предложены вопросы о памятниках и архитектурных сооружениях Санкт-Петербурга. Вы должны из нескольких предложенных вариантов ответа выбрать правильный. За заголовком следует раздел оценок, в котором указывается количество баллов (количество правильных ответов), необходимое для достижения уровня, и сообщение, информирующее испытуемого о достижении уровня. В простейшем случае сообщение это оценка. Для каждого уровня указывается количество правильных ответов и, в следующей строке, сообщение. Вот пример раздела оценок: 100 Отлично 85 Хорошо

306 Глава 10. Примеры программ Удовлетворительно 50 Плохо За разделом оценок следует раздел вопросов теста. Каждый вопрос начинается текстом вопроса. В следующей строке указывается количество альтернативных ответов, номер правильного ответа и признак наличия иллюстрации. Если вопрос сопровождается иллюстрацией, то значение признака должно быть равно единице, если нет нулю. В следующей строке, если значение признака наличия иллюстрации равно единице, указывается имя файла иллюстрации. Далее следуют альтернативные ответы, каждый из которых должен представлять собой абзац текста. Вот пример вопроса: Архитектор Зимнего дворца herm.jpg Бартоломео Растрелли Карл Росси Огюст Монферран В приведенном примере к вопросу даны три варианта ответа, правильным является первый ответ (архитектор Зимнего дворца Бартоломео Растрелли). К вопросу есть иллюстрация (третье число во второй строке единица), которая находится в файле herm.jpg. В листинге 10.1 в качестве примера приведен текст файла вопросов для проверки знания истории памятников и архитектурных сооружений Санкт-Петербурга. Листинг Файл теста (spb.txt) История Санкт-Петербурга Сейчас Вам будут предложены вопросы о знаменитых памятниках и архитектурных сооружениях Санкт-Петербурга. Вы должны из нескольких предложенных вариантов ответа выбрать правильный. 7 Вы прекрасно знаете историю Санкт-Петербурга! 6 Вы много знаете о Санкт-Петербурге, но на некоторые вопросы ответили неверно. 5 Вы недостаточно хорошо знаете историю Санкт-Петербурга. 4 Вы, вероятно, только начали знакомиться с историей Санкт-Петербурга? Архитектор Исаакиевского собора: is.jpg Доменико Трезини

307 302 Часть II. Практикум программирования Огюст Монферран Карл Росси Александровская колонна воздвигнута в 1834 году по проекту Огюста Монферрана как памятник, посвященный: деяниям императора Александра I подвигу народа в Отечественной войне 1812 года Архитектор Зимнего дворца herm.jpg Бартоломео Растрелли Карл Росси Огюст Монферран Михайловский (Инженерный) замок жемчужина архитектуры Петербурга, построен по проекту: Винченцо Бренна Старова Ивана Егоровича Баженова Василия Ивановича Остров, на котором находится Ботанический сад, основанный императором Петром I, называется: bot.jpg Заячий Медицинский Аптекарский Невский проспект получил свое название по имени реки, на которой стоит Санкт-Петербург по имени близко расположенного монастыря, Александро-Невской лавры в память о знаменитом полководце Александре Невском Скульптура памятника Петру I "Медный всадник" выполнена Фальконе Клодтом Файл теста можно подготовить в текстовом редакторе, который сохраняет "чистый" (без символов форматирования) текст, например в Блокноте. В момент записи текста на диск вместо стандартного расширения txt следует указать какоелибо другое, например exm (от англ. Examiner экзаменатор). Такое решение позволит настроить операционную систему так, что тест будет запускаться автоматически, в результате двойного щелчка на имени файла теста.

308 Глава 10. Примеры программ 303 Форма приложения Форма программы тестирования приведена на рис Рис Форма программы "Экзаменатор" Вопрос отображается в поле Label5, варианты ответа в полях Label1 Label4. Переключатели RadioButton1 RadioButton4 обеспечивают выбор ответа, а RadioButton5 (во время работы программы он не отображается) сброс переключателей выбора ответа. Кнопка Button1 (она доступна только в том случае, если ответ выбран) обеспечивает переход к следующему вопросу. Следует обратить внимание, что текст на кнопке Button1 во время работы программы меняется. В начале и в конце работы программы на кнопке находится текст Ok, а в процессе тестирования Дальше. Если вопрос сопровождается иллюстрацией, то она отображается в поле компонента Image1. В табл приведены значения свойств формы, в табл значения свойств компонентов. Таблица Значения свойств формы Свойство BorderIcons.biMinimize BorderIcons.biMaximize BorderStyle Значение False False bssingle

309 304 Часть II. Практикум программирования Таблица Значения свойств компонентов Компонент Свойство Значение Label1 WordWrap True AutoSize True Label2 WordWrap True AutoSize True Label3 WordWrap True AutoSize True Label4 WordWrap True AutoSize True Label5 WordWrap True AutoSize True Image1 Proportional True Panel1 Align albottom BevelOuter bvnone Height 41 RadioButton5 Visible False Следует обратить внимание, что значения свойств, определяющих положение компонентов, предназначенных для отображения вопроса, альтернативных ответов и иллюстрации, вычисляются во время работы программы, после того как будет прочитан очередной вопрос. Положение компонента Label1 отсчитывается от нижней границы компонента Label5 или, если вопрос сопровождается иллюстрацией, от нижней границы компонента Image1. Положение компонента Label2 отсчитывается от нижней границы компонента Label1. Аналогичным образом вычисляется положение компонентов Label3 и Label4. Отображение иллюстрации Очевидно, что размер области формы, которая может быть использована для отображения иллюстрации, зависит от длины вопроса, а также от количества и длины альтернативных ответов. Чем длиннее вопрос и ответы, чем больше места в поле формы они занимают, тем меньше места остается для отображения иллюстрации. В рассматриваемой программе размер области отображения иллюстрации (компонента Image1) вычисляется после прочтения очередного вопроса, после "загрузки" в компоненты Label вопроса и альтернативных ответов, когда становится известным их размер.

310 Глава 10. Примеры программ 305 Доступ к файлу теста Для того чтобы программа "Экзаменатор" была действительно универсальной, у пользователя должна быть возможность задать имя файла теста. Обеспечить возможность настройки программы на работу с конкретным файлом можно несколькими способами. Например, программа может считать имя файла теста из файла конфигурации (ini-файла) или получить его из командной строки. Командная строка это строка, которую пользователь должен набрать в окне Запуск программы, для того чтобы запустить программу. В простейшем случае командная строка это имя exe-файла. В командной строке после имени exeфайла можно указать дополнительную информацию, которую надо передать программе, например имя файла, с которым должна работать программа. Программа может получить параметр, указанный в командной строке, как значение функции ParamStr(n), где n порядковый номер параметра. Количество параметров командной строки находится в глобальной переменной ParamCount. Следует обратить внимание, что значение ParamStr(0) это полное имя exe-файла программы. Далее приведен фрагмент кода, который демонстрирует использование функции ParamStr. if ParamCount = 0 then ShowMessage('Ошибка! Не задан файл вопросов теста.'); exit; fn := ParamStr(1); // имя файла параметр команды запуска программы Рис Параметры командной строки надо ввести в поле Parameters

311 306 Часть II. Практикум программирования Как было сказано, если программе нужны параметры, то они указываются в команде ее запуска после имени exe-файла. При запуске программы из среды разработки, если программе необходимы параметры, их надо указать в поле Parameters окна Project Options (рис. 10.3), которое становится доступным в результате выбора в меню Run команды Parameters. Текст программы После настройки формы и компонентов можно приступить к набору текста программы (листинг 10.2). Сначала в раздел private объявления формы надо поместить объявления вспомогательных процедур и функций, а в раздел var секции implementation объявления переменных. После этого следует набрать процедуры и функции, а затем создать процедуры обработки событий. Следует обратить внимание, что событие Click на компонентах RadioButton1 RadioButton4 обрабатывает одна процедура. Листинг Экзаменатор < Универсальная программа тестирования. (с) Культин Н.Б., Тест загружается из файла, имя которого указано в команде запуска программы. >unit ExamMainForn; interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, jpeg; // обеспечивает отображение JPEG-иллюстраций type TForm1 = class(tform) Label1: TLabel; Label2: TLabel; Label3: TLabel; Label4: TLabel; Label5: TLabel; Label6: TLabel;

312 Глава 10. Примеры программ 307 RadioButton1: TRadioButton; RadioButton2: TRadioButton; RadioButton3: TRadioButton; RadioButton4: TRadioButton; RadioButton5: TRadioButton; Image1: TImage; Button1: TButton; Panel1: TPanel; procedure FormActivate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure RadioButtonClick(Sender: TObject); private // эти объявления вставлены сюда вручную procedure Info; function VoprosToScr: boolean; procedure ShowPicture; // выводит иллюстрацию procedure ResetForm; // "очистка" формы перед выводом вопроса procedure Itog; // результат тестирования public < Public declarations >var Form1: TForm1; implementation var f:textfile; fn:string; // имя файла вопросов level:array[1..4] of integer; // сумма, соответствующая уровню mes:array[1..4] of string; // сообщение, соответствующее уровню vopr: record text: string; // вопрос src: string; // иллюстрация otv: array[1..4] of string; // варианты ответа

313 308 Часть II. Практикум программирования notv: integer; // количество вариантов ответа right: integer; // номер правильного ответа cv: integer = 0; // номер вопроса otv : integer; // номер выбранного ответа sum: integer; // информации о тесте procedure Tform1.Info; var s: string; readln(f,s); Form1.Caption := s; readln(f,s); Label5.caption:=s; // прочитать из файла информацию об оценках procedure GetLevel; var i:integer; for i:=1 to 4 do readln(f,level[i]); // оценка readln(f,mes[i]); // сообщение // отображение иллюстрации procedure TForm1.ShowPicture; var w,h: integer; // максимально возможные размеры картинки // вычислить допустимые размеры картинки w:=clientwidth - 10; h:=clientheight - Panel1.Height Label5.Top - Label5.Height - 10;

314 Глава 10. Примеры программ 309 // вопросы if Label1.Caption <> '' then h:=h-label1.height-10; if Label2.Caption <> '' then h:=h-label2.height-10; if Label3.Caption <> '' then h:=h-label3.height-10; if Label4.Caption <> '' then h:=h-label4.height-10; // если размер картинки меньше w на h, то она не масштабируется Image1.Top:=Form1.Label5.Top+Label5.Height+10; if Image1.Picture.Height > h then Image1.Height:=h else Image1.Height:= Image1.Picture.Height; if Image1.Picture.Width > w then Image1.Width:=w else Image1.Width:=Image1.Picture.Width; Image1.Visible := True; // вывести вопрос function TForm1.VoprosToScr: boolean; var i: integer; p: integer; if EOF(f) then // достигнут конец файла VoprosToScr := False; exit; readln(f, vopr.text); // прочитать вопрос if vopr.text = '*' then // признак конца теста

315 310 Часть II. Практикум программирования VoprosToScr := False; exit; cv := cv + 1; caption:='вопрос ' + IntToStr(cv); Label5.caption:= vopr.text; // вывести вопрос // прочитать информацию об ответе: количество вариантов, // номер правильного ответа и признак наличия иллюстрации readln(f,vopr.notv,vopr.right, p); // p - признак иллюстрации if p <> 0 then // есть иллюстрация readln(f,vopr.src); // имя файла иллюстрации Image1.Tag:=1; try Image1.Picture.LoadFromFile(vopr.src); except on E:EFOpenError do Image1.Tag:=0; end end else Image1.Tag := 0; // нет иллюстрации // читаем варианты ответа for i:= 1 to vopr.notv do readln(f,vopr.otv[i]); for i:= 1 to vopr.notv do case i of 1: Label1.caption:= vopr.otv[i]; 2: Label2.caption:= vopr.otv[i]; 3: Label3.caption:= vopr.otv[i]; 4: Label4.caption:= vopr.otv[i]; // здесь прочитана иллюстрация и альтернативные ответы // текст вопроса уже выведен if Image1.Tag =1 // есть иллюстрация к вопросу then ShowPicture;

316 Глава 10. Примеры программ 311 // вывод альтернативных ответов if Label1.Caption <> '' then if Image1.Tag =1 then Label1.top:=Image1.Top+Image1.Height+10 else Label1.top:=Label5.Top+Label5.Height+10; RadioButton1.top:=Label1.top; Label1.visible:=TRUE; RadioButton1.Visible:=TRUE; if Label2.Caption <> '' then Label2.top:=Label1.top+ Label1.height+5; RadioButton2.top:=Label2.top; Label2.visible:=TRUE; RadioButton2.Visible:=TRUE; if Label3.Caption <> '' then Label3.top:=Label2.top+ Label2.height+5; RadioButton3.top:=Label3.top; Label3.visible:=TRUE; RadioButton3.Visible:=TRUE; if Label4.Caption <> '' then Label4.top:=Label3.top+ Label3.height+5; RadioButton4.top:=Label4.top; Label4.visible:=TRUE; RadioButton4.Visible:=TRUE; Label6.Caption := ''; VoprosToScr := True; procedure TForm1.ResetForm; // сделать невидимыми все метки и переключатели

317 312 Часть II. Практикум программирования Label1.Visible:=FALSE; Label1.caption:=''; Label1.width:=ClientWidth-Label1.left-5; RadioButton1.Visible:=FALSE; Label2.Visible:=FALSE; Label2.caption:=''; Label2.width:=ClientWidth-Label2.left-5; RadioButton2.Visible:=FALSE; Label3.Visible:=FALSE; Label3.caption:=''; Label3.width:=ClientWidth-Label3.left-5; RadioButton3.Visible:=FALSE; Label4.Visible:=FALSE; Label4.caption:=''; Label4.width:=ClientWidth-Label4.left-5; RadioButton4.Visible:=FALSE; Label5.width:=ClientWidth-Label5.left-5; Image1.Visible:=FALSE; // определение достигнутого уровня procedure TForm1.Itog; var i:integer; buf:string; buf:=''; buf:='результаты тестирования' + #13 + 'Всего вопросов: '+ IntToStr(cv) + #13 + 'Правильных ответов: ' + IntToStr(sum); i:=1; while (sum < level[i]) and (i<4) do i:=i+1; buf:=buf+ #13+ mes[i]; Label5.Top:= 20; Label5.caption:=buf;

318 Глава 10. Примеры программ 313 procedure TForm1.FormActivate(Sender: TObject); if ParamCount = 0 then Label5.caption:= 'Не задан файл вопросов теста.'; Button1.caption:='Ok'; Button1.tag:=2; Button1.Enabled:=TRUE end else fn := ParamStr(1); assignfile(f,fn); try reset(f); except on EFOpenError do ShowMessage('Файл теста '+fn+' не найден.'); Button1.caption:='Ok'; Button1.tag:=2; Button1.Enabled:=TRUE; exit; // ключ /t активизирует режим отладки/обучения if ParamStr(2) = '/t' then Label6.Visible := True; ResetForm; Info; GetLevel; // прочитать и вывести информацию о тесте // прочитать информацию об уровнях оценок // щелчок на кнопке Button1 procedure TForm1.Button1Click(Sender: TObject); case Button1.tag of 0: Button1.caption:='Дальше';

319 314 Часть II. Практикум программирования Button1.tag:=1; RadioButton5.Checked:=TRUE; // вывод первого вопроса Button1.Enabled:=False; ResetForm; VoprosToScr; 1: // вывод остальных вопросов if otv = vopr.right then sum := sum + 1; RadioButton5.Checked:=TRUE; Button1.Enabled:=False; ResetForm; if not VoprosToScr then closefile(f); Button1.caption:='Ok'; Form1.caption:='Результат'; Button1.tag:=2; Button1.Enabled:=TRUE; Label6.Visible := False; Itog; // вывести результат 2: // завершение работы Form1.Close; // процедура обработки события Click // для компонентов RadioButton1-RadioButton4 procedure TForm1.RadioButtonClick(Sender: TObject); if sender = RadioButton1 then otv:=1 else if sender = RadioButton2 then otv:=2 else if sender = RadioButton3 then otv:=3 else otv:=4;

320 Глава 10. Примеры программ 315 Button1.enabled:=TRUE; Label6.Caption := 'Выбран ответ: ' + IntToStr(otv) + ' Правильный ответ: ' + IntToStr(vopr.right); end. Работает программа "Экзаменатор" так. После запуска программы процедура обработки события FormActivate проверяет, указан ли при запуске программы параметр "имя файла теста". Если параметр не указан (в этом случае значение функции ParamCount равно нулю), в поле Label5 выводится сообщение об ошибке, и после щелчка на кнопке Ok программа завершает работу. Если параметр указан, то имя файла теста (значение функции ParamStr(1)) записывается в переменную fn и делается попытка открыть файл. Если файл открыт, вызывается процедура Info, которая считывает из файла теста информацию о тесте и выводит ее в поле метки Label5. Затем вызывается процедура GetLevel, которая считывает из файла теста информацию об уровнях оценки и записывает ее в массивы level и mes. Следует обратить внимание, что в программе реализован режим проверки файла теста: если в командной строке указан второй параметр и этот параметр строка /t, то свойству Visible компонента Label6 присваивается значение True. В результате во время работы программы в поле компонента Label6 после выбора варианта ответа в нижней части окна отображается номер правильного ответа. После того как испытуемый прочтет информацию о тесте и сделает щелчок на кнопке Ok, начинается процесс тестирования. Следует обратить внимание, что кнопка Button1 используется для завершения работы программы (если не указан файл теста), активизации процесса тестирования, перехода к следующему вопросу (после выбора варианта ответа) и завершения работы программы. Какое из перечисленных действий будет выполнено в результате щелчка на кнопке Button1, определяет значение свойства Tag этой кнопки (см. процедуру обработки события Click). В начале работы программы значение свойства Tag кнопки Button1 равно нулю. Поэтому в результате первого щелчка на кнопке выполняется та часть программы, которая обеспечивает отображение первого вопроса и записывает в свойство Tag единицу. В процессе тестирования значение свойства Tag равно единице. Поэтому процедура обработки события Click увеличивает на единицу счетчик правильных ответов (если выбран правильный ответ) и вызывает функцию VoprosToScr, которая считывает из файла очередной вопрос и выводит его на форму. Если текущий вопрос последний (в этом случае значение функции равно False), то вызывается процедура Itog, которая выводит результат тестирования, и в свойство Tag кнопки Button1 записывается двойка. В результате, после следующего щелчка на кнопке Button1, программа завершает работу. Отображение вопроса выполняет функция VoprosToScr. Сначала она делает попытку прочитать из файла очередной вопрос. Если прочитанная строка "звез-

321 316 Часть II. Практикум программирования дочка", то это значит, что достигнут конец теста (как показал опыт, необходимо явно указать признак конца теста). Если попытка прочитать была успешна, из файла теста считывается информация о количестве вариантов ответа, номер правильного ответа и признак наличия иллюстрации. Далее считывается имя файла иллюстрации (если признак наличия иллюстрации равен единице) и варианты ответа. Следует обратить внимание, что хотя вопросы считываются непосредственно в свойства Caption компонентов Label, на форме они сразу после прочтения не отображаются (значение свойства Visible компонентов Label1 перед чтением очередного вопроса устанавливается равным False). После этого, если к вопросу есть иллюстрация, вызывается процедура ShowPicture, которая выводит иллюстрации. Затем выводятся варианты ответа. Необходимо обратить внимание, что положение области отображения первого ответа (компонента Label1) отсчитывается от нижней границы области отображения иллюстрации (компонента Image1) или от нижней границы области отображения вопроса (компонента Label5). Процедура ShowPicture выводит иллюстрацию, но сначала на основе информации о размере компонентов Label1 Label5 (значение свойства AutoSize равно True, поэтому размер компонента определяется текстом, который загружен в свойство Caption) она вычисляет размер области, которую можно использовать для отображения иллюстрации. Процедура ResetForm путем выбора невидимого переключателя RadioButton5 сбрасывает переключатели выбора ответа и делает недоступной кнопку Дальше (Button1). Обработку события Click на переключателях RadioButton1 RadioButton4 выполняет одна, общая для всех компонентов, процедура TForm1.RadioButtonClick. Параметр Sender этой процедуры позволяет определить, на каком объекте произошло событие. Процедура фиксирует в переменной otv номер переключателя и соответственно номер выбранного ответа. Также она делает доступной кнопку перехода к следующему вопросу. Процедура Itog, сравнивая набранную сумму баллов sum со значением элементов массива level, определяет, какого уровня достиг испытуемый, и выводит соответствующее сообщение с присвоением значения свойству Label5.Caption. Запуск программы Программа "Экзаменатор" получает имя файла теста из командной строки. Поэтому чтобы "запустить тест", пользователь должен в окне Запуск программы набрать имя программы тестирования и указать файл теста. Это не совсем удобно. Чтобы облегчить жизнь пользователю, можно настроить операционную систему так, что программа тестирования будет запускаться в результате щелчка на значке файла теста. Настройка выполняется следующим образом. Сначала надо раскрыть папку, в которой находится файл теста (exm-файл), и сделать двойной щелчок на значке файла. Так как расширение exm не является стандартным, то система не знает, какую программу надо запустить, чтобы открыть exm-файл. Поэтому она

322 Глава 10. Примеры программ 317 предложит указать программу, с помощью которой надо открыть файл на экране появится окно Выбор программы. В этом окне нужно сделать щелчок на кнопке Обзор, раскрыть папку, в которой находится программа "Экзаменатор", и выбрать exe-файл. После этого надо установить флажок Использовать выбранную программу для всех файлов такого типа. Вид окна Выбор программы после выполнения всех перечисленных действий приведен на рис Рис Теперь файлы с расширением exm будет открывать "Экзаменатор" В результате описанных действий в реестр операционной системы будет внесена информация о том, что файлы с расширением exm надо открывать с помощью программы "Экзаменатор" (имя файла, на котором сделан щелчок, передается программе как параметр). Следует обратить внимание, что задачу настройки операционной системы можно возложить на программу, обеспечивающую ее установку. Сапер Всем, кто работает с операционной системой Windows, хорошо знакома игра "Сапер". В этом разделе рассматривается аналогичная программа. Пример окна программы в конце игры (после того как игрок открыл клетку, в которой находится мина) приведен на рис

323 318 Часть II. Практикум программирования Рис Окно программы "Сапер" Правила и представление данных Игровое поле состоит из клеток, в каждой из которых может быть мина. Задача игрока найти все мины и пометить их флажками. Используя кнопки мыши, игрок может открыть клетку или поставить в нее флажок, указав тем самым, что в клетке находится мина. Клетка открывается щелчком левой кнопки мыши, флажок ставится щелчком правой. Если в клетке, которую открыл игрок, есть мина, то происходит взрыв (сапер ошибся, а он, как известно, ошибается только один раз), и игра заканчивается. Если в клетке мины нет, то в этой клетке появляется число, соответствующее количеству мин, находящихся в соседних клетках. Анализируя информацию о количестве мин в клетках, соседних с уже открытыми, игрок может обнаружить и пометить флажками все мины. Ограничений на количество клеток, помеченных флажками, нет. Однако для завершения игры (выигрыша) флажки должны быть установлены только в тех клетках, в которых есть мины. Ошибочно установленный флажок можно убрать, щелкнув правой кнопкой мыши в клетке, в которой он находится. В программе игровое поле представлено массивом N + 2 на M + 2, где N M размер игрового поля. Элементы массива с номерами строк от 1 до N и номерами столбцов от 1 до M соответствуют клеткам игрового поля (рис. 10.6), первые и последние столбцы и строки соответствуют границе игрового поля.

324 Глава 10. Примеры программ M M + 1 N N + 1 Рис Клетке игрового поля соответствует элемент массива В начале игры каждый элемент массива, соответствующий клеткам игрового поля, может содержать число от 0 до 9. Ноль соответствует пустой клетке, рядом с которой нет мин. Клеткам, в которых нет мин, но рядом с которыми мины есть, соответствуют числа от 1 до 8. Элементы массива, соответствующие клеткам, в которых находятся мины, имеют значение 9. Элементы массива, соответствующие границе поля, содержат 3. В качестве примера на рис изображен массив, соответствующий состоянию поля в начале игры Рис Массив в начале игры

325 320 Часть II. Практикум программирования В процессе игры состояние игрового поля меняется (игрок открывает клетки и ставит флажки), и соответственно меняются значения элементов массива. Если игрок поставил в клетку флажок, то значение соответствующего элемента массива увеличивается на 100. Например, если флажок поставлен правильно в клетку, в которой есть мина, то значение соответствующего элемента массива станет 109. Если флажок поставлен ошибочно, например в пустую клетку, элемент массива будет содержать число 100. Если игрок открыл клетку, то значение элемента массива увеличивается на 200. Такой способ кодирования позволяет сохранить информацию об исходном состоянии клетки. Форма Главная (стартовая) форма игры "Сапер" приведена на рис. 10.8, значения ее свойств в табл Рис Главная форма программы "Сапер" Таблица Значения свойств стартовой формы Свойство Caption BorderStyle BorderIcons.biMaximize Position Значение Сапер bssingle False podesktopcenter Следует обратить внимание, что размер формы не соответствует размеру игрового поля. Нужный размер формы будет установлен во время работы программы. Делает это функция обработки события FormActivate, которая на основе информации о размере игрового поля (количестве клеток по вертикали и горизонтали) и клеток, устанавливает значение свойств ClientHeight и ClientWidth, определяющих размер клиентской области главного окна программы.

326 Глава 10. Примеры программ 321 Главное окно программы содержит только один компонент MainMenu1, который представляет собой главное меню программы. Структура меню (окно конструктора меню) приведена на рис Рис Структура меню программы "Сапер" После того как будет сформирована структура меню (процесс настройки меню подробно описан в главе 3), надо создать процедуры обработки события Click для команд Новая игра, Справка и О программе. Игровое поле На разных этапах игры игровое поле выглядит по-разному. Сначала поле просто разделено на клетки. Во время игры в результате щелчка правой кнопкой мыши в клетке появляется флажок. Щелчок левой кнопкой тоже меняет вид клетки: клетка меняет цвет и в ней появляется цифра или мина (игра на этом заканчивается). Рассмотрим объекты, свойства и методы, обеспечивающие работу с графикой. Начало игры В начале игры программа должна расставить мины и для каждой клетки поля подсчитать, сколько мин находится в соседних клетках. Процедура NewGame (ее текст приведен в листинге 10.3) решает эту задачу. Листинг Процедура NewGame // новая игра расставить мины и для каждой клетки // вычислить, сколько мин находится в соседних клетках procedure NewGame();

327 322 Часть II. Практикум программирования var row,col : integer; // координаты клетки n : integer; k : integer; // количество поставленных мин // количество мин в соседних клетках // очистим элементы массива, соответствующие клеткам игрового поля for row :=1 to MR do for col :=1 to MC do Pole[row,col] := 0; // расставим мины Randomize(); // инициализация ГСЧ n := 0; // количество мин repeat row := Random(MR) + 1; col := Random(MC) + 1; if (Pole[row,col] <> 9) then Pole[row,col] := 9; n := n+1; until (n = NM); // для каждой клетки вычислим кол-во мин в соседних клетках for row := 1 to MR do for col := 1 to MC do if (Pole[row,col] <> 9) then k :=0 ; if Pole[row-1,col-1] = 9 then k := k + 1; if Pole[row-1,col] = 9 then k := k + 1; if Pole[row-1,col+1] = 9 then k := k + 1; if Pole[row,col-1] = 9 then k := k + 1; if Pole[row,col+1] = 9 then k := k + 1; if Pole[row+1,col-1] = 9 then k := k + 1; if Pole[row+1,col] = 9 then k := k + 1; if Pole[row+1,col+1] = 9 then k := k + 1; Pole[row,col] := k; status := 0; // начало игры nmin := 0; // нет обнаруженных мин nflag := 0; // нет флагов

328 Глава 10. Примеры программ 323 После того как процедура NewGame расставит мины, процедура ShowPole (ее текст приведен в листинге 10.4) выводит изображение игрового поля. Листинг Процедура ShowPole // показывает поле procedure TForm1.ShowPole(status : integer); var row,col : integer; for row := 1 to MR do for col := 1 to MC do Kletka(row, col, status); Процедура ShowPole выводит изображение поля последовательно, клетка за клеткой. Вывод изображения отдельной клетки выполняет процедура Kletka, ее текст приведен в листинге Процедура Kletka используется для вывода изображения поля в начале игры, во время игры и в ее конце. В начале игры (значение параметра status равно нулю) функция выводит только контур клетки, во время игры количество мин в соседних клетках или флажок, а в конце отображает исходное состояние клетки и действия пользователя. Информацию о фазе игры процедура Kletka получает через параметр status. Листинг Процедура Kletka // рисует клетку procedure TForm1.Kletka(row, col, status : integer); var x,y : integer; // координаты области вывода x := (col-1)* W + 1; y := (row-1)* H + 1; case status of 0: Canvas.Brush.Color := clbtnface; Canvas.Rectangle(x-1,y-1,x+W,y+H); exit; 1: if Pole[row,col] < 100

329 324 Часть II. Практикум программирования then // не открытая серая Canvas.Brush.Color := clbtnface else // открытая белая Canvas.Brush.Color := clwhite; Canvas.Rectangle(x-1,y-1,x+W,y+H); // нарисовать клетку if (Pole[row,col] >= 101) and (Pole[row,col] <= 108) then Canvas.Font.Size := 13; Canvas.Font.Color := clblue; Canvas.TextOut(x+3,y+2,IntToStr(Pole[row,col] -100)); if (Pole[row,col] >= 201) and (Pole[row,col] <= 209) then Flag(x,y); 2: // игра завершена if Pole[row,col] < 100 then Canvas.Brush.Color := clbtnface else Canvas.Brush.Color := clwhite; Canvas.Rectangle(x-1,y-1,x+W,y+H); if (Pole[row,col] >= 101) and (Pole[row,col] <= 108) then Canvas.Font.Size := 13; Canvas.Font.Color := clblue; Canvas.TextOut(x+3,y+2,IntToStr(Pole[row,col] -100)); exit; // возможно, в клетке мина, флаг или мина, помеченная флагом // в клетке мина, но она была открыта if Pole[row,col] = 9 then Mina(x, y);

330 Глава 10. Примеры программ 325 // мина + флаг if (Pole[row,col] >= 200) then Flag(x, y); if (Pole[row,col] > 200) then Mina(x, y); if Pole[row,col] = 109 then Canvas.Brush.Color := clred; Canvas.Rectangle(x-1,y-1,x+W,y+H); Mina(x,y); Игра Во время игры программа воспринимает нажатия кнопок мыши и в соответствии с правилами игры открывает клетки или ставит в клетки флажки. Основную работу выполняет процедура обработки события MouseDown (ее текст приведен в листинге 10.6). Процедура получает координаты точки формы, в которой игрок щелкнул кнопкой мыши, а также информацию о том, какая кнопка была нажата. Сначала процедура преобразует координаты точки, в которой игрок нажал кнопку мыши, в координаты клетки игрового поля. Затем делает необходимые изменения в массиве Polе и, если нажата правая кнопка, вызывает функцию Flag, которая рисует в клетке флажок. Если нажата левая кнопка в клетке, в которой нет мины, то эта клетка открывается, на экран появляется ее содержимое. Если нажата левая кнопка в клетке, в которой есть мина, то вызывается процедура ShowPole, которая показывает все мины, в том числе и те, которые игрок не успел найти. Листинг Обработка события MouseDown на поверхности игрового поля // нажатие кнопки мыши на игровом поле procedure TForm1.Form1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var row, col : integer; if status = 2 then // игра завершена exit;

331 326 Часть II. Практикум программирования if status = 0 then // первый щелчок status := 1; // преобразуем координаты мыши в индексы клетки поля row := Trunc(y/H) + 1; col := Trunc(x/W) + 1; if Button = mbleft then if Pole[row,col] = 9 then // открыта клетка, в которой есть мина Pole[row,col] := Pole[row,col] + 100; status := 2; // игра закончена ShowPole(status); end else if Pole[row,col] < 9 then Open(row,col); end else if Button = mbright then if Pole[row,col] > 200 then // уберем флаг и закроем клетку nflag := nflag - 1; Pole[row,col] := Pole[row,col] - 200; x := (col-1)* W + 1; y := (row-1)* H + 1; Canvas.Brush.Color := clltgray; Canvas.Rectangle(x-1,y-1,x+W,y+H); end else // поставить в клетку флаг nflag := nflag + 1; if Pole[row,col] = 9 then nmin := nmin + 1; Pole[row,col] := Pole[row,col]+ 200; if (nmin = NM) and (nflag = NM) then status := 2; // игра закончена

332 Глава 10. Примеры программ 327 ShowPole(status); end else Kletka(row, col, status); Процедура Flag (листинг 10.7) рисует флажок. Флажок (рис ) состоит из четырех примитивов: линии (древко), замкнутого контура (флаг) и ломаной линии (буква "М"). Процедура рисует флажок, используя метод базовой точки, т. е. координаты всех точек, определяющих положение элементов рисунка, отсчитываются от базовой точки. Процедура Mina (листинг 10.8) рисует мину. Мина (рис ) состоит из восьми примитивов: два прямоугольника и сектор образуют корпус мины, остальные элементы рисунка линии ("усы" и полоски на корпусе). Обеим процедурам в качестве параметров передаются координаты базовой точки рисунка и указатель на объект, на поверхности которого надо рисовать. (x, y) (x + 8, y + 12) (x + 30, y + 12) (x + 4, y + 4) (x + 4, y + 10) (x + 4, y + 14) (x + 4, y + 20) (x + 4, y + 36) Рис Флажок (x, y) (x+16, y+26) (x+8, y+30) (x+6, y+36) (x+24, y+26) (x+30, y+32) (x+6, y+36) Рис Мина

333 328 Часть II. Практикум программирования Листинг Процедура Flag // рисует флаг procedure TForm1.Flag(x, y : integer); var p : array [0..3] of TPoint; // координаты точек флажка m : array [0..4] of TPoint; // буква М // зададим координаты точек флажка p[0].x:=x+4; p[0].y:=y+4; p[1].x:=x+30; p[1].y:=y+12; p[2].x:=x+4; p[2].y:=y+20; p[3].x:=x+4; p[3].y:=y+36; // нижняя точка древка m[0].x:=x+8; m[0].y:=y+14; m[1].x:=x+8; m[1].y:=y+8; m[2].x:=x+10; m[2].y:=y+10; m[3].x:=x+12; m[3].y:=y+8; m[4].x:=x+12; m[4].y:=y+14; with Canvas do // установим цвет кисти и карандаша Brush.Color := clred; Pen.Color := clred; Polygon(p); // флажок // древко Pen.Color := clblack; MoveTo(p[0].x, p[0].y); LineTo(p[3].x, p[3].y); // буква М Pen.Color := clwhite; Polyline(m); Pen.Color := clblack;

334 Глава 10. Примеры программ 329 Листинг Процедура Mina // рисует мину procedure TForm1.Mina(x, y : integer); with Canvas do Brush.Color := clgreen; Pen.Color := clblack; Rectangle(x+16,y+26,x+24,y+30); Rectangle(x+8,y+30,x+16,y+34); Rectangle(x+24,y+30,x+32,y+34); Pie(x+6,y+28,x+34,y+44,x+34,y+36,x+6,y+36); MoveTo(x+12,y+32); LineTo(x+26,y+32); MoveTo(x+8,y+36); LineTo(x+32,y+36); MoveTo(x+20,y+22); LineTo(x+20,y+26); MoveTo(x+8, y+30); LineTo(x+6,y+28); MoveTo(x+32,y+30); LineTo(x+34,y+28); Справочная информация В результате выбора в меню? команды Справка появляется окно справочной информации (рис ). Активизирует процесс отображения справочной информации процедура обработки события Click (листинг 10.9) на элементе меню N3. Следует обратить внимание, что перед тем как вызвать процедуру WinExec, которая запускает утилиту отображения справочной информации, процедура проверяет, не отображается ли уже справочная информация. Если окно справочной информации открыто, то вызывается функция SetForegroundWindow, которая перемещает окно справочной информации на передний план. Листинг Отображение справочной информации // выбор из меню? команды "Справка" procedure TForm1.N3Click(Sender: TObject); var h : HWND; h := FindWindow('HH Parent','Сапер'); if h = 0 then WinExec('hh.exe saper.chm', SW_RESTORE)

335 330 Часть II. Практикум программирования else ShowWindow(h,SW_RESTORE); Windows.SetForegroundWindow(h); Рис Окно справочной системы программы "Сапер" Информация о программе При выборе из меню? команды О программе на экране появляется одноименное окно (рис ). Рис Выбрав ссылку, можно перейти на страницу издательства "БХВ-Петербург"

336 Глава 10. Примеры программ 331 Чтобы программа во время своей работы могла вывести на экран окно, отличное от главного (стартового), в проект нужно добавить форму. Делается это выбором в меню File команды New Form - Delphi for Win32. В результате выполнения этой команды в проект добавляются новая форма и соответствующий ей модуль. Форма О программе (Form2) приведена на рис , значения свойств формы и компонентов в табл и Рис Форма О программе Таблица Значения свойств формы О программе Свойство Name Caption BorderStyle BorderIcons.biSystemMenu Position Значение Form2 О программе bssingle False pomainformcenter Таблица Значения свойств компонентов формы О программе Компонент Свойство Значение Label3 Font.Color clnavy Font.Style.Underline Cursor True crhandpoint Button1 ModalResult mrok Вывод окна О программе выполняет функция обработки события Click, которое происходит в результате выбора в меню? команды О программе (лис-

337 332 Часть II. Практикум программирования тинг 10.10). Следует обратить внимание, что в секцию implementation главного модуля необходимо поместить ссылку на модуль формы О программе директиву uses AboutForm. Непосредственно отображение окна выполняет метод ShowModal, который выводит окно, как модальный диалог. Модальный диалог перехватывает все события, адресованные другим окнам приложения, в том числе и главному. Таким образом, пока модальный диалог находится на экране, продолжить работу с приложением, которое вывело модальный диалог, нельзя. Листинг Отображение окна О программе procedure TForm1.N4Click(Sender: TObject); Form2.ShowModal; На поверхности формы О программе есть ссылка на Web-страницу издательства "БХВ-Петербург". Предполагается, что в результате щелчка на Web-ссылке в окне браузера будет открыта указанная страница. Запуск браузера обеспечивает функция ShellExecute (листинг 10.11). Эта функция достаточно универсальна. Она обеспечивает выполнение операций с файлами, тип которых известен операционной системе. В данном случае необходимо открыть Web-страницу, поэтому в качестве параметров функции указаны команда open и адрес страницы. Следует обратить внимание, что функция ShellExecute не запускает конкретную программу, а информирует операционную систему о необходимости открыть указанный файл. Поэтому в результате щелчка на ссылке будет запущен браузер, установленный на компьютере пользователя. Листинг Щелчок на Web-ссылке procedure TForm2.Label3Click(Sender: TObject); < Чтобы функция ShellExecute была доступна, в директиву uses надо добавить ShellAPI. >ShellExecute(Application.Handle, 'open', PChar(Label3.Caption), '', '', SW_RESTORE); Окно О программе закрывается в результате щелчка на кнопке OK. Здесь необходимо обратить внимание, что событие Click на кнопке OK не обрабатывается. Окно закрывается, т. к. значение свойства ModalResult кнопки Button1 равно mrok (по умолчанию значение этого свойства равно mrnone).

338 Глава 10. Примеры программ 333 Текст программы Полный текст программы "Сапер" приведен в листингах (модуль главной формы) и (модуль формы О программе). Листинг Модуль главной формы (MainForm.pas) unit MainForm; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Menus, StdCtrls, OleCtrls; type TForm1 = class(tform) MainMenu1: TMainMenu; N1: TMenuItem; N2: TMenuItem; N3: TMenuItem; N4: TMenuItem; procedure Form1Create(Sender: TObject); procedure Form1Paint(Sender: TObject); procedure Form1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure N1Click(Sender: TObject); procedure N3Click(Sender: TObject); procedure N4Click(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private Procedure Kletka(row, col, status : integer); // рисует клетку Procedure ShowPole(status : integer); Procedure Mina(x, y : integer); // рисует мину Procedure Flag(x, y : integer); // рисует флаг Procedure Open(row, col : integer); // открывает текущую и соседние // клетки, в которых нет мин public

339 334 Часть II. Практикум программирования var Form1: TForm1; implementation uses AboutForm; // ссылка на модуль формы "О программе" const MR = 10; // кол-во клеток по вертикали MC = 10; // кол-во клеток по горизонтали NM = 10; // кол-во мин W H = 40; // ширина клетки поля = 40; // высота клетки поля var Pole: array[0..mr+1, 0.. MC+1] of integer; // минное поле // значение элемента массива: // 0..8 количество мин в соседних клетках // 9 в клетке мина // клетка открыта // в клетку поставлен флаг nmin : integer; // кол-во найденных мин nflag : integer; // кол-во поставленных флагов status : integer; // 0 начало игры; 1 игра; 2 результат procedure NewGame(); forward; // генерирует новое поле // рисует клетку procedure TForm1.Kletka(row, col, status : integer); var x,y : integer; // координаты области вывода x := (col-1)* W + 1; y := (row-1)* H + 1; case status of 0:

340 Глава 10. Примеры программ 335 1: Canvas.Brush.Color := clbtnface; Canvas.Rectangle(x-1,y-1,x+W,y+H); exit; if Pole[row,col] < 100 then // не открытая серая Canvas.Brush.Color := clbtnface else // открытая белая Canvas.Brush.Color := clwhite; Canvas.Rectangle(x-1,y-1,x+W,y+H); // нарисовать клетку if (Pole[row,col] >= 101) and (Pole[row,col] <= 108) then Canvas.Font.Size := 13; Canvas.Font.Color := clblue; Canvas.TextOut(x+3,y+2,IntToStr(Pole[row,col]-100)); if (Pole[row,col] >= 201) and (Pole[row,col] <= 209) then Flag(x,y); 2: // игра завершена if Pole[row,col] < 100 then Canvas.Brush.Color := clbtnface else Canvas.Brush.Color := clwhite; Canvas.Rectangle(x-1,y-1,x+W,y+H); if (Pole[row,col] >= 101) and (Pole[row,col] <= 108) then Canvas.Font.Size := 13; Canvas.Font.Color := clblue; Canvas.TextOut(x+3,y+2,IntToStr(Pole[row,col]-100)); exit;

341 336 Часть II. Практикум программирования // возможно, в клетке мина, флаг или мина, // помеченная флагом мина if Pole[row,col] = 9 then Mina(x, y); // мина + флаг if (Pole[row,col] >= 200) then Flag(x, y); if (Pole[row,col] > 200) then Mina(x, y); if Pole[row,col] = 109 then Canvas.Brush.Color := clred; Canvas.Rectangle(x-1,y-1,x+W,y+H); Mina(x,y); // рисует поле procedure TForm1.ShowPole(status : integer); var row,col : integer; for row := 1 to MR do for col := 1 to MC do Kletka(row, col, status); // рекурсивная функция открывает текущую и все соседние // клетки, в которых нет мин procedure TForm1.Open(row, col : integer); if Pole[row,col] = 0 then Pole[row,col] := 100; Kletka(row,col, 1); Open(row,col-1);

342 Глава 10. Примеры программ 337 Open(row-1,col); Open(row,col+1); Open(row+1,col); // примыкающие диагонально Open(row-1,col-1); Open(row-1,col+1); Open(row+1,col-1); Open(row+1,col+1); end else if (Pole[row,col] < 100) and (Pole[row,col] <> -3) then Pole[row,col] := Pole[row,col] + 100; Kletka(row, col, 1); // новая игра расставить мины и для каждой клетки // вычислить, сколько мин находится в соседних клетках procedure NewGame(); var row,col : integer; // координаты клетки n : integer; // количество поставленных мин k : integer; // количество мин в соседних клетках // очистим элементы массива, соответствующие клеткам игрового поля for row :=1 to MR do for col :=1 to MC do Pole[row,col] := 0; // расставим мины Randomize(); // инициализация ГСЧ n := 0; // количество мин repeat row := Random(MR) + 1; col := Random(MC) + 1; if (Pole[row,col] <> 9) then Pole[row,col] := 9;

343 338 Часть II. Практикум программирования n := n+1; until (n = NM); // для каждой клетки вычислим кол-во мин в соседних клетках for row := 1 to MR do for col := 1 to MC do if (Pole[row,col] <> 9) then k :=0 ; if Pole[row-1,col-1] = 9 then k := k + 1; if Pole[row-1,col] = 9 then k := k + 1; if Pole[row-1,col+1] = 9 then k := k + 1; if Pole[row,col-1] = 9 then k := k + 1; if Pole[row,col+1] = 9 then k := k + 1; if Pole[row+1,col-1] = 9 then k := k + 1; if Pole[row+1,col] = 9 then k := k + 1; if Pole[row+1,col+1] = 9 then k := k + 1; Pole[row,col] := k; status := 0; // начало игры nmin := 0; // нет обнаруженных мин nflag := 0; // нет флагов // рисует мину procedure TForm1.Mina(x, y : integer); with Canvas do Brush.Color := clgreen; Pen.Color := clblack; Rectangle(x+16,y+26,x+24,y+30); Rectangle(x+8,y+30,x+16,y+34); Rectangle(x+24,y+30,x+32,y+34); Pie(x+6,y+28,x+34,y+44,x+34,y+36,x+6,y+36); MoveTo(x+12,y+32); LineTo(x+26,y+32); MoveTo(x+8,y+36); LineTo(x+32,y+36); MoveTo(x+20,y+22); LineTo(x+20,y+26);

344 Глава 10. Примеры программ 339 MoveTo(x+8, y+30); LineTo(x+6,y+28); MoveTo(x+32,y+30); LineTo(x+34,y+28); // рисует флаг procedure TForm1.Flag(x, y : integer); var p : array [0..3] of TPoint; // координаты точек флажка m : array [0..4] of TPoint; // буква М // зададим координаты точек флажка p[0].x:=x+4; p[0].y:=y+4; p[1].x:=x+30; p[1].y:=y+12; p[2].x:=x+4; p[2].y:=y+20; p[3].x:=x+4; p[3].y:=y+36; // нижняя точка древка m[0].x:=x+8; m[0].y:=y+14; m[1].x:=x+8; m[1].y:=y+8; m[2].x:=x+10; m[2].y:=y+10; m[3].x:=x+12; m[3].y:=y+8; m[4].x:=x+12; m[4].y:=y+14; with Canvas do // установим цвет кисти и карандаша Brush.Color := clred; Pen.Color := clred; Polygon(p); // флажок // древко Pen.Color := clblack; MoveTo(p[0].x, p[0].y); LineTo(p[3].x, p[3].y); // буква М Pen.Color := clwhite; Polyline(m); Pen.Color := clblack;

345 340 Часть II. Практикум программирования // выбор из меню? команды "О программе" procedure TForm1.Form1Create(Sender: TObject); var row,col : integer; // В неотображаемые элементы массива, которые соответствуют // клеткам по границе игрового поля, запишем число -3. // Это значение используется функцией Open для завершения // рекурсивного процесса открытия соседних пустых клеток for row :=0 to MR+1 do for col :=0 to MC+1 do Pole[row,col] := -3; NewGame(); // "разбросать" мины Form1.ClientHeight := H*MR + 1; Form1.ClientWidth := W*MC + 1; // нажатие кнопки мыши на игровом поле procedure TForm1.Form1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var row, col : integer; if status = 2 then // игра завершена exit; if status = 0 then // первый щелчок status := 1; // преобразуем координаты мыши в индексы клетки поля row := Trunc(y/H) + 1; col := Trunc(x/W) + 1; if Button = mbleft then if Pole[row,col] = 9 then // открыта клетка, в которой есть мина Pole[row,col] := Pole[row,col] + 100; status := 2; // игра закончена ShowPole(status); end

346 Глава 10. Примеры программ 341 else end else if Pole[row,col] < 9 then Open(row,col); if Button = mbright then if Pole[row,col] > 200 then else end // уберем флаг и закроем клетку nflag := nflag - 1; Pole[row,col] := Pole[row,col]-200; // уберем флаг x := (col-1)* W + 1; y := (row-1)* H + 1; Canvas.Brush.Color := clltgray; Canvas.Rectangle(x-1,y-1,x+W,y+H); // поставить в клетку флаг nflag := nflag + 1; if Pole[row,col] = 9 then nmin := nmin + 1; Pole[row,col]:=Pole[row,col]+200;// поставили флаг if (nmin = NM) and (nflag = NM) then end status := 2; // игра закончена ShowPole(status); else Kletka(row, col, status); // выбор меню "Новая игра" procedure TForm1.N1Click(Sender: TObject); NewGame(); ShowPole(status); // выбор из меню? команды "Справка" procedure TForm1.N3Click(Sender: TObject); var h : HWND; h := FindWindow('HH Parent','Сапер');

347 342 Часть II. Практикум программирования if h = 0 then else WinExec('hh.exe saper.chm', SW_RESTORE) ShowWindow(h,SW_RESTORE); Windows.SetForegroundWindow(h); // команда "О программе" procedure TForm1.N4Click(Sender: TObject); Form2.ShowModal; // отобразить окно "О программе" // обработка события Paint procedure TForm1.Form1Paint(Sender: TObject); // отобразить игровое поле ShowPole(status); // завершение работы программы procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); var end. h : HWND; h := FindWindow('HH Parent','Сапер'); if h <> 0 then // открыто окно справочной информации SendMessage (h,wm_close,0,0); Листинг Модуль формы О программе (AboutForm.pas) unit AboutForm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,

348 Глава 10. Примеры программ 343 Forms, Dialogs, StdCtrls, shellapi; // для доступа к ShellExecute type TForm2 = class(tform) Button1: TButton; Label1: TLabel; Label2: TLabel; Label3: TLabel; procedure Label3Click(Sender: TObject); private < Private declarations >public < Public declarations >var Form2: TForm2; implementation // щелчок на URL (на Label3) procedure TForm2.Label3Click(Sender: TObject); < Чтобы функция ShellExecute была доступна, в директиву uses надо добавить ShellAPI. >ShellExecute(Application.Handle, 'open', PChar(Label3.Caption),'', '', SW_RESTORE); end. MP3-плеер Программа "MP3-плеер", как не трудно догадаться, позволяет прослушивать файлы MP3. Отличительной ее особенностью является то, что в окне (рис ) не отображается заголовок (тем не менее пользователь может переместить окно), а регулировка громкости осуществляется непосредственно в окне программы.

349 344 Часть II. Практикум программирования Рис Окно программы "MP3-плеер" Форма Форма программы "MP3-плеер" приведена на рис Рис Форма программы "MP3-плеер" Компонент Image1, его следует поместить на форму первым, используется для отображения фонового рисунка. Компоненты SpeedButton1 SpeedButton3 обеспечивают управление работой плеера. Кнопка SpeedButton4 используется для активизации стандартного диалога Обзор папок. В поле компонента ListBox1 во время работы программы отображается список MP3-файлов, находящихся в выбранном пользователем каталоге. Компонент TrackBar1 служит для регулировки громкости воспроизведения. Компонент Panel1 используется в качестве подложки компонента TrackBar1. Во время работы программы в поле компонента Label1 отображается имя воспроизводимого файла, а в поле компонента Label2 время воспроизведения. Компонент MediaPlayer1 обеспечивает воспроизведение MP3-

350 Глава 10. Примеры программ 345 файлов. Компоненты SpeedButton4 и SpeedButton5 обеспечивают хранение картинок Play и Stop. Компонент PopupMenu1 обеспечивает отображение контекстного меню, команда Закрыть которого используется для завершения работы программы (заголовок в окне программы не отображается, поэтому закрыть окно программы обычным образом, щелчком на кнопке Закрыть, нельзя). Значения свойств формы приведены в табл Следует обратить внимание, что цвет формы (значение свойства Color) совпадает с "прозрачным" цветом (свойство TransparentColor), поэтому во время работы программы окно программы будет прозрачным. Значения свойств компонентов приведены в табл После настройки компонентов следует изменить значения свойств Width и Height формы в соответствии с размером компонента Image1 (в результате служебные компоненты SpeedButton5 и SpeedButton6, а также компоненты MediaPlayer1, Timer1 и PoupupMenu1 окажутся за границей формы и в окне дизайнера формы не будут отображаться). Таблица Значения свойств формы Свойство BorderStyle Color TransparentColor Значение bsnone clfuchsia clfuchsia Таблица Значения свойств компонентов Компонент Свойство Значение Image1 Height 193 Width 214 Picture Stretch True SpeedButton1 NumGlyphs 2 Glyph Flat Enabled True False SpeedButton2 NumGlyphs 2 Glyph Flat Enabled True False

351 346 Часть II. Практикум программирования Таблица 10.7 (окончание) Компонент Свойство Значение SpeedButton3 NumGlyphs 2 Glyph Flat Enabled True False SpeedButton4 NumGlyphs 1 Glyph SpeedButton5 NumGlyphs 2 Glyph SpeedButton6 NumGlyphs 2 Glyph SpeedButton7 NumGlyphs 1 Glyph Panel1 Width 24 Height 81 BevelOuter Color bvnone clsilver TrackBar1 Orientation trvertical Width 24 Height 81 TickMarks tmboth TumbLegth 10 Hint Громкость Showint True PoupupMenu1.Items[0] Name N1 Caption Закрыть Bitmap PoupupMenu1.Items[1] Name N2 Caption Свернуть Bitmap Timer1 Interval 1000

352 Глава 10. Примеры программ 347 После настройки компонентов следует установить размер формы в соответствии с размером компонента Image1 так, чтобы компоненты MediaPlayer1, SpeedButton5 и SpeedButton6 оказались за границей формы. В результате форма должна выглядеть так, как показано на рис Рис Окончательный вид формы программы "MP3-плеер" Регулятор громкости Задать необходимую громкость воспроизведения MP3-файла можно с помощью API-функции waveoutsetvolume. Для того чтобы эта функция стала доступной, в текст программы надо поместить ссылку на модуль MMSystem (указать имя модуля в директиве uses). Инструкция вызова функции в общем виде выглядит так: r = waveoutsetvolume(идентификаторустройства, Громкость) Параметр ИдентификаторУстройства задает устройство воспроизведения (точнее, звуковой канал), громкость которого надо установить. При регулировке громкости воспроизведения MP3-файла значение этого параметра должно быть равно WAVE_MAPPER (константа WAVE_MAPPER определена в модуле MMSystem). Параметр Громкость (двойное слово) задает громкость воспроизведения: младшее слово определяет громкость левого канала, старшее правого. Максимальной громкости звучания канала соответствует шестнадцатеричное значение FFFF, минимальной Таким образом, чтобы установить максимальную громкость воспроизведения в обоих каналах, значение параметра Громкость должно быть $FFFFFFFF (в Delphi при записи шестнадцатеричных констант используется префикс $). Уровню громкости 50% соответствует константа $7FFF7FFF. Необходимо обратить внимание, что функция waveoutsetvolume регулирует громкость воспроизведения звукового канала, а не общий уровень звука. Изменение громкости осуществляется с помощью компонента TrackBar1. Следует обратить внимание, что при вертикальном расположении компонента верхнему положению движка соответствует нулевое значение свойства Position, нижнему значение, заданное свойством Max. Поэтому уровень громкости, соответствующий положению движка, вычисляется как разница между текущим и макси-

353 348 Часть II. Практикум программирования мально возможным значением свойства Position (это значение задает свойство Max), умноженная на $FFFF. Непосредственное изменение громкости осуществляет процедура обработки события Change (листинг 10.14), регулятора громкости (компонента TrackBar1), которое происходит в результате перемещения движка мышью или клавишами перемещения курсора. Сначала она вычисляет значение громкости для левого канала, затем к полученному значению добавляет сдвинутое на 16 разрядов это же значение (в результате в старшем и младшем словах находятся одинаковые значения), и полученное таким образом значение передается в качестве параметра функции waveoutsetvolume. Листинг Обработка события Change компонента TrackBar1 // пользователь изменил положение регулятора громкости procedure TForm1.TrackBar1Change(Sender: TObject); volume := $FFFF * (TrackBar1.Max - TrackBar1.Position); volume := volume + (volume shl 16); waveoutsetvolume(wave_mapper,volume); Перемещение окна Значение свойства BorderStyle формы равно bsnone, поэтому в окне программы заголовок не отображается (см. рис. 10.5), и поэтому, на первый взгляд, пользователь, вроде бы, лишен возможности перемещения окна по экрану привычным для себя способом. Тем не менее окно программы все-таки переместить можно. Для этого надо установить указатель мыши в свободную (не занятую компонентами) точку окна, нажать левую кнопку мыши и, удерживая ее нажатой, перетащить окно в нужную точку экрана (такой способ весьма распространен). Описанный способ перемещения окна обеспечивает процедура обработки события MouseDown в поле компонента Image1 (листинг 10.15). Процедура "обманывает" операционную систему, сообщает ей (путем посылки соответствующего сообщения), что пользователь нажал кнопку мыши в заголовке окна, т. е. выполнил действие, требующее перемещения окна. Листинг Обработка события MouseDown в поле компонента Image1 procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); // Заголовка у окна нет. Обманем Windows. Пусть OC думает, // что кнопка нажата, и удерживается в заголовке окна.

354 Глава 10. Примеры программ 349 // В этом случае пользователь может перемещать окно обычным образом ReleaseCapture; SendMessage(Form1.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0) Текст программы Полный текст программы "MP3-плеер" приведен в листинге Листинг MP3-плеер < MP3-плеер с регулятором громкости. (с) Культин Н.Б., >unit MainForm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, ExtCtrls, MPlayer, ComCtrls, MMSYSTEM, FileCtrl, Menus; // эти ссылки вставлены вручную type TForm1 = class(tform) MediaPlayer1: TMediaPlayer; // кнопки SpeedButton1: TSpeedButton; // предыдущая композиция SpeedButton2: TSpeedButton; // Play/Stop SpeedButton3: TSpeedButton; // следующая композиция SpeedButton4: TSpeedButton; // выбор папки // невидимые кнопки SpeedButton5 и SpeedButton6 обеспечивают // хранение картинок Play и Stop SpeedButton5: TSpeedButton; SpeedButton6: TSpeedButton; ListBox1: TListBox; // список композиций Timer1: TTimer;

355 350 Часть II. Практикум программирования Label1: TLabel; // воспроизводимая композиция Label2: TLabel; // время воспроизведения Image1: TImage; // фон PopupMenu1: TPopupMenu; // контекстное меню N1: TMenuItem; // "Закрыть" N2: TMenuItem; // "Свернуть" // регулятор громкости Panel1: TPanel; // панель TrackBar1: TTrackBar; procedure Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure N2Click(Sender: TObject); procedure N1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure ListBox1Click(Sender: TObject); procedure SpeedButton2Click(Sender: TObject); procedure SpeedButton1Click(Sender: TObject); procedure SpeedButton3Click(Sender: TObject); procedure TrackBar1Change(Sender: TObject); procedure Timer1Timer(Sender: TObject); procedure SpeedButton4Click(Sender: TObject); // эти объявления вставлены сюда вручную procedure Play; // воспроизведение procedure PlayList(Path: string); // формирует список MP3-файлов private < Private declarations >public < Public declarations >var Form1: TForm1;

356 Глава 10. Примеры программ 351 implementation var SoundPath: string[255]; // каталог, в котором находятся MP3-файлы min,sec: integer; // время воспроизведения volume: LongWord; // громкость воспроизведения: // старшее слово правый канал, // младшее слово левый procedure TForm1.FormCreate(Sender: TObject); PlayList(''); // сформировать список MP3-файлов ListBox1.ItemIndex := 0; Label1.Caption:=ListBox1.Items[ListBox1.itemIndex]; // установить уровень громкости TrackBar1.Position := 7; // старшее слово переменной volume правый канал, // младшее левый volume := (TrackBar1.Position - TrackBar1.Max+1)* $FFFF; volume := volume + (volume shl 16); waveoutsetvolume(wave_mapper,volume); // уровень громкости // формирует список MP3-файлов procedure TForm1.PlayList(Path: string); var SearchRec: TSearchRec; // структура SearchRec содержит информацию // о файле, удовлетворяющем условию поиска ListBox1.Clear; // сформировать список MP3-файлов if FindFirst(Path + '*.mp3', faanyfile, SearchRec) = 0 then // В каталоге есть файл с расширением mp3. // Добавим имя этого файла в список ListBox1.Items.Add(SearchRec.Name);

357 352 Часть II. Практикум программирования // Есть еще MP3-файлы? while (FindNext(SearchRec) = 0) do ListBox1.Items.Add(SearchRec.Name); ListBox1.ItemIndex := 0; // щелчок на названии произведения procedure TForm1.ListBox1Click(Sender: TObject); if SpeedButton2.Tag = 0 then // вывести в поле метки Label1 имя выбранного файла Label1.Caption:=ListBox1.Items[ListBox1.itemIndex] else Form1.Play; // активизировать процесс воспроизведения // щелчок на кнопке "Воспроизведение" procedure TForm1.SpeedButton2Click(Sender: TObject); // свойство Tag хранит информацию о состоянии // плеера: 0 стоп; 1 воспроизведение if SpeedButton2.Tag = 0 then // начать воспроизведение Form1.Play; end else // если кнопка "Воспроизведение" нажата, // то повторное нажатие останавливает воспроизведение SpeedButton2.Tag := 0; MediaPlayer1.Stop; SpeedButton2.Glyph := SpeedButton5.Glyph; Timer1.Enabled := False; SPeedButton2.Hint := 'Play'; Label2.Caption := '0:00'; // кнопка "Предыдущий трек" procedure TForm1.SpeedButton1Click(Sender: TObject);

358 Глава 10. Примеры программ 353 if ListBox1.ItemIndex > 0 then ListBox1.ItemIndex := ListBox1.ItemIndex - 1; if SpeedButton2.Tag = 1 then Play; // кнопка "Следующий трек" procedure TForm1.SpeedButton3Click(Sender: TObject); if ListBox1.ItemIndex < ListBox1.Count then ListBox1.ItemIndex := ListBox1.ItemIndex + 1; if SpeedButton2.Tag = 1 then Play; // пользователь изменил положение регулятора громкости procedure TForm1.TrackBar1Change(Sender: TObject); volume := $FFFF * (TrackBar1.Max - TrackBar1.Position); volume := volume + (volume shl 16); waveoutsetvolume(wave_mapper,volume); // воспроизвести композицию, название которой выделено в списке ListBox1 procedure TForm1.Play; Timer1.Enabled := False; Label1.Caption:=ListBox1.Items[ListBox1.itemIndex]; MediaPlayer1.FileName := SoundPath + ListBox1.Items[ListBox1.itemIndex]; try Mediaplayer1.Open; except on EMCIDeviceError do ShowMessage('Ошибка обращения к файлу '+ ListBox1.Items[ListBox1.itemIndex]); exit;

359 354 Часть II. Практикум программирования MediaPlayer1.Play; min :=0; sec :=0; Timer1.Enabled := True; SpeedButton2.Hint := 'Stop'; SpeedButton2.Tag := 1; // сигнал таймера procedure TForm1.Timer1Timer(Sender: TObject); // изменить счетчик времени if sec < 59 then inc(sec) else sec :=0; inc(min); // вывести время воспроизведения Label2.Caption := IntToStr(min)+':'; if sec < 10 then Label2.Caption := Label2.Caption +'0'+ IntToStr(sec) else Label2.Caption := Label2.Caption + IntToStr(sec); // если воспроизведение текущей композиции не завершено if MediaPlayer1.Position < MediaPlayer1.Length then exit; // воспроизведение текущей композиции закончено Timer1.Enabled := False; // остановить таймер MediaPlayer1.Stop; // остановить плеер if ListBox1.ItemIndex < ListBox1.Count // список не исчерпан then ListBox1.ItemIndex := ListBox1.ItemIndex + 1; Play; // активизировать воспроизведение MP3-файла end // Щелчок на кнопке "Папка".

360 Глава 10. Примеры программ 355 // Выбрать папку, в которой находятся MP3-файлы procedure TForm1.SpeedButton4Click(Sender: TObject); var Root: string; // корневой каталог pwroot : PWideChar; Dir: string; Root := ''; // корневой каталог папка Рабочий стол GetMem(pwRoot, (Length(Root)+1) * 2); pwroot := StringToWideChar(Root,pwRoot,MAX_PATH*2); if not SelectDirectory('Выберите папку, в которой находятся MP3-файлы', pwroot, Dir) then Dir :='' else Dir := Dir+'\'; // каталог, в котором находятся MP3-файлы, выбран SoundPath := Dir; PlayList(SoundPath); // нажатие кнопки мыши в поле компонента Image1 procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); // Заголовка у окна нет. Обманем Windows. Пусть OC думает, // что кнопка нажата, и удерживается в заголовке окна. // В этом случае пользователь может перемещать окно обычным образом ReleaseCapture; SendMessage(Form1.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0) // выбор команды "Закрыть" в контекстном меню procedure TForm1.N1Click(Sender: TObject); Form1.Close; end.

📎📎📎📎📎📎📎📎📎📎