Скачиваний:
24
Добавлен:
12.05.2015
Размер:
2.1 Mб
Скачать

Ini файли

У INI - файлів Є багато переваг. З ними працювати просто і зручно, вони підтримують три типи даних - String, Integer і Boolean. Крім того, якщо б ми зберігали налаштування в простий текстовий файл, то при зміні однією з налаштувань потрібно було б перезаписувати весь файл, а ini файл дозволяє перезаписати тільки цей параметр.

Звичайно, є і деякі правила використання такого роду файлів.

Першим ділом, якщо ви збираєтеся працювати з ini-файлами, в секцію uses, яка знаходиться відразу під словом interface, потрібно додати модуль inifiles, в ньому описані всі дані для роботи з ini-файлами. Додати модуль просто - після останнього вказаного у списку модуля стоїть крапка з комою. Замініть її на кому, додайте слово inifiles а потім поставте крапку з комою.

Далі. За замовчуванням, ini файли створюються в директорії, де встановлений Windows. Там з'являється файл з розширенням *.ini і вашими налаштуваннями. Це не завжди зручно, особливо якщо припустити, що Windows може бути перевстановлено. Набагато краще створювати файл в директорії, де встановлена ваша програма. А як дізнатися цю теку?

ExtractFilePath(Application.ExeName)

Вивчимо створення файлу ini-на практиці. Створіть новий додаток. У секцію uses додайте модуль inifiles.

Насамперед, пропишемо збереження параметрів при закритті програми. Для цього створіть обробник подій OnDestroy для форми. Така подія відбувається при руйнуванні об'єкта форма, тобто, при завершенні роботи з програмою. Створіть розділ var і пропишіть там змінну типу ini-файл:

var

ini : TIniFile; //объявляемпеременнуютипаinifile

begin

Далі ми повинні створити файл, якщо його не існувало, чи відкрити його, якщо він вже є:

ini := TInifile.Create(ExtractFilePath(Application.ExeName)+'my.ini');

Зверніть увагу, що ми створюємо файл у тій же директорії, звідки запущена програма. Якщо б ми вказали просто

ini := TInifile.Create('my.ini');

то файл був би створений в директорії Windows! Далі збережемо позицію вікна, тобто властивості Left та Top форми:

//сохраняемпозициюокна:

ini.WriteInteger('Position', 'L', Form1.Left); ini.WriteInteger('Position', 'T', Form1.Top);

У ini-файлу є три процедури для запису даних різних типів. WriteInteger, WriteString і WriteBool. Відповідно, ці функції записують ціле число, рядок і логічний тип. У цих функцій є по 3 параметра. Перший - це розділ ini - файлу. У самому файлі він виходить в квадратних дужках. Розділ ви можете назвати як завгодно, не обов'язково Position. Другим параметром є назва зберігається параметра, у прикладі цей параметр ми вказали у вигляді літер ‘L’ і ‘T’, хоча можна було б написати і ‘Left’, ‘Top’ або якось інакше.

Третім параметром функції є його значення. Оскільки властивості Left та Top форми мають значення у вигляді цілого числа, ми використовуємо функцію для запису цілих чисел WriteInteger. В результаті наведеного прикладу ini - файлі з'явиться напис, подібна цією:

[ Position]

L=14

T=50

Далі спробуємо зберегти розміри форми (властивості Width, Height). Для цього в ini - файлі створимо розділ Size (додайте код до попереднього):

//сохраняемразмерыокна:

ini.WriteInteger('Size', 'W', Form1.Width);

ini.WriteInteger('Size', 'H', Form1.Height);

Після того, як ми зберегли параметри ini - файл, його необхідно закрити:

//закрываемфайл:

ini.Free;

Все, файл закритий і налаштування збережені. Збережете, відкомпілюйте його, і подивіться, як він працює. Відкрийте створений ini - файл, це простий текстовий файл, який ви зможете прочитати, і навіть відредагувати.

Однак ми зробили тільки половину справи. Налаштування-то ми зберегли, а як їх прочитати? Робиться все це точно також, тільки навпаки. Замість властивості руйнування форми використовуємо властивість її створення (onCreate), замість запису використовуємо читання, і замість того, щоб значення властивостей форми записувати у файл, ми будемо читати їх з файлу та присвоювати ці значення властивостям форм. Відповідно, ми маємо три процедури зчитування параметрів з ini-файлу: ReadInteger, ReadString і ReadBool.

Отже, робимо обробник подій для форми onCreate:

{присозданииформы}

procedure TForm1.FormCreate(Sender: TObject);

var

ini : TIniFile; //объявляемпеременнуютипаinifile

begin

//теперьсоздаемее:

ini := TInifile.Create(ExtractFilePath(Application.ExeName)+'my.ini'); //применяемпозициюокна:

Form1.Left := ini.ReadInteger('Position', 'L', 329);

Form1.Top := ini.ReadInteger('Position', 'T', 261);

//читаемразмерыокна:

Form1.Width := ini.ReadInteger('Size', 'W', 384);

Form1.Height := ini.ReadInteger('Size', 'H', 312);

//закрываемфайл: ini.Free; end;

Тут цікавий третій параметр функції запису. Здавалося б, значення нам не потрібні, ми беремо їх із зазначених розділів. Але третій параметр обов'язковий, в ньому вказується значення «за замовчуванням».

При створенні форми надайте їй потрібний розмір і положення і подивіться значення властивостей Left, Top, Width, Height. Ці значення і ви запишете як прийнятих за замовчуванням. Якщо файлу ще не було або сталася якась помилка при читанні цього параметра, наприклад, не знайдена потрібний рядок, буде застосовуватися значення за замовчуванням. Якщо ж потрібна рядок прочитана, то буде застосовуватися те значення, яке зазначено в ній.

Збережете, відкомпілюйте його, і подивіться, як працює програма. Отриманий ini файл буде містити подібний текст:

[ Position]

L=329

T=261

[Size]

W=384

H=312

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

L=0

Або навіть

L=-100

Потім запустіть програму, і подивіться, як зміниться положення вікна! До речі, це хороший спосіб ховати програму. Якщо ви властивості Left вкажіть більше, ніж має властивість Width (ширина вікна), і при цьому зробите число негативним, вікно програми як би зрушиться вліво за межі екрану. Програма запущена, її видно в системної рядку, а вікна то і немає!

Перетягніть вікно робочої програми і вийдіть з неї. Знову запустіть - положення вікна, яке було останнім, збереглося!

Давайте продовжимо програму і створимо строковий параметр. Встановіть на форму компонент Label, у властивості Caption якого напишіть: «Введіть новий заголовок вікна». Нижче встановіть компонент Edit, у нього користувач буде вводити текст. Ще нижче - кнопку з написом «Застосувати».

Видаліть текст з компонента Edit, у обробнику кнопки напишіть наступне:

Form1.Caption := Edit1.Text;

Передбачається, що ви не міняли назв форми або Edit. Якщо ж ви змінили ім'я форми, наприклад, на fMain, то напишіть

fMain.Caption

Далі, перейдіть в редактор коду. У процедурі руйнування форми onDestroy відразу після запису висоти форми, перед закриттям файлу, додайте рядки:

//сохраняемзаголовокформы: ini.WriteString('Param', 'C', Form1.Caption);

А в процедурі onCreate, знову ж, перед закриттям ini-файлу, додайте:

//читаемзаголовокформы:

Form1.Caption := ini.ReadString('Param', 'C', 'Программа');

Як бачите, робота з рядками мало відрізняється від роботи з числами! Збережете, зберіть і подивіться, як він працює. Введіть нову назву вікна, натисніть кнопку «Застосувати», вийдіть із програми та знову відкрийте її. Текст в системної рядку програми має зберегтися.

Тепер попрацюємо з логічним параметром. Встановіть на форму один CheckBox. У його властивості Caption напишіть «Параметр». Нам, власне, не важливо, який саме параметр можна зберегти, наприклад, дозволяти чи натискання на кнопку, чи дозволяти показ форми «Про програму», видимий або невидимий буде якийсь компонент... Параметрів, які ви, можливо, захочете зберегти, може бути дуже багато. Зараз нам важливо одне: властивість Checked компонента CheckBox може бути або True (параметр увімкнено)або False (параметр вимкнено). Ось це нам і потрібно зберегти в ini-файл, а потім вважати це з нього.

Перед закриттям ini-файлу у процедурі події onDestroy запам'ятаємо стан прапорця:

//запоминаемфлажок:

ini.WriteBool('Param', 'CB1', CheckBox1.Checked);

Якщо параметр вимкнено, то замість False у параметр ini-файлу запишеться 0. Якщо включений - запишеться 1.

Тепер перед закриттям ini-файлу у процедурі події onCreate додамо рядка для зчитування даних з ini-файлу:

//читаемсостояниефлажка:

CheckBox1.Checked := ini.ReadBool('Param', 'CB1', True);

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

Лекція 19. Робота з текстовими файлами

TStringList

Практично будь-яка програма взаємодіє з якими-або типами файлів. Delphi дозволяє нам працювати з файлами різних типів. Текстовий файл - одна з цих різновидів. У цих файлах інформація розташована не суцільним блоком даних, а у вигляді рядків, які закінчуються символом кінця рядка і переведення каретки. Можна працювати з текстовими файлами загальним способом, тобто, побайтно зчитувати і записувати дані. Однак це не завжди зручно. Розглянемо приклад:

Привет! Как

жизнь?

Якщо ми вважаємо ці 2 рядки суцільним блоком, то отримаємо рядок:

Привет!’ + #13 + #10 + ‘Как жизнь?’

Це дуже незручно, так як для обробки рядків нам кожен раз доведеться шукати ці два символи кінця рядка. А якщо рядків багато, наприклад 100? І якщо потрібно отримати доступ до 75-й рядку?

Тут на допомогу приходить об'єкт TStrings, який ми використовували при обробці Memo, і який є простим контейнером для зберігання рядків. Однак використовувати об'єкт TStrings безпосередньо не можна, так як цей об'єкт є шаблоном для інших об'єктів, що працюють з рядками. Згадайте, в компоненті Memo і Lіstbox ми теж не використовували цей об'єкт безпосередньо, ми використовували його «нащадків» - Lines і Items. Тому краще використовувати більш просунутий варіант цього об'єкта - TStringList. Цей варіант успадковує всі можливості об'єкта TStrings і додає ще нові. Це, мабуть, найпростіший спосіб роботи з текстовими файлами.

Щоб скористатися цим об'єктом, потрібно спочатку визначити змінну цього типу, потім запустити її, а в кінці закрити. У простому варіанті це буде виглядати так:

var

f : TStringList; //объявилипеременную - объект

begin

f := TStringList.Create(); //проинициализировали

f.Free; //освободили (уничтожили) end;

Тут ми створюємо об'єкт і тут же його знищуємо, не працюючи з ним. Коли ми оголосили змінну типу об'єкт TStringList, ми вказали компілятору, скільки оперативної пам'яті буде потрібно виділити в подальшому для цієї змінної. Коли ми проинициализировали об'єкт методом Create, ми виділили під нього оперативну пам'ять. Надалі, з цим об'єктом можна працювати, записувати в нього текст, читати текст, користуватися властивостями, методами і подіями цього об'єкта. Метод Free руйнує об'єкт, знищує його в оперативній пам'яті. Після цього до змінної типу f TStringList звертатися вже не можна. Все, що повинен був робити цей об'єкт, повинно бути описано до методу Free.

Читання та запис з таким об'єктом відбувається знайомим нам способом. Припустимо, нам потрібно прочитати текстовий файл і змінити в ньому 10-ий рядок:

var

f : TStringList;

begin

f := TStringList.Create();

//читаемтекстизфайла:

f.LoadFromFile('c:\myfile.txt');

//меняемтекст 10-йстроки (строкиначинаютсяс 0):

f[9] := 'Новаястрока';

//сохраняемизмененныйтекстобратновфайл:

f.SaveToFile('c:\myfile.txt'); f.Free; end;

Звернітьувагу, щомизвернулисядо 9-муіндексу, такякнумераціяіндексівпочинаєтьсязнуля, і f[9] містить 10-юрядок.

Властивість Count цього об'єкта возвращает загальна кількість рядків, починаючи з першої. Давайте напишемо приклад, в якому ми будемо серед всіх рядків шукати рядок «Привіт, Москва!». Якщо така рядок буде знайдена, то програма виведе номер шуканої рядка, інакше виведе повідомлення, що такий рядка немає.

Спочатку скористайтесь власним редактором текстів і створіть текстовий файл, куди запишіть 10-15 рядків. Один з рядків зробіть «Привіт, Москва!».

Потім робимо новий додаток, встановлюємо кнопку «Виконати» і діалог OpenDialog. Потім створюємо обробник подій для кнопки, куди записуємо наступний код:

procedure TForm1.Button1Click(Sender: TObject); var

f : TStringList; i : Integer;

begin

//еслиничегонеоткрыли, выходимизпроцедуры: if not OpenDialog1.Execute then Exit;

//читаемизфайла:

f := TStringList.Create();

f.LoadFromFile(OpenDialog1.FileName);

//ищемстроку:

for i := 0 to f.Count-1 do begin

if f[i] = 'Привет, Москва!' then begin

ShowMessage('Строка найденапод№ ' + IntToStr(i+1)); Break; end; //if end; //for

//закрываемфайл: f.Free; end;

Тут код досить прозорий, проте ще раз звертаю вашу увагу на те, що індекси рядків починаються з нуля, а властивість Count возвращает загальна кількість рядків. Тобто, якщо в тексті лише 2 рядки і Count поверне цифру 2, то індекси цих рядків будуть 0 і 1, тому в циклі ми використовували діапазон від 0 до Count-1, інакше б помилка переповнення циклу.

Отже, крім того, що ми вже розглянули, цей об'єкт має й інші відомі вам за Memo методи:

Add - додати новий рядок у кінець:

f.Add('Новий рядок');

Clear - очистити об'єкт (не буде тексту):

f.Clear;

Insert - вставити рядок. Є 2 параметра - індекс вставляється рядка, і сама рядок:

f.Insert(3, 'Нова 4-я рядок, наступні рядки зрушитися вниз');

Delete - видалити рядок. Є параметр - індекс видаляється рядка.

f.Delete(f.Count-1); //видалили останній рядок

Загальні принципи роботи з файлами

У попередніх прикладах ми зберігали текст у файл і прочитували текст з файлу за допомогою властивостей:

Memo1.Lines.SaveToFile()

Memo1.Lines.LoadFromFile()

Цей метод зручний і гарний, однак він не дозволяє нам контролювати весь процес запису у файл і читання з файлу. Тобто, ці функції весь процес читання і запису виконують приховано від нас.

