Уроки по Delphi

         

Begin


C:='A'; // в переменную С заносим символ А

B:=Ord(C); // получаем значение символа А равное 65

B:=100; // В переменную В заносим 100

C:=Chr(B); // получаем символ d



Булевый тип.


Здесь можно сказать, что переменные, имеющие булевый тип, могут принимать два возможных значений. Это true (истинно) или false (ложно). Объявляется он как тип Boolean.

С уважением, ведущий уроков Semen semen@krovatka.net



Целочисленный тип.


Здесь переменные могут содержать целые числа, не имеющие дробной части



Тип

Значения

Объем

Shortint

–128..127

8-бит со знаком

Smallint

–32768..32767

16-бит со знаком

Integer

–2147483648.. 2147483647

32-бит со знаком

Longint

–2147483648.. 2147483647

32-бит со знаком

Int64

–2^63..2^63–1

64-бит со знаком

Byte

0..255

8-бит без знака

Word

0..65535

16-бит без знака

Longword

0..4294967295

32-бит без знака

Cardinal

0..4294967295

32-бит без знака

Здесь и далее, число после знака ^ означает степень. Тем не менее, это правило нельзя применять в программе.

Среди всего многообразия предложенных данных рекомендую пользоваться типом Integer, который кроме всего имеет логический тип представления целых чисел.



Часть урока для новичков


Здравствуйте. В этом уроке мы с вами немного расширим наши знания, напишем простую рабочую программу.

Запускаем для начала Delphi.

Напишем простую программу реакции нажатия на кнопку. Как вы уже заметили из этих нескольких запусков Delphi, сразу открывается новый пустой проект. Нам предлагается начать новый проект.

Как вы помните, проектируемая форма или окно, которое будет видеть пользователь при запуске программы на выполнение, по умолчанию называется Form1. Перед расположением на ней кнопок, давайте его назовем как-нибудь по-русски. Например: "Это моя программа!". Это можно сделать в инспекторе объектов (Object Inspector). Там отображаются свойства выбранных компонентов на форме. Поскольку на форме ничего пока нет, то выбранной считается сама форма Form1 (выбранный элемент можно увидеть в самом верху инспектора объектов в ниспадающем списке). Меняем заголовок проектируемой формы. Свойство Caption

на страничке свойств Properties. Пишем в редактируемом поле правее фразу заголовка. Во время ввода названия обратите внимание – набираемый текст немедленно в точности повторяется на форме. Об этом и говорилось в прошлом уроке. Мы с вами видим результат своей работы еще до запуска программы на выполнение.

Если во время работы вас стесняют размеры окон инспектора объектов, проектируемой формы, редактора кода или они наоборот, выглядят великовато, поменяйте их размеры, отпозиционируйте в удобное место.

Далее поместим на нашу проектируемую форму три стандартные кнопки. Выбираем на листообразной палитре компонентов компонент под названием Button одинарным щелчком мышки. Вот как от выглядит

Примечание: при подведении мышки на палитру компонентов выскакивает всплывающая подсказка, объясняющая пользователю ее название.

Итак, мы щелкнули по этому компоненту и он остался нажатым. Теперь необходимо щелкнуть на нашей форме, где мы хотели бы эту кнопку оставить. Повторите эту операцию еще два раза, пока на форме не окажется три кнопки Button1, Button2, Button3. Попробуйте подвигать эти кнопки по форме при нажатой левой клавишей мышки. Установите их в произвольном порядке, но чтобы они не мешали друг другу.




Теперь назовем эти кнопки по их дальнейшему функциональному назначению. Щелкаем один раз (!) по кнопке Button1. Она оказывается выделенной. В инспекторе объектов моментально отображаются все доступные свойства для этой кнопки. Меняем название кнопки, как вы догадываетесь, тоже сменой свойства Caption. Пишем: "Свернуть". Далее выбираем кнопку Button2. Переименовываем ее в "Белая". Повторим операцию смены названия для третьей кнопки "Закрыть".

Внешний вид программы готов. Как вы убедились – нет ничего сложного в оформлении внешнего вида проектируемой формы-окна. Пока что ваши действия в написании программы можно сравнить с художником, который мазками своей кисти постепенно вырисовывает картину. Да, но эту картину можно в любой момент подкорректировать.

Теперь немного поработаем руками и головой.

Задействуем последний нетронутый нами элемент Delphi – редактор кода. Как можно догадываться, и как упоминалось в прошлом уроке, тут происходит ручной набор некого программного кода, именуемого языком программирования Object Pascal. Но не стоит сразу его пугаться. Это довольно несложный в изучении язык, тем более, что для его достижения, Delphi предоставляет прекрасные возможности. Минимум набора текста, дополнение кода, шаблоны кода, контекстный список параметров. Все это намного упрощает жизнь программисту.

Для начала рассмотрим поподробнее такое понятие, как реакции на события.

Реакция на события. Что же это такое? Не буду описывать всех тонкостей работы операционной системы Windows, расскажу лишь на пальцах. Допустим, вы запустили любую программу на выполнение. Сами того не зная, вы породили кучу событий, передаваемых этому приложению. Во первых до непосредственного создания любого окна в памяти, Windows посылает сообщение приложению: "окно сейчас будет создано". Или такие: "Окно сейчас будет активизировано", "Окно сейчас отобразится". Но на простом примере, непосредственно касающегося нас, скажу, когда вы нажимаете на любую кнопку, Windows посылает сообщение приложению: "По такой-то кнопке щелкнули". И если программист использовал это сообщение и написал реакцию на него, то программа выполнит ее.



Напишем реакцию нажатия на кнопку Button3

или "Закрыть". Дважды щелкаем по этой кнопке. Delphi автоматически подготавливает редактор кода для минимизации ввода данных с клавиатуры, набирая за нас заголовок процедуры реакции нажатия на кнопку. Вот он:

procedure

TForm1.Button3Click(Sender: TObject);

begin

end;

Указатель (курсор) находится между словом begin

и end. Именно там и напишем команду программного закрытия окна, аналогично действию системной кнопки в заголовке окна. Поскольку это окно единственное и главное, то будет закрыта и вся программа.

Если действие касается окна, а его имя Form1, то указываем ссылку на это окно. Имя, но не текст, который оно отображает. Имя – это свойство Name.

Набираем вручную Form1. Теперь надо добраться до вложенных в него свойств и процедур. Для этого ставим точку. Через несколько секунд Delphi Выводит для нас дополнение кода.

Это подобие списка доступных констант, переменных, функций для данного объекта (Form1). Набираем Close. Обратите внимание, дополнение кода помогает в наборе, и указывает на существующую функцию, выбрав в списке слово Close. Если этот случай касается сравнительно длинных названий, то выбрать необходимое можно в списке стрелками перемещения указателя или мышкой. Завершение ввода из списка дополнения кода осуществляется нажатием на кнопку Enter, точка, точка с запятой, пробел, открывающаяся скобка (в зависимости от ситуации). Мы поставим точку с запятой. Итак наше описание реакции на нажатия на кнопку "Закрыть" выглядит следующим образом:

Form1.Close;

Если нажать на эту кнопку, в момент работы программы конечно, это окно закроется. Напишем реакции на нажатия на другие кнопки Button1

и Button2.

Рассмотрим еще один компонент Application. Это особый компонент. Его свойства недоступны для инспектора объектов потому, что он доступен только во время запуска приложения на выполнение и следовательно его нет на палитре компонентов. Application – это ссылка (или переменная) на ваше работающее приложение. Delphi автоматически создает данный компонент в связи с его обязательным присутствием.



Первая наша кнопка "Свернуть" или Button1

должна осуществлять свертывание проекта в панель задач. Поэтому пишем реакцию на ее нажатие. Дважды щелкаем по ней. В редакторе кода пишем, используя знания написания команд предыдущего случая.

Application.Minimize;

Для кнопки "Развернуть" реакция нажатия выглядит как:

Form1.Color:=clWhite;

Примечание: обратите внимание на точку с запятой в конце строки. Ее присутствие обязательно и она означает окончание команды. Обратите внимание на знак присваивания := (двоеточие и знак равно). Этот знак изначает, что свойству или переменной слева присваивается значение, указанное справа.

Что же произойдет при нажатии на эту кнопку. Окно программы окрасится в белый цвет. Для этого в свойство Color

компонента программы Form1

заносится константа белого цвета. По желанию можно поэкспериментировать с другими цветами, поставив в форму еще несколько кнопок и задавая цвет при нажатия на них clBlack, clNavy и т.п. (перечень всех доступных цветов см. свойство Color в Object Inspector, и не забудьте для этого выбрать форму Form1

одинарным щелчком).

Если нажать на кнопку "Свернуть", то программа свернется в панель задач.

Теперь запустим программу на выполнение кнопкой F9. Теперь можно видеть не только оформленную внешне программу, то и любоваться ее некоторыми простыми действиями.

Не забудьте сохранить проект на диске "Save All", как это было сказано в прошлом уроке, в отдельный каталог. Например "Урок2" или "Lesson2". Таким образом вы всегда будите знать где находится ваша программа, написанная с помощью того или иного урока.


Часть урока для новичков


Здравствуйте. На этом занятии мы немного вспомним темы прошлых уроков и расширим наши знания.

Итак, как вы уже знаете, язык который мы изучаем называется Object Pascal. И это значит, что вся работа сводится на управление этими самыми объектами.

Каждый компонент, который вы помещаете на форму, имеет свое отражение в окне Инспектора Объектов (Object Inspector). Как вы помните, Object Inspector

имеет две “странички” - “Properties” (Свойства) и “Events” (События). Создание программы в Delphi сводится к “нанесению” компонентов на форму (которая, кстати, также является компонентом) и настройке взаимодействия между ними путем:

1. изменения значения свойств этих компонентов.

2. написания адекватных реакций на события.

Как Вы уже успели, наверное, заметить, свойство является важным атрибутом компонента. Для пользователя (программиста) свойство выглядит как простое поле какой-либо структуры, содержащее некоторое значение. Однако, в отличие от “просто” поля, любое изменение значения некоторого свойства любого компонента сразу же приводит к изменению визуального представления этого компонента. Свойства служат двум главным целям. Во-первых, они определяют внешний вид формы или компонента. А во-вторых, свойства определяет поведение формы или компонента.

Существует несколько типов свойств, в зависимости от их “природы”, т.е. внутреннего устройства.

·

Простые свойства - это те, значения которых являются числами или строками. Например, свойства Left и Top принимают целые значения, определяющие положение левого верхнего угла компонента или формы. Свойства Caption и Name (из примера прошлого урока) представляют собой строки и определяют заголовок и имя компонента или формы.

·         Перечислимые свойства - это те, которые могут принимать значения из предопределенного набора (списка). Простейший пример - это свойство типа Boolean, которое может принимать значения True или False

(истинно или ложно). Например свойство Enabled




для кнопки Button

будет означать, может ли пользователь или нет нажать на нее. Если установлено False, то кнопка становится серой, недоступной. Т.е. свойство кнопки Enabled

может принитать только два значения и не более.

·         Вложенные свойства - это те, которые поддерживают вложенные значения (или объекты). Object Inspector изображает знак “+” слева от названия таких свойств. Имеется два вида таких свойств: множества

и комбинированные значения. Object Inspector изображает множества в квадратных скобках. Если множество пусто, оно отображается как []. Установки для вложенных свойств вида “множество” обычно имеют значения типа Boolean. Наиболее распространенным примером такого свойства является свойство Style с вложенным множеством булевых значений. Комбинированные значения отображаются в Инспекторе Объектов как коллекция некоторых величин, каждый со своим типом данных. Некоторые свойства, например, Font, для изменения своих значений имеют возможность вызвать диалоговое окно. Для этого достаточно щелкнуть маленькую кнопку с тремя точками в правой части строки Инспектора Объектов, показывающей данное свойство.

Delphi позволяет легко манипулировать свойствами компонента как в режиме проектирования, так и в режиме выполнения программы соответственно программно.

В режиме проектирования манипулирование свойствами осуществляется с помощью Дизайнера Форм (Forms Designer

или как говорилось выше проектировщик форм) или, как Вы уже видели, на страничке “Properties” Инспектора Объектов. Например, для того чтобы изменить свойства Height (высоту) и Width (ширину) кнопки, достаточно “зацепить” мышкой за любой ее угол и раздвинуть до нужного представления. Того же результата можно добиться, просто подставив новые значения свойств Height и Width в окне Object Inspector.

С другой стороны, в режиме выполнения пользователь (программист) имеет возможность не только манипулировать всеми свойствами, отображаемыми в Инспекторе Объектов, но и управлять более обширным их списком. В следующем разделе мы рассмотрим, как это делается.


Часть урока для новичков


Перед непосредственным написанием программ необходимо усвоить правилами работы над разрабатываемым проектом.



Часть урока для новичков


Как уже упоминалось в первых уроках, основой Delphi есть язык программирования Object Pascal. В своей основе он содержит так знакомый всему миру и очень популярный язык Pascal. Разумеется, для того, чтобы довольно быстро и без особых проблем изучить Delphi, необходимо знать этот язык. Для начинающих рекомендуется ознакомиться с ним. Потребуется знание элементарных понятий синтаксиса, типов данных, структур модулей программ.

Для непосвященных в азы программирования на Паскале постараюсь в нескольких уроках выдать кратко те самые основы.

Итак, основой любого языка программирования есть команда присваивания и выглядит как двоеточие и знак равенства :=

Здесь сразу нужно оговориться, что знак присваивания указывает на внесение указанной переменной некоторого значения, например:

x := 10;

Где x - переменная.

Переменная это указатель в память, где хранятся данные определенного формата. Подробнее переменные мы рассмотрим после следующего урока.

Каждый оконный проект в Delphi имеет один текст программы и как минимум один модуль.

Что же такое программа. Она храниться на диске с названием заголовка программы и расширением DPR. Вот ее архитектура:



Часть урока для новичков


В прошлом уроке мы рассматривали структуру программы и модулей. Они состоят из заголовка и программного блока.

Процедуры и функции представляют собой кирпичики, дополнительные блоки, содержащиеся в программном блоке. И по своему строению очень напоминает модуль или программу. Тоже состоит из заголовка и блока.

Заголовок состоит из первого слова procudure

для процедуры и function

для функции. После этого слова следует идентификатор, по которому данная процедура или функция будет вызываться из другого места программы (возможно даже из этой же процедуры). После идентификатора могут следовать в скобках входящие или выходящие параметры. Заголовок функции завершается описанием ее типа.

После заголовка может следовать (по необходимости) описание внутренних типов, констант, переменных, меток. На эту тему мы поговорим с вами в следующем уроке.

Далее следует сам программный блок процедуры или функции, заключенный в операторные скобки begin и end.

Пример процедуры:

procedure MyProcedure (InData: Integer); // заголовок процедуры. InData - вносимый параметр

Var i1,i2,i3: Integer; // описание переменных в данной процедуре

begin

// текст программы

end;

Функция от процедуры отличается только тем, что кроме выполняемого программного блока может возвращать определенное значение (по окончанию сама принимает некоторое значение).

Пример функции:

function MyFunction (InData: Integer): Integer; // возвращаемый тип данных - Integer

Var i1,i2,i3: Integer;

begin

// текст программы

Result:= 1; // возвращаемое значение

end;

Для осмысления вышесказанного рассмотрим следующий пример.

function MyFunction (x: String): String;

begin

Result:= x+'рамма';

end;

procedure MyProcedure;

begin

ShowMessage(MyFunction('прог'));

end;

Допустим мы имеем некий программный код, который в определенный момент вызывает на выполнение процедуру MyProcedure. Порядок выполнения программы можно расписать следующим образом.

1. Вход в процедуру MyProcedure.




2. Вызывается функция MyFunction и управление передается в нее. При этом туда передается строковый параметр "прог".

3. В функции выполняется арифметическая операция со строкой. К вносимому параметру прибавляется строка "рамма" и все это передается в качестве возвращаемого параметра функции Result.

4. Возвращение в процедуру MyProcedure. Теперь MyFunction

принимает значение "программа" и это значение передается в качестве параметра в процедуру ShowMessage.

5. Процедура ShowMessage является стандартной (она описана в стандартном модуле, автоматически подключаемом изначально). Эта процедура производит вывод окошка с сообщением и кнопкой Ok.

6. После вывода окошка с текстом "программа" текущая позиция выполнения программы выходит из процедуры MyProcedure

в то место, откуда она была вызвана.

Написание всего текста кода проекта сводится к написанию программного блока внутри процедур и функций, с переходом друг к другу по мере необходимости.

Продолжаем понемногу писать текстовый редактор. Последний вариант редактирования можно скачать с прошлого урока здесь.

В почти каждом MDI-приложении есть меню "Окно" или "Window", в котором первыми пунктами стоят различного рода операции с позиционированием дочерних окон. Сделаем что-то подобное в нашем многооконном приложении.

Открываем проект на приложении в среде программирования Delphi. Комбинацией клавиш Shift+F12 выводим на экран главную форму MainForm.

Начиная с версии Delphi 4, на странице палитры компонентов Standart есть компонент TActionList. Его назначение, а также другие полезные компоненты можно узнать из урока№ 6. Устанавливаем этот компонент на проектировочную форму MainForm. Дважды щелкаем по нему мышкой или выбираем пункт "Action List Editor" из выпадающего меню по правой кнопке мыши. Открывается окно изменения действий. Нажимаем комбинацию клавиш Ctrl+Ins, или выбираем пункт "New Standart Action" из ниспадаюшего меню на желтой кнопки.



Появляется окно добавления стандартных действий, которые в последствии можно привязать к пунктам меню или кнопкам.

С помощью нажатой кнопки Shift или Ctrl выберите все стандартные действия, которые относятся к категории Window. Это и есть действия управлением состоянием окон. Нажимаем на кнопку Ok. Теперь вы видите, что в списке действий появилась категория Window (в левой части), в которой находятся все выбранные стандартные действия с окнами (в правой части).

По умолчанию все стандартные действия будут иметь названия на английском языке, поэтому будем из русифицировать вручную. Выбираем первый пункт списка действий Arrange1 и меняем свойство Caption на "Упорядочить значки". Здесь вы сразу заметите, что изначально это свойство имеет значение "&Arrange". Знак амперсанта означает, что следующий за ним символ будет подчеркнут. Это свойство полезно для быстрого выбора пункта данного меню с помощью горячей подчеркнутой клавиши, когда меню активизировано (подробнее можно узнать из справочной системы Windows). Вы сами можете назначать свои буквы быстрого доступа к пунктам меню самостоятельно. Скажу только, что за свою многолетнюю практику ни разу не встречал человека, который пользовался бы таким доступом в меню, поэтому в своих программах подчеркнутых букв не делаю. Но вы пожете не пренебрегать правилами хорошего тона в программировани и все делать по правилам.

Таким образом меняем свойства Caption для всех действий, согласно таблице


&Arrange


Упорядочить значки


&Cascade


Каскадом


C&lose


Закрыть


&Minimize All


Минимизировать все


Tile &Horizontally


Упорядочить по горизонтали


&Tile Vertically


Упорядочить по вертикали

Теперь можно закрыть окно с действиями. Вызываем проектировщик пунктов меню MainMenu1 для формы MainForm.

Удаляем все пункты из меню "Окна". Там из наших первых уроков присутствует только один пункт "Упорядочить". Дальше все довольно просто. Для первого пустого пункта меню изменяем свойство Action в Arrange1. Этот пункт меню сразу изменит свое название на "Упорядочить значки". В проектировщике меню сразу добавляется новый пункт. Изменяем его свойство Action на Cascade1.



И так далее. Добавляем в меню все стандартные действия с окнами.

Теперь сохраняйте проект и запускайте приложение на выполнение. Обратите внимание, что если дочерних окон нет, то эти пункты меню становятся серыми и недоступными для пользователя.

Еще раз отмечу прелесть визуального программирования. На этом примере вы убедились, что можно не писать ни строчки программного кода в редакторе, а добавлять необходимые компоненты, изменять некоторые свойства и в результате получается некоторое работоспособное приложение.

У нас остался незадействованным меню "Правка". Там содержатся три стандартных пункта "Вырезать", "Копировать" и "Вставить". Эти действия являются стандартными и содержатся в списке стандартных действий в ActionList1. Они находятся в категории Edit. Добавьте их в список действий самостоятельно. Не забудьте о русификации свойства Caption. В проектировщике меню измените свойство Action у пунктов правки на соответствующие пункты стандартных действий.

На этот урок все. Скачать вышерассмотренный пример можно отсюда.


Часть урока для продвинутых программистов


Речь в данном разделе пойдет о не совсем распространенном, но таком интересном, с точки зрения организации отчетов, компоненте F1Book (Formula One) из палитры компонентов ActiveX.

Данный компонент является подобием табличной многостраничной таблицы Excel, с формулами, сеткой, границы и заливка и т.п. Кроме того от совместим с файловым форматом Excel 5. Файлы такого формата можно считывать с диска, обрабатывать, записывать на диск. При работе с компонентом F1Book, можно создавать произвольные отчеты произвольной формы программно, или вручную в момент отображения самой таблицы. Присутствует функции вывода на принтер, задания области печати, полей страницы, масштаба. Большая схожесть с программой MS Excel.

Но существует и проблема. F1Book является VCI ActiveX объектом и требует регистрации в системе для его использования в пользовательских приложениях на другом компьютере. При инсталляции Delphi делает это автоматически. Устанавливает необходимые библиотеки в системный каталог, регистрирует в реестре. Если такую программу запустить на машине, где отсутствует Delphi, программа выдаст ошибку выполнения, наподобие "класс не зарегистрирован". Но остальная часть программы может и работать, несмотря на это, если эта таблица не находится в главном окне, и ее во время работы не открывать.

Что говорит хэлп этого компонента по этому поводу: "Вы можете отправить следующие файлы со своим приложением" и приводится список файлов библиотек. Но про регистацию компонента в реестре Windows не сказано. Если вы являетесь специалистом и можете самостоятельно зарегистрировать эти библиотеки – пожалуйста. Получится, такая себе обрезанная версия без возможности печати и программного редактирования. Можно, конечно обратиться за технической помощью на торговый сайт или сделать заказ по почте или телефону. Это не серьезно. Вот как эта Formula регистрируется. Повторяю, это производится на компьютере, где Delphi не установлен.

Необходимо скопировать в каталог %WINDOWS_DIR%\SYSTEM следующие файлы:

VCF132.OCX

MFCANS32.DLL

OC30.DLL

MSVCRT20.DLL

Эти файлы находятся в аналогичном каталоге на компьютере, где есть Delphi.

Далее запустить файл регистрации. Файл регистрации компонента F1Book можно скачать здесь.

Учтите, что этот файл создан для регистрации библиотек в системном виндоувском каталоге C:\WINDOWS\SYSTEM. Если у вас он звучит по другому, следует подкорректировать этот файл вручную, исправив соответствующие строки.

Идеальным случаем установки программы, с задействованным компонентом Formula One будет применение инсталляционной упаковочной программы типа Wise или InstallShield, куда можно упаковать все необходимые файлы программы и библиотеки компонента с последующей корректной регистрацией.

А кто не пробовал экспериментировать с этим компонентом – попробуйте. Не пожалеете.

С уважением, ведущий уроков Semen semen@krovatka.net




В прошлом уроке мы рассматривали компонент F1Book, который применяется для организации работы, наподобие программы MSExcel. В этом уроке мы остановимся на непосредственной работе с упомянутой программой.

Если у вас установлен Delphi 5, то его нововведением было появление закладки Servers на палитре компонентов. Но можно и без участия этих компонентов получать доступ к программам групп MS Office.

Рассмотрим пример организации отчета в Excel. Пример можно скачать здесь.

Для начала скажу, что если вы хотите создавать в Excel что-нибудь серьезное, то вам необходимо неплохо ореинтироваться на VBA, в число которых я не вхожу. Но важна сама идея.

Создаем OLE объект:

ExcelApplication := CreateOleObject ('Excel.Application');

где ExcelApplication - переменная типа

Variant.

Не забудьте объявить в разделе подключаемых модулей ComObj.

Далее вся работа сводится к посылке сообщений к созданному приложению Excel.

Данную методику можно применять как замену различным отчетам типа Report. Преимущества налицо.

С уважением, ведущий уроков Semen semen@krovatka.net



Часть урока для продвинутых программистов


Запуск других приложений из вашего

Существует две наиболее часто применяемых функций. WinExec и ShellExecute.

Функция WinExec

WinExec, оставлена для совместимости с ранними версиями Windows, но я рекомендую для обычного запуска программы с командной строкой использовать эту. У нее мало параметров запуска.

Используемый модуль – Windows.

Описание: WinExec (FileName: PChar; CmdShow: Cardinal) : Cardinal;

где: FileName – путь, имя запускаемого приложения, параметры командной строки. Все указывается в одной строке;

CmdShow – стиль окна. Показывает, в каком состоянии будет отображаться окно при запуске.

Параметры отображения окна CmdShow:

SW_HIDE

Запускаемое приложение делается невидимым

SW_MAXIMIZE

Расширяет окно на весь экран

SW_MINIMIZE

Запускаемое приложение минимизируется. После запуска активизируется окно высшего уровня, т.е. окно, откуда было запущено это приложение

SW_RESTORE

Делает окно таким, каким оно было запущено в последний раз

SW_SHOW

Отображает окно в своем текущем размере и позиции

Для не-Windows приложений определяет состояние окна файл PIF, если таковой имеется в наличии. Если необходимо закрывать окно после завершения работы такого приложения необходимо создать PIF ярлык к нему в том же каталоге с таким же названием и поставить галочку "Закрывать окно по завершению сеанса работы". Все дополнительные параметры запуска устанавливаются там же.

Возвращаемые значения функции WinExec:

31

нормальный запуск

0

системе не хватает памяти или ресурсов

ERROR_BAD_FORMAT

EXE файл поврежден или имеет неверный формат (Windows на такой файл говорит, что он не является приложением Win32)

ERROR_FILE_NOT_FOUND

файл не найден

ERROR_PATH_NOT_FOUND

путь к файлу задан неверно

Если имя выполняемого файла в параметре FileName не содержит пути директории, Windows производит поиск выполняемого файла в такой последовательности:

1. Каталог из которого приложение было запущено. Это рабочий каталог




2. Системный каталог Windows (обычно C:\WINDOWS\SYSTEM).

3. Каталог Windows.

4. Директории, указанные в переменной операционной среды в среде PATH. Узнать о каталогах этой переменной можно, введя PATH в командной строке сеанса MS-DOS.

Пример использования функции WinExec в программе:

WinExec ('command.com', SW_SHOW); // запускает сеанс MS-DOS

Функция ShellExecute

Функция ShellExecute не только запускает программы, а открывает, редактирует или печатает файл, с учетом зарегестрированного типа, а также открывает указанную папку проводником. Возвращает Handle ссылку на открытое окно.

Используемый модуль ShellAPI. Его нужно не забыть указать в разделе Uses.

Описание: ShellExecute (hWnd: HWND; Operation: PChar; FileName: PChar; Parametrs: PChar; Directory: PChar; CmdShow: Integer): HINST;

где: hWnd – Handle родительского окна, в который будут передаваться сообщения запускаемого приложения. Можно указывать Handle-ссылку окна вашего приложения.

Operation – производимая операция. Open

– открыть, print – напечатать, explore – открыть папку. По умолчанию open, если указать nil.

FileName – имя файла или документа, интернет ссылка, e-mail адрес.

Parametrs – параметры, передаваемые приложению в командной строке.

Directory – каталог по умолчанию.

CmdShow – стиль окна. Показывает, в каком состоянии будет отображаться окно при запуске. Подробно рассказано выше.

Вместо параметров Operation, Parametrs и Directory можно ставить nil. Они являются не обязательными параметрами для запуска.

В случае успешного запуска возвращает Handle окна, в случае неудачи возвращает код ошибки меньше или равное 32. Список возможных ошибок можно прочитать в справке по этой команде.

Пример использования команды ShellExecute в программе:

ShellExecute (Form1.Handle, nil, 'http://programming.dax.ru', nil, nil, SW_RESTORE); //запуск нашей странички

ShellExecute (Form1.Handle, nil, PChar (Application.ExeName), nil, nil, SW_RESTORE); //запуск второй копии этой программы

ShellExecute (Form1.Handle, nil, 'iexplore', 'http://programming.dax.ru', nil, SW_RESTORE); //запуск в отдельном окне

