Upload
others
View
28
Download
1
Embed Size (px)
Citation preview
7~УКk DELPHI ПРОГРАММИРОВАНИЕ С НУЛЯ
Илья БЕСКОРОВАЙНЫЙ
VсиБИРСКОЕ УНИВЕРСИТЕТСКОЕ ИЗДАТЕльство НОВОСИБИРСК • 2008
УДК 681.3.06 ББК 32.973.26-018.2
Б53
Бескоровайuый, И. В.
Б53 Азбука Delphi: программирование с нуля [Текст]/ И. В. Беско-ровайный. Новосибирск: Сиб. унив. изд-во, 2008. 112 с.
ISBN 978-5-379-00279-4
В книге кратко излагаются теоретические основы программирования и ООП, а работа с базовыми компонентами Delphi дается на примерах. Это позволяет за несколько дней научиться создавать несложные приложения под
Windows. В книге содержатся рекомендации как, используя Delphi, превратить ком
пьютер в эффективный инструмент решения повседневных задач. А приведенный иллюстративный материал может быть использован как основа для любого учебного Rypca по Delphi.
Отдельная глава полностью посвящена работе с файлами. В справочном приложении дается краткое описание специализированных компонентов и биб
лиотек.
Книга написана простым и понятным языком и будет полезна студентам
вузов и старшеклассникам в качестве учебного пособия по Delphi.
ISBN 978-5-379-00279-4
УДК 681.3.06 ББК 32.973.26w018.2
© Бескоровайный И. В., 2008 © Сибирское университетское
издательство, оформление, 2008
СОДЕРЖАНИЕ
Введение" ..... "" ............. " ..... "." .. """."" ...... "" ... " ....... " .. """ .... "." ..... " .. 5
Глава 1. Знакомство со средой программирования " .. " ..... "" ... " .... 8
Глава 2. Object Pascal ..... ""."." ... " ............... "" .... " .... " ... "" .... " ...... " ... 17
Глава З. Учимся программировать .............. "." ... " .......... """."" "" ... 25
3.1. Типы данных" .............. "." .... " ... " .......... " .. " ...... " .. ""." .. " ..... "."." 27 Целочисленные типы .............. " ....... " ......................... " ................... 27 Вещественные, или дробные, типы ........... " .......... " ................ " .. ". 28 Символьные переменные ............ " ......... "."." .. " ...................... " ... " 29 Логический, или Булев, тип " ............ " ........ " ..... """ .... " .................. 30 Тип список ......................... " ..... " ................... "" ........... " ............ -" .. ".31 Тип диапазон .. " ...... " .. "." .... " .. "." ".". " ............ " ......... "" ... " .. " ""." .. 31 (/:>'}IHKLJ,l'IИ пpeo6pfJ.:ЗOEJaHl'tfl тl'tпов ...................................................... 32
3.2. Выражения, операторы и операнды ... "" .............. "." ... " .. " .. "." 33 3.3. Процедуры и функции ... "." ... " ... " .. "" .. ""."" ... " .... " ... " ..... " .. " ... 34 3.4. Массивы"" ...... " .. ".:'" .... " ......... " .... " ... """ ... " .............. ""."" .. """. 36 3.5. Циклы .. "" ........ " .... " .. """"." .. " ......... "" ....... "" .. """ " ... """"" " ..... 38 3.6. Условный оператор ....... " .................... "."."" .. " ........... "." ........ ".41 3.7. Функции для работы с текстом ........... "." .. "" ... " ... "." .... " ...... " ... 45
Глава 4. Объектно-ориентированное программирование"" ........ 50
Глава 5. Работа с файлами в Delphi . " .. "". "". """ .. "" ..... "" " ... " ....... 57
Глава 6. Примеры взаимодействия объектов разных классов." ... " ...... " .... " ....... " .. " ... " ........... " ............. " .. 70
6.1. Приложение «Кнопки» ....... " ..... " .... " .... " ..... """" ................. " ...... 70 6.2. Фильтр ввода .......... """ .. " ......... " ... "." .... "."" .... " ..... " ... " ........... " 73 6.3. Контекстные меню ............ """" ... "." ... "" ........... " ..... """ ... " .. """ 74
Глава 7. Процесс отладки проrраммь1."""""."" .......... " .... " .. """"" 77
-3-
Глава 8. Библиотека компонентов Delphi (справочное приложение) ....................... " ........................ " 84
8.1 . Меню .. ". ".""""""" " ...... " .. " ....... ""." "" ." ... "." .......... ".""." .. " ..... 90 8.2. Работа с текстом .. " .... " ...... """"." .. " ....... " ....... "" ..... " .......... "" ... 93 8.3. Кнопки".""."" .. """."." .. " .. "" .. """" ... "" ... "." .... " .. " ........... "" ...... 99 8.4. Списки."." .. """."." ..... """." .. " .... """."""."."" .. "" .... " ... """ .... " 103 8.5. Остальные компоненты ... " ............... " .......................... "."" ....... 105
Scrolf Bar ........... " " .....•....... " ... " ..... " .. " .... " .. " ... " .. " .. " ....................... 105 lmage ..... " ...... " ....... " ...................... "." ... " .. "." .. "" .............. "."." ..... 106
Shape ... """"" ... ".".""" ..... """.""" .. """".""" ..... "" .. :".""."." .. """ 107 Bevel ."."." ........ """"."" .. "." ...... " ..... " ..... " ....... " .. " ................. " ...... 107 Scrol/Box " ... " .. " .. "" .. " ..... " "." " .. " " .. " ". " .. "." .............. "" ..... "" .... ". 107 Splitter .... "."""" " ..... " ... " ........ "" "" " . "" ... "."." ...... ".""." ......... "" ... 1 08 ControlBar." .. " .. "" .. ". " .. " " ... """""" ... " ... ". ".":" .. " ""." .... "."" .... ". 108 Chart"." .... " ..... " .. """" ... " .. """" ... " .. "."." .. ""." .. "" .. "." ...... """"" .. 109
ВВЕПЕНИЕ
Книги по программированию можно разделить на три большие
группы: для начинающих, для опытных пользователей и для профес· сионалов в некоторой области. Книги первой группы представляют собой довольно объемные тома, где большую часть занимает описание интерфейса и установок, а меньшая часть отведена непосредственно программированию. Книги второй группы меньше по объему, по· скольку авторы не считают нужным включать в них описание очевид
ных или интуитивно понятных вещей. Книги третьей группы, по большому счету, представляют собой справочники или рассмотрение вопросов столь же интересных и сложных, сколь редко встречающих·
ся в реальной практике программирования. Эти книги, как правило,
довольно объемны.
Таким образом, несложно по весу этой книги догадаться, в какую
группу ее постарались отнести те, благодаря кому книга появилась в ваших руках.
Книга окажется полезной тем, кто достаточно хорошо знаком
с операционной системой Windows, чтобы у него не вызывали затруднений такие термины, как Окно, Инструментальная панель, Встроен
ная подсказка, Файл и т. д., и кто хочет научиться разрабатывать приложения под Windows. Если читатель имеет определенный опыт программирования на таких языках, как Pascal или С, то это, несомненно, послужит неплохим подспорьем. Полезным также окажется
знание основ объектно-ориентированного программирования, но если
таковое отсутствует, это не страшно, поскольку новые понятия пос
тепенно усваиваются по ходу чтения книги. В главе 4 кратко описаны некоторые аспекты объектно-ориентированного программирования, однако для лучшего усвоения материала целесообразнее будет про·
двигаться по книге по порядку, за исключением последней главы,
которая, являясь справочной, может оказаться полезной при работе
с примерами, разборе возникших вопросов или написании собственных приложений.
Вы не найдете в книге исчерпывающих описаний меню, системы
встроенной справки и детального разбора интерфейса описываемого приложения. Чем хороша ОС Windows, так это интуитивно понятным и, в основном, дружественным интерфейсом. Таким образом, если
пользователь работал какое-то время в одном приложении, то ему не составит труда запустить другое и начать работать в нем. Естествен-
-5-
Азбука Delphi: программирование с нуля
но, это утверждение касается только интерфейсной части и совсем не относится к области решаемых приложением задач. Если вы два года работали в текстовом процессоре наподобие MS Word, едва ли это даст вам знание того, как эффективно работать в системе трехмерного проектирования, хотя, запустив эту систему и некоторое
время побродив по справке, вы начнете понимать основную логику
работы. При изложении материала, по возможности, мы старались избе
жать смысловых повторов. Это значит, что если какое-либо действие
описано подробно во второй главе, то в третьей или последующих таких описаний больше не будет. Если какой-то момент забылся, придется вернуться к соответствующему месту книги и прочитать его
снова.
Основная сложность в процессе программирования - не в том,
чтобы запомнить, что означает какая-то команда или сколько байт в оперативной памяти занимает та или иная переменная, хотя без этого
работа будет не столь эффективной, сколь могла бы быть. Основная сложность - научиться разбивать задачу на более простые фрагмен
ты и составлять алгоритмы решения поставленных задач. У читывая,
что алгоритм решения по большей части содержится в постановке
задачи, важно уметь формулировать для себя задачи таким образом, чтобы они могли быть разбиты на простейrµие действия, доступные
компьютеру. Не стоит требовать от компьют~ра слишком многого. Как ни бьются специалисты по искусственному интеллекту, компьютер
по-прежнему не может заменить человека в творческой части работы,
однако он способен очень быстро делать рутинную ее часть. Как пра
вило, именно это от него и требуется в реальных условиях.
Итак, если вы хотите научиться разрабатывать приложения для Windows, то Delphi - это то, что вам нужно. Интегрированная сре
да разработки (IDE) позволяет в кратчайшие сроки создавать действующие приложения, на ходу проектируя и видоизменяя их пользо
вательский интерфейс. Delphi - одна из первых систем, использующих технологию быстрой разработки приложений (Rapid Application Development-RAD). Эта технология является довольно распространенной и невероятно гибкой. Первый факт позволяет общаться с другими разработчиками, обмениваясь опытом и постоянно совер
шенствуясь, а второй факт говорит о том, что, используя Delphi, вы можете спроектировать практически все что угодно: от простейшего
калькулятора или будильника до системы управления базами данных
или WеЬ-серверов. В то же время Delphi обладает достаточно широкими возможностями по защите операционной системы и компьюте-
-6-
Введение
ра от неосторожных действий начинающего разработчика. Эти фак
торы делают Delphi уникальной средой программирования для тех, кто решил научиться разрабатывать приложения под Windows. Являясь отличной средой для самостоятельного изучения, Delphi при этом практически не ограничивает фантазию разработчика. Если со врем~нем вам станет не хватать возможностей Delphi, вы сможете подобрать для себя среду программирования, более подходящую харак
теру решаемых задач. На тот момент, когда возникнет такая необходимость, вы сможете провести анализ необходимых возможностей пакета и выбрать на рынке то, что вам действительно поможет быстро и качественно решать стоящие перед вами задачи. Однако начать
лучше всего именно с Delphi, причем не откладывая это дело в долгий ящик.
Глава 1. Знакомство со средой проrраммирования
Интегрированная среда разработки (lntegrated Development Environment, IDE) - это среда, в которой разработчику предоставляется все необходимое для написания, отладки, запуска и тестироw вания приложений. Создатели Delphi сделали очень многое, чтобы облегчить процесс создания эффективных приложений. В состав IDE входит несколько элементов:
• редактор кода;
•отладчик;
• набор панелей инструментов;
• обширная библиотека компонентов;
• редактор изображений;
• инструментарий баз данных.
Запустите Delphi. Предполагается, что установка не вызвала проблем, с которыми вы
не смоп1и справиться. После завершения процесса загрузки экран вы
глядит, как показано на рис. 1.1. Вообще вид первоначального экрана зависит от того, какая версия Delphi используется. В данном случае -Delphi 6.0, и все примеры, приведенные в книге, разрабатывались и тестировались именно в этой версии. Однако едва ли при переходе к более ранним или более поздним версиям возникнут какие~либо трудности.
Некоторые элементы основного окна практически такие. же, как и в
других Windоws-приложениях, поэтому не будем останавливаться на разборе меню или основных кнопок панели инструментов. Упомянем только несколько основных команд и их последовательностей, исполь
зуемых при разработке приложений. Создание нового проекта приложения - последовательность File ~ New ~ Application, приводящая к появлению новой пустой формы и Окна редактора кода, таких, как вы
можете видеть на рис. 1.1. При сохранении проекта или его заготовки по команде Save All предлагается ввести имя проекта и имя каждого из модулей, включенных в проект. Для запуска приложения служит команда Run ~ Run или кнопка с зеленой стрелкой на панели инструментов.
Справа сверху на панели инструментов расположена палитра библиотеки визуальных компонентов ( Visual Component Library, VCL ). Ближе к истине будет называть ее просто библиотекой, поскольку на вкладках содержатся как визуальные компоненты, например поля
ввода текста и кнопки, так и невизуальные, например таймер.
-8-
Глава 1. Знакомство со средой проrраммирования
Рис. 1.1. Экран Delphi 6.0 сразу после загрузки
Слева в основном поле окна расположен Инспектор объектов Object lnspector (см. рис. 1.1 ), с его помощью задаются свойства компонентов и назначаются обработчики событий. Над Инспектором объектов отображается Структура дерева объектов~ окно Object TreeView, в котором показана иерархия объектов, задействованных в приложе
нии. Полезность Object TreeView ограничена, и многие программисты предпочитают закрывать его вовсе, используя освободившееся место
для разворачивания Инспектора объектов.
Правее расположена пустая форма, готовая для размещения на ней компонентов VCL. Для того чтобы поместить нужный компонент на форму, нужно открыть соответствующую страницу палитры VCL, щелкнуть по компоненту, а затем щелкнуть на том месте формы, где должен располагаться компонент.
Форма, на которой размещаются компоненты, является основой большинства приложений Delphi. Она представляет собой окно создаваемого приложения. В данном случае удобство IDE заключается в том, что программист уже в процессе создания приложения может
видеть, каким будет у его приложения интерфейс для общения с пользователем и непосредственно редактировать взаимное расположение
и свойства (цвет, размер) элементов интерфейса для придания ему большей эргономичности.
-9-
Азбука Delphi: программирование с нуля
Еще одной частью IDE является Окно редактора кода (рис. 1.2). При запуске это окно практически полностью скрыто за формой и
лишь немного выглядывает из-за нее снизу и справа; изменить это
положение можно, изменив размеры формы или окна редактора. На работу с редактором кода уходит, как правило, больше всего времени при разработке приложения. Ведь если шаблонные опер~ции, такие как создание и удаление заголовков процедур, описание типов, опре
деление подключаемых модулей и некоторые другие, среда програм
мирования выполняет за программиста, то определение конкретных
действий, которые должны выполняться при работе приложения и, собственно, являются целью создания приложения, практически целиком ложатся на самого программиста.
Слева находится встроенное окно Code Explorer (см. рис. 1.2). Это окно рассматриваться не будет, его вполне можно закрыть.
В редакторе кода применяется выдеJ1ение цветом синтаксических элементов. Ключевые слова в Object Pascal выделяются жирным шрифтом, а комментарии - синим курсивом. Комментарием считается весь текст, заключенный в фигурные скобки { } , скобки со звездочкой(**) или расположенный между двойной косой чертой// и концом строки.
,--- TFOfm1 ф fiiJ VariaЫes/Constants tE··G]) Uses
Code Ех larer
Mesэages, SysUtilэ, Variants,
TForml = c1ass(TForm) privat.e
{ P.civate decla.cations } рм1iс
{ РиЫiс declarations } end;
TForml;
Рис. 1.2. Окно редактора кода
- 10 -
Глава 1. Знакомство со средой программирования
Delphi содержит достаточно много настроек, но все их знать на начальном этапе вовсе не обязательно, большинство вполне можно оставить по умолчанию. Для изменения настроек среды нужно выбрать пункт меню Tools ~ Environment Options ...
В открывшемся Окне настроек среды (рис. 1.3) рассмотрим настройки, находящиеся на странице Preferences (Предпочтения).
Самым ценным пунктом, пожалуй, здесь являются настройки автоматич~ского сохранения Autosave options. Установка галочки Editor frles соответствует сохранению всех модифицированных файлов, а установка галочки Project desktop - сохранению расположения
элементов рабочего стола Delphi. Автосохранение файлов происходит каждый раз при запуске приложения, а сохранение рабочего стола - каждый раз при закрытии Delphi. Автоматическое сохранение модифицированных файлов позволяет избежать повторного набора кода при аварийном завершении работы. Однако если пишутся маленькие приложения, единственной целью создания которых являет
ся проверка работы отдельной функции или фрагмента алгоритма, и повторно использовать их не предполагается, то лучше эту установ
ку снять. Как обычно, не существует таких установок, которые были бы оптимальны в каждом конкретном случае.
Рис. 1.3. Диалог Environment Options
- 11 -
Азбука Delphi: программирование с нуля
Рассмотрим теперь раздел настроек процесса компиляции и за
пуска готовой программы Compiling and running. Здесь доступны следующие пункты:
• Показывать окно процесса компиляции (Show compiler progress ). Здесь галочку лучше убрать, ведь вся информация, которая выводится в окне процесса компиляции, дублируется в дополни
тельном окне, появляющемся внизу редактора кода (рис. 1.4), на которое, кстати, стоит обращать пристальное внимание. В нем отображаются ошибки, возникшие при компиляции, а также советы (Hints) по оптимизации отдельных участков кода или предупреждения (Warnings) о возможности возникновения ошибок периода выполнения приложения.
• Предупреждать, когда необходима перекомпиляция пакета (Warn on package rebuild). Пока вы только начинаете работать с Delphi, лучше активизировать эту опцию, позже вы сможете сами ре
шить, нужна она вам или просто отнимает лишнее время.
• Минимизировать оболочку, когда запущена редактируемая в IDE программа (Miпimize оп run).
• Прятать окна Инспектора объектов и визуальной формы при запуске программы (Hide designers on run).
Последние две опции не влияют на работу приложения, но, буду
чи отключенными, экономят время процессора на сворачивание
соответствующих окон. Так что, в общем случае, выбор остается за программистом.
На других страницах Окна настроек среды находится еще неко
торое количество полезных опций, таких как настройка отображения сетки на форме (вкладка Designer, раздел Grid options) или настройки состава Палитры компонентов (Palette ). Все они, впрочем, обладают условной полезностью, и для большинства задач программирования их можно оставить такими, какими они выставляются при установке
Delphi.
Рис. 1. 4. Список ошибок периода компиляции
- 12 -
Гnава 1. Знакомство со средой программирования
Диалог, вызываемый пунктом меню Project-+ Options"., включает в себя несколько страниц опций, позволяющих изменять парамет
ры компиляции проекта. Все настройки после компиляции сохраняют
ся в файле с расширением .opt. Рассмотрим некоторые из страниц этого диалога:
•Закладка Forms. Здесь перечI:Iслены все формы, включенные в проект; можно указать, какие из них должны появляться на
экране при запуске приложения, какая форма будет основной.
•на странице Application задаются название приложения (оно будет отображаться на панели Windows при запуске), файл помощи и иконка приложения.
• Страница Compiler содержит установки компилятора. В разделе Создание кода (Code generation) можно задать или запретить возможность оптимизации итогового кода, создание стэка вызо
вов процедур и функций, возможность отслеживать возникновение деления числа на нуль, а также увеличение размера, зани
маемого элементами структур до числа байт, которым проще оперировать процессору. Первая опция, будучи установленной,
ускоряет работу приложения. Увеличение числа в списке Record field alignment (выравнивание полей записи) немного ускоряет работу программы за счет незначительного увеличения потреб
ляемой памяти, вторая и третья опции замедляют работу прило
жений. В разделе Настройки синтаксиса (Syntax options) в основном расположены настройки работы с динамической памятью. Раздел Ошибки периода выполцения (Runtime errors) содержит опции, контрол11рующие выход индексов массивов и
строк за объявленные пределы, правильность работы операций ввода и вывода, а также появление ситуации переполнения при
работе с целочисленными величинами. Опции раздела Отладка (Oebugging) в основном добавляют вспомогательную информацию в различные файлы проекта. На этой же вкладке в разделе Сообщения (Messages) можно отключить вывод предупреждений Warnings и советов Hints в дополнительное окно редактора кода (в некоторых версиях раздел Сообщений компилятора-Compiler Messages ~может быть вынесен на отдельную вкладку).
Рассмотрение остальных вкладок диалога не входит в задачи этой
книги.
Следующим шагом в изучении Delphi будет написание простого приложения. Если вы успели закрыть Delphi, то запустите его снова. Создастся новое приложение. Разместите на форме компоненты Label
- 13 -
Азбука Delphi: программирование с нуля
и Button. Разместить их можно как угодно. Если они будут находиться непосредственно на форме, то программа будет работать. Щелкните один раз по объекту Label на форме в Инспекторе объектов отобразятся свойства этого объекта (рис. 1.5).
Примечание
Для того чтобы бьшо легче найти нужный элемент в библиотеке, все они снабжены всплывающими подсказками: если курсор навести на пикто
грамму и задержать на пару секунд, появится всплывающая подсказка
{Hint), в которой отображено название соответствующего компонента.
Как видно на рисунке, основное поле Инспектора объектов представ-
ляет собой таблицу из двух столбцов. В левом столбце находятся названия свойств, а в правом - их значения. Убедитесь, что в поле значения свойства Name объекта Label находится надпись Label 1 (см. рис. 1.5). Если нет добавьте ее. Сотрите надпись в поле значения свойства Caption (заголовок). На этом работа с данным объектом временно завершена. Впоследствии вы сможете изменить шрифт и содержание надписи, а также еще некоторые свойства. (Основные свойства наиболее часто используемых компонентов рассматриваются в последней главе.)
Рис. 1.5. Вид Инспектора объектов при выделении объекта Label
Щелкните на любом месте формы - содержимое Инспектора объ
ектов изменится. Теперь он будет показывать свойства самой формы.
Щелкните на кнопке, которую вы
разместили на форме. С помощью Инспектора объектов измените зна
чение свойства Caption на что-либо осмысленное, например, «Пуск!».
В начальный момент там должно было находиться значение Buttonl.
Примечание
При задании имен элементов (N ame) недопустимо использовать ключевые
слова Object Pascal, однако при задании заголовков (Caption) вы можете писать все что угодно.
Наверняка вы заметили, что в ок
не Инспектора объектов расположе
ны две страницы: Properties ( Свойства) и Events (События). До сих пор работа шла с первой страницей: вы определяли свойства объектов. Убедитесь, что в Инспекторе отображают-
- 14 -
Глава 1. Знакомство со средой программирования
ся свойства кнопки Buttonl и переключитесь на страницу Events (см. рис. 1.5). Она выглядит практически так же, как и страница Properties, за исключением того, что в левом столбце находятся названия возможных событий, а в правом - названия назначенных обработчиков. Проектируемое приложение должно реагировать на нажатие кнопки: событие OnClick.
Дважды щелкните в поле справа от этого события. Открылся редактор кода. В коде произошли некоторые изменения по сравнению с тем, как он вьШIЯДел в момент создания нового приложения (см. рис. 1.2). Изменилось описание типа TF опn 1 ~ в нем добавилось объявление процедуры обработки нажатия кнопки:
procedure ButtonlClick(Sender: TObject);
В разделе implementation появился шаблон определения этой процедуры:
procedure TForml.ButtonlClick(Sender: TObject); beqin
end;
Курсор установлен в этом шаблоне, и сразу после перt·хf\Да в Окно редактора кода можно начинать писать текст обработчика. Поместите в шаблон следующую строку:
Labell.Caption :~ 'Hello World!';
Изначально поле Caption могло содержать какую-либо строку, например «Press the button!». Тогда эта строка находилась бы на форме до нажатия кнопки.
Если, поставив точку после Label 1, вы ненадолго остановитесь, то появится подсказка, в которой будут перечислены все методы и
свойства объекта Labell. Если подсказка не появилась, например, курсор был перемещен от надписи и потом возвращен на нее, то на
жатие Ctrl +Пробел вызовет эту же подсказку. Далее можно просто выбрать из списка нужный пункт. Напечатав две-три первые буквы нужного свойства или метода, можно ощутимо сократить количество пунктов в подсказке и, как следствие, время поиска нужного. С по
мощью стрелок t и i выделите нужный пункт и нажмите Enter. Выбранный пункт автоматически будет записан после точки.
Запустите программу на выполнение, выбрав пункт меню Run ~ Run либо нажав быструю кнопку с зеленой стрелкой или F9. Если вы все сделали правильно, то на экране появится окно вашего приложения
(рис. 1.6 а), выглядящее практически так же, как и форма (отличить их проще всего по наличию сетки на форме и отсутствию ее в окне
- 15 -
Азбука Delphi: программирование с нуля
а б
Рис. 1.6. Окно программы «Hello World!»: а - до нажатия, б - после нажатия кнопки
работающей программы). Нажмите кнопку. На месте, где вы расположили объект Labell, появилась надпись «Hello World!» без кавычек (рис. 1.6 б).
Закройте окно приложения, нажав крестик в углу либо выбрав пункт Run ~ Reset в меню. Второй способ очень полезен, если программа зациклилась и нет возможности закрыть ее непосредственно
с формы. В остальных случаях к этому методу лучше не прибегать. Сохраните проект в отдельной папке.
В этой главе в общих чертах была рассмотрена среда программи
рования Delphi. Это очень важный раздел, поскольку разработчик приложений в Delphi сталкивается с ней постоянно в ходе работы. Если сохранять каждый проект в отдельной папке, ваше приложение · можно будет легко переносить на другой компьютер, также будет
проще его удалить, если оно вам больше не нужно.
Контрольные упражнения
1. Допишите рассмотренную программу так, чтобы заголовок кнопки «Пуск!» по щелчку на ней заменялся на «Нажми еще!».
2. Измените настройки приложения таким образом, чтобы при его запуске на форме отображалась надпись «Привет!».
3. Поместите на форму еще несколько объектов. Сделайте так, чтобы все их заголовки изменялись при нажатии кнопки «Пуск!».
rпава 2. Object Pascal
В предыдущей главе вами было написано приложение, выводящее одну надпись в диалоговом окне. При всей своей видимой простоте оно
выполняет очень много задач: создание окна программы, создание объ
ектов кнопки (Button) и метки (Label), отслеживание положения курсора мыши, определение координат курсора, динамическое изменение
свойства объекта Label, удаление всех объектов, включая саму форму, из памяти при завершении работы приложения. Большинство из этих действий берет на себя операционная система, и разработчик может даже не задумываться над многими процессами, происходящими во
время работы программы. Применительно к данному приложению вся
работа программиста состояла в том, чтобы разместить два экземпляра компонентов из библиотеки VCL на форме и написать строку кода в автоматически созданной процедуре обработки события.
Рассмотрим теперь более подробно, как выглядят тексты кода созданного приложения. В общем случае проект состоит из нескольких файлов, которые, по умолчанию, имеют следующие имена:
Projectl .dpr - главный файл проекта.
Unitl .pas - файл, автоматически появляющийся в начале работы. Такой файл называется модулем и по умолчанию имеет имя Unit* .pas, где вместо звездочки ставится номер модуля в проекте. В модулях со
храняется написанный программистом код, первоначальный вид кото
рого можно увидеть на рис. 1.2. В примере приложения из предыдущей главы в модуль дописывался обработчик события. Таким образом, каж
дой форме в приложении соответствует свой модуль с обработчиками событий, специфическими типами, переменными и подпрограммами. Однако могут существовать и модули, не привязанные к какой-либо
конкретной форме: в них находятся служебные процедуры. Например, в некоторых специфических случаях полезным оказывается написать математические подпрограммы самостоятельно, расширив при этом
возможности математического аппарата Object Pascal (относительно скудные), поместить их в отдельный модуль (к примеру, math.pas) и использовать в других модулях по мере необходимости.
Unitl .dfm- файл, в который сохраняется информация о внешнем виде главной формы.
Projectl.res -· файл, содержащий иконку для проекта, тоже создается автоматически.
Projectl .opt (или Projectl .cfg в зависимости от версии Delphi) является текстовым файлом для сохранения установок, связанных
- 17 -
Азбука Delphi: программирование с нуля
с данным проектом. Например, в нем хранятся установленные вами директивы компилятора.
Все эти имена задаются автоматически для вновь создаваемого
проекта. Их можно и нужно изменить на что-то осмысленное в процессе работы над приложением и при его сохранении на диск.
После компиляции проекта появляются файлы с расширениями: .dcu- скомпилированные модули; .ехе - собственно исполняемый файл. Непосредственно код программы расположен в двух файлах: голов
ном файле программы (расширение .dpr) и файле модуля (расширение .pas). Головной файл проекта стоит полностью оставить на совести Delphi, так же как и файл с параметрами формы: если что-то надо будет в ней изменить, то лучше это сделать непосредственно из IDE.
Программист, как правило, участвует только в написании модулей проекта. Delphi создает заготовку модуля, включает в нее описание формы и всех объектов, на ней размещенных, обеспечивает связь с библиотеками и стандартными модулями. Программист в основном пишет обработчики событий компонентов и, при необходимости, объявляет различные переменные, задает значение констант, создает описание типов, вспомогательных функций и процедур.
Ниже приведен текст модуля нашего проекта. В комментариях указано, что и куда в этот текст может добавить программист.
unit Unitl; interf ace {список подключаемых модулей}
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type TForml ~ class(TForm) //класс формы
Buttonl: TButton; Labell: TLabel; procedure ButtonlClick(Sender: TObject); private { Private declarations } {Объявление переменных и методов, включаемых в класс формы,
но недоступных напрямую для других классов, описанных вне
данного модуля проекта.}
puЬlic
{ PuЬlic declarations } {Объявление переменных и методов, включаемых в класс формы,
и доступных для других классов.}
end; - 18 -
Глава 2. Object Pascal
var Forщl: TForrnl; // объект класса TForm - ваша форма
{Объявление типов, констант, переменных, функций и процедур,
доступных из других модулей проекта, но не включенных
в класс формы. } implementation //реализация модуля {$R *.dfrn} //ключи компилятора
{Предложения uses, объявления типов, констант, переменных,
к которым не будет доступа из других модулей. Тут же должны быть
реализации всех объявленных в разделе interf ace функций и процедур, а также могут быть реализации любых вспомогательных,
не объявленных ранее функций и процедур.}
procedure TForrnl.ButtonlClick(Sender: TObject); //обработчик нажатия кнопки
Ьegin
Labell.Caption .- 'Hello World!'; end; end.
Модуль начинается со слова unit, после которого пишется имя модуля, совпадающее с именем файла, в ко:rором был сохранен модуль, в данном случае: Unit1. Текст модуля состоит из двух разделов: interface -интерфейс модуля и implementation - реализация модуля. Все, что вы и Delphi поместите в первый раздел, может быть с учетом атрибутов видимости использовано другими модулями проекта. Размещенное в разделе implementation недоступно другим модулям проекта.
Зарезервированные слова private и puЫic определяют область видимости глобальнЬ1х переменных и подпрограмм. Подробнее о директивах puЫic, private и protected можно будет прочесть в главе 4.
В раздел реализации можно включить функции, процедуры, а также переменные и константы, используемые в обработчиках событий данного модуля.
Как видите, в текст модуля включены пояснения - комментарии.
Комментарии следует помещать в текст вновь создаваемых программ в количестве, достаточном для последующего понимания особенностей ее работы. Естественно, не стоит писать комментарий для каждой строки.
Для того чтобы понять, как работает ваше приложение, рассмотрим текст головного файла проекта Projectl .dpr:
proqram Projectl; uses
Forms, U n i t 1 i n 'U n i t 1 . р а s ' { F о rm 1 } ; {$R *.res}
- 19 -
Азбука Delphi: программирование с нуля
begin Application.Initialize; Application.CreateForm(TForrnl, Forml); Application.Run;
end.
Как видите, здесь все довольно просто. Сначала происходит подключение файла форм и модуля проекта, а потом начинается собственно работа приложения, состоящая из трех фаз: инициализации, создания формы и запуска приложения.
Все. На этом линейная определенность в работе приложения заканчивается. Что будет происходить дальше, решается на этапе работы программы, а не на этапе разработки. Эта особенность является
одной из характерных черт, присущих всем Windоws-приложениям: последовательность работы приложений в гораздо большей степени зависит от действий пользователя, нежели в программах, написанных
дляМS-DОS.
Допустим, вы открыли текстовый редактор и печатаете в нем текст. В этом случае программа .выполняет один набор процедур: вывод текста на экран, автоматическую проверку орфографии, авто
форматирование текста и прочие полезные фоновые действия. Вы решили, что пора прерваться, и сохраняете набранный текст, для того чтобы в дальнейшем вернуться к нему и продолжить работу. В этом случае текстовый редактор начнет выполнять ряд действий, предпи
санньiх ему разработчиком, но это определенно будут уже не те же
· операции, что выполнялись при наборе текста. Таким образом, пользователь определяет, чем в данный момент
должна заниматься программа, и задает основные направления дей
ствий. Каждое нажатие на клавиши клавиатуры, кнопки мыши, пере
мещение мыши является системным событием, которое интерпретируется ядром Windows и превращается в сообщение, которое поступает к соответствующим объектам действующих проrрамм. Например,
щелчок левой кнопки мыши на кнопке Save в панели управления текстового редактора Превращается в сообщение, поступающее программе Текстовый редактор. Там оно в свою очередь вызывает запуск определенной последовательности действий - процедуры (procedure ). И именно эта процедура содержит инструкции, которые будут выполняться программой.
Однако сообщение в программу может поступить не только будучи вызванным действиями пользователя. Сообщение может прийти от системного таймера (обычная ситуация для игр: основной цикл большинства игр запускается именно сообщением от таймера, которое
- 20 -
Гnава 2. Objett Pascal
Рис. 2.1. Вид окна приложения «Две кнопки» сразу после запуска
генерируется с определенной периодичностью), от другой программы, от периферийного устройства (принтер передает сообщение о готовности к печати и пр.). Как результат мы имеем непрерывный поток сообщений, передаваемых во всех направлениях.
Сложно? Запутанно? Только на первый взгляд, даже если вы имеете опыт программирования на языках структурного программиро
вания, таких как Pascal или С. На самом деле все довольно очевидно и прозрачно, и вы быстро освоитесь с такой особенностью программирования под Windows. Важным здесь является то, что программы под Windows выполняются в последовательности, в общем случае не определенной на этапе разработки. Иногда такую программу называют событийно~управляемой, а программирование, соответственно,
событийно-ориентированным. Рассмотрим еще один пример: окно приложения содержит две
кнопки (Buttonl, Button2) и метку (Labell) (рис. 2.1). Метка содержит число, которое увеличивается на 1 каждый раз, когда по:Льзователь нажимает одну кнопку, и обнуляется каждый раз, как пользователь нажимает вторую кнопку.
Для создания этого приложения нужно поместить на форму два объекта Button и объект Label, дать кнопкам осмысленные заголовки (Caption), а заголовку метки задать значение «0» (нуль). Затем нужно создать обработчики событий OnClick. Это можно сделать так же, как и в предыдущем примере, или просто двойным щелчком по самой
кнопке.
Примечание Двойной щелчок по объекту, расположенному на форме, вызывает диа
лог редактирования одного из его свойств либо создает шаблон определенного события, это свойство или событие предопределено для каждого класса. Как правило, это то свойство или метод, которые наиболее часто используются при разработке приложений. В случае кнопок это
событие OnClick.
- 21 -
Азбука Delphi: программирование с нуля
В шаблон обработчика нажатия первой кнопки нужно поместить строчку:
Labell.Caption := IntToStr(StrToint(Labell.Caption) + 1);
Столь громоздкая формула вызвана тем, что поле Caption содержит текстовую информацию. Для того чтобы работать с ней как с
числом, нужно произвести преобразование текста в целое число. Этим
и занимается функция StrTolnt(). В качестве аргумента она принимает строку текста, которую затем анализирует на предмет возможнос
ти интерпретации как числа, и, если строка содержит только разре
шенные символы, сама функция приобретает значение, записанное
в этой строке, но уже как число, а не как текст. С результатом можно
выполнить арифметические операции (прибавить единицу). Функция lntToStr() производит обратное преобразование. Оно более простое, так как любое число может быть интерпретировано как текст, но, тем не менее, необходимое, поскольку свойству строкового типа может быть присвоено только строковое значение. О преобразовании типов будет подробнее рассказано в главе 3.
Теперь вы можете запустить приложение и опробовать кнопки
в работе. Если вы все правильно сделали, первая кнопка увеличива
ет число на единицу, но вторая пока просто нажимается, не приводя
к каким-либо видимым изменениям. Завершите работу программы.
В шаблон обработчика нажатия второй кнопки нужно поместить строку:
Labell.Caption := '0';
Нуль здесь взят в апострофы, чтобы указать компилятору, что эту ве.Ji(ичину нужно рассматривать как строковые данные, а не как чис
ло. Эта же строка без апострофов вызвала бы ошибку периода компиляции с сообщением о несовместимости типов.
Мы рассмотрели довольно простую программу, отражающую, тем
не менее, принцип, о котором шла речь выше. Модуль проекта содержит
две процедуры: TForm1 .Button1 Click и TForm1 .Button2Click. Содержимое головного файла проекта с точностью до имен модуля и проекта такое же, как в предыдущем примере, т. е. определенность последова
тельности действий заканчивается сразу после команды Application. Run. И до стадии функционирования проекта неизвестно, в каком порядке будут выполняться эти процедуры и будут ли выполнятся вообще, ведь пользователю ничего не мешает просто закрыть приложение
сразу после запуска. Закрытие приложения, кстати, тоже событие, на которое программа может реагировать предопределенным образом.
- 22 -
Глава 2. Object Pascal
Итак, механизм следующий: в приложение поступает сообщение
о возникновении определенного события с параметрами (где конк
ретно, на каком объекте какого именно окна приложения произошло
это самое событие). По умолчанию вызывается стандартный систем
ный обработчик этого события. Однако, если действий, выполняемых системным обработчиком события, недостаточно, а обычно так и
бывает, то разработчик определяет свою процедуру, вызываемую при возникновении конкретного события, - собственный обработчик.
Остановимся чуть подробнее на событиях. Код в рассмотренных
примерах довольно прост, и часто даже нет необходимости задумываться о том, что написанная процедура обработчика является ответом
на сообщение о событии, посланное операционной системой. Но зачастую это знание оказывается весьма полезным. В сообщении помимо собственно уведомления о событиц передается и некоторая связанная с ним информация. Например, при возникновении события
«Нажата кнопка мыши» приложение получает координаты точки, в которой это произошло. Эти координаты могут быть использованы в обработчике события OnM01JseDown:
procedure TForml.FormMouseDown(Sender: TObject;
beqin
Button: TMouseButton; Shift: TShiftState; Х, У: Integer);
Canvas.TextOut(X, У, IntToStr(X) + ',' + IntToStr(Y)); end;
Результат работы такой программы может выглядеть, например,
так, как показано на рис. 2.2. Примечание
Для работы этого и следующего примера нужно создать новое при
ложение и задать обработчики событий OnMouseDown и OnKeyDown формы.
Также можно создать обработчик для события OnKeyDown (нажата клавиша клавиатуры), который будет реагировать на вводимую с клавиатуры информацию.
procedure TForml.FormKeyDown(Sender: TObject; var Кеу: Word; Shift: TShiftState);
begin ShowMessage(Chr(Key)); //вывод окна сообщения
end;
Пример работы этой программы показан на рис. 2.3.
- 23 -
Азбука Delphi: программирование с нуля
Рис. 2.2. Результат работы процедуры OnMouseDown
Рис. 2.3. Результат нажатия кнопки G
Приведенные здесь примеры имели целью лишь продемонстри
ровать особенности событийно-ориенгированного программирования и раскрыть суть понятия «событие». Сам процесс написания кода
обработчиков событий подробнее рассмотрен в следующих главах.
:Контрольное упражнение
Модифицируйте код второго приложения таким образом, чтобы при нажатии кнопки мыши выводилось сообщение, показывающее, в какой четверти окна приложения оно было произведено. Для определения размеров окна приложения во время работы программы воспользуйтесь свойствами Width и Height формы.
rпава 3. Уч1мс1 nроrрамм1ровать
В настоящей главе более подробно рассмотрены базовые понятия язык~.программирования, используемого в Delphi: Object Pascal. Тем, кто занимался программированием на Pascal, можно эту главу пропустить. Тем, кто программировал на каких-либо других языках, стоит бегло ее просмотреть, обращая внимание на примеры, для того чтобы освоить синтаксис языка. В основном же эта глава для тех, кто не име
ет или почти не имеет опыта написания собственных программ.
Как и любой язык, язык программирования начинать изучать лучше с алфавита. Алфавит языка Pascal включает буквы, цифры, специальные символы и пробелы. Буквы языка- латинские буквы от 'а' до 'z' и от 'А' до 'Z ', сюда же относят и знак подчеркивания _. Различий между прописными и строчными буквами нет (MyVar, МYVAR и даже m Yv AR компилятор воспримет как имя одного и того же объекта, однако первый вариант читается лучше, и использование подобного
шаблона говорит о более высоком профессионализме разработчика). Специальные символы языка следующие:
t-*/=, .' ':;<>[]{}()А@$#
К специальным символам относят также некоторые пары символов,
а именно:
<> <= >= := (~ *)
При компиляции проекта эти пары символов воспринимаются как
одно целое, если они не разделены пробелами. Переменная~ одно из базовых понятий языка программирования.
Под переменной понимают ячейку памяти, соответствующую опре
деленному имени. То есть, задавая в проекте переменную, мы даем указание компилятору выделить область памяти определенного раз
мера в доступном сегменте и поставить имя переменной в соответст
вие данной области. В дальнейшем с переменной в зависимости от
ее типа можно выполнять те или иные операции (математические, логические и т. д. ). Например, строка кода
а := 4;
поместит в переменную а число 4 (символ := является командой присваивания, т. е. переменной, стоящей в левой части выражения, будет
присвоен результат выражения, расположенного справа от знака при
сваивания). Именно так и следует воспринимать работу с перемен-
- 25 -
Азбука Delphi: программирование с нуля
ными. Программисту, как правило, нет необходимости помнить
о структуре памяти и способе хранения переменных в ней. Наряду с переменными существует еще один способ хранения, при
меняемый для констант - данных, которые не должны быть изменены
в ходе работы программы. Описываются они следующим образом:
const <Name> = <Value>;
Значение константы может быть задано либо непосредственно, либо
как выражение с участием других констант, значение которых известно
заранее. В следующем примере константы обьявлены локальными, об
ласть их видимости ограничивается данным модулем проекта:
type TForml = class(TForm) {описание свойств и методов класса}
private {Private declarations)
puЬlic
{PuЬlic declarations} end; const
Minimum = О; Maximum = 100; Middle = (Maximurn - Minirnum) div 2; CharsCount = Ord('Z') - Ord('A') + 1;
var
В приведенном примере показано, что в выражениях, описывающих константы, могут применяться операторы и функции языка.
Тип константы подбирается автоматически. Если выражение представляет собой строку символов, то принимается строковый тип
String, если длина строки при этом равна 1, то принимается символьный тип Char. Если переменная представляет собой дробное число, то тип интерпретируется как Extended. Если же значение представлено целым числом, то принимается один из целочисленных типов,
минимальный из возможных по размеру занимаемой памяти. (Под
робности можно посмотреть в Help на закладке, посвященной настоящим константам: true constants.)
Ввиду того что существуют «настоящие константы» (true constants ), должны существовать и «не настоящие». Однако бесполезно искать в справке материалы по false constant, поскольку вторым видом констант являются типизированные константы (typed constants ). Отличаются
- 26 -
Глава 3. Учимся программировать
они тем, что могут, в отличие от настоящих, содержать массивы, запи
си и указатели, а также не могут участвовать в выражениях, определяю
щих значение других констант. Другой их особенностью является то,
что при установке ключа компилятора {$J+} они начинают вести себя как обычные переменные с начальным значением. По умолчанию задан ключ компилятора { $J-}, при котором типизированные константы ведут себя как переменные, доступные только для чтения. Ключи компиля
тора записываются сразу после зарезервированного слова implementation или в файле конфигурации проекта (см. начало главы 2).
Задается типизированная константа практически так же, как настоя
щая, но с указанием типа:
const <Name>: <Туре> = <Value>;
Например, конструкция
const Min = 2; Мах: Integer = Min + 2;
окажется верной, а вот такая:
const Мах: Integer = 2; Middle: Integer = Мах - 6;
вызовет ошибку.
Переменная и константа могут содержать целые числа, дробные числа, строку текста или отдельный символ, могут даже принимать зна
чение true или false для логических переменных. Однако вся информация хранится в памяти компьютера только как последовательность нулей и
единиц. Для того чтобы указать компилятору, как интерпретировать данные по адресу, на который указывает имя переменной, нужно определить
тип переменной. Также тип переменной определяет ра3мер ячейки па
мяти (в байтах или битах), в которой расположено значение переменной. Рассмотрим наиболее часто используемые типы.
3.1. Типы данных Целочисленные типы переменных предназначены для хранения
числа без дробной части. Они используются в вычислениях, которые не подразумевают возможности возникновения дробного результа
та (сложение, умножение, вычитание, взятие остатка от деления,
частного от такого деления и т. д.). Существует около 10 целочисленных типов. Наиболее часто применяемые приведены в табл. 3 .1.
- 27 -
Азбука Delphi: программирование с нуля
Таблица 3.1
Тип Диапазон значений Размер, байт Наличие знака
lnteger -2 147 483 648 ... 2 147 483 647 4 Знаковый
lnt64 -2вз."26з -1 в Знаковый
Byte 0 ... 255 1 Беззнаковый
Word 0."65 535 2 Беззнаковый
Они отличаются размером отводимой для хранения данных памяти и интерпретацией старшего бита: для знаковых величин в старшем бите кодируется знак, а в беззнаковых (unsigned) все биты используются для записи модуля положительного числа.
Объем памяти, отводимый под хранение данных, определяет мак
симальное по модулю число, которое можно записать в эту перемен
ную.
Целочисленным переменным можно присваивать как десятичные,
так и шестнадцатеричные числа. Для этого перед шестнадцатеричным
числом нужно поставить$. Например:
var i, j: Integer; beqin
1 :"'° 125; j := $70; {В шестнадцатеричном представлении это число
эквивалентно 125 в десятичном.}
end;
В примере сначала в переменную i помещается десятичное число 125, а потом в переменную j помещается то же самое число, но в шестнадцатеричном представлении. Так как компьютеру все равно,
в какой системе счисления (десятичной или шестнадцатеричной)
записана константа в выражении, то содержимое переменных по
завершении программы будет одинаково.
Вещественные, или дробные, типы данных предназначены для
хранения чисел с плавающей точкой. Они используются в математиче
ских оnераЦиях. В .каждом конкретном случае программист определяет, каким типом переменных пользоваться. Целочисленные пере
менные отнимают у процессора меньше времени и требуют меньше памяти, чем вещественные. Поэтому использовать вещественные пе
ременные стоит только там, где это действительно необходимо. Существует несколько типов дробных переменных, отличаются
они только размером в байтах. Все типы знаковые, в табл. 3 .2 приведены наиболее распространенные из них.
- 28 -
Глава 3. Учимся проrраммировать
Таблица 3.2
Тип Диапазон значений Максимальное количество
Размер, байт цифр в числе
Real 5,0· 1о-324 ••. 1,7-10308 15-16 8 Single 1,5· 10-45 •.• 3,4-1038 7-8 4
DouЫe 5,О· 10-324 .•• 1, 7.1 озоs 15-16 8
Extended з,5.1 о-4951 ... 1, 1.1 о4эз2 19-20 10
Принципиально, что дробные и целые числа, равные по величине, не эквивалентны.
var i: Integer; j: Real;
beqin i := 10; ] := 10;
end;
Несмотря на то, что в переменные i и j помещается одно и то же значение, переменная i содержит чис_ло 1 О, а переменная j - число 10,0. Чтобы сделать эти переменные эквивалентными, нужно округлить значение J.
Символьные переменные интерпретируют содержимое памяти
как текст. Символьные данные - это цепочка чисел, каждое из ко
торых представляет собой порядковый номер символа в специальной
таблице. -В Windows используется таблица ANSI. Delphi берет эту таблицу прямо из операционной системы, так что количество символов и их расположение зависит от ОС. В таблице ANSI хранятся, наряду со спецсимволами, только русский и английский алфавиты. Однако алфавитов, как и языков на Земле, куда больше двух. Из этих
соображений ввели поддержку UNICODE (16-битная таблица). В ней первые 256 значений совпадают с таблицей ANSI, а остальные являются специфичными. Для поддержки lТNICODE в Delphi введен тип
· WideString. Используемые в Delphi основные строковые типы представлены
в табл. 3.3. Таблица 3.3
Тип Максимальная длина Примечание
ShortString 255 символов -AnsiString 231 символов ANSI
WideString 230 символов UNICODE
- 29 -
Азбука Delphi: программирование с нуля
Также, для обеспечения совместимости с более ранними версиями, существует тип String. Именно он используется в примерах книги, где необходимы переменные строкового типа:
va:r
Str: String;
beqin Str . - 'Hello ! ' ;
end;
Возможно также указание длины строки при объявлении пере
менной:
va:r
S: String[100];
Число 100 в квадратных скобках после типа String показывает максимальное количество символов в строке. Строку можно рассматривать как последовательность переменных символьного типа, работать можно либо со строкой целиком, либо с конкретным символом, для этого нужно указать номер символа, к которому необходимо обратиться:
S : = 'Hello ! ! ! ' ; S[l] :== 'S';
Теперь в переменной S содержится строка «Hello! ! 1>> без кавычек. Нумерация элементов в строке начинается с единицы. Существу
ет и нулевой символ, но к нему не рекомендуется обращаться напрямую. Он содержит в себе длину строки, и для того, чтобы прочесть или записать его значение, существуют функция Length и процедура SetLength соответственно.
Логический, или Булев, тип (по аналогии с булевой алгеброй, оперирующей логическими выражениями). Физически переменные
этого типа занимают один бит и, как следствие, могут принимать
лишь два значения: О или 1. Для удобства программирования эти значения заменены на false (Ложь) и true (Истина), соответственно. Переменные логического типа применяются при составлении логи
ческих выражений, которые подробно будут рассмотрены ниже, здесь приводится лишь краткий пример:
var bull: Boolean; // Объявить логическую переменную bull i: Integer; //и целочисленную переменную -
- 30 -
begin bull .- true; i : = 5;
if bull = true then i := 1
else i . - о;
end;
Глава 3. Учимся программировать
При выполнении программы в переменную i сначала будет помещено число 5, а затем, поскольку выполнено условие равенства логической переменной bull значению true, - число 1.
Такая формулировка условного оператора ifнe единственная и не
самая лаконичная, зато самая очевидная, подробнее этот вопрос будет
рассмотрен ниже в настоящей главе.
Тип список создается программистом. Переменная этого типа
может принимать только одно из оговоренных значений. В чем-то это
похоже на логический тип, переменные которого принимают только
два значения. Описание типа происходит следующим образом:
type typeName = (vall, ... , valn);
Например:
type TSound = (Click, Clack, Clock);
Переменная задается так же, как и переменная любого другого
типа:
var FirstSound:TSound;
Тип диапазон является усечением какого-либо существующего
простого типа, кроме вещественного. Диапазон задается указанием
нижней и верхней границ:
type <TypeName> = Low .. Hi;
Например:
type SomeNumbers = -128 .. 127; Caps = 'А' .. 'Z';
Переменные типа «диапазон» можно создавать в разделе объявления переменных без предварительного создания самого типа.
var SomeNum: 1 .. 500;
- 31 -
Азбука Delphi: проrраммирование с нуля
Переменная такого типа может принимать значения только внутри диапазона или на его границах. При попытке присвоить ей какоелибо другое значение возникнет ошибка. Например, код
type Percentile ~ 0 .. 99; var i: Percentile;
i 100;
приведет к ошибке совместимости типов.
Функции преобразования типов
Часто при использовании данных возникает необходимость изменить их тип: округлить, изменить основание системы счисления,
перевести данные из строкового типа в вещественный, из логичес
кого в строковый.
Такая необходимость возникает из-за того, что типы отличаются друг от друга объемом занимаемой памяти, а большинство команд принимают в качестве параметров только значения вполне опреде
ленного типа. В случае несоответствия типов формальных и фактических параметров подпрограмм компилятор выдает сообщение об ошибке. Как правило, этого можно избежать, если использовать команды преобразования типов. В Delphi их содержится несколько десятков. Наиболее часто применяемые приведены ниже.
function StrToint(const S: Strinq): r;
преобразует строку S, в которую записано целое число, в целочисленный тип.
function StrToBool(const S: String): Boolean;
возвращает true, если в S содержится число не равное нулю или слово 'TRUE'; false возвращается, если в S содержится 'О' или 'FALSE'. Во всех остальных случаях функция возвращает сообщение об ошибке.
function St~ToFloat(const S: Strinq): Extended;
преобразует строку S в вещественное число. Строка должна содержать знак, цифры и десятичный разделитель в порядке, характерном для записи десятичных дробей. Также строка может содержать ман
тиссу, состоящую из знака, буквы е (Е) и целого числа. Пробелы в начале и в конце строки игнорируются. Если S имеет другой формат, функция возвращает сообщение об ошибке.
- 32 -
Глава З. Учимся программировать
Наравне с этими функциями существуют функции обратного пре
образования:
function IntToStr(Value: Integer): Strin9; function BoolToStr(B: Boolean): String; function FloatToStr(Value: Extended): String;
3.2. Выражения, операторы и операнды После рассмотрения типов переменных, наиболее часто исполь
зуемых в программировании на Delphi, остается всего один шаг до начала написания программ с простыми обработчиками событий. Нужно подробно рассмотреть тело подпрограммы, т. е. ту часть, ко
торую и предстоит писать программисту (остальное пока стоит предоставить выполнять IDE).
Для начала необходимо ввести понятие выражение. Оно сходно с математическим понятием и может быть представлено как сово
купность данных (операндов) и символов действий, которые предпи
сывается с этими данными выполнить (сложить, вычесть, умножить
и т. д. ), - операторов. Например, выражение
ь + с
предписывает сложить переменные Ь и с. В данном случае Ь и с явля
ются операндами, а знак«+» является оператором. Результат работы этого выражения может быть включен в качестве операнда в другое
выражение, например в выражение с оператором присваивания:
а := Ь + с
Порядок выполнения операций в подобных выражениях аналоги
чен принятому в математике. Например, в выражениях с операторами
присваивания сама операция присваивания выполняется последней
после того, как выполнены все остальные операции, находящиеся
справа от оператора :=. Слева от этого оператора должна находиться переменная соответствующего типа, в которую помещается результат
вычислений.
Еще одним показ,ательным примером операторов является услов
ный оператор, примеры которого встречались выше. Если перевести
его составляющие дословно, получится следующая конструкция:
ЕСЛИ (выражение) ТО (действия)
IF (expression) THEN (statements)
- 33 -
Азбука Delphi:· программирование с нуля
Подробное описание условного оператора приведено ниже в этой же главе.
3.3. 11роцедуры и функции Важным в программировании является понятие подпрограммы.
Подпрограммы используются в языках программирования с целью
уменьшения трудоемкости написания программ путем многократно
го использования одного и того же фрагмента кода с возможностью передачи в него перед выполнением различных значений исходных
данных. Подпрограммы в Object Pascal делятся на две группы: функции и процедуры.
Функции (function) выполняют предписанные им действия по преобразованию исходных данных и изменению параметров работы приложения и принимают некое значение в результате своей работы ·таким
образом, что функции можно применять в математических выражениях, логических операторах илй в качестве аргументов при вызове других
подпрограмм. Говорят, что функция «возвращаеп> некое значение.
Процедуры (procedure) отличаются от функций только тем, что не могут принимать значения.
Все обработчики событий в Delphi представляют собой процедуры. Программист пишет в основном именно их, поэтому под термином
«подпрограмма» будем понимать именно процедуру или функцию,
которую программист пишет в модуле (unit) проекта. Так как, собственно говоря, код самого приложения находится в головном файле
проекта и «пишется» IDE. При описании заголовки процедур и функций выглядят следую
щим образом:
procedure <РrосNаmе>(<список формальных параметров>); function <FuncName>(<cпиcoк формальных параметров>):
<тип возвращаемого результата>;
При вызове подпрограммы записывается только ее имя и список
фактических параметров, соответствующих списку формальных параметров, заданному при описании подпрограммы.
Формальными называются параметры, которые заданы при опи
сании подпрограммы, они не имеют конкретного значения, но при
надлежат к определенному типу. В процессе описания подпрограммы
они могут быть использованы в выражениях в качестве имен перемен
ных. Сами же значения переменных будут подставлены на их место
при вызове подпрограммы, когда список формальных параметров
будет заменен списком фактических параметров.
- 34 -
Глава 3. Учимся nроrраммировать
Список формальных параметров может содержать зарезервированные слова const и var. Например:
procedure MyProc(Vall: Real; var Val2: Real; ValЗ: Boolean; const Val4: I r; Val5:Real);
В этом примере Val 1, ValЗ и Val5 обычные переменные. Изме-нение их в пределах процедуры MyProc не вызовет изменений переменных, которые были использованы в качестве фактических параметров при вызове, поскольку при вызове в подпрограмму передается
копия значения. Val2 - изменяемая переменная, при таком определении в подпрограмму передается не значение параметра, а адрес его
расположения в памяти. Как следствие, любые изменения с формальным параметром в процедуре MyProc сказываются на переменной, использованной в качестве фактического параметра при вызове. Val4 -константа. Механизм передачи значения здесь тот же, что и в преды
дущем случае, однако попытка изменить значение формального параметра вызовет ошибку.
Второй вариант отличается от первого тем, что передача адреса
происходит быстрее, чем передача самого значения, если речь идет
не о простой переменной, а, например, о массиве.
Несколько слов об области видимости переменных. Переменные в Delphi могут быть локальными или· глобальными. Глобальные переменные объявляются после слова var в разделе interface. Они доступны во всех процедурах и функциях модуля, а видимость их в других модулях зависит от того, в каком разделе (private или puЫic) они объявлены. Локальные переменные объявляются после слова var в подпрограмме. Такие переменные видимы только в пределах данной под
программы и недоступны в других процедурах или функциях. Ввиду этого имена переменных разных областей видимости могут совпадать,
и ;_это не вызовет проблем в работе. Например, для счетчика циклов
в примерах данной книги используется, как правило, переменная i: Integer. Такая переменная может быть объявлена в нескольких подпрограммах (локальная) и в разделе interface (глобальная). В тех подпрограммах, где нет собственного описания переменной i, при ее упоминании будет подразумеваться глобальная переменная, а в тех, где такое
описание есть, - локальная, и получить доступ к глобальной переменной с тем же именем будет невозможно.
Любая программа состоит из последовательности операторов, исполняющихся один за другим в порядке прочтения. Отклонения от
этого порядка исполнения создаются циклами и условными операто
рами с участием операторных скобок. Простой оператор представля
ет собой вызов подпрограммы, написанной самим разработчиком
35 -
Азбука Delphl: nроrраммирование с нуля
приложения или входящей в стандартные модули поставки Borland Delphi, либо математический оператор:
а : 1; / / математический оператор
Ь :=а + 6; // математический оператор Pow(z, а, 3); //вызов процедуры с участием параметров z 1 а, 3 с Sqrt(4); {Взятие квадратного корня из четырех и помещение
результата в переменную с.}
Последовательность операторов может расцениваться как один составной оператор, если они взяты в так называемые операторные
скобки:
begin
end;
Операторные скобки оказываются весьма полезными при работе циклов или усЛовных операторов, но об этом позже. Код каждой подпрограммы также заключается в операторные скобки.
Object Pascal поддерживает основные математические операции. Это реализовано в виде операторов (в математическом смысле) и функций, возвращающих вычисленцое значение. Операторы:
а + Ь 11 Сложение, а - ь 11 вычитание, а * ь 11 умножение, а / ь 11 деление,
а mod Ь 11 взятие остатка от деления а на Ь,
а div Ь //целая часть частного от деления а на Ь.
Функций куда больше, вот лишь некоторые из них:
Sqrt(a} //Взятие квадратного корня из а,
Sqr(a) // возведение а в квадрат,
Abs(a) //модуль а,
Ехр(а) // экспонента а,
Ln(a) //натуральный логарифм а.
Набор функций, поддерживаемых математическим аппаратсiм Delphi, можно найти в Help.
3.4. Масс1вы До сих пор рассматривались переменные, содержащие в каждый
момент времени только одно значение, это не всегда удобно. Для
- 36 -
Глава 3. Учимся nроrраммировать
обработки большого набора однородных данных предусмотрен другой способ представления данных: массивы. Массив это структура, содержащая набор данных одного типа. Массив объявляется следующим образом:
<Имя массива>: аrrау[<нижний индекс> .. <верхний индекс>] of <тип данных>;
Например:
MyArray: array[l .. 1001 of Integer;
Описывать можно не только одномерные массивы. Существует возможность также задавать многомерные массивы (массивы из мас
сивов):
MyArrayl: array[б .. 12, 1 .. 8] of Real;
Использование многомерных массивов очень удобно при работе с матрицами. Обращение происходит к каждому элементу в отдельности по его индексу в массиве, например:
MyArray[l] := 4; MyArray[15) :"' 8; MyArray1[7,8] := MyArray{7];
Можно также присваивать один массив другому, если они экви
валентны по размеру и типу элементов. Например:
procedure TForml.FormActivate{Sender: TObject); var А, В: array[l .. 10] of Integer;
Bl: array[l .. 11] of Integer; С: array[l .. 2, 1"10] of Integer; i: Integer;
be9in for i := 1 to 10 do
A[i] :"" i; В :=А; //корректно
Bl :=А; //не корректно
С[2] :~В; //не корректно
end;
При задании индексов во время обращения к элементам массива нужно следить за тем, чтобы значение индекса находилось в заданных цределах. Выход за пределы вызовет ошибку периода выполнения. Впрочем, если установлена опция Range checking на странице Compiler диалога Project Options, то за соблюдением этого условия будет следить компилятор.
- 37 -
Азбука Delphi: программирование с нуля
3.5. ЦllКJIЫ Работа с массивами немыслима без циклов. Если представить,
что в массиве несколько сотен элементов и обращение к каждому из
них придется описывать вручную (как правило, такие обращения
в программах происходят по много раз по мере изменения данных в
массиве), то сразу возникают сомнения в необходимости написания приложения. Цикл, как следует из его названия, - это способ организации повторного использования одного и того же участка кода.
Повторяться этот участок может заданное число раз или до выпол
нения поставленного условия.
Существует несколько вариантов цикла. Цикл for предназначен для выполнения оператора (простого или составного) заданное чис
ло раз. Фактически этот цикл является счетным механизмом. Он изменяет переменную-счетчик от нижнего значения до верхнего с
шагом в единицу и, когда переменная достигнет верхнего предела,
заканчивает выполнение оператора и передает управление следующе
му за ним участку кода.
for i := 1 to 20 do MyArray(i] ::= 1;
Такой цикл запишет единицы в первые 20 элементов массива MyArray.
Циклы также полезны при выполнении любых повторяющихся операций, например, вычисления факториала числа или суммы всех чисел от одного значения до другого:
var , i, sum: Integer;
begin fact : == 1; sum : О;
for i := 1 to 7 do beqin
surn end;
end;
:= fact * i; sum + i;
В данном примере цикл будет выполнять все операторы, заключенные в операторные скобки семь раз, каждый раз увеличивая переменную· счетчик на 1. Переменная-счетчик обладает всеми свойствами переменных своего типа, и к ней Применимы все соответствующие опе·
- 38 -
Глава 3. Учимся программировать
рации. Вот только изменять ее в теле цикла нужно осторожно. Если
ошибиться в преобразованиях переменной-счетчика, то цикл может
выполняться не предусмотренное количество раз. Например, может
выполниться только один раз, в ходе которого переменная-счетчик
выйдет за пределы установленного диапазона. Другой вариант~ вы
полнение цикла бесконечное число раз - возникает в ситуации, ког
да переменная-счетчик изменяется в теле цикла таким образом, что
постоянно остается в заданном диапазоне, несмотря на ее изменение
самим циклом.
В приведенном выше примере цикл увеличивает значение пере
менной-счетчика. Возможен вариант, когда цикл уменьшает это зна
чение с каждой итерацией. Описывается он следующим образом:
for i := 20 downto 1 do beqin
end;
Иногда, чаще всего при работе с файлами, возникает ситуация,
когда неизвестно, сколько итераций понадобится циклу, чтобы обработать все данные. Другая ситуация неопределенности возникает при
решении математических задач методом последовательного прибли
жения с заранее заданной погрешностью. В таком случае, как прави
ло, неизвестно, через сколько итераций будет достигнут требуемый уровень точности решения. Для таких задач предусмотрены циклы
с пост- и предусловием.
Цикл с предусловием вида
while i > О do beqin
end;
будет исполнять набор операторов, заключенных в операторные скобки, пока на начало исполнения каждой итерации будет выполнено заданное логическое условие. Внутри операторных скобок должно происходить
преобразование переменных, входящих в предусловие. Если этого не случится, то, начавшись, цикл будет продолжаться бесконечно. При
встрече цикла Delphi проверяет условие и, если оно истинно, исполняет последуК?щие операторы. Затем снова возвращается к предусловию, и история повторяется. Если же условие ложно в самый первый раз, то
возникает ситуация, когда цикл не выполнится ни разу. Этот вариант
имеет право на существование, поскольку возможно, что алгоритм тре
бует такого положения вещей. Рассмотрим пример:
- 39 -
Азбука Delphi: программирование с нуля
while not EoF(pf) do beqin
end;
Данный цикл производит некие действия до тех пор, пока не будет
достигнут указатель на конец файла, связанного с переменной pf. Это может быть чтение строк и помещение их в массив, могут быть математические операции и т. д.
Иногда более удобной формой является цикл с постусловием. Он записывается следующим образом:
repeat
until eps < 0,001;
Такой цикл будет исполняться до тех пор, пока не выполнится ус
ловие, стоящее после слова until. Если перевести операторы этих циклов с английского на русский, то будет гораздо проще запомнить,
какой из них как работает. Так, цикл с предусловием в переведенном варианте выглядит как:
ПОКА [выполняется условие] ДЕЛАТЬ
НАЧАЛО
КОНЕЦ;
А цикл с постусловием:
ПОВТОРЯТЬ
ДО ТЕХ ПОР ПОКА [не выполняется условие];
Как видите, все действительно очевидно. Особенностью цикла с постусловием является то, что при любом значении переменных,
входящих в условие завершения выполнения цикла, цикл выполнит
ся хотя бы один раз. Это происходит потому, что Delphi сначала выполняет все операторы, находящиеся между repeat и until, а затем проверяет условие завершения. Если оно не выполняется, то проис
ходит возврат к первому после repeat оператору и все повторяется еще раз. Как правило, каждая следующая итерация приводит к изме
нению переменных, входящих в условие завершения работы цикла.
Если этого не происходит, то последовательность операторов между
repeat и until будет повторяться до тех пор, пока не будет остановлена каким-либо еще образом.
- 40 -
Глава 3. Учимс.я nроrраммировать
Однако и здесь Delphi предоставляет программисту дополнительную степень свободы в виде команд управления ходом выполнения цикла.
Вот основные из них:
• Continue - вызывает прекращение данной итерации цикла и пе
реход к началу выполнения следующей итерации.
• Break вызывает прекращение работы цикла и передачу уп-равления к следующим за ним операторам.
• Exit - выход из текущей процедуры.
Зачастую можно видеть в программах бесконечные циклы с постили предусловием, которые завершаются именно по оператору break. Такой вариант поведения программы полезен, когда условие прекраще~ ния цикла относительно сложное или когда выход из цикла возможен
в нескольких точках его выполнения. Примеры бесконечного цикла:
while 4 > 3 do beqin
end; repeat
until 4 3;
В теле таких циклов должна находиться хотя бы одна конструкция
вида
if <условие> then beqin
break; end;
3.6. Vсповныi оператор Условиые операторы используются для выбора исполнения од
ного из возможных действий в завщ:;имости от некоторого условия.
В общем виде условный оператор выглядит так:
if <логическое выражение> then begin
end;
- 41 -
Азбука Delphi: программирование с нуля
Например:
if j > 13 then beqin
] :=о 12;
х := 0.8;
end;
В данном примере операторы, заключенные в скобки begin ... end, выполнятся только в том случае, если выполнено условие j > 13. Если условие не выполняется, то оператор пропускается, и управление
передается следующим после него операторам.
Возможен более сложный вариант условного оператора:
if <логическое выражение> then begin
{операторы 1} end
else begin
{операторы 2} end;
В этом примере если условие выполняется, то исполняются {опера
торы 1}, а {операторы 2} пропускаются. Если условие не выполняется, то наоборот, исполняются только {операторы 2}.
Затронем вопрос, относящийся к выполнению или невыполнению
логических условий. Логическое выражение может принимать толь
ко два значения: true или false. Здесь действуют правила булевой. алгебры. Если приведенное в выражении условие выполняется, то
выражение в целом получает значение true. В случае
if tr.ue then ...
условный оператор будет всегда выполняться вне зависимости от
внешних условий. Соответственно в случае
if f alse then ...
условный оператор не будет выполнен ни при каких значениях переменных программы.
Можно вычислить значение логического выражения заранее и в ус
ловие подставить только одну переменную, например:
var IsOK: Boolean; i: Integer;
- 42 -
beqin
IsOK : == i > 1;
if IsOK then ShowMessage('true');
end;
Глава 3. Учимся проrраммировать
Логическая переменная IsOK примет выражение true или false в зависимости от истинности логического выражения в правой части
оператора присваивания, и сообщение будет выдано или нет в соответствии со значением этой переменной.
До сих пор в примерах приводились только простые условия.
Возможно также задание комплексных условий. Например, истинность выражения
IsIN := (а > 0) and (а < 10);
покажет, что значение переменной а больше О и меньше 1 О одновременно.
В табл. 3 .4 приведены логические операторы Delphi и их значения в зависимости от значений операндов. Все эти логические операторы
могут комбинироваться в соответствии с правилами булевой алгебры. Например, вполне имеет право на существование следующая кон
~трукция:
IsOK := ((а> 1) and (Ь < 3)) or ((с> 3) xor (d > 6) xor (f < 0)) or ((t < z) and {not IsEmpty));
Таблица 3.4
Оператор Логическаsч операция х у Результат Пример
notX НЕ
XandY и false
true
true
false
XorY или false
true
true
false
XxorY Исключающее ИЛИ false
true
true
- 43 -
false
true
false
true
false
true
false
true
false
true
false
true
true
false
false
false
false
true
false
true
false
true
true
false
notC
(а>2) and (Ь<с)
{а>2) or
(Ь<с)
(а>2) xor (Ь<с)
Азбука Delphi: программирование с нуля
Разумеется, все эти переменные и условия составлены случайным
образом, но в целом пример демонстрирует возможную комбинацию.
Нужно очень внимательно следить за правильностью составления
таких выражений, обращая внимание на значение выражения в целом. Если будут нарушены правила синтаксиса Object Pascal, то' компилятор выдаст предупреждение или ошибку, а вот если где-то будет пос
тавлен неверный знак или логический оператор, то приложение на
чнет работу, но делать будет совсем не то, что ожидает программист.
Если выражение скомбинировано из трех и более условий, то, возможно, стоит составить таблицу истинности, где будут просмотрены
все возможные варианты состояния переменных, входящих в условия,
и соответствующее этим параметрам значение выражения.
К условным операторам относится и оператор выбора. Он записывается следующим образом:
Case <имя переменной> of <1-е значени'е>: <выполняемые действия>
<2-е значение>: <выполняемые действия>
<n-e значение>: <выполняемые действия> else <выполняемые действия>
end;
Выполняется только тот оператор (или группа, заключенная в опе
раторные скобки), который стоит после значения, совпадающего с
текущим значением указанной после слова Case переменной. Если текущего значения переменной нет в списке, то будет выполнена последовательность действий, расположенная после слова else. Блок else может отсутствовать, тогда никаких действий выполнено не будет, и работа программы продолжится со следующей после окончания опе
ратора выбора строки. Если необходимо, чтобы выполнялось несколько операторов, используются операторные скобки begin ... end.
В качестве переменной выбора может выступать переменная любого из следующих типов (в подсказке Delphi они носят название ordinal types): Integer, Char, Boolean, - а также списков и диапазонов.
Например:
Case s of 1: begin
ShowMessage('Яблoкo');
ShowMessage(' Зеленое'); end;
- 44 -
2 .. 4: ShowMessage('Гpyшa');
5: ShowMessage{'Aneльcин');
6, 7: ShowMessage('Дыня'); else · Ьegin
ShowMessage('Coчный');
ShowMessage (' Арбуз'); end;
end;
Глава 3. Учимся программировать
В этом примере текст вьщаваемого сообщения будет зависеть от того,
какое значение будет иметь переменная s на момент выполнения оператора выбора. Как правило, такие записи используются для упрощения составных условий. С использованием обычного условного оператора эта конструкция выглядела бы очень громоздко.
3.7. Фун1q11 дnи раliоты с текстом В заключение рассмотрим некоторые функции для работы со стро
ками. В стандартной поставке Borland Delphi 6.0 содержится около 70 функций для работы со строками, поэтому здесь рассмотрим только несколько наиболее полезных и часто используемых. Информацию об остальных, если они понадобятся, можно почерпнуть во встроенной справке.
Как правило~ работа со строками заключается в преобразовании содержимого строки или преобразовании ее типа. Первая часть состоит в поиске каких-либо символов или их последовательностей в строке, их замене или удалении, в объединении или сравнении двух строк. Вторая часть сводится к преобразованию вводимой пользователем текстовой информации в данные того типа, который соответствует поставленной задаче (см. раздел 3 .1 ).
Если учесть, что строка представляет собой массив символов, где к каждому символу можно обратиться по отдельности, то, используя циклы, можно большинство из этих операций осуществить самостоятельно. Разумеется, не всегда имеет смысл изобретать велосипед,
однако стоит это сделать пару раз с целью накопления опыта напи
сания программ на Delphi. Ниже рассматриваются некоторые простые примеры работы со строками в Delphi.
function CompareStr(const Sl, S2: String): Integer; function CompareText(const Sl, S2: Stringi: Integer;
Эти функции сравнивают строки S 1 и S2. Первая функция обращает внимание на регистр букв, а вторая - нет. Сравнение происходит
- 45 -
Азбука Delphi: программирование с нуля
посимвольно: берется первый символ из каждой строки. Если сим
волы не одинаковые (с учетом регистра), то вычисляется, насколько отличаются их коды в таблице символов операционной системы, и эта разница возвращается в качестве результата работы. Если симво
лы одинаковые, то берется следующий символ из каждой строки, и
операция повторяется. Таким образом, если функция вернула отрицательное значение, то первая строка считается «меньше» второй.
А точнее, в первой различающейся символьной паре символ первой
строки имеет код меньший, чем код соответствующего символа вто
рой строки, на модуль значения функции. Аналогичная ситуация,
если функция вернула поло~ительное значение. Рассмотрим следующий пример:
procedure TForml.?ormActivate(Sender: TObject); var Sl, S2: String; beqin
Sl : 'abcdefgh'; 82 : = ' fh, ;
ShowMes (IntToStr (CompareStr {Sl, S2))); end;
В данном случае сразу после запуска будет выдано сообщение «-1». Поэкспериментируйте со значениями Sl и S2, чтобы лучше понять
работу данных функций. Попробуйте заменить какую-нибудь маленькую букву большой, использовать регистронезависимый вариант функции.
function Length(S): Integer;
возвращает количество символов в строке. Например, при описании
строкового типа с указанием максимальной длины результатом дан
ной функции будет именно число символов.
Подпрограмма
procedure TForml.ForrnActivate(Sender: TObject); var Sl: String[20]; beqin
Sl := 'abcdefgh'; ShowMessage(IntT tr(Length(Sl)));
end;
выдаст сообщение «8», а не «20». Функция
function Copy(S: String; Index, Count: Integer): String;
- 46 -
Глава 3. Учимся программировать
возвращает в качестве своего значения фрагмент строки S длиной Count, начиная с символа S[Index]. Например:
procedure TForml.FormActivate(Sender: TObject)i var Sl: String[20]; beqin
Sl := 'abcdefgh'; ShowMessage(Copy(sl, 3, 3));
end;
вернет в сообщении «cde». Если Index больше, чем длина строки, то функция возвращает пустую строку.
procedure Delete(var S: String; Index, Count: IntegerJ;
удаляет подстроку из Count символов в строке S, начиная с символа S[Index]. Delete - процедура, соответственно результат не может быть возвращен в виде значения самой Delete. В данном случае изменяется содержимое переменной S. В этом можно убедиться на примере:
procedure TForrnl.FormActivate(Sender: TObject); var Sl: String[20]; begin
Sl := 'abcdefgh'; Delete(sl, 2, 2); ShowMessage(Sl);
end;
Очевидно, что если в данном случае Index больше количества символов в строке, то никаких изменений со строкой. не произойдет.
procedure Insert(Source: String; var S: String; Index: Integer);
вставляет строку Source в строку S между символами S[lndex-1] и S[Index]. Если lndex меньше 1, то он принимается равным 1. (Вы, конечно, помните, что нумерация символов в строке начинается с 1 ?)
procedure TForml.FormActivate(Sender: TObject); var Sl, S2: String[20]; begin
Sl :== 'abcdefgh'; S2 :== 'xyz'; Insert(S2, Sl, 3); ShowMessage(Sl);
end;
- 47 -
Азбука Delphi: nроrраммирование с нуля
В этом примере можно вставить пустую строку (S2 := '' ;), тогда процедура Insert не сделает ничего и в сообщении будет только первоначальный вариант строки S 1.
function IsDelimi ter (const Del' , S: String; Index: Integer) : Boolean;
Эта функция используется для определения того, является ли символ S[Index] равным одному из символов строки Delimiters. Как следует из названия функции, предполагалось, что она будет использоваться для поиска разделителей: запятых, пробелов, точек и т. д. Но из примера, приведенного ниже, видно, что с тем же успехом эту функцию можно использовать для поиска любых других символов:
procedure TForml.Form.Activate(Sender: TObj ); var Sl, 52: String[20J; begin
Sl : 'abcdefgh'; ShowMessage(BoolToStr (IsDelimiter('d', Sl, 4)));
end;
Как видите, реализацию этой функции очень легко повторить самостоятельно с применением условного оператора. Куда более полезна функция
function LastDelimi r (const Delirniters, S: ring): Integer;
Она возвращает в качестве значения индекс последнего встречающегося символа или набора символов из строки Delimiters.
Есть еще несколько редко встречающихся, ввиду своей специфич
ности, команд.
function Concat(Sl(, S2, ... , Sn]: String): String;
Параметры, заключенные в квадратные скобки, являются необязательными, т. е. их можно записывать, а можно и опустить. Это общее
правило для записи параметров подпрограмм в таком виде, когда
указываются только внутренние имена параметров (так называемые
формальные параметры) и их типы. В свою очередь, параметры, передающиеся в подпрограммы при их вызове, носят название фак
тических параметров.
Функция Concat производит конкатенацию, или «склеивание», любого количества строк и возвращает результирующую строку в
качестве своего значения. Оператор «+» производит ту же операцию для двух строк. Причем оператор «+» работает быстрее функции.
function DupeString(const AText: String; ACount: Int r): Str~
- 48 -
Глава 3. Учимся программировать
Результирующая строка составляется путем приклеивания строки
AText к самой себе ACount раз. Вряд ли эти две функции будут вам часто нужны, но все равно стоит поэкспериментировать со следую
щим примером. Для того чтобы использовать функции работы со
строками, необходимо включить в состав подключаемых модулей модуль StrUtils. Сделать это можно, дописав его, через запятую, к тем модулям, что уже подключены в предложении uses интерфейсной части модуля.
procedure TForml.FormActivate(Sender: TObject); var Sl, S2: String[20]; begin
Sl := 'abcdefgh'; S2 := 'xyz'; ShowMessage(Concat(Sl, ' ' S2, '', Sl)); ShowMessage(DupeString('xyz', 5));
end;
Как видите, ничего головоломного. В С, например, первоначально
в официальную поставку вообще не входил модуль работы со строками. Такие функции писались программистами самостоятельно, и
только позже модули работы со строками были включены в состав среды программирования.
В данной главе рассмотрены основные правила программирова
ния на языке Object Pascal. Здесь собрана почти вся теория книги, но это необходимо, поскольку двигаться без этих знаний дальше нецелесообразно.
Контрольные упражнения
1. Напишите алгоритм программы, которая будет заполнять двухмерный массив логического типа следующим образом: в первом и втором столбце будут представлены четыре варианта сочетания двух
операндов, а в последующих столбцах - результаты выполнения
логических операций И, ИЛИ, исключающее ИЛИ.
2. Напишите программу, запрашивающую у пользователя одно число за другим и суммирующую их друг с другом.
3. Напишите программу, заполняющую главную диагональ двухмерной матрицы единицами, а остальные элементы - нулями.
4. Решите предыдущую задачу с заполнением второй диагонали. 5. Заполните массив из центральной ячейки по спирали к краям
" увеличивающимися числами.
Гпава 4. Оliьектно-ориент11рованное проrрамм11рован11е
Как вы уже поняли, технологии программирования развиваются едва ли не быстрее, чем сами компьютеры. Языки программирования прошли в своем развитии от программ в компьютерных кодах, кото
рые представляются многим теперешним программистам архаичным
кошмаром, до визуального программирования в IDE и движутся дальше. Куда приведет это развитие, покажет время. В данной главе дается описание основ объектно-ориентированного программирования (ООП) и поясняется общая суть этого явления.
В Delphi разработчик приложения оперирует объектами. В качестве примера объектов можно привести элементы, которые вы размещаете на форме, выбрав из палитры. Такое представление наиболее удобно при редактировании внешнего вида форм. Однако объект не ограничивается своим визуальным представлением, к тому же у не
которых объектов визуального представления просто нет. В процессе дальнейшей работы объект предстает перед программистом как переменная (структура) того или иного типа, называемого классом.
Сами объекты называются экземплярами класса. Например, кнопки Button 1 и Button2 - экземпляры класса TButton.
Основной особенностью ООП является то, что под объектом понимают набор переменных (свойств) и подпрограмм (методо~), объединенных (инкапсулированных) в одну структуру (рис. 4.1). Так достигается возможность максимально реалистичного описания объектов окружающей среды и процесса их взаимодействия.
Иногда в структуре объекта проводится разделение подпрограмм на методы работы с переменными объекта и на обработчики событий, написанные программистом. В данной книге такого разделения не делается, поскольку качественно они ничем не отличаются. Различие
состоит лишь в том, что обработчики событий начинают свою работу вследствие каких-либо внешних по отношению к приложению
событий (изменения размера, щелчка мыши, работы системного таймера и т. д.), а собственные методы объекта начинают работать, будучи вызваны другими объектами этого же приложения или одним
из методов этого же объекта. Мы уже использовали свойства объекта Label 1 в примерах пер
вых двух глав. В тот раз было использовано свойство Caption (заголовок) данного объекта. Однако если рассмотреть свойства объектов класса TLabel, то становится понятно, что их гораздо больше,
- 50 -
Глава 4. Объектно-ориентированное программирование
г ~ 1 • Своиства !
~···
Объект
Рис. 4.1. Блок-схема объекта, состоящего из методов и свойств
чем отображенных и доступных для изменения в Инспекторе объектов.
В версии Borland Delphi 6.0 та.ких свойств у него 36, причем часть из них еще дробится на более мелкие свойства и методы, например
свойство Canvas (холст). Все свойства наиболее часто используемых объектов можно найти в главе 8.
Использовались и методы объекта Buttonl и Button2 в примерах из главы 2. В Инспекторе объектов на вкладке Events вы найдете обработчики выделенного в данный момент объекта. Также существует довольно большой набор методов, не входящих в число обработчиков событий. Чтобы увидеть все свойства и методы, достаточно поставить точку после имени соответствующего объекта или воспользоваться справкой.
О точках
Так как объект Buttonl является структурой, то нужно каким-то образом обращаться к элементам этой структуры, иначе полезность ее будет существенно ограничена. Свойства объектов, как перемен
ных класса, можно задавать на этапе разработки приложения с помощью Инспектора объектов. На этапе работы приложения также можно изменять свойства программно, либо напрямую присваивая им
новые значения, либо используя методы объекта, которые изменяют
его свойства тем или иным заранее предусмотренным образом. Допустим, вам нужно, чтобы в процессе работы приложения в случае возникновения того или иного события изменялся размер кнопки Button 1, например, при наведении на нее мыши.
Для этого можно использовать событие OnMouseMove, которое происходит, когда курсор мыши оказывается наведен на кнопку. Вы
зовите шаблон обработчика этого события. Напишите в нем следующие две строчки:
Buttonl.Height := 35; Buttonl.Width := 85;
Числа взяты на 10 больше тех, что устанавливаются по умолчанию. Запустите приложение. В результате ваша кнопка реагирует на наве-
- 51 -
А36ука Delphi: проrрамммрованме с нуля
дение на нее мыши: если мышь навести на кнопку, кнопка становится
больше размером, но обратно свой исходный размер уже не обретает. Рассмотрим другой вариант развития событий, уже вполне при
менимый в конкретной работе. Разместите на форме несколько кнопок, например, пять. Для каждой кнопки используйте два события:
OnEnter и OnExit. Напишите в первом для каждой кнопки увеличение ширины и высоты до одних и тех же размеров, а во втором ~ умень
шение до первоначальных размеров. Запустите приложение, пример
ный результат его работы показан на рис. 4.2. Не очень интересно создавать пять обработчиков, отличающихся
только двумя цифрами друг от друга. Этого можно избежать, если использовать дополнительную информацию от сообщения.
Параметром, передаваемым в процедуры OnEnter и OnExit, является переменная Sender типа TObject (дословно - отправитель). Используя это, можно составить условие вида
if Sender = Buttonl then ."
и написать одну процедуру, обрабатывающую сообщение OnEnter или OnExit от какого угодно количества кнопок. Делается это следующим образом: пишется процедура обработки события для первой кнопки, но в самой процедуре учитывается возможность вызова ее событиями других кнопок:
if Sender = Buttonl then Big(Buttonl); if Sender = Button2 then Big(Button2); if Sender = ButtonЗ then Big(ButtonЗ); if Sender = Button4 then Big(Button4); if Sender "" Button5 then Big(Button5);
Затем для всех остальных кнопок обработчик события не создает
ся заново, а выбирается из выпадающего списка (в Инспекторе объектов это маленький треугольник справа от полей OnEnter и OnExit). Теперь, если событие OnEnter произошло с любой из обработанных таким образом кнопок, процедура, написанная один раз, обработает их все - без захламления кода модуля лишними заголовками. Процедура, использованная в примере, описывается перед всеми обра. ботчиками событий в разделе implementation и выглядит следующим образом:
procedure big(Number:TButton); Ьeqin
NumЬer.Height := 35; Number.Width :"" 85;
end; - 52 -
Глава 4. Объе1С'fио-ориентированиое nроrраммирование
Рис 4.2. Внешний вид приложения «Пять кнопок»
Поступив аналогично с обработчиками события OnExit, вы получите довольно лаконичную по сравнению с первым вариантом про
грамму.
В рассмотренном примере вы изменяли свойства Height (высота) и Width (ширина) объектов типа TButton, непосредственно присваивая им новые значения. Вы поняли,' что сделать это довольно просто. Достаточно после имени (Name) объекта поставить точку, и Delphi сам предложит вам вариантъ1 продолжения выражения. Стро:ка вида
Forml.Buttonl.Caption := 'Пуск!';
означает, что приложение должно найти на форме с именем F orm 1 данного проекта объект с именем Button 1 и его свойству Caption присвоить значение «Пуск!». В предыдущих примерах указание на
форму опускалось. Это допустимо, потому что объекты находились в пределах одной формы. Если же объекты находятся на разных формах, то необходимо указывать, какой именно объект вы имеете в виду, поскольку в пределах нескольких форм могут встретиться объекты с одинаковыми именами.
Вернемся теперь к атрибутам видимости, с которыми мы уже сталкивались в главе 2. Переменные и подпрограммы, описанные в разделе private, невидимы извне данного модуля или программы. Другими словами, подпрограммы этого раздела не могут быть напрямую вызваны из другого модуля, а переменные не могут быть непосредст· венно изменены из другого модуля. Если описание нескольких клас
сов ( class) разместить в пределах одного модуля, то они могут ис· пользовать переменные и подпрограммы из разделов private друг друга, обращаясь к ним по именам с указанием класса.
Переменные и подпрограммы из раздела puЫic видны и могут
быть использованы из любого другого класса, расположенного в любом модуле проекта.
В описании может присугствовать раздел protected. Переменные и подпрограммы из этого раздела доступны к использованию только
для классов, описанных в данном модуле и являющихся потомками
данного класса (о наследовании см. ниже).
- 53 -
Азбука Delphi: программирование с нуля
Однако переменные и методы разделов private и protected могут быть вызваны из других модулей посредством методов из раздела puЫic этого же класса. Это сделано для того, ·чтобы не допустить
некорректного изменения тех или иных свойств объекта, описываемого классом. Например, объект - стена, свойство - высота. Высота физически не может быть отрицательной или нулевой, и для того, чтобы предотвратить присвоение ей такого значения (что, в
свою очередь, может привести к ошибкам других вычислений или сбою в программе), нужно описать это свойство в разделе private, а в разделе puЫic описать два метода: GetHeight и SetHeight. Первый будет возвращать высоту стены, а второй задавать высоту согласно переданным параметрам и с учетом физических условий. В данном
случае он должен будет отслеживать положительность вводимого значения.
Помимо инкапсуляции, технология ООП позволяет воспользовать
ся еще одним немаловажным механизмом - наследованием. Под наследованием понимают передачу свойств и методов одно
го объекта (предка) другому (потомку). Это позволяет избежать пов
торного описания похожих свойств и методов. Можно объявить от
носительно простой объект предком более сложного и дописать для последнего необходимые свойства и методы, являющиеся его индивидуальными особенностями. Иногда объект-предок не имеет при
кладной ценности сам по себе, но позволяет экономично создать целую линейку объектов-потомков.
Последним важным механизмом ООП, который полезно будет
рассмотреть в этой главе, является полиморфизм. Допустим, что в наследующем объекте один или несколько обработ
чиков событий должны работать не так, как в объекте-предке. Для этого достаточно написать в объекте-потомке свои обработчики событий с теми же названиями ~ и новый объект уже реагирует на них иначе,
нежели объект-предок. Для реализации такого подхода в объекте-предке эти методы должны быть описаны как виртуальные (virtual). Полезность виртуальных методов переоценить трудно, поскольку в новых,
более сложных объектах переопределяемые методы, как правило, рас
ширяются с точки зрения функциональности, берут на себя дополнительные задачи наряду с решаемыми в них у объекта-предка.
В качестве примера наследования и полиморфизма обычно бе
рутся геометрические фигуры. Допустим, есть некий объект Figure (геометрическая фигура).
У него есть свойства Color (цвет) и Style (стиль штриховки), координаты некой ключевой точки (Х, У), относительно которой может за-
- 54 -
Глава 4. ОбъеКП10-ориентированное программирование
даваться форма объекта, и методы Draw и Erase: первый рисует объект, а второй стирает его. Метод Draw у каждого класса собственный, хотя и носит одно и то же название у всех классов. Такое возможно
благодаря существованию виртуальных методов. Создавая новые объекты и указывая объект Figure в качестве их предка, мы сразу получаем объекты, которые имеют некий цвет и стиль штриховки внут
ренней заливки.
Класс-потомок TCircle (окружность) включает в свое описание свойство, соответствующее радиусу. Метод Draw, являясь абстрактным методом в классе-предке~ должен быть детально описан в кл.ас
се-потомке, исходя из специфики поставленной задачи.
Описание класса предка может выглядеть следующим образом:
type TFigure = class
procedure Draw; virtual; procedure Erase; Color: Integer; //Цвет. Style: Integer; //Стиль заливки. Х,У: Integer; //Координаты ключевой точки.
end;
При описании классов-потомков имя класса-предка указывается
в скобках после зарезервированного слова class. Если же в скобках после слова class не указан класс-предок, то предком такого класса считается класс TObject, который содержит только команды выделения и освобождения памяти для объектов.
Для переопределения виртуального метода используется зарезер
вированное слово override. Только виртуальные методы могут быть переписаны.
type TCircle = class(TFigure) // Указание на класс-предок.
procedure Draw; override; procedure Erase; Radius: Integer; // Радиус.
end;
Аналогично создаются другие классы-потомки класса TFigure. Такой подход позволяет использовать существующие на данный
момент объекты в качестве шаблонов для вновь создаваемых. Эта особенность ООП использована и в самой библиотеке компонентов Delphi. Например, кнопка с картинкой (BitBtn) является потомком
- 55 -
Азбука Delphi: программирование с нуля
обычной кнопки (Button). Естественно, что потомок одного объекта вполне может быть предком для другого. Если вернуться от любого объекта к его самому первому предку, то можно обнаружить, что все объекты, используемые в Delphi, произошли от одного типа объектов TObject. В этом легко убедиться, открыв справку по любому объекту и нажав сверху слева ссылку Hierarchy.
В данной главе рассмотрена структура объектов и способ работы с их методами и свойствами. Мы еще будем возвращаться к этой теме,
поскольку именно в моделировании взаимодействия объектов друг с
другом и с окружением состоит идея реализованной в Delphi технологии программирования. ООП - наиболее естественный способ описания окружающего мира, следовательно, ООП имеет преимущества при написании приложений, в которых моделируются свойства
реальных объектов. Этой особенностью, а также применением технологии быстрой разработки приложений обусловлена простота понимания основных принципов программирования в Delphi.
Контрольные упражнения
1. Видоизмените приведенную выше программу таким образом, чтобы изменялись координаты активной кнопки.
2. Напишите приложение, которое по щелчку на форме выдает сообщение с координатами курсора мыши на момент щелчка. Для
вывода сообщения используйте процедуру ShowMessage.
rпава 6. Palioтa с файпами в Delphi
Файл - область памяти на диске, имеющая уникальное имя.
Другими словами, это некий набор данных, имеющий заголовок.
Очевидно, что операции с файлом могут влиять или на всю область целиком, используя только ее заголовок, или на отдельные данные,
содержащиеся в этой области. В Delphi предусмотрены оба варианта работы с файлами.
В Delphi встроено несколько общих файловых операций. Эти процедуры и функции для работы с файлами оперируют ими целиком, поэтому в большинстве случаев программисту достаточно указать
только имя файла. Функция
f unction (const leName: String): Boolean;
удаляет файл с диска. Не существует соответствующей операции для восстановления удаленного файла, поэтому программист должен проследить, чтобы приложение запросило у польЗователя подтверждение на удаление. DeleteFile (FileName) - функция логического
типа. Она возвращает true, если файл был удален, и false в противном случае, например, если файл не существует или у него установлен атрибут Read-Only.
Следующие три команды используются при поиске файлов:
function FindFirst(const Path: S:ring; Attr: Integer; var F: TSearchRec): Integer;
function FindNext(var F: TSearchRec): Integer; procedure FindClose(var F: TSearchRec);
FindFirst ищет первый файл с именем, удовлетворяющим заданнЬ1м атрибутам поиска в заданной директории (Attr и Path). FindNext находит следующий файл, удовлетворяющий заданным условиям; работа
ет ориентируясь на вызов команды FindFirst. FindClose освобождает память, занятую для работы предыдущих двух команд. Всегда следует вызвать FindClose после того, как результаты поиска были должным образом обработаны и к ним уже не нужно будет обращаться.
Эти команды принимают TSearchRec в качестве одного из параметров. Если файл найден, то поля TSearchRec модифицируются в соответствии с информацией о найденном файле, если не найден, то возвращается нулевое значение. Описание этой записи выглядит следующим образом:
- 57 -
Азбука Delphi: программирование с нуля
type TFileName = String; TSearchRec = record
Time: Integer; //Время последнего изменения файла. Size: Integer; //Размер файла в байтах.
Attr: Integer; //Атрибуты файла. Name: TFileName; //Имя файла с расширением.
ExcludeAttr: Integer; FindHandle: THandle; FindData: TWin32FindData; {Содержит дополнительную информацию,
такую как :1
время создания файла, время последнего доступа
к файлу, полное и короткое имя.}
end;
Параметр Path - строка, содержащая путь к каталогу, в котором
необходимо провести поиск, а также маску имен файлов. Например: '.\test\*. * '.
Параметр Attr указывает специальные файлы, среди которых также нужно проводить поиск в дополнение к обычным файлам. Он может принимать следующие значения:
• faReadOnly - файлы с установленным атрибутом «только для_ чтения»;
• faHidden - скрытые файлы;
• faSysFile - системные файлы;
• faArchive - архивные файлы;
• faAnyFile - любые файлы;
• FaVolumelD - разделы диска;
• FaDirectory- директории. В следующем примере используются метка (Label 1) и две кнопки
(Search и Again), расположенные на форме. Когда пользователь нажимает кнопку Search, в указанной директории находится первый файл, а в заголовке Caption метки Labell появляются имя и размер найденного файла (рис. 5 .1 ). Каждый раз, когда пользователь нажимает
Рис. 5.1. Внешний вид работающего приложения
- 58 -
Глава 5. Работа с файлами в Delphi
кнопку Again, в заголовке метки появляются имя и размер следующего файла.
var SearchRec: TSearchRec; procedure TForml.SearchClick (Ser:der: TObject); begin
FindFirst('C:\Program Files\Borland\delphiб\Ыn\*.*', faAnyFile, SearchRec);
Labell.Caption .- SearchRec. Name + ' is ' + IntToStr(SearchRec.Size) + ' bytes in size';
end; procedure TForml.AgainClick(Sender: TObject); begin
if (FindNext(SearchRec) = 0) then Labell.Caption .- SearchRec.Name + ' is ' +
IntToStr(SearchRec.Size) + ' bytes in size' else
FindClose(Sear\hRec); end;
Рассмотрим следующую функцию:
function RenameFile(const OldFileName, NewFileName: String): Boolean;
Она используется для переименования файла с именем OldFileName в NewFileName. Если операция прошла успешно, то функция возвращает true. Если функция по той или иной причине не может переименовать файл, например, если файл с именем NewFileName уже существует, то возвращается false.
Вы не можете переместить файл, как это делается по команде «Перенос», с одного диска на другой. Для этого нужно сначала копировать
файл, а затем удалить старый. Например, с помощью функции
function CopyFileTo(const Source: String; const Destination: String): Boolean;
Здесь первый параметр - строка, содержащая полное имя файла,
который нужно скопировать; второй - куда скопировать. Функция
вернет значение false, если файл по адресу, указанному во втором параметре, уже существует.
Теперь рассмотрим работу с данными, содержащимися в файлах.
При работе с файлами на уровне их составляющих элементов может быть использовано несколько видов файлов. Как правило, используют
ся типизированные и текстовые файлы. (В ряде приложений может
- 59 -
Азбука Delphi: nроrраммированме с нуля
оказаться полезным еще один вид файлов - нетипизированные, однако описание работы с ними не входит в задачи этой книги.)Текстовый файл рассматривается как набор последовательности строк, а типизированный - как последовательность элементов определенно
го типа и, как следствие) одинаковой длины. (Типом данных в файле может быть любой тип фиксированного размера.)
Напрямую с содержимым файла Pascal не работает. Нужно связать с файлом объявленную ранее переменную. Делается это с помощью команды
procedure AssignFile(var F; FileName: String);
Переменная F может быть описана одним из двух вариантов:
F: text; // текстовый файл F: file of <typename> // файл данных типа typename
где FileName - полное имя файла, который нужно связать с переменной.
Следующей командой (после связывания с файлом переменной) должна быть одна из трех команд, определяющих вид открытия файла.
procedure Reset(var F [: File; RecSize: Word] };
открывает для чтения существующий файл, связанный с файловой переменной F. RecSize. Она задает размер записи, который будет использоваться при передаче данных. Если этот параметр не указан, то
размер записи принимается 128 байт.
procedure Rewrite(var F: File [; Recsize: Word) );
создает новый файл или открывает существующий, стирая его содержимое. После этого можно записывать в него данные. Смысл параметров тот же, что и в предыдущем случае.
procedure Append(var F: Text);
открывает существующий текстовый файл для записи, но не стирает содержимое, новые данные добавляются в конец файла.
Далее в зависимости от того, какой тип файла был открыт, могут применяться около трех десятков различных команд, среди которых
для типизированных файлов:
procedure Read (F , Vl [ ,' V2, ... , Vn ] ) ;
для текстовых файлов:
procedure Read ( [ var F: Text; ] Vl [, V2, ... , Vn ] ) ;
- 60 -
Гnава 5. Работа с файnами в Delphi
Они считывают значения из файла, связанного с переменной F в указанные переменные.
procedure Readln([ var F: Text; ] Vl [, V2, ... ,Vn ]);
Эта процедура считывает значения указанных переменных из текущей строки текстового файла, после чего переходит на следующую строку. Если список переменных для считывания пуст, то результатом работы будет размещение указателя на следующую строку.
Для типизированных файлов
procedure Write(F, Vl 1 ••• ,Vn);
записывает в файл данные типа, соответствующего файловой переменной F;
procedure Write([var F: Text; ] Pl [ , Р2, ... , Pnl);
записывает в текстовый файл одно или несколько значений. Значения могут быть символьного, строкового, целочисленного, дробного или логического типов.
procedure Writeln{[ var F: Text; ] Pl [, P2 1 ••• ,Pn ]);
делает то же, что и предыдущий вариант, но после записи данных
вставляет символ перевода строки ( carriage return/line feed). При открытии файла в его начало помещается указатель, который
сдвигается каждый раз при чтении данных. Также указатель можно
переместить соответствующими командами. Положение указателя важно знать, чтобы иметь представление о том, что именно в следующий момент будет считываться приложением. Для типизированных файлов смещение указателя от начала файла указывается в байтах.
function FilePos(var F): Longint;
Эта функция используется для определения положения указателя. Когда файл только открывается и указатель находится в самом на
чале файла, данная функция возвращает О, во всех остальных случаях возвращается смещение указателя от начала файла в байтах. Функция не работает, если файл не открыт~ а также если он открыт как текстовый.
При использовании циклов с предусловиями, а также в некоторых
других случаях полезным бывает определить тот момент, когда указатель, смещаясь по файлу, оказывается в его конце. Специально для этого разработана функция Eof(var F).
Для типизированных файлов:
function Eof {var F): Boolean;
- 61 -
Азбука Delphi: программирование с нуля
Для текстовых файлов:
function Eof [ (var F: Text)J: Boolean;
Эта функция проверяет, равняется ли смещение указателя от начала. файла его размеру. F - переменная, связанная с файлом. Функция возвращает true, если указатель находится в самом конце файла или файл пуст, во всех остальных случаях возвращается false. Эту функцию можно использовать только с файлами, открытыми для чтения; попытка использования ее с файлами, открытыми по командам Rewrite или Append, вызовет ошибку.
Полезной бывает функция определения размера файла:
function FileSize{var F): Integer;
Однако она имеет одну особенность: возвращает размер файла не в байтах, а в количестве записей. Например, если файл открыт как file of Byte, то функция вернет его размер в байтах, а если тот же файл открыть как
file of Integer, то возвращенное значение будет вчетверо меньше, поскольку под одну переменную типа integer используется 4 байта.
procedure CloseFile( leVar); // ранее открытый файл.
Иногда складывается такая ситуация, что не все данные успели
передаться в файл до его закрытия. В этом случае помимо потери
данных может возникнуть сложность с попыткой прочитать данные
из этого файла в дальнейшем. Чтобы такого не происходило, нужно
применять команду
procedure Flush(var t: Text);
которая дописывает все данные, оставшиеся в буфере файлового обмена, в целевой файл, что гарантирует, что все символы, переданные на данный момент в файл, на самом деле туда попали.
Существует еще несколько полезных функций для работы с файлами, однако они не столь применимы, и в общем случае будет достаточ
но перечисленных выше. Если вам понадобятся еще какие-либо возможности, посмотрите список доступных операций в подсказке. Как правило, список их будет различным в зависимости от версии Delphi.
Отдельно стоит рассмотреть работу с текстовыми файлами. Как следует из их названия, это файлы, содержимое которых является текстом. В Delphi предусмотрен очень удобный способ работы с такими файлами: все строки файла считываются в одну переменную типа TStringlist. Перед использованием ее необходимо объявить:
var F: TStringList;
- 62 -
Глава 5. Работа с файлами в Delphi
Теперь компилятор «знает», что существует переменная F типа TStringlist, но при попытке ее использования выдаст ошибку, потому что переменной не выделена необходимая память. Для использования переменной необходимо ее инициализировать: выделить необходимое количество памяти для ее хранения и присвоить начальное значение.
Для этого служит метод Сгеаtе.
Если память выделена, то при завершении работы с переменной она должна быть возвращена системе. Если этого не сделать, то сколь
угодно большая память рано или поздно будет израсходована «мертвыми» объектами. Для уничтожения объекта и возврата памяти системе существует метод Free.
Таким образом, использование переменной типа TStringList, в об-щем случае, выглядит следующим образом: ·
var F: TStringList;
begin F :; TStringList.Crea
{операции с использованием
F.Free; end;
Примечание В отличие от простых типов данных, для которых функции выделения и освобождения памяти берет на себя компилятор, для классов необходимо вызывать специальные методы Сгеаtе и Fгее, называемые соответст
венно конструктором и деструктором класса. Сравнивая С и Delphi, можно заметить, что в С также есть конструкторы и деструкторы с заранее определенными именами. Имя конструктора в С совпадает с име
нем класса, а имя деструктора состоит из имени класса и тильды~.
Вернемся к классу TStringlist. Раз он разработан для работы с фай-лами, то в нем должны быть предусмотрены инструменты для чтения из файла и записи в файл. Для чтения из файла существует метод:
procedure LoadFromFile(const FileName: String);
Он записывает в свойство Strings объекта, из которого он был вызван, строки из указанного в качестве параметра процедуры файла. Это свойство является массивом строк. Соответственно, работа со счи
танными из файла строками происходит по правилам работы с ячейками массивов. Каждая строка файла, отделенная символом перевода строки, становится ячейкой массива. Например:
F.LoadFromFile ('D:\Text.txt');
- 63 -
Азбука Delphi: программирование с нуля
помешает все строки, содержащиеся в указанном файле, в свойство объекта F.Strings. Индексация массива начинается с нуля. То есть в результате выполнения следующих инструкций
F := TStringList.treate; F.LoadFromFile('D:\Text.txt'); Labell.Caption :~ F.Strings[O]; F.Free;
в заголовок Caption метки Labe11 будет помещена первая строка текста, содержащегося в файле.
При чтении из файла количество прочитанных строк запомина
ется в свойстве Count типа Integer и может быть использовано для проверки количества элементов в созданном массиве строк. Это мо
жет оказаться полезным, если заранее неизвестно количество строк
и возможен выход за пределы массива, что может вызвать ошибку и даже аварийное завершение приложения.
С массивом строк, расположенным в свойстве Strings, можно производить различные манипуляции, например:
function Add(const S: String): Integer;
добавляет строку S. Если массив отсортирован, то строка записывается на положенное в порядке сортировки место; если массив не
отсортирован, то строка дописывается в конец. Функция возвращает
номер позиции, на которую бьша записана строка. Отсчет по-прежне
му начинается с нуля.
procedure Clear;
очищает массив строк: удаляет все строки, которые в нем нахо
дились.
procedure Dele (Index: Integer);
удаляет из массива Strings строку с номером Index + 1.
procedure Exchange(Indexl, Index2: Integer);
меняет местами строки с индексами? указанными в качестве пара~
метров.
function Find(const S: String; var Index: Integer): Boolean;
используется, чтобы получить значение индекса строки S в отсортированном (в порядке таблицы ANSI) массиве Strings. Перед применением поиска необходимо провести сортировку методом Sort.
- 64 -
Глава 5. Работа с файлами в Delphi
procedure Insert(Index: Integer; const S: String);
добавляет строку S на позицию, определенную значением параметра Index. Нумерация начинается с нуля.
procedure Sort;
сортирует строки в массиве согласно ANSI. Еще один способ сортировки - присвоить свойству Sorted значение true.
Зачастую измененный массив строк нужно сохранить в тот же или
другой файл, для этого служит следующий метод:
procedure SaveToFile(const FileName: String);
Данная процедура сохраняет каждый элемент массива в отдельную
строку в файле, указанном в качестве параметра FileName. Если указанного файла не существует, он будет создан.
F := TStringList.Create; F.LoadFromFile('D:\Text.txt'); F.Sorted := true; F.SaveToFile('D:\Text.txt'); F.Free;
Эта конструкция инициализирует переменную типа TStringlist, считывает в нее строки из файла Text.txt, сортирует массив, а потом записывает результат в тот же файл и освобождает память, занимаемую переменной.
Еще один способ работы с файлами заключается в использовании
специализированного класса TFileStream. Его объекты и методы обеспечивают лучшую совместимость версий компилятора, с одной сто
роны, но увеличивают количество операций, которые программист
вынужден описывать вручную, с другой.
Для начала работы необходимо объявить переменную этого типа:
var F: TFileStream;
Далее ее необходимо инициализировать. Инициализацией объекта занимается конструктор Create:
constructor Create(const FileName: String; Mode: Word[;Rights: Cardinal]);
Параметром FileName конструктору в виде строки передается имя файла или полный путь к файлу, с которым предполагается работать.
- 65 -
Азбука Delphi: программирование с нуля
Параметром Mode определяется режим или способ открытия, этот параметр может принимать следующие значения:
• fmCreate - открыть файл для записи; если этот файл не существует, то он будет создан;
• fmOpenRead - открыть файл для чтения; если файл не сущест
вует, то произойдет ошибка: чтение из несуществующего файла невозможно~
• fmOpenWrite - открыть файл для записи; если указанный файл не существует, то произойдет ошибка;
• fmOpenReadWrite - открыть файл для чтения и записи (редак
тирования).
Третий параметр (Rights) определяет, могут ли другие приложения работать с открытым файлом и, если могут, то каким образом. Пара
метр может принимать одно из нескольких значений:
• fmShareCompat - другие приложения могут работать с этим
файлом без ограничений;
• fmShareExclш~ive - другие приложения не могут открыть ука
занный файл;
• fmShareDenyWrite - файл может быть открыт другими прило
жениями только для чтения;
• fmShareDenyRead - файл может быть открыт другими прило
жениями только для записи;
• fmShareDenyNone - не показывать другим приложениям, что
файл уже используется.
Очевидно, что если приложение однопользовательское и работает со специфическими файлами, которые не могут быть случайно или специально открыты другими приложениями, то последний параметр
можно не указывать.
По завершении работы с файлом его нужно закрыть, для этого доста
точно уничтожить объект типа TFileStream, освободив ту область памяти, в которой он содержался. Сделать это можно, вызвав метод Free:
F.Free;
Для работы с файлами посредством объекта типа TFileStream используются три метода: Read, Write и Seek. Как следует из их названий, первый предназначен для чтения из файла, второй для записи, а третий для перемещения внутри файла. Рассмотрим каждый метод подробнее. Строка
F.Read(Buffer,20);
66 -
Глава 5. Работа с файлами в Delphi
указывает приложению прочитать 20 байт и поместить результат в переменную Buffer. Например:
procedure TForml.FormActivate(Sender: TObje ); var
F: TFileStream; // Переменная типа объект ream. ffer: array[O .. S]of char; //Буфер для хранения про~итанных
данных.
begin F :~ TFileStream.Create('D:\Text.txt', fmOp~nRead);
F.Read(Buffer, 6); F. Labell.Caption := Buffer;
end;
Используя такой обработчик событий, программист при запуске приложения увидит в заголовке метки первые шесть байт, содержащие
ся в указанном файле (разумеется, только в том случае, если этот файл существует и программист не забыл поместить метку на форму).
Особенностью метода Read является то, что он возвращает число прочитанных байт. Это число обычно равно второму параметру, передаваемому в метод. Переданное и возвращенное числа могут быть разными в случае, если при чтении был достигнут конец файла либо возникла какая-то физическая неполадка на диске.
Но что делать, если необходимо прочесть шесть байт, начиная с двадцатого? Для этого существует метод Seek:
function Seek(const Of t: I 64; Origin: TSeekOrigin): Int64;
Здесь Offset - число байт, на которые надо сдвинуться; Origin - ука
зание на одну из точек, из которых возможно начало движения. Воз
можны три варианта:
• soFromBeginning с начала файла;
• soFromCurrent от текущей позиции к концу файла;
• soFromEnd - от конца файла к началу.
Таким образом, строка
F.Seek(20, soFromBeginning);
указывает приложению переместить указатель текущей позиции в от
крытом файле на 20 байт, начиная с начала. Еще одной особенностью метода Seek является то, что он возвра
щает новую позицию указателя в файле относительно начала. Используя эту его особенность, можно легко узнать размер файла:
67 -
Азбука Delphi: проrраммирование с нуnя
Size: F.Seek(O, soFromEnd);
Выполнение этой инструкции приведет к тому, что указатель пере
местится в конец файла, а в переменную Size будет помещен размер этого файла в байтах.
Последним рассматриваемым здесь методом будет метод Write. В этот метод также передаются два параметра: переменная, содер
жимое которой нужно записать, и количество байт, которые будут записаны. Если переменная занимает большее число байт, то будет
взято только указанное количество первых, остальные будут проигнорированы.
procedure TForml.ForrnActivate(Sender: TObject); var
F: TFileStream; // Переменная типа объект TFil ream. Buffer: array[O .. lOO]of Char; //Буфер для хранения
прочитанных данных
begin Buffer := 'qwertyuiopasdfghjkl'; F := TFileStrearn. F.Write(Buffer, 6); F.Free;
end;
( 'D: \Text. txt', fmOpenReadWrite);
Заглянув в указанный файл после работы приложения, можно
будет увидеть только первые шесть символов массива Buffer. После чтения и записи текущая позиция указателя в файле уста
навливается в конец обработанного блока данных. То есть в случае
чтения указатель переходит на конец прочитанной области, а в случае
записи - на конец записанной. Если данные добавлялись в конец
файла или кроме переданных байт в файле больше ничего нет, то указатель будет находиться в конце файла.
В данной главе бьmи рассмотрены методы работы с файлами данных, которые находят огромное применение практически во всех более-менее сложных приложениях. Если приложению нужно запом
нить какую-то информацию в промежутках между работой, то файлы один из вариантов. Особое значение имеет работа с файлами, например, в приложениях, являющихся редакторами данных.
Контрольные упражнения
1. Написать программу, которая бы по щелчку на кнопке выводила следующую строку из файла в любой визуальный компонент
формы.
- 68 -
Глава 5. Работа с файлами в Delphi
2. Написать программу, которая по щелчку на кнопке записывала бы строку из поля ввода в предопределенный файл.
3. Написать программу, которая, не дожидаясь действий пользователя, считывала бы данные из одного файла и выводила их в другой в измененном виде, например, только четные строки или с измене
нием регистра букв, а затем закрывалась без команды пользователя.
Для того чтобы инструкции начали выполняться сразу по запуску
программы, можно использовать событие формы OnCreate; для выхода из приложения - процедуру Exit.
Гnава G. Примеры вза11модейс1в11и оliыктов разных кпассов
В этой главе на нескольких примерах мы рассмотрим процесс
создания приложений с нуля. На практике будет показано, как использовать материал, изложенный ранее в книге вместе с новой ин
формацией этой главы. Ответы на возникающие в ходе прочтения вопросы по большей части можно найти в главе 8.
В.1. Пр11пожен11е «Кнопки»
Первое приложение (рис. 6.1) иллюстрирует работу с кнопками. При запуске приложения в окне расположены четыре кнопки
с фиксацией (SpeedButton), одна простая кнопка (Button), радиоrруппа из четырех пунктов (RadioGroup) и один индикатор с флажком (CheckBox). Для визуализации их работы в окне также расположена графическая форма (Shape).
При нажатии на одну из четырех кнопок с фиксацией форма и цвет объекта Shape 1 устанавливаются в соответствии с рисунком на кнопке, а точка в радиогруппе, обозначающая текущий выбор, перемещается на соответствующий пункт. При выборе другого пункта из радио
группы ничего не происхрдит, зато, если нажать кнопку «Старт!»,
форма и цвет объекта Shape 1 устанавливаются в соответствии с выбранной радиокнопкой (цвет соответствует рисункам на кнопках с
фиксацией), а состояние кнопок с фиксацией изменяется. Установка или снятие флажка рядом с надписью «Штриховка» меняет стиль заливки объекта Shape 1.
Рис. 6.1. Приложение «Кнопки»
70 -
Глава 6. Примеры взаимодействия объектов разных классов
Рис. 6.2. Размещение объектов на форме
Таким образом, практически все объекты в окне взаимосвязаны. Чтобы этого достичь, нужно выполнить следующие действия:
l. Разместить на форме объекты и задать им свойства Caption и ltems в соответствии с рис. 6.2.
2. Запустить редактор изображений (можно использовать поставляемый вместе с Delphi редактор изображений Tools ~ lmage Ed itor, стандартный PaintBrush или любой другой, который позволяет создавать изображения размером 1бх16 пике елей в формате .bmp). Создать в нем четыре изображения: красный круг, синий эллипс, зеленый прямоугольник и желтый квадрат.
3. Установить свойство Shape объекта Shapel в значение stCircle, а свойство Brush.Color этого же объекта в значение cl Red.
4. Задать свойство Glyph кнопок с фиксацией таким образом, чтобы они выглядели, как показано на рис. 6.1.
5. Задать свойство Grouplndex всех четырех кнопок с фиксацией равным единице (или любому другому положительному числу). Свойство AllowAllUp установить в значение false.
6. Дважды щелкнуть по кнопке с нарисованным красным кругом. Написать в появившийся шаблон обработчика следующий код:
Shapel.Shape := stCircle; Shapel. . Color : clRed; RadioGroupl.:temlndex : О;
Здесь первая строка задает форму объекта Shapel (круг), вторая строка устанавливает цвет заливки (красный), а третья
строка устанавливает точку в RadioGroup на первый пункт.
- 71 -
Азбука Delphi: nрQrраммирование с нуля
7. Записать в обработчики событий OnClick для остальных трех кнопок с фиксацией аналогичный код, изменяя при этом цвет и форму для объекта Shape 1, а также номер строки в радиогруппе (нумерация строк начинается с нуля). Имена констант, соответствующих цвету и форме для Shape 1, можно найти в вьшадающих списках соответствующих свойств в Инспекторе объектов.
8. Дважды щелкнуть на кнопке «Старт!!> левой кнопкой мыши. Откроется шаблон обработчика события OnClick.
9. Ввести в него следующий код:
Case RadioGroupl.Itemindex of О: begin
Shapel.Shape := stCi Shapel.Brush.Color : SpeedButtonl.Down ·-
end; 1: be9in
end;
З: Ьeqin
end; end;
clRed;· true;
-Этот код будет выполнять три оператора: установка формы
объекта Shape 1, установка цвета его заливки и изменение состояния кнопок с фиксацией. В приведенном листинге пропущены три участка кода. Их несложно заполнить самостоятель
но по аналогии.
1 О. Создать шаблон обработчика события OnClick для объекта CheckBoxl. Ввести в него следующий код:
if CheckBoxl.Checked then Shapel.Brush.Style := bsBDiagonal
else Shapel.Brush.Style :~ bsSolid;
Этот условный оператор проверяет состояние флажка, и, если он установлен, свойство Style объекта Shapel изменяется на bsBDiagonal: заливка объекта становится штриховой. Если флажок не установлен,
то заливка становится сплошной путем присвоения этому свойству
значения bsSolid . .Все. Приложение готово.
- 72 -
Глава 6. Примеры взаимодействия объектов разных классов
6.2. Ф11пь тр ввода Следующее приложение (рис. 6.3 а) осуществляет фильтрацию
вводимых символов. Это может оказаться очень полезным, если от
корректности вводимых пользователем данных зависит работоспособность приложения в целом, например в приложениях, выполняющих некие математические действия с введенными данными.
В поле Edit вводятся символы, но появляются в нем только цифры; для сравнения в заголовке метки Label появляются все вводимые символы.
Работает приложение относительно просто, и его разработка занимает куда меньше времени, чем в предыдущем случае. Необходимо:
1. Разместить на форме экземnляр объекта Label и объекта Edit, как показано на рис. 6.3 б.
2. Удалить текст из заголовка метки и из поля ввода. Для этого нужно задать в Инспекторе объектов свойства Label1.Caption и Edit1 .Text пустыми. Задать свойство Caption самой формы.
3. Выбрать в Инспекторе объектов событие OnKeyPress для объекта Editl. Дважды щелкнуть по нему левой кнопкой мыши -появится шаблон обработчика события TForm1 .Edit1 KeyPress. Обратите внимание на передаваемые в обработчик данные, в частности, на переменную Кеу. Зарезервированное слово var перед ней означает, что если изменить значение переменной в
обработчике, то значение глобальной переменной Кеу также изменится.
4. В шаблон обработчика событий вставить следующий код:
Labell.Caption := Labell.Caption + Кеу; if not (Кеу in [ '0' .. '9']) then Кеу := Chr (0};
а б
Рис. 6.3. а- пример фильтрации вводимых символов; б - примерный вид формы приложения
- 73 -
Азбука Delphi: программирование с нуля
Здесь первая строка добавляет в заголовок метки Labell символ, соответствующий нажатой клавише, а условный оператор перед встав
кой этого же символа в поле ввода заменяет его пустым символом
(Chr(O)), если он не лежит в диапазоне символов от О до 9. Символы взяты в апострофы, это значит, что они интерпретируются как данные
символьного типа, что соответствует типу переменной Кеу.
На этом разработка приложения завершена. Применив такой под
ход в своем приложении, разработчик освобождается от необходимости каждый раз проверять правильность записи введенных поль
зователем данных перед их использованием в математических опе
рациях. Можно позволить пользователю вводить, например, только
русские буквы, или только буквы английского алфавита, или цифры
и определенный набор специальных символов. Все будет регулироваться теми символами, которые стоят в квадратных скобках. Услож
няя логическое условие, можно разрешить вводить только один де
сятичный разделитель, а минус вводить только в первую позицию
поля, а также многое другое.
&.3. Контекстные меню Последний пример этой главы демонстрирует работу с контекст
ными меню и сообщениями. Также он показывает одну интересную
особенность разработки интерфейса. Внешний вид приложения показан на рис. 6.4 а.
1. Создайте новое приложение и разместите на нем по два экземпляра компонентов PopUpMenu и Edit так, как показано на рис. 6.4 б.
2. Задайте свойства Text полей ввода и заголовок формы.
3. С помощью конструктора, вызываемого двойным щелчком по иконке PopUpMenu, создайте контекстные меню, как показано на рис. 6.5.
4. В окне Object TreeView дважды щелкните на первом элементе первого контекстного меню (Каждый). Появится редактор кода
с подготовленным шаблоном обработчика события OnClick. В этот обработчик впишите следующий код:
MessageBox (0, 'Красный', 'Цвет', МВ_ОК)
Точку с запятой ставить не обязательно, потому что сразу за
этим оператором следует закрывающая операторная скобка
( end); это нужно просто запомнить. ·
- 74 -
Глава 6. Примеры взаимодействия объектов разных классов
5. Повторите пункт 4 для остальных шести строк первого контекстного меню, изменяя в параметрах оператора MessageBox только цвет, перебрав все цвета радуги от красного до фиолетового.
6. Задайте свойство PopUpMenu для одного из полей ввода (Edit1 ), выбрав из выпадающего списка PopUpMenu1.
7. Повторите пункт 6 для второго поля ввода, привязав к нему еще «мертвое», т. е. ничего пока не выполняющее, всплываю
щее меню.
8. Щелкните один раз левой кнопкой мыши по первому элементу (Red) второго контекстного меню в окне Object TreeView. Откройте в Инспекторе объектов страницу событий и найдите событие OnClick. Выберите из выпадающего списка обработчик щелчка по элементу Каждый уже готового меню.
9. Повторите пункт 8 для остальных шести строк второго контекстного меню. Таким образом вы задаете, что щелчок по одинаковым по смыслу строкам разных меню будет обрабатываться одной и той же подпрограммой. Это избавляет от повторного написания кода и заметно уменьшает размеры про
граммного модуля.
Запустите приложение. Окно приложения должно выглядеть при
мерно так, как показано на рис. 6.4 а. Вызовите контекстное меню щелчком правой кнопки мыши по одному из полей ввода и выберите любой его пункт, например, четвертый сверху. Результатом этих дейст
вий будет сообщение, внешний вид которого показан на рис. 6.6. Отметим, что это сообщение, вызываемое через MesageBox, обла
дает большими возможностями, чем сообщение, вызываемое функцией ShowMesage. Как минимум в нем можно менять сам заголовок и тип сообщения. (О дополнительных возможностях MessageBox можно прочитать в Help.)
а 6
Рис. 6.4. а - внешний вид приложения RainBow; б - форма приложения
- 75 -
Азбука Delphi: программирование с нуля
а 6
Рис. 6.5. Контекстные меню: а-первое; 6- второе
Рис. 6. 6. Сообщение программы
В данной главе были рассмотрены несколько общих примеров,
иллюстрирующих работу с некоторыми простейшими компонентами
библиотеки компонентов Delphi. Целью главы было показать последовательность разработки приложения (а не особенности работы с теми или иными объектами), а также убедить читателей, что создавать работоспособные и полезные приложения, используя IDE Delphi, можно буквально за несколько минут. Естественно, более сложные и объемные приложения потребуют больше времени и, возможно, большего числа разработчиков, но суть самого процесса при усложнении задачи практически не меняется.
Контрольное упражнение
Добавьте в приложение «Кнопки» возможность выбора цвета и геометрии фигуры при помощи контекстного меню.
rпава 7. Процесс отпадки проrраммы
В предыдущих главах были рассмотрены основные возможности
языка Object Pascal - то, без чего создание сколько-нибудь полезных приложений в Delphi весьма маловероятно. Однако IDE не носила бы такого названия, если бы не предоставляла программисту доволь
но широкого спектра возможностей по отладке и тестированию про
граммы. В данной главе речь пойдет именно о том, каким образом программист может упростить себе жизнь на одном из последних
этапов разработки приложения - этапе отладки кода.
В процессе написания приложений в них неизбежно возникают ошибки. В лучшем случае это ошибки синтакс:И:ческие: опечатки, пропущенные аргументы, забытые определения переменных, ошибки несовместимости типов переменных или аргументов функций,
т. е. ошибки механического характера. Их очень легко отследить и, как правило, довольно просто исправить. Другой вид ошибок - ошибки логические или смысловые. Технически все написано правильно:
программа успешно компилируется и запускается, но работает не так, как предполагал программист, либо вовсе аварийно завершается или
виснет. Такие ошибки локализовать гораздо сложнее, и их не всегда
удается исправить «малой кровью», иногда приходится переписывать
или дописывать значительные участки кода.
Информация лучше воспримется, если ее продемонстрировать на
примере. Создайте приложение. Поместите на форму кнопку и задайте ей обработчик события OnClick. В него поместите следующий код:
procedure TForml.ButtonlClick(Sender: TObject); var i, j, k: Integer; begin
i .- 13; j : = 2;
k : = 8; Buttonl.Caption := IntToStr(k);
end;
Все предельно просто, но в то же время·достаточно показательно.
Откомпилируйте и запустите приложение, после чего закройте его и
перейдите в редактор кода. Он несколько изменился с момента пос
леднего редактирования. Внизу появилось дополнительное окно с двумя советами (Hint) (рис. 7.1).
_;. 77 -
Азбука Delphi: программирование с нуля
Рис. 7.1. Дополнительное окщ) редаКтора кода
В этом окне расположены две строки, начинающиеся со слова
Hint (совет). В этих строках сообщается, что переменные i и j не используются в приложении.
Еще одно изменение - синие точки, появившиеся слева от неко
торых строк кода. (Если на вкладке Compiler диалога Project options установлена опция Optimization, то синие точки появляются так, как показано на рис. 7.2. Если эта опция не установлена, то синими точками будут помечены все операторы программы.)
Эти синие точки показывают, какие строки вошли в исполняемый код приложения. Две неотмеченные строки с операторами присваи
вания были оптимизированы компилятором, поскольку их наличие или отсутствие никак не сказывается на работе приложения. На любой строке (инструкции), вошедшей в исполняемый код, можно ставить
точку прерывания выполнения. Для того чтобы сделать это, нужно установить курсор на строке и нажать F5 или щелкнуть левой кнопкой мыши по серой панели возле этой строки. Если контрольная точка
var i,j,k:integer;
begin i: = 13;
j:=2;
k:=B;
Buttonl. Caption : = IntToStr (k) ;I
Рис. 7.2. Результат компиляции с оптимизацией
- 78 -
Глава 7. Процесс отладки программь~
размещена в строке, оптимизированной при компиляции, то прерывания вы11олнения, разумеется, не произойдет, а при последующем пе
реходе в редактор кода можно будет увидеть, что обозначение контрольной точки изменилось. Очевидно, что такие контрольные точки бесполезны, поэтому лучше сначала откомпилировать приложение и получить информацию о результатах оптимизации, чтобы подобных ситуаций не возникало.
Итак, установите контрольную точку в строке
k : 8;
Запустите программу, нажмите кнопку. Выполнение программы приостановится и откроется Окно редактора кода. В нем будет выделена строка, перед которой остановилось выполнение программы.
Первое, что стоит сделать, это посмотреть значения переменных, для этого достаточно навести на переменную курсор мыши и на некоторое
время задержать его (это можно делать как в строке остановки программы, так и в любой другой, находящейся до или после нее). Другой способ узнать значение переменной выделить ее или просто поставить курсор на соответствующую переменную и нажать Ctrl + F7, вызвав, таким образом, окно Evaluate/Modify (рис. 7.3).
Если вы попытаетесь узнать значения переменных i I'!.'!И j, то из этого ничего не выйдет, поскольку они не вошли в исполняемый код,
следовательно, приложение просто не знает об их существовании.
Попытка узнать значение переменной k приведет к появлению сообщения (см. рис. 7.3): «Переменная 'k' не доступна здесь из-за оптимизации», другими словами, переменной k еще не было присвоено значение, поэтому компилятор еще не выделил под нее память (то, на чем было акцентировано внимание выше: работа программы приостановилась перед выполнением инструкций, находящихся в указанной строке). Другими словами, данная стро~а в качестве контрольной точки мало чем может быть полезна при отладке. Для того чтобы выполнить одну строку программы, используются команды F7 или F8. Отличаются они тем, что F7, встретив строку с вызовом подпрограммы, не входящей в стандартные библиотеки, перенесет указатель следующей выполняемой строки в начало подпрограммы, а F8 просто выполнит встреченную подпрограмму, не отвлекая программиста
процессами, происходящими внутри нее. Какую из клавиш целесообразнее использовать, решает программист в каждой конкретной ситуации: если с содержимым подпрограммы все уже выяснено и там
не может быть никаких неожиданностей, то и отвлекаться на нее не стоит. В данном случае все равно, какую :клавишу вы нажмете. Уп
равление перейдет в следующую строку и там снова приостановит работу приложения.
- 79 -
Азбука Delphi: программирование с нуля
Рис. 7. 3. Окно просмотра и изменения значений переменных и выражений в случае несуществующей переменной
Рис. 7. 4. Окно просмотра и изменения значения переменной k
; ~: !\eik 8
Рис. 7. 5. Список наблюдаемых переменных
Вызовите еще раз окно Evaluate/Modify для переменной k. Теперь в поле Result будет находиться текущее значение переменной. Его при желании можно изменить. В данном случае менять его можно на
любое целое число из диапазона Integer. Однако в других случаях
- 80 -
Глава 7. Процесс отладки программы
возможны и более жесткие ограничения, которые программист должен
отслеживать самостоятельно, например, если переменная влияет на
индекс элемента массива, то неосторожное задание ее величины может
привести к выходу за границы массива, что крайне нежелательно,
если только программист не хочет постав11ть эксперимент «а что будет,
если?». Примерный вид окна Evaluate/Modify показан на рис. 7.4. Измените значение переменной k. Для этого введите новое значе
ние в поле New Value (новое значение) и нажмите Enter или кнопку Modify инструментальной панели. Переменная примет новое значение, и если теперь продолжить выполнение приложения, то на заголовке
кнопки вместо 8 появится введенное значение. Здесь же предоставляется возможность отслеживать не только значения переменных, но и
значения выражений с их участием. Если в поле Expressipn ввести вместо k выражение k + 10 и нажать кнопку Evaluate инструментальной панели, то в поле Res1Jlt появится число 18, а поле New value станет недоступным для редактирования. Здесь можно писать любые выражения, доступные языку Object Pascal, в том числе и лос~ческие, например, выражение k >О в поле Result выдаст true, а k :<j).-" ·-false.
Кнопка Watch в том же окне добавляет выбранную переменную в список наблюдаемых (Watch List) (рис. 7.5).
Основное достоинство этого списка в том, что его можко,размес
тить на пустом месте рабочего стола и, по мере продвижен~о коду, отслеживать изменения, происходящие со значениями вь:fбt1>анных переменных. В частности, его можно прикрепить к Окну редактора
кода в качестве дополнительного окна, для этого достат°'61о подта-• щить список мышкой в нижнюю часть окна редактора ко~·;.
Переменную в этот список можно добавить и без диалога:·~vаluаtе/ Modify. Достаточно установить на ней курсор и нажать Ctrl + F5.
Таким образом, останавливая работу программы в нужнь1х местах и следя за поведением переменных, можно обнаружить смЬiсловые
ошибки, которые по-другому найти очень проблематично. Однако
если контрольная точка находится в теле цикла с большим количест
вом повторений, то останавливать работу программы при каждом
повторении - довольно трудоемкая и практически бесполезная за
дача, если сбой происходит не при начальных значениях переменной
цикла. Для такой ситуации правильнее было бы поручить IDE, а точнее Отладчику (Debugger), приостановить работу программы, когда будет выполнено некое условие. Сделать это можно, щелкнув правой кнопкой мыши по контрольной точке Breakpoint properties. Откроется диалог (рис. 7.6), в котором можно задать условие приостановки работы программы в данной контрольной точке.
- 81 -
Азбука Delphi: программирование с нуля
Рис. 7. 6. Диалог задания условий для приостановки работы программы
В строке Condition можно написать логическое выражение. Когда оно примет значение true при прохождении этой контрольной точки, работа приложения приостановится. В строке Pass count можно задать количество повторов до остановки. Например, число 2 в этом поле означает, что выполнение программы будет приостановлено,
только когда эта точка будет проходиться во второй раз.
Описанные выше способы поиска логических ошибок кода помогают найти практически все ошибки, однако иногда полезнее исполь
зовать другой инструмент, предназначенный не совсем для этих целей.
Имеются в виду сообщения. Каждый пользователь Windows много раз видел маленькие окошки с различными сообщениями, предупреждениями, запросами и запр~тами. Как правило, эти сообщения не приносят положительных эмоций. У программиста в Delphi есть возможность поставить сообщения себе на службу. Строка
ShowMessage('k =' + IntToStr(k));
вызовет появление сообщения во время работы приложения (рис. 7. 7).
Рис. 7. 7. Сообщение, вызванное процедурой ShowMessage()
- 82 -
Глава 7. Процесс отладки программы
В текст сообщения может быть включена любая информация, как правило, это значения каких-либо переменных. Можно также встро
ить процедуру ShowMessage() в условный оператор, тогда сообщение будет выдаваться в предусмотренных случаях. Возможности ограничены только фантазией программиста и размером его монитора: не
стоит пытаться вписать в текст сообщения все значения массива размером 5Ох50. Как видите, варианты, которые можно реализовать с
помощью сообщений, в основном те же, что и для контрольных точек,
однако иногда сообщения проще и удобнее в условиях конкретной задачи.
В данной главе были рассмотрены наиболее часто используемые способы отслеживания нюансов работы вашего приложения. Как правило, контрольные точки с применением условий и сообщения,
будучи правильно примененными, помогают найти все тонкие места кода, из-за которых приложение может работать некорректно.
Контрольное упражнение
Выберите любое приложение из приведенных ранее в книге или
разработанных вами самостоятельно и дополните код таким образом,
чтобы можно было использовать все варианты работы с контрольны
ми точками (условные остановки, отслеживание текущих значений
переменных и выражений с их участием, индикация выполнения
определенного участка кода).
rпава~ 8. &11in1oтe1a компонентов Delpl1i (справочное пр1nоJКен1е)
Библиотека компонентов (Visual Component Library, VCL) Delphi включает множество стандартных классов объектов, с помощью которых разработчик создает свое приложение. Палитра компо
нентов расположена в правой части панели инструментов IDE (рис. 8.1).
Число вкладок или страниц в палитре больше, чем может быть
одновременно расположено на экране. Для того чтобы просмотреть ту часть, что скрыта за краем, можно воспользоваться кнопками со
стрелками, расположенными в правой части палитры.
Ранее уже упоминалось, что для размещения компонента на
форме необходимо открыть соответствующую страницу палитры, щелкнуть по компоненту левой кнопкой мыши, а затем щелкнуть примерно в том месте формы, где предполагается расположить объект. После размещения объект можно двигать по форме и растягивать/сжимать мышью, редактировать его положение и размер из
Инспектора объектов, менять свойства Align, Width и Height непосредственно в коде приложения. Размер объекта можно указать и в мо-мент вставки, если выделить мышью некоторую область. ·
По двойному щелчку компон~нт разместится в центральной час
ти формы. Если необходимо разместить несколько однотипных объектов на форме, это можно сделать, нажав Shift перед щелчком по компоненту в палитре. В этом случае каждый новый щелчок на фор
ме будет размещать там новый экземпляр выбранного компонента до
тех пор, пока не будет нажата кнопка со стрелкой в левой части па
литры.
Имена компонентов появляются на всплывающих подсказках.
В табл. 8.1 дано краткое описание закладок, включенных по умолчанию в палитру Delphi 6.
Рис. 8.1. Палитра компонентов IDE
- 84 -
Глава 8. Библиотека компонентов Delphl (справочное приложение)
Таблица 8.1
Компонент Описание
Standard Содержит наиболее часто используемые компоненты
Additional Дополнение к предыдущей панели
Компоненты, обеспечивающие взаимодействие с объектами
Win32 32-разрядного (кросс-платформенного) интерфейса. Выбрав в меню File-+ New - CLX Application, можно заметить, что вкладка Win32 заменяется вкладкой Common Controls
System Компоненты специализированного системного управления (таймер, плеер и т. д.)
Основные компоненты для организации доступа к базам данных* DataAccess без использования каких-либо специальных механизмов доступа,
таких как dbExpress, BDE, ADO и т. д.
Компоненты для отображения и редактирования данных. Используются при создании пользовательского графического
Data Control интерфейса приложений, работающих с базами данных, и могут применяться вне зависимости от того, какая технология
взята за основу при построении БД
Компоненты для организации доступа к БД. Рассчитаны на работу с базами 082, lnterBase, MySQL и Oracle. Работают только
dbExpress посредством SQL-зanpocoв, обеспечивают доступ к данным в кросс-платформенных приложениях. Это наиболее быстрый и наименее ресурсоемкий способ доступа к базам данных
Позволяет строить многоуровневые {работающие на нескольких машинах) приложения для работы с БД. Такие приложения
DataSnap обмениваются данными no локальной сети или по сети Интернет. Технология позволяет создавать, например, централизованное управление производственным процессом и подцержку
тонких (thin) клиентов
Компоненты для организации доступа к БД. Рассчитаны на работу ВОЕ (Borland с базами Paradox, dBase, FoxPro и текстовыми файлами. Database Подцерживают работу посредством SQL-эапросов с любыми БД. Engine) На настоящий момент технология устарела и поставляется
в основном для совместимости с более ранними версиями
Компоненты для организации доступа, позволяют работать с любыми хранилищами информации: текстовыми файлами,
ADO таблицами, базами данных. ADO работает с запросами SQL,
(ActiveX Data позволяет отслеживать изменения в источнике данных
(транзакции). Лучше всего подходит длs:~ работы с базами данных Object) Microsoft: MS SQL Serveг или MS Access. Использование ADO удобно при переносе на другой компьютер с ОС Windows: нет необходимости в дополнительно устанавливаемом ПО
* Компоненты Delphi позволяют работать только с реляционными БД.
- 85 -
Азбука Delphi: программирование с нуля
Продолжение табл. 8.1
Компонент Описание
Компоненты, реализующие непосредственный доступ к БД, lпterBase созданным на основе lпterBase. Работают с запросами SQL
и позволяют отслеживать транзакции
lпternet Поддерживает создание приложений для WеЬ-серверов
Содержит компоненты, обеспечивающие работу с различными сетевыми протоколами, среди которых: Интернет-протокол
FastNet TCP/IP, протокол передачи файлов FГР (File Traпsfer Protocol), протокол работы с гипертекстовыми документами НТТР, специальный протокол передачи новостей NNTP (Networking News Traпsfer Protocol) и др.
Поддерживает функции многомерного анализа данных.
Decision Cube Компоненты этой страницы позволяют представить информацию базы данных в более удобном для пользователя виде
Позволяет визуально разрабатывать отчеты. Поддерживает полосы, отступы, колонтитулы, заголовки, подведение итогов.
QReport Можно строить отчет из любого источника (таблицы, списки, выборки, массивы и т. д. ). Есть возможность предпросмотра для проверки результатов, автоматического выполнения расчетов
Содержит стандартные диалоговые окна Windows для обеспече-Dialogs ния привычного интерфейса вновь создаваемых приложений для
таких операций, как открытие, сохранение и печать файлов
Содержит компоненты, идентичные элементам управления Windows 3.1 для совместимости с приложениями, созданными
Win 3.1 с помощью ранних версий Delphi. Большинство из них поддерживает практически те же функции, что и более новые 32-битные компоненты
Примеры компонентов, которые программист может создавать
Samples и добавлять в палитру. Коды этих компонентов по умолчанию
находятся в директории \Source\Samples
ActiveX Объекты ActiveX. Полностью работоспособные портативные приложения, созданные независимыми разработчиками
СОМ+ Содержит всего один элемент, позволяющий приложению работать как автоматический контроллер загрузки данных в каталог СОМ+
lndy Clients lnternet Direct. Интернет-компоненты с открытым кодом, включаемые в популярные Интернет-протоколы
lndy Servers, Практические дополнения к предьщущему
lпdy Misc
Servers Оболочки для общих СОМ серверов
Как видите, Delphi предоставляет очень широкий спектр функций, реализуемых через компоненты библиотеки. Однако редко програм
мисты используют сразу все компоненты, и даже сразу все страницы
- 86 -
Глава 8. Библиотека компонентов Delphi (справочное приложение)
практически никогда не используются. Ниже рассматриваются ком
поненты, находящиеся на первых двух страницах палитры: Standard и Additional (табл. 8.2).
Компонент
Закрытый список
Таблица 8.2
Тип Описание
Компоненты страницы Standard
MainMenu
PopupMenu
Label
Edit
Memo
Button
CheckBox
RadioButton
ListВox
Создает главное меню для формы. Чтобы получить доступ к пунктам меню, необходимо добавить экземпляр на форму и вызвать Дизайнер меню (Menu Designer) двойным щелчком мыши
Создает всплывающее меню, вызываемое при нажатии правой кнопки мыши. Чтобы получить доступ к пунктам меню, необходимо добавить экземпляр на форму и вызвать Дизайнер меню (Menu Designer) двойным щелчком мыши
Объект для вывода на форму текста, который пользователь не может изменить
Поле редактирования текстовой информации. Здесь пользователь может ввести или изменить однострочный текст
Поле редактирования текстовой информации. Здесь пользователь может ввести или изменить многострочный текст
Кнопка с надписью, обычно используется для инициации определенного действия
Флажок, который пользователь может выставлять как Да/Нет или true/false. Используется для представления группы настроек, которые не являются
взаимоисключающими: можно выбрать больше одного флажка в группе
Флажок, который пользователь может выставлять как Да/Нет или true/false. Используется для представления группы настроек, являющихся взаимоисключающими:
можно выбрать только один флажок в группе
Выпадающий список выбора с полосой вертикальной прокрутки. Чтобы сформировать список, необходимо добавить экземпляр на форму и нажать кнопку с многоточием возле пункта ltems в Инспекторе объектов
- 87 -
Азбука Delphi: программирование с нуля
Компонент
Комбинированный список
Нете кетовая таблица
Продолжение табл. 8.2 Тип Описание
Выпадающий список выбора, объединенный с полем ввода (Edit). Пользователь может ввести данные в поле или выбрать из списка.
ComboBox Чтобы сформировать сnисок, нужно добавить экземпляр на форму и нажать кноnку
ScroolBar
GroupBox
RadioGroup
Panel
с многоточием возле пункта ltems в Инспекторе объектов
Используется для прокрутки содержимого окон и форм. В обработчике событий OnScroll программистом определяется, что именно
происходит при изменении положения
бегунка. Также используется для предоставления возможности выбора значения величины из заданного диапазона
Контейнер. У помещенных на него компонентов есть возможность группового
редактирования опций
Контейнер, содержащий радиокноnки. Удобный способ задания количества и состава радиокнопок, последующего обращения к их свойствам из кода приложения
Панель, которая может содержать другие компоненты. Обычно используется для создания инструментальных панелей и строк статуса
Компоненты страницы Additiona/
Кнопка, на которой могут быть размещены
BitBtn изображение и заголовок
Кнопка, которая может содержать иконку (glyph), но обычно не содержит заголовок.
SpeedButton SpeedButton часто компонуются в группы на объекте Panel для создания инструментальной панели
Похож на компонент Edit, но обеспечивает ввод и редактирование данных
MaskEdit в соответствии с некоторой маской (время, телефонные номера)
Таблица, используемая для отображения
StringGrid строковых (текстовых) данных
DrawGrid
Таблица, используемая для отображения нетекстовых данных
- 88 -
Глава 8. Библиотека компонентов Delphi (справочное приложение)
Рамка
(····.!, х- ~: ~i; - ~ ·~: ';'у - • _.:i
Контейнер с прокруткой
Список флажков
График
lmage
Shape
Bevel
Элемент для размещения на форме содержимого графических файлов
Самостоятельный rрафический примитив, используемый для отображения графических форм
Линия или контейнер с трехмерным оформлением
Контейнер с изменяемыми размерами, в котором, при необходимости, отображаются
ScroolBox линейки прокрутки
Прокручиваемый список (похожий на
CheckBoxlist ScroolBox), в котором каждый элемент списка представляет собой CheckBox
Разделитель, часто используемый на форме между двумя элементами управления,
Splitter чтобы позволить пользователю изменять мышью размеры элементов во время работы приложения
Не редактируемый текстовый компонент, похожий на метку (label). Как правило,
StaticText используется для обеспечения пользователя информацией о состоянии приложения в текущий момент
Основа для размещения инструментальных панелей с возможностью динамического
ControlBar перемещения их мышью относительно друг друга в процессе работы приложения
labeledEdit
ColorBox
Chart
Компонент Edit со встроенным компонентом Label
Комбинированный список, позволяющий пользователю выбрать цвет
Компонент для вывода графиков
Перечисленные основные компоненты палитры можно условно
объединить друг с другом по их функциональности. А в рамках группы рассмотреть их различия и сделать вывод о том, какой элемент
- 89 -
Азбука Delphi: программирование с нуля
в каких случаях применять. Однако одно свойство нужно упомянуть до такого разделения, поскольку оно является общим для всех объ
ектов. Это имя объекта (Name) то уникальное (в пределах формы) обозначение, которое присваивается объекту при размещении его на форме и может быть задано программистом. Используя имя объекта в процессе работы, подпрограммы обращаются к свойствам и методам этого объекта следующим образом:
<Имя>.<Свойство>
Еще одним общим свойством всех объектов является свойство Tag. Оно не несет изначально никакой целевой нагрузки и может быть использовано для хранения каких-либо данных вспомогательного характера.
8.1. Меню Первыми в списке идут два типа меню. Создание их довольно
однотипно, к тому же, как правило, всплывающее (PopUp) меню не добавляют в приложение, в котором отсутствует главное (Main) меню. Практически в любом Windоws-приложении присутствуют главное и всплывающее меню~ что избавляет от необходимости уточнять, для чего они применяются.
Особенностью MainMenu является то, что его пиктограмма в процессе работы приложения не видна. Поэтому располагать пиктограм~ му нужно так, чтобы она просто не мешала располагать другие объекты. В процессе заполнения меню (и при запуске приложения) его компоненты появляются в верхней части формы.
Несмотря на то что в приложении отображается только одно меню, объектов этого типа на форме может быть несколько. В различных режимах работы приложения часто требуются различные наборы функций, отображаемые в главном меню. Самый простой способ удовлет
ворить эту потребность~ создать несколько вариантов главного меню и своевременно их сменять. Делается это путем присвоения свойству Menu формы приложения имени соответствующего варианта:
MainMenн2;
Имена пунктов и страниц меню хранятся в свойстве ltems, которое заполняется с помощью конструктора (рис. 8.2 а).
Конструктор предоставляет довольно широкий спектр команд при . работе над созданием или редактированием главного меню. Такие команды, как вставка или удаление пункта, добавление подменю или перетаскивание пунктов мышкой, позволяют в кратчайшие сроки создать меню практически любой разумной сложности.
- 90 -
Глава 8. Библиотека компонентов Delphi (справочное приложение}
а 6
Рис. 8.2. а Конструктор главного меню; б свойства пункта меню
Набор свойств :каждого пункта меню (рис. 8.2 б) позволяет реализовывать любые эффекты, встречающиеся в главных меню типичных приложений.
AutoCheck определяет автоматическое изменение свойства Checked при выборе пункта (опции) меню.
Checked определяет состояние опции как объекта типа CheckBox: если установлено true, рядом с опцией появляется га~очка.
AutoHotkeys определяет, будет ли программа отслеживать уникальность :клавиш быстрого вызова для подопций:
maAutomatic - проверка автоматически при создании подопции;
maManual - проверка с помощью вызова специального метода
(RethinkHotkeys ); maParent - проверка по правилу, установленному для опции
верхнего уровня.
91
Азбука Delphi: nроrраммирование с нуля
AutoLineReduction задает автоматическое удаление лишних разделителей между опциями многоколончатого меню второго уровня.
Возникновение лишних разделителей возможно) например, при ди
намическом построении меню в процессе работы приложения. Принимает те же значения, что и AutoHotkeys; для проверки использует метод RethinkLines.
Bitmap позволяет установить рядом с пунктом меню иконку, иллюстрирующую ее содержимое.
Break позволяет создать многоколончатый список подменю, уста-навливает несколько разновидностей разрывов раздела на столбцы:
mbNone - значение по умолчанию, разделение запрещено;
mbBarBreak - колонки разделяются линией;
mbBreak - колонки разделяются расстоянием.
Caption содержит заголовок пункта меню. EnaЫed, будучи установленным в значение false, делает соответ
ствующий пункт меню серым и не реагирующим на щелчки мыши.
Radioltem, установленное в значение true, позволяет работать с пункгами меню, имеющими одинаковые значения свойства Grouplndex, как с объектом RadioGroup. (Свойству AutoCheck необходимо предварительно задать true.)
ShortCut позволяет выбрать сочетание клавиш для быстрого вызова пункта меню из предложенных.
VisiЫe, установленное в значение false, делает опцию невидимой. Основное событие для этого объекта OnClick, соответствующее
щелчку левой клавиши мыши на пункте меню.
Компонеmу палитры PopUpMenu соответствует контекстное меню, которое вызывается щелчком правой клавиши мыши на некотором
объекте формы. В него обычно включают те команды главного меню, которые чаще всего используются при работе с данным объектом. Компонент, так же как и MainMen1J, является невизуальным. Контекстных меню может быть несколько для каждой формы. Все оконные компоненты имеют свойство (PopUpMenu), которое указывает, какое контекстное меню, из размещенных на форме, будет вызываться по щелчку правой кнопки мыши. При первоначальном размещении объекта на форме свойство PopUpMenu пусто, и его можно задать, выбрав контекстное меню из выпадающего списка, содержащего все разме
щенные на форме контекстные меню. Формирование контекстного меню происходит практически так же,
как и в случае с главным меню. Однако существует возможность ускорения этой работы путем копирования через буфер элементов главного меню, поскольку в контекстном меню, как было отмечено выше, обычно повторяются наиболее часто используемые пункты главного меню.
- 92 -
Глава 8. Библиотека компонентов Delphi (справочное приложение)
8.2. Palioтa с текстом К компонентам для работы с текстом относятся Label, StaticText,
Panel (в качестве дополнительной возможности может содержать текст, хотя в основном этот компонент применяется для группировки
элементов управления), Edit, MaskEdit, LabeledEdit, Memo, StringGrid. Первые три компонента из перечисленных выше обычно используются для отображения различных надписей на форме. Рассмотрим их подробнее.
Текст, отображаемый в компоненте, определяется содержимым
свойства Caption. Как и большинство остальных свойств, его можно задавать при проектировании формы или программно в процессе работы приложения. Например:
Labell.Caption :='Текст для размещения на форме';
Апострофы указывают, что свойство имеет строковый тип, а значит, и присваиваться ему могут только строковые значения. Если же необходимо вывести в метку числовую информацию, то применяют уже использованные в предыдущих главах функции IntToStr(Integer) и FloatToStr(Float). К строке в правой части выражений, аналогичных приведенному выше, применимы все функции для работы со строковым типом данных.
Для метки (Label) единственно доступными возможностями оформления надписи являются свойства Color (определяет цвет фона для текста) и Font (определяет параметры используемого в объекте шрифта). StaticText и Panel, помимо этого, имеют еще и свойство BorderStyle, определяющее рамку вокруг текста. А компонент Panel, ко всему прочему, имеет еще и свойства Bevellnner, BevelOuter, BevelWidth, BorderWidth, которые представляют достаточно широкие возможности по оформлению надписи.
Положение и размеры визуальных компонентов (а все компоненты для работы с текстом являются визуальными) задаются свойствами Тор, Left, Width и Height. Первые два определяют координаты левого верхнего угла объекта относительно левого верхнего угла клиентской области формы или панели, на которой объект расположен. Последние два - ширину и высоту объекта в пикселях.
Также можно задать несколько стандартных вариантов расположения объекта вдоль одного из краев клиентской области путем задания соответствующего значения свойства Align.
Свойство Alignment определяет форматирование текста по левому краю, по правому краю или по середине.
Свойство WordWrap определяет допустимость переносц,_, если длина текста превышает соответствующий размер объекта.
- 93 -
Азбука Delphi: программирование с нуля
Отметим, что если в значение свойства Caption входит символ &, то в заголовке следующая за символом & буква или цифра отображается подчеркнутой. Нажатие подчеркнутого символа на клавиатуре при
нажатой клавише Alt приводит к переносу фокуса на объект формы, который указан в свойстве FocusControl.
Если рассмотренные выше объекты предназначены в основном
для вывода текста, то все остальные: Edit, MaskEdit, LabeledEdit, Memo, StringGrid - служат для ввода, редактирования и вывода тек
стовой информации в том или ином виде. Наиболее простой компонент поле ввода Edit. Благодаря этому он применяется чаще всего в различных диалогах и формах для ввода и редактирования тексто
вых данных. У него отсутствует свойство Caption, однако имеется его аналог свойство Text. Для того чтобы в поле появился какой-либо текст, достаточно присвоить этому свойству необходимоt; значение
программно:
Editl.Text : 'Текст, вводимый на форме';
или в соответствующем поле Инспектора объектов. Однако чаще Edit используется для считывания текста.
var Str: String; begin
Str := Editl.TeXti end;
Впоследствии с этим значением можно производить любые операции, предусмотренные для типа String. Например, вывести в любой другой объект:
var Str: String; begin
Str := Editl.Text; Edit2.Text := Str; Labell.Caption : Edit2.Text;
end;
В рассмотренном примере текст, находящийся в объекте Edit 1, будет выведен на форму с помощью двух других объектов: Edit2 и Label 1.
При вводе данных в Edit пользователю предоставляются основные возможности работы с текстом, характерные для редактирования текста в текстовых редакторах. Есть возможность выделения текста, пос
ледующей работы с буфером обмена, отмены последней операции
(UnDo ). Имеются также свойства, недоступные из Инспектора объектов: Sellength, SelStart, SelText. Они содержат длину выделенного фрагмента, номер первого выделенного символа, сам выделенный текст.
94 -
Глава 8. Библиотека компонентов Delphi (справочное приложение)
Иногда бывает полезно вывести текст, который пользователь не имеет права менять. Это можно сделать, и используя поле ввода.
Необходимо установить свойство ReadOnly равным true, а свойство AutoSelect false. В этом случае окно ввода начинает выполнять функции метки, однако позволяет размещать текст, превышающий
длину самого поля с возможностью прокрутки текста для его про
смотра. Метка таких возможностей не имеет. Аналогично свойству Caption, свойство Text работает только с тек
стовыми данными. Для преобразования в текстовый вид используются приведенные выше функции IntToStr и FloatToStr, а также функции обратного преобразования StrTolnt и StrToFloat.
Свойство Maxlength определяет максимальное количество вводимых символов. Если значение установлено равным нулю, то длина
не ограничена.
Свойство Modified доступно только из кода приложения и показывает, был ли изменен текст, содержащийся в поле ввода.
PasswordChar позволяет применять Edit для ввода паролей. Если значение этого свойства отлично от начального значения #0, то вместо символов, вводимых пользователем, в поле Edit будут появляться символы, указанные в свойстве PasswordChar. Как правило, это * или #.
Компонент MaskEdit отличается от Edit только тем: что в нем можно задать маску вводимой строки. Это осуществляется с помощью
свойства EditMask, которое состоит из трех полей, разделенных точкой с запятой;. Первое поле содержит специальным образом заданную маску ввода (табл. 8.3). Второе устанавливается в 1 или О и определяет, будет ли свойство Text содержать вводимые пользователем данные вместе с символами маски ИJIИ без них. Третье определяет символ, показывающий позиции, которые еще не заполнены пользо
вателем.
Ввести маску можно непосредственно в соответствующее поле
Инспектора объектов, а можно нажать многоточие справа от этого
поля и вызвать редактор масок (рис. 8.3). Поэкспериментируйте с этим редактором. Логика его работы
очень проста и полностью отражена в приведенной выше таблице.
Компонент LabeledEdit представляет собой простое объединение метки (Label) и поля ввода (Edit). Его свойства в основном повторяют свойства поля ввода, в том числе Text. А Editlabel содержит основные свойства метки (Label)~ в том числе Caption.
Компонент Memo - это поле для ввода, редактирования и выво
да мноrострочного текста. Фактически этот компонент является рас
ширением компонента Edit. Как следствие, Memo обладает возможностями работы с буфером обмена и отмены последней операции
- 95
Азбука Delphi: программирование с нуля
(UnDo ). Параметры шрифта одинаковы для всего текста и определяются свойством Font. (Существует и более продвинутая версия окна редактирования многострочного текста: RichEdit, которая позволяет работать с текстом ~ «обогащенном» формате RTF.)
Таблица 8.3
Служебные символы Значение EditMask
L* е этого символа должна быть буква
А На этом месте должна быть буква или цифра
с Любой неnустой символ
о В данной позиции может быть только цифра
9 В данной позиции может быть цифра, но позиция может остаться пустой
# . ~ " Могут быть только цифра, знаки+/- либо пустой символ ~···}
Позволяет следующий символ рассматривать ' \ не как служебный, а как текстовый
Используется для разделения часов, минут и секунд в
соответствующих шаблонах. Если в настройках системы •,
установлен другой символ-разделитель, будет использоваться он \
' .) Применяется для разделения года, месяца 1 и дня в соответствующих шаблонах •,
' Используется для разделения полей маски
Автоматически вставляет пробел в текст. При вводе текста эта позиция пропускается курсором
Определяет подавление пробелов: если символ есть ! в любом месте маски, подавляются пробелы перед текстом;
в противном случае идущие после текста
> Преобразует последующие символы до конца строки или до символа< в заглавные i
< Преобразует последующие символы до конца строки или до символа > в строчные
<> Изменение регистра не осуществляется
Такие свойства, как Alignment и WordWrap, для компонента Memo имеют тот же смысл, что и для меток, и определяют выравнивание
текста и допустимость переносов длинных строк. ReadOnly и Maxlength также аналогичны рассмотренным выше. WantReturn и WantTab~ установленные в значение false, запрещают вводить в текст символы перевода строки и табуляции. ScroolBars указывает наличие полос прокрутки текста.
* Наряду с заглавными буквами используются и строчные, обладающие допопнительной возможностью размещения пустого символа.
- 96 -
Глава 8. Библиотека компонентов Delphi (справочное приложение)
E:>(tension Social Security S hort Zip Code Long Zip Code Date
Рис. 8.3. Редактор масок ввода
позволяет изменять текст на эт аnе
проект~.iрования формы
Рис. 8.4. Редактор списка строк
(415]555·1212 15450 555.55.5555 90504 90504·0000 06.27.94 09:05:15РМ
13:45
Lines является основным свойством рассматриваемого объекта. Оно имеет тип TString и содержит строки текста из поля редактирования. Существует несколько способов задания текста н;а этапе про
ектирования: ввод непосредственно в соответствующее поле Инспек
тора объектов с помощью Редактора списка строк (рис. 8.4) или посредством Окна редактора кода _Code Editor.
Получить доступ к содержащемуся в объекте тексту можно с помощью Lines.Strings[] или Memo1.Text. Например, строка
ShowMessage(Memol. nes.Strings[O]);
вызывает появление сообщения, содержащего первую строку ( нумерация строк начинается с О).
ShowMess (L'1епю1. х~) ;
- 97 -
Азбука Delphi: программирование с нуля .
в отличие от предыдущего примера, вызовет появление сообщения со всем текстом, содержащимся в поле редактирования Memo.
Стоит учесть, что построчный доступ при включенном переносе слов (WordWrap) малополезен, поскольку содержимое строки существенно зависит от изменения размеров поля ввода Memo. Например, если задано:
Memol.Align := alClient;
то текст, размещенный в нескольких строках при свернутом окне
приложения, вполне может вписаться в одну-две строки, когда окно
приложения будет развернуто на весь экран.
Свойство Count хранит количество строк текста и предназначено только для чтения.
Методы Clear и Add предназначены, соответственно, для удаления содержимого окна и добавления еще одной строки в конец списка.
Для работы с файлами предусмотрены методы LoadfromFile и SaveToFile.
Компонент StringGrid представляет собой таблицу, содержащую данные строкового типа. Данные могут быть доступны для чтения
или для чтения и записи одновременно. Таблица может иметь полосы прокрутки и фиксированные столбцы и строки, которые не про
кручиваются при использовании этих полос. В общем, этот компонент позволяет создавать таблицу, аналогичную наиболее часто встречающимся в различных приложениях.
С помощью свойства
Cells[ACol 1 ARow: Integer]: ring;
можно получить доступ к конкретной ячейке, а с помощью свойств
Cols[]: TStrings и Rows[]: TSt ngs
доступ к списку строк, содержащихся в указанном столбце и списку
столбцов, содержащихся в указанной строке, соответственно. Свойства ColCount и RowCount содержат количество столбцов
и строк. LeftCoJ и TopRow индексы самой левой верхней ячейки из
тех, что видимы на данный момент в результате прокрутки. А Col и Row содержат индексы выделенной ячейки.
Основным методом, используемым при работе, является OnSelectCell, срабатывающий при выделении.
В процедуру
procedure StringGridl ARow:
ее ll(Sender: TObject; ACol, ; var CanSelect: Boolean);
98 -
Глава 8. Библиотека компонентов Delphi (справочное приложение)
передаются индексы выбранной ячейки и логическая переменная,
характеризующая возможность ее выбора. Таблица StringGrid является производным объектом от таблицы
DrawGrid. Функциональность StringGrid расширена за счет добавления свойств и методов для работы с текстом (Cells, Cols, Rows). Остальные свойства этих таблиц схожи.
Компонент DrawGrid, как и многие другие компоненты в Delphi, имеет свойство Canvas (холст, канва), на которой можно размещать изображения, используя графические процедуры класса. Имеется
также метод CellRect, возвращающий координаты области, отведенной под указанную ячейку:
function CellRect(ACol, ARow: Longint): TRect;
Нумерация строк и столбцов начинается с нуля.
8.3. Кнопки Наиболее простой кнопкой является компонент Button, немногим
реже используется BitBtn. Свойства и методы у них в основном одни и те же, но второй компонент позволяет добавлять на кнопку поясняю
щую картинку.
Основное свойство кнопки - Caption, а основной метод-OnClick. Свойство Caption позволяет задать клавиши быстрого доступа. Если перед каким-либо символом заголовка поставить&, то этот символ
будет отображен подчеркнутым, а нажатие Alt +символ приведет к тем же результатам, что и нажатие кнопки. OnClick определяет поведение приложения в случае щелчка по данной кнопке.
Свойство Cancel, установленное в true, ассоциирует кнопку с нажатием клавиши Esc. Свойство Default, установленное в true, ассоциирует кнопку с нажатием клавиши Enter. Исключение представляет собой ситуация, когда в фокусе находится другая кнопка, в этом
случае при нажатии Enter будет выполнен обработчик события OnClick кнопки, которая находилась в фокусе.
Вышесказанное относится к обоим видам кнопок. Теперь рас
смотрим особенности кнопки класса BitBtn. Изображение размещается на этой кнопке посредством свойства
Glyph. Нажав кнопку с многоточием рядом с полем значения этого свойства в Инспекторе объектов, вы увидите диалог просмотра изображения (рис. 8.5).
При нажатии кнопки Load открывается стандартный диалог открытия файла, в котором нужно указать файл с расширением .bmp. После
- 99 -
Азбука Delphi: проrраммирование с нуnя
Рис. 8.5. Окно выбора картинки для свойства Glyph
этого достаточно нажать ОК, чтобы выбранное изображение появилось на кнопке. Файл может содержать несколько изображений (до четы
рех)*. Каждое изображение отображается при соответствующем статусе кнопки. Первое отображается по умолчанию, второе - когда кнопка недоступна, третье - когда нажата, четвертое при фиксации кнопки (SpeedButton) в нажатом состоянии. Количество изображений в загруженном файле можно узнать из свойства NumGlyphs.
Положение надписи и изображения на кнопке определяется свойством Margin. По умолчанию свойство равно -1, что соответствует нахождению заголовка и изображения в центре кнопки.
Свойство Layout задает относительное положение надписи и изображения, Spacing -расстояние между надписью и изображением в пикселях.
Свойство Kind позволяет выбрать одну из целого набора стандартных кнопок, что заметно ускоряет процесс разработки формы на начальном этапе, не лишая приложение при этом элементов оформления. В выбираемых типах заданы соответствующие. заголовки, введены изображения, также указаны некоторые другие свойства. Однако в
окончательном варианте приложения все использованные стандартные
варианты оформления лучше заменить собственными, поскольку ис-
* В этом случае изображения должны располагаться в файле rориз.онтально слева направо, быть одинаковой ширины и высоты.
- 100 -
Глава 8. Библиотека компонентов Delphi (справочное приложение)
пользование стандартных дизайнерских решений уменьшает возможности разработчика заранее предопределенным набором штампов.
Кнопка с фиксацией SpeedButton, так же как и предыдущая, имеет возможность размещения на ней изображения, но кроме этого имеет возможность быть зафиксированной в нажатом состоянии до наступления определенных событий.
У этой кнопки также есть свойство Caption, однако чаще всего оно остается пустым: значение кнопки полностью передается только
изображением. Особенностью этого вида кнопок является свойство Grouplndex
(индекс группы). Если он равен О, то у кнопки отсутствует фиксация. Если Grouplndex >О и при этoмAllowAllUp = true, то кнопка по щелчку нажимается и остается в нажатом состоянии до повторного щелч
ка. Если есть группа кнопок с одинаковым ненулевым значением
свойства Grouplndex и установленным свойством AllowAllUp, то одно-. временно нажатой может быть только одна из кнопок: при нажатии на другую кнопку из этой группы первая отщелкивается. Щелчок по нажатой кнопке приводит к ее освобождению. Если же Allow AllUp = false, то при щелчке на нажатую кнопку ничего не происходит, поскольку
состояние, когда ни одна кнопка из группы не нажата, запрещено.
Можно сказать, что в таком варианте кнопки работают аналогично радиокнопкам, которые ниже будут рассмотрены подробнее.
Состояние кнопки во время работы приложения можно узнать по свойству Down логического типа. Оно истинно, если кнопка зафиксирована в нажатом состоянии, и ложно в обратном случае.
Радиокнопки (RadioButton) - элементы, которые, будучи соб
раны в одну группу, предоставляют пользователю возможность вы
брать один из взаимоисключающих вариантов.
Проще всего создавать и редактировать такие группы, используя
компонент RadioGroup - панель группы радиокнопок. Эта панель
содержит регулярно расположенные радиокнопки (рис. 8.6). * Надпись вверху панели задается свойством Caption, а надписи
отдельных кнопок - свойством ltems, цмеющим тип TStrings. Щелкнув на кнопку с многоточием в соответствующем поле Инспектора
объектов, вы можете вызвать Редактор списка (String List Editor). Количество пунктов соответствует количеству введенных строчек.
Свойство Columns позволяет расположить кнопки в несколько столбцов. По умолчанию Columns = 1, т. е. кнопки размещаются в
одну колонку. Itemlndex содержит индекс выбранной кнопки. По умолчанию значение этого свойства -1. Это означает, что ни одна кнопка не выбрана.
* Регулярность ~равномерное расположение по высоте. - 101 -
Азбука Delphi: программирование с нуля
Иногда целесообразнее использовать нерегулярное расположение радиокно
пок, такую возможность дают объекты
RadioButton, расположенные на панели GroupBox. Эта панель только служит контейнером для других элементов, в данном
случае - радиокнопок, поэтому главным
Рис. 8.6. Объект RadioGroup ее свойством будет заголовок, аналогич-ный заголовку компонента RadioGroup.
Радиокнопки, размещенные на панели, имеют каждая свое свойство
Caption. Также они имеют свойство Checked, определяющее, выбрана ли та или иная кнопка. Специфика элемента управления позволяет
выбрать только одну радиокнопку. На этапе проектирования в Checked должно быть установлено значение true для одной из кнопок.
Радиокнопки можно размещать как на панели GroupBox, так и на обычной панели (Panel) или прямо на форме. Объединение их в группы будет происходить автоматически для всех радиокнопок, распо
ложенных на одном объекте.
Индикаторы с флажком (CheckBox) внешне схожи с компонентом RadioButton, однако имеют несколько другое назначение: если набор радиокнопок предоставляет пользователю выбрать один из
взаимоисклюЧающих вариантов, то каждый индикатор с флажком позволяет установить или снять какую-либо опцию. В данном случае
индикаторы группируются только внутренней общностью настроек, за которые они отвечают.
По умолчанию возможны два состояния индикатора: флажок установлен (State = cbChecked) и флажок снят (State = cbUnchecked), однако если установить AllowGrayed = true, то будет возможно и третье состояние - промежуточное ( cbGrayed), когда установленный флажок окрашен в серый цвет. Все эти свойства можно устанавливать ·· как во время проектирования, так и программно.
Индикаторы с флажком могут использоваться одновременно для
установки опций и для отображения их текущего состояния. Во время работы приложения состояние индикатора можно про
верять по значению свойства Checked. Оно истинно, если индикатор установлен (его свойство State равно cbChecked), и ложно в остальных двух случаях.
Как и у радиокнопки, содержимое и положение заголовка задает
ся свойствами Caption и Alignment. Аналогично компоненту RadioGroup, представляющему собой
простой способ группировки радиокнопок, существует компонент
CheckListBox (рис. 8.7), который позволяет быстро разместить на
- 102 -
Глава 8. Библиотека компонентов Delphi {справочное приложение)
форме несколько близких по смыслу индикаторов, а также выделить Их общей рамкой.
Элементы списка содержатся в свойстве ltems, имеющем тип TStrings, т. е. обращаться к отдельной строке (элементу списка) можно как к элементу массива по ее индексу. Нумерация начинается с
нуля. Изменить этот список во время работы мож-но, используя методы класса TStrings (см. Help).
Рис. 8. 7. Объект CheckListBox
Свойство Sorted позволяет сортировать список по алфавиту. Если свойство установлено, то каждый вновь добавляемый элемент будет располагаться не в конце списка, а в соответствии со своим наиме~
нованием.
Состояние же самих индикаторов отображается в свойствах Checked и State. Оба они являются массивами, только первое является массивом логического типа, а второе типа перечисления. Эти свойства можно
просматривать или устанавливать программно:
CheckListBoxl.Checked [О) := true; CheckListBoxl.State [10 cbGrayed;
В этом примере первый индикатор списка устанавливается в состояние «выбраю>, а одиннадцатый - в промежуточное состояние. Очень удобно проверять или устанавливать значения индикаторов с помощью циклов for.
Важной чертой компонента является событие OnClickCheck. Оно возникает при каждом щелчке пользователя по индикатору (измене
нии состояния). В этот обработчик не передается номер строки, в которой произошло изменение, но изменение индикаторов можно проверить из массивов, рассмотренных выше.
8.4. Списки К спискам на первых двух страницах палитры можно отнести та
кие компоненты, как ListBox, ComboBox, а также ColorBox (рис. 8.8). Основная их функция - позволять пользователю выбирать из списка одно значение (в случае ComboBox и ColorBox) или несколько значений (в случае ListBox).
Основным свойством для первых двух списков является массив значений ltems, аналогичный встречавшемуся ранее. У списка выбора цвета такого свойства нет, поскольку список цветов не изменяется.
Свойство MultiSelect компонента ListBox, установленное в true, определяет возможность выбрать несколько элементов списка. Если установлено свойство ExtendedSelect, то множественный выбор происходит с использованием клавиши Shift.
- 103
Азбука Delphi: программирование с нуля
ListBo:-i а
ь с
d
Рис. 8.8. Списки
Если выбрано несколько строк сразу, то проверить, какие именно
выделены, можно по свойству Selected[lndex: lnteger] - массиву
переменных логического типа. Если множественный выбор запрещен,
то можно воспользоваться свойством ltem 1 ndex, которое содержит номер выбранного пункта (нумерация с нуля); если ничего не выбрано, его значение будет равно -1.
Также есть свойство Columns, определяющее количество столбцов списка, и Sorted, устанавливающее сортировку элементов по алфави1у.
Компонент ComboBox может принимать несколько вариантов внешнего вида в зависимости от значения свойства Style. Существует пять возможных значений этого свойства:
1. csDropDown - выпадающий список с полем ввода для набора
текста вручную. Высота поля ввода и расстояние между пунк
тами списка определяются высотой текста.
2. csSimple - поле ввода. После начала ввода предлагается за
вершение из списка, если имеется пункт с совпадающими пер
выми символами.
3. csDropDownlist - выпадающий список без возможности руч
ного ввода текста. Пользователь может только выбрать один из
перечисленных в списке вариантов. Высота поля ввода и расстоя
ние между пунктами списка определяются высотой текста.
4. csOwnerDrawFixed - то же, что предыдущий, разница в том,
что высота поля ввода и расстояние между пунктами списка
задаются значением свойства ltemHeight.
5. csOwnerDrawVariaЫe - то же, что предыдущий, но пункты спис
ка могут иметь различную высоту. Этого можно достичь, изменяя
свойство ltemHeight в обработчике события OnMeasureltem. Для остальных стилей списка это свойство программно недоступно.
- 104 -
Глава 8. Библиотека компонентов Delphi (справочное приложение)
Свойство Text содержит введенный пользователем или выбранный из списка текст, ltemlndex - номер выбранного пункта, Maxlength определяет максимальное число вводимых пользователем символов.
Как и в предыдущем случае, имеется свойство Sorted. Список выбора цветов аналогичен по функциональным возмож
ностям комбинированному списку. Разумеется, в нем нет свойства
Style и нет возможности редактировать состав при проектировании формы.
8.5. Dстаnьные компоненты Объединять в отде.льные группы оставшиеся компоненты боль
шого смысла нет, поэтому просто рассмотрим их по порядку пере
числения в табл. 8.2.
ScrollBar Основные его свойства - Position, Min и Мах. Последние два
задают соответственно нижнюю и верхнюю границы диапазона, тог
да как первое задает текущее выбранное значение в этом диапазоне. Все три свойства - переменные целого типа. Например, если Min = О,
а Мах = 5, то Position может принимать только шесть значений (от О до 5), а ползунок, соответственно, шесть положений.
Свойство Кind, принимая значения trHorizontal или trVertical, задает горизонтальное или вертикальное положение объекта.
LargeChange задает количество пунктов, на которые перемещается ползунок при нажатии клавиш PgUp или PgDn. При нажатии стрелок вверх и вниз ползунок перемещается на 1 пункт.
PageSize.зaдaeт размер ползунка в единицах диапазона (от Min до Мах).
Компонент ScrollBar (рис. 8.9) предназначен для прокрутки содержимого окон и форм. В обработчике событий OnScroll программистом определяется, что именно происходит, если изменяется по
ложение бегунка. Поскольку большинство объектов содержат уже встроенные линейки прокрутки, ScrollBar чаще используется для предоставления возможности выбора значения из заданного диапа
зона: крайние положения ползунка соответствуют ГJ?аницам диапа
зона, а возможные промежуточные положения ползунка соответству
ют остальным значениям из этого диапазона.
Рис. 8.9. ScrollBar
- 105 -
Азбука Delphi: программирование с нуля
lmage Многие визуальные компоненты имеют свойство Canvas, оно поз
воляет рисовать на объекте с применением таких методов, как Аге, Ellipse, Floodfill, LineTo, Rectangle и многих других. Однако при рисовании таким способом возникают проблемы с обновлением изображения при частичном перекрытии его другими окнами. Приходится до
писывать обработчики событий, которые бы перерисовывали нанесенное изображение при каждом изменении, произошедшем с формой.
Компонент lmage освобождает программиста от этой задачи и самостоятельно отслеживает состояние изображения, перерисовывая
его при необходимости. Рассмотрим пример рисования на канве объекта Image 1:
Imagel.Canvas.MoveTo(20, 20); Imagel.Canvas.LineTo(SO, 50);
Здесь графический курсор сначала перемещается в точку с коррдинатами Х = 20 и У= 20. Координаты берутся относительно левого верхнего угла объекта Imagel. Второй строчкой приведенного примера рисуется линия из текущего положения графического курсора
(20, 20) в точку с координатами Х = 50, У= 50. С помощью стандартных методов рисования на канве можно со
здавать относительно сложные изображения. При этом не требуется
изменять код, если разработчик решит переместить область изображения в другое место формы, поскольку система координат переместится вместе с объектом Image 1.
Однако чаще этот компонент используют для вывода изображений
из файлов. В свойство Picture можно занести имя .bmp или .ico файла. Для
этого нужно нажать кнопку с многоточием справа от соответствую
щего поля в Инспекторе объектов. Откроется уже знакомый вам по компоненту BitBtn диалог открытия файла (см. рис. 8.5), в котором можно будет выбрать необходимый файл.
Также для сохранения и считывания изображения из файла во вре
мя работы приложения существуют два предопределенных метода:
procedure LoadFromFile(const FileName: String); procedure SaveToFile(const FileName: String);
например:
Imagel.Picture.LoadFromFile('C:\Bitmap.bmp');
С помощью свойств Stretch (растяжение изображения до размеров объекта класса Image), Proportional (пропорциональность растяжения),
- 106 -
Глава 8. Библиотека компонентов Delphi (справочное приложение)
AutoSize (автоматическое изменение размера объекта класса Image до размеров выбранного изображения) и Transparent (прозрачность всех пикселей, цвет которых совпадает с цветом нижнего левого пик
селя) можно определить внешний вид изображения. Свойство Center центрирует выбранное изображение на площади объекта.
Shape Компонент Shape используется для добавления на форму геомет
рических примитивов и имеет одноименное свойство, определяющее его форму. Его можно задавать как во время проектирования, так
и в процессе работы приложения:
Shapel.Shape : s rcle;
Данная строка изменит форму объекта Shape 1, сделав его круглым. Размеры определяются текущими свойствами Width и Height.
Свойство Brush определяет цвет и способ штриховки заполнения:
Shapel.Brush.Color : clGreen; Shapel.Brush.Style : bsDiagCross;
Эти строки устанавливают цвет заливки зеленым, а саму заливку
делают диагональной клеткой.
Как правило, этот компонент используется для декоративного
оформления формы или в тех случаях, когда на форме пользователю
предоставляется возможность манипулировать простейшими геомет
рическими объектами.
Bevel Этот компонент в отличие от компонента Panel не может исполь·
зоваться для группировки, например, радиокнопок, но позволяет
зрительно выделить несколько объектов общей рамкой. Свойства, использующиеся для задания внешнего вида компонента Bevel, Shape и Style.
ScrollBox Этот компонент предназначен для создания на форме области,
которая вмещает в себя объекты большие по размеру, чем место, занимаемое самой областью. Такой ход применяется для экономии места на форме или когда нельзя заранее сказать, какого размера
объект будет помещен на форму. Например, в объект рассматривае· мого типа может быть помещена метка с текстом, превосходящая по
размеру сам объект. В этом случае отображается только часть метки
- 107 -
Азбука Delphi: программирование с нуля
и появляются линейки прокрутки, позволяющие увидеть остальную ее часть.
Чтобы разместить в ScrollBox несколько объектов, полезно бывает увеличить контейнер, а после завершения размещения уменьшить
его обратно.
Splitter · Этот компонент является разделителем и применяется в тех слу
чаях, когда необходимо изменять размер панелей в ходе работы приложения. Для примера проведите следующий эксперимент:
1. Разместите на пустой форме панель (Panel). Задайте ее свойству Align значение alTop.
2. Запустите приложение и попытайтесь изменить размер панели,
растягивая ее вниз. Таким образом изменить размер панели нельзя, поэтому, скорее всего, у вас ничего не в_ыйдет. Закрой
те приложение.
3. Разместите на форме разделитель (Splitter).
4. Задайте его свойству Align значение alTop.
5. Задайте его высоту (Height) равной четырем пикселям.
6. Запустите приложение и попытайтесь перетащить мышкой разделитель вниз. Разделитель должен перетаскиваться, а вмес
те с ним должна изменяться высота панели.
Таким способом можно разместить целый ряд панелей, отделенных друг от друга объектами типа Splitter.
Одним из полезных свойств разделителя является свойство MinSize -минимальный размер панелей, между которыми находится этот ком-
понент.
Событие OпMoved наступает после того, как разделитель был
перемещен на новое место и пользователь отпустил кнопку мыши.
В обработчике этого события можно предусмотреть действия, необ
ходимые для корректной работы приложения, например, сортировку элементов на панели, проверку правильности их размеров и взаим
ного расположения.
ControlBar Этот компонент предоставляет широчайшие возможности по со
зданию инструментальных панелей. Достаточно разместить на нем,
как на панели, несколько более мелких компонентов. Задать им свой
ства DragMode = dmAutoшatic и DragKind = dkDock - и во время работы приложения их можно будет перетаскивать по этому контейнеру, меняя местами и располагая в любом порядке. А если задать
- 108 -
Глава 8. Библиотека компонентов Delphi (справочное приложение)
свойство AutoDrag панели как true, то при перемещении за границы контейнера элементы начнут отцепляться от панели и оформляться
в отдельные окна.
Chart Компонент Chart (рис. 8.1 О) происходит от типа TPanel и, как
следствие, имеет все свойства компонента Panel. Проще говоря, Chart - это стандартный компонент типа TPanel с большим набором дополнительных возможностей для вывода графиков. Chart является панелью для размещения на ней объектов типа TSeries.
Примечание Во встроенной справочной системе Delphi имеется довольно обширный и подробный раздел, который называется TeeChart User Guide.
Свойства панели графиков и самих графиков можно задавать на
этапе проектирования приложения с помощью многостраничного
диалога Editing Chart (рис. 8.11 ), который вызывается по щелчку правой кнопки мыши.
При создании панель появляется на форме пустой и сама не имеет средств для отображения последовательностей чисел в виде графиков. Панель имеет свойство Canvas, что позволяет использовать примитивную графику. Для отображения на панели графика нужно сначала создать объекты Series. Это можно сделать из диалога редактирования свойств диаграммы, страница Chart, вкладка Series, кнопка Add. Появляется еще один диалог, позволяющий выбрать вид отображения конкретного графика: линейный, точечный, круговая диаграмма, столбчатая диаграмма и т. д. При добавлении графиков на панель они отображаются в общем виде на форме. В окне запущен
ного приложения панель графиков будет оставаться пустой до тех пор, пока не будут указаны конкретные значения. Это можно сделать, используя метод AddXY. Например:
for i := О to 50 do Seriesl.AddXY(0.02 * Pi * i, Sin(0.02 * Pi * i), ", clRed);
Этот цикл попиксельно добавляет график синуса, отображаемый красным цветом. Аналогично можно брать значения из какого-либо массива значений или из файла с данными.
Для того чтобы очистить серию и тем самым стереть график и подготовить его к приему новых данных, применяется метод Clear.
Chartl. Ser ies [ 1] . Clear;
или
Seriesl.Clear;
- 109 -
Азбука Delphi: программирование с нуля
Рис. 8.1 О. Объект Chart
~ Pi 11 Series1
~~11 Serie.:~
Рис. 8.11. Диалог редактирования свойств графика
- 110 -
Глава 8. Библиотека компонентов Delphi (справочное nр~ложение)
Такая строка удалит первый график с панели; теперь в этот график можно вводить новые значения аргумента и функции.
Если необходимо предоставить пользователю возможность просматривать на выбор часть графиков из некоторого набора, то имеет
смысл использовать объекты CheckBox. Например, пользователь может выводить тот или иной график на панель, установив соответствую
щий флажок, и убирать, сняв его. Это можно реализовать, например,
следующим кодом:
if Checkboxl.Checked then Seriesl.Active := true
else Seriesl.Active := false;
Написав аналогичный код для остальных графиков, можно пре
доставить пользователю широкие возможности по анализу и сравне
нию данных.
В этой главе была рассмотрена работа практически со всеми компонентами первых двух страниц Палитры компонентов. Именно с
этими страницами начинающий пользователь работает больше всего. Остальные компоненты, как правило, либо представляют собой расширение компонентов первых двух страниц, либо применяются в очень специфических задачах (см. табл. 8.1 )~ которые не приходится решать начинающим разработчикам приложений в Delphi.
СИБИРСКОЕ УНИВЕРСИТЕТСКОЕ ИЗДА теnьсrво
Дпя писем: Тел./факс:
Отдел продаж:
630058, Poccиst. r. Новосибирск. а/я 134 (383) 332·52~32
[email protected] Москва: (495} 661-09-96 Новосибирск: (383) 330-50-19
книга - почтой: [email protected]
Информация дnя авторов, актуальный прайс-лист и подробное описание продукции издательства - на официальном сайте
www.sup99.ru
Бескоровайный Илья Викторович
АЗБУКА DELPHI: ПРОГРАММИРОВАНИЕ С НУЛЯ
Редактор О. И. Сал.мина Менеджер проекта О. И. Ca;r1Jmнa
Обложка В. Ю. Анттюв, О. И. Сш1.J1-1uна
Корректоры Л. А. Федотова, А.1. А. Русанова Комш>ютерная верстка М С. Дрожжин
Соответствует гигuенuческши требованиям к книж·ным изданuя."11. (сан.-эпuд. закл. }'{о 54.НС.05.953.П.013186.12.05 от 26.12.05)
Подписано в печать 19 .10.07. Формат 6Ох9О/1 б. Бумаrа газетная. ечать офсетная. Усл. печ. л. 7. Уч.-изд. л. 5. Тираж 3100 экз. Заказ № 1207
Сибирское университетское издательство
630058, Новосибирск, ул. Русская, 39
ОАО «Советская Сибирь» 630048, Новосибирск, ул. Немировича-Данченко, 104