Московский финансово-промышленный университет «Синергия» Кафедра Информационных систем Култыгин О.П. Handbook по дисциплине «Автоматизированная разработка программного обеспечения» Программа магистерской подготовки Москва 2013 Содержание Тема 1. История развития языков программирования ...................................................3 Тема 2. RAD-среда программирования Delphi ............................................................. 11 Тема 3. Процедурное программирование ..................................................................... 22 Тема 4. Управляющие конструкции языков программирования ................................. 33 Тема 5. ООП – Объектно-ориентированное программирование ................................. 45 Тема 6. Структура приложений и файлы проектов приложений ................................. 51 Литература ..................................................................................................................... 65 Основная литература: ................................................................................................. 65 Дополнительная литература: ..................................................................................... 65 2 Тема 1. История развития языков программирования В начале никаких языков программирования не существовало. Для первых ЭВМ программы писались на машинном языке. Это было весьма трудоемким и кропотливым делом. Затем разработчики ЭВМ поняли, что проще создать программу, которая бы сама переводила исходный код, написанный по определенным правилам, в машинный язык. Так появился первый компилятор Ассемблер. Компилятор – это программа, которая переводит исходный программный код в машинный язык, и создает полноценный исполняемый программный модуль. Такие модули имеют расширения *.com и *.exe. Модули с расширением *.com отличаются меньшим размером и еще встречаются в программных комплексах, создававшихся под операционную систему MS DOS. Исполнимые модули в современных программных комплексах, разрабатываемых под операционную систему MS Windows, имеют расширение *.exe. Также существуют интерпретаторы – программы, которые не создают исполняемых программных модулей. Интерпретаторы представляют собой оболочку, в которую нужно загрузить файл с исходным текстом программы, затем интерпретаторы построчно переводят код в машинный язык и тут же выполняют его. Наиболее известным интерпретатором является язык Basic. Неудобство использования интерпретаторов и программного обеспечения, написанного на них, не позволяют использовать их широко. Для распространения программ, созданных на интерпретаторе, необходимо на компьютер пользователя установить не только написанную программу, но и сам интерпретатор. А пользователю требуется научиться эксплуатировать этот интерпретатор: загружать в него программу, запускать команду выполнения, а также научиться пользоваться самой программой. Однако в некоторых случаях интерпретаторы бывают весьма удобны, например, интерпретаторы PHP и Perl, используемые в Web-программировании, выполняются на стороне сервера, и не доставляют пользователю проблем. Язык Ассемблер наиболее приближен к машинному языку, поэтому его называют языком низкого уровня. Писать программы на языке Ассемблер было проще, чем на «машинном» языке, в результате программы разрабатывались быстрей. Создание Ассемблера способствовало бурному развитию языков программирования. Появилось множество языков высокого уровня: C, C++, Pascal, PL, Fortran и многие другие. Правила разработки программного кода на языках высокого уровня более приближены к человеческим языкам, поэтому модули на таких языках создавать еще проще и быстрей. Языки программирования совершенствовались. Первые языки высокого уровня были процедурными. В них логика программы строилась на 3 использовании функций и процедур, которые можно вызвать из любого места программы. Затем появились объектно-ориентированные языки программирования (ООП). В них логика программы строилась на объектах, каждый из которых имел собственные свойства, методы и события, которые могли быть унаследованы потомками этого объекта. То есть, создание программных комплексов значительно упростилось. Вместо того, чтобы писать десяток страниц программного кода, достаточно просто объявить какой-нибудь объект. Последним этапом эволюционирования языков программирования в настоящий момент стала визуальная среда программирования. Можно просто выбрать объект – компонент, перетащить его на форму, и уже в процессе разработки программы видеть то, что должно получиться в результате. Примерно также при редактировании текста в программе MS Word сразу видно то, что должно получиться при печати этого текста на лист бумаги. Среда разработки программ взяла на себя почти всю «черновую» работу по созданию программного кода. Программирование перестало быть нудным и трудоемким, и превратилось в творческий процесс. Конечно, за удобство создания программ приходится расплачиваться увеличением размера исполняемого программного модуля, которое особенно заметно при создании небольших программ, и практически незаметно при создании сложных программных комплексов. Современные персональные компьютеры имеют быстрые процессоры, большие объемы оперативной памяти и жесткие диски значительных размеров. Таким образом, на указанное неудобство можно не обращать внимание. Разумеется, бывают исключения. Если при создании программного кода критическими являются размер исполняемого файла и скорость его выполнения, тогда лучше использовать языки Assembler или C. Такая необходимость возникает при написании операционных систем и драйверов. На современном рынке, однако, более востребовано прикладное программное обеспечение: утилиты, программы общего назначения, программы для работы с базами данных. Здесь Delphi является бесспорным лидером среди визуальных сред разработки программ. История Delphi начинается с 60-х годов XX века, когда профессор Н. Вирт разработал язык высокого уровня Pascal. Это был лучший язык для изучения программирования, и для создания программ для операционной системы MS DOS. Затем, в 1983 году, А. Хейлсберг совместно с другими программистами, которые только что организовали компанию Borland, разработал компилятор Turbo Pascal, который стал следующим шагом в эволюции Delphi. Затем появился Object Pascal, который уже использовал объектно-ориентированный подход к программированию. Когда появилась первая версия Windows – MS Windows 3.10, программисты Borland создали Delphi 1. Это была уже 4 объектно-ориентированная среда для визуальной разработки программ, основанная на языке Object Pascal. С появлением MS Windows 95 появилась Delphi 2, затем Delphi 3, 4, 5. Язык программирования Object Pascal, который являлся стержнем Delphi, претерпел такие существенные изменения, что с появлением Delphi 6 компания Borland, которая уже превратилась в корпорацию, официально объявила о переименовании языка Object Pascal в язык Delphi. Итак, Delphi – это и визуальная среда разработки программ, и один из лучших языков программирования. В рамках курса мы будем изучать язык Delphi 7, наиболее стабильную версию языка программирования для 32-разрядных версий операционной системы MS Windows. В настоящее время есть более новые версии Delphi ориентированные на технологию .NET. Однако, рынок программного обеспечения, созданного с помощью Delphi, образуют в основном программные продукты, написанные на языке Delphi 7, и даже на более ранних версиях. Основу Delphi составляет не только сам язык, но и RAD (Rapid Application Development) – среда быстрой разработки программ. Благодаря визуальному программированию, а также достаточно большой библиотеке визуальных компонентов, Delphi позволяет создавать программы наиболее быстро и эффективно, принимая на себя основную работу, и оставляя программисту творческий процесс. Разумеется, возможность быстрого создания профессиональных приложений для Windows делает Delphi – программистов востребованными во всех отраслях человеческой деятельности. Первая программа. Обычно при изучении среды разработки приложений принято разрабатывать первую программу, которая выводит текст «Привет, мир!». Создадим программу, которая выводит этот текст тремя различными способами. Но вначале познакомимся с самой средой программирования Delphi. При загрузке Delphi 7 на экране видны следующие окна: 5 Рис. 1. Рабочая среда Delphi 7 У RAD среды Delphi множество окон, панелей и компонентов. Большинство из них будет изучаться постепенно, а пока нужно обратить внимание на 5 окон: 1. Главное окно Delphi. Здесь находится основное меню, различные панели инструментов и палитра компонентов, состоящая из множества вкладок. 2. Конструктор формы. Здесь визуально видно, как будет выглядеть экранная форма программы, будет создаваться интерфейс с помощью переноса на форму различных компонентов и размещения их таким образом, чтобы интерфейс выглядел привлекательным. Часто при разработке программ придется переключаться между конструктором форм и редактором кода, делается это клавишей F12. 3. Редактор кода. Здесь виден исходный код программы, который создан самой Delphi. Тут же будет вводиться собственный код программиста. 4. Инспектор объектов. Он предназначен для управления объектами проекта и состоит из двух вкладок – Properties (Свойства) и Events (События). 5. Дерево объектов. Здесь виден текущий на данный момент объект. Это окно будет особенно полезно, когда на форме появится множество компонентов. 6 Когда открывается Delphi, она автоматически создает и отображает новый проект (приложение). На рисунке представлен проект, который содержит только одну стандартную форму. Форма – это тоже объект, который представляет собой окно программы. Чтобы программа делала что-то полезное, нужно внести в нее изменения. Выведем текст «Привет, мир!» первым способом. Для этого в инспекторе объектов надо найти свойство Caption. В данный момент текущим является объект – форма, и свойство Caption формы отвечает за надпись на системной строке программы. Это - синяя полоса вверху любого программного окна. По умолчанию, свойство Caption содержит надпись «Form1», также называется и сама форма. Изменим эту надпись на «Привет, мир!». Уже в процессе ввода текста видно, что надпись в системной строке принимает новый вид. Итак, требуемый текст введен первым способом. Теперь нужно сохранить проект. Проект – это совокупность файлов разных форматов, из которых создается программа. Об этом подробней будет рассказано в одной из следующих лекций, а пока требуется запомнить правило – каждую программу (проект), которую вы создаете, нужно сохранять в отдельной папке. Чтобы сохранить проект, выполним следующие действия: 1. Выбираем команду меню «File – Save All» (сохранить все), либо нажимаем горячие клавиши <Shift+Ctrl+S>, либо нажимаем одноименную кнопку на панели инструментов. Рис. 2. Кнопки Save (Сохранить) и Save All (Сохранить всѐ) на панели инструментов 2. Затем выходит окно с предложением сохранить модуль – текстовый файл с исходным кодом, принадлежащий форме. Файл имеет расширение *.pas. Имеется в виду, что каждый проект сохраняется в отдельную папку, поэтому вначале щелкнем правой кнопкой мыши по свободному месту окна с папками, и выберем команду «Создать Папку». Дадим папке какое-либо имя, например, «01». После создания папки открываем ее. 3. Далее в поле «Имя файла» укажем имя сохраняемого модуля. Имя может быть любым, но обязательно латинскими символами. Еще имя модуля не должно совпадать с именем формы. Обычно, имена форм и модулей стараются делать информативными, так чтобы по имени можно было определить, что это за файл. Поскольку это главная форма проекта, дадим ей имя «Main», и нажмем кнопку «Сохранить». 7 4. Затем система предлагает дать имя проекту в целом. Имя проекта будет совпадать с именем исполняемого программного файла. Если надо, к примеру, получить файл «hello.exe», тогда проекту дается имя «hello». Далее нажимается кнопка «Сохранить». Чтобы скомпилировать программу, то есть, преобразовать исходный код в выполняемый exe-файл, для этого выбирается команда меню «Run», либо нажимается горячая клавиша F9, либо кнопка «Run» на панели инструментов. На кнопке изображена зеленая стрелка, указывающая вправо. В результате, программа будет не только скомпилирована, но и запущена. Если посмотреть на системную строку Delphi, то можно увидеть надпись «Delphi 7 – hello [Running]», а окна инспектора объектов и дерева объектов при этом исчезают. Это говорит о том, что программа находится в режиме выполнения. Выполняемая программа имеет точно такой же вид, как проектируемая главная форма, только на форме отсутствует точечная сетка, предназначенная для облегчения дизайна. Окно полученной программы содержит все стандартные кнопки Windows – программы. Щелкнув по крестику в правой верхней части окна, закроем программу, и вернемся к проектируемой форме. Обратите внимание, что свойства в Инспекторе объектов принадлежат выделенному в данный момент компоненту. Выделяются компоненты простым щелчком мыши. Двойной щелчок мыши создает обработчик события – процедуру. Если ошибочно создать таким образом процедуру, то можно просто сохранить проект, ничего не вписывая в процедуру. При сохранении пустые процедуры будут автоматически удалены. Удалять их вручную не рекомендуется. Попробуем второй способ. Обратите внимание на Палитру компонентов. Текущей является вкладка Standard, и на ней находится множество значков – компонентов. Когда вы подводите указатель мыши к какому-либо компоненту, через некоторое время появляется подсказка с именем компонента. Нам нужен компонент Label, который представлен на вкладке в виде кнопки с изображением жирной буквы «А». Щелкните по этой кнопке, затем щелкните по свободному месту на форме, чтобы вставить компонент. Компонент Label появляется на форме. Этот компонент представляет собой обычную надпись. Сейчас он выделен, и содержит надпись по умолчанию, - «Label1». Теперь Инспектор объектов показывает свойства этого компонента, а не формы. Label также имеет свойство Caption, которое вы можете изменить в Инспекторе объектов. Найдите это свойство, и вместо «Label1» впишите «Привет, мир!». Текст в компоненте Label изменился. Если вам не нравится место, в котором оказался компонент, вы можете перетащить его мышью на другое место. Кроме того, точное местоположение компонента вы можете задать, если выделите его, и будете нажимать клавиши перемещения курсора, удерживая при этом клавишу <Ctrl>. 8 Теперь попробуйте еще одно свойство компонента Label – свойство Font (шрифт). Найдите это свойство в инспекторе объектов, и выделите его. Справа появится кнопка с тремя точками, нажмите ее. Откроется стандартное окно выбора шрифта. Здесь вы можете выбрать наименование шрифта, его размер, начертание (например, жирный курсив) и цвет текста. Поэкспериментируйте с размером компонента, его положением и шрифтом. Почти все компоненты, с которыми нам придется иметь дело, имеют эти свойства, так что в дальнейшем вам будет легче осваивать новый компонент. Снова сохраните проект и нажмите кнопку Run или <F9>. Убедитесь, что надпись появилась на форме, после чего закройте программу и вернитесь к форме. Попробуем третий, немного более сложный способ. Пока что мы создавали программу, не написав ни единой строки кода. Мы занимались только дизайном, все остальные трудности Delphi брала на себя. Теперь попробуем вывести это же сообщение, как только пользователь нажмет на форме кнопку. Для начала нужно установить на форму кнопку. Этот компонент также находится на вкладке Standard палитры компонентов, и выглядит как кнопка с надписью «ОК». При наведении на него указателя мыши выскакивает подсказка «Button». Щелкнув по компоненту, щелкните затем по тому месту на форме, где вы хотели бы видеть эту кнопку. Изменим надпись на кнопке. Убедитесь, что кнопка выделена, и найдите в инспекторе объектов ее свойство Caption. Замените надпись «Button1» на надпись «Жми сюда!». Если надпись не умещается на кнопке, вы можете растянуть кнопку мышью, или использовать для этого клавиши управления курсором с нажатой кнопкой <Shift>. Далее нужно создать обработчик нажатия на кнопку. Обработчик представляет собой процедуру, в которой надо будет вписать программный код. Этот код будет выполняться программой всякий раз, когда пользователь нажимает на эту кнопку. Чтобы создать этот обработчик, следует дважды нажать на кнопку на форме. Вы сразу попадаете в редактор кода и видите, что процедура уже создана, курсор мигает в том месте, где мы должны ввести свой код. Просто впишем строку: ShowMessage ('Привет, мир!'); Полный текст процедуры будет выглядеть следующим образом: procedure TForm1.Button1Click (Sender: TObject); begin 9 ShowMessage ('Привет, мир!'); end; Сохраните проект, откомпилируйте его и запустите на выполнение. При нажатии на кнопку будет появляться указанная надпись. Мы создали полноценную программу, выводящую надпись «Привет, мир!» тремя различными способами, вписав при этом лишь одну строку исходного кода. Полученный файл hello.exe находится в указанной папке, например «C:\Program Files\Borland\Delphi7\Projects\01». При сохранении проекта вы можете указывать и другие папки, и проект будет сохранен по указанному адресу. Полученный программный файл hello.exe можно переносить с одного компьютера на другой, при этом наличие среды разработки Delphi для его выполнения не требуется. 10 Тема 2. RAD-среда программирования Delphi В любом языке программирования приходится использовать переменные. При загрузке программы, компьютер вначале считывает все необходимые данные в оперативную память, после чего уже имеет возможность работать с ними. Переменная – это ячейка оперативной памяти, которая может хранить данные какого-то одного типа. Переменная похожа на ячейку в MS Excel, там тоже в ячейке можно указать нужный формат данных. Если переменная имеет тип данных «строка», то в нее нельзя записать число. А в переменную с типом «целое число» нельзя записать вещественное число. В языках программирования вещественным называют число, у которого есть запятая, после которой имеются цифры, хотя бы нули: 125,00 348,24 Каждая переменная имеет уникальное имя. Для компьютера имя переменной – это адрес ячейки памяти, где хранятся данные. Присваиванием имен (идентификаторов) переменным занимается программист. Имена переменным в Delphi даются по определенным правилам: 1. Имя переменной может содержать любое количество английских букв, цифр и знака подчеркивания, другие символы недопустимы. 2. Первым символом обязательно должна быть буква. 3. В Delphi нет разницы, какие буквы вы даете переменным – большие или маленькие. То есть, myperem, MyPerem, MYPEREM – это одна и та же переменная. Совет: Переменную можно назвать одной буквой. Однако уже через месяц или раньше вам очень трудно будет вспомнить, зачем вы делали переменную F, и какие данные в ней хранятся. Поэтому старайтесь давать переменным осмысленные имена и сочетайте большие буквы с маленькими для разделения на слова. Хорошие примеры – MinZarplata или Glav_Param. Типы переменных. Каждая переменная имеет свой тип. Тип переменной обязательно нужно указывать, потому что разные типы переменных занимают разный размер, и компьютеру нужно знать, сколько байт в оперативной памяти требуется отвести под указанную переменную. Создание переменной состоит из двух этапов: 11 1. Объявление переменной (указываем имя и тип переменной). Переменная объявляется в специальном разделе var (позже мы познакомимся с этим разделом). 2. Присвоение переменной какого-то значения. Объявление переменной выглядит так: var Peremennaya1 : Real; Peremennaya2, Peremennaya3 : Integer; Как видно из примера, в начале указывается имя переменной, затем, после двоеточия указывается тип переменной. Если нужно объявить несколько переменных одного типа, их имена разделяются запятыми. В приведенном примере мы объявили одну вещественную переменную типа Real и две целые переменные типа Integer. Присваивать значение переменным можно неоднократно. Переменная потому и называется так, что ее значение в процессе работы программы может меняться. Оператор присвоения значения выглядит так: := Примеры присвоения значений переменным: A := 10; B := 20.35; C := 'Это строка'; D := True; A := 3+5-1; Механизм присвоения значения работает следующим образом: вначале рассчитывается значение в правой части команды, то есть, после знака «:=». Затем результат этого значения записывается в переменную. В последней строке примера мы использовали выражение «3+5-1». Вначале получается результат, в нашем случае он равен 7. Затем этот результат записывается в переменную. В дальнейшем, имя переменной можно использовать в различных выражениях, например: 12 A1 := 3; A2 := A1 + 7; A1 := A1 + 1; В первой строке мы записали в переменную число 3. Вторая строка содержит выражение, результатом которого будет число 10. А вот третья строка интересней. Как вы полагаете, что будет записано в переменную A1? Если ваш ответ 4, вы совершенно правы: вначале рассчитывается результат правой части команды, где в переменной A1 еще старое значение, затем он записывается в эту же переменную, изменяя ее значение. В таблице 1 перечислены основные типы переменных: Таблица 1. Название типа Описание Integer Целое число Real Вещественное число String Строка Boolean Логический тип Пояснения Переменная может содержать только целые числа, как со знаком, так и без знака. Переменная может принимать в качестве значения целые и дробные числа, со знаком и без знака. Переменная может хранить любые символы и наборы символов. В переменную String можно записать до 2 Гб символов. Булева переменная, может быть либо False (Ложь), либо True (Истина). На самом деле, типов переменных значительно больше, и по мере усложнения программ мы будем изучать эти типы. В таблице представлены только основные типы. Строки. С этим типом переменных приходится работать довольно часто. В примере выше мы указывали строку 'Это строка', а в прошлой лекции имели дело со строкой 'Hello, world!'. Вы уже заметили, что все строки должны быть заключены в одинарные кавычки. Размер строки практически не ограничен. Вы можете записать в строку любой текстовый файл, все зависит от того, сколько места есть у Вас на диске, и какая ОЗУ у Вас установлена. Изучать новое удобней сразу на практике, поэтому запускайте Delphi. Автоматически должен создаться новый проект. Напишем простенькую программу, в которой поработаем со строкой. Все компоненты, которые нам для этого понадобятся, находятся на вкладке Standard Палитры компонентов. 13 Поместите на форму, один под другим, следующие компоненты: Label, Edit и Button. С Label и Button вы уже знакомы по прошлой программе – Label это простая надпись, а Button – кнопка на форме. Компонент Edit представляет собой поле для ввода пользователем какого то значения, какой то строки. Растяните компонент Edit, сделав его примерно вдвое длинней. Выделите компонент Label (одним нажатием мыши!), и в его свойстве Caption вместо «Label1» впишите «Как твое имя?». Теперь выделите компонент Edit, и удалите «Edit1» из свойства Text, оставив там пустое поле. Свойство Text этого компонента содержит тот текст, который в данный момент находится в поле ввода. Вообще, со свойствами компонента можно обращаться, как с переменными. В большинство свойств можно заносить значения не только в Инспекторе объектов, в момент разработки формы, но и во время выполнения программы. Выделите кнопку, и в ее свойстве Caption напишите «Нажми меня!». Для красоты, переместите ее в центр формы. Измените размер формы, чтобы на ней не было много пустого места. А также в свойстве Caption формы напишите «Приветствие». Напомним, чтобы выделить форму, нужно один раз щелкнуть мышью по любому свободному месту формы. Если вы все сделали правильно, у вас должна получился такая форма: Рис. 3. Внешний вид формы Теперь создадим обработчик события для кнопки. Для этого дважды щелкните по кнопке, которую вы разместили на форме. Сразу же вы попадаете в Редактор кода, и курсор мигает между строками begin и end. Begin – это начало процедуры, после него точка с запятой не ставится. End – конец процедуры, после этого оператора точка с запятой обязательна. Если вы присмотритесь, то в последней строке редактора кода увидите end с точкой – это конец программы. Переменные указываются (описываются) перед началом процедуры. Следовательно, строку begin нужно будет опустить, а перед ней вписать раздел переменных var, и указать переменную s. Процедура должна выглядеть так: 14 procedure TForm1.Button1Click (Sender: TObject); var s: String; begin s:= 'Привет, ' + Edit1.Text + '!'; ShowMessage (s); end; Обратите внимание, что когда вы поставите точку после названия компонента Edit1, выйдет список доступных свойств, методов и событий. Когда мы впишем первую букву «T», список сортируется – в нем останутся только команды на букву «T», причем Delphi оценивает контекст, и оставляет в списке только те команды, которые в этом контексте могут быть использованы. В нашем случае, это строка. Свойство Text, которое останется в списке, может быть использовано в качестве строки (Рис. 4). Теперь нет нужды вводить «Text» самим – это свойство выделено в списке, и нам достаточно нажать <Enter>, чтобы вставить его в код. На будущее запомните: всегда, когда вы ставите точку после названия компонента, дождитесь такого списка. Дело не только в том, что так легче вводить код. Если список так и не появился, значит вы допустили ошибку. Возможно, в названии компонента, или даже в предыдущей строке. Теперь сохраните проект в отдельную папку, модуль, как обычно, назовите Main, а проект можете назвать, например, Privet. После сохранения скомпилируйте его и посмотрите, какое сообщение будет выходить при различных строках, которые введет пользователь в поле ввода Edit1. Справедливости ради стоит заметить, что в данном примере вовсе необязательно было использовать переменную – мы это сделали лишь в учебных целях. Такого же эффекта можно было добиться, если использовать выражение: 'Привет, ' + Edit1.Text + '!' 15 прямо в команде ShowMessage: procedure TForm1.Button1Click (Sender: TObject); begin ShowMessage ('Привет, ' + Edit1.Text + '!'); end; Рис. 4. Выбор свойства из списка – подсказки Стиль. Если вы надумаете пойти устраиваться на работу программистом, первым делом вас попросят показать ваши исходные коды. И дело тут вовсе не в том, что кто-то хочет украсть вашу программу, чтобы потом выдать ее за свою. Понимающие люди в первую очередь смотрят на стиль оформления кода. Давайте рассмотрим предыдущий пример: procedure TForm1.Button1Click (Sender: TObject); var s: String; begin s:= 'Привет, ' + Edit1.Text + '!'; ShowMessage(s); end; 16 Такой код вполне работоспособен, однако выглядит он не очень «читабельно», в нем легко запутаться. Посмотрите такой пример: procedure TForm1.Button1Click (Sender: TObject); var s: String; begin s:= 'Привет, ' + Edit1.Text + '!'; ShowMessage (s); end; Уже лучше, не правда ли? Каждая команда располагается на своей собственной строке, читаемость кода стала намного легче. Однако в таком коде сразу сложно разобраться, где заканчивается один раздел и начинается другой. Классический стиль требует, чтобы вложенные команды выделялись несколькими пробелами, сдвигались вправо: procedure TForm1.Button1Click (Sender: TObject); var s: String; begin s:= 'Привет, ' + Edit1.Text + '!'; ShowMessage (s); end; Такой код читать еще легче. Старайтесь сразу привыкать к такому стилю, чтобы не только другим было легко читать ваш код, но и вы сами не запутались в нем. Позднее мы еще вернемся к стилям программирования. 17 Комментарии. Комментарий – это текст, который программист в качестве подсказки вписывает для себя. Компьютер такой текст игнорирует, он нужен только для вас. При компиляции все комментарии просто вырезаются. В хорошем коде комментариев должно быть не менее трети от всего размера кода. Когда Вы будете делать большие проекты, без комментариев не обойтись. Уже через месяц, открыв исходный код большой программы, будет очень сложно вспомнить, что тут к чему, и как работает, поэтому не скупитесь на комментарии! Комментарии бывают многострочные и однострочные. Многострочный комментарий заключается в фигурные скобки, и в этих строках не должно быть действующих операторов. Пример: {Это многострочный комментарий!} Как видите, кавычки после закрывающей скобки ставить не нужно. Однострочный комментарий ставится после символов «//», то есть двух слэшей. Этот комментарий может находиться как на отдельной строке, так и после (но не до!) оператора. Примеры: //комментарий на отдельной строке a := "Действующий оператор"; //комментарий после оператора Не скупитесь на комментарии, обязательно указывайте, какую переменную и для чего вы создавали, что делает тот или иной блок кода. Говорят, в корпорации Microsoft увольняют программистов, если комментарии в их программах составляют менее трети общего кода! Собственный Блокнот. Каждый пользователь, работающий с Windows, хоть раз да использовал простой текстовый редактор Блокнот. Сейчас мы создадим немного упрощенную версию Блокнота, которая позволяет вводить текст, сохранять его на диск, и загружать с диска. Для этого нам требуется создать новый проект. Если в данный момент у вас уже открыт какой либо проект, выберите команду меню «File – Close All (Файл – Закрыть всѐ)», затем «File – New – Application (Файл – Новое – Приложение)». У нас есть новая форма, и пока больше ничего нет. Сразу изменим некоторые свойства формы. В свойстве Caption впишите «Мой блокнот» (естественно, без кавычек). Свойство Name изменим на «fMain». 18 Совет: чтобы легче ориентироваться в названиях модулей (файлов с кодом) и форм, лучше сразу выработать правила: 1. Перед названием формы будем указывать маленькую букву f, чтобы показать, что это именно форма. 2. Имена форм будем подбирать информативные, чтобы было понятно, с какой формой мы имеем дело. 3. Модули форм будем называть также, но без буквы f. 4. Главную форму всегда будем называть fMain, а модуль, соответственно, Main. Эти рекомендации не являются обязательными, но в дальнейшем, когда пойдут проекты с множеством форм, они помогут вам ориентироваться в названиях. Можете выработать собственные правила. Сохраним проект в отдельную папку. Модуль называем Main, а проекту даем имя MyNotebook. Теперь познакомимся с новым, мощным компонентом Memo, который находится на вкладке Standard и предназначен для ввода пользователем многострочного текста. Установите этот компонент на форму, в верхнюю левую часть формы, и растяните его по форме, оставив внизу немного места. Вниз установите, одну рядом с другой, три кнопки Button. У вас должно получится что-то похожее на рис. 5. Теперь выделим первую кнопку, и в свойстве Caption этой кнопки напишем «Сохранить». На второй кнопке напишем «Загрузить», на третьей – «Очистить». Выделим компонент Memo, который представляет собой большое белое поле. У компонента есть одно интересное свойство Lines, которое содержит строки текста, набранного в компоненте. 19 Рис. 5. Заготовка редактора текстов Инспекторе объектов, нажмем на кнопку с тремя точками справа от свойства и тем самым откроем редактор текста. Здесь можно набрать текст, который будет выведен в компонент «по умолчанию», при каждой загрузке программы. Нам как раз нужно, чтобы текста никакого не было, поэтому удалите все строки, какие там есть и нажмите «ОК». Компонент Memo очистился. Это еще не все. Нам нужно, чтобы при вводе текст автоматически переносился на другую строку, а пользователь имел возможность его пролистывать. Найдите свойство ScrollBars (компонент Memo должен быть выделенным), это свойство отвечает за наличие полос прокрутки. Выберите значение ssVertical, чтобы появилась вертикальная полоса прокрутки. С интерфейсом покончено, осталось вписать код, который будет выполняться программой. Дважды нажмем на первую кнопку, создадим обработчик события для кнопки «Сохранить». В месте, где мигает курсор, впишем только одну строку: Memo1.Lines.SaveToFile ('MyFile.txt'); 20 Метод SaveToFile() свойства Lines компонента Memo сохраняет весь текст в указанный файл. Если вы не указываете путь к файлу, по умолчанию файл будет создан там, откуда была запущена программа. Для второй кнопки напишем две строки: if FileExists ('MyFile.txt') then Memo1.Lines.LoadFromFile ('MyFile.txt'); Разберем эти строки. Что, если пользователь нажмет эту кнопку до того, как что-нибудь сохранит в файл? Файла то еще нет! Первая строка как раз выполняет проверку на существование файла. Если файла нет, то вторая строка выполняться не будет. Если он есть, тогда вторая строка считает текст из этого файла в компонент Memo. С условными операторами мы будем знакомиться позже, тогда смысл первой строки будет понятней. Для третьей кнопки код еще проще: Memo1.Clear; Эта команда очищает компонент Memo от текста. Вот, собственно, и вся программа. Сохраните ее и скомпилируйте, проверьте, как она работает. Оцените легкость программирования – для создания полноценного редактора текстов мы написали всего 4 строки кода! Поэкспериментируйте со свойством Font (Шрифт) компонента Memo, посмотрите, каким образом будет изменяться шрифт текста. 21 Тема 3. Процедурное программирование Целые числа. Программистам сплошь и рядом приходится использовать данные целого типа. Целое число – это число, не имеющее запятой. Число может быть беззнаковым (положительным), и со знаком минус (отрицательным). Примеры: 1 -12 1234567 В прошлой лекции мы упомянули лишь тип Integer, как основной тип целых чисел. Действительно, этот тип приходится использовать чаще всего, однако существуют и другие типы целых чисел. В таблице 2 представлены целые типы: Таблица 2. Целые типы данных Тип Integer Cardinal Shortint Smallint Longint Int64 Byte Word Longword Диапазон возможных значений -2147483648 .. 2147483647 0 .. 4294967295 -128 .. 127 -32768 .. 32767 -2147483648 .. 2147483647 -263 .. 263 - 1 0 .. 255 0 .. 65535 0 .. 4294967295 Размер памяти под переменную 4 байта 4 байта 1 байт 2 байта 4 байта 8 байт 1 байт 2 байта 4 байта Примечание Знаковое Без знака Знаковое Знаковое Знаковое Знаковое Без знака Без знака Без знака Давайте разберемся, как работает любая программа. Когда мы загружаем программу, она считывается в оперативную память компьютера. Туда же считываются и данные, с которыми программа работает. Только после этого программа начинает выполняться. Если современные жесткие диски имеют достаточно большой размер, этого нельзя сказать об оперативной памяти. Поэтому ее следует экономить везде, где только можно. Чаще всего вам придется работать с переменными типа Integer, это самый распространенный тип целых чисел, он годится почти для всех расчетов. Однако бывают моменты, когда не нужно такого большого диапазона значений. Например, вы будете использовать переменную для 22 счетчика какого-то цикла, и знаете, что он будет длиться, к примеру, от 1 до 100. В таком случае, указав в качестве счетчика тип Integer, мы зря украдем у оперативной памяти 3 байта! 3 байта – это немного, но ведь в большой программе переменных будет очень много, и если все они будут тратить память попусту, то такая программа будет, мягко говоря, непрофессиональной. Приучайтесь сразу тратить столько байт оперативной памяти, сколько нужно. Не зря ведь придумали столько типов! Если Вы знаете, что в переменной будут числа от нуля и выше, то нет никакой необходимости брать знаковый тип, ведь отрицательным Ваше число все равно не будет! Вещественные числа. Как мы уже говорили раньше, вещественные числа – это числа с запятой, после которой идут десятичные значения. Еще говорят, что они имеют плавающую точку (запомните это определение, оно будет часто встречаться). Некоторые начинающие программисты считают, что лучше такой тип переменных использовать всегда, даже при обработке целых чисел. Это большое заблуждение! Операции над числами с плавающей точкой отнимают у процессора гораздо больше времени, и требуют больше памяти. Компьютер воспринимает вещественное число, как два целых, и делает двойную работу при обработке чисел до запятой, и после нее. Однако иной раз бывает необходимо использовать именно такой тип данных. К примеру, если нужно поделить одно целое на другое. Хорошо, если это будет «4/2», результат тоже будет целым – 2. А если «4/3»? Тогда результатом будет 1,3333… и уж тут без вещественного числа не обойтись! А ведь мы заранее не знаем, какие числа будет делить пользователь, поэтому лучше сразу иметь в виду, что результат может быть не целым числом. Как и целые, вещественные числа имеют несколько типов. В таблице 3 они представлены: Таблица 3. Вещественные типы данных Тип Диапазон возможных значений Real48 Real Single Double Extended Comp 2.9 * 10-39 .. 1.7 * 1038 5.0 * 10-324 .. 1.7 * 10308 1.5 * 10-45 .. 3.4 * 1038 5.0 * 10-324 .. 1.7 * 10308 3.6 * 10-4951 .. 1.1 * 104932 -263+1 .. 263-1 -922337203685477.5808 ... 922337203685477.5807 Currency 23 Значащих цифр максимально 11-12 15-16 7-8 15-16 19-20 19-20 Размер в байтах 6 8 4 8 10 8 19-20 8 Третий столбец таблицы указывает количество максимально значащих цифр. Цифры, которые выходят за этот предел, будут игнорироваться. Тут важно помнить, что вещественные числа не равны целым. То есть, число 3,0 не будет равно 3! Чтобы сравнить оба эти числа, придется округлить вещественное число. Изучим целые и вещественные типы на практике. Для этого создадим простую программу, которая делит одно целое число на другое. Результат будет выводиться, как вещественное число. Откройте Delphi, создайте новый проект. На форму нужно поместить три компонента Label, три компонента Edit и одну кнопку, чтобы получилась такая картина: Рис. 6. Внешний вид формы Совет: чтобы выполнить одинаковую операцию над несколькими компонентами сразу, их можно выделить один за другим, удерживая клавишу <Shift>. Например, если таким образом выделить все три компонента Edit, то затем можно разом очистить их свойство Text. Сохраните проект под именем MyCalc. Затем дважды щелкните по кнопке, чтобы создать обработчик нажатия на кнопку. Перед begin процедуры следует создать раздел var, и объявить там три переменных: var Perem1, Perem2 : Integer; Perem3 : Double; 24 Затем вернемся в тело процедуры (между командами begin и end), и присвоим целым переменным введенные пользователем значения. Здесь нужно понять одну важную вещь. Пользователь будет вводить значения в компоненты Edit, и там они будут храниться в свойстве Text в виде строкового типа данных. Строку нельзя будет присвоить переменной какого-либо другого типа данных, присвоение Perem1 := Edit1.Text; //ошибочное присвоение //– несовместимость типов данных будет ошибочным. Разница довольно существенная: даже если пользователь вводит, казалось бы, целое число, например, 123 то компьютер видит строку символов, а вовсе не число: '123' Выход – преобразовать один тип данных в другой, в нашем случае, строку в целый тип. Преобразованием типов приходится заниматься сплошь и рядом, по мере изучения материала мы будем знакомиться с различными способами преобразования. Преобразовать строку в целый тип можно с помощью функции: StrToInt ('String'); В качестве параметра (в скобках) указывается строка. Функция преобразует ее в целое число и вернет его как результат. Примеры использования функции (эти примеры не нужно вводить в редактор кода): var s: String; i: Integer; begin s := '1234'; i := StrToInt (s); //параметр – строковая переменная 25 i := StrToInt (‘123456’); //параметр – строка i := StrToInt (Edit1.Text); //параметр – свойство Text //компонента Edit, имеющее строковый тип end; Как видно из примера, имеется масса возможностей передать в функцию строку. В первом случае преобразования мы передаем строковую переменную s, в которой хранится строка '1234'. Функция преобразует эту строку в целое число, и в результате в переменную i попадет уже число 1234. Во втором случае мы передаем непосредственно строку '123456', а в переменную i попадает преобразованное из этой строки число. В третьем случае мы в качестве параметра передаем тот текст, который пользователь ввел в поле ввода Edit1. Здесь следует сделать оговорку. Функция сработает правильно, если пользователь ввел туда действительно целое число. В противном случае возникнет ошибка. Пользователь – личность непредсказуемая, поэтому программист в таких случаях перед преобразованием типов всегда делает проверку – а действительно ли в поле ввода имеются только цифры от 0 до 9? Нет ли там случайно буквы или запятой? Такую проверку мы научимся делать позднее. Пока что придется самим следить, чтобы в этих полях ввода были только целые числа. Вернемся к программе. Сразу после begin присваиваем целым переменным значения, которые ввел пользователь: Perem1 := StrToInt (Edit1.Text); Perem2 := StrToInt (Edit2.Text); В третью, вещественную переменную, мы должны записать результат деления первого числа на второе. Тут может крыться еще один «подводный камень» - что, если во второе поле пользователь ввел число 0? Еще со школы все мы знаем, что на ноль делить нельзя. Если же мы попробуем это сделать, то компьютер, в лучшем случае, зависнет. Здесь опять придется делать проверку на правильность введенных данных, ставить, как говорят, «защиту от дураков». Подробнее о таких проверках мы поговорим на следующих лекциях, когда изучим условные конструкции. А пока просто наберите этот код: {защита от ошибочного ввода} If Perem2 = 0 then begin //если это ноль, то: 26 //выводит сообщение ShowMessage ('На ноль делить нельзя!'); Edit3.Text := '0'; //как результат записываем ноль end else begin //иначе: Perem3 := Perem1 / Perem2; //делим //преобразуем вещественное в //строку и записываем результат Edit3.Text := FloatToStr (Perem3); end; Здесь следует обратить внимание на предпоследнюю строку. Функция FloatToStr() в качестве параметра принимает вещественное число, и возвращает это же число в виде строки. Например, в результате преобразования s := FloatToStr (123.45); переменной s будет присвоена строка '123.45', которую затем уже можно будет вывести пользователю в качестве результата. В нашем примере мы результат деления двух целых чисел преобразуем в строку и выводим его в поле Edit3. Справедливости ради следует заметить, что в качестве параметра можно передавать не только значение, но и выражение. Например, если указать Edit3.Text := FloatToStr (Perem1 / Perem2); то надобность в использовании вещественной переменной Perem3 отпадает. Попробуйте, как работают оба варианта. Процедуры. Это очень важная тема, обратите на нее особое внимание! Раньше языки программирования были построчными. Единственным вариантом возврата к нужной строке был оператор GO. Очень много ошибок было сделано этим оператором! Современные 27 языки программирования также имеют этот оператор, но применять его считается дурным тоном. Иногда бывает необходимо выполнять часть кода неоднократно. Этот самый код выносят в отдельную подпрограмму – процедуру. Процедура – подпрограмма, которая выполняет какие-то действия, и которую можно вызвать из другого места программы. После выполнения процедуры выполнение программы продолжается с того места, откуда она была вызвана. Процедура живет самостоятельной жизнью, и в любой момент ее можно вызвать, чтобы выполнить какие то действия. Чтобы процедуру можно было вызвать из программы, ее необходимо объявить выше того места, где мы будем ее вызывать. Синтаксис процедуры такой: procedure NameProc (Param : Тип); var //объявление переменных (необязательно) begin //тело процедуры end; Вызвать такую процедуру можно, просто указав ее имя. Проверим это на практике. Вернемся к нашему проекту, и выше процедуры обработки кнопки создадим такую процедуру: procedure Soobshenie; begin ShowMessage ('Ошибка! На ноль делить нельзя!'); end; В данной процедуре мы не используем переменных, поэтому раздел var отсутствует. Все, что делает наша процедура – выводит сообщение о том, что на ноль делить нельзя. Обратите внимание, что если нет входящих параметров, то скобки указывать необязательно. Теперь снова перейдем в процедуру обработки нажатия кнопки, и вместо вывода сообщения, что на ноль делить нельзя, произведем вызов процедуры: 28 Soobshenie; Теперь сохраните проект, скомпилируйте его и посмотрите, что получилось. Не забудьте, что вводить в Edit1 и Edit2 можно только цифры от 0 до 9. Теперь о параметрах. Параметры – это входные данные. То есть, мы можем вызвать процедуру и задать ей нужные данные. Параметры процедуры записываются в круглых скобках с указанием типа. Если параметров нет, скобки можно не ставить. Пример процедуры с параметрами: procedure Primer (a,b : Integer); begin a * b; end; Обратите внимание, что обязательно нужно указывать тип параметров. Теперь мы можем вызвать эту процедуру, указав ей, какие цифры нужно перемножить. Примеры вызова процедуры: Primer (10, 20); //передаем целые числа Primer (a, 100); //передаем переменную a с целым числом, и целое число Primer (c, d); //передаем две переменных с целым числом Сразу следует сказать об области видимости переменных. Бывают переменные глобальные и локальные. Глобальные переменные видны во всей программе, их мы будем использовать позже. А локальные переменные создаются внутри процедуры, в разделе var, и видны только в этой процедуре. Локальные переменные создаются в памяти в то время, когда процедура начинает работу, и уничтожаются, когда процедура закончила работу. Таким образом, мы можем сделать две или более процедур, и указать в них переменные с одинаковым именем. Это будут разные переменные, и они не будут мешать друг другу. 29 Функции. Функции – это такие же подпрограммы, как и процедуры. Отличие функций от процедур в том, что они не просто выполняют какие-то действия и расчеты, но и могут возвращать результат определенного типа. Поскольку они возвращают результат, необходимо указать тип этого результата. Синтаксис функции такой: function NameFunc (Param : Тип) : Тип_возвращаемого_значения; var //объявление переменных (необязательно) begin //тело процедуры Result := результат вычислений; end; Здесь следует обратить внимание на два момента: после имени функции и параметров в круглых скобках, после двоеточия, указывается тип возвращаемого значения. Кроме того, в каждой функции по умолчанию имеется переменная Result, которая имеет тот же тип, что и тип возвращаемого значения. Эту переменную специально объявлять не нужно, она уже готова к работе. В отличие от других языков, в Delphi этой переменной можно присваивать значение неоднократно. Результатом будет последнее присвоенное значение. Есть еще один способ вернуть из функции результат вычислений: использовать переменную с таким же именем, как и имя функции. Эту переменную тоже объявлять не нужно. В нашем примере, строка Result := результат вычислений; будет полностью идентичной строке NameFunc := результат вычислений; Какой из способов использовать – решайте сами, оба способа правильны. Снова вернемся к нашей программе, и для закрепления знаний добавим в нее функцию. Функция также должна быть описана выше того места, где мы будем ее вызывать. Можете создать ее между нашей процедурой и процедурой нажатия на кнопку. 30 function Delenie(a,b : Integer) : Real; begin Result := a / b; end; Теперь заменим ту строку, где в третью переменную записывается результат деления первых двух, на вызов функции и передачу ей этих двух чисел: Perem3 := Delenie(Perem1, Perem2); Конечно, примеры эти примитивны. Реально функции и процедуры выполняют гораздо более важные вещи, чем деление одного числа на другое. Со временем наши программы будут содержать множество таких функций и процедур. События. В Delphi событие означает, что какой то компонент, которому мы назначили событие, изменился. Событие – это процедура, которой передается управление в случае, если произошли запрограммированные изменения. События могут быть самыми разными – изменение текста в поле Edit, нажатие кнопки мыши или клавиши, или просто мышь оказалась над компонентом. Давайте улучшим наш пример, введем в него событие. Выделите компонент Edit1. Сейчас мы зададим ему событие OnChange, которое происходит всякий раз при изменении текста в этом компоненте. Давайте представим себе пользователя, работающего с нашей программой. Ему будет приятно, если он начнет менять текст в первом поле, а остальные поля автоматически очистятся, чтобы быть готовыми для новых расчетов! Выделим компонент Edit1. Перейдем в инспекторе объектов на вкладку Events (события). Дважды щелкнем по событию OnChange (Изменение). Создастся процедура обработки события, и мы попадем в редактор кода. Там мы впишем две строки: Edit2.Clear; Edit3.Clear; 31 Теперь вставим еще одну «защиту «. Ведь третье поле нужно только для результата? А вдруг пользователь начнет там вводить данные? Страшного ничего не произойдет, в расчетах это поле не участвует, но все равно неприятно, когда твою программу используют неправильно. Выделите компонент Edit3. На вкладке Properties (свойства) найдите свойство ReadOnly (только для чтения), и вместо False (ложь), поставьте True (истина). Все, теперь пользователь не сможет вводить данные в это поле, только программа сможет выводить в него результат. Сохраните проект, выполните Run и убедитесь в этом. 32 Тема 4. Управляющие конструкции языков программирования Строковые типы данных. Мы уже работали со строковым типом данных String. Однако, как и в случае с цифрами, бывает несколько строковых типов. Строка состоит из набора символов. Компьютер работает в двоичной системе исчисления, он работает только с цифрами. И символы компьютер воспринимает в виде цифр. Для этого в операционной системе существует таблица символов, где каждому символу соответствует какая либо цифра. Самая первая таблица символов, созданная для операционной системы MS-DOS, называлась ASCII. Эти символы были 7-ми битными, как следствие, таблица содержала максимум 127 символов. Поскольку в одном байте содержится 8 бит, один бит терялся впустую. У этой таблицы были свои языковые расширения, то есть, для каждого языка делалась своя такая таблица. Для операционной системы Windows была разработана таблица символов ANSI, которая используется по сей день. Эта таблица использует 8-ми битные символы, то есть, содержит 256 символов, от 0 до 255. В Delphi такая таблица используется, как основная. Большее количество символов позволило вставить в таблицу не только основные символы, но и цифры, знаки препинания, специальные символы. Мы будем работать, в основном, с этой таблицей. Для того, чтобы удовлетворить потребности пользователей других языков, была создана таблица UNICODE. Эта таблица использует 16 бит, и в нее помещается от 0 до 65 535 символов. Эта таблица, начиная с Windows 2000, используется все более широко, и когда-нибудь станет стандартом, но пока что она встречается гораздо реже, чем ANSI. Имеются следующие строковые типы данных: ShortString – короткая строка из ANSI символов. Количество символов может быть от 0 до 255. Иными словами, строку длиной более 255 символов в ShortString записать нельзя. LongString – длинная строка из ANSI символов. Длина строки здесь почти не ограничена, в одну переменную такого типа можно записать текстовый файл с романом «Война и мир». Этот тип используется по умолчанию, то есть, когда мы указываем тип String, подразумевается именно этот тип данных. С ним нам чаще всего и придется работать. WideString – длинная строка из UNICODE символов. От LongString отличается только тем, что использует иную таблицу символов. Существует еще один тип строк – PChar. Этот тип работает с процессором совершенно иначе, чем предыдущие типы строк. Он очень неудобен в использовании и остался для совместимости с программами 33 старых версий. Кроме того, встроенные в Windows функции работают именно с таким типом данных, поэтому его все же придется использовать. Когда мы начнем работать с этими функциями, разберем этот тип подробней. Символьные типы данных. Программисту приходится работать не только со строками, но и с отдельными символами. Символ – это любая буква, цифра, арифметический знак, знаки препинания или пробел. Кроме того, существуют специальные символы, например, символ перехода на новую строку. Delphi поддерживает два типа символьных данных: AnsiChar – символ ANSI таблицы. Используется по умолчанию, то есть, когда мы указываем тип Char, подразумеваем именно этот тип. WideChar – символ UNICODE таблицы. Разница между ними такая же, как и у строк этих типов. Как и в случае со строками, программист обычно объявляет тип Char, что соответствует типу AnsiChar. Логический тип данных. Логический тип данных используется в условных выражениях. Он необходим для создания логики программы. Условное выражение проверяет – соответствует ли действительность заданному условию? Если соответствует, то выражение возвращает результат True (Истина). Если не соответствует, то возвращается False (Ложь). Таким образом, логический тип данных может иметь только одно из двух этих значений. Boolean – логический тип данных. В таблице 4 приведены операнды, с помощью которых формируют логические выражения: Таблица 4. Логические операнды Логическая операция = > < == Описание Равно Больше Меньше Точное равно Логическая операция >= <= <> Описание Больше или равно Меньше или равно Не равно Предположим, у нас есть два числа, и нужно проверить их на равенство. Эти числа хранятся в переменных целого типа a и b. Тогда выражение сравнения будет выглядеть так: 34 a = b; Если в обоих переменных хранится одно и то же значение, то результатом такого сравнения будет True, иначе – False. Begin ... end. В языке программирования Delphi, как и в других языках, существуют программные скобки, показывающие начало и конец какого-либо блока кода. Эти скобки имеют вид: Begin //блок кода end; Обратите внимание, что после слова Begin точка с запятой не ставится. Эти программные скобки обязательны для каждой процедуры или функции, даже если они содержат только одну команду. Однако эти же скобки часто применяются в управляющих конструкциях или циклах. Циклы, как и управляющие конструкции, работают только с одним оператором (командой). А что, если в этом месте требуется использовать более одного оператора? Тут на помощь приходят эти самые программные скобки. Весь блок кода, сколько бы команд там не было, заключенный между Begin и End воспринимается, как один оператор. Мы не раз будем использовать эти скобки. Управляющая конструкция if. В предыдущих примерах нам уже приходилось применять эту конструкцию на практике, теперь пришло время познакомиться с ней поближе. Управляющая конструкция if является основной конструкцией, на которой строится логика программы. Простейший синтаксис этой конструкции выглядит так: if условие then оператор; Кто знаком с английским языком, уже ухватил суть этой конструкции. На русский язык это выражение можно перевести так: если условие то оператор; Другими словами, если какое-то заданное условие верно, то выполняется указанный оператор. Иначе этот оператор не выполняется. Часто одного оператора бывает недостаточно, тогда используют программные скобки: 35 if условие then begin Оператор 1; Оператор 2; ... Оператор n; end; Операторы, заключенные в программные скобки begin ... end воспринимаются, как единый оператор. Иногда и этого бывает недостаточно. Что, если по логике программы требуется выполнить один оператор, если условие верно, и другой, если оно неверно? Тут приходит на помощь расширенная конструкция: if условие then оператор 1 else оператор2; Как видно из синтаксиса, если указанное условие истинно, то выполнится оператор 1. Иначе выполнится оператор 2. Еще одно правило, которое следует запомнить: перед словом else точка с запятой не ставится! Конечно же, с помощью программных скобок можно расширить и эту конструкцию: if условие then begin Оператор 1; … Оператор n; end else begin Оператор 1; … 36 Оператор n; end; Сейчас самое время упомянуть о классических стилях программирования. Мы уже говорили, что хороший стиль программирования подразумевает каждый отдельный оператор писать на своей строке, применять отступы. В литературе по программированию часто встречается два классических стиля. Один стиль выглядит так: if условие then begin Оператор 1; … Оператор n; end; То есть, слово begin пишется на отдельной строке. Другой стиль выглядит иначе: if условие then begin Оператор 1; … Оператор n; end; Оба эти стиля являются классическими, какой из них применять – дело вкуса. На этой лекции будет использоваться второй стиль. Однако и это еще не все. В предыдущем примере мы рассматривали только одно условие. Если оно верно, то выполнялся один оператор, а если нет – другой. А что, если по логике программы требуется проверить более, чем одно условие? И снова мы можем расширить эту конструкцию. Взгляните на синтаксис: 37 if условие 1 then оператор 1 else if условие 2 then оператор 2 else оператор 3; Здесь, если условие 1 будет верно, конструкция выполняет оператор 1 и завершает работу. Если это условие неверно, проверяется условие 2. Если условие 2 верно, то выполняется оператор 2, и конструкция завершает работу. Если же оба условия неверны, выполняется оператор 3. Расширений else if в такую конструкцию можно вставлять сколько угодно. Давайте теперь проанализируем часть кода из примера прошлой лекции. If Perem2 = 0 then begin Soobshenie; Edit3.Text := '0'; end else begin Perem3 := Delenie(Perem1, Perem2); Edit3.Text := FloatToStr(Perem3); end; Вам уже должно быть понятно, что если Perem2 действительно равна 0, то выполнится первый блок кода. Иначе будет выполнен второй блок кода. Цикл for. Еще один мощный инструмент в Delphi – циклы. Эти циклы выполняют оператор или блок операторов заданное количество раз. Синтаксис цикла for следующий: for счетчик := нач_значение to кон_значение do оператор; В расширенном варианте синтаксис такой: for счетчик := нач_значение to кон_значение do begin 38 оператор 1; оператор 2; … оператор n; end; Здесь: счетчик – переменная любого целого типа; нач_значение – начальное значение счетчика; кон_значение – конечное значение счетчика. В качестве счетчиков используют обычно целые переменные. Чтобы не тратить попусту оперативную память, счетчикам назначают тип Byte или Word, в зависимости от того, какое значение может оказаться у конечного значения. В самом начале счетчик будет равен начальному значению. Когда выполнятся все указанные операторы блока, управление вновь вернется к началу цикла. Счетчик увеличится на единицу, и цикл будет выполнен снова. И так далее, до тех пор, пока счетчик не станет равным конечному значению. Давайте изучим цикл for на примере. Пусть нам нужно перевести температуру от 0 до 100 градусов из шкалы Цельсия в шкалу Фаренгейта. Правила здесь просты: нужно взять температуру по Цельсию, умножить ее на 9/5 и к результату прибавить 32. Сам результат нужно будет программно добавить в компонент Memo в виде строки: «X градусов по Цельсию = Y градусов по Фаренгейту». Создайте новое приложение. Сразу же свойству Name формы присвойте имя fName. В свойстве Caption напишите «Перевод температур из Цельсия в Фаренгейты». Сохраните проект в новую папку. В верхнюю часть окна поместите компонент Memo и в свойстве Lines удалите из него весь текст. Растяните Memo по форме, как на рисунке 7: Ниже Memo добавьте кнопку Button, в свойстве Caption которой укажите «Расчет температур». Теперь создадим обработчик нажатия на кнопку, дважды щелкнув по ней. Процедура выглядит так: 39 procedure TfMain.Button1Click(Sender: TObject); var i : Byte; //счетчик begin for i := 0 to 100 do //делаем от 0 до 100 Memo1.Lines.Add(IntToStr(i) + ' градусов Цельсия = ' + FloatToStr(CelToFar(i)) + ' Фаренгейта'); end; Рис. 7. Внешний вид приложения Здесь следует обратить внимание на последний оператор. Оператор 40 Memo1.Lines.Add('Строка'); добавляет строку 'Строка' в компонент Memo. При этом, в качестве строки, мы использовали сложное выражение, формирующее строку из разных частей. Разберем каждую часть подробно. IntToStr(i) В качестве результата вернет значение i (счетчика) в виде строки. Как уже говорилось, это значение за каждый проход цикла будет увеличиваться на единицу. То есть, в первом проходе i будет равна 0, во втором 1, и так далее, пока не станет равна 100. Выполнив оператор в последний раз, цикл прекратит свою работу. А мы каждый раз будем получать это число в виде строки. Далее у нас идет строка: ' градусов Цельсия = ' С помощью знака «+» (знак конкатенации) мы добавляем одну строку к другой. В результате, при первом проходе мы получим строку: '0 градусов Цельсия = ' Обратите внимание, что во второй строке в начале и в конце есть пробелы. Без пробела строки сольются: «0градусов Цельсия =», а это некрасиво. Далее мы имеем строку: FloatToStr(CelToFar(i)) Функция FloatToStr() возвращает вещественное число в виде строки. В качестве вещественного числа мы использовали вызов функции CelToFar(i), передав ей в качестве параметра целое число – текущее значение счетчика. Функция использует это число, как градус Цельсия, преобразует его в Фаренгейт и вернет значение в виде вещественного числа. Этой функции пока еще не существует, мы напишем ее позже. В результате мы получим строку «0 градусов Цельсия = 32». Далее мы добавляем еще строку ' Фаренгейта' в результате чего получаем готовую строку «0 градусов Цельсия = 32 Фаренгейта». И уже готовую строку с помощью метода Add() мы добавляем в компонент Memo. 41 Еще одно правило, которое можно увидеть из этого примера – если оператор получается длинным, его можно перенести на следующую строку после арифметического знака или запятой. Функцию CelToFar() нужно будет создать выше нашей процедуры: function CelToFar(a : Byte): Real; begin Result := a * 9/5 + 32; end; Функция принимает параметр в виде целого числа, переводит его в Фаренгейты и возвращает в качестве результата, как вещественное число. Наша программа готова. Сохраните ее, скомпилируйте и посмотрите, как она работает. Если у вас не умещается весь полученный текст в Memo, добавьте в компонент вертикальную полосу прокрутки. Инструкция множественного выбора case. Case – инструкция множественного выбора. В случае, когда нужно выбрать один вариант из нескольких, можно конечно, воспользоваться конструкцией if. Однако case позволяет сделать это намного эффективнее. Синтаксис инструкции такой: case Селектор of значение 1 : begin Инструкции 1 end; значение 2 : begin Инструкции 2 end; значение n : begin Инструкции n end 42 else begin Инструкции иначе end; Здесь «Селектор» – это переменная целого типа или символьного типа, либо выражение, которое возвращает целое число или букву. Блок else не является обязательным. В случае, если инструкция содержит только один оператор, использовать программные скобки begin .. end необязательно. Пример применения case: case i of 1 : ShowMessage('i = 1'); 2 : ShowMessage('i = 2'); 3 : ShowMessage('i = 3'); 4 : ShowMessage('i = 4'); else : ShowMessage ('i не равно 1, 2, 3 или 4'); end; Инструкция (цикл) WHILE. Эта инструкция применяется в том случае, когда нужно выполнить несколько раз определенные действия. Причем заранее не известно, сколько раз этот цикл выполнять – это становится ясно только во время работы программы. Синтаксис инструкции такой: while условие do инструкция; или расширенный вариант: while условие do begin инструкция 1; инструкция 2; ... 43 инструкция n; end; Здесь условие – переменная или выражение логического типа. Сначала вычисляется условие. Если условие ложно (False), то цикл не выполняется и сразу заканчивает работу. Если же условие истинно (True), то цикл выполняет инструкции, затем вновь возвращается к условию. Если условие опять возвращает True, цикл выполняется еще раз, и так до тех пор, пока условие не вернет False. Будьте осторожны с этим циклом! Не включайте туда условие, которое никогда не станет False. Компьютер в таком случае «зациклится», то есть зависнет. Обычно для этого внутрь цикла помещают счетчик – переменную целого типа. Только в отличие от цикла for, здесь переменная не увеличивает свое значение автоматически, за этим должен следить сам программист. Пример применения инструкции: i := 1; while i < 10 do begin ShowMessage('Число равно ' + IntToStr(i)); i := i + 1; end; В этом простом примере мы целой переменной i присваиваем значение 1. Затем выполняем цикл while до тех пор, пока i не станет больше или равно 10. Если бы мы внутри цикла не указали «i := i + 1;», то этого никогда бы не произошло, и мы зациклили бы программу! Попробуйте выполнить этот пример в новом проекте, привязав код к обработке нажатия кнопки. В дальнейшей практике Вам не раз придется использовать этот цикл с куда более полезными примерами. 44 Тема 5. ООП – Объектно-ориентированное программирование Объект. Объект – это совокупность свойств, методов и событий. То есть объект состоит из этих свойств, методов и событий, а они обеспечивают его полноценную работу. Представим себе кнопку. Она обладает: Свойствами – цвет, текст на кнопке, шрифт текста и так далее. Событиями – события пользовательских действий, например, пользователь нажал на кнопку, указатель мыши оказался над кнопкой, и т.п. Методами – обеспечивающими работу кнопки, например прорисовка кнопки в нажатом и не нажатом виде, прорисовка фокуса (то есть, фокус ввода находится на кнопке). Раньше приходилось затрачивать много времени и усилий, чтобы нарисовать такую кнопку на форме, код кнопки мог занимать страницу – полторы. Теперь мы имеем автономный объект, который достаточно бросить на форму, и он уже готов к употреблению. Нужно две кнопки? Три? Нет проблем – кидаем на форму столько кнопок, сколько нужно, и не заботимся о том, как программно эти объекты описаны в коде. Итак: Свойства – это переменные, которые влияют на состояние объекта. Например, ширина, высота, положение кнопки на форме или надпись на ней. Методы – это те же процедуры и функции, то есть это то, что объект умеет делать (вычислять). Например, объект может иметь процедуру для вывода какого-то текста на экран. Кнопка при нажатии меняет форму – это метод кнопки, процедура прорисовки вида нажатой и не нажатой кнопки. События – это те же процедуры и функции, которые вызываются при наступлении определенного события. Например, пользователь нажал на кнопку, вызывается процедура обработки этого нажатия. Или мышка оказалась над кнопкой – вызывается процедура обработки этого события, если программист ее создал. Как уже упоминалось в первой лекции, программирование с применением объектов для программиста существенно упростилось. Вместо того, чтобы вписывать сотни строк кода, описывающего поведение одной единственной кнопки, программист просто объявляет объект типа «Кнопка». Далее появились системы визуального программирования, такие как Delphi, которые используют компонентную модель программирования. Здесь уже программисту не нужно самостоятельно задавать такие свойства объекта, как его положение на форме, высоту и ширину. Вы просто устанавливаете нужный компонент на форму, и мышкой или клавиатурой двигаете, 45 растягиваете или сжимаете его до нужных размеров. Серьезное программирование превратилось в детский конструктор, где Вы создаете интерфейс программы даже не задумываясь над тем, сколько кода для этого нужно было бы написать! Delphi это делает за Вас. Компоненты – это более совершенные объекты. То есть, это объекты, с которыми можно работать визуально. Справедливости ради следует отметить, что существуют и не визуальные компоненты, например, диалоги, с которыми нам скоро предстоит познакомиться. Не следует путать понятия «объект» и «компонент». Каждый компонент – это объект, но не каждый объект является компонентом. Представим себе отвлеченный пример. Допустим, у нас на палитре компонентов есть компонент ТЧел, который представляет собой усредненного человека. Если мы мышкой щелкнем по этому компоненту, а затем щелкнем по форме, то мы создадим отдельный, автономный объект этого компонента, который уже обладает всеми свойствами, методами и событиями, присущими каждому человеку. Как у любого объекта, у него есть свойство Name – имя компонента, то имя, по которому мы будем в дальнейшем обращаться к этому объекту. Delphi по умолчанию присвоит ему текст «Чел1». Если мы установим на форму еще один такой компонент, то будет создан еще один автономный объект, у которого свойство Name будет содержать строку «Чел2». Итак, свойство Name – это переменная строкового типа, принадлежащая любому объекту, и являющаяся идентификатором (опознавателем), по которому к этому объекту нужно обращаться. Далее, у нашего воображаемого объекта есть и другие свойства. К примеру, свойства Имя, Фамилия, Отчество. Это также строковые переменные, принадлежащие этому объекту. Мы можем вписать в них нужный текст программно: Чел1.Имя := 'Иван'; Чел1.Отчество := 'Иванович'; Чел1.Фамилия := 'Иванов'; Обратите внимание, что свойство Name содержит имя объекта, по которому мы к этому объекту обращаемся. Если бы мы изменили в свойстве Name Чел1 на Человек, то код выглядел бы иначе: Человек.Имя := 'Иван'; Точно также, эти свойства мы можем менять в Инспекторе объектов, в момент создания программы. Так, мы меняли свойства Caption у кнопок, выводя на них нужный нам текст. А перемещая кнопку на нужную позицию, мы тем самым меняли свойства Left и Top. 46 Далее, этот объект имеет не только строковые, но и другие типы свойств – переменных. Например, возраст (в годах) и рост (в сантиметрах) – это будут переменные целого типа: Чел1.Возраст := 30; Чел1.Рост := 180; Объект может иметь и символьную переменную, например, свойство Пол (м – мужской, ж – женский): Чел1.Пол := 'м'; Также такой объект может иметь и логический тип данных, например, Военнообязанный, то есть, был в армии или нет: Чел1.Военнообязанный := True; Очень часто бывает, что в различных компонентах мы указываем в инспекторе объектов начальные значения, как бы по умолчанию, а затем во время работы программы меняем их на нужные. В программе с убегающей кнопкой, мы задали кнопке начальное положение, а затем, во время работы программы, мы изменяли свойства Left и Top. А когда пользователь вводил текст в компонент Edit, программно менялось его свойство Text. Итак, мы получили более-менее оформленный объект человека. Мы можем накидать целую форму таких объектов, используя компонент ТЧел, и создать целый город Челов. И у каждого заполнить приведенные в примерах свойства, так что все объекты будут разными, хотя и произошли от одного общего компонента. Вернемся к реальным компонентам. Возьмем компонент TEdit. Если мы кинем на форму такой компонент, то Delphi автоматически установит свойство Name равным строке Edit1. В дальнейшем мы будем обращаться к этому объекту по этому имени. Если же мы в Инспекторе объектов изменим свойство Name с Edit1 на, скажем, MyEdit, то обращаться придется уже не к Edit1, а к MyEdit. Предположим, мы изменили это свойство. Не забываем, что свойство Name – переменная типа строка. Далее, в компоненте TEdit нас интересуют еще несколько свойств. Это свойства Left и Top, которые имеют целый тип, и обеспечивают положение компонента на форме. Свойство Left указывает в пикселях расстояние от левой границы формы до компонента, а свойство Top – такое же расстояние от верхней границы. Еще есть свойства Width (ширина компонента) и Height (высота компонента). Это тоже целые типы, они указывают значение в пикселях. 47 Также нас интересует свойство Text. Это строковая переменная, она указывает, какой текст отображается в этом компоненте. Мы можем ввести в него текст в Инспекторе объектов, и тогда при работе программы он сразу же будет отображаться в поле ввода. Гораздо чаще его оставляют пустым. В Инспекторе объектов просто очищают это свойство, а программно можно присвоить компоненту пустую строку: MyEdit.Text := ''; Затем, во время выполнения программы, пользователь вводит какой то текст в наш Edit. Нам нужно обработать этот текст, и получить к нему доступ мы можем, указав имя нужного объекта и его свойство: s := MyEdit.Text; В данном примере мы присвоили строковой переменной s тот текст, который в данный момент хранился в поле ввода нашего компонента Edit. Напомним, что присвоение происходит справа – налево, то есть вначале указывается переменная, которой мы собираемся присвоить значение, затем идет знак присваивания «:=», после чего идет то значение, которое мы присваиваем этой переменной. Мы имеем возможность программно изменить текст в этом объекте, и в данном случае будем указывать переменную – свойство Text: MyEdit.Text := 'Новый текст'; Теперь наш объект будет отображать строку с новым текстом. Кроме того, мы программно можем изменять и другие свойства. Например, свойство Left – положение от левой границы формы. Мы можем указать: MyEdit.Left := MyEdit.Left – 5; Если мы укажем такой текст, например, в процедуре обработки нажатия кнопки, то каждый раз, когда мы нажимаем на кнопку, объект MyEdit будет сдвигаться влево на 5 пикселей. Попробуйте сами! Создайте новое приложение, установите на форму компонент TEdit и кнопку. Если Вы не изменили свойство Name, то придется обращаться к объекту по имени, которое по умолчанию присвоила ему Delphi: Edit1.Left := Edit1.Left - 5; 48 Конечно, если Вы пишете не какую-нибудь учебную программу, то менять положение объектов не стоит, даже если у Вас есть такие возможности. Кроме того, Вы имеете возможность программно изменить свойство Name у объекта, но это будет грубейшей ошибкой – как только программа обратится к этому объекту по старому имени, тут же произойдет ошибка выполнения программы – она просто не найдет этот объект. Можете попробовать в данном примере, дописав строку: Edit1.Name := 'MyEdit'; Как только Вы нажмете кнопку в первый раз, объект послушно сместится влево, и сразу же за этим поменяет имя. Но попробуйте нажать кнопку еще раз, и программа сразу зависнет – она уже не видит объект с новым именем, ведь в коде обработки кнопки мы по прежнему обращаемся к объекту по старому имени, а объекта с таким именем уже не существует! Не волнуйтесь, ничего страшного не произошло. Просто выберите в меню команду Run – Program Reset. Это заставит программу досрочно прекратить выполнение и закрыться. Не забывайте еще вот о чем: не у всех компонентов, которые выводят текст на экран, есть свойство строкового типа Text. Например, у компонента TLabel таким свойством является Caption, и если мы хотим в инспекторе объектов вписать в объект какой-то текст, вписывать его нужно именно в свойство Caption. Точно также, Если мы желаем программно изменить текст в объекте Label1, то делаем присвоение нового текста его свойству Caption: Label1.Caption := 'Новый текст'; Однако, есть объекты и посложней. Возьмем, к примеру, TMemo. У него нет свойства Text в инспекторе объектов, однако это свойство доступно программно. Переменная – свойство Text объекта Memo1 содержит весь текст, все строки, которые находятся в данный момент в компоненте. Однако гораздо чаще применяют свойство Lines. Это свойство уже не простая строковая переменная, а целый массив строк, где каждый элемент содержит отдельную строку текста. Можно сказать, что Lines – это объект в объекте, который также имеет свои методы, то есть функции и процедуры. Чаще всего используют методы SaveToFile() и LoadFromFile(), которые соответственно, сохраняют текст в файл и читают текст из файла. Свойство Lines имеет тип TStrings, то есть не просто строку, а набор строк. Многие компоненты имеют свойства такого типа. Например, компонент TListBox имеет свойство Items, которое принадлежит к тому же типу TStrings, следовательно, имеет такие же методы, как и Lines у 49 компонента TMemo. Это значит, что вызвав нужный метод, мы можем, к примеру, считать текст из файла и в Memo, и в ListBox: Memo1.Lines.LoadFromFile('myfile.txt'); ListBox1.Items.LoadFromFile('myfile.txt'); Вы неоднократно будете встречаться со свойствами типа TStrings в разных компонентах. Это очень мощный инструмент, и нужно учиться пользоваться им. События. События – это процедуры, которые выполняются всякий раз, когда это событие происходит. Например, событие OnChange компонента Edit происходит всякий раз, когда меняется текст в поле ввода. События находятся на вкладке Events Инспектора объектов. Если мы ничего не создадим, то при наступлении этого события ничего и не произойдет. Однако, если мы дважды щелкнем мышью по этому событию – создастся процедура обработки этого события. И тот программный код, который мы введем, будет выполняться всякий раз, когда это событие происходит. Событие OnChange применяют довольно часто, когда нужно проверить правильные ли данные вводит пользователь. Хорошая новость – большинство событий (а также свойств и методов), у большинства компонентов одинаковы. Например, практически любой компонент для ввода пользователем текста имеет событие OnChange. Применяете ли вы Edit или Memo, это событие работает одинаково. 50 Тема 6. Структура приложений и файлы проектов приложений Меню. Свойства Enabled и Visible. Эти свойства присутствуют практически у всех визуальных компонентов, и позволяют использовать интересные приемы программирования. Откройте проект с редактором текста из прошлой лекции. У вас там есть три кнопки – «Сохранить», «Загрузить» и «Очистить». Давайте предположим, что пользователь набрал какой-то важный текст и сохранил его. На следующий день он снова загрузил ваш редактор, но по ошибке вместо кнопки «Загрузить» он нажал кнопку «Сохранить». Что произойдет? Никакого текста в данный момент компонент Memo не содержит. Если будет выполнена команда Memo1.Lines.SaveToFile('MyFile.txt'); то наш файл перезапишется – важный текст будет стерт, а взамен ничего не запишется, так как текста нет. Пользователь, совершив небольшую ошибку, потеряет свою работу. Конечно, виноват пользователь – он нажал не ту кнопку. Но в большей степени здесь виноват программист, что не предусмотрел такого развития событий, и не сделал для своей программы элементарной защиты от ошибок пользователя. Исправим ошибку. Свойство Enabled отвечает за доступность компонентов. Это свойство имеет логический тип, и может быть либо True, либо False. Выделите кнопку с надписью «Сохранить», найдите ее свойство Enabled и вместо установленного по умолчанию значения True, выберите значение False. На форме не произошло видимых изменений, мы не сможем их увидеть в режиме разработки программы. Однако сохраните проект, скомпилируйте командой Run и посмотрите на результат – кнопка «Сохранить» видна на форме, но ее надпись выходит серым, неактивным цветом, а на кнопку невозможно нажать. Теперь у пользователя нет возможности нажать на эту кнопку, значит, он не совершит такой ошибки. Но это только полдела, надо все-таки в процессе работы программы ему эту возможность вернуть, иначе как же он сможет сохранить текст? В лекции 5 мы обсуждали событие OnChange, которое происходит всякий раз, когда компонент изменяется. Этим мы и воспользуемся. Выделите компонент Memo, в Инспекторе объектов перейдите на вкладку Events (События) и найдите там событие OnChange. Дважды щелкните по нему, чтобы сгенерировать процедуру – обработчик этого события. В этой процедуре запишем всего только одну строку: Button1.Enabled := True; 51 Таким образом, при изменении компонента Memo (пользователь изменил текст), мы делаем доступной кнопку «Сохранить». Теперь пользователь может сохранить свои изменения. Подумаем теперь вот о чем: когда пользователь сохранил текст, следует ли оставлять кнопку «Сохранить» доступной? Очевидно, нет, если у текста не произошли изменения. Поэтому щелкайте дважды по кнопке «Сохранить», и после строки, где текст сохраняется, добавьте строку: Button1.Enabled := False; Сразу, как только текст будет сохранен, кнопка «Сохранить» снова станет недоступной, пока пользователь не произведет очередных изменений в тексте. Также сделайте недоступной эту кнопку после того, как пользователь нажмет кнопку «Очистка», эта кнопка очищает набранный текст, что само по себе заставляет работать событие OnChange компонента Memo, однако сохранять пустоту смысла нет. Свойство Visible компонентов работает практически также, как свойство Enabled, только вместо того, чтобы делать компонент недоступным, оно делает его невидимым. Visible также имеет логический тип, и может быть либо True (компонент видим), либо False (невидим). Самостоятельно измените все приведенные выше рекомендации, использовав вместо свойства Enabled свойство Visible. Посмотрите на результат. Скрывать или отключать можно не только кнопки, но и вообще все видимые компоненты – редактор Memo, поле Edit, надпись Label. Даже у формы есть эти свойства, поэтому при некоторой фантазии вы можете добиваться вполне профессиональных эффектов. Главное меню. Любая более-менее серьезная программа имеет собственное меню. Пришла пора познакомиться с этим компонентом. Для примера снова загрузим наш редактор текстов. Выделите все кнопки на форме и удалите их. Также удалите и панель, на которой эти кнопки были. Затем нам потребуется удалить все процедуры обработки этих кнопок, но тут нужно проявить осторожность – нельзя просто взять, и удалить процедуру. Каждая сгенерированная процедура прописана в коде и выше. Чтобы без ошибок удалить все ненужные последние процедуры, следует просто удалить из них тот код, который мы писали сами, оставив «пустую» процедуру – имя процедуры и строчки begin. end. 52 Рис. 8. Удаление ненужных процедур После того, как вы сохраните проект, все пустые процедуры будут удалены автоматически. Это касается последних процедур – если после такой пустой процедуры будет присутствовать действующая процедура или функция, они обе останутся в коде. Таким образом, у вас должна получиться форма, на которой расположен только компонент Memo, и больше ничего. В редакторе кода не должно остаться ни одной процедуры. В общем, только заготовка программы. Выделите компонент Memo, и убедитесь, что в его свойстве Align установлено значение alClient, то есть, Memo растянуто на всю форму. На вкладке Standard найдите компонент MainMenu (главное меню), и установите его на любое место формы, прямо на компонент Memo. Компонент MainMenu не визуальный, то есть, пользователь все равно не будет его видеть. Будем создавать меню. Дважды щелкните по MainMenu, чтобы вызвать редактор меню. Когда редактор откроется, вы увидите, что первый пункт меню выделен синим цветом. Пусть выделение остается, перейдите на свойство Caption и введите текст «Файл». Нажав <Enter>, вы сформируете в меню команду «Файл», а выделение переместится направо, к следующей команде. Другой пункт меню мы пока делать не будем, щелкните мышью немного ниже команды «Файл», чтобы 53 выделить пункт ниже. Получается, что мы сделали пункт меню «Файл», и сейчас делаем подменю этого пункта. Нам потребуются следующие команды: «Сохранить». «Загрузить». «Очистить». «-». «Выход». Рис. 9. Внешний вид редактора главного меню Предпоследняя команда, знак «-» (минус), формирует в меню разделительную полосу. Как только вы закроете редактор меню, строка с главным меню сейчас же появится над компонентом Memo. Щелкните один раз по слову «Файл», и откроется подменю. Щелкните по команде «Сохранить», и будет создана процедура обработки этой команды. Команда «Сохранить» по-прежнему выглядит, как: Memo1.Lines.SaveToFile('MyFile.txt'); Все остальные команды введите аналогичным образом. Команда «Выход» выглядит так: Close; //выход из программы Всплывающее меню. Всплывающее меню вызывается, когда пользователь щелкает правой кнопкой мыши по объекту – форме или какому либо другому компоненту. Найдите на вкладке Standard компонент PopupMenu (всплывающее меню), и также установите его поверх компонента Memo. 54 Редактор этого меню вызывается таким же образом, как и редактор главного меню. Во всплывающем меню, однако, только одна ветка меню, где команды указываются одна под другой. Другими словами, нет возможности делать пункты меню (Файл, Правка, Вид и т.д.) и подпункты (Файл -> Создать, Файл -> Загрузить и т.д.). Создайте следующие команды: «Сохранить». «Загрузить». «Очистить». «-». «Выход». Чтобы создать обработчик события для команды, нужно дважды щелкнуть по ней в редакторе всплывающего меню. Сами команды точно такие же, как и у главного меню. Напишите код для всех указанных пунктов всплывающего меню. Примечание: в дальнейших лекциях мы будем изучать компонент ActionList, который позволяет использовать одну команду для одноименных пунктов главного и всплывающего меню, а также панели инструментов. Пока что нам придется дублировать команды главного и всплывающего меню. Теперь всплывающее меню нужно привязать к форме, само по себе оно работать не будет. Для этого нам нужно выделить форму, что является непростой задачей – компонент Memo растянут на все окно, и нет возможности щелкнуть по свободному месту формы. Форму проще всего выделить в окне Дерева объектов (Object TreeView). Если у вас в данный момент это окно закрыто, открыть его можно командой меню View-> Object TreeView , или горячими клавишами <Shift+Alt+F11>. В этом окне легко можно выделить любой компонент, в том числе и форму. Итак, выделите форму. В окне Инспектора объектов отразятся свойства формы. Нас интересует свойство PopupMenu. Оно имеет вид списка, в котором мы можем выбрать то или иное всплывающее меню. Поскольку такое меню у нас только одно, его и выбираем. Теперь можно сохранить проект, скомпилировать его и запустить на выполнение. Щелчок правой кнопкой на любом месте формы приведет к вызову всплывающего меню. Всплывающее меню также называют контекстными – дело в том, что многие компоненты имеют свойство PopupMenu – редактор Memo, панели и многие другие компоненты. Можно установить несколько всплывающих меню с разными командами, и привязать к различным компонентам свои собственные PopupMenu. Тогда щелчок правой кнопкой над одним компонентом приведет к вызову одного всплывающего меню, над другим – другого. 55 Модальные окна. Модальным называется дочернее окно, которое не дает главной форме работать, пока не закончена работа этого модального окна. До сих пор мы делали проекты только с одной формой. Настало время изучить многооконные проекты. Воспользуемся предыдущим примером – редактором текстов. Откройте этот проект, если он закрыт. Чтобы создать новое окно, выберите команду «File – New – Form». В свойстве Caption новой формы напишите «О программе», форму назовите fAbout и сохраните проект. Модуль новой формы, соответственно, назовите About. На форму установите компонент Label, напишите в его свойстве Caption «Программа: MyNotebook v 1.0». Ниже установите еще один Label. Свойство AutoSize (автоматическое изменение размера) поставьте в False, а свойство WordWrap (перенос слов на другую строку) – в True. Здесь напишите такой текст: Программа предназначена для простого редактирования текстов. Программа позволяет сохранять текст в файл, и считывать его из файла. Файл создается там же, откуда запущена программа, и имеет имя MyFile.txt. Ниже установим еще один Label. Пишем в нем: «Автор: такой-то», можете указать свои собственные фамилию, имя и отчество. Будет красиво, если текст этого компонента выйдет посередине. Чтобы добиться этого, свойство AutoSize (автоматическое изменение размера) установите в False, свойство Aligment (выравнивание текста) в taCenter. Ниже устанавливаем кнопку. Пишем на ней «ОК» и создаем обработчик кнопки для выхода из формы. Инструкция Close главной формы закрывает всю программу, а инструкция Close модального окна закрывает только это окно. Можете «поиграть» со свойствами Font и Color компонентов, чтобы форма выглядела красивей. Подобные окна не имеют кнопок «Свернуть» и «Развернуть», поэтому в свойстве BorderStyle формы fAbout выберите значение bsDialog. А в свойстве Position (Позиция формы при ее открытии) выберите poMainFormCenter, чтобы форма появлялась по центру главного окна. Обратите внимание на последнее свойство. Обычно для главных окон программы это свойство устанавливают в poDesktopCenter, чтобы форма появлялась по центру рабочего стола, запомните это на будущее. Теперь, чтобы можно было вызывать это окно модально, нужно «привязать» его к главной форме. Сейчас в редакторе кодов имеется две вкладки – Main и About, это модули главной формы и формы «О программе». Перейдите на вкладку главного окна и нажмите <F12>, чтобы вызвать это окно. Выберите команду «File – Use Unit». Откроется 56 окно, где мы увидим нашу модальную форму. Выделите ее и нажмите OK. Теперь, если Вы посмотрите код главной формы, под разделом implementation Вы увидите такую картину: implementation uses About; Delphi вставила инструкцию, при которой главная форма использует все, что описано в коде модального окна. Можно было набрать все это вручную, однако во избежание ошибок рекомендуется пользоваться командой «File – Use Unit». Тогда код точно будет введен без ошибок, и именно в то место, которое нужно. uses About; Теперь в главной форме снова откройте редактор главного меню, дважды щелкнув по компоненту MainMenu. Выделите пункт справа от «Файл», и в свойстве Caption напишите «Справка». А в пункте ниже напишите «О программе»: Рис. 10. Редактор главного меню При разработке меню не забывайте о стандартах, принятых в Windows. Никто не запрещает Вам засунуть подраздел «О программе» в Пункт «Файл» или какой-нибудь другой. Однако пользователь будет искать его именно в разделе «Справка». Не заставляйте пользователя привыкать к другим стандартам, если хотите, чтобы ваши программы пользовались спросом! 57 Теперь нужно написать код вызова модального окна. Дважды щелкните по подразделу меню «О программе», чтобы создать процедуру обработки этого подраздела. Там введите инструкцию: fAbout.ShowModal; Помните, что мы вызываем форму модального окна (fAbout), поэтому нужно обращаться именно к ней. Сохраните проект, скомпилируйте программу и посмотрите результат. Структура проекта. До сих пор мы создавали программы, не задумываясь о том, какие файлы при этом создаются, для чего они нужны. Нам понятно, что файл проекта с расширением *.exe – это сама программа, которая получилась после компиляции проекта. Файлы с расширением *.pas – файлы модулей, содержащих исходный код программы. Настало время подробней изучить структуру проекта, из каких файлов он состоит, для чего они нужны, какова их внутренняя структура. Модуль. Проект в Delphi представляет собой набор программных единиц – модулей. Модуль на английском языке называется Unit. Когда мы разрабатываем форму, мы одновременно обрабатываем модуль этой формы. Модуль – это файл с расширением *.pas. Это расширение – сокращение от языка программирования Pascal, на котором строилась Delphi. Иначе говоря, модуль – исходный код программы, или исходник. Как только мы создаем новую форму, сразу же создается и модуль для нее. Delphi заполняет этот модуль необходимым кодом, а нам остается создавать код для обработки кнопок и различных событий. Давайте на примере уже рабочей программы посмотрим, из чего состоит проект. Откройте проект MyNotebook из лекции 8. Перейдите на редактор кода. На самом верху вы видите строку: unit Main; Unit – это зарезервированное слово, обозначающее Модуль формы. Зарезервированными называются служебные слова, которые уже имеют собственные названия в компиляторе, и такие названия нельзя давать переменным, константам, массивам, компонентам, функциям или процедурам. Зарезервированные слова в коде всегда выделяются жирным шрифтом, вы можете это видеть на примере таких слов, как unit, interface, uses, begin или end, и так далее. 58 Когда мы создавали форму, то сразу дали ей имя (свойство Name) fMain. А когда сохраняли проект, Delphi прежде всего запросила имя для модуля формы. Мы назвали модуль также как форму, но без начальной буквы f - Main. Вот почему наверху кода unit (Модуль) этой формы имеет имя Main. Теперь вы понимаете, как важно соблюдать правила наименования форм и модулей? Рис. 11. Фрагмент редактора кода Когда в проекте будет много форм, то и модулей будет много, и если вы не будете соблюдать эти правила, то запросто запутаетесь в названиях. И также важно давать формам и модулям осмысленные имена. В сложных проектах таких форм и модулей может быть сотни! Но вернемся к проекту MyNotebook. Взгляните на код. Далее идет служебное слово interface. Эта директива сообщает компилятору, что начинается раздел интерфейса. В этом разделе, прежде всего, указываются модули, подключенные к данному модулю. Они описываются после служебного слова uses. Когда мы вводим какуюлибо функцию или процедуру, мы не задумываемся, откуда она берется. А эта функция описана в одном из подключенных модулей! Давайте в этом убедимся. Спустите курсор в нижнюю часть модуля. Последней у нас описана процедура вызова модального окна fAbout. Ниже этого вызова введите строку: ShowMessage('Привет!'); Она тут совершенно не нужна, и потом мы ее удалим. А пока что установите курсор внутри самого слова ShowMessage, и нажмите <Ctrl>+<F1>. Тем самым, вы вызываете контекстную подсказку именно 59 об этой команде. И в самом начале справки вы видите указание, в каком модуле описана данная процедура: Unit Dialogs or QDialogs Теперь вернитесь к разделу uses, в котором описаны подключенные модули. Вы можете убедиться, что модуль Dialogs в списке присутствует. Если бы это было не так, то при попытке вызвать процедуру ShowMessage компилятор сразу сообщил бы нам об ошибке, он просто не нашел бы описания этой процедуры и не знал, откуда ее вызвать. Данный раздел подключенных модулей Delphi генерирует самостоятельно, и добавлять сюда другие модули вручную приходится лишь в том случае, когда вы хотите воспользоваться процедурой, описанной в модуле, которого в списке нет. Вы точно знаете, что такая процедура или функция существует, но компилятор выдает ошибку. Тогда вы устанавливаете курсор на имя процедуры и вызываете контекстную подсказку. И смотрите, в каком модуле описана данная процедура. Если этого модуля в списке нет, а процедура вам нужна позарез, добавьте модуль в список. Кстати, количество подключенных к форме модулей объясняет, почему, казалось бы, небольшой проект имеет достаточно большой размер *.exe файла. Ведь подключенные модули содержат множество описаний функций и процедур, и даже если мы их не используем, они все равно доступны. Однако беспокоиться по этому поводу не стоит – нам едва ли придется писать программы, для которых критичен размер исполняемого файла. Это, как правило, драйверы или вирусы, и хотя на Delphi можно написать и то, и другое, для этих задач больше подходит чистый С. А если вы будете использовать Borland C++ Builder или Microsoft C++ Visual, то и они имеют множество подключенных модулей, и размер даже маленькой программки окажется большим. Вернемся к нашему коду. Далее идет служебное слово type, которое описывает форму – какие в ней находятся компоненты, как они называются, и какие процедуры используются. В этом блоке вы видите разделы: private { Private declarations } public { Public declarations } 60 В раздел private можно добавлять описания собственных функций и процедур, которые не должны быть видны из других подключенных модулей. А если вы эти же самые процедуры и функции опишите в разделе public, то их можно будет использовать и в других модулях проекта. Позже мы не раз будем пользоваться этой возможностью. Далее идет описание глобальных записей, констант и переменных. Здесь у нас есть только одна глобальная переменная: var fMain: TfMain; Это – форма. Да, да, не удивляйтесь, форма – это переменная типа TForm. Точнее, не просто переменная, а объект со своими свойствами, событиями и методами. Здесь же вы можете объявить собственные переменные или массивы, чтобы они стали глобальными. Глобальные переменные видны во всем модуле, в любой, описанной ниже процедуре или функции. Вот пример: допустим, нам много раз придется проверять адрес, откуда запущена программа. Скажем, по этому адресу у нас лежит файл, в который мы то и дело сохраняем различные настройки программы, или считываем их. А в процессе работы программы текущая папка может быть изменена на другую. В результате, обратившись к файлу просто по имени, мы рискуем вызвать ошибку в программе. Чтобы этого избежать, нужно полностью указывать и адрес файла, и его имя. В этом разделе глобальных переменных мы можем создать собственную переменную: var fMain: TfMain; Adress: String; //для адреса, откуда запущена программа Затем выделить главную форму, перейти на вкладку Events (События) и двойным щелчком мыши сгенерировать событие OnCreate, которое происходит однажды, при создании формы, или же OnActivate, которое происходит, когда форма становится активной. В этом событии прописать такую строку: Adress := ExtractFilePath(Application.ExeName); Переменную adres в процедуре описывать не нужно – она глобальная, и мы ее уже описали. Функция Application.ExeName вернет полное имя файла нашей программы, а функция ExtractFilePath() 61 вычленит из этого имени адрес, и вернет его. В дальнейшем, в любой процедуре или функции модуля, где будет необходимо обратиться к файлу, например, myparam.ini, который находится там же, откуда запущена программа, достаточно указать ... Adress + 'myparam.ini' ... и мы получим имя файла вместе с полным адресом, откуда бы программа ни была запущена. Далее идет раздел implementation. Это раздел реализации. Он содержит сами процедуры и функции, которые поддерживают работу этой формы. Кроме того, в этом разделе также может присутствовать директива uses, в которой объявлены модули, подключенные к данной форме. Данный раздел необязателен – если в вашем проекте только одна форма, то раздела не будет вовсе. Если есть еще одна форма, как в нашем примере – форма fAbout, модуль которой называется About, то этот раздел добавляется автоматически, как только вы воспользуетесь командой File -> Use Unit. Сразу под ключевым словом implementation у нас есть строка uses About; которая говорит о том, что к данной форме подключен модуль About. Можно модули добавлять и вручную, однако лучше все-таки делать это с помощью команды меню – так вы будете гарантированы от возможных ошибок. Однако если вы создали ошибочную связь с другой формой, то удалить эту связь можно вручную. Далее вы видите такую строку, выделенную зеленым цветом: {$R *.dfm} Это не комментарий, как можно было бы подумать, а директива компилятору, что в процессе компиляции следует использовать файл описания формы. Файлы, которые составляют проект, мы рассмотрим чуть позже. После этой директивы, собственно, и начинается работа программиста. Далее идут процедуры и функции, сгенерированные для обработки событий компонентов формы. Сюда же программист может добавить собственные процедуры и функции, однако не забывайте правила – чтобы воспользоваться процедурой или функцией, она должна быть описана выше того места, где мы хотим ее вызвать! Самой последней строкой в модуле обязательно должна значится завершающая модуль команда end с точкой. Точка указывает, что модуль завершен, после нее других строк быть не должно. 62 Закройте этот проект, сохранять его не нужно, команда ShowMessage, которую мы дали для примера, в программе не нужна. Создайте новый проект и перейдите в редактор кода. Сразу после директивы компилятора вы видите строку: end. Обратите внимание, что слова begin перед ней нет, и быть не должно! Это – единственное исключение из правила скобок begin..end. Собственно, это даже не исключение, потому что эта end не является закрывающей скобкой, это завершение модуля. Теперь, если мы будем устанавливать на форму компоненты, и обрабатывать для них события, будут формироваться процедуры между директивой компилятору и завершающей end. Файлы проекта. Мы уже говорили, что проект состоит из множества файлов. Откройте папку с проектом MyNotebook, и посмотрите, какие файлы там есть. В таблице 5 приведены описания файлов, составляющих проект, и их расширений: Описание содержит основные файлы проекта. Иногда приходится использовать в проекте и другие файлы, и мы будем их изучать, когда придет время применять их на практике. Таблица 5. Описание файлов проекта Расширение *.cfg *.dof *.dpr *.res *.pas *.dfm *.ddp Описание файла Этот файл содержит конфигурацию всего проекта, поэтому он один, и имеет имя, как у проекта. Редактировать этот файл вручную не стоит, если не хотите испортить проект. Этот файл содержит опции проекта – настройки, версию Delphi, версию проекта, используемые модули и многое другое. Редактировать этот файл вручную нельзя. Это сам проект. Это текстовый файл с описанием используемых форм и модулей, а также процедура инициализации программы. Редактировать такой файл вручную можно, но только опытным программистам, которые точно знают, что они делают, и для чего это нужно. Даже им это бывает необходимо очень редко, поэтому лучше файл не трогать. Однако можно загрузить проект, просто щелкнув по нему дважды, либо поставить на него курсор и нажать Enter. Если Delphi не загружена, она загрузится и загрузит проект. Файл ресурсов проекта, таких как иконки, курсоры и так далее. Модуль формы. Этот файл мы видим в редакторе кода, его мы редактируем. Таких файлов имеется по одному на каждую форму проекта. Это файлы с информацией о форме. Тут содержатся описания используемых компонентов и их настройки. Это вспомогательные файлы модуля, они генерируются автоматически. 63 *.dcu *.exe *.~* Откомпилированный файл модуля. Фактически, та же программа, но только для одного модуля. Работать самостоятельно она не может. Готовая программа. Компилятор собирает все *.dcu в единый исполняемый файл. Если вы в проекте не использовали других файлов, таких как базы данных, например, то этот файл единственный, который нужен. При распространении программы отдавайте пользователю только его. Однако это не значит, что все остальные файлы можно удалить. Ведь в них ваш проект! В будущем, возможно, вам понадобится процедура или функция, аналогичная той, которую вы использовали в проекте. Чтобы заново не изобретать велосипед, просто откройте старый проект и посмотрите, как вы реализовали эту функцию. Скопируйте ее и вставьте в новый проект, если нужно. Файлы, расширение которых начинается с тильды – это резервные копии модулей и форм. Когда вы производите изменения и сохраняете проект, появляется такой файл. Это предыдущее сохранение. Если вы сохранили проект, и увидели, что ошиблись – его можно восстановить из этого файла. Однако лучше всего делать архивные копии проекта перед каждым серьезным изменением программы. 64 Литература Основная литература: 1. Белладжио Д. Разработка программного обеспечения: управление изменениями / Д. Белладжио, Т. Миллиган. – М.: ДМК Пресс, 2009. -384с. 2. Дейтел Х.М. Операционные системы в 2- томах. Том 2: Распределенные системы, сети, безопасность / Х.М. Дейтел, П.Д. Дейтел, Д.Р. Чофнес. - 3-е изд. – М.: БИНОМ, 2013. – 704с. Дополнительная литература: 1. Культин Н. Основы программирования в Delphi XE. – СПБ.: БХВ-ПЕТЕРБУРГ, 2011. – 416 с. 2. Осипов Д. Delphi XE2. – СПБ.: БХВ-ПЕТЕРБУРГ, 2012. – 912 с. 3. Осипов Д.Л. Базы данных и Delphi. Теория и практика. – СПБ.: БХВ-ПЕТЕРБУРГ, 2011. – 752 с. 4. Рубанцев В. Большой самоучитель Delphi XE3. Учебное пособие, - М: Я+R, 2012. – 1274 с. 5. Тейксейра Стив, Пачеко Ксавье. Delphi 5. Руководство разработчика. Том 1 и 2. - М.: Издательский дом Вильямс, 2000. 6. Фленов М.Е. Библия Delphi. - 3-е издание. – СПБ.: БХВПЕТЕРБУРГ, 2011. – 686 с. 65