Однак буває, коли програміст відчуває необхідність контролю цих процесів, крім того, не для всіх типів ці функції доступні. Наприклад, у нас є текст посеред змінної. Ми вже говорили, що символьна мінлива може зберігати майже необмежену кількість символів, тобто в змінну ми можемо записати весь текстовий файл, включаючи і символи переходу на інший рядок і повернення каретки (#13 #10). З функціями SaveToFile() і LoadFromFile() можуть працювати дані, які мають тип TStrings, а рядки не можуть викликати їх. Тому доводиться робити запису у файл безпосередньо, а також безпосередньо їх зчитувати.

Для роботи з файлами багато програмістів воліють використовувати функції WinAPI. Незважаючи на грізне звучання, нічого особливо складного в цих функціях немає, тим не менш, такі функції мають один великий недолік. Корпорація MicroSoft постійно вносить якісь зміни у свої операційні системи. Так, у перших версіях Windows, для читання файлів використовувалася функція WinAPI _lread. Потім з'явилася функція ReadFile і Microsoft стала рекомендувати використовувати в програмуванні саме її. А потім з'явилася функція ReadFileEx, яка дозволяє працювати з файлами великих розмірів. Після кожної зміни цих функцій дуже часто доводиться переробляти всю програму, щоб вона могла працювати з новою операційною системою. А це забирає багато часу і створює додаткові незручності і для програмістів, і для користувачів.

Тому в Delphi рекомендується використовувати вбудований спеціалізований об'єкт TFileStream. Якщо Microsoft введе якісь нововведення, Boland врахує їх в об'єкті, і нам залишиться лише перекомпілювати нашу програму, не змінюючи її коду. Крім того, використання TFileStream набагато простіше, ніж функції WinAPI. TFileStream - це об'єкт, і як кожен об'єкт, його потрібно спочатку оголосити, а потім запустити.

Насамперед необхідно створити змінну типу TFileStream :

var

f : TFileStream;

Таким чином, ми оголосили змінну типу об'єкт TFileStream, і надалі можемо працювати з цієї змінної, як з об'єктом. Тобто, вказувати ім'я цієї змінної, а після точки вибирати властивості, методи і події цього об'єкта. Однак оголосити змінну мало, потрібно ще проініціалізувати її.

f := TFileStream.Create(параметры);

У методу Create об'єкта TFileStream може бути три параметри, причому третій параметр необов'язковий, його можна не вказувати. Розберемося з цими параметрами.

Ім'я файлу - цей параметр - проста рядок, яка може містити тільки ім'я файлу, або повне ім'я файлу, включаючи і адресу.

Режим відкриття. Тут можна вказати один з наступних параметрів:

  • fmCreate - Створити файл з вказаним у першому параметрі ім'ям. Якщо файл вже існує, він відкриється в режимі запису.

  • fmOpenRead - Відкрити файл тільки для читання. Якщо файл не існує, станеться помилка, тому перш потрібно виконати перевірку на існування файлу. Запис у файл у цьому режимі неможлива.

  • fmOpenWrite - Відкрити файл для запису. При цьому поточний вміст файлу знищується, і файл перезаписується.

  • fmOpenReadWrite - Відкрити файл для редагування, тобто, і читання і запису.

  • Права, з якими буде відкритий файл. Цей параметр необов'язковий, його можна не вказувати. Він має наступні можливі значення:

  • fmShareCompat - Інші програми теж мають право працювати з відкритим файлом.

  • fmShareExclusive - Інші програми не зможуть відкрити файл.

  • fmShareDenyWrite - Інші програми зможуть відкрити файл тільки для читання, записати в нього дані вони не зможуть.

  • fmShareDenyRead - Інші програми зможуть відкрити файл тільки для запису, для читання вони не зможуть його відкрити.

  • fmShareDenyNone - не заважати іншим програмам працювати з файлом.

Приклад:

f := TFileStream.Create('С:\MyFile.txt' , fmOpenReadWrite, fmShareExclusive);

Для чого потрібні права доступу до файлу? Наприклад, текстовий файл може бути відкритий стандартною програмою «Блокнот», цей же ми можемо відкрити файл з нашого власного текстового редактора. Редактор менеджера файлів FAR також може відкрити цей текстовий файл. І програма MSWord теж може відкрити його! Тепер припустимо, що наша програма робить запис у файл. У цей же самий час якась інша програма також зберігає зміни в цьому файлі. Наша програма зробила зміни і закрила файл, а інша програма навіть не підозрює про ці зміни, і просто перезаписує файл зі своїми змінами. Таким чином, наші зміни просто губляться!

Теж саме може статися, якщо ваша програма виконана в мережевому варіанті. Наприклад, є якийсь спеціальний файл, у який співробітники відділу записують свої зміни. Цей файл може перебувати на якомусь мережевому диску, і доступ до цього файлу має кожен співробітник. І у кожного співробітника встановлена ваша програма. Таким чином, якщо одночасно двоє співробітників будуть проводити зміни у файлі, то дані одного з них загубляться, так як програма іншого перезапише файл без урахування цих змін. Якщо ви пишете однокористувацький програму, і доступ до файлу буде мати тільки вона, то про третій параметр можете зовсім забути.

Після того, як ви створили об'єкт, проинициализировали його і попрацювали з ним, файл потрібно закрити і звільнити пам'ять, займану цим об'єктом. Для цього досить викликати метод:

f.Free;

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

Коли ви тільки відкрили файл, позиція курсора встановлюється на початок, і будь-яка спроба читання або запису буде застосована до цієї позиції курсору. Щоб прочитати або записати в іншу позицію, потрібно пересунути курсор. Для цього використовують метод Seek, який має два параметри:

  • Число, що показує кількість байт (символів), на які потрібно перемістити курсор.

  • Звідки потрібно рухатися. Тут може бути три варіанти:

    • SoFromBeginning - рухатися на вказану кількість байт від початку файлу.

    • SoFromCurrent - рухатися від поточної позиції курсору.

    • SoFromEnd - рухатися на вказану кількість байт від кінця файлу до його початку.

Отже, щоб перемістити курсор від початку файлу на 10 байт, потрібно виконати команду:

f.Seek(10, soFromBeginning);

Метод Seek - це функція, вона завжди повертає кількість байт зміщення від початку файлу. Цією властивістю можна скористатися, щоб дізнатися загальна кількість байт у файлі:

РазмерФайла := f.Seek(0, soFromEnd);

Правда, якщо нам потрібно просто подивитися розмір файлу, то цей трюк не зовсім придатний: нам доведеться відкрити файл, переміститися в його кінець, і потім закрити його. Як дізнатися розмір файлу більш підходящим способом, дізнаємося пізніше.

Для читання файлу з потрібно використовувати метод Read, у якого теж є два параметри:

  • Змінна, в яку буде записаний прочитаний текст.

  • Кількість байт, які варто прочитати.

Розглянемо приклад читання файлу з 10 символів, починаючи з 15-й позиції:

var

f: TFileStream; //объявляемпеременную

buf: array[0..10] ofChar; //буфердляхраненияпрочитанныхданных begin

//открываемфайлfilename.txtдлячтенияизаписи: f:= TFileStream.Create('с:\filename.txt', fmOpenReadWrite); //перемещаемсяна 15 символоввперед: f.Seek(15, soFromBeginning);

//читаемвбуфер 10 символовизустановленнойпозиции: f.Read(buf, 10);

Memol.Lines.Add(buf); //скопировалиэти 10 символоввMemo//уничтожаемобъектитемсамымзакрываемфайл: f.Free; end;

Метод Read повертає кількість реально прочитаних байт. Воно повинно бути дорівнює тій кількості байт, які ми вказали при виклику цього методу. Є дві причини, за якими ця кількість може бути не одно вказаною:

  1. При читанні було досягнуто кінець файлу, і функція прочитала менше байт, ніж планувалося.

  2. Помилка на диску або будь-яка інша проблема.

Для запису в файл використовується метод Write. Є два параметра і у нього:

  1. Змінна, вміст якої потрібно записати.

  2. Число байт для запису.

Користуватися методом запису можна точно також, як і методом читання.

Примітка: після читання або запису позиція курсора зміщується на кількість прочитаних або записаних байт. Тобто, позиція курсора встановлюється після прочитаного або записаного блоку.

У наступної лекції ми продовжимо вивчення роботи з файлами.

Лекція 20. Робота з файлами

Файли

Під словом файл в Delphi (в будь-якій мові програмування) розуміється область зовнішньої пам'яті ПК (жорсткий диск, дискета, компакт-диск і т.п.), яка має ім'я.

Файли в Delphi можуть оброблятися різними способами, у тому числі і через функції WinAPI. Файли можуть бути трьох варіантів:

1. Типізовані. Такі файли належать до якого-то типу даних, і можуть працювати тільки з ним. Це дуже зручно для створення файлів записів. Такі файли ми будемо вивчати трохи пізніше.

2. Текстові файли. Такий тип файлів призначений для роботи з текстом.

3. Нетипізовані файли. Призначені для побайтовой роботи з файлами будь-якого типу. Наприклад, такий тип зручно використовувати для копіювання файлу, його перенесення або перейменування.

Файли оголошуються наступним чином:

var

f1 = Fileof<тип>; //типизированныефайлы

f2 = TextFile; //текстовыйтипфайлов f3 = File; //нетипизированныефайлы

Однак, привласнивши якийсь змінної файловий тип, ми ще не можемо працювати з файлом. Для цього ми спочатку повинні зв'язати цю змінну з файлом. Робиться це наступним чином:

AssignFile(f1, 'filename.txt'); //связываниепеременнойсфайлом

Після цієї функції, ми можемо звертатися зі змінною f1 як з файлом filename.txt. Однак, якщо такого файлу немає, ми отримаємо помилку, тому перед використанням зв'язування бажано робити перевірку на наявність файлу. Така перевірка проводиться функцією FileExists(). Її синтаксис наступний:

FileExists('c:\01\myfile.txt');

В якості параметра у функцію передається адресу файлу та його ім'я. Можна передавати тільки ім'я, але тоді програма буде шукати файл у поточному каталозі. Функція повертає True, якщо такий файл існує, і False інакше. Тому ми можемо використовувати її для перевірки наявності файлу:

adres := 'c:\01\myfile.txt';

ifFileExists(adres) thenbegin

AssignFile(f1, adres); //связалифайловуюпеременнуюсфайлом //дальнейшаяработасфайлом end; //if

Надалі, при роботі з файлом, може виникнути виняткова ситуація. Увага! Такий термін ми зустрічаємо в перший раз, проте він дуже важливий для розуміння. Що таке виняткова ситуація? Це будь-яка помилка програми, яка може статися під час роботи. Наприклад, ви відкрили якийсь мережний файл і почали роботу з ним. А інший користувач в цей час взяв, та видалив цей файл. При спробі читання з неіснуючого файлу або запису в нього, станеться помилка і створиться виняткова ситуація. Якщо ви не обробите цю помилку, то комп'ютер, швидше за все, намертво зависне.

Тому в будь-якій ситуації, коли є ризик виникнення надзвичайної ситуації, програміст ЗАВЖДИ повинен її обробити. Для цього існує блок try-finally-end:

try

//блоккода, вкоторомможетпроизойтиошибка

finally

//код, которыйдолженвыполнитьсявлюбомслучае, например, кодзакрытияфайла end; //try

Якщо наша програма зробила непоправну помилку, то код, укладений в блоці finally виконається в будь-якому випадку. Таким кодом зазвичай роблять закриття файлу. Якщо навіть помилка і сповниться, файл все одно буде закритий і програма не буде, а буде продовжувати роботу. Привчіть себе ЗАВЖДИ використовувати цей блок при роботі з сумнівним кодом. Як би добре не була програма, завжди знайдеться користувач, який змусить її зробити помилку.

Давайте створимо проект, який буде копіювати файл в інше місце, і на цьому прикладі познайомимося з реальною роботою з файлами.

Створюємо новий проект.

Встановлюємо на форму Edit, у полі якого будемо писати, куди ми хочемо копіювати файл. Також над ним можна встановити Label, в якому введемо: «Вкажіть теку, куди ми будемо копіювати».

Встановлюємо на форму кнопку, на якій пишемо «Копіювати файл».

Також з вкладки Dialogs встановлюємо на форму компонент OpenDialog, щоб ми могли вибрати файл для копіювання.

Рис. 20.1. Зовнішній вигляд програми

Далі, нам залишилося обробити натискання на кнопку. Всі наші дії з відкриття файлу і його копіювання будуть відбуватися в процедурі натискання на кнопку. Ось повний лістинг цієї процедури:

procedure TForm1.Button1Click(Sender: TObject); var

fFrom, fTo : File; //нетипизированныефайл-источник, ифайл-копияcolRead, colWrite : Integer; //количествопрочитанныхизаписанныхбайтbuf : array [1..2048] of Char; //буферсимволовдлякопируемоготекстаfilename : string; //переменнаясадресомиименемфайлаbegin

//еслинетадреса, кудакопировать, товыходим ifEdit1.Text = '' thenbegin

ShowMessage('Укажите папку, куданужнокопироватьфайл!');

Edit1.SetFocus;

Exit;

end; //if

//еслиневыбралифайл, товыходимif not OpenDialog1.Execute then Exit; //смотримадресиимяфайла: filename := OpenDialog1.FileName;

try

//связываемпеременныесфайлами:

AssignFile(fFrom, filename); //откуда

AssignFile(fTo, Edit1.Text+'\'+ExtractFileName(filename)); //куда

//открываемфайлдлячтения: Reset(fFrom, 1); //открываемфайлдлязаписи: Rewrite(fTo, 1);

//обнуляемпеременные colRead := 0; colWrite := 0;

//копируем, поканенаступитконецфайла: whilecolRead = colWritedobegin

BlockRead(fFrom, buf, SizeOf(buf), colRead);

if colRead = 0 then break;

BlockWrite(fTo, buf, colRead, colWrite); end; //while

finally

//закрываемфайлы

CloseFile(fFrom);

CloseFile(fTo);

ShowMessage('Файл скопирован!'); end; //try end;

Тепер розглянемо цей код і познайомимося з новими функціями.

Після того, як за допомогою функцій AssignFile() ми зв'язали змінні з файлами, звідки ми збираємося копіювати, і куди, ми користуємося функціями Reset і Rewrite.

Reset відкриває файл тільки для читання. Всі спроби змінити такий файл приведуть до помилку. При відкритті файлу покажчик (курсор) встановлюється на початок файлу. Ця функція працює трохи по-різному з різними типами файлів. У разі нетипізованого файлу, функція Reset має два параметра - файлову змінну і довжину запису в байтах. Ми вказали в прикладі, що наша запис - 1 байт. Так зручніше для обробки коду.

Rewrite має такі ж параметри, що і Reset, але вона відкриває файл для запису. Причому якщо файлу немає, то він створюється, а якщо є - перезаписується. Покажчик також встановлюється в першу позицію.

Далі ми «обнулені» змінні типу Integer. Про цих змінних мова попереду, а поки нам потрібно, щоб вони були рівні один одному і мали значення 0.

Далі ми починаємо умовний цикл while, де перевіряється рівність цих двох змінних.

А ось далі йде цікава функція BlockRead. Вона призначена для роботи тільки з нетипізованими файлами, для роботи з файлами іншого типу використовують функцію Read і Readln. Функція BlockRead зчитує інформацію відразу блоками, що значно прискорює процес копіювання файлів. Ця функція має чотири параметри, причому останній необов'язковий. Розберемося з цими параметрами.

1. Змінна файлового типу, раніше пов'язана з файлом функцією AssignFile().

2. Буфер, куди будуть записуватися прочитані дані. Оскільки файл нетипизирован, дані можуть бути будь-якого типу - символьні, як у текстовому файлі, або двійкові, як у програмі, тобто, виконуваному файлі. В якості буфера у нас є масив символів, куди ми ці дані і зчитуємо. Чим більше такий масив, тим більше даних запишеться за один раз, і тим швидше буде відбуватися копіювання. Проте захоплюватися збільшенням буфера теж не варто. В даному випадку ми використовували такий розмір, який вказаний в довідковій системі самої Delphi по функції BlockRead. Подальше збільшення розміру буфера не приносить помітних переваг.

3. Кількість байтів, які потрібно переглянути. Тут ми використовували функцію SizeOf(), яка повертає розмір масиву. Тим самим ми вказали, що потрібно прочитати максимальна кількість байт, яке поміститься в цей масив.

4. Необов'язковий параметр - це мінлива цілого типу. Після читання даних, в цю змінну заноситься кількість реально прочитаних байт. Взагалі кількість реально прочитаних байт має відповідати кількості зазначених байт, які потрібно переглянути. Проте є кілька винятків. По-перше, може статися помилка читання даних. По-друге, розмір реально прочитаних байт може бути менше заявленого розміру, якщо ми прочитали файл до кінця, і останній прочитаний шматочок виявився меншим, ніж розмір буфера. І по-третє, він може зовсім дорівнювати нулю, якщо курсор (курсор) коштує на кінці файлу.

Отже, ми викликаємо функцію BlockRead. Ми вказуємо файл, з якого потрібно читати, буфер, куди потрібно помістити прочитані дані, указуємо кількість байт, які потрібно переглянути, та даємо змінну, куди запишеться кількість реально прочитаних байт. І якщо ми прочитали 0 байт, це означає, що ми дісталися до кінця. У цьому випадку ми виконуємо директиву break, яка завершує цикл while.

Якщо ж кількість прочитаних байт більше нуля, значить, у буфері є прочитані дані, отже, їх потрібно записати. Для цього ми викликаємо процедуру BlockWrite, яка має такі ж параметри, але не читає дані, а записує їх. Подивіться, в якості третього параметра ми використовуємо змінну colRead, в якій зберігається кількість прочитаних раніше байт. Тобто, якщо в останньому шматочку у нас виявилося менше байт, ніж ми можемо помістити в буфер, значить, така ж кількість ми повинні записати.

Ось і всі премудрості копіювання файлів! Не забуваємо, що робота файлу нерідко призводить до помилки, тому її потрібно помістити в блок try...finally...end.

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

У нашому прикладі залишилося кілька недостатоков. Як копіювання файлу виглядає з точки зору користувача? Він вважає, що, віддавши програмі команду копіювати, він отримує точну копію файлу, рівну оригіналу у всьому. А як копіювання файлу виглядає з точки зору програміста? Програміст розуміє, що він відкриває вихідний файл, звідки буде брати дані, і створює новий файл, куди ці дані потім він запише. Все добре, ось тільки дата та час створення файлу будуть різні - копія буде мати поточну дату. Особливої ролі не грає, проте іноді потрібно і дату присвоювати ту, яку має вихідний файл. Як це зробити, ми поговоримо наступного лекції.

Другий недолік: ми твердо вважаємо, що користувач ввів у полі Edit адреса в такому форматі:

C:\MyDir

Потім до цього адресою ми додаємо зворотній слеш («\»). А якщо він сам уже поставив його?:

C:\MyDir\

Тоді в результаті коду

AssignFile(fTo, Edit1.Text+'\'+ExtractFileName(filename)); //куда

ми отримаємо помилку, так як ім'я файлу може виглядати так: «C:\MyDir\\myfile.dat». Спробуйте самостійно організувати перевірку на наявність зворотного слеш в кінці адреси. Наприклад,

if Edit1.Text[Length(Edit1.Text)] <> '\' then Edit1.Text := Edit1.Text + '\';

А потім додавати ім'я файлу до адресою вже без цього знаку:

AssignFile(fTo, Edit1.Text+ ExtractFileName(filename)); //куда

Перенесення файла здійснюється точно також, проте після того, як ми скопіювали файл, і закрили обидва файлу, вихідний файл потрібно видалити. Для цього існує функція DeleteFile(), в якості параметра якої ми вказуємо адресу та ім'я видаленого файлу. У нашому прикладі ми можемо вказати OpenDialog1.FileName. Функція поверне істину, якщо видалення відбулося успішно, і брехню в іншому випадку.

Лекція 21. Записи

Записи

Запис - це структура даних, що складається з полів різного типу. Можна сказати, що запис - це міні-об'єкт, який має властивості, але не має методів і подій. Для чого потрібні записи? Так багато для чого. Їх можна використовувати для збереження налаштувань програми замість ini-файлів із записів можна зробити невелику базу даних на користувачів програми, і так далі. Використання записів обмежена тільки уявою програміста.

Записи оголошуються в розділі type перед розділом var, і можуть бути локальними для процедур і функцій, так і глобальними. Зрозуміло, якщо ви використовуєте запису для збереження параметрів програми, бажано їх робити глобальними. В мові C / C++ такий тип називається структурою, а в Delphi - записом. Синтаксис оголошення запису наступний:

type

<имя_записи> = record

…; //описаниеполей end; //record

Однак мало описати таку запис, потім потрібно ще оголосити змінну типу цієї запису:

var

myPerem : имя_записи;

Надалі можна звертатися до окремих полів запису, вказуючи цю змінну:

myPerem.имя_поля

Давайте подивимося, як можна зберегти в записі розміри і положення форми. Створіть новий проект. Перед розділом глобального блоку var створіть розділ type. Неважливо, що вище вже є такий розділ, ми можемо створити ще один, головне, щоб він був перед var.

type

myForm = record

Left : Integer;

Top : Integer;

Width : Integer;

Height : Integer; end; // record

Тепер у нас є описана запис myForm, в якій є 4 поля: Left, Top, Width, Height. Як ви вже здогадалися, в ці поля ми будемо записувати положення та розміри форми.

Тепер увага! Запис - це фактично новий тип даних! Ми можемо оголосити скільки завгодно змінних типу myForm, а також використовувати типізований файл, присвоївши йому тип myForm. Подивіться на наступні приклади:

var

perem : myForm; //переменнаянашеготипа

ar : array[0..10] ofmyForm; //массив

f : FileofmyForm; //типизированныйфайл

Повернемося до нашого проекту. Нижче записи є глобальний розділ var, де описано мінлива Form1. Нижче її додамо ще одну змінну:

myF : myForm;

Тепер ми можемо при руйнуванні форми створити двійковий файл, у якому будуть зберігатися положення і розмір форми. Створіть обробник подій onDestroy для форми, і в ньому напишіть наступний код:

var

f : File of myForm; //типизированныйфайлbegin

//сохраняемданныевзапись: myF.Left := Form1.Left; myF.Top := Form1.Top; myF.Width := Form1.Width; myF.Height := Form1.Height;

//создаемилиперезаписываемфайл:

try

AssignFile(f, 'my.dat');

Rewrite(f);

Write(f, myF);

finally

CloseFile(f); end; // try

Зверніть увагу, що ми не робимо перевірку на існування файлу, оскільки якщо файлу немає, він буде створений, а якщо є - перезаписаний. Крім того, в якості імені ми використовуємо рядок ‘my.dat’. Файл з таким ім'ям буде створений в поточній папці. Оскільки наша програма не змінює папок, то поточної буде та, в якій знаходиться програма. Збережете, зберіть, подивіться, як працює програма. Закрийте її та файловим менеджером перейдіть в папку з проектом. Подивіться, чи з'явився файл my.dat, спробуйте його прочитати.

Як бачите, дані у файлі зберігаються в двійковому вигляді, що-або прочитати там досить складно. Іноді зручніше користуватися таким способом, тому що завжди може знайтися користувач, який спробує грати з налаштуваннями в текстових файлах. Ini файли мають текстовий тип, і користувач буде мати можливість змінювати там налаштування вручну. Неважливо, якщо ми зберігаємо тільки розміри і положення форми, але ми можемо зберігати й інші, більш серйозні параметри! Однак паролі безпосередньо зберігати не вийде - текст все ж буде видно.

Щодо тексту... Тип string має динамічний характер. Іншими словами, розмір змінної такого типу може змінюватися в процесі роботи програми. Тип динамічного характеру (рядок String або динамічний масив) можна вказати у запису, проте його не можна використовувати в типизированном файлі, так як розмір поля повинен бути заздалегідь відомий.

Перейдемо в розділ, де ми описували нашу запис. Після опису поля Height розглянемо ще одне поле:

Caption : String[50]; //размерстрокизаданжестко, егоможноиспользоватьвфайле

Тепер перейдемо в обробку події onDestroy, і додамо рядок:

myF.Caption := Form1.Caption;

у розділі збереження даних в запис. Ще раз збережете, відкомпілюйте і запустіть програму. Закрийте її та подивіться на файл my.dat. Як бачите, напис у заголовку форми з'являється у файлі без змін, так що будь-які ваші паролі все-таки можна буде прочитати. Змініть заголовок форми, знову зберіть проект і подивіться в файл - рядок-колишньому читається.

Тепер нам потрібно при відкритті форми застосувати всі ці налаштування. Якщо ви пам'ятаєте приклад з ini - файлом, налаштування ми застосовували в події форми onCreate, також вчинимо і тут. Створіть це подія для форми. Тут нам доведеться перевіряти наявність файлу - адже якщо програма запускалася у клієнта вперше, то файлу там ще не було, він буде створений при закритті програми. Тому будемо застосовувати налаштування в тому випадку, якщо файл існує.

var

f : File of myForm;

begin

//еслифайланет - выходим:

if not FileExists('my.dat') then Exit;

//открываемфайлисчитываемнастройкивзапись:

try

AssignFile(f, 'my.dat');

Reset(f);

Read(f, myF); //считываемзаписьвнашуглобальнуюпеременную

finally

CloseFile(f); end; //try

//применяемполученныенастройки: Form1.Left := myF.Left;

Form1.Top := myF.Top;

Form1.Height := myF.Height;

Form1.Width := myF.Width;

Form1.Caption := myF.Caption;

end;

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

Такий спосіб збереження параметрів здається менш зручним, ніж ini файли, однак компенсується тим, що тут можна зберігати дані будь-якого типу - рядки і дати, цілі і дійсні числа, логічні дані і навіть масиви.

Самостійно змінити програму. Додайте на форму один CheckBox, стан якого (включений чи ні) будемо зберігати в файл. Додайте запис ще одне поле логічного типу, і в залежності від того, чи встановлено прапорець у CheckBox, записуйте у нього або True або False.

Лекція 22. Динамічні масиви

Протягом курсу ми нерідко вживали в програмах масиви, і знаємо, що вони оголошуються таким чином:

Имя_массива: array[длина_массива] ofтип_данных;

Ми знаємо, що типи даних можуть бути будь-якими, а довжина масиву вказується у вигляді діапазону від початкового до кінцевого значення. Працювати з такими масивами дуже зручно, проте проблема в тому, що вони не можуть змінювати свій розмір. А якщо ми заздалегідь не знаємо, який розмір повинен бути у масиву?

Припустимо, у нас в програмі є користувача настройки. Тобто, кілька користувачів зареєструвалися в програмі і мають право на її використання. Кожен з них бажає, щоб програма зберігала його налаштування, щоб потім йому не доводилося перенастроювати її. Приміром, розмір та позиція головного вікна. Одному зручно, коли вікно праворуч. Інший бажає бачити його ліворуч. А третій взагалі, хоче, щоб воно було розгорнуто на весь екран! Вихід? Створити запис для збереження налаштувань користувача, і оголосити масив типу цього запису. Кожному користувачеві буде належати свій індекс цього масиву. Проте, дуже рідко буває, коли програміст заздалегідь знає кількість зареєстрованих користувачів. Який розмір масиву робити тоді, 10? А раптом користувачів буде менше, тоді у масиву будуть порожні індекси... А раптом більше?! Тоді буде переповнення масиву, програма видасть помилку і не зможе працювати... На допомогу приходять динамічні масиви.

Динамічними називаються масиви, при оголошенні розмір яких не вказується. А під час виконання програми такі масиви можуть змінювати довжину.

Оголошуються такі масиви звичайним чином, у розділі змінних:

var

da : array of Integer;

Як бачите, тут зазначено, що оголошується масив цілих чисел, однак довжина при цьому не вказується. Надалі з таким масивом ми можемо виконувати різні дії - збільшувати або зменшувати його довжину, дізнаватися поточну довжину, дізнаватися нижче і вище значення діапазону масиву, і, звичайно, привласнювати значення будь-якого з його елементів, або навпаки, зчитувати ці значення. Ну і звичайно, записувати значення масиву в файл і зчитувати їх з файлу. Познайомтеся з функціями, які все це роблять.

SetLength(масив, довжина);

Встановлює зазначену у довжину зазначеного масиву. Приклад:

SetLength(da, 10);

В результаті динамічний масив da містить 10 елементів. Зверніть увагу, що елементи динамічних масивів завжди починаються з нуля!

Якщо потім ми напишемо:

SetLength(da, 8);

то довжина масиву зменшиться на 2 елемента. Якщо ці елементи містили якесь значення, вони будуть знищені!

Length(da);

Цю функцію ми вже використали, щоб дізнатися довжину рядка. Вона також показує кількість елементів у динамічному масиві. Згадайте, що рядок - це просто масив символів, а якщо ми не обговорюємо довжини рядка, а просто пишемо «s : String», то отримуємо динамічну рядок символів. Нам досить присвоїти яке значення рядку, і пам'ять буде виділена автоматично під поточний розмір цього рядка. Однак у випадку роботи з динамічними масивами, нам самим доведеться встановлювати їх розмір.

Low(da);

Вказує нижчий індекс масиву da, як правило, це нуль.

High(da);

Вказує вищий індекс масиву. Подивіться приклад:

SetLength(da, 10); //установилиразмерв 10 элементов Low(da); //вернет 0, какнизшийэлемент High(da); //вернет 9, каквысшийэлемент

Таким чином, ці функції зручно використовувати в циклі обробки кожного елемента масиву:

for i := Low(da) to High(da) do da[i]:= значение;

Однак перед такими операціями потрібно встановити якийсь розмір масиву, інакше станеться помилка. Наступний приклад перевіряє розмір масиву. Якщо у масиву немає індексів, він встановлює один індекс:

if Length(da) = 0 then SetLength(da, 1);

Часто буває необхідно додати один елемент масиву, але програміст не знає, скільки в даний момент у масиву є елементів. Це можна зробити таким чином:

SetLength(da, Length(da) + 1);

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

Цих функцій цілком достатньо для роботи з динамічними масивами, проте перед застосуванням їх на практиці, вам слід знати, як робота динамічними масивами виглядає з точки зору комп'ютера.

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

Коли ми встановлюємо цю розмірність функцією SetLength(), комп'ютер виділяє пам'ять під вказану кількість елементів. При цьому ім'я масиву служить показівник на перший елемент масиву в пам'яті. Комп'ютера все одно, які дані зберігаються в елементах, його завдання - виділити для цього достатньо пам'яті. Тому видалити якийсь елемент всередині масиву не так просто.

Тепер розглянемо на практиці роботу з динамічним масивом і напишемо програму - телефонний довідник.

Створіть форму, на кшталт:

Рис. 22.2. Зовнішній вигляд програми

Тут у нас 7 компонентів Label, 2 ComboBox, 1 MaskEdit для введення номера телефону і 4 Edit для введення даних користувача. Крім того, 2 кнопочки з написами «Додати телефон» і «Вийти з програми». У вікні MaskEdit створіть маску для введення телефону, на зразок зазначеної в малюнку, але з кодом свого міста.

Розташуйте компоненти на формі так, як підказує вам дизайнерський смак. Верхній ComboBox буде потрібен для вибору телефону зі списку, щоб відразу ж висвічувалися дані цього запису.

Перевірте властивість TabOrder всіх компонентів для введення, вони повинні йти один за іншим при натисканні клавіші <Tab>. Властивість TabOrder показує індекс компонента на формі. Той компонент, у якого TabOrder дорівнює 0, при відкритті форми матиме фокус вводу, тобто, буде виділеним. Коли користувач натискає клавішу <Tab>, виділення перейде до компоненту з TabOrder рівним 1, і так далі. З допомогою цього властивості ми можемо вказувати черговість виділення компонентів, як правило, вона йде зверху вниз, зліва направо. Зрозуміло, такі компоненти, як Label, фокусу вводу не мають і у них відсутня властивість TabOrder.

Форму перейменуйте в fMain, модуль буде називатися Main а проект - tfSprav. Назви компонентів залишимо за замовчуванням, їх не так багато, і ми не заплутаємося. Встановіть форму в стиль bsDialog, а позицію - по центру екрана.

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

type

myTFSprav = record

TelNum : String[15]; //номертелефона

Mobil : Boolean; //мобильник - да? Нет?

Imya : String[20]; //имявладельцателефона

Otchestvo : String[20]; //егоотчество

Familiya : String[20]; //егофамилия

Adres : String[50]; //егоадресend; //record

var

fMain: TfMain;

sprav: array of myTFSprav; //объявляемдинамическиймассивнашейзаписи

Як бачите, ми вказали чимало полів у запису. Однак, не всі з них будуть обов'язкові для заповнення. Є дані? Запишемо. Немає? Поля можна залишити порожніми. Фактично, необхідними записами є тільки дві - номер телефону та ім'я його власника. Номер телефону нам потрібен, тому що це ж телефонний довідник, і немає сенсу в запису, якщо ми не вказуємо там поле з номером телефону. Ім'я нам теж необхідно, адже навіщо нам у файлі номер телефону, якщо ми не знаємо, чий це номер?

Відразу ж створимо обробник подій для кнопки «Вийти з програми». Там пишемо просто:

Close;

Далі обробимо кнопку «Додати телефон». На самому початку адже у нас ще немає записів, тому насамперед користувач введе парочку - іншу телефонних номерів.

{Добавитьтелефон}

procedure TfMain.Button1Click(Sender: TObject);

var

i : Integer; //длясчетчиказаписей

begin

{Проверяемобязательныепараметры:} //еслиномерателефонанет, выходим: ifMaskEdit1.Text = '8(374)- - - ' thenbegin//здесьвведитесвойкодгорода

ShowMessage('Впишитеномертелефона!');

MaskEdit1.SetFocus;

Exit;

End;

//if

//еслиименинет, выходим: if Edit1.Text = '' then begin

ShowMessage('Впишите имявладельцателефона!');

Edit1.SetFocus;

Exit;

End;

//if

//действительнолипользовательхочетсохранитьтелефон?

ifApplication.MessageBox('Вы уверены, чтохотитесохранитьэтоттелефон?', 'Внимание!', MB_YESNOCANCEL+MB_ICONQUESTION)<>IDYESthenExit;

{Пользовательуказалномертелефонаиимя, ижелаетсохранитьтелефон всписок.}

//проверяемномертелефонанасовпадениесимеющимисяномерами,

//еслитаместьзаписи. Дляпроверкииспользуемцикл, сверяемкаждуюзапись.

//Еслитакаязаписьужеесть, сообщаемобэтомивыходимизпроцедуры:

if length(sprav) > 0 then

for i := Low(sprav) to High(sprav) do

if sprav[i].TelNum = MaskEdit1.Text then begin ShowMessage('Такой номеружеесть!'); Exit; end; //if

//добавляемновыйэлементкмассиву: SetLength(sprav, Length(sprav)+1);

//записываемновыйтелефонвсписок: sprav[High(sprav)].TelNum := MaskEdit1.Text; if ComboBox2.ItemIndex = 0 then

sprav[High(sprav)].Mobil := True else sprav[High(sprav)].Mobil := False; sprav[High(sprav)].Imya := Edit1.Text; sprav[High(sprav)].Otchestvo := Edit2.Text; sprav[High(sprav)].Familiya := Edit3.Text; sprav[High(sprav)].Adres := Edit4.Text;

//очищаемполянаформе: MaskEdit1.Text := '8(374)- - - '; ComboBox2.ItemIndex := 0; Edit1.Text := ''; Edit2.Text := ''; Edit3.Text := ''; Edit4.Text := '';

ShowMessage('Телефон '+ sprav[High(sprav)].TelNum + ' добавлен!');

//обновимComboBox стелефонами: ChangeCombo; end;

Процедури ChangeCombo ще немає, ми хотіли її для того, щоб користувач міг вибрати потрібний телефон зі списку, а процедура оновлює список. Оскільки ця процедура повинна бути описана вище того місця, де ми її використовуємо, то опишемо її самої першої, після директиви компілятору {$R *.dfm}:

{ПроцедураобновленияComboBox1}

procedure ChangeCombo;

var

i : Integer; //счетчикдляцикла

begin

//еслимассивпустой - выходим:

ifLength(sprav) = 0 thenExit;

//есличто-тоесть, тосначалаочистимComboBox:

fMain.ComboBox1.Items.Clear;

//затемдобавимвнегокаждыйномертелефонаизмассива:

for i := 0 to High(sprav) do

fMain.ComboBox1.Items.Add(sprav[i].TelNum); end;

Збережете і зберіть його, впишіть кілька телефонів і переконайтеся, що вони потрапляють в ComboBox. Програма вже щосили працює з динамічним масивом і зберігає в нього дані. Однак поки що дані зберігаються тільки в пам'яті, потрібно навчити програму зберігати їх у файл. Створюємо для форми обробник подій onDestroy, тобто, коли форма руйнується, наші дані потраплять у файл. Файл буде створено, якщо його не було, або перезаписуватися, якщо він вже був.

{привыходеизпрограммыобновимфайл:} procedureTfMain.FormDestroy(Sender: TObject); var

f : File of myTFSprav;

i : Integer;

begin

//создаемилиперезаписываемфайл:

try

AssignFile(f, 'mySprav.dat'); Rewrite(f);

//записываемвседанныеархива: for i := Low(sprav) to High(sprav) do Write(f, sprav[i]);

finally

CloseFile(f);

end; // try

end;

Як бачите, ми оголосили файл такого ж типу, який має масив. У минулій лекції ви вже записували запис у типізований файл, однак тепер нам доведеться записувати не один запис, а цілий масив таких записів. Тому ми використовуємо функцію:

Write(f, sprav[i]);

причому робимо це в циклі від першого до останнього елемента масиву. Ця функція записує файл f запис потрібного типу sprav[i]. У прикладі у нас в цьому рядку зберігається тільки один елемент масиву, всі його поля, після чого курсор переміщується до кінця. У наступному проході циклу зберігатися буде вже наступний елемент масиву, і так далі, збільшуючи файл.

Ну і нарешті, зверніть увагу, що як файла ми вказали тільки ім'я та розширення, отже, він буде створюватися в тій же папці, звідки запущена програма. Ви можете використовувати приклади з минулих лекцій і змінити ім'я файлу у функції AssignFile(), вказавши там і адресу програми.

Збережіть приклад і зберіть його. Впишіть кілька записів і вийдіть із програми. Подивіться в директорію - там повинен з'явитися файл - довідник.

Наша програма вже вміє зберігати дані у файл, тепер навчимо її завантажувати їх з файлу. Створюємо для форми обробник onCreate, і вписуємо туди код:

{Привключениипрограммызагружаемсписок} procedureTfMain.FormCreate(Sender: TObject); var

f : File of myTFSprav; begin try

AssignFile(f, 'mySprav.dat');

Reset(f);

//считываемвседанныевархив:

while not Eof(f) do begin

//добавляемновыйэлементмассива

SetLength(sprav, Length(sprav)+1);

Read(f, sprav[High(sprav)]); end; //while finally

CloseFile(f); end; // try

//обновимComboBox стелефонами:

ChangeCombo;

end;

Звернітьувагу, щоминезнаємозаздалегідь, скількитамзаписів, томувикористовуватицикл for неможемо. Зате ми можемо використовувати while. Функція Eof(f) поверне істину, коли буде досягнуто кінець файлу f. І кожен раз, для нового запису, ми додаємо один елемент масиву. Ми повинні зробити це раніше, ніж будемо зчитувати запис, тому що, якщо для запису не виділена пам'ять, то нікуди буде зчитувати нову запис і програма видасть помилку.

Наприкінці ми знову викликаємо процедуру ChangeCombo, щоб додати в ComboBox всі телефони з масиву. Збережете, зберіть його і перевірте роботу. У ComboBox повинні завантажуватися всі телефони з файлу.

Проте ще програмі не вистачає «родзинки» - треба б, щоб дані цієї запису відображалися на формі, коли користувач вибере той чи інший телефон в ComboBox. Виділяємо цей компонент і пишемо для нього обробник подій onChange:

{Выбралидругойобъектвcombobox1}

procedure TfMain.ComboBox1Change(Sender: TObject);

begin

MaskEdit1.Text := sprav[ComboBox1.ItemIndex].TelNum; if sprav[ComboBox1.ItemIndex].Mobil then

ComboBox2.ItemIndex := 0 else ComboBox2.ItemIndex := 1;

Edit1.Text := sprav[ComboBox1.ItemIndex].Imya; Edit2.Text := sprav[ComboBox1.ItemIndex].Otchestvo; Edit3.Text := sprav[ComboBox1.ItemIndex].Familiya; Edit4.Text := sprav[ComboBox1.ItemIndex].Adres; end;

ShowMessage(s); end; //конецпроцедуры

Уданомуприкладі, здопомогоюциклу for миелементмасиву a[i] привласнювалирядок «Рядок №0», причомуномеррядказмінювавсяприкожномупроходіциклу. Потім отриману рядок ми додавали в строкову змінну s також додавали і символи переходу на новий рядок, щоб наприкінці вивести все це за допомогою ShowMessage(). Спробуйте зробити новий додаток з однією кнопкою на формі, і присвойте цей код обробці кнопки.

Тепер розглянемо такий приклад, але вже для двомірного масиву:

var

a : array [0..9, 0..4] of string; i,j : Integer; s : string; begin

for i := 0 to 9 do

for j := 0 to 4 do begin

a[i, j] := 'Строка№'+IntToStr(i)+

' Столбец№'+IntToStr(j); s := s + a[i,j]+#13+#10; end; //for 2

ShowMessage(s);

end; //конецпроцедуры

Як бачите, тепер нам доводиться використовувати два циклу for - один для рядків, і один для стовпців. Спочатку виконується цикл для першого рядка, і керування передається на вкладений цикл. Там відбувається обробка масиву від першого до останнього стовпця першого рядка.

Потім програма повертається в перший цикл, додає до лічильника одиницю і відбувається така ж обробка для другого рядка, від першого до останнього рядка. І так далі, поки не буде оброблений останній стовпчик останнього рядка!

Тривимірні масиви обробляти ще складніше. Така таблиця буде виглядати вже як кубічна:

Рис. 23.1. Подання тривимірного масиву

Оголошуватися такий масив буде вже так:

a : array[0..9, 0..4, 0..7] ofstring;

І для його циклічної обробки знадобиться вже три циклу for! Як буде виглядати чотиривимірний масив, графічним чином взагалі неможливо описати. Незважаючи на те, що у вас є можливість працювати хоч з десятимерным масивом, потреба більше, ніж у тривимірному в практиці програміста майже не зустрічається, а найчастіше доводиться працювати з одно - і двомірними масивами.

Сітка рядків StringGrid

Тепер на практиці познайомимося з сіткою рядків, схожу на ту, що використовується у файлах MSExcel. Створіть новий додаток, встановіть на форму сітку StringGrid з вкладки компонентів Additional. Поруч знаходиться ще сіточка DrawGrid. Різниця між ними невелика, але першу використовують частіше. Сітка StringGrid самостійно промальовує дані в комірках, при використанні сітки DrawGrid ці дані доведеться промальовувати вручну. Сітку DrawGrid зазвичай використовують для промальовування графіки, і потреба в ній виникає набагато рідше, ніж у StringGrid.

Як бачите, сітка StringGrid дуже схожа на ту, що ви бачили в MSExcel. Поки що вона порожня. Виділіть її і подивіться на властивості.

BorderStyle відповідає за стиль обрамлення. Може мати тільки два значення - з обрамленням і без нього.

ColCount - кількість колонок в сітці. За замовчуванням їх 5.

DefaultColWidth - ширина колонок за замовчуванням. На жаль, всім колонок встановлюється фіксована ширина. Однак під час роботи програми ширину кожної колонки можна буде змінити програмно.

DefaultDrawing - промальовування даних за замовчуванням. Якщо стоїть True, то компонент сам буде відображати введені дані, інакше це доведеться робити програмісту.

DefaultRowHeight - висота рядків за замовчуванням. Встановлено 24 пікселя, але цей розмір завеликий, тому рядки виходять такими високими.

FixedCols - кількість фіксованих колонок. Вони виділяються сірим кольором, і завжди перші. Це властивість можна назвати заголовком рядків. Практично не буває необхідності робити більше однієї такої колонки. За замовчуванням якраз одна колонка є, спробуйте зробити дві, а потім поверніть одну.

FixedRows - кількість фіксованих рядків. За замовчуванням теж одна, і працює також, як і FixedCols. Як правило, ця рядок служить заголовком колонок.

GridLineWidth - товщина розділових ліній. Спробуйте поставити нуль - лінії зникнуть. Поверніть одиницю.

Options - найголовніше властивість компонента. Воно містить багато налаштувань, які розкриються, якщо клацнути по плюса зліва від назви властивості. Ці додаткові властивості мають наступні призначення:

    • goFixedVertLine - малювати чи вертикальні лінії у фіксованих осередків? За замовчуванням True.

    • goFixedHorzLine - промальовування горизонтальних ліній у фіксованих осередків.

    • goVertLine - промальовування вертикальних ліній у всіх інших (нефіксованих) осередків.

    • goHorzLine - промальовування горизонтальних ліній у нефіксованих осередків.

    • goRangeSelect - дозвіл виділяти декілька комірок. Не працює, якщо включений елемент goEdit.

    • goDrawFocusSelect - дозволено виділяти комірку, яка знаходиться у фокусі введення.

    • goRowSizing - дозволено змінювати висоту рядка перетягуванням мишею.

    • goColSizing - дозволено змінювати ширину колонки перетягуванням мишею.

    • goRowMoving - чи можна мишею переміщати рядка на інше місце.

    • goColMoving - чи можна мишею переміщати колонки на інше місце.

    • goEditing - чи можна редагувати сітку? Тобто вводити дані з клавіатури. Ігнорується, якщо включений елемент goRowSelect.

    • goTabs - чи можна перемикатися на інші комірки за допомогою клавіші <Tab>.

    • goRowSelect - виділяється вся рядок. Якщо одно False, то тільки одна комірка.

    • goAlwaysShowEditor - якщо Так, то редагувати клітинку можна відразу при виділенні. Якщо False, то для редагування потрібно натиснути <Enter> або <F2>.

    • goThumbTracking - чи дозволена промальовування даних у комірках прокрутки. Якщо ні, то дані будуть оновлені після прокручування.

RowCount - кількість рядків у сітці.

З іншими властивостями ви вже знайомі з минулим компонентів, тут вони практично нічим не відрізняються.

Отже, приступимо до прикладу. Ми вже говорили, що за замовчуванням дається дуже велика висота рядків, зменшіть її до 16 (властивість DefaultRowHeight). У нас повинно бути 5 рядків і 5 колонок, причому фіксованими будуть по одній колонці і одному рядку. Далі, користувач повинен мати можливість вводити дані в комірки, тому в розділі Options у властивості goEditing встановимо True.

Тепер створюємо для форми подія onShow, і вписуємо наступний код:

begin

//заполняемзначениямипервуюколонку: StringGridl.Cells[0,1]:= 'Иванов'; StringGridl.Cells[0,2]:= 'Петров'; StringGridl.Cells[0,3]:= 'Николаев'; StringGridl.Cells[0,4]:= 'Бонд';

//заполняемзначениямипервуюстроку:

StringGridl.Cells[1,0]:= 'Годрожд.';

StringGridl.Cells[2,0]:= 'Месторожд.'; StringGridl.Cells[3,0]:= 'Прописка';

StringGridl.Cells[4,0]:= 'Семейноеположение';

//меняемширинуколонокStringGridl.ColWidths[4]:= 120; StringGridl.ColWidths[3]:= 90; StringGridl.ColWidths[2]:= 90;

end;

Як бачите, звернення до окремих осередків тут точно таке, як до двухмерному масиву. Першим індексом служить рядок, другим - колонка. Нумерація індексів починається з нуля, тому верхня ліва осередок буде мати індекс [0, 0].

Властивість ColWidths [і] встановлює у точках ширину колонки з індексом i.

Збережете, зберіть його і подивіться результат. Якщо ваша сітка надто маленька або навпаки, занадто велика, змінити її розмір, щоб всі колонки вміщалися в сітці.

Поліпшимо наш приклад, додавши маску для введення дати до стовпчику «Рік народження». Робиться це зовсім просто - виділяєте сітку, перейдіть на вкладку Events і генерируете подія onGetEditMask. Ця подія відбувається, коли користувач редагує сітку. Там вписуєте тільки один рядок:

if ACol=l then value := '99.99.9999 г.'

Якщо ви подивіться на параметри цієї події, то побачите, що в нього передаються такі параметри, як ACol і ARow. Це - індекс поточної колонки і поточного рядка. Параметр Value містить текст маски. Тобто, якщо користувач редагує колонку з індексом 1 - це друга колонка, де вказується рік народження, ми встановлюємо маску введення дати.

Як бачите, робота з сіткою не така вже складна. Принцип роботи такий сітки полягає в тому, що ви заповнюєте сітку даними, як правило, заголовками рядків і стовпців, а потім дозволити користувачу редагувати їх. Після чого введені користувачем дані можна переписати в двомірний масив, або в масив з записами, а потім зберегти у файл.

Лекція 24. Прикрашення проекту

Сьогодні займемося прикрас проектів - будемо додавати в проект зображення самими різними способами. Ви повинні пам'ятати, що не варто перенасичувати проект, створювати фон форм диких забарвлень, тут і там вставляти абсолютно непотрібні картинки. Все це говорить не тільки про непрофесіоналізм, але і про поганому смаку. Однак іноді прикрашення не заважають, а навпаки, створюють для проекту особливий стиль. Згадайте про програвача WinAMP - він практично весь складається з таких прикрас, але це виправдано, оскільки програма емулює звичайний побутовий програвач. Іншими словами, потрібно вміти вставляти в проект зображення, але при цьому мати почуття міри.

Нам доведеться вивчити кілька корисних компонентів. Створіть новий додаток.

Image

Компонент знаходиться на вкладці компонентів Additional і являє собою контейнер прямокутної форми, в який можна помістити зображення. Встановіть його на форму, і подивіться на його властивості.

Властивість AutoSize змушує компонент автоматично приймати розмір зображення, якщо встановлено True.

Властивість Picture - основна властивість компонента. Воно дозволяє відкрити діалогове вікно, у якому можна вибрати зображення для завантаження в контейнер. Для цього потрібно натиснути на кнопку з трьома точками в правій частині властивості. Потім натиснути кнопку Load для завантаження зображення.

Властивість Center розмістить зображення по центру контейнера, якщо AutoSize не одно True. Інакше властивість ігнорується.

Властивість Proportional дозволяє або забороняє пропорційно зменшити висоту і ширину зображення, якщо воно не може цілком вміститися в контейнері.

Властивість Stretch дозволяє або забороняє зображення цілком заповнювати контейнер.

Давайте напишемо простий браузер для зображень, програму, яка дозволить переглядати файли графічних форматів. Новий додаток у вас вже створено, додайте на просту форму панель, перемістіть її вгору форми, і властивість Align зробіть alTop (панель повинна зайняти весь верх форми). Очистіть властивість Caption форми, і у нас вийшла панель інструментів.

Правда, нам потрібен тільки один інструмент - кнопка для завантаження зображень. Встановіть на панель кнопку, напишіть на ній «Завантажити зображення».

Властивість Align компонента Image встановіть alClient, щоб розтягнути його по всій іншій частині форми. У цього контейнера повинні бути наступні налаштування в Об'єктному інспектора:

AutoSize = False;

Center = True;

Proportional = True;

Stretch = True;

Тепер встановіть на будь-яке місце компонент OpenPictureDialog з вкладки Dialogs панелі компонентів. Цей компонент працює точно також, як діалог відкривання файлів, тільки він призначений для відкриття зображень.

Створіть обробник подій для натискання на кнопку, і там напишіть код:

if OpenPictureDialog1.Execute then

Image1.Picture.LoadFromFile(OpenPictureDialog1.FileName);

Ось і весь браузер! Після натискання на кнопку програма буде виводити діалогове відкриття зображення. Вибравши зображення, ви завантажте його в контейнер Image. Збережете, зберіть і подивіться, як працює програма. Для перегляду підійдуть будь-які зображення - картинки, фотографії і т.п. Якщо у вас на комп'ютері встановлено пакет MSOfficeXP/2000 або вище, то велику колекцію зображень ви зможете знайти на:

C:\Program Files\MicroSoft Office\Media\cagcat10

Програма із завантаженою картинкою має виглядати так:

Рис. 24.1. Зовнішній вигляд програми

Якщо ж по вашому задумом потрібно встановити якусь одну картинку для поліпшення дизайну форми, то ви можете завантажити цю картинку прямо, через властивість Picture компонента, в Об'єктному інспектора.

Одне зауваження: фотографії, як правило, мають формат JPEG, а підтримка такого формату у компонента Image вимкнена. Щоб програма могла працювати з цим форматом, додайте в розділ підключених модулів Uses модуль JPEG.

Іmagelіst

Компонент знаходиться на вкладці Win32 і призначений для зберігання декількох зображень. Цей компонент використовується в багатьох випадках - для зберігання колекції зображень, що вставляються в головне і спливаюче меню програми, коли зліва від команди виводиться і картинка, для виводу зображення на кнопки панелі інструментів, і так далі.

Вивчимо цей компонент на практиці. Створіть новий додаток. Встановіть на форму компонент MainMenu і Іmagelіst. Далі, у головному меню створіть розділи «Файл» і «Допомога». У розділі «Файл» створіть підрозділи «Відкрити», «Зберегти», «-» і «Вихід». А в розділі «Допомога» -підрозділ «Про програму».

Тепер двічі клацніть по компоненту Іmagelіst. Відкриється діалогове вікно завантаження зображень:

Рис. 24.2. Вікно завантаження зображень компонента Іmagelіst.

Скористаємося тими самими стандартними зображеннями, які ми використовували для кнопок. Натисніть кнопку «Add» (додати зображення) і перейдіть в каталог зі стандартними картинками для кнопок:

C:\Program Files\Common Files\Borland Shared\Images\Buttons

Виберемо картинку «Fileopen». Тут же виходить запит, в якому говориться, що картинка складається з 2-х зображень, і виникає запитання, чи хочете ви розбити його на 2 картинки? Справа в тому, що стандартні зображення для кнопок складаються з двох малюнків - коли кнопка знаходиться в активному стані (Enabled = True), і в неактивному. У Іmagelіst ж можна ввести тільки одну картинку. Відповідайте «Yes». Подвійне зображення буде розбите, і у вікно потраплять дві картинки. Виділіть сіру картинку і натисніть кнопку «Видалити», щоб видалити її зі списку, вона нам не потрібна. В більшості випадків для команди меню досить одного зображення. Якщо ви зробите команду неактивній, Delphi самостійно змінює це зображення, малюнок виходить сірим кольором.

Теж саме зробіть з картинками «Filesave», «Dooropen» і «Help».

Як бачите, індекс зображень починається з нуля. Натисніть кнопку «ОК», щоб прийняти список зображень.

Тепер виділіть компонент MainMenu, і у властивості Images виберіть наш Іmagelіst.

Двічі клацніть по MainMenu, щоб відкрилося вікно редактора меню. Виберіть пункт «Відкрити» (клацнувши по ньому один раз, а не два, інакше створиться обробник подій). Властивість команди меню ImageIndex відповідає за те, яке саме зображення буде відповідати цій команді. За замовчуванням воно дорівнює -1, тобто, картинка не вибрано. Перша картинка має індекс 0, друга - 1, і так далі. Виберіть зображення з картинки FileOpen, воно повинно бути першим. Ви можете вибрати будь-яке зображення, не обов'язково по порядку.

Для пункту «Зберегти» вибирайте друге зображення, для «Вихід» - третє, а для «Про програму» - останнє.

Закрийте редактор меню, і відкрийте меню на формі. Ви повинні побачити, що зліва від тексту з'явилися наші картинки. Ось таким простим чином можна прикрасити наше меню. До речі, те ж саме справедливо і для спливаючого PopupMenu. Спробуйте самостійно додати в нього такі команди, як в розділі «Файл», і застосувати для них ті ж картинки (для цього не потрібно створювати ще один Іmagelіst, можна скористатися цим же).

Самостійне завдання

Створіть новий додаток. Мета: завантаження зображення на форму, в компонент Image. Інструменти для вибору команд: тільки MainMenu і PopupMenu (тобто, на відміну від першого прикладу, завантажуватися зображення буде не по натисненню кнопки, а вибором меню «Файл – Завантажитизображення», або спливаючим меню «Завантажити зображення». На панелі інструментів формі не буде.). Також в меню повинна працювати команда «Вихід». У головному меню по команді «Допомога - Про програму» повинно виходити вікно «Про програму». Спосіб та інструменти завантаження зображень такі ж, як у першому прикладі. Імена форм і модулів, а також проекту подумайте самостійно.

При завантаженні зображення у заголовку головною форми крім назви програми також має виходити і ім'я завантаженого файлу.

Лекція 25. Мультимедіа в Delphi

Що таке мультимедіа? На думку відразу ж приходять файли звукових, музичних і відео форматів. Незважаючи на гадану складність специфіки мультимедійних програм, зробити аудіо-відео на Delphi досить просто. Давайте відразу розглянемо це на прикладі. Створіть новий додаток. Форму перейменуйте в fMain, у властивості Caption вкажіть «Мій медіапрогравач» і збережіть проект під назвою myPlayer.

В нижню частину форми встановіть кришку. Потягніть її по всьому низу (Align = alBottom). Вище встановіть ще одну панель, потягніть її по всій решті частини форми (Align = alClient), це буде екран. Зрозуміло, властивості Caption обох панелей потрібно очистити.

Далі на нижню форму встановимо компонент Edit, в нього ми будемо записувати ім'я завантаженого файлу. Відразу можна встановити у нього властивість ReadOnly у True, адже користувачеві не потрібно редагувати ім'я файлу. І очистіть у цього компоненту текст.

Нижче компонента Edit встановіть компонент MediaPlayer з вкладки System панелі компонентів. Це основний компонент для програвання і звуку і відео. У нього є кілька корисних властивостей, з якими ми познайомимося трохи пізніше. Встановіть у Edit такі ж властивості Left та Width як у MediaPlayer. А сам плеєр (властивість Name) перейменуйте в MP, щоб не потрібно було писати довгого імені при зверненні до його властивостях.

Далі, нам знадобиться головне меню, щоб можна було відкрити файл. Створіть там пункт «Файл» і підпункти «Відкрити», «-» і «Вихід». Ще один пункт «Допомога» та підпункт «Про програму».

Тепер у верхній панелі (це у нас Panel2) встановіть чорний колір (властивість Color). Це буде екран для відтворення відеофайлів. Взагалі то ви можете встановити екран будь-який колір, або зовсім його не міняти, однак чорний фон для відтворення відео більш приємний для очей, тому екрани в подібних програмах роблять зазвичай чорного кольору, на відміну від білих екранів кінотеатрів.

Повернемося до компоненту MediaPlayer. Як бачите, він складається з ряду кнопочок, які мають власні імена та призначення. Крім того, компонент MediaPlayer має методи, які дублюють ці кнопочки, що дозволяє використовувати компонент у двох варіантах - видимим і невидимим. У першому випадку, користувачеві надається можливість управляти відтворенням безпосередньо з цього компонента, у другому випадку компонент не видно, і ми можемо прив'язати його методи до власних кнопочок або пунктів меню. Ми скористаємося першим методом.

Давайте розглянемо кнопки зліва направо, їх імена і призначення. Назви кнопок та їх описи докладно наведені в таблиці 25.1:

Таблиця 25.1. Назви та описи кнопок Mediaplayer.

Имя кнопки

Назначение кнопки

Play

Відтворення

Pause

Пауза відтворення або запису.

Stop

Зупинка відтворення або запису.

Next

Перехід на наступний трек або на кінець.

Prev

Перехід на попередній трек або на початок.

Step

Переміщення вперед на задане число кадрів.

Back

Переміщення назад на задане число кадрів.

Record

Початок запису.

Eject

Звільнення об'єкта, завантажених у пристрій.

Кожній кнопці, як уже говорилося, відповідає метод з таким же дією і ім'ям. Тобто, натискання на кнопку «Play» призведе до того ж, що і команда

MP.Play;

тобто, до програвання обраного файлу.

Розберемо властивості компонента.

AutoEnable. Це властивість вмикає і вимикає автоматичне визначення доступності кнопок компонента. Наприклад, якщо немає пристрою, який потрібно звільняти, то кнопка Eject буде недоступна.

AutoOpen. Якщо це властивість одно True, то компонент відразу при завантаженні програми починає програвати вказаний файл. Однак при проектуванні потрібно буде відразу вказати назву файла. Ми ж робимо плеєр, і заздалегідь не знаємо, який файл буде програвати користувач, тому залишимо його значення False.

DeviceType. Містить тип пристрою. Наприклад, музика або відео файл. За умовчанням встановлено тип dtAutoSelect, тобто компонент буде самостійно визначати тип пристрою. Так його і залишимо.

Display. Цікава властивість. Воно вказує, який саме компонент ми будемо використовувати, як екран для перегляду відео. Виберемо там Panel2, яку ми пофарбували чорним кольором.

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

FileName. Містить ім'я медіа-файлу. Якщо ми вкажемо його жорстко, то властивість AutoOpen можна поставити у True, і тоді при завантаженні програми файл автоматично почне відтворюватися. Але ми залишимо його порожнім, а прописувати файл будемо програмно.

VisibleButtons. Працює аналогічно EnableButtons, тільки робить зазначені кнопки невидимими.

З методів компонента цікаві наступні:

Open. Відкриває мультимедійний пристрій. Перед тим, як програвати файл, пристрій обов'язково потрібно відкрити.

DisplayRect. Метод вказує розмір та позицію екрану. Цей метод повинен отримати в якості параметра 4 цілих числа.

Close. Закриває пристрій.

З теорією покінчено, перейдемо до практиці. Нам потрібно також компонент OpenDialog, щоб користувач міг вибрати файл. В результаті, у вас повинна вийти така форма:

Тепер про форматах файлів. На жаль, не кожен формат можна буде програти в такому програвачі. Різні відео - та аудіо файли мають власні формати. Наприклад, навіть файл *.avi може бути різного формату. Компонент розрахований під звукові формати *.wav та *.mid, якщо ж в операційній системі встановлений медіа плеєр, то можна програвати і *.mp3.

Підтримуваний відео-формат - *.avi. Однак, якщо встановлені різні «кодеки», тобто драйвери під різні формати, то можна дивитися і файли формату mpeg4, однак *.dat, в якому зазвичай робляться кліпи, подивитися не вдасться.

Тому у властивості Filter компонента OpenDialog потрібно буде вказати відповідний фільтр:

Відео файли *.avi;*.mpg

Аудіо файли *.wav;*.mid;*.mp3

Всі файли *.*

Давайте відразу ж обробимо команду меню «Файл - Вихід, і напишемо там

Close;

Тепер будемо мати на увазі, що користувач може закрити програму і кнопкою, а нам обов'язково потрібно буде закрити компонент MediaPlayer. Тому робимо для форми обробник подій onDestroy і там його закриваємо:

MP.Close;

Тепер створюємо обробник для команди меню «Файл - Відкрити». Тут ми маємо на увазі, що користувач може спробувати відкрити файл з несумісним форматом. Тоді виникне помилка. Тому ми скористаємося блоком виняткових ситуацій

try– except – end;

Цей блок відрізняється від знайомого нам

try– finally– end;

тим, що в разі виникнення помилки буде виконаний блок except - end;, а якщо помилки не буде, блок не виконається. На відміну від нього, блок finally - end; буде виконаний в будь-якому випадку. Отже, наш код обробки команди меню «Файл - Відкрити»:

procedureTForm1.N2Click(Sender: TObject); begin

if not OpenDialog1.Execute then Exit; try

Edit1.Text := ExtractFileName(OpenDialog1.FileName);

MP.FileName := OpenDialog1.FileName;

MP.Open;

MP.DisplayRect := Rect(0,0,Panel2.Width, Panel2.Height);

except

ShowMessage('Ошибкаоткрытияфайла!');

Exit;

End: // try

End;

Щотутвідбувається? Якщо діалог з користувачем не відбувся, то ми виходимо з процедури, нічого не роблячи. Інакше відкриваємо блок try - except - end. Якщо між try і except станеться помилка (користувач спробує відкрити файл не підтримується формату), то виконається блок між except - end, в якому ми пояснюємо користувачеві, що сталася помилка відкриття файлу.

Далі ми виводимо в Edit1 назву виділеного файлу, після чого цей же файл присвоюємо медіа плеєра. Потім ми відкриваємо плеєр. Рядок

MP.DisplayRect := Rect(0,0,Panel2.Width, Panel2.Height);

Змушує відеозображення прийняти розміри форми: ми вказуємо координати верхнього кута 0,0 - це самий верхній лівий кут панелі, і ширину і висоту панелі, як координати іншого куту. В результаті розмір відеозображення займе всю панель. Однак майте на увазі, що фільм може бути і широкоформатним, тому він буде спотворений.

Щоб виправити справу, користувачеві доведеться змінювати розмір форми мишкою. Якщо ми не передбачимо зміна користувачем розмірів екрану, розміри зображення змінюватися не будуть. Створимо для форми подія onResize, яке відбувається кожного разу, коли користувач змінює розміри форми, і продублюємо там зазначену вище рядок.

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

Збережете, зберіть його, і подивіться, як він працює.

Стандартні звуки Windows можна знайти в папці

C:\windows\media

А відео файл можна знайти за адресою

C:\ program files\borland\delphi7\demos\coolstuf\speedis.avi

Залишилося тільки зробити форму About, і виводити її по команді меню «Довідка Про програму». Але це вже зробіть самостійно.

Лекція 26. Побайтовое копіювання / перенесення файлів

В житті кожного програміста не раз зустрічається необхідність написати програму, яка могла б забезпечити стандартні функції роботи з файлами - копіювання, переміщення, видалення та інше. Сьогодні ми будемо створювати програму, яка не тільки все це робить, але ще і виводить повідомлення про розмір файлу, і показує індикатор копіювання. Файл будемо копіювати побайтово, точніше, використовувати буфер копіювання, в який будемо зчитувати символи.

Створіть новий проект. Форму відразу ж перейменуйте в fMain, у властивості Caption напишіть «Робота з файлами» а проект збережіть нову папку під назвою «FileManager». Стиль форми зробіть bsDialog, а положення форми - poDesktopCenter.

Далі, встановіть на форму компонент Label, у властивості Caption якого напишіть«:». Поруч встановіть Edit, і видаліть з нього текст. У цей Edit ми будемо заносити вихідний файл, або що ми збираємося копіювати або переносити.

Нижче встановіть ще по одному Label і Edit. У Label напишіть «Куди:», а у Edit видаліть текст. Обидва Edit трохи розширити, щоб могли вміститися імена файлів із довгими адресами.

Нижче встановіть ще один Label, і в ньому напишіть: «Розмір файлу:».

Ще нижче будуть дві кнопки - «Копіювати» і «Перенести».

У самому низу встановимо індикатор копіювання. Зазвичай для таких цілей встановлюють індикатор ProgressBar з вкладки Win32. У нього за показ процентного відносини відповідає властивість Position. Встановіть цей компонент і спробуйте цій властивості використовувати різні значення від 0 до 100, щоб подивитися, як змінюється зовнішній вигляд компонента. Однак ми для цих цілей скористаємося іншим компонентом, тому видаліть ProgressBar і перейдіть на вкладку Samples. Там виберіть компонент Gauge, і встановіть його на форму. Як бачите, за замовчуванням Gauge має квадратну форму. Властивості Height присвойте значення 30, а властивості Width - 270. Таким чином, ми додали Gauge горизонтальну спрямованість. Цей компонент не тільки виглядає привабливіше попереднього, але також показує відсоток виконаної дії! За цей відсоток відповідає властивість Progress, спробуйте присвоїти йому різні значення від 0 до 100. Яким саме компонентом користуватися в таких випадках - справа смаку кожного програміста. Але ви повинні знати, що в стандартній поставці Delphi є два компоненти, які виконують цю дію. Поверніть Progress в нуль. Ви, напевно, вже встигли помітити, що компонент за замовчуванням забарвлюється в чорний колір. Це трохи похмуро, тому у властивості ForeColor виберіть темно-синій колір.

Для того, щоб користувач не вводив багато зайвого тексту, полегшимо йому роботу і встановимо два діалогу - OpenDialog і SaveDialog. А для їх виклику праворуч від компонентів Edit встановіть по одній кнопочку, ширину і висоту яких зробіть такий же, як висота Edit, тобто, 21, щоб кнопочки вийшли квадратними. В результаті, компонент Edit і стоїть праворуч від нього квадратна кнопка здаються одним елементом інтерфейсу. У вас повинна вийти форма приблизно з таким інтерфейсом:

Рис. 26.1. Зовнішній вигляд програми.

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

Функція перенесення файлу відрізняється від функції копіювання тільки тим, що в кінці роботи ми видалимо вихідний файл, отже, для переноса будемо викликати процедуру копіювання, щоб не писати двічі один і той же код.

Для того, щоб не плутатися при написанні коду, перейменуйте верхній Edit в Otkuda, а нижній - в Kuda (властивість Name). Так ми будемо точно знати, де і що у нас знаходиться.

Тепер займемося написанням коду програми. Для початку оголосимо процедуру, яка буде побайтово копіювати файл. Раніше ми вже вводили в програми власні функції і процедури. Ми їх ніде не оголошували, а просто описували вище того місця, звідки надалі викликали. Цей спосіб мав один мінус - така процедура не була частиною об'єкта Форма, і для того, щоб звернутися до якого-небудь компоненту, нам доводилося спочатку звертатися до формі, наприклад:

Form1.Edit1.Text

Однак власну процедуру або функцію можна зробити частиною форми, якщо спочатку її оголосити. Робиться це в розділі private, відразу під коментарем:

private

{ Privatedeclarations }

proceduremyCopy;

Тепер нашу процедуру можна викликати з будь-якої частини форми. Якщо б ми описали її в розділі public, то її можна було б викликати і з інших модулів.

Ми оголосили цю процедуру в об'єкті TfMain, щоб можна було звертатися до неї з будь-якого місця програми і безпосередньо використовувати компоненти форми. Далі сгенерируем тіло нашої процедури. Це можна зробити і вручну, а можна доручити це Delphi. Для цього встановіть курсор на ім'я оголошеної процедури, і натисніть клавіші <Ctrl+Shift+C>, порожня процедура буде автоматично сгенерирована. Зверніть увагу, що ім'я процедури починається зі звернення до імені форми:

procedureTfMain.myCopy; var

f1, f2: File; // первыйивторойфайлcop: array[1..2048] ofChar; //буферкопирования

sizefile, sizeread: Int64; //размерфайлаиразмерпрочитанногоcolRead, colWrite : Integer; //прочитаноизаписаноfOtkuda, fKuda : String; //адресаиименафайловbegin

//даемкомпиляторудирективу, чтобынеотслеживалошибкиввода-вывода:

{$I-}

//проверяем, указанылифайлы. еслинет - выходим

if (Otkuda.Text='') or (Kuda.Text='') then begin

ShowMessage('Укажите какойфайлкопировать/переносить, икуда');

Exit; end;

//iftry

//связываемфайловыепеременные:

AssignFile(f1, Otkuda.Text);

AssignFile(f2, Kuda.Text);

//открываемпервыйфайлдлячтения:

Reset(f1, 1);

//определяемегоразмервпеременную:

sizefile := FileSize(f1);

//отображаемразмерфайлавкилобайтах:

Label3.Caption := 'Размерфайла: '+IntToStr(Round(sizefile / 1024)) + ' Кб.';

//создаемилиперезаписываемвторойфайл:

Rewrite(f2, 1);

//делаем, поканедостигнутконецисходногофайла

colRead := 0;

colWrite := 0;

sizeread := 0;

Screen.Cursor := crHourGlass; //песочныечасы

while colRead = colWrite do begin

BlockRead(f1, cop, SizeOf(cop), colRead);

if colRead = 0 then break;

//двигаеминдикаторкопирования

BlockWrite(f2, cop, colRead, colWrite);

sizeread := sizeread + colRead;

Gauge1.Progress := Round(100*sizeread/sizefile); end; //while Screen.Cursor := crDefault; //обычныйвидкурсора

finally

CloseFile(f1);

CloseFile(f2);

end; //try

//исправляемдатуfOtkuda := Otkuda.Text;

fKuda := Kuda.Text; PravkaDate(fOtkuda, fKuda);

if IOResult <> 0 then

Application.MessageBox('Ошибка прикопированиифайла!', 'Внимание!!!', MB_OK+MB_ICONERROR) else ShowMessage('Копирование успешнозавершено!'); //включаемобработчиккомпиляторомошибок{$I+} end;

Отже, що нового для нас в цьому коді? По-перше, директиви компілятору. Директива ${I-} відключає обробку компілятором помилок введення-виведення (Input-Output), нам це необхідно, щоб отримати результат - була помилка чи ні. Нижче ми знову включаємо цю обробку: ${I+}.

Далімиперевіряємо - чивистачаєнамінформаціїдлякопіюваннячипереміщення, тобточивибравкористувач, щокопіюватиікуди. Якщо немає - виводимо повідомлення і виходимо, нічого не роблячи.

Даліякзвичайно, мипов'язуємофайлизфайловимизмінними, відкриваємопершийфайлдлячитання, іншийдлязапису.

Обнуляем змінні, які будуть працювати як лічильників прочитаних і записаних байт. Цікавий рядок

Screen.Cursor := crHourGlass; //песочныечасы

Якщо ви виділите форму і подивіться властивість Cursor, то побачите список, в якому присутній і вказане значення. Ця команда на час копіювання файлу робить курсор у вигляді пісочного годинника, щоб користувач бачив, що процес іде. Потім ми повертаємо курсору початковий вигляд:

Screen.Cursor := crDefault; //обычныйвидкурсора

Далі, всередині циклу копіювання, ми підраховуємо загальний розмір прочитаних байт, щоб потім розрахувати відсоток виконаної роботи і пересунути індикатор Gauge:

sizeread := sizeread + colRead;

Gauge1.Progress := Round(100*sizeread/sizefile);

Оскільки властивість Progress має цілий тип, нам доводиться округляти отриманий результат до найближчого цілого функцією Round().

На жаль, при копіюванні звичайними засобами Delphi файл успішно копіюється і переноситься, але при цьому дата і час створення копії будуть поточними - адже для операційної системи ми просто створили новий файл! Для виправлення дати ми викликали процедуру PravkaDate,куди в якості параметрів передали адреси та імена вихідного файлу та копії. Тепер нам потрібно описати цю процедуру, причому зробити це вище тієї процедури, з якої ми її називаємо.

{Процедураисправлениядаты. x - адресиимяфайла-оригинала,

y - файла-копии} procedurePravkaDate(varx, y : String); //исправлениедатыивременисозданияфайла

var

origin, kopiya : integer; //дескрипторыDateOrigin : integer; //времясоздания

begin

try

//открываемфайлы:

origin := FileOpen(x, fmOpenRead);

kopiya := FileOpen(y, fmOpenWrite);

//получаемдату:

DateOrigin := FileGetDate(origin);

//записываемдатувфайл-копию:

FileSetDate(kopiya, DateOrigin);

finally

FileClose(origin); FileClose(kopiya); end; //try end;

Ця процедура використовує функції WinAPI, які дозволяють здійснювати над файлами більш серйозні дії, але зате вони складніше у використанні. Коли ми відкриваємо файли, то в змінні - дескриптори заноситься ідентифікатор відкритого файлу у вигляді цілого числа. За цим кодом відбувається подальша робота з файлом. Далі ми отримуємо дату та час створення файлу у форматі MS-DOS, який являє собою довгий ціле число. За це у нас відповідає функція FileGetDate(), куди в якості параметра ми передаємо дескриптор файлу. Точно також ми прописуємо отримані дату - час у файл - копію за допомогою функції FileSetDate(). Наприкінці ми закриваємо файли.

Цю процедуру ви можете виписати окремо, і потім вставляти в будь-яку програму, де доводиться виправляти дату і час у копії.

Далі обробляємо кнопку «Копіювати». Там ми викликаємо процедуру копіювання, а потім встановлюємо індикатор копіювання в нуль. Якщо цього не зробити, то після кожного копіювання цей індикатор буде показувати 100%.

myCopy;

Gauge1.Progress := 0;

З кнопкою «Перенесення» трохи складніше. Якщо користувач не вказав всі необхідні параметри, то процедура копіювання поверне йому повідомлення про це, і вийде, нічого не роблячи. Адже після цієї процедури нам потрібно буде видалити оригінал! Тому перед викликом процедури копіювання нам доведеться ще раз перевірити - чи всі заповнив користувач? Якщо ні, то видаляти оригінал ми не будемо:

myCopy;

Gauge1.Progress := 0;

if (Otkuda.Text='') or (Kuda.Text='') then Exit;

DeleteFile(Otkuda.Text);

Ну, а з кнопками біля наших Edit зовсім просто - перший обробляє OpenDiaog:

if OpenDialog1.Execute then

Otkuda.Text := OpenDialog1.FileName;

АвторойобрабатываетSaveDialog:

if SaveDialog1.Execute then

Kuda.Text := SaveDialog1.FileName;

Ось і вся програма! На перший погляд вона складна, але якщо проаналізувати код і спробувати зазначені прийоми в різних прикладах, то програма виявиться не такою вже й складною, адже всі використовувані функції і процедури нам вже знайомі. А головне, їх майже без змін можна використовувати в будь-якому проекті, де потрібно зробити перенесення або копіювання файлу! Збережете, зберіть і подивіться, як працює програма. Файли невеликих розмірів будуть оброблятися моментально, щоб ви встигли роздивитися процес, вибирайте файли побільше. Розмір буфера можна було б зменшити, але це призведе до збільшення циклу копіювання, що зменшують швидкість обробки файлу, а збільшення буфера відчутного прискорення не принесе.

Лекція 27. Панель інструментів

Що таке панель інструментів? Ми вже знаємо, що у багатьох програмах є панелі, на яких встановлені кнопочки з малюнками. Іноді кнопочки містять текст, частіше містять тільки зображення. При наведенні покажчика миші на таку кнопочку, через деякий час, виходить підказка - що це за кнопка. Як правило, кнопки панелі інструментів дублюють команди головного і (або) спливаючого меню, полегшуючи користувачеві роботу з програмою. Є два способи організувати панель інструментів, розглянемо обидва.

Кнопка SpeedButton

Створіть новий додаток. Встановіть на форму панель, очистіть у неї властивість Caption, властивість Align встановіть alTop, щоб вона зайняла весь верх форми. Властивість Height встановіть 24.

Тепер перейдіть на вкладку Additional і знайдіть кнопку SpeedButton. Встановіть її на панель. Щоб притиснути її до самого лівому краю, властивість Left встановіть у 0. Тепер погляньте на властивість Height, воно дорівнює 22. У панелі це властивість одно 24. Отже, щоб кнопка була посередині панелі, потрібно властивість Top встановити в 1. Тоді зверху і знизу кнопки вийде за 1 пікселя.

Тепер розглянемо, що ця кнопка вміє робити. Насамперед, вона відрізняється від інших кнопок тим, що не має фокусу вводу. Що це означає? При роботі програми один з компонентів має фокусу вводу, він виділений. Якщо це компонент для введення тексту (Edit, Memo), то користувач відразу може вводити текст. Якщо це кнопка, то користувач може натиснути <Enter>, що буде рівносильне натискання на кнопку миші. Крім того, клавішею <Tab> можна перемістити фокус вводу від одного компонента до іншого, порядок виділення компонентів визначається їх властивістю TabOrder. А кнопка SpeedButton фокусу вводу не має, її не можна виділити клавішею <Tab>, а якщо ви клацніть по ній мишею, то фокус вводу повернеться до того компоненту, в якому був до цього.

Властивість Glyph у цієї кнопки працює також, як у BitBtn і дозволяє завантажити на кнопку зображення. Відкрийте діалогове вікно цього властивості і виберіть картинку «DoorOpen» з стандартною колекції Delphi.

Двічі клацніть по кнопці SpeedButton і напишіть там вихід з програми:

Close;

Збережете, зберіть його, і подивіться, як кнопка працює.

У всіх сучасних додатках такі кнопки зазвичай виглядають більш плоскими. Щоб прибрати опуклість кнопки, змініть властивість Flat на True.

Тепер познайомимося ще з одним властивістю, яка має більшість компонентів. Це система підказок Hint, підказка виходить, коли користувач наведе курсор миші на компонент. У властивості Hint кнопки напишіть «Вийти з програми». Однак цього замало, потрібно ще дозволити підказці виходити на екран. За це відповідає властивість ShowHint. Але увага! Якщо ми встановимо це властивість у True, то тільки ця кнопка зможе виводити підказку, для інших кнопок його теж доведеться виставляти в True. Щоб цього не робити, можна встановити властивість ShowHint в True у батьківського компонента - панелі або самої форми. Тоді всі компоненти, розташовані на цьому об'єкті, будуть мати True в цій властивості. Давайте встановимо це властивість True у форми. Тепер і панель, і кнопка теж мають True в ShowHint. І будь-яка наступна кнопка, яку ми кинемо на панель поруч з першої також матимуть можливість виводити підказку. Зберіть проект і подивіться, як він працює.

Однак це ще не межа можливостей кнопки SpeedButton. Її ще можна групувати. Встановимо поруч ще дві такі кнопки. Зробіть так, щоб вони були впритул один до одного, але трохи віддалік від першої кнопки. На першу накладіть малюнок «led2on», на другу - «led2off» з колекції зображень Delphi:

Рис. 27.1. Фрагмент панелі інструментів

Ці кнопки розташовані поруч, і мають схожі картинки. Таким чином, користувач вже бачить, що вони відносяться до якого-то одній властивості. Виділіть обидві кнопки (утримуючи <Shift>і встановіть у них властивість GroupIndex в одиницю. Цим самим ми згрупували їх в одну індексний групу. Тепер у будь-який з них встановіть властивість Down (натиснута) у True. Збережете, зберіть його, і подивіться, як працює індексний група. Натисніть іншу клавішу, перша відіжметься. І навпаки. Саме таким чином ви можете, наприклад, вибрати накреслення шрифту або вирівнювання абзацу у MSWord.

Ви можете встановлювати скільки завгодно груп, і кожній їм у властивості GroupIndex свою цифру. Нуль означає, що кнопка не належить ні до якої групи. У кнопки з груповим індексом 0 ви просто не зможете перевести властивість Down у True.

Така панель інструментів виглядає привабливіше, ніж просто панель з кнопками, не чи правда? Проте є і більш професійний спосіб організації панелі інструментів.

Переставні панелі інструментів

Якщо вам в додатку потрібно лише пару-трійку кнопок на панелі інструментів, то простіше скористатися першим варіантом. Але якщо ви робите серйозне додаток і бажаєте, щоб у нього була професійна панель інструментів, то краще скористатися переміщуваною панеллю, як програм з MSOffice. Такі панелі можна рухати, можна відривати їх від форми і робити з них окреме вікно. Втім, давайте відразу ж напишемо приклад програми з такою панеллю.

Створіть новий додаток, встановіть на форму компонент ControlBar з вкладки Additional. Це простий компонент, працює як панель, але він дозволяє панелей інструментів переміщатися всередині себе за бажанням користувача. Властивість Align встановіть Top, а властивість AutoSize - у True. Тоді компонент буде автоматично розтягуватися або звужуватися, коли ви будете рухати панелі інструментів всередині нього.

Тепер перейдіть на вкладку Win32, і знайдіть там компонент ToolBar. Це і є панель інструментів, встановіть її поверх ControlBar. Як бачите, ControlBar одразу прийняв потрібну висоту. Якщо ви знімете виділення з цього компонента, то побачите, що він має оббирання зверху. Зазвичай на програмах такий волани немає, тому знімемо її. Властивість EdgeBorders компонента розкривається, і показує, які волани є. Встановіть ebTop в False. Тепер наша панель інструментів має професійний вигляд, тільки кнопочок їй бракує. Бажано і тут встановити властивість AutoSize в True.

Почнемо додавати кнопки. Клацніть правою кнопкою миші на панелі інструментів і виберіть команду NewButton. З'явилася нова кнопка. Команда NewSeparator у цьому меню створює роздільники між кнопками. Видалити кнопку або розділювач просто - виділяєте її та натисніть <>Delete.

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

Тепер встановимо на форму компонент Іmagelіst і завантажимо ті ж три картинки: opendoor.bmp, led2on.bmp і led2off.bmp.

Тепер перейдіть до панелі інструментів, і у властивості Images виберіть наш Іmagelіst. Зображення зі списку автоматично завантажилися в кнопки. Якщо вам не сподобалося розподіл цих картинок, можете змінити їх, змінюючи властивість ImageIndex. Перша картинка там має індекс 0, друга - 1, і так далі. Таким чином, ви можете присвоювати кнопок різні картинки зі списку.

Давайте розглянемо деякі корисні властивості кнопки на панелі інструментів. Сама кнопка називається ToolButton, але такого компонента на панелі інструментів немає, її можна завантажити, натиснувши на панелі інструментів правою кнопкою миші і вибравши команду NewButton. Кнопка по своїм властивостям схожа з кнопкою SpeedButton. Розглянемо їх детальніше.

AllowAllUp. Якщо встановлено у True, то кнопка синхронізує свій стан з іншими кнопками групи - у будь-який момент може бути натиснута тільки одна кнопка групи. Це властивість працює тільки в тому випадку, якщо властивість Grouped (угрупування) у кнопки також встановлено у True. Виділіть два останніх кнопки і встановіть у нього обидва ці властивості в True.

Caption. Містить напис на кнопці, яка буде виходити разом з зображенням, якщо у самої панелі інструментів властивість ShowCaptions встановити в True. У першій кнопки в цій властивості напишіть «Вихід», у другий - «Активна», а в третій «Неактивна». Що там буде активно чи ні, не має значення, зараз ми просто вчимося працювати з панеллю інструментів. Тепер виділіть саму панель інструментів, і встановіть властивість ShowCaptions в True. Як бачите, кнопки стали великими, і разом з зображенням на них виходить також і текст. У деяких програмах ви можете зустріти такі панелі інструментів. Знову поверніть це властивість в False. Щоб повернути кнопок початковий розмір, виділіть першу кнопку і змініть її розміри. Встановіть властивості Height і Width рівним 23.

Down. Як і раніше, це властивість відповідає за стан кнопки - натиснута вона чи ні. Щоб повторити попередній приклад, встановіть у першій сгруппированной кнопки Down у True.

ImageIndex. З цією властивістю ми вже зрозуміли, воно відповідає за картинку, яка зображена на кнопці.

Style. Стиль кнопки. Мабуть, найцікавіше властивість. Якщо ви скомпилируете приклад, то побачите, що при натисканні на кнопку, вона повертається в віджате стан. А як бути, якщо нам, як у минулому прикладі, потрібно, щоб завжди була натиснута тільки одна кнопка з групи? Ми вже згрупували дві останні кнопки і вказали їм синхронізувати стан з іншими кнопками групи. Тепер знову виділіть їх, а у властивості Style виберіть tbsCheck. Цей стиль дозволяє кнопці залишатися в натиснутому стані. Щоб подолати її, потрібно буде знову клацнути по кнопці. Якщо ж кнопки згруповані, як у нашому прикладі, то натискання на іншу кнопку буде домагатися своєї першу.

Сама панель інструментів також має ряд цікавих властивостей, які потрібно знати. Виділіть її, і подивіться на Інспектор Об'єктів. Такі властивості, як AlignWidth, Height і ми розглядати не будемо, оскільки знайомі з ними по інших компонентів.

AutoSize. Якщо Так, то панель автоматично вирівнює висоту, враховуючи висоту кнопок.

ButtonHeight. Визначає висоту кнопок, створюваних на цій панелі.

ButtonWidth. Визначає ширину кнопок на панелі. Щоб кнопки були квадратними, залишайте ці властивості рівними один одному.

Caption. Назва панелі інструментів, яке буде видно, якщо зняти панель інструментів з місця і зробити з неї окреме вікно. Вкажіть у цьому властивості «Файл».

Flat. Якщо одно True, то кнопки виглядають сучасно, без опуклостей.

List. Працює, якщо ShowCaprions встановлений в True. Якщо одно True, то зображення пригорнеться до лівої межі, а текст - до правого. Інакше зображення буде зверху, а текст - знизу кнопки.

ShowCaprions. Дозволяє або забороняє показ тексту на кнопках.

Крім того, ми знаємо, що кнопки можуть мати різні стани, і бути активними або неактивними (це залежить від властивості Enabled). Звичайна кнопка в неактивному стані має малюнок сірого тону. Кнопки мають три варіанти зображення - звичайне, неактивна, і коли над кнопкою розташовується вказівник миші. В один контейнер Іmagelіst не вдається завантажити зображення різних станів кнопок. Якщо ви бажаєте використовувати всі три стани, то вам доведеться встановлювати три контейнери Іmagelіst для зображень. У кожен контейнер ви додаєте картинку свого стану, важливо, щоб ці картинки мали однаковий індекс, тобто, щоб вони відповідали один одному по черговості в списку. Далі, ви встановлюєте:

Images. Тут ви вказуєте контейнер з звичайним зображенням кнопки, у нас це ImageList1. DisabledImages. Тут ви вказуєте контейнер з зображеннями недоступних кнопок. HotImages. Тут вказується контейнер з зображеннями кнопок у момент, коли вказівник миші знаходиться над ними.

Зазвичай такі премудрості не потрібні, достатньо вказувати тільки звичайне зображення кнопок. Але знати про такі можливості компонентів потрібно.

DragKind. Мабуть, найбільш цікава властивість панелі інструментів. Воно може мати два варіанти - dkDrag (за замовчуванням), і dkDock. Якщо встановлено dkDrag панель можна буде переміщати тільки всередині ControlBar. Для цього потрібно покажчиком миші вхопитися за вертикальну риску в лівій частині панелі і переміщати її. А ось якщо встановити в цій властивості dkDock панель інструментів можна буде зняти з ControlBar, встановити її всередині вікна програми або навіть за її межами. Спробуйте це зробити.

Тепер ще один цікавий приклад панелі інструментів. Кнопки на панелі можна пов'язувати з головним або спливаючим меню! Тобто, якщо у вас є головне меню, і там є команда «Вихід», яку ви вже запрограмували, то немає потреби писати цей код кнопки, досить пов'язати її з компонентом головного меню.

У нас немає коду обробника події кнопки «Вихід». Якщо є - видаліть. Встановіть на форму головне меню. Вкажіть там розділ «Файл» і підрозділи «Відкрити», «-» і «Вихід». Зв'яжіть меню з нашим Іmagelіst, і пункту «Вихід» присвойте картинку opendoor.bmp. Тепер створіть обробник подій для цього пункту і пропишіть там

Close;

Збережете, зберіть його, і подивіться, як він працює. Тепер виберіть кнопку «Вихід» на панелі інструментів, і у властивості MenuItem виберіть пункт меню, з яким потрібно асоціювати кнопку (у мене це N4). Якщо ви все зробили правильно, то при роботі програми, якщо користувач вибере команду «Вихід» в меню або натисне кнопку «Вихід» на панелі інструментів, виконається один і той же код.

Точно також кнопки можна пов'язати з спливаючим меню, за це відповідає властивість PopupMenu, в якому ви можете вибрати спливаюче меню. Властивість DropDownMenu пов'язує кнопку з допоміжним меню, якщо воно є.

Ми вивчили способи організації професійної панелі інструментів, тепер ваші програми будуть виглядати солідніше.

Лекція 28. ActionList і MDI-вікна

У цій лекції нам належить вивчити дві дуже цікаві теми, після чого ви зможете створювати додатки цілком професійного вигляду. Ці теми - компонент ActionList і многодокументные MDI-додатки.

ActionList

Компонент ActionList є останнім на вкладці Standard колекції компонентів. Це невизуальный компонент, користувач його не побачить. Призначення цього компонента - організація механізму дій. Що це значить? Ми вже вміємо робити головне і спливаюче меню, причому навіть вміємо додавати зображення поруч з назвами пунктів меню. Ми вміємо створювати панель інструментів. Все це разом дозволяє зробити програму цілком професійного вигляду. Ми піклуємося про користувача, дозволяємо йому давати команди програмі трьома різними способами, нехай користувач сам вибирає, який спосіб йому здається зручніше. І користувачеві дійсно, зручно, завантаживши вашу програму, він потрапляє у звичну обстановку - бачить меню, клацніть правою кнопкою, і бачить спливаюче меню. Також присутній і панель інструментів, де кожна кнопка виводить довідку, варто тільки потримати на неї вказівник миші.

А як нам, програмістам, все це організувати? Наприклад, потрібно відкрити новий файл. Добре, якщо це дія обмежиться одним - двома командами, а якщо буде потрібно виконувати попередні перевірки й підготовку? Копіювати однакову дію в три різних місця - для команди головного меню, спливаючого меню і для кнопки на панелі інструментів? Можна, але це незручно, і неефективно.

ActionList дозволяє нам організувати механізм дій. Ми прив ’ язуємося до якого-небудь дії цього компонента свої команди, зображення, підказку. А потім присвоюємо це дія потрібного пункту головного меню, потрібної кнопку на панелі інструментів. І який би спосіб віддати команду користувач не вибрав, виконається один і той самий код, зазначений у дії ActionList. Більш того, цей компонент має власну колекцію часто застосовуваних дій. Достатньо тільки вставити їх і перейменувати на російську мову, код дій писати вже не потрібно. Зручно? Не те слово, такий спосіб організації програми заощадить вам багато часу, особливо на складних проектах.

Суть дій компонента проста. Спочатку ви створюєте дію за допомогою вбудованого в компонент редактора дій. Присвоює дії ім'я, зображення (якщо ви використовуєте Іmagelіst з колекцією зображень), підказку, категорію. Потім створюєте для дії подія onExecute, де описуєте всі команди, які повинні виконуватися при виборі даної команди. Потім ви пов'язуєте дану дію з потрібною командою головного і спливаючого меню, кнопки панелі інструментів. От і все! Докладніше роботу з компонентом ми розглянемо трохи пізніше, на практичної розробки програми.

MDI

MDI (MultiDocumentInterface) - це спосіб створення многодокументных вікон, коли нова, дочірнє вікно створюється всередині головного, і не може вийти за її межі. Пригадайте, наприклад, пакет MSOffice 97, де Word, Excel та інші програми відкривали файл у вікні, що було всередині головного вікна. Якщо вам доводилося бачити найпоширеніший сьогодні продукт для бухгалтерського обліку «1С Підприємство», то ви також бачили MDI-вікна, саме на цій технології побудована ця програма.

Такий спосіб вважається застарілим і корпорація Microsoft рекомендує не користуватися ним, проте в багатьох випадках він є найбільш зручним способом. Наприклад, сама Microsoft, незважаючи на власні рекомендації, використовує цю технологію. Натисніть «Пуск - Виконати». У вікні для вводу команди наберіть «MMC» (зрозуміло, без лапок, латинськими символами). З'явиться MDI-вікно Консолі управління системою, яка знаходиться всередині головного вікна, і не може вийти за її межі.

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

Властивість Name форми перейменуйте в fMain. У властивості Caption напишіть «Текстовий редактор MyEdit». Збережете їх в окрему папку, модуль назвіть Main, а проект у цілому - MyEdit. Тепер у властивості Position виберіть poDescktopCenter, щоб форма з'являлася по центру Робочого столу.

Звернемо увагу на властивість FormStyle. Це властивість відповідає за стиль форми, за умовчанням встановлено fsNormal (звичайне). До цих пір ми не міняли його, для більшості вікон таке значення властивості якраз і потрібно. Але зараз ми робимо головне вікно многодокументного програми, тому виберіть для FormStyle значення fsMDIForm (головне MDI-вікно). Для дочірнього MDI-вікна ми надалі будемо вибирати значення fsMDIChild.

На форму встановіть компонент ControlBar з вкладки Additional, встановіть у нього властивість Align рівне alTop, а властивість AutoSize в True.

Тепер поверх ControlBar встановимо компонент ToolBar з вкладки Win32. У властивості EdgeBorders цього компонента переведіть у False подсвойство ebTop, щоб зняти верхню оббирання. Властивість AutoSize і Flat також встановимо в True. Також зверніть увагу на властивість ShowHint цього компонента. При значенні True, воно дозволяє виводити підказку для кнопок, розташованих на цій панелі інструментів, при наведенні на них курсору миші. Встановимо True в цій властивості. Не забувайте час від часу зберігати проект. Так, панель інструментів у нас вже є, кнопки будемо створювати по ходу роботи.

По черзі встановіть на форму головне меню (MainMenu), спливаюче меню (PopupMenu) і ActionList з вкладки Standard, додайте компонент Іmagelіst з вкладки Win32. Далі встановіть діалоги OpenDialog і SaveDialog з вкладки Dialogs. Це всі компоненти невизуальные, тому їх можна помістити на будь-яке вільне місце форми.

Почнемо з компонента Іmagelіst, так як картинки нам знадобляться і для обох меню і панелі інструментів. Команди, які буде виконувати програма, будуть наступними: відкрити файл, зберегти, створити, вирівнювати вікна каскадом, горизонтально і вертикально. Усі ці команди будуть супроводжуватися зображеннями, тому доведеться підібрати відповідні картинки, наприклад:

Рис. 28.1. Набір зображень для програми.



Підберітьтакіжабосхожізображення, абостворітьїхсамостійноідодайтевІmagelіstпідтимижіндексами.

Приготуванняголовноговікнапрограмимайжезакінчено, тепернампотрібностворитидочірнєвікно, уякомубудезнаходитисясамредактортекстів. Виконайте команду «File - New - Form», щоб створити нове вікно. Властивість Name нової форми перейменуйте в fEditor (так будемо називати наш редактор), у властивості Caption напишіть «Дочірнє вікно». Властивості FormStyle виберітьзначення fsMDIChild. Збережіть цей модуль як Editor. Тепер спробуйте скомпілювати проект і запустити програму на виконання. Відразу видно недоліки: дочірнє вікно з'являється відразу ж, а при спробі закрити його, воно просто згортається. Нас це не влаштовує, адже нам потрібно відкрити це вікно по команді, а при закритті знищувати його. Виправити це можна. Закрийте програму (але не проект!). Виберіть команду меню Project - Options. У вас відкриється вікно, в якому можна задавати деякі налаштування проекту в цілому. У правій частині вікна, в полі «Auto-createforms» виведено список форм проекту, які створюються автоматично. Виділіть назву дочірньої форми fEditor, натисніть кнопку зі стрілкою вправо, щоб перемістити її в поле «Availableforms»:

Рис. 28.2. Налаштування проекту.

Тепер ця форма не буде створюватися автоматично, нам доведеться робити це вручну. Тепер потрібно буде виправити і закриття цієї форми, щоб вона не згорталась, а дійсно закривалася. Для цього виділіть дочірню форму, створіть для неї обробник подій onClose і впишіть туди наступний код:

Action := caFree;

Таким чином, при спробі закрити форму, ми знищуємо об'єкт, яким ця форма є. Підемо далі. На цій формі нам не потрібні ні меню, панель інструментів. Встановіть на неї тільки компонент Memo, очистіть весь текст у властивості Lines, а властивість Align переведіть у alClient, щоб компонент зайняв всю форму. Не забудьте зберегти проект. Дочірня форма готова, переходимо знову до головної формі. Ви помітили, що нам не довелося виконувати команду меню «File - Useunit», щоб головна форма бачила дочірню? Коли ви робите MDI-додаток, дочірня форма вже буде видно з головною, проте тепер вона не створюється автоматично, тому нам доведеться все ж виконати цю команду. Виділіть головну форму, виконайте команду меню «File - Useunit» і виберіть модуль Editor, щоб його можна було запустити на виконання з головного вікна.

Займемося компонентом ActionList. Спочатку зв'яжемо його з нашою колекцією малюнків. Виберіть компонент, і у властивості Images виберіть наш Іmagelіst. Далі двічі клацніть по ActionList, щоб відкрити редактор дій. З'явиться вікно редактора, розділене на дві області. Область Categories містить категорії дій, а область Actions - самі дії. Створимо нову дію. Для цього клацніть правою кнопкою по області Actions і виберіть NewAction (нова дія). Дія створиться:

Рис. 28.3. Редактор дійActionList

За замовчуванням, дія має ім'я Action1. Виділіть дію і перейменуйте його властивість Name в FileNew. Тепер налаштуємо це дія. У властивості Caption напишіть «Новий», це дія буде відкривати вікно редактора з новим документом. У властивості Category напишіть «File», в цій категорії будемо зберігати всі дії, пов'язані з командою меню «Файл». Відразу ж наше дію зникло з вікна - створена нова категорія, і дія перенесена в неї. Виберіть File в області Categories, а в області Actions знову виберіть наше дію.

Тепер нас цікавить властивість Hint - рядок підказки, яка вискакує при наведенні покажчика миші на об'єкт. Підказка вискакує, якщо властивість ShowHint об'єкта встановлено у True, ми це зробили тільки для панелі інструментів. Значить, поради будуть виходити тільки для кнопочок. У властивості Hint напишіть наступне: «Створити новий файл».

Наступне цікава властивість - ImageIndex, воно показує індекс зображення для вибраної дії. Коли ви відкриєте властивість, відкриється список зображень, з яких можна вибрати потрібну:

Рис. 28.4. Редагування дії

Дія ми підготували, однак воно працювати просто так не буде. Щоб змусити дію працювати, потрібно на вкладці Events (Події) створити для нього обробник подій onExecute. Двічі клацніть по цій події, і напишіть у ньому наступний код:

fEditor := TfEditor.Create(Owner); //создаемновоеокно

ЗміннаfEditor була автоматично оголошена в розділі глобальних змінних дочірньої форми:

var

fEditor: TfEditor;

За допомогою функції Create() ми створюємо новий об'єкт (форму), і присвоюємо його цієї змінної. Тобто, створюємо дочірню форму вручну. Таким же чином ви зможете створювати надалі інші дочірні вікна, вказуючи їх ідентифікатор.

Дія створено. Тепер створимо кнопку на панелі інструментів, яка буде виконувати цю дію. Перейдіть до панелі інструментів ToolBar, у властивості Images вкажіть наш Іmagelіst, щоб панель використовувала ті ж зображення, що і ActionList. Тепер натисніть на панелі правою кнопкою, виберіть команду «New Button». З'явилася нова кнопка. Виділіть її, і у властивості Action виберіть наша дія - FileNew. Зараз же на кнопочку з'явилося потрібне зображення, і тепер натискання на неї призведе до створення нового дочірнього вікна. Збережете, зберіть його і виконайте. Кілька разів натиснувши на кнопку, ви створили кілька дочірніх вікон з редактором. Закриття дочірнього вікна вже не згортає його, а знищує.

Прив ’ яжемо це ж дію до пункту головного меню. Виділіть MainMenu, у властивості Images виберіть цей же Іmagelіst. Двічі клацніть по головному меню, щоб відкрити редактор. Створіть категорію «Файл» і виберіть «Створити». У властивості Action цієї команди виберіть наша дія FileNew. Збережете, зберіть і запустіть його. У меню з'явилася команда «Файл - Новий», яка має ту саму картинку і виконує ту ж дію, що і кнопка на панелі інструментів.

Зробимо все ті ж самі дії для спливаючого меню. Але тут немає категорій, тому просто створіть команду «Новий». Не забудьте попередньо прив'язати до PopupMenu наш Іmagelіst, а до головної формі у властивості PopupMenu - саме спливаюче меню.

Створити нове вікно нескладно, складніше відкрити вже існуючий файл. Знову відкрийте редактор дій ActionList. У категорії File створіть нове дію, перейменуйте його в FileOpen. Виділіть дію, у властивості Hint напишіть «Відкрити файл», у властивості Caption «Відкрити», у властивості ImageIndex виберіть потрібну картинку. У подія onExecute введіть код:

if OpenDialog1.Execute then begin

fEditor := TfEditor.Create(Owner);

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

fEditor.Caption := OpenDialog1.FileName;

end; //if

Спочатку ми перевіряємо, чи вибрав користувач файл? Якщо так, то створюємо нове дочірнє вікно. За замовчуванням, воно активно, тому можемо звертатися до нього за ім'ям змінної fEditor. Завантажуємо в Memo вибраний файл, потім в заголовок вікна виводимо ім'я файлу.

Створіть нову кнопку, прив'яжіть до неї це дія. Створити в головному меню команду «Файл-Відкрити», прив'яжіть цю дію. У спливаючому меню створіть команду «Відкрити», і прив'яжіть до неї цю дію. Збережіть, зберіть і запустіть проект, спробуйте кілька разів відкривати різні текстові файли. Для більшого ефекту можете в компонентах OpenDialog і SaveDialog створити фільтри для розширення *.txt.

Для дії FileSave зберегти файл (код) події onExecute буде такий:

//еслиактивногоокнанет - выходим:

if ActiveMDIChild = nil then exit;

//есливстрокеокнаестьимяфайла, сохраняемвнего:

ifLength(ActiveMDIChild.Caption)> 0 then

fEditor.Memo1.Lines.SaveToFile(fEditor.Caption) //иначеспрашиваем, кудасохранять, исохраняем:

else if SaveDialog1.Execute then begin

fEditor.Memo1.Lines.SaveToFile(SaveDialog1.FileName);

fEditor.Caption := SaveDialog1.FileName;

end; //elseif

Як видно з коду, команда ActiveMDIChild буде містити nil (нічого, порожньо), якщо немає жодного активного вікна. Всі інші операції з цією дією створіть самостійно, і прив'яжіть це дію до команди головного меню «Файл - Зберегти», до команди контекстного меню «Зберегти», до нової кнопки на панелі інструментів. Також самостійно створіть дію FileExit (вихід з програми), і прив'яжіть його до кнопки і відповідних пунктів головного і спливаючого меню.

У MDI - додатків є кілька корисних властивостей і методів. Розглянемо деякі з них. ActiveMDIChild - вказує на активну дочірнє вікно. Наприклад, текст у

ActiveMDIChild.Caption

є заголовком активної дочірньої форми.

MDIChildCount возвращает ціле число - кількість відкритих дочірніх вікон. MDIChildren дозволяє отримати доступ до будь-якого, навіть неактивному дочірньому вікна. Наприклад,

MDIChildren[1]

це перше дочірнє вікно

Наостанок навчимося вставляти в ActionList стандартні дії. Відкрийте редактор дій. Клацніть по ньому правою кнопкою та виберіть команду «NewStandardAction». Відкриється список, в якому можна вибрати категорію і команди дій. Нам потрібна категорія Window (Вікно). Виділіть цю категорію і наступні команди в ній:

Рис. 28.5. Вибір дій категорії Window.

Як тільки ви натиснете OK, ці дії потраплять до списку ActionList, в категорію Window. Відсортуйте команди таким чином, і для зазначених команд вкажіть зображення:

Рис. 28.6. Нова стандартна категорія

За порядком, у властивостях Caption цих дій вкажіть: «Вирівнювання каскадом, Вирівнювання горизонтально, Вирівнювання вертикально, Впорядкувати, Згорнути все, Закрити вікно». Зробіть відповідні пункти в розділі «Вікно головного меню, і у властивості Action цих пунктів присвойте відповідні команди. Ніякого коду вводити не потрібно, він вже міститься у цих командах. Збережете, зберіть і подивіться, як він працює. Нові команди меню будуть недоступні, поки немає відкритих вікон, при відкритих вікнах ними можна буде користуватися. Не написав жодного рядка коду, ми організували обробку вікон багатовіконного проекту, використовуючи стандартні дії ActionList.

Лекція 29. Введення до бази даних

Бази Даних

Бази Даних - досить велика і серйозна область програмування. Жодне підприємство, фірма або організація сьогодні не обходяться без програм, що забезпечують роботу з Базами Даних. Що ж таке БД (База Даних)?

Уявіть собі, наприклад, бібліотеку. У будь-який бібліотеці ведеться облік книг. Вони реєструються за автором, за змістом, за видавництву, за реєстраційним номером і так далі. Тепер уявіть, що бібліотекарю потрібно створити звіт за кількістю книг якого-то конкретного автора. Якщо вся інформація зберігається тільки у вигляді паперових карток, то перед бібліотекарем стоїть досить непросте завдання. Цей автор міг написати не одну книгу, а десяток, кожна книга в бібліотеці може бути представлена в 5-10 примірниках, і ще не факт, що всі картки лежать по порядку. Отже, бібліотекарю доведеться перебрати всі картки, щоб бути впевненим, що звіт правильний. А якщо цих карток у бібліотеці кілька мільйонів?!

Тут прийдуть на допомогу Бази Даних. База Даних - це механізм вводу, збереження і вибірки інформації за різними параметрами. Припустимо, зараз бібліотекарю веліли створити звіт за кількістю книг якого-то автора. Бібліотекар цей звіт створив. А на наступний день від нього зажадали звіт за кількістю книг якийсь тематики, наприклад, окремо з математики і фізики. Якщо книги не зареєстровані в Базі Даних, то бібліотекарю простіше звільнитися, ніж робити таку неймовірну роботу. А ось якщо є комп'ютер, і якщо встановлена спеціальна програма з базами даних, то створити подібний звіт - справа кількох хвилин.

У старих мовах програмування і СУБД (Систем Управління Базами Даних) під терміном База Даних розумівся файл, в якому дані зберігаються в табличному поданні. Такими СУБД, наприклад, є dBase, Clipper, FoxPro і т.п.

Зараз під поняттям База Даних розуміють, як правило, папку, в якій може зберігатися велика кількість таблиць. Причому ці таблиці зазвичай пов'язані один з одним, і випадкове видалення однієї таблиці може призвести до руйнування всієї БД.

Бази Даних можуть бути трьох різних типів:

Локальні БД. Це програми, що використовують механізм роботи з БД, які знаходяться на цьому комп'ютері і не використовуються іншими комп'ютерами.

Мережеві БД. По своїй суті, вони схожі на локальні. Сама База Даних може лежати на общесетевом диску або папці. При цьому різні комп'ютери в організації працюють з цією загальною базою даних, тобто всі співробітники організації мають доступ до цієї БД, вносять до неї дані, роблять вибірку і т.п.

Клієнт - Серверні додатки. Це сама «просунута» модель роботи з БД, і зазвичай використовується у великих підприємствах, в яких потрібно обробляти велику кількість даних. Суть цієї моделі в наступному. На головному комп ’ ютері (сервері) зберігається загальна База Даних. Крім того, на ньому запущено спеціальна програма - серверна СУБД, яка отримує запити від користувачів (клієнтів), обробляє їх і повертає результат. Така модель БД різко знижує навантаження на мережу і збільшує надійність зберігання даних.

Адже у випадку мережевий БД кожен клієнт завантажує свою копію загальної БД, змінює її і потім прописує зміни в загальну БД. А в цей же час інший клієнт може прописувати свої зміни в цю ж БД. Якщо програміст не дуже добре розробив доступ до БД та індексацію, то цю Базу Даних дуже легко зруйнувати. У клієнт - серверної моделі це виключено. Серверна СУБД обробляє запити по черзі, і повертає кожного клієнта свій результат. Причому клієнту повертається не вся таблиця, а саме те, що він просив, тобто залежно від запиту клієнта сервером була сформована вибірка з бази даних, яка і передана клієнтові.

Спочатку середовище Delphi не орієнтована на роботу з Базами Даних, однак реалізація механізмів для роботи з БД настільки різноманітна, що робить Delphi одним з найбільш потужних і зручних середовищ розробки додатків БД. Серед цих механізмів можна назвати BDE, ODBC, ADO і безліч інших. Більш того, сторонні розробники також розробляють механізми для доступу до БД, так що Delphi на сьогоднішній день може працювати майже з усіма існуючими форматами БД. На сьогоднішній день у Delphi немає як або серйозних конкурентів для розробки програмного забезпечення, що використовує бази даних.

Однак для початку розберемося з термінологією.

Поле. Це стовпчик таблиці, загальний для всіх записів. Наприклад, поле «Прізвище».

Запис. Рядок таблиці, що описує якийсь об'єкт. Наприклад, якийсь автор, у якого в полі «Прізвище» записано «Іванов», а в інших полях - інша інформація, що відноситься до цього автора.

Таблиця. Набір записів в Базі Даних.

База Даних. Одна або кілька таблиць, пов'язаних один з одним.

Тепер уявімо собі саму таблицю.

Таблиця 29.1. Приклад таблиці авторів

Фамилия

Имя

Отчество

Год рождения

1

Лермонтов

Михаил

Юрьевич

1814

2

Пушкин

Александр

Сергеевич

1799

3

Толстой

Алексей

Константинович

1817

4

Толстой

Лев

Николаевич

1828

Тут стовпці «№», «Прізвище» і так далі є полями таблиці. Рядки - «1», «2» і так далі - записами таблиці. Кожен запис відповідає інформації про автора. Причому зверніть увагу, що рядки під номером 3 і 4 мають у полі «Прізвище» однакові дані, хоча вони відносяться до різних авторів. Для цього і було введено полі «№», щоб однозначно ідентифікувати автора. Таке поле називається ключовим, і саме з нього буде проводиться вибірка даних. Таким чином, книги Л.М. Толстого ніколи не потраплять в каталог автора А.К. Толстого.

Ключовим називається поле, яке дозволяє однозначно ідентифікувати запис.

Тепер уявіть собі іншу таблицю - каталог ніг.

Таблиця 29.2. Приклад каталогу книг.

Автор

Наименование

Кол-во экз.

1

4

Детство, Отрочество, Юность

20

2

4

Война и мир

15

3

4

Два гусара

14

4

3

Сборник стихотворений

5

Тут поле «№» ідентифікує вже книгу, а не автора. А в полі «Автор» немає прізвища, імені та по батькові, зате є номер - ідентифікатор автора з першої таблиці. Тепер, якщо зв'язати полі «Автор» цієї таблиці з полем «№» попередньої, ми можемо в звіті отримати повну інформацію і про книгу, і про автора. Такі таблиці називаються реляційними, тобто, пов'язаними, і на сьогоднішній день є найбільш поширеними.

Індекс. Це спосіб упорядкування даних. Якщо ми зробимо полі «Прізвище» першої таблиці індексним, то зможемо сортувати дані з цього поля, що забезпечує більш швидкий пошук даних. Наприклад, якщо ми не знаємо імені та по батькові даних, то можемо шукати автора за прізвища. Вказавши в рядку пошуку «Толстой», ми отримаємо книги всіх авторів, які мають таке прізвище. Серед цього списку вже простіше знайти потрібну інформацію.

BDE

BDE (Borland Database Engine) - механізм доступу до баз даних, розроблений самою компанією Borland. Зараз цей механізм вважається застарілим, однак він забезпечує найбільш простий доступ до баз даних різних форматів, і починати знайомство з БД зручніше саме з нього.

Створіть новий додаток. Ми продемонструємо роботу з БД і використання навігаційних методів на демонстраційної таблиці, що поставляється разом з Delphi.

Назвіть форму fMain, дайте їй стиль bsDialog, і позицію по центру робочого столу. У властивості Caption напишіть «БД Навігатор».

Далі, встановіть на форму панель GroupBox, у властивості Caption якого вкажіть «Навігація».

На цю панель встановіть 4 кнопки, на яких вкажіть « | " Початок»,«<<», «Вперед >>» і «Кінець >|». Це будуть кнопки навігації - переміщення по таблиці.

Нижче встановіть ще один GroupBox, у властивості Caption якого напишіть «Закладка». У таблиці можна встановити закладку на будь-який запису, а потім перейти на неї з будь-якого місця. У цій панелі ми будемо додавати закладки, видаляти її і перескакувати на неї.

Ще нижче помістіть звичайну панель, і видаліть текст. Встановіть на нього компонент Label, у властивості Caption якого вкажіть «Позиція:». Тепер потрібно, щоб ця напис завжди була посередині. Тому властивість AutoSize переведіть у False, а властивість Alignment - в taCenter. Розтягніть Label, щоб вона займала майже всю ширину панелі. Тут разом з написом «Позиція:» буде виходити номер запису в таблиці.

Тепер перейдіть на вкладку Data Controls. Тут містяться компоненти відображення даних з БД. Знайдіть DBGrid і помістіть її на форму нижче панелі. Сітка DBGrid дуже схожа на сітку StringGrid, яку ми вивчали, але призначена для відображення та редагування даних з таблиці БД. Відразу властивості ReadOnly (лише для читання) присвойте True, щоб користувач не міг зіпсувати демонстраційну таблицю своїм редагуванням даних.

Щоб отримати доступ до БД, потрібно ще дещо. На вкладці BDE є компонент Table, який забезпечує доступ до таблиці засобами механізму BDE. Компонент не візуальний, тому можете кинути його на будь-яке місце, наприклад, поверх сітки. Тепер у властивості DatabaseName виберіть демонстраційну базу даних DBDEMOS, яка поставляється разом з Delphi. У властивості TableName необхідно вказати саму таблицю, до якої ми бажаємо отримати доступ. Виберіть там biolife.db.

Ми вже вибрали базу даних, тобто набір таблиць, і вибрали таблицю. Однак, щоб вивести в сітку дані з таблиці, потрібний компонент управління даними таблиці. Знайдіть на вкладці Data Access компонент DataSource. Цей компонент забезпечує зв'язок даних з таблиці Table з навігаційними функціями, такими, як DBGrid. Киньте його поруч з Table. У властивості DataSet компонента потрібно вказати, з якою ж саме таблицею ми бажаємо працювати. У даному прикладі у нас одна таблиця, але може бути і десяток! Виберіть там Table1.

Тепер виділіть сітку. Їй потрібно вказати компонент, який буде керувати цією сіткою. У властивості DataSource вкажіть наш DataSource1. Все, тепер ми зробили все приготування. Єдине, що залишилося зробити - відкрити файл з таблицею. Виділіть компонент Table1, і у властивості Active вкажіть True. Цим самим ми змусимо таблицю відкритися.

Увага! Робота з базою даних відрізняється від роботи з файлами іншого типу. Коли ваша програма працює, наприклад, з текстовим файлом, вона відкриває цей файл, зчитує його вміст в оперативну пам'ять ПК і закриває його. Користувач працює вже не з файлом, а з його вмістом в оперативній пам'яті. Коли він дає команду зберегти результати роботи, програма знову відкриває файл і перезаписує в нього дані з оперативної пам'яті.

З базами даних все інакше. Як тільки ви відкрили таблицю - вказали True у компонента Table1 у властивості Active, або ж програмно дали команду:

Table1.Active := True;

або

Table1.Open;

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

Якщо ви все зробили правильно, то повинні отримати щось на зразок цього:

Рис. 29.1. Зовнішній вигляд програми

Навігація

Для переміщення по записах таблиці існують такі методи:

First - перехід на перший запис. Last - перехід на останній запис. Next - перехід на наступний запис. Prior - перехід на попередній запис.

Створимо обробники натискання на кнопки на панелі Навігації. У обробнику кнопки «Початок» напишемо

Table1.First;

У обробнику кнопки «Назад» напишемо

Table1.Prior;

У обробнику кнопки «Вперед» напишемо

Table1.Next;

У обробнику кнопки «Кінець» напишемо

Table1.Last;

Збережете, відкомпілюйте і подивіться, як він працює. Покажчик позиції повинен пересуватися, коли користувач натискає на кнопки. Крім того, переміщатися можна буде за допомогою самої сітки DBGrid.

Типи даних

Гортаючи таблицю, ви бачили такі дані, як MEMO і GRAPHIC, а самі дані не відображалися. Справа в тому, що таблиці мають різні типи даних. Бувають числові типи, строкові, типи «Дата». Звичайно, в строковий тип можна ввести текст довжиною не більше ніж 255 символів. Якщо ж вам потрібно помістити в таблицю більш довгий текст, наприклад, анотацію до книзі, то можна скористатися типом MEMO. При цьому створюється новий файл, у якому зберігатиметься сам текст, а в таблиці вказано посилання на цей текст. Довжина тексту в MEMO необмежена. Для відображення MEMO буде потрібно спеціальний компонент.

Поле GRAPHIC зберігає зображення - картинки, фотографії. Для їх відображення також потрібен спеціальний компонент.

Поліпшимо наш приклад, створивши форму, в якій, як у картці, ми зможемо бачити всі дані поточного запису. Однак для початку познайомимося з Data Module. Це модуль, який не має форми. Його дуже зручно використовувати в багатовіконних проектах, де вікна працюють із загальними даними. Для нас загальні дані - це компоненти Table і DataSource.

Насамперед, закрийте таблицю, вказавши False в її властивості Active.

Виберіть File -> New -> Data Module. У вас з'явиться нове вікно, але це не форма. Змініть ім'я цього вікна на fDM, щоб не потрібно було писати довгого звернення до компонентів. Тепер поверніться на головну форму і виділіть компоненти Table і DataSource. Виберіть Edit -> Cut (вирізати). Потім перейдіть у вікно fDM, і виберіть команду Edit -> Paste (вставити). Якщо у вас ці компоненти були в нижній частині форми, то вони можуть опинитися поза зони видимості вікна fDM. Це не страшно - прокрутіть, або збільшіть його розміри, щоб бачити компоненти, а потім перетягніть їх у верхню частину вікна. Після чого можна зменшити розміри fDM. Збережіть вікно в модулі DM.pas.

Далі переходимо на головну форму. Ця форма повинна бачити вікно fDM, щоб працювати з компонентами, встановленими в ньому, тому виберіть команду Use -> Unit і там виберіть DM.

Тепер виділіть сітку. В її властивості DataSource потрібно вибрати наш DataSource, який виглядає вже як fDM.DataSource1. Тепер відкрийте таблицю, вказавши True в її властивості Active. Як бачите, дані по -, як і раніше відображаються, хоча компоненти доступу зберігаються в іншому модулі.

Тепер ми готові до того, щоб створити нове вікно для перегляду даних. Створіть нову форму, назвіть її fViewer, і збережіть у модулі Viewer. Не забудьте вибрати команду Use -> Unit і там - DM, адже нова форма також буде працювати з компонентами Table і DataSource. У властивості Caption напишіть «Картка».

Тепер перейдіть на вкладку DataControls. Знайдіть там компонент DBEdit. Від звичайного Edit він відрізняється тим, що підтримує зв'язок з базою даних. Киньте на форму, один за іншим, 6 таких компонентів. Нижче киньте DBMemo, а праворуч від DBEdit - DBImage. У вас повинна вийде форма приблизно такого виду:

Рис. 29.2. Зовнішній вигляд форми картки

Зліва від компонентів доступу до даних виберіть відповідну кількість Label, і пропишіть пояснення до полів, як на картинці вище.

Виділіть всі компоненти доступу (тільки всі DBEdit, DBMemo і DBImage). Властивість ReadOnly у них встановіть у True, щоб користувач не зіпсував дані демонстраційної таблиці. Далі, у властивості DataSource виділених компонентів потрібно вибрати наш fDM.DataSource1. Тепер доведеться з кожним компонентом працювати окремо. Виділіть верхній DBEdit, і у властивості DataField (поле) виберіть «Species No». Всі компоненти DBEdit, зверху вниз, повинні бути прив'язані до наступних полів:

Species No

Category

Common_Name

Species Name

Length (cm)

Length_In

Компонент DBMemo повинен бути прив'язаний до поля Notes, а компонент DBImage - до поля Graphic. До речі, у DBMemo встановіть вертикальну смугу прокручування, щоб можна було гортати занадто великі примітки.

Власне, картка готова. Залишилося тільки викликати її з головної форми. Якщо ви ще не зробили для головної форми команду File -> Use Unit, і не вибрали там модуль Viewer, то зробіть це зараз. Потім виділіть сітку і згенеруйте для неї подія onDblClick, де пропишіть виклик вікна - картки:

fViewer.ShowModal;

Тепер користувач, двічі клацнувши по тій або запису, викличе картку, де будуть відображені дані цього запису. Однак перед компіляцією проекту потрібно щось виправити. Ми запрограмували наші 4 кнопки переміщення, коли ще компонент Table був на головній формі. А тепер він у вікні DM, тому додайте fDM перед зверненням до таблиці у всіх 4 кнопках, як у прикладі:

fDM.Table1.First;

Збережете, зберіть, і подивіться, як він працює. На наступної лекції ми продовжимо роботу над цим проектом.

Лекція 30. Створення власної таблиці

Повернемося до додатка з базами даних, розробленим у минулій лекції. Нам залишилося навчитися користуватися закладками

Закладки (Bookmarks)

Закладки дозволяють зберегти положення в наборі даних, щоб пізніше можна було повернутися до цього ж місця. В Delphi за це відповідає тільки одна властивість. Все, що потрібно зробити - це оголосити змінну типу TBookmarkStr та присвоїти їй положення, яке ми хочемо запам'ятати:

var

bm : TBookmarkStr;

begin

bm := Table1.Bookmark;

А коли потрібно повернутися на закладку, робимо зворотне призначення:

Table1.Bookmark := bm;

Щоб звільнити закладку, потрібно їй просто напросто присвоїти пустий рядок:

bm := '';

Як бачите, закладка реалізується змінної типу TBookmarkStr, а в компоненті Table є властивість Bookmark такого ж типу.

Тепер повернемося до нашого проекту і напишемо код для наших кнопок, відповідальних за закладки. Насамперед, змінної - закладкою ми повинні будемо користуватися з трьох процедур - за кількістю кнопок управління закладкою. Отже, мінлива повинна бути глобальною. Опишемо її в розділі глобальних змінних:

var

fMain: TfMain;

bm : TBookmarkStr;

Далі, кнопка «Встановити» у нас доступна, інші кнопки не доступні. Чому? Тому, що ми не можемо перейти на закладку, або видалити її, поки ми цю закладку не встановили. Як тільки ми встановимо закладку, то навпаки, кнопки «На закладку» і «Видалити» у нас повинні стати активними. А ось «Встановити» - ні, тому що її спочатку потрібно звільнити, видалити. Маємо це на увазі, і пишемо код для кнопку «Встановити»:

//получаемзакладку: bm := fDm.Table1.Bookmark;

//разрешаемилизапрещаемкнопки:

Button5.Enabled := False;

//установить Button6.Enabled := True;

//назакладку Button7.Enabled := True; //Удалить

Тепер код для кнопки «На закладку»:

//перейтиназакладку fDM.Table1.Bookmark := bm;

І наостанок, код кнопки «Видалити»:

//удалитьзакладку:

bm := '';

//разрешаемилизапрещаемкнопки:

Button5.Enabled := True; //установить

Button6.Enabled := False; //назакладкуButton7.Enabled := False; //Удалить

Ось і вся робота з закладками!

Властивості BOF, EOF і циклічна обробка

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

Table1.First; //перешлинапервуюзаписьwhile not Table1.EOF do begin

{какиетодействиясзаписью}

Table1.Next; //переходнаследующуюзапись end; //while

Точно також, можна від кінця таблиці переходити до початку, і використовувати при переході властивість Prev - перехід до попереднього запису.

Відкриття і закриття таблиці або пов'язаних таблиць

У попередньому прикладі ми під час розробки встановили властивість Active таблиці в True. Однак нерідко буває, що відкривати і закривати таблицю доводиться програмним шляхом. Таким чином, для відкриття таблиці ми можемо використовувати властивість Active або методи Open і Close, які роблять одне й те ж:

//открываемтаблицы:

Table1.Open;

Table2.Active := True;

//закрываемтаблицы:

Table1.Close;

Table2.Active := False;

Властивості RecordCount і RecNo таблиці

Ці властивості доводиться використовувати досить часто, щоб з'ясувати номер поточного запису або загальна кількість записів (рядків у таблиці. RecordCount возвращает ціле число, що показує загальну кількість записів, а RecNo - номер поточного запису.

Давайте будемо наш проект, вказавши яка запис є поточною, і яка загальна кількість записів. Відкрийте попередній проект. Подія onDataChange компоненту DataSource виникає кожного разу, коли відбуваються зміни в даних таблиці. Наприклад, переміщення від одного запису до іншої. Однак, компонент Label, в який нам потрібно прописати ці дані, знаходиться в головному вікні, а компонент DataSource - у вікні DM. Так що доведеться відкрити модуль DM, і використовувати команду File -> Use Unit, в якій вказати головне вікно fMain.

Тепер виділіть компонент DataSource і згенеруйте для нього подія onDataChange. В коді цієї події запишемо:

fMain.Label1.Caption := 'Позиция: ' + IntToStr(Table1.RecNo) + ' из ' + IntToStr(Table1.RecordCount);

Як бачите, ми використовували обидва нових властивості. В результаті при завантаженні програми повинна вийти рядок, начебто такий:

Позиція: 1 з 28

Наш «БД - Навігатор» повністю працездатний. Можете закрити цей проект, більше він нам не потрібно.

Створення власної таблиці

Найбільш простий спосіб створити власну таблицю - скористатися вбудованою утилітою Delphi - DatabaseDesktop. Проте спершу створіть новий додаток. Форму відразу перейменуйте в fMain, модуль збережіть під ім'ям Main, а проект - CatBooks. Ми будемо робити каталог книг для бібліотеки. У властивості Caption форми можете написати «Бібліотечний каталог». Тепер за допомогою файлового менеджера створіть на диску D: (Якщо D: диск у вас немає, то на диску C:) ще одну теку - Data (дані). Таким чином, адресу нашої бази даних буде d:\data.

Тепер запустіть утиліту DatabaseDesktop, яка знаходиться там же, де Delphi. Ця програма - не просто броузер БД, вона ще дозволяє створювати таблиці та індексні файли. Зараз виберемо команду File - New - Table. З'явиться віконце, в якому можна вибрати формат створюваної таблиці. Відкрийте список - він досить значний. Таблиці формату dBase найбільш поширені, проте вони дуже вразливі і не мають великого різноманіття типів полів. Тому залишимо формат за замовчуванням - Paradox 7. Відразу, як ви натиснете OK, з'явиться редактор полів. Тут нам потрібно:

  1. У полі FieldName вказати назву поля (колонки).

  2. У полі Type вибрати тип поля. Для цього достатньо натиснути пробіл, щоб відкрився список всіх полів.

  3. У полі Size вказується довжина рядків у символах. Якщо Ви вибрали, наприклад, дату, то це поле недоступне для редагування, оскільки у всіх записів буде фіксована довжина.

  4. Поле Key дозволяє вказати ключове полі. Для цього теж досить натиснути пробіл. Установка на полі ключа призводить до того, що всі запису таблиці будуть сортуватися по цим полем. Наприклад, якщо встановити ключ у поля «Прізвище», то всі записи отсортируются по прізвищах, від А до Я. Якщо ключа немає, то записи будуть в тому порядку, в якому їх ввели в таблицю.

Типи даних таблиць Paradox 7

Таблиця 30.1. Типи даних таблиць Paradox 7

Тип

Буква

Описание

Alpha

A

Строка от 1 до 255 символов. Размер поля указывается в Size.

Number

N

Целое число от 10-307до 10308(15 значащих цифр)

$ Money

$

Число в денежном формате.

Short

S

Короткое целое от -32767 до 32767

Long Integer

I

Длинное целое от -2147483648 до 2147483647

Date

D

Дата

Time

T

Время с полуночи в миллисекундах

Time stamp

@

Дата и время

Memo

M

Строковое поле неограниченной длины. В разделе Size можно указать длину от 1 до 240 символов, остальные символы хранятся в файле с таким же именем, но расширением mb.

Formated Memo

F

Как Memo, но также имеются дополнительные возможности: указать тип и размер шрифта, цвет символов, способ оформления…

Graphic

G

Графический файл сохраняется прямо в поле.

Logical

L

Логический тип.

± Autoincrement

+

Целое число, увеличивающееся на единицу автоматически, при добавлении новой записи (счетчик).

Bytes

Y

Двоичные данные.

Binary

B

Двоичные данные. Как и Memo, хранятся в отдельном файле. Обычно содержат аудио- или видео-данные.

Отже, вводимо такі поля:

Таблиця 30.2. Необхідні для програми поля

Field Name

Type

Size

Key

1

2 3 4 5 6 7

Key1

+

Avtor

I

Nazvanie

A

100

Exemp

S

Cena

$

Date

D

Prim

M

200

Тепер натисніть кнопку «Save As», назвіть файл books і збережіть його в папці d:\data. Тут будуть зберігатися дані про книгах. Поле з автором ми встановили як ціле. Пізніше ми зробимо ще одну таблицю, і в ній будемо вести облік всіх авторів. І обидві таблиці у нас будуть об'єднані. Тепер, якщо ви погляньте на папку d:\data, то виявите там два файли:

books.bd - сама таблиця

books.MB - Memo поле таблиці

Ось тепер настав час створити додаток, що буде працювати з цієї таблиці. Головна форма у нас вже є, так що відразу створюємо модуль даних (File - New - Data Module). У властивості Name напишіть fDM, а сам модуль збережіть як DM. Відразу ж перейдіть на головну форму і з допомогою File - Use unit зв'яжіть її з DM.

Тепер встановлюємо на DM компонент Table з вкладки BDE. У властивості DatabaseName компонента введіть адресу нашої бази: «d:\data». У властивості TableName виберіть нашу таблицю books.db. Змінимо ім'я таблиці, і вкажемо TBooks у властивості Name компонента Table1. Так як у нас у додатку буде більше однієї таблиці, бажано кожної з них дати осмислене ім'я, щоб потім не заплутатися.

Відразу ж встановлюємо на DM компонент DataSource з вкладки Data Access. Назва компоненту змінимо на DS1, щоб було коротше. У властивості DataSet виберемо нашу таблицю.

Переходимо на головну форму. Встановлюємо головне меню і створюємо такі пункти:

  1. «Файл» - «-», «Вихід»

  2. «Редагування» - «Додати книгу», «Додати автора»

  3. «Сортування» - «За автором», «За назвою»

  4. «Допомога» - «Про програму»

Тепер встановимо панель, властивості Align присвоїмо alTop, щоб вона зайняла весь верх, властивість Caption очистимо й кинемо на неї два компоненти Label, один за іншим. На першій напишемо: «Всього книг:», на другий - «На загальну суму:». Як випливає з назви, ці дані ми будемо виводити на панель.

Нижче встановлюємо сітку DBGrid з вкладки Data Controls. Властивість Align переведемо в alClient. У властивості DataSource виберемо наш DS1. Властивість ReadOnly переведемо в True, так як для редагування даних у нас будуть інші інструменти, сітка потрібна тільки для перегляду.

Тепер переходимо на вікно DM, виділяємо компонент з таблицею та відкриваємо її, встановивши Active в True. На головній формі сітка повинна відображати дані таблиці, поки ще порожні.

Однак заголовки стовпців не дуже привабливі з погляду клієнта. Виправимо це. Виділіть таблицю TBooks, і двічі клацніть по ній. Має з'явитися вікно редактора полів:

Рис. 30.1. Вікно редактора полів компонента Table

Тепер клацніть правою кнопкою з цього вікна, і виберіть команду Add all fields (додати всі поля).

Виділяємо поле Key1. Воно нам треба тільки як лічильник записів, так що користувачеві його можна не відображати. Сховайте його, встановивши його властивість Visible в False. Поле зникло з сітки, але не загубилося - воно все ще присутній у таблиці.

Далі перейдіть до поля Avtor. Тут нас цікавлять два властивості: DisplayLabel і DisplayWidth. Перше відповідає за виведений текст назви колонки, напишіть тут «Автор». Друге відповідає за ширину колонки. Встановіть 5, так як у нас тут будуть тільки цифри.

У поля Nazvanie змініть текст виводиться на «Назва книги».

У поля Exemp змініть текст виводиться на «К-ть екз.».

У поля Cena змініть текст виводиться на «Ціна».

У поля Date змініть текст виводиться на «Дата пост.».

Нарешті, поле Prim повинно містити анотацію до книзі, але оскільки це поле типу Memo, то в сітці воно все одно не вийде. Тому сховайте це поле, також як поле Key1.

Оскільки наш додаток має ще вміти підставляти прізвище автора книги замість цифри, нам потрібно зробити ще одну таблицю. Знову запустіть утиліту Database Desktop. Далі все, як і минулого разу: File - New - Table. Тип таблиці Paradox 7.

Перше поле назвіть Key2. В принципі, як саме ми назвемо ключ, особливої різниці немає, просто ми його так назвали, щоб не плутати з першим ключем. Отже, тип поля - Autoincrement.

Друге поле назвіть FIO, тип - Alpha, розмір - 30 символів. Більше полів нам не потрібно. Звичайно, можна було б зробити окремі поля на прізвище, ім'я та по батькові, однак такі ускладнення займуть більше часу і не додадуть нових знань, так що їх не варто робити в навчальній програмі.

Натискаємо кнопку «Save as», ім'я таблиці даємо avtors, а папку для таблиці - «d:\data». Можна виходити з утиліти, таблиця вже існує.

Тепер нам потрібно форма для редагування авторів, а також ще по одному компоненту Table і DataSource. Спочатку киньте ці компоненти у вікно DM. Таблицю перейменуйте в TAvtors, а DataSource - в DS2.

Далі, у таблиці у властивості DatabaseName напишіть адресу нашої бази даних - d:\data, а у властивості TableName виберіть таблицю avtors.bd.

У компонента DS2 у властивості DataSet виберіть компонент TAvtors.

Тепер приступаємо до формі редагування авторів. Створюємо нову форму, перейменовуємо її в fAvtors, у властивості Caption пишемо «Автори», властивість BorderStyle переводимо в bsDialog, з допомогою File - Use Unit додаємо до форми модуль DM. Тепер зберігаємо форму під ім'ям Avtors.

Далі, встановлюємо на форму наступні компоненти:

Label - напишіть на ній наступний текст: «Впишіть Прізвище В.О. автора:»

Звичайний Edit. Очистіть у нього властивість Text, а у властивості MaxLength (максимальна довжина) вкажіть 30. Саме такий розмір ми вказали у поля FIO при проектуванні таблиці.

Нижче встановлюємо дві кнопки, на яких пишемо: «Додати» і «Видалити поточну позицію». Ще нижче - сітку DBGrid, у властивості DataSource якої потрібно вибрати DS2. Все, приготування закінчені, можна переводити таблицю TAvtors в активний стан (Active переводимо в True). У вас повинна вийти форма на зразок цього:

Рис. 30.2. Зовнішній вигляд форми авторів

Кнопку «Додати» зробіть поки недоступною. Ми будемо давати користувачу доступ до неї тоді, коли він що-небудь введе в полі Edit.

Тепер так само, як ми редагували назви полів у першому таблиці, зробіть це в другій. Поле Key2 ховати не потрібно, просто в заголовку стовпця вкажіть «№». А в заголовку другого стовпчика - Прізвище «В.О.».

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