Артем Зубов
трюки
эффекты
Артем Зубов
Москва • Санкт-Петербург • Нижний Новгород • Воронеж Новосибирск • Ростов-на-Дону • Екатеринбург • Самара Киев • Харьков • Минск
2005
ББК 32.973.23-018.2 УДК 681.3,06
391
Зубов А. 3-91 Программирование на Delphi. Трюки и эффекты (+CD). — СПб.: Питер, 2005. — 396 с.: ил. — (Серия «Трюки и эффекты»).
i ISBN 5-469-00434-1 «Программирование на Delphi», как и все издания отличной серии «Трюки и эффекты», адресована тем, кто хочет научиться делать с помощью уже знакомых программных пакетов новые, интересные вещи. В первой части книги много говорится о среде разработки Delphi (самых последних версий) и программировании на языке Object Pascal. Благодаря этому издание подходит и новичкам, и начинающим программистам. Вторая часть книги описывает удивительные возможности, скрытые в языке, и на примерах учит читателя программистским фокусам — от «мышек-невидимок» и «непослушных окон» до воспроизведения МРЗ и управления офисными программами Word и Excel из приложений Delphi.
ББК 32.973.23-018.2 УДК 681.3.06
Все права защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав.
Информация, содержащаяся в данной книге, получена из источников, рассматриваемых издательством как надежные. Тем не менее, имея в виду возможные человеческие или технические ошибки, издательство не может гарантировать абсолютную точность и полноту приводимых сведений и не несет ответственности за возможные ошибки, связанные с использованием книги.
ISBN 5-469-00434-1
©ЗАО Издательский дом «Питер», 2005
Краткое оглавление Введение
9
Часть 1. Программирование в Delphi Глава 1. Общие сведения Глава 2. Интерфейс программы Глава 3. Структура программы Глава 4. Стандартные типы данных Глава 5. Операторы Глава 6. Процедуры и функции Глава 7. Свойства, методы и события Глава 8. Работа с файлами Глава 9. Техника программирования Глава 10. Компиляция и отладка программы Глава 11. Создание приложения
12 15 39 44 55 63 73 84 108 124 131
Часть 2. Трюки и эффекты Глава 12. Хитрости с мышью и клавиатурой Глава 13. Эффектные формы Глава 14. Проигрывание звуковых записей Глава 15. Трюки с Word и Excel Глава 16. Программирование для Интернета Глава 17. Вывод графики с помощью GUI Глава 18. Использование DirectX Глава 19. Реестр Windows Глава 20. Локальные сети Глава 21. Полезные и интересные программы Глава 22. FAQ и сайты о Delphi Заключение Приложение. Описание компакт-диска
174 190 208 233 242 258 294 333 349 361 386 392 393
Оглавление Введение От издательства
9 10
Часть 1. Программирование в Delphi Глава 1. Общие сведения
12
Глава 2. Интерфейс программы
15
2.1. Среда разработки 2.2. Создание приложений 2.3. Обзор компонентов
16 28 34
Глава 3. Структура программы
39
3.1. Файлы, используемые в Delphi
40
3.2. Структура PAS-файла 3.3. Требования к тексту программы
41 42
Глава 4. Стандартные типы данных
44
4.1. Простые типы 4.2. Строковые типы 4.3. Тип Variant 4.4. Объявление переменных 4.5. Определение своих типов данных 4.6. Константы
45 47 48 48 50 53
Глава 5. Операторы
55
5.1. Оператор присваивания 5.2. Операторы ввода-вывода 5.3. Оператор безусловного перехода 5.4. Операторы условного перехода
56 56 57 57
Оглавление 5.6. Циклы 5.7. Операции с числами 5.8. Основные математические функции
59 60 61
Глава 6. Процедуры и функции
63
6.1. Процедуры 6.2. Функции 6.3. Процедуры, возвращающие значение 6.4. Забавный эксперименте функцией 6.5. Перегружаемые процедуры и функции 6.6. Дополнительные сведения о процедурах и функциях
64 66 66 69 69 71
Глава 7. Свойства, методы и события
73
7.1. Свойства 7.2. Методы 7.3. События
74 81 82
Глава 8. Работа с файлами
:
84
8.1. Текстовые файлы 8.2. Типизированные файлы 8.3. Нетипизированные файлы 8.4. Удаление записей 8.5. Обработка ошибок 8.6. Процедуры и функции для работы с файлами
86 90 101 102 104 106
Глава 9. Техника программирования
108
9.1. Оптимизация программы 9.2. Комментарии 9.3. Точный подбор цвета
109 118 119
Глава 10. Компиляция и отладка программы
124
10.1. Компиляция 10.2. Отладка
125 126
Глава 11. Создание приложения
131
11.1. Компоненты и их свойства 11.2. Остроумные фразы
132 134
Оглавление 11.3. Базы с сообщениями
141
11.4. Программирование интерфейса
143
11.5. Загрузка данных
144
11.6. Интеллект программы
147
11.7. Добавление общих фраз
150
11.8. Встраивание в программу возможности сообщать о текущем времени
154
11.9. Коллекция вопросов и ответов программы
156
11.10. Полный листинг программы
161 •
,
Часть 2. Трюки и эффекты Глава 12. Хитрости с мышью и клавиатурой
174
12.1. Указатель мыши: найти и перепрятать
175
12.2. Клавиши Ctrl, Alt, Shift и кнопки мыши
182
12.3. Определяем код клавиши
184
12.4. Быстрый опрос клавиатуры
186
Глава 13. Эффектные формы
190
13.1. Формы необычного вида
191
13.2. Кнопки необычного вида
195
13.3. Способы перемещения окон
196
13.4. Прозрачные окна
197
13.5. Программы-«невидимки»
201
Глава 14. Проигрывание звуковых записей
208
14.1. Как «поселить» звук в программе
209
14.2. Проигрывание музыки вформате МРЗ
220
14.3. Видеопроигрыватель и видеокнопка
229
Глава 15. Трюки с Word и Excel
233
15.1. Технологии СОМ и OLE
234
15.2. Управление Word и Excel из другой программы
236
Оглавление Глава 16. Программирование для Интернета
242
16.1. Разрабатываем браузер
243
16.2. Трюки с браузером Internet Explorer
248
16.3. Письмецо в конверте
254
Глава 17. Вывод графики с помощью GUI
258
17.1. GUI и Canvas
259
17.2. Выбор технологии для экранных трюков
261
17.3. Вывод текста на экран
262
17.4. Загрузка изображений в ImageList
266
17.5. Эффектно выводим графику на экран
271
17.6. Рисуем на Рабочем столе
290
Глава 18. Использование DirectX
294
18.1. Что такое DirectX
295
18.2. Библиотека DelphiX
296
18.3. Компонент TDXDraw
299
18.4. Очень быстрый вывод графики на экран
301
18.5. Часы
312
18.6. Палитра
319
18.7. Программы-«заготовки»
324
18.8. Работа со звуком
:
328
Глава 19. Реестр Windows
333
19.1. Что такое реестр
334
19.2. Реестр в подробностях
337
19.3. Запись в реестр и чтение из него
337
19.4. Автоматический запуск программы
344
Глава 20. Локальные сети
349
20.1. Разновидности сетей
350
20.2. Программы для передачи сообщений по локальной сети
352
20.3. Управление другим компьютером посети
359
Оглавление Глава 21. Полезные и интересные программы
361
21.1. Работа с изображениями в формате JPEG 21.2. Хранение изображений в ЕХЕ-файле 21.3. Запуск других приложений при помощи своей программы 21.4. Эффекты с кнопкой Пуск и Панелью задач 21.5. Постоянно открывающийся лоток привода компакт-дисков 21.6. Появляющийся время от времени «Блокнот» 21.7. «Подмигивающий» автомобиль 21.8. Разрешение экрана 320x200 пикселов в Windows
362 363 365 368 369 374 377 383
Глава 22. FAQ и сайты о Delphi
386
22.1. FAQ, илиЧаВо 22.2. Сайты о Delphi
387 390
Заключение
392
Приложение. Описание компакт-диска
393
Введение Если вы ищете серьезную книгу, например о работе с базами данных, то данное издание вам вряд ли подойдет. Здесь речь идет о таких вещах, в полезности которых можно усомниться. Книга рассчитана на читателей, немного знакомых с программированием, но, впрочем, необязательно хорошо умеющих программировать. Для понимания текста достаточно уметь написать на языке Basic программу, вычисляющую, сколько будет два плюс два. Конечно, это только нижняя планка! Более опытные пользователи тоже смогут почерпнуть для себя что-то полезное. В некоторой степени именно поэтому книга разделена на две части. В первой части описано программирование на Delphi. Те читатели, которые умеют программировать на Delphi, сразу могут переходить ко второй части — непосредственно трюкам и эффектам. Теперь об обеих частях книги более подробно. Из первой можно получить общие сведения о Delphi, описание интерфейса (включая подробный обзор среды разработки и меню), а также о типах данных, операторах, процедурах и функциях. Всевозможные справочные сведения тоже собраны в первой части (так что даже опытные пользователи могут обращаться к ней, чтобы посмотреть, как вызывается функция, которой они давно не пользовались). В конце первой части приведен пример написания нормально работающего приложения Windows. Скучновато выглядит первая часть, не так ли? Но зато во второй собраны более веселые вещи: перемещающиеся кнопки, непослушные указатели мыши, звуковые эффекты, «дрессированные» Word и Excel, вывод графики (в том числе с помощью DirectX!) и прочие трюки и эффекты. К книге прилагается компакт-диск, на котором собраны все опубликованные в ней примеры. Описание компакт-диска приведено в приложении. Думаю, что если вы дошли до этих слов, то настало время переворачивать страницу и читать дальше!
От издательства Ваши замечания, предложения, вопросы отправляйте по адресу электронной почты
[email protected] (издательство «Питер», компьютерная редакция). Мы будем рады узнать ваше мнение! На web-сайте издательства http://www.piter.com вы найдете подробную информацию о наших книгах.
Часть 1 Программирование в Delphi Глава 1. Общие сведения Глава 2. Интерфейс программы Глава 3. Структура программы Глава 4. Стандартные типы данных Глава 5. Операторы Глава 6. Процедуры и функции Глава 7. Свойства, методы и события Глава 8. Работа с файлами Глава 9. Техника программирования Глава 10. Компиляция и отладка программы Глава 11. Создание приложения
12 15 39 44 55 63 73 84 108 124 131
Глава 1
Общие сведения
Глава 1. Общие сведения Наверное, стоит вкратце рассказать о Delphi — просто чтобы знать, что это вообще за язык и откуда он взялся. Конечно, данная книга — исключительно о трюках и эффектах; но ведь любая вещь выйдет лучше, если хорошо знать инструмент, с которым работаешь. А система программирования — это именно инструмент. Delphi — это не что иное, как Object Pascal. Язык Pascal достиг своей вершины в начале 1990-х годов, во времена распространения его седьмой версии. Как раз тогда началась эра визуального программирования и создания программ для появившейся и быстро распространяющейся системы Windows. Поэтому после седьмой версии языка программистам представили не восьмую версию Pascal, а первую Delphi. Проект с таким названием появился в середине 1993 года, а в 1995 году в продажу поступила первая версия Borland Delphi. . Интересна история появления именно такого названия проекта. Почему именно Delphi? Все просто: «Если вы хотите разговаривать с Оракулом, идите в Дельфы». Это, конечно, намек на систему доступа к базам данных, называемую Oracle, и город Дельфы в Греции, где и находился мифологический Оракул. Почему для создания эффектных программ очень хорошо подходит Delphi? Сравним Delphi с другими распространенными системами программирования — Visual Basic и C++. Если сравнивать Delphi, например, с C++, то преимущество первого из языков будет заключаться в том, что программировать на нем можно гораздо проще и быстрее. Зачем выполнять лишние действия и писать сложные программы, если нужно всего лишь как можно быстрее создать что-то впечатляющее? При сравнении же Delhpi с Visual Basic оказывается, что рассматриваемый в данной книге язык имеет перед ним куда более серьезное преимущество. Дело в том, что программы (исполняемые ЕХЕ-файлы), написанные на Visual Basic, получаются сами по себе небольшого размера, но для работы требуют дополнительных файлов (библиотек DLL). Поэтому даже простая программа — при условии, что ее можно будет запускать на любом компьютере, — займет около 2 Мбайт. При создании готовых программ на Delphi в ЕХЕ-файл включается уже все необходимое и внешние файлы не требуются. Поэтому размер ЕХЕ-файла обычно бывает больше созданного в Visual Basic (от 200 Кбайт), но будет работать сам по себе, если его запустить на любом другом компьютере. Для эффектов это как раз то, что нужно! Вышедшая в 1995 году Borland Delphi 1.0 стала просто революцией после седьмой версии Pascal. Но если сравнивать с нынешними версиями Delphi, то возможности Delphi 1 на сегодняшний день очень и очень скромные. Компания Borland (а позже Inprise, после чего снова Borland) постоянно выпускала новые версии Delphi, примерно по одной в год. С появлением новой тогда операционной системы Windows 95 (32-разрядной (!), в отличие от Windows 3.1) была выпущена Delphi 2, в которой были учтены возможности и стандарты новой операционной системы. Основными отличиями первой версии Delphi от второй являются поддержка 32-разрядности и новые компоненты, выполненные в стиле Windows 95. По сути,
Глава 1. Общие сведения Delphi 2 и сейчас может успешно применяться для разработки приложений, если не использовать многие современные технологии и компоненты (например Интернет, СОМ). Однако если программист хочет использовать все современные технологии, новые компоненты, более удобную среду разработки приложений, то ему приходится идти в ногу со временем и работать с последними версиями Delphi. Если пользоваться различными дополнительными компонентами, в том числе бесплатными и свободно распространяемыми (например, скачивая их из Интернета), то минимально необходимой версией стоит считать Delphi 3. Для нее в Интернете еще можно найти компоненты, а вот для Delphi 2 я не видел. В 1997 году как раз и была выпущена третья версия Delphi. Delphi 3 была ориентирована на бурно развивающуюся тогда сеть Интернет и на клиент-серверные технологии. Кроме значительно расширенного инструментария для разработки интернет-приложений, упростилось создание управляющих элементов ActiveX. Кроме того, в Delphi 3 появились новые драйверы баз данных и SQL. В Delphi 4 были введены динамические массивы, более удобная среда разработки, улучшенная отладка программ, новые компоненты и поддержка CORBA. Delphi 5 в 2000 году порадовала улучшенной средой разработки, поддержкой баз данных и многоязыковой поддержкой. Появилась система Internet Express для создания HTML-страниц. В целом Delphi 4 и 5 были не столь революционными, как три предыдущие версии Delphi. Delphi б, увидевшая свет в 2001 году, принесла с собой опять же множество новых компонентов, еще теснее интегрируясь с Интернетом. Delphi 7 порадовала разработчиков программного обеспечения небольшими улучшениями среды разработки. Более важно то, что в седьмой версии Delphi появилась полная поддержка тем Windows XP. Как в Delphi 2 были добавлены новые компоненты в стиле Windows 95, точно так же спустя шесть лет новые компоненты позволили создавать приложения в стиле новой операционной системы. И, наконец, последняя изданный момент версия Delphi — восьмая. Borland Delphi 8 предназначена для создания приложений, которые будут использоваться на платформе .NET Framework. Эта платформа предназначена для создания, развертывания и запуска web-сервисов и приложений. Конечно, не обошлось и без других нововведений. Например, среда разработки изменилась и выглядит аналогично другим продуктам Borland. Вот так до нынешнего времени развивалась очень популярная в России (и не только в России) система программирования.
Глава 2
Интерфейс программы Q Среда разработки Q Создание приложений Q Обзор компонентов
Глава 2. Интерфейс программы
2.1. Среда разработки Здесь описана среда разработки Delphi 7, потому что в последнее время я использую именно эту версию. Вообще интерфейсы всех версий Delphi очень похожи, и различия между ними минимальны. \ \ Конечно, если сравнивать Delphi 3 и 8, то на первый взгляд кажется, что разница есть. Однако если присмотреться, то можно заметить, что кнопки выглядят немного по-другому, расположены чуть иначе, но в принципе все точно так же. Интерфейс Delphi с самого начала был хорошо продуман и удобен, привыкнуть к нему можно очень быстро. Когда я начинал программировать на Delphi, то среда разработки этой системы стала мне родной почти сразу. К сожалению, сейчас я уже не помню, за какое время привык к ней, но точно за очень короткое. Привыкнуть к Photoshop было сложнее, а от Проводника Windows я вообще не в восторге, даже спустя многие годы работы на компьютере. Ну а после первой версии Delphi внесено много изменений, отточивших и без того хороший интерфейс (рис. 2.1). ' Project! File
Ed*
Search View Project
Run Component Database
Tools Window Help j j [my
3 I
f j U S * e i O ; S l i i s ^ 4 H ' Standard j Additional j Win32 j Si>stem| DataAccessj Data Controls i dbExoressj DalaSnaoi BDE
iShffiiaH !>- III i V ; H ^ ЭВ" *fe А ГЯГ §] CssJ 'ft ft p Я «™ Q. .ill D JS • s iseView -'
[7J Fmml ;5 Umll pas
Unitl
:
: ; ; : .. 4» , I publi с I { Public declarations > I end;
! const I I
total_roonthes-12; alcogolics=3; |
jvar ; :
Form!: TFocml; Implement ati on
-'•22:.' 19' : • !JMoc№ed
'" [insert
j\Code/Diagtam/"
Рис. 4.2. Константы в тексте программы
Константы — это некие значения, используемые в программе, которые, как следует из названия, постоянны и заранее известны во время разработки программы. Задаются константы в разделе const, который может находиться до или после var И type. При объявлении констант не нужно указывать их тип. Просто через знак равенства пишется, чему равна константа. Листинг 4.6
const total_monthes=12; alcogol ics=3; Константы в программе можно использовать точно так же, как переменные. Конечно, нельзя присвоить константе значение, а также использовать ее как счетчик цикла.
Глава 4. Стандартные типы данных Часто константы используют для величин, которые после компиляции программы не нужно изменять, но которые могут изменяться при написании или модернизации программы. Допустим, в константе можно хранить номер версии программы. Тогда в тексте программы всегда используется константа, а чтобы изменить номер версии, достаточно просто исправить одно число в объявлении константы. Листинг 4.7
const vers=Myprog 1.0; // константа с версией программы begin // [...]
// где-то в глубине программы Label 12.Caption := vers; // вывод версии программы на экран // [...] end. Таким образом, не нужно искать в тексте программы значения, которые необходимо изменить, а следует выносить все такие вещи в раздел констант. Тогда они на виду и искать их не надо. Впрочем, что я вам рассказываю, где применять константы? Однажды вы сами придете к этому, если еще не пришли.
Глава 5
Операторы а Q Q Q Q О Q
Оператор присваивания Операторы ввода-вывода Оператор безусловного перехода Операторы условного перехода Циклы Операции с числами Основные математические функции
Глава 5. Операторы Именно операторы выполняют все действия в программе. В этой главе описаны операторы Delphi, а также некоторые встроенные функции.
5.1. Оператор присваивания
•
Оператор присваивания в Delphi состоит из знаков двоеточия и равенства, написанных один за другим. Слева от оператора присваивания пишется переменная, которой нужно присвоить значение, а справа — выражение, которое нужно вычислить. Листинг 5.1
А := 25; // переменной А присваивается значение 25 str := Париж; {переменной str присваивается строковое значение "Париж"} А := В+С+10; {переменные В и С складываются, затем к ним прибавляется 10, после чего результат помещается а А} Shark := 25*23+MoveX; При вводе выражений необходимо помнить, что они обязательно должны быть записаны в одну строку. Операции выполняются слева направо, но умножение и деление всегда до сложения и вычитания. Чтобы заставить Delphi выполнить, например, сложение раньше умножения, нужно поставить круглые скобки: а := ( 5 + 2 ) * 3 ; а := 5+2*3;
В первом случае переменной а присвоится значение 21. Во втором — сначала выполнится умножение 2 на 3, и в итоге получится 11. Кстати, строки тоже можно складывать с помощью плюса, но вычитать с помощью минуса нельзя.
5.2. Операторы ввода-вывода Очень важные в Pascal операторы ввода-вывода в Delphi не выглядят такими важными. Ведь вывод сейчас осуществляется через компоненты на форме, да и считывание данных тоже. Это все справедливо для приложений, использующих окна-формы. Если вы пишете консольное приложение (выглядит как окно DOS), то в нем можно использовать Write, WriteLn, Read, ReadLn точно так же, как в Pascal. Например, п : = Read ожидает ввода с клавиатуры значения, а затем помещает это значение в переменную п. Оператор ReadLn читает с клавиатуры строку символов, а в остальном используется как Read. Операторы W r i t e и W r i t e L n применяются для вывода значений переменных.
Операторы условного перехода Эти операторы могут пригодиться и тогда, когда нужно прочитать или записать файл. Работа с файлами рассмотрена в гл. 8, поэтому просто приведу примеры использования данных операторов. Для чтения из файла используются операторы Read и ReadLn. Листинг 5.2
Read(F,p); {чтение записи из файла F в переменную р} ReadLn(F.s); {чтение строки из текстового файла F в переменную s} Write(F.p); {запись переменной р в файл} WriteLn(F,s); {запись строки в текстовый файл}
5.3. Оператор безусловного перехода Оператор безусловного перехода goto устарел. Использовать его не рекомендуется, так как часто именно применение этого оператора ведет к ошибкам в программе и путанице, к тому же сейчас существуют другие, более совершенные методы программирования. Но знать об этом операторе безусловного перехода, несомненно, нужно. Вдруг с помощью него вы создадите что-нибудь интересное. Оператор goto перебрасывает выполнение программы в другое место. Лучше показать это на примере. Листинг 5.3 procedure TForml.GotoExample; label m; begin a := 5; b := 25; goto m; a:=3;
m: end;
Здесь процедура выполняется до оператора goto, а затем перескакивает сразу на метку т. Строка а : = 3; не выполняется. Метки доджны быть описаны в разделе label.
5.4. Операторы условного перехода Оператор if Чтобы проверить какое-то условие и выполнить ту или иную часть программы, существует оператор i f . В простейшем случае он записывается так: if a=5 then a := а*2;
Глава 5. Операторы Если условие а=5 верно (в данном случае), то выполняется действие, записанное после then. Можно выполнять сразу несколько действий, вписав их в составной оператор begin-end. Листинг 5.4 if a=5 then begin {набор действий} а := а*2; b := True; end; Таким образом, все выполняемые действия должны быть записаны между словами begin и end (составной оператор). Обратите внимание, что после begin не ставится точка с запятой, так как это продолжается один оператор (до end). А между begin и end в конце строк точки с запятой ставятся. Оператор if можно записать и так, как показано в листинге 5.5. Листинг 5.5 if a=5 then begin {набор действий} end else begin
{другой набор действий} end;
Тогда если а=5, то выполняется первый набор действий. Если же а не равно пяти, то выполняются другие действия (записанные после слова else).
Оператор выбора case Иногда бывает удобно использовать не if, а оператор case, который проверяет сразу много значений. Листинг 5.6 case A of 1: s = один; 2: s = два; 4: s = три; 5: s = четыре; 6: s = пять; 8: s = шесть; else s := ; end; // case
Циклы В приведенном примере оператор case сверяет значение переменной А с указанными вариантами. Если А=1, то выполнится первая строка, если А=4 — третья и т. д. Если же А не будет равна ни одному из значений, то выполнится s : = ' ' после else (else указывать необязательно).
5.6. Циклы В Delphi, как и полагается в приличном языке программирования, можно сделать цикл с известным количеством повторений, цикл с предусловием и цикл с постусловием.
Цикл с известным количеством повторений for Циклом с известным количеством повторений является for. Вот его пример: for i := 1 to 20 do s := s + ' ';
В цикле for указываются начальное и конечное значения переменной (в нашем случае для переменной i они равны 1 и 20 соответственно). Действия, которые нужно выполнять в цикле, указываются после do. После do, кстати, может быть и составной оператор begin-end.
Цикл с предусловием while-do Данный цикл очень часто используется, например при чтении и записи файлов. Листинг 5.7
s := ; i := 0 whi le i New > Application. На форму поместите компоненты ComboBox и этикетки Labell и Label2. В качестве значений свойства Items компонента ComboBox укажите названия месяцев: Январь, Февраль, Март, Апрель, Май, Июнь, Июль, Август, Сентябрь, Октябрь, Ноябрь, Декабрь. Именно так, не ИСкажайте. Такую программу можно реализовать разными способами. Способ, который мы применим, предполагает, что месяцы можно вписывать в любом порядке, но сами названия должны быть написаны с большой буквы без ошибок (чтобы совпадали с текстом программы). Теперь приступим непосредственно к написанию программы. Перейдите в Object Inspector на вкладку Events и дважды щелкните мышью на событии OnChange, чтобы открыть редактирование процедуры ComboBoxlChange. Из этой процедуры будем вызывать процедуру WhatMonth (именно процедуру, а не функцию), которой будем сообщать выбранный месяц, а из нее будут возвращаться два параметра: номер месяца и количество дней в нем. Эти значения будем выводить на Labell и Label2. Пишем процедуру WhatMonth (ее можно расположить до или после ComboBoxlChange). Нужно либо вписать ее в класс формы вручную, либо щелкнуть на заголовке процедуры правой кнопкой мыши и выбрать Complete class at cursor (рис. 6.1). Листинг 6.4
procedure TForml.WhatMonth.(s: String; var nm, days: Integer); // определение номера месяца и количества дней begin if з=Январь then begin nm := 1; days := 31; end; if з=Февраль then begin nm := 2; days := 28; end; if s=Mapr then begin nm := 3; days := 31; end; if з=Апрель then b e g i n nm := 4; days := 30; end; if з=Май then b e g i n nm := 5; days := 31; end; if з=Июнь then b e g i n nm := 6; days := 30; end; if з=Июль then b e g i n nm := 7; days := 31; end;
Глава 6. Процедуры и функции
if з=Август then begin nm := 8; days := 31; end; if з=Сентябрь then begin nm := 9; days := 30; end; if з=0ктябрь then begin nm := 10; days := 31; end; if з=Ноябрь then begin nm := 11; days := 30; end; if з=Декабрь then begin nm := 12; days := 31; end; end; Find Declaration Run
Component
Database
Jools
Win|
[ A i l Slandatd j Additional i Win32 j Si|
Drl*F4
£k>se Page Open F_le at Cursor
f "V {
Clrl*Enter
jj DalaSnaol BDE j-il_!«
New Edit Window
-II
Browse Symbol at Cursor Topic Search
*
»
[o] Button 1 Eljlabell
>
! procedure TForrol . UhacHoj Propeues | Events | i Actipii iSAnd-prs Г Bit* ode
I ilakLeilakfopj ' IbdLefiToRighi" " False"'''"'•;''"•'
{ГСарфп iffiConsjiaints i Cursti
;t(TSbeCore crDefault
Draglursor DlaQi^ind Diaptode Enabed
crDrag :dkOrag jdmManual {True
" ' " '
Integer) ;
I// ояределе«ие яо [begin
iif
|i£ jif iif iif iif
Январь' then begif: Февраль 1 then begi Нарт 1 then begin ; Апрель-' then begii ^| View Explorer Май 1 then begin nl Properties Июнь1 then beciin L. ..... , 31 ГЙ ....... IModiiied "
I \CodeXbiagram/
]№»•«)":' "
] ||Windo..
Рис. 6.1. Выбор в меню пункта Complete class at cursor .
Удобнее было бы применить здесь оператор case, но, увы, он не работает с переменными типа string. Обратите внимание на заголовок процедуры. В нее передается переменная s. Результат работы процедуры возвращается через переменные nm и days, поэтому перед ними стоит ключевое слово var. Процедуру будем вызывать из ComboBoxlChange. Так что подбор количества дней будет выполняться тогда, когда пользователь поменяет месяц в списке. Листинг 6.5
procedure TForml.ComboBox1Change(Sender: TObject); var k1, K2: Integer; begin WhatMonth(ComboBox1.Text,k1,k2);
Перегружаемые процедуры и функции LabeИ.Caption := Номер месяца в году + IntToStr(kl); Label2.Caption := Количество дней в этом месяце + lntfoStr(k2);
end; Значения переменных nm и days в процедуре whatMonth возвращаются в переменные kl и k2. Можете запустить программу и посмотреть, что все работает: значения подбираются в процедуре WhatMonth, передаются в ComboBoxlChange и уже оттуда выводятся на экран.
6.4. Забавный эксперимент с функцией Может быть, сделаем паузу? Да, вызывая вполне серьезную на вид функцию, будем получать совсем несерьезный результат. Создайте новый проект и поместите на форму кнопку Button 1 и этикетку Labell. В процедуре Buttoniciick (процедура обработки события OnClick кнопки Buttonl) напишите всего лишь одну строку кода: Labell.Caption := IntToStr(25); и запустите программу. Если нажать кнопку, то на этикетке появится текст «25». Теперь сделаем вид, что функция IntToStr, которая преобразует число в строку, нам не нравится, и поэкспериментируем с ней. Напишем такую функцию (не забудьте объявить ее в классе формы). Листинг 6.6
function TForml. lntToStr(i: Shortint): String; // наша собственная функция IntToStr begin IntToStr := Вигвам - индейская народная изба!;
end; Если теперь запустить программу и нажать кнопку, то вместо «25» появится совсем другое! Работает не стандартная функция Delphi, а наша.
6.5. Перегружаемые процедуры и функции Стандартную процедуру или функцию можно перегрузить. Это означает, что можно написать несколько процедур и функций с одинаковыми именами, но эти процедуры и функции будут разными, а самое главное — они будут обрабатывать разные параметры. Скажем, для обработки строковых значений будет вызываться одна функция, а для обработки числовых значений — другая, а для значений еще каких-то типов
Глава 6. Процедуры и функции (например, вещественных) — третья функция. И все эти три функции могут называться одинаково. Традиционно привожу пример. Создайте новый проект и разместите на форме три кнопки Button и одну этикетку Label. Теперь в свойствах Caption кнопок напишите следующее:дляВиШэп! — Целые числа, для Button2 —Дробные числа и для Buttons — Строки. При нажатии первой кнопки на этикетку будет выводиться целое число, при нажатии второй — дробное, а при нажатии третьей — текстовая строка. Все это будет выполнять процедура OutOnLabel. На самом деле получатся три процедуры, но все они будут называться одинаково. Напишем эти процедуры и обработку событий нажатия кнопок. Листинг 6.7
procedure TForml.OutOnLabel(s: String); // вывод на этикетку строки текста begin Label 1.Caption := s; end; procedure TForml.OutOnLabel(i: Integer); // вывод на этикетку целого числа begin Labe11.Caption := IntToStr(i); end; procedure TForml.OutOnLabel(i: Extended); // вывод на этикетку дробного числа begin Labe11.Caption := FloatToStr(i); end; procedure TForml. ButtonlCI ick(Sender: TObject); begin OutOnLabel(731); end; procedure TForml.Button2CI ick(Sender: TObject); begin OutOnLabel(255.6); end; procedure TForml.ButtonSCIick(Sender: TObject); begin OutOnLabel(Опять двадцать пять); end;
Дополнительные сведения о процедурах и функциях
6.6. Дополнительные сведения о процедурах и функциях Когда вы пытаетесь обратиться к процедуре, находящейся в другом модуле, но специально не объявляли его вручную, Delphi обязательно задаст вопрос: «Form 'FormГ references form 'Form2' declared in unit 'Unit2' which is not in your USES list. Do you wish to add it?» (рис. 6.2). В ответ на него нужно нажать кнопку Yes и запустить программу на выполнение снова. Если вы нажали Yes, то Delphi автоматически объявит форму, к которой происходит обращение, и больше этот вопрос задавать не будет. Разумеется, если вы хотите вызывать процедуру, принадлежащую одной форме (модулю), из другой формы (модуля), то ее нужно объявлять в разделе public, а не private, иначе другая форма не увидит этой процедуры. Можно вообще не объявлять процедуру или функцию ни в private, ни в public. Это неправильно, и так лучше не делать, однако работать все равно будет (в пределах одного модуля). Единственное «но» — процедура, которую вы вызываете, должна располагаться в тексте выше места вызова, иначе Delphi не сможет ее обнаружить. Пример не объявленной нигде процедуры и ее использования приведен в листинге 6.8. | File £d* Search УЮЧ
"
'
Project
Run Component
'
Qatabase Joo|s
II ! s S fii j * * О
ЩЫи» це|р I j jr
; Standard |миопа|| WMaj Sustemj Data Access I Data Controls j dbExpie») DataSnaol BDE i * '1 > ]
A.,[3T.g CD i* « Щ И ^ LJ fli LJ J3
: .' ;
--:-ШГ--|-Гч1г'Ч,
Ur*1
] Unit2 j
*• - Ч
J
; implementation
Fotm'Fomil'fefefencesfwmfofm? declared in unit UnitZ which is not in you
; Properties i I Г '! II ii if
j
:
• Щ:.:й!ШЬ-.'!->: :.:::.' . : .--.^ •••:•,•:. •• '•.••••.
Form2
Evert*
Action : АсйуеСогйо! | • '.Menu GbiectMenutiei OrAdivrrte ! DnCarftesize j OnCiick i
if s-'Иарт'
then begin nm :• 3; days :• 31; end;
Д™"П...."1!^",Ш i
31 •" 27
(ModSed ~
jinseif~
:
[Error] Unit1.pas{31): Undeclared identilier: WhalMonth' [Error] Unit1.pas(31):';' expected but 7 found [Error] Unill pas[34|: Undeclared identifier: V [Frrnrl I lniH nns(Fi1 ] I InrlprJwflH iHftntififtr' 'Fnrm?1
.; DnCloseQuefyi |[ ^QnComtfainedj -i OnCofii;r.'pnDeate I 'OnDWOdk^'"]"
K\9uM/~
i;AI«hown
И
Общих методов значительно меньше, Чем свойств и событий. Рассмотрим их:
. • SetFocus — устанавливает фокус клавиатуры на объект;
• Update — вызывает принудительную перерисовку объекта. Похожим образом работает Repaint; • Hide — скрывает объект. Свойству Visible присваивается значение False; • show — делает объект видимым. Свойству Visible присваивается значение True. Можно использовать для вызова другой формы. Разновидность вызова другой формы — метод showModal;
Глава 7. Свойства, методы и события • функция Focused — возвращает True, если фокус клавиатуры находится на проверяемом объекте; • функция CanFocus — возвращает True, если проверяемый объект видимый (Visible=True) и активный (Enabled=True). Эта функция присутствует не у всех компонентов, а только у тех, которые могут получать фокус клавиатуры (например, Label не может).
7.3. События Как уже не раз упоминалось, программирование в Windows —. это написание процедур и функций по обработке событий. Чаще других используются следующие события: • OnActivate — генерируется при активации объекта (например, при первом вызове формы). В обработке этого события можно производить различные инициализации (а также в обработке события OnCreate); • OnClick — генерируется, когда к объекту подводят указатель мыши и нажимают ее левую кнопку; • OnClose — происходит при закрытии формы. Здесь можно размещать код, сохраняющий данные перед закрытием формы; • OnCreate — генерируется при создании объекта; • OnDblClick — его порождает двойной щелчок мышью на объекте; • OnDeactivate — происходит при деактивации объекта; • On Destroy — создается при уничтожении объекта; • OnDockDrop — генерируется, когда один объект перетаскивают на другой и отпускают кнопку мыши. Оно может быть вызвано, только если DockSite=True; • OnDockOver — происходит, когда на объект попадает другой объект, переносимый мышью. Это событие генерируется для объекта-приемника и может быть вызвано, только если DockSite=True; • OnDragDrop — используется для определения момента, когда пользователь переместил объект; • OnDragOver — используется для перетаскивания и отпускания; • OnEndDock — генерируется, когда перетаскивание завершено и кнопка мыши отпущена; • OnHide — происходит, когда объект становится невидимым; • OnKeyDown — происходит при нажатии клавиши на клавиатуре. Отличается от On Key Press тем, что происходит, когда пользователь нажал клавишу (отпускать ее необязательно — событие OnKeyDown уже произошло). Процедуре обработки события сообщается числовой код клавиши (типа Word) и информация о нажатых клавишах Ctrl, Alt, Shift и кнопках мыши (тип TShif tstate); • OnKeyPress — генерируется при нажатии клавиши. Но, в отличие от OnKeyDown, происходит только тогда, когда пользователь нажал и отпустил клавишу. Пока
События он ее не отпустил, событие On Key Press не происходит. Сообщается код нажатой клавиши (типа char), а точнее — символ, который ей соответствует; OnKeyUp — похоже на OnKeyDown, но генерируется при отпускании нажатой клавиши. Процедуре обработки события сообщаются код клавиши (типа Word) и информация о нажатых клавишах Ctrl, Alt, Shift и кнопках мыши (тип TShif tstate); OnMouseDown — происходит при нажатии кнопки мыши (причем неважно, какой). Процедуре обработки события сообщается информация о нажатых кнопках мыши, о клавишах Ctrl, Alt, Shift и координатах указателя мыши. Событие происходит независимо от того, отпустил пользователь кнопку мыши или нет; OnMouseMove — происходит при перемещении указателя мыши над объектом. Сообщаются информация о клавишах Ctrl, Alt, Shift и координаты указателя мыши; OnMouseUp — аналогично OnMouseDown, но генерируется не тогда, когда пользователь нажал кнопку мыши, а в момент ее отпускания; OnMouseWheel — генерируется при прокрутке колеса мыши. Как правило, происходят сразу два события: OnMouseWheel (во всех случаях) вместе с OnMouseWheelDown или с OnMouseWheelUp; OnMouseWheelDown — генерируется, если пользователь прокручивает колесо мыши к себе; OnMouseWheelUp — генерируется, если пользователь прокручивает колесо мыши от себя; On Paint — происходит при перерисовке объекта; OnResize — происходит при изменении размеров объекта. Неважно, делает это пользователь, допустим, мышью или программист с помощью соседней процедуры, все равно генерируется событие OnResize. В его обработке (если оно относится к форме Form) удобно пересчитывать координаты и размеры компонентов; OnShow — происходит при отображении объекта на экране.
Глава 8
Работа с файлами Q О Q Q О Q
Текстовые файлы Типизированные файлы Нетипизированные файлы Удаление записей Обработка ошибок Процедуры и функции для работы с файлами
Глава 8. Работа с файлами Чтобы создать эффектный проект, не помешает освоить работу с файлами. Причем, например, не через драйверы баз данных, а просто записать что-либо куданибудь или прочитать. Delphi позволяет выполнять такие действия. Если не касаться нестандартных способов доступа к файлам и банальной работы с базами данных, то остаются три способа доступа к файлам в Delphi: текстовые, типизированные и нетипизированные файлы. Использование процедур и функций для работы с текстовыми, типизированными и нетипизированными файлами очень похоже, различия лишь в деталях. Для работы с файлом необходимо назначить ему файловую переменную, то есть ассоциировать ее с именем файла на диске. Через эту переменную будет выполняться вся работа с файлом. Переменная назначается с помощью процедуры AssignFile, которая имеет следующий синтаксис: procedure AssignFile(var F; FileName: String); Вызов AssignFile инициализирует файловую переменную F любого типа. После вызова данной процедуры F становится ассоциированной с внешним файлом FileName до того момента, пока он не будет закрыт. Если параметр FileName не задан, то AssignFile ассоциирует F со стандартным входным или выходным файлом. Нельзя использовать AssignFile с открытыми файлами. После вызова AssignFile файл можно создать или открыть для чтения и записи. Для создания файла используется процедура Rewrite: procedure R e w r i t e ( v a r F: File [; RecSize: Word ] ) ; После вызова данной процедуры создается файл с именем, ассоциированным с указанной файловой переменной (F). В случае использования Rewrite с нетипизированными файлами можно указать длину записи (RecSize). Если длина не задана, то она автоматически устанавливается равной 128 байт. Если файл с таким же именем, как у создаваемого файла, уже существует, то Rewrite удалит его и создаст новый. Обратите внимание, что процедура Rewrite удаляет существующий файл, не задавая никаких вопросов. Программист должен сам позаботиться о том, чтобы программа спрашивала у пользователя, удалить или оставить существующий файл. Если файл с таким же именем уже открыт, то он будет закрыт и создан заново. Если создаваемый файл текстовый, то после создания он будет открыт только в режиме записи. Все остальные типы файлов (нетекстовые) после вызова Rewrite доступны как для записи, так и для чтения. Если же файл не нужно создавать, то его можно открыть процедурой Reset: procedure Reset(var F [: File; RecSize: Word ] );
•
Процедура Reset открывает существующий файл. При этом текущая позиция чтения/записи будет в начале файла. Если файл уже открыт, то после вызова Reset он будет закрыт и открыт заново.
Глава 8. Работа с файлами Как и в случае с Rewrite, при использовании Reset с нетипизированными файлами можно указать длину записи. По умолчанию она равна 128 байт. После работы с файлом его необходимо закрыть. Пока файл не закрыт, другие программы не могут с ним работать. Операционная система автоматически закрывает все открытые файлы при выходе из программы. Но все-таки пользователи должны закрывать файлы сами. Для закрытия файла в Delphi предусмотрена процедура cioseFile: procedure CioseFile(var F ) ; Она прекращает связь файловой переменной с заданным в AssignFile именем файла и освобождает файл, то есть его смогут использовать другие программы.
8.1. Текстовые файлы Открытие файла как текстового еще не значит, что он обязательно содержит удобочитаемый текст, который пользователь может просмотреть в текстовом редакторе. Вполне можно с помощью доступа к файлу как к текстовому хранить в нем совсем не текстовую информацию. Все зависит от того, что мы хотим читать и записывать и как в данном случае удобнее это делать. Если рассматривать структуру текстового файла, то он содержит символы, организованные в строки. Символы же могут быть любыми (даже невидимыми при выводе на экран). Текстовые файлы открываются и закрываются так же, как и любые другие. Но процедуру Reset в текстовых файлах можно использовать лишь для их чтения. Для записи текстового файла его нужно открыть с помощью Rewrite (в этом случае создается новый файл) или использовать процедуру Append: procedure Append(var F: Text);
Она открывает текстовый файл (именно текстовый, с другими типами файлов процедура Append не работает) для дополнения, то есть новые строки текста будут добавляться в конец файла после существующих строк. Если открывать текстовый файл с помощью Rewrite, то он будет доступен для записи, но строки будут добавляться в начало файла. Рассмотрим примеры работы с текстовыми файлами.
Формирование текстового файла Создайте новый проект, поместите на форму кнопку и дважды щелкните на ней, чтобы попасть в окно редактора кода (рис. 8.1).
Текстовые файлы
£ie Ed* Search View fjrojecl £!"" Component Eataba» look Window Це)р ! i [my
шщ
"3 i €a Щ> I
tj US * 0 i i? & ^ & & \\ « fflAnchors ljakLeli.3kfop] ""BiDWode""'" Cared il^CaRtion. ^Constraints i Cutsw ; : :
iFalse iButtpnl i(TSi28Cpn IciDefautt
;i pragCutsor: ictDrag ii Diagkind jdkDrag ; DiagMpde idmManual" i ! ' Enabled |Тше i [Й Font [JTF'ontj IJAi'shown
iModilied::: :Olimeil"T;''r"i\CodeXPiagram/r
"" ft SI Kf
Рис. 8.1. Редактирование процедуры обработки события OnClick кнопки Buttonl Введите эту процедуру вместо пустой. Листинг 8.1
procedure TForm1.Button1CI ick(Sender: TObject);
var
F: TextFi le; begin AssignFi le(F,myf i le.txt); Rewrite(F); WriteLn(F,Привет! Это первая строка в текстовом файле); C l o s e F i le(F); end;
После запуска программы при нажатии кнопки будет создан файл m y f i l e . t x t в той же директории, где расположена запускаемая программа. В файл запишется строка текста, а затем он будет закрыт.
Добавление строк в файл Теперь можно попробовать добавить в файл строки. Сделаем для этого еще одну кнопку на форме (ButtonZ) и запишем в процедуру Button2Click такой код.
Глава 8. Работа с файлами Листинг 8.2
.
procedure TForm1.Button2CI ick(Sender: TObject); var F: TextFi le; begin AssignFi le(F,myfi le.txt); Append(F); WriteLn(F,Хлебом не корми, дай только строки добавлять!); C l o s e F i le(F); end;
Пока примеры маленькие и скучные. Когда рассмотрим большее количество процедур и функций, можно будет писать соответствующие программы.
Чтение файла И еще один пример работы с текстовыми файлами. На этот раз — чтение файла, в котором что-то написано. Поместите на форму еще одну кнопку (третью) и рядом с ней компонент Memol. В процедуру Buttons. Click введите код. Листинг 8.3
procedure TForm1.Button3CI ick(Sender: TObject); var F: TextFi le; s: String; begin AssignFi le(F,myfi le.txt); Reset(F); ReadLn(F.s); // читаем строку из файла в Memol Memol. Lines.Add(s); // добавляем строки в Memol C l o s e F i le(F); end;
Данная программа прочитает только первую строку из файла. Чтобы прочитать все строки, нужно изменить процедуру до такого вида. Листинг 8.4
procedure TForml.ButtonSCI ick(Sender: TObject); var F: TextFi le; s; String; begi n AssignFi le(F,myf i le.txt); Reset(F);
Текстовые файлы whi le not Eof(F) do begin ReadLn(F.s); // читаем строку из файла в Memo"! Memol. Li nes. Add(s); //добавляем строки в Memo!
end;
CloseFi le(F); end; Здесь можно наблюдать цикл while, условием выполнения которого является not Eof ( F ) , что в переводе на русский язык означает «не конец файла» (функция Eof (F) возвращает True, если достигнут конец файла). Пока не будет достигнут конец файла, цикл будет повторяться, читая по одной строке и добавляя ее в Memol (рис. 8.2,8.3).
Button! Button? Buttons
Рис. 8.2. Вид окна программы
Button! BultonZ "lultonT
Memol Привет! Это первая строка в текстовом Файле
Рис. 8.3. Вид окна программы в режиме выполнения
Глава 8. Работа с файлами Вообще можно за один раз прочитать текстовый файл в Memo. Для этого нужно написать всего лишь одну строку кода: Memol .Lines . LoadFromFile,( 'myf ile. txt') ;
Казалось бы, зачем читать файл по строке, если так проще? Когда необходимо прочитать текстовый файл целиком, тогда, конечно, этот простой способ подходит. Если же вам нужно выбирать по какому-либо критерию, одни строки читать, а другие пропускать, то как раз и пригодится метод чтения по одной строке. Модифицируем наш пример так, чтобы читались все строки, кроме начинающихся с символа «;». Листинг 8.5
procedure TForml. Button3CIick(Sender: TObject); var F: TextFi le;
s: String; , begin AssignFi le(F,myf i le.txt); Reset(F); whi le not Eof(F) do begin ReadLn(F.s); // читаем строку из файла в Memol if so then if s[1]=; then Continue; Memol. Lines. Add(s); // добавляем строки в Memol . end; CloseFile(F); end; После чтения строки из файла в переменную s проверяем: если первым символом строки s является точка с запятой (if s [ 1 ] = ';'), то даем команду перейти к следующей итерации цикла while (читаем следующую строку из файла). Проверка so' ' нужна, чтобы не возникла ошибка чтения файла, если вдруг встретится пустая строка. У пустой строки нет первого символа, который мы пытаемся проверить (s [ 1 ]). В текстовых файлах можно читать и записывать не только строки целиком, но - и отдельные символы. Если строки читаются/записываются с помощью ReadLn/ WriteLn, то отдельные символы — с помощью Read/Write.
8.2. Типизированные файлы Такие файлы я особенно люблю. Писал я как-то код игры и использовал типизированные файлы для удобства ее сохранения. Я создал тип Game (type Game=record), в котором хранил все данные, относящиеся к конкретной игре в текущий момент
Типизированные файлы и к определенному игроку. В общем, это все переменные, которые используются в течение игры и которые нужно сохранять и загружать. Так как все данные об игре хранятся в одной переменной типа Game (который я сам придумал), то для сохранения игры достаточно записать в SAV-файл всего лишь одну эту переменную. Ее размер, конечно, получался большой — десятки, а то и сотни килобайт, но удобство в том, что не нужно было заниматься подготовкой к сохранению, выискивая среди огромного количества те переменные, которые нужно сохранять. А потом заниматься этим же еще и при загрузке сохраненной игры. Все проще: раз — и сохранил игру в виде одной переменной, раз — и загрузил. Удобно, быстро и с минимальной вероятностью ошибиться. И все это благодаря типизированным файлам. Если использовать простейшие типизированные файлы, то тип объявлять не нужно. Например, работа с типизированным файлом типа integer выглядит так. Листинг 8.6 procedure TForml.ButtonlCIick(Sender: TObject); var F: F i l e of Integer; i, n: Integer; begin Randomize; AssignFi le(F,bredt.out); Rewrite(F); for i := 1 to 20 do begin n := Random(10); Write(F.n); end; CloseFi le(F); 5по\*/Меззаде(Файл вроде бы записан.); end; Данная процедура создает файл bredl. out, после чего генерирует 20 случайных чисел и записывает их в файл в нетекстовом виде. Естественно, при просмотре файла текстовым редактором получается ерунда. Обратите внимание на процедуру ShowMessage. Она выводит на экран окно с текстом. Это очень удобный и простой способ донести до пользователя какое-нибудь короткое сообщение. В таком виде типизированные файлы используются редко. Я не помню, чтобы мне хоть раз доводилось пользоваться ими без объявления своего собственного типа. Поэтому перейдем к использованию типизированных файлов с объявлением своего типа данных.
Глава 8. Работа с файлами Создадим новый проект (File > New > Application) и будем делать записную книгу, чтобы вносить в нее адреса друзей и знакомых (данный проект можно найти на компакт-диске, прилагаемом к книге, по адресу ExamplesXNotebook). Поместите на форму все необходимые компоненты: три этикетки (Label), три текстовых поля (Edit) и две кнопки (Button). Измените некоторые свойства формы Forml в соответствии с табл. 8.1. Таблица 8.1. Значения свойств формы Forml Свойство
Значение
Borderlcons biMaximize
False
BorderStyle ,
bsSingle
Caption
Записная книга
Position
poScreenCenter
Остальные свойства оставьте по умолчанию. В свойстве Caption компонентов Labell, Label2 и Labels укажите Фамилия, Год рождения и E-mail. Свойство Text компонентов Editl, Edit2 и Edits сделайте пустыми. Значением свойства Caption кнопки Buttonl должно быть «, а кнопки ButtonZ — ». В итоге должна получиться примерно такая форма, как показана на рис. 8.4. / Записн.чп книга
Фамилия : Год рождения ; E-mail
Рис. 8.4. Форма «Записная книга»
На всякий случай скажу, что мы изменяли и для чего. В свойстве Borderlcons формы можно установить такие параметры: отображать ли меню окна (и кнопку закрытия окна — крестик'в правом верхнем углу), разрешить ли сворачивание окна и разворачивание его во весь экран, показывать ли кнопку помощи (значок вопроса). Изменив значение свойства biMaximize на False, мы запретили разворачивание формы во весь экран, иначе она будет выглядеть не очень симпатично. BorderStyle — стиль формы. Стиль bsSingle означает, что пользователю нельзя изменять размеры формы. Caption — это текст заголовка формы, a Position указывает место, где форма будет появляться на экране (в данном случае в центре). Теперь можно писать код программы. Создадим новый тип данных, который назовем MyBook. Перейдите в окно редактирования текста программы (в его заголовке написано Unitl.pas), найдите раздел type и после слова end; добавьте новый тип данных.
Типизированные файлы Листинг 8.7 MyBook=record f i o : String[150]; year: Integer; emai I: String[150]; end; Откройте редактирование процедуры FormActivate формы Forml: щелкните на форме мышью, а затем в Object Inspector на вкладке Events дважды щелкните в пустом поле рядом со строкой события OnActivate. В редакторе текста программы откроется пустая процедура TForml.FormActivate. Она будет выполняться при активации формы, а активация происходит при запуске программы. Поэтому в начале работы нашего приложения будем проверять, существует ли файл с данными. Если его нет, то создадим пустой файл. Листинг 8.8
procedure TForml. FormAct ivate(Sender: TObject); var F: Fi le of MyBook; p: MyBook; begin // проверяем наличие файла if Fi leExists(book.dat)=False then begin AssignFi le(F,book.dat); Rewrite(F); p . f i o := ; p.year := 0; p . e m a i I := ; Write(F.p); C l o s e F i le(F); end; end; Если создается пустой файл, то в него добавляется одна пустая запись. Здесь была использована функция FileExists. Использовать ее несложно. Функции передается один параметр — имя файла. Она возвращает True, если файл с таким именем существует, и False, если его нет. Пусть сразу же после запуска программы из файла считывается первая запись и данные из нее выводятся на экран. Если файл только что создан, то одна запись все равно выведется — пустая. Для этого мы ее и создавали. Объявим глобальную переменную гее, в которой будет храниться номер текущей записи. Нужно объявить ее в разделе var выше всех процедур перед словом implementation.
Глава 8. Работа с файлами Листинг 8.9
var Form"!: TForml; гее: Integer;
implementation Добавим в процедуру FormActivate чтение первой записи.. Листинг 8.10
// читаем первую запись AssignFi l.e(F, book, dat); Reset(F); Read(F.p); CloseFi le(F); rec := 0; Здесь все просто. Открываем файл, читаем первую запись (то есть нулевую, так как в файлах нумерация записей начинается с нуля) и присваиваем переменной гее номер нулевой записи. Можете запустить получившуюся программу на выполнение (клавиша F9) и удостовериться, что файл book, dat появился в той же директории, где и программа. Напишем код вывода на экран данных из первой записи. Листинг 8.11
// выводим Edifl.Text Edit2.Text Edit3.Text
на экран данные из первой записи := p. f io; := IntToStr(p.year); := p.emai I;
Как видите, обращение к данным идет через точку. То, что мы указывали в описании типа, теперь доступно в переменной р типа MyBook, но через точку. В строке, где выводится на экран год (р. year), применена функция intToStr. Delphi не позволяет присваивать текстовым переменным числовые значения, и наоборот. Поэтому для того чтобы это сделать, нужно сначала преобразовать значения. Для преобразования из текста в число служит функция strToint, а для преобразования из числа в текст — IntToStr. Год у нас хранится в переменной year типа Integer. Чтобы вывести его как string (в текстовое поле), пришлось применить функциюIntToStr. Не забудьте, что две кнопки на форме внизу нужны, чтобы переходить от одной записи к Другой: левая позволяет листать записную книгу назад, а правая — вперед. А что делать, если листать некуда? Нужно отключить эти кнопки, изменив значение свойства Enabled кнопок Button с False на True. Мы это сделаем с помощью программирования — добавим в процедуру активации формы еще такие строки:
Типизированные файлы // включаем/отключаем кнопки if rec=0 then Buttonl.Enabled := False else Buttonl.Enabled := True;
Если запись и так нулевая, то отключаем кнопку листания назад. Если не нулевая (else), то включаем. Правую кнопку (листание вперед) отключать не будем — позже сделаем так, чтобы в случае обнаружения конца файла создавалась новая запись. Переменные для работы с файлами (F и р) сделаем глобальными, чтобы они были доступны каждой процедуре в нашей программе. В данном случае это оправдано, иначе их придется объявлять в каждой процедуре. Поэтому убираем F и р из раздела var процедуры FormActivate и записываем их рядом с объявлением переменной гее. Листинг 8.12
var Formt: TForml; rec: Integer; F: Fi le of MyBook; p: MyBook; Ч
'
'
:
;
• '
•
i mplementat ion
После всех добавлений у нас получилась такая процедура активации формы. Листинге. 13
procedure TForml. FormActivate (Sender: TObject); begin
// проверяем наличие файла
if Fi leExists(book.dat)=False then
begin
AssignFi le(F,book.dat); • Rewrite(F);
p. f io := ; p.year ;= 0; p.emai I := ; Write(F.p); CloseFi le(F);
end; // читаем первую запись AssignFi le(F,book.dat); Reset(F); Read(F.p); CloseFile(F);
rec := 0;
Глава 8. Работа с файлами
// выводим на экран данные из первой записи Edit"!.Text := p.fio; Edit2.Text := lntToStr(p. year); Edit3.Text := p.emai I ; // включаем/отключаем кнопки if rec=0 then Button"!. Enabled := F a l s e else Button"!.Enabled := True; end; Можете запустить приложение и посмотреть, что кнопка листания назад действительно стала неактивной. Теперь напишем код, который будет выполняться при нажатии кнопки листания вперед. В окне редактирования текста программы откройте процедуру click кнопки ButtonZ аналогично тому, как открывали процедуру активации формы. Для этого нужно выделить кнопку Button2 и в Object Inspector на вкладке Events дважды щелкнуть мышью на событии On Click. Можно также дважды щелкнуть на самой кнопке. Тогда сразу откроется окно редактирования текста программы с процедурой Button2Click. Первым делом при переходе к другой записи (при нажатии кнопки) нужно сохранять данные, которые есть на экране. Если пользователь изменил фамилию, год рождения или e-mail, то это автоматически запишется. Здесь нам и пригодится переменная гее, в которой хранится номер текущей записи (она в данный момент на экране). Пока у нас получилась такая процедура. Листинг 8.14
procedure TForml.Button2CIick(Sender: TObject); begin // записываем текущую информацию в файл p. f io := Editl.Text; p.year := StrTolnt(Edit2.Text); p.emai I := Edits.Text; AssignFi le(F,book.dat); Reset(F); Seek(F, rec); Write(F.p); C l o s e F i le(F); end; Приведенная в этом листинге процедура Seek устанавливает позицию чтения/ записи на нужную запись в файле. После чего эту запись можно прочитать или переписать. Процедура Seek не работает в текстовых файлах. Их можно читать и записывать только последовательно! Но сейчас мы, к счастью, работаем не с текстовым файлом, а с типизированным, поэтому и применили процедуру Seek.
Типизированные файлы Если номер текущей записи, например, 12, то Seek установит позицию чтения/записи на эту запись и ее можно будет переписать (с помощью Write) или прочитать (с помощью Read). Только будьте внимательны — записи в файлах De.lphi всегда нумеруются с нуля. Если с помощью Seek неправильно задать позицию чтения/записи, то можно переписать совсем не те записи! Но мы будем делать это правильно — зачем нам лишние проблемы? Итак, в листинге 8.14 заполняется переменная р (используя данные, содержащиеся в текстовых полях), открывается файл с данными нашей записной книги, устанавливается позиция чтения/записи, переписывается запись, и файл закрывается. Теперь нужно сделать возможным переход к следующей записи. Параллельно придется создать новую запись, так как все это привязано к одной кнопке. %
Процедура Button2Click стала такой. Листинг 8.15
procedure TForml. Button2CI ick(Sender: TObject); begin // записываем текущую информацию в файл p. f io := Edit"!.Text; p.year := StrTolnt(Edit2.Text);
. - . . . „ -r p.emai I := Edits.Text; • AssignFi le(F, book.dat); Reset(F); Seek(F, rec); Write(F.p); CloseFi le(F); // переходим к следующей записи Inc(rec); // увеличиваем номер записи на единицу AssignFi le(F,book.dat); Reset(F); if Fi leSize(F)=rec then // обнаружен конец файла, создаем новую запись begin Seek(F, rec); p. f io := ; p.year : = 0; p.emai I := ; Write(F.p); end; // читаем запись Seek(F, rec); Read(F,p); CloseFi le(F);
4 Зак. 899
Глава 8. Работа с файлами // выводим Edit"!.Text Edit2.Text Edit3.Text end;
данные на экран := p.f io; := IntToStr(p.year); := p.emai I;
Таким образом, при нажатии кнопки данные записываются в файл, а затем переменная гее увеличивается на единицу. После чего проверяем, совпадает ли номер записи с размером файла (а так как нумерация с нуля, то в случае совпадения одной записи не хватает — создаем ее). Размер файла определяется функцией FileSize, которую мы и использовали. Наконец, читаем запись и выводим ее на экран. Запустите приложение, чтобы посмотреть, что получилось. Можно вводить данные и щелкать мышью на кнопке листания вправо. В общем, работает, но есть много неудобств. Например, не показывается номер текущей записи. Из-за этого, если ничего не вводить, непонятно, листается вообще записная книга или нет. И если закрыть программу, то последняя введенная запись не сохраняется (ведь мы задали сохранение записи только при нажатии кнопки ButtonZ). Да и левая кнопка все время отключена. Нужно исправить все описанные недочеты. Сначала добавим на форму этикетку (Label), с помощью которой будет выводиться номер текущей записи. Помещаем этикетку Label4 между кнопками (рис. 8.5). * Записная книга
Фамилия Год рождения E-mail '
Рис. 8.5. Добавление этикетки LabeW между кнопками
В свойстве Caption этикетки Label4 ничего не пишем, а оставляем его пустым. Нужно изменить значение свойства AutoSize этикетки Label4 на False, а то она так и норовит изменить свой размер в соответствии с введенным в нее текстом, чего мы совсем не хотим. Нам нужно, чтобы она находилась там, куда ее поместили! И еще, изменим значение свойства A l i g n m e n t той же этикетки на taCenter, чтобы текст на ней располагался по центру. Добавим в конец процедуры FormActivate следующую строку: L a b e l 4 . C a p t i o n := I n t T o S t r ( r e c ) ;
Такую же строку введем в конце процедуры Button2Click. Запустите программу и посмотрите на получившейся результат. Теперь все намного удобнее, чем было. Стало видно, какая по порядку запись сейчас на экране (рис. 8.6).
Типизированные файлы
Рис. 8.6. Программа в режиме выполнения — при нулевой записи левая кнопка неактивна (Enabled=False)
Включим левую кнопку (Buttonl), добавив в конец процедуры Button2Click еще одну строку: if rec>0 then Buttonl.Enabled := True; Теперь стало еще лучше. Настало время написать код для Buttonl. Здесь все очень просто. Нужно лишь сохранить запись, уменьшить гее на единицу, прочитать данные с нужной позиции в файле, вывести их на экран, а также в конце процедуры отключить Buttonl (если запись нулевая) и вывести номер записи. В общем, необходимо сделать практически все то же, что и в случае с ButtonZ. Пишем похожую процедуру. Листинг 8.16
procedure TForml.ButtonlCI ick(Sender: TObject); begin // записываем текущую информацию в файл p. f io := Edit"!.Text; p.year := StrTolnt(Edit2.Text); p.emai I := Edits.Text; AssignFi le(F,book.dat); Reset(F); Seek(F, rec); Write(F.p); CloseFi le(F); // переходим к предыдущей записи Dec(rec); // уменьшаем номер записи на единицу // читаем запись AssignFi le(F,book.dat); Reset(F); Seek(F, rec); Read(F.p); CloseFi le(F); // выводим данные на экран Ed itl.Text := p. fio; Edit2.Text := IntToStr(p.year); Edit3.Text := p.emai I ; // выводим номер текущей записи
Глава 8. Работа с файлами
Label4.Caption := IntToStr(rec); // проверяем номер записи и, возможно, отключаем // 'левую кнопку if rec=0 then Button"!. Enabled := False; end; Запустив программу, вы убедитесь, что сейчас можно листать записную книгу вперед и назад и все записи сохраняются автоматически. Добавим еще сохранение последней введенной записи. Это лучше всего сделать при закрытии окна программы. Таким образом, напишем процедуру обработки события OnClose формы Forml. Аналогично созданию предыдущих процедур (с помощью Object Inspector) в редакторе текста программы открываем процедуру Formclose и вводим в нее такой код (можно скопировать его из процедуры Buttoniciick или Button2Click). Листинг 8.17 procedure TForm"!. FormClose(Sender: TObject; var Action: TCIoseAct ion); begin // записываем текущую информацию в файл p. f io := Edit"!.Text; p.year := StrTol nt(Edit2.Text); p.emai I := Edits.Text; AssignFi le(F,book.dat); Reset(F); Seek(F, rec); W r i t e ( F , p); C l o s e F i le(F); end;
Теперь при выходе из программы выполнится процедура Formclose, и последняя запись автоматически сохранится. Все, задуманный проект готов (рис. 8.7)! Получилась полноценная функционирующая, хоть и простая, записная книга, которую мы создали, используя типизированные файлы. Наша программа работает быстро и занимает мало места.
Фамилия Год рождения Е-тай
}Толян •'•;
[1961 |нет
Рис. 8.7. Получившаяся записная книга Данную программу можно улучшить. Вероятно, вы заметили, что в нескольких местах программы повторяется один и тот же код (в разных процедурах). Такой под-
Нетипизированные файлы ход неправильный. Повторяющиеся фрагменты кода лучше формировать в виде отдельной процедуры и просто вызывать ее нужное количество раз. Но об этом поговорим в гл. 9. Там же приведен полный окончательный текст нашей программы, хотя, если вы все сделали правильно, он у вас уже есть.
8.3. Нетипизированные файлы Данные файлы похожи на типизированные, но отличаются тем, что тип не указывается. Данные читаются целыми частями — по несколько сот или тысяч байт. Это, пожалуй, самый быстрый доступ к файлам. Но разбираться в этих данных придется вручную. Такого автоматического разбора по переменным, как в типизированном файле, здесь нет. Для работы с нетипизированными файлами в Delphi предусмотрены процедуры BlockRead и BlockWrite. Они используются почти так же, как Read и Write, за исключением того, что указывается не переменная, а буфер (массив) и количество записей, которые нужно прочитать или записать. Не совсем понятно? Тогда, чтобы разобраться, нужно привести пример (его можно найти на компакт-диске, прилагаемом к книге, по адресу Examples\FilesReadNWrite). Создадим новый проект. Красоту наводить не будем — в данный момент это ни к чему. Просто поместим на форму две кнопки (Buttonl и Button2) и назовем их Запись и Чтение соответственно (рис. 8.8). В процедуре Buttoniciick будем записывать нетипизированный файл, а в процедуре Button2ciick — его читать.
Рис. 8.8. Форма с двумя кнопками Листинг 8.18
procedure TForml.ButtonlCI ick(Sender: TObject); var BUF: array [1. .100000] of Byte; F: Fi le; begin AssignFile(F,block.dat); Rewrite(F,512); Bloc kWrite(F,BUF, 100); CloseFi le(F); end;
Глава 8. Работа с файлами Таким образом записывается нетипизированный файл. Файловая переменная объявляется так же, как для работы с типизированным файлом, только не указывается тип: F: File;
Открывается файл тоже аналогично. Только в процедурах Rewrite или Reset можно задать длину записи в байтах (в данном примере — 512). Если длину не указать, то она устанавливается равной 128 байт. Затем данные записываются в файл с помощью процедуры BlockWrite. В качестве параметров мы указали файловую переменную F, буфер BUF (массив, из, которого записываются данные) и количество записей (100). Имейте в виду, что если длина записи составляет 512 байт, а в BlockWrite задано 100 записей, то реально на диск из массива записывается 51 200 байт, то есть эти два параметра умножаются друг на друга. Листинг 8.19 procedure TForml. Button2CI ick(Sender: TObject); var BUF: array [1.. 100000] of Byte; F: Fi le; begin AssignFi le(F,block.dat); Reset(F,512); • BlockRead(F,BUF,100); C l o s e F i le(F); end; В приведенном листинге показано, как происходит чтение нетипизированного файла в массив. Здесь все точно так же, как при записи файла, только вместо BlockWrite используется BlockRead. В общем, использовать нетипизированные файлы несложно. Они очень полезны, когда важна скорость работы.
8.4. Удаление записей В Delphi нет готовой процедуры для удаления записей из файла. Поэтому приходится придумывать способы сделать это. Я обычно использую следующий метод. Чтобы удалить запись из файла, нужно открыть еще один файл и скопировать в него по порядку все записи из первого файла, за исключением удаляемой. Затем файлы закрывают, и второй файл переименовывают, чтобы его имя стало как у первого. В следующем примере из файла out. dat удаляется пятая запись.
Удаление записей Листинг 8.20
procedure TForml.Button2CI ick(Sender: TObject); var F1: Fi le of Integer; // файл, из которого удаляется запись р: Integer; F2: Fi le of Integer; // временный файл I : Integer; begin // открываем файл, из которого удаляется запись AssignFile(F1,out.dat); Reset(F1); // создаем временный файл AssignFi le(F2,temp); Rewrite(F2); i := -1; // счетчик записей // читаем каждую запись в цикле whi le not Eof(F1) do begin Read(F1,p); Inc(i); // увеличиваем счетчик записей // если запись не пятая, то выносим ее во временный файл if i5 then Write(F2,p); end;
,
// закрываем файлы CloseFile(F2); C l o s e F i le(F1); // удаляем основной файл Erase(F1); // переименовываем временный файл в основной Rename(F2,out.dat); end;
Разумеется, можно удалять запись не только по номеру, но и по любым другим параметрам (например, по ее содержимому). Можно усовершенствовать алгоритм, используя процедуру Truncate, которая удаляет все записи, начиная с текущей. Данный метод иногда медленно работает с очень большими файлами. Поэтому если размер файла, из которого нужно удалять запись, очень большой, то имеет смысл применять другой метод, который даже проще первого. Он заключается в том, что мы не удаляем запись физически, а просто помечаем ее как удаленную. Конечно, это получится только в случае использования типизированных файлов. При этом размер самого файла не будет изменяться. Например, имеем типизированный файл такого типа.
Глава 8. Работа с файлами Листинг 8.21
DelFi le=record рг: Boolean; fio: String[200]; n: Integer; end; Для обозначения того, удалена запись или нет, создано поле рг типа Boolean. При создании новой записи рг присваивается значение False. Это означает, что запись не удалена. Если ее нужно удалить, то рг просто присваивается True. Естественно, при работе с файлом нужно учитывать поле рг (если pr=True, то считать запись несуществующей). Остальные поля записи используются как обычно. В процессе работы записи просто помечаются как удаленные, а затем, по мере их накопления, выполняется какое-то действие (сжатие, индексация), вместе с которым уничтожаются помеченные записи. Кстати, именно так и удаляются записи практически во всех СУБД (системах управления базами данных). Положительная сторона данного метода еще и в том, что помеченные, но еще не удаленные окончательно записи можно восстановить! Такой метод удаления записей работает очень быстро независимо от размера файлов. Но это не совсем удаление, поэтому лучше использовать комбинацию этих двух методов.
8.5. Обработка ошибок При использовании файлов ошибки могут возникать чаще, чем при каких-либо других действиях: пользователь удалил файлы, файлы не читаются, отсутствует гибкий диск или компакт-диск в приводе и т. д. Поэтому важно правильно обработать такие ошибки. В принципе если даже не писать никакого кода для обработки ошибок, то Delphi вполне приемлемо сделает это самостоятельно. Это значит, что программа не закроется, прекратив выполнение, а просто выдаст сообщение об ошибке. Но можно сделать программу более дружелюбной по отношению к пользователю. Для обработки ошибок при работе с файлами существует функция lOResult, которая возвращает код ошибки последней операции с файлами. Если ошибка не произошла, то возвращается нуль. При использовании lOResult нужно отключать встроенный обработчик ошибок ввода-вывода. В этом деле поможет директива ($!-}. Соответственно, после вызова lOResult встроенный обработчик ошибок неплохо бы включать. Для этого существует директива {$! + }. Ошибки при работе с файлом обрабатываются следующим образом.
Обработка ошибок Листинг 8.22 procedure TForml.ButtonlCI ick(Sender: TObject); var F: F i l e ; begin
AssignFi le(F,ourfi le.dat);
{$(-} Reset(F);
{$!+} if lOResultoO then begin ShowMessage(VBbi и ах, файл открыть не удалось. ..); Exit;
end; {здесь находится блок работы с файлом} C-loseFi le(F); end;
Результат выполнения приведенной программы показан на рис. 8.9. Соответственно, таким же образом обрабатывать события можно не только при открытии файла (Rewrite, Reset), но также при его чтении и записи.
. ^ f" Ш • S3 i Pj &
.
,.
JAddKondj Win32) Swslemj Data Access! Dala Conttds j dbExaess j DataSnaoi BDE |-llt
!
V % A PET i| sj i» e ШШ^и Ш U iffi
1ШШ
ОэШ Запись
'^
Чтение
Увы и ах. Файл открыть не удалось...
скрыть не у д а л о с ь . . . ' ) ;
If "ТЯГ—!|
1
44: 1
JModfied
jinsert
|War™ng|Unil1.pas(52): Unsafe code tlockRead'
И IF Рис. 8.9. Корректная обработка ошибки
j \Code/Diaaiam/
3
Глава 8. Работа с файлами
8.6. Процедуры и функции для работы с файлами Рассмотрим их в алфавитном порядке: •
Append — открывает существующий текстовый файл в режиме добавления. Новые строки записываются в конец файла. Работает только для текстовых файлов;
• AssignFile — ассоциирует имя внешнего файла с файловой переменной. Эта процедура всегда должна предшествовать любым другим действиям с файлами; • BlockRead — читает одну или более записей из нетипизированного файла в массив; • BlockWrite — вносит одну или более записей из массива в нетипизированный файл; • Choi r — изменяет текущую директорию. По умолчанию директория для чтения или записи совпадает с той, откуда запускалась программа (ЕХЕ-файл); •
CloseFile — закрывает открытый файл. Если после вызова данной процедуры нужно выполнять какие-либо действия с файлом, то необходимо вызвать AssignFile;
• Eof — возвращает True, если обнаружен конец файла. В противном случае возвращает False; • Eoln — возвращает True, если обнаружен конец строки в текстовом файле. В противном случае возвращает False; • Erase — удаляет файл; •
FilePos — возвращает текущую позицию чтения/записи. Нумерация начинается с нуля;
• FileSize — возвращает размер файла. Эта функция не работает с текстовыми файлами; •
Flush — записывает буфер на диск при выводе текста в текстовый файл;
• Getoir — возвращает текущую директорию на выбранном диске; • lOResult — возвращает код ошибки последней операции с файлом (целое значение). Если операция была завершена успешно, то данная функция возвращает нуль; •
MkDir — создает директорию;
• Read — читает запись из файла. При использовании с текстовым файлом читает один символ, с нетипизированным — может читать несколько записей; • ReadLn — читает строку из текстового файла; • Rename — переименовывает файл; • Reset — открывает файл для чтения и записи;
Процедуры и функции для работы с файлами • Rewrite — создает и открывает новый файл; • RmDir — удаляет пустую директорию; • Seek — перемещает позицию чтения/записи в файле. Не используется с текстовыми файлами; • SeekEof — пропускает все пробелы, знаки табуляции и маркеры конца строки Eoln др маркера конца файла Eof или до первого значащего символа и возвращает True, если маркер обнаружен. Работает исключительно с текстовыми файлами; • SeekEoln — пропускает все пробелы и знаки табуляции до маркера конца строки Eoln или до первого значащего символа и возвращает True, если маркер обнаружен. Работает исключительно с текстовыми файлами; •
SetTextBuf — ассоциирует буфер ввода/вывода с текстовым файлом;
• Truncate — удаляет все записи в типизированном или нетипизированном файле, начиная с текущей позиции; • W r i t e — помещает запись в файл. При использовании с текстовым файлом записывает один символ, с нетипизированным — может вносить несколько записей; •
WriteLn — записывает строку в текстовый файл.
Глава 9
Техника программирования Q Оптимизация программы О Комментарии Q Точный подбор цвета
Оптимизация программы
9.1. Оптимизация программы Когда программист пишет текст программы, он обычно работает «на два фронта». Дело в том, что программу читает не столько пользователь, сколько компьютер. Нужно оптимизировать программу для компьютера, то есть писать ее так, чтобы она работала быстро и, по возможности, без ошибок, а также занимала меньше памяти. Делать же так, как показано в следующем листинге, не рекомендуется. Листинг 9.1
А[2] А[3] А[4] А[5]
:= := := :=
В[2]; В[3]; В[4]; В[5];
Пример хоть и абсурдный, но взят из практики. Естественно, в данном случае обязательно нужно использовать цикл. Листинг 9. 2
for i := 1 to 5 do
Когда компилятор преобразовывает текст программы в машинный код, циклы тоже организуются. В машинном коде это возможно, хотя не так явно и удобно, как предусмотрено в языке высокого уровня Delphi. Поэтому, как правило, если текст программы меньше, то и получаемый машинный код получается компактнее. Подчеркиваю, так бывает не всегда, но циклы целесообразно использовать, если однообразное действие повторяется более двух-трех раз. Иногда похожие и повторяющиеся действия удобно организовать в виде процедуры или функции. И потом вызывать ее, когда необходимо выполнить эти действия. Увеличивать переменную на единицу лучше не так: n := n + 1;
а следующим образом: Inc ( n ) ;
Соответственно, при уменьшении переменной на единицу вместо n : = n - 1 лучше применять Dec (n) . Операции Inc и Dec выполняются быстрее присваивания и занимают меньше места в памяти. В качестве объекта оптимизации можно использовать программу «Записная книга» (рис. 9.1), которую мы создавали в разд. 8.2. Рассмотрим текст этой программы и выясним, как ее можно оптимизировать.
Глава 9. Техника программирования Записная книга
Фамилия
j Майк
Год рождения E-mafl
:
[email protected] Рис. 9.1. Записная книга, созданная ранее
'
Стандартное начало всех листингов, которое Delphi генерирует автоматически (unit, uses, класс формы), здесь и далее приводить не буду, чтобы не давать лишнюю информацию. Если вам нужны полные тексты программ, то можете найти их на компакт-диске, который прилагается к книге. Листинг 9.3
type MyBook=record f i o : String[150]; year: Integer; emai I: String[150]; end; var
Form"!: TForml; rec: Integer; F: Fi le of MyBook; p: MyBook; implementat ion {$R *.dfm} procedure TForml.FormActivate(Sender: TObject); begin // проверяем наличие файла if Fi leExists(book. dat) = False then begin AssignFi le(F,book.dat); Rewrite(F); p.fio := ; p. year := 0; p.emai I := ; Write(F, p); CloseFi le(F); end; // читаем первую запись AssignFi le(F,book.dat);
Оптимизация программы
Reset(F); Read(F.p); CloseFi le(F); rec := 0; // выводим на экран данные из первой записи Edit"!.Text := p. f io; Edit2.Text := IntToStr(p.year); Edit3.Text := p.emai I ; // включаем/отключаем кнопки if rec=0 then Button"!. Enabled := False else Button"!. Enabled := True; Label4.Caption := IntToStr(rec); end; procedure TForm1.Button2CIick(Sender: TObject); begin // записываем текущую информацию в файл p.fio := Editl.Text; p.year := StrTolnt(Edit2.Text); p.emai I := Edits.Text; AssignFi le(F,book.dat); Reset(F); Seek(F, rec); Write(F.p); CloseFile(F); // переходим к следующей записи Inc(rec); // увеличиваем номер записи на единицу AssignFi le(F, book.dat); Reset(F); if FileSize(F)=rec then // обнаружен конец файла, создаем новую запись begin Seek(F, rec); p.fio ;= ; p. year := 0; p.ema i I : = ; Write(F.p); end; // читаем запись Seek(F, rec); Read(F, p); CloseFile(F); // выводим данные на экран Editl.Text := p.fio; Edit2.Text := IntToStr(p.year); Ed its.Text := p.emai I ; Label4.Caption := IntToStr(rec);
Глава 9. Техника программирования if гес>0 then Button!.Enabled := True; end; procedure TForml.ButtonlCI ick(Sender: TObject); beg i n
// записываем текущую информацию в файл p. f io := Edit"!.Text; p.year := StrTolnt(Edit2.Text); p.emai I := Edits.Text; AssignFi le(F,book.dat); Reset(F); Seek(F, rec); Write(F.p); CloseFi le(F); // переходим к предыдущей записи Dec(rec); // уменьшаем номер записи на единицу // читаем запись AssignFi ie(F,book.dat); Reset(F); Seek(F, rec); Read(F.p); CloseFi le(F); // выводим данные на экран Ed itl.Text := p.fio; Edit2.Text := lntToStr(p. year); Ed it3.Text := p. emai I ; // выводим номер текущей записи Label4.Caption := IntToStr(rec); // проверяем номер записи и, возможно, отключаем // левую кнопку if rec=0 then Button"!. Enabled := False; end; procedure TForml. FormClose(Sender: TObject; var A c t i o n : T C I o s e A c t ion); begin
// записываем текущую информацию в файл p.fio := Edit"!.Text; p.year := StrTolnt(Edit2.Text); p.emai I := Edits.Text; AssignFi le(F,book.dat); Reset(F); Seek(F, rec); Write(F.p); CloseFi le(F); end;
end.
Оптимизация программы Обратите внимание, что текущая информация записывается в файл в нескольких процедурах. Причем фрагменты программы, выполняющие запись в файл, совершенно одинаковые! Писать программу подобным образом нерационально. Нужно выделить данные фрагменты в отдельную процедуру, а потом вызвать ее нужное количество раз. Это уменьшит размер получаемого ЕХЕ-файла и повысит удобочитаемость текста программы. Создаем процедуру TForml .WriteToFile. Листинг 9.4 procedure TForml.WriteToFi le; // записываем текущую информацию в файл begin p. f i o := Edit"!. Text; p.year := StrTolnt(Edit2.Text); p.emai I := EditS.Text; AssignFi le(F,book.dat); Reset(F); Seek(F, rec); Write(F.p); C l o s e F i le(F); end;
В процедурах Button2Click, Buttoniciick и FormClose заменяем приведенный в листинге 9.4 фрагмент на вызов процедуры WriteToFile. Программа сразу стала менее громоздкой! Не будем останавливаться на этом — оптимизировать так оптимизировать! Ведь не только запись в файл, но и чтение выполняется у нас из разных процедур. Чтение записи и вывод на экран присутствуют в процедурах FormActivate, ButtonlClick И Button2Click. Выделяем чтение из файла тоже в отдельную процедуру. Листинг 9.5 procedure TForml.ReadFromFi le; // читаем запись и выводим ее на экран
beg in // читаем запись AssjgnFi le(F, book.dat); Reset(F); Seek(F, rec); Read(F.p); CloseFi le(F); // выводим данные на экран Edit"!. Text := p. fio; Edit2.Text := IntToStr(p.year); Edit3.Text := p.emai I ;
end;
Глава 9. Техника программирования В процедурах Buttoniciick и Button2ciick заменяем приведенный выше фрагмент вызовом процедуры ReadFromFile. А в FormActivate придется заменить следующие строки. Листинг 9.6 // читаем первую запись AssignFi le(F, book.dat); Reset(F); Read(F.p); C l o s e F i le(F); rec := 0;
// выводим на экран данные из первой записи Edit"!.Text := p . f i o ; Edit2.Text := IntToStr(p.year); Edit3.Text := p.emai I; Заменяем их таким фрагментом программы. Листинг 9.7 гее := 0;
ReadFromFi le; Аналогично чтению и записи выделяем в отдельную процедуру создание новой записи. Новая запись создается только в двух процедурах программы. Но все равно организация отдельной процедуры оправдана, так как в данном случае код короче. К тому же повышается удобочитаемость текста программы пользователем. И если придется что-то изменять, то проще сделать это в одной отдельной процедуре, чем вносить одинаковые изменения в несколько фрагментов кода. Пишем процедуру создания новой записи. Листинг 9.8 procedure TForml.NewRec; // создаем новую запись begin Seek(F, rec); p. f io := ;
p. year : = 0; p.emai I : = ; Write(F.p); CloseFi le(F); end;
В данной процедуре пропущены функции открытия файла. Это сделано потому, что процедура NewRec вызывается из Button2ciick, где файл открывается с помощью Reset, а также из Activate For m, где файл создается при помощи Rewrite. Поэтому файл открывается до вызова процедуры NewRec.
Оптимизация программы Кстати, после того как в процедуре Button2Click вместо большого фрагмента программы появилась всего одна строка — вызов процедуры NewRec (листинг 9.9), — отпала необходимость в begin и end (листинг 9.10). Листинг 9.9 if Fi leSize(F)=rec then // обнаружен конец файла, создаем новую запись begin NewRec; end;
Листинг 9.10 if Fi leSize(F)=rec then // обнаружен конец файла, создаем новую запись NewRec; После проведенной нами работы возможностей для оптимизации программы практически не осталось. Может быть, можно еще что-то улучшить, но это уже не столь очевидно, как выполненные действия. Кстати говоря, неплохо бы изначально продумывать написание программы, чтобы потом не нужно было ее оптимизировать. Осталось привести полный текст программы после оптимизации. Программа стала короче, при этом понятнее и проще для модернизации, а действия выполняет те же. Листинг 9.11 type MyBook=record fio: String[150]; year: Integer; emai I : S t r i n g [ 1 5 0 ] ; end; var
Form"!: TForml; rec; Integer; F: Fi le of MyBook; p: MyBook; implementation {$R *.dfm} procedure TForml. FormAct ivate(Sender: TObject); begin // проверяем наличие файла
i
Глава 9. Техника программирования if Fi leExists(book.dat)=False then begin
AssignFile(F,book.dat); Rewrite(F); NewRec; // создаем новую запись end; гее := 0; ReadFromFi le; // включаем/отключаем кнопки if rec=0 then Button"!. Enabled := False else Buttonl.Enabled := True; Label4.Caption := IntToStr(rec); end; procedure TForm1.Button2CI ick(Sender: TObject); begin WriteToFi le; // записываем текущую информацию в файл Inc(rec); AssignFi le(F, book, dat); Reset(F); if Fi leSize(F)=rec then // обнаружен конец файла, создаем новую запись NewRec; ReadFromFi le; // читаем запись Label4.Caption ;= lntToStr(rec); if rec>0 then Buttonl.Enabled := True; end; procedure TForml.ButtontCI ick(Sender: TObject); begin WriteToFi le; // записываем текущую информацию в файл Dec(rec); ReadFromFi le; // читаем запись Label4.Caption := IntToStr(rec); // выводим номер записи // проверяем номер записи и, возможно, отключаем // левую кнопку if rec=0 then Buttonl.Enabled := False; end; procedure TForml. FormClose(Sender: TObject; var Action: TCIoseAction); begin
WriteToFi le; // записываем текущую информацию в файл end; procedure TForml.WriteToFi le; // записываем текущую информацию в файл beg in
Оптимизация программы p.f io := Edifl .Text; p.year := StrTolnt(Edit2.Text); p.emai I := Edit3.Text; AssignFi le(F,book.dat); Reset(F); Seek(F, rec); Wrtte(F.p); C l o s e F i le(F); end; procedure TForml.ReadFromFi le; // читаем запись и выводим ее на экран begin A s s i g n F i le(F, book.dat); Reset(F); Seek(F, rec); Read(F,p); C l o s e F i le(F); Edit"!.Text := p. f io; Edit2.Text := I ntToStr(p.year); Edit3.Text ;= p.emai I; end; procedure TForml.NewRec; // создаем новую запись begin Seek(F, rec); p.fio : = ; p. year := 0;
p . e m a i I := ; Write(F.p); C l o s e F i le(F);
,
end;
При создании программы не стоит забывать, что пользователи тоже будут ее читать. Если вы работаете в команде и участвуете в разработке какого-либо проекта, то ваш код точно будут читать другие пользователи. Если же, например, вы самостоятельно пишете программу на Delphi, то все равно через некоторое время будете ее просматривать. Да и непосредственно при написании программы проще, если все оформлено красиво и легко читается. Чтобы программа проще воспринималась, нужно делать отступы от левого поля. Но не как попало, а выделять отступами тело цикла, составные операторы beginend. Для повышения удобочитаемости можно также отделять оператор присваивания с двух сторон пробелами (а : = 5). Специально иллюстрировать отступы и пробелы незачем — вы можете увидеть их практически в любом листинге этой книги.
Глава 9. Техника программирования Некоторые программисты не особо задумываются над названиями переменных. Они так сосредоточены на написании программы, что для придумывания названий переменных у них не остается ни сил, ни энергии, ни фантазии. В таком случае есть один способ, помогающий создать уникальное имя переменной, нисколько не думая. Просто наберите на клавиатуре несколько любых символов. Получится что-то вроде sdhgf d — вполне подходит в качестве имени переменной. Однако не спешите радоваться! Сейчас я огорчу тех читателей, кому данный способ понравился. Переменные нужно называть так, чтобы их имена напоминали о том, для чего предназначена каждая переменная. Допустим, переменную, которая используется для суммирования, нужно назвать total, summa или sum. Тогда текст программы будет проще воспринимать. Легче будет и вспомнить, для чего нужна та или иная переменная. Таким образом, включить воображение все равно придется.
9.2. Комментарии Когда я только постигал азы программирования, то узнал, что желательно писать в тексте программы комментарии. Естественно, я отнесся к этому довольно скептически, не понимая, зачем нужны комментарии. Над программой я работаю один, следовательно, знаю и помню, что. в ней написал. Однако прошло некоторое время, и наступил момент, когда пришлось создавать новую версию какой-то программы. Тогда я и пожалел, что не пояснил комментариями фрагменты кода. Я многое забыл, и, чтобы внести изменения в программу, сначала пришлось долго в ней разбираться. С тех пор я снабжаю свои программы комментариями. И делаю это даже тогда, когда уверен, что не буду переписывать программу. Когда уверен, что не будешь вносить изменения в программу, — непременно придется это сделать! Но я вам не буду советовать писать комментарии в тексте программы. Только когда вы сами однажды обнаружите, что ничего не понимаете в программе, которую сами же написали несколько месяцев назад, тогда комментарии в программе вам будут просто необходимы! И вы будете писать их с удовольствием! Как уже говорилось в разд. 3.3, для добавления комментариев в Delphi можно использовать две косые черты //, а также фигурные скобки { ) . Короткий комментарий (строка или часть строки) проще оформить с помощью косых черточек, а длинный (несколько строк) — поместить в фигурные скобки. Комментариев в тексте программы не должно быть слишком мало или много, а ровно столько, сколько необходимо для понимания того, что выполняется в комментируемом фрагменте кода, для чего нужна та или иная переменная. Писать длинные пояснения не нужно. Рассмотрим пример программы с комментариями, необходимыми для ее понимания.
Точный подбор цвета Листинг 9.12 procedure TForm1.MakeMoney(m: Integer); 1 // создание денег с помощью чит-кода // т- количество добавляемых денег begin if lnputCode=False then Exit; // запрос чит-кода Randomize; m := Random(IOOOO); A.money := A. money + m;
;
end;
Как видите, комментариев немного, но все фрагменты, по которым могут возникнуть вопросы, снабжены комментариями. Теперь приведу пример неправильного употребления комментариев в той же процедуре. Листинг 9.13 procedure TForm1.MakeMoney(m: Integer); // процедура для создания денег чит-кодом // т- количество добавляемых денег begin if InputCode-False then Exit; {если введенный пользователем код неправильный, то выполняется выход из процедуры} Randomize; // инициализация генератора случайных чисел m := Random(IOOOO); // генерирование денег (случайное число) A.money := A.money + m; // добавление денег
end; Данный листинг перегружен комментариями. Своей функции — облегчать чтение программы — комментарии не выполняют, потому что такое нагромождение только вредит. Программист и так знает, что Randomize инициализирует генератор случайных чисел. Не нужно указывать это еще и в комментарии. Комментировать же предназначение переменных очень желательно.
9.3. Точный подбор цвета В Delphi свойству Color или переменной типа TColor в качестве значения можно передать цвет. Вообще тип TColor включает в себя числовые значения от -$7FFFFFFF-1 до $7FFFFFFF. И если вам хочется сделать компонент какого-то нестандартного оттенка, то придется поэкспериментировать с числовыми значениями. Те читатели, которые хотя бы иногда играют в компьютерные игры, наверное, знают, что во многих играх есть возможность, нажав определенную комбинацию клавиш или введя какое-то секретное слово (это называется чит-кодом), увеличить количество денег на игровом счете либо выполнить какие-нибудь подобные действия. Этот пример как раз иллюстрирует, как реализовать такой эффект.
Глава 9. Техника программирования Обычный красный цвет, который входит в стандартные (см. табл. 7.1), можно получить следующим образом: Labell.Color := clRed;
Абсолютно такого же красного цвета можно добиться, присвоив такое значение: Labell.Color := $OOOOFA; FFOOOO
Как же подобрать число, которое соответствует нужному цвету и оттенку. Конечно, можно просто подставлять разные значения одно за другим и смотреть, что получилось. Но есть более эффективный способ! Для этого нам нужны следующие программы: графический редактор Paint, встроенный в Windows Калькулятор и, естественно, Delphi. Вместо Paint можно использовать Adobe Photoshop, так как работать будем только с палитрой цветов. Заменить стандартный К а л ь к у л я т о р Windows можно любым другим калькулятором для инженерных расчетов. А вот без Delphi не обойтись. Сначала нужно визуально подобрать цвет, который мы хотим задать для Labell. Запускаем редактор Paint и открываем в нем окно изменения палитры цветов (рис. 9.2): Палитра > Изменить палитру > Определить цвет. Если же вы работаете в Photoshop, то перейдите на палитру Color (Синтез) (рис. 9.3).
Основная палитра1
вгтгтг • г г г~ г т т т
тттттттт
щлшттштг Дополнительные цвета:
г— r-~ j — — |__ ^ — | — —
i
I
1
I
I
i
ш
1
Оттенок: |[160 Красный: }5
j
Контрастно
j
Зеленый |0
аркость:.[о
Отмена }
Добавить в набор
Для получения справки выберите КОМАНДУ 'Вызов справки" из меню "Справка". :ЩП^\ @$ $ ^ О ЩШ W «9 ij B^ej gaWindo,.,j ВУМ1сго»...| 5SMicio5..
Рис. 9.2. В Paint цвет подбирается в окне «Изменение палитры»
Теперь можно представить себя художником и подобрать какой-нибудь нестандартный цвет. Не зря же существуют видеокарты с поддержкой 24-битного цвета?
Точный подбор цвета В общем, подбирайте любой цвет нужного вам оттенка. При этом обращайте внимание на три окошка, в которых записывается цифровое представление цвета. Например, если выбрать просто красный цвет, то в данных окошках будет стоять 255, 0, 0. У оранжевого цвета эти параметры другие: 255,128, 0 (рис. 9.4, 9.5). После окончательного подбора цвета данные числа нам как раз и понадобятся. .-, Adobe Photoshop Файл Редактирование Изображение
Слой Выделение Фильтр
Просмотр Окно Помощь
[Ч»рм»пД;;;;;;^| Непрозрачность: IQOtf Толщина: fs
пике. Г? Сглаживание
Стрелки: Г* В нэчэле J? 8 конце Фооиэ.
j2s
и. \
^«"«'••••«•^^ Инженерный (рис. 9.6). Щ Калькулятор Дравка Еиа | Справка
Hex
|Г I Sta |
(? Dec
Г Oct
!™U 2ji \BuM/ • . .•--...••:•;:.;:..; ^ ^ .'':::-':' _ _
!
.
'"
Рис. 10.2. Предупреждения (Warning) и ошибки (Error) в окне редактирования кода. Выделена строка, в которой обнаружена ошибка
Например, напишем такие строки и запустим компиляцию (Ctrl+F9). Листинг 10.1 procedure Comon; beg i n . ShowMwssage(llpo6Hafl надпись); end;
В данном случае имеется опечатка в вызове функции ShowMessage. В нижней области появились следующие надписи: [Error] U n i t l . p a s ( 2 6 ) : Undeclared i d e n t i f i e r :
'ShowMwssage'
[Fatal Error] P r o j e c t l . d p r ( 5 ) : Could not compile used unit ' U n i t l . p a s '
Первая надпись сообщает об ошибке (Error), указывает, в каком она находится файле (Unitl. pas) и строке (26), а также ее тип (undeclared identifier необъявленный идентификатор). Как видите, система не знает слова showMws sage, не нашлось его и в описаниях переменных, типов и классов. В этом случае ошибку нетрудно найти и исправить. Иногда ошибка оказывается совсем не там, куда указывает Delphi. Например, если мы объявили переменную i как string, а потом пытаемся использовать ее в цик-
Глава 10. Компиляция и отладка программы ле for, то Delphi найдет ошибку в строке цикла for. Исправлять же ее нужно в разделе объявления переменных — var. Бывают и более сложные ситуации, когда ошибку приходится искать чуть ли не полдня. При создании крупных проектов на отладку и тестирование иногда тратится не меньше времени, чем непосредственно на программирование. Есть даже такая должность — тестировщик. Занимающий ее человек тестирует написанное программистами приложение. Во второй строке (Fatal Error) компилятор сообщает, что не может скомпилировать файл Unitl .pas из-за ошибок. Кроме ошибок в этой же области выводятся предупреждения (warning). Они в отличие от ошибок не мешают полной компиляции проекта и созданию исполняемого ЕХЕ-файла. Но это не значит, что на них не нужно обращать внимания. Очень часто, анализируя предупреждения, можно понять, где в программе скрывается ошибка, или обнаружить какие-нибудь неочевидные погрешности в коде. Так что не стоит игнорировать предупреждения — мне они много раз помогали. Следующим инструментом отладки является пошаговое выполнение программы (рис. 10.3). Для его запуска нужно нажать F7. И чтобы перейти к следующей строке программы, следует также нажимать эту клавишу. В процессе построчного выполнения программы можно смотреть значения различных переменных. Для этого следует подвести указатель мыши к нужной переменной в тексте программы. На всплывающей подсказке будет написано ее значение в данный момент.
Urall lend; procedure TForml.ButtonZClick(Sender: jvar BUF: 1 array [1..100000] of Byte; F: File; jbegin UssignFile (Г, ' b l o c k . d a t ' ) ; iBlockRead(F,BUF, 100); jCloseFile(F); i end;
TObject) ;
J
end.
J 45: 1
(Modifed
Рис. 10.3. Режим построчного выполнения программы (выполняется 45 строка)
Если вам не нужно просматривать всю программу построчно, а всего лишь уточнить какой-нибудь ее момент, то можно перейти к детальному рассмотрению определенного фрагмента программы (как вариант — одной процедуры). Для этого нужно нажать клавишу F4. Данный режим похож на режим трассировки (F7), но
Отладка в этом случае программа выполняется по одной строке, начиная с той, в которой находился курсор в момент нажатия F4. Клавишу F4 можно нажимать и тогда, когда вы запустили программу в режиме трассировки (F7). Если нужно сразу перейти к какой-либо другой строке процедуры, то нажмите F4. Когда выполнение программы дойдет до этой строки, оно приостановится и будет ждать нажатия F7. Можно назначить точку приостановки выполнения программы (точку останова). Для этого щелкните мышью на вертикальной полосе слева в окне ввода текста программы. Там появится красный кружок, и вся строка выделится красным цветом (рис. 10.4). Когда вы скомпилируете программу (F9), ее выполнение приостановится при достижении этой выделенной красным цветом строки, и вы сможете проконтролировать, например, значения переменных. Unirt lend; iproceclure TFocwl. But-tonZCIick(Sender: TObject); jvar BUF: array [1..100000] of Byte; F: File; [begin JAssignFile(F,'block.dat') ; JReset(F,512); iCloseFile(F); lend;
46: 1
I
Modified
Рис. 10.4. Точка останова
Когда вы переходите в режим отладки Step Over (нажимаете F8), программа тоже выполняется по шагам, но при этом не отображается выполнение процедур и фун-х кций. Если вы точно знаете, что ошибка, допустим, не в процедуре OutGraphic, то, достигнув строки с вызовом этой процедуры, нажимайте не F7, a F8. В Delphi параметры отладки можно изменять в окне Debugger Options (рис. 10.5), которое вызывается командой Tools > Debugger Options. Например, на вкладке Event Log этого окна можно включить или отключить вывод различных сообщений программы. По-английски отладка называется debugging. Как же произошло слово debugging и почему ошибки в программе называют bug? Происходило все задолго до изобретения первых микросхем. Тогда компьютеры были построены на реле и занимали очень много места — целые залы и этажи зданий. Соответственно, для обслуживания одного такого компьютера требовался 5 Зак. 899
Глава 10. Компиляция и отладка программы штат специалистов. Обслуживание и ремонт требовались часто — детали были ненадежными. Но однажды компьютер перестал работать не из-за ненадежности деталей (как потом оказалось), и специалисты начали искать причину. Через некоторое время ее нашли — причиной оказался жук (bug), который забрался внутрь компьютера и замкнул собой один из контактов. Жука вытащили, а компьютер запустили.
General Event Log j Language Exception: j OS Exceptions | ^General
f Messages •:
--.——
------
i Jy CJeai tog on run
| ]7 Breakpoint messages
[ 17 Unlimited length
| J7 Process messages \ P' Ibread messages
[Display process info with event
• $7 Module messages i p flutput messages i Г' Window messages
UseEvent Log Colors
i Foreground; |Щ Custom...
^j
Background: JQ Window BackgT^j
integrated debugging
Рис. 10.5. Окно параметров отладчика
После обнаружения и устранения неисправности в журнале эксплуатации компьютера записали «debugging» (удаление жука). Так и повелось, что любые неисправности компьютеров, а потом и ошибки программного обеспечения по-английски стали называть bug.
Глава 11
Создание приложения U Q Q О Q Q Q Q
Компоненты и их свойства Остроумные фразы Базы с сообщениями Программирование интерфейса Загрузка данных Интеллект программы Добавление общих фраз Встраивание в программу возможности сообщать о текущем времени Q Коллекция вопросов и ответов программы Q Полный листинг программы
Глава 11. Создание приложения
11.1. Компоненты и их свойства Дришло время подвести итог первой части книги. Сделать это лучше всего, создав какой-нибудь интересный проект. Например, программу, которая может общаться с пользователем (ее можно найти на компакт-диске, прилагаемом к книге, по адресу Examples\Speaker). Начнем с создания нового проекта и изменения свойств формы (табл. 11.1). В этой главе и далее (во второй части книги) задание свойств оформлено в виде таблицы. Для масштабных проектов это нагляднее и удобнее, чем приводить значения свойств прямо в тексте. Таблица 11.1. Значения свойств формы Forml Свойство
Значение
Borderlcons biMaximize
False
BorderStyle
bsSingle
Caption
Суперговорилка
Position
poDesktopCenter
Width
522
Height
358
На форму помещаем компоненты Labell, Memol, Editl и Buttonl. Первый из них нужен для вывода замечаний программы. Когда пользователь будет набирать текст, программа с помощью Labell будет выводить остроумные выражения. Значения свойств компонента Labell задаем согласно табл. 11.2. Таблица 11.2. Значения свойств компонента LabeM Свойство
Значение
AutoSize
False
Caption Height
17
Left
0
Top
8
Width
513
В приведенной таблице (и далее в книге) значением свойства Caption является пустая строка. Это значит, что свойство нужно очистить, то есть удалить его содержимое. Компонент Memol будет использоваться для вывода сообщений — диалога между программой и пользователем. Свойству Readonly присваиваем значение True (табл. 11.3), чтобы в одно Memol нельзя было ввести текст.
Компоненты и их свойства Таблица 11.3. Значения свойств компонента Memol Свойство
Значение
Height
265
Left
0
Lines Readonly
True
TabOrder
2
Top
32
Width
513
Окно редактирования свойства Lines компонента Memol показано на рис. 11.1. 7 Suing LM Editor D lines
Рис. 11.1. Окно редактирования свойства Lines компонента Memol
Добавленный нами на форму компонент Editl предназначен для ввода сообщений пользователем. Его свойства и их необходимые значения собраны в табл. 11.4. Таблица 11.4. Значения свойств компонента Editl Свойство
Значение
AutoSize
False
Height
21
Left
0
Text TabOrder
0
Top
304
Width
417
Чтобы послать набранную в поле Editl фразу в окно Memol, нужно нажать кнопку Buttonl.
Глава 11. Создание приложения Отправить сообщение также можно, нажав клавишу Enter. Для этого значение свойства Default нужно изменить на True (табл. 11.5). Таблица 11.5. Значения свойств компонента Buttonl Свойство
Значение
Caption
Сказать!
Default
True
Height
23
Left
416
TabOrder
1
Top
304
Width
97
После задания значений всех свойств окно приложения должно выглядеть так, как показано на рис. 11.2.
Рис. 11.2. Форма Forml
11.2. Остроумные фразы Теперь можно начать наполнение созданного каркаса программы кодом. Параллельно программированию будем создавать текстовые файлы — базу данных, из которой программа будет брать фразы для общения. В качестве базы данных не всегда целесообразно использовать текстовые файлы, но в данном случае подходят именно они. Начнем с обработки вводимого пользователем текста, то есть зададим реакцию программы на процесс набора с клавиатуры. Для этого создадим текстовый файл с фразами, которые программа будет выводить в Labell. Несмотря на то что файл
Остроумные фразы имеет текстовый формат, зададим для него расширение DAT. Должен получиться файл l a b e l s f i l e . d a t следующего содержания. Листинг 11.1 Эй, хватит писать всякие нехорошие вещи. Уже экран краснеет! И набирает, и набирает... М д а . . . А мне отвечать. .. Тебя где писать учили вообще? В холме Огров? Ну, как курица лапой! Работай над почерком! Помню, сидим мы с админом, пиво пьем.. . Зависнуть, что ли? Ты пишешь какие-то глупости! Такой ерунды я уже давно не видела. Пиши, пиши! Склифосовский! Молодец! Правильно говоришь! Что вы делаете сегодня вечером? Я - Суперговорилка, а ты?
Пожалуй, достаточно. Данные фразы будут появляться не очень часто, поэтому на первое время их хватит. Разумеется, вместо приведенных выражений вы можете написать любые другие, но не более ста — на такое количество рассчитан массив phrases. Если же необходимо ввести больше остроумных фраз, то измените размерность массива. Теперь загрузим эти фразы в программу. Нужно, чтобы время от времени программа реагировала на нажатие пользователем клавиш клавиатуры и выдавала одно из выражений листинга 11.1. Реализовать это можно по-разному. Например, установить таймер и в событии OnTimer (то есть с определенной периодичностью) генерировать случайное число. Если это число совпадет с указанным, то будет выводиться фраза. Но мы сделаем немного по-другому — будем считать количество введенных пользователем символов. Если оно превысит определенную величину (например, 40 символов), то при вводе каждого нового знака будет возможно появление фразы. Когда остроумное выражение появляется, счетчик введенных символов нужно обнулить. После этого весь процесс начинается сначала. Такой способ не зависит от времени (а только от количества введенных символов) и позволяет не допускать слишком частое появление фраз. Для счетчика введенных символов зададим переменную symbols. Она должна быть объявлена вне какой-либо процедуры, чтобы быть доступной всем процедурам в модуле Unitl. Количество символов, которые нужно набрать, чтобы появилась фраза, зададим с помощью константы needsymb. Листинг 11.2 const needsyrnb=40;
var Form"!: TForml;
Глава 11. Создание приложения symbols: Integer; // счетчик символов implementation
Когда пользователь набирает в поле Editl какой-нибудь текст, при вводе каждого символа возникает событие OnChange. Поэтому считать количество символов будем в обработке этого события. При таком подходе счетчик символов будет увеличиваться и тогда, когда пользователь удаляет буквы. В этом же событии будем проверять, достигло ли количество введенных символов нужного предела, и если это произошло, то будем генерировать случайное число и при выполнении определенного условия выводить фразу. Фразу выведем в отдельной процедуре — OutPhrase. Листинг 11.3
procedure TForml.Edit1Change(Sender: TObject); var n: Integer; begin Inc(symbols); if symbols>needsymb then // введено нужное количество символов, можно выводить фразу beg i n n := Random(10); i f n=5 then begin OutPhrase; symbols := 0; end; end; end;
Если пользователь ввел нужное количество символов (задается константой needsymb), то совсем не обязательно, что будет выведена фраза. В этом случае генерируется случайное число п, и только когда оно равно пяти (п=5), выводится фраза. Таким образом, даже при достижении нужного количества символов вероятность появления фразы во время ввода каждого нового символа составляет '/)0. Перед использованием Random нужно запустить генератор случайных чисел Random! ze, иначе возвращаемые функцией Random значения всегда будут одинаковые. Random! ze достаточно запустить один раз, поэтому сделаем это в процедуре активации формы FormActivate. В этой же процедуре загрузим фразы из текстового файла. Чтобы не открывать и не читать файл каждый раз, когда нужно вывести лишь одну фразу, прочитаем файл один раз в массив, а потом по мере необходимости будем брать из массива нужную фразу по номеру.
Остроумные фразы Добавим в раздел var массив строк pjirases и переменную count, предназначенную для подсчета загруженных из файла фраз и их хранения. Листинг 11.4
var Form"!: TForml; symbols: Integer; // счетчик символов phrases: array [1..100] of String[255]; count: Integer;
Процедура FormActivate примет такой вид. Листинг 11.5 procedure TForml.FormActivate(Sender: TObject); begin Randomize; ReadLabe I Phrases; end;
Напишем процедуру чтения фраз из текстового файла в массив phrases (ReadLabelPhrases). Листинг 11.6
procedure TForml.ReadLabeI Phrases; // чтение фраз из текстового файла в массив
var F: TextFi le; s: String; begin AssignFi le(F, f i le_phrases); Reset(F); count := 0; w h i le not Eof(F) do begin , ReadLn(F.s); if s= then Continue; Inc(count); phras,es[count] := s;
end; CloseFi le(F);
end; Поясню данную процедуру. В принципе здесь все стандартно: открывается текстовый файл, который, пока не будет обнаружен конец файла, построчно читается в массив. После чтения каждой строки счетчик увеличивается на единицу. Затем файл закрывается. Перед чтением файла счетчик строк (переменная count) обнуляется. В данном примере это необязательно было делать, так как процедура
Глава 11. Создание приложения ReadLabelPhrases вызывается один раз из FormActivate, следовательно, переменная count равна нулю, поскольку ранее не использовалась. Но в целях повышения надежности программы счетчик прочитанных строк лучше все-таки обнулять на случай, если процедуру придется вызывать еще раз (такое вполне может произойти по мере совершенствования программы). Обратите внимание на строку AssignFile(F,file_phrases);
В ней имя файла задано константой, которая объявлена в разделе const. Листинг 11.7
const needsymb=40; f i le_phrases=labelsfi le.dat;
Это сделано для того, чтобы при желании легко изменить имя файла, с которым работает программа. Процедура ReadLabelPhrases и использующая ее программа могут прекрасно работать, но при этом фразы могут не выводиться. Чтобы исключить такую возможность, нужно запретить выполнение программы в случае отсутствия файла с фразами. Тогда процедуру ReadLabelPhrases нужно немного изменить. Листинг 11.8
procedure TForml. ReadLabel Phrases; // чтение фраз из текстового файла в массив
var
F: TextFile; s: String; begin AssignFi le(F, f i le_phrases);
{$!-}
Reset(F);
{$!+} if lOResultoO then
beg i n
ShowMessage(OuiM6ilength(phrases[NumbF]) then // остановка таймера Timerl. Enabled := False; end;
Процедура элементарная: на этикетку выводится очередной символ, после чего счетчик символов увеличивается на единицу и проверяется, все ли символы выведены. Если все, то таймер останавливается, тем самым прекращается вывод символов. Можете изменить некоторые параметры по своему желанию. Например, изменив интервал таймера с 50 на другое число, можно замедлить или ускорить вывод символов. Если константу needsymb сделать равной 20 вместо 40, то фразы будут появляться чаще. Чтобы изменить вероятность появления фраз, задайте другое число в строке п := R a n d o m ( 1 0 ) ; процедуры EditlChange.
11.3. Базы с сообщениями Наша программа пока не общается с пользователем, но уже может комментировать набор текста. Сделаем так, чтобы она реагировала на простые приветствия и вопросы. Причем программа должна распознавать построенные по-разному фразы. Создадим еще один текстовый файл, содержащий всевозможные приветствия. Вид его будет таким: • первая строка — приветствие, введенное пользователем; • вторая строка — ответ программы на приветствие. После этого идут следующие две строки и парами все остальные приветствия с ответами. Для повышения удобочитаемости разделим пары пустой строкой, а программу при чтении файла заставим пропускать пустые строки. Файл с приветствиями (privetf lie. dat) будет иметь следующее содержание. Листинг 11.13
Привет Привет!
Глава 11. Создание приложения Hel lo Привет!
How are you Все отлично! Как дела Лучше всех! Здравствуй Привет, привет! Здравствуйте Ухты, как важно... Привет! Здорово Ага, привет! Салют Привет, коли не шутишь! Добрый день Здравствуй! Добрый вечер Hello! Доброе утро Утро добрым не бывает.
Для прощальных фраз создадим похожий файл (byef ile. dat). Листинг 11.14 Пока Пока!
Счастливо Пока, пока! Удачи У какой еще дачи? До свидания Будь!
Думаю, этого достаточно, программа ведь создается для примера. Вы можете добавлять разные фразы по своему усмотрению.
Программирование интерфейса Восклицательные, вопросительные знаки и точки в конце введенных пользователем сообщений не поставлены намеренно, потому что неизвестно, как именно он наберет фразу. /
11.4. Программирование интерфейса При сравнении строк в нашей программе нужно будет использовать функцию преобразования из прописных букв в строчные, потому что введенное пользователем слово «Привет» не равно «привет». В Delphi есть функция Lowercase, которая преобразует прописные буквы в строчные, но работает только с латинскими символами (русские буквы она оставляет без изменений). Для преобразования русских прописных букв в строчные существует функция AnsiLowerCase. С латинскими буквами она, разумеется, тоже справляется прекрасно. Займемся той частью программы, которая работает при нажатии кнопки Сказать! (Buttonl). Когда пользователь нажмет ее, нужно вывести набранную им фразу в поле сообщений (Memol), преобразовать в строчные буквы и очистить строку ввода (Editl). После чего необходимо вызвать процедуру обработки сообщения пользователя. Ей займемся потом, как самой сложной. Сначала отладим механизм вывода сообщений. Листинг 11.15
i
procedure TForm1.Button1CI ick(Sender: TObject); // нажатие кнопки "Сказать!" var user: String; // сообщение пользователя comp: String; // сообщение программы begin user := Editl.Text; Memol.Lines.Add(User: + user); user : = AnsiLowerCase(user); Editl.Text := ; comp := Intel lect(user); Memol.Lines.Add(Comp: + comp); Editl.SetFocus; end; Подбор ответа на введенную пользователем фразу возложим на функцию intellect. Ей в качестве параметра нужно передавать сообщение пользователя (оно имеет тип string), а возвращаться будет ответ программы (тоже тип string). Пока сделаем так, чтобы функция intellect всегда выдавала одно и то же сообщение. Тогда будет можно запустить программу и проверить, как она работает. Листинг 11.16
function TForml.Intellect(s: String): String; // возвращает ответ компьютера // (s - исходное сообщение пользователя) begin
Глава 11. Создание приложения Intellect := He понимаю; end;
Можно запустить программу и посмотреть, что получилось. А вышло здорово! Что программе ни скажи — у нее на все готов ответ. Правда, пока ответ один и тот же; «Не понимаю». Зато все работает.
11.5. Загрузка данных Перед использованием данных, хранящихся в текстовых файлах, их (данные) желательно загрузить в память. Чтобы работать с этими файлами, зададим еще две константы, в которых будем хранить имена файлов. Листинг 11.17
const
needsymb=40; fi le_phrases=labelsfi le.dat; f i le_privet=privetfile.dat; f i le_bye=byefi le.dat; Введем тип Hesh и будем использовать его для создания массивов, в которых удобно хранить парные фразы (например, приветствие и ответ на него). Листинг 11.18 type Hesh=record s1: String[255]; // сообщение пользователя s2: String[255]; // ответ компьютера на сообщение end;
Создадим массивы privets и byes, объявив их в разделе var. Там же объявим переменные Pcount и Bcount для хранения количества загруженных приветственных и прощальных фраз. Листинг 11.19 var Form"!: TForml;
symbols: Integer; // счетчик символов phrases: array [1. . 100] of String[255]; count: Integer; // общее количество загруженных фраз NumbF: Integer; // номер фразы, которая выводится OutS: Integer; // номер символа, который выводится privets: array [1..100] of Hesh; Pcount: Integer; // количество.загруженных приветствий byes: array [1..100] of Hesh; Bcount: Integer; // количество загруженных прощальных фраз
Загрузка данных Напишем процедуры ReadPrivets и ReadByes, которые будут загружать фразы соответственно с приветственными и прощальными сообщениями в массивы privets И byes.
i
Листинг 11.20
procedure TForml.ReadPrivets; // загрузка приветствий в массив privets var F: TextFi le; s: String; mode: Integer; // режим чтения: 0 - фраза, 1 - ответ на фразу begin AssignFile(F,f i le_privet); {$!-} Reset(F); {$!+} if lOResultoO then begin 8п(мМеззаде(Ошибка открытия файла + f i le_privet); Form!. Close; end; mode := 0; Pcount := 0; whi le not Eof(F) do begin ReadLn(F.s); if s= then Continue; if mode=0 then beg i n Inc(Pcount); privets[Pcount].s1 := AnsiLowerCase(s); mode := 1; end else begin privets[Pcount].s2 := s; mode := 0; end; end; CloseFile(F); end; Процедура ReadPrivets содержит два режима чтения файла: mode=0 и mode=l. В первом случае процедура выполняется по одной ветке и прочитанная строка присваивается si (здесь же происходит увеличение счетчика Pcount). Затем режим переключается в mode=l, прочитанная строка присваивается s2, а режим переключается обратно в mode=0. Таким образом режимы чередуются при каждой
Глава 11. Создание приложения итерации цикла while. Если же встречается пустая строка, то процедура переходит к следующей итерации цикла с помощью оператора Continue. Процедура ReadByes аналогична ReadPrivets. Различаются они только названиями переменных и массивов. Листинг 11.21
procedure TForml.ReadByes; // загрузка прощальных фраз в массив byes var F: TextFi le; s: String; mode: Integer; // режим чтения: 0 - фраза, 1 - ответ на фразу beg in AssignFi le(F,fi le_bye); if lOResultoO then beg i n ShowMessage(OujH6ilength(phrases[NumbF]) then // остановка таймера Timer!. Enabled := False; end;
function TForml.AskPrivet(s: String): String; // Проверяет, является ли сообщение пользователя (s) приветствием, // и возвращает ответ компьютера, если он найден. // Если ответ не найден, то возвращает пустую строку. , var i : Integer; // счетчик в цикле n: Integer; // нужна для определения Роз sc: String; // ответ компьютера s1: String; // нужна для подбора begin // сканирование массива приветствий sc := ; for i := 1 to Pcount do begin s1 := privets[i].s1; n := Pos(s1,s); if n0 then // подходящий ответ найден beg in sc := privets[i].s2; Break; end; end; AskPrivet := sc; end; function TForml.AskBye(s: String): S t r i n g ; // Проверяет, является ли сообщение пользователя (s) прощальным, // и возвращает ответ компьютера, если он найден. // Если ответ не найден, то возвращает пустую строку. var i: Integer; // счетчик в цикле n: Integer; // нужна для определения Pos sc: String; // ответ компьютера s1: String; // нужна для подбора begin // сканирование массива прощальных фраз sc := ;
Полный листинг программы
for i : = 1 to Bcount do beg in s1 := byes[i].s1; n •.= Pos(s1, s); if n0 then // подходящий ответ найден begin so := byes[i].s2; Break; end; end; AskBye := sc; end; funct ion TForml. AskBase(s: String): String; // Подбирает ответ компьютера из файла базовых фраз // и возвращает его, если находит. // Если ответ не найден, то возвращает пустую строку. var i : Integer; //счетчик в цикле n: Integer; // нужна для определения Роз so: String; // ответ компьютера s1: String; // нужна для подбора begin // сканирование массива базовых фраз sc := ; for i := 1 to BASEcount do begi n s1 := base[ i ].s1; n := Pos(s1, s); if n0 then // подходящий ответ найден beg i n sc := base[ i ]. s2; Break; end; end; AskBase := sc; end; function TForml. AskTime(s: String): String; // Если пользователь задал вопрос о времени, // то сообщает время (возвращает ответ типа String). //.Если ответ не найден, то возвращает пустую строку. var found: Boolean; // имеет значение True, есл^вопрос о времени sc: String; // формируемый ответ компьютера
Глава 11. Создание приложения n: Integer; // нужна для генерации случайного числа hour, mi n, sec, msec: Word; / / д л я преобразования времени now: TDateTime; / / д л я определения даты begin found := False;
if Роз(сколько врем,з)0 then found := True; if Роз(который час,з)0 then found := True; if Роз(рано,s)0 then found := True; if Pos(поздно,s)<X) then found := True; if Роз(скока время,s)0 then found := True; if Роз(время сколько,s)0 then found := True; if Роз(времени сколько,s)0 then found := True; if Роз(сейчас времени,s)0 then found := True; if Роз(времени сейчас,s)0 then found := True; if Роз(сейчас время,s)0 then found := True; if Роз(время сейчас,s)0 then found := True; if found=False then // вопрос не о времени sc : = else begin // вопрос о времени // определение текущего времени now := Time; DecodeTime(now, hour.min, sec, msec); // генерация одного из вариантов ответа' п := Random(4); case n of 0: begin sc := Сейчас + IntToStr(hour) + ч. ; sc := sc + IntToStr(min) + мин.; end; // 0 1: begin sc : = Сколько время? ; if hour Redit
Стрелки; Г" В начале f?'В конце Фоомэ.
> Rwluct Grainin*» > Drop Studow Hull nug»J > Vijnetl. (lull im«9«)
Рис. 13.5. При перетаскивании на окне осталась часть изображения, которое было под ним
Прозрачные окна имеет смысл использовать тогда, когда почти все окно закрыто какими-то компонентами или на окно выводится непрямоугольное изображение. Например, ходящие и танцующие по экрану девушки. Обратите внимание, что невидимые, нестандартные окна и кнопки потребляют намного больше ресурсов компьютера, чем стандартные средства. Поэтому, если, например, вы сделали окно в форме танка, не удивляйтесь, что такое окно будет медленно перемещаться на некоторых компьютерах. Однако практически все игры выполнены с использованием нестандартных средств, поэтому будет ли программа работать медленно — зависит от конкретной реализации. Есть более удачный способ делать прозрачные, а точнее, полупрозрачные окна. Этот способ стало возможно применять начиная с шестой версии Delphi. В класс TForm была введена поддержка полупрозрачности форм, то есть пользователю видно то, что окно должно было бы закрывать собой. Причем прозрачность можно плавно менять. Для этого были добавлены свойства AlphaBlend, AlphaBlendValue, TransparentColor, TransparentColorValue. Сразу вынужден огорчить пользователей и программистов, у которых установлена cHcreMaWindows 95 или Windows 98, а также тех, у кого маломощный компьютер. Свойства полупрозрачности работают в Windows 2000 и более новых операционных системах и не работают на компьютерах, устаревших сильнее, чем Pentium I.
Прозрачные окна Конечно, такие компьютеры устарели, но их все-таки еще очень много. Совсем не важно, на каком компьютере компилировалась программа. Будут работать эти эффекты или не будут, зависит только лишь от того, на каком компьютере и с какой операционной системой программу запускают. Однако если вы пишете программу и у вас эти эффекты не работают, то вы не сможете их нормально отладить. Итак, свойство AlphaBLend может принимать значения True и False. Соответственно, если задать True, то полупрозрачность формы включена, а если False — выключена. По умолчанию это свойство установлено в False. Насколько форма должна быть прозрачной, указывается в свойстве AlphaBlendValue. Если циклически менять значение этого свойства, то окно будет красиво плавно возникать на экране из ниоткуда или, наоборот, плавно исчезать. Если свойству AlphaBlendValue присвоить О, то окно будет полностью прозрачным. Если присвоить 255 — то полностью непрозрачным. Можно задавать прозрачность окна по определенному цвету. Для этого используются свойства TransparentColorValue и TransparentColor. Через свойство TransparentColor можно включать (True) и выключать (False) прозрачность окна по цвету. По умолчанию прозрачность выключена (False). Свойство TransparentColorValue показывает, какой цвет считать прозрачным. Если в процессе разработки программы (в окне Object Inspector) присвоить свойству AlphaBlend — True, a AlphaBlendValue, например, 128, то окно ведет себя совершенно обычным образом, но получается прозрачным даже без написания дополнительного кода. В книге не приведено изображение полученного прозрачного окна, так как черно-белая иллюстрация не может передать эффект. Готовое приложение и полные листинги рассмотренного примера вы можете найти на компакт-диске, прилагаемом к книге, по адресу Examples \Blend (NT only). Рассмотрим листинг программы, окно которой плавно исчезает с экрана при закрытии. Программа небольшая, поэтому я привожу ее целиком, абсолютно без сокращений. Все, что нужно сделать в режиме визуального программирования, это поместить на форму таймер Timerl. Можете разместить на форме также какие-нибудь компоненты, чтобы посмотреть, как выглядит исчезающая форма с компонентами. Листинг 13.8
unit Unit1; Interface
uses Windows,Messages, SysUti Is, Variants, Classes,Graphics,Controls, Forms, Dialogs, ExtCtrls;
type TForml = class(TForm)
Т i me r1: TT i me r;
Глава 13. Эффектные формы procedure Tirner1Timer(Sender: TObject); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); Private { Private declarations } Publ ic { Publ ic declarations }
end; var Form"!: TForml; Implementat ion {$R *.dfm} procedure TForml.Timer1Timer(Sender: TObject); begin Form"! . A l p h a B l e n d V a l u e := Form"!. AlphaBlendValue - 5; if Form"!. AlphaBlendValue=0 then Form"! .Close;
end; procedure TForml. FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin if Timer"!. Enabled=False then begin CanClose := False; Timer"!. Enabled := True; exit;
end; CanClose := True; end; end.
Алгоритм работы программы таков. Когда пользователь пытается закрыть окно, генерируется событие CLoseQuery и выполняется процедура FormCloseQuery, то есть запрос на закрытие окна. Если в этой процедуре переменной CanClose присвоить False, окно не будет закрыто. Мы так и делаем, присваиваем CanClose значение False, так как закрывать окно пока рано, нужно продемонстрировать эффект. Запускаем таймер, который занимается уменьшением значения свойства ALphaBlendValue. Также по таймеру проверяется, достигла ли форма полной прозрачности. Когда достигла, закрываем форму принудительно. Снова срабатывает процедура FormCloseQuery, но теперь таймер уже запущен, и CanClose устанавливается в True — приложение закрывается. Готовое приложение и полные листинги рассмотренного примера вы можете найти на компакт-диске, прилагаемом к книге, по адресу Examples YPlavnblend (NT only).
Программы-«невидимки»
13.5. Программы-«невидимки» Вам никогда не хотелось создать такую программу, которую пользователь не замечает? Программа проработала, а пользователь ничего не заметил. Если поместить программу в автозагрузку, то она будет работать при каждой перезагрузке компьютера, а пользователь ничего не заметит. Речь идет совсем не о вирусе, а например, о программе, в которую можно ввести день рождения хозяина компьютера. Эта программа при каждой перезагрузке будет проверять, совпадает ли текущая дата с датой, введенной в ее память. Если дата совпала, программа может проиграть музыку и поздравить юбиляра. Можно, конечно, сделать программу, которая будет напоминать о днях рождения друзей и знакомых. Но я сейчас говорю именно о незаметной для пользователя программе. Незаметность, конечно, относительна. Опытный пользователь рано или поздно обратит на нее внимание, а например, какой-нибудь ваш друг, который использует компьютер только для того, чтобы музыку слушать и в Quake играть, не заметит ни за что. Если написать поздравляющую программу, а потом аккуратно поставить ее ему на компьютер, то, думаю, он обрадуется, увидев, что умный компьютер не забывает о нем и поздравляет с днем рождения. Создадим такую программу. После установки эту программу нужно настроить: указать день рождения хозяина компьютера, на который вы внедряете это ценное программное обеспечение, и запустить программу вручную. После этого она добавится в автозагрузку и будет работать сама. Насчет помещения программы в автозагрузку не все очевидно. Дело в том, что некоторые операционные системы (например, Windows 2000 и Windows XP) восстанавливают системные файлы после несанкционированного доступа к.ним. По этой причине заносить программу в автозагрузку будем максимально корректным способом, чтобы наша программа работала в любой операционной системе. Также программа не должна иметь ни одной формы. Она должна работать быстро и незаметно, а значит, быть очень маленькой. Этого можно добиться, убрав из приложения поддержку форм и других стандартных средств, из-за которых ЕХЕ-файл, созданный в Delphi, не бывает меньше 200, а то и 400 Кбайт. Вы спросите: «А как же тогда поздравлять нашего друга?» Для этого можно придумать разные решения. Во-первых, создавать форму, не используя библиотеки Delphi (с помощью функций Windows API). Однако этот способ в нашем достаточно простом случае не очень уместен, к тому же можно сделать гораздо изящнее. В нашем случае лучше будем запускать, например, браузер Opera с поздравлением. Можно открывать файл с картинкой или запускать звуковой файл с поздравлением. Конкретных реализаций такого способа может быть множество. Начнем, пожалуй. Создайте новый проект в Delphi. Затем сразу нажмите кнопку со знаком минус для удаления формы из проекта (или через меню Project > Remove from Project). Выберите Unitl и, подтвердив выбор, удалите форму из проекта. После чего можно сохранить проект. Желательно не сохранять проект до удаления формы, иначе она останется со всеми остальными файлами и ее надо будет удалять
Глава 13. Эффектные формы вручную. Когда будете сохранять проект, назовите его Noform. Теперь нажмите кнопку View Unit или Ctrl+F12 или через меню View > Units выберите Projectl. В окне редактирования кода откроется текст программы. Те, кто программировал на языке Паскаль, узнают родные знакомые черты: здесь все то же самое, что и в Паскале. Программа выполняет построчно все, что расположено между begin и end. Именно здесь будем писать весь необходимый нам код. Удалите строки: Application.Initialize; Application.Run;
Должно получиться такое пустое приложение, как показано в следующем листинге. Листинг 13.9
program Projectl; uses Forms;
i
{$R *.res}
•
\
begin {Здесь будем писать программу} end. Обратите внимание на uses Forms. Модуль Forms нам не нужен — никакие формы мы вызывать не собираемся, как я уже говорил. А ЕХЕ-файл «благодаря» этому модулю получается очень большой — около 400 Кбайт. Теперь удалим uses Forms и откомпилируем программу. Фантастика! Всего 9 Кбайт! Файл уменьшился в десятки раз. Чтобы была возможность работать с некоторыми функциями, касающимися операционной системы, придется объявить модуль Windows: uses Windows. Размер получаемого ЕХЕ-файла практически не изменился. Для того чтобы поместить нашу программу в автозагрузку, придется объявить модуль Registry: uses Registry. Из-за него ЕХЕ-файл увеличится приблизительно на 100 Кбайт. Про реестр и автозагрузку читайте в гл. 20, поэтому на них я сейчас подробно останавливаться не буду. Для работы с датами нужно объявить модуль Sysutils, а для вызова других программ (нам надо будет запускать браузер, чтобы донести поздравления до взора пользователя) необходим модуль shellApi. Все эти модули, за исключением Registry, практически не увеличивают размер получаемого ЕХЕ-файла. Файл размером 120 Кбайт —вполне неплохо.
Программы-«невидимки» Теперь пишем программу. Я немного поменяю тактику — не буду объяснять по порядку каждую добавляемую в программу строку, а приведу сразу весь листинг программы. Программа специально подробно комментирована, и разобраться в ней будет легко, тем более с моими дальнейшими пояснениями. Листинг 13.10 program Noform; uses Windows, Registry, SysUtils, ShellApi; {$R «.res}
var
Regp: TRegistry; today: TDateTime; year, month, day: word; F: TextFi le; // файл с датами F2: TextFi le; // создаваемый файл-поздравление s: String; // сюда читаются строки из файла sKey: String; // для работы с реестром n: Integer; // для поиска символов в строке yearF, monthF, dayF: integer; kolvo: Integer; // номер поздравления за се'годня st: String; {рабочая переменная для работы с kolvo} begin sKey := Software\Microsoft\Windows\CurrentVersion\Run; kolvo : = 0; // помещаем программу в автозагрузку Regp := TRegistry.Create; Regp.RootKey:=HKEY_LOCAL_MACHINE; Regp.OpenKey(sKey,True); Regp.WriteStri ng(Noform,paramstr(O)); Regp. Free; // определяем текущую дату today := Date; DecodeDate(today,year,month, day); // читаем из файла даты и сравниваем их assignfi le(F,dates.txt); {$!-} Reset(F); {$!+} // если ошибки открытия файла с датами нет, // продолжаем работу if ioresult=0 then begin
Глава 13. Эффектные формы whi le not eof(F) do begin Readl_n(F, s); if s= then continue; // пустая строка, игнорируем // преобразовываем дату, записанную в строке, // в переменные типа Integer - dayF, monthF, yearF n := pos(., s); dayF := StrTolnt(copy(s, 1, n-1)); delete(s,1,n); n := pos(., s); monthF := StrTolnt(copy(s,1, n-1)); delete(s,1,n); n := pos( , s); yearF := StrTolnt(copy(s,1, n-1)); delete(s,1,n); // в s остается имя поздравляемого // сравниваем, если дата совпадает, // то выводим поздравление на экран if (year=yearF) and (month=monthF) and (day=dayF) then // поздравляем beg in // создаем файл i nc(kol vo); st := out + IntToStr(kolvo) + .html; AssignFi le(F2, st); Rewrite(F2); WriteLn(F2,); WriteLn(F2,); Wr i teLn( F2, ПОЗДРАВЛЕНИЕ); WriteLn(F2,); W r i t e L n ( F 2 , < b o d y bgcolor="#FFFFFF">); WriteLn(F2, + s +
); WriteLn(F2,Поздравляем С ДНЕМ РОЖДЕНИЯ!!I); WriteLn(F2,); WriteLn(F2,); C l o s e F i le(F2);
// открываем созданный файл ShelIExecute(0,open,pchar(st),,,SW_SHOW); end;
end; CloseFi le(F); end else // если файла с датами нет, // удаляем автозагрузку из реестра beg in
Программы-«невидимки» Regp := TRegistry.Create; Regp.RootKey:=HKEY_LOCAL_MACHINE; Regp.OpenKey(sKey,True); Regp.DeleteValue(Noform); Regp.Free; end; end.
Сначала расскажу о том, как работает эта программа, не вдаваясь в детали. Первый раз ее нужно запустить вручную. Причем перед этим создать текстовый файл dates. txt строго определенного формата. Формат файла хоть и должен четко выдерживаться, весьма прост: день рождения (дд. мм. гггг), затем пробел и имя поздравляемого. В файле может быть сколько угодно строк, даже можно делать несколько поздравлений в один день. Допускаются и пустые строки — они будут просто игнорироваться программой. Например, содержимое файла dates . txt приведено в следующем листинге. Листинг 13.11
26,04.2004 Neo 05.08.1981 Артем 20.10.1980 Майк
В номерах дней и месяцев можно не указывать первый ноль, то есть не обязательно писать «05», можно просто «5», главное — разделять день, месяц и год точкой. Год при этом должен быть написан полностью. Итак, мы хотим внедрить эту программу на компьютер хорошего знакомого. Составляем файл dates. txt, располагаем его в одной папке с Nof orm. exe. Если предполагается поздравлять только одного хозяина компьютера, то dates. txt можно создать за пять секунд — там будет всего одна строка. Затем запускаем Nof orm. exe. Программа заносит себя в автозагрузку через реестр и при каждой перезагрузке Windows будет на страже. При каждом запуске программа пытается занести себя в автозагрузку, потом проверяет наличие файла dates. txt. Если файл есть, то программа считывает строки по одной, сверяет написанные там даты с текущей. При совпадении дат создается HTML-файл с поздравлением и открывается в браузере, который установлен по умолчанию, например Opera (рис. 13.6). Если совпадает несколько дат, в браузере открывается несколько окон с несколькими поздравлениями. Если файл dates. txt не обнаружен, то это условный знак программе. Тогда она удаляет себя из автозагрузки и больше не будет запускаться сама, пока ее снова не запустят вручную. Это очень удобно — если надо, чтобы программа автоматически загружалась, создаем файл dates.txt и запускаем один раз Noform.exe. Если надо убрать из автозагрузки, нужно просто удалить dates, txt, не трогая саму программу.
Глава 13. Эффектные формы .Opera - ( П О З Д Р А В Л Е Н И Е )
rie НрШ Prinl New
:
Si JBb -'/bcalhosl/C.7Stve[/MikePil«/Tjsk02 • bo -| Go j (
Юбиляр Поздравляем!!! С ДНЕМ РОЖДЕНИЯ!!
IQ П О З Д Р А В ЛЕН...
Рис. 13.6. Поздравление от программы
Вот и все. Теперь разберемся с Delphi-кодом. После запуска происходит помещение программы в автозагрузку Windows. Для этой цели используется реестр (см. гл. 20). В автозагрузку программа вписывает себя в любом случае при каждом запуске. Затем определяем дату с помощью функции Date. Функция возвращает результат в переменную типа TDateTime. Для преобразования дня, месяца и года в удобную нам форму использована процедура DecodeDate. После работы этой процедуры текущая дата содержится в переменных day, month, year в числовом виде. Существуют также функции Time и DecodeTime, предназначенные для аналогичного преобразования времени. После определения текущей даты читается файл dates. txt. Проверку на наличие файла я сделал при помощи loresult. Можно было для этого использовать функцию FilesExists, но в данном случае больше подходит loresult 3 . При использовании FilesExists, если файл есть, но по какой-то причине не открывается (например поврежден), то программа выдаст ошибку и будет выдавать постоянно при каждой перезагрузке. А при loresult программа просто удалит себя из автозагрузки и больше не будет докучать. Как говорится, почувствуйте разницу. При совпадении дат создается HTML-файл с именем OUTx.html, где х — это номер поздравления за день. Если поздравлений на сегодня, например, пять, то будет создано пять файлов. Имена у файлов должны различаться. Именно для этого нужны переменные kolvo и st. Подробно работа с функциями FileExists и loresult описана в гл. 8.
Программы-«невидимки» При создании HTML-файла правильнее было бы использовать шаблоны и из них составлять файл. Однако в данном случае это было бы неуместно, потому что важно, чтобы файл Noform.exe работал сам без большого количества внешних файлов. Поэтому я пошел на такой непопулярный шаг, как создание файла построчно, с HTML непосредственно в программе на Delphi. После создания открываем полученный HTML-файл в браузере. Используем для этого ShellExecute. Как правило, в любой правильно работающей операционной системе при обращении к HTML-файлу автоматически запускается браузер. Поэтому я не стал явно обращаться, например, к Opera, а просто сделал вызов созданного HTML-файла. Система сама разберется, что запускать. Естественно, будет запущен браузер, который используется по умолчанию. Вот и все. Напомню, что описываемая программа предназначалась для иллюстрации создания приложения без форм. Также мы рассмотрели создание незаметной для пользователя программы, которая работает, запускаясь при каждой перезагрузке Windows, и до поры до времени никак себя не проявляет. Однако если у пользователя (или еще у кого-то, записанного в dates. txt) день рождения, программа проявляет себя через браузер. Не каждый пользователь догадается, откуда это все берется. Эта программа должна работать на компьютере с любой операционной системой (я имею в виду только разные версии Windows, о Linux речь не идет). Потому что ничего недозволенного она не делает, и даже осторожные Windows 2000 и Windows ХР не будут против этой программы. Готовое приложение и полные листинги рассмотренного примера вы можете найти на компакт-диске, прилагаемом к книге, по адресу ExamplesXNof orm-new. Если будете испытывать эту программу на чьем-нибудь компьютере, будьте осторожны. Если бы кто-нибудь преподнес мне такой, пусть и безобидный, но сюрприз, я бы не обрадовался.
Глава 14
Проигрывание звуковых записей Q Как «поселить» звук в программе Q Проигрывание музыки в формате МРЗ Q Видеопроигрыватель и видеокнопка
Как «поселить» звук в программе
14.1. Как «поселить» звук в программе Краткое описание Вы не замечали, что звук иной раз преображает компьютерную игру или заставку? Наверное, замечали. Когда в программе присутствуют звуки, она становится гораздо интереснее. Добавить звуки в Delphi-программу очень просто. Для этого предназначен компонент MediaPlayer, использование которого интуитивно понятно. Компонент расположен на вкладке System. Когда вы добавляете его на форму, по умолчанию он видим (свойство Visible=True). Я ни разу не оставлял его видимым в своих программах, потому что его внешний вид не подходил ни одной из них. Если внешний вид компонента не подходит для вашей программы, изменяйте свойство Visible на False. Проигрывать звуки и музыку MediaPlayer может вполне адекватно, даже будучи невидимым. Можно делать невидимыми и отдельные кнопки MediaPlayer. Изменять видимость кнопок можно при помощи свойства VisibleButtons. Рассмотрим некоторые значения свойства DeviceType, определяющего форматы данных, с которыми может работать MediaPlayer: • dtAVIVideo — видеоролики в формате AVI; • dtCDAudio — музыкальные файлы в формате AudioCD; • dtSequencer — MIDI-файлы (музыка встроенного синтезатора звуковой платы); • dtVideodisc — видеодиски; • dtWaveAudio — цифровой звук (файлы с расширением WAV). Обратите внимание, что MediaPlayer не может проигрывать МРЗ — ему неизвестен этот формат. Однако если в операционной системе установлен кодек для проигрывания МРЗ (или, например, MPEG4), то и MediaPlayer сможет проигрывать такие файлы. Если же этот кодек не установлен, то MediaPlayer бессилен. Кодек для проигрывания МРЗ пользователь может установить самостоятельно, однако ведь совершенно необязательно, что он это сделает. Стандартная способность проигрывать МРЗ на сегодняшний день есть только в Windows XP. Если-вы пишете программу, рассчитанную только на эту операционную систему, то можно использовать МРЗ в MediaPlayer. Однако я бы не советовал создавать программу только для Windows XP. Гораздо лучше, когда программа может проигрывать МРЗ и при этом работает даже в Windows 95. Как это сделать, читайте в разд. 14.2. Использование MediaPlayer напоминает использование бытового видеомагнитофона. Команды (методы) имеют такие же названия, например, MediaPlayerl .Play включит проигрывание загруженного в MediaPlayer файла. Перед вызовом методов MediaPlayer нужно загрузить в него проигрываемый файл: MediaPlayerl.FileName := ;
Глава 14. Проигрывание звуковых записей После этого можно применять команды. Наиболее часто используемые методы MediaPlayer: • Open — открывает программу для проигрывания файла и сам файл; • Play — запускает файл на проигрывание; • Pause — пауза. Если снова дать команду Pause, пауза снимается, проигрывание продолжается с места, на котором произошла остановка; • Stop — останавливает проигрывание; • Record — включает запись; • Eject — выдвигает лоток привода компакт-дисков. Наиболее часто применяются следующие свойства MediaPlayer: • AutoEnable — автоматически включает и выключает кнопки на компоненте MediaPlayer, если свойство Visible=True; • AutoOpen — если это свойство установлено как True, то MediaPlayer при запуске программы автоматически открывает устройство для проигрывания, соответствующее выбранному типу в DeviceType. Если AutoOpen=False, то открывать нужно принудительно с помощью метода Open; • Capabilities — определяет возможность использования мультимедиа-устройств. Возможные значения: mpCan Eject (устройство может извлекать носитель, например, компакт-диск), mpCanPlay (устройство может проигрывать звук или видео), mpCanRecord (устройство может записывать), mpCanStep (устройство имеет пошаговый режим проигрывания, например покадровый в видеролике), mpUsesWindow (устройство использует окно для проигрывания — как правило, для проигрывания видеороликов); • ColoredButtons — показывает, цветные или черно-белые должны быть кнопки на проигрывателе; • DevicelD — идентификационный номер (ID) текущего открытого устройства мультимедиа. Если ни одно устройство не открыто проигрывателем, это свойство равно нулю; • DeviceType — указывает, какой тип файлов мультимедиа проигрывать. Если присвоить этому свойству dtAutoDetect, то тип будет определяться автоматически; • Display — определяет, на каком компоненте-контейнере показывать видео, если проигрывается видеофайл. Например, в качестве проигрывателя можно использовать GroupBox; • EnabledButtons — показывает, включены или выключены конкретные кнопки проигрывателя; • FileName — имя файла, которое нужно проигрывать; • Length — длина открытого файла; • Mode — текущий режим проигрывателя. Возможные режимы: mpNotReady (не готов), mpStopped (остановлен), mpPlaying (проигрывает файл), mpRecording (записывает файл), mpPaused (пауза), mpOpen (открыт);
Как «поселить» звук в программе • Position — текущая позиция проигрывания открытого файла; • Shareable — если свойство Shareable установлено в False, то другие компоненты и приложения не могут обратиться к аудиоустройству, если оно уже используется одним компонентом или приложением. По умолчанию это свойство равно False; • TimeFormat — отвечает за формат, в котором выводится информация о длине файла и позиции проигрывания. Свойство используется вместе с StartPos, Length, Position, Start, EndPos. Устройства поддерживают не все форматы (например, формат, учитывающий кадры, возможен только для видео, но не для звуковых устройств). Возможные значения приведены в табл. 14.1; Таблица 14.1. Возможные значения TimeFormat
Значение tfMilliseconds tfHMS tfMSF tf Frames tfSMPTE24 tfSMPTE25 tfSMPTESO
Характеристика Миллисекунды Часы, минуты и секунды (Hours, Minutes и Seconds), целое четырехбайтное значение Минуты, секунды, кадры (Minutes, Seconds, Frames) Кадры Часы, минуты, секунды, кадры (Hours, Minutes, Seconds, Frames) Пакеты из 25 кадров Пакеты из 30 кадров
• Visible — показывает, отображать ли MediaPlayer на экране. Можно прятать сам компонент и использовать только его функции и свойства, а дизайн проигрывателя задавать самому либо вообще не использовать никакой дизайн (например, в игре вообще не нужно отображение проигрывателя, а нужен только сам звук); • VisibleButtons — скрывает отдельные кнопки компонента. Можно также использовать стандартные для большинства компонентов свойства: Cursor, Enabled, Height, Left, Top, Width.
Пример проигрывателя Вам не дают покоя лавры известных программ-проигрывателей: WinAmp, Sonique или Windows Media Player? Тогда можно попробовать создать свой проигрыватель. Возможно, он не станет настолько же популярным, но его несомненным плюсом будет то, что вы сделаете его сами, причем именно так, как хочется вам. Даром телепатии я, к сожалению, не обладаю, предвидеть, какой проигрыватель вы захотите создать, я не могу, поэтому просто опишу разработку проигрывателя. Разобравшись в этой программе, вы сможете сделать свою, даже если у вас нет опыта создания проигрывателей.
Глава 14. Проигрывание звуковых записей
N fie "?j
£tjil !
Search
yiewPjojecl
S • S ' iP S: • ;fc iS ^
fiun
Component
ualabase
Ipols
^ ^ Standard f Additional j Win32
^indow System j Data Access I Data Controls I dbEmressi OataSnao ] 8DE
•
Form! О
MediaHayeil
:JForm1
TK
STOP
FF>:
«ИВВН ''" ' '':-':'-•'-••••- ~Ш. ~v£?^-~
Рис. 14.2. Форма в режиме разработки
Надписи на этикетках я сделал просто для наглядности, так они будут выглядеть в режиме выполнения программы, а при запуске эти надписи все равно будут очищаться. В Labell (самый большой индикатор) будет показываться текущая позиция в файле. В Label2 (справа сверху) должно отображаться имя открытого файла В Labels (справа в середине) — длина открытого файла. В Label4 (справа снизу) — сколько осталось времени до конца файла. Ширина формы (Width) в итоге должна быть равна 309 пикселов, а высота (Height) — 109 (рис. 14.3). Однако в режиме разработки удобнее временно увеличить ширину и высоту формы (см. рис. 14.2). В таком случае можно, например, отодвинуть в сторону компоненты, которые не видны.
Рис. 14.3. Форма в конечном итоге должна выглядеть так
Интерфейс готов, но прежде чем начать писать код, добавим нашему проигрывателю еще одну особенность. Когда пользователь будет подводить указатель мыши к какой-либо кнопке, указатель будет принимать форму руки. Для этого свойство Cursor каждой из кнопок нужно поменять с crDefault на crHandPoint. Можно запустить программу на выполнение и посмотреть, что получилось. Результат внешне напоминает советские магнитофоны середины 1980-х годов. Можно, конечно, проявить фантазию и разработать интерфейс, напоминающий
Глава 14. Проигрывание звуковых записей китайские магнитофоны середины 1990-х или даже японские начала 2000-х годов. Я же обошелся стандартными средствами. Перейдем к написанию кода. Сначала инициализируем все необходимое в FormActivate и затем реализуем возможность открытия звуковых файлов с помощью обработчика нажатия кнопки Buttonl (на которой написано OPEN). Листинг 14.1
procedure TForml. FormActivate(Sender: TObject); begin // инициализация этикеток Label LabeM.Font.Color :=clGreen; Label 2. Font. Co I or :=clGreen; Labels. Font. Co I or :=clGreen; Label 4. Font.Co I or := cIGreen; Labe11.Caption := 00:00; Label2.Caption := no fi le...; LabelS.Caption := 00:00; Labe14.Caption := 00:00; // задание фильтра для OpenDialog OpenDialogl.Fi Iter := Wav fi les (*.wav)i*.WAV;
end; Эта процедура выполняется при запуске программы. Содержимое индикаторов устанавливаются в нуль, задается темно-зеленый цвет, благодаря чему в режиме ожидания у нашего проигрывателя, как и у самых популярных программ, индикаторы будут бледнеть. Здесь также задается фильтр для окна открытия файлов. Чтобы в этом окне не отображались ненужные форматы, допускается только один тип файлов, которые может воспроизводить проигрыватель, — WAVE. Теперь определим процедуру, которая выполняется при открытии звукового файла. Листинг 14.2
procedure TForml.ButtonlCI ick(Sender: TObject); // кнопка OPEN
beg in i f OpenO i a Iog1.Execute=True then begin // открываем файл MediaPlayert.Fi leName := OpenDialogl.Fi leName; MediaPlayerl.Open; // включаем индикаторы LabeM.Font.Color : = c l L i m e ; Labe 12. Font. Co I or : = c l L i m e ; LabelS.Font.Co I or := c I L i m e ;
Как «поселить» звук в программе Label 4. Font.Co lor := cILime; // присваиваем индикаторам значения LabeИ.Caption := lntToStr(MediaPlayer1.Position); Label2.Caption := MediaPlayerl.Fi leName; Labe13.Caption := IntToStr(MediaPlayerl.Length); Labe14.Caption := lntToStr(MediaPlayer1.Length MediaPlayerl.Position);
end; end; Практически все содержимое этой процедуры выполняется при условии, что пользователь действительно выбрал какой-то файл. Если он нажал кнопку Отмена в диалоге открытия файлов, то функция OpenDialogl. Execute вернет False, и все, что расположено между begin и end, выполнено не будет. Если запустить программу на выполнение и попробовать открыть какой-нибудь файл, то можно заметить, что значения индикаторов не соответствуют характеристикам открываемого файла. Дело в том, что значения указаны в миллисекундах. Перепишем процедуру так, чтобы отображаемая на индикаторах информация соответствовала действительности, а также вынесем вывод значений на индикаторы в отдельную процедуру Output. Листинг 14.3
procedure TForm1.Button1CIick(Sender: TObject); // кнопка OPEN begin if OpenDialogl.Execute=True then
beg i n // открываем файл MediaPlayerl.Fi leName := OpenDialogl.Fi leName; MediaPlayerl.Open; // включаем индикаторы Labe11.Font.Co I or := с IL i me; Label2.Font.Color := cILime; LabelS.Font.Co I or := cILime; Label4.Font.Color := cILime; // присваиваем индикаторам значения Output;
end; end; i Теперь создадим еще одну новую функцию MusicToStr, а также процедуру Output, которая преобразует длину файла, текущую позицию или количество оставшегося до конца звучания времени в форму, легкую для восприятия. Преобразовывать миллисекунды в минуты и секунды будем не с помощью свойства TimeFormat, а проводя несложные вычисления, так как возвращаемые свой-
Глава 14. Проигрывание звуковых записей ством TimeFormat значения сильно зависят от формата проигрываемого файла. Изза этого часто могут получаться неправильные значения, например, вместо 0 часов, 0 минут, 1 секунды результат может быть таким: 17 часов, 7 минут и 0 секунд. Если же получать значения в миллисекундах и переводить их в минуты, то таких неточностей удастся избежать. Листинг 14.4 function TForm1.MusicToStr(n: Longint): String; // процедура преобразует длину файла, // текущую позицию или количество оставшегося // до конца звучания времени в String // вместо п здесь, соответственно, значение // переменных posit, leng или remain
var s: String; // рабочая переменная для формирования // удобочитаемых значений индикаторов проигрывателя sec: Longint; m i n : Longint; beg in
// получаем секунды и минуты из миллисекунд (п) sec := trunc(n/1000); m i n := trunc(sec/60); sec : = sec - min*60; s := IntToStr(min); // добавляем в строку 0, если секунд меньше десяти // чтобы получалось 2:06, а не непонятное значение 2:6; if sec Макрос > Начать запись). Затем выполняем все те действия, о которых хотим узнать. После чего останавливаем запись макроса и смотрим его текст в редакторе Visual Basic. Для просмотра исходного текста макроса выполните команду Сервис > Макрос > Макросы, выберите только что записанный макрос и нажмите кнопку Изменить. Только не забывайте, что синтаксис, естественно, у некоторых команд другой (мы же пользовались VBA, а не Delphi), однако разобраться можно. Теперь займемся остальными тремя кнопками, причем рассмотрим их сразу в одном листинге.
Управление Word и Excel из другой программы Листинг 15.4 procedure TForml.Button3CIick(Sender: TObject); // добавление таблицы begin Wrd.ActiveDocument.Tables.Add(Wrd.Select ion.Range,2,2);
end; procedure TForml.Button4CIick(Sender: TObject); // сохранение документа begin Wrd.Act i veDocument.SaveAs(c:\DeIph i123.doc);
end; procedure TForml.ButtonSCI ick(Sender: TObject); // закрытие Word begin Wrd.Quit;
end; Этот листинг практически не нуждается в комментариях. Если метод Quit не вызвать, то открытый Word не закроется, даже если закрыть программу, которая его вызвала. Это не так критично, если вы открываете Word в видимом режиме. Если же в невидимом, то важно не забывать закрывать Word методом Quit, иначе в памяти может остаться несколько открытых Word. Они будут занимать ресурсы и замедлять работу всей системы. Результат работы программы показан на рис. 15.3. l-IOIxl
U.' Microsoft Wotd - Документ 1 Р^ ^айл Цравка &ид Вставка Формат Сервис Хаблмца QKHO 2
ifbj* ej i» [& !£ U * 'J! Обычный
- TimesNewRoman
- 12
- 1 [ ж ! / С Н i|S в'З'В i 1= := ч^ t£ •10 • I • 11 • I • 12 • I -13 •
Горячий привет яшйШШШК 1ййВУ Подпись:
р Действия- Cj Q ;Стр'. i
рвд'Т"
от
соседней программы
АетоФигары- Ч Ч ПО В -4 1/1
JHa 5,4см
Ст S
кЬл"1б'
Рис. 15.3. Результат работы программы, написанной на Delphi
•з
J
Глава 15. Трюки с Word и Excel Готовое приложение и полные листинги рассмотренного примера вы можете найти на компакт-диске, прилагаемом к книге, по адресу ExamplesXoleWord. i
Excel
:
Работа с Excel строится по такому же принципу, как и с Word, а методы и свойства у Excel немного отличаются. На рис. 15.4 показана форма программы управления Excel, а в листинге 15.5 описан принцип работы с ней.
Gol Оом
Рис. 15.4. Форма программы для управления Excel Листинг 15.5
var
Forml: TForml; e x l : OleVariant; i.mplementat ion {$R *.dfm} procedure TForml.ButtonlCI ick(Sender: TObject); // открываем Excel beg i n // создаем объект exl := СreateOIeObj ect(ExceI.AppI i cat ion); // делаем видимым e x l . V i s i b l e := True; // добавляем новую таблицу exl .AppI icat ion.Workbooks.Add; // так можно ввести текст в ячейку exl. Appl i cat ion. Worksheets. 1т.ет[Лист1]. Cel Is. ltem[5,1].FormulaR1C1 := PDP-6; // а так сделать начертание текста полужирным exl.Appl icat ion.Worksheets.11ет[Лист1]. CelIs.ltem[1,1].Font.Bo Id := True;
Управление Word и Excel из другой программы // так можно ввести формулы в ячейки e x l . A p p l icat ion.Worksheets.11ет[Лист1]. Gel Is. lten)[1,1].FormulaR1C1 := =18*2; end; procedure TForm1.Button2CIick(Sender: TObject); // закрываем Excel begin exl.Quit; end; Готовое приложение и полные листинги рассмотренного примера вы можете найти на компакт-диске, прилагаемом к книге, по адресу Examples\01еЕхсе1.
Другие СОМ-серверы Создавать объекты, поддерживающие технологию OLE, можно, используя не только Word и Excel, но и любые другие СОМ-серверы. Например, в следующем листинге описано, как запустить Photoshop, если он установлен на вашем компьютере. Листинг 15.6 procedure TForm1.Button1CIick(Sender: TObject); var phshop: OleVariant; begin phshop := CreateOleObject(Photoshop.Appl ication); end; Можно изучить реестр Windows (только действуйте очень аккуратно, не изменяйте незнакомые опции). Например, в разделе HKEY_CLASSES_ROOT можно найти разнообразные СОМ-серверы. Если у какой-либо программы в реестре есть параметр CLSID, то это сервер.
Глава 16
Программирование для Интернета Q Разрабатываем браузер Q Трюки с браузером Internet Explorer Q Письмецо в конверте
Разрабатываем браузер
16.1. Разрабатываем браузер Начиная с пятой версии в Delphi появился компонент WebBrower, который позво4 ляет написать свой собственный браузер . Однако созданный таким образом браузер будет использовать Internet Exlporer, встроенный в Windows, и поэтому будет очень похож на этот браузер. Можно полностью поменять интерфейс, но принцип работы все равно будет, как у Internet Explorer. Конечно, если писать все с нуля и вручную, можно создать какую угодно программу, в том числе и совершенно автономный браузер. Программисты, у которых много свободного времени, могут попробовать сделать и таким образом, а мы поступим по-другому. Если же вы хотите вставить в свою программу браузер, компонент WebBrowser очень подойдет. WebBrowser находится на вкладке Internet. Нужно просто перетащить его на форму и увеличить до нужных размеров. Внешне компонент напоминает Memo или RichEdit, только без рамки. Можете сразу открыть какой-нибудь HTML-документ с жесткого диска своего компьютера (если у вас, конечно, такой найдется) и убедиться, что браузер действительно работает. В процедуре активации формы FormActivate напишите следующие строки: WebBrowserl.Offline := True; WebBrowserl.Navigate('c:\File.htm');
Первая строка переключает браузер в автономный режим. Это делается для того, чтобы браузер не пытался постоянно подсоединиться к Интернету, находясь в неподключенном к Интернету режиме. Вторая строка открывает в браузере нужный HTML-документ. Вместо с: \File. htm, разумеется, нужно написать путь к вашему файлу. После этого можно запустить проект на выполнение и посмотреть, как HTML-файл открылся в окне нашей Delphi-программы. Убедившись, что браузер работает, можно продолжать его разработку. Назовем наш браузер Gamadrillo. Почему Gamadrillo? А почему бы и нет? Есть же Mozilla! Вот, по аналогии. Поместите на форму помимо находящегося там WebBrowser текстовую строку Edit и кнопку Buttonl. Размещение этих компонентов особого значения не имеет. 4
Браузер вполне возможно создать и в более старых версиях Delphi, но там это сделать немного сложнее. Например, нужно импортировать библиотеку с помощью команды Project > Import Type Library.
Глава 16. Программирование для Интернета На данном примере я хочу также показать, как делать окно программы, которое может изменяться в размерах, то есть расположение, ширина и высота всех компонентов при изменении размеров формы будут автоматически пересчитываться. В дополнение к основным компонентам поместите на форму компонент ToolBar с вкладки Win32. По умолчанию ToolBar располагается в верхней части формы. На нем будут находится кнопки управления браузером: Назад, Обновить, Стоп, Домашняя страница, Вперед. Высоту нужно изменить, мелкие кнопки в браузере — это уже «не модно». Установите свойство Height компонента ToolBar равным 52 пикселам. Затем можно добавлять на ToolBar кнопки. Для этого щелкните правой кнопкой мыши на компоненте ToolBar и выберите в меню строку New Button. Разделитель между кнопками добавляется, как и кнопки, только нужно выбрать строку New Separator. Итак, нужно добавить на ToolBar пять кнопок. Удобнее всего добавить сначала одну кнопку. Затем изменить ее размеры (Width = 67, Height = 47), а затем добавить еще четыре кнопки. В таком случае последние четыре кнопки сразу будут нужного размера. На кнопках нужно разместить картинки. Можете нарисовать их в графическом редакторе (например, Paint или Photoshop), затем загрузить получившиеся картинки в ImageList и назначить картинки кнопкам. Это можно сделать с помощью свойства Images компонента ToolBar. Сами же кнопки имеют свойство Imagelndex, которое позволяет назначить кнопке изображение по его номеру в ImageList. После размещения компонентов на форме она стала похожа на показанную на рис. 16.1.
Buttonl
Рис. 16.1. Полученная форма
Если у вас выглядит не так, не расстраивайтесь — все компоненты будут передвигаться на нужные места уже в процессе работы программы. Приступим к ее написанию.
Разрабатываем браузер Удалите из FormActivate две строки, написанные в начале данного раздела, и приведите процедуру FormActivate к виду, показанному в следующем листинге. Листинг 16.1
procedure TForml. FormActivate(Sender: TObject); begin // инициализация Form"!.Caption : = Gamadri I lo; Editl.Text := ; Button"! .Caption := Фас! ; Editl.TabOrder := 0; Button"!. Default :=True; Buttonl.TabOrder := 1; WebBrowserl.TabOrder := 2; Editl.SetFocus; end; Здесь выполняются все те действия, которые можно задать и при визуальном программировании. Однако в программе, размеры окна которой пользователь может изменять, все действия такого рода гораздо удобнее писать в виде кода, а при визуальном программировании просто почти беспорядочно разместить компоненты на форме. Компоненты будут размещаться по местам, а также изменять свои размеры при изменении размеров окна (формы). Все это зададим при помощи процедуры FormResize. Листинг 16.2
procedure TForml. FormResize(Sender: TObject); begin // меняем расположение компонентов Editl.Left := 0; Edit"!. Top := Too I Bart. Height + 2; Buttonl.Top := Editl.Top; Buttonl.Left := Forml.CI ientWidth - Button!.Width - 2; WebBrowser!. Left := 0; WebBrowser!.Top := Editl.Top + Edit!.Height + 2; // меняем размеры некоторых компонентов E d i t ! . W i d t h := Forml.CI ientWidth - Button!.Width - 4; Button!.Height := Edit!.Height; WebBrowser!.Width := Forml.CI ientWidth; WebBrowser!.Height := Forml.CI ientHeight (Editl.Top + Edit!.Height + 2); end; Все размеры вычисляются относительно размеров окна — F o r m l . c i i e n t w i d t h и Forml.ClientHeight.
Глава 16. Программирование для Интернета Теперь напишем обработчики событий нажатий кнопок, и наш браузер будет готов. i Обработка событий кнопки Buttonl выполняется при помощи процедуры Navigate. Данная процедура либо загрузит какую-то страницу из Интернета (если указать URL), либо откроет файл с диска (если указать путь). Кстати, записывать http: // в адресной строке нашего браузера необязательно. Хотя, возможно, это зависит от браузера, используемого в операционной системе. Можно для надежности указы1 вать ВFormActivate НС E d i t l . T e x t := ' ' ;, a Editl.Text := ' h t t p : / / ; . Итак, рассмотрим обработку нажатия кнопки, при помощи которой загружается страница. Листинг 16.3
procedure Tforml. ButtonlCI ick(Sender: Tobject); beg i n WebBrowserl.Navigate(Editl.Text); Editl.SetFocus; end; Процедура Navigate позволяет браузеру загружать страницу из Интернета или с диска, a SetFocus переводит фокус клавиатуры назад в адресную строку. Далее описываем обработку нажатий кнопок на панели ToolBar. Для обработки каждой кнопки достаточно одной строки кода, то есть соответствующей команды браузера. Листинг 16.4
procedure TFormt.ToolButtonlCI ick(Sender: TObject); beg i n WebBrowserl.GoBack; //назад end;
procedure TForm1.ToolButton2CIick(Sender: TObject); begin WebBrowserl.Stop; // остановить загрузку end; procedure TForm1.ToolButton3CI ick(Sender: TObject); begin WebBrowserl. Refresh; //обновить end;
procedure TForm1.ToolButton4CI ick(Sender: TObject); begin
Разрабатываем браузер WebBrowserl.GoHome; // на домашнюю страницу end; procedure TForml.ToolButtonSCIick(Sender: TObject); begin WebBrowserl.GoForward; //вперед end; Здесь приведены сразу несколько процедур — обработчиков событий кнопок на панели ToolBar. Если запустить такую программу, то получится работающий браузер. Произведем кое-какие улучшения. Например, неудобно открывать HTML-документ с диска — если в документе встречаются какие-либо ссылки (например, рекламные баннеры), то браузер сразу же пытается соединиться с Интернетом, выводя окна подключения и разные предупреждения. Если нажать кнопку Назад, когда на предыдущую страницу нельзя попасть, то появляется сообщение об ошибке. Попробуем исправить эти недочеты. Если на предыдущий документ нельзя переместиться, то кнопку «Назад» можно отключить так, как показано в следующем листинге. Листинг 16.5
procedure TForm1.ToolButton1CIick(Sender: TObject); begin try WebBrowserl .GoBack; //назад except ShowMessage(He могу идти назад, за нами Москва!); end; // try end; В данном листинге сначала пробуем (при помощи try) дать браузеру команду возвратиться на страницу назад — GoBack. Если не получается, то сообщение об ошибке не появляется, а выполняется код, следующий за ключевым словом except, который выводит на экран строку: «Не могу идти назад, за нами Москва!» Можно вообще не выводить сообщение, а поступить как-нибудь по-другому. Автоматически переключать браузер в автономный режим, если открывается файл с диска, можно следующим способом. Если открывается локальный файл, то в адресной строке будет написано, например, с: \File. htm или а: \Docs \otchet. html, то есть при открытии в браузере HTML-файла с диска в пути указано имя этого диска, затем двоеточие. Именно двоеточие в качестве второго (обратите на это внимание) символа в адресной строке и будем искать. Если оно есть, то включаем автономный режим, если двоеточия нет — выключаем. Перед этим обязательно нужно проверять, имеет ли
Глава 16. Программирование для Интернета адресная строка хотя бы два символа, иначе при попытке обратиться к несуществующему второму символу возникнет ошибка. Листинг 16.6
procedure TForm1.Button1CI ick(Sender: TObject); begin // проверка, откуда загружается файл if length(Edit1.Text)>1 then begin if Edit1.Text[2]=: then WebBrowserl. Off I ine := True e l s e WebBrowserl.Off I ine := False; end; // загрузка документа WebBrowserl.Navigate(Edit1.Text); Editt.SetFocus; end; Теперь, если попробовать открывать документы с диска и из Интернета, то браузер включает и выключает автономный режим. На рис. 16.2 показан получившийся работающий браузер. •TZTnlxl
/• г Gamadrilto
c:\wiahtm
Фес!
проблем не знаю, а вообще пакетный режим это прога Direct CD идет в комплекте с CD Creatorom, скачай с внета последние версии и пользуйся. Nero Burning ROM 5.5.6.4 к нему идет дополнение IN CD, он не совместим с Direct CD, но работает по тому же принципу форматируется CDRW из 700 метров отсается примерно 580 и работай дальше с этим диском в проводнике. , Ответ от Aliene : Ставь Неро из последних 5.5.9.0 с русификатором. Никаких проблем. М.ооотт,.,™,.™* г,„„.г, ТЮГТГлиПТТЛТСИП
d
zl
Рис. 16.2. Работающий браузер
Готовое приложение и полные листинги рассмотренного примера вы можете найти на компакт-диске, прилагаемом к книге, по адресу Examples\Browser.
16.2. Трюки с браузером Internet Explorer В этом разделе сделаем программу, которая будет работать «пультом дистанционного управления» браузером Internet Explorer.
Трюки с браузером Internet Explorer Основываться эта программа будет на технологии СОМ (см. гл. 15). Создадим OLE-объект Internet Explorer. Разместите на форме девять кнопок и одну текстовую строку. Можно в полном беспорядке, как показано на рис. 16.3.
Buttons ];;::: Button4 ] : ; ; ; Button?
Рис. 16.3. В режиме разработки компоненты на форме расположены беспорядочно Поменяйте только четыре свойства формы, указанные в табл. 16.1. Таблица 16.1. Значение свойств формы Form"! Свойство
Значение
BorderStyle
bsSingle
Borderlcons bi'Maximize
False
Position
poDesktopCenter
FormStyLe
fsStayOnTop
Последнее свойство FormStyle нужно изменить для того, чтобы форма всегда располагалась поверх всех окон, иначе будет неудобно управлять Internet Explorer. Начнем писать программу. Причем располагать компоненты в нужном порядке будем с помощью процедуры активации формы. Листинг 16.7
procedure TForml. FormAct ivate(Sender: TObject); var i : Integer; // счетчик в цикле // массив для надписей к кнопкам САР: аггау[0..7] of String[10];
Глава 16. Программирование для Интернета
begin // задание свойств всех компонентов // задание свойств основных кнопок САР[0] = OPEN; САР[1] = БАСК; САР[2] = НОМЕ; САР[3] = STOP; САР[4] = REFRESH; САР[5] = FORWARD; САР[6] = CLOSE; САР[7] = ABOUT; for i := 0 to 7 do beg in case i of 0: Sender = Button"!; 1: Sender = Button2; 2: Sender = ButtonS; 3: Sender = Button4; 4; Sender = ButtonS; 5: Sender = Button6; 6: Sender = Button?; 7: Sender = ButtonB; end; // case with Sender as TButton do begin Left :F i*79; Top := 0; Width := 79; Height ;= 60; Caption := CAP[i]; Font.Size := 10; end; // with end; // for // задание свойств формы Form"!.Caption := IE- Remote Control; Forml.CI ientWidth := Button!.Width * 8; Forml.CI ientHeight := Buttonl.Height + Edit1.Height; // задание свойств текстового поля и кнопки Go! Editl.Top := Buttonl.Height; Editl.Left := 0; Editl.Width := Forml.CIientWidth - Buttons.Width; Editl.Text := ; Button9.Top := Editl.Top; ButtonQ.Height := Editl.Height;
Трюки с браузером Internet Explorer Button9. Left := Forml.CI ientWidth - ButtonQ. Width; Button9. Caption := Go! ; Buttong. Default := True; Editl.SetFocus; end; Практически весь интерфейс создается процедурой TForml . FormActivate. Самым интересным фрагментом здесь является присвоение свойств первым восьми кнопкам. Можно было пойти простым путем и задавать их свойства так, как показано в следующем листинге (показаны только первые две кнопки, а не все восемь): Листинг 16.8 Buttonl. Left := 0; Buttonl. Top := 0; Buttonl. W i d t h := 79; Buttonl. Height := 60; Buttonl. Caption := OPEN; Buttonl. Font. Size ;= 10; Button2. Left := 79; Button2.Top := 0; Button2. W i d t h := 79; Button2. Height := 60; Button2. Caption := BACK; Button2. Font. Size := 10; Как видите, здесь все сделано простым перечислением кнопок, однако мне кажется, что при помощи Sender получается более изящно. Кроме того, намного сокращается количество строк программы. Если разобраться в методе, то и читается программа так же легко, как если бы все было решено простым способом. Работает программа следующим образом. *
В цикле, где i изменяется от нуля до семи, поочередно назначается в качестве Sender одна из кнопок (сначала это Buttonl, потом Button2 и т. д.). Затем изменяются свойства текущей кнопки через Sender. Таким образом, свойства всех кнопок можно задать в цикле. Разобравшись с интерфейсом, приступим к управлению браузером. Объявим переменную IE (это и будет наш браузер) в разделе var, общем для всех процедур. Листинг 16.9 var Form"!: TForml; IE: OleVariant; //наш Internet Explorer
Открывать Internet Explorer будем кнопкой OPEN, закрывать — кнопкой CLOSE.
Глава 16. Программирование для Интернета Листинг 16.10 procedure TForm1.Button1CIick(Sender: TObject); // кнопка OPEN begin // создаем объект IE := Created I eObject( Internet Explorer. Appl icat ion); // и делаем его видимым I E . V i s i b l e := True;
end; procedure TForm1.Button7CI ick(Sender: TObject); // кнопка CLOSE
beg i n IE.Quit;
end; Настолько подробных комментариев, как в приводимых листингах, в обычных программах, наверное, не стоит писать. Здесь комментарии служат для того, чтобы вы лучше поняли структуру и принцип действия программы. Остальные кнопки будут выполнять обычные для браузера действия (переход на предыдущий документ, переход на домашнюю страницу, остановку загрузки, обновление, переход на следующий документ). Приведу их одним листингом без подробных пояснений, тем более аналогичные действия производились в разд. 16.1. Листинг 16.11 procedure TForml. Button2CI ick(Sender: TObject); // кнопка BACK beg in try lE.GoBack; // назад except Зпо\»/Мез5аде(Нельзя перейти на предыдущую страницу.); end; // try end; procedure TForm1.Button3CI ick(Sender: TObject); // кнопка HOME begin lE.GoHome; // на домашнюю страницу
end; procedure TForml. Button4CI ick(Sender: TObject); // кнопка STOP
beg i n IE.Stop; // остановить загрузку
end;
Трюки с браузером Internet Explorer procedure TForml.ButtonSC I ick(Sender: TObject); // кнопка flEFRESH begin
IE.Refresh; // обновить страницу
end; procedure TForml.Button6CI ick(Sender: TObject); // кнопка FORWARD begin
try lE.GoForward; // вперед except 8по\л/Меззаде(Нельзя перейти на следующую страницу.); end; // try end;
Обработку нажатия кнопки ABOUT рассмотрим в разд. 16.3. На рис. 16.4 показана получившаяся программа управления браузером. Обратите внимание, что в режиме выполнения кнопки выстроились в ряд. «Kioto»;
о т
Остановить
Обновить
Домой;
Г
л
Поиск
Избранное
136 k свободно
Журнал
V 'if. - Диствнциинное управление BACK
OPEN
HOME
STOP
REFRESH FORWARD
aose
ABOUT
| 1 | !
*.
*JCh2_04 ШСЬ2_05 :*YJCh2_06
doc doc doc
:Ц..|
30-05.200421:53—
РгодесП
exe 424360 11.05.200416:29 :a-:
k02 • book\WORK_FILES\Examplet\Di«l IE2> [^ F3 Просмотр
\
F4 Правка
:
F5 Копня
;
FE Первмещ
Рис. 16.4. Программа управления браузером
!
F7 СоадКат
F8 Удалить
AH+F4 Выход
Глава 16. Программирование для Интернета Мы не рассмотрели лишь одну кнопку — Go! Нажатием на нее должен вызываться метод Navigate. Необходимо также сделать возможность автоматического переключения в автономный режим, как это было выполнено для браузера в разд. 16.1. Можно позаимствовать код оттуда, немного переделав его. Листинг 16.12 procedure TForml. ButtonQCI ick(Sender: TObject); // кнопка Go! begin // проверка, откуда загружается файл if length(Edit1.Text)>1 then begin if Edit1.Text[2]=: then IE.Off I ine := True e l s e IE.Off I ine := False; end; // загрузка документа lE.Navigate(Editl.Text); Editl.SetFocus; end; Запускаем приложение и наслаждаемся управлением браузером при помощи нашей программы. Готовое приложение и полные листинги рассмотренного примера вы можете найти на компакт-диске, прилагаемом к книге, по адресу Examples\Dist IE.
16.3. Письмецо в конверте Мы уже можем управлять браузером Internet Explorer. Добавим полученной программе возможность написать электронное письмо разработчику браузера. Очень просто сделать так, чтобы нажатием на кнопку автоматически открывался почтовый клиент (например, Outlook Express, The Bat! и др.) и автоматически создавалось электронное письмо с вписанными адресом и темой. Для этого достаточно одной строчи кода: ShellExecute(0,'open','mailto:
[email protected]','','',SW_SHOW); Для работы этой функции нужно добавить в раздел uses модуль ShellApi, иначе программа не будет компилироваться. Указанная команда запускает почтовый клиент, который установлен в системе по умолчанию.
Письмецо в конверте В данном случае откроется создание нового письма адресату
[email protected]. Можно автоматически вставлять также тему письма. Делается это следующим образом: ShellExecute(0,'open', 'maiIto:
[email protected]?subj="Текст"' , 1
', '',SW_SHOW);
Фрагмент ?subj="TeKCT" говорит почтовому клиенту о том, какие именно слова нужно вставить в поле Тема. Если интерфейс почтового клиента англоязычный, то поле называется Subj (от имени этого поля и произошло название команды). Можно добавить возможность создания письма в программу из разд. 16.2. Как вы помните, в ней осталась не задействована кнопка ABOUT, хотя и добавлена на форму. Сделаем так, чтобы при нажатии на эту кнопку открывалось дополнительное окно с информацией о создателях программы и адресом электронной почты. При щелчке на адресе будет открываться почтовый клиент. Помимо кнопки в проект нужно добавить форму, которая будет появляться при нажатии на кнопку ABOUT. Форму в проект можно добавить, щелкнув на кнопке New Form или выполнив команду меню File > New > Form. Задайте этой форме свойства, указанные в табл. 16.2. Таблица 16.2. Значение свойств формы Form2
Свойство
Значение
Для чего изменили
Borderlcons biMinimize
False
Запрещаем пользователю сворачивать эту форму
Borderlcons biMaximize
False
Запрещаем пользователю разворачивать форму на весь экран
BorderStyle
bsSingle
Caption
About
Position
poDesktopCenter
Height
200
Width
400
Интерфейс рассматриваемой программы англоязычный, поэтому и данная кнопка должна иметь английское название
На форму нужно поместить четыре этикетки Label, после чего можете заполнить их так, как показано на рис. 16.5, а можете и не заполнять — это не принципиально.
Глава 16. Программирование для Интернета Чтобы было видно, что Label4 — это ссылка, нужно изменить шрифт на синий с подчеркиванием, а также изменить свойство Cursor компонента Label4 на crHandPoint.
ТЕ CONTROL for Internet Explorer||| : Wnier & Reader:-
Рис. 16.5. Полученная форма Поместите на форму кнопку Buttonl, которая будет закрывать окно About. Если установить свойства кнопки Default и Cancel в True, то когда пользователь нажмет клавишу Enter или Esc, сработает кнопка Buttonl, что весьма удобно. Рассмотрим код обработки события OnClick. Листинг 16.13 procedure TForm2.Button1CI ick(Sender: TObject); begin Form2. Close; end; Создаем электронное письмо при помощи La be 14. Пишем обработчик события OnClick для Label4. Листинг 16.14 procedure TForm2. Label4CI ick(Sender: TObject); begin
She IIExecute(0,open, mai I to:vasya@mylo.ги?зиЬ]="Текст", ,,SW_SHOW); end; Разумеется, надо как-то открывать полученное окно. Для этого напишем обработку события OnClick кнопки ABOUT. Листинг 16.15
^
*
procedure TForml.ButtonSCIick(Sender: TObject); // кнопка ABOUT
Письмецо в конверте begin Form2.ShowModal; end; После этого при первой компиляции программы Delphi задаст вопрос: «Form 'FormГ references from 'Form2' declared in unit 'Unit2' which is not in your USES list. Do you wish to add it?» Если перевести эту фразу на русский язык, то получится: «Форма Forml ссылается на форму Form2, объявленную как Unit2, которая отсутствует в разделе USES. Хотите ли вы добавить ее в USES?» Разумеется, нужно ответить Yes. Можно, конечно, и вручную вписывать Unit2 в uses, но, согласитесь, нажать один раз Yes — гораздо проще и быстрее, а результат получается аналогичный. Готовое приложение и полные листинги рассмотренного примера вы можете найти на компакт-диске, прилагаемом к книге, по адресу Examples\Dist IE.
-
9 Зак. 899
Глава 17
Вывод графики с помощью GUI Q G Q Q U Q
GUI и Canvas Выбор технологии для экранных трюков Вывод текста на экран Загрузка изображений в ImageList Эффектно выводим графику на экран Рисуем на Рабочем столе
GUI и Canvas
17.1. GUI и Canvas Около восьми лет назад, то есть очень давно по компьютерным меркам, с компьютерной графикой все было проще, но, с другой стороны, и сложнее, чем сейчас. Во времена популярности MS-DOS программист имел свободный доступ к видеокарте. Программы использовали либо текстовый, либо графический режим. Байты посылались непосредственно в область видеопамяти — и сразу же на экране возникал результат в виде буквы или точки. Если смотреть с этой стороны, то разрабатывать игры для DOS было проще. Игры, как правило, использовали графический режим. SD-ускорителей еще не было, как, естественно, и самих SD-игр и приложений. Первой трехмерной игрой (на самом деле она была только частично трехмерной) была Wolfenstein 3D, которую компания id Software выпустила в 1991 году. Следующей трехмерной игрой была знаменитая DOOM, появившаяся в 1993 году. Немного позже вышла полностью трехмерная Quake. Все эти игры работали под DOS, не требовали 3D-yciesklopClock1> | ;
F3 Просмотр
'
F4 Правка
•ттш\
;
F5 Копна
;
F6 Пе
F7 СозоКатт
F8 Удалить
AII+F4 Выход
Рис. 17.7. В правом верхнем углу экрана находятся часы
Готовое приложение и полные листинги рассмотренного примера вы можете найти на компакт-диске, прилагаемом к книге, по адресу Examples \DesktopClock. На Рабочий стол изображения также можно выводить путем создания прозрачной формы (см. гл. 13) либо сменой фоновой картинки Рабочего стола.
Глава 18
Использование DirectX а Q О Q Q О Q Q
Что такое DirectX Библиотека DelphiX Компонент TDXDraw Очень быстрый вывод графики на экран Часы Палитра Программы-«заготовки» Работа со звуком
Что такое DirectX
18.1. Что такое DirectX Однажды меня перестало устраивать слишком медленное «быстродействие» стандартной графической подсистемы Windows, и я начал искать способы более быстрого вывода графики на экран. Большое количество игр, которые работали в Windows, генерируя на экране красивые сказочные миры, наводили на мысль о том, что такой способ есть. DirectX является одним из таких способов. Библиотека DirectX — это набор СОМ-объектов (см. гл. 15), которые реализуют интерфейсы для работы с видео, звуком, аппаратурой ввода-вывода (клавиатура, джойстик, мышь и др.). Функции DirectX могут выполняться как программно, так и аппаратно. Если видеокарта поддерживает какую-то функцию из набора DirectX, то функция выполняется аппаратно, разгружая основной процессор. Если же функция не поддерживается, то она выполняется программно. В этом случае быстродействия компьютера может не хватить для нормального выполнения, а загрузка процессора сильно возрастет. Первая реализация DirectX не выдерживала никакого сравнения с OpenGL. Однако от версии к версии Microsoft улучшала DirectX В итоге сейчас два этих API примерно равны друг другу, в чем-то DirectX опережает OpenGL, в чем-то отстает. Вы, наверное, играли в компьютерные игры. Я имею в виду не «Сапер» или «Пасьянс», а сложные компьютерные игры. В таком случае вы могли заметить, что для игр, использующих DirectX, на компакт-дисках всегда записан и DirectX необходимой для игры версии. После установки DirectX в Windows использовать этот графический API могут любые программы. DirectX распространяется бесплатно; на компакт-диске, прилагающемся к книге, записан DirectX 9.0b (в директории DirectX). DirectX состоит из следующих основных компонентов: • DirectDraw — позволяет напрямую работать с видеопамятью и аппаратными функциями оборудования; • DirectSD — для программирования трехмерной графики; • DirectSound — позволяет работать со звуком, при этом предоставляя много удобных возможностей; • DirectSoundSD — для работы с трехмерным звуком; • Directlnput — для работы с устройствами ввода-вывода: клавиатурой, мышью, джойстиком и др.; • DirectPlay — для обеспечения поддержки сети в программе, использующей DirectX (TCP/IP, DirectDial, соединение по локальной сети). Рассмотрим некоторые важныетюнятия, использующиеся при программировании трехмерных приложений. • Surface (поверхность). По способам использования Surface похож на Canvas. Surface — это участок видеопамяти, который используется для хранения изображения. Не все Surface отображаются на экране, а только primary (основная" поверхность).
Глава 18. Использование DirectX • Double buffering (двойная буферизация). Применяется для создания плавной анимации. Имеется в виду следующий принцип: пока изображение строится в одном буфере, на экран выводится из другого. Потом все повторяется, только буферы меняются местами. • Page flipping animation (анимация переключением страниц). Чтобы исключить мерцание изображения (очень заметное, например, если сделать анимацию при помощи объекта image), в DirectX (и вообще при программировании игр) изображение строится в одной области видеопамяти, которая затем становится видимой. После этого изображение строится во второй области памяти, вслед за построением становится видимой вторая область, а изображение опять строится в первой. • Frames per second (FPS, кадров в секунду). Название говорит само за себя — количество кадров, выводимых в секунду. Естественно, чем больше, тем лучше. Для нормального восприятия игры достаточно приблизительно 25 кадров в секунду. Количество кадров в секунду напрямую зависит от быстродействия системы. Один и тот же выдеоэффект на разных компьютерах с различными видеокартами может выполняться не с одинаковым быстродействием — FPS может изменяться в несколько раз. DirectX позволяет определить этот параметр, после чего можно вывести его в текстовом виде на экран. • Sprites (спрайты). С напитком Sprite это не имеет ничего общего. Спрайты это изображения (bitmap), используемые в играх. Например, несколько положений одного персонажа — это и есть спрайт. Как правило, спрайты применяются в двухмерных играх. • SD-модели. Трехмерные модели создаются в специальных редакторах (например, 3D Studio Max), затем на них накладываются текстуры, чтобы объекты имели реальный вид и цвет, а не выглядели как выточенные из куска железа. После этого можно применять их в трехмерных приложениях в качестве персонажей, предметов и т. д. В этой книге почти не будет затронуто создание трехмерных эффектов, поскольку это весьма сложный вопрос, требующий отдельного подробного описания. Однако несколько примеров все-таки рассмотрим. Программирование непосредственно на DirectX дает больше возможностей, чем программирование с применением дополнительных инструментов. Однако, используя специальные компоненты, можно добиться впечатляющих результатов за довольно короткий срок. К тому же специальные компоненты облегчают задачу программиста еще и тем, что выполняют рутинную часть работ, а также частично делают наглядным создание программы.
18.2. Библиотека DelphiX Одним из вспомогательных средств при создании программ, использующих DirectX, является библиотека DelphiX, разработанная японским программистом Хироюки Хори (Hiroyuki Hori). Эта библиотека распространяется бесплатно. Вы можете найти библиотеку DelphiX на компакт-диске, прилагаемом к книге, в директории Tools.
Библиотека DelphiX Компоненты DelphiX устанавливаются очень просто и обычно без проблем. Если вы устанавливаете DelphiX с компакт-диска, прилагающегося к этой книге, то для Delphi 3, Delphi 4 или Delphi 5 это надо делать, запустив соответствующий файл (например, i n s t a l l _ f o r 5 . e x e для Delphi 5), расположенный по адресу Tools\DelphiX - DX7 - D3-4-5\Bin. В принципе для любой версии Delphi можно запускать файл Install_for3 .exe. Для установки с компакт-диска DelphiX для Delphi выше 6 версии запустите файл Delphix_for6.dpk, расположенный по адресу ToolsXDelphiX - DX7 -- D6. Загрузится Delphi с окном Package - DelphiX_for6.dpk, Нажмите кнопку Compile — компоненты перекомпилируются, затем нажмите кнопку Install. Будет произведена установка компонентов. Если вы все сделали правильно, то появится сообщение «Package has been installed. The following new component(s) have been registered:». Это означает, что установка была успешно завершена и перечисленные в списке компоненты (их должно быть около 20) успешно зарегистрированы. После всего закройте Delphi, согласившись с вопросом о том, надо ли сохранить изменения. Скопируйте вручную все файлы с расширениями DCU, DFM, INC и PAS (хотя переписать эти файлы проще) в директорию Lib, которая находится в директории с установленным Delphi (у меня, например, эта дирестория находится ПО адресу с : \ P r o g r a m F i l e s \ B o r l a n d \ D e l p h i 7).
Я таким же образом успешно установил DelphiX для Delphi 6 в седьмую Delphi. После этого в Delphi на палитре компонентов появится новая вкладка — DelphiX — со всеми компонентами DelphiX (рис. 18.1). % Delphi 7 Praiecll | Fife Edit Search View
Project
Run
Component Database
Tools Window Help j j [my
»j ; ^J ^ |
: ^ US • fii i JsP'lS?'' SB'S»!' '4^ Ind» Clients j Indv Servers] IrxJv Interceots j Indu I/O Handlers j InduMiscj COM.) Servers DelphiX ) . < ! > .
Рис. 18.1. Вкладка DelphiX на палитре компонентов
Рассмотрим компоненты этой вкладки: • TDxDraw — применяется для работы с поверхностями (Surface) DirectDraw; • TDxDIB — для хранения DIB (Device Independent Bitmap); •
TDxImageList — для хранения нескольких DIB, это аналог TImageList в DirectX;
• TDx3D — для трехмерной графики; • TDxSound — для работы со звуком, не просто проигрывающий звуковой файл, а позволяющий изменять различные настройки, например скорость проигрывания, тон и многое другое. Очень полезный и интересный компонент; •
TDxWave — для хранения WAVE-файлов;
• TDxWaveList — аналогично TImageList, только для звуков;
Глава 18. Использование DirectX • TDxInput — для доступа к объекту Directlnput. С помощью этого объекта можно работать с мышью, клавиатурой, джойстиком; • TDxPlay — для работы компьютеров по сети; • TDxSpriteEngine — облегчает работу со спрайтами. Я практически не использую этот компонент, предпочитаю писать код, связанный со спрайтами, вручную; • TDxTimer — аналог обычного TTimer, однако более точный, позволяющий измерять более короткие промежутки времени; • TDxpaintBox — аналог TImage, однако более подходящий для DirectX. Все компоненты имеют интуитивно понятный интерфейс, поэтому довольно быстро можно запомнить, для чего нужен каждый компонент. В процессе выполнения на экране не отображается ни один из компонентов, кроме TDirectDraw. В нем и выводится все изображение, то есть это просто высокотехнологичный «холст». Версии DelphiX, находящиеся на компакт-диске, который прилагается к книге, рассчитаны на работу с DirectX 7. Программы, которые вы напишете с их помощью, могут не работать, если на компьютере установлен DirectX выше шестой версии. Хотя вряд ли есть компьютеры с таким DirectX, у большинства пользователей установлен DirectX выше восьмой версии. Вы можете поискать в Интернете DelphiX для более новой версии DirectX. Однако DelphiX обновляется очень редко, да и вряд ли вам сразу понадобятся какие-то специальные возможности, присутствующие только в DirectX 8 или в DirectX 9. Если ваш уровень программирования настолько высок, что вам понадобились возможности восьмой и девятой версий DirectX, то DelphiX вам не нужен — работайте непосредственно с DirectX. Для создания простейшего DirectX-приложения нужно всего лишь поместить на форму компонент DirectDraw (рис. 18.2).
(TDXDiaw)
Рис. 18.2. Вывод изображения происходит только в TDXDraw
Компонент TDXDraw Если запустить программу на выполнение, то вы увидите на форме, в том месте, где расположен DirectDraw, черный прямоугольник. Этот черный прямоугольник — отображение поверхности DirectX (Surface). Мы ничего не рисовали, поэтому он просто черный. Тем не менее произошел вывод изображения с помощью DirectX. Если DirectX на компьютере не установлен, то даже черный прямоугольник не будет выведен. Если у вас все получилось, то можно перейти к следующему разделу и заняться программированием графических спецэффектов на DelphiX.
18.3. Компонент TDXDraw DirectDraw имеет очень большое значение, поэтому стоит уделить ему особое внимание. Как правило, если TDXDraw вообще присутствует на форме, то он занимает ее всю. TDXDraw удобно увеличивать до размеров всего окна, присвоив свойству Align значение alClient. В таком случае TDXDraw займет всю клиентскую часть формы. Приложения, использующие DirectX, обычно работают в полноэкранном режиме, потому что так скорость выше и для эффектов выделяется вся поверхность экрана. Как переключаться в полноэкранный режим, читайте ниже. Рассмотрим свойства TDXDraw: • Align — определяет, привязывать или нет компонент к какому-либо краю или увеличить его до размеров всего окна. Возможные значения: alBottom, alClient, alCustom, alLeft, alNone, alRight, alTop. Наиболее употребимые — alNone и alClient; • Autolnitialize — если это свойство равно True (по умолчанию — True), то при запуске приложения инициализация DirectDraw произойдет автоматически. Инициализацию можно также запускать принудительно; • AutoSize — думаю, в данной главе это уже не требует объяснений; • Color — цвет компонента TDXDraw, однако когда DirectDraw будет инициализирован, то изображение на TDXDraw в любом случае будет черным; • Cursor — вид указателя мыши над TDXDraw; • Display — параметры экрана при переключении в полноэкранный режим. Определяет разрешение по горизонтали (Width), вертикали (Height) и количество бит на одну точку. Если это 8 бит, то получается 256-цветный режим, если 16 бит, то, соответственно, 65 536 цветов, 24 бита — это более 16 млн цветов; • Height — высота области, в которой выводится изображение, если режим не полноэкранный; • Hint — текст подсказки, возникающий, если подержать мышку над TDXDraw; • Left — расположение компонента на форме; • Options — различные настройки компонента (подробнее рассмотрены ниже); • PopupMenu — если в этом свойстве описать команды всплывающего меню, то при нажатии правой кнопкой мыши на DirectDraw будет вызвано это меню;
Глава 18. Использование DirectX • ShowHint — определяет, показывать ли подсказку, заданную свойством Hint; • SurfaceHeight и SurfaceWidth — размеры отображаемой области, подобны свойствам Height и Width; • Тор — расположение компонента на форме; • Visible — показывает, отображается ли компонент визуально; • Width — ширина области, в которую выводится изображение, если режим не полноэкранный. Приведем более подробное описание свойства Options: • doFullScreen — включение/выключение полноэкранного режима; •
doAUowReboot — определяет, можно ли использовать нажатие Ctrl+Alt+ Delete в программе;
• doWaitForBlank — задает, будет или нет ожидаться вертикальная развертка при выполнении операции флиппинга (метод Flip). Если включить эту опцию (по умолчанию она включена), то количество кадров в секунду немного снижается; • doAllowPalette256 — определяет, будет ли использоваться 256-цветная палитра; • doSystemMemory — задает применение системной памяти вместо видеопамяти. Если установить True (по умолчанию — False), немного снижается значение FPS (количество кадров в секунду); • doStretch — определяет, сжимать или растягивать изображение в соответствии с разрешением экрана; • do3D — разрешает ЗО-ускорение; • doHardware — определяет, включать ли аппаратное ускорение (2D или 3D). По умолчанию включено; • doRetainedMode — определяет, включен SD-режим Retained или Immediate. Используется, только если включено SD-ускорение; • doSelectDriver — задает, будет ли использоваться драйвер DirectDraw в полноэкранном режиме. Рекомендуется оставлять этот параметр в значении по умолчанию, равном True; • doDrawPrimitive — задает, разрешается ли использовать рисование примитивов; • doZBuffer — определяет, использовать ли Z-буфер, который при программировании трехмерной графики отвечает за отображение объектов на заднем плане; • doTexture — определяет, применяются ли текстуры на трехмерных объектах; • doRGB — задает, использовать ли цвета RGB для трехмерных объектов; • doMono — определяет, использовать черно-белые цвета или цветные; • doDither — если при установке этой опции в True на палитре не окажется подходящего цвета, то вместо него будет использован близкий цвет. Настройку doDither не имеет смысла применять в 24-битных видеорежимах.
Очень быстрый вывод графики на экран
18.4. Очень быстрый вывод графики на экран Используем сведения, полученные из предыдущего раздела, чтобы разработать простейшее DirectX-приложение, которое будет выводить на экран текст. В программе будет возможность переключаться из оконного режима в полноэкранный и обратно. Создадим в Delphi новый проект и разместим на форме три компонента: кнопку Button, таймер DXTimer и, конечно же, DirectDraw. Разумеется, компоненты DelphiX уже должны быть установлены. Если вы добавите кнопку до того, как поместите DXDraw, то DXDraw будет закрывать собой кнопку. По этой причине для каждой кнопки нужно выполнить следующее: щелкнуть на кнопке правой кнопкой мыши и в появившемся контекстном меню выбрать Control > Bring To Front. Некоторые компоненты нельзя передвинуть на передний план по сравнению с DXDraw. Таким компонентом, например, является Label. Если у вас весь экран занят DXDraw, то от этикеток Label придется отказаться совсем. Впрочем, Label вполне удачно можно заменить на Edit или StaticText. На кнопке напишите «ЗАКРЫТЬ», для этого свойству Caption задайте значение ЗАКРЫТЬ. Значение свойства Align компонента DirectDraw установите равным alClient. Некоторые свойства таймера необходимо изменить так, как показано в табл. 18.1. Таблица 18.1. Значения свойств компонента DXTimeM Свойство
Значение
Причина изменения значения
ActiveOnly
True
Чтобы таймер был всегда активен
Enabled
False
Чтобы таймер изначально был выключен, удобнее запускать его вручную
Interval
0
В отличие от обычного таймера, у которого нулевое значение определяет остановку таймера, у DXTimer нулевое значение — это максимальная частота срабатывания. Если установить это значение в ноль, быстродействие программы будет зависеть исключительно от мощностей компьютера
Приступим к написанию кода программы. Несмотря на то что DelphiX максимально упрощает процесс создания DirectX-приложений, основная работа состоит в написании кода, а не в перемещении кнопок по форме. Чтобы DirectX-приложение нормально работало, как минимум нужно написать следующие строки.
Глава 18. Использование DirectX
procedure TForra1.0XDraw1 I n i t i a l ize( Sender: TObject); begin DXTimerl . Enabled :=True; end; procedure TForml. DXTimer1Timer(Sender: TObject; LagCount; Integer); begin
if not DXDrawl. CanDraw then Exit; // проверка Di rectX DXDrawl. Surface. Fi I I (0); //заливка цветом // здесь можно рисовать все, что нужно
//
DXDrawl . Surface. Canvas. Release; DXDrawl.FI i p; end;
procedure TForml iDXDrawlFina I ize(Sender: TObject); beg in DXTimerl. Enabled := False; end; Как видите, таймер запускается в процедуре DXDrawl Initialize, а в DXTimerlTimer выполняются все действия, связанные с рисованием. Рассмотрим, какие именно. Сначала нужно проверить, инициализирован ли DirectX. Если нет, то бессмысленно выполнять все остальные действия. Строка DXDrawl. Surf асе. Fill ( 0 ) ; заливает цветом поверхность DirectDraw. В данном случае черным (код цвета — ноль). Если написать, например, DXDrawl . Surface . Fill (clwhite) , то цвет поверхности будет белый. Далее может располагаться код, в котором будут вызываться какие-то процедуры, функции, предназначенные для вывода графики на экран. Можно располагать весь код для рисования непосредственно в теле процедуры DXTimerlTimer. Однако если процедур и функций для рисования много, то не следует размещать их в DXTimerlTimer, иначе получится нагромождение процедур, что снизит удобочитаемость кода. Наконец, нужно переключиться на другую поверхность (точно так же, как при программировании в DOS нужно было переключаться на другую видеостраницу). Это достигается вызовом метода Flip. Когда DirectDraw завершает работу, таймер нужно отключать, что и производится в DXDrawlFinalize.
Очень быстрый вывод графики на экран Описанные выше действия — это минимум для работы DirectX-приложения. Теперь рассмотрим, как переключаться в полноэкранный режим. Переключаться в полноэкранный режим будем при инициализации DirectX. Переключение на полный экран будет производиться в процедуре DXDrawl i n i t i a l i z i n g (не путайте с Initialize!). Листинг 18.2
procedure TForml.DXDrawlInitial izing(Sender: TObject); // переключение в полноэкранный режим begin DXDrawl.Opt ions := DXDrawl.Opt ions + [doFulI Screen]; DXDrawl. Display.Width :=800; DXDrawl. Display.Height :=600; DXDrawl. Display.ВitCount := 16; end; В данном коде сначала задаем опцию, отвечающую за включение полноэкранного режима (doFullScreen). Затем устанавливаем нужное разрешение экрана (800 х 600 пикселов) и цветность в битах. Необходимое количество видеопамяти можно определить при помощи не очень сложных вычислений: 800 умножаем на 600, что дает нам 480 000 байт. На каждую точку нужно два байта (для 16-битного цвета), поэтому умножаем получившееся количество на два. Получается 960 000 байт. Используются две видеостраницы, поэтому получившееся число следует умножить еще на два. Таким образом, получается, что для полноэкранных видеоэффектов на дисплее размером 800 х 600 пикселов при 65 536 цветов нужно 1 920 000 байт, или около 2 Мбайт7. Если на видеокарте будет меньше 2 Мбайт, то программа будет работать, но все, для чего He-хватило места в видеопамяти, будет размещено в системной, поэтому об аппаратном ускорении придется забыть, как и о быстром переключении страниц. Как видите, обязательно нужно приблизительно подсчитывать минимальные системные требования, пользуясь расчетами, приведенными выше. Память также необходима для загруженных картинок. Изображения обычно занимают очень много места в памяти. Память объемом 2 Мбайт уже давно не актуальна. На сегодняшний день видеопамять измеряется десятками мегабайт. Один из моих коллег недавно спросил у системного администратора, какого объема память в его компьютере, имея в виду оперативную (RAM). Администратор ответил, что 128 Мбайт. Все сослуживцы удивились, так как даже 256 Мбайт — это немного, и то, что у системного администратора 128 Мбайт, не очень было похоже на правду. На следующий день оказалось, что администратор имел в виду видеопамять. Прогресс в области вычислительной техники идет настолько быстро, что когда уже просто говорят «128 Мбайт», неизвестно, что имеется в виду — видеопамять или Если быть точным, то 1,831 Мбайт.
Глава 18. Использование DirectX оперативная, А примерно 10 лет назад 128 Мбайт было неплохим объемом даже для жесткого диска. Но все же у пользователей может быть такой старый компьютер, поэтому обращайте внимание на то, какие должны быть системные требования у вашей программы. Если же ваша программа более скромна и работает в режиме 640 х 480 и с 256 цветами, то даже при использовании двух переключающихся поверхностей нужно всего лишь 614 400 байт. Если же вы хотите сделать какую-то сложную игру в видеорежиме 1280 х 1024 при 24-битном цвете, то понадобится 7,5 Мбайт, не считая подгружаемой графики. Что касается видеорежимов с разрешением 1600 пикселов по горизонтали, то для них и 10 Мбайт будет мало. Вернемся к переключению в полный экран. Кроме листинга 18.2 нужно еще дописать принудительное переключение из полноэкранного режима при выходе в Windows. Это необходимо сделать в процедуре DXDrawlFinalize. В D X D r a w l F i n a z l i z e нужно также выключать таймер. Как процедура должна выглядеть, показано в следующем листинге. Листинг 18.3
procedure TForml. DXDrawlFinalize(Sender: TObject); begin // инициализируем таймер DXTimerl.Enabled := False; // выключаем полноэкранный режим DXDrawl.Opt ions := DXDrawl.Opt ions - [doFulIScreenj; DXDrawl. D i s p l a y . W i d t h :=640; DXDrawl. Display. Height :=480; DXDrawl.Display.ВitCount := 16;
end; Запустите проект и посмотрите, что получилось. При открытии окна программы должен включаться полноэкранный режим 800 х 600 х 16, а при закрытии окна программы — выключаться и возвращаться установленный ранее режим. Обычно если создают полноэкранное DirectX-приложение, то делают так, чтобы признаков формы (строки заголовка и т. д.) видно не было. Сделаем так же. Для этого изменим свойство BorderStyle формы Forml на bsNone. Чтобы можно было закрывать форму более удобным способом, а не только при помощи Alt+F4, допишем код для кнопки закрытия. Листинг 18.4
procedure TForml.ButtonlCI ick(Sender: TObject); // закрытие приложения
beg i n AppI i cat i on.Term i nate;
end;
Очень быстрый вывод графики на экран Если теперь запустить программу, то программа открывается во весь экран, нет лишних элементов. Пока мы в программе, мы видим только ее окно. Перейдем к выводу текста на экран, причем сначала обычного текста. Добавим в DXTimerlTimer вызов процедуры Drawing. Опишем саму процедуру и сделаем в ней вывод текста на экран. Листинг 18.5 procedure TForml.Drawing; // вывод графики begin // устанавливаем стили шрифта DXDrawl.Surface.Canvas.Brush.Style := bsClear; DXDrawl.Surface.Canvas.Font.Co I or := clWhite; DXDrawl.Surface.Canvas.Font.Size := 18; // выводим время в правый нижний угол экрана DXDrawl.Surface.Canvas.TextOut(675, 550, TimeToStr(Now)); // в левом нижнем уголу показываем количество кадров в секунду, // с которым видеокарте удается выводить изображение // на экран DXDrawl.Surface.Canvas.TextOut (10,550, FPS=+ IntToStr(DXTimerl.FrameRate)); end; Этот код не нуждается в подробных комментариях. Здесь выводится текущее время, а также количество кадров в секунду (FPS), которое возвращается функцией FrameRate таймера. Выведем посередине экрана красивую «плывущую по волнам» надпись «Hello World», которая будет выполнена в виде картинки. Ее нужно создать в каком-нибудь графическом редакторе, например Paint или Adobe Photoshop. Добавив на форму компонент DXImageList, можно загрузить в него картинку и работать с ней в программе. Можно загружать картинку и в режиме выполнения. Это делается так: DXImageListl.Items.Add; DXImageListl.Items[0].Transparent := ; DXImageListl.Items[0].Picture.LoadFromFile(); Вместо [ о ], разумеется, нужно подставлять разные номера: переменную, если добавляете в цикле, или числа в порядке очередности. Свойство Transparent отвечает за прозрачность. Того, чтобы картинка «плыла по волнам», можно добиться следующим способом: DXImageListl.Items[0].DrawWaveX (DXDrawl.Surfасе, X, Y, Width, Height, Transparent, Maxamp, Length, Phase);
Глава 18. Использование DirectX Здесь: • х — координата по горизонтали; • Y — координата по вертикали; • width — ширина картинки; • Height — высота картинки; • Transparent — прозрачность. Если этот цвет будет совпадать с цветом на картинке, который считается прозрачным, то цвет выводиться не будет. Например, если прозрачным считается черный цвет, то это свойство должно быть равно нулю; • Махатр — максимальная амплитуда, например 2,3 или 4; • Length — длина волны, например 60 или 80; • phase — одна из 256 фаз. Здесь нужно задавать изменяющуюся в цикле переменную, например FAngleM. При этом переменная FAngle должна изменяться с каждым вызовом процедуры, поэтому эту переменную нужно объявить раньше всех остальных переменных: var
FAngle: Integer; Обратите внимание, что, добавив на форму компонент DXImageList, нужно назначить ему DXDraw, с которым он может работать. Невыполнение этого условия — очень распространенная ошибка, из-за которой даже при отсутствии других ошибок изображение не выводится. Назначить DXDraw компоненту DXImageList можно простым изменением свойства DXDraw в окне Object Inspector. Модифицируем процедуру Drawing так, чтобы в нашей программе появилось несколько спецэффектов. Листинг 18.6
procedure TForml.Drawing; // вывод графики begin // добавляем надпись в центр экрана DXImageListl.ltems[0].DrawWaveX (DXDrawl.Surfасе, 200, 250, 350, 100, О, 3, 60, FAngle*4); .
// маленькие исчезающие надписи в углах DXImageListl.ltems[0].DrawAdd (DXDrawl.Surfасе, Bounds(50, 120, 150, 40), 0, Trunc(Cos256(FAngIe)*126+127)); DXImageListl.ltems[0].DrawAdd (DXDrawl.Surface, Bounds(550, 120, 150, 40), 0, Trunc(Cos256(FAngle)*126+127));
Очень быстрый вывод графики на экран
// большая вращающаяся и исчезающая надпись в центре DXImageListl.ltems[0].DrawRotateAlpha (DXDrawl.Surfасе, 375, 300, 350, 100, 0, 0.5, 0.5, FAngle, Trunc(Cos256(FAngle)*126+127)); inc(FAngle); // устанавливаем стили шрифта DXDrawl.Surface.Canvas.Brush.Style := bsClear; DXDrawl. Surface.Canvas.Font.Color := clWhite; DXDrawl.Surface.Canvas.Font.Size := 18; // выводим время в правый нижний угол экрана DXDrawl.Surfасе.Canvas.TextOut(675. 550, TimeToStr(Now)); // в левом нижнем углу показываем количество кадров, // с которым видеокарте удается выводить изображение // на экран DXDrawl.Surfасе.Canvas.TextOut (10,550, FPS= + lntToStr(DXTimer1.FrameRate)); end;
Результат работы этой процедуры: в центре экрана появляется большая картинка, которая изгибается волнами, над ней такая же картинка, но вращающаяся и появляющаяся время от времени. В углах две маленькие исчезающие картинки. Как вы видите из листинга 18.6, данный эффект реализован с помощью таких методов DirectDraw, как DrawWaveX, DrawAdd И DrawRotateAlpha. Рекомендую изучить файл помощи к DelphiX, который можно взять с компактдиска, прилагающегося к книге. Файл помощи, конечно, на английском языке, однако в нем содержится много полезной информации, которую можно найти только в специализированных книгах. В примере с графикой, который мы сейчас делаем, кнопка ЗАКРЫТЬ в углу экрана, по-моему, портит весь вид. Давайте ее уберем и сделаем так, чтобы программа закрывалась самым распространенным способом — при помощи клавиши Esc. Итак, убираем с формы кнопку Buttonl. Код, который был в процедуре Buttoniciick (была всего лишь одна строка — Application.Terminate), вставляем в процедуру обработки нажатия, как показано в следующем листинге. Листинг 18.7
procedure TForml. FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin // если нажимается клавиша Esc, закрываем приложение if key=27 then Appl icat ion.Terminate; end;
V
Глава 18. Использование DirectX Несмотря на то что данная программа работает правильно, для надежности нужно изменить свойство KeyPreview формы с False на True. Если это свойство равняется True, то все нажатия клавиш распознаются формой, вне зависимости от того, какой компонент на форме активен. Можно подставить в процедуре D X T i m e r l T i m e r вместо строки DXDrawl. Surf асе . Fill (0) ; // заливка цветом
такую строку: DXDrawl.Surfасе.Fill(clWhite); // заливка цветом
или такую: DXDrawl.Surfасе.Fill(clBlue) ; // заливка цветом Полученные результаты вас, скорее всего, удивят. Приведу полный листинг программы, рассмотренной в этом разделе. Листинг 18.8 unit Unit"!; interface uses Windows,Messages, SysUti Is, V a r i a n t s , C l a s s e s , Graphics, Controls, Forms, Dialogs, DXDraws, StdCtrls, DXCIass; type TForml = class(TForm) DXDrawl: TDXDraw; DXTimerl: TDXTimer; DXImageListl: TDXImageList; procedure DXDrawl I n i t i a l ize(Sender: TObject); procedure DXTimer1Timer(Sender: TObject; LagCount: Integer); -procedure DXDrawlFi nal ize(Sender: TObject); procedure D X D r a w l I n i t i a l izing(Sender: TObject); procedure FormKeyDown(Sender: TObject; var Key: Word; S h i f t : TShiftState)'; private procedure Drawing; { P r i v a t e declarations } publ ic { Publ ic declarations } end;
Очень быстрый вывод графики на экран
var FAngle: Integer; Form"!: TForml; implementation {$R *.dfm} procedure TForml. DXDrawl I n i t i a l ize( Sender: TObject); begin DXT i me r1. Enabled :=True; end; procedure TForml . DXTimer1Timer(Sender: TObject; LagCount: Integer); begin
if not DXDrawl . CanDraw then exit; // проверка Di rectX DXDrawl . Surface. Fi I I (0); // заливка цветом
// здесь можно рисовать все, что нужно // Drawing;
DXD rawl . Su rf асе . Canvas . Re I eas e ; DXDrawl. F I i p; end; procedure TForml. Drawing; // вывод графики begin // добавляем надпись в центр экрана DXImageListl: ltems[0].DrawWaveX (DXDrawl. Surf асе, 200, 250, 350, 100, О, 3, 60, FAngle*4); // маленькие исчезающие надписи в углах DXImageListl. ltems[0].DrawA6d (DXDrawl. Surface, Bounds(50, 120, 150, 40), 0, Trunc(Cos256(FAngle)*126+127)); DXImageListl. ltems[0].DrawAdd (DXDrawl. Surf ace, Bounds(550, 120, 150, 40), 0, Trunc(Cos256(FAngle)*126+127)); // большая вращающаяся и исчезающая // надпись в центре DX I mageL i st1 . I tems[0] . DrawRotateA I pha
Глава 18. Использование DirectX (DXDrawl.Surface, 375, 300, 350, 100, 0, 0.5, 0.5, FAngle, Trunc(Cos256(FAngle)*126+127)); inc(FAngle);
// устанавливаем стили шрифта DXDrawl.Surface.Canvas.Brush.Style := bsClear; DXDrawl.Surface.Canvas.Font.Color := clWhite; DXDrawl.Surface.Canvas.Font.Size := 18; // выводим время в правый нижний угол экрана DXDrawl.Surfасе.Canvas.TextOut(675, 550, TimeToStr(Now)); // в левом нижнем углу показываем количество кадров, // с которым видеокарте удается выводить изображение // на экран DXDrawl.Surface.Canvas.TextOut (10,550, FPS=+ IntToStr(DXTimerl.FrameRate)); end; procedure TForml. DXDrawl Final ize(Sender: TObject); beg i n // останавливаем таймер DXTinner").Enabled := False; // выключаем полноэкранный режим DXDrawl.Opt ions := DXDrawl.Opt ions - [doFu11 Screen]; DXDrawl.Display.Width :=640; DXDrawl.Display.Height ;=480; DXDrawl.Display.ВitCount := 16; end; procedure TForml.DXDrawlInitializing(Sender: TObject); // переключаем в полноэкранный режим begin DXDrawl.Opt ions := DXDrawl.Options + [doFulI Screen]; DXDrawl.Display.Width := 800; DXDrawl. Display.Height :=600; DXDrawl. Display.ВitCount := 16; end; procedure TForml. FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin // если нажимается клавиша Esc, закрываем приложение if key=27 then Appl ication.Terminate; end; end. На рис. 18.3 и 18.4 приведен результат выполнения программы.
Очень быстрый вывод графики на экран
Рис. 18.3. Результат выполнения программы в момент, когда три исчезающие надписи не видны
Рис. 18.4. Результат выполнения программы в момент, когда исчезающие надписи видны
Готовое приложение и полные листинги рассмотренного примера вы можете найти на компакт-диске, прилагаемом к книге, по адресу Examples\Directx-f irst.'
Глава 18. Использование DirectX
18.5. Часы В этом разделе создадим приложение, которое не только красиво выглядит (благодаря тому, что сделано с помощью DelphiX), но и достаточно функционально. Создадим механические часы, которые будут работать на экране компьютера (рис. 18.5).
Рис. 18.5. Часы
После создания нового проекта нужно добавить четыре компонента: три стандартных: DXDraw, DXTimer и DXImageList, а также PopupMenu. Этих компонентов будет вполне достаточно. После этого нужно изменить некоторые свойства компонентов, как показано в табл. 18.2. Таблица 18.2. Значение свойств компонентов Компонент
Свойство
Значение
DXDrawl
Align
alClient
DXImageListl
DXDraw
DXDrawl8
DXTimerl
Enabled
False
DXTimerl
Interval
0
Forml
Borders tyle
bsNorie
Это значение нужно устанавливать, когда компонент DXDraw уже есть на форме.
Часы В DXImageList нужно загружать графику, предварительно подготовив ее в графическом редакторе. Это должны быть картинки BMP с красиво изображенными числами от 1 до 12, которые будут служить часовыми отметками на циферблате будущих часов. Можете загружать не арабские числа и даже вообще не числа, а любые картинки. Я, например, сделаю два набора графики — с арабскими числами и с римскими, которые можно будет переключать с помощью компонента PopupMenu. Оба набора будут постоянно загружены, поэтому в DXImageList будет не 12 изображений, а 24. С помощью пунктов контекстного меню просто будет переключаться индексная переменная, по которой графика будет браться либо с нулевого элемента в DXImageList, либо с двенадцатого. Размеры изображений пусть будут 50 х 50 пикселов. Прозрачный фон — черный. Если графика загружена в DXImageList на этапе программирования, то сами BMPфайлы хранятся вместе с исходными текстами программы также в качестве исходных файлов. Однако в нашем случае графика будет подгружаться при открытии программы. Таким образом, в исполняемом файле графики не будет, а один набор можно будет менять на другой. Эту программу я не буду комментировать отдельно, а снабжу комментариями сам текст программы. Отдельно я опишу только не рассмотренные ранее принципы, которые применяются в программе. Простой вывод графики (без, например, поворотов и искажений) осуществляется методом Draw следующим образом: DXImageListl.Items[].Draw (<surfасе>,X,Y,ТС); Где — это номер изображения в DXImageList (обратите внимание, что отсчет ведется с нуля!), х и Y — координаты вывода изображения, а тс (TransparentColor) — цвет, который считается прозрачным. В большинстве примеров из этой книги прозрачным цветом считается черный, что не всегда удобно, так как прозрачный цвет не выводится на экран. Черный — довольно часто используемый цвет, поэтому в качестве прозрачного лучше брать какой-нибудь редко применяющийся цвет, например малиновый или ярко-зеленый. Ваше решение должно зависеть от того, какую программу вы пишете. Определение координат, по которым нужно выводить картинку с цифрой, ведется по довольно непростым формулам, так как картинки располагаются на воображаемой окружности (циферблат ведь круглый): X := t r u n c ( - s i n ( ( ( p i / 6 ) * ( i - 1 ) ) - p i + ( p i / 6 ) ) * r a d i u s ) + dx; Y := t r u n c ( c o s ( ( ( p i / 6 ) * ( i - D ) - p i + ( p i / 6 ) ) * r a d i u s ) + dy;
При этом данные формулы должны применяться в цикле, где i изменяется от 1 до 12, radius — это радиус циферблата, a dx и dy — центр окружности циферблата.
Глава 18. Использование DirectX Приведенные формулы вполне можно оптимизировать, однако оставляю это вам, потому что математику не очень люблю9, а вот трудную работу перекладывать на читателей мне нравится гораздо больше. Хорошо, что у меня хватило терпения вывести хотя бы такие формулы. Расчет координат стрелок, которые прорисовываются на Canvas-поверхности с помощью метода Line (не DirectX, а обычного, относящегося к Canvas), производится по схожей формуле. Листинг 18.9 unit Unit"!; interface uses Windows, Messages, SysUtils, Variants, C l a s s e s , G r a p h i c s , C o n t r o l s , Forms, Dialogs, DXCIass, DXDraws, Menus; type TForml = class(TForm) DXDrawl: TDXDraw; DXImageListl: TDXImageList; DXTimerl: TDXTimer; PopupMenul: TPopupMenu; N1: TMenultem; N2: TMenultem; N3: TMenultem; N5: TMenultem; procedure DXDrawlInitial ize(Sender: TObject); procedure DXTimer1Timer(Sender: TObject; LagCount: Integer); procedure DXDrawlFinal ize(Sender: TObject); procedure D X D r a w l I n i t i a l izing(Sender: TObject); procedure FormActivate(Sender: TObject); procedure N1CI ick(Sender: TObject); procedure N2CI ick(Sender: TObject); procedure FormKeyDown(Sender: TObject; var Key: Word; -Shift: TShiftState); procedure N5CI ick(Sender: TObject); private procedure Drawing; { Private declarations } Может быть, для некоторых читателей это звучит парадоксально, но я всегда считал, что математика и программирование — это вещи настолько же разные, как и ботаника с историей. Что-то общее есть, но далеко не все.
Часы publ ic { Publ ic declarations } end;
var Form"!: TForml; indexGr: integer; //набор графики //О - первый набор // 12 - второй набор
implementation {$R *.dfm} procedure TForml.DXDrawlInitialize(Sender: TObject); begin DXTimer"!. Enabled :=True; end; procedure TForml. Draw ing; // прорисовка часов const rad1=250; // радиус окружности (по X) rad2=250; // радиус окружности (по Y) dx=370; // размещение (сдвиг) циферблата dy=275; // то же, но по у 1еп1=2; // длина часовой стрелки в два раза // короче расстояния от центра // циферблата до цифры 1еп2=1.5; // отношение длины минутной стрелки 1епЗ=1.1; // отношение длины секундной стрелки var i : Integer; // счетчик в цикле X, Y: Integer; // координаты картинок Now: TDateTime; // для хранения времени hour, m i n , sec, msec: Word; // для преобразования // времени hour"!, m i n i , seel: Real; // для вычисления // координат стрелок begin // вывод картинок по окружности for i := 1 to 12 do begin // рассчитываем координаты размещения // очередной картинки цифры, обозначающей час X := trunc(-sin(((pi/6)*(i-1))-pi+(pi/6))*rad1) + dx; Y := trunc(cos(((pi/6)*(i-1)Hpi+(pi/6))*rad2) + dy;
Глава 18. Использование DirectX // выводим в рассчитанные координаты // нужную картинку DXImageListl.ltems[i+indexGr-1].Draw (DXDraw1.Surface,X,Y,0); end;
// рисуем стрелки // сначала выясняем, // сколько сейчас времени Now := Time; Decode! i me (Now, hour, m in, sec, msec); // так как часы не электронные, то преобразуем время // в двенадцатичасовой формат if hour>12 then hour := hour - 12; // теперь вычисляем координаты // и рисуем часовую стрелку hour"! := hour + min/60; X := trunc(-sin(((pi/6)*(nour1-1))-pi+(pi/6))*(rad1/len1)) + dx+25; Y := trunc(cos(((pi/6)*(hour1-1))-pi+(pi/6))*(rad2/len1)) + dy+25; DXDrawl.Surface.Canvas.Pen.Color := clWhite; DXDrawl.Surface.Canvas.MoveTo(dx+25,dy+25); DXDrawl.Surface.Canvas.L i neToCX,Y); // вычисляем координаты и рисуем минутную стрелку m i n i := min/5; X := trunc(-sin(((pi/6)*(min1-1))-pi+(pi/6))*(rad1/len2)) + dx+25; Y := trunc(cos(((pi/6)*(min1-1))-pi+(pi/6))*(rad2/len2)) + dy+25; DXDrawl.Surface.Canvas.Pen.Color := clWhite; DXDrawl.Surface.Canvas.MoveTo(dx+25,dy+25); DXDrawl.Surface.Canvas.LineTo(X.Y); // вычисляем координаты и рисуем секундную стрелку seel := sec/5; X := trunc(-sin(((pi/6)*(sec1-1))-pi+(pi/6))*(rad1/len3)) + dx+25; Y := trunc(cos(((pi/6)*(sed-1))-pi+(pi/6))*(rad2/len3)) + dy+25; DXDrawl.Surface.Canvas.Pen.Color := c l W h i t e ; DXDrawl.Surface.Canvas.MoveTo(dx+25,dy+25); DXDrawl.Surface.Canvas.L i neTo(X,Y); end; procedure TForml. DXTimer1Timer(Sender: TObject; LagCount: Integer); begin if not DXDrawl.CanDraw then Exit; // проверка DirectX DXDrawl.Surface. Fi 11(0); // заливка цветом
Часы
//////////////У///////////////////////// // // // здесь можно рисовать все, что нужно // Drawi ng; DXDrawl. Surface. Canvas. Release; DXDraw1.PI i p; end; procedure TForml. DXDrawl Final ize(Sender: TObject); beg i n // останавливаем таймер DXT i me r1. Enabled := False; // выключаем полноэкранный режим DXDrawl. Opt ions := DXDrawl. Opt ions - [doFu I I Screen]; DXDrawl. D i s p l a y . Width := 640; DXDrawl. Display. Height :=480; DXDrawl. Di splay. В itCount := 16; end; procedure TForml. DXDrawl I n i t i a l iz ing (Sender: TObject); // переключаем в полноэкранный режим begin DXDrawl. Opt ions := DXDrawl. Opt ions + [doFul I Screen]; DXDrawl. D i s p l a y . Width :=800; DXDrawl. Display. Height :=600; DXDrawl. Di splay. В itCount := 16; end; procedure TForml . FormAct i vate(Sender: TObject); var i : Integer; // счетчик в цикле s: String; // имя файла с картинкой begin // инициализация переменных indexGr := 0; Forml . KeyPreview := True; // загрузка графики в DXImageListl for i := 1 to 24 do beg in // получаем имя файла // файлы называются по порядку от 01.bmp // до 24. bmp s := IntToStrC i ); if i ' } ; "
Вместо подставляете имя вашей переменной, а вместо имя подраздела, в котором будете сохранять данные.
Запись, чтение и удаление данных На втором этапе для записи данных в реестр используются следующие процедуры (допустим, переменная, которая записывается в реестр, называется reg): reg.WriteString(,,); // запись строк reg.Writelnteger(,,); // целых чисел
Запись в реестр и чтение из него reg.WriteBool(,,); // булевых величин reg.WriteFloat (,,); // дробных чисел reg.WriteDate(,,); // даты reg.Writelime(,,); // времени reg.WriteDateTime(,,); // даты и времени
Первая процедура предназначена для записи строковых значений, вторая — целых, третья — булевых, четвертая — значений с плавающей точкой. Для чтения существуют очень похожие функции: <str> := reg.ReadString(,, < d e f > ) ; // запись строк := reg.Readlnteger(,,<def>); // целых чисел := reg.ReadBool(,, <def>) ; // булевых величин < f l > := reg.ReadFloat(,,<def>); // дробных чисел := reg.ReadDate(,,<def>); // даты := reg.ReadTime(,,<def>); // времени := reg.ReadDateTime(,,<def>); // даты и времени
В передаваемых параметрах, помимо раздела и параметра, указывается еще один параметр (def), который является значением по умолчанию. Если в реестре не будет найден читаемый параметр, то переменной присвоится значение, указанное по умолчанию. Например, если при выполнении следующей команды: Edit2.Text := reg.ReadString('IQ-Data','keyl','нет');
в секции iQ-Date не будет обнаружен параметр keyl, то Edit2 . Text будет присвоено значение нет, так как именно это значение указано как присваиваемое по умолчанию. Удалять данные из реестра проще всего. Например, удалить параметр можно так: reg.DeleteKey(,); Раздел можно удалить следующим образом: reg.EraseSection();
Чтобы удалить раздел, относящийся к вашей программе, надо создать переменную в разделе, который находится на один уровень выше в иерархии разделов: reg := TReglniFile.Create('Software'); reg.EraseSection();
а не reg := TReglniFile.Create('Software\');
Глава 19. Реестр Windows Будьте осторожны, удаляйте только то, что нужно, иначе можете случайно удалить что-то очень важное. Специальной защиты от удаления параметров в Windows нет, система вполне может разрешить вам стереть данные, относящиеся к другим программам. Естественно, это приведет к неработоспособности некоторых программ или даже всей операционной системы.
Освобождение переменной После работы с реестром переменную нужно освободить: reg.Free; При следующей серии действий с реестром переменную reg можно будет создать заново (Create).
Эксперименты с реестром Рассмотрим целиком программу обмена данными с реестром. Листинг 19.1
unit Unit"!; interface uses Windows,Messages,SysUti Is,Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Registry; type TForml = class(TForm) Editt: TEdit; StaticTextl: TStaticText; StaticText2: TStaticText; Edit2: TEdit; Button!1: TButton; Button2: TButton; ButtonS: TButton; Button4: TButton; procedure ButtonlCI ick(Sender: TObject); procedure Button2CI ick(Sender: TObject); procedure ButtonSCIick(Sender: TObject); procedure Button4CI ick(Sender: ^Object); private { Private declarations }
Запись в реестр и чтение из него publ ic { Publ ic declarations } end; var Form"!: TForml; reg: T R e g l n i F i le;
implementat ion {$R ..dfm} procedure TForml. Button"! СI ick( Sender: TObject); // процедура записи в реестр begin // проверяем, введено ли имя и значение 10 if Edit1.Text= then Exit; if Edit2.Text= then Exit; // записываем в реестр reg := TReglniFi le.Create(Software\IQ-QQQ); //задаем // название раздела reg.Wr i teSt r i ng(IQ-Data,Ed i 11.Text,Ed i t2.Text); reg.Free; ShowMessage(Кажется, записалось...); end; procedure TForml.Button2CIick(Sender: TObject); // читаем из реестра begin // проверяем, введено ли имя if Edit1.Text= then begin Згю\л/Меззаде(Сначала введите имя и только потом щелкайте сюда); Exit; end; // читаем из реестра искомый параметр reg := TReglniFile.Create(Software\IQ-QQQ); Edit2.Text := reg. ReadString(IQ-Data,Editl.Text,); reg.Free; end; procedure TForml.ButtonSCIick(Sender: TObject); // удаляем параметр из реестра var s: String;
Глава 19. Реестр Windows begin // проверяем, введено ли имя if Edit1.Text= then begin s := Сначала введите имя и только потом щелкайте сюда; ShowMessage(s); exit;
end; // удаляем параметр reg := T R e g l n i F i le.Create(Software\IQ-QQQ); reg.DeIeteKey(IQ-Data,Ed i 11.Text); reg.Free; 5помМеззаде(Удалилось, к а ж е т с я . . . ) ; end;
procedure TForml. Button4CI ick(Sender: TObject);
'
-
/
// удаляем подраздел begin reg := T R e g l n i F i le.Create(Software); reg. EraseSection( IQ-QQQ); reg.Free; 'ЗпсмМез5аде(Подраздел IQ-QOQ удален); end; end.
• Это вся программа. Обратите внимание, что при обращении к подразделам в реестре путь к ним должен указываться через обратный слеш (\), а не через прямой (/), который используется при операциях с дисками. Например, нужно задавать путь так Sof tware\iQ-QQQ. Если вы укажете Sof tware/IQ-QQQ, то вместо того, чтобы в разделе Software создать подраздел IQ-QQQ, будет создан подраздел, который так и называется: Sof tware /IQ-QQQ.
Готовое приложение и полные листинги рассмотренного примера вы можете найти на компакт-диске, прилагаемом к книге, по адресу Examples\Registry. Учтите, что приведенная программа размещает данные в разделе HKEY_CURRENT_usER\Sof tware. О том, как поместить данные в определенный раздел реестра, читайте в разд. 19.4.
19.4. Автоматический запуск программы Сделаем так, чтобы Windows автоматически запускал программу. Сделать это совсем несложно — нужно добавить параметр, значением которого будет путь к запускаемой программе, в раздел HKEY_LOCAL_MACHiNE\Sof twareXMicrosoft\Windows\CurrentVersion\Run.
Автоматический запуск программы Например, если нам надо автоматически запускать Word при загрузке Windows, то в вышеуказанный раздел помещаем такой строковый параметр: Параметр=Иогс1, Значение=с:\Program FilesXMicrosoft O f f i c e \ O f f i c e \ W i n w o r d . e x e (указываете путь к папке, в которой у вас установлен Word). Рассмотрим, как, используя Dephi, добавить программу в автозагрузку. Сначала нужно объявить Registry в uses и переменную reg типа TreginiFile, чтобы программа могла работать с реестром. Листинг 19.2 // создаем reg reg := TRegIn i F i Ie.С reate; // назначаем раздел reg.RootKey := HKEY_LOCAL_MACHINE; // открываем раздел, с которым будем работать reg.OpenKey (\Softwa ге\М i сrosoft\Wi ndows\Cu r rentVe rsi on, True); // добавляем в автозагрузку reg.WriteString(Run,Del phi Effects,paramstr(O)); // закрываем параметр reg.CloseKey; // освобождаем reg reg.Free; Значение True в процедуре ОрепКеу означает, что если такой раздел в реестре не существует, то разрешается его создать. Значение False, соответственно, запрещает создание нового раздела. Отдельно опишу, какие параметры передаются в процедуру Wr iteStr ing. Run — это название раздела в Sof tware\Microsof t\Windows\CurrentVersion. В этом разделе описаны программы, которые надо запускать автоматически. DelphiEf f ects название параметра. Желательно придумывать такое название параметра, чтобы оно ни в коем случае не совпало с каким-нибудь уже существующим. Третий параметр — путь к программе, которая будет запускаться автоматически. Путь должен быть ПОЛНЫМ, то есть, например, таким: c:\Program F i l e s \ M y S o f t \ l a l a l a . e x e . Полный путь к самой себе программа может получить, если вместо пути написать paramstr(0). На рис. 19.3 показано окно получившейся программы. /• Иллюстрация автозапуска программы Поместить npur P.JMMJв автозапуск
]|
Удалить из автозапуска
Рис. 19.3. Программа для задания автоматической загрузки программы через реестр
Глава 19. Реестр Windows Если вместо раздела Run указать раздел RunOnce, то программа будет запускаться не постоянно при загрузке Windows, а запустится один раз. Удаление из автозагрузки производится путем удаления параметра из секции Run. Листинг 19.3
// создаем reg reg := TReglniFi Ie.Create; // назначаем раздел reg.RootKey := HKEY_LOCAL_MACHINE; // открываем раздел, с которым будем работать гед.ОрепКеу (\Software\Microsoft\Windows\CurrentVersion, True); // удаляем параметр reg. DeleteKey(Hurt,Del phi Effects); // закрываем параметр reg.Close.Key; // освобождаем reg reg.Free; Готовое приложение и полные листинги рассмотренного примера вы можете найти на компакт-диске, прилагаемом к книге, по адресу Examples\RegistryAutorun.
Задание автозагрузки программы без использования реестра Рассмотренный выше способ помещения программы в автозагрузку — самый оптимальный. Существуют еще два способа, не такие замечательные, но тоже вполне работающие. Первый — поместить программу в меню Пуск > Программы > Автозагрузка. Второй — вписывание программы в раздел Run файла w i n . ini. Это очень «древний» способ, я не рекомендую вам его применять. Если хотите воспользоваться первым способом, то нужно получить путь к меню Пуск > Программы > Автозагрузка и добавить туда ярлык своей программы. Дело в том, что путь может быть разным, в зависимости от того, куда установлена операционная система и какая версия Winxlows используется. В многопользовательской системе может быть несколько папок с меню Пуск. Чтобы определить путь к нужному нам разделу меню Пуск, нужно, подключив библиотеку Registry, прочитать путь к папке автозагрузки из реестра, где он хранится в разделе HKEY_CURRENT_USER\ s o f t w a r e \ M i c r o s o f t \ w i n d o w s \ C u r r e n t V e r sion\Explorer\Shell Folders. В этом разделе нужно найти параметр startup и прочитать его. Там и будет искомый путь к папке Автозагрузка.
Автоматический запуск программы В этом подразделе реестра хранятся и пути ко всем другим папкам меню Пуск. Можете изучить содержимое этого подраздела при помощи «Редактора реестра». Рассмотрим, например, некоторые параметры, в которых хранятся пути к следующим папкам: • AppData — к папке Application Data, в которой приложения хранят свои данные; • Cache — Internet Explorer с временными файлами Интернета; • Cookies - Cookies с небольшими файлами, которые сервер записывает на компьютер пользователя; • Desktop — папке, содержащей ярлыки Рабочего стола; • , Favorites - папке, хранящей ярлыки из меню Пуск > Избранное; • Fonts — папке, содержащей установленные шрифты; • My Pictures -Мои рисунки; • Personal — Мои документы; • Programs — папке с ярлыками меню Пуск > Программы; • Recent — папке с ярлыками недавно открывавшихся документов; • SendTo — папке с ярлыками Отправить (то есть отправить в Мои документы, на Рабочий стол и т. д.); • start Menu - папке с ярлыками меню Пуск; • startup — папке с ярлыками Автозагрузка. Вы можете выяснить путь к одной из этих папок и совершить необходимые действия. В нашем случае (для автоматической загрузки программы) — поместить ярлык своей программы в папку, путь к которой хранится в параметре startup. Можно, допустим, написать программу, которая очищает список недавно открытых документов, который показывается в меню Пуск > Документы. Достаточно, прочитав параметр recent, узнать путь к папке, хранящей этот список, и удалить из нее все ярлыки. В следующем листинге приведен фрагмент программы, определяющий путь к папке Автозагрузка меню Пуск, который затем помещается в переменную s. Листинг 19.4 reg := TReglniFi le.Create; reg.RootKey := HKEY_CURRENT_USER; // открываем параметр только для чтения reg.OpenKeyReadOnly (\Software\Microsoft\Windows\CurrentVersion\Explorer\); s := reg. ReadString(ShelI Folders,startup,); reg.CloseKey; reg.Free;
Глава 19. Реестр Windows Если вместо startup подставить в листинг другой параметр (например, desktop), то можно получить пути к специальным папкам Windows (рис. 19.4). Путеводная звезда Г Пути к некоторым папкам Window*""""'""~ | ;Автозапуск"
iCAWINOOWSXrnaeHoe меню\Программы\Автоэаграэка
Рис. 19.4. Программа показывает все основные служебные папки Windows
Данный способ полезен тем, что пути будут получены вне зависимости от версии Windows. Готовое приложение и полные листинги рассмотренного примера вы можете найти на компакт-диске, прилагаемом к книге, по адресу Examples\RegistryGetPath.
Глава 20
Локальные сети Q Разновидности сетей Q Программы для передачи сообщений по локальной сети Q Управление другим компьютером по сети
Глава 20. Локальные сети
20.1. Разновидности сетей Сети бывают, например, рыболовные. Есть еще сети электроснабжения. Существуют также локальные компьютерные сети, о них и пойдет речь в этой главе. Сейчас практически каждый человек наслышан о сети Интернет, хотя большинство не совсем понимает, что это такое. Думаю, что читатели этой книги весьма подготовлены в таком плане и знают, что Интернет — это сеть, состоящая из других, более мелких компьютерных сетей со всего мира. У Интернета нет единого центра, как и нет одного управляющего, то есть Интернет — децентрализован. Это очень неплохо, но в последнее время государственные организации пытаются начать контролировать содержимое Интернета. Вообще сеть — это несколько компьютеров, объединенных с помощью кабеля или еще чего-то, что позволяет им передавать информацию друг другу. Два компьютера, соединенные каким-нибудь шнуром, по которому идут электрические сигналы, — это уже сеть. Маленькая, простая, но сеть. Обычно двух компьютеров, просто соединенных кабелем, недостаточно. На небольших предприятиях применяются маленькие локальные сети, содержащие 3-10 компьютеров. Однако даже если в сети всего два компьютера, то все равно лучше их соединять не через СОМ- или USB-порт, а с помощью сетевых карт. Они стоят очень недорого, но позволяют очень быстро передавать данные по сети — до 100 Мбит/с. Получается, что фильм в формате MPEG4 будет копироваться с одного компьютера на другой примерно минуту. Такой скорости невозможно достичь, используя СОМ-или USB-порт. Топология14 сети может быть разной. Основных топологий две. Есть еще комбинированные из этих двух, но если долго комбинировать, то, согласно математическим законам, можно получить 256 различных вариаций. Итак, две основные топологии: • «шина» — все компьютеры соединены последовательно одним кабелем. С обоих концов должны стоять терминаторы — специальные устройства, подавляющие любые посторонние сигналы, чтобы они не отражались и не шли назад. При такой топологии сети применяется коаксиальный кабель. Максимальная скорость передачи данных — всего лишь 10 Мбит/с; • «звезда» — каждый компьютер в сети подключен при помощи отдельного провода к концентратору (его также называют hub). Такой принцип построения сети является более надежным и скоростным, чем «шина». Максимальная скорость передачи данных — 100 Мбит/с. Мы не будем подробно касаться технических вопросов создания сетей (есть несколько способов — «на витой паре», коаксиальным кабелем, с концентратором Термин «топология» в данном случае обозначает устройство сети, принцип, на котором сеть построена.
Разновидности сетей или же без него), все-таки книга не об этом. Рассмотрим более близкие теме нашей книги вопросы программирования для локальных сетей. Информация по сети передается в виде пакетов. Конечно, не в полиэтиленовых, а в таких условных пакетах — так называют блоки информации. Пакеты создаются в соответствии с протоколами — правилами обмена информацией в сети. Существует несколько наиболее распространенных протоколов, одним из которых является TCP/IP. В сетях используется очень большое количество протоколов. Для передачи файлов — FTP, для получения электронной почты — РОРЗ, для ее отправки — SMTP. В принципе большинству программистов подробно знать все протоколы необязательно, так как разработано много компонентов для работы .с любыми сетевыми протоколами. Однако, чтобы получались нормально работающие программы, следует иметь хотя бы общее представление о протоколах, которые вы используете в своей программе. Я же хочу вам показать, как создать сетевую программу, работающую напрямую без применения протоколов. Конечно, программист при этом не сможет полностью использовать возможности сети, но зато получит выигрыш по времени. Эта программа будет построена на двух принципах. Во-первых, в своей программе можно использовать программы, команды и службы Windows. Во-вторых, если у компьютеров есть доступ к общему дисковому пространству, то вполне можно организовать обмен с помощью файлов. Например, я уже создавал такую программу. На одном из компьютеров (это был специально выделенный сервер сети) была установлена программа, которую можно было запустить с любого компьютера. Если программа на одном из компьютеров записывала что-то в файл, то все остальные программы на других компьютерах, «увидев», что время изменения файла стало другим (можно также отслеживать по размеру файла, но это менее надежно), по очереди перечитывали файл. Чтобы не возникали ошибки доступа к файлу (одновременно файл могла читать только одна программа), был реализован специальный механизм, когда программы ждали того момента, когда файл освободится, и тогда начинали его читать. Компьютеры работают быстро, поэтому получалась иллюзия одновременной работы с файлом. Как видите, при этом не была использована ни одна сетевая технология. Доступ к дисковому пространству на сервере осуществлялся операционной системой автоматически, а дальше все работало обычными способами. Мы же в следующем разделе рассмотрим даже более простой способ работы сети без специальных протоколов.
Глава 20. Локальные сети
20.2. Программы для передачи сообщений по локальной сети В сетевых операционных системах (Windows 2000/XP) есть команда NET. В Windows 95/98/Ме такая команда тоже есть, но она выполняет функции связи компьютеров по сети, поэтому нам не подходит. Из-за этого программа, которую мы сейчас будем создавать, работает только в Windows 2000/ХР. Помимо прочих функций, команда NET выполняет пересылку сообщений другим компьютерам сети. При этом отправкой и приемом сообщений занимается специальная служба Windows NT, которая всегда автоматически загружается при каждом запуске Windows. Благодаря этой службе, чтобы пользователь другого компьютера сети получил ваше сообщение, ему не нужно абсолютно ничего запускать. Если его компьютер включен и если система Windows загружена, то он обязательно получит сообщение. Для отправки сообщения вам нужно написать в командной строке «net send comp_kassa Привет!», если, допустим, имя компьютера, на который вы посылаете сообщение, — COMP_KASSA. Пользоваться командной строкой весьма неудобно, особенно если приходится работать, например, в Word, Excel или еще какой-либо программе. Программа (назовем ее Messenger), которую мы будем разрабатывать, и станет удобной оболочкой для этой команды. Можно также сказать, что мы разрабатываем программу, которая будет использовать команду NET «в своих интересах» — для пересылки сообщений по сети. Программа будет довольно объемной. Не в плане количества исходного текста, а в том, что будут использоваться три формы и очень много компонентов. Полностью программа приведена на компакт-диске, который прилагается к книге, по адресу Examples\Messenger (NT only). Мы же рассмотрим ее частично, просто для того, чтобы проиллюстрировать принципы работы. В программе Messenger предусмотрено окно, с помощью которого можно добавлять и удалять компьютеры из списка доступных. Этот список хранится в обычном текстовом файле net. txt, поэтому его вполне можно корректировать и вручную. Таким образом, без описания второй формы вполне можно обойтись. Третья форма — это просто краткая справка по программе, в которой описано, что делает программа и как нужно посылать сообщения. Подробно разбирать создание третьей формы также не обязательно. Остается первая, основная форма (рис. 20.1), именно ее создание мы рассмотрим.
Программы для передачи сообщений по локальной сети /'-. Messengef файл Sw ; Кому посылать сообщение?' Выбрать все компьютеры Убрать все компьютеры | Отправить сообщение
1
'. F7 Очищать строку с сообщением после отправки
Рис. 20.1. Формы программы для передачи сообщений по сети
Создайте в Delphi новый проект и поместите на "форму компоненты GroupBoxl, Editl, CheckBoxl, Labell, Timerl, MainMenul, Labell. На рамку GroupBox добавьте еще несколько компонентов: CheckListBoxl, Buttonl, ButtonZ и Buttons. Разместите все это примерно так, как показано на рис. 20.2. В MainMenu добавьте пункты Файл > Выход и Вид > Отображать часы. Можете также посмотреть на изображение меню, чтобы все сделать аналогичным образом (см. рис. 20.2). * Отображать часы
Рис. 20.2. Главное меню программы в режиме разработки
Чтобы был более понятен приведенный ниже листинг, опишем, как работает программа. Приложение использует два текстовых файла: net. txt и adresat. txt. В net. txt хранятся сетевые имена доступных компьютеров, то есть таких, кому в принципе можно отправить сообщение. В файле adresat. txt — список только тех компьютеров, которым действительно будет отправлено сообщение. Причем файл adresat. txt автоматически обновляется при выходе из программы (в соответствии с содержимым CheckListBoxl), а при запуске программы файл читается и исходя из его содержимого устанавливаются флажки в CheckListBoxl. Сообщение нужно набирать в текстовом поле Editl внизу окна. При нажатии кнопки Buttons выполняется процедура Button3Click, которая отправляет сообщение всем компьютерам (при помощи команды NET), отмеченным в CheckBoxListl. Затем процедура Buttonsclick очищает текстовое поле и помещает на этикетку Labell текст о том, что сообщение отправлено.
Глава 20. Локальные сети Помимо этих основных функций, в программе, есть часы в правом нижнем углу окна, которые показывают текущее время. Часы можно отключать через меню Вид > Отображать часы. Таймер нужен как раз для того, чтобы обновлять содержимое Labell, отображая часы. Интервал таймера — 50. Теперь рассмотрим листинг программы, который содержит подробные комментарии. Листинг 20.1
unit Unit"!; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Menus, StdCtrls, S h e l l A p i , ExtCtrls, Chec'kLst;
type TForml = class(TForm) GroupBoxl: TGroupBox; MainMenul: TMainMenu; N1: TMenultem; N2: TMenultem; Editl: TEdit; CheckBoxl: TCheckBox; Labell: TLabel; N8: TMenultem; N9: TMenultem; Timer"!: TTimer; CheckListBoxl: TCheckListBox; Label2: TLabel; Buttonl: TButton; Button2: TButton; ButtonS: TButton; procedure LoadNetAdresat; procedure SaveNetAdresat; procedure procedure procedure procedure
N2CI ick(Sender: TObject); FormActivate(Sender: TObject); ButtonlCI ick(Sender: TObject); Button2CI ick(Sender: TObject);
Программы для передачи сообщений по локальной сети procedure FormClose(Sender: TObject; var Action: TCIoSeAction); procedure ButtonSCI ick(Sender: TObject); procedure N9CI ick(Sender: TObject); procedure Timer1Timer(Sender: TObject); private { Private declarations } public { Publ ic declarat ions } end; var Form"!: TForml;'
implementation {$R *.dfm} procedure TForml.N2CI ick(Sender: TObject); // пункт Выход меню Файл // закрывает форму begin Form"! .Close; end; procedure TForml.LoadNetAdresat; // при запуске программы выполняется загрузка списка // адресатов и установка соответствующих флажков var FT: TextFi le; s: String; i : Integer; begin // загрузка списка адресатов CheckListBoxl.Items.LoadFromFile(net.txt); // установка соответствующих флажков AssignFi le(FT,adresat.txt); Reset(FT); whi le not Eof(FT) do begin ReadLn(FT.s); for i := 0 to CheckListBoxl.Items.Count-1 do if s=CheckListBox1.Itemsfi] then CheckListBox1.Checked[ i ] :=True; end; CloseFHe(FT); end;
Глава 20. Локальные сети procedure TForml. FormAct ivate(Sende,r: TObject); var FT: TextFi le; begin // проверка наличия файлов и их создания AssignFi le(FT, net.txt); {$!-} Reset(FT); {$!+}
if IQResultoO then Rewrite(FT); CloseFi le(FT); AssignFi le(FT,adresat.txt); Выполнить, хотя мне кажется, что Total Commander или FAR Manager15 в данном случае удобнее), записываем, например, следующую строку: brcc32.exe pictur. гс Программа быстро выполнится (на мгновение появится черное окно консоли) и создаст нужный нам для дальнейшей работы файл pictur . res. Теперь создаем в Delphi процедуру, которая выведет изображение на форму (если вы используете изображения в формате JPEG, не забудьте объявить библиотеку Jpeg в uses, если же картинки в формате BMP, то объявлять jpeg не нужно). Листинг 21. 5 implementation
J:6fl
{$R *.dfm} Total Commander (раньше назывался Windows Commander) и FAR Manager — очень распространенные оболочки для управления файлами в Windows (как Norton Commander в MS-DOS).
Запуск других приложений при помощи своей программы {$R p i c t u r . r e s }
. •
procedure LoadGraphics(i: Integer); // загружаем графику из ЕХЕ-файла
var jpg: TJPEG I mage; ResStream: TResourceStream; begin jpg := TJPEGI mage. Create; ResStream := TResourceStream.CreateFromID (HInstance, i , RT_RCDATA); jpg. LoadFromStream(ResStream); // загружаем Form"!. Imagel.Canvas.Draw(0, 0, jpg); // выводим на форму jpg. Free; ResStream. Free;
end; В листинге приведена также небольшая часть текста программы над процедурой LoadGraphics. Обратите внимание на строку {$R pictur. res}. Это и есть подключение файла, созданного утилитой Ьгсс32. Процедура LoadGraphics выводит на форму изображение под номером i. В нашем RES-файле три картинки. Значит, можно отобразить картинку под номером один, либо под номером два, либо под номером три.
21.3. Запуск других приложений при помощи своей программы Программы, при помощи которых можно запускать другие программы, известны всем. Это Проводник, FAR Manager, Windows Commander. Рассмотрим, как сделать так, чтобы можно было запускать другие приложения, используя свою программу. Есть несколько способов запускать внешние программы. Первый — с помощью исключительно API-функций. Этот способ не будем рассматривать из-за относительной сложности использования (хотя исполняемый файл получается компактный). Второй — используя winExec. Это самый простой способ.
,
Третий — с помощью ShellExecute.
WinExec
..'••.
•
•
Вызвать winExec — это почти то же самое, что набрать имя файла, который хотите запустить, в строке выполнения программы (Пуск * Выполнить). winExec имеет
Глава 21. Полезные и интересные программы очень простой синтаксис. Ее легко использовать. Для применения winExec ничего дополнительного в uses объявлять не нужно, достаточно стандартных библиотек. Листинг 21.6 procedure TForm1.Button1CI ick(Sender: TObject); begin WinExec(c:\windows\notepad.exe,SW_SHOWNORMAL); end;
.' •
•
/
Этот пример запускает текстовый редактор Блокнот. Первый параметр WinExec указывает путь к программе и сам ЕХЕ-файл, второй параметр определяет, как запускать программу. SW_SHOWNORMAL означает, что программу следует запустить в обычном режиме. Если указать SW_MINIMIZE, то программа запустится и сразу же свернется на Панель задач. Если в параметрах WinExec вы хотите указать не путь к файлу программы в кавычках, а переменную типа string, то ее нужно преобразовывать в PChar так, как показано в следующем листинге. •' ' : '• . . . . ' . . Листинг 21.7 procedure TForm1.Button1CI ick(Sender: TObject); begin WinExec(PChar(s),SW_SHOWNORMAL); end;
•
Это связано с тем, что Windows оперирует текстом в формате, который в Delphi представлен как PChar.
ShellExecute ShellExecute имеет более широкие возможности, чем WinExec. Однако ее использование увеличивает размер получаемого исполняемого файла, причем передается больше параметров. Листинг 21.8
procedure TForm1.Button1CI ick(Sender: TObject); begin She I I Execute (0,open,с:\w indows\notepad.exe,,,SW_SHOWNORMAL); end;
,
Если запустить программу с такой процедурой, то результатом будет запуск текстового редактора Блокнот.
Запуск других приложений при помощи своей программы В отличие от winExec здесь можно назначать рабочую директорию программы, передавать программе параметры и т. д. С помощью ShellExecute можно также запускать программы, ассоциированные с каким-нибудь типом файлов. Например, конструкция, описанная в следующем листинге, запустит почтовый клиент, в котором автоматически будет создано новое письмо по шаблону. Листинг 21.9
procedure TForm1.Button2CIick(Sender: TObject); begin She 1 1 Execute (0,open,mai lto:
[email protected],,,SW_SHOW);
end; Приложение, содержащее представленный ниже код, откроет программу, которая по умолчанию запускается для просмотра JPEG-файлов. Листинг 21.10
procedure TForm1.Button2CI ick(Sender: TObject); . begin Shel IExecute(0,open,c:\1. jpg,,,SW_SHOW);
end; У пользователей могут быть установлены разные программы для просмотра графических файлов. Если нет какой-нибудь другой программы, то в Windows по умолчанию используется Internet Explorer, я же, например, очень давно применяю для этих целей ACDSee32. Также в роли программы для просмотра графических файлов могут выступать Microsoft Photo Editor, Paint или даже Adobe Photoshop. Следующая процедура откроет Word. Я думаю, вы уже поняли принцип вызова других приложений при помощи ShellExecute. Листинг 21.11
procedure TForm1.Button2CIick(Sender: TObject); begin ShelI Execute (0,open,с:\Мои документы\иоЬз^ос,, ,SW_SHOW);
end; Обратите внимание, что, применяя WinExec, так сделать не получится. Однако при использовании ShellExecute приходится расплачиваться большим размером исполняемого файла. Если вы применяете переменную типа string в ShellExecute, то ее так же, как и в WinExec, нужно преобразовывать в PChar функций PChar (s: String) : PChar.
Глава 21. Полезные и интересные программы
21.4. Эффекты с кнопкой Пуск и Панелью задач При помощи следующих процедур устроим маленький беспорядок на компьютере пользователя. Сначала скроем кнопку Пуск, все равно ею уже давно никто не пользуется. Убрать ее несложно. В следующем листинге показано как: Листинг 21.12 procedure TForm1.RadioButton1CI ick(Sender: TObject); // выключаем кнопку Пуск begin EnableWi.ndow (FindWindowEx (FindWindow(Shel LTrayWnd, ni I), 0, Button, ni I), False); end; В этой одной строке кода (я разбил ее на три только для того, чтобы поместилась на страницу) происходят поиск окна Shetl_TrayWnd (это и есть Панель задач) и изменение его параметров. Последний параметр False говорит о том, что кнопку надо выключить. Если без кнопки стало неуютно, то ее снова можно включить. Это производится той же процедурой, только параметр будет True. Листинг 21.13
procedure TForm1.RadioButton2CI ick(Sender: TObject); // включаем кнопку Пуск begin EnableWindow (FindWindowEx (FindWindow(Shell_TrayWnd, n i I ) , 0 , B u t t o n , n i I ) , T r u e ) ; end; Если не нужна Панель задач (она уж точно никому не нужна), то ее можно спрятать. Листинг 21.14
procedure T'Forml. RadioButtonSCIick(Sender: TObject); // прячем Панель задач begin ShowWindow(Fi ndWindow(Shell_TrayWnd, nil),sw_hide);
end;
Постоянно открывающийся лоток привода компакт-дисков Как видите, вызываем процедуру отображения окна. Само окно находим функцией FindWindow. О том, что панель нужно спрятать, сообщает параметр sw_hide. Чтобы не нервировать пользователей, лучше вернуть Панель задач на ее обычное место — вниз экрана. Листинг 21.15
procedure TForml.RadioButton4CI ick(Sender: TObject); // возвращаем Панель задач на ее обычное место begin ShowWindow(FindWindow(Shell_TrayWnd,nil),sw_show); end;
Готовые приложения и полные листинги рассмотренных примеров вы можете найти на компакт-диске, прилагаемом к книге, по адресам ExamplesXPusk и Examples\Веzobraz.
21.5. Постоянно открывающийся лоток привода компакт-дисков Рассмотрим программу, которая будет заставлять производить странные действия привод для компакт-дисков. Внешне программа себя проявлять не будет, кроме как наличием в списке выполняемых приложений «Диспетчера задач», но время от времени лоток для компакт-дисков будет открываться и закрываться. После запуска программы будет выведено сообщение, после чего начнется бесконечный цикл, в процессе работы которого лоток привода компакт-дисков будет выдвигаться через случайное количество секунд. Можно было бы, конечно, даже спрятать эту программу из списка выполняемых, однако это было бы слишком жестоко. Пускай хотя бы у опытных пользователей будет возможность справиться с этой шуткой. Мне рассказывали, как некий системный администратор написал примерно такую же программу, которая то выдвигала, то убирала лоток для компакт-дисков, и установил ее на компьютер бухгалтерии фирмы, в которой работал. Через некоторое время он зашел в бухгалтерию, чтобы посмотреть, как же сотрудники справляются с постоянно открывающимся лотком привода компакт-дисков. Оказалось, что они просто заклеили отсек скотчем! При этом привод постоянно пытался выдвинуть лоток, но ему это не удавалось. Бухгалтеров, не особо вникающих в суть издаваемых компьютером звуков, такое решение проблемы вполне удовлетворило. Я же ни в коем случае не рекомендую вам так делать. Несмотря на то что все производители аппаратного обеспечения компьютера стараются сделать защиту от повреждений (то есть проблематично сломать даже при большом желании), все равно лучше не испытывать устройства компьютера на прочность. Если вы не уверены, что ваши эксперименты пройдут безопасно, то не стоит их проводить. 13 Зак. 899
Глава 21. Полезные и интересные программы Примеры, приведенные в этой книге, разумеется, протестированы мной на соб16 ственном компьютере , и достаточно безопасны. Если же вы будете что-то изменять в этих программах, то делайте это осторожно. Например, не делайте слишком маленькую паузу между открытием и закрытием дверцы привода компакт-дисков. Да и постоянное ее движение может привести к преждевременному износу, поэтому не переусердствуйте. Теперь, когда я вас предупредил обо всех последствиях, можно перейти к программе. В приложении будет применяться несколько особенностей, ранее в книге не описанных, поэтому объясню их назначение. Процедура sleep ожидает указанное количество миллисекунд, прерывая выполнение программы. В нашем случае применена для того, чтобы задавать промежуток между открытиями лотка для компакт-дисков. Для использования процедуры Sleep нужно объявлять в uses библиотеку Windows. Отправка сообщения устройству выполняется при помощи команды mcisendstring. Именно с ее помощью можно управлять чтением компакт-дисков. Для применения нужно объявить библиотеку MMSystem. Вывод сообщений на экран осуществлен с помощью MessageDlg (похожие функции выполняет showMessage). Для применения в программе этой функции необходимо объявить библиотеку Dialogs. Далее приведен листинг программы. В новом проекте были удалены модуль формы (Project > Remove from Project > Forml > Ok) и все лишние библиотеки из раздела uses. Программа написана в модуле проекта View > Units > Projectl). Листинг 21.16
program Projectl;
\ uses Windows, MMSystem, Dialogs; {$R *.res}
var n: Integer; // для генерации случайных чисел begin II ******************************* // открываем лоток на фиксированное
Чаще всего компьютер зависал при написании именно этой главы, особенно во время экспериментов с кнопкой Пуск и Панелью задач.
Постоянно открывающийся лоток привода компакт-дисков _ // время и выводим сообщение sleep( 1000*5); // ждем пять секунд // открываем лоток привода компакт-дисков mciSendString(Set cdaudio door open wait, n i l , 0, 0); messaged I g( Необходимо положить компакт-диск в привод компакт-дисков, mt Information, [mbOk], 0); sleep(1000*2); // ждем две секунды // закрываем лоток привода компакт-дисков mciSendString(Set cdaudio door closed w a i t , ni 1 , 0, 0); // организуем цикл, в котором лоток для компакт-дисков // будет выезжать через случайное количество секунд randomize; w h i le True do // пример бесконечного цикла begin n := Random(15)+1; sleep(1000*n); //ждем n-ное количество секунд // затем открываем лоток mciSendString(Set cdaudio door open wait, n i l , 0, 0); sleep(1000*1); // через секунду // закрываем mciSendString(Set cdaudio door closed wait, ni 1 , 0, 0);
end; end. Вот и все. Текст программы снабжен достаточно подробными комментариями. Файл ЕХЕ, конечно, получается не очень компактный (почти 400 Кбайт), но это не очень много. Приведенную программу можно усовершенствовать, установив возможность добавления в автозагрузку. Тогда даже перезагрузка компьютера не поможет — лоток будет открываться и закрываться до тех пор, пока пользователь не уберет программу из списка выполняемых приложений или пока не произойдет вызов программы с параметром по. Добавим в программу проверку параметров командной строки. Если вызвать программу с параметром yes, то она добавится в автозагрузку, если с параметром по, то удалится из автозагрузки17. Параметр вызываемой программе можно указывать несколькими способами. Например, с помощью командной строки (Пуск > Выполнить) либо в файловых менеджерах (FAR Manager, Total Commander). Необходимо написать его после имени программы через пробел.
Глава 21. Полезные и интересные программы В следующем листинге показаны нововведения (помимо прочего, в uses добавлена библиотека Registry). Листинг 21.17
program Project"!;
uses Windows, MMSystem, Dialogs, Registry; {$R *. res}
var n: Integer; // для генерации случайных чисел procedure AddToAutorun; // добавляем программу в автозагрузку var reg: TRegIniFi le; begin // создаем reg reg := T R e g l n i F i le.Create; // назначаем раздел реестра Windows reg.RootKey := HKEY_LOCAL_MACHINE; // открываем раздел, с которым будем работать reg.OpenKey (\Software\Microsoft\Windows\CurrentVersion, True); // записываем reg.WriteString(Run,CrazyCD3005,paramstr(O)); // закрываем параметр reg.CloseK.ey; // освобождаем reg reg.Free; МеззадеО!д(Поместили в автозагрузку, rut Information, [mbOk], 0); end; procedure RemoveFromAutorun; // удаляем программу из автозагрузки var reg: T R e g l n i F i le; beg in // создаем reg reg := T R e g I n i F i le.Create; // назначаем раздел reg.RootKey := HKEY_LOCAL_MACHINE;
Постоянно открывающийся лоток привода компакт-дисков // открываем раздел, с которым будем работать гед.ОрепКеу (\Software\Microsoft\Windows\CurrentVersion, True);
// удаляем папаметр reg.DeleteKey(Run, CrazyCD3005); // закрываем параметр reg.CloseKey; // освобождаем reg reg.Free; Меззаде01д(Убрали из автозагрузки, mt Informal ion,[mbOk],0); end; begin // анализируем параметры командной строки // 1 - означает первый параметр, которых // может быть много if paramstr(1) then // параметр есть begin if paramstr(1)=yes then AddToAutorun; if paramstr(1)=no then RemoveFromAutorun; end; // сначала открываем лоток на фиксированное // время и выводим сообщение sle.ep( 1000*5); // ждем пять секунд // открываем лоток привода компакт-дисков mciSendString(Set cdaudio door open wait, n i l , 0, 0); Меззаде01д(Необходимо положить компакт-диск в привод компакт-дисков, mt Information,[mbOk], 0); sleep(1000*2); // ждем две секунды // закрываем лоток привода компакт-дисков mciSendString(Set cdaudio door closed w a i t , n i I , О, О);
//** // организуем цикл, в котором лоток для компакт-дисков // будет выезжать через случайное количество секунд randomize; whi le True do // пример бесконечного цикла
I
—
Глава 21. Полезные и интересные программы
begi n n := random(15)+1; sleep(1000*n); // ждем n-ное количество секунд // затем открываем лоток mciSendString(Set cdaudio door open wait, n i l , 0, 0); sIeep(1000*1); // через секунду // закрываем mciSendString(Set cdaudio door closed w a i t , ni 1 , 0, 0); end; end. Готовое приложение и полные листинги рассмотренного примера вы можете найти на компакт-диске, прилагаемом к книге, по адресу ExamplesXCrazyCD.
21.6. Появляющийся время от времени «Блокнот» Используя предыдущий пример как заготовку, можно создать множество других интересных программ, даже не касающихся устройств компьютера. Постоянно выезжающий лоток привода компакт-дисков мне не нравится гораздо больше, чем какие-то программные сбои. Допустим, я знаю, что беспрерывно возникающий на экране Блокнот не принесет вреда компьютеру. Рассмотрим пример такой программы, которая каждые несколько секунд будет открывать Блокнот. Так же, как и у программы для привода компакт-дисков, у нее не будет окна, и точно так же эту программу можно будет добавлять в автозагрузку. Добавится возможность принудительно закрывать программу, однако необычным способом: программа перед каждым запуском Блокнота будет проверять, существует ли файл stop. dat. Если файл появился, то программа закрывает сама себя. Таким образом, чтобы закрыть программу, нужно создать файл stop. dat. Если вы снова захотите запустить программу, то этот файл нужно будет удалить. Как видите, программой можно управлять. Промежуток в секундах, по истечении которого запускается новая копия Блокнота, сделаем достаточно большим. Благодаря этому получится эффект загадочности, и будет непонятно, почему изредка открывается текстовый редактор. Чтобы определить наличие файла stop.dat, воспользуемся функцией FileExists, для чего придется объявить библиотеку SysUtils в разделе uses. Блокнот обычно находится в директории с Windows, поэтому необходимо выяснить путь к директории, в которой установлена операционная система. Для этого можно задействовать функцию Windows API — GetWindowsDirectory, использовать которую не очень сложно, но и не так просто, как многие другие. Перед вызовом функции необходимо провести некоторые действия.
Появляющийся время от времени «Блокнот» Во-первых, нужно объявить массив символов char: windir: array [0..200] of Char; // директория Windows Во-вторых, заполнить его пробелами: FillChar(windir,200,Ord('.')); В-третьих, вызвать функцию GetwindowsDirectory и затем, удалив ненужные пробелы (функцией Trim), использовать полученный путь к директории Windows в своих целях. В итоге получилась программа, показанная в следующем листинге. Листинг 21.18 program Project"!; uses Windows, MMSystem, Dialogs, Registry, SysUtils; {$R *. res}
var n: Integer; // для генерации случайных чисел w i n d i r : array [0..200] of Char; // директория Windows s: String; // путь к Блокноту procedure AddToAutorun; // добавление программы в автозагрузку var reg: T R e g l n i F i le; begin // создаем reg reg := T R e g l n i F i le.Create; // назначаем раздел r.eg.RootKey := HKEY_LOCAL_MACHINE; // открываем раздел, с которым будем работать reg.OpenKey (\Software\Microsoft\Windows\CurrentVersion, True); // записываем reg.WriteString(Run,CrazyNotepad3005,paramstr(O)); // закрываем параметр reg.CloseKey; // освобождаем reg reg.Free;
Глава 21. Полезные и интересные программы
messaged1д(Поместили в автозагрузку, mtInformation,[mbOk],0); end; procedure RemoveFromAutorun; // удаление программы из автозагрузки var reg: TReglniFi le; begin // создаем reg reg := T R e g l n i F i le.Create; // назначаем раздел reg.RootKey := HKEY_LOCAL_MACHINE; // открываем раздел, с которым будем работать reg.OpenKey (\Softwa re\M i с rosoft\Wi ndows\Cu r rentVe rs i on, True); // удаляем параметр reg.DeIeteKey(Run,CrazyNotepadSOOS); // закрываем параметр reg.CloseKey; // освобождаем reg reg.Free; МеззадеО!д(Удалили из автозагрузки, mtInformation,[mbOk],0); end; begin // анализируем параметры командной строки // 1 - означает первый параметр, которых // может быть много if paramstr(1) then // параметр существует beg in if paramstr(1)=yes then AddToAutorun; if paramstr(1)=no then RemoveFromAutorun; end; // начинаем цикл, в ходе которого запускается // Блокнот Randomize; whi le True do // бесконечный цикл begin n := Random(120)+1; // промежуток может достигать двух // минут
«Подмигивающий» автомобиль sleep(1000*n); // ждем n-ное количество секунд // затем проверяем, есть ли файл stop.dat if Fi leExists(stop.dat)=True then E x i t ; // после чего определяем директорию Windows Fi I I C h a r ( w i n d i r,200, Ord( )); // заполняем строку // пробелами GetWindowsDirectory(windiг,200); // определяем директорию s := T r i m ( w i n d i r ) ; // удаляем лишние пробелы f s := s + \notepad.exe; // запускаем Блокнот WinExec(pchar(s),SW_SHOW); end; end.
Готовое приложение и полные листинги рассмотренного примера вы можете найти на компакт-диске, прилагаемом к книге, по адресу Examples\CrazyNotepad. Можно вместо Блокнота запускать и другие программы. Однако необязательно те программы, запуск которых вы разработали, установлены на всех компьютерах. Например, даже такая распространенная программа, как стандартный Калькулятор, в Windows 98 и Windows XP располагается в разных директориях18. Блокнот же хорош тем, что во всех версиях Windows располагается всегда непосредственно в той директории, куда установлена система.
21.7. «Подмигивающий» автомобиль Возможно, пользователям, неравнодушным к автомобилям, понравится следующее небольшое приложение. Программа, которую мы рассмотрим в этом разделе, выводит на форму фотографию автомобиля, и затем можно управлять этим автомобилем, включая указатели поворота и аварийную сигнализацию (когда мигают оба указателя поворота одновременно). Перед написанием кода нужно поместить на форму три кнопки Button, две этикетки Label и один таймер (рис. 21.1). /
1соя вишневая по.
ИИ 131
Рис. 21.1. Окно программы в режиме разработки 18
В Windows XP Калькулятор расположен по адресу: с: \Windows\System32\calc.exe, а в Windows 95/98 здесь: с: \ W i n d o w s \ c a l c . ехе.
Глава 21. Полезные и интересные программы Листинг 21.19
u n i t Unit"!; i interface uses Windows, Messages, SysUt i Is, Variants, Classes, Graphics, Controls, Forms, Dialogs, Jpeg, StdCtrls, ExtCtrls; type TForm"! = class(TForm) Label 1: TLabel; Label2: TLabel; Timer"!: TTimer; Button"!: TButton; Button2: TButton; ButtonS: TButton; procedure FormActivate(Sender: TObject); procedure FormPaint(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCIoseAction); procedure ButtonlCI ick(Sender: TObject); procedure Timer1Timer(Sender: TObject); procedure Button2CI ick(Sender: TObject); procedure ButtonSCI ick(Sender: TObject); private { Private declarations } publ ic { P u b l i c declarations } end; ;
const interv1=400; // интервал для сигналов поворота interv2=200; // интервал для аварийной сигнализации
var Form"!: TForml; bmp: TBitmap; mode: Byte; // режим действия //О - никакого действия // 1 - мигает правый указатель поворота // 2 - мигает левый указатель поворота // 3 - режим включения сигнализации m i g : Byte; // количествово миганий в аварийном режиме implementation {$R *.dfm}
«Подмигивающий» автомобиль
procedure TForml. FormAct ivate(Sender: TObject); var jpg: TJpeglmage; //для загрузки JPG i : Integer; // счетчик в цикле begin // загружаем изображение jpg := TJpeglmage.Create; jpg.LoadFromFi le(21093.jpg); bmp := TBitmap.Create; bmp.Assign(jpg); jpg. Free; ВitBIt(Form1.Canvas.Handle,0,0,200,112, bmp.Canvas.Hand Ie,0,0,SRCCOPY); // размещаем компоненты // правый указатель поворота Label 1 . V i s i b l e := False; Labe11.Caption := ; Label 1.AutoSize := False; Labe11.Co I or := c l Y e l l o w ; Label 1.Width := 2; Labe11.Height := 6; Labe11.Left := 9; Labe11.Top := 51; // левый указатель поворота L a b e l 2 . V i s i b l e := False; Labe12.Caption := ; Labe 12. AutoSize := False; Labe12.Co I or := с I Ye I low; Label2.Width : = 3; Labe12. Height := 8; Labe 12. Left := 72; Labe12.Top := 51; // кнопки на форме for i := 1 to 3 do begin case i of 1: Sender := Buttonl; 2: Sender := Button2; 3: Sender ;= ButtonS; end; // case with Sender as TButton do begin Width := 66; Height := 24; Top := 112; end; // w i t h end; // i
Глава 21. Полезные и интересные программы Button2.Left := 0; Buttons.Left := 68; Buttonl.Left := 136; // таймер Timer"!. Interval := intervl; Timer"!. Enabled := False; // размер формы Form"!.Width := 208; Form"!.Height •;= 162; end; procedure TForml. FormPaint(Sender: TObject); beg i n BitBIt(Forra1.Canvas.Handle, 0,0,200,112,bmp.Canvas.Hand Ie,0,0,SRCCOPY); end; procedure TForml. FormClose(Sender: TObject; var Action: TCIoseAct ion); begin bmp.Free;
AppI icat ion. Term i nate; end; procedure TForml. ButtontCI ick(Sender: TObject); // правый указатель поворота // если выключен - включаем, // если включен - выключаем begin if mode1 then beg in L a b e l 2 . V i s i b l e := False; Label 1 . V i s i b l e := True; mode := 1; Тimerl.Enabled :=True; end else begin Label 1 . V i s i b l e := False; mode := 0; Timerl. Enabled := False; end; end; procedure TForml.Timer1Timer(Sender: TObject); // в таймере просто включаем и выключаем // нужные этикетки (Label) для создания // эффекта мигания указателей поворота
«Подмигивающий» автомобиль
begin case mode of 1: beg i n // правый указатель поворота if Labe11.Visible=FaIse then Label 1.Vis i b l e := True else Label 1 . V i s i b l e := False; end; // mode=1 2: b e g i n // левый указатель поворота if Label2.Visible=False then Label2.Visible := True else L a b e l 2 . V i s i b l e := False; end; // mode=2 3; begin // аварийная сигнализация if Label 1.Visible=True then begin Label 1 . V i s i b l e := False; Label2. V i s i b l e := False; Inc(mig); end else beg i n L a b e 1 1 . V i s i b l e := True; Label2.Visible := True; end; if mig=2 then // выключаем режим аварийной // сигнализации ' beg i n Label 1.Vis i b l e := False; Label2.Visible := False; Timerl. Enabled := False; Timer"!. Interval := intervl; end; end; // mode=3 end; // case end; procedure TForm1.Button2CI ick(Sender: TObject); // левый указатель поворота // если выключен - включаем, // если включен - выключаем begin if mode2 then beg i n
Глава 21. Полезные и интересные программы Label 1 . V i s i b l e := False; L a b e l 2 . V i s i b l e := True; mode := 2; TimeM. Enabled := True; end else begin Label2.Visible := False; mode := 0; Timer"!. Enabled := False; end; end; procedure TForm1.Button3Cl.ick(Sender: TObject); // режим аварийной сигнализации begi n m i g ;= 0; Label 1 . V i s i b l e := False; L a b e l 2 . V i s i b l e := False; mode := 3; Timer"!. Interval := interv2; Timer"!. Enabled :=True; end; end. На рис. 21.2-21.4 показана программа в режиме выполнения. Результат не очень хорошо заметен на черно-белых фотографиях в книге. Чтобы полностью насладиться полученной программой, см. приложение, находящееся на компакт-диске по адресу ExamplesXCar.
Рис. 21.2. Окно программы в режиме выполнения
Рис. 21.3. Включен левый указатель поворота
Разрешение экрана 320 х 200 пикселов в Windows ! I•:'• Iвоя вишневая де,.
Рис. 21.4. Включение охранной сигнализации i
В этой программе очень много возможностей для усовершенствования. Можно найти (или сделать самому, например в программе Sound Forge) соответствующие звуки и сопровождать включение указателей поворота щелчками, а сигнализации — короткими высокочастотными звуками. Можно добавить условие, что, если указателем мышки щелкнуть на двери или колесах, сработает охранная сигнализация (запустится соответствующий звуковой файл). Таким образом, есть возможность заставить машину заводиться и шуметь. Допустим, можно также добавить возможность случайной поломки автомобиля.
21.8. Разрешение экрана 320 х 200 пикселов в Windows Вы думаете, что Windows не работает при разрешении экрана 320 х 200 пикселов, что даже на самых древних видеокартах даже Windows 95 работает при 640 х 480? Рассмотрим программу, при помощи которой можно включить режим 320 х 200 пикселов на некоторое время. Создайте новый проект, разместите на форме шесть кнопок Button (рис. 21.5). Собственно, переключением разрешения занимается только одна процедура — Resolution, а каждая из кнопок просто передает нужные параметры этой процедуре.
800x600 1280x1024
^
1024x768 1600x1200
Рис. 21.5. Программа в режиме разработки
Если же какой-то видеорежим не может быть включен из-за недостаточной мощности видеокарты пользователя19, то он просто не включится. 19
У меня, например, максимальное значение, на которое способна видеокарта, — 1024 х 768 пикселов.
Глава 21. Полезные и интересные программы Листинг 21.20
unit Unit"!; interface uses Windows, Messages, SysUtiis, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForml = class(TForm) Button"!: TButton; Button2: TButton; ButtonS: TButton; Button4: TButton; ButtonS: TButton; ButtonB: TButton; procedure ButtonlCI ick(Sender: procedure Button2CIick(Sender: procedure ButtonSCIick(Sender: procedure Button4CI ick(Sender: procedure ButtonSCI ick(Sender: procedure Button6CIick(Sender: private { Private declarations } publ ic { Publ ic declarations } end;
TObject); TObject); TObject); TObject); TObject); TObject);
var Forml: TForml; implementation {$R *.dfm} procedure Resolution(x, у : Word); var dm : TDEVMODE; begin ZeroMemory(@dm, sizeof(TDEVMODE)); dm.dmSize := sizeof(TDEVMODE); dm.dmPelsWidth := x; dm.dmPelsHeight := y; dm.dmFields := DM_PELSWIDTH or DM_PELSHEIGHT; ChangeDisplaySettings(dm, 0); end;
Разрешение экрана 320 х 200 пикселов в Windows procedure TForml. ButtonlCI ick(Sender: TObject); beg i n Reso I lit ion (320, 200);
end; procedure TForml. Button2CI ick(Sender: TObject); begin
Resolution(640,480); end; procedure TForml. ButtonSCI ick(Sender: TObject); begin Resolution(800,600); end; procedure TForml. Button4CI ick(Sender: TObject); begin Resolutipn(1024, 768); end; procedure TForml. ButtonSCI ick(Sender: TObject); begin Resolution(1280,1024); end; procedure TForml. Button6CI ick(Sender: TObject); begin Resolution(1600,1200); end;
end.
/" 1 1еоекяючение вндеовежимов 640x480
800x600 1600x1200
Рис. 21 -6. Windows в разрешении 320 х 200 пикселов (здесь изображен целый экран) Готовое приложение (рис. 21.6) и полные листинги рассмотренного примера вы можете найти на компакт-диске, прилагаемом к книге, по адресу ExamplesWideoMode.
Глава 22
FAQ и сайты о Delphi Q FAQ, или ЧаВо Q Сайты о Delphi
FAQ, или ЧаВо
22.1. FAQ, илиЧаВо В этом разделе собраны сведения, призванные помочь решить несложные вопросы, которые тем не менее вполне могут отнять несколько часов продуктивной программистской деятельности. FAQ, или ЧаВо (answers on Frequently Asked Questions — ответы на Часто задаваемые Вопросы), очень распространены в Интернете. Данный раздел содержит не совсем FAQ. Здесь собраны в основном вопросы, которые возникали у меня или у моих коллег. Их ценность именном в том, что с подобными проблемами можете столкнуться и вы, программируя на Delphi. Приведенные в этом разделе ответы на вопросы ориентированы на использование именно с данной книгой и призваны решать вопросы, которые могут возникнуть с опубликованными листингами программ.
Странное поведение программы Вопрос. Код программы точно правильный, но программа ведет себя неадекватно: без всякой видимой причины изменяется какая-нибудь переменная (например, счетчик цикла). Что делать? Ответ. Можно попробовать отключить оптимизацию. Для этого выполните команду Project > Options, в открывшемся окне Project Options перейдите на вкладку Compiler и в области Code generation снимите флажок Optimization. Если проблема была действительно в этом, то она решится, и программа заработает как положено. Но чаще бывает так, что программист просто не может найти собственную ошибку в программе. Примечание. Если вы отключили оптимизацию, но проблема осталась нерешенной, значит, дело не в этом. Тогда внимательнее ищите ошибку в программе, включив оптимизацию (она очень полезна).
Не получается корректно добавить процедуру Вопрос. Программа выдает ошибку: Undeclared identifier:
''
Что она означает и как ее исправить? Ответ. Вы не объявили процедуру в разделе interface. Сделайте это вручную или. щелкните на названии процедуры правой кнопкой мыши и в открывшемся контекстном меню выберите пункт Complete class at cursor. Тогда процедура будет объявлена автоматически.
В DirectX не изменяется палитра Вопрос. Почему при попытке изменить палитру ничего не происходит? Цвета остаются такими же, как были.
Глава 22. FAQ и сайты о Delphi Ответ. В видеорежимах с 16-, 24- и 32-битным цветом палитра не изменяется. Примечание. В 16-битном видеорежиме — 65 536 цветов, а в 24-битном — 16,7 млн.
В DelphiX не выводятся изображения из DXImageList Вопрос. Вроде бы все правильно, код точно верный, но изображения из DXImageList не выводятся на DXDraw. Почему такое может быть? Ответ. Скорее всего, в свойстве DXDraw компонента DXImageList забыли выбрать DXDraw, с помощью которого изображения будут выводиться на экран. Не выводите с помощью Surface. Canvas ничего до вывода изображений из DXImageList. Например, если вы выводите точки и линии с помощью Surface. Canvas, то рисуйте их после вывода изображений из DXImageList.
Не работает команда NET в Windows 98 Вопрос. Почему не работает команда NET (отправка сообщений по локальной сети на другие компьютеры)? Ответ. Эта команда работает только в сетевых операционных системах Windows 2000/ХР. В Windows 95/98/Ме нет службы сообщений.
Не работают свойства AlphaBlend и AlphaBlendValue Вопрос. Почему в Windows 98 изменение в программе свойств AlphaBlend и AlphaBlendValue ни к чему не приводит? Окно программы в режиме выполнения как было непрозрачным, так и осталось. Ответ. Форма не станет прозрачной путем изменения свойств AlphaBlend и AlphaBlendValue в операционных системах Windows 95/98. Она будет прозрачной, только если запускать программу в Windows 2000/ХР. Тем не менее не имеет значения, в какой операционной системе компилировалась программа. Скомпилировать ее можно даже в Windows 95, но форма станет прозрачной, только если запустить получившийся ЕХЕ-файл в Windows 2000/ХР.
Один компонент расположен поверх другого, а нужно наоборот .
Вопрос. Что делать, если один компонент на форме закрывает собой другой компонент, а нужно наоборот, чтобы второй компонент частично накладывался на первый?
FAQ,или ЧаВо Ответ. Помещайте компоненты на форму в нужном порядке. Чем позже компонент добавлен на форму, тем выше он будет располагаться (на переднем плане). Используйте также команды меню Edit > Bring to Front и Edit > Send to Back. Примечание. Некоторые компоненты всегда находятся под другими. Например, этикетка Label всегда расположена ниже кнопки Button. В таком случае единственный выход — помещать этикетку, например, на рамку Group Box или (что даже лучше) на компонент Panel, который тоже является контейнером.
Правильное оформление выхода из программы Вопрос. Как правильно оформить выход из программы, то есть организовать запрос: «Уверены ли вы, что хотите выйти из программы?» Ответ. Нужно обрабатывать не событие OnClose главной формы, а событие OnCloseQuery, и реализовать в процедуре запрос. Если пользователь ответил отрицательно, то изменять значение переменной CanClose с True на False. Примечание. Наиболее корректный способ завершать приложение заключается в использовании Application. Terminate (это нужно записать в обработчике события OnClose главной формы).
Delphi не понимает формат JPEG Вопрос. Нужно работать с JPEG-файлом, но Delphi почему-то не понимает этот формат. Что делать? Ответ. В Delphi есть стандартная библиотека Jpeg. Нужно просто объявить ее в разделе uses. Для работы с Jpeg (загрузки в память, вывода на экран) используйте ТИП TJpeglmage. .
Обращение к приложению и к экрану Вопрос. Как обращаться к методам и свойствам приложения и экрана? Ответ. К методам и свойствам приложения следует обращаться с помощью объекта Application, экрана -1- с помощью Screen. Например: Application. Terminate, Screen.Width.
Работа с архивами и создание нестандартных компонентов Вопрос. Как работать с архивными файлами ZIP, RAR и другими? Как сделать красивые и нестандартные кнопки и прочие компоненты? Ответ. Скачивайте из Интернета специальные компоненты или создавайте их вручную.
Глава 22. FAQ и сайты о Delphi
Создание адекватного цикла Вопрос. Если в программе нужно долго что-то вычислять с помощью цикла, то для пользователя создается впечатление зависшей программы. Как сделать, чтобы было видно, что программа работает? Ответ. Нужно добавить в цикл строку Application.PrосеssMessages;
Правильное написание: «в Delphi» или «на Delphi» Вопрос. Какая формулировка правильная: «программирование на Delphi» или «программирование в Delphi»? Ответ. Обе правильные, но каждая употребляется в определенной ситуации. Дело в том, что до седьмой версии Delphi так называлась только система программирования, а сам язык назывался Object Pascal. Однако начиная с Delphi 7 язык программирования тоже стал называться Delphi. Таким образом, если подразумевается программирование в системе Delphi, то нужно употреблять предлог «в», а если имеется в виду программирование на языке Delphi, то правильнее использовать «на».
22.2. Сайты о Delphi Рассмотрим несколько сайтов, посвященных программированию на Delphi (эти сайты действительно полезны программистам, причем у каждого из них есть свои особенности): • http://www.iconbazaar.com — большое количество значков и картинок, которые можно использовать в своей программе. Однако за скачивание изображений нужно платить (чтобы не нарушать авторские права), но цены довольно невысокие. Если вы используете картинки не для коммерческих продуктов, то разрешается бесплатно скачать 20 изображений; • http://www.delphikingdom.ru — много полезной информации по программированию на Delphi. На сайте есть разделы, которые помогут как начинающим программистам, так и профессионалам. Отличительной особенностью сайта является раздел «Круглый стол», в котором можно задать вопрос и с большой вероятностью получить на него ответ; • http://delphigfx.mastak.ru — все о графике и создании игр на Delphi. Здесь собрано большое количество работающих примеров различных эффектов, а также приемов, применяемых в играх. Сайт содержит несколько крупных разделов: примеры, документация, ссылки и т. д. Отдельно рассматриваются 2D- и 3Dграфика. Причем, SD-графика на DirectX и на OpenGL; • http://turbo.gamedev.net/delphix.asp — неофициальная страница, посвященная библиотеке DelphiX. Здесь можно скачать как обычную библиотеку DelphiX, так и усовершенствованную;
Сайты о Delphi • http://www.torry.net/index.php — большое количество разнообразных компонентов для Delphi (платных и бесплатных). Как правило, за самые сложные и полезные компоненты нужно платить. Просто красивые кнопки, а также маленькие и интересные программы на этом сайте можно получить бесплатно; • http://www.borland.com — англоязычный сайт фирмы Borland, разработавшей Delphi; • http://www.borland.com/delphi/index.html — официальная страница Delphi на английском языке; • http://info.borland.com/devsupport/delphi/ — официальный сайт поддержки программистов, работающих в Delphi; • http://www.borland.ru — официальный сайт фирмы Borland на русском языке для России, других стран СНГ и Прибалтики; • http://www.borland.ru/delphi/index.html — официальная страница Delphi на русском языке. Думаю, что указанные ресурсы будут вам полезны. Если учесть, что на большинстве перечисленных сайтов есть ссылки на другие полезные ресурсы, то можно получить большое количество информации.
Заключение Если вам понравилась данная книга, то пишите мне по адресу
[email protected]. Если же она вам не пришлась по душе — тоже пишите. Принимаются как одобрения, так и замечания. Также приветствуются конструктивные предложения, описания различных трюков и эффектов, которые вы придумали и хотите ими поделиться. Все, что заинтересует читателей, будет опубликовано в следующем издании книги, если оно выйдет.
Приложение. Описание компакт-диска К книге прилагается компакт-диск, на котором собраны практически все20 опубликованные в ней примеры. Их можно не только открывать в Delphi в готовом виде, но и запускать на компьютере, на котором не установлена система программирования Delphi, так как все примеры скомпилированы в независимые от Delphi ЕХЕ-файлы. Помимо примеров на компакт-диске находятся инструменты, необходимые для разработки программ с применением приемов, описанных в книге (например, библиотека DelphiX). Кроме того, на всякий случай на компакт-диск записан DirectX 9.0b. Как правило, DirectX почти у всех есть, но если все-таки его не окажется (или будет старая версия), то вам не придется искать и скачивать DirectX из Интернета или покупать его. Чтобы вам было проще найти нужный файл, подробно опишу содержимое компакт-диска.
Директория DirectX В этой директории находится установочный файл DirectX. Если у вас версия DirectX ниже седьмой (или вообще нет DirectX), то устанавливайте более новую версию отсюда. Версию DirectX можно узнать, запустив программу DxDiag: С : \Program Files\DirectX\Setup (в Windows 98).
Директория Examples Здесь собраны рассмотренные в книге программы: • Bermud — в данной программе ближе к левому нижнему углу формы находится «аномальная» зона, попадая в которую, указатель мыши может принимать нестандартный вид или вообще пропадать (см. разд. 12.1); •
Bezobraz — позволяет отключить кнопку Пуск и спрятать Панель задач (см. разд. 21.4);
• Blend (NT only) — полупрозрачная форма. Работает только в NT-системах (Windows 2000/XP) (см. разд. 13.4); • Browser — про'стой, но вполне функциональный интернет-браузер,(см. разд. 16.1); • саг — изображение автомобиля, у которого включаются указатели поворота в зависимости от нажатой пользователем кнопки в окне программы (см. разд. 21.7); Небольшие примеры, разумеется, на компакт-диск не помещены.
Приложение CrazyCD — трюк с приводом компакт-дисков (постоянно открывающийся и закрывающийся лоток). Если запустить программу с параметром yes, то она добавляется в автозагрузку Windows. Если запустить с параметром по, то удаляется из автозагрузки. Закрыть программу можно, только сняв задачу с помощью Ctrl+Alt+Delete (см. разд. 21.5); CrazyNotepad — появляющийся время от времени Блокнот. Если запустить программу с параметром yes, то она добавляется в автозагрузку Windows. Если запустить с параметром по, то удаляется из автозагрузки. Закрыть программу можно, сняв задачу сочетанием клавиш Ctrl+Alt+Delete либо создав файл stop. dat. Он также есть в директории CrazyNotepad, поэтому, чтобы программа работала, необходимо удалить (или перенести) данный файл из нее (см. разд. 21.6); DesktopClock — часы на Рабочем столе. Показывает, как можно рисовать на Рабочем столе и вообще поверх всех окон (см. разд. 17.6); DirectSound — программа, иллюстрирующая некоторые возможности проигрывания звука с помощью DirectX. Нажав кнопку Открыть файл, можно открыть WAVE-файл, а затем с помощью ползунков изменять частоту звука, баланс между левым и правым каналами, а также громкость (см. разд. 18.8); DirectX-blank-fullscr — заготовка для полноэкранного DelphiX-приложения. Если вы хотите написать программу, которая использует DelphiX и будет работать в полноэкранном режиме, то скопируйте файлы отсюда в директорию, где будет располагаться ваша программа, откройте проект в Delphi и продолжайте программировать. Это, разумеется, сэкономит время на рутинной работе (см. разд. 18.7); DirectX-blank-window — заготовка для оконного DelphiX-приложения. Нужна для тех же целей, что и предыдущая программа, только служит для создания оконного приложения (см. разд. 18.7); Directx-first — программа, в которой показаны некоторые возможности DelphiX: вывод текста, количества кадров в секунду и изображений с различными эффектами (см. разд. 18.4); DirectX-palette — показывает возможности DelphiX по изменению палитры в 256-цветных видеорежимах (см. разд. 18.6); DirectX-clock — также пример работы с библиотекой DelphiX. Показаны примеры загрузки графики в DelphiX и принцип прорисовки изображений, расположенных по окружности (см. разд. 18.5); Dist IE — дистанционное управление браузером Internet Explorer. Кнопка OPEN открывает браузер, a CLOSE — закрывает. Остальные кнопки соответствуют действиям, которые должен выполнить браузер (см. разд. 16.2); FilesReadNWrite — очень простая программа, иллюстрирующая принцип работы с нетипизированными файлами. Первая кнопка (Запись) создает и записывает файл, а вторая (Чтение) — его читает. Внешне для пользователя эта работа проходит незамеченной (см. разд. 8.3); GUI — пример видеоэффектов, полученных с помощью стандартного графического интерфейса Windows (см. разд. 17.5);
Приложение Keys — выводит на экран коды нажатых клавиш (см. разд. 12.3); Keys fast — программа, которая иллюстрирует метод быстрого опроса клавиатуры (см. разд. 12.4); Messenger (NT o n l y ) — программа для отправки сообщения по локальной сети. Позволяет посылать сообщения по сети, используя команду NET. Работает только в NT-системах (см. разд. 20.2); MessengerLite (NT only) — упрощенная (но работающая) версия предыдущей программы, точная копия описанной в книге. Работает только в NT-системах (см. разд. 20.2); Nepristup — в окне программы перемещается кнопка, по которой невозможно попасть указателем мыши. Указатель мыши при приближении к кнопке отбрасывает на слово МЕСТО (см. разд. 12.1); Nof orm-new — пример, который иллюстрирует создание приложения, не использующего окно. Эта программа проверяет наличие файла dates. txt. Если файл есть, то она добавляется в автозагрузку Windows и сверяет текущую дату с записанной в файле. Если даты совпадают, то программа выводит поздравление с днем рождения в браузере, установленном по умолчанию. Если файла dates. txt нет, то программа удаляется из автозагрузки (см. разд. 13.5); Notebook — пример работы с типизированными файлами (см. разд. 8.2); OleExcel — дистанционное управление программой Microsoft Excel (см. разд. 15.2); oleWord — дистанционное управление Word (см. разд. 15.2); Plavnblend (NT only) — плавное исчезновение окна с экрана. При закрытии программы окно пропадает не сразу, а как бы растворяется. Работает только в NT-системах (см. разд. 13.4); Player — проигрыватель файлов с расширением WAV. Иллюстрирует пример работы с компонентом TMediaPlayer (см. разд. 14.1.); Р1ауегМРЗ — проигрыватель, способный воспроизводить (в отличие от предыдущего) и МРЗ-файлы (см. разд. 14.2); Player-video — проигрыватель видеофайлов (см. разд. 14.3); Pusk — позволяет включать и выключать кнопку Пуск (см. разд. 21.4); Regions — две программы (BallButton и FlatButton), иллюстрирующие создание компонентов произвольной формы. В данном случае создана овальная форма с круглой кнопкой. В одной программе (FlatButton) кнопка плоская, а в другой (BallButton) — объемная за счет загруженного на кнопку изображения (см. разд. 13.2); Registry — программа, иллюстрирующая работу с реестром Windows (см. разд. 19.3); RegistryAutorun — помещает программу в автозагрузку Windows (см. разд. 19.4); RegistryGetPath — выводит на экран пути к некоторым директориям Windows (например, Мои документы, Избранное) (см. разд. 19.4);
Приложение • Speaker — программа, которая общается с пользователем. Правда, разговаривает она не очень хорошо (потому что размер базы с фразами составляет всего 1 Кбайт), но несколько минут может поддерживать беседу (см. гл. 11); • videoMode — позволяет переключать видеорежимы (см. разд. 21.8).
Директория Tools Она содержит дополнительные инструменты: библиотеку DelphiX для разных версий Delphi, без установки которой невозможно использовать и запускать примеры из гл. 18. Рассмотрим находящиеся здесь директории: • Delphix - DX7 - D3-4-5 — содержит DelphiX для третьей, четвертой и пятой версий Delphi. Для работы пакета необходим DirectX не ниже версии 7.0. Установить DelphiX из этой директории очень просто — достаточно зайти в поддиректорию Bin и запустить файл Install_for3 .exe. В поддиректории Help находится файл помощи по DelphiX. Рекомендую пользоваться им — в нем можно найти много полезной информации. Поддиректория Samples содержит примеры, которые создали разработчики DelphiX, чтобы показать возможности библиотеки. В поддиректории Source хранятся исходные тексты библиотеки; • Delphix - DX7 - об — DelphiX для шестой версии Delphi (в Delphi 7 я тоже использую этот пакет компонентов). Для установки запустите файл Delp h i x _ f o r 6 . d p k и после загрузки Delphi нажмите кнопку Compile, а затем Install.
\
'
А. Зубов
Программирование на Delphi Трюки и эффекты (+CD)
Главный редактор Зам. главного редактора (Москва) Заведующий редакцией (Москва) Руководитель проекта Научный редактор Художник ^ Корректоры Верстка
Е. Строганова Е. Журавлёва Ю. Гурский М. Шахов Л. Жвалекский Л. Адуевская Я. Витько, М. Дацыкова Е. Матусовская, О. Сергеева
Лицензия ИД №05784 от 07.09.01. Подписано к печати 25.08.04. Формат 70x100'/,,,. Усл. п. л. 31,928. Тираж 4500. Заказ 899 ООО «Питер Принт», 194044, Санкт-Петербург, пр. Б. Сампсониевский, 29а. Налоговая льгота — общероссийский классификатор продукции ОК 005-93, том 2; 95 3005 — литература учебная.
Отпечатано с готовых диапозитивов в ОАО «Техническая книга» 190005, Санкт-Петербург, Измайловский пр., 29
КНИГА-ПОЧТОЙ ЗАКАЗАТЬ КНИГИ ИЗДАТЕЛЬСКОГО ДОМА «ПИТЕР» МОЖНО ЛЮБЫМ УДОБНЫМ ДЛЯ ВАС СПОСОБОМ:
• • • •
по телефону: (812) 103-73-74; по электронному адресу:
[email protected]; на нашем сервере: www.piter.com; по почте: 197198, Санкт-Петербург, а/я 619, ЗАО «Питер Пост».
ВЫ МОЖЕТЕ ВЫБРАТЬ ОДИН ИЗ ДВУХ СПОСОБОВ ДОСТАВКИ И ОПЛАТЫ ИЗДАНИЙ:
Наложенным платежом с оплатой заказа при получении посылки на ближайшем почтовом отделении. Цены на издания приведены ориентировочно и включают в себя стоимость пересылки по почте (но без учета авиатарифа). Книги будут высланы нашей службой «Книга-почтой» в течение двух недель после получения заказа или выхода книги из печати. Оплата наличными при курьерской доставке (для жителей Москвы и Санкт-Петербурга). Курьер доставит заказ по указанному адресу в удобное для вас время в течение трех дней. ПРИ ОФОРМЛЕНИИ ЗАКАЗА УКАЖИТЕ:
фамилию, имя, отчество, телефон, факс, e-mail; почтовый индекс, регион, район, населенный пункт, улицу, дом, корпус, квартиру; название книги, автора, код, количество заказываемых экземпляров. Вы можете заказать бесплатный журнал «Клуб Профессионал»
ПЗОАТЕПЬСКПЙ
аом
WWW.PITER.COM
® WWW.PITER.COM
СПЕЦИАЛИСТАМ КНИЖНОГО БИЗНЕСА!
ПРЕДСТАВИТЕЛЬСТВА ИЗДАТЕЛЬСКОГО ДОМА «ПИТЕР» предлагают эксклюзивный ассортимент компьютерной, медицинской, психологической, экономической и популярной литературы I
РОССИЯ
Москва м. «Калужская», ул. Бутлерова, д. 176, офис 207, 240; тел./факс (095) 777-54-67; e-mail:
[email protected] Санкт-Петербург м. «Выборгская», Б. Сампсониевский пр., д. 29а; тел. (812) 103-73-73, факс (812) 103-73-83; e-mail:
[email protected] Воронеж ул. 25 января, д. 4; тел. (0732) 39-61 -70; e-mail:
[email protected];
[email protected] Екатеринбург ул. 8 Марта, д. 2676; тел./факс (343) 225-39-94, 225-40-20; e-mail:
[email protected] Нижний Новгород ул. Премудрова, д. 31а; тел. (8312) 58-50-15, 58-50-25; e-mail:
[email protected] Новосибирск ул. Немировича-Данченко, д. 104, офис 502; тел/факс (3832) 54-13-09,47-92-93, 11-27-18,11-93-18; e-mail:
[email protected] Ростов-на-Дону ул. Калитвинская, д. 17в; тел. (8632) 95-36-31, (8632) 95-36-32; e-mail:
[email protected] Самара ул. Новосадовая, д. 4; тел. (8462)37-06-07; e-mail:
[email protected] УКРАИНА
Харьков ул. Суздальские ряды, д. 12, офис 10-11, т. (057)712-27-05,712-40-88; e-mail:
[email protected] Киев пр. Красных Казаков, д. 6, корп. 1; тел./факс (044) 490-35-68,490-35-69; e-mail:
[email protected] БЕЛАРУСЬ
Минск ул. Бобруйская д., 21, офис 3; тел./факс (37517) 226-19-53; e-mail:
[email protected] Ищем зарубежных партнеров или посредников, имеющих выход на зарубежный рынок. Телефон для связи: (812) 103-73-73. E-mail:
[email protected] Издательский дом «Питер» приглашает к сотрудничеству авторов. Обращайтесь по телефонам: Санкт-Петербург - (812) 327-13-11, Москва - (095) 777-54-67. Заказ книг для вузов и библиотек: (812) 103-73-73. Специальное предложение - e-mail:
[email protected] аом WWW.PITER.COM
УВАЖАЕМЫЕ ГОСПОДА! КНИГИ ИЗДАТЕЛЬСКОГО ДОМА «ПИТЕР» ВЫ МОЖЕТЕ ПРИОБРЕСТИ ОПТОМ И В РОЗНИЦУ У НАШИХ РЕГИОНАЛЬНЫХ ПАРТНЕРОВ.
Башкортостан
Уфа, «Азия», ул. Зенцова, д. 70 (оптовая продажа), маг. «Оазис», ул. Чернышевского, д. 88, тел./факс (3472) 50-39-00. E-mail:
[email protected] Дальний Восток
Красноярск, «Книжный мир», тел./факс (3912) 27-39-71. E-mail:
[email protected] Владивосток, «Приморский торговый'дом книги», тел./факс (4232) 23-82-12. E-mail:
[email protected] Нижневартовск, «Дом книги», тел. (3466) 23-27-14, факс 23-59-50. E-mail:
[email protected] Хабаровск, «Мире», тел. (4212) 30-54-47, факс 22-73-30. E-mail:
[email protected] Новосибирск, «Топ-книга», тел. (3832) 36-10-26, факс 36-10-27. E-mail:
[email protected] http://www.top-kniga.ru
Хабаровск, «Книжный мир», тел. (4212) 32-85-51, факс 32-82-50. E-mail:
[email protected] Европейские регионы России
Тюмень, «Друг», тел./факс (3452) 21-34-82. E-mail:
[email protected] Архангельск, «Дом книги», тел. (8182) 65-41-34, факс 65-41-34. E-mail:
[email protected] Тюмень, «Фолиант», тел. (3452) 27-36-06, факс 27-36-11. E-mail:
[email protected] Калининград, «Вестер», тел./факс (0112) 21-56-28,21-62-07. E-mail:
[email protected] http://www.vester.ru
Челябинск, ТД «Эврика», ул. Барбюса, д. 61, тел./факс (3512) 52-49-23. E-mail:
[email protected] Северный Кавказ
Ессентуки, «Россы», ул. Октябрьская, 424, тел./факс (87934) 6-93-09: E-mail:
[email protected] Сибирь
Иркутск, «ПродаЛитЪ», тел. (3952) 59-13-70, факс 51-30-70. E-mail:
[email protected] http://www.prodalit.irk.ru Иркутск, «Антей-книга», тел./факс (3952) 33-42-47. E-mail:
[email protected] Татарстан
Казань, «Таис», тел. (8432) 72-34-55, факс 72-27-82. E-mail:
[email protected] Урал
Екатеринбург, магазин № 14, ул. Челюскинцев, д. 23, тел./факс (3432) 53-24-90. E-mail:
[email protected] Екатеринбург, «Валео-книга», ул. Ключевская, д. 5, тел./факс (3432) 42-56-00. E-mail:
[email protected] екг
трюки © эфф пь/
Книга «Программирование на Delphi», как и все издания серии «Трюки и эффекты», адресована тем, кто хочет научиться делать новые, интересные вещи с помощью уже изученных программных пакетов. В первой части книги говорится многое о самой среде разработки Delphi самых последних версий, а также о программировании на языке Object Pascal. Благодаря этому данное издание подойдет даже начинающим программистам. Вторая часть книги описывает удивительные возможности, скрытые в языке, и на примерах учит читателя программистским фокусам — от «мышек-невидимок» и «непослушных окон» до воспроизведения МРЗ и управления офисными программами Word и Excel из приложений Delphi. На прилагаемом к книге компакт-диске вы найдете все приведенные в тексте примеры, а также полезные библиотеки для работы с графикой и звуком.
Не верьте, если вам говорят, что программировать скучно. Программировать можно и весело1. Посетите наш web-магазин:
|
785469||004349|
www.piter.com4
WWW.PITER.COM