ShellExecute (Form1.Handle, nil, 'mailto:semen@krovatka.net?subject=delphi', nil, nil, SW_RESTORE); //написать мне письмо

ShellExecute (Form1.Handle, nil, 'c:\1.doc', nil, nil, SW_RESTORE); //открыть документ

С уважением, ведущий уроков Semen semen@krovatka.net


Часть урока для продвинутых программистов


Но эта часть урока никак нельзя назвать уроком для продвинутых программистов, речь в нем пойдет об известном многим окне-заставке, которое появляется при загрузке большинства программ. Может кому-то и пригодится.

Итак, зачем же это окно все-таки нужно? Допустим, вы пишите относительно большую программу, и соответственно ее процесс загрузки занимает весьма продолжительное время, в течениии которого пользователь на экране монитора ничего не видит. Сидит и думает, а запустил ли я эту программу или нет, и пробует снова. Это первая причина. Ну, а остальные - это возможность приукрасить внешний вид, придать солидность, марку фирмы (профессиональности) изготовителя.

Что же в себе содержит окно-заставка? Да все, что угодно. От бесполезной информации, до названия программы, версии продукта, лицензии и контактного телефона. Лучше всего все это дело организовать в прямоугольный рисунок с максимальным размером 640х480 (минимальное экранное разрешение).Такой пример вы видите выше.И этот рисунок надо разместить в отдельной форме.

Рассмотрим пример. Ваша программа имеет название PROJECT1, главное окно проекта называется FORM1, окно с заставкой FORM2.

У окна Form2 устанавливаем свойства Position в poScreenCenter (располагать это окно посередине экрана), свойство BorderStyle в bsNone (окно без заголовка). Дальше размещаем в этом окне компонент TImage. Загружаем в него изображение через свойство Picture и устанавливаем свойство для рисунка и окна AutoSize в true. Все, рисунок имеет размер всего окна, а размер окна автоматически подстраивается под размер рисунка.

Дальше открываем файл проекта DPR на редактирование из меню "Project" пунктом "View Source".

Там мы видим следующие строки:

Application.Initialize; // инициализация приложения

Application.CreateForm(TForm1, Form1); // создать главное окно

Application.CreateForm(TForm2, Form2); // создать окно-заставку

// дальше могут быть создано много окон в приложении

Application.Run; // запустить приложение




Нам необходимо создать и отображать окно- заставку еще до создания главного окна в памяти и продолжать ее отображение по появления главного окна. Но необходимо обмануть приложение, ведь главным окном является то, что было создано первым.

Вот пример:

Application.Initialize; // инициализация приложения

Form2 := TForm2.Create(Application); // создать окно-заставку

Form2.Show; // сделать окно видимым

Form2.Update; // перерисовать окно

//(поскольку приложение еще не запущено, это необходимо делать вручную)

Application.CreateForm(TForm1, Form1); // создать главное окно

// дальше создаются все остальные окна в вашей программе

Form2.Close; // закрыть окно-заставку

Application.Run; // запуск приложения

Если у вас программа изначально такая же маленькая, как эта, то окошко заставки мигнет и пользователь ничего не успеет прочитать. Кроме того, в наш век все постоянно увеличивающихся компьютерных скоростей самая навороченная программа может лет так через 10 запускаться за несколько секунд, что тоже может создать некоторое неудобство для программиста.

Для этого делаем так.

Во-первых, убираем строку Form2.Close; из файла проекта. Дальше устанавливаем свойство FormStyle окна Form2 в fsStayOnTop (окно будет всегда отображаться поверх всех, что позволит пользователю любоваться им даже после появления главного окна). Устанавливаем в окно Form2 компонент TTimer. Устанавливаем свойство Interval в минимальный промежуток видимости этого окна. Например 3000 - 3 секунды. Событие срабатывания таймера:


Часть урока для продвинутых программистов


Всегда хочется каким-то образом оформить свое приложение, чтобы оно выглядело по-новому и немного нестандартно.

Рассмотрим создание главного меню программы, аналогичное главному меню в главном окне среды программирования delphi.

1. Создаем обычное меню на компоненте TMainMenu, но за одним исключением. У окна формы необходимо очистить свойство Menu и мы сразу видим, что меню исчезает из проектировочной формы.

2. Устанавливаем в форму компонент TControlBar (страница Additional). Свойство Align для него в alTop.

3. Устанавливаем в ControlBar1 компонент TToolBar (страница Win32). Для него все вложенные свойства для EdgeBorders в false. Свойство Flat устанавливаем в true. Свойства ShowCaptions и AutoSize тоже в true.

4. Создаем в Toolbar1 столько кнопок, сколько у нас пунктов имеет меню. Для всех кнопок меняем свойства AutoSize и Grouped на true.

