Томский межвузовский центр дистанционного образования
Н.Ю. Хабибулина
ПРОГРАММИРОВАНИЕ ПОД WINDOWS Знакомство с DELPHI
Учебное пособие
ТОМСК - 2000
Министерство образования Российской Федерации ТОМСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ СИСТЕМ УПРАВЛЕНИЯ И РАДИОЭЛЕКТРОНИКИ (ТУСУР)
Кафедра компьютерных систем в управлении и проектировании (КСУП)
Н.Ю. Хабибулина
ПРОГРАММИРОВАНИЕ ПОД WINDOWS
Знакомство с DELPHI
Учебное пособие
2000
Хабибулина Н.Ю. Программирование под WINDOWS. Знакомство с DELPHI: Учебное пособие. − Томск: Томский межвузовский центр дистанционного образования, 2000. − 133 с.
Хабибулина Н.Ю., 2000 Томский межвузовский центр дистанционного образования, 2000
3
СОДЕРЖАНИЕ Цели и задачи курса ………………………………………………… 1. Основы программирования в WINDOWS ……………………… 1.1. Соглашения WINDOWS API ………………………………. 2. Класс и объект ……………………………………………………. 2.1. Инкапсуляция ……………………………………………….. 2.2. Наследование ……………………………………………….. 2.3. Полиморфизм ……………………………………………….. 2.4. Структура класса ……………………………………………. 2.4.1. Уровни доступа ……………………………………….. 2.4.2. Конструкторы …………………………………………. 2.4.3. Деструкторы …………………………………………... 2.4.4. Поля данных …………………………………………... 2.4.5. Методы ………………………………………………… 2.4.6. Что такое SELF? ………………………………………. 2. Знакомство с DELPHI …………………………………………… 2.1. Краткий обзор интегрированной среды разработки DELPHI ………………………………………………….…... 2.1.1. Инспектор объектов …………………………………... 2.1.2. Рабочее пространство DELPHI ………………………. 2.2. Первая программа (WINDOWS - приложение)……………. 2.3. Процесс создания программы в DELPHI. Проекты в DELPHI …………………………………………. 2.4. Главное меню и панель инструментов DELPHI ………….. 2.4.1. Пункт меню “File” …………………………………….. 2.4.2. Управление проектом ………………………………… 2.4.3 Пункт меню “Edit” …………………………………….. 2.4.4 Пункт меню “Search” ………………………………….. 2.4.5. Пункт меню “View” …………………………………... 2.4.6. Пункт меню “Project” …………………………………. 2.4.7. Пункт меню “Run” …………………………………… 2.4.8. Пункт меню Project | Options …………………………. 2.4.9. Конфигурация среды программирования (IDE) ……. 2.4.10. Панели инструментов DELPHI ……………………... 3. Библиотека визуальных компонентов ………………………….. 3.1. Обзор VCL …………………………………………………... 3.2. Классы приложения и формы ……………………………… 3.3. Классы компонентов ……………………………………….. 4. Свойства элементов ……………………………………………… 4.1. Управление свойствами визуальных компонент в режиме выполнения ……………………………………….
5 6 6 6 9 9 10 11 12 14 17 19 20 21 22 22 23 26 26 31 34 34 35 36 37 37 37 38 38 43 47 48 48 50 50 51 53
4
4.2. Примеры. Программа SHAPEDEM.DPR ………………….. 5. Методы в DELPHI ……………………………………………….. 5.1. Создание методов с помощью визуальных средств ……… 5.2. Структура модуля …………………………………………… 5.3. Передача параметров ……………………………………….. 5.4. Более сложные методы и управляющие элементы ……….. 5.5. Информация периода выполнения. Программа CONTROL3 ……………………………………. 6. События в DELPHI ………………………………………………. 6.1. Понимание событий ………………………………………… 6.2. Обработка сообщений WINDOWS в DELPHI ……………. 7. Работа с палитрой компонентов ………………………………… 7.1. Размещение нескольких копий компонента ………………. 7.2. Размещение компонента в центре формы ………………… 7.3. Стандартные компоненты ………………………………….. 7.4. Страница Additional ………………………………………… 7.5. Страница Dialogs ……………………………………………. 7.6. Страница System ……………………………………………. 7.7. Компоненты категории Win32 ……………………………... 7.8. Компоненты для работы с базами данных ………………... 7.9. Компоненты категории Win 3.1 ……………………………. 7.10. Компоненты категории Internet …………………………... 7.11. Компоненты категории Sample …………………………… 7.12. Компоненты категории ActiveX ………………………….. 8. Графические компоненты ……………………………………….. 8.1. Классы GDI …………………………………………………. 9. Формы ……………………………………………………………. 9.1. Формы для диалоговых панелей …………………………… 10. Модель многодокументного интерфейса ……………………... Список литературы …………………………………………………. Методические указания по выполнению контрольных работ …... Контрольная работа № 1 ………………………………………….... Контрольная работа № 2 ……………………………………………
53 68 68 71 72 77 81 84 86 90 93 93 94 94 97 99 100 102 102 103 103 103 103 104 112 113 113 114 115 116 116 121
5
ЦЕЛИ И ЗАДАЧИ КУРСА Основная цель курса — научить будущего специалиста применению новых технологий программирования на основе языка высокого уровня и использования существующего программного обеспечения. Для достижения указанной цели в процессе изучения дисциплины последовательно решаются следующие задачи: • в качестве базового языка изучается алгоритмический язык Object Pascal; • в качестве среды программирования изучается Delphi; • рассматриваются основные принципы объектно-ориентированного и визуального программирования; • изучаются возможности поддерживающего программного обеспечения системы Delphi. Изучение рассматриваемого курса рассчитано на один семестр. В ходе изучения курса студенты дистанционной формы обучения должны ознакомиться с предоставленным курсом лекций и по результатам изучения решить две контрольные работы (задания приведены в конце данного методического пособия). При этом необходимо проявить свое умение пользоваться дополнительной литературой и творческий подход к решению поставленной задачи. В конце семестра студенты по контрольным работам должны получить зачет, а по теоретической части сдать экзамен.
6
1. ОСНОВЫ ПРОГРАММИРОВАНИЯ В WINDOWS 1.1. Соглашения WINDOWS API С точки зрения программиста, Windows представляет собой набор подпрограмм, обращаясь к которым можно относительно просто создать эффектный интерфейс со всеми присущими Windows атрибутами (окнами, кнопками, полосами скроллинга и т.д.). Все процедуры и функции операционной среды, вызываемые прикладными программами во время работы, называется интерфейсом прикладной программы — API (от англ. Application Programming Interface). Программный интерфейс Windows включает более 800 функций, запомнить их крайне сложно, поэтому программист должен знать способ их записи (нотацию), чтобы по имени понять назначение функции или переменной. Нотация — это соглашение об именовании функций, переменных и констант в некоторой системе. Имена функций Windows построены по принципу «глагол–существительное», например: CreateWindow (Создать окно), LoadMenu (Загрузить меню), SendMessage (Послать сообщение). В основе имен переменных и параметров лежит венгерская нотация, которая названа так в честь родины ее создателя Charles Simonyi. В венгерской нотации имена переменных и параметров функции содержит префикс, описывающий тип данных или указывающий на способ использования переменной либо параметра. В качестве примера рассмотрим следующие имена: biSystemMenu, biMaximize, biMinimize — данные параметры могут быть использованы для задания типа BorderIcons (значков окна Windows); clRed, clWhite, clBlue — данные параметры используются при установке цвета (Color). 2. КЛАСС И ОБЪЕКТ Создание любых Windows — приложений базируется на объектной технологии. Прикладные программы, пользуясь услугами Windows, создают различные объекты и управляют ими. Типичными объектами являют окна, меню, пиктограммы, диалоговые блоки, таймер и т.д. Даже выполняемая пользовательская программа рассматривается как объект системы Windows. Не трудно догадаться, что управлять окнами и множеством других объектов программы — задача не из простых. Для ее решения в Windows используется Механизм обработки событий. Под событием понимается факт свершения элементарного действия, от которого может зависеть ход выполнения программы. Например, собы-
7
тиями являются: нажатие клавиши на клавиатуре, перемещение мыши, истечение заданного промежутка времени. Чтобы разобраться в понятиях класса и объекта в программировании, рассмотрим сначала пример из нашей обыденной жизни. В реальном мире нас окружают различные объекты — в некотором смысле самостоятельные образования, которые обладают теми или иными параметрами и которые либо сами выполняют какие-либо действия, либо над ними можно выполнять какие-то действия. К таким объектам относятся окружающие нас предметы, животные, да и сами люди. Каждый объект, таким образом, характеризуется набором параметров и набором действий. Так, например, объект стол обладает параметрами, определяющими его габариты, качество (тип древесины), цвет и т. д. Стол можно создать (смастерить), передвигать с места на место, на него можно ставить другие предметы, наконец, его можно уничтожить, но в любом случае он представляет собой нечто целое, пока существует. Объекты можно расклассифицировать. Так, можно рассматривать класс столов — объектов, обладающих некими общими характеристиками. Каждый стол будет представителем класса столов. Этот класс и будет задавать общие характеристики всех столов. Можно пойти дальше и рассмотреть класс мебели, в который входит и класс столов. Все свойства класса мебели одновременно являются и свойствами класса столов, например, ножки, которых может быть и три, и четыре, и может быть другое количество. Однако класс столов обладает некоторыми специфическими, только ему присущими свойствами, например, стол может раздвигаться или не раздвигаться. Есть некоторые действия, которые можно выполнять с любой мебелью, в том числе и со столами. Например, мебель можно чистить. Однако разные представители мебели чистятся по-разному, например стол можно просто протереть, а мягкую мебель придется чистить с помощью пылесоса. Таким образом, для разных видов мебели придется уточнить понятие чистки, хотя оно будет всюду называться одинаково. Отсюда можно сделать вывод, что класс столов является детализацией класса мебели, он как бы порождается классом мебели и наследует все его свойства и действия, может быть, с некоторым уточнением. В этом смысле класс столов можно считать потомком класса мебели, а класс мебели, порождающий класс столов, - его предком. Далее можно рассмотреть класс изделий, в который будет включен класс мебели; и о классе изделий, и о классе мебели можно сделать такие же заключения, что и о классах мебели и столов. Таким образом, можно себе представить довольно сложную иерархическую структуру «родственных» отношений классов различных объектов. С другой стороны, объект стол служит для каких-то целей. Например, на него можно поставить посуду (которую, кстати, также можно считать
8
классом объектов, порождаемым классом изделий, упоминавшимся выше). В этом случае стол можно рассматривать как «владельца», «начальника» или «хозяина» этой посуды. Отношение «хозяина» с объектами«работниками» не следует смешивать с отношением родства «предок – потомок» так же, как мы не смешиваем родственные отношения между людьми и служебные отношения между ними. Подобные понятия существуют и в объектно-ориентированном программировании (ООП). Здесь также имеются понятия класса, иерархии классов, отношения «предок – потомок» и «хозяин – работник», наследование потомками свойств предков и т.д. Эта аналогия помогает проще понять терминологию ООП, которое и создавалось с учетом указанных отношений. В Delphi понятия «начальник – подчиненный» и «хозяин – работник» несколько отличаются друг от друга. Так же, как и в обычной жизни, отношение «начальник – подчиненный» создает целую иерархию взаимоотношений (один и тот же человек может быть начальником одних и подчиненным других людей). «Хозяин» же обычно один, а все остальные — его «работники». К сожалению, в Delphi отношение «начальник – подчиненный» называется отношением родства (Parent — родитель), так же, как и отношение классов. Чтобы не путать эти понятия, будем все-таки в дальнейшем это отношение называть отношением «начальник – подчиненный». В основе ООП лежат понятия класса (class), сочетающего в себе как данные, так и действия над ними, и его физической реализации — объекта. Класс является своеобразным типом и объявляется в разделе объявления типов. Он в некотором роде похож на тип-запись (record), но включает в себя не только поля данных, но также и подпрограммы для обработки этих данных, именуемые методами, а также так называемые свойства, сочетающие в себе характеристики полей и методов. Таким образом, в классе сосредоточены его характеристики и поведение. Объект же представляет собой переменную соответствующего класса и задается в разделе объявления переменных. Не для всех классов можно создать соответствующие объекты. Ряд из них, особенно стоящих в начале иерархического дерева всех классов, не позволяют это сделать, либо создаваемые на их основе объекты оказываются неработоспособными, например исходный класс TObject. Это связано с тем, что такие классы не дают всестороннего описания того или иного законченного объекта (как, например, нельзя создать просто образец мебели, а можно создать конкретный стол, конкретный стул и т. д.). Эти классы называются абстрактными и для них обычно характерно наличие так называемых абстрактных методов. Несмотря на эту особенность, создавать такие классы целесообразно, так как в них можно сосредоточить то общее, что характерно для всех представителей этого класса с тем, чтобы не по-
9
вторять это общее многократно во всех потомках. Идеи создания нового типа - класса были заложены уже при введении процедурного типа, отождествляющего данные и действия над ними. Фактически класс включает в себя, кроме данных, элементы процедурных типов, правда, несколько иначе оформленные и с расширенным набором особенностей. Введение нового типа данных потребовало пересмотреть некоторые концепции языка Паскаль: ввести новые понятия, как, например, «инкапсуляция», «наследование», «полиморфизм» и «виртуальность», новые зарезервированные слова, новые приемы работы с компонентами этого типа. ООП характеризуется тремя основными свойствами: инкапсуляцией (encapsulation), наследованием (inheritance) и полиморфизмом (polymorphism). 2.1. Инкапсуляция Инкапсуляция означает объединение в одном классе и данных, и действий над ними. Примером может служить перемещаемый по экрану отрезок, определяемый координатами своих концов (данные), и процедурой, обеспечивающей это перемещение (метод). При этом включенные в объект подпрограммы (методы), как правило, оперируют с данными этого объекта или обращаются к другим методам этого объекта или методам объектовпредков. Это позволяет объединить в одном месте все характерные особенности объекта, что облегчает понимание работы программы, ее отладку, модификацию. Принципы инкапсуляции предполагают также отказ от непосредственного обращения к полям данных класса, хотя это в ряде случаев и возможно. Для обращения к данным обычно используют соответствующие методы. Именно с этой целью введены в ООП и свойства, позволяющие обращаться к полям посредством тех или иных методов этого класса. 2.2. Наследование Наследование позволяет создавать иерархию классов, начиная с некоторого первоначального (предка) и кончая более сложными, но включающими (наследующими) элементы предшествующих классов (потомков). Эта иерархия в общем случае может иметь довольно сложную древовидную структуру. Каждый потомок несет в себе характеристики своего предка (содержит те же данные, методы и свойства), а также обладает собственными характеристиками. При этом наследуемые данные, методы и свойства описывать у потомка нет необходимости, а использовать можно. Это существенно упрощает запись схожих объектов, если установить между
10
ними наследственную связь. В качестве такой иерархии можно представить множество перемещаемых по экрану геометрических фигур. В основу такой иерархии можно положить класс, отвечающий за отображение на экране и перемещение по нему некоторой абстрактной геометрической фигуры. Хотя реальный объект такого класса создать и нельзя, этот класс все-таки может быть полезен, так как в нем можно сосредоточить характеристики, общие для всех классов, создаваемых на его основе (например, инициализация объектов, удаление объектов и т. д.). На основе этого класса можно создать класс отображения и перемещения отрезка прямой. Здесь дополнительно задаются координаты начала и конца отрезка и соответствующие методы, обеспечивающие перемещение отрезка и выполняющие какие-то другие действия с отрезком. Методы же, связанные с инициализацией объекта, удалением объекта и т. д., этот класс может заимствовать у своего предка. На основе этого класса можно создать класс отображения и перемещения многоугольника, состоящего из отрезков прямой линии, и т. д. С другой стороны, на основе исходного абстрактного класса можно создать класс отображения и перемещения окружности, для которой следует задавать ее центр и радиус. На основе этого класса, в свою очередь, можно создать класс отображения и перемещения круга - заполненной окружности и т. д. Один и тот же класс может быть потомком одних классов и предком других. У каждого класса может быть любое количество предков, при этом он наследует характеристики всех своих предков. Однако в Object Pascal (основа Delphi) у каждого класса существует только один непосредственный предок. Между непосредственным предком и самим классом нет никаких промежуточных классов. Непосредственных потомков же у любого класса может быть любое количество. Исходный класс и все его потомки, в том числе и не являющиеся непосредственными, будем называть семейством этого класса, а сам исходный класс - родоначальником этого семейства. В Object Pascal у всех классов имеется общий исходный предок TObject, родоначальник всех других классов. Этот класс обладает минимальным набором возможностей, пригодных для любых классов (быть может, с некоторыми уточнениями и переделками), в частности по созданию объектов, удалению объектов, получению общих характеристик класса и т.д. 2.3. Полиморфизм Полиморфизм означает, что для различных родственных классов можно задать единый образ действий (например, перемещение по экрану любой геометрической фигуры). Затем для каждого конкретного класса
11
составляется своя подпрограмма, выполняющая это действие непосредственно для него (естественно, что перемещение по экрану отрезка отличается от перемещения многоугольника, а перемещение многоугольника, в свою очередь, отличается от перемещения окружности и т. д.), причем все эти подпрограммы могут иметь одно и то же имя. Когда потребуется перемещать конкретную фигуру, будет выбрана из всего множества одноименных подпрограмм соответствующая подпрограмма. В этой возможности - иметь несколько подпрограмм с одним и тем же именем и имеющих одно и то же назначение, но для разных объектов - и заключается полиморфизм ООП. Вопрос, какая же конкретно подпрограмма будет использоваться в том или ином случае, определяется типом конкретного объекта, для которого следует выполнить соответствующие действия. Так, если объект является перемещаемым отрезком, то выбирается его подпрограмма, если перемещаемым многоугольником - его подпрограмма, и т. д. 2.4. Структура класса Обычно классы задаются в разделе описания типов или иных модулей и, чтобы их можно было использовать в программе и других модулях, эти объявления должны быть сделаны в интерфейсе модуля. Структура класса начинается с зарезервированного слова class, после которого в круглых скобках указывается непосредственный предок класса. Если предок не указан, предполагается, что им не является класс TObject. Далее обычно в виде отдельных строк записываются поля данных, методы (подпрограммы класса) и свойства. Завершается класс зарезервированным словом end. Для методов в объявлении класса записываются только их заголовки. Само же тело методов описывается впоследствии – в исполнительной части модуля. Определив тот или иной класс, можно затем ввести переменные этого типа, называемые объектами. Объекты вводятся так же, как и другие переменные. Основной особенностью объектов является то, что их можно создавать только динамически, а сама переменная представляет собой указатель на объект, который размещается в динамической памяти. Следовательно, прежде чем использовать в программе объект, его следует сначала создать, а после завершения работы с ним – уничтожить. Для создания объектов используются специальные методы класса – конструкторы, а для уничтожения – методы, называемые деструкторами. Хотя переменнаяобъект и является указателем, работа с ней происходит в дальнейшем как с обычной переменной, без использования символа ^. Обычно переменныеобъекты объявляются в интерфейсе модуля с тем, чтобы их можно было использовать в самой программе и других модулях.
12
Пример объявления элементов класса: type TForm1 = class (TForm) {Объявление класса-потомка через класспредок} Button1 : TButton; Button2 : TButton; procedure Button1Click (Sender : TObject); procedure Button2Click (Sender : TObject); private {Скрытые элементы} FNumber : Integer; protected {Защищенные элементы} procedure SetNumber (ANumber : Integer); virtual; public {Общедоступные элементы} Count : Integer; Published {Опубликованные элементы} property Number : Integer read FNumber write SetNumber; end; Для классов характерны следующие моменты: 1. Управление доступом 2. Конструкторы 3. Деструкторы 4. Поля 5. Методы (функции и процедуры) 6. Скрытый указатель специального назначения Self 2.4.1 Уровни доступа Уровни доступа класса влияют на то, каким образом он может быть использован. Классы имеют четыре уровня доступа: – Private – элементы, которые могут быть использованы только в пределах того модуля, где описан класс; – Public – элементы, которые могут быть использованы всюду в программе; – Protected – элементы, которые могут быть использованы только семейством класса, которые важны лишь для функционирования объектов конкретного класса и его потомков, но которые могут трансформироваться в потомках;
13
Published – элементы, которые могут быть использованы всюду в программе, но, кроме того, информация о них на этапе проектирования программы помещается в инспектор объектов. В условиях совместной разработки приложений чаще имеет место другая ситуация: один программист выступает в роли создателя класса, а все другие — в качестве его пользователей. В рамках любого класса может быть выделена как открытая (public) часть, доступная для внешнего мира, так и его закрытая (private) составляющая, представляющая собой внутреннюю реализацию класса. Садясь за руль автомобиля, водитель не стремится вникнуть во все тонкости его работы? Все что его интересует — это возможность безопасной езды. В этом случае руль, педали, рычаг переключения скоростей, спидометр и т. д. — это тот же открытый интерфейс, устанавливающий правила взаимодействия автомобиля и водителя. Последний хорошо знает, чем и как нужно управлять, чтобы автомобиль выполнял все, что от него требуется. И наоборот, двигатель, коробка передач, электропроводка автомобиля скрыты от постороннего взгляда. Двигатель предусмотрительно спрятан в надежном месте, где его никто не видит, если только специально не захочет этого. Эта деталь является несущественной с точки зрения ежедневно эксплуатации автомобиля — вот она и скрыта от вашего взора (является закрытой для водителя). Точно так же класс скрывает от пользователя свою внутреннюю реализацию. Итак, все внутренним процессы класса скрыты от пользователя, а открытым для него является лишь специальный пользовательский интерфейс. Несколько сложнее объяснить, что представляют собой защищенные (protected) элементы класса. К ним, как и к его закрытым элементам, пользователи класса обращаться не могут. Однако они вполне доступны для классов, являющихся производными от данного класса. Продолжая автомобильную аналогию, предположим, что необходимо усовершенствовать автомобиль и превратить его в удлиненный лимузин. Вот теперь потребуется кое-что узнать и о внутренней структуре автомобиля. Как минимум, необходимо знать, как следует модифицировать карданный вал и кузовную раму. Придется немного испачкаться и — в качестве проектировщика лимузина покопаться в деталях, которые раньше казались несущественными (эти детали защищенные элементы класса). Внутренние процессы двигателя будут по-прежнему скрыты, поскольку с точки зрения модификации кузова они не представляют интереса. Итак, в разделе protected класса содержатся элементы, знание о которых может потребоваться при его расширении. Уровень доступа published (опубликованный) играет заметную роль написании компонентов. Все элементы, объявленные в этом разделе, поя–
14
вятся на этапе проектирования в окне инспектора объектов. В Object Pascal уровень доступа к элементам класса может быть описан с помощью одного из следующих четырех ключевых слов: private, public, protected и published. Уровень доступа к структурные элементам класса задается во время его объявления с помощью ключевого слова class. Например: TVehicle = class private CurrentGear : Integer; Started : Boolean; procedure StartElectricalSystem; procedure StartEngine; protected procedure StartupProcedure; public HaveKey : Boolean; procedure SetGear (Gear : Integer); procedure Brake (Factor : Integer); procedure ShutDown; end; Совсем необязательно в каждом конкретном классе использовать все возможные варианты. Например, в приведенном примере отсутствует раздел published. В принципе можно вообще отказаться от применения уровней доступа, однако, обычно в объявлениях классов содержатся, по крайней мере, разделы private и public. 2.4.2. Конструкторы Классы в Object Pascal имеют особый метод, называемый конструктором. Конструктор — это метод, с помощью которого создаются экземпляры класса. Конструктор используется для инициализации элементов данных класса, выделения дополнительной памяти и других необходимых начальных действий. В только что приведенном примере класса TVehicle конструктор отсутствует. В этом случае можно воспользоваться конструктором базового класса (если специально не оговорено, все классы Object Pasсal являются наследниками класса TObject, который имеет конструктор Сгеаte. Именно он будет вызван в данном случае). Хотя в простых случаях использование конструктора базового класса вполне допустимо, для более
15
сложных классов почти всегда требуется специальный конструктор. Имя конструктора может быть произвольным, однако он обязательно должен объявляться с ключевым словом constructor. Именно оно служит его отличительным признаком. Включим в класс TVehicle объявление конструктора: TVehicle = class private CurrentGear : Integer; Started : Boolean; procedure StartElectricalSystem; procedure StartEngine; protected procedure StartupProcedure; public HaveKey : Boolean; procedure SetGear (Gear : Integer); procedure Brake (Factor : Integer); procedure ShutDown; constructor Create; {конструктор} end; Обратите внимание, что конструктор является методом специального вида. В его объявлении отсутствует возвращаемый тип, поскольку конструктор не может возвращать никакого значения. Если вы попытаетесь указать тип возвращаемого значения, то произойдет ошибка времени компиляции. У класса может быть несколько конструкторов. Это реализовывается следующим образом: можно просто присвоить новому конструктору другое имя, например: TVehicle = class { оставшаяся часть объявления класса } constructor Create; constructor CreateModel(Model : string); end; Здесь объявлено два конструктора: один под именем Create, а другой под именем CreateModel. А зачем вообще могут понадобиться несколько конструкторов? Дело в том, что в этом случае появляется несколько альтернативных возможностей для создания экземпляров класса. Например, класс может иметь как
16
обычный конструктор без параметров, так и конструктор, принимающий один или несколько параметров, предназначенных для инициализации полей данных определенными значениями. Предположим далее, что есть класс TMyRect, инкапсулирующий поведение прямоугольника. Этот класс может иметь сразу несколько конструкторов. Конструктор по умолчанию может, например, инициализировать все поля нулями, а еще один — допускать их инициализацию произвольными значениями. Объявление класса TMyRect выглядит следующим образом: TMyRect = class private Left : Integer; Top : Integer; Right : Integer; Bottom : Integer; public function GetWidth : Integer; function GetHeight : Integer; procedure SetRect(ALeft, ATop, ARight, ABottom : Integers); constructor Create; constructor CreateVal (ALeft, ATop, ARight, ABottom : Integers); end; А вот как должны выглядеть определения конструкторов: constructor TMyRect.Create; begin inherited Create; Left := 0; Top := 0: Right := 0; Bottom := 0; end; constructor TMyRect.CreateVaKALeft, ATop, ARight, ABottom :Integer); begin inherited Create; Left := ALeft; Top := ATop; Right := ARight; Bottom := ABottom; end;
17
Первый конструктор просто инициализирует каждое поле нулевым значением (На самом деле, инициализируя все поля нулями, конструктор Сгеаtе выполняет лишнюю работу. Дело в том, что при создании объекта класса все его поля данных обнуляются автоматически). Второй присваивает соответствующим полям значения переданных в него параметров. Переменные, содержащиеся в списке параметров, являются локальными для этого конструктора, поэтому, чтобы отличить их от полей класса, каждая из них начинается с буквы А (вообще использование префикса А является обычным для программ Delphi). Обратите внимание на ключевое слово inherited. Так когда же мы сможем воспользоваться нашими такими разными конструкторами? Ответ прост — во время образования экземпляра класса. Экземпляром класса называется объект, созданный в соответствии с объявлением класса. В следующем фрагменте кода происходит создание двух экземпляров класса TMyRect — одного с помощью конструктора Create, а другого с помощью конструктора CreateVal: var Rect1 : TMyRect; Rect2 : TMyRect; begin Rect1 := TMyRect.Create; Rect2 := TMyRect.CreateVal(0, 0, 100, 100); end; Память для классов Object Pascal всегда выделяется динамически, поэтому все переменные классов, по сути дела, являются указателями. Таким образом, Rect1 и Rect2 в предыдущем примере — это указатели на класс TMyRect. 2.4.3. Деструкторы Деструктор — это специальный метод, автоматически вызываемый перед разрушением объекта. Обычно он используется для освобождения всей выделенной объекту памяти и для других операций, связанных с очисткой. Присутствие деструктора в объявлении класса не является обязательным, поскольку вместо него может быть использован деструктор базового класса. Как и конструктор, деструктор не возвращает никакого значения. Хотя теоретически класс может иметь несколько деструкторов, на практике это встречается довольно редко. Если класс содержит только
18
один деструктор, необходимо присвоить ему имя Destroy. Ниже приведен (для краткости мы приводим его здесь не полностью): код класса TMyRect, дополненный деструктором. TMyRect = class private Left : Integer; Top : Integer; Right : Integer; Bottom : Integer; Text : PChar; { новое поле } public function GetWidth : Integer; function GetHeight : Integer; procedure SetRect(ALeft, ATop, ARight., ABottom : Integer) ; constructor Create; constructor CreateVal(ALeft, ATop, ARight, ABottom : Integer); destructor Destroy; override; end; constructor TMyRect.Create; begin inherited Create; { Выделяем память для ограниченной нулем строки. } Text := AllocMem(1024) ; end; destructor TMyRect.Destroy; begin { Освобождаем выделенную память. } FreeMem(Text); inherited Destroy; end; В конструкторе класса TMyRect происходит динамическое выделение памяти для строки с ограничивающим нулем Text (типа PChar), тогда как в деструкторе эта память освобождается. Обратите внимание на ключевое слово override. Оно сообщает компилятору, что вы переопределяете одноименный метод базового класса. Обычно вызов inherited является первым оператором в конструкторах и последним - в деструкторах.
19
2.4.4. Поля данных Поля данных — это переменные, объявленные в объявлении класса; можно рассматривать их как переменные, имеющие своей областью действия класс. Поля класса мало чем отличаются от полей записи за исключением того, что объявив их с ключевыми словами private, public или protected, можно контролировать доступ к ним. Любое поле доступно для всех методов своего класса, однако, при определенных условиях оно становится видимым и за его пределами. Например, закрытые и защищенные поля принадлежат только своему классу и вне его перестают быть видимыми. Напротив, к открытым полям вы легко можете обратиться и за пределами класса, но только через один из его объектов. Давайте снова вернемся к классу TMyRect. В нем нет открытых полей. Попытавшись выполнить приведенный ниже код, появляется сообщение об ошибке времени компиляции Undeclared identifier: 'Left' (необъявленный идентификатор: 'Left'): Rect := TMyRect.CreateVal(0, 0, 100, 100); Rect.Left := 20; ( ошибка компиляции! } Компилятор сообщает вам, что поле Left является закрытым, и вы не можете получить к нему доступ. Если бы оно было объявлено в разделе public, компиляция прошла бы успешно. Для управления доступом к закрытым полям в Object Pascal используются свойства. Свойства могут быть предназначены для чтения и записи, только для чтения или только для записи (причем последнее встречается довольно редко). Каждое свойство может иметь специальные методы чтения и записи, которые вызываются соответственно в момент чтения или записи в него информации. Однако применение этих методов не является обязательным, поскольку свойство может иметь прямой доступ к своему закрытому полю. При любом обращении к свойству происходит вызов соответствующих методов чтения или записи (разумеется, при их наличии). Особенно полезным является последний, поскольку он может быть использован для проверки корректности ввода или выполнения других важных задач в момент присвоения свойству значения. Таким образом, доступ к закрытым полям никогда не бывает прямым, он всегда осуществляется через свойства. Когда вы создаете несколько экземпляров класса, каждый из них содержит свои собственные данные. Например, переменной Left каждого из объектов могут быть присвоены совершенно разные значения: Rect1 := TMyRect.CreateVal(100, 100, 500, 500); Rect2 := TMyRect.CreateVal(0, 0, 100, 100);
20
Здесь было создано два экземпляра класса TMyRect. И хотя оба они идентичны по структуре, в памяти они хранятся раздельно. При этом каждый (объект содержит свой собственный набор данных — в первом случае в поле "Left хранится значение 100, а во втором 0). Это весьма напоминает выставку автомобилей: каждая модель изготовлена по одним и тем же чертежам, но все ее конкретные воплощения — самостоятельные объекты. Все они отличаются по цвету, обивке сидений, основным характеристикам и т.д. 2.4.5. Методы Методы — это процедуры и функции, принадлежащие классу. Они локальны по отношению к своему классу; их как бы не существует за его пределами. Методы могут быть вызваны только внутри самого класса или при посредстве его объектов. Они имеют доступ ко всем открытым, защищенным и закрытым полям класса и сами могут быть объявлены в одном из его разделов — private, protected или public. 1. Открытые (или общедоступные) методы, вместе со свойствами, представляют собой пользовательский интерфейс класса. Именно благодаря им пользователи класса получают доступ ко всем его функциональным возможностям. Допустим, что создан класс, предназначенный для проигрывания и записи звуковых файлов. В этом случае открытыми могут, например, оказаться методы Open (Открыть файл), Play (Проиграть), Record (Записать), Save (Сохранить), Rewind (Перемотать) и т. д. 2. Закрытые методы предназначены для внутреннего использования классом и не должны вызываться его пользователями; их, собственно, и закрыли для того, чтобы надежно спрятать от внешнего мира. Довольно часто при создании экземпляров класса требуется выполнить определенные начальные действия (вы уже знаете, что в момент создания класса вызывается конструктор). Иногда такого рода инициализация может быть весьма важной и занимать не один десяток строк программы. Чтобы как-то разгрузить конструктор, в классе для этих целей может быть предусмотрен специальный метод Init. Этот метод всегда должен вызываться из конструктора и никогда — напрямую пользователями класса. В последнем случае можно с большой долей уверенности ожидать каких-нибудь неприятностей. Таким образом, метод Init лучше всего объявить закрытым, сохранив тем самым и целостность класса, и нервы пользователей. 3. Защищенными называются методы, доступные только для классов, производных от данного (как и закрытые методы, они остаются недоступными для внешнего мира).
21
2.4.6. Что такое SELF? У каждого класса есть скрытое поле Self, которое представляет собой указатель на соответствующий экземпляр класса. Очевидно, что это определение требует некоторых пояснений. Сначала давайте посмотрим, как выглядело бы объявление класса TMyRect, если бы поле Self не было скрытым: TMyRect = class private Self : TMyRect; Left : Integer; Top : Integer; Right : Integer; Bottom : Integer; Text : PChar; public function GetWidth : Integer; function GetHeight : Integer; procedure SetRect(ALeft, ATop, ARight, ABottom : Integer); constructor Create; constructor CreateVal(ALeft, ATop, ARight, ABottom : Integer); destructor Destroy; override; end; Именно так класс TMyRect выглядит с точки зрения компилятора. При создании в памяти объекта класса указатель Self автоматически инициализируется адресом этого объекта: Rect := TMyRect.CreateVal (0, 0, 100, 100); { Теперь 'Rect' и 'Rect.Self имеют одно и то же значение, } {поскольку оба содержат адрес объекта в памяти. } Каждый экземпляр класса имеет собственную копию полей данных. При этом все объекты класса совместно используют одни и те же методы (нет никакого смысла дублировать эту часть кода). Как же компилятор узнает, к какому объекту относится тот или иной вызов метода? Для этой цели служит скрытый параметр Self, который имеют все методы данного класса. Чтобы проиллюстрировать это, добавим к классу TMyRect следующее определение функции GetWidth: function TMyRect.GetWidth : Integer; begin Result := Right - Left;
22
end; Но так эта функция выглядит для нас с вами. Компилятор же видит ее несколько иначе: function TMyRect.GetWidth : Integer; begin Result := Self.Right - Self.Left; end ; Конечно, с технической точки зрения это не совсем так, но для нашего обсуждения такая схема вполне приемлема. Как видите, основную роль здесь играет все тот же Self. Никогда не изменяйте указатель Self. Вы можете с его помощью передать другим методам указатель на ваш объект или использовать его в качестве параметра при создании других объектов, но никогда не изменяйте его значения! 2. ЗНАКОМСТВО С DELPHI Delphi представляет собой самый популярный программный продукт фирмы Borland, предназначен для скоростной разработки приложений Windows. С его помощью производство программного обеспечения для Windows становится довольно быстрым. В распоряжении разработчика оказывается вся мощь компилируемого языка программирования (Object Pascal), помещенного в оболочку средств скоростной разработки приложений (RAD). Это значит, что при проектировании интерфейса пользователя (сюда относятся различного рода меню, диалоговые панели, главное окно приложения и т.д.) разработчик имеет возможность воспользоваться механикой drag-and-drop. И все это разработчик получает практически бесплатно — даже не жертвуя скоростью выполнения приложений, поскольку Delphi сразу генерирует быстрый машинный код. Delphi замечательно справляется с тем, что касается сокрытия низкоуровневой «кухни» программирования под Windows, но даже она не сможет писать программы. В конце концов программист должен изучать программирование. Иногда это может оказаться весьма длительным и не таким уж простым делом. Однако Delphi способна сделать этот путь почти безболезненным и даже приятным. 2.1. Краткий обзор интегрированной среды разработки DELPHI Данный раздел содержит краткий обзор интегрированной среды разработки Delphi (IDE). После первого запуска программы вы должны увидеть пустую форму и саму интегрированную среду, что иллюстрирует
23
рис. 2.1. Интегрированная среда разработки Delphi разбивается на три части. В верхней части находится окно, которое можно считать главным. Оно содержит панели инструментов и палитру компонентов. Панели инструментов Delphi дают возможность с помощью одного нажатия кнопки выполнять такие задачи, как, например, открытие, сохранение или компиляция проекта. Палитра компонентов, в свою очередь, содержит широкий набор компонентов, которые программист может помещать на форму. (К компонентам относятся текстовые метки, поля редактирования, списки, кнопки и т.д.) Для удобства их использования все компоненты разбиты на группы. Для того, чтобы поместить компонент на форму, достаточно сначала щелкнуть на нем в палитре, а затем в нужном месте на форме.
Рисунок 2.1 — Интегрированная среда Delphi с пустой формой Компонентом называется автономная часть двоичного программного кода, предназначенная для выполнения конкретных предопределенных функций. Компонентом может быть, например, текстовая метка, поле редактирования, список и т.д. 2.1.1. Инспектор объектов Инспектор объектов является составной частью IDE Delphi. Он работает совместно с конструктором форм, помогая создавать компоненты.
24
Инспектор объектов находится в левой части экрана чуть ниже главного окна. С его помощью можно изменять свойства и события компонентов, работая в среде Delphi Окно инспектора объектов состоит из трех основных частей: Селектора компонентов. • Страницы Properties (свойства). • Страницы Events (события). Селектор компонентов Селектор компонентов представляет собой выпадающий комбинированный список, расположенный в верхней части Инспектора. Селектор компонентов позволяет выбирать компонент для просмотра и редактирования. Селектор компонентов отображает имя компонента и класс, от которого этот компонент происходит. Например, компонент с именем Memo будет представлен в окне селектора следующим образом: Меmo: ТМеmo Имя класса не выводится в выпадающем списке компонентов, а присутствует только в верхней части окна селектора. Для выбора компонента сначала щелкните на кнопке вызова списка, а затем на нужном компоненте. Компонент, выбранный в селекторе, будет выделен и в форме. Страницы Properties и Events будут отображать свойства и события для выделенного компонента. Страница Properties Страница Properties инспектора объектов отображает все свойства выбранного компонента, доступные на стадии проектирования. Свойства компонента управляют его работой. Например, изменение свойства Color компонента приводит к изменению цвета его фона. Список доступных свойств изменяется от компонента к компоненту, хотя обычно все компоненты имеют несколько общих элементов (например, свойства Width — ширина и Height — высота). Свойства определяют функционирование компонента. Страница разделена на два столбца. Столбец Property слева показывает имя свойства. Столбец Value справа предназначен для выбора или ввода значения свойства. Если выбранный компонент имеет больше свойств, чем вмещает окно инспектора объектов, можно использовать линейку прокрутки. Свойства могут быть целыми числами, переселениями, множествами, строками, объектами, иметь другие типы. (В деталях свойства будут обсуждаться позже.) При работе со свойствами инспектор объектов учитывает
25
их тип данных. В Delphi имеется несколько встроенных редакторов свойств, управляющих вводом соответствующих данных. Например, свойство Тор представлено величиной целого типа. Поскольку int является базовым типом данных, никакой специальной поддержки для него не требуется и редактор данного свойства достаточно прост. Редактор целочисленных свойств позволяет набрать число прямо в столбце значений. Во многих случаях редактор будет содержать список возможных значений. Значения таких свойств относятся к перечислимому или логическому (булеву) типу. Если вы щелкнете на столбце значений, то увидите справа кнопку вызова списка. Нажатие на эту кнопку приведет к отображению выпадающего списка возможных значений. Если внимательно посмотреть на инспектор объектов, то можно заметить, что перед именами некоторых свойств стоит знак плюс. Он указывает на то, что данное свойство представляет собой множество или класс; свойство любого из этих видов может быть раскрыто для отображения набора значений или свойств класса. Для раскрытия свойства дважды щелкните на его имени в столбце Property или выберите пункт Expand в контекстном меню инспектора объектов. Для закрытия списка дважды щелкните на имени свойства еще раз или выберите в контекстном меню пункт Collapse. Страница Events Страница Events содержит список событий, которые может обрабатывать данный компонент. Страница Events содержит список событий компонента, которые происходят при его взаимодействии с пользователем. Например, при щелчке на компоненте генерируется событие, которое сообщает пользователю об этом факте. Программист может запрограммировать реакцию компонента на эти события с помощью специального программного кода, который при возникновении события каждый раз будет выполнять вполне определенные действия. Как и в случае со свойствами, набор событий, на которые можно запрограммировать реакцию, меняется от компонента к компоненту. Под событием подразумевается нечто, происходящее в результате взаимодействия компонента с пользователем или операционной системой. Обработчиком события называется раздел кода приложения, активируемый в ответ на возникшее событие. Использование этой страницы крайне просто. Для создания обработчика события вам нужно дважды щелкнуть в столбце Value рядом с именем этого события. При этом Delphi создаст функцию со всеми параметрами, необходимыми для обработки. На экране появится окно редактора кода с курсором, расположенным внутри обработчика. Пользователю останется только начать ввод кода. Имя генерируемой функции составляется из значения свойства Name компонента и имени обрабатываемого события. На-
26
пример, если имеется кнопка ОК и обрабатывается событие OnClick, функция получит имя OKClick. Можно разрешить автоматическую генерацию имен обработчиков событий или задавать имена функций вручную. Чтобы указать Delphi имя функции-обработчика, наберите его в столбце Value рядом с названием события и нажмите Enter. В появившемся окне редактора кода будет содержаться функция с указанным именем. Создав функцию - обработчик события для некоторого компонента, можно использовать ее для любых компонентов, генерирующих то же событие. Иногда, например, удобно иметь несколько кнопок, использующих одно событие OnClick. Например - пункт главного меню, пункт контекстного меню и кнопку панели инструментов, для которых можно использовать общий обработчик события OnClick. Несмотря на то, что вы имеете дело с тремя различными компонентами, они могут совместно использовать общий обработчик OnClick. Столбец Value страницы Events содержит кнопку для вызова списка обработчиков, совместимых с текущим событием. Все, что нужно сделать — это выбрать нужное имя из списка. 2.1.2. Рабочее пространство DELPHI Центральное место в интегрированной среде разработки Delphi занимает рабочее пространство, в котором первоначально отображается конструктор форм. Конструктор форм предназначен для создания форм в приложениях пользователя. В Delphi форма представляет собой окно программы. Она может быть главным окном приложения, диалоговой панелью или окном любого другого типа. В процессе создания формы можно с помощью конструктора форм размещать на ней компоненты, а также перемещать эти компоненты или изменять их размеры. С конструктором форм тесно связан редактор кода, с помощью которого при создании приложений можно вводить программный код. Все перечисленные инструменты — инспектор объектов, конструктор форм, редактор кода и палитра компонентов — очень тесно взаимодействуют друг с другом в процессе создания прикладный программ. 2.2. Первая программа (WINDOWS - приложение) В панели Component Palette щелкните на ярлычок Additiоnal. Щелкните на пиктограмме, которая содержит маленькую кнопку ОК с галочкой. 1. Щелкните на центре окна Form1. Должна появится кнопка с надписью BitBtn1. Кнопка имеет маленькие квадратики по периметру. Эти квадратики называются регуляторами размера кнопки. Пойдем дальше и
27
попытаемся изменить размер кнопки путем нажатия мышью на одном из квадратиков и перетаскивания его на нужное место. Эти квадратики также показывают то, что кнопка активна. (Когда в нашем окне формы содержится много объектов, только один из них может быть активным, т.е. только один содержит регуляторы размера). 2. Нажмите клавишу Esc. Регуляторы размера должны исчезнуть с кнопки, что означает, что она больше не активна. (Теперь активной является форма, хотя она и не имеет регуляторов размера). Теперь щелкните мышью по кнопке. Она снова станет активной. 3. В то время как кнопка остается активной, перейдите к окну инспектора объектов Object Inspector. В этом окне описывается некоторые свойства кнопок. Заметьте, что поле свойств называется Caption. Щелкните на слове Caption в левой колонке. На печатайте слово «НАЖМИ !!!!» рисунок 2.2. Если щелкнуть на левой колонке то вся фраза «НАЖМИ !!!!» подсветится. Если щелкнуть на правой колонке, то мерцающий курсор появится в том месте, где произведен щелчок.
Рисунок 2.2 — Окно формы с кнопкой
28
4. Нажмите клавишу F9, для того чтобы запустить программу. (Также программу можно запустить, используя пункт меню Run | Run. Или можно щелкнуть мышью на кнопке, на которой нарисована стрелка вправо). Когда программа запустится, ее внешний вид должен быть идентичен приведенному на рисунке 2.3. Нажмите кнопку. Ничего не случиться, потому что нет сообщения программе, что необходимо делать при нажатии кнопки. Закройте программу щелчком на кнопке системного меню в левом верхнем углу. Должен восстановиться экран Delphi, готовый для работы с этой программой или другими.
Рисунок 2.3 — Программа работает !
Продолжим работу с этой программой: 1. При двойном щелчке мыши на кнопке— окно исходного кода должно стать активным. 2. Не трогая мышь. Печатаем прямо в том месте окна, где мигает курсор. Для создания отступа необходимо нажать клавишу Tab и ввести следующую команду: ShowMessage(′Учим Delphi′); 3. Запускаем программу нажатием клавиши F9. Когда она запустится, нажиме кнопку с меткой «НАЖМИ !!!!».
29
Должно появиться окно (рисунок 2.4) с сообщением «Учим Delphi» Это типичное окно с сообщением, которое можно увидеть во многих программах Windows. Нажатие кнопки ОК возвращает к программе.
Рисунок 2.4 — Окно сообщения Код программы должен иметь следующий вид: unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1;
30
implementation {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); begin ShowMessage('Учим Delphi'); end; end. Давайте подумаем, что произошло. Находясь в Delphi и щелкнув два раза на кнопке, мы напечатали слово ShowMessage, за которым в круглых скобках шло сообщение. После того как запустили программу (нажали кнопку F9), каждый раз при нажатии на кнопку данное сообщение отображалось на экране. Это случалось не один раз, а каждый раз, когда нажимаем на кнопку. Как же мы написали код, обрабатывающий событие – событие Click – для кнопки? Мы сказали компьютеру, что нужно делать каждый раз, когда нажимается кнопка – выводить сообщение. И когда мы запустили программу, именно это и случилось. Это является стержнем программирования под Windows: назначение кода различным событиям. В программе событие Click наступает, когда пользователь щелкает мышью по кнопке, и тогда запускается код для этого события. При работе в среде Windows взаимодействие с программой производится через элементы контроля, которые программа предоставляет. Пользователь нажимает кнопки, печатает в редакторах, открывает меню и т.д. Элементы контроля позволяют связать пользователя и компьютер. Вся совокупность элементов контроля является интерфейсом (связью) между пользователем и компьютером. Итак, когда пишут программу на Delphi, необходимо создать элементы контроля – интерфейс пользователя. А также необходимо сообщить программе, что нужно делать, когда пользователь активизирует какой-либо элемент контроля. Продолжим работу с нашим примером. 1. На странице Standard щелкните на кнопке Edit. она представляет собой квадратик с буквами ab внутри. В результате в окне формы появится элемент редактирования в своем стандартном размере. 2. Активизируйте форму и щелкните мышью два раза по кнопке, чтобы перейти к редактору кода для кнопки. Измените содержимое круглых скобок, так чтобы строка выглядела следующим образом:
31
ShowMessage (Edit1.Text); 3. Запустите программу, нажав F9. Щелкните мышью на элементе контроля Edit и напечатайте что-нибудь. Затем щелкните по кнопке. Должно появиться окно с сообщением, содержащим слова, которые напечатали в элементе контроля Edit. 2.3. Процесс создания программы в DELPHI. Проекты в Delphi Давайте запишем программу, которую только что создали, и посмотрим, как Delphi рассматривает приложения и как она упрощает процесс создания программ. 1. Откройте менеджер файлов и создайте каталог, в который поместите свою программу. 2. Находясь в Delphi, выберите File | Save Project. 3. Первым диалоговым окном будет “Save Unit1 as”. Главное окно формы и ее модуль имеют имя Unit1. Найдите соответствующий каталог и запишите под именем MyUnit. 4. Нажмите OK. Следующим диалоговым окном будет “Save Project1 as”. Каталог по умолчанию должен совпадать с каталогом, в который Вы записали MyUnit. Если это не так, измените каталог. Теперь Delphi запросило имя проекта. Наберите Test в поле File name и нажмите OK для записи. Созданная программа (Windows – приложение) сохранилась в проекте с именем Test. Проект — это набор файлов, которые используются при создании автономного исполняемого файла или динамически присоединяемой библиотеки (DLL). Кроме одиночного проекта, Delphi позволяет вам создавать так называемые группы проектов. Группа проектов — это набор проектов Delphi. Группы проектов используются для управления несколькими взаимосвязанными проектами, составляющими программный продукт. При каждом запуске Delphi создается новая безымянная группа. В нее попадут все проекты, которые создаются. Можно сохранить эту группу или рассматривать ее как временную. После записи проекта различные части программ будут записаны в разных файлах с разными расширениями (назначение каждого файла отображено в таблице 2.1). Delphi управляет проектами с помощью нескольких вспомогательных файлов. Этих файлов будет около восьми. (Их точное число зависит от установок IDE.) Файлы с расширением, начинающимся с тильды (~), являются резервными копиями- Delphi может создать несколько резервных файлов, в зави-
32
симости от количества исходных файлов в проекте и установленных опций проекта. Для нового проекта Delphi создает как минимум четыре файла (имеется в виду типичное GUI-приложение): − Исходный файл проекта. − Модуль главной формы. − Файл ресурсов главной формы. − Файл ресурсов проекта. Исходный файл проекта содержит стартовый код Delphi. Просмотреть этот файл можно, выбрав в главном меню пункт View | Project Source. Модуль главной формы содержит объявления и определения класса главной формы. Delphi будет создавать дополнительный модуль для каждой новой формы проекта. Файл ресурсов главной формы и файл ресурсов проекта представляют собой двоичные файлы, описывающие главную форму и значок приложения. При указании Delphi компилировать проект, она компилирует исходный файл проекта, модуль главной формы и любые другие, имеющиеся в проекте модули. Этот процесс разбивается на несколько этапов. Сначала компилятор Object Pascal преобразует исходные файлы (каждый pas – файл и соответствующие им dfm – файлы) в двоичные объектные файлы. Эти объектные файлы имеют расширение .dcu. Затем компилятор ресурсов собирает все ресурсы, такие как значки и файлы форм, в двоичный файл ресурсов. После этого в работу вступает компоновщик. Он берет двоичные файлы, созданные компиляторами, подключает необходимые библиотечные файлы и связывает их вместе, чтобы получить конечный исполняемый файл. Когда все это завершается, получается автономная программа (файл с расширением .exe), которая может быть запущена обычным образом. Короче говоря, компиляция состоит из двух частей: создания объектных файлов и связывания всех объектных файлов для создания исполнимого файла. Хорошо, но для чего предназначены все эти файлы? В таблице 2.1 перечислены расширения имен файлов, используемых Delphi, и даны описания той роли, которую они играют в проекте.
33
Таблица 2.1 Типы файлов, используемых в Delphi Расширение .pas .dfm
.dsk
.dot .exe .cfg .dcu .dpr .res
Описание Исходные файлы Object Pascal. Имеется по одному файлу для каждого модуля и, кроме того, те файлы, которые включаются в проект. Файл формы. В действительности он является замаскированным двоичным файлом ресурсов (.res). Он содержит описание формы и всех ее компонентов. Каждой форме соответствует .dfm – файл. Файл конфигурации рабочего пространства. Этот файл отслеживает расположение компонентов IDE на момент последнего сохранения (или закрытия) проекта. В нем сохраняются размер и положение всех открытых окон, поэтому при открытии проекта IDE выглядит точно так же, как в момент, когда его закрывали. Файл опций проекта. Он содержит установочные параметры из диалога Project Option. Конечный исполняемый файл программы. Файл конфигурации проекта. В основном он служит для хранения текущих установок компилятора и компоновщика. Двоичные объектные файлы. Они генерируются компилятором при обработке модулей, написанных на Object Pascal. Исходный файл проекта. Компилированный двоичный файл ресурсов.
Файлы, используемый Delphi, можно разделить на две категории: файлы, на основе которых строится проект, и файлы, создаваемые при его компиляции и компоновке. Для переноса исходных файлов на другой компьютер не следует переписывать все файлы. Необходимо только скопировать те файлы, которые Delphi использует для построения приложения. Минимальный набор составляют файлы с расширениями .pas, .dfm, и .dpr. Все остальные файлы Delphi создаст заново при построении программы. Можно сохранить также файл рабочей конфигурации (.dsk), который отражает состояние IDE на тот момент, когда последний раз работали над проектом.
34
2.4. Главное меню и панель инструментов DELPHI Главное меню содержит все пункты, необходимые для работы с Delphi. Поскольку программирование в Delphi является в значительной степени визуальным, главное меню, вообще говоря, используется менее интенсивно по сравнению с другими средами программирования. Но если вы предпочитаете работать с меню, то оно содержит почти все, что может когда-либо потребоваться. Необходимо заметить сразу, что разные версии Delphi имеют меню разного вида. Но все пункты и понятия, которые описаны ниже, можно найти во всех версиях Delphi. 2.4.1. Пункт меню “File” Если нужно сохранить проект, то Вы выбираете пункт главного меню “File” (с помощью мышки или по клавише Alt+F). Пункт меню “File” выглядит следующим образом: New … New Application New Form New DataModule Open … Reopen Save Save as Save Project As Save all Close Close all --------------------Use Unit… Add to Project Remove from Project… --------------------Print --------------------Exit --------------------Как видно, здесь есть несколько секций. Вот их назначение: • Первая секция дает возможность управления проектом в целом; предоставляет список ранее редактировавшихся проектов; можно быстро
35
открыть нужный. • Вторая секция дает контроль над формами, модулями и компонентами проекта. • Третья позволяет добавлять и удалять файлы из проекта. • Четвертая управляет печатью. • Пятая секция - выход из Delphi Большинство операций из пункта меню “File” можно выполнить с помощью Менеджера Проекта (Project Manager), который можно вызвать из пункта меню View. Некоторые операции доступны и через SpeedBar (панель инструментов). Данная стратегия типична для Delphi: она предоставляет несколько путей для решения одной и той же задачи, Вы сами можете решать, какой из них более эффективен в данной ситуации. Каждая строка пункта меню “File” объяснена в Справочнике. Выберите меню “File” и нажмите F1, появится экран справочника. Большинство из пунктов первой секции очевидны. Первые два пункта второй секции позволяют создать новую форму или новый модуль. Выбирая “New Form”, создается новая форма и модуль, связанный с ней; выбирая “New Unit”, создается один модуль. “New Component” вызывает диалог для построения заготовки нового визуального компонента. В результате создается модуль, который можно скомпилировать и включить в Палитру Компонент. “Open File” открывает при необходимости любой модуль или просто текстовый файл. Если модуль описывает форму, то эта форма тоже появится на экране. При создании нового модуля Delphi дает ему имя по умолчанию. Можно изменить это имя на что-нибудь более осмысленное (например, MAIN.PAS) с помощью пункта “Save As“. “Save ” сохраняет только редактируемый файл, но не весь проект. “Close” удаляет файл из окна Редактора. Нужно обратить внимание: Необходимо регулярно сохранять проект через File | Save Project либо через нажатие Ctrl+S. 2.4.2. Управление проектом Перейдем к Менеджеру Проектов, который помогает управлять проектом. Менеджер Проектов, рисунок 2.6, разделен на две части. Верхняя панель с управляющими кнопками. Нижняя - список модулей, входящих в проект.
36
Рисунок 2.6 - Кнопки сверху используются для удаления и добавления модулей в проект Кнопки с плюсом и минусом используются для добавления и удаления файлов в проекте. Эти изменения влияют на файлы с исходным текстом, то есть, если добавить в проект модуль, то ссылка на него появится в файле с расширением DPR. Краткое описание других кнопок: Третья слева кнопка - просмотр текста модуля, на котором стоит курсор. Четвертая - просмотр формы, если есть таковая для данного модуля Пятая - вызов диалога настройки проекта. Последняя - сохранение изменений на диске. 2.4.3. Пункт меню “Edit” “Edit” содержит команды “Undo” и “Redo”, которые могут быть очень полезны при работе в редакторе для устранения последствий при неправильных действиях, например, если случайно удален нужный фрагмент текста. Отметьте для себя, что Справочник (on-line help) объясняет как нужно использовать пункт меню Options | Environment для настройки команды “Undo”. Возможность ограничить количество команд “Undo” может пригодиться, если Вы работаете на машине с ограниченными ресурсами. Команды “Cut”, “Copy”, “Paste” и “Delete” - как во всех остальных приложениях Windows, но их можно применять не только к тексту, но и к визуальным компонентам. Оставшиеся пункты помогают быстро “приукрасить” внешний вид формы.
37
2.4.4. Пункт меню “Search” Используя пункт “Find” можно найти по тексту программы любую строку. А используя “Replace”, можно не только найти, но и заменить найденную строку на другую. В “Search” есть команда “Find Error” (поиск ошибки), которая поможет отследить ошибку периода выполнения программы. Когда в сообщении об ошибке указан ее адрес, можно выбрать пункт меню Search | Find Error и ввести этот адрес. Если это представится возможным, то среда переместит курсор в то место программы, где произошла ошибка. 2.4.5. Пункт меню “View” Основные составляющие пункта меню “View”: • Project Manager (Менеджер Проекта). • Project Source - загружает главный файл проекта (DPR) в Редактор • Установка, показывать или нет Object Inspector на экране. • Установка, показывать или нет Alignment Palette. То же самое доступно из пункт меню Edit | Align. • Browser - вызов средства для просмотра иерархии объектов программы, поиска идентификатора в исходных текстах и т.п. • Watch, Breakpoint и Call Stack - связаны с процедурой отладки. • Component List - список компонент, альтернатива Палитре Компонент. Используется для поиска компонента по имени или при отсутствии мыши. • Window List - список окон, открытых в среде Delphi. • Toggle Form/Unit, Units, Forms - переключение между формой и соответствующим модулем, выбор модуля или формы из списка. • New Edit Window - открывает дополнительное окно Редактора. Полезно, если нужно, например, просмотреть две разных версии одного файла. • SpeedBar и Component Palette - установки, нужно ли их отображать. 2.4.6. Пункт меню “Project” В пункте меню “Compile” проект можно скомпилировать (compile) или перестроить (build). Если выбрать Compile или Run, то Delphi перекомпилирует только те модули, которые изменились со времени последней компиляции. Build all, с другой стороны, перекомпилирует все модули, исходные тексты которых доступны. Команда Syntax Check только проверяет правильность кода программы, но не обновляет DCU файлы.
38
Пункт Information выдает информацию о программе: размеры сегментов кода, данных и стека, размер локальной динамической памяти и количество скомпилированных строк. 2.4.7. Пункт меню “Run” Можно использовать “Run” для компиляции и запуска программы и для указания параметров командной строки для передачи в программу. Здесь же имеются опции для режима отладки. 2.4.8. Пункт меню Project | Options “Options” наиболее сложная часть системного меню. Это центр управления, из которого можно изменять установки для проекта и для всей рабочей среды Delphi Диалог из пункта Project | Options включает пять страниц: • На странице Forms перечислены все формы, включенные в проект; можно указать, нужно ли автоматически создавать форму при старте программы или вы ее создадите сами. • На странице Application определяются элементы программы такие, как заголовок, файл помощи и иконка. • Страница Compiler включает установки для генерации кода, управления обработкой ошибок времени выполнения, синтаксиса, отладки и др. • На странице Linker можно определить условия для процесса линковки приложения • Страница Directories/Conditionals - здесь указываются директории, специфичные для данного проекта. Все установки для проекта сохраняются в текстовом файле с расширением .OPT и можно вручную их исправить.
39
Страница Forms
Рисунок 2.7 На странице Forms можно выбрать главную форму проекта. Изменения, которые делаются, отобразятся в соответствующем файле DPR. Например, в нижеследующем проекте, Form1 является главной, поскольку появляется первой в главном блоке программы: program Project1; uses Forms, Unit1 in 'UNIT1.PAS' {Form1}, Unit2 in 'UNIT2.PAS' {Form2}; {$R *.RES} begin Application.CreateForm(TForm1, Form1); Application.CreateForm(TForm2, Form2); Application.Run; end. Если изменить код так, чтобы он читался begin Application.CreateForm(TForm2, Form2); Application.CreateForm(TForm1, Form1);
40
Application.Run; end. то теперь Form2 станет главной формой проекта. Можно использовать эту страницу для определения, будет ли данная форма создаваться автоматически при старте программы. Если форма создается не автоматически, а по ходу выполнения программы, то для этого нужно использовать процедуру Create. Кстати, в секции Uses имя формы в фигурных скобках является существенным для Менеджера Проектов и удалять его не стоит. Не нужно вообще ничего изменять вручную в файле проекта, если только не создавать DLL. Страница Applications На странице Applications (см. рис.2.8) можно задать заголовок (Title), файл помощи (Help file) и пиктограмму (Icon) для проекта.
Рисунок 2.8 - Страница общих установок для приложения Страница Compiler Ранее уже говорилось, что установки из пункта меню “Project | Options ” сохраняются в соответствующем файле с расширением OPT. Давайте рассмотрим директивы компилятора на странице Compiler (рис.2.9).
41
Рисунок 2.9 - Страница для определения директив компилятора Следующая таблица показывает, как различные директивы отображаются в OPT файле, на странице Compiler и внутри кода программы: OPT File Options Page Editor Symbol F Force Far Calls {$F+} A Word Align Date {$A+} U Pentium-Safe FDIV {$U+} K Smart Callbacks {$K+} W Windows (3.0) Stack Frame {$W+} R Range Checking {$R+} S Stack Checking {$S+} I IO Checking {$I+} Q Overflow Checking {$Q+} V Strict Var Strings {$V+} B Complete Boolean Evaluation {$B+} X Extended Syntax {$X+} T Typed @ Operator {$T+} P Open Parameters {$P+} D Debug Information {$D+} L Local Symbols {$L+} Y Symbol Information {$Y+} N Numeric Processing {$N+}
42
Страница Linker Теперь давайте перейдем к странице Linker, показанной на рис.2.10.
Рисунок 2.10 − Страница линковщика Установки отладчика рассматриваются ниже. Если буфер линковщика расположен в памяти, то линковка происходит быстрее. Размер стека (Stack Size) и локальной динамической памяти (Heap Size) весьма важны. Delphi устанавливает по умолчанию и Stack Size, и Heap Size в 8192 байт каждый. Вам может понадобиться изменить размер стека в программе, но обычно это не более 32Кб. В сумме эти два размера не должны превышать 64Кб, иначе будет выдаваться ошибка при компиляции программы. Страница Directories/Conditionals Страница Directories/Conditionals (рис.2.11) дает возможность расширить число директорий, в которых компилятор и линковщик ищут DCU файлы.
43
Рисунок 2.11 - Страница Directories/Conditionals В файле DELPHI.INI содержится еще один список директорий. В OPT файле - список директорий для конкретного проекта, а в файле DELPHI.INI - список относится к любому проекту. Output directory - выходная директория, куда складываются EXE и DCU файлы, получающиеся при компиляции. Search path - список директорий для поиска DCU файлов при линковке. Директории перечисляются через точку с запятой ; Conditional defines - для опытного программиста и на первом этапе создания проекта не требуется. Для информации можно вызвать Справочник (on-line help). 2.4.9. Конфигурация среды программирования (IDE) Пункт меню “Tools | Environment Options… ” предоставляет Вам большой набор страниц и управляющих элементов, которые определяют внешний вид и работу IDE. Delphi позволяет сделать следующие важные настройки: 1. Определить, что из проекта будет сохраняться автоматически. 2. Можно менять цвета IDE. 3. Можно менять подсветку синтаксиса в Редакторе. 4. Можно изменить состав Палитры Компонент. 5. Указать “горячие клавиши” IDE.
44
Первая страница пункта меню “ Tools | Environment Options…” показана на рис.2.12
Рисунок 2.12 - Страница Preferences В группе “Desktop Contents” определяется, что будет сохраняться при выходе из Delphi. Если выбрать Desktop Only - это сохранит информацию о директориях и открытых окнах, если выбрать Desktop And Symbols - это сохранит то же самое плюс информацию для броузера (browser). В группе “Autosave” указывается, что нужно сохранять при запуске программы. Если позиция Editor Files выбрана, то сохраняются все модифицированные файлы из Редактора при выполнении команд Run|Run, Run|Trace Into, Run|Step Over, Run|Run To Cursor или при выходе из Delphi. Если позиция Desktop выбрана - сохраняется рабочая среда при закрытии проекта или при выходе из Delphi. Если Вы позже откроете проект, то он будет иметь тот же вид, что и при его закрытии. В группе “Form Designer” можно установить, показывать ли сетку (grid) на экране и выравнивать ли объекты по ней, и размер ячеек сетки. В группе “Debugging”: опция Integrated Debugging - использовать ли встроенный отладчик; Step Program Block - отладчик остановится на первой строке модуля, в котором есть отладочная информация; Break On Exception - останавливать ли программу при возникновении исключительной ситуации; Minimize On Run - свертывать ли Delphi при запуске программы. После закрытия программы среда Delphi восстанавливается. Hide Designers On Run - прячет окна Дизайнера (Инспектор Объектов, формы)
45
при запуске приложения. Show Compiler Progress - показывать ли окно, в котором отражается процесс компиляции программы. “Gallery” - указывает, в каких случаях нужно предоставлять “галерею” (коллекцию заготовок и экспертов). Страницы Editor Options, Editor Display и Editor Colors позволяют Вам изменить цвета и “горячие” клавиши, используемые IDE. Страница Editor Display показана на рис.2.13, а Editor Colors - на рис.2.14.
Рисунок 2.13 - Страница Editor Display Существует несколько способов изменить назначение “горячих” клавиш, используемых Редактором. Например, многие пользователи привыкли, что по клавише F5 максимизируется окно Редактора. Для этого им надо использовать расположение клавиш, называемое “Classic” (Keystroke mapping : Classic). Всего есть четыре вида конфигурации клавиш: • “Default” - характерно для Microsoft. Если Вы новичок в Windows или уже привыкли к этому расположению клавиш, то это подойдет. • “Classic” - более известно ветеранам Borland C++ и Borland Pascal. Поддерживает многие комбинации клавиш WordStar и отладчик управляется старым добрым способом. • Остальные два вида - имитируют редакторы Epsilon и BRIEF.
46
Рисунок 2.14 - Страница Editor Colors Точное описание назначения клавиш можно найти в Справочнике (в Help | Topic Search набрать “key mapping”). Цвета IDE можно изменить на странице Editor Colors. И, наконец, Editor Options (рис.2.15).
Рисунок 2.15 − На странице Editor Options можно настроить тонкие детали работы редактора
47
Многие из установок на данной странице не очень важны для большинства пользователей, поэтому остановимся лишь на некоторых. “Use syntax highlight” - выделять ли цветом синтаксические конструкции в Редакторе Исходного текста. “Find text at cursor” - если включено, то при поиске (Ctrl+F) в качестве подстроки для поиска будет браться то слово, на котором стоит курсор. Обо всех опциях можно подробнее узнать в Справочнике (F1). Установки сохраняются в файле DELPHI.INI, который находится в директории Windows. 2.4.10. Панели инструментов Delphi Панели инструментов Delphi упрощают выполнение часто повторяющихся действий. Кнопку легче отыскать, чем пункт главного меню, не говоря уже о том, что для этого нужно гораздо меньше работать мышью. Панель инструментов Delphi может быть полностью перестроена. Панели можно размещать в любом месте главного окна. Главное меню, инструментальные панели и палитру компонентов можно расположить так, чтобы они максимально соответствовали -вашим предпочтениям. Настройка панели инструментов крайне проста. Delphi позволяет добавлять, удалять и переставлять кнопки произвольным образом. Настройка панели осуществляется через контекстное меню, для вызова которого нужно поместить курсор мыши на панель и нажать правую кнопку. Для настройки панели инструментов выберите в меню пункт Customize. После этого на экране появится диалоговая панель Customize. Эта диалоговая панель имеет три закладки: 1. Первая из них — Toolbars — показывает имеющиеся панели инструментов; те из них, что видимы в данный момент, помечены. Можно подключить или удалить существующие панели, а также вернуться к их исходной конфигурации по умолчанию. 2. Вторая закладка, с меткой Commands, показывает все доступные инструментальные кнопки. Для добавления кнопки к панели просто найдите ее в окне списка Commands и переместите с помощью мыши в нужное место любой панели инструментов. Чтобы удалить кнопку, ухватите ее мышью и стащите с панели. Если в результате действий возник беспорядок, просто вернитесь на страницу Toolbars и нажмите Reset, чтобы вернуть инструментальную панель в исходное состояние. 3. Третья закладка – Options – содержит различные установки, определяющие, например, будут ли выводиться подсказки и если да, то в каком виде.
48
3. БИБЛИОТЕКА ВИЗУАЛЬНЫХ КОМПОНЕНТОВ Компоненты — это объекты, которыми после их помещения на форму можно манипулировать с помощью свойств, методов и событий. Впервые концепция программирования, основанного на форме, получила распространение в языке Visual Basic компании Microsoft. Однако, в отличие от Visual Basic, в Delphi в качестве языка программирования был использован потомок языка Pascal. Этот новый язык, названный Object Pascal, был объектно-ориентированным языком программирования. Таким образом, между Delphi и Object Pascal был заключен своеобразный союз: произошло объединение объектно- и формо-ориентированного программирования. Kpoме того, с помощью Delphi теперь уже можно было создавать автономные (и потом совершенно независимые!) исполняемые файлы, для которых не требовалось никаких DLL времени выполнения. Это были полностью компилируемые, а не интерпретируемые программы, выполнявшиеся в десятки раз быстрее приложений, написанных на Visual Basic. VCL (библиотека визуальных компонентов) — это библиотека классов, предназначенная для создания на Object Pascal приложений для Window. Одним из самых замечательных свойств VCL является то, что она была разработана с помощью концепции свойств, методов и событий — так называемой визуально-компонентной модели. Компоненты VCL — это объекты, предназначенные для выполнения вполне конкретных задач программирования. Их оболочкой являются классы Object Pascal. 3.1 Обзор VCL Библиотека визуальных компонентов (VCL) является прекрасно разработанной классовой оболочкой. Как и в большинстве других хороших библиотек классов, в ней широко используется механизм наследования. Основной составляющей VCL являются классы, представляющие различные компоненты. Однако существуют и другие классы VCL, непосредственно не связанные с компонентами. К их числу относятся вспомогательные и служебные классы, а также классы, предназначенные для выполнения различного рода рутинных задач. Иерархия классов VCL, служащих представлениями компонентов, довольно сложна. На самом верху иерархической структуры находится объект TObject. На рис. 3.1 показаны некоторые из базовых классов и их потомки.
49
TObject TPersistent
Невизуальные компоненты
TComponent
TТimer и т.д.
Визуальные компоненты TControl
TGraphicControl
TWinControl
TSpeedButton и т.д.
TPanel и т.д.
Рисунок 3.1 – Иерархия классов VCL TObject является прародителем всех классов, представляющих компоненты VCL. (Не забывайте, что вообще все классы Object Pascal являются производными от TObject.) Сразу же за TObject следует TPersistent. Этот класс отвечает за способность компонентов сохранять себя в файлах и в памяти, а также предназначен для выполнения ряда других Более близким родственником компонентов в цепочке наследования является класс TComponent. Он обеспечивает для них все основные функциональные возможности. Невизуальные компоненты являются прямыми потомками самого класса TComponent. Визуальные компоненты наследуют классу TControl, который, как видно из рис. 3.1, также является потомком TComponent. TControl придает визуальным компонентам дополнительные функциональные свойства. Отдельные визуальные компоненты выводятся либо из TGraphicControl, либо из TWinControl. При размещении компонента на форму, Delphi создает для него в объявлении класса формы соответствующий указатель — теперь можно обращаться к нему из своих программ. В качестве имени переменной-указателя Delphi использует свойство компонента Name. Например, в момент помещения на форму компонента Memo средой Delphi создается переменная типа TMemo, которой присваивается имя Memo. Точно так же, когда на форме появилась командная кнопка, Delphi создала для ее представления переменную типа TButton. Но еще раньше из
50
TForm был выведен новый класс и создан его экземпляр для представления формы пользователя. Очевидно, что прежде чем работать с VCL, необходимо иметь некоторое представление о ее классах и компонентах. 3.2 Классы приложения и формы Классы приложения и формы лежат в основе экранных форм и объекта Application. Будучи потомками TComponent, они сами являются компонентами. Класс TAppIication Класс TAppIication инкапсулирует все базовые операции приложения Windows. Он берет на себя все тяготы, связанные с управлением его пиктограммой, предоставлением контекстной справки и базовой обработкой сообщений. Любое Delphi-приложение содержит указатель Application на объект типа TAppIication. С его помощью можно отображать окна сообщений, управлять контекстной справкой и создавать подсказки для командных кнопок и строки состояния. Класс TAppIication отличается от других объектов VCL тем, что некоторые из его свойств (icon, HelpFile и Title) можно установить на странице Application диалоговой панели Project Options. Класс TForm Класс TForm инкапсулирует экранные формы, которые используются для представления главных окон, диалоговых панелей, вторичных и любых других окон. Класс TForm — это своего рода рабочая лошадка VCL. Он содержит порядка 60 свойств, 45 методов и 20 событий. 3.3. Классы компонентов В эту группу входит широкий диапазон классов, которые можно подразделить на несколько категорий: • стандартные компоненты; • компоненты страницы Additional; • компоненты категории Win32; • компоненты для работы с базами данных; • классы стандартных диалогов; • компоненты категории System; • компоненты категории Win 3.1: • компоненты категории Internet; • компоненты категории Sample и др. Более подробно некоторые компоненты будут рассмотрены позже.
51
4. СВОЙСТВА ЭЛЕМЕНТОВ Каждый компонент, который помещается на форму, имеет свое отражение в окне Инспектора Объектов (Object Inspector). Как уже отмечалось, Object Inspector имеет две “странички” - “Properties” (Свойства) и “Events” (События). Создание программы в Delphi сводится к “нанесению” компонент на форму (которая, кстати, также является компонентом) и настройке взаимодействия между ними путем: • изменения значения свойств этих компонент • написания адекватных реакций на события. Более подробно сосредоточимся на свойствах и, в меру необходимости, затронем создание откликов на события. Свойство является важным атрибутом компонента. Для пользователя (программиста) свойство выглядит как простое поле какой-либо структуры, содержащее некоторое значение. Однако, в отличие от “просто” поля, любое изменение значения некоторого свойства любого компонента сразу же приводит к изменению визуального представления этого компонента, поскольку свойство инкапсулирует в себе методы (действия), связанные с чтением и записью этого поля (которые, в свою очередь, включают в себя необходимую перерисовку). Свойства служат двум главным целям. Во-первых, они определяют внешний вид формы или компонента. А во-вторых, свойства определяют поведение формы или компонента. Существует несколько типов свойств, в зависимости от их “природы”, т.е. внутреннего устройства. • Простые свойства - это те, значения которых являются числами или строками. Например, свойства Left и Top принимают целые значения, определяющие положение левого верхнего угла компонента или формы. Свойства Caption и Name представляют собой строки и определяют заголовок и имя компонента или формы. • Перечислимые свойства - это те, которые могут принимать значения из предопределенного набора (списка). Простейший пример - это свойство типа Boolean, которое может принимать значения True или False. Рисунок 4.1 − Отображение • Вложенные свойства - это те, кото- комбинированных значений рые поддерживают вложенные значения (или вложенных свойств объекты). Object Inspector изображает знак “+” слева от названия таких свойств. Имеется два вида таких свойств: мно-
52
жества и комбинированные значения. Object Inspector изображает множества в квадратных скобках. Если множество пусто, оно отображается как []. Установки для вложенных свойств вида “множество” обычно имеют значения типа Boolean. Наиболее распространенным примером Рисунок 4.2 – Изменение такого свойства является свойство Style размеров с помощью Дис вложенным множеством булевых значезайнера Форм ний. Комбинированные значения отображаются в Инспекторе Объектов как коллекция некоторых величин, каждый со своим типом данных (рис. 4.1). Некоторые свойства, например, Font, для изменения своих значений имеют возможность вызвать диалоговое окно. Для этого достаточно щелкнуть маленькую кнопку с тремя точками в правой части строки Инспектора Объектов, показывающей данное свойство. Delphi позволяет легко манипулировать свойствами компонент как в режиме проектирования (design time), так и в режиме выполнения программы (run time). В режиме проектирования манипулирование свойствами осуществляется с помощью Дизайнера Форм (Forms Designer) или на страничке “Properties” Инспектора Объектов. Например, для того чтобы изменить свойства Height (высоту) и Width (ширину) кнопки, достаточно “зацепить” мышкой за любой ее угол и раздвинуть до нужного представления (см. рис.4.2). Того же результата можно добиться, просто подставив новые значения свойств Height и Width в окне Object Inspector (см. рис.4.3). С другой стороны, в режиме выполнения пользователь (программист) имеет возможность не только манипулировать всеми свойствами, отображаемыми в Инспекторе Объектов, но и управлять более обширным их списком. В следующем разделе мы рассмотрим, как это делается. Рисунок – 4.3 − Изменение размеров с помощью Инспектора Объектов
53
4.1. Управление свойствами визуальных компонент в режиме выполнения Все изменения значений свойств компонент в режиме выполнения должны осуществляться путем прямой записи строк кода на языке Паскаль. В режиме выполнения невозможно использовать Object Inspector. Однако, доступ к свойствам компонентов довольно легко получить программным путем. Все, что необходимо сделать для изменения какого-либо свойства - это написать простую строчку кода аналогично следующей: MyComponent.Width := 35; Вышеприведенная строка устанавливает ширину (Width) компонента в значение 35. Если свойство Width компонента еще не было равно 35 к моменту выполнения данной строки программы, компонент визуально изменит свою ширину. Таким образом, нет ничего магического в Инспекторе Объектов. Object Inspector просто является удобным способом выполнения в режиме проектирования того, что может быть осуществлено программным путем в режиме выполнения. Более того, как уже было сказано выше, у компонента могут быть свойства, не отображаемые в окне Инспектора Объектов. Объектно-ориентированный язык Паскаль, лежащий в основе Delphi, в качестве базового имеет принцип соответствия визуальных компонент тем вещам, которые они представляют. Разработчики Delphi поставили перед собой цель, чтобы, например, представление компонента Button (кнопка), инкапсулирующее некий код, соответствовало визуальному изображению кнопки на экране и являлось как можно более близким эквивалентом реальной кнопки, которую можно найти на клавиатуре. И именно из этого принципа родилось понятие свойства. Если изменить свойства Width и Height компонента Button, кнопка соответствующим образом изменит свои ширину и высоту. Нет необходимости после изменения свойства Width указывать объекту, чтобы он перерисовал себя, хотя при обычном программировании именно так и нужно поступать. Свойства - это более чем просто данные. Напротив, они делают эти данные “живыми”, свойства дают иллюзию, как будто имеем дело с реальными объектами, а не с их программным представлением. 4.2. Примеры. Программа SHAPEDEM.DPR Программа SHAPEDEM.DPR, изображенная на рис. 4.4, демонстрирует различные способы, с помощью которых можно изменять пользовательский интерфейс при выполнении программы. Эта программа не производит никаких полезных действий, кроме демонстрации того, как легко мож-
54
но создать “дельфийское” приложение с настраиваемым интерфейсом. Программа SHAPEDEM содержит всего лишь объект TShape, размещенный на форме, вместе с двумя полосами прокрутки и несколькими кнопками. Эта программа интересна тем, что позволяет в режиме выполнения изменять размер, цвет и внешний вид объекта TShape, равно как размер и цвет самой формы.
Рисунок 4.4 - Программа SHAPEDEM имеет 2 полосы прокрутки и несколько кнопок Листинг А показывает код программы SHAPEDEM. Код головного модуля этой программы приведем по частям - по мере его написания. Листинг А: Исходный код программы SHAPEDEM.DPR. program Shapedem; uses Forms, Mina in 'MAIN.PAS' {Form1}; begin Application.CreateForm(TForm1, Form1); Application.Run; end. В примере полосы прокрутки (ScrollBars) используются для изменения размера фигуры, изображенной в средней части экрана, как показано на рис.4.5.
55
Рисунок 4.5 - Вы можете использовать полосы прокрутки, кнопки и список для изменения внешнего вида приложения Для выбора нового вида фигуры используйте выпадающий список (ComboBox), а для изменения цвета фигуры или окна (формы) используйте стандартное диалоговое окно выбора цвета, вызываемое кнопками “Цвет фигуры” и “Цвет формы”. Что нужно сделать пользователю (программисту) для того, чтобы получить возможность “в режиме выполнения” изменять цвет какого-либо элемента или всего окна (формы)? Для этого достаточно выполнить всего лишь несколько действий. 1. Для изменения цвета окна просто выберите компонент ColorDialog из палитры компонентов (она находится на страничке “Dialogs”) и поместите его на форму. 2. Кроме того, поместите на форму обычную кнопку (компонент Button, находится на страничке “Standard”). Для удобства чтения с помощью Object Inspector измените имя компонента (свойство Name) с “Button1” (которое дается по умолчанию) на “FormColor”, а его заголовок (свойство Caption) - на “Цвет формы”. 3. Дважды щелкните по кнопке “Цвет формы” - Delphi сгенерирует заготовку метода, который выглядит следующим образом: procedure TForm1.FormColorClick(Sender: TObject); begin end;
56
4. Теперь введите две простые строчки кода: procedure TForm1.FormColorClick(Sender: TObject); begin if ColorDialog1.Execute then Form1.Color := ColorDialog1.Color; end;
Введите эти две строки
Данный код во время выполнения при нажатии кнопки “Цвет формы” вызывает стандартное диалоговое окно выбора цвета, как показано на рис.4.6. Если в этом диалоговом окне щелкнуть кнопку OK, выполнится следующая строка: Form1.Color:=ColorDialog1.Color; Этот код установит свойство Color формы Form1 в цвет, который был выбран с помощью диалогового окна ColorDialog1. Та же самая техника может использоваться для изменения цвета фигуры (компонент Shape, объект TShape). 1. Поместите на форму другую кнопку, измените (при желании) ее имя на “ShapeColor”, а заголовок - на “Цвет Фигуры”. 2. Дважды щелкните по ней мышкой и создайте метод аналогичный следующему:
Рисунок 4.6 − Диалоговое окно “Color” дает возможность изменить цвет “во время выполнения”
procedure TForm1.ShapeColorClick(Sender: TObject); begin if ColorDialog1.Execute then Shape1.Brush.Color := ColorDialog1.Color; end;
Введите эти две строки
Все эти действия можно проделать и автоматически - например, можно изменить цвет определенного элемента формы, чтобы привлечь внима-
57
ние пользователя к какому-либо действию. Весь механизм Windows-сообщений, используемый при взаимодействии компонент во время выполнения, оказывается скрытым от программиста, делая процесс создания программ наиболее легким. Сложное программирование в среде Windows становится доступным “широким” массам программистов. Например, программирование изменения размера фигуры с помощью полос прокрутки, требовавшее в “чистом” Windows сложной обработки сообщений в конструкции типа “case”, в Delphi сводится к написанию одной-единственной строчки кода. 1. Для начала, поместите два компонента ScrollBar на форму (находится на страничке “Standard”) и установите свойство Kind первого компонента в sbHorizontal, а второго - в sbVertical. 2. Переключитесь на страничку “Events” в Инспекторе Объектов и создайте заготовки метода для отклика на событие OnChange для каждой полосы прокрутки. Напишите в каждом из методов по одной строчке следующим образом: procedure TForm1.ScrollBar1Change(Sender: TObject); begin Shape1.Width := ScrollBar1.Position * 3; end; procedure TForm1.ScrollBar2Change(Sender: TObject); begin Shape1.Height := ScrollBar2.Position * 2; end;
Введите эту строку
Введите эту строку
Код, показанный здесь, устанавливает свойства Width и Height фигуры TShape в соответствие с положением “бегунка” на полосах прокрутки (сомножители 3 и 2 введены только для лучшего представления). Последняя часть программы SHAPEDEM демонстрирует большие возможности языка Object Pascal, на основе которого построен Delphi. Можно ввести элементы в список компонента ComboBox как в режиме проектирования, так и при выполнении программы. При этом в режиме проектирования можно просто ввести нужные элементы в список Items, щелкнув маленькую кнопку с тремя точками в правой части строки Инспектора Объектов, показывающей данное свойство (Items). Появится диалоговое окно текстового редактора (String List Editor), в котором введете элементы (рис.4.7). Можно заметить, что список этих элементов совпадает со списком опций свойства Shape компонента
58
Рисунок 4.7 - Текстовый редактор для ввода строк Shape1 (Shape). Другими словами, если выделить компонент Shape1 на форме (просто щелкнув по нему) и посмотреть свойство Shape в Инспекторе Объектов, то появляется список возможных видов фигур, которые может принимать данный компонент. Это как раз те самые виды фигур, которые перечислялись в списке у компонента ComboBox1. Этот список можно найти в on-line справочнике по Delphi по контексту “TShapeType”. Или же, если заглянуть в исходный код класса TShape, там можно увидеть те же элементы, формирующие перечислимый тип TShapeType: TShapeType = (stRectangle, stSquare, stRoundRect, stRoundSquare, stEllipse, stCircle); Итак, смысл всего сказанного в том, что за всеми объектами, которые используются в “дельфийской” программе, стоит некий код на Паскале, к которому имеется доступ при “прямом” программировании. Это значит, что можно изменить поведение любой части программы во время выполнения путем написания соответствующего кода. В нашем конкретном случае, нужно написать только одну строчку кода, которая будет выполнена в качестве отклика на щелчок пользователем по выпадающему списку ComboBox1. Чтобы написать код этого отклика, в режиме проектирования необходимо сделать следующее: 1. Выделите компонент ComboBox1 на форме (как всегда, просто щелкнув по нему левой кнопкой мыши), затем перейдите на страничку “Events” в Инспекторе Объектов.
59
2. Дважды щелкните по пустому полю напротив события OnClick. В редакторе автоматически сгенерируется следующая заготовка метода: procedure TForm1.ComboBox1Click(Sender: TObject); begin end; 3. Теперь вставьте одну строчку кода, чтобы метод выглядел следующим образом: procedure TForm1.ComboBox1Click(Sender: TObject); begin Shape1.Shape := TShapeType(ComboBox1.ItemIndex); end;
Введите эту стро-
Эта строчка кода устанавливает свойство Shape компонента Shape1 в вид, который пользователь выберет в выпадающем списке. Этот код работает благодаря соответствию между порядковыми членами перечислимого типа и числовыми значениями различных элементов в ComboBox. Другими словами, первый элемент перечислимого типа имеет значение 0, что соответствует первому элементу, показанному в ComboBox (см. рис.4.7). Давайте рассмотрим этот подход несколько подробней. Если рассмотреть декларацию перечислимого типа TShapeType, можно увидеть, что первый его элемент называется “stRectangle”. По определению, компилятор назначает этому элементу порядковый номер 0. Следующему по порядку элементу назначается номер 1 и т.д. Таким образом, слова “stRectangle”, “stSquare” и т.п., в действительности, просто символизируют порядковые номера в данном перечислимом типе. На элементы в списке ComboBox также можно сослаться по их порядковому номеру, начиная с 0. Именно поэтому так важно (в данном случае) вводить указанные строки в строгом соответствии с декларацией типа TShapeType. Таким образом, используя преобразование типа “TshapeType (ComboBox1.ItemIndex)”, можно указать компилятору, что общего имеют элементы в ComboBox и перечислимый тип в TShapeType: а именно, порядковые номера. Итак, Delphi является очень гибким и мощным программным средством, которое позволяет быстро реализовать логику программы и предоставляет полное управление приложением.
60
Программа SHAPEDEM2 Программа SHAPEDEM проста в написании и в освоении. Однако при изменении пользователем размера окна она будет выглядеть “некрасиво”. Давайте изменим ее таким образом, чтобы программа сама обрабатывала изменение размера окна, а заодно изучим компонент меню. Для достижения этих целей сделаем следующее: 1. Кнопки и выпадающий список уберем с экрана и вместо них поместим на форму компонент меню (MainMenu) 2. “Заставим” полосы прокрутки изменять свое положение в зависимости от размера окна 3. “Заставим” свойство Position полос прокрутки изменяться, чтобы правильно отражать размер формы. Рисунок 4.8 показывает, как будет выглядеть программа после этих изменений.
Рисунок 4.8 - Программа SHAPDEM2 имеет возможность реагировать на изменение пользователем размера окна
61
Листинг FormOnResize.
B:
Программа
SHAPDEM2
включает
метод
Представлен главный модуль. unit Main; interface uses WinTypes, WinProcs, Classes, Graphics, Forms, Controls, ColorDlg, StdCtrls, Menus, Dialogs, ExtCtrls; type TForm1 = class(TForm) Shape1: TShape; ColorDialog1: TColorDialog; ScrollBar1: TScrollBar; ScrollBar2: TScrollBar; MainMenu1: TMainMenu; Shapes1: TMenuItem; ShapeColor1: TMenuItem; FormColor1: TMenuItem; Shapes2: TMenuItem; Rectangle1: TMenuItem; Square1: TMenuItem; RoundRect1: TMenuItem; RoundSquare1: TMenuItem; Ellipes1: TMenuItem; Circle1: TMenuItem; Exit1: TMenuItem; procedure NewShapeClick(Sender: TObject); procedure ShapeColorClick(Sender: TObject); procedure FormColorClick(Sender: TObject); procedure ScrollBar2Change(Sender: TObject); procedure ScrollBar1Change(Sender: TObject); procedure FormResize(Sender: TObject); procedure Exit1Click(Sender: TObject); private { Private declarations } public { Public declarations } end;
62
var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.NewShapeClick(Sender: TObject); begin Shape1.Shape := TShapeType((Sender as TMenuItem).Tag); end; procedure TForm1.ShapeColorClick(Sender: TObject); begin if ColorDialog1.Execute then Shape1.Brush.Color := ColorDialog1.Color; end; procedure TForm1.FormColorClick(Sender: TObject); begin if ColorDialog1.Execute then Form1.Color := ColorDialog1.Color; end; procedure TForm1.ScrollBar2Change(Sender: TObject); begin Shape1.Height := ScrollBar2.Position; end; procedure TForm1.ScrollBar1Change(Sender: TObject); begin Shape1.Width := ScrollBar1.Position; end; procedure TForm1.FormResize(Sender: TObject); var Menu, Caption, Frame: Integer; begin Caption := GetSystemMetrics(sm_cyCaption); Frame := GetSystemMetrics(sm_cxFrame) * 2; Menu := GetSystemMetrics(sm_cyMenu); Scrollbar1.Max := Width;
63
Scrollbar2.Max := Height; Scrollbar2.Left := Width - Frame - Scrollbar2.Width; Scrollbar1.Top := Height - ScrollBar2.Width - Frame Caption - Menu; Scrollbar1.Width := Width - Scrollbar2.Width - Frame; Scrollbar2.Height := Height - Frame - Caption - Menu Scrollbar1.Height; end; procedure TForm1.Exit1Click(Sender: TObject); begin Close; end; end. Главное меню для программы создается с помощью компонента MainMenu (находится на страничке “Standard” палитры компонентов). Поместив его на форму, дважды щелкните по нему мышкой - откроется редактор меню, в котором можно ввести нужные названия пунктов меню и, при желании, изменить их имена (задаваемые Delphi по умолчанию) для удобочитаемости. Создадим меню программы SHAPEDEM2 с тремя главными пунктами: “Цвета ”, “Фигуры”, “Выход”. Для первого пункта создадим следующие подпункты: • Цвет фигуры • Цвет окна Для второго: • Прямоугольник • Квадрат • Закругленный прямоугольник • Закругленный квадрат • Эллипс • Окружность Третий пункт меню не будет содержать никаких подпунктов. После создания всех пунктов и подпунктов меню для работы программы SHAPEDEM2 нужно назначить номера для каждого из подпунктов меню, связанных с типом фигуры. Для этого воспользуемся свойством Tag, имеющимся у каждого пункта меню. Свойство Tag (типа Integer) специально введено в каждый компонент Delphi с тем, чтобы программисты могли использовать его по своему усмотрению. Назначим 0 свойству Tag пункта “Прямоугольник”, 1 - пункту “Квадрат”, 2 - пункту “Закруг-
64
ленный прямоугольник” и т.д. Цель такого назначения будет объяснена позднее. Два метода, созданные для подпунктов изменения цвета аналогичны тем, которые были в программе SHAPEDEM: procedure TForm1.ShapeColorClick(Sender: TObject); begin if ColorDialog1.Execute then Shape1.Brush.Color := ColorDialog1.Color; end; procedure TForm1.FormColorClick(Sender: TObject); begin if ColorDialog1.Execute then Form1.Color := ColorDialog1.Color; end; Как видите, ничего не изменилось по сравнению с первой версией программы, хотя данные методы уже вызываются из меню, а не из кнопок. Аналогично, методы, реализующие реакцию на выбор подпунктов меню изменения вида фигуры также очень похожи на методы выбора фигуры через выпадающий список: procedure TForm1.NewShapeClick(Sender: TObject); begin Shape1.Shape := TShapeType((Sender as TMenuItem).Tag); end; Этот код “работает” правильно благодаря тому, что перечислимый тип TShapeType в качестве начального имеет значение 0 и в свойство Tag подпунктов меню также записаны порядковые номера, начинающиеся с нуля. Обратите внимание, что в данном примере используется оператор as, который позволяет надежно преобразовывать типы из одного в другой: в частности, преобразовать параметр Sender (имеющий общий тип TObject) в тип TMenuItem. Как правило, параметр Sender в Delphi - это управляющий элемент, посылающий сообщения функции, в которой он фигурирует. В данном случае, Sender является пунктом меню, и, следовательно, можно работать с этим параметром как если бы он был декларирован с типом TMenuItem. Главная причина использования оператора as состоит в том, что он
65
обеспечивает очень ясный синтаксис, даже если проводить сложное двухуровневое преобразование типов. Более того, оператор as обеспечивает проверку преобразования в режиме выполнения программы. Когда используется оператор as, можно быть увереным в том, что преобразование Sender в TMenuItem реально будет произведено лишь в том случае, если Sender действительно имеет тип TMenuItem. Две полосы прокрутки в программе SHAPEDEM2 всегда будут располагаться возле границ окна, независимо от его размеров. Выполнение этих действий требует написания несколько более сложного программирования, чем было ранее. Как было указано ранее, Delphi, хотя и скрывает от программиста детали Windows-программирования, однако не запрещает обращаться к функциям Windows API (прикладного пользовательского интерфейса). Таким образом, Delphi поддерживает низкоуровневое программирование на уровне Windows API. procedure TForm1.FormResize(Sender: TObject); var Menu, Caption, Frame: Integer; begin Caption := GetSystemMetrics(sm_cyCaption); Frame := GetSystemMetrics(sm_cxFrame) * 2; Menu := GetSystemMetrics(sm_cyMenu); Scrollbar1.Max := Width; Scrollbar2.Max := Height; Scrollbar2.Left := Width - Frame - Scrollbar2.Width; Scrollbar2.Height := Height - Frame - Caption - Menu; Scrollbar1.Top := Height - Scrollbar2.Width - Frame - Caption - Menu; Scrollbar1.Width := Width - Scrollbar2.Width - Frame; end;
Это вызовы функций Windows API
Код, показанный здесь, является реакцией на событие OnResize. Это событие перечислено среди других на страничке “Events” Инспектора Объектов в состоянии, когда выбрана форма (окно). Можно ожидать, событие (сообщение) OnResize посылается форме (окну) каждый раз, когда пользователь “захватывает” мышкой за какой-либо край окна и делает размер окна большим или меньшим. Однако, это же сообщение (событие) посылается окну и тогда, когда происходит максимизация окна (но не минимизация).
66
Первое, что делается в данном методе - запрашиваются системные параметры, определяющие размеры заголовка окна, огибающей его рамки и меню. Эта информация “добывается” путем вызова функции GetSystemMetrics, являющейся частью Windows API. Функции GetSystemMetrics передается один аргумент в виде константы, определяющей вид запрашиваемой информации. Например, если передавать функции константу sm_cyCaption, получают в качестве результата высоту заголовка окна (в пикселах). Полный список этих констант имеется в on-line справочнике Delphi. В методе FormResize программа вычисляет новые размеры полос прокрутки: Scrollbar1.Max := Width; Scrollbar2.Max := Height; Scrollbar2.Left := Width - Frame - Scrollbar2.Width; Scrollbar2.Height := Height - Frame - Caption - Menu; Scrollbar1.Top := Height - Scrollbar2.Width - Frame - Caption - Menu; Scrollbar1.Width := Width - Scrollbar2.Width - Frame; Вычисления, приведенные здесь, включают простые математические действия. Например, левая сторона вертикальной полосы прокрутки должна быть равна ширине всего окна (формы) за вычетом ширины рамки и ширины самой полосы прокрутки. Это элементарная логика, и реализовав ее в программе, мы получим вертикальную полосу прокрутки, всегда располагающуюся возле правого края окна (формы). В программе SHAPEDEM свойство Max каждой полосы прокрутки оставалось равным значению по умолчанию - 100; это означало, что после того как бегунок полосы прокрутки пройдет все доступное расстояние (как для вертикальной, так и для горизонтальной полосы прокрутки), свойство Position будет установлено в 100. Если бегунок возвращался к началу, свойство Position устанавливалось равным свойству Min, которое, по умолчанию, 0. В программе SHAPEDEM2 можно изменять значения свойств Min и Max так, чтобы диапазон значений Position полос прокрутки отражал текущий размер окна (формы), даже при изменении формой своего размера в режиме выполнения. Здесь приведены соответствующие строки из метода FormResize. procedure TForm1.FormResize(Sender: TObject); begin
67
... Scrollbar1.Max := Width; Scrollbar2.Max := Height; ... end; Две строчки кода, показанные выше, просто устанавливают максимальные значения полос прокрутки равными ширине и высоте формы соответственно. После этого всегда можно сделать помещенную на форму фигуру такой же “большой”, как и сама форма. После введения таких изменений больше не потребуется умножать свойство Position на какойлибо множитель. procedure TForm1.Scrollbar2Change (Sender: TObject); begin Shape1.Height := Scrollbar2.Position; end; Если после этого запустить программу SHAPDEM2 на выполнение (F9), то увидем, что она работает корректно при любом изменении размера формы. Более того, теперь можно выбирать фигуры и цвета из меню, что придает программе более строгий вид. В конце хотелось бы сделать одно маленькое замечание. Каждая форма, по умолчанию, имеет две полосы прокрутки (HorzScrollbar и VertScrollbar), которые появляются автоматически всякий раз, когда размер формы становится меньше, чем область, занимаемая управляющими элементами, расположенными на этой форме. Иногда эти полосы прокрутки могут быть очень полезными, но в данной ситуации они сделают совсем не то, что хотелось бы. Поэтому, для надежности, можно установить их вложенные свойства Visible в False.
68
5. МЕТОДЫ В DELPHI Чтобы полностью понять и почувствовать все преимущества Delphi, нужно хорошо изучить язык Object Pascal. И хотя возможности визуальной части Delphi чрезвычайно богаты, хорошим программистом может стать только тот, кто хорошо разбирается в технике ручного написания кода. 5.1. Создание методов с помощью визуальных средств В предыдущем разделе показано, что синтаксический “скелет” метода может быть сгенерирован с помощью визуальных средств. Для этого, напомним, нужно в Инспекторе Объектов дважды щелкнуть мышкой на пустой строчке напротив названия интересующего события в требуемом компоненте. Заметим, если эта строчка не пуста, то двойной щелчок на ней просто переместит в окне Редактора Кода в то место, где находится данный метод. Для более глубокого понимания дальнейшего изложения кратко напомним основные концепции объектно-ориентированного программирования. Класс - это категория объектов, обладающих одинаковыми свойствами и поведением. При этом объект представляет собой просто экземпляр какого-либо класса. Например, в Delphi тип “форма” (окно) является классом, а переменная этого типа - объектом. Метод - это процедура, которая определена как часть класса и инкапсулирована (содержится) в нем. Методы манипулируют полями и свойствами классов (хотя могут работать и с любыми переменными) и имеют автоматический доступ к любым полям и методам своего класса. Доступ к полям и методам других классов зависит от уровня “защищенности” этих полей и методов. Пока же для нас важно то, что методы можно создавать как визуальными средствами, так и путем написания кода вручную. Пример Давайте рассмотрим процесс создания программы CONTROL1, которая поможет нам изучить технику написания методов в Delphi. Для создания программы CONTROL1 поместите с помощью мышки компонент Edit (находится на страничке “Standard” Палитры Компонентов) на форму. После этого ваша форма будет иметь вид, показанный на рисунке 5.1. Рисунок 5.1 - Главная форма Теперь перейдите в Object программы CONTROL1 Inspector, выберите страничку “Events”
69
и дважды щелкните в пустой строчке напротив события OnDblClick, как показано на рисунке 5.2. После этого в активизировавшемся окне Редактора Вы увидите сгенерированный “скелет” метода Edit1DblClick, являющегося реакцией на событие OnDblClick:
Рисунок 5.2 - Чтобы создать метод, просто дважды щелкните справа от слова OnDblClick
procedure TForm1.Edit1DblClick(Sender: TObject); begin end;
После генерации процедуры можно оставить ее имя таким, каким “установил” Delphi, или изменить его на любое другое (для этого просто введите новое имя в указанной выше строке Инспектора Объектов справа от требуемого события и нажмите Enter). Теперь в окне Редактора Кода введите смысловую часть метода: procedure TForm1.Edit1DblClick(Sender: TObject); begin Edit1.Text:= 'Дважды щелкнули в строке редактирования'; end; Сохраните программу. Во время выполнения дважды щелкните на строке редактирования. Текст в этой строке изменится в соответствии с тем, что мы написали в методе Edit1DblClick (см. рисунок 5.3).
Рисунок 5.3 - Содержимое управляющего элемента TEdit изменяется после двойного щелчка по нему Предоставим полный код программы CONTROL1 (листинг С и D).
70
Листинг С: Программа CONTROL1 - демонстрирует, как создавать и использовать методы в Delphi. program Control1; uses Forms, Main in 'MAIN.PAS' {Form1}; begin Application.CreateForm(TForm1, Form1); Application.Run; end. Листинг D: Головной модуль программы CONTROL1. unit Main; interface uses WinTypes, WinProcs, Classes, Graphics, Controls, Printers, Menus, Forms, StdCtrls; type TForm1 = class(TForm) Edit1: TEdit; procedure Edit1DblClick(Sender: TObject); end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.Edit1DblClick(Sender: TObject); begin Edit1.Text := 'Дважды щелкнули в строке редактирования'; end; end. После того, как программа загрузится в память, выполняются две строчки кода в CONTROL1.DPR, автоматически сгенерированные компилятором: Application.CreateForm(TForm1, Form1); Application.Run;
71
Первая строка запрашивает память у операционной системы и создает там объект Form1, являющийся экземпляром класса TForm1. Вторая строка указывает объекту Application, “по умолчанию” декларированному в Delphi, чтобы он запустил на выполнение главную форму приложения. В данном месте мы не будем подробно останавливаться на классе TApplication и на автоматически создаваемом его экземпляре - Application. Важно понять, что главное его предназначение - быть неким ядром, управляющим выполнением Вашей программы. Итак, мы видели, что большинство кода Delphi генерирует автоматически. В большинстве приложений все, что остается сделать пользователю - это вставить одну или несколько строк кода, как в методе Edit1DblClick: Edit1.Text := 'Дважды щелкнули в строке редактирования'; Хотя внешний интерфейс программы CONTROL1 достаточно прост, она (программа) имеет строгую внутреннюю структуру. Как уже говорили (см. раздел 2.3), каждая программа в Delphi состоит из файла проекта, имеющего расширение .DPR и одного или нескольких модулей, имеющих расширение .PAS. Модуль, в котором содержится главная форма проекта, называется головным. Указанием компилятору о связях между модулями является предложение Uses, которое определяет зависимость модулей. 5.2. Структура модуля Нет никакого функционального различия между модулями, созданными пользователями в Редакторе, и модулями, сгенерированными Delphi автоматически. В любом случае модуль подразделяется на три секции: • Заголовок • Секция Interface • Секция Implementation Таким образом, “скелет” модуля выглядит следующим образом: unit Main;
{Заголовок модуля}
interface
{Секция Interface}
implementation
{Секция Implementatiоn}
end.
72
В интерфейсной секции (interface) описывается все то, что должно быть видимо для других модулей (типы, переменные, классы, константы, процедуры, функции). В секции implementation помещается код, реализующий классы, процедуры или функции. 5.3. Передача параметров В Delphi процедурам и функциям (а, следовательно, и методам классов) могут передаваться параметры для того, чтобы обеспечить их необходимой для работы информацией. Программа PARAMS демонстрирует, как использовать передачу параметров в методы Delphi. Кроме того, она показывает, как: • создавать свои собственные процедуры • добавлять процедуру в класс, формируя метод класса • вызывать одну процедуру из другой. Программа PARAMS позволяет вводить фразы в строки редактирования. После нажатия кнопки “Вызов процедуры WriteAll” строка из управляющего элемента EditSource скопируется в шесть управляющих элементов - строк редактирования, как показано на рисунке 5.4. 1. Разместите на форме семь компонентов Edit, переименуйте с помощью Инспектора Объектов седьмой компонент (Edit7) в EditSource. 2. Положите на форму компонент Button, и в Object Inspector измените его заголовок (свойство Caption) на “Вызов процедуры WriteAll” (естественно, можно заменить его шрифт, цвет и т.д.). После завершения проектирования формы класс TForm1 будет выглядеть следующим образом: TForm1 = class(TForm) Edit1: TEdit; Edit2: TEdit; Edit3: TEdit; Edit4: TEdit; Edit5: TEdit; Edit6: TEdit; EditSource: TEdit; Button1: TButton; end; 3. Следующий шаг состоит в добавлении метода, вызываемого по нажатию пользователем кнопки Button1. Это, напомним, можно сделать дву-
73
мя способами: • Перейти в Инспекторе Объектов на страничку “Events” (предварительно выбрав компонент Button1 на форме), выбрать слово OnClick и дважды щелкнуть мышкой на пустой строчке справа от него • Просто дважды щелкнуть на компоненте Button1 на форме. Delphi сгенерирует следующую “заготовку”: procedure TForm1.Button1Click(Sender: TObject); begin
Рисунок 5.4 - Программа PARAMS позволяет вызовом одной процедуры заполнить 6 строк редактирования
end; Цель программы PARAMS - научить писать процедуры и передавать в них параметры. В частности, программа PARAMS реагирует на нажатие кнопки Button1 путем вызова процедуры WriteAll и передачи ей в качестве параметра содержимого строки редактирования EditSource (EditSource.Text). procedure TForm1.Button1Click(Sender: TObject); begin WriteAll(EditSource.Text); end; Важно понять, что объект EditSource является экземпляром класса TEdit и, следовательно, имеет свойство Text, содержащее набранный в строке редактирования текст. По умолчанию свойство Text содержит значение, совпадающее со значением имени компонента (Name) - в данном случае “EditSource”. Свойство Text, естественно, можно редактировать как в режиме проектирования, так и во время выполнения. Текст, который должен быть отображен в шести строках редактирования, передается процедуре WriteAll как параметр. Чтобы передать параметр процедуре, просто напишите имя этой процедуры и заключите передаваемый параметр (параметры) в скобки - вот так:
74
WriteAll(EditSource.Text); Заголовок этой процедуры выглядит следующим образом: procedure TForm1.WriteAll(NewString: String); где указано, что передаваемый процедуре параметр NewString должен иметь тип String. Вспомним, что задача процедуры WriteAll состоит в копировании содержимого строки редактирования EditSource в шесть других строк редактирования Edit1-Edit6. Поэтому процедура должна выглядеть следующим образом: procedure TForm1.WriteAll(NewString: String); begin Edit1.Text := NewString; Edit2.Text := NewString; Edit3.Text := NewString; Edit4.Text := NewString; Edit5.Text := NewString; Edit6.Text := NewString; end; Поскольку процедура WriteAll не является откликом на какое-либо событие в Delphi, то ее нужно полностью написать “вручную”. Простейший способ сделать это - скопировать заголовок какой-либо уже имеющейся процедуры, исправить его, а затем дописать необходимый код. Возвратимся еще раз к заголовку процедуры. Заголовок состоит из пяти частей: procedure TForm1.WriteAll(NewString: String);
•
Первая часть - зарезервированное слово “procedure”; пятая часть - концевая точка с запятой “;”. Обе эти части служат определенным синтаксическим целям, а именно: первая информирует компилятор о том, что определен синтаксический блок “процедура”, а вторая указывает на окончание заголовка (собственно говоря, все операторы в Delphi должны заканчиваться точкой с запятой). • Вторая часть заголовка - слово “TForm1”, которое квалифицирует то обстоятельство, что данная процедура является методом класса TForm1.
75
•
Третья часть заголовка - имя процедуры. Вы можете выбрать его любым, по вашему усмотрению. В данном случае мы назвали процедуру “WriteAll”. • Четвертая часть заголовка - параметр. Параметр декларируется внутри скобок и, в свою очередь, состоит из двух частей. Первая часть - имя параметра, вторая - его тип. Эти части разделены двоеточием. Если Вы описываете в процедуре более чем один параметр, нужно разделить их точкой с запятой, например: procedure Example(Param1: String; Param2: String); После создания “вручную” заголовка процедуры, являющейся методом класса, ее нужно включить в декларацию класса, например, путем копирования (еще раз напомним, что для методов, являющихся откликами на дельфийские события, данное включение производится автоматически): TForm1 = class(TForm) Edit1: TEdit; Edit2: TEdit; Edit3: TEdit; Edit4: TEdit; Edit5: TEdit; Edit6: TEdit; EditSource: TEdit; Button1: TButton; procedure Button1Click(Sender: TObject); procedure WriteAll(NewString: String); end;
Введите эту строку
В данном месте нет необходимости оставлять в заголовке метода слово “TForm1”, так как оно уже присутствует в описании класса. Листинг Е показывает полный текст головного модуля программы PARAMS (не включен сюда файл проекта, поскольку он практически одинаков для всех программ).
76
Листинг Е: Исходный код головного модуля программы PARAMS показывает, как использовать строки редактирования и как передавать параметры. Unit Main; interface uses WinTypes, WinProcs, Classes, Graphics, Controls, Printers, Forms, StdCtrls; type TForm1 = class(TForm) Edit1: TEdit; Edit2: TEdit; Edit3: TEdit; Edit4: TEdit; Edit5: TEdit; Edit6: TEdit; EditSource: TEdit; Button1: TButton; procedure Button1Click(Sender: TObject); procedure WriteAll(NewString: String); end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.WriteAll(NewString: String); begin Edit1.Text := NewString; Edit2.Text := NewString; Edit3.Text := NewString; Edit4.Text := NewString; Edit5.Text := NewString; Edit6.Text := NewString; end;
77
procedure TForm1.Button1Click(Sender: TObject); begin WriteAll(EditSource.Text); end; end. При экспериментах с программой PARAMS можно попробовать изменить имена процедур и параметров. Однако, следует помнить, что ряд слов в Delphi являются зарезервированными, и употреблять их в идентификаторах (именах процедур, функций, переменных, типов, констант) не разрешается - компилятор сразу же обнаружит ошибку. К ним относятся такие слова, как “procedure”, “string”, “begin”, “end” и т.п.; полный же список их приведен в on-line справочнике Delphi. Не старайтесь запомнить сразу все зарезервироРисунок 5.5 - Внешний вид программы ванные слова - компилятор CONTROL2 “напомнит” Вам о неправильном их использовании выдачей сообщения типа “Identifier expected.” (Ожидался идентификатор, а обнаружено зарезервированное слово). 5.4. Более сложные методы и управляющие элементы Продолжим изучение компонент и способов создания их методов. В программе CONTROL1, рассмотренной в начале раздела, был сгенерирован метод, являющийся откликом на событие OnClick строки редактирования Edit1. Аналогично, можно сгенерировать метод, являющийся реакцией на событие OnDblClick. В программе CONTROL2 расширен список находящихся на форме компонентов и для многих из них определены события OnClick и OnDblClick. Для исследования можно просто скопировать файлы проекта CONTROL1 в новую директорию CONTROL2, изменить имя проекта на CONTROL2.DPR (в этом файле после ключевого слова “program” также должно стоять название “CONTROL2”) и добавить
78
компоненты Label, GroupBox, CheckBox, RadioButton, Button на форму (эти компоненты находятся на страничке “Standard” Палитры Компонентов). Форма будет иметь примерно следующий вид – рисунок 5.5. Заметим, что необходимо “положить” компонент GroupBox на форму до того, как добавлять компоненты CheckBox и RadioButton, которые, в нашем примере, должны быть “внутри” группового элемента. Иначе, объекты CheckBox1, CheckBox2, RadioButton1 и RadioButton2 будут “думать”, что их родителем является форма Form1 и при перемещении GroupBox1 по форме не будут перемещаться вместе с ней. Таким образом, во избежание проблем, компонент, который должен быть “родителем” других компонент (Panel, GroupBox, Notebook, StringGrid, ScrollBox и т.д.), нужно помещать на форму до помещения на нее его “детей”. Если Вы все же забыли об этом и поместили “родителя” (например, GroupBox) на форму после размещения на ней его “потомков” (например, CheckBox и RadioButton) - не отчаивайтесь! Отметьте все необходимые объекты и скопируйте (с удалением) их в буфер обмена с помощью команд меню Edit|Cut. После этого отметьте на форме нужный объект (GroupBox1) и выполните команду меню Edit|Paste. После этого все выделенные ранее объекты будут помещены на форму, и их “родителем” будет GroupBox1. Описанный механизм является стандартным и может быть использован для всех видимых компонент. Выберите объект Label1. Создайте для него метод, являющийся откликом на событие OnDblClick. Введите в метод одну строчку, например: procedure TForm1.Label1DblClick(Sender: TObject); begin Edit1.Text := 'Двойной щелчок на Label1'; end; Запустите программу (F9) на выполнение и дважды щелкните мышкой на метке Label1. Вы увидите, что строка редактирования изменится, и в ней появится текст “Двойной щелчок на Label1”. Теперь закройте приложение и возвратитесь в режим проектирования. Добавьте обработчики событий OnClick и OnDblClick для каждого объекта, имеющегося на форме. Текст вашего головного модуля будет выглядеть следующим образом:
79
Листинг F: Головной модуль программы CONTROL2. Unit Main; interface uses WinTypes, WinProcs, Classes, Graphics, Controls, StdCtrls, Printers, Menus, Forms; type TForm1 = class(TForm) Label1: TLabel; Edit1: TEdit; Button1: TButton; GroupBox1: TGroupBox; CheckBox1: TCheckBox; CheckBox2: TCheckBox; RadioButton1: TRadioButton; RadioButton2: TRadioButton; procedure Edit1DblClick(Sender: TObject); procedure Label1DblClick(Sender: TObject); procedure CheckBox1Click(Sender: TObject); procedure CheckBox2Click(Sender: TObject); procedure RadioButton1Click(Sender: TObject); procedure RadioButton2Click(Sender: TObject); procedure Button1Click(Sender: TObject); end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.Edit1DblClick(Sender: TObject); begin Edit1.Text := 'Двойной щелчок на Edit1'; end; procedure TForm1.Label1DblClick(Sender: TObject);
80
begin Edit1.Text := ' Двойной щелчок на Label1'; end ; procedure TForm1.CheckBox1Click(Sender: TObject); begin Edit1.Text := 'Щелчок на CheckBox1'; end; procedure TForm1.CheckBox2Click(Sender: TObject); begin Edit1.Text := 'Щелчок на CheckBox2'; end; procedure TForm1.RadioButton1Click(Sender: TObject); begin Edit1.Text := 'Щелчок на RadioButton1'; end; procedure TForm1.RadioButton2Click(Sender: TObject); begin Edit1.Text := 'Щелчок на Radiobutton2'; end; procedure TForm1.Button1Click(Sender: TObject); begin Edit1.Text := 'Щелчок на Button1'; end; end. Эта программа служит двум целям: • Она показывает, как создавать процедуры (методы) и как “наполнять” их содержательной “начинкой”. • Она демонстрирует технику работы с управляющими элементами Windows.
81
5.5. Информация периода выполнения. Программа CONTROL3 Методы программы CONTROL2, являющиеся откликами на события OnClick и OnDblClick, во многом похожи друг на друга. Открытость среды Delphi позволяет получать и оперировать информацией особого рода, называемой информацией периода выполнения (RTTI - run-time type information). Эта информация организована в виде нескольких уровней. Верхний уровень RTTI представлен как средство проверки и приведения типов с использованием ключевых слов is и as. Ключевое слово is дает программисту возможность определить, имеет ли данный объект требуемый тип или является одним из наследников данного типа, например, таким образом: if MyObject is TSomeObj then ... Имеется возможность использовать RTTI и для процесса приведения объектного типа, используя ключевое слово as: if MyObject is TSomeObj then (MyObject as TSomeObj).MyField:=... что эквивалентно: TSomeObj(MyObject).MyField:=... Средний уровень RTTI использует методы объектов и классов для подмены операций as и is на этапе компиляции. В основном, все эти методы заложены в базовом классе TObject, от которого наследуются все классы библиотеки компонент VCL. Для любого потомка TObject доступны, в числе прочих, следующие информационные методы: • ClassName - возвращает имя класса, экземпляром которого является объект; • ClassInfo - возвращает указатель на таблицу с RTTI, содержащей информацию о типе объекта, типе его родителя, а также о всех его публикуемых свойствах, методах и событиях; • ClassParent - возвращает тип родителя объекта; • ClassType - возвращает тип самого объекта; • InheritsFrom - возвращает логическое значение, определяющее, является ли объект потомком указанного класса;
82
• InstanceSize - возвращает размер объекта в байтах. Эти методы могут использоваться в коде программы напрямую. Нижний уровень RTTI определяется в дельфийском модуле TypInfo и представляет особый интерес для разработчиков компонент. Через него можно получить доступ к внутренним структурам Delphi, в том числе, к ресурсам форм, инспектору объектов и т.п. Итак, доступ к информации периода выполнения в Delphi позволяет динамически получать как имя объекта, находящегося на форме, так и название класса, которому он принадлежит (и еще много другой полезной информации; но об этом - в дальнейших уроках). Для этого используется свойство Name, имеющееся у любого класса-наследника TComponent (а таковыми являются все компоненты, входящие в дельфийскую библиотеку VCL), и метод ClassName, доступный для любого потомка класса базового TObject. А, поскольку класс TComponent, в свою очередь, является наследником класса TObject, то он доступен для всех компонент из библиотеки VCL. Вернувшись к нашим примерам, мы можем заменить целую “кучу” методов двумя, реализующими события OnClick и OnDblClick для всех объектов сразу. Для этого можно скопировать все файлы из CONTROL2 в новый директорий CONTROL3 или использовать для работы уже имеющуюся на диске программу. Создадим стандартным образом методы ControlDblClick и ControlClick для какого-либо объекта (например, для Label1). Введем в них следующие строки: procedure TForm1.ControlDblClick(Sender: TObject); begin Edit1.Text := 'Двойной щелчок на' + (Sender as TComponent).Name + ' (класс' + Sender.ClassName + ')'; end; procedure TForm1.ControlClick(Sender: TObject); begin Edit1.Text := 'Щелчок на ' + (Sender as TComponent).Name + ' (класс' + Sender.ClassName + ')'; end;
Введите эти три строки
Введите эти три строки
83
Теперь назначим данные методы всем событиям OnClick и OnDblClick, имеющимся у расположенных на форме объектов. Размер программы существенно сократился, а функциональность ее значительно выросла. В режиме выполнения после, например, щелчка на объекте CheckBox1 приложение будет иметь вид, изображенный на рисунке 5.6.
Рисунок 5.6 - Программа CONTROL3 выводит информацию не только об имени объекта, но и о названии его класса (типа) Итак, используя информацию периода выполнения, можно сделать программу очень гибкой и универсальной.
84
6. СОБЫТИЯ В DELPHI Одна из ключевых целей среды визуального программирования скрыть от пользователя сложность программирования в Windows. При этом, однако, хочется, чтобы такая среда не была упрощена слишком, не до такой степени, что программисты потеряют доступ к самой операционной системе. Программирование, ориентированное на события - неотъемлемая черта Windows. Некоторые программные среды для быстрой разработки приложений (RAD) пытаются скрыть от пользователя эту черту совсем, как будто она настолько сложна, что большинство не могут ее понять. Истина заключается в том, что событийное программирование само по себе не так уж сложно. Однако, есть некоторые особенности воплощения данной концепции в Windows, которые в некоторых ситуациях могут вызвать затруднения. Delphi предоставляет полный доступ к подструктуре событий, предоставляемой Windows. С другой стороны, Delphi упрощает программирование обработчиков таких событий. Объекты из библиотеки визуальных компонент (VCL) Delphi, равно как и объекты реального мира, имеют свой набор свойств и свое поведение - набор откликов на события, происходящие с ними. Список событий для данного объекта, на которые он реагирует, можно посмотреть, например, в Инспекторе Объектов на странице событий. (На самом деле, на этой странице представлен список свойств, которые имеют тип вроде TMouseMoveEvent и представляют из себя процедуры-обработчики событий. Существует соглашение по названиям данных свойств. Например, OnDblClick соответствует двойному щелчку мыши, а OnKeyUp - событию, когда нажатая клавиша была отпущена.) Среди набора событий для различных объектов из VCL есть как события, импортируемые из Windows (MouseMove, KeyDown), так и события, порождаемые непосредственно в программе (DataChange для TDataSource). Поведение объекта определяется тем, какие обработчики и для каких событий он имеет. Создание приложения в Delphi состоит из настройки свойств используемых объектов и создания обработчиков событий. Простейшие события, на которые иногда нужно реагировать - это, например, события, связанные с мышкой (они есть практически у всех видимых объектов) или событие Click для кнопки TButton. Предположим, что необходимо перехватить щелчок левой кнопки мыши на форме. Чтобы сделать это - создайте новый проект, в Инспекторе Объектов выберите страницу событий и сделайте двойной щелчок на правой части для свойства OnClick. Вы получите заготовку для обработчика данного события:
85
procedure TForm1.FormClick(Sender: TObject); begin end; Напишите здесь следующее: procedure TForm1.FormClick(Sender: TObject); begin MessageDlg('Hello', mtInformation, [mbOk], 0); end; Каждый раз , когда делается щелчок левой кнопки мыши над формой будет появляться окно диалога (см. рис.6.1).
Рисунок 6.1 - Диалог, появляющийся при щелчке мыши на форме Код, приведенный выше, представляет из себя простейший случай ответа на событие в программе на Delphi. Он настолько прост, что многие программисты могут написать такой код и без понимания того, что они на самом деле отвечают на сообщение о событии, посланное им операционной системой. Хотя программист получает это событие через третьи руки, тем не менее, он на него отвечает. Опытные программисты в Windows знают, что при возникновении события, операционная система передает не только уведомление о нем, но и некоторую связанную с ним информацию. Например, при возникновении события “нажата левая кнопка мыши” программа информируется о том, в каком месте это произошло. Если необходимо получить доступ к такой информации, то нужно вернуться в Инспектор Объектов и создать обработчик события OnMouseDown: procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin
86
Canvas.TextOut(X, Y, 'X='+IntToStr(X)+' Y='+IntToStr(Y)); end; Запустите программу, пощелкайте мышкой на форме:
Рисунок 6.2 В Delphi очень просто отвечать на события. И не только на события, связанные с мышкой. Например, можно создать обработчик для OnKeyDown (нажата клавиша): procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin MessageDlg(Chr(Key), mtInformation, [mbOk], 0); end; Теперь, когда имеются начальные знания о программировании событий в Delphi, самое время вернуться назад и посмотреть на теорию, стоящую за тем кодом, что написали. После получения представления о том, как работает система, можно вернуться к среде Delphi и посмотреть, как использовать полностью имеющиеся возможности. 6.1. Понимание событий Событийное программирование есть не только в Windows, и данную черту можно реализовать не только в операционной системе. Например, любая DOS программа может быть основана на простом цикле, работающем все время жизни программы в памяти. Ниже находится гипотетический пример, как данный код может выглядеть:
87
begin InitializeMemory; repeat CheckForMouseEvent(Events); CheckForKeyPress(Events) HandleEvents(Events); until Done := True; DisposeMemory; end. Это типичный пример программы, ориентированной на события. Она начинается и заканчивается инициализацией и освобождением памяти. В программе присутствует простой цикл repeat..until, который проверяет появление событий от мыши и клавиатуры и затем дает возможность программисту ответить на эти события. Переменная Events может быть записью с простой структурой: TEvent = record X, Y: Integer; MouseButton: TButton; Key: Word; end; Тип TButton, указанный выше, можно декларировать так: TButton = (lButton, rButton); Эти структуры позволяют проследить, где находится мышь, каково состояние ее кнопок, и значение нажатой клавиши на клавиатуре. Конечно, это пример очень простой структуры, но заложенные здесь принципы отражают то, что происходит внутри Windows или внутри других систем, ориентированных на события, вроде Turbo Vision. Если бы программа, приведенная выше, была редактором текста, то обработчик HandleEvent для такой программы мог бы иметь вид: procedure HandleEvent(Events: TEvent); begin case Events.Key of ’A’..’z’: Write(Events.Key); EnterKey: Write(CarriageReturn); EscapeKey: Done := True; end;
88
end; Согласно коду выше, программа будет печатать букву ‘a’ при нажатии этой клавиши и перейдет на новую строку, если нажата клавиша ‘Enter’. Нажатие ‘Esc’ приведет к завершению программы. Код такого типа может быть очень удобным, в частности, если писать программу, в которой требуется анимация. Например, если нужно перемещать несколько картинок по экрану, то может понадобиться сдвинуть их на несколько точек, затем проверить, нажимал ли пользователь кнопки. Если такое событие было, то его можно обработать, если нет, то двигать дальше. Приведенный пример дает некоторое понимание работы ориентированной на события системы. Единственное, что осталось пропущенным почему система Windows так спроектирована. Одной из основных причин, почему Microsoft сделал Windows по такой схеме, является тот факт, что множество задач работает в среде одновременно. В многозадачных системах операционная система должна знать, щелкнул ли пользователь мышкой на определенное окно. Если это окно было частично перекрыто другим, то это становится известно операционной системе, и она перемещает окно на передний план. Понятно, что неудобно заставлять само окно выполнять эти действия. Операционной системе лучше обрабатывать все нажатия клавиш и кнопок на мыши и затем передавать их в остальные программы в виде событий. Если кратко, программист в Windows почти никогда не должен напрямую проверять “железо”. Система выполняет эту задачу и передает информацию программе в виде сообщений. Когда пользователь щелкает мышкой, операционная система обрабатывает это событие и передает его в окно, которое должно обработать данное событие. Созданное сообщение, в этом случае, пересылается в некую процедуру DefWindowProc окна (default window procedure). DefWindowProc - аналог процедуры HandleEvent из примера, приведенного выше. Каждое окно в Windows имеет свою DefWindowProc. Чтобы полностью понять данное утверждение, представьте, что каждая кнопка, каждый ListBox, каждое поле ввода и т.д. на самом деле являются окнами и имеют свою процедуру DefWindowProc. Это очень гибкая и мощная система, но она может заставить программиста писать очень сложный код. Delphi дает возможность быть защищенным от такой структуры программы. Почти все, что происходит в Windows, принимает форму сообщений и, если необходимо их использовать в Delphi (в большей мере это необходимо при написании своих собственных компонент), то нужно понять, как эти сообщения работают. Если посмотреть на DefWindowProc в справочнике по Windows API,
89
то можно увидеть следующее определение: function DefWindowProc(Wnd: HWnd; Msg, wParam: Word; lParam: LongInt); Каждое сообщение, посылаемое в окно, состоит из четырех частей: первая часть - handle окна, получающего сообщение, Msg сообщает, что произошло а третья и четвертая части (wParam и lParam) содержат дополнительную информацию о событии. Вместе эти четыре части являются аналогом показанной выше структуры TEvent. Вторая часть сообщения имеет длину 16 бит и сообщает, что за событие произошло. Например, если нажата левая кнопка на мыши, то переменная Msg содержит значение WM_LBUTTONDOWN. Существуют десятки различного типа cообщений, и они называются вроде WM_GETTEXT, WM_HSCROLL, WM_GETTEXTLENGTH и т.п. Список всех сообщений можно видеть в справочнике по Windows API (on-line help). Последние две переменные, длиной 16 и 32 бита, называются wParam и lParam. Они сообщают программисту важную дополнительную информацию о каждом событии. Например при нажатии кнопки мыши, lParam содержит координаты указателя мыши. Одна из хитростей заключается в том, как выделить нужную информацию из этих переменных. В большинстве случаев Delphi освобождает программистов от необходимости выполнять данную задачу. Например, в обработчике события OnMouseDown для формы пользователь просто использует координаты X и Y. Программисту не нужно прилагать усилия для получения сообщения и связанных с ним параметров. Все, что связано с событиями, представлено в простом и непосредственном виде: procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Итак, если подвести итог, то должно стать ясным следующее: • Windows является системой ориентированной на события; • События в Windows принимают форму сообщений; • В недрах VCL Delphi сообщения Windows обрабатываются и преобразуются в более простую для программиста форму; • Обработка событий в Delphi сводится к написанию для каждого объекта своих обработчиков; • События в программе на Delphi вызываются не только сообщения-
90
ми Windows, но и внутренними процессами. 6.2. Обработка сообщений Windows в Delphi Конечно, нельзя придумать такую библиотеку объектов, которые бы полностью соответствовали потребностям программистов. Всегда возникнет необходимость дополнения или изменения свойств и поведения объектов. В этом случае, так же, как и при создании своих собственных компонент в Delphi, часто требуется обрабатывать сообщения Windows. Поскольку Object Pascal является развитием и продолжением Borland Pascal 7.0, то это выполняется сходным с BP способом. Общий синтаксис для декларации обработчика сообщений Windows: procedure Handler_Name(var Msg : MessageType); message WM_XXXXX; Handler_Name обозначает имя метода; Msg - имя передаваемого параметра; MessageType - какой либо тип записи, подходящий для данного сообщения; директива message указывает, что данный метод является обработчиком сообщения; WM_XXXXX - константа или выражение, которое определяет номер обрабатываемого сообщения Windows. Рассмотрим обработку сообщений на примере. Например, при нажатии правой кнопки мыши на форме в программе появляется всплывающее меню (pop-up menu, если оно было привязано к этой форме). Программист может захотеть привязать к правой кнопке какое-нибудь другое событие. Это можно сделать так: type TForm1 = class(TForm) PopupMenu1: TPopupMenu; MenuItem1: TMenuItem; MenuItem2: TMenuItem; MenuItem3: TMenuItem; private { Private declarations } procedure WMRButtonDown(var Msg : TWMMouse); message WM_RBUTTONDOWN; public { Public declarations } end;
91
Подчеркнут код, добавленный в декларацию объекта TForm1 вручную. Далее, в секции implementation нужно написать обработчик: procedure TForm1.WMRButtonDown(var Msg : TWMMouse); begin MessageDlg('Right mouse button click.', mtInformation, [mbOK], 0); end; В данном случае при нажатии правой кнопки мыши будет появляться диалог. Вообще-то, у класса TForm уже есть унаследованный от дальнего предка обработчик данного события, который называется точно также и вызывает то самое pop-up меню. Если в новом обработчике сообщения нужно выполнить действия, которые производились в старом, то для этого применяется ключевое слово inherited. Если слегка модифицировать наш обработчик, то после диалога будет появляться pop-up меню: procedure TForm1.WMRButtonDown(var Msg : TWMMouse); begin MessageDlg('Right mouse button click.', mtInformation, [mbOK], 0); inherited; end; Однако, есть еще способ обработки всех сообщений, которые получает приложение. Для этого используется свойство OnMessage объекта Application, который автоматически создается при запуске программы. Если определен обработчик события OnMessage, то он получает управление при любом событии, сообщение о котором направлено в программу. Следующий код будет приводить к появлению диалога при двойном щелчке мыши на любом объекте в приложении. procedure TForm1.FormCreate(Sender: TObject); begin Application.OnMessage:=AOM; end; procedure TForm1.AOM(var Msg: TMsg; var Handled: Boolean); begin Handled:=False; if Msg.Message = WM_LBUTTONDBLCLK then begin
92
MessageDlg('Double click.', mtInformation, [mbOK], 0); Handled:=True; end; end; Конечно, в обработчике нельзя выполнять операции, требующие длительного времени, поскольку это приведет к замедлению выполнения всего приложения.
93
7. РАБОТА С ПАЛИТРОЙ КОМПОНЕНТОВ Палитра компонентов Delphi предназначена для выбора компонентов или других элементов управления (таких, как ActiveX), размещаемых на форме. Палитра компонентов представляет собой многостраничное окно. Для отображения компонентов, расположенных на какой-либо странице, нужно щелкнуть на соответствующей закладке. Размещение компонента на форме представляет собой двухступенчатый процесс. Сначала выберите в палитре компонентов кнопку, соответствующую нужному компоненту. Затем щелкните на форме. Компонент появится на форме, причем его верхний левый угол будет там, где находился курсор мыши в момент щелчка. Компонент, размещенный только что на форме, имеет маленькие квадратики по периметру. Эти квадратики называются регуляторами размера компонента. Эти квадратики также показывают то, что компонент активен (когда в окне формы содержится много объектов, только один из них может быть активным, т.е. только один содержит регуляторы размера). Для изменения размера компонента достаточно нажать мышью на одном из квадратиков и перетаскивать его в нужном направлении. Для перемещения компонента по форме необходимо переместить курсор мыши на компонент, нажать левую кнопку и не отпуская ее, перемещать компонент в необходимое место формы. 7.1. Размещение нескольких копий компонента Для размещения нескольких копий компонента, не выбирая его каждый раз, необходимо при выборе компонента нажать и удерживать клавишу Shift. Затем отпустите клавишу. Кнопка компонента останется нажатой и будет при этом выделена синей рамкой. Щелкните на форме для размещения первого компонента. Кнопка в палитре компонентов по-прежнему нажата. При каждом следующем щелчке в форму будет помещаться новый экземпляр компонента. Для остановки этого процесса щелкните на кнопке указателя в палитре компонентов (кнопка со стрелкой). Продемонстрируем это: 1. Создайте новый проект (File | New Application). 2. Нажмите и удерживайте клавишу Shift на клавиатуре, затем щелкните на кнопке компонента Label в палитре компонентов. 3. Щелкните три раза на форме, перемещая каждый раз курсор мыши на новое место. Щелкните на кнопке со стрелкой в палитре компонентов, чтобы остановить копирование и вернуться в режим разработки формы.
94
5. Получили форму с тремя метками. 7.2. Размещение компонента в центре формы Delphi обеспечивает упрощенный способ размещения компонента в форме. Просто дважды щелкните на нужной кнопке в палитре компонентов, и компонента появиться в форме. При этом он будет центрирован по горизонтали и вертикали. Компоненты, размещенные таким образом, можно перемещать в любое место формы, как и компоненты, размещенные обычным путем. Теперь дадим краткий обзор стандартных и дополнительных компонент из Палитры Компонент Delphi (страницы Standard и Additional), страницы диалогов (страница Dialogs), системных компонент (страница System). 7.3. Стандартные компоненты Стандартные компоненты инкапсулируют наиболее распространенные средства управления Windows. К их числу относятся классы: TButton, ТЕdit, TListBox, TMemo, TMainMenu, TScrollBar, TPopupMenu, TCheckBox,TRadio-Button, TRadioGroup, TGroupBox, TPanel И TActionList. Большинство из перечисленных классов инкапсулирует какое-нибудь средство управления. Класс TMainMenu инкапсулирует главное меню приложения. Двойной щелчок на компоненте MainMenu во время проектирования приводит к активации конструктора меню. С помощью свойств MainMenu можно управлять такими аспектами поведения элементов меню, как, например, их блокировка. Кроме того, можно проверять, являются ли они «отмеченными», задавать для них идентификаторы контекстной справки, текст подсказки и т. д. С каждым элементом меню ассоциировано событие Onclick, что позволяет связать с каждым выбранным пунктом свой обработчик события. На первой странице Палитры Компонент размещены, обычно, 14 объектов (рис.7.1), определенно важных для использования. Мало кто обойдется длительное время без кнопок, списков, окон ввода и т.д. Все эти объекты такая же часть Windows, как мышь или окно. Набор и порядок компонент на каждой странице являются конфигурируемыми. Так, можно добавить к имеющимся компонентам новые, изменить их количество и порядок. Это можно сделать, вызвав всплывающее меню (нажать правую кнопку мыши, когда указатель над Палитрой).
95
Рисунок 7.1 − Компоненты, расположенные на первой странице палитры компонентов Стандартные компоненты Delphi перечислены ниже с некоторыми комментариями по их применению. Для более полного изучения данных компонент было бы полезно иметь под рукой компьютер с тем, чтобы посмотреть, как они работают и как ими манипулировать. Курсор - не компонент, просто пиктограмма для быстрой отмены выбора какого-либо объекта. TMainMenu позволяет поместить главное меню в программу. При помещении TMainMenu на форму это выглядит, как просто иконка. Иконки данного типа называют "невидимыми компонентом", поскольку они невидимы во время выполнения программы. Создание меню включает три шага: 1. Помещение TMainMenu на форму; 2. Вызов Дизайнера Меню через свойство Items в Инспекторе Объектов; 3. Определение пунктов меню в Дизайнере Меню. TPopupMenu позволяет создавать всплывающие меню. Этот тип меню появляется по щелчку правой кнопки мыши на объекте, к которому привязано данное меню. У всех видимых объектов имеется свойство PopupMenu, где и указывается нужное меню. Создается PopupMenu аналогично главному меню. TLabel служит для отображения текста на экране. Можно изменить шрифт и цвет метки, если дважды щелкнете на свойство Font в Инспекторе Объектов. А можно это же легко сделать и во время выполнения программы, написав всего одну строчку кода. TEdit - стандартный управляющий элемент Windows для ввода. Он может быть использован для отображения короткого фрагмента текста и позволяет пользователю вводить текст во время выполнения про-
96
граммы. TMemo - иная форма TEdit. Подразумевает работу с большими текстами. TMemo может переносить слова, сохранять в ClipBoard фрагменты текста и восстанавливать их, и другие основные функции редактора. TMemo имеет ограничения на объем текста в 32Кб, это составляет 10-20 страниц. TButton позволяет выполнить какие-либо действия при нажатии кнопки во время выполнения программы. В Delphi все делается очень просто. Поместив TButton на форму, по двойному щелчку можно создать заготовку обработчика события нажатия кнопки. Далее нужно заполнить заготовку кодом, например, таким: procedure TForm1.Button1Click(Sender: TObject); begin MessageDlg('Are you there?',mtConfirmation,mbYesNoCancel,0); end; TCheckBox отображает строку текста с маленьким окошком рядом. В окошке можно поставить отметку, которая означает, что что-то выбрано. Например, если посмотреть окно диалога настроек компилятора (пункт меню Options | Project, страница Compiler), то можно увидеть, что оно состоит преимущественно из CheckBox’ов. TRadioButton позволяет выбрать только одну опцию из нескольких. Если опять открыть диалог Options | Project и выбрать страницу Linker Options, то можно видеть, что секции Map file и Link buffer file состоят из наборов RadioButton. TListBox нужен для показа прокручиваемого списка. Классический пример ListBox’а в среде Windows - выбор файла из списка в пункте меню File | Open многих приложений. Названия файлов или директорий и находятся в ListBox’е. TComboBox во многом напоминает ListBox, за исключением того, что позволяет водить информацию в маленьком поле ввода сверху ListBox. Есть несколько типов ComboBox, но наиболее популярен спадающий вниз (drop-down combo box), который можно видеть внизу окна диалога выбора файла.
97
TScrollbar - полоса прокрутки, появляется автоматически в объектах редактирования, ListBox’ах при необходимости прокрутки текста для просмотра. TGroupBox используется для визуальных целей и для указания Windows, каков порядок перемещения по компонентам на форме (при нажатии клавиши TAB). TRadioGroup используется аналогично TGroupBox, для группировки объектов TRadioButton. TPanel - управляющий элемент, похожий на TGroupBox, используется в декоративных целях. Чтобы использовать TPanel, просто поместите его на форму и затем положите другие компоненты на него. Теперь при перемещении TPanel будут передвигаться и эти компоненты. TPanel используется также для создания линейки инструментов и окна статуса. Панель — это прямоугольная область на форме, рассматриваемая как единое целое вместе с размещенными на ней элементами управления. Компонент Panel является контейнером, который может содержать другие компоненты. Свойства панели управляют типом ее обрамления, внешним видом (панель может быть приподнятой, утопленной или плоской), а также шириной рамки. Различные сочетания перечисленных свойств позволяют создавать множество разновидностей трехмерных панелей. Это список объектов на первой странице Палитры Компонент. Если нужна дополнительная информация, то выберите на Палитре объект и нажмите клавишу F1 - появится Справочник с полным описанием данного объекта. 7.4. Страница Additional На странице Standard представлены управляющие элементы, появившиеся в Windows. На странице Additional (см. рисунок 7.2) размещены объекты, позволяющие создать более красивый пользовательский интерфейс программы.
Рисунок 7.2 − Компоненты, расположенные на странице Additional палитры компонентов
98
Список компонент: TBitBtn - кнопка вроде TButton, однако на ней можно разместить картинку (Glyph). TBitBtn имеет несколько предопределенных типов (bkClose, bkOK и др), при выборе которых кнопка принимает соответствующий вид. Кроме того, нажатие кнопки на модальном окне (Form2.ShowModal) приводит к закрытию окна с соответствующим модальным результатом (Form2.ModalResult). TSpeedButton - кнопка для создания панели быстрого доступа к командам (SpeedBar). Пример - SpeedBar слева от Палитры Компонент в среде Delphi. Обычно на данную кнопку помещается только картинка (Glyph). TTabSet - горизонтальные закладки. Обычно используется вместе с TNoteBook для создания многостраничных окон. Название страниц можно задать в свойстве Tabs. Но проще это сделать в программе при создании формы (OnCreate) : TabSet1.Tabs := Notebook1.Pages; А для того, чтобы при выборе закладки страницы перелистывались нужно в обработчике события OnClick для TTabSet написать: Notebook1.PageIndex := TabSet1.TabIndex; TNoteBook - используется для создания многостраничного диалога, на каждой странице располагается свой набор объектов. Используется совместно с TTabSet. TTabbedNotebook - многостраничный диалог со встроенными закладками, в данном случае - закладки сверху. TMaskEdit - аналог TEdit, но с возможностью форматированного ввода. Формат определяется в свойстве EditMask. В редакторе свойств для EditMask есть заготовки некоторых форматов: даты, валюты и т.п. Спец. символы для маски можно посмотреть в Справочнике. TOutline - используется для представления иерархических отношений связанных данных. Например - дерево директорий.
99
TStringGrid - служит для представления текстовых данных в виде таблицы. Доступ к каждому элементу таблицы происходит через свойство Cell. TDrawGrid - служит для представления данных любого типа в виде таблицы. Доступ к каждому элементу таблицы происходит через свойство CellRect. TImage - отображает графическое изображение на форме. Воспринимает форматы BMP, ICO, WMF. Если картинку подключить во время дизайна программы, то она прикомпилируется к EXE файлу. TShape - служит для отображения простейших графических объектов на форме: окружность, квадрат и т.п. TBevel - элемент для рельефного оформления интерфейса.
THeader - элемент оформления для создания заголовков с изменяемыми размерами для таблиц. TScrollBox - позволяет создать на форме прокручиваемую область с размерами большими, нежели экран. На этой области можно разместить свои объекты. 7.5. Страница Dialogs На странице Dialogs (рисунок 7.3) представлены компоненты для вызова стандартных диалогов Windows. Внешний вид диалогов зависит от используемой версии Windows. В Windows существуют стандартные диалоговые панели для таких распространенных операций, как, например, открытие и сохранение файлов, а также выбор цвета или шрифта. Каждая из них представлена в VCL одним из следующих классов: TOpenDialog, TSaveDialog, TOpenPictureDialog, TSavePictureDialog, TFontDialog, TColorDialog, TPrint-Dialog И TPrinterSetupDialog. Сюда же можно отнести классы TFindDialog и TReplaceDialog. Все компоненты этой группы являются невизуальными, поскольку у них отсутствует интерфейс времени проектирования. Разумеется, все они становятся видимыми во время выполнения программы.
100
Рисунок 7.3 – Компоненты, расположенные на странице Dialogs палитры компонентов Объекты, представленные на данной странице невидимы во время выполнения и вызов диалогов происходит программно, например: if OpenDialog1.Execute then Image1.Picture.LoadFromFile(OpenDialog1.FileName); Диалоги Windows в порядке появления на странице Dialogs: – – – – – – – –
OpenDialog SaveDialog FontDialog ColorDialog PrintDialog PrinterSetupDialog FindDialog ReplaceDialog
- выбрать файл - сохранить файл - настроить шрифт - выбор цвета - печать - настройка принтера - поиск строки - поиск с заменой
7.6. Страница System Страница представляет набор компонент для доступа к некоторым системным сервисам типа таймер, DDE, OLE и т.п.
Рисунок 7.4 – Компоненты, расположенные на странице System палитры компонентов TTimer - таймер, событие OnTimer периодически вызывается через промежуток времени, указанный в свойстве Interval. Период времени может составлять от 1 до 65535 мс. TPaintBox - место для рисования. В обработчики событий, связанных с мышкой передаются относительные координаты мышки в TPaintBox, а не абсолютные в форме.
101
TFileListBox - специализированный ListBox, в котором отображаются файлы из указанной директории (свойствово Directory). На названия файлов можно наложить маску, для этого служит свойство Mask. Кроме того, в свойстве FileEdit можно указать объект TEdit для редактирования маски. TDirectoryListBox - специализированный ListBox, в котором отображается структура директорий текущего диска. В св-ве FileList можно указать TFileListBox, который будет автоматически отслеживать переход в другую директорию. TDriveComboBox - специализированный ComboBox для выбора текущего диска. Имеет свойство DirList, в котором можно указать TDirectoryListBox, который будет отслеживать переход на другой диск. TFilterComboBox - специализированный ComboBox для выбора маски имени файлов. Список масок определяется в свойстве Filter. В свойстве FileList указывается TFileListBox, на который устанавливается маска. С помощью последних четырех компонент (TFileListBox, TDirectoryListBox, TDriveComboBox, TFilterComboBox) можно построить свой собственный диалог выбора файла, причем для этого не потребуется написать ни одной строчки кода. TMediaPlayer - служит для управления мультимедийными устройствами (типа CD-ROM, MIDI и т.п.). Выполнен в виде панели управления с кнопками Play, Stop, Record и др. Для воспроизведения может понадобиться как соответствующее оборудование, так и программное обеспечение. Подключение устройств и установка ПО производится в среде Windows. Например, для воспроизведения видео, записанного в формате AVI, в потребуется установить ПО MicroSoft Video (в Windows 3.0, 3.1, WFW 3.11). TOLEContainer - контейнер, содержащий OLE объекты. Поддерживается OLE 2.02.
102
TDDEClientConv,TDDEClientItem, TDDEServerConv, TDDEServerItem - 4 объекта для организации DDE. С помощью этих объектов можно построить приложение как DDE-сервер, так и DDEклиент. 7.7. Компоненты категории Win32 В VCL существуют классы компонентов, инкапсулирующие многие 32-разрядные нестандартные элементы управления Windows. Вот эти классы: TList-View, TTreeView, TTrackBar, TProgressBar, TTabControl, TPageControl, TRichE-dit, TImageList, TStatusBar, TAnimate, TDateTimePicker, TToolBar, TCoolBar И другие. Известная сложность, присущая самой природе некоторых из этих элементов управления, отражается и на представляющих их классах VCL. 7.8. Компоненты для работы с базами данных В VCL есть значительное число как визуальных, так и невизуальных компонентов, предназначенных для работы с базами данных. К числу последних относятся DataSource, Database, Table и Query. Все они инкапсулируют операции базы данных, происходящие «за кулисами». Классы визуальных компонентов баз данных видимы для пользователя и рассчитаны на непосредственное взаимодействие с ним. Например, компонент DBGrid используется для отображения таблиц в виде сеток. Таким образом, он представляет собой интерфейс между базой данных и пользователем, предназначенный для просмотра и редактирования хранящихся на диске таблиц. Компонент DBNavigator обеспечивает удобный кнопочный интерфейс для передвижения пользователей по таблицам баз данных. Он включает командные кнопки для перемещения на следующую, предыдущую, первую и последнюю записи и кнопки для принятия и отмены результатов редактирования. Еще одна группа привязанных к данным компонентов предназначена для сцепления стандартных средств управления Windows с полями баз данных. Среди них следует отметить классы TDBText, TDBEdit, TDBListBox, TDBImage И другие. Обычно ассоциируются с программированием баз данных и компоненты, расположенные на странице QReport палитры компонентов. Они значительно облегчают написание отчетов, особенно в тех случаях, когда источником информации являются таблицы баз данных.
103
7.9. Компоненты категории Win 3.1 Было бы непоправимой ошибкой пренебрегать этой группой компонентов только из-за названия страницы, на которой они расположены. Здесь можно обнаружить настоящие шедевры, например, TabSet и Notebook. Здесь же находится несколько классов компонентов, с помощью которых можно создавать нестандартные диалоги открытия и сохранения файлов: TFileListBox, TDirectoryListBox, TDriveComboBox И TFilterComboBox. 7.10. Компоненты категории Internet В зависимости от версии Delphi (Standard, Professional или Client/Server) в окне палитры компонентов вы можете обнаружить закладку Internet, которая содержит компоненты, предназначенные для программирования в Internet — HTML, FTP, SMTP, POPS и HTTP. Здесь же находятся компоненты, предназначенные для общего сетевого программирования посредством интерфейса Winsock API. Большинство из них — оригинальные компоненты VCL, хотя по крайней мере один, THTML, является элементом управления ActiveX. 7.11. Компоненты категории Sample Эта страница содержит несколько примеров компонентов VCL. Каждый из них поставляется с исходными файлами, что позволяет изучить все тонкости их работы. Вот эти компоненты: Gauge, ColorButton, SpinButton, SpinEdit, Directory-Outline и Calendar. 7.12. Компоненты категории ActiveX Страница ActiveX палитры компонентов содержит элементы управления ActiveX, которые можно использовать в своих приложениях. Эти средства включают Chart FX компании Software FX, Inc., элемент управления Graph компании Bits Per Second, Ltd., а также программные продукты Visual Components, Inc., — Visual Speller, Formula One Spreadsheet и Formula One VtChart.
104
8. ГРАФИЧЕСКИЕ КОМПОНЕНТЫ В данном разделе рассказывается о том, какие возможности есть в Delphi для создания приложений, использующих графику; как использовать компоненты для отображения картинок; какие средства есть в Delphi для оформления программы. Кроме того, приводится описание важного свойства Canvas, которое предоставляет доступ к графическому образу объекта на экране. В стандартную библиотеку визуальных компонент Delphi входит несколько объектов, с помощью которых можно придать своей программе совершенно оригинальный вид. Это - TImage (TDBImage), TShape, TBevel. TImage позволяет поместить графическое изображение в любое место на форме. Этот объект очень прост в использовании - выберите его на странице Additional и поместите в нужное место формы. Собственно картинку можно загрузить во время дизайна в редакторе свойства Picture (Инспектор Объектов). Картинка должна храниться в файле в формате BMP (bitmap), WMF (Windows Meta File) или ICO (icon). (TDBImage отображает картинку, хранящуюся в таблице в поле типа BLOB. При этом доступен только формат BMP.) Как известно, форматов хранения изображений гораздо больше трех вышеназванных (например, наиболее известны PCX, GIF, TIFF, JPEG). Для включения в программу изображений в этих форматах нужно либо перевести их в формат BMP, либо найти библиотеки третьих фирм, в которых есть аналог TImage, “понимающий” данные форматы. При проектировании следует помнить, что изображение, помещенное на форму во время дизайна, включается в файл .DPR и затем прикомпилируется к EXE файлу. Поэтому такой EXE файл может получиться достаточно большой. Как альтернативу можно рассмотреть загрузку картинки во время выполнения программы, для этого у свойства Picture (которое является объектом со своим набором свойств и методов) есть специальный метод LoadFromFile. Это делается, например, так: if OpenDialog1.Execute then Image1.Picture.LoadFromFile(OpenDialog1.FileName); Важными являются свойства объекта Center и Stretch - оба имеют булевский тип. Если Center установлено в True, то центр изображения будет совмещаться с центром объекта TImage. Если Stretch установлено в True, то изображение будет сжиматься или растягиваться таким образом, чтобы заполнить весь объект TImage. TShape - простейшие графические объекты на форме типа круг, квадрат и т.п. Вид объекта указывается в свойстве Shape. Свойство Pen опреде-
105
ляет цвет и вид границы объекта. Brush задает цвет и вид заполнения объекта. Эти свойства можно менять как во время дизайна, так и во время выполнения программы. TBevel - объект для украшения программы, может принимать вид рамки или линии. Объект предоставляет меньше возможностей по сравнению с TPanel, но не занимает ресурсов. Внешний вид указывается с помощью свойств Shape и Style. Свойство объектов Canvas У ряда объектов из библиотеки визуальных компонент есть свойство Canvas (канва), которое предоставляет простой путь для рисования на них. Эти объекты - TBitmap, TComboBox, TDBComboBox, TDBGrid, TDBListBox, TDirectoryListBox, TDrawGrid, TFileListBox, TForm, TImage, TListBox, TOutline, TPaintBox, TPrinter, TStringGrid. Canvas является в свою очередь объектом, объединяющим в себе поле для рисования, карандаш (Pen), кисть (Brush) и шрифт (Font). Canvas обладает также рядом графических методов : Draw, TextOut, Arc, Rectangle и др. Используя Canvas, Вы можете воспроизводить на форме любые графические объекты - картинки, многоугольники, текст и т.п. без использования компонент TImage,TShape и TLabel (т.е. без использования дополнительных ресурсов), однако при этом Вы должны обрабатывать событие OnPaint того объекта, на канве которого Вы рисуете. Рассмотрим подробнее свойства и методы объекта Canvas. Свойства Canvas : Brush - кисть, является объектом со своим набором свойств: Bitmap - картинка размером строго 8x8, используется для заполнения (заливки) области на экране. Color - цвет заливки. Style - предопределенный стиль заливки; это свойство конкурирует со свойством Bitmap - какое свойство Вы определили последним, то и будет определять вид заливки. Handle - данное свойство дает возможность использовать кисть в прямых вызовах процедур Windows API . ClipRect - (только чтение) прямоугольник, на котором происходит графический вывод. CopyMode - свойство определяет, каким образом будет происходить копирование (метод CopyRect) на данную канву изображения из другого места: один к одному, с инверсией изображения и др.
106
Font - шрифт, которым выводится текст (метод TextOut). Handle - данное свойство используется для прямых вызовов Windows API. Pen - карандаш, определяет вид линий; как и кисть (Brush) является объектом с набором свойств: Color - цвет линии Handle - для прямых вызовов Windows API Mode - режим вывода: простая линия, с инвертированием, с выполнением исключающего или и др. Style - стиль вывода: линия, пунктир и др. Width - ширина линии в точках PenPos - текущая позиция карандаша, карандаш рекомендуется перемещать с помощью метода MoveTo, а не прямой установкой данного свойства. Pixels - двухмерный массив элементов изображения (pixel), с его помощью Вы получаете доступ к каждой отдельной точке изображения (см. пример к данному уроку). Методы Canvas: Методы для рисования простейшей графики - Arc, Chord, LineTo, Pie, Polygon, PolyLine, Rectangle, RoundRect. При прорисовке линий в этих методах используются карандаш (Pen) канвы, а для заполнения внутренних областей - кисть (Brush). Методы для вывода картинок на канву - Draw и StretchDraw, В качестве параметров указываются прямоугольник и графический объект для вывода (это может быть TBitmap, TIcon или TMetafile). StretchDraw отличается тем, что растягивает или сжимает картинку так, чтобы она заполнила весь указанный прямоугольник (см. пример к данному уроку). Методы для вывода текста - TextOut и TextRect. При выводе текста используется шрифт (Font) канвы. При использовании TextRect текст выводится только внутри указанного прямоугольника. Длину и высоту текста можно узнать с помощью функций TextWidth и TextHeight. Объект TPaintBox На странице System Палитры Компонент есть объект TPaintBox, который можно использовать для построения приложений типа графического редактора или, например, в качестве места построения графиков (если, ко-
107
нечно, у Вас нет для этого специальных компонент третьих фирм). Никаких ключевых свойств, кроме Canvas, TPaintBox не имеет, собственно, этот объект является просто канвой для рисования. Важно, что координаты указателя мыши, передаваемые в обработчики соответствующих событий (OnMouseMove и др.), являются относительными, т.е. это смещение мыши относительно левого верхнего угла объекта TPaintBox, а не относительно левого верхнего угла формы. Примеры Пример 1 В первом примере (проект SHAPE.DPR, рис.8.1) показано, как во время выполнения программы можно изменять свойства объекта TShape. Изменение цвета объекта (событие OnChange для ColorGrid1): procedure TForm1.ColorGrid1Change(Sender: TObject); begin Shape1.Brush.Color:=ColorGrid1.ForeGroundColor; end;
Рисунок 8.1 – Результат работы программы TShapeDemo Для создания данной программы необходимо произвести следующие действия:
108
1. Запустить новое приложение (File | New Application). На созданной форме расположите в соответствии с рисунком 8.1 следующие компоненты: в правом верхнем углу - RadioGroup (свойство Caption изменить на Shape) и 6 RadioButton (у каждой кнопки сменить свойство Caption соответственно на Circle, Ellipse и т.д.) – страница Standart палитры компонентов; − в левом верхнем углу - Shape – страница Additional; − внизу формы – ColorGrid – страница Samples. При двойном нажатии на каждой RadioButton в код программы внести соответствующие изменения: procedure TForm1.RadioButton1Click(Sender: TObject); begin Shape1.Shape:=stCircle; end; 2. В код программы добавьте следующие строчки: procedure TForm1.ColorGrid1Change(Sender: TObject); begin Shape1.Brush.Color:=ColorGrid1.ForeGroundColor; end; Общий вид получившейся программы должен быть таким: unit primer_draw; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, ExtCtrls, ColorGrd; type TForm1 = class(TForm) ColorGrid1: TColorGrid; Shape1: TShape; RadioGroup1: TRadioGroup; RadioButton1: TRadioButton; RadioButton2: TRadioButton; RadioButton3: TRadioButton; RadioButton4: TRadioButton; RadioButton5: TRadioButton; RadioButton6: TRadioButton;
109
procedure ColorGrid1Change(Sender: TObject); procedure RadioButton1Click(Sender: TObject); procedure RadioButton2Click(Sender: TObject); procedure RadioButton3Click(Sender: TObject); procedure RadioButton4Click(Sender: TObject); procedure RadioButton5Click(Sender: TObject); procedure RadioButton6Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.ColorGrid1Change(Sender: TObject); begin Shape1.Brush.Color:=ColorGrid1.ForegroundColor; end; procedure TForm1.RadioButton1Click(Sender: TObject); begin Shape1.Shape:=stCircle; end; procedure TForm1.RadioButton2Click(Sender: TObject); begin Shape1.Shape:=stEllipse; end; procedure TForm1.RadioButton3Click(Sender: TObject); begin Shape1.Shape:=stRectangle; end; procedure TForm1.RadioButton4Click(Sender: TObject); begin
110
Shape1.Shape:=stRoundRect; end; procedure TForm1.RadioButton5Click(Sender: TObject); begin Shape1.Shape:=stRoundSquare; end; procedure TForm1.RadioButton6Click(Sender: TObject); begin Shape1.Shape:=stSquare; end; end. 5. Нажмите F9, чтобы запустить программу. Исправьте все ошибки, если они есть. Если их нет, то появившееся окно должно иметь вид, представленное на рисунке 8.1. Выбирая любую из RadioButton слева должна появляться соответствующая фигура, а выбирая цвет внизу экрана, она (фигура) должна менять свой цвет. Пример 2 Во втором примере (проект PIXELS.DPR, рис.8.2) показано, как осуществить доступ к отдельной точке на изображении (на канве). По нажатию кнопки “Fill” всем точкам изображения присваивается свой цвет: procedure TForm1.Button1Click(Sender: TObject); var i, j : Longint; begin Button1.Enabled:=False; with Canvas do for i:=1 to Width do begin Application.ProcessMessages; for j:=1 to Height do Pixels[i,j]:=i*j; end; Button1.Enabled:=True; end;
111
Рисунок 8.2 - Работа с точками на канве Пример 3 В третьей программе (проект DRAW.DPR, рис.8.3) приведен пример использования методов, выводящих изображение - Draw и StretchDraw. 1. Разместите на форме элемент Image (страница Additional). 2. В инспекторе объектов необходимо нажать на свойстве Picture и загрузить любой рисунок из предлагаемых (Load). Прорисовка изображений происходит в обработчике события OnPaint для формы: procedure TForm1.FormPaint(Sender: TObject); begin with Canvas do begin Draw(0,0, Image1.Picture.BitMap); StretchDraw(Rect(250,0,350,50),Image1.Picture.BitMap) end; end;
Рисунок 8.3 - Вывод изображений на канву
112
8.1. Классы GDI Классы, представляющие интерфейс графических устройств (GDI), выполняют немалый объем работы в GUI-приложениях Windows. Они инкапсулируют такие объекты, как битовые матрицы, шрифты, контексты устройств (DC), кисти и перья. Именно благодаря им становится возможным отображение в окнах графики и текста. GDI-классы не связаны с каким-то конкретным компонентом, однако многие компоненты содержат в качестве свойств экземпляры этих классов. Например, у поля редактирования есть свойство Font (шрифт), которое является объектом типа TFont. Программистам, знакомым с традиционным программированием в Windows, хорошо известно такое понятие, как контекст устройства. Тем не менее, в VCL вы нет широкого применения этого термина. Это связано с тем, что VCL инкапсулирует контексты устройств Windows в классе TCanvas. В VCL для ссылки на традиционный контекст устройства используется термин канва. Канва предлагает программисту область, в которой он может рисовать с помощью таких методов, как, например, MoveTo, LineTo или TextOut. Там же посредством методов Draw и StretchDraw могут отображаться битовые матрицы. Согласитесь, что концепция канвы, на которой можно рисовать, представляется более осмысленной, чем архаичный термин контекст устройства. А теперь приведем список наиболее часто используемых классов GDI: 1) Класс TCanvas содержит объекты других GDI-классов. Например, при выполнении последовательности операций MoveTo /LineTo цвет проводимой линии определяется цветом текущего пера канвы, заданного с помощью свойства Pen (объекта типа треп). Свойства класса ТРеп определяют также тип проводимой линии: ее толщину, стиль (сплошная, штриховая, пунктирная и т. д.) и режим рисования. 2) Класс TBrush представляет кисть, с помощью которой происходит закрашивание объектов канвы: прямоугольников (метод FillRect), многоугольников (Polygon) и эллипсов (Ellipse). Свойствами TBrush являются Color, Style и Bitmap. Свойство Style служит для задания стиля закрашивания объектов, а свойство Bitmap позволяет определить битовую матрицу - шаблон заполнения. 3) Класс TBitmap инкапсулирует растровые операции VCL. Его свойствами являются Palette, Height, Width и TransparentColor, а методами — LoadFromFile, LoadFromResourceID И SaveToFile. Кроме TCanvas, TBitmap используют другие классы компонентов, такие как, например, Timage, TBitBtn и TSpeedButton. Объект класса TBitmap может служить в качестве внеэкранной битовой матрицы. Битовые матрицы этого типа обычно используются в приложениях с интенсивным применением графики. Они по-
113
зволяют реализовать методики, уменьшающие мерцание экрана и существенно повышающие эффективность работы таких приложений. 4) Класс TFont отвечает за операции со шрифтами. Его свойства — Color, Height и Style (стиль может быть полужирным, курсивным, нормальным и т. д.). Этот класс используется всеми классами компонентов, в которых происходит отображение текста. В дополнение к перечисленным, существуют и другие классы GDI, которые или выполняют вспомогательные задачи или, будучи расширениями базового класса, придают ему дополнительные функциональные возможности. 9. ФОРМЫ Формы являются основными составляющими приложений Delphi. Каждое GUI-приложение имеет, по крайней мере, одну форму, которая служит главным окном. Форма главного окна может быть просто пустым окном, может содержать элементы управления или растровое изображение. В типичной программе Windows главное окно обычно имеет меню. Окно может также содержать дополнительные элементы, например, панель инструментов или строку состояния. При создании главного окна приложения существует большое разнообразие вариантов. Каждое приложение уникально и предъявляет свои требования. 9.1. Формы для диалоговых панелей Формы применяются и в качестве диалоговых панелей. На самом деле, для пользователя нет разницы между формой, функционирующей как диалоговая панель, и настоящей панелью диалога. Диалоговые панели обычно имеют следующие характерные черты, отличающие их от обычных окон: 1) Фиксированный размер. Диалоговые панели предназначены, как правило, для выполнения конкретной задачи, и изменение размера либо не имеет смысла, либо нежелательно. 2) Почти всегда имеется кнопка ОК. Некоторые диалоговые панели имеют кнопку Close, выполняющую ту же функцию. Для простых диалоговых панелей, вроде About, эта кнопка чаще всего единственная. 3) Диалоговые панели могут также иметь кнопки Cancel и Help. 4) Как правило, в линейке заголовка есть только кнопка закрытия окна. Кнопки для свертывания и развертывания обычно отсутствуют. 5) Некоторые диалоговые панели являются многостраничными диалогами с закладками. Диалог может содержать несколько страниц, снабженных закладками (ярлычками), позволяющих пользователю выбрать нужную страницу. При щелчке на закладке открывается соответствующая
114
страница диалоговой панели. 6) Использование, клавиши Tab для перемещения между элементами управления диалоговой панели. Разумеется, из каждого правила есть свои исключения. Большинство диалоговых панелей имеет стандартные свойства, но некоторые могут выполнять специфические задачи и в том или ином отношении отклоняться от стандарта. В Delphi диалоговая панель является просто разновидностью формы. Программист создает ее так же, как и форму главного окна или любую другую. Чтобы зафиксировать размер диалоговой панели, вы можете изменить значение свойства BorderStyle на bsDialog или bsSingle. При использовании bsDialog панель будет содержать в строке заголовка только кнопку закрытия, как это традиционно принято для диалогов. Кроме этого, не нужно предпринимать никаких специальных действий для того, чтобы форма вела себя как диалоговая панель. Все формы Delphi имеют встроенную поддержку управления клавишей Tab. Можно установить порядок переключения с помощью свойств TabOrder отдельных элементов управления, расположенных на форме диалога. Модальная диалоговая панель должна быть закрыта перед продолжением работы с приложением. При вызове такого диалога главное окно приложения блокируется. Большинство диалогов являются модальными. Немодальная диалоговая панель позволяет пользователю продолжать работу с приложением. В качестве примера можно привести диалог Find в некоторых текстовых процессорах. Диалоговая панель Delphi (как, впрочем, и любая форма) может быть модальной или немодальной. Для отображения модальной панели нужно вызвать метод showModal класса TForm. Чтобы создать немодальную диалоговую панель, используется метод Show. 10. МОДЕЛЬ МНОГОДОКУМЕНТНОГО ИНТЕРФЕЙСА До сих пор мы создавали только приложения с однодокументным интерфейсом (SDI — single-document interface). SDI-приложения имеют одно главное окно и могут при необходимости отображать диалоговые панели, но не имеют других вторичных окон. Некоторые программы следуют модели многодокументного интерфейса (MDI — multiple-document interface). MD1-приложения состоят из родительского главного окна (MDI-родителя) и дочерних окон (MDI-потомков). Примером программ, использующих модель MDI, служат редактор системной конфигурации Windows (SYSEDIT). Одним из наиболее очевидных свойств модели MDI является то, что дочерние окна ограничены главным окном и могут перемещаться только в пределах этого окна. MDI-
115
приложения практически всегда имеют в главном меню пункт Window. Этот пункт обычно содержит подпункты Cascade и Tile, позволяющие расположить дочерние окна каскадом или мозаикой. При сворачивании дочернего окна его значок остается в пределах главного окна. Если же сворачивается обычное (не MDI) дочернее окно, значок размещается на рабочем столе Windows. СПИСОК ЛИТЕРАТУРЫ 1. Кент Рейсдорф. Delphi 4. Освой самостоятельно: Пер. с англ. – М.:ЗАО «Издательство БИНОМ», Лаборатория Базовых Знаний, 1999. – 752 с. 2. Когсвелл Д. Изучи сам программирование баз данных в Delphi 2.0 сегодня/ Пер. с англ. – Мн.: ООО «Попурри», 1997. – 448 с. 3. www.delphi32.com 4. www.dev-soft.com 5. www.teemach.com 6. www.delphideli.com 7. www.icm.edu.pl/delphi
116
МЕТОДИЧЕСКИЕ УКАЗАНИЯ ПО ВЫПОЛНЕНИЮ КОНТРОЛЬНЫХ РАБОТ Каждая контрольная работа содержит 3 задания, в которых необходимо составить программы. Если в задании имеется таблица вариантов, то необходимо рассчитать номер варианта по формуле V = (N * n) div 100, где V – вариант; N − общее количество вариантов в контрольной работе; n – две последние цифры пароля. Если вариантов нет, то выполняется одна эта задача во всех вариантах. КОНТРОЛЬНАЯ РАБОТА № 1 Задание 1 Ответьте на следующие вопросы: 1) Каково назначение списка uses модуля? 2) Зачем нужны закрытые поля и методы? 3) Когда вызывается деструктор класса? 4) Каким образом можно разрешить пользователям читать и изменять значения закрытых полей? 5) Для чего применяется ключевое слово as? Задание 2 Составить программу, в которой необходимо ввести двумерный массив и произвести с ним следующие действия: Вариант 1 2 3 4 5
Задание Найти минимальный и максимальный элементы. Поменять строку, в которой находится максимальный элемент, со столбцом, в котором найден минимальный элемент. Найти суммы главной и побочной диагоналей массива. Найти суммы строк массива и переставить строки массива так, чтобы значения полученных сумм располагались по возрастанию. Найти суммы столбцов массива и переставить столбцы массива так, чтобы значения полученных сумм располагались по возрастанию. Найти суммы строк массива и переставить строки массива так, чтобы значения полученных сумм располагались по убыванию.
117
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Найти суммы столбцов массива и переставить столбцы массива так, чтобы значения полученных сумм располагались по убыванию. Поменять местами строки и столбцы массива так, чтобы максимальный элемент находился в левом верхнем углу. Поменять местами строки и столбцы массива так, чтобы минимальный элемент находился в правом верхнем углу. Поменять местами строки и столбцы массива так, чтобы максимальный элемент находился в правом верхнем углу. Поменять местами строки и столбцы массива так, чтобы минимальный элемент находился в левом верхнем углу. Поменять местами строки и столбцы массива так, чтобы максимальный элемент находился в правом нижнем углу. Поменять местами строки и столбцы массива так, чтобы минимальный элемент находился в правом нижнем углу. Поменять местами строки и столбцы массива так, чтобы максимальный элемент находился в левом нижнем углу. Поменять местами строки и столбцы массива так, чтобы минимальный элемент находился в левом нижнем углу. Найти суммы столбцов массива и переставить столбцы массива так, чтобы значения полученных сумм располагались по возрастанию. Найти суммы столбцов массива и переставить столбцы массива так, чтобы значения полученных сумм располагались по убыванию. Найти минимальный и максимальный элементы. Поменять строку, в которой находится максимальный элемент, со столбцом, в котором найден минимальный элемент. Поменять местами строки и столбцы массива так, чтобы максимальный элемент находился в правом верхнем углу. Найти суммы строк массива и переставить строки массива так, чтобы значения полученных сумм располагались по убыванию. Найти суммы и количество положительных и отрицательных элементов массива.
Задание 3 Создать проект, содержащий 3 кнопки со следующими функциями: 1-я кнопка a) прячет / показывает 2-ю; b) блокирует / включает 2-ю; c) переключает видимость 2-й и 3-й (видна только одна из кнопок по
118
1 2 3 4 5 6 7 8 9 10
а b c d e a b c d e
а b c d e f b b c c
а b c d e b c d e a
11 12 13 14 15 16 17 18 19 20
a b c d e a b c d e
3 кнопка
2 кнопка
1 кнопка
Вариант
3 кнопка
2 кнопка
1 кнопка
Вариант
очереди); d) переключает блокировку 2-й и 3-й (активна только одна из кнопок по очереди); e) меняет подсказки 2-й и 3-й. 2-я кнопка (учесть реальные ограничения, связанные с размером формы) a) сдвигает первую на 10 пикселей вверх; b) сдвигает первую на 10 пикселей вниз; c) сдвигает первую на 10 пикселей вправо; d) сдвигает первую на 10 пикселей влево; e) сжимает форму на 5 пикселей со всех сторон; f) раздвигает форму на 5 пикселей во все стороны. 3-я кнопка a) вкл./выкл. системную кнопку; b) вкл./выкл. кнопку “развернуть”; c) вкл./выкл. кнопку “свернуть”; d) перебирает тип курсора; e) перебирает тип рамки. Щелчок на форме и
восстанавливают начальное состояние. Двойной щелчок и - закрывают форму. Для каждой кнопки определить подсказку (Hint).
e f a b c d e f a b
c d e a b d e a b c
Задание 4. Буксировка экранных объектов. Буксировка экранных объектов позволяет эффективно переносить информацию между отдельными компонентами с помощью мыши. Разберем механизм буксировки на примере переноса строк из одного списка в другой.
119
Рис. 1 Создайте новый проект, содержащий два компонента ListBox1 и ListBox2 с надписями сверху. Заполните первый список названиями дней недели (см. рис.1.). Следующие шаги содержат примеры обработки событий четырех этапов буксировки. Этап 1. Начало буксировки. Возникает при перемещении мыши с нажатой левой кнопкой. Установите свойство DragMode компонента ListBox1 в dmAutomatic, т.е. компонент берет на себя обработку этого этапа. Если установить значение этого свойства в dmManual, то программисту придется вручную обнаруживать начало буксировки (например, по событию MouseDown) и запускать ее механизм (метод BeginDrag). В момент начала буксировки компонент генерирует событие OnStartDrag. Запишите в обработчик этого события строку Color:=clRed; Этап 2. Буксировка объекта над компонентами. Во время буксировки при перемещении курсора мыши над компонентами они генерируют событие OnDragOver. Выделим следующие параметры обработчика этого события: Source - объект — источник буксировки, X,Y - координаты курсора, Accept - булевская переменная, определяющая, принимает или нет данный компонент буксируемую информацию. Запишите в этот обработчик для компонента ListBox2 строки, разрешающую буксировку только из ListBox1: Accept := Source=ListBox1; Этап 3. Оставление информации на целевом компоненте. Во время буксировки при отпускании кнопки мыши над целевым компонентом он генерируют событие OnDragDrop. В обработчике именно этого события должно быть произведено то действие, которому соответствует буксировка. В нашем случае это перенос строки из левого списка в правый. Запишите в обработчик события ListBox2.OnDragDrop оператор, добавляющий в список строк ListBox2 выделенную строку из ListBox1: with ListBox1 do ListBox2.Items.Add(Items[ItemIndex]); Этап 4. Завершение буксировки. Компонент - источник буксировки в момент отпускания генерирует событие OnEndDrag, параметр
120
Target которого соответствует другому, целевому компоненту (в нашем случае это ListBox2). В обработчике этого события запишите оператор, удаляющий из первого списка выбранную строку и оператор, изменяющий цвет формы: if Target = ListBox2 then with ListBox1 do Items.Delete(ItemIndex); Color := clYellow; Выполните компиляцию и проверьте работу механизма буксировки. ЗАДАНИЕ НА САМОСТОЯТЕЛЬНУЮ РАБОТУ Создайте новый проект (или измените предыдущий), содержащий 2 списка ListBox, компонент Edit, компонент Panel, две кнопки и четыре надписи (см рис.2). Данный проект должен обеспечивать работу следующих функций:
Рис. 2 • копирование строк из Edit в оба списка; • перенос строк из первого списка во второй и обратно; • удаление строк методом их буксировки на черную панель; • чтение строк из файла в первый список; • сохранение в файле строк второго списка. Рекомендация: Предусмотреть обработку событий: ListBox1: OnDragDrop, OnDragOver, OnEndDrag; ListBox2: OnDragDrop, OnDragOver, OnEndDrag; Panel1: OnDragOver. Обработку начала буксировки из Edit1 выполнить вручную, т.е. уста-
121
новить Edit1.DragMode=dmManual и в обработчик Edit1.OnMouseDown записать: if (Button=mbLeft) and (Edit1.Text<>'') Edit1.BeginDrag(False);
then
КОНТРОЛЬНАЯ РАБОТА № 2 Задание 1. Ответьте на следующие вопросы: 1) Как можно удалить кнопки из панели инструментов? 2) Перечислите типы файлов, необходимых для построения приложения в Delphi? 3) Какой метод VCL используется для немодального отображения формы, а какой для модального? 4) Все ли компоненты видимы во время проектирования? Назовите хотя бы один невизуальный компонент VCL. 5) Могут ли несколько компонентов совместно использовать один и тот же обработчик событий? Задание 2. Редактор текстов. Шаг 1. Назвать форму TextEditor и сохранить проект под именем Editor.dpr. Шаг 2. Установить на форму компонент Panel1 и задайте его свойства Align = alTop, Caption = ‘’. Шаг 3. Установить на форму компонент Memo1 и задайте его свойства Align = alClient, Lines = ‘’, WordWrap = TRUE, ScrolBars = ssBoth, HideSelection = False (при переходе фокуса к другому компоненту не гасится выделение выделенного текста). Шаг 4. Установить на форму компонент OpenDialog1 со следующими свойствами DefaultExt = TXT, Filter = Текстовые файлы | *.txt | Все файлы | *.* Title = Открытие текстового файла, Options (установить в TRUE) ofHideReadOnly - из окна диалога удаляется выключатель «Открыть только для чтения» ofFileMustExist - генерируется сообщение об ошибке, ес-
122
ли выбран несуществующий файл, ofNoReadOnlyReturn - генерируется сообщение об ошибке, если выбран файл типа ReadOnly. Шаг 5. Установить на форму компонент SaveDialog1 со следующими свойствами DefaultExt = TXT, Filter = Текстовые файлы | *.txt | Все файлы | *.* Title = Сохранение текстового файла, Options (установить в TRUE) ofHideReadOnly ofNoReadOnlyReturn ofOverwritePrompt - . генерируется сообщение об ошибке, если выбран уже существующий файл. Шаг 6. Установить на панель кнопку с именем &Open и записать следующий обработчик события OnClick: With OpenDialog1 do if Execute then begin Memo1.Lines.LoadFromFile(FileName); Caption:='TextEditor '+ExtractFileName(FileName); SaveDialog1.FileName:=FileName; FileName:=''; end; Шаг 7. Установить на панель кнопку с именем &Save и с обработкой события OnClick: Memo1.Lines.SaveToFile(SaveDialog1.FileName); Шаг 8. Установить на панель кнопку с именем Save&As и с обработкой события OnClick: With SaveDialog1 do if Execute then begin Memo1.Lines.SaveToFile(FileName); Caption:='TextEditor '+ExtractFileName(FileName); end; Шаг 9. Установить на панель кнопку с именем E&xit и с обработкой события OnClick: Close Шаг 10. Поместить на форму компонент FontDialog1 с обработкой события OnApply: Memo1.Font:=FontDialog1.Font Шаг 11. Разместить на панели кнопку с именем &Font и с обработкой события OnClick: With FontDialog1 do if Execute then Memo1.Font:=Font Шаг 12. Установить на форму компонент PrinterSetupDialog1. Шаг 13. Установить на панель кнопку с именем Print Set&up и с обработкой события OnClick:
123
PrinterSetupDialog1.Execute Шаг 14. Установить на форму компонент PrintDialog1 Шаг 15. Установить на панель кнопку с именем &Print и с обработкой события OnClick: var FileOut : TextFile; k : Integer; begin if PrintDialog1.Execute then begin AssignPrn(FileOut); Rewrite(FileOut); Printer.Canvas.Font:=Memo1.Font; for k:=0 to Memo1.Lines.Count-1 do Writeln(FileOut,Memo1.Lines[k]); CloseFile(FileOut) end; end; Процедуры работы с принтером находятся в модуле Printers. Укажите его имя в списке uses раздела interface модуля Unit1. Шаг 16. Поместить на панель кнопку с именем Fi&nd и с обработкой события OnClick: FindDialog1.Execute Шаг 17. Поместить на панель кнопку с именем &Replace и с обработкой события OnClick: ReplaceDialog1.Execute Шаг 18. Поместить на форму компоненты FindDialog1 и ReplaceDialog1 и установить в TRUE следующие общие свойства группы Options frHideMachCase - прячет выключатель «С учетом регистра», frHideWholeWord - прячет выключатель «Только слово целиком». frHideUpDown - прячет кнопки выбора направления поиска. Шаг 19. Определить обработчик события OnFind компонента FindDialog1; Текст обработчика var Buff,FT,P : PChar; BuffLen : Integer; Begin With Sender as TfindDialog do begin
Комментарий Описание вспомогательных переменных. В начале процедуры указано, что используемые ниже свойства и методы будут относиться к объекту Sender (источник события), который рассматривается как объект класса TFindDialog.
124
Текст из Memo1 копируется в динамическую переменную Buff. Для этого в BuffLen заносится длина текста, под переменную Buff отводится память и выполняется копирование. GetMem(FT,Length(FindText)+1); Аналогично в динамическую пеStrPCopy(FT,FindText); ременную FT копируется содержимое строки поиска. В переменную P заносится адрес P:=Buff+Memo1.SelStart+Memo1.SelLength; начала поиска. P:=StrPos(P,FT); В переменную P заносится адрес подстроки, совпавшей с образцом для поиска. If P=Nil Если адрес нулевой (образец не then MessageBeep(0) найден), то генерируется звуковой сигнал. Иначе, если образец найден, он Else begin Memo1.SelStart:=P-Buff; выделяется в Memo1. BuffLen:=Memo1.GetTextLen+1; GetMem(Buff,BuffLen); Memo1.GetTextBuf(Buff,BuffLen);
Memo1.SelLength:=Length(FindText) end; В завершении процедуры разруFreeMem(FT); шаются динамические переменFreeMem(Buff,BuffLen); ные. end; end; Шаг 20. Для компонента ReplaceDialog1 в качестве обработчика события OnFind указать (в окне Object Inspector) обработчик этого же события компонента FindDialog1 (вот для чего в этой процедуре строка поиска FindText берется не у конкретного компонента FindDialog1 или ReplaceDialog1, а у источника события Sender). Шаг 21. Для компонента ReplaceDialog1 ввести текст обработчика события OnReplace. Текст обработчика With ReplaceDialog1 do Repeat if Memo1.SelText<>FindText then FindDialog1Find(Sender);
Комментарий Организован цикл. Если выделенный фрагмент не совпадает с образцом поиска, то вызовом обработчика события OnFind компонента FindDialog1 найти следующий совпадающий фрагмент.
125
if Memo1.SelLength=0 then Break; Memo1.SelText:=ReplaceText; until not (frReplaceAll in Options);
Если фрагмент не найден, то завершить цикл. Заменить выделенный фрагмент образцом для замены. Если не выбран режим «Заменить все», то остановить цикл.
Самостоятельно: 1. На панель добавить кнопку, позволяющую Вариант 1,5,9,13,17 2,6,10,14,18 3,7,11,15,19 4,8,12,16,20
Задание Отсортировать текст по числу слов в строке Переставить строки в порядке возрастания количества буквы О в строке Отсортировать текст по числу цифр в строке Переставить строки по правилу первая – последняя, вторая – предпоследняя, третья с начала – третья с конца.
2. На панель добавить кнопку, позволяющую сохранить в файле ‘proba.txt’ Вариант Задание 1,6,11,16 Первые K строк 2,7,12,17 Строки с четным числом слов 3,8,13,18 K строк начиная с L-ой 4,9,14,19 Строки, в которых ровно K слов 5,10,15,20 Строки, в которых ровно L букв а. Числа K и L вводятся пользователем. Дополнение Среди прочих компонент Memo обладает рядом свойств, доступных только во время работы приложения Свойство SelText SelLength SelStart Modified
Значение Выделенный фрагмент текста Длина выделенного фрагмента Номер первого символа выделенного фрагмента начиная с начала всего текста. Если выделения нет, то позиция курсора внутри текста TRUE, если текст был изменен.
126
Задание 3. Визуализация решения алгоритмической задачи. Решить алгоритмическую задачу: 1. Дано N точек на плоскости. Найти окружность, на которой лежат наибольшее число точек данного множества. 2. Дано N точек на плоскости. Найти такую окружность с центром в одной из точек и проходящую хотя бы через одну из оставшихся, число данных точек, лежащих внутри и вне этой окружности. 3. Дано N точек на плоскости. Найти окружность наименьшего радиуса с центром в одной из точек, содержащую внутри все заданные точки. 4. Дано N точек на плоскости. Найти окружность с центром в одной из точек, на которой лежат наибольшее число точек данного множества. 5. Дано N точек на плоскости. Провести окружность, проходящую по крайней мере через 3 исходные точки и содержащую внутри наибольшее число точек. 6. Дано N точек на плоскости. Найти окружность максимальной площади, проходящую по крайней мере через 3 исходные точки. 7. Дано N точек на плоскости. Найти окружность наименьшей длины, проходящую по крайней мере через 3 исходные точки. 8. Дано N точек на плоскости. Выбрать из них три такие, чтобы различались наименьшим образом число точек, лежащих внутри и вне окружности, проходящей через эти три точки. 9. Дано N точек на плоскости. Выбрать из них три такие, чтобы различались наименьшим образом число точек, лежащих внутри и вне треугольника с вершинами в этих трех точках. 10.Дано N точек на плоскости. Выбрать из них три такие, чтобы число точек, лежащих внутри треугольника с вершинами в этих трех точках было максимальным. 11.Дано N точек на плоскости. Выбрать из них три такие, чтобы число точек, лежащих на сторонах треугольника с вершинами в этих трех точках было максимальным. 12.Дано N точек на плоскости. Выбрать из них три такие, чтобы была наименьшей площадь треугольника с вершинами в этих трех точках. 13.Дано N точек на плоскости. Выбрать из них три такие, чтобы был наибольшим периметр треугольника с вершинами в этих трех точках. 14.Дано N точек на плоскости. Найти квадрат, по крайней мере две вершины которого совпадают с заданными точками, содержащий внутри максимальное число заданных точек. 15.Дано N точек на плоскости. Найти квадрат, по крайней мере две вершины которого совпадают с заданными точками, на сторонах которого находится максимальное число точек. 16.Дано N точек на плоскости. Найти такой квадрат, по крайней мере две вершины которого совпадают с заданными точками, число чтобы за-
127
данных точек, расположенных внутри и вне которого, различались наименьшим образом. 17.Дано N точек на плоскости. Найти квадрат максимальной площади, по крайней мере две вершины которого совпадают с заданными точками и внутри которого расположено начало координат. 18.Дано N точек на плоскости. Найти параболу вида y=ax 2 +bx+c, на которой лежат наибольшее число точек данного множества. 19.Дано N точек на плоскости. Найти прямую, на которой расположено максимальное число заданных точек. 20.Дано N точек на плоскости. Выбрать из них две такие, чтобы число точек, лежащих внутри окружностей заданного радиуса с центрами в этих трех точках было одинаковым. Решение задачи оформить в виде проекта на Delphi, в котором обязательно предусмотреть: • специальное параллельное окно, содержащее компонент PaintBox для графического представления данных, • таблицу StringGrid для табличного представления данных, • главное меню со следующими пунктами: − Данные/Создать - ввод нового набора исходных данных; − Данные/Открыть - чтение набора исходных данных из текстового файла (использовать OpenDialog); − Данные/Сохранить и Данные/Сохранить как - Запись текущего набора данных в текстовый файл (использовать SaveDialog); − Расчет - вычисления по условиям задания; − Результат/Рисунок - представление результата расчета на графике; − Результат/Файл - вывод результата расчета в текстовый файл(использовать SaveDialog); − Справка/О программе - окно с текстом задания и правилами управления проектом. • кнопку, запускающую процедуру расчета по условиям задания; • рекомендуемый размер графической области 400х400 пикселей для квадрата (-100≤x≤100, -100≤y≤100); • контроль исходных данных на корректность (недостаточное число точек, выход точек за расчетную область -100≤x≤100, -100≤y≤100); • возможность настройки величины погрешности в отдельном компоненте Edit; • возможность ввода новых точек щелчком по графику или добавлением строки в таблице; • возможность изменением в таблице; • применение разного цвета для графического представления исход-
128
ных данных, результата и вспомогательных объектов; • специальную панель для представления результата расчета; • компонент StatusBar для представления текущей информации. • меню Справка/Об авторе. Для примера предлагается пример решения упрощенной тестовой задачи: Заданы три точки на плоскости. Определить, принадлежит ли начало координат (0,0) треугольнику, образованному этими точками. На форму (Width=600, Height=470) расположены следующие компоненты: • PaintBox1 (Width=Height=400) - для отображения графической области (-100<=x<=100, -100<=y<=100); • LabX1,LabX2,LabX3,LAbX4 - метки для указания граничных координат графической области; • StatusBar1 - панель состояния внизу формы с четырьмя секциями; • StringGrid1 (ColCount=3, RowCount=4, ScrollBars=ssNone, Options.goEditing=FALSE) - таблица строк для отображения координат вершин треугольника; • CheckBox1 (Checked=TRUE, Caption = Автоматический расчет) выключатель автоматического решения условия задачи; • Button1 (Caption='Решение') - кнопка, запускающая процедуру решения условия задачи. В разделе interface описаны переменные P : array[0..3] of record массив четырех точек, где P[0] - начало коx,y : double ординат, а P[1]-P[3] - вершины треугольника end; x1,x2,y1,y2 : double; границы прямоугольной графической области l : Integer; номер вершины треугольника, на которую спозиционирован указатель мышки В разделе implementation описаны функции, цель которых - перевод координат из экранных в реальные и наоборот: function zx(x:Integer):Double; begin Result:=x1+x/Form1.PaintBox1.Width*(x2-x1); end; function zy(y:Integer):Double; begin Result:=y2-y/Form1.PaintBox1.Height*(x2-x1); end; function px(x:Double):Integer; begin Result:=Round((x-x1)/(x2-x1)*Form1.PaintBox1.Width); end;
129
function py(y:Double):Integer; begin Result:=Round((y2-y)/(y2-y1)*Form1.PaintBox1.Height); end; Описан обработчик события Form1.OnCreate: X1:=-100; LabX1.Caption:=FloatToStrF(X1,ffFixed,6,1); X2:= 100; LabX2.Caption:=FloatToStrF(X2,ffFixed,6,1); Y1:=-100; LabY1.Caption:=FloatToStrF(Y1,ffFixed,6,1); Y2:= 100; LabY2.Caption:=FloatToStrF(Y2,ffFixed,6,1); P[0].x:= 0; P[0].y:= 0; P[1].x:=-50; P[1].y:=40; P[2].x:= 50; P[2].y:= 0; P[3].x:= 0; P[3].y:=50; PaintBox1.Cursor:=crCross; With StringGrid1 do begin Rows[0].Text:=' '#10#13' x'#10#13' y'; Cols[0].Text:='Вершина'#10#13' 1'#10#13' 2'#10#13' 3'; end;
Определение и вывод на форму значений координат графической области
Координаты начала координат Начальные координаты вершин треугольника Рисунок курсора над PaintBox1 Заголовки строк и столбцов таблицы. Доступ ко всем ячейкам нулевой строке и нулевого столбца через свойства Rows[0] и Cols[0] соответственно.
В разделе implementation описана процедура, которая рисует на PaintBox1 желтым цветом оси координат и указанным цветом сам треугольник. procedure TForm1.GraphShow(Col:TColor); procedure Line(i,j : integer); begin PaintBox1.Canvas.MoveTo(px(p[i].x),py(p[i].y)); PaintBox1.Canvas.LineTo(px(p[j].x),py(p[j].y)); end; begin With PaintBox1,Canvas do begin Pen.Color:=clYellow; MoveTo(px(0),py(-100)); LineTo(px(0),py(100));
130
MoveTo(px(-100),py(0)); LineTo(px(100),py(0)); Pen.Color:=Col; Line(1,2); Line(2,3); Line(3,1); end; if CheckBox1.Checked then Button1.Click; end; Обратите внимание: в описание объекта TForm1 вручную включено описание интерфейса данного метода. Вывод текущей информации о вершинах в строки таблицы StringGrid1 реализованы в процедуре InfoData, которая оформлена методом класса TForm1. procedure TForm1.InfoData; var i : integer; begin With StringGrid1 do for i:=1 to 3 do begin Cells[1,i]:=FloatToStrF(P[i].x,ffFixed,6,1); Cells[2,i]:=FloatToStrF(P[i].y,ffFixed,6,1); end; end; Начальный вывод рисунка оформлен в обработчике события OnPaint компонента PaintBox1. procedure TForm1.PaintBox1Paint(Sender: TObject); begin With PaintBox1,Canvas do begin Brush.Color:=clBlue; Rectangle(0,0,Width,Height); Pen.Width:=2; GraphShow(clRed); InfoData; end; end; Определение текущего положения указателя мыши на графической области и индикация его попадания на вершину треугольника организованы в обработчике события OnMouseMove компонента PaintBox1: procedure TForm1.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer); var i : integer; begin
131
With StatusBar1 do begin Panels[1].Text:='x :'+FloatToStrF(zx(x),ffFixed,7,1); Panels[2].Text:='y :'+FloatToStrF(zy(y),ffFixed,7,1); l:=0; for i:=1 to 3 do if Abs(x-Px(P[i].x))+Abs(y-Py(P[i].y))<=2 then l:=i; if l<>0 then begin Panels[3].Text:='Вершина '+IntToStr(l); PaintBox1.Cursor:=crArrow; end else begin Panels[3].Text:=''; PaintBox1.Cursor:=crCross; end; end end; Механизм переноса вершин треугольника по граф. области реализован обработчиками трех событий компонента PaintBox1: начало переноса procedure TForm1.PaintBox1MouseDown(Sender: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if l<>0 then PaintBox1.BeginDrag(True); end;
TObject;
Button:
перемещение вершины procedure TForm1.PaintBox1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); begin if Sender = PaintBox1 then begin Accept:=True; GraphShow(clBlue); p[l].x:=zx(x); P[l].y:=zy(y); GraphShow(clRed); InfoData; end; end; завершение переноса - "отпускание" вершины procedure TForm1.PaintBox1DragDrop(Sender,Source: TObject; X,Y: Inte-
132
ger); begin Button1.Visible:=True; StatusBar1.Panels[0].Text:=''; end; При нажатии Button1 вычисляется сумма площадей трех треугольников, каждый из которых образован одной из сторон данного треугольника и началом координат. Если полученная сумма совпадает с площадью данного треугольника, то начало координат является его внутренней точкой procedure TForm1.Button1Click(Sender: TObject); function Sq(i,j,k:integer):double; begin Result:=Abs((p[i].x-p[k].x)*(p[j].y-p[k].y)(p[j].x-p[k].x)*(p[i].y-p[k].y))/2; end; begin With StatusBar1.Panels[0] do if Abs(Sq(0,1,2)+Sq(0,2,3)+Sq(0,3,1)-Sq(1,2,3))<1e-6 then Text:='Точка (0,0) принадлежит треугольнику' else Text:='Точка (0,0) не принадлежит треугольнику'; Button1.Visible:=False; end; Если вызов этого метода установить в текст процедуры GraphShow (в данном примере активно при выбранном выключателе CheckBox1), то индикация принадлежности начала координат треугольнику будет вызываться не только при нажатии кнопки, но и при переносе вершины. Задание 4. Многооконный текстовый редактор. Создать на DELPHI проект, реализующий возможности многооконного текстового редактора. Проект должен включать: Главное меню с разделами Файл (Создать, Открыть..., Закрыть, Сохранить, Сохранить как..., Сохранить все, Автосохранение..., Выход); Правка (Вырезать, Копировать, Вставить, Удалить, Найти..., Заменить...); Оформление (Шрифт..., Выравнивание..., Цвет фона...); Печать (Настройка принтера..., Печать...); Окна (Каскадом, Рядом, Свернуть, Свернуть все, Закрыть все); Справка (О программе...).
133
Пункты меню с многоточием открывают соответствующее окно диалога. Для пунктов меню, выделенных подчеркиванием, предусмотреть кнопки на инструментальной панели. Бессмысленные в определенный момент пункты меню и кнопки инструментальной панели (например Копировать при отсутствии выделенного текста) представляются в "бледном" виде (для желающих). Активность кнопки "Сохранить" должна информировать об изменении текста после последнего сохранения. При закрытии окна с несохраненным текстом выдавать окно с предложением о сохранении. Реализовать отключаемый режим автосохранения текста с выбором интервала времени. На инструментальной панели установить выпадающие списки доступных шрифтов (Screen.Fonts) и размеров.