5. Для каждой кнопки меняем свойство MenuItem на соответствующий пункт меню ("Файл", "Правка", Окна" и т.д.)

Оформление меню окончено.

С помощью свойств DragKind (установить в dkDock) и DockSite для приемника перетаскиваемого можно получить интересные эффекты при перетаскивании меню и других Dock'табельных компонентов. Такой простой пример можно скачать отсюда. Он еще далек от совершенства, но важна сама идея.

С уважением, ведущий уроков Semen semen@krovatka.net



Действительный тип.


В переменных этого типа числа представляют данные, состоящие из целой и дробной части. Причем все это храниться в одной ячейке данных, что само собой дает некоторую погрешность. В таком случае можно посоветовать использовать переменную большей точности или отделять целые числа и хранить в отдельных переменных.

Если за начальное значение таких типов принять 0, то минимальное значение, которое приведет к изменению его величины можно считать порогом (или точностью).

Тип

Порог

Максимальное значение

Кол-во цифр в значении

Объем, байт

Real

2,9*10^-39

1,7*10^38

11-12

6

Single

1,5*10^-45

3,4*10^38

7-8

4

Double

5,0*10^-324

1,7*10^308

15-16

8

Extended

3,4*10^-4932

1,1*10^4932

19-20

10

Comp

1,0

9,2*10^18

19-20

8

Currency

0,0001

9,2*10^14

19-20

8

Последние два типа применяются для финансовых арифметических операций.

Тип Real оставлен для совместимости с ранними версиями Delphi и Pascal. Большинство программистов работают на компьютерах с процессорами 5 серии (встроенный сопроцессор) или выше, поэтому рекомендуется пользоваться переменными типа Double.



End;


Функция UpCase преобразует строчную букву в прописную. Но она работает только с символами английского алфавита.



End;


Как вы наверное заметили, имя файла, в котором храним переменные состояния окна, мы указали без пути, просто pos.ini. В этом случае файл создается и открывается в текущем каталоге. По умолчанию это каталог, из которого была запущена программа.

С уважением, ведущий уроков Semen semen@krovatka.net



If FontDialog.Execute then Edit.Font := FontDialog.Font;


Здесь показан пример изменения шрифта в компоненте Edit. Причем во время вызова диалога FontDialog

в нем изначально будет установлен шрифт, заданный в компоненте Edit.

Свойство Device указывает типы отображаемых шрифтов. fdBoth - все, fdPrinter - принтерные, fdScreen

- экранные.



If IOResult<>0 then Exit;


Read(f,i); //считать из файла значение

Form1.Top:=i; // верхнее положение окна

Read(f,i);

Form1.Left:=i; // левое положение окна

Read(f,i);

Form1.Width:=i; // ширина окна

Read(f,i);

Form1.Height:=i; // высота окна

CloseFile(f);




Write(f,Form1.Top);

Write(f,Form1.Left);

Write(f,Form1.Width);

Write(f,Form1.Height);

CloseFile(f);



If OpenDialog.Execute then Memo.Lines.LoadFromFile(OpenDialog.FileName);


Здесь показан пример загрузки текстового файла в компонент Memo (страница Standart), выбранного с помощью диалога открытия файла OpenDialog.

Текст заголовка диалога в свойстве Title. Фильтр, который будет отфильтровывать файлы по расширению указывается в свойстве Filter. Его целесообразно установить сразу во время разработки проекта, поскольку при этом открывается удобная для занесения значений таблица.

Свойство InitialDir

указывает программе каталог по умолчанию, который будет открыт в диалоговом окне. Если это свойство не задано, то берется каталог, из которого запущена программа.



If OpenDialog1.Execute then //


Как говорилось выше, знак // и последующий текст набирать необязательно. Он означает комментарий в тексте программы, который никаким образом не влияет на выполнение программы а полезен только для программиста. Комментарий в программе можно отделить либо двумя знаками деления (тогда отделяется только строка от этих знаков до конца) либо фигурными скобками {} (тогда отделяется в комментарий весь текст между ними).

Поскольку команды создания окна, задания заголовка считаются сложным материалом для начинающего программиста, то пока рассматривать подробно их не буду.

Функция Execute для компонента OpenDialog1 вызывает диалоговое окно открытия файла и возвращает значение исхода выбора. Если значение равно true (истинно), то файл был выбран и false (ложно) - пользователь нажал на "Отмена".

В последующих уроках мы будем продолжать писать эту программу, а пример на сегодняшний урок можно скачать здесь.



Кратко описание работы программы.


При создании главного окна, программа получает указание об отсутствии необходимости автоматического отображения главного окна после запуска.

Дальше, после создания всех окон (в нашем случае двух) становится видимым окно Form2. Если пользователь нажал на кнопку "Ok" или в поле ввода пароля нажал на клавишу Enter (!), то вызывается обработчик события OnClick, который занесет соответствующее значение в переменную результата работы этого окна и вызовет команду закрытия Close.

В процедуре обработки закрытия окна вначале идет проверка результата, правильности ввода пароля. Если одно и другое условие выполняется, то поступает команда Form1.Show, которая выведет главное окно, после которой команда Exit выведет обработчик события из процедуры, тем самым исключающая дальнейшее выполнение строк этой процедуры.

Если по каким либо причинам, все вышеуказанные условия не выполняются (пользователь закрыл окно кнопкой "Отмена", закрыл окно кнопкой "Закрыть" или нажал комбинацию клавиш Alt+F4, ввел неправильный пароль), то выполняется команда Termanate (терминатор, который убивает работающее приложение :).

В программе в качестве пароля вводится адрес нашего сайта. Если вы желаете указать другой пароль, можно изменить соответствующую строку в программе.

Скачать только что рассмотренный пример можно здесь.

С уважением, ведущий уроков Semen semen@krovatka.net



Модальные окна.


Все SDI-окна можно разделить по свойствам доступа друг к другу как модальные и немодальные. Они определяют, может пользователь или нет переключаться на другие окна. Когда модальное окно открыто, все другие открытые окна становятся недоступными. Пример можно увидеть практически из любой программы, вызвав пункт меню "About" ("О программе"). Отображаемое окно в приложении, как правило, не дает переключится на другое, пока не будет закрыто. Такое свойство иногда бывает очень полезным. Например, когда нужно чтобы пользователь ввел пароль, а затем получил доступ к определенному окну с данными, или окно фильтра данных, указав условия отбора, получает доступ к результату.

Итак, модальными или немодальными могут быть только SDI-окна.

С сегодняшнего урока мы с вами начинаем разрабатывать довольно сложный пример простого многооконного текстового редактора.



Оъявление. Автор уроков для начинающих


По вашему дополнительному пожеланию ссылка на автора идеи будет помещена в начале урока. В таком случае обязательно делайте соответствующую пометку и указывайте свое имя с обратным адресом в письме с предложением.

С уважением, ведущий уроков Semen semen@krovatka.net



Оъявление. Автор уроков для начинающих


По вашему дополнительному пожеланию ссылка на автора идеи будет помещена в начале урока. В таком случае обязательно делайте соответствующую пометку и указывайте свое имя с обратным адресом в письме с предложением.

С уважением, ведущий уроков Semen semen@krovatka.net



Перетаскивание файлов в приложение


Иногда очень полезно избавить пользователя от лишних операций при открытии файла. Он должен нажать на кнопку "Открыть", затем найти интересующий каталог, выбрать файл. Проще перетащить мышкой файл сразу в окно приложения. Рассмотрим пример перетаскивания Drag & Drop в окно произвольного текстового файла, который сразу же открывается в компоненте Memo1.

Простой пример текстового редактора, который рассматривается ниже, можно скачать здесь (3KB).

Для начала в разделе Uses необходимо подключить модуль ShellAPI.

В private области окна нужно вставить следующую строку:



Приложения MDI и приложения SDI.


Прежде чем устанавливать вид и свойства вашего приложения надо подумать о том, какой вид приложения вы желаете создать. Это во многом зависит от его функционального назначения. Можно выбирать между так называемыми MDI- или SDI-приложениями.

MDI – сокращенно от Multiple Document Interface (интерфейс для одновременной работы со многими документами), а SDI – от Single Document Interface (интерфейс для работы с одним документом). В MDI приложениях два или более окон могут быть активны одновременно. В SDI-приложениях это невозможно. Здесь в каждый момент времени может быть активным только одно окно.

MDI-приложения являются удобным средством для одновременного выведения на экран текста или данных, которые хранятся в различных файлах. Такую структуру построения окон можно использовать для редактирования текстов, открывая и выводя на экран одновременно несколько различных документов. С помощь этих приложений можно также производить табличные вычисления, обрабатывая несколько таблиц одновременно, перенося или сравнивая данные из одной в другую. Пример такой работы над файлами – программа MS Word. Здесь файлы текстового и графического формата открываются в отдельных окнах, находящихся внутри главного окна программы.

Различные открываемые документы в окнах имеют общее рабочее пространство, в пределах которого они отображаются, называемое родительским окном. Всегда в MDI-приложении родительское окно является главной формой приложения. Все внутренние формы, называемые дочерними окнами отображаются в пределах рабочего пространства родительского окна и не могут быть помещены за ее пределы. Даже при свертывании. При максимизации таких окон, они занимают все рабочее пространство родительского окна, оставаясь внутри его.

Родительское окно может быть в MDI-приложениях только одно, а дочерних окон может быть теоретически бесконечно.

Большинство MDI-приложений имеют меню "Window" ("Окно") со следующими пунктами: Cascade, Arrange Icons, Tile Horizontal, Tile Vertical. С их помощью пользователь может управлять дочерними окнами приложения. Обычно нижняя часть меню отделена и представляет список всех открытых окон. Это свойство весьма удобно для быстрого перехода к тому или иному редактируемому файлу.



Рассмотрим один очень полезный пример.


Рассмотрим один очень полезный пример. Нам надо еще до запуска программы предложить пользователю ввести некий пароль. Если пароль введен верно, то отображается главное окно проекта и пользователь может работать. Если пароль введен неверно, то программа немедленно завершает свою работу.
Запускаем delphi и создаем новый проект. В новом проекте окно Form1 будет главным окном проекта.
Для события создания окна OnCreate пишем:
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.ShowMainForm:=false; //не отображать главное окно приложения
end;
Выбираем пункт меню "New Form" из меню "File". Появляется новое окно Form2. Его мы будем использовать в качестве ввода пароля. Оформляем его для этого надлежащим образом. Пример вы видите на рисунке.

Уменьшаем окно так, чтобы оно по размерам было как у подобных окон.
Свойство BorderStyle устанавливаем в bsSingle. При этом размер окна во время работы программы будет постоянен.
Свойство BorderIcons. Подсвойства biMinimize и biMaximize устанавливаем в false. Это окончательно недопустит изменение состояния окна (свертывание, разворачивание на весь экран).
В свойстве Caption пишите любую на ваше усмотрения понятную фразу, например "Введите пароль".
Свойство Position в значение poDesktopCenter. При этом окно при любом экранном разрешение всегда изначально будет расположено посередине экрана.
На странице палитры компонентов Standart выбираем и устанавливаем в форму компонент TPanel. Убираем у него значение Caption, придаем свойству Align значение alTop и окончательно изменяем его размер на четь больше половины окна.
На странице Additional выбираем компонент TBitBtn и устанавливаем в форму две кнопки, ниже панели.
Для первой кнопки свойство Kind в bkOk, для второй bkCancel.
Для первой кнопки свойство Caption оставляем в для всех понятное значение Ok, для второй пишем "Отмена".
На странице Standart выбираем и устанавливаем на компонент Panel компонент TEdit.
Для компонента Edit1 меняем свойство PasswordChar на знак * (звездочка). Это принятый во всех программах символ ввода пароля. При наборе пароля вместо символов будет отображаться звездочки.


Для компонента Edit1 убираем значение Caption.
Для Form2 в свойстве Icon подбираете соответствующую иконку.
Мы только что оформили полностью готовое окно для ввода пароля. Я же могу предложить вашему внимание свой вариант, который более красочно оформлен.
Вы всегда можете оформлять свои приложения по своему. Не обязательно придерживаться каких-либо правил по оформлению программы, но тем не менее пользователь не должен теряться при виде ваших "фантазий" и должен четко представлять, что от него требуется.
Дальше делаем вызов окна ввода пароля на экран. Поскольку мы с вами убрали свойство вывода главного окна, то после запуска программы на экране не будет ничего. Нам же нужно вывести окно запроса пароля, и мы это делаем с помощью программы DPR. Вызываем ее на экран с помощью пункта меню "View Source" из меню
"Project". В строке до
Application.Run пишем:
Form2.Show;
Еще до запуска приложения, но после создания всех окон на экран выводится окно Form2.
Дальше переключаем редактор кода на модуль Unit2, вызываем проектируемое окно клавишей F12, в инспекторе объектов для компонента Form2 (проектируемая форма) создаем реакцию на событие закрытия окна OnClose и в самой процедуре пишем:
Procedure TForm2.FormClose (Sender: TObject; var Action: TCloseAction);
begin
If Form2.ModalResult=mrOk then //если пользователь нажал на кнопку Ok, то
If Edit1.Text='programming.dax.ru' then //если набран необходимый пароль, то
      begin
      Form1.Show; //показать главное окно
      Exit; //выйти из этой процедуры
      end;
Application.Terminate; {если все вышеуказанные условия не выполняются, то остановить программу}
end;
Для кнопки BitBtn1 реакция на событие OnClick
procedure TForm2.BitBtn1Click(Sender: TObject);
Begin
Form2.ModalResult:=mrOk; //результат работы этого окна
Close; //закрыть это окно
end;
Для кнопки BitBtn2 событие OnClick
procedure TForm2.BitBtn2Click(Sender: TObject);
begin
Close; //закрыть это окно
end;
Поскольку мы окно Form2 вызываем командой Show, а не ShowModal, то присваивание результата работы окна не приведет к автоматическому закрытию и вызову процедуры OnClose. Поэтому после этой команды следует команда закрытия окна Close.

Пример MDI-приложения.


1.

Запускаем Delphi. В меню "File" выбираем пункт "New Application".

2.   Переименовываем свойство имени формы Name из Form1 в MainForm.

3.   Устанавливаем свойство в инспекторе объектов FormStyle в fsMDIForm. Его можно выбрать из ниспадающего списка доступных свойств. Свойство означает, что данное окно будет родительским в MDI-приложении.

4.  

В палитре компонентов выбираем MainMenu.
Это второй компонент слева на вкладке Standart. Помещаем его на проектировщик формы Form1. Дважды щелкаем по нему. Появляется конструктор меню окна. Здесь и далее для того, чтобы вызвать конструктор для размещенного в форме компонента главного меню (MainMenu), необходимо или дважды кликнуть по нему мышкой или дважды кликнуть в Object Inspector по свойству Items. Далее, пользуясь свойством Caption задайте меню такой вид, как на рисунке. Примечание: выбрать изменяемый пункт можно мышкой, для изменения названия выберите в инспекторе объектов свойство Caption, для отделения секций меню линией используйте знак тире в свойстве Caption для нужного пункта меню. Создание меню очень простое. Вы сразу видите результат изменения, добавления или удаления пунктов в редакторе формы. Вы всегда можете пополнить пункты меню, используя дополнения, выделенные пунктирной рамкой. Всегда можно вставить или удалить пункт меню в нужное место. Для этого воспользуйтесь правой клавишей мыши. Выбрать интересующее действие можно из выпадающего меню.

Итак, содержание главного меню:

Файл

Правка

Окна

Помощь

Новый

Вырезать

Упорядочить

О программе

Открыть

Копировать

Сохранить

Вставить

Сохранить как…

Закрыть

-

Выход

5.   Выбираем пункт меню "Окна" и переименовываем его имя (идентификатор) Name в инспекторе объектов в WindowMenu.




6.   Выбираем мышкой форму MainForm. В Object Inspector в свойстве WindowMenu устанавливаем их ниспадающего списка пункт меню WindowMenu. Теперь во время работы приложения все дочерние окна будут списком отображаться в меню "Окна".
7.   Поскольку дочерних окон в этом приложении будет много и по внешнему виду они будут подобные, то нет смысла проектировать каждое окно отдельно. Спроектируем одно. Для помещения в проект новой формы выбираем из меню "File" пункт "New Form".
8.   Устанавливаем размеры окна Form2 меньшими главного родительского окна.
9.   Переименовываем свойство Name для этого окна из Form2 в ChildForm.
10.                    Устанавливаем свойство дочернего окна для MDI-приложения FormStyle в fsMDIChild.
11.                    Выбираем из палитры компонентов Memo
и устанавливаем его в дочернюю форму ChildForm.
12.                    Меняем свойство Align для этого компонента на alClient. Он немедленно расширяется на все доступное пространство окна.
13.                    Далее будем писать событие выбора пункта меню "Новый". При нажатии на него должно появляться дочернее окно. Поступаем следующим образом. Поскольку дочерних окон будет множество, то мы программа должна их автоматически при необходимости создавать. По умолчанию Delphi устанавливает автоматическое создание окна во время запуска приложения. Это избавляет программиста от написания некоторых команд. В данном случае мы все сделаем сами. Выбираем из меню "Project" пункт "Options…". На вкладке Forms снимаем установку "Auto-create forms" для окна ChildForm. Для этого выбираем его и перегоняем одинарной стрелкой в сторону "Available forms". Нажимаем кнопку OK.


14.                    Сохраняем проект на диске. Для этого проводником Windows создаем отдельную папку для хранения этого примера. В меню "File" выбираем пункт "Save All". Дальше появляются диалоги сохранения модулей окон и файл проекта. Модуль Unit1 родительского окна можно назвать, сохранив его под именем MainUnit. Модуль Unit2 дочернего окна приложения. можно сохранить под именем ChildUnit. Весь проект можно сохранить под именем MyEdit.
15.                    Теперь напишем в редакторе кода процедуру создания дочернего окна. Выбираем окно MainForm и дважды щелкаем по компоненту MainMenu1. Выбираем пункт меню "Новый". В Object Inspector переходим на вкладку событий Events. Дважды щелкаем по полю, правее OnClick. Или нажимаем Ctrl+Enter. Появляется готовый заголовок процедуры нажатия на пункт меню "Новый". Между begin и end пишем следующую строку:
TChildForm.Create(Self);
Поскольку в этом окне создается другое, то в переименованный модуль MainUnit необходимо подключить ChildUnit. Для этого выбираем из меню "File" пункт "Use Unit…" и указываем модуль ChildUnit. Нажимаем OK.
Все, запускаем программу на выполнение. Во время работы обратите внимание на список появившихся окон в меню "Окна", по которым удобно переключаться на необходимое окно.
В следующих уроках мы подробнее остановимся на написании этого примера. Если в этом уроке у вас что-то не получается, то пример на данном этапе разработки можно скачать здесь (2 KB). Если вы в последствии открываете свой или скаченный проект, не забывайте, что нужно открывать DPR файл.
 Часть урока для продвинутых программистов

Procedure TChildForm.FormCreate(Sender


Теперь пишем процедуру нажатия на меню "Сохранить как...". Для этого в главную форму MainForm устанавливаем компонент TSaveDialog

, находящейся на странице Dialogs палитры компонентов. Поскольку фильтр отображаемых файлов в диалоговых окнах открытия и сохранения файла одинаков, то копируем строчку свойства Filter для компонента OpenDialog1 в аналогичное свойство для компонента SaveDialog1. Дальше с помощью Object Inspector создаем процедуру реакции на нажатие кнопки "Сохранить как...".



Procedure TChildForm.Memo1Change(Sender


Тогда нам нужно убрать запрос на сохранение в случае "ручного" сохранения файла, когда пользователь сам выбрал необходимый пункт меню. Изменяем следующие процедуры сохранения файла.

Процедура "Открыть"

procedure TMainForm.N3Click(Sender: TObject);

begin

if OpenDialog1.Execute then

   begin

   ChildForm:=TChildForm.Create(Self);

   ChildForm.Memo1.Lines.LoadFromFile(OpenDialog1.FileName);

   ChildForm.Caption:=OpenDialog1.FileName;

   ChildForm.Memo1.Tag:=0; // добавить

   end;

end;

Процедура "Сохранить как..."

procedure TMainForm.N5Click(Sender: TObject);

begin

if MDIChildCount=0 then Exit;

SaveDialog1.Title:='Сохранить файл как...';

if SaveDialog1.Execute then

   begin

      (ActiveMDIChild as TChildForm).Memo1.Lines.SaveToFile(SaveDialog1.FileName);

      (ActiveMDIChild as TChildForm).Caption:=SaveDialog1.FileName;

      (ActiveMDIChild as TChildForm).Memo1.Tag:=0; // добавить

   end;

end;

Процедура "Сохранить"

procedure TMainForm.N4Click(Sender: TObject);

begin

if MDIChildCount=0 then Exit;

if (ActiveMDIChild as TChildForm).Caption='НОВЫЙ' then

N5Click(Sender) else

   begin // добавить

   (ActiveMDIChild as TChildForm).Memo1.Lines.SaveToFile((ActiveMDIChild as TChildForm).Caption);

   (ActiveMDIChild as TChildForm).Memo1.Tag:=0; // добавить

   end; // добавить

end;

Добавленные строки отделены комментарием "//добавить".

А теперь непосредственно создаем процедуру OnCloseQuery для окна ChildForm.

procedure TChildForm.FormCloseQuery(Sender: TObject;

var CanClose: Boolean);

Var Res:Integer; // переменная для хранения результата выбора действия

begin

if Memo1.Tag=0 then // если не сохранять, то 1, иначе - 2

   begin // 1:

   CanClose:=true; // можно закрыть окно




   end else

   begin // 2:

   Res:=Application.MessageBox('Вы желаете сохранить изменения?','Вопрос',MB_YESNOCANCEL + MB_ICONQUESTION);

   // вопрос на сохранение

   if Res=IDYES then // нажата кнопка "Да"

   begin

   ChildForm.BringToFront; // Расположить данное окно выше всех (сделать активным)

   MainForm.N4Click(Sender); // Вызвать процедуру "Сохранить"

   if Memo1.Tag=1 then CanClose:=false;

   // если пользователь все-таки не сохранил файл, то окно не закрывать

   end;

   if Res=IDNo then CanClose:=true; // если "Нет", то можно закрыть

   if Res=IDCANCEL then CanClose:=false; // если "Отмена", то не закрывать

   end;

end;

Находясь в редакторе кода в файле ChildUnit необходимо подключить модуль главной формы MainUnit через пункт меню "UseUnit".

Аналогом команды MessageBox в Delphi есть команда MessageDLG. Более подробную информацию как по этой, так и по другой можно прочитать в справочной системе. Старайтесь всегда при неизвестных командах пользоваться подсказкой в справочной системе. Надо, чтобы указатель (курсор) находился на интересующем вас слове, и нажать комбинацию клавиш Ctrl+F1.

Архив примера текстового редактора с сегодняшними изменениями вы можете забрать тут.

С уважением, ведущий уроков Semen semen@krovatka.net


Procedure TForm1.Button1Click(Sender


Набираем строку:

Button1.Width:=40;

Запускаем программу на выполнение (F9). Смотрим на результат своей работы.



Procedure TForm1.FormCreate(Sender: TObject); begin DragAcceptFiles(Handle, True); end;


С уважением, ведущий уроков Semen semen@krovatka.net



Procedure TForm1.FormShow(Sender


// окно-заставку. Если оно уже закрыто, то ничего не происходит

end;

Теперь пользователь может наблюдать одно и то же в момент каждого запуска.

Но и этого однообразия тоже можно избежать, загружая необходимый рисунок из файла. Тем более, всегда и всем рекомендую отделять все рисунки от исполняемого EXE файла. Загружаем растровый рисунок в компонент Image1 в момент создания окна.



Procedure TForm2.FormCreate(Sender


// не удалась, то выводится сообщение об ошибке

end;

end;

Этот готовый примерчик можно скачать здесь.

Далее скажу, что для действительно огромных программ с большим количеством компонентов можно организовать что-то наподобие процесса загрузки Corel, где отображено инициализации приложения, создания ядра системы и т.п. бесполезную информацию. Это освобождает пользователя от немного занудного лицезрения одной и той-же картинки, инстинктивно привлекая внимание к движущейся части. Пользователь как-бы совместно переживает весь процесс загрузки, осмысливается понять такие тяжело доступные понятия, как например "создание контейнера баз данных".

Для этого необходимо установить в форму компонент метки, например TLabel и в нужных местах файла проекта менять ее текст.

Form2.Label1.Caption:='создание главного окна';

Form2.Label1.Update;

Application.CreateForm(TForm1, Form1);

Form2.Label1.Caption:='создание окна Form3';

Form2.Label1.Update;

Application.CreateForm(TForm3, Form3);

// и т.д. и т.п.

Еще можно и ProgressBar привязать. Тогда вы получите действительно живую и изначально, еще до самого запуска, навороченную программу. Но как известно, важна не только обертка, но еще и начинка...

С уважением, ведущий уроков Semen semen@krovatka.net



Procedure TForm2.Timer1Timer(Sender


Эта часть программы имеет пользу только, если прошло три секунды, уже на экране есть главное окно и необходимо закрыть заставку. А если прошло более трех секунд и только тогда появилось главное окно, то заставка закрывается сразу. Для этого событие OnShow для главного окна Form1 содержит только одну строчку:



Procedure TMainForm.N4Click(Sender


Получилась немного сложноватые строки даже для специалиста. Но не пугайтесь этого. Все команды с параметрами можно мысленно разбить на части, что упрощает просмотр.

Если текст в текстовых редакторах меняется, то обычно при выходе программа делает запрос на сохранение измененных данных. Если ответить "Да", то файл сохраняется. Если файл новый и не был ни разу сохранен, то будет предложен диалог сохранения файла под именем, или проще "Сохранить как...". Если ответить "Нет", то файл (окно) закроется без сохранения. Если нажать "Отмена", то ничего с окном не произойдет и пользователь продолжит редактирование.

Для окон есть событие OnCloseQuery, которое следует применять именно в этих случаях. Оно вызывается именно тогда, когда пользователь пытается закрыть окно и в качестве вносимого параметра CanClose определяет, может ли продолжаться закрытие окна или нет.

У каждого компонента в Delphi есть свойство Tag. Оно имеет тип Integer и является свойством, которым пользуются только программисты. Tag в программе ни на что не влияет. По умолчанию принимает нулевое значение.

В нашей программе я предлагаю использовать его для флага состояния редактирования. Если текст изменяется, то он в последствии подлежит запросу на сохранение.

Если свойство Tag для компонента Memo1 установлен в 0, то файл закрывается без запросов, иначе - 1.

Событие OnChange для компонента Memo1:



Procedure TMainForm.N5Click(Sender


Строка (ActiveMDIChild as TChildForm) позволяет получить доступ из главного окна к активному дочернему. Эта строка равноценна ChildForm, только выполняется она только для активного окна.

После сохранения файла заголовок дочернего окна содержит полный путь и название редактируемого файла.

Делаем процедуру сохранения файла для пункта "Сохранить". Следует при этом помнить, что если файл новый и название не определено, то вместо этого необходимо вызывать процедуру "Сохранить как..."



Procedure WMDropFiles(var Msg


Процедура обработки этого сообщения будет выглядеть следующим образом:



Проект Delphi


Как уже говорилось в первом уроке, любой проект имеет, по крайней мере, шесть файлов, связанных с ним. Три из них относятся к управлению проектом из среды программирования и напрямую программистом, как правило, не меняются. Вот этот перечень файлов:

·

Главный файл проекта, изначально называется PROJECT1.DPR. Если необходимо переименовать название проекта, нужно перезаписать этот файл, выбрав в меню File команду Save Project As… При задании имени следует придерживаться правил задания имен в Object Pascal. Имя не должно содержать пробелов (используйте в таком случае знак подчеркивания), должно начинаться с буквы (цифры допустимы, но со второй позиции), не должно содержать других символов, кроме букв и цифр. Под каким именем вы сохраняете проект, под таким именем и будет создаваться исполняемый EXE файл, который вы всегда сможете самостоятельно переименовать.

·         Модуль программы, который автоматически появляется в начале работы Unit1. Файл называется UNIT1.PAS по умолчанию, но его можно назвать любым другим именем, вроде MAIN.PAS. Это делается аналогично переименованию названия проекта. Необходимо выбрать в меню File команду Save As… Сохраняется активный модуль в редакторе кода.

·         Файл главной формы, который по умолчанию называется UNIT1.DFM, используется для сохранения информации о внешнем виде формы (окна разрабатываемого приложения). При переименовании названия модуля автоматически меняет свое название.

·         Файл PROJECT1.RES изначально содержит иконку для проекта. Создается автоматически. Имеет одноименное название с названием проекта.

·         Файл PROJECT1.DSK содержит информацию о состоянии рабочего пространства. Состояние окон во время разработки приложения.

·         Исполняемый EXE файл, который создается в процессе компиляции проекта.




Разумеется, если сохранить проект под другим именем, то изменят название и файлы с расширением RES и DSK.

После компиляции программы получаются файлы с расширениями:

DCU - скомпилированные модули PAS для последующей быстрой компиляции некоторые файлы не перезаписываются

EXE - исполняемый файл

~PA, ~DP - backup файлы редактора. Автоматически создаются и содержат информацию, перед последним сохранением.

Коротко про пункты главного меню Delphi

Пункт меню “File”

Если нужно сохранить проект, то Вы выбираете пункт главного меню “File” (с помощью мышки или по клавише Alt+F). У вас, в зависимости от установленной версии, он может выглядеть по другому, но это не меняет картины в целом. Пункт меню “File” выглядит следующим образом:

Как вы можете видеть, здесь есть шесть секций, разбиваемых по их функциональному назначению.

·         Первая секция дает возможность управления проектом в целом. Создать новый… (проект, форму, модуль, отчет и т.д.). Создать новый проект. Создать новую форму (окно).

·         Вторая секция позволяет открывать с диска файлы проекта.

·         Третья позволяет сохранять открытые файлы на диске. Здесь можно закрыть активный файл в редакторе кода или закрыть весь проект.

·         Четвертая позволяет подключать в текущий модуль другие в открытом проекте.

·         Пятая управляет печатью.

·         Шестая секция - выход из Delphi.

Каждая строка пункта меню “File”, как и другого пункта, объяснена в справке. Выберите меню “File”, подведите под интересующий вас пункт и нажмите F1.

Большинство из пунктов первой секции очевидны. “New Application” начинает новый проект, “Open Project” открывает существующий проект и т.д. Вложенный список “Reopen” позволяет открыть файл или проект, который недавно открывался.



При создании нового модуля Delphi дает ему имя по-умолчанию. Вы можете изменить это имя на что-нибудь более предпочтительное, чтобы название подходило к функциональным принадлежностям данного модуля (например, MAIN.PAS) с помощью пункта для первого сохранения “Save“ или для последующего изменения названия “Save As…“.

“Save File” сохраняет только редактируемый файл, но не весь проект. Для сохранения всех файлов проекта пользуйтесь "Save All". Вообще всегда удобнее пользоваться пунктом меню сохранить все, чем сохранять файлы по отдельности, тем более, что сохраняются только измененные.

“Close” закрывает файл из окна редактора. Если файл был изменен произойдет запрос сохранений перед закрытием.

“Close All” закрывает все открытые файлы проекта.

Пункт меню “Edit”

“Edit” содержит команды “Undo” и “Redo”, которые могут быть очень полезны при работе в редакторе для устранения последствий при неправильных действиях, например, если случайно удален нужный фрагмент текста или удален компонент из проектировшика форм.

Команды “Cut”, “Copy”, “Paste” и “Delete” - как во всех остальных приложениях Windows, но их можно применять не только к тексту, но и к визуальным компонентам. Например, если необходимо размножить необходимый компонент на форме. Принцип действия первой и второй секции человеку, работающему в Windows, думаю ясен. Третья секция предназначена для работы с редактором формы. Не буду полность описывать ее содержание. Расскажу только о некоторых. “Bring to Front” - установить выбранный компонент на уровень выше; “Send to Back” - компонент на уровень ниже; “Align...” - выравнивание компонентов, совместное позиционирование; “Size” - точные размеры компонента; “Tab Order” - порядок перехода по элементам формы с помощью кнопки Tab.

Сохранение проекта

Как известно, чтобы в последствии открывать свой проект для дальнейшего корректирования и управления необходимо его сохранить на диске.

Первый шаг - создать поддиректорию для программы. Лучше всего создать директорию, где будут храниться все ваши программы и в ней - создать поддиректорию для данной конкретной программы. Например, вы можете создать директорию LESSONS и внутри нее директории, связанные с тем или иным уроком LES4, которые содержали бы программы, над которыми мы работали.

После создания поддиректории для хранения вашей программы желательно выбрать пункт меню “File”  “Save All”. Сохранить нужно будет два файла. Первый - модуль (unit), над которым Вы работали, второй - главный файл проекта. Здесь и далее сколько окон (форм) в вашем проекте, столько и модулей.

Открытие проекта

Для открытия проекта необходимо выбрать пункт меню файл "Open Project" и выбрать название интересующего вас проекта DPR. При этом открываются как сам проект, так и становятся доступными все привязанные к нему файлы.

Если вы хотите открыть для редактирования отдельный модуль, или модуль формы, необходимо выбрать пункт "Open" и выбрать файл PAS. В этом режиме будет доступно для редактирования только форма и модуль. Чтобы запустить это окно на выполнение сначала открывайте проект.


Program Project1; uses Forms


Заголовок программы

Список подключаемых модулей

Поключаемые файлы ресурсов

Текст программы

Файл программы состоит из заголовка и блока программы. Блок программы включает объявление подключаемых модулей (частей программы), подключаемые ресурсные файлы (иконки, указатели мыши, рисунки), текст программы (создание окон, запуск приложения на выполнение).

Вызвать файл программы на редактирование можно из меню "Project" пунктом "View Source". Во избежание появления ошибок, некорректной работы программы настоятельно не рекомендуется самостоятельно вручную редактировать этот файл. Все необходимые изменения производятся в Delphi автоматически.

Заголовок или название программы всегда должен совпадать с названием проекта. Если вам необходимо изменить название проекта, то следует пересохранить проект под другим именем.

Для того, чтобы разбить программу на несколько отдельных задач, которые можно редактировать по отдельности и применяются модули. В Delphi каждое отдельное проектируемое окно имеет свой модуль. Но их в программе может быть гораздо больше.

Структура модуля:



Символьный тип.


Символьный тип называется Char. Он занимает один байт в памяти и это значит, что может содержать 255 возможных значений символов, что соответствует стандартной кодировке ANSI.

Функция Ord(C) возвращает значение порядкового номера символа С в таблице кодировки. Возвращаемое значение имеет тип Byte.

Обратное преобразование осуществляется функцией Chr(B).

Пример такого преобразования при нажатии на кнопку Button1:



Сохранение данных в реестр. На


Для начала краткая информация о реестре. Проще говоря, реестр windows, эта огромная база данных, хранящая в себе всевозможные пользовательские и системные данные. Установленное оборудование, драйвера, установленные шрифты, установленные принтеры и многое другое записано в соответствующих разделах.

Как же все-таки увидеть данные реестра? Очень просто. Надо нажать на кнопку "Пуск", выбрать пункт "Выполнить" и ввести название программы REGEDIT, которая отображает реестр для чтения и редактирования. Эта программа находится в каталоге windows. Помните, что некорректное изменение некоторых системных ключей в реестре может привести к постоянным сбоям в системе, может привести даже к полному краху операционной системы, поэтому изменения и удаление следует производить очень осторожно.

Внешний вид этой программы состоит из двух панелей. Левая древовидная и правая, в которой отображаются параметры ключей. Ключ - это элемент реестра, который может содержать некоторые данные или содержать другие ключи. Это древовидная структура, которая хранит в себе вложенные параметры, объединенные общей тематикой. Не буду рассказывать назначение отдельных ключей, это может занять не одну книгу, скажу только, что программы, которые работают с реестром в качестве ini файла, автоматически записывают и читают данные из глобального ключа HKEY_CURRENT_USER. Он же дублируется в ключе HKEY_USERS\ИМЯ_ТЕКУЩЕГО_ПОЛЬЗОВАТЕЛЯ.

Дальше рассмотрим запись и чтение данных. Тут методика аналогична работе с ini файлами.

Для начала в разделе подключаемых модулей Uses нужно указать модуль Registry, который необходим для использования команд работы с реестром.

Объявление реестровой переменной

Var ПЕРЕМЕННАЯ:TRegIniFile;

Создание реестровой переменной, через которую будем читать и писать данные

ПЕРЕМЕННАЯ:=TRegIniFile.Create(НАЗВАНИЕ_КЛЮЧА);

Пример объявления, создания и удаления.

procedure TForm1.FormShow(Sender: TObject);

Var RegIniFile:TRegIniFile; // реестровая переменная

begin




RegIniFile:=TRegIniFile.Create('MySelfRegistryApplication'); // создание реестровой переменной

RegIniFile.Free; // уничтожение вручную созданного объекта

end;

Подробно о командах чтения и записи.

Чтение и запись целочисленного значения, типа integer:

RegIniFile.ReadInteger(СЕКЦИЯ,ПАРАМЕТР,ЗНАЧЕНИЕ_ПО_УМОЛЧАНИЮ);

RegIniFile.WriteInteger(СЕКЦИЯ,ПАРАМЕТР,ЗНАЧЕНИЕ);

Дальше аналогично вышерассмотренной команде следует чтение и запись:

двоичного значения ReadBool и WriteBool;

строкового значения ReadString и WriteString;

Если необходимо сохранить данные не в отдельной секции, а в ключе, то вместо параметра СЕКЦИЯ необходимо указать пустую строку, или две кавычки ''.

Сохранение остальных типов данных осуществляется не в секции, а непосредственно в ключе. Для этих команд при обращении к значениям нужно проверять их наличие. Например, если соответствующие параметры не были созданы, то их чтение приведет к ошибке. проверка их наличия производится командой

RegIniFile.ValueExists(ПАРАМЕТР)

Эта команда является функцией, возвращающей истинно (true) или ложно (false), и ее можно использовать в паре с чтением определенного параметра. Например, чтение числа с плавающей точкой:

if RegIniFile.ValueExists('MyFloat') then // если данный параметр существует, то

Edit2.Text:=FloatToStr(RegIniFile.ReadFloat('MyFloat')); // прочитать параметр

Пример.

В новом проекте помещаем в форму следующие компоненты:

· 2 компонента CheckBox

· компонент ComboBox, установите в свойстве Items некоторые строки.

· 2 компонента Edit

· компонент DateTimePicker (страница Win32 палитры компонентов)

Процедура OnShow для окна Form1

procedure TForm1.FormShow(Sender: TObject);

Var RegIniFile:TRegIniFile; // реестровый объект

begin

RegIniFile:=TRegIniFile.Create('MySelfRegistryApplication'); // создание реестровой переменной

Form1.Left:=RegIniFile.ReadInteger('Form1','Form1Left',Form1.Left); // левая граница окна

Form1.Top:=RegIniFile.ReadInteger('Form1','Form1Top',Form1.Top); // верхняя граница окна



Form1.Height:=RegIniFile.ReadInteger('Form1','Form1Height',Form1.Height); // высота окна

Form1.Width:=RegIniFile.ReadInteger('Form1','Form1Width',Form1.Width); // ширина окна

// Восстановление состояния компонентов

CheckBox1.Checked:=RegIniFile.ReadBool('Form1','CheckBox1Checked',CheckBox1.Checked);

CheckBox2.Checked:=RegIniFile.ReadBool('Form1','CheckBox2Checked',CheckBox2.Checked);

ComboBox1.ItemIndex:=RegIniFile.ReadInteger('Form1','ComboBox1ItemIndex',ComboBox1.ItemIndex);

Edit1.Text:=RegIniFile.ReadString('Form1','Edit1Text',Edit1.Text);

if RegIniFile.ValueExists('MyFloat') then // если такой параметр существует, то:

Edit2.Text:=FloatToStr(RegIniFile.ReadFloat('MyFloat')); // чтение числа с запятой

if RegIniFile.ValueExists('MyDate') then // если такой параметр существует, то:

DateTimePicker1.Date:=RegIniFile.ReadDate('MyDate'); // чтение даты

RegIniFile.Free; // уничтожение вручную созданного объекта

end;

Процедура OnClose для окна Form1

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

Var RegIniFile:TRegIniFile;

begin

RegIniFile:=TRegIniFile.Create('MySelfRegistryApplication');

RegIniFile.WriteInteger('Form1','Form1Left',Form1.Left);

RegIniFile.WriteInteger('Form1','Form1Top',Form1.Top);

RegIniFile.WriteInteger('Form1','Form1Height',Form1.Height);

RegIniFile.WriteInteger('Form1','Form1Width',Form1.Width);

// Сохранение состояния компонентов

RegIniFile.WriteBool('Form1','CheckBox1Checked',CheckBox1.Checked);

RegIniFile.WriteBool('Form1','CheckBox2Checked',CheckBox2.Checked);

RegIniFile.WriteInteger('Form1','ComboBox1ItemIndex',ComboBox1.ItemIndex);

RegIniFile.WriteString('Form1','Edit1Text',Edit1.Text);

RegIniFile.WriteFloat('MyFloat',StrToFloat(Edit2.Text));

RegIniFile.WriteDate('MyDate',DateTimePicker1.Date);

RegIniFile.Free;

end;

Обратите внимание, что в этом примере для записи состояния компонента Edit2 используется преобразование строковой величины в число. Если в этом компоненте будут недопустимые символы, то при закрытии окна будет выдаваться ошибка. Для разделения дробной от целой части (запятая), используйте соответствующий символ, установленный в конфигурации windows как символ разделителя.



После запуска и закрытия этой программы можно запустить редактор реестра и просмотреть результат записи данных. Они находятся в ключе HKEY_CURRENT_USER, подключе MySelfRegistryApplication, как было указано в программе. Данные состояния окна, компонентов CheckBox1, CheckBox2 и Edit1 находятся в секции Form1, остальные данные: число в компоненте Edit2, дата в компоненте DateTimePicker1 находятся непосредственно в ключе MySelfRegistryApplication.

Посмотреть на параметры, записанные вышерассмотренной программой автоматически при закрытии, можно на двух рисунках. В первом рисунке вы видите данные даты и дробного числа, во втором рисунке вы видите содержание секции Form1. Эта секция является одновременно подключом ключа MySelfRegistryApplication.

Удаление того или иного параметра приведет к сбросу сохраняемой величины и при запуске программы будет установлено начальное значение по умолчанию. Удаление всего ключа MySelfRegistryApplication приведет к сбросу всех данных нашей программы. Удалить параметр или ключ в редакторе реестра можно кнопкой Del или выбрав соответствующий пункт из выпадающего меню по правой кнопке мышки.

Программное удаление параметра осуществляется командой

RegIniFile.DeleteKey(СЕКЦИЯ, ПАРАМЕТР);

Этот пример можно забрать по ссылке тут.

Хочу еще сказать, что с помощью реестра можно сохранять кроме вышерассмотренных типов данных еще формат времени, значение денежной единицы, нетипизированные данные. Последний тип данных может представлять собой шестнадцатиричный код некоторых данных.



Некоторые команды для работы со строками

Вспомним, а для некоторых узнаем некоторые команды для работы со строками.

Строка является набором символов. Строковые переменные можно склеивать, разрезать, копировать, удалять символы.

Для склеивания нескольких строк можно воспользоваться знаком + как в арифметической операции сложения.

Копирование части строки в другую строку производится функцией Copy. Она возвращает результат копирования:

РЕЗУЛЬТАТ:=Copy(ИСХОДНАЯ_СТРОКА, ПЕРВЫЙ_СИМВОЛ, ДЛИНА_ЧАСТИ_СТРОКИ);



Удаления части в строке производится процедурой Delete:

Delete(СТРОКА,ПЕРВЫЙ_СИМВОЛ, ДЛИНА);

Для вставки части строки в другую применяем процедуру Insert

Insert(ВСТАВЛЯЕМАЯ_СТРОКА, РЕЗУЛЬТИРУЮЩАЯ_СТРОКА, НОМЕР_СИМВОЛА_ВСТАВКИ);

В любой момент можно узнать длину строки с помощью функции Length(СТРОКА), а установить длину строки можно процедурой SetLength(СТРОКА, НОВАЯ_ДЛИНА).

В pascal'е строковая переменная объявляется как тип String. Если объявляем такую переменную, значит мы будем ее использовать для работы со строками. Но в отличие от языка программирования pascal, в таких строках запрещен доступ к нулевому символу (в этом символе хранилось длина строки). Поскольку в операционной системе windows все строки имеют стандарт PChar (строки, заканчивающиеся символом #0), то тип String здесь оставлен для совместимости. Фактически длина строки String не ограничена 255 символами, как в pascal. Но применяя этот тип, вы незаметно для себя, применяете тип PChar. Все операции перевода одного типа в другой delphi производит автоматически.

Вот пример процедуры обработки строк.

procedure StringOper;

Var st1,st2,st3,st4:String; // объявление строковых переменных

i:integer; // целочисленная переменная

begin

st1:='это 1 строка';

st2:='это 2 строка';

st3:=st1+' '+st2; // результат 'это 1 строка это 2 строка'

st4:=Copy(st3,1,5); // копирование части строки. Результат 'это 1'

Delete(st4,1,4); // удаление части строки. Результат '1'

Insert(' строка',st4,2); // вставка части строки в строку st4. Результат '1 строка'

st4[1]:='2'; // изменение первого символа строки с 1 на 2

i:=Length(st4); // определение длины строки i=8

SetLength(st4,7); // установка новой длины строки. Результат '1 строк'

end;

С уважением, ведущий уроков Semen semen@krovatka.net


Страница Additional


Компоненты на этой странице предназначены для красочного оформления приложения, усовершенствованные компоненты из предыдущей страницы.



Страница Dialogs


На странице Dialogs размещаются стандартные диалоговые элементы Windows, как то окна открытия, сохранения файлов, окна выбора шрифта, принтера и т.п. Эти компоненты пользуются стандартным системным интерфейсом.



Страница Standart


Компоненты на этой странице представляют стандартные Windows-элементы (кнопки, списки, меню и т.п.)

TMainMenu позволяет вам поместить главное меню в окно. Такое меню присутствует в большинство программах. При помещении TMainMenu на проектируемую форму, оно выглядит в виде значка. Для того, чтобы добавить, изменить, удалить пункты меню используйте свойство этого компонента Items в Object Inspector. При этом вызывается окно построителя пунктов меню.



Страница System


На этой странице находится набор компонентов для доступа к системным ресурсам, таким как OLE, DDE.



Компоненты на странице Win32 обеспечивают




Компоненты на странице Win32 обеспечивают доступ к 32-битным элементам Windows (Windows95/NT).

Строковый тип.


В языке программирования Pascal максимальная длина строки String имела 255 символов. В Delphi оставлена такая строка, но называется она ShortString. Максимальная длина переменная String теперь ограничивается только размерами памяти. Данные в строковых, как и в символьных переменных, помещаются в кавычки, которые отделяют данные от команд программы.

Пример:



Свойство BorderIcons


Это свойство является вложенным. Т.е. если вы в инспекторе объектов нажмете на знак +, то откроется еще несколько подсвойств. Свойство BorderIcons имеет смысл устанавливать только в соответствующих значениях свойства BorderStyle.

biSystemMemu – указывает, отображать или нет иконку и кнопки управления окном.

biMinimize – отображать или нет кнопку управления "свернуть".

biMaximize – отображать или нет кнопку "развернуть" ("восстановить").

biHelp – отображать или нет кнопку "помощь".

Возможны два значения для этих свойств: true

(истинно) и false (ложно).



Свойство BorderStyle


bsDialog – У окна нет иконки. Отображена только кнопка управления "Закрыть". Размер окна постоянный. Такой тип окон применяется чаще всего в диалоговых окнах, например окно запроса на сохранения проекта, если вы пытаетесь выйти из delphi не сделав сохранения.

bsNone – У окна нет иконки, кнопок управления, заголовка. Размер окна постоянный. Закрыть такое окно можно только программно или с помощью комбинации клавиш Alt+F4. Этот тип окон применяется в заставке при запуске программы. На окне расположен компонент TImage, который содержит рисунок.

bsSingle – В окне присутствует иконка, есть заголовок. Кнопки управления свернуть, развернуть (восстановить), закрыть. Размер окна постоянный.

bsSizeable – Тип окна по умолчанию. Имеет все элементы, указанные в предыдущем значении свойства плюс изменение размеров окна.

bsSizeToolWin – Этот тип окна установлен в окне инспектора объектов. Такое окно не имеет иконки, есть заголовок, кнопка управления "закрыть". Можно изменять размер окна.

bsToolWindow – Аналогичный предыдущему значению, за исключением того, что нельзя изменять размеры окна.

Все вышеописанные типы свойства BorderStyle устанавливаются только для работающего приложения, т.е. мы видим изменения в оформлении окна только после запуска программы.



Свойство Position


poDefault – Windows само определяет размер и положение окна.

poDefaultPosOnly – произвольное положение окна.

poDefaultSizeOnly – только произвольный размер окна.

poDesigned – такой-же размер и положение, кокой был при разработке. Значение по умолчанию. Вам следует знать, что не на всех компьютерах установлено такое же экранное разрешение, какое есть у вас, и значит окна в программе будут находиться в совершенно другом месте.

poDesktopCenter – окно находится в центре экрана.

poScreenCenter – окно находится в центре экрана. Работает с многодисплейным режимом отображения.



Свойство WindowState


wsMaximized – Окно первоначально будет развернуто во весь экран.

wsMinimized – Окно первоначально будет свернуто в значок.

wsNormal – Окно имеет те же размеры, что и во время разработки. Значение по умолчанию.



TCalendar - календарь. Свойство Year, Month, Day (год, месяц, день) разбиты на три отдельные части.


С уважением, ведущий уроков Semen semen@krovatka.net



TPageScroller cодержит в себе


С уважением, ведущий уроков Semen semen@krovatka.net



Unit Unit1; interface uses Windows


Заголовок модуля

Заголовки доступных свойств

    подключаемые модули

    описание типа

    скрытый раздел свойств

    открытый раздел свойств

    описание переменной

Описание свойств

Подключаемые файлы ресурсов

Вся ваша ручная работа сводится в написание программного кода в автоматически создаваемых процедурах внутри раздела implementation. Весь текст модуля автоматически изменяется при соответствующем изменении состояния проекта. Например, если вы добавили или удалили из проектируемой формы кнопку или переименовываете свойство NAME для любого компонента.

Если переименовать форму Form1 в MainForm, добавить в не компонент Memo1, и задействовать событие OnChange (изменение текста внутри этого компонента) то программный код изменится следующим образом:




// из объявленного класса

implementation

{$R *.DFM}

procedure

TMainForm.Memo1Change(Sender: TObject);

begin

// здесь следует текст реакции на событие изменения текста

// в компоненте Memo1

end;

end.

Примечание: после двойной дроби // следует комментарий.

Как правило программист не выходит за области implementation

раздела, а тут самостоятельно не создает процедуры. Программная оболочка делает это автоматически по мере необходимости и ликвидирует пустые лишние заголовки процедур при сохранении файла модуля.

Продолжим написание программы многооконного текстового редактора.

Напомню, что в пример был начат в уроке 5 и его можно скачать здесь. Это простое MDI-приложение, где все открываемые файлы в отдельных дочерних окнах размещаются внутри родительского окна (принцип можно увидеть на примере текстового редактора MS Word).

Сразу отмечу некоторые недостатки этой программы. Во первых, новое окно с полем для редактирования появляется уже с текстом "Memo1", во вторых, дочернее окно невозможно закрыть. При закрытии дочернего окна оно минимизируется.

Уже рассматривалось в первых уроках, что проектируемое приложение в момент запуска будет иметь одинаковый вид, свойства, какие были заданы на этапе разработки. Поэтому необходимо убрать все тестовые строки из компонента Memo1 во время разработки, чтобы они не отображались в запущенном приложении.

Для этого сначала отображаем проектируемую форму ChildForm на экран. Для этого нажимаем комбинацию клавиш Shift+F12 или выбираем пункт "Forms..." в меню "View". В появившемся окне выбираем эту самую форму и нажимаем Ok.

Выделяем компонент Memo1 в проектируемой форме одинарным щелчком мышки. В инспекторе объектов находим свойство этого компонента Lines. Изменяем это свойство, нажав на кнопку с троеточием в поле с TStrings. Появляется окно String List Editor, в котором надо убрать все буквы, оставив его пустым. Далее нажимаем Ok.

Теперь мы видим в проектировщике формы ChildForm, что компонент Memo1 не содержит никаких строк.




Дальше сделаем дочернее окно нормально-закрываемым. Для этого необходимо выделить компонент ChildForm (саму проектируемую форму). Но поскольку на этой форме расположен компонент Memo1, который имеет размер всей рабочей области формы, сделать визуально нам это не удастся. Поэтому выбираем прямо из инспектора объектов.

В верхней части Object Inspector есть ниспадающий список, в котором нужно выбрать ChildForm. Далее переходим на вкладку событий Events. Находим событие OnClose (закрытие окна) и дважды щелкаем по пустому полю правее. Создается готовый каркас процедуры закрытия окна с заголовком:

procedure TChildForm.FormClose(Sender: TObject; var Action: TCloseAction);

Здесь вносимый в процедуру параметр Action определяет поведение данной формы при закрытии. Он может принимать следующие значения:

caNone - форма не предпринимает никаких действий.

caHide - форма причется от глаз пользователя, но не уничтожается из памяти. Поскольку мы с вами "вручную" (программно) создаем дочернюю форму, то нам нужно ее при закрытии уничтожить из памяти.

caFree - форма закрывается и уничтожается из памяти.

caMinimize - форма минимизируется. Это значение для MDI-дочерних окон установлено по умолчанию.

Пишем внутри созданной процедуры:

Action := caFree;

Теперь запустите приложение на выполнение (F9) и посмотрите на ее работу.

Дальше организуем довольно сложный на первый взгляд процесс загрузки текстового файла. На самом деле сама загрузка файла делается одной строкой. Немного сложнее организовать выбор этого файла.

Итак, открывается файл при выборе пункта меню "Открыть", которое находится в главной форме. Отображаем проектируемую форму MainForm поверх всех окон (Shift+F12). Устанавливаем в форму компонент OpenDialog
. Он находится на палитре компонентов на странице Dialogs.

Назначение этого компонента следующее. Во всех текстовых редакторах, как и во многих других программах, прежде чем открыть какой-либо файл, вам предлагается выбрать его из диалогового окна открытия файла. Такое диалоговое окно вы только что установили в программу. Поскольку этот компонент является невизуальным, т.е. он использует стандартный системный интерфейс открытия файлов, его невозможно отобразить в момент проектирования и визуально отредактировать его внешний вид, но тем не менее можно ему задать некоторые свойства.



Выделяем его и в инспекторе объектов устанавливаем следующие свойства:

Свойство Title устанавливает заголовок такого окна. Заносим в это свойство строку "Открыть текстовый файл".

Свойство Filter фильтрует список по расширению в названиях файлов. Нажимаем на троеточие в этом свойстве. На экран выводится окно Filter Editor, в котором можно легко задать все возможные маски файлов. В левой части Filter Name пишется отображаемое в фильтре название. В правой части Filter сам фильтр. Пишем так, как указано на рисунке. Нажимаем на Ok.

Дальше напишем процедуру открытия файла. При выборе соответствующего пункта меню сначала открывается диалоговое окно выбора файла и при успешном выборе создается форма ChildForm, в которой в компонент Memo1 загружается этот файл.

Выбираем пункт меню "Файл" в форме MainForm и выбираем пункт "Открыть". Создается пустая процедура реакции на событие выбора этого меню. В ней пишем:


Управление свойствами визуальных компонентов в режиме выполнения


Все изменения значений свойств компонент в режиме выполнения должны осуществляться путем прямой записи строк кода на языке Паскаль. В режиме выполнения невозможно использовать Object Inspector. Хотя добраться до него возможно и возможно поменять его, но изменения вступят в силу только после перекомпиляции проекта.

Компиляция - это процесс перевода вашей программы в машинный код. В данном случае это процесс создания исполняемого EXE файла.

Однако, доступ к свойствам компонентов довольно легко получить программным путем. Все, что вы должны сделать для изменения какого-либо свойства - это написать простую строчку кода в редакторе кода аналогично следующей:

Component.Width := 40;

где Component - интересующий вас компонент. Например Button1. Название компонента указано в свойстве Name.

Вышеприведенная строка устанавливает ширину (Width) компонента в значение 40 пикселей. Если свойство Width компонента еще не было равно 40 к моменту выполнения данной строки программы, Вы увидите, как компонент визуально изменит свою ширину.

Естественно, что изменить просто так свойство нельзя. Его надо изменять только в ответ на какое-либо событие. Например на клик мышкой по кнопке. Для этого создается одноименная процедура обработки события. Рассмотрим пример.

Разместим в проектировщике форм Form1

кнопку Button1. Напишем реакцию на событие нажатия по этой кнопке.

Подробнее о реакциях на событие можно прочитать в уроке №2.

Запускаем Delphi. В меню



Краткое введение в курс Delphi


Delphi (Делфи)

Здравствуйте. Меня зовут Semen. Я ведущий уроков по Delphi. Мы с вами попатаемся научиться, а для некоторых просто вспомнить, некоторые азы и тонкости этой замечательной среды программирования.

Что же такое – Delphi? Это среда разработки, используемой прежде всего для создания и поддержки приложений, предназначенных как для отдельных персональных компьютеров, так и для серверов. Delphi, как и разработанные с ее помощью приложения, могут функционировать под практически любой 32 разрядной операционной системой типа Windows 95, 98, 2000, NT. Это довольно легкая в изучении среда, и в то же время довольно сложная. Изучить ее полностью и досконально – невозможно. Но мы постараемся с вами преодолеть этот путь семимильными шагами – практикой. Ведь легче всего запоминается то, с чем сталкиваешься непосредственно.

Delphi имеет пользовательский графический интерфейс, подобный Visual Basic и C++. Человек, ранее работавший в подобной среде, не будет чувствовать себя не в своей тарелке. Честно говоря, на данный момент множество фирм приняло за стандарт данный интерфейс для собственных приложений. Хорошим стимулом к получению знаний по данному предмету является знание хоть какого-нибудь языка программирования, или принципов написания программы. Идеально – знание языка программирования Pascal. Ведь весь исходный текст программы на Delphi пишется на языке Object Pascal, практически ничем не отличающимся от принципов, заложенных в такой знаменитой программной оболочке. Синтаксис, принцип модуля, процедуры, функции, все взято за основу.

О Delphi говорят как о среде быстрого создания приложений. Это технология визуального программирования, т.е. пользователь оформляет свою будущую программу, и видит результаты своей работы еще до запуска самой программы. В принципе, сам процесс написания приложения разделяется на две части. Мышеклацательная – программист располагает на окна своей программы необходимые элементы, позиционирует, устанавливает нужные размеры, меняет свойства. Клавиатуротопательная – собственно, написание программного кода, описание свойств элементов, доступных только во время работы приложения, описание реакций на событие появление окна, нажатия на кнопку и др. Для задания каких-либо свойств элементу разрабатываемого приложения вовсе не обязательно писать массивные текстовые строки, достаточно изменить это свойство в инспекторе объектов (так называемом мониторе свойств выбранного элемента). Это изменение автоматически дополнит или модифицирует программный код.




Это большой плюс в визуальной технологии программирования. Создавая или модифицируя свой программный продукт, пользователь не зная или не обращая внимания на некоторые свойства элемента программы, а используя только необходимые, пишет полностью готовый рабочий продукт, порой выступающий на равных по сложности, с написанными на невизуальном редакторе.
Дополнительное удобство в работе в среде Delphi это мощная справочная система. Контекстно-зависимая от текущего выбранного элемента или строки программы, позволяет получить подробнейшую справку. Вложенные примеры позволяют, не отходя далеко от интересующей вас информации, просмотреть реализацию уже готовой, возможно полезной для вас, программы. Естественно, справка, как и сама среда разработки, описана на английском языке. Одновременно существуют и русифицированные файлы справки. Я даже слышал о выходе русской версии Delphi 5.
Для начинающих можно сказать, все, что вы видите на экране во время работы различных Windows приложений, все элементы (кнопки, бегунки, меню и т.п.) можно реализовать в Delphi.
Контекстно-зависимое внедрение файлов справки позволяет поднять уровень проектируемого приложения на порядок выше. А не сказать про поддержку работы с базами данных – значит не сказать многого. При работе в среде программирования посредством так называемого BDE (Borland Database Engine), системного администратора баз данных, можно получать прямой доступ к таким стандартным форматам данных, как dBASE, Paradox, FoxPro, Access, ASCII таблицам. Набор драйверов Borland SQL Links обеспечивает все необходимые соединения с SQL-серверами.
Далее немного вашего внимания уделю процессу установки Delphi на компьютер.
Требования к аппаратуре и системе при установке Delphi различных версий. Я бы рекомендовал придерживаться этого правила при выборе варианта установки той или иной версии.

Версия
Дисковое пространство для установки
Оперативная память (минимум – рекомендуемая)
Процессор (минимум)
Операционная система (минимальные требования)
Delphi 3
50-170 MB
8–12 МВ
80486
Windows 95, 98;
Windows NT 3.51 Service Pack 5
Delphi 4
60-154 МВ
16–32 МВ
486/66
Windows 95, 98;
Windows NT 4.0 (Service Pack 3)
Delphi 5
80-200 МВ
32–64 МВ
Pentium 90
Windows 95, 98;
Windows NT 4.0 (Service Pack 3)
<


Дисковое пространство указано в пределах типа установки. От компактной до полной. Данные взяты из технической документации по установке.
Далее, на словах, что необходимо для работы. Это мышь, нормальный цветной монитор, желательно звуковую карту и CD-привод.
Чем же отличаются эти версии. Прежде всего, более последующая версия имеет лучший интерфейс, добавляются новые компоненты, появляется больше возможностей при написании и отладки приложений. Действительно, сильный скачек в перемене внешнего вида произошел из версии 3 в 4. Теперь значки меню имеют стандартный, общепринятый вид, добавлен проводник по программному коду, для быстрого перехода к необходимому месту в программе и пр. Я лично пользуюсь 4 версией, и мне, пока, хватает.
Итак, установка. Если во время инсталляции будет предложен вариант установки, рекомендую не мучится и выбрать типичную установку, которая установлена галочкой по умолчанию. Будет произведено копирование основных компонентов, библиотек, шаблонов. Выборочная инсталляция обычно для тех, кто уже имел опыт работы, и знает, с какими компонентами он работает
После успешного завершения установки и перезагрузки компьютера (при возникших проблемах установки – читайте прилагаемую документацию) вы увидите в меню "программы" установленные: среда разработки Delphi, дополнительные программы, файлы помощи.
Запустим значок с надписью Delphi N, где N – номер версии. При этом запускается сама оболочка создания приложений, называемая интегрированной средой разработки IDE (Integrated Development Environment). Именно это название часто встречается в литературе и файлах справки. Именно эта среда и включает в себя множество функций для создания приложения. Даже у самого производителя – корпорации Borland International наилучшей рекламой для этого продукта было сказано, что сам Delphi писался на Delphi.
Появившемся на экране окнам можно дать следующую предварительную характеристику.
Главное окно.
Оно располагается в верхней части экрана, в котором располагается меню, панель инструментов и палитра компонентов. Панель инструментов представляет собой набор кнопок для быстрого доступа к необходимой функции меню. На палитре компонентов, представляющей собой множественные тематические страницы, располагаются визуальные и невизуальные компоненты вашей будущей программы (невизуальные компоненты видны только при проектировании приложения). Пожалуйста, не перепутайте инструменты и компоненты. Главное окно остается открытым все время работы IDE. Закрывая его, вы тем самым закрываете Delphi и все открытые в нем окна.


Инспектор объектов. Как было сказано выше, при разработке приложения его очень часто приходится использовать. Окно Object Inspector содержит две страницы. На первой, Properties, постоянно отображаются все доступные свойства выбранного компонента. В левой колонке содержится список, в правой – текущие значения по умолчанию. На второй странице, Events, возможные обработчики событий для выбранного компонента. В левой колонке – названия, в правой – соответствующие свойства или процедуры. На рисунке вы можете видеть Object Inspector с установленными свойствами формы1 (это окно вышей программы).
Что же такое свойства и реакции на события.
Каждый объект в Delphi, даже само окно разрабатываемого приложения, имеют определенные свойства. Например цвет, размер, отображаемый текст и т.п. Эти свойства можно менять еще до запуска проектируемой программы на выполнение. В зависимости от изменяемого свойства результат можно просматривать уже сразу.
Реакции на события, это результат произошедшего системного события, например щелчок мыши, нажатие на кнопку, открытие окна и т.п. Реакцию на событие назначают программно, указывая список действий, которые необходимо произвести. Например, если пользователь выполняет клик по кнопке, производится копирование файла.
Подробнее с инспектором объектов будет сказано по ходу работы с новыми программами в следующих уроках.
Окно редактора кода. Оно имеет заголовок на момент первого запуска Unit1. В редакторе кода могут быть открыты сразу несколько файлов. Каждый открытый файл размещается на отдельной странице, а его название отображается на вкладке в верхней части.
Если в ващей программе три окна, то они будут взаимодействовать в процессе работы с тремя так называемыми модулями (Unit). Все эти модули и отображаются в редакторе.
В окне кода программист непосредственно пишет текстовую часть программы.
Текстовая часть разбивается на несколькие части, называемые процедурами и функциями, которые работают отдельно одна от другой. Возможно и перенаправление выполнения программы их одного места в другое.


Как правило, в серьезные проектах основную работу программист производит именно здесь. Я вас на первых порах не буду загружать множественными командами, разбираться мы будем потихоньку, начиная с самого простого. Здесь важно понять принцип.
Проектировщик форм. Как вы уже, наверное, знаете или догадываетесь, каждое Windows-приложение выполняется в собственном окне. Минимальное количество таких окон равно 1. Delphi при запуске автоматически предлагает пользователю новый проект, открывая пустое (незаполненное) окно под названием Form1 и назначает его главным окном. То есть вы уже видите то самое окно, которое будет содержать ваша программа. Перенося на него элементы из палитры компонентов, вы тем самым предварительно оформляете его. Главное окно в проекте может быть только одно. Все другие создаваемые окна будут дочерними. Закрывая главное окно стандартной кнопкой закрытия окна, или программно, вы закрываете и все дочерние окна. Представьте это себе на примере текстового редактора MS Word. Главное окно создается первым, и соответственно пользователь его видит тоже первым. Максимально возможное количество открытых проектировщиков форм – количество окон в вашем проекте. Но для начала мы с вами будем работать только с одним окном в проекте.
Итак, при запуске Delphi мы с вами видим уже открытый новый проект для создания приложения. Давайте запустим его на выполнение. Это можно сделать в любом окне IDE нажатием на кнопку F9, выбрав соответствующий пункт "Run" в меню "Run" или выбором одноцветной кнопки на панели инструментов. Происходит сравнительно недолгий процесс компиляции (преобразование удобочитаемых для вас данных в удобочитаемую форму для компьютера), в результате которого создается EXE файл. Далее этот файл, в случае успешного создания, запускается на выполнение. Во время выполнения из множества окон Delphi остается только главное окно и окно редактора кода.
Поздравляю! Практически ничего не делая, и сами того не ожидая, вы создали собственную программу. Эта программа имеет вид стандартного Windows-окна с заголовком Form1, со стандартными кнопками: свернуть, на весь экран, закрыть. Его можно позиционировать, изменять размер. По умолчанию она отображается в том же виде, что и во время разработки, имеет те же размеры и позицию на экране.


Закроем запущенную программу. Delphi автоматически переводит окна в то состояние, которое было до запуска проекта на выполнение. Снова на экране появляется инспектор объектов и редактор формы.
Для избежания отображения ненужных окон во время запуска проекта, я рекомендую установить опцию автоматического свертывания всего Delphi со своими окнами в панель задач. Войдите в меню Tools, выберите пункт Environment Options и на вкладке Preferences установите галочку Minimize on run (минимизировать при запуске). Далее Ok. Теперь попробуйте снова запустить проект на выполнение. Теперь вам ничего не мешает лицезреть свое творение.
Теперь попробуем сохранить проект на диске. Настоятельно рекомендую сохранять на диске новый проект сразу, поскольку он находится в памяти компьютера, и вы понимаете, какие могут быть последствия, если пропадет электричество или ваш компьютер решит немного отдохнуть от работы. Сохраняем все файлы проекта. Для этого выбираем пункт Save All, находящийся в меню File. Delphi предлагает сохранить модуль программы Unit1 как… Сохраним его под этим же названием, что и предлагается. Замечание: сколько существует окон в вашей программе, столько и будет модулей (Unit).   Поэтому имеет смысл сохранять каждый проект в отдельный каталог. Особенно, если у вас много окон в программе. Далее будет предложено сохранить проект как… т.е. задать название всего проекта. Как будет называться проект, под таким же именем и будут создаваться исполняемые EXE файлы. Названия файлов должны состоять из одного слова или слов, написанных английскими буквами, цифры допустимы только начиная со второго символа, пробелы – недопустимы (используйте в таких случаях знак подчеркивания).
Для информации привожу перечень сохраняемых при этом файлов на диске:
DPR – файл проекта. Содержит он основной код программы, ссылки на все окна (формы) проекта и относящиеся к ним модули. В нем также содержится код инициализации. Имеет одноименное название с проектом.
PAS – pascal файл. Он содержит текст, который вы видите в окне редактора кода так называемого модуля программы.


DFM – delphi form. Представляет собой файл с полными данными о проектировщике формы. Позиция, размер, расположенные компоненты и пр. Форма приложения является неотъемлемой частью модуля PAS и имеет то же название.
DCU – двоичный файл модуля. Имеет одноименное название с модулем.
RES – ресурсный файл. Содержит в себе иконки, значки указателя мыши, картинки, звуки.
DOF, DSK – содержат настройки проекта.
CFG – содержит настройки конфигурации проекта.
EXE – откомпилированная программа. Сохраняется автоматически при запуске проекта на выполнение. Обновляется в момент компиляции. Имеет одноименное название проекта. Полностью самостоятельное приложение.
По ходу работы в среде Delphi могут автоматически создаваться файлы, имеющие расширение, начинающееся символом ~. Это резервные копии файлов, которые создаются при их повторном сохранении.
Итак, мы с вами установили, запустили Delphi, даже создали новую программу. В следующий уроке попробуем рассмотреть простейшие примеры программ, одновременно рассматривая и некоторые особенности работы в Delphi.
Не пугайтесь изобилия информации, изложенной в первом уроке. Некоторые подробно рассмотренные данные вы сможете использовать в справочных целях. По ходу следующих занятий мы с вами будем возвращатся к предыдущим темам, и постепенно у вас будет складываться общая картина всех знаний, полученных от маленькой теории, и прежде всего от практики.
С уважением, ведущий уроков Semen semen@krovatka.net

Палитра компонентов, часть первая


В нескольких уроках будут рассмотрены основные страницы с компонентами, содержащиеся в палитре компонентов главного окна Delphi. Будут рассмотрены описания некоторых свойств, место их применения. На этом занятии будут рассмотрены страницы Standart, Additional и Win32.

Не обязательно заучивать, запоминать нужные или ненужные на данном этапе обучения те или иные компоненты. Эти данные вы можете использовать в дальнейшем в справочных целях.

Поскольку у меня установлена 4 версия Delphi, то объяснять буду именно для нее. В других версиях имеются некоторые различия, но это не меняет всей картины в целом.

Первой кнопкой на каждой вкладке палитры компонентов всегда указатель в виде курсора . Это не компонент, эта кнопка позволяет быстро отказаться от уже выбранного компонента. Если вам нужно в форму установить несколько одинакавых компонента, то выбирайте его из палитры при нажатой клавишей Shift. В этом случае после перенесения компонента в окно, кнопка на палитре продолжает оставаться нажатой. Чтобы выйти из этого режима используйте вышейказанную кнопку отмены.



Палитра компонентов, часть вторая


Продолжаем рассматривать элементы в палитре компонентов. На этом занятии будут рассмотрены страницы System, Dialogs, Samples.



Переменные.


Как я уже говорил неоднократно, для того, чтобы вам легче было изучать язык программирования Object Pascal, необходимы некоторые азы знания языка Pascal. Потребуются только поверхностные знания синтаксиса, основных операторов, типов данных. Не отходя далеко от нашего сайта вы сможете изучить и этот язык в разделе "Программирование на языке Pascal"

Но тем не менее мы с вами постепенно просматриваем общие правила, так сказать азы, чтобы дальше углубляться без вопросов в более сложный материал. Перед вами последний ознакомительный урок с языком Pascal, после чего мы будем углубляться в сам Delphi.

Как уже говорилось ранее, переменные – это некоторая область в памяти, хранящая данные. Перед использованием переменной необходимо указать ее тип, и при необходимости размер. Места, где объявляются переменные, мы уже немного рассматривали в примерах.



Текстовый редактор


На этом уроке мы с вами продолжим писать текстовый редактор MyEdit. Последнюю версию можно посмотреть в уроке №9 и скачать здесь. Если вы самостоятельно писали эту программу по урокам, но настоятельно рекомендую скачать именно мою версию, поскольку у нас может не совпадать названия пунктов меню.

Итак, наша программа уже обладает почти всеми стандартными функциями работы с простым текстом. Напишем еще процедуры сохранения текста на диск.

Если редактируемый файл новый, то вместо сохранения будет предложено файл "сохранить как...", где пользователь должен указать имя файла.

Для начала изменяем свойство Caption для окна ChildForm на 'НОВЫЙ'. Это можно сделать и программно. Для этого для реакции на событие OnCreate для этого дочернего окна создаем процедуру.



Компонент TMemo


Он находится на палитре компонентов Standart. Как мы уже успели понять, он представляет собой поле для отображения и редактирования неформатированного текста. Текст можно загрузить из файла, набрать на клавиатуре, вставить из буфера обмена. Т.е. он имеет многие доступные свойства для редактирования текста, что способствует его применению в простейших текстовых редакторах для просмотра (редактирования) текста, наподобие того примера, который мы с вами периодически дописываем вот уже несколько уроков.

В этом компоненте текст всегда имеет одинаковый шрифт, задающийся через свойство Font.

Программно для компонента Memo1, расположенного на форме это делается следующим образом. Допустим, на форме расположено три компонента: поле редактирования текста Memo1 (страница Standart), диалог выбора шрифта FontDialog1 (страница Dialogs) и кнопка Button1 (страница Standart). Реакция на событие нажатия на кнопку Button1

procedure TForm1.Button1Click(Sender: TObject);

begin

if FontDialog1.Execute then // если пользователь выбрал шрифт

Memo1.Font:=FontDialog1.Font; // установить выбранный шрифт

end;

Если необходимо, чтобы в диалоге выбора шрифта изначально был выбран текущий шрифт, установленный компоненте Memo1, то перед вызовом диалога Execite устанавливаем в диалоге нужный шрифт.

procedure TForm1.Button1Click(Sender: TObject);

begin

FontDialog1.Font:=Memo1.Font; // установить шрифт в диалоге

if FontDialog1.Execute then

Memo1.Font:=FontDialog1.Font;

end;

Можно изменять шрифт для компонента TMemo (и не только для него) программно. Для этого в свойстве Font устанавливаются необходимые свойства во время работы приложения.

Memo1.Font.Name:='Arial'; // установка имени шрифта

Memo1.Font.Size:=14; // установка размера шрифта

Memo1.Font.Color:=clRed; // цвет шрифта

{для того, чтобы увидеть полный список доступных стандартных цветов воспользуйтесь справочной системой, например, подведя курсор под clRed и нажав на Ctrl+F1}

Memo1.Font.Style:=[fsBold]; // установить жирный стиль




Style представляет собой массив свойств стиля шрифта. Необходимо в квадратных скобках указывать через запятую необходимые стили. Пустые кавычки будут означать установку шрифта без стиля. Список доступный свойств стиля шрифта:
fsBold – жирный;
fsItalic – наклонный;
fsUnderline – подчеркнутый;
fsStrikeout – перечеркнутый.
Свойство WordWrap устанавливает, может или нет текст автопереносится на следующую строчку, если он достиг левого края компонента. Автоперенос работает и когда пользователь во время работы приложения изменяет размеры компонента.
Текст в компоненте доступен через свойство Lines. Он содержит массив строк, находящихся в компоненте. Можно получить доступ как к отдельной строке, так и к всему тексту. Это свойство можно редактировать и во время работы приложения, и во время разработки в инспекторе объектов.
Загрузка строк из файла через процедуру
Memo1.Lines.LoadFromFile('c:\1.txt');
где "c:\1.txt" – загружаемый файл в компонент. Следует при этом помнить, что компонент TMemo ограничен в размерах текста.
Сохранение строк в файл.
Memo1.Lines.SaveToFile('c:\1.txt');
Количество строк в компоненте можно узнать через свойство Memo1.Lines.Count, а доступ к отдельной строчке через функцию Strings. Например:
ShowMessage(Memo1.Lines.Strings[0]); // Показывает в окошке первую строчку текста.
Strings – это массив строк, начинающихся с нулевого знака, заканчивающиеся Count-1. Об этом следует помнить при написании подобных процедур доступа, иначе произойдет выход за доступный предел массива, что вызовет ошибку в программе.
Можно для примера организовать цикл поочередного отображения строк из компонента Memo1. Для реакции на нажатия на кнопку Button1 это выглядит следующим образом:
procedure TForm1.Button1Click(Sender: TObject);
Var i:Integer; // объявление целочисленной переменной
begin
if Memo1.Lines.Count<>0 then // проверка ненулевого количества строк
for i:=0 to Memo1.Lines.Count-1 do // задается цикл, равный кол-ву строк
ShowMessage(Memo1.Lines.Strings[i]); // вывод строк в сообщение


end;
Очистить Strings можно одной процедурой Memo1.Lines.Clear.
Добавить строчку –
Memo1.Lines.Add('добавленная строка');
Вставить строчку в заданное место –
Memo1.Lines.Insert(0,'вставленная строка');
Последний пример вставляет текстовую строку в первую строку (первая строка -1 = 0).
Удалить строчку –
Memo1.Lines.Delete(0); // удаляет первую строку
При применении процедуры удаления строк Delete помните, что сначала нужно проверять компонент TMemo на наличие таковых вообще. Проверка через функцию Count, которая возвращает их целочисленное значение.
Работа с выделенным текстом программно аналогична работе пользователя вручную. Если пользователь выберет процедуру вырезания выделенного текста в буфер или его удаление, то он пропадет. Выделение текста производится с помощью двух процедур.
Memo1.SelStart:=0; // установить начальное положение выделения
Memo1.SelLength:=15; // установить длину выделенного текста
Если необходимо всегда видит выделенный текст, даже при переходе управления на другой элемент управления, то установите свойство HideSelection в false.
Работа с выделенным фрагментом текста
Memo1.ClearSelection; // удалить выделенный текст
Memo1.CutToClipboard; // вырезать в буфер обмена
Memo1.CopyToClipboard; // скопировать в буфер обмена
Memo1.PasteFromClipboard; // вставить из буфера обмена
Memo1.SelText; // эта функция возвращает текстовую строку выделенного текста
Отмена последнего изменения – процедура Memo1.Undo.
Компонент TMemo является оконным визуальным компонентом, т.е. у него имеются свойства, характерные множеству компонентов, таких как Border, Color, Align, Enabled, Visible.
С уважением, ведущий уроков Semen semen@krovatka.net

Завершение проектирования MyEdit.


На сегодняшнем уроке мы с вами закончим рассматривать и написание программы простейшего текстового редактора. Предыдущую версию программы можно забрать здесь.

Итак, наша программа, лишенная некоторых нерассмотренных возможностей, практически готова. Ее можно применять как для просмотра, так и для редактирования текстовых файлов небольшого размера и вполне может заменить стандартный блокнот в Windows. К тому же, наша программа может работать с несколькими текстовыми файлами одновременно.

Организуем закрытие главного окна при выборе пункта меню "Выход". Для этого по известной нам методике, вызываем главное окно проекта MainForm на редактирование. Для этого нажимаем Shift+F12 и выбираем его в списке. Дальше, чтобы организовать реализацию события выбора пункта меню пользователем OnClick можно двумя способами:

1. Для дважды кликнуть по компоненту главного меню MainMenu1, после чего вызывается на редактирование редактор пунктов этого меню. После чего нужно выбрать пункт "Выход" и в инспекторе объектов (вкладка Events) выбрать процедуру OnClick на этом пункте, тем самым создав новую процедуру обработки события.

2. Просто из уже готового (отображаемого) меню в окне MainForm выбрать пункт "Выход". Delphi автоматически по умолчанию воспринимает как выбор программистом события OnClick для этого меню и создает все ту же пустую процедуру реакции программы на событие.

Какой из этих методов в дальнейшей работе – выбирать вам. Второй экономит время написания программы, первый нагляднее. Такой способ быстрого выбора событий для компонентов, которые установлены по умолчанию, характерны для всех компонентов в delphi. Например, двойной щелчок мышки по форме создает событие OnCreate для этой формы, для кнопки TBotton – событие OnClick, TMemo – событие OnChange. Постепенно в ходе обучения я буду обращать внимание на такие моменты оптимизации вашей работы.

В готовой процедуре реакции на событие выбора пункта меню "Выход" пишем только одно слово:




procedure TMainForm.N8Click(Sender: TObject);
begin
Close; // Закрыть главное окно (закрыть программу)
end;
Можно написать MainForm.Close, что равнозначно вышесказанному. Но поскольку данный модуль (Unit) предназначен для работы с формой (MainForm) и с его компонентами, то все команды в этом модуле воспринимаются как команды для окна, если мы непосредственно не указываем получателя.
Закрывая главное окно, мы тем самым закрываем все дочерние окна. Команда закрытия главного окна работает следующим образом.
1. После получения команды закрытия поочередно посылается аналогичная команда на все дочерние окна.
2. Если в дочерних окнах файл был изменен, происходит запрос на сохранение, в течении которого главное окно ждет.
3. Если пользователь ответил на запрос "Отмена", то одно из дочерних окон не закрывается и процесс закрытия главного окна прекращается. Текстовый файл по прежнему доступен для редактирования.
4. В случае успешного закрытия всех дочерних окон закрывается и главное окно приложения.
5. Программа после закрытия главного окна завершает работу.
Весь такой трудоемкий процесс закрытия происходит очень быстро и незаметно для пользователя, и что главное – полностью автоматически. Во-первых (как это не прискорбно для некоторых "любителей" Windows) благодаря свойствам операционной системы управления MDI-приложениями; во-вторых благодаря нам, мы этот случай рассматривали только при закрытии единственного дочернего окна и не подозревая, что применяем ко всему проекту. Все в программах, как и в этом мире взаимосвязано, особенно все, что касается дочерних процессов.
У нас в меню "Файл" остался один незадействованный пункт "Закрыть". Его применяют для закрытия активного дочернего окна (если пользователю лень нажать на крестик в правом верхнем углу). Мы этот пункт уже применили в меню "Окна", и поэтому его необходимость повторного существования отпадает. Его можно удалить или назначить соответствующее свойство из списка стандартных действий.


Для этого открываем редактор главного меню (MainMenu1) двойным щелчком мыши. Выбираем пункт меню "Закрыть" и в свойстве Action (Object Inspector на странице Properties) выбираем стандартное действие Close1. Тут, возможно, из-за различий в версиях delphi некоторые различия. Поступайте в таких случаях всегда "по смыслу", находя наиболее похожие или применимые значение для выбора, или в самых запущенных случаях обращайтесь к автору этого урока за помощью.
И наконец, самый последний пункт меню "О программе". Ведь каждый уважающий себя программист обязательно ставит свою подпись на каждом программном продукте. В таком окне еще обычно даются каналы связи технической поддержки, версия программы и дата изготовления. Можно написать еще много разной информации, чего сразу не припомнишь.
Изготавливаем новое окно для нашего проекта. Это окно будет модальным, т.е. пользователь ничего не сможет сделать с другими окнами, пока не закроет это. Модальное окно появляется поверх других окон, делая все остальные недоступными.
Выбираем из меню "File" главного окна delphi пункт "New Form". Появляется новая чистая форма. Сразу изменяем ее размер на более малый, ведь окно About никогда огромным не бывает, впрочем как вам угодно.
Дальше описываю все дальнейшие ваши шаги с пояснениями.
1. Изменяем свойство Name для этого нового окна из Form1 в About. Теперь доступ к нему из программы будет только по этому идентификатору.
2. Изменяем свойство Caption на "О программе". Заголовок окна содержит этот текст, понятный пользователю, куда он попал, и какая здесь содержится информация.
3. Выбираем пункт меню delphi "Save All" и сохраняем новое окно под именем AboutUnit.
4. Изменяем свойство Pisition на poScreenCenter. Это означает, что окно "О программе" будет изначально появляться ровно по центру экрана, независимо от текущего экранного разрешения.
5. Изменяем свойство BorderStyle на bsDialog. Это окно у нас служит в качестве диалогового, и значит пользователь не должен иметь возможность его свернуть или развернуть на весь экран.


6. Устанавливаем в форму кнопку TBitBtn
, находящейся на палитре компонентов на странице Additional. Располагаем эту кнопку в удобное для вашего взгляда положение. Например в правый нижний угол. Дальше изменяем свойство Kind для этой кнопки в bkOK. Кнопка сразу приобретет новое название OK и на ней появится рисунок зеленой галочки. Если пользователь выберет эту кнопку, то окно About закроется без написания каких-нибудь процедур с нашей стороны.
7. Располагаем в форме несколько компонентов TLabel
(сколько нужно), в которых в свойстве Caption пишите всю необходимую в таких случаях информацию. Шрифт можно изменить в свойстве Font.
8. Теперь непосредственно привяжем вызов этого окна выбором соответствующего пункта меню. Для этого выбираем пункт меню "О программе" в главном окне нашей программы MainForm и в процедуре OnClick, для этого пункта меню, пишем одну строчку.
procedure TMainForm.N15Click(Sender: TObject);
begin
About.ShowModal;
end;
Дальше выбираем пункт меню Use Unit из меню файл главного окна delphi и выбираем модуль AboutUnit. Мы только что подключили к главному модулю проекта модуль окна About. Если этого не сделать – ничего страшного. При запуске программы на исполнение delphi примет это за упущение со стороны программиста и сделает запрос на автоматическое подключение соответствующего модуля, после чего вам следует повторно запустить программу на выполнение.
Это вроде бы все. Можете запускать программу и любоваться своей работой. Как всегда, рассмотренный материал в виде примера можно скачать в конце урока здесь.
Эта программа совсем не сложная, хотя и содержит некоторые строки, непонятные для новичков. Но и без этого вы смогли убедиться, что писать программы в программной оболочке delphi не сложно.
В следующих уроках мы с вами будем начнем новый пример и будем постепенно продвигаться в азах программирования, приобретая новые знания.
С уважением, ведущий уроков Semen semen@krovatka.net

Компонент TForm


Хотя компонент TForm отсутствует в палитре компонентов, но все же имеет присущи для него свойства, события.

Итак, компонент TForm это окно в оконном приложении. Оно может быть минимум одно, максимальное ограничение на количество не накладывается. Первое созданное окно в приложении автоматически становится главным. При закрытии главного окна приложение завершает свою работу и, занимаемая им память, освобождается.

Если необходимо, чтобы главное окно формы автоматически не появлялось на экране, то в событии OnCreate для этого окна, или в программе DPR до команды создания главного окна необходимо указать следующую строку:

Application.ShowMainForm:=false;

В этой команде указывается, что в этом приложении запрещено отображение главной формы (окна). В этом случае программисту нужно самостоятельно вызывать команду

Form1.Show;

для отображения этого окна. Здесь Form1 – главное окно приложения.

Данный метод может оказаться полезным для вывода запроса пароля на запуск программы. Именно это диалоговое окно нужно показать пользователю перед показом главного окна, но не делая его главным окном, т.е. при закрытии окна ввода пароля, приложение не завершает свою работу. И при вводе неверного пароля можно без проблем завершить работу программы командой

Form1.Close;

Или дать команду завуршения работы приложения.

Как уже говорилось в предыдущих уроках, каждый компонент в программе, как и сама форма, имеет уникальное имя, указанное в свойстве Name. Заголовок окна содержится в свойстве Caption. По заголовку окна пользователь узнает о функциональном назначении программы или текущего окна.

Кроме заголовка в верхней части окна находится иконка и кнопки управления состоянием. По умолчанию иконка такая же самая, как и иконка в проекте. Чтобы сменить иконку в текущем окне необходимо в инспекторе объектов выбрать свойство Icon в котором выбрать соответствующий файл-рисунок. Файл должен быть с расширением ICO.

Для того, чтобы сменить иконку в проекте, нужно войти в меню "Project", далее "Options...", на вкладке Application вы видите текущий рисунок файла проекта. Сменить его можно кнопкой "Load Icon...". Иконка проекта хранится в файле ресурсов с расширением RES.



Работа с файлами


В этом уроке рассмотрим очень важную тему в программировании - работа с файлами. Практически в любой программе нужно копировать, открывать, изменять, сохранять, иногда удалять файлы.

Как известно, файлы имеют различные форматы и к ним применяются различные программы для обработки. Например, файлы рисунков BMP, текстовые файлы TXT и т.п. Файлы имеют различные имена и определенное расширение, имеющее в среде MS-DOS максимально три символа. Если у файла расширение RAR, то пользователь и операционная система знает, что это архиватор, причем windows для обработки этого файла применяет средства, указанные в специальном разделе реестра.

Как открывать зарегистрированные файлы и исполняемые файлы, было рассказано в уроке 4

Копирование файлов

Для копирования файлов применяется функция CopyFile. Формат ее такой:

CopyFile(Исходный_файл,Записуемый_файл,Флаг_перезаписи);

где: Исходный_файл – полный путь и название файла, который копируется;

Записуемый_файл – полный путь и название файла, куда копируется исходный файл;

Флаг_перезаписи – будет или нет перезаписан файл, если таковой уже существует (true - не будет, false - будет перезаписан).

CopyFile является функцией, которая возвращает флаг успешной или нет операции копирования.

Например, следующая строка:

if not CopyFile('c:\command.com','c:\1.com',true) then ShowMessage('Ошибка копирования');

скопирует файл command.com в файл 1.com только в том случае, если последнего нет, в противном случае будет выдаваться сообщение об ошибке копирования.

Перемещение файлов

Для перемещения файлов применяется функция MoveFile. Она имеет следующий формат:

MoveFile(Исходный_файл,Записуемый_файл);

Ее параметры аналогичны вышерассмотренной команде за исключением отсутствия флага перезаписи.

Пример:

if not MoveFile('c:\1.com','c:\2.com') then ShowMessage('Ошибка перемещения');

Переименование файлов

Функция RenameFile

if not RenameFile('c:\2.com','c:\3.com') then ShowMessage('Ошибка переименования');

У всех вышесказанных командах параметры исходного и конечного файла имеет тип PChar. Это строковый тип с завершающимся нулем. Возможно, вы раньше слышали о таком формате строковых данных. В такую переменную можно занести строку очень большой длины. Ее размер ограничивается наличием нулевого байта в конце. Теоретически такая переменная может иметь бесконечный размер. На практике она ограничивается размерами выделяемой памяти для программы (2Гб).




Преобразование обычной строковой переменной, типа String в PChar производится функцией:
PChar(Строковая_переменная).
При этом, для команд файловых операций возможны объединение строк. Например:
procedure TForm1.Button1Click(Sender: TObject);
Var InDir,OutDir:String; // объявление строковых переменных
begin
InDir:='c:\1\'; // каталог исходных файлов
OutDir:='c:\2\'; // каталог записуемых файлов
CopyFile(PChar(InDir+'1.txt'),PChar(OutDir+'1.txt'),false);
CopyFile(PChar(InDir+'2.txt'),PChar(OutDir+'2.txt'),false);
end;
Здесь налицо операция соединения двух строковых переменных.
В итоге этой процедуры будет копирование файлов c:\1\1.txt и c:\1\2.txt в каталог c:\2. Файлы копируются под теми же именами.
Удаление файлов
Функция DeleteFile
if not DeleteFile('c:\3.com') then ShowMessage('Ошибка удаления');
Работа с текстовыми файлами. Чтение из текстового файла
На прошлых уроках мы рассматривали пример простейшего текстового редактора. В нем, в компоненте Memo процесс загрузки и записи текстового файла делался следующим образом:
Memo1.Lines.LoadFromFile(Имя_файла); // загрузка
Memo1.Lines.SaveToFile(Имя_файла); // сохранение
Все это благодаря свойству Lines, в котором хранятся строки.
Но на практике иногда необходимо прочитать только определенную строку или совершить операцию добавления строки в уже существующий файл.
Следующий пример обработки текстового файла очень похож на аналогичную на языке Pascal. Знающие люди могут ощутить разницу, поскольку есть некоторые отличия.
procedure TForm1.Button1Click(Sender: TObject);
Var f:TextFile; // объявление файловой переменной
st:String; // строковая переменная
begin
AssignFile(f,'c:\1.txt'); // привязка названия файла к файловой переменной
{$I-} // отключение контроля ошибок ввода-вывода
Reset(f); // открытие файла для чтения
{$I+} // включение контроля ошибок ввода-вывода
if IOResult<>0 then
// если есть ошибка открытия, то
begin
ShowMessage('Ошибка открытия файла C:\1.TXT');
Exit; // выход из процедуры при ошибке открытия файла


end;
 
While not EOF(f) do // пока не конец файла делать цикл:
begin
ReadLn(f,st); // читать из файла строку
ShowMessage(st); // выводить строку пользователю
end;
CloseFile(f); // закрыть файл
end;
Прокомментирую некоторые строки этого примера.
Команда AssignFile осуществляет привязку строки пути файла к файловой переменной. Все дальнейшие операции с файловой переменной автоматически осуществляются с указанным файлом. Для избежания путаниц, указывайте полный путь к файлу.
{$I-} и {$I+} являются директивами компилятору, что в этом месту соответственно следует отключить и включить контроль ошибок ввода-вывода. В данном случае при неудачной попытке открытия файла c:\1.txt (файл отсутствует или открыт для записи другой программой) наша программа не выдаст аварийной ошибки и продолжит выполнение данной процедуры. Это свойство полезно для обработки всех возможных случаев в работе программы.
IOResult – переменная, которая хранит в себе код ошибки последней операции ввода-вывода. Если она равна нулю, то последняя операция была успешно выполнена.
EOF(Файл) – функция, возвращающая признак конца файла. Т.е. она показывает, достигнут или нет конец открытого файла.
ReadLn(Файл,Переменная) – процедура считывания переменной из файла. В отличие от команды Read производит считывание строки с завершающимся символом перевода строки под кодами 13 и 10 (клавиша Enter).
CloseFile(Файл) – процедура закрытия ранее открытого файла.
Работа с текстовыми файлами. Запись в текстовый файл
Рассмотрим пример:
procedure TForm1.Button1Click(Sender: TObject);
Var f:TextFile; // указатель на текстовый файл
begin
AssignFile(f,'c:\1.txt'); // привязка названия к переменной
{$I-}
Append(f); // открыть файл для добавления
if IOResult<>0 then // если ошибка открытия (напр. файла нет)
begin
{$I-}
Rewrite(f); // создать новый файл
{$I+}
if IOResult<>0 then // ошибка создания файла
begin
ShowMessage('Ошибка создания файла C:\1.TXT');
Exit;
end;
end;
WriteLn(f,'Привет'); // запись в файл строки с символами перевода строки
CloseFile(f); // закрыть файл
end;
Процедура Append(Файл) открывает файл для записи и устанавливает указатель записи в конец файла, т.е. все добавляемые к файлу строки будут записаны в конец файла.
В нашем случае в самом начале файла 1.txt может не оказаться на диске, поэтому команда открытия файла для добавления вызовет ошибку. В этом случае срабатывает наш собственный контроль ошибок и выполняется команда создания файла.
Запись и чтение из файла не текстовых данных
Рассмотрим пример, в котором программа при выходе сохраняет свои размеры и положение окна на экране в файл и при последующем запуске восстанавливает состояние, которое было при выходе. Программа состоит из двух процедур OnShow и OnClose для окна Form1 программы.

Ini файлы


Для хранения некоторых данных, например параметров программы, как это было рассмотрено в предыдущем уроке, для последующего их использования можно применять реестр или ini файлы. Я имею в виду хранение установок программы во время бездействия программы, чтобы при последующем запуске, она восстанавливала свое состояние. В ini файлах можно также хранить и скрытые от пользователя данные, такие как серийный номер программы, срок истечения лицензии, закодированные пароли на запуск и пр. Такие данные естественно не защищены от постороннего вмешательства, но при умелом использовании кодирующих средств, можно исключить ручное редактирование этих параметров.

TIniFile является низкоуровневым 16-битным классом, совместимым с операционной системой windows 3.x файловой системы хранения параметров в INI файлах. Для хранения параметров в современных программах рекомендуется использовать реестр. Но, как правило, не так просто такую программу скопировать на другой компьютер с переносом всех настроек, а совместное копирование ini файла избавит вас от такой проблемы.

Класс TIniFile находится в модуле IniFiles, который надо указывать в разделе подключаемых модулей Uses.

Объявление переменной, в которую будем заносить (или читать) данные:

Var IniFile : TIniFile;

где: IniFile - любой идентификатор, который будет использоваться в программе. Можно, например, для сокращения просто i.

Дальше переменную необходимо создать:

IniFile := TIniFile.Create('Название_файла');

где: Название_файла - файл, в котором будут храниться данные. Если вы напишите, например project.ini без указания пути к файлу, то такой файл создастся или будет читаться из каталога WINDOWS. Для размещения ini файла в каталоге программы, или относительно его (например КАТАЛОГ_ПРОГРАММЫ\INI) указывайте вместе с названием файла текущий каталог программы.

В конце использования переменной ее необходимо уничтожить и освободить занимаемую память. Это делается одной командой:

IniFile.Free;

В промежутке между созданием и уничтожением переменной IniFile находятся команды чтения (записи) параметров.




Если мы заглянем в любой файл настроек, например win.ini, то можно увидеть следующую архитектуру файла:

[windows]

NullPort=None

ILOVEBUNNY32=1

ScreenSaveActive=1

[Desktop]

Wallpaper=(None)

TileWallpaper=0

WallpaperStyle=0

[Intl]

iCountry=380

Строка, заключенная в квадратные скобки [] называется заголовком секции, и все данные между двумя заголовками являются секцией. В одной секции может быть только один уникальный параметр, т.е. название параметров в секции не должны повторяться дважды. После названия параметра следует знак равенства, после которого сама хранимая величина.

Чтение параметров:

Все команды чтения параметров являются функциями, т.е. сами возвращают требуемые значения.

Для чтения параметра Integer (целочисленной величины):

ПЕРЕМЕННАЯ_Integer:=IniFile.ReadInteger(СЕКЦИЯ,ПАРАМЕТР,ЗНАЧЕНИЕ_ПО_УМОЛЧАНИЮ);

где: СЕКЦИЯ и ПАРАМЕТР - строковые значения. На из месте можно писать как саму строковую величину, например 'MAINPARAM', так и подставлять строковую переменную.

Если не обнаружится данного ini файла, или не обнаружится указанной секции, или в секции не будет заданного параметра, то функция возвращает значение_по_умолчанию. Это свойство очень удобно для задания в программе начальных значений.

Далее список функций, которые можно применять при чтении данных из ini файла. Их структура аналогична вышерассмотренной команде за исключением двух: возвращает параметр определенного типа, значение по умолчанию имеет тот же тип.

ReadString - чтение строковой переменной

ReadBool - чтение логической переменной

ReadDate - чтение даты

ReadTime - чтение времени

ReadDateTime - чтение даты и времени в одном параметре

ReadFloat - чтение числа с плавающей точкой

Запись параметров:

Все команды записи параметров являются процедурами. Они не возвращают никаких параметров, и, в случае неудачной попытки записи, программа выдает сообщение об ошибке.

Для записи параметра Integer:

IniFile.WriteInteger(СЕКЦИЯ,ПАРАМЕТР,ЗАПИСЫВАЕМАЯ_ВЕЛИЧИНА);

Далее для записи других типов данных:


WriteString - запись строковой переменной

WriteBool - запись логической переменной

WriteDate - запись даты

WriteTime - запись времени

WriteDateTime - запись даты и времени в одном параметре

WriteFloat - запись числа с плавающей точкой

Рассмотрим пример.

Создаем новый проект, в форму Form1 помещаем компоненты Edit, ComboBox, два компонента CheckBox, две кнопки BitBtn.
Для компонента ComboBox1 изменяем свойство Style в csDropDownList и редактируем свойство Items, занося туда несколько произвольных строк. Дальше для BitBtn1 свойство Caption изменяем на Сохранить, а для кнопки BitBtn2 - Восстановить. Окно принимает вид, показанный на рисунке.
В процедуре события  FormCreate для окна Form1 пишем команду
ReadParams;
В процедуре события FormClose для окна Form1 пишем команду
WriteParams;
В процедуре нажатия на кнопку "Сохранить" аналогично закрытию окна команда WriteParams, для кнопки "Восстановить" - ReadParams.
Дальше привожу текст модуля Unit1 обратите внимание на выделенные строки. Их следует прописать вручную.
// ----------------Начало модуля Unit1------------------
unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

StdCtrls, Buttons, inifiles; // подключение модуля, позволяющего работать с ini файлами

type

TForm1 = class(TForm)

Edit1: TEdit;

ComboBox1: TComboBox;

CheckBox1: TCheckBox;

CheckBox2: TCheckBox;

BitBtn1: TBitBtn;

BitBtn2: TBitBtn;

procedure ReadParams; // процедура чтения параметров

procedure WriteParams; // процедура записи параметров

procedure FormCreate(Sender: TObject);

procedure FormClose(Sender: TObject; var Action: TCloseAction);

procedure BitBtn1Click(Sender: TObject);

procedure BitBtn2Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.ReadParams;

Var IniFile:TIniFile; // объявление переменной, через которую будут обрабатываться данные


begin

IniFile:=TIniFile.Create('project.ini'); // создаем переменную

Form1.Top:=IniFile.ReadInteger('FORM1','Form1Top',Form1.Top); // верхнее положение окна

Form1.Left:=IniFile.ReadInteger('FORM1','Form1Left',Form1.Left); // левое положение окна

Form1.Height:=IniFile.ReadInteger('FORM1','Form1Height',Form1.Height); // высота

Form1.Width:=IniFile.ReadInteger('FORM1','Form1Width',Form1.Width); // ширина

Edit1.Text:=IniFile.ReadString('FORM1','Edit1Text',Edit1.Text); // текст в Edit1

ComboBox1.ItemIndex:=IniFile.ReadInteger('FORM1','ComboBox1ItemIndex',ComboBox1.ItemIndex); // выбранный пункт

CheckBox1.Checked:=IniFile.ReadBool('FORM1','CheckBox1Checked',CheckBox1.Checked); // состояние CheckBox1

CheckBox2.Checked:=IniFile.ReadBool('FORM1','CheckBox2Checked',CheckBox2.Checked); // состояние CheckBox2

IniFile.Free; // если сами создаем, то сами уничтожаем.

end;

procedure TForm1.WriteParams;

Var IniFile:TIniFile;

begin

IniFile:=TIniFile.Create('project.ini');

IniFile.WriteInteger('FORM1','Form1Top',Form1.Top);

IniFile.WriteInteger('FORM1','Form1Left',Form1.Left);

IniFile.WriteInteger('FORM1','Form1Height',Form1.Height);

IniFile.WriteInteger('FORM1','Form1Width',Form1.Width);

IniFile.WriteString('FORM1','Edit1Text',Edit1.Text);

IniFile.WriteInteger('FORM1','ComboBox1ItemIndex',ComboBox1.ItemIndex);

IniFile.WriteBool('FORM1','CheckBox1Checked',CheckBox1.Checked);

IniFile.WriteBool('FORM1','CheckBox2Checked',CheckBox2.Checked);

IniFile.Free;

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

ReadParams;

end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

begin

WriteParams;

end;

procedure TForm1.BitBtn1Click(Sender: TObject);

begin

WriteParams;

end;

procedure TForm1.BitBtn2Click(Sender: TObject);

begin

ReadParams;

end;

end.
// ----------------Конец модуля Unit1------------------
Вышерассмотренный пример можно забрать по этой ссылке (2 Кб).
Запустите программу несколько раз и проследите за восстановлением внешнего вида программы. Попробуйте сохранять и восстанавливать параметры с помощью кнопок.


Для нашего примера создается файл project.ini, который находится в каталоге WINDOWS. Просмотрите его содержимое. У меня он получился такой.
[FORM1]

Form1Top=256

Form1Left=147

Form1Height=301

Form1Width=368

Edit1Text=111

ComboBox1ItemIndex=2

CheckBox1Checked=1

CheckBox2Checked=0
Если у вас в программе несколько окон, параметры которых необходимо сохранять все в том же окне, то используйте различные секции с названиями соответствующих окон, например программа с двумя окнами будет "запоминать" свое состояние следующим образом:
[FORM1]

Form1Top=100

Form1Left=100

Form1Height=300

Form1Width=500
[FORM2]

Form1Top=200

Form1Left=200

Form1Height=100

Form1Width=200

Итак, файл project.ini пишется и читается в каталоге WINDOWS. Следующий пример позволяет построить путь к файлу ini относительно каталога, где находится ваша запущенная программа. Этот кусок программы помещается до создания IniFile, и еще необходимо объявить строковую переменную Path.
Var Path:String; // Переменная, где будет храниться путь к программе

...

Path:=Application.ExeName; // полный путь и название запущенной программы

Path:=ExtractFileDir(Path); // отбрасываем название программы. Остается путь.

if Path[Length(Path)]<>'\' then Path:=Path+'\'; // если последний символ не \ то добавить его

{Последняя строка нужна для присвоения последнего символа '\',

потому что при обрезке файла 'C:\PROG.EXE' получим 'C:\',

а при обрезке 'C:\WINDOWS\PROG.EXE' получим 'C:\WINDOWS' }

IniFile:=TIniFile.Create(Path+'project.ini'); // полный путь к программе и имя ini файла
Теперь ini файл лежит недалеко от запускаемой программы.
С уважением, ведущий уроков Semen semen@krovatka.net

Корректное построение программного кода. Рисование. Построение графика функции


Бесконечные циклы, громоздкие операции и прочие зависшие программы.


Ну кто из вас хоть раз не завершал зависшее приложение через Ctrl+Alt+Del? А ведь в большинстве случаев в ситуации, когда программа зависает, виноват программист, а не пользователь, который ее довел до этого состояния. Зачастую программы имеют в себе до 30% различной проверочных команд. Когда проверяются переполнение списков, проверяется корректность созданного объекта, компонента, проверяется или запущено то или иное внешнее приложение. Именно от этих условий и зависит дальнейшее корректное выполнение вашего приложения.

Рассмотрим принцип работы следующей программы.

Допустим, в оконном модуле есть две процедуры. Первая процедура - реакция на событие нажатия на кнопку Button1, вторая процедура - реакция на событие нажатия на кнопку Button2. Исходный текст процедур:

procedure TForm1.Button1Click(Sender: TObject);

begin

while true do

   begin

      // бесконечный цикл

   end;

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

ShowMessage('Нажата кнопка Button1');

end;

В первой процедуре мы видим типичный программный код, при выполнении которого программа зависнет. Или говоря языком юзера - бесконечно выполняет одно и то же. Естественно, что выхода из такой процедуры не существует, а сама программа ожидает выхода из нее, чтобы выполнять другие события, в том числе и системные.

Принцип работы операционной системы windows основан на посылке сообщений компонентам программы. События при этом становятся в очередь и ожидают своей обработки. Поскольку выхода из первой процедуры нами не предусмотрено, то очередь сообщений не будет обрабатываться, и при нажатии на кнопки Ctrl+Alt+Del через некоторое время, мы видим, что приложение "не отвечает на системные запросы".

В подобных случаях это совсем не означает, что программа не работает. Она может зациклиться на обработки одной или нескольких команд, ожидая выхода из цикла по определенному условию (например, прочтения данных с диска) или обрабатывать большой объем данных опять таки в одной процедуре. А, как известно, корректная работа программы забота программиста, то есть нас с вами. Мы должны учесть все возможные ситуации, просчитать приблизительное время обработки операций даже на слабых компьютерах, при необходимости применить компонент ProgressBar, для того, чтобы пользователь не скучал, глядя на "неотвечающую" программу.




В вышерассмотренном примере, если нажать на кнопку Button1, а потом Button2, то реакция на событие нажатия на вторую кнопку будет помещена в очередь сообщений, но само событие не будет выполнено никогда. Кроме того, приложение перестанет получать все системные сообщения, т.е. окно нельзя будет переместить, свернуть, закрыть. Окно автоматически перестанет перерисовываться (имею в виду старые операционные системы windows).

Есть выход. В автоматически создаваемом компоненте при запуске программы Application, есть такая команда ProcessMessages, которая приостанавливает выполнение текущей процедуры, пока не будет выполнена очередь всех сообщений, посланных в программу. Если вставить строку

Application.ProcessMessages;

внутри продолжительных операций, то гарантировано получим не полностью зависшее приложение, а при умелом написании программного кода, из подобного цикла можно выйти по желанию пользователя (кнопка "Стоп"). И без создания дополнительного потока (считаю, что создание второго потока в программе занятие трудное и в больших проектах с этим можно запутаться). Такой пример "вечного" цикла мы рассматриваем в этом уроке.

Исключительные ситуации.

Это другой неприятный момент для пользователя и программиста. Это когда появляются неожиданные англоязычные ошибки и выполняемая процедура завершает свою работу с места появления ошибки. Разработчики Borland Delphi таким контролем за ошибками и выходом из работающих модулей программы позаботились об остальных частях программы. Если ошибка появилась в первой строке, то имеет ли смысл выполнять вторую?

Рассмотрим такой пример:

procedure TForm1.Button1Click(Sender: TObject);

Var x,y,z:Double; // переменные.

begin

   x:=0;

   z:=1;

   y:=z/x; // деление на нуль

   ShowMessage(FloatToStr(y));

end;

При выполнении данного кода мы получаем сообщение "Floating point division by zero". И процедура дальше уже не обрабатывается. После команды деления мы не видим его результат ShowMessage(РЕЗУЛЬТАТ) Или еще такой пример:


procedure TForm1.Button1Click(Sender: TObject);

begin

   Form1.ShowModal; // открыть окно модально

   ShowMessage('Окно открыто модально');

end;

Если у нас окно Form1 уже открыто, то повторная попытка открыть его модально приведет к возникновению ошибки "Cannot make a visible window modal". И опять программа завершает обработку процедуры. Это приведены примитивные процедуры, но бывает такие, что даже сам программист не ожидает появления подобной ситуации, но нужно предусмотреть все.

Что же делать, если мы открыли несколько файлов, заполняем список, создали вручную некоторые объекты. Выход из процедуры приводит к тому, что файлы не закрыты и повторный вызов этой процедуры приведут к возникновению другой ошибки - открытие уже открытого файла. Созданные объекты мы теряем при выходе из процедуры, а при повторном их создании, если они глобальные, получаем еще одну ошибку. А эта трата драгоценной памяти, которая занимается объектом и остается даже после выхода их программы. И так далее со всеми вытекающими последствиями.

Во-первых, надо при всех операциях деления предусматривать что-то подобное

if x<>0 then y:=z/x; // если x не равно нулю, то делить.

даже если x никогда в вашей программе не становится нулем, но поверьте, ничего невозможного не бывает.

Я имею в виду, что при всех арифметических операциях нужно проверять все возможные входящие данные на их совместимость. Например:

Var st:String;

y:Double;

...

InputQuery('Ввод числа','введите любое число',st);

y:=StrToFloat(st);

Если не проконтролировать корректность ввода числа в строковую переменную st, то при попытке преобразования его в переменную типа Double, произойдет ошибка.

При сомнениях, насчет отображения того или иного окна на экране использовать, например:

if not Form1.Visible then Form1.ShowModal; // если окна нет на экране, то открыть его модально

И так далее. Есть много опасных с этой точки зрения команд, которые при совместном использовании с другими командами или при отсутствии таковых обязательно вызывают ошибку.


В уроке15 мы рассматривали пример, в котором есть директива компилятору об отключении контроля ошибок ввода-вывода. Мы открывали файл, при отключенном автоматическом контроле, после чего сами обрабатывали ошибку открытия.

{$I-} // отключение контроля ошибок ввода-вывода

Reset(f); // открытие файла для чтения

{$I+} // включение контроля ошибок ввода-вывода

if IOResult<>0 then // если есть ошибка открытия, то

   begin

      ShowMessage('Ошибка открытия файла C:\1.TXT');

      Exit; // выход из процедуры при ошибке открытия файла

   end;

Такой метод можно применять только с старыми форматами команд, совместимыми с языком программирования pascal и работающими только с устройствами ввода-вывода.

В delphi можно применять следующее:

try // начало опасно-ошибочной части процедуры:

   Form1.ShowModal;

except // если возникла ошибка, то выполняется следующее:

   ShowMessage('Ошибка открытия окна');

end; // конец try

ShowMessage('Дальнейшая обработка процедуры');

Заметьте, что этот кусок кода не вызовет ошибки выполнения. Программа просто вам сообщит об ошибке открытия окна, после чего процедура продолжит свое выполнение. Еще обратите внимание, что если в части, после except возникнет другая ошибка выполнения, то процедура таки точно завершит свою работу немедленно. Все команды, которые могут вызвать ошибку надо писать после try и до except.

Теперь можно без опаски потери данных, после except корректно закрыть все открытые файлы, при необходимости окна, сообщить пользователю о возникшей ошибке и самому выйти из процедуры с помощью Exit.

Аналогом try-except есть try-finally

procedure TForm1.Button1Click(Sender: TObject);

Var StringList:TStringList; // список строк

begin

try

   StringList:=TStringList.Create; // создание списка строк

finally // при успешной обработки создания списка:

   StringList.Free; // удалить и освободить память


end;

ShowMessage('Дальнейшая обработка процедуры');

end;

После finally следует строки программы, которые следует выполнять в случае успешной обработки команд, следующие после try.

Рисование на форме или на компоненте PaintBox. Генератор колебаний. Пример

Большое спасибо Александру, за идею примера, который мы рассмотрим в этом уроке. Извиняюсь за то, что перевел тему с генератора на построитель функции синуса и косинуса.

У формы и у компонента PaintBox
(страница System палитры компонентов) есть свойство Canvas. Это свойство представляет собой растровое изображение, на котором можно рисовать или в которое можно загрузить рисунок. Я не буду рассказывать подробно об особенностях рисования, тем более, что я в этом не силен, но основные сведения дам.

Свойство Canvas доступно только во время работы приложения и с помощью его можно:

* Canvas.Brush - свойство фона. У него можно установить свойство Canvas.Brush.Color в необходимый цвет и с помощью следующей за ней командой Canvas.FillRect(ClientRect) можно очистить всю рабочую область компонента под заданный цвет. С помощью свойтва Canvas.Brush можно в качестве фона установить рисунок. Для этого есть свойство Canvas.Brush.Bitmap, которому нужно присваивать переменную с растровым рисунком.

* Canvas.MoveTo(x,y) - устанавливает перо в заданную точку, где x и y - координаты точки, относительно компонента. Начало координат, точка [0,0] находится в верхнем левом углу. После этой команды перо установлено, но точка не нарисована. Чтобы провести линию от текущего положения пера до заданного Canvas.LineTo(x,y). Поставить точку определенного цвета на холсте Canvas.Pixels[x,y]:=ЦВЕТ_ТОЧКИ.

* Через Canvas можно писать текст, рисовать дуги, сектор круга, овал, прямоугольник, ломаную линию, кривую.

* Свойства пера содержатся в Canvas.Pen. Здесь можно задать толщину пера Canvas.Pen.Width:=ТОЛЩИНА_В_ТОЧКАХ. Задать цвет Canvas.Pen.Color:=ЦВЕТ.

Рассмотрим пример генератора колебаний, который можно скачать здесь (3,1КБ). Генератор сам по себе есть только на экране. Колебания, которые он создает, рассчитываются по формуле. Следовательно, он вам может оказаться полезным. Можно изменить рассчетную функцию и вы получите для этой функции график.


Рекомендую скачать пример, и уже на нем рассматривать работу. Тем не менее, даю основные сведения, которые вам понадобятся при самостоятельном написании этой программы.

1. Программа имеет только одно окно Form1, у которого сразу переименовываем заголовок на подходящее название.

2. Устанавливаем свойство Form1.Position в poDesktopCenter, чтобы окно при каждом запуске и при любом экранном разрешении всегда было ровно посередине экрана.

3. Устанавливаем свойство Form1.BorderStyle в bsSingle, для неизменяемого размера окна. Оставляем во вложенных свойствах BorderIcons только biSystemMenu в true, остальные в false. Это для того, чтобы окно нельзя было свернуть в значек, развернуть во весь экран и окно имело иконку в заголовке.

4. Устанавливаем в форму компонент PaintBox, два компонента RadioButton, CheckBox, три кнопки Button и TrackBar, расположенный на странице Win32.

5. RadioButton1.Caption переименовываем в "Sin". Этот флаг будет признаком рисования синусоиды. RadioButton2.Caption переименовываем в "Cos" - косинусоида. Начальное значение флага Checked для RadioButton1 в true.

6. CheckBox1.Caption переименовываем в "Все". Если флаг установлен, то будет рисоваться два графика.

7. Названия кнопок Button1 - "Старт", Button2 - "Стоп (пауза)" и Button3 - "Выход". Названия на кнопках меняются через свойство Caption. Теперь назначение этих кнопок понятно.

8. Компонент TrackBar1 свойство минимального значения Min устанавливаем в 1, максимальное значение Max - 50.

9. PaintBox1, на котором будет непосредственно рисоваться график размеры высоты Height=140, ширина Width=500.

Привожу текст модуля для окна Form1.

Сразу после слова implementation
в модуле окна объявляем глобальные переменные, которые будут доступны из любой процедуры в этом модуле.

Var stop:boolean; // признак рисования

x:Integer; // координата оси X

Реакция на событие нажатия на кнопку Button1 (Начало рисования)

procedure TForm1.Button1Click(Sender: TObject);


Var y:Integer; // ось Y

begin

if x=0 then // если точка в начале координат, то:

   begin

      PaintBox1.Canvas.Brush.Color:=clWhite; // цвет фона белый

      PaintBox1.Canvas.FillRect(ClientRect); // заливка всей рабочей области

   end;

stop:=false; // флаг старта процесса рисования

While not stop do // бесконечный цикл, пока флаг остановки не поднят:

   begin

      if (RadioButton1.Checked)or(CheckBox1.Checked) then // если установлен "Sin" или "Все", то:

         begin

            y:=Round(Sin(pi*x/100)*50)+70; // вычисление положения синусоиды

            PaintBox1.Canvas.Pixels[x,y]:=clBlack; // нарисовать черную точку

         end;

      if (RadioButton2.Checked)or(CheckBox1.Checked) then // если установлен "Cos" или "Все", то:

         begin

            y:=Round(Cos(pi*x/100)*50)+70; // вычисление положения косинусоиды

            PaintBox1.Canvas.Pixels[x,y]:=clBlack; // нарисовать черную точку

         end;

      inc(x); // увеличить значение X на едицину. Аналог X:=X+1

      if x>500 then // если X вышел за пределы PaintBox1, то:

         begin

            x:=0; // установить X на начало координат

            PaintBox1.Canvas.Brush.Color:=clWhite; // Цвет фона белый

            PaintBox1.Canvas.FillRect(ClientRect); // Очистка рабочей области PaintBox1


         end;

      Sleep(TrackBar1.Position); // Процедура "засыпает" на заданное время в миллисекундах

      Application.ProcessMessages; // Обработка всей очереди сообщений

   end;

end;

Коротко расскажу работу этой процедуры.

Как только нажата кнопка "Старт" Компонент PaintBox1 очищается и начинается бесконечный цикл While, выйти из которого можно только, пока переменная Stop не примет значение true. Это можно сделать кнопкой Button2, соответствующая процедура которой обработается во время Application.ProcessMessages. С помощью бегунка TrackBar1 можно менять скорость рисования кривой. Этот параметр передается в команду Sleep.

Процедура нажатия на кнопку остановки Button2:

procedure TForm1.Button2Click(Sender: TObject);

begin

Stop:=true; // установить флаг остановки процесса рисования

end;

Процедура создания окна Form1OnCreate:

procedure TForm1.FormCreate(Sender: TObject);

begin

x:=0; // начальное значение X

end;

Если нажата кнопка "Выход", то реакция на это событие будет таким:

procedure TForm1.Button3Click(Sender: TObject);

begin

Close; // закрыть окно

end;

И реакция перед закрытием окна OnClose. Без этой процедуры, если рисование включено, то окно не закроется.

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

begin

Stop:=true; // остановить (если включен) цикл рисования

end;

После запуска программы, установки флажка "Все" и нажатии на кнопку "Старт" на экране отобразится этот график:



В принципе, с нашей программой можно отслеживать практически любые функции. Просто надо описать свою отслеживаемую функцию вместо моих строк Y:=Sin...

Если вы хотите убрать постоянную прорисовку графика функции, то следующий код в программе

      if x>500 then // если X вышел за пределы PaintBox1, то:

         begin


            x:=0; // установить X на начало координат

            PaintBox1.Canvas.Brush.Color:=clWhite; // Цвет фона белый

            PaintBox1.Canvas.FillRect(ClientRect); // Очистка рабочей области PaintBox1

         end;

измените на:

      if x>500 then // если X вышел за пределы PaintBox1, то:

         begin

            x:=0; // установить X на начало координат

            Stop:=true; // остановка рисования

         end;

Напомнню, что график функции вы видите перевернутым. Начало координат в мониторе находится в верхнем левом углу. Математики тут найдут выход. Для переворачивания функции нужно от значения рабочей высоты элемента, на котором рисуем, вычитать значение функции

Y:=140 - ФУНКЦИЯ;

Как видите, поле для экспериментов велико. Приступайте к самостоятельному изучению.
С уважением, ведущий уроков Semen semen@krovatka.net

Всплывающая подсказка


Этот вид подсказок удобен, прежде всего, тем, что назначение элемента, на который подведен указатель мыши, видно до непосредственной его активации. Например, кнопка "Применить" можно снабдить подсказкой "Сохранить все настройки без закрытия окна".

Как же настроить цвет фона, паузу перед появлением подсказки, время отображения подсказки, мы рассмотрим в этой части урока.

Во время инициализации и запуска приложения на выполнение, происходит считывание системных переменных в переменные программы, в т.ч. и параметров всплывающей подсказки. Эти переменные и свойства можно менять во время работы приложения, некоторые еще при проектировании, добываясь тем самым отличия от стандартных windows-приложений.

Для того, чтобы у интересующего вас параметра появлялась всплывающая подсказка, необходимо, прежде всего установить сам текст подсказки. Это свойство Hint. Для каждого компонента программы Hint может быть разный.

Второй шаг, это указать, что подсказку для интересующего нас компонента надо показывать. Это свойство ShowHint. Его нужно установить в true для отображения, и false наоборот.

Два вышеперечисленных свойства доступны еще до запуска программы, во время редактирования проекта. По умолчанию свойство ShowHint имеет значение false, т.е. подсказка не высвечивается.

Целесообразнее устанавливать эти свойства в инспекторе объектов, чем программно, этим самым вы не засоряете текст кода программы. В редакторе кода порой бывает так много строк, что трудно сразу разобраться в возможной ошибке и подобные строки могут сбивать с толку. Исключением в нашем случае может быть постоянно меняющаяся всплывающая подсказка. Например, при изменении функционального назначения кнопки, меняется и текст подсказки.

Программная установка текста всплывающей подсказки для кнопки Button1:

Button1.Hint:='Нажми на меня, и ты узнаешь, что ты наделал';

Программная активация подсказки для кнопки Button1 (только дает возможность всплывающей подсказке высвечиваться, а не выводит ее на экран!)




Button1.ShowHint:=true;

Теперь поговорим про цвет фона подсказки и про некоторые другие параметры.

Цвет фона всплывающей подсказки для всего приложения меняется очень легко.

Application.HintColor:=ЦВЕТ;

Цвет задается через зарезервированные константы или цифровым кодом. Например, белый цвет может быть задан с помощью константы clWhite, с помощью шестнадцатеричного представления $00FFFFFF, или обычным, привычным нам десятичным числом 16777215. Ниже представлена таблица цветов, которые определены как стандартные константы цветов.

Константа
Цвет
clAqua
clBlack
clBlue
clFuchsia
clGray
clGreen
clLime
clMaroon
clNavy
clOlive
clPurple
clRed
clSilver
clTeal
clWhite
clYellow

Стандартный цвет всплывающей подсказки, установленный в свойствах экрана windows, хранится в константе clInfoBk. Она является элементом со статусом "только для чтения" и изменение недопустимо.

Установка паузы перед появлением всплывающей подсказки.

Application.HintPause:=ЗНАЧЕНИЕ;

Значение задается в миллисекундах.

Изменение этого свойства может вам показаться бессмысленным. Но иногда необходимо, чтобы пользователь видел всплывающую подсказку немедленно после наведения на компонент мышкой. Иногда нужно вообще убрать на продолжительный срок появление подсказки, чтобы она лишний раз не привлекала к себе внимание. Обычное начальное значение в windows 500 мс (полсекунды).

Время отображения подсказки на экране равно 2500 мс (2,5 с). Программно для вашего приложения это время задается следующим образом.

Application.HintHidePause:=ЗНАЧЕНИЕ;

Это все по общим свойствам всплывающей подсказки. Можете для закрепления материала скачать пример по этой ссылке. Этот пример может быть интересен и специалистам в области программирования. В нем объясняется принцип отображения подсказки в компоненте TStatusBar без каких-либо дополнительных компонентов.

Еще примечание к примеру: числовые значения вводятся в текстовом виде в компонентах Edit1 и Edit2. На ходу происходит их преобразование в числовые с помощью циклов try-except (урок 18). Некорректное введение числового значения контролируется. Но если программу запускать на выполнение через оболочку delphi (run), а не из проводника windows или другим методом запуска, то delphi контролирует самостоятельно такое преобразование и внутри цикла try-except. И в случае обнаружения ошибки прервет выполнение программы. Это неудобство встречается в delphi3, delhpi4, delphi5 (опробовано лично мной). Поэтому такой некорректный ввод чисел лучше всего тестировать, отдельно запуская программу, или с помощью процедуры Val.
С уважением, ведущий уроков Semen semen@krovatka.net

Сообщения для пользователя


Автор уроков по delphi для начинающих благодарит вебмастера, программиста, просто хорошего человека jurii jurii@karelia.ru, без которого написание этого урока было бы просто невозможным.

В этом уроке мы с вами рассмотрим организацию некоторых сообщений в программе.

Сообщения присутствуют повсюду: когда вы пытаетесь закрыть не сохраненный проект, при появлении ошибки, когда программа сообщает о некотором событии.

Сообщения, "вылетающие" при работе программы, можно разделить на те, которые программист предусмотрел, и системные сообщения.

В большинстве случаев второй тип сообщений имеет непонятный для обычного пользователя вид. Как правило, сообщается англоязычный термин, иногда имеется и шестнадцатеричный адрес ошибки. Например, сообщение "I/O Error" говорит программисту или пользователю об ошибке ввода-вывода. Это может быть попытка записи данных в неоткрытый файл, попытка открыть несуществующий файл и т.п. Если такая ошибка в вашей русскоязычной версии программы имеет место, то, скорее всего данной ситуации программист просто не предусмотрел. В таких случаях, программа может себя повести совершенно непредсказуемо. Ведь вы помните из прошлых уроков, что не проконтролированный кусок программы на присутствие ошибки ведет к моментальному выходу из обрабатываемой процедуры, со всеми вытекающими из этого последствиями.

Но это маленькое отклонение от темы. Идея такова, надо самостоятельно просчитывать все возможные случаи и самостоятельно обрабатывать эти ситуации. Иногда, если надо, предупреждать пользователя об ошибках, может даже сообщать об окончании обработки данных. Вот о таких сообщениях мы и поговорим в этом уроке.

Можно разделить все программные сообщения на: информационные сообщения ("Загрузка данных с дискеты завершена") , предупреждающие сообщения ("Файл модифицирован. Сохранить?"), сообщения об ошибке ("Файл данных программы не найден. Требуется переустановка программы"). Эта разбивка на типы сообщений является, естественно, не полным, его можно продолжать, но об этом немного позже.




Ради экономии своего времени, вы можете всегда, из любого места программы показать пользователю, к примеру, следующее сообщение:



Конечно, сообщение может быть и серьезным, можно подобные окошки использовать и для других целей вывода информации. Дело ваше. Я иногда этот вид сообщений использую для вывода информации о состоянии программы на этапе программирования.

В чем же заключается экономия времени и экономия текста кода программы. Такое сообщение выводится на экран одной строчкой:

ShowMessage('Привет!');

Тип данных в скобках - String.

Все довольно просто, мы с вами эту команду неоднократно применяли в прошлых уроках.

На этой команде работа процедуры (не всей программы!) приостанавливается. Пока пользователь не нажмет на кнопку Ok, работа с приложением становится невозможным, т.е. нельзя "добраться" до окна, расположенного позади. Т.е. это сообщение открывается модально.

Как вы заметили, заголовок окна простой. Он содержит в себе текст, который отображен на панели задач. По умолчанию имеет название запускаемого EXE файла. Изначально это Project1, в последствии вы его можете сохранить под другим именем ("Save Project As..."), при этом название проекта, вместе с ним название компилируемого EXE файла меняется.

Изменить название запущенной программы в панели задач можно в любом месте программы с помощью команды:

Application.Title:='Название программы';

К примеру, вы обрабатываете довольно объемный размер данных (чтение файлов), и хотите показывать процент выполнения задания прямо в панели задач (как это сделано в программе DrWeb). Ведь пользователь не всегда сможет смотреть на ваш 10-минутный процесс обработки данных, а скорее всего переключится на другую, менее трудоемкую операцию (карточный пасьянс), постоянно следя за процессом обработки на панели задач.

Изначально, еще до запуска программы на выполнение, на этапе разработки, вы можете это задать название программы в панели задач с помощью главного меню delphi "Project", дальше пункт "Options...", в открывшемся окне на вкладке Application указать в поле Title необходимую строку. Эта строка и будет отображена в панели задач. При этом следует помнить, что слишком длинная фраза в кнопке на панели задач полностью не будет показана. При этом она будет обрезана троеточием, а для того, чтобы узнать полное название запущенной программы, нужно будет подвести мышку (всплывающая подсказка Hint вам покажет полное название).


Вы уже знаете все прелести простой команды вывода строки ShowMessage. Из недостатков отмечу, что нельзя отдельно от названия программы в панели задач, менять заголовок окошка, нельзя получить иконку в окошке, нельзя отображать другие кнопки.
В delphi есть, можно сказать, встроенная команда отображения окна сообщения. Звучит оно так:

MessageDLG(ТЕКСТ_СООБЩЕНИЯ,ТИП_СООБЩЕНИЯ,КНОПКИ,ИНДЕКС_ПОМОЩИ);

Скажу сразу, что к нашим программам мы пока не пишем дополнительно файлов справки, поэтому ИНДЕКС_ПОМОЩИ у нас всегда будет нулевым. Для информации скажу, что если у нас таковой файл имеется, то можно в таком сообщении сделать кнопку "Help". Если пользователь озадачен вопросом или сообщением, то может, не закрывая этого окна, узнать подробнее о дальнейших этапах работы при выборе того или иного пункта.

ТЕКСТ_СООБЩЕНИЯ - строковая величина. Как в предыдущей команде, сообщение показывается внутри окна.

ТИП_СООБЩЕНИЯ - может принимать несколько значений. От этих значений зависит содержимое заголовка и иконка в левом верхнем углу окна.

Тип сообщения
Описание
Вид окна
mtWarning
Можно использовать в предупреждающих сообщениях. Например, "Вы действительно желаете удалить все данные с диска С:"

mtError
Обычное окошко вывода сообщения об ошибки. Всем знаком его вид т.к. это наиболее частое окно в windows :)

mtInformation
Какая-нибудь информация. Например, "Не найден файл настройки, создается заново"

mtConfirmation
Это запрос. Запрос на сохранение перед выходом, спрашивает перед удалением параметра, и т.п. На ваш собственный вкус

mtCustom
Это сообщение полностью аналогично ShowMessage


КНОПКИ - содержит в себе массив кнопок, которые следует показывать в сообщении.

Даю перечень кнопок.
* mbYes

* mbNo

* mbOK

* mbCancel

* mbHelp

* mbAbort

* mbRetry

* mbIgnore

* mbAll
Рассказывать про каждую кнопку не буду, т.к. все равно ее название нельзя сменить. А если вам англоязычный термин непонятен, то тогда какой смысл ее применять :).


Массив кнопок задается в квадратных кавычках []. Например, нам надо задать три кнопки Yes, No, Cancel. Это делается так [mbYes,mbNo,mbCancel].

Поскольку кнопки в сообщении могут быть разные, то MessageDLG является функцией. Она возвращает результат нажатой кнопки.

Соответственно указанным выше кнопкам результат может принимать следующие значения
* mrNone - окно сообщения закрыто не с помощью кнопки (Alt+F4 или кнопкой "закрыть")

* mrAbort

* mrYes

* mrOk

* mrRetry

* mrNo

* mrCancel

* mrIgnore

* mrAll

Рассмотрим пример. Нам надо спросить у пользователя о дальнейших действиях перед выходом из программы.

1. Сохранить файл.

2. Не сохранять файл.

3. Продолжить редактирование.

Var R:Word; // переменная, в которой хранится результат

...

R:=MessageDLG('Сохранить файл перед выходом?',mtConfirmation,[mbYes,mbNo,mbCancel],0);

if R=mrYes then // если нажата кнопка Yes

   begin

      // сохраняем файл и завершаем программу

   end;

if R=mrNo then // если нажата кнопка No

   begin

      // завершаем работу программы без сохранения

   end;

if R=mrCancel then // если нажата кнопка Cancel

   begin

      // продолжаем работу без сохранения

   end;

Мы рассмотрели команду MessageDLG. Это очень гибкая команда, есть много достоинств, но есть один существенный недостаток - англоязычный интерфейс.

Следующая команда использует системные сообщения пользователю вашей операционной системы. Т.е., если у вас установлена, например немецкая версия windows, то кнопки будут иметь соответствующие названия на немецком языке.

Вот эта команда:

MessageBox(Handle,ТЕКСТ_СООБЩЕНИЯ,ЗАГОЛОВОК_ОКНА,ТИП_СООБЩЕНИЯ);

Первый параметр - указатель на владельца окна сообщения. Этот параметр вам пока ничего не говорит, устанавливайте его в Handle (это ссылка на окно, откуда это сообщение вызывается).

ТЕКСТ_СООБЩЕНИЯ и ЗАГОЛОВОК_ОКНА - имеют тип PChar, поэтому, во избежание недоразумений и появления неизвестного рода ошибок, выдаваемых компилятором, меняйте тип String в PChar "на ходу". Например:


MessageBox(Handle,PChar('ТЕКСТ_СООБЩЕНИЯ'),PChar('ЗАГОЛОВОК_ОКНА'),...

Это был перевод из одного типа строковой величины в другой тип.

Теперь поговорим о немного сложном параметре ТИП_СООБЩЕНИЯ. Он включает в себя иконку и кнопки.

Кнопки:

* MB_ABORTRETRYIGNORE - кнопки "Прервать", "Повторить", "Пропустить".

* MB_OK - кнопка "Ok".

* MB_OKCANCEL - кнопки "Ok", "Отмена".

* MB_RETRYCANCEL - кнопки "Повторить" и "Отмена".

* MB_YESNO - две кнопки "Да" и "Нет".

* MB_YESNOCANCEL - кнопки "Да", "Нет", "Отмена".

Для того, чтобы отобразить иконку, нужно указать:

* MB_ICONEXCLAMATION

* MB_ICONWARNING

* MB_ICONINFORMATION

* MB_ICONASTERISK

* MB_ICONQUESTION

* MB_ICONSTOP

* MB_ICONERROR

* MB_ICONHAND

Если у вас в сообщении несколько кнопок, а по умолчанию нужно выбрать определенную, то такая кнопка задается:

MB_DEFBUTTON1 - где последняя цифра указывает номер кнопки, выбранной по умолчанию. Это свойство может быть полезным, например, чтобы обезопасить данные от случайного уничтожения. "Удалить файл?". Две кнопки - "Да", "Нет". По умолчанию мы программно выбираем вторую кнопку. Если пользователь сразу нажал на Enter, не осознавая своего поступка, можно сказать по привычке, то ничего страшного не произойдет.
Как же указать параметры иконки, кнопок, кнопки по умолчанию в одном параметре ТИП_СООБЩЕНИЯ. Очень просто. Простым знаком +

Например:

MessageBox(Handle,PChar('Выйти из программы?'),PChar('Мое сообщение'),MB_ICONINFORMATION+MB_OKCANCEL+MB_DEFBUTTON2);
Выглядит это в программе так, как показано на рисунке:
Итак, MessageBox можно считать идеальной командой вывода сообщения пользователю, которая будет совместима со всеми языковыми версиями windows.
Контроль нажатия на кнопку в MessageBox мы осуществляем аналогично MessageDLG, только возвращаемая величина может принимать следующие значение (соответственно нажатой кнопке):
 
* IDABORT

* IDCANCEL

* IDIGNORE

* IDNO

* IDOK

* IDRETRY

* IDYES

Программа психологических тестов


Автор благоларит jurii jurii@karelia.ru, за идею этого урока.

На этом уроке мы закрепим знания, полученные в прошлых уроках, и как всегда, изучим много нового.

Программа "Тесты" скорее всего, может пригодиться многим. Тем более что мы ее будем писать, изначально универсальной. Вопросы с ответами будет подгружаться из специального текстового файла.

Рассмотрим пошаговую работу над созданием программы.

Каждую глобальную задачу, как и программу, необходимо разбивать на отдельные, мелкие задачи, определить пути и методы решения. Не определив изначально пути решения, можно постепенно войти в тупик. В тупике задача становится невыполнимой, приходится все начинать сначала. Поэтому вы всегда должны четко представлять свою задачу и четко представлять общие направления решения, возможно даже алгоритм решения проблемы.

Итак, вот пример такого планирования.

Сам текст теста располагается в текстовом файле. Значит, в одном текстовом файле у нас будет храниться вся необходимая для него информация, в том числе и служебная, относительно скрытая от пользователя. Почему мы делаем именно текстовый файл, а не работаем с двоичным, потому что заносить данные в такой файл легко, не требуется писать отдельной программы создания файлов теста, написать и изменить можно в любом текстовом редакторе.

Для планировки структуры такого файла мы должны разбить всю хранимую в нем информацию на отдельные строки. Эти строки могут быть объединены по одинаковым признакам. Читаться такой файл программой будет по строкам.

Каждый тест должен имееть следующую структуру:

1. Название.

2. Количество вопросов.

<Начало блока вопросов>

      3. Вопрос.

      4. Возможные варианты ответов (для нашей программы сделаем четыре варианта     ответов).

      5. Баллы, присуждаемые за тот или иной ответ.

<Конец блока вопросов>

6. Несколько вариантов результатов теста, зависимых от набранных баллов, которые показываются тестируемому человеку (для нашей программы сделаем четыре результата теста).




Все жестко заданные количества строк легко изменить, сделав соответствующие изменения в коде программы, или добавив дополнительный параметр в файл теста. Возможно, если с вашей стороны будет интерес, эту тему мы продолжим рассматривать в следующих уроках.

Тест, взятый для примера в этом уроке, был любезно выдерт мною из первого попавшегося журнала на столе моей сестры. Это журнал "Cool girl" №23 от 1998 года. Староват, да ничего, подойдет. Да простят меня мужчины, но это тест для молодых девушек. Впрочем, если что не нравится - текстовый редактор вам в руки и вперед. Если вы отошлете файл своих собственных тестов (рабочий, проверенный файл тестов, в частности, ищу тест определения темперамента человека) мне, автору уроков, то это будет замечательно. Все ваши тесты постараюсь разместить прямо в этом уроке.

Как создать файл тестов. Создаем в любом текстовом редакторе, желательно в блокноте windows новый файл.

Важно, в таком редакторе убрать опцию "автоперенос слов", чтобы нас не сбивало с толку перенесенные строки, ведь они у нас иногда будут очень длинными.

Первая строчка в файле должна содержать название теста. Для моего теста "Умеешь ли ты отдыхать?". Каждая такая строчка должна завершаться нажатием на клавишу Enter (перевод строки).

Следующая строка содержит число вопросов в тесте. Их у нас 16.

Дальше у нас следует блок вопросов и ответов на них. Этот блок должен повторяться количество раз, заданное в строке количества вопросов - 16.

Первая строка - вопрос.

Вторая строка - первый ответ.

Третья строка - количество баллов за этот ответ.

Четвертая строка - второй ответ.

и т.д.

В конце файла теста следуют результаты теста.

Первая строка - число, начальный интервал баллов.

Вторая строка - число, конечный интервал баллов.

Третья строка - вариант результата теста.

и т.д. для всех результатов теста.

Файл теста мы с вами рассмотрели, теперь примемся за написание программы.

Снова спланируем нашу задачу.

1. Сделаем, чтобы при запуске программы, она спрашивала у нас, какой тест необходимо загрузить. Если теста нет или пользователь отказывается открывать тест, то программа завершает свою работу.


2. При успешном выборе теста, программа открывает этот файл, считывает название, считывает количество вопросов, и предлагает первый вопрос с ответами на него.

3. После перебора всех вопросов открывается окно результатов теста.

4. Завершение работы программы.

Внешний вид программы можно спланировать так, вопрос и возможные варианты ответов будут размещены на разных панелях. На панелях разместим компоненты отображения текста (Label) и по одной кнопке на каждый вариант ответа.

Окно результатов теста планировать не будем, текст результата можно высветить на первых порах разработки программы с помощью команды ShowMessage. В последствии вы можете самостоятельно дорабатывать уже свою программу красочным оформлением, интересными особенностями.

В delphi создаем новый проект.

Для главной формы проекта Form1 устанавливаем свойство начальной позиции окна Position по центру рабочего стола poDesktopCenter. Свойтсво BorderStyle в bsDialog, чтобы окно имело неизменяемый размер.

Оформляем внешний вид программы.

В форме размещаем пять компонентов Panel, после каждого добавленного компонента изменяем свойство Align в alTop, и убирая текст из свойства Caption. Для последнего, пятого компонента Panel5 свойство Align устанавливаем в alClient.

Для более красочного оформления и отличия панели вопроса от панелей ответов, панель Panel1 свойство BevelInner установим в bvLowered. Установим размер компонентов и формы сверху вниз, как показано на рисунке.

Теперь в панелях разместим по компоненту Label, убрав свойство автоматического изменения размера AutoSize в False, и установив свойство автоматического перевода строки WordWrap в True. Если текст не будет помещаться по длине компонента Label, то эти слова будут перенесены в другую строку. Для каждого вопроса устанавливаем по одной кнопке Button (или BitBtn для размещения в кнопке рисунка). Переименовываем названия кнопок на названия "Ответ 1", "Ответ 2" и т.п.

После размещения всех визуальных компонентов в программу, установим размер компонентов Label1 - Label5 в максимально возможное, чтобы вопросы и ответы полностью были видны.


Для улучшения восприятия вопросов и ответов для этих же компонентов установите свойство шрифта Font на свой вкус.

Последний компонент - OpenDialog для открытия файла вопросов. Можете установить фильтр для открываемых файлов, например только для файлов с расширением TES для того, чтобы случайно не открыть другой, не файл теста.

Окончательный внешний вид окна вашей программы смотрите на этом рисунке.

Теперь примемся за написание кода программы.

Объявим несколько глобальных переменных, которые будут действительны за пределами одного модуля.

После слова implementation
в редакторе кода пишем:

Var f: TextFile; // текстовый файл теста

    FOpen: Boolean; // признак открытого файла

    TestName: String; // название теста

    QCurrent: Integer; // текущий номер вопроса

    QCount: Integer; // всего вопросов в тесте

    QUser: Integer; // сумма баллов

В инспекторе объектов выбираем компонент Form1 и для события (страница Events) OnShow (сработает до отображения окна на экране) пишем:

procedure TForm1.FormShow(Sender: TObject);

begin

FOpen:=false; // снимаем признак открытого файла (мы его еще не открывали)

if OpenDialog1.Execute then // если файл теста выбран,

   begin // то:

      AssignFile(f,OpenDialog1.FileName); // привязка имени выбранного файла в диалоге к файловой переменной

      {$I-} // отключения автоматической обработки ошибок ввода-вывода

      Reset(f); // открываем файл

      {$I+} // возвращаем опцию контроля ошибок ввода-вывода

      if IOResult<>0 then // если была ошибка открытия файла, то

         begin

            MessageDLG('Ошибка открытия файла',mtError,[mbOk],0); // сообщение на экран

            Application.Terminate; // завершить работу программы


         end;

      FOpen:=true; // включаем признак, что у нас файл был открыт

      ReadLn(f,TestName); // читаем из файла название теста

      Form1.Caption:='Психологический тест - '+TestName; // заголовок окна устанавливаем в название теста

      ReadLn(f,QCount); // читаем количество вопросов теста

      QCurrent:=0; // текущий вопрос - нулевой

      QUser:=0; // сброс количества баллов за ответы

      LoadQuestion; // переход к процедуре чтения вопроса

   end else Application.Terminate; // если файл теста не выбран, то завершить работу программы

end;

Единственной непонятной строкой для компилятора будет строка LoadQuestion. Эту процедуру мы создадим самостоятельно. В редакторе кода в разделе public объявляем новую процедуру:

procedure LoadQuestion;

А после написанной нами процедуры реакции на появления окна, после строки окончания процедуры "end;", но до строки окончания модуля "end." создадим самостоятельно заголовок процедуры:

procedure TForm1.LoadQuestion;

begin

end;

Вы только что самостоятельно создали свою собственную процедуру, которую можно вызвать на выполнение из любого места программы (даже из другого модуля), в этой процедуре доступны все свойства и методы компонентов программы.

Пишем внутри этой процедуры обработку чтения одного вопроса из файла:

procedure TForm1.LoadQuestion;

Var Str_F: String; // временная строковая переменная для чтения данных из файла

begin

Inc(QCurrent); // увеличиваем порядковый номер текущего вопроса на 1 (равноценно команде QCurrent:=QCurrent+1)

// ЧИТАЕМ ВОПРОС

ReadLn(f,Str_F); // читаем вопрос из файла

Label1.Caption:=Str_F; // присваиваем компоненту текст вопроса

// ЧИТАЕМ ПЕРВЫЙ ОТВЕТ

ReadLn(f,Str_F); // читаем первый ответ из файла

Label2.Caption:=Str_F; // присваиваем компоненту текст ответа


ReadLn(f,Str_F); // читаем количество баллов за этот ответ

Button1.Tag:=StrToInt(Str_F); // присваиваем пользовательскому свойству Tag балл за этот ответ компоненту Button1

// ЧИТАЕМ ВТОРОЙ ОТВЕТ

ReadLn(f,Str_F);

Label3.Caption:=Str_F;

ReadLn(f,Str_F);

Button2.Tag:=StrToInt(Str_F);

// ЧИТАЕМ ТРЕТИЙ ОТВЕТ

ReadLn(f,Str_F);

Label4.Caption:=Str_F;

ReadLn(f,Str_F);

Button3.Tag:=StrToInt(Str_F);

// ЧИТАЕМ ЧЕТВЕРТЫЙ ОТВЕТ

ReadLn(f,Str_F);

Label5.Caption:=Str_F;

ReadLn(f,Str_F);

Button4.Tag:=StrToInt(Str_F);

end;

Возможно, вы еще не встречались со свойством Tag для компонентов. Это, можно сказать, пользовательское свойство. Оно имеет тип Integer, и ни на что конкретно в программе не влияет. Программист может его использовать в собственных целях, что иногда уменьшает количество объявляемых переменных в программе. Еще бывает удобно, когда компонент имеет в свойстве Tag какое-нибудь служебное число, как в нашем случае.

Создадим обработчик события для кнопок OnClick. Для всех кнопок это будет одна процедура и создается она стандартным способом, выбрав в инспекторе объектов компонент Button1, перейдя на страницу Events и щелкнув по полю OnClick, или можно дважды щелкнуть по самой кнопке Button1. Вот текст процедуры:

procedure TForm1.Button1Click(Sender: TObject);

Var i: Integer; // временная числовая переменная для выявления балла за отвеченный вопрос

s1: String; // временная переменная первого результата теста

p11,p12: Integer; // цифровой промежуток первого результата теста (минимальное число и максимальное)

s2: String;

p21,p22: Integer;

s3: String;

p31,p32: Integer;

s4: String;

p41,p42: Integer;

begin

i:=0; // сброс переменной

if Sender=Button1 then i:=Button1.Tag; // если была нажата кнопка Button1, то в i занести значение кол-ва баллов за этот ответ

if Sender=Button2 then i:=Button2.Tag;

if Sender=Button3 then i:=Button3.Tag;

if Sender=Button4 then i:=Button4.Tag;

QUser:=QUser+i; // увеличение общего количества баллов

if QCurrent=QCount then //если количество вопросов исчерпано, то


   begin

      ReadLn(f,p11); // чтение результатов и промежутков баллов из файла

      ReadLn(f,p12);

      ReadLn(f,s1);

      ReadLn(f,p21);

      ReadLn(f,p22);

      ReadLn(f,s2);

      ReadLn(f,p31);

      ReadLn(f,p32);

      ReadLn(f,s3);

      ReadLn(f,p41);

      ReadLn(f,p42);

      ReadLn(f,s4);

      if (QUser>=p11) and (QUser<=p12) then ShowMessage(s1); // выявление попадания в тот или иной промежуток и выдача результата теста

      if (QUser>=p21) and (QUser<=p22) then ShowMessage(s2);

      if (QUser>=p31) and (QUser<=p32) then ShowMessage(s3);

      if (QUser>=p41) and (QUser<=p42) then ShowMessage(s4);

      Close; // закрыть программу

   end else LoadQuestion; // если кол-во вопросов не исчерпано, то прочитать из файла новый вопрос (переход к процедуре чтения)

end;

Далее, для того, чтобы эта процедура вызывалась при нажатии и на другие кнопки (Button2 - Button4) нужно для каждой отдельно выбранной кнопке в инспекторе объектов установить реакцию на событие OnClick из ниспадающего списка в Button1Click.

И последнее, для завершения работы надо корректно закрыть открытый нами ранее файл. Это делается с помощью события Form1Close (выбрать компонент Form1 и создать процедуру OnClose) с помощью признака открытого файла FOpen написать одну строку внутри процедуры:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

begin

if FOpen then CloseFile(f); // если файл был открыт, то закрыть его

end;

Ваша программа готова к работе.
Рассмотренную программу вместе с прилагаемым тестом можно забрать по этой ссылке (7,5КБ).
С уважением, ведущий уроков Semen semen@krovatka.net

Программный поиск файлов


В этом уроке мы с вами ознакомимся с основными принципами программной организации поиска файлов. Для начала определимся, зачем нам это может быть нужно. Например, вам нужно при запуске программы на выполнение просканировать определенный каталог на присутствие DOC файлов, и при наличии таковых открыть их на редактирование или напечатать. А как вам такая идея: фоновый поиск EXE файла в сети, и при обнаружении новой версии, автоматическое обновление.

Многим известны программы, где можно искать файлы, правила поиска файла. Файлы можно искать как с файловых командирах (нортон, волков, дос навигатор, фар), так в любой операционной системе. В операционной системе windows диалоговое окно поиска файла вызывается "Пуск" - "Поиск" - "Файлы и папки". В открывшимся окне необходимо задать условие искомого файла (название, маска) и путь начального поиска (каталог). На других вкладках этого диалогового окна можно расширить возможности поиска по дате изменения, по содержащемуся тексту, по размеру.

Вспомним правила поиска файлов. Вы можете задать как имя искомого файла, так и его маску, если название неизвестно или необходимо найти несколько. Т.е. применяя специальный шаблон поиска, вы можете организовать условия выборки найденных файлов. Сразу оговорюсь, что поиск можно применять как к файлам, так и к каталогам. Будем их называть элементами файловой системы. В шаблон маски искомых элементов может входить:

1. Буквы и цифры в названии и расширении.

2. Символ * (звездочка, математический знак "умножить"), заменяющий любое количество всевозможных букв и цифр в названии или расширении.

3. Символ ? (знак вопроса), заменяющий одну букву или цифру в названии или расширении искомого элемента.

Например, вы ищите все текстовые файлы с расширением TXT. В поле имени искомого файла вам нужно ввести "*.TXT" (пишется без кавычек) и система найдет все такие файлы в указанном диске или каталоге. Если вам надо найти все файлы с названием semen, то в поле поиска файла нужно ввести "semen.*". Если вам нужно найти элементы с третьей буквой k и с первой буквой t в расширении, то вводите "??k*.t*". Здесь знак вопроса указывает на любой символ, третьим символом по порядку идет буква k, далее название файла (каталога) может состоять из любого количества букв и цифр, указываем звездочку. В расширении первая буква t, дальше следует любое расширение.




Примечание: файлы и каталоги в операционной системе windows ищутся без учета регистра, т.е. строчние и прописные буквы не различаются.

Теперь рассмотрим программный поиск файлов с помощью языка программирования object pascal.

Вся организация цикла поиска, а именно это и есть цикл с продолжением поиска, сводится к:

1. Задание условий поиска. Это каталог и маска искомого элемента или элементов, атрибуты элемента(ов). При задании условий поиска сразу происходит поиск первого подходящего под условие. Это функция FindFirst.

2. Продолжение поиска следующего элемента по заданным в первом пункте условиям. Это функция FindNext и она может вызываться сколько угодно раз, пока все файлы и каталоги, удовлетворяющие условию, не будут найдены.

3. Закрытие поиска и освобождение памяти, выделяемую системой под поиск. Команда FindClose.

Функция FindFirst.

Синтаксис:

FindFirst (КАТАЛОГ_ПОИСКА_И_МАСКА_ФАЙЛА , АТРИБУТЫ_ИСКОМОГО_ФАЙЛА , ПОИСКОВОЯ_ПЕРЕМЕННАЯ);

где: Каталог для поиска и маска искомого элемента - строковая величина, имеющая тип String, может, например, содержать 'c:\*.*' - все элементы в корне диска С. Обратите внимание, что указывается полный путь для поиска.

Атрибуты искомого элемента это пользовательские или системные атрибуты, которые может иметь файл (каталог, метка диска). Вот их перечень:

faReadOnly - Файлы "только чтение". Такой атрибут устанавливается на файлы, которые не рекомендовано изменять, удалять. Такой атрибут имеют файлы, например, записанные на компакт-дисках.

faHidden - Скрытые файлы. При обычных установках браузера и командира эти файлы невидимы.

faSysFile - Системные файлы.

faVolumeID - Файл метки диска. Такой элемент в своем имени имеет название диска (максимум 11 символов).

faDirectory - Атрибут признака каталога.

faArchive - Обычный файл. По умолчанию устанавливается на заново создаваемых файлах.

faAnyFile - Если установить в качестве атрибута искомых элементов, то будет произведен поиск по всем вышесказанным атрибутам.


Эти вам нужно искать только элементы, имеющие атрибут "каталог" и "скрытый", то можно применить знак математического сложения, например faDirectory + faHidden.

Поисковая переменная имеет тип TSearchRec. В нее, при успешном результате поиска, будет занесены все необходимые данные о найденном файловом элементе.

Поскольку FindFirst является функцией, то она должна сама возвращать некоторое значение. Это значение имеет тип Integer и означает результат поиска файла (код ошибки поиска). Если файл найден, то принимает нулевое значение.

Функция FindNext.

FindNext ( ПОИСКОВАЯ_ПЕРЕМЕННАЯ );

Эта функция продолжает поиск, заданный в функции FindNext. Возвращает значение результата поиска (нулевое в случае успешного поиска).

Процедура FindClose.

FindClose ( ПОИСКОВАЯ_ПЕРЕМЕННАЯ );

Закрывает поиск и освобождает память, выделенную системой под поиск.

Теперь рассмотрим пример. Допустим, нам надо найти все файлы и каталоги в каталоге DELPHI, находящийся на диске C:. В дальнейшем, вы можете самостоятельно, изменяя маску, менять условия поиска. Для формы с компонентом ListBox1 и кнопкой Button1 реакция на OnClick по кнопке:

procedure TForm1.Button1Click(Sender: TObject);

Var SR:TSearchRec; // поисковая переменная

FindRes:Integer; // переменная для записи результата поиска

begin

ListBox1.Clear; // очистка компонента ListBox1 перед занесением в него списка файлов

FindRes:=FindFirst('c:\delphi\*.*',faAnyFile,SR); // задание условий поиска и начало поиска
While FindRes=0 do // пока мы находим файлы (каталоги), то выполнять цикл

   begin

      ListBox1.Items.Add(SR.Name); // добавление в список название найденного элемента

      FindRes:=FindNext(SR); // продолжение поиска по заданным условиям

   end;

FindClose(SR); // закрываем поиск

end;

Представленный пример кода, в принципе, является основой для организации более углубленного поиска, поиска файлов по времени создания, по содержащимся словам. Если вы запустите эту программу на выполнение, то при нажатии на кнопку Button1 вы увидите в списке в первой и второй строке элементы "." и "..". Это элементы, имеющие атрибут "каталог". Первый содержит связь с корневым каталогом диска, второй содержит связь к каталогом верхнего уровня. Со вторым вы встречаетесь в дисковых командных оболочках, например нортон, когда выбираете каталог ".." и нажимаете на "ввод". Тем самым вы попадаете в каталог на уровень выше. Естественно, в нашей поисковой программе такие элементы не надо вносить в список, поэтому мы игнорируем их нахождение. Исправляем процедуру нажатия на кнопку Button1:


procedure TForm1.Button1Click(Sender: TObject);

Var SR:TSearchRec;

    FindRes:Integer;

begin

ListBox1.Clear;

FindRes:=FindFirst('c:\delphi\*.*',faAnyFile,SR);

While FindRes=0 do

   begin

      if ((SR.Attr and faDirectory)=faDirectory) and // если найденный элемент каталог и

      ((SR.Name='.')or(SR.Name='..')) then // он имеет название "." или "..", тогда:

         begin

            FindRes:=FindNext(SR); // продолжить поиск

            Continue; // продолжить цикл

         end;

      ListBox1.Items.Add(SR.Name);

      FindRes:=FindNext(SR);

   end;

FindClose(SR);

end;

В этом случае, при нахождении каталога с именем "." или с именем ".." программа продолжит обработку цикла поиска без вывода найденного имени элемента в компонент списка ListBox1.

Теперь рассмотрим тип TSearchRec. Он имеет в себе несколько полезных свойств:

Name - название найденного каталога (файла);

Size - размер файла в байтах;

Attr - атрибуты каталога (файла);

Time - упакованное значение времени и даты создания каталога (файла).

Все вышеперечисленные свойства мы уже рассмотрели или они понятны сразу, за исключением свойства Time. Оно имеет тип Integer и содержит в себе упакованное значение даты и времени создания файла. Распаковка производится с помощью функции FileDateToDateTime, которая в результате возвращает значение даты и времени.

Теперь добавим в нашу форму компонент DateTimePicher1 (страница Win32) и допишем несколько строк.

procedure TForm1.Button1Click(Sender: TObject);

Var SR:TSearchRec;

    FindRes:Integer;

begin

ListBox1.Clear;

FindRes:=FindFirst('c:\delphi\*.*',faAnyFile,SR);


While FindRes=0 do

   begin

      if ((SR.Attr and faDirectory)=faDirectory) and

      ((SR.Name='.')or(SR.Name='..')) then

         begin

            FindRes:=FindNext(SR);

            Continue;

         end;

      if FileDateToDateTime(SR.Time)<DateTimePicker1.Date then // если у файла (каталога) дата создания меньше, чем установлено в DateTimePicker1, то

         begin

            FindRes:=FindNext(SR); // продолжить поиск

            Continue; // продолжить цикл

         end;

      ListBox1.Items.Add(SR.Name);

      FindRes:=FindNext(SR);

   end;

FindClose(SR);

end;

Как вы уже заметили, мы отбираем файлы и каталоги по дате создания, начиная с указанной в компоненте DateTimePicker1.

Теперь попробуем организовать поиск файлов во всех вложенных каталогах. Это не так просто, как может показаться на первый взгляд. Нам придется вручную организовывать весь цикл входа-выхода из каталога, перебор файлов. Немного сложноватый материал, но возможно те из вас, кто уже работал с языком программирования pascal или другим, знакомы с технологией многократности и многовложенности использования одного и того же программного кода. Коротко объясню алгоритм работы такой программы.

1. Задание начальных условий поиска, поиск первого элемента.

2. Если найден файл, то выводим его и соответственно обрабатываем (выводим в список, открываем, удаляем и т.п.).

3. Если найден каталог, то начинаем новую процедуру поиска. Но программный код остается прежним. Мы просто заново вызываем и входим в эту же процедуру поиска.


4. Обрабатываем таким же образом все вложенные в этот каталог файлы и каталоги (начинаем новый поиск в обнаруженном каталоге).

5. Если элементов во вложенном каталоге больше нет, то обработка процедуры поиска в нем завершается, и мы выходим из нее. При этом мы оказываемся в том же месте, откуда и вызвали эту процедуру. Но она была вызвана из этой же процедуры. Поэтому программа продолжает свое выполнение дальше с момента возврата.

Таким образом, сколько витков программа наматывает на так называемый клубок, столько витков она и размотает. Программа на выполнении проходит все дерево вложенных каталогов, выполняя один и тот же кусок программного кода! И при этом данные условий поиска не перепутываются, и для каждой уникальной процедуры они сохраняются.

Рассмотрим пример. Создайте новый проект. Для создания отдельной процедуры поиска нам нужно объявить ее в соответствующем разделе (создаем ее вручную, поэтому и самостоятельно объявляем).

В разделе public пишем строку:

procedure FindFile(Dir:String);

А в разделе кода программы, до слова "end." вставляем пустой каркас процедуры

procedure TForm1.FindFile(Dir:String);

begin

end;

На форму вставляем компонент списка ListBox1, Button1, Edit1. Для компонента Edit1 свойство Text устанавливаем в "c:\delphi\". Обратите внимание на последний символ, знак "\", присутствие которого в начальном пути поиска обязательно. Дальше процедура OnClick для кнопки Button1 выглядит следующим образом:

procedure TForm1.Button1Click(Sender: TObject);

begin

ListBox1.Clear; // очистка списка файлов

FindFile(Edit1.Text); // поиск файлов с начальными условиями, заданных в Edit1

end;

Созданная нами вручную процедура поиска:

procedure TForm1.FindFile(Dir:String);

Var SR:TSearchRec;

    FindRes:Integer;

begin

FindRes:=FindFirst(Dir+'*.*',faAnyFile,SR);

While FindRes=0 do

   begin

      if ((SR.Attr and faDirectory)=faDirectory) and

      ((SR.Name='.')or(SR.Name='..')) then


         begin

            FindRes:=FindNext(SR);

            Continue;

         end;

      if ((SR.Attr and faDirectory)=faDirectory) then // если найден каталог, то

         begin

            FindFile(Dir+SR.Name+'\'); // входим в процедуру поиска с параметрами текущего каталога + каталог, что мы нашли

            FindRes:=FindNext(SR); // после осмотра вложенного каталога мы продолжаем поиск в этом каталоге

            Continue; // продолжить цикл

         end;

      ListBox1.Items.Add(SR.Name);

      FindRes:=FindNext(SR);

   end;

FindClose(SR);

end;

Если вы в компоненте Edit1 в качестве начального условия поиска файлов зададите корневую папку диска, например "С:\", то вы получите полный перечень всех файлов на данном диске. Обратите внимание на скорость поиска файлов и скорость работы вашей программы.
С уважением, ведущий уроков Semen semen@krovatka.net

Защита программы от бесплатного использования. Испытательный срок.


Вместо предисловия.

Безопасность и хакинг. Это два разных понятия. Сколько будут существовать компьютерные программы их всегда будут ломать. Чем сильнее защита, тем к ней сильнее интерес, и сколько будет существовать его величество дизассемблер, столько программистам терпеть убытки. Насколько обеднеет всемогущий Билл я не знаю, но по карману обычного программера бьют больно. Виноваты в этом не только хакеры. Как ни странно виноваты в этом мы сами. Покупая нелицензионные программные продукты, бродя по просторам internet в поисках крека мы тем самым поддерживаем пиратские компании, производящие подпольные лазерные диски, увеличиваем спрос на крек, принуждаем отдельных людей заниматься взломом. А государство со своей позиции борьбы с теневой экономикой (а это именно и есть теневая экономика) выглядит не очень. По крайней мере по сравнению с западными странами.

На этом уроке мы рассмотрим немного теории, которая будет полезна как для начинающих, так и для профессиональных программистов. Для вторых этот урок будет наиболее полезен, поскольку считаю, что профессионал начинается там, где его программы стают востребованными. Вам может показаться удивительным, но от начинающего писателя программ до разработчика востребованных приложений - один шаг. В моем случае переход от языка программирования pascal на object pascal занял не больше недели, после чего была написана довольно интересная программа, структура которой и порядок работы  до сих пор загадка :).

В большинстве случаев перед продажей программного продукта заказчик требует испытательный или ознакомительный срок. Возможно даже, что он испытывает программу уже на ранних стадиях разработки, желая убедиться в работоспособности, убедиться в правильности направления процесса программирования. И будет совсем неудивительно, если он вдруг откажется от ваших услуг, услуг программиста. Это не означает, что заказчик бросил пользоваться программой. Возможно, его устраивает и текущая версия. Или ваша программа полностью готова, тогда такая проблема может иметь место. Труднее может быть в случае распространения программы в сети internet, когда вы ее пускаете в свободное плаванье, надеясь, что она не утонет в море бизнес приложений, а принесет желаемый доход в ваш кошелек.




Автор этой статьи имеет довольно горький опыт работы с заказчиком. Свыше 50% полностью готовых и работоспособных программ до сих пор не оплачены, находятся на стадии "испытания" или отбракованы навсегда. Скорее всего, во всем этом виноват наш постсоветский менталитет, стремление к халяве. Тут вы можете не согласиться, но я у вас спрошу, а ваш интернет браузер, почтовая программа, наконец, операционная система, купленная? Порой, мы даже и не задумываемся над этой проблемой, покупая компакт диск с новой версией программы, с новой версией операционной системы. Труд человека должен быть оплачиваем. Программный продукт этот как и результат производства, например стул, требует затрат как физических, так и умственных сил.

Как бы то ни было, а защищать свои программы от наглого халявного эксплуатирования вы должны самостоятельно. И просто так погрозить пальчиком бывает недостаточно. По уровню вашей квалификации в области программирования пользователь может и судить о возможной угрозе, поэтому может хватить и простого предупреждения. Если ваши программы предназначены для определенного заказчика, то, скорее всего у этой организации, группы людей, или одного пользователя плохо с программированием (если сами не смогли справиться со своей проблемой). Отсюда вывод - поломать вашу защиту им будет настолько же проблематично, насколько самим написать программу от начала и до конца. Я не учитываю тех людей, которые этим занимаются профессионально, так называемых хакеры, но услуга взлома одной средней программы может стоить намного дороже ее самой. Имеет смысл ломать только при глобальном, неоднократном использовании пользователями, например, ломают даже аппаратную защиту HASP, используемую бухгалтерским программным комплексом 1С. Это я веду к тому, что абсолютно 100% защиты от взлома, нелицензионного тиражирования нет. Поэтому вы не должны слишком много сил отдавать организации защиты. Если на то будет большая необходимость, ее сломают все равно. Но иметь защиту от простого чайника надо обязательно. Хватит и нескольких строчек продуманного программного кода.


Так же вы не должны использовать один и тот же алгоритм защиты в программах, поставляемых одному и тому же заказчику. Это усложнит поиск обходных путей "лицензирования".

Теперь поговорим о методах защиты и способах воздействия на сознание бесстыжего пользователя. Это может быть как простое предупреждение с временной блокировкой работы, как приложения, так и всей операционной системы или прекращение работы программы на данном компьютере вообще (с физическим удалением всех компонентов программы - деинсталляция).

Итак, программа написана и установлена на компьютер заказчика. Ей дан испытательный срок. Не дай Бог вам сказать прямо об этом пользователю. Тогда он может искать окружные методы еще до ответных действий программы. При этом его действия могут быть непредсказуемы, например позовет знакомого программиста со стороны и похвастается, что эта программа имеет какую-то хитрую защиту. Если возникнет остановка производства после, например полугодового использования всего программного комплекса, то вас найдут и за кратчайшие сроки, причем тут стоимость программы может и возрасти. Инфляция, знаете ли :). Как кто-то когда-то сказал, стоимость данных может быть большей самого компьютера. Поэтому ставя зеркализацию дисков на компьютеры, в первую очередь думают про сохранение данных на случай выхода из строя дискового накопителя.

Допустим, вы даете пользователю 30 дней испытательного срока. Об этом должно быть обязательно где-нибудь ненавязчиво написано, типа "Незарегистрированная версия. Пожалуйста, зарегистрируйтесь в 30 дневный срок". Желательно в заставке при загрузке программы (урок 8), можно и в окне "О программе". Тогда вы можете во всем обвинить заказчика, что он не придерживался условий эксплуатации программы, если одним прекрасным утром программа исчезает с диска со всеми набранными на месяц данными.

Теперь мы подошли к способам бесплатной работы, предупреждения (и наказания). Я разделю их на три группы:

1. Постоянное предупреждение о том, что программа еще не куплена, и просьба связаться с программистом. Поверьте, такая методика может приносить больше плодов, чем другие известные на сей день. Приведу пример. Мне предлагали для примера моей новой программы одну программу, которая замечательно работает, имеет отличный интерфейс, много удобных дополнительных утилит. Пользователю она не понравилась потому, что каждые три минуты высвечивалось окошко, что ее необходимо зарегистрировать. Это окошко закрывается одним нажатием кнопки мышки, но пользователя это очень бесит. Вы можете в таком окне еще привязать таймер, который будет "вешать" программу на заданное время при OnCloseQuery этого окна. Это будет сильно тормозить нормальную работу с программой, потому, что окно будет закрываться несразу.




procedure TForm2.FormCloseQuery(Sender: TObject; var CanClose: Boolean);

begin

Sleep(5000); //Ждать 5 секунд, после чего процедура закрытия окна продолжится

end;

2. Отказ от работы после истечения заданного срока. Довольно большой процент программ, имеющих изначально не тронутую хакерами защиту, имеют такой способ испытательного срока.

Слабым местом для этого вида защиты есть то, где вы храните информацию о первом запуске программы (дата, время, количество запусков и т.п.). Наиболее оптимальным местом хранения такой информации есть реестр windows. Реже - ini файлы, еще реже - другие файлы.

Почему реестр, а не другое место. На мое мнение, для чайников туда место закрыто (некоторые даже не знают что это такое), обилие информации, среди которой можно затерять вашу скрытую информацию. Самым идеальным будет совместное использование как реестра, так и INI файла. Если в одном из них информация уничтожена начинающим хакером, то вы всегда можете это обнаружить и программно наказать. Сразу скажу, что есть хорошо известные многим программы, которые отслеживают любые изменения в реестре, предательски выдавая добавленные строки. Достаточно их удалить и произойдет сброс счетчика дней.

Программа с таймером в реестре работает по такому принципу. Первый запуск программы, поиск в реестре вашей специальной записи. Если таковой нет (первый запуск), то она создается. Запись включает в себя дату и время запуска.

ВНИМАНИЕ! Многие хакерские примочки не что иное, как простой запускатель программы с переводом времени (переводится время назад и программа запускается). Поэтому повторяем операцию записи последнего запуска обязательно и при завершении работы программы, возможно даже после определенного времени работы, скажем в 5 минут.

Последующие запуски сводятся к чтению данных и исправление их на текущее, за одним исключением. Если текущее время меньше, чем было в прошлом запуске (время было переведено назад), то, во-первых, оно переписывается под текущее (время перевели, например, чтобы в бухгалтерской программе провести задним числом документ, многие программы неправильно решают, что их пытаются обмануть и блокируют свою работу), во-вторых увеличивается специальный штрафной балл на единицу, соответственно, срок использования программы увеличивается на единицу дней.


Если таких переводов времени слишком много (работает автоматический перевод времени), то срок использования программы закончится уже после 30 запусков.

Если после очередного запуска вы насчитали (ТЕКУЩАЯ_ДАТА - ДАТА_ПРЕДЫДУЩЕГО_ЗАПУСКА + КОЛИЧЕСТВО_ДНЕЙ_РАБОТЫ_ПРОГРАММЫ)>=30, тогда вы знаете что делать :). Можно просто завершать работу, а можно поступить так, как сказано в следующем пункте.

3. По истечению бесплатного использования программы она делает специальную пометку в реестре и полностью удаляется. Если пользователь оставил заначку в виде резервной копии программы, то она тоже сама удалится, как только прочитает эту пометку при запуске. Хотя я и предлагаю вашему вниманию этот метод, все же не советую его использовать т.к. он наиболее конфликтный. 100% гарантии у вас нет, что эта процедура не сработает на уже купленной версии, и вы можете тем самым потерять ценного клиента. Но, тем не менее, вот идея. Перед уничтожением всех данных программы, а обычно они располагаются в каталоге вашей программы, необходимо закрыть все открытые файлы. Далее все файлы по очереди уничтожаются командой:

DeleteFile(НАЗВАНИЕ_ФАЙЛА);

При этом их восстановление возможно специальной утилитой, например UnErase. Сложнее будет восстановить физически затертый файл. Для этого вам надо такой файл создать заново процедурой Rewrite и закрыть. Далее файл нулевого размера можно удалить. Восстановление таких данных без специальных средств дисковых редакторов невозможно. Стандартными средствами восстанавливаются только перезаписанные нами нулевые файлы. Пользователь при этом только напрасно потеряет время. Можно сказать уверенно, что все данные из такого удаленного файла навсегда утеряны.

Если вы еще не находитесь в шоке или азарте от такого откровения, то я вам расскажу про то, как удалить вашу запущенную программу.

Ваша программа уже очистила все свои рабочие файлы и готова сама удалиться. Но если вы попытаетесь ее удалить, то ничего не выйдет. Программа запущена и пока она работает, ее и с места двинуть нельзя. Удалить ее только после закрытия может внешний процесс. Для этого вы программно создаете BAT файл, в котором пишите:


:povtor

del ВАША_ПРОГРАММА

if exist ВАША_ПРОГРАММА goto povtor

Далее запускаете этот bat файл на выполнение командой WinExec или ShellExecute, после чего закрываете свою программу. Используя преимущества многозадачности системы, вы порождаете внешний процесс, который удалят вашу программу.

Для тех, кто не знаком с командами DOS разъясняю. Первая строчка является меткой, к которой можно перейти из любой строки bat файла командой GOTO. Вторая строка пытается удалить вашу программу с диска. Пока ваша программа запущена, ей это не удастся сделать. Третья строка проверяет наличие файла на диске. Если он еще не удален, то происходит переход на метку з названием POVTOR, где снова производится попытка удалить программу.

Откровенно скажу, что этот цикл повторится не один раз, пока ваша запущенная EXE программа не исчезнет, но я вас уверяю, что пользователь не успеет глазом моргнуть, как для него все будет уже закончено. Единственным следом разрушающей деятельности останется этот BAT файл, а может и не остаться, если вы четвертой строкой укажете:

del ТЕКУЩИЙ_BAT_ФАЙЛ

Я пробовал, BAT файл действительно может удалить сам себя.

А теперь три примера для каждой рассмотренной группы.

Пример первый вы можете скачать по этой ссылке (5КБ). Не буду подробно останавливаться на работе этой программы, можете самостоятельно раскрыть проект и изучить работу как отдельных команд, так и всей программы в целом.

Пример второй немного сложнее.

На уроке 17 мы рассмотрели методы сохранения данных в реестр. Скажу, что такая методика для нашего случая не подходит т.к. все данные записываются в раздел HKEY_CURRENT_USER, и в многопользовательской операционной среде такие данные будут вестись по-разному. Когда время для одного пользователя истечет, то программой может пользоваться другой пользователь. А их может быть неограниченное количество. Нам же надо все данные писать в одно место реестра. Выберем это место HKEY_LOCAL_MACHINE\SOFTWARE с подключом, например LAB. Название подключа можете придумать самостоятельно, а опытные пользователи, имеющие опыт работы с реестром, вообще могут придумать оригинальное место хранения данных своей программы. Вообще, в HKEY_LOCAL_MACHINE\SOFTWARE пишут свои данные различные программы. Перед началом написания такой программы не забудьте добавить в раздел подключаемых модулей Uses модуль Registry для работы с реестром.

Пример можно забрать по этой ссылке
(4КБ).

В третьем примере вы можете узнать, как программа уничтожает себя и все свои данные. Ее вы можете забрать по этой ссылке
(3КБ). Пример удаления запущенной EXE программы был взят мной из моей же программы, в которой есть свойство автоматического обновления по сети.
С уважением, ведущий уроков Semen semen@krovatka.net

Программа с многоязычным интерфейсом


Автор благодарит читателя этих уроков, программиста Alexey Salo за идею, без которой написание данного материала было бы невозможным. Сразу отмечу, что урок получился не совсем для начинающих, количество написанного кода в примерах испугало даже меня, но тем не менее, попробуйте разобраться. Повсюду даны описание команд, некоторые мы рассматривали в предыдущих уроках. Возможно непонятные моменты рассмотрены более подробно.

Для начала немного теории.

Ваша собственная программа может быть полезна не только вам, но и вашим друзьям, организации, где вы работаете. Если вы работаете не только на себя. Живя в нашем веке компьютерных технологий, информация может распространяться с довольно большой скоростью. Примером тому служат нашумевшие недавно волны интернет-вирусов, за считанные дни облетевшие по многим серверам мира. Так же дело и обстоит с полезными программами. Отличием полезной программы от вредоносной есть сам метод переноса от компьютера к компьютеру. По степени ее уникальности и полезности она может понравиться многим. Я не буду говорить о методах рекламы программных продуктов, они такие же самые, как и реклама обычных продуктов, будь то интернет-ресурс или обычных хозяйственный товар. Дело в том, что ваша программа, выпущенная в свободное распространение (выложенная на сайте, отправленная друзьям по почте и т.п.) абсолютно независимо от вашего желания может попасть любому человеку. Этот человек может быть другой национальности, абсолютно не понимающий русского языка.

Если ваша программа изначально рассчитана на свободное распространение, свободное распространение с ограниченными функциями для последующего приобретения, если ваша программа может оказаться полезной для многих (утилита, игра, экранная заставка), то надо стараться изначально ее оформлять с англоязычным интерфейсом. Все дело в том, что большинство пользователей компьютеров в полной мере или частично знакомы с английским языком. Следовательно, разобраться в такой вашей программе смогут больше человек в мире, чем, скажем в программе с белорусским языковым интерфейсом. Здесь имеется в виду ни что иное, как глобальное внедрение вашего приложения в масштабе целой планеты, а не касательно, скажем, ваших знакомых. Но делая программу, даже с языком, являющимся международным (даже с китайским языком :), трудно рассчитывать на популярность во многих странах.




Большую популярность получили программы с многоязычным интерфейсом. Я имею в виду, что описанные выше программы получают большую популярность, чем аналогичные с однотипным диалоговым языком. К примеру, это некоторые командные оболочки (FAR, Windows Commander), антивирус DrWeb, интернет броузер Opera. В таких программах нужный язык можно выбрать из списка в окнах настройки.

В большинстве случаев, весь языковой интерфейс хранится в отдельном файле. И это правильно. Не стоит загромождать исполняемый EXE файл излишком информации, а настройка и адаптация нового языка, первоначально не включенного в такой программный пакет, происходит быстро и без перекомпиляции всего проекта.

Какой же метод выбрать для хранения и последующего считывания данных из файла языков. В большей степени для этого подходит рассмотренные нами в 16 уроке ini файлы. Такие файлы легко переносятся на другой компьютер вместе с самой программой, возможно редактировать из любого текстового редактора, изменить текст может любой человек.

Различные секции в таком ini-файле будут хранить в себе отдельные языковые интерфейсы. Например, секция

[RUSSIAN]

будет озаглавливать русскоязычный внешний вид программы,

[ENGLISH]

- англоязычный, и т.д. Я думаю, что с этим проблем у пользователя не будет.

Названия хранимых параметров состоят из названия окна (формы), в котором находится компонент плюс название самого компонента. Параметр должен состоять из одного слова. Хранимая величина - текст, который отображается на экране на этом компоненте. Это может быть свойство Caption или свойство Text, в зависимости от типа (класс) компонента. Например, для компонента Button1, находящегося в окне Form1 записываемый параметр и значение выглядит:

Form1Button1=Кнопка1

В нашей программе при чтении такого параметра должна произойти замена:

Form1.Button1.Caption := 'Кнопка1';

Естественно, это делается автоматически для всех визуальных компонентов, на каких есть текст. Проблема может состоять в том, что таких компонентов на каждой форме может быть, скажем 200. Тогда это очень загромоздит программный код. При оперативном исправлении такой программы (добавление, удаление компонентов), необходимо будет исправлять и эту часть кода. Выходом из создавшейся проблемы может быть свойства для определенного окна ComponentCount и Components. Свойство Components позволяет через массив получить доступ к любому элементу управления формы. Свойтсво ComponentCount показывает, сколько этих элементов управления (компонентов) у нас присутствует в окне. Нам нужно будет просто организовать цикл от 1 до ComponentsCount и для каждого компонента прочитать соответствующее значение Caption или Text из INI файла.


Внутри такого цикла нужно определять тип компонента. Ведь для кнопки (Button, BitBtn, SpeedButton), метки (Label, StaticText), флажка (CheckBox, RadioButton) и пр. свойство Caption определяет текст, который будет виден на этом компоненте. Для Edit, Memo, ComboBox и пр. свойтсво Text. Следовательно, очень важно верно определить тип, выбранного из цикла компонента, чтобы в последствии правильно занести соответствующее значение в соответствующее свойство.

Следующим этапом, когда мы определили тип компонента, следует само чтение данных из ini-файла. Вот примерный кусок кода такой программы:

if ComponentCount<>0 then // если в окне есть хотя бы один элемент управления (компонент)

for i:=1 to ComponentCount do // цикл от 1 до кол-ва компонентов

      if Components[i-1].ClassType = TButton then // если текущий элемент является элементом класса TButton, то

            (Components[i-1] as TButton).Caption:= ЧТЕНИЕ_ДАННЫХ_ИЗ_INI

Разъясню последнюю строчку из этого примера. Через

(Components[i-1] as TButton)

Мы получаем доступ к свойствам компонента, представляя его к классу TButton. Для этого в предпоследней строке примера мы и производим проверку класса выбранного циклом компонента. Если такую проверку не производить, то во время выполнения программы при обращении, скажем к компоненту класса TEdit к свойству Caption, появится сообщение об ошибке (У TEdit свойство Text!).

(i-1) как вы наверное уже догадались, список массива элементов управления формы начинается с нуля. А заканчивается ComponentCount-1.

Еще один нюанс. Если такой многоязычный ini-файл был потерян в процессе эксплуатации, то пользователя рискует остаться вообще без каких либо намеков в вашей программе. Поэтому рекомендую все-таки изначально давать текст вашим компонентам как в обычной программе. В случае невозможности чтения данных из INI файла текст на этих компонентах останется нетронутым.

Теперь я даю вам универсальный модуль, который должен присутствовать в каждом оконном модуле вашей программы, где необходимо менять язык по указанию пользователя. Я являюсь автором этого куска программы, поэтому, возможно, что-то можно и сократить, тем не менее...


В раздел Uses необходимо дописать модуль для работы с ini-файлами:

Uses IniFiles;

Раздел public дописываем одну строку объявления процедуры:

public

   { Public declarations }

   procedure ChangeLang(LangSection:string);

Сама процедура пишется изначально вручную вместе с заголовком:

procedure TForm1.ChangeLang(LangSection:string);

Var i:Integer; // временная числовая переменная для выборки всех компонентов

    LangIniFile:TIniFile;

    ProgramPath:String; // строковая переменная для получения каталога, где находится запущенный EXE файл

begin

if ComponentCount<>0 then // если в окне больше одного компонента

   begin

      ProgramPath:=ExtractFileDir(Application.ExeName); // получаем каталог, где лежит запущенный EXE файл

      if ProgramPath[Length(ProgramPath)]<>'\' then ProgramPath:=ProgramPath+'\'; // гарантированно устанавливаем последний символ '\' в конце строки

      LangIniFile:=TIniFile.Create(ProgramPath+'lang.ini'); // подготавливаем INI файл. Он должен иметь название lang.ini и должен находиться в каталоге программы

      Caption:=LangIniFile.ReadString(LangSection,Name,Caption); // читаем заголовок окна

      for i:=1 to ComponentCount do // перебираем все компоненты в этом окне

         begin

            if Components[i-1].ClassType=TButton then // если выбран из массива компонент Button, то изменяем текст на кнопке

               (Components[i-1] as TButton).Caption := LangIniFile.ReadString(LangSection, Name+Components[i-1].Name, (Components[i-1] as TButton).Caption);

// Напомню описание функции ReadString:

// LangIniFile.ReadString( СЕКЦИЯ, ПАРАМЕТР, ЗНАЧЕНИЕ_ПО_УМОЛЧАНИЮ );


// 1. LangSection - передаваемый параметр в процедуру. В процедуру передается название секции для выбранного языка

// 2. Name+Components[i-1].Name - Name - название формы, Components[i-1].Name - название компонента

// 3. (Components[i-1] as TButton).Caption - в случае неудачного чтения этого параметра из ini файла (нет такого параметра), то ничего меняться не будет

// аналогично для других типов:

            if Components[i-1].ClassType=TLabel then

               (Components[i-1] as TLabel).Caption := LangIniFile.ReadString(LangSection, Name+Components[i-1].Name, (Components[i-1] as TLabel).Caption);

            if Components[i-1].ClassType=TEdit then

               (Components[i-1] as TEdit).Text := LangIniFile.ReadString(LangSection, Name+Components[i-1].Name, (Components[i-1] as TEdit).Text);

            // ...

            // ...

            // ...

         end;

      LangIniFile.Free; // освобождаем ресурс

   end;

end;

Этот пример можно забрать по этой ссылке
(4КБ). Обратите внимание, в программе два окна. В каждом модуле для каждого отдельного окна присутствует эта вышеописанная процедура.

Вместо строк // ... вы можете добавлять другие типы компонентов, например, можете описать тип компонента TCheckBox, если таковой имеет место в вашей программе. В идеале описывайте по шаблону все или большинство типов компонентов, имеющиеся в наличие в delphi. Для этого вам понадобится несколько десятков строк программного кода, но зато вы гарантированно можете применять эту процедуру не только в одной вашей программе, не проверяя наличие всех используемых типов компонентов.


Напомню, аналогичную вышеприведенную процедуру, без изменений, вы должны вписать в каждый модуль вашей программы. При смене языка, например, в программе с тремя окнами (Form1, Form2, Form3) происходит следующим куском программного кода:

Form1.ChangeLang('RUSSIAN');

Form2.ChangeLang('RUSSIAN');

Form3.ChangeLang('RUSSIAN');

Поскольку процедуру смена языка мы объявляем в разделе public, то доступ к этой процедуре мы можем получить из любого места программы. Как вы заметили из примера, для переключения на русский язык указана имя секции RUSSIAN.

Теперь рассмотрим содержание самого INI файла. Его вы должны создавать самостоятельно. Во-первых, нужно помнить правила орфографии windows ini-файла, во-вторых, названия параметров должны соответствовать имени формы плюс названия компонента, хранимое значение следует за знаком равенства. Для примера, ini-файл с двумя языками, с двумя окнами, на каждом окне находится по две кнопки.

; начало файла lang.ini

[RUSSIAN]

Form1Button1=Кнопка 1 на форме 1

Form1Button2=Кнопка 2 на форме 1

Form2Button1=Кнопка 1 на форме 2

Form2Button2=Кнопка 2 на форме 2

[ENGLISH]

Form1Button1=Button 1 on form 1

Form1Button2=Button 2 on form 1

Form2Button1=Button 1 on form 2

Form2Button2=Button 2 on form 2

; конец файла lang.ini

Кроме текста на визуальных элементах управления вашей программы, текст содержится еще в заголовке программы, в панели задач. Еще могут присутствовать всплывающие подсказки Hint. Полномасштабный перевод всех компонентов на несколько языков в ini файле может несколько затруднить сам процесс программирования. Но ради гибкости настройки интерфейса программы пожертвовуйте лишним временем. Ведь для изменения текста, добавления нового языка (возможно даже это делаете не вы) не нужно перекомпилировать проект. Это максимально упрощает языковую настройку. Можете вообще сделать ini-файл одноязычным. Перевести его можете после окончания проектирования, или вообще предоставьте это профессиональному переводчику.

Одним из слабых мест в такой программе есть необходимость помещать процедуру смены языка в каждый модуль. Если у вас в программе множество окон, это довольно сильно загромождает программный код, следовательно, увеличивает размер программы, следовательно, замедляет ее работу. Как же сделать так, чтобы весь процесс смены языка во всем приложении умещался с одной процедуре. Очень просто. А может и не просто. А в общем, через компонент Application. Он является по своей сути самой программой, значит,  содержит в себе все компоненты форм (уже упоминалось в первых уроках, что сама форма и есть компонент). Таким образом:


if Application.ComponentCount<>0 then // если в приложении есть компоненты форм (не консольное приложение)

   for i:=1 to Application.ComponentCount do // перебираем все компоненты

      if Application.Components[i-1].ClassParent=TForm then // если выбранный компонент является подклассом окна, то

         begin

            // обработка переключения языка для этого окна

         end;

Очень похоже на предыдущий пример...

Извините, что на первый взгляд эта тема урока кажется, немного сложноватой для начинающего, но тут важно разобраться в общих принципах, в нескольких свойствах, внимательно перечитать весь урок снова и все может стать понятным. Обязательно рассмотрите прилагаемый первый пример, прежде чем продолжать рассматривать этот урок далее.

Опять ищем слабые места в программе. Для начинающих программистов может быть новостью, что в одной программе не может быть двух и более окон с одинаковыми названиями. А как же дело обстоит с MDI приложениями. Дочернее окно проектируется в единственном варианте, а в внутри родительской формы, во время работы программы, оно может создаваться теоретически в неограниченном количестве. Имена же самой вновь создаваемой дочерней форме присваиваются системой автоматически. Следовательно, читать параметр из ini-файла по свойству Name не подойдет. Таким методом можно максимально прочитать язык для компонентов только для одного дочернего MDI-окна. Отсюда следует, что нужно читать данные согласно свойству ClassName, которое является уникальным для отдельного класса окна. Например, для окна Form1, являющегося главным MDI-окном такой класс TForm1. Для окна Form2, дочернего MDI-окна класс TForm2. Вот, вы наконец и узнали, что же это за такая туква Т, стоящая в начале названия компонента. Это надкласс, объединяющий однотипные компоненты (в том числе и окно программы) в единую группу, с одинаковыми вложенными свойствами. Это краткое описание, можно сказать, своими словами.

Эти все "наваяния" организованы во втором примере. Там смена языка происходит из одной процедуры абсолютно для всех окон. Тем самым мы уменьшили программный код за счет увеличения качества, увеличили количество вложенных циклов. Программа примера номер 2 является незаконченным каркасом MDI-приложения. Не удивляйтесь, почему программа не выполняет функции редактора.

Забирайте второй пример по этой ссылке (8КБ).

Var st:String; // Объявление строковой переменной st


st:='привет'; // Заносим в переменную st текстовую строку

ShowMessage(st); // Вывод на экран окошка с сообщением

Здесь весь пример куска программы можно привести в более упрощенный вид:

ShowMessage('привет');