Практикум по Delphi для решения прикладных задач


         

Тест


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

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

На первой форме происходит регистрация учащегося.




 

          

Фрагмент программы (unit1):

uses Unit2;

{$R *.dfm}

procedure TForm1.Button2Click(Sender: TObject);

begin

 Close;

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

  Form2.Label3.Caption:=Form1.Edit1.Text;

  Form2.ShowModal;

end;
           На второй форме предлагается выбрать один из тестов.

 


           Фрагмент программы (unit2):

 

uses Unit3, Unit6;

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);

begin

 Form3.ShowModal;

end;

procedure TForm2.Button2Click(Sender: TObject);

begin

 Form2.Close;

end;

procedure TForm2.Button3Click(Sender: TObject);

begin

 Form6.Edit2.Text:='';

 Form6.ShowModal;

end;

           На третьей форме предлагается проути тест по информатике.

 


          

Фрагмент программы (unit3):

uses Unit4, Unit2;

{$R *.dfm}

procedure TForm3.Button1Click(Sender: TObject);

begin

 k:=0;

 if (Form3.Edit1.Text='монитор') or (Form3.Edit1.Text='Монитор') or

                             (Form3.Edit1.Text='МОНИТОР') then k:=k+1;

 if (Form3.Edit2.Text='Клавиатура') or (Form3.Edit2.Text='клавиатура')

                            or (Form3.Edit2.Text='КЛАВИАТУРА') then k:=k+1;

 if Form3.Edit3.Text='8' then k:=k+1;

 Form4.Label2.Caption:=IntToStr(k);

 if k=0 then Form4.Label1.Caption:='Очень плохо' else

                               if k=1 then Form4.Label1.Caption:='Плохо' else

                                        if k=2 then Form4.Label1.Caption:='Хорошо' else

                                                    if k=3 then Form4.Label1.Caption:='Очень хорошо';

 Form3.Edit1.Text:='';

 Form3.Edit2.Text:='';

 Form3.Edit3.Text:='';

 Form4.ShowModal;

end;

procedure TForm3.Button2Click(Sender: TObject);

begin

 Form3.Close;

end;

           На следующей форме отображается результат тестирования и предложение о промотре ответа.

 


           Фрагмент программы (unit4):

uses Unit1, Unit5, Unit3, Unit2;

{$R *.dfm}

procedure TForm4.Button2Click(Sender: TObject);

begin

  Form4.Close;

end;

procedure TForm4.Button1Click(Sender: TObject);

begin

   Form4.Close;

   Form3.Close;

end;

procedure TForm4.Button3Click(Sender: TObject);

begin

  Form5.ShowModal;

end;

На следующей форме отображаются правильные ответы.

 


           Фрагмент программы (unit5):

uses Unit4, Unit3, Unit2, Unit1;

{$R *.dfm}

procedure TForm5.Button1Click(Sender: TObject);

begin

  Form4.Close;

  Form3.Close;

  Form2.Close;

  Form1.Close;

  Form5.Close;

end;

           Если учащийся выбрал тест по математике, то ему предлагается проверить свои знания таблицы умножения. На следующей форме случайным образом выбираются числа. Учащийся должен ввести значение произведения в текстовое поле. С помощью кнопки «проверка» выясняется правильность введенного ответа. Если ответ правильный, то можно сгенерировать следующий пример. После нескольких примеров можно проверить свой рейтинг.

 


          

Фрагмент программы (unit6):

uses Unit7, Unit1;

{$R *.dfm}

procedure TForm6.Button1Click(Sender: TObject);

 var n,i:integer;

begin

    randomize;

    a:=random(10)-0;

    b:=random(10)-0;

    Form6.Label1.Caption:=IntToStr(a);

    Form6.Label2.Caption:=IntToStr(b);

    Form6.Edit2.Text:='';

    Form6.Label6.Caption:='';

end;

procedure TForm6.Button2Click(Sender: TObject);

begin

 if (a*b=StrToInt(Form6.Edit2.Text)) then

   begin

     Form6.Label6.Caption:='Правильно';

     m:=m+1;

     r:=r+1;

     q:=q+1;

   end

 else

   begin

     Form6.Label6.Caption:='Не правильно';

     r:=r-1;

     q:=q+1;

   end;

  Form6.Label1.Caption:='';

  Form6.Label2.Caption:='';

end;

procedure TForm6.Button3Click(Sender: TObject);

var

  c:real;

begin

Form7.Label2.Caption:=Form1.Edit1.Text;

Form7.Label1.Caption:='Вы ответили на '+IntToStr(q)+' вопросов, из них правильно  '+IntToStr(m);

Form7.Label4.Caption:='Ваш рейтинг = '+IntToStr(r);

c:=m/q;

if c=0 then Form7.Label3.Caption:='Очень плохо' else

if (c>0)and(c<0.5) then Form7.Label3.Caption:='Плохо' else

if c=0.5 then Form7.Label3.Caption:='Надо доучить' else

if (c>0.5) and (c<1) then Form7.Label3.Caption:='Хорошо' else

if c=1 then Form7.Label3.Caption:='Молодец!';

     Form7.ShowModal;

end;

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

 


Фрагмент программы (unit7):

uses Unit6;

{$R *.dfm}

procedure TForm7.Button1Click(Sender: TObject);

begin

  Form7.Close;

  Form6.Close;

end;

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

function ShowModal: Integer;

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


Практическая работа № 36

«Проигрыватель»

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

1. Создать на диске папку (например C:\MyDelphi\MyMPlayer), в которой будет создан проект, скопировать в эту папку несколько музыкальных файлов и клипов. Для демонстрации полноценной работы проигрывателя понадобятся мультимедийные файлы wav, mid, wma, mp3, avi, которые можно найти в соответствующих папках.

Видеофайлы:

C:\Program Files\Borland\Delphi5\Demos\Coolstuf\

Аудиофайлы:

C:\Windows\Media\

C:\Program Files\Microsoft Office\Office10\Media\

C:\Мои документы\Мои музыкальные записи\

2. Создать простейшую программу проигрывания аудиофайлов (см. практическая работа № 10). Для этого поместить на форму кнопку, медиапроигрыватель, диалог загрузки. 

          

3. Настроить фильтр диалога на файлы мультимедиа, соответственно заполнив таблицу. Убрать ненужные нам кнопки проигрывателя, отключив их в Инспекторе Объектов (VisibleButtons). В обработчике нажатия на кнопку описать загрузку файла.

 

 

 

 

 

 


OpenDialog – диалог загрузки

Основное свойство – FileName: string, основной метод – Execute. При вызове из программы метода Execute происходит вывод на экран диалога. Параметры вывода определяются свойствами компонента: Title, Options, Filter и др. Если пользователь выберет файл для открытия и нажмет "Открыть", то в свойство FileName компонента будет записано имя этого файла в формате string, а результатом метода Execute будет значение true. Иначе, если пользователь нажмет в диалоге кнопку "Отмена", результат Execute будет false, т.е. Execute является булевой функцией.

MediaPlayer –  медиапроигрыватель.

Основное свойство – FileName: string, основные методы – Play, Pause, Stop. После того как в свойство FileName помещено имя файла мультимедиа, этот файл нужно открыть с помощью метода Open (если установлено свойство AutoOpen, то открытие происходит автоматически, но это не всегда хорошо, поскольку, если FileName не задано или задано неверно, то при автооткрытии произойдет ошибка).

Когда файл открыт, он может быть воспроизведен. Это можно сделать с помощью кнопок самого компонента, либо вызвав метод Play. То же касается остановки и паузы – методов Stop и Pause. Для настройки внешнего вида компонента используется множество свойств, к примеру, можно скрыть часть кнопок, настраивая свойство VisibleButtons.

4. В обработчике нажатия на Button1 вызывается метод диалога Execute, и если он выдает результат true (пользователь выбрал файл и нажал "Открыть"), то медиапроигрывателю передается имя этого файла и файл открывается. Теперь пользователь может воспроизвести его, нажав кнопки проигрывателя.

procedure TForm1.Button1Click(Sender: TObject);

begin

  if OpenDialog1.Execute then

  begin

    MediaPlayer1.FileName := OpenDialog1.FileName;

    MediaPlayer1.Open;

  end;

end;

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

 


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

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

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

6. Проигрыватель готов. Необходимо оформить пользовательский интерфейс. Поместите на форму панель, а на ней расположите компонент Image, растянутый на всю панель. Добавьте таймер. Сохраните модуль как MainUnit и проект как MyMPlayer в приготовленную папку. 

 


Дайте каждому компоненту подходящее название. Обработчик кнопки поменяйте согласно последним изменениям и добавьте растяжение видеоэкрана. Для того чтобы видеофайлы воспроизводились не в отдельном окне, а в заданном месте, например, на Panel1, необходимо соответствующим образом установить свойство медиапроигрывателя Display. Для того чтобы установить и растянуть нужным образом изображение, нужно установить свойство DisplayRect:

MPlayer.DisplayRect := Rect(0, 0, ScreenPanel.Width, ScreenPanel.Height);

где Rect(x1, y1, x2, y2: integer):TRect – функция, преобразующая четыре числа к формату TRect – прямоугольник, а свойство DisplayRect имеет именно этот формат.

Для того чтобы переименовать компоненты измените свойство Name. Настройте параметры компонентов:

1)      MainForm: TmainForm

Caption = ‘Мультимедиа-проигрыватель’

ClientWidth = 402

ClientHeigth = 290

2)      LifeTimer: Ttimer

Enabled = false

Interval = 100

3)      MediaOpenDlg: TOpenDialog

Options: ofFileExist = true

Title = ‘Загрузить файл мультимедиа’

4)      LifeImage: TImage

Align = alClient

5)      ScreenPanel: TPanel

Align = alTop

Caption = ‘’

Width = 402

Heigth = 252

6)      MediaOpenBtn: TButton

Captoin = ‘Открыть’

7)      MPlayer: TMediePlayer

Display = ScreenPanel

VisibleButtons = [btPlay, btPause, btStop]

          

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

Для этого добавим соответствующий "рассчитывающий" и "рисующий" код. 

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

const
  XSize = 40;
  YSize = 25;
type
  TLifeCells = array [0 .. XSize - 1, 0 .. YSize - 1] of boolean;
var
  MainForm: TMainForm;
  A: TLifeCells;

Создадим процедуру, случайным образом заполняющую массив.

procedure RandomCells;
var
i, j: integer;
begin
  for i := 0 to XSize - 1 do

     for j := 0 to YSize - 1 do

          A[i, j] := random < 0.5;
end;

Это процедура случайного заполнения поля. Булева переменная присваивается логически, а не с помощью условного оператора if random < 0.5 then A[I,j] := true else A[i.j] := false.

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

function NumOfCells(x, y: integer): integer;
var
i, j, xx, yy: integer;
begin
  Result := 0;
  for i := -1 to 1 do
  for j := -1 to 1 do
  begin
    xx := x + i; yy := y + j;
    if xx = -1 then xx := XSize - 1;
    if yy = -1 then yy := YSize - 1;
    if xx = XSize then xx := 0;
    if yy = YSize then yy := 0;
    if A[xx, yy] then inc(Result);
  end;
  if A[x, y] then dec(Result);
end;

Это функция подсчета живых соседей. Обратите внимание на переменные xx и yy. Подобным образом реализуется "сшивка" верхнего и нижнего, левого и правого краев. В конце исключается сама клетка из числа соседей: if A[x, y] then dec(Result);

Создадим процедуру, рисующую поле клеток. 

procedure DrawCells;
var
i, j: integer;
begin
  with MainForm.LifeImage.Canvas do
  begin
    Brush.Color := сlBlue;
    Pen.Color := Brush.Color;
    Rectangle(0, 0, XSize * 10, YSize * 10);
    Brush.Color := clRed;
    for i := 0 to XSize - 1 do
    for j := 0 to YSize - 1 do
    if A[i, j] then Rectangle(i * 10, j * 10, i * 10 + 10, j * 10 + 10);
  end;
end;

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

procedure TMainForm.LifeTimerTimer(Sender: TObject);
var
i, j: integer;
B: TLifeCells;
begin
  for i := 0 to XSize - 1 do
  for j := 0 to YSIze - 1 do
  case NumOfCells(i, j) of
  2: B[i, j] := A[i, j];
  3: B[i, j] := true;
  else B[i, j] := false;
  end;
  A := B;
  DrawCells;
end;

Каждый такт таймера пересчитываются живые клетки. Алгоритм игры, как известно, состоит в следующем. Если у живой клетки два или три соседа, то она остается живой, если меньше двух, – погибает от одиночества, если больше трех, – гибнет от тесноты. Рождается клетка, если у нее ровно три соседа. С помощью оператора case реализуются правила. Использование локального массива B необходимо. Если записывать сразу в A, то клетки нового поколения будут также считаться при учете соседей, что недопустимо: каждому – свое. В конце, однако, нужно скопировать новое поколение, содержащееся в B, в массив A. Простые массивы можно копировать присвоением: A := B.

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

procedure TMainForm.MediaOpenBtnClick(Sender: TObject);
begin
  if MediaOpenDlg.Execute then
  begin
    MPlayer.FileName := MediaOpenDlg.FileName;
    MPlayer.Open;
    MPlayer.DisplayRect := Rect(0, 0, ScreenPanel.Width, ScreenPanel.Height);
    MPlayer.Play;
    if ExtractFileExt(MPlayer.FileName) = '.avi' then LifeImage.Visible := false
    else begin
    LifeImage.Visible := true;
    RandomCells;
    LifeTimer.Enabled := true;
    end;
  end;
end;

Экран вывода "растягивается" на всю панель. Добавлен автоматический запуск воспроизведения. В зависимости от расширения воспроизводимого файла, LifeImage, на котором происходит "Жизнь", либо прячется (в случае '.avi'), либо выводится (в других случаях).

 


8. Необходимо предоставить пользователю возможность настройки цветов. На форму добавляем три компонента – два диалога выбора цвета FieldColorDlg: TColorDialog (Color = clNavy), CellColorDlg: TColorDialog (Color = clYellow) и одну кнопку SetColorBtn: TButton (Caption = ‘Цвета’).

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

 


ColorDialog (основное свойство Color: TColor, основной метод – Execute) -  диалог выбора цвета. Если пользователь выбрал цвет и нажал "ОК", результат этого метода – true, если пользователь нажал "Отмена", то результат false.

procedure TMainForm.SetColorBtnClick(Sender: TObject);
begin
  if MessageDlg('Изменить цвета?', mtConfirmation, [mbYes, mbNo], 0) = mrNo then exit;
  if MainForm.FieldColorDlg.Execute then ScreenPanel.Color := MainForm.FieldColorDlg.Color;
  MessageDlg('Теперь измените цвет клеток', mtInformation, [mbOk], 0);
  CellColorDlg.Execute;
  DrawCells;
end;

 

MessageDlg(const Msg: string; DlgType: TMsgDlgType; Buttons: TMsgDlgButtons; HelpCtx: Longint): Word; - вызов окна сообщения. Это служебное окно.

Msg – текст сообщения.

DlgType – тип диалога. Возможные значения: mtWarning, mtError, mtInformation, mtConfirmation, mtCustom.

Buttons – используемые кнопки. Переменная типа "множество", возможные элементы: mbYes, mbNo, mbOK, mbCancel, mbAbort, mbRetry, mbIgnore, mbAll, mnNoToAll, mbYesToAll, mbHelp.

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

MessageDlg возвращает число, равное одной из следующих констант: mrNone, mrAbort, mrYes, mrOk, mrRetry, mrNo, mrCancel, mrIgnore, mrAll. Эти константы – так называемый модальный результат (modal result) нажатия на соответствующую кнопку диалога.

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

Функции MessageDlg и Execute Delphi позволяет вызывать как процедуры:

MediaOpenDlg.Execute вместо if MediaOpenDlg.Execute then …

При этом невозможно проконтролировать, какие кнопки были нажаты: "OK" или "Отмена".

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

Внесите изменения в DrawCells:

with MainForm.LifeImage.Canvas do
begin
Brush.Color := MainForm.FieldColorDlg.Color;
Pen.Color := Brush.Color;
Rectangle(0, 0, XSize * 10, YSize * 10);
Brush.Color := CellColorDlg.Color;

Brush.Color := MainForm.CellColorDlg.Color;

……….

 

 


9. На данный момент форма может быть масштабирована во время работы приложения. Для того чтобы зафиксировать размер формы, необходимо установить соответствующее свойство BorderStyle. Кроме того, нужно запретить разворачивание формы на весь экран (это также нежелательно).

Перечислим основные свойства и события формы.

Свойства

BorderIcons: TBorderIcons – иконки заглавной панели формы, тип свойства – множество, возможные элементы: biSystemMenu, biMinimize, biMaximize, biHelp (в данном случае нужно отключить biMaximize, чтобы форму нельзя было развернуть).

BorderStyle: ТBorderStyle – стиль бордюра bsDialog, bsSingle, bsNone, bsSizeable, bsToolWindow, bsSizeToolWin. Если установить bsNone, то форма будет без заглавной панели, а значит и без кнопки закрытия программы. Следовательнр, выходить из программы возможно либо с помощью Alt-F4, либо предусмотреть дополнительную кнопку с методом Close.

ClientWidth, ClientHeigth: integer – ширина и высота клиентской области формы.

FormStyle: TFormStyle – стиль формы. Наиболее важным значением является fsStayOnTop. Если установлено это значение, то форма будет находиться над остальными формами, даже если она не активна, и пересекается с активной на данный момент формой.

Position: TPosition – расположение формы. Здесь задаются различные начальные положения формы, например, задать положение по центру экрана (poScreenCenter). При запуске программы она окажется в центре.

WindowState: TWindowState – состояние окна. Возможные значения: wsNormal, wsMinimized, wsMaximized. С помощью этого свойства можно разворачивать и сворачивать форму.

События

OnActivate, OnDesactivate(Sender: TObject) – события, возникающие, когда форма активируется и дезактивируется (при этом в форму переходит или ее покидает фокус ввода).

OnCloseQuery(Sender: TObject; var CanClose: Boolean) – событие, возникающее при попытке закрыть форму. Например, некоторая программа спрашивает: "Вы хотите завершить программу?". Это – результат действия подобного события. Делается это в обработчике подобным образом: CanClose := MessageDlg('Exit now?', mtConfirmation, [mbYes, mbNo], 0) = mrYes. 

OnCreate, OnDestroy(Sender: TObject) – событие, необходимое для описания действий, требуемых в начале или в конце работы приложения (открытие и закрытие файлов, чтение и сохранение настроек, инициализация переменных и т.п.).

OnHide, OnShow(Sender: TObject) – вызываются, когда форма становится видимой или невидимой (при присвоении соответствующего значения свойства Visible).

OnResize(Sender: TObject) – происходит, когда форма растягивается или сжимается. Здесь описывают изменение размеров и расположение компонентов на форме.

10. Продолжить оформление медиапроигрывателя.

1) Для того чтобы случайное заполнение было более случайным, в событии формы OnCreate добавить Randomize.

 

procedure TMainForm.FormCreate(Sender: TObject);

begin

   Randomize;

   RandomCells;

end;

2) Также желательно зациклить воспроизведение медиафайла, описать обработчик события медиапроигрывателя OnNotify. У MediaPlayer есть два свойства – Position: LongInt и Length: LongInt. Это, соответственно, текущая позиция в записи и длина записи. Событие проигрывателя OnNotify возникает при завершении различных управляющих методов MediaPlayer. Состояние проигрывателя можно узнать из свойства Mode.

Когда запись будет доигрываться до конца, будет вызываться метод Play, запускающий ее с начала. При остановке проигрывателя «перематываем» запись на начало.

if Position = Length then Play

procedure TMainForm.MPlayerNotify(Sender: TObject);

begin

  with MPlayer do case Mode of mpPlaying: if Position = Length then Play;

  mpStopped: Rewind;

  end;

end;

3)    Установить  название приложения и выбрать ему иконку. В главном меню открыть Project => Options. На вкладке Application, задать название приложения (это название, в частности, будет отображаться снизу на панели задач Windows), а также загрузить подходящую иконку. Стандартную иконку можно найти в папке C:\Program Files\Common Files\Borland Shared\Images\Icons, создать свою иконку – в редакторе Delphi (меню Tools => Image Editor) или в любом другом редакторе ресурсов.

 

 


 11. Проанализируем недостатки: не выводится название проигрываемого файла, компоненты на форме не очень-то гармонируют друг с другом, неизвестно, сколько процентов записи проиграно на данный момент, нельзя устанавливать скорость визуализации, нет возможности редактирования и сохранения позиций игры "Жизнь".

Создайте копию проекта. Добавить в проект новую форму для управления воспроизведением медиафайла. Для создания новой формы щелкните на кнопку меню. Есть возможность сделать это и в пункте меню File => New… Там же можно создать форму с помощью мастера.

Добавьте на форму пять кнопок SpeedButton, TrackBar и таймер.

 


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

 


Основные свойства – Glyph:TPicture (картинка), Down:boolean (нажата), основное событие – OnClick. У этой кнопки есть еще несколько важных свойств. Для того чтобы кнопка могла быть нажата, ее свойство Group: integer должно быть больше нуля. В этом случае все кнопки с одинаковым значением группы становятся переключателями – если нажать одну, остальные отщелкиваются. Это может быть нежелательно в случае одной кнопки с ненулевой группой – один раз нажав, ее нельзя отключить. В этом случае поможет свойство AllowAllUp: boolean. Современный вид кнопкам придает свойство Flat: boolean.

В программе откажемся от стандартного интерфейса MediaPlayer, и, сделав его невидимым, будем обращаться к нему через созданные кнопки. Коллекция иконок для кнопок находится в папке C:\Program Files\Common Files\Borland Shared\Images\Buttons\.

Настройка TrackBar проводится с помощью свойств ThumbLength: integer (длина бегунка), TickMarks: TTickMark (положение рисок линейки), TickStyle: TTickStyle (стиль рисок).

Настройте форму и компоненты. Измените имена компонент.

1) ControlForm: TControlForm

Caption = ‘медиафайл не загружен’

BorderStyle = bsToolWindow

ClientHeight = 24

ClientWidth = 402

2) NewSpBtn: TSpeedButton

AllowAllUp = true

Flat = true

Glyph: rety.bmp

GroupIndex = 1

3) PosTrk: TTrackBar

ThumbLength = 15

TickMarks = tmBoth

TickStyle = lsNone

4) PosTimer = TTimer

Enabled = false

Interval = 500

5) MediaOpenSpBtn: TSpeedButton

Flat = true

Glyph: MdOpen.bmp

6) PlaySpBtn: TSpeedButton

AllowAllUp = true

Flat = true

Glyph: vcrplay.bmp

GroupIndex = 1

7) PlaySpBtn: TSpeedButton

AllowAllUp = true

Flat = true

Glyph: vcrpause.bmp

GroupIndex = 1

8) StopSpBtn: TSpeedButton

AllowAllUp = true

Flat = true

Glyph: vcrstop.bmp

GroupIndex = 1

12. Когда требуется обратиться к каким-то переменным, функциям, типам, объектам, описанным в другом модуле, необходимо описать ссылку на этот модуль в разделе uses. Однако тонкость состоит в том, что uses может располагаться и в разделе interface, и в разделе implementation. При запуске приложения новое окно должно находиться под главным окном. Но если описать ссылки двух модулей друг на друга в разделах interface, возникнет так называемая ошибка круговой ссылки. В рамках Pascal два модуля не могут использовать интерфейс друг друга. Ссылка в интерфейсе нужна для того, чтобы при описании типов, классов, переменных интерфейса использовать типы и константы, описанные в других модулях. В данном интерфейсе используется, к примеру, класс TForm, TSpeedButton, TTrackBar, TTimer, описанные, соответственно, в модулях Forms, Buttons, ComCtrls, ExtCtrls. То есть использование других модулей в интерфейсе дает возможность, прежде всего, описать новые типы с помощью уже описанных типов. Именно поэтому запрещена круговая ссылка. Тогда потенциально возможно было бы описать: TypeA = array[0..4] of TypeB – в одном модуле и TypeB = array [0..99] of TypeA – в другом. То есть ignotum per ignotius – неизвестное через еще более неизвестное (лат.).

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

Var ControlFom: TControlFom;

implementation

{$R *.DFM}

procedure TControlFom.FormCreate (Sender: Tobject);

begin

  Top := MainForm.Top + MainForm.Height + 4;

  Left := MainForm.Left;

end;

 

end.

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

 


 Если нажать кнопку Yes, то в раздел uses автоматически добавится ссылка на нужный модуль. Однако, система не всегда автоматически работает. Лучше добавлять ссылки самостоятельно. В данном случае – в раздел implementation.

implementation

  uses MainUnit;

Здесь идет обращение к MainForm, и данная форма должна быть к этому моменту (то есть моменту создания ControlForm) уже созданной.

По умолчанию при создании приложения полагается, чтобы при запуске выводилась только главная форма. Однако в данном случае в программе должны быть видны обе формы. Обязательно, у ControlForm установить свойство Visible := true. Иначе форма будет не видна при запуске программы.

Рис. 85.

   

Можно описать тот же код, с точностью до перемены ссылок, в обработчике MainForm.OnCreate главной формы. Для того чтобы выбрать нужный модуль, нажмите на кнопку панели инструментов (выбрать модуль для редактирования). Соседняя кнопка служит для выбора форм (выбрать форму для редактирования). Следующая кнопка – переключатель модуль/форма.

 


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

13.  Оставим главной форме только предназначение экрана. Удалите код внутри обработчиков кнопок, а также сами кнопки.

procedure TMainForm.MediaOpenBtnClick (Sender : TObject);

begin

 

end;

procedure TMainForm.SetColorBtnClick (Sender : TObject);

begin

end;

Установите медиапроигрывателю Visible := false, переместите его поверх панели. Если панель в форму добавлена позже проигрывателя, то проигрыватель окажется под панелью. Поэтому предварительно поменяйте порядок – щелкните правой кнопкой мыши на проигрыватель и выберите "Bring To Front".

 


Теперь измените клиентскую высоту формы. Экран готов.

 


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

 

procedure TControlForm.PosTimerTimer(Sender: TObject);
begin
  PosTrkBar.Position := MainForm.MPlayer.Position;
end;

procedure TControlForm.PosTrkBarChange(Sender: TObject);
begin
  if not PosTimer.Enabled then
  MainForm.MPlayer.Position := PosTrkBar.Position;
end;

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

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

Обратите внимание на два таймера – один запускает визуализацию, а другой двигает бегунок. Передвижения бегунка действенны, только когда воспроизведение остановлено – PosTimer.Enabled := false. Иначе происходит конфликт – таймер меняет позицию бегунка, а бегунок в ответ меняет позицию проигрывателя. Если необходимо менять позицию во время воспроизведения, придется вводить дополнительные проверки.

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

procedure TControlForm.PlaySpBtnClick(Sender: TObject);
begin
  MainForm.MPlayer.Play;
  PosTimer.Enabled := true;
end;

procedure TControlForm.PauseSpBtnClick(Sender: TObject);
begin
  MainForm.MPlayer.Pause;
  PosTimer.Enabled := false;
end;

procedure TControlForm.StopSpBtnClick(Sender: TObject);
begin
  MainForm.MPlayer.Stop;
  PosTimer.Enabled := false;
end;

Запуская и останавливая проигрыватель, мы запускаем и останавливаем таймер. Необходимо внести изменения в код в модуле MainUnit, событие OnNotify проигрывателя:

 

procedure TMainForm.MPlayerNotify(Sender: TObject);
begin
  if ControlForm.RewSpBtn.Down then
  with MPlayer do
  if NotifyValue = nvSuccessful then
  begin
    Notify := true;
    Play;
  end;
end;

Здесь происходит проверка, зажата ли кнопка "цикличного воспроизведения" во второй форме. При открытии файла необходимо включить все кнопки и бегунок (изначально Enabled := false), запустить таймеры, настроить PosTrkBar на длину записи и вывести название воспроизводимого файла в Caption формы:

procedure TControlForm.MediaOpenSpBtnClick(Sender: TObject);
begin
  with MainForm do
  if MediaOpenDlg.Execute then
  begin
    MPlayer.FileName := MediaOpenDlg.FileName;
    MPlayer.Open;
    MPlayer.Display := ScreenPanel;
    MPlayer.DisplayRect := Rect(0, 0, ScreenPanel.Width, ScreenPanel.Height);
    MPlayer.Play;
    LifeImage.Visible := not (ExtractFileExt(MPlayer.FileName) = '.avi');
    LifeTimer.Enabled := true;
    ControlForm.PosTimer.Enabled := true;
    ControlForm.PosTrkBar.Max := MPlayer.Length;
    ControlForm.PosTrkBar.Enabled := true;
    ControlForm.PlaySpBtn.Enabled := true;
    ControlForm.PauseSpBtn.Enabled := true;
    ControlForm.StopSpBtn.Enabled := true;
    ControlForm.Caption := MediaOpenDlg.Filename;
  end;
end;

14. Добавить к медиапроигрывателю игру «жизнь». Для управления визуализацией потребуется отдельная форма. С помощью этой формы можно очищать, заполнять и редактировать конфигурации клеток игры «Жизнь». Более того, появляется возможность сохранять интересные комбинации в специальные файлы с введенным нами расширением .lif. Кроме того, можно будет управлять скоростью «жизни» и, что уже реализовывалось, задавать цвет поля и клеток.

                  Создайте новую форму, сохраните ее модуль, поместите на нее пять кнопок Button, одну SpeedButton, один TrackBar, две надписи Label и диалоги загрузки и сохранения.

Измените параметры компонент:

LifeSaveDlg: TSaveDialog

DefaultExt = ‘lif’

Filter = ‘файлы игры «Жизнь» (*.lif)|*.lif’

Title = ‘Загрузить позицию игры’

LifeOpenDlg: TOpenDialog

DefaultExt = ‘lif’

Filter = ‘файлы игры «Жизнь» (*.lif)|*.lif’

Options.ofFileMustExist = true

Title = ‘Сохранить текущую позицию игры’

 


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

 

procedure TControlForm.MediaOpenSpBtnClick(Sender: TObject);
begin
  with MainForm do
  if MediaOpenDlg.Execute then
  begin
    MPlayer.FileName := MediaOpenDlg.FileName;
    MPlayer.Open;
    MPlayer.Display := ScreenPanel;
    MPlayer.DisplayRect := Rect(0, 0, ScreenPanel.Width, ScreenPanel.Height);
    MPlayer.Play;
    LifeImage.Visible := not (ExtractFileExt(MPlayer.FileName) = '.avi');
    LifeForm.Visible := LifeImg.Visible;

    LifeForm.EditSpBtn.Down := false;   

    LifeTimer.Enabled := true;
    ControlForm.PosTimer.Enabled := true;
    ControlForm.PosTrkBar.Max := MPlayer.Length;
    ControlForm.PosTrkBar.Enabled := true;
    ControlForm.PlaySpBtn.Enabled := true;
    ControlForm.PauseSpBtn.Enabled := true;
    ControlForm.StopSpBtn.Enabled := true;
    ControlForm.Caption := MediaOpenDlg.Filename;
  end;
end;

На новой форме LifeForm располагаются кнопки LifeOpenBtn, LifeSaveBtn, FillBtn, ClearBtn, SetColorBtn, EditSpBtn, SpeedTrkBar. Настройте компоненты новой формы:

LifeForm: TLifeForm

BorderStyle = bsToolWindow

Caption = ‘Визуализация’

ClientWidth = 402

ClientHeight = 56

EditSpBtn: TSpeedButton

AllowAllUp = true

Caption = ‘редактировать’

GroupIndex = 1

SpeedTrkBar:TTrackBar

Frequency = 50

Max = 1000

Min = 1

Position = 100

ThumbLength = 15

TickMarks = tmTopLeft

 

В обработчике загрузки медиафайла необходимо добавить следующие строки.

LifeForm.Visible := LifeImg.Visible;

Если LifeImg видимый, т.е. нужна визуализация для звукового файла, то форма управления визуализацией тоже выводится, если же LifeImg спрятан (воспроизводится видеофайл), то и присутствие формы управления визуализацией излишне.

LifeForm.EditSpBtn.Down := false;

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

MPlayer.Play;
LifeImg.Visible := not (ExtractFileExt(MPlayer.FileName) = '.avi');
LifeForm.Visible := LifeImage.Visible;
LifeForm.EditSpBtn.Down := false;LifeTimer.Enabled := true;
ControlForm.PosTimer.Enabled := true;

….

15. Необходимо описать обработчики событий для новых компонентов. Определим при создании формы ее местоположение.

В модуле LifeUnit:

var LifeForm: TLifeForm;

implementation

uses MainUnit, ControlUnit;

procedure TLifeForm.FormCreate(Sender: TObject);
begin
  Top := ControlForm.Top + ControlForm.Height + 4;
  Left := ControlForm.Left;
end;

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

В модуле LifeUnit:

procedure TLifeForm.ClearBtnClick(Sender: TObject);
var
  i, j: integer;
begin
  for i := 0 to XSize - 1 do
  for j := 0 to YSize - 1 do
  A[i, j] := false;
  DrawCells;
end;

procedure TLifeForm.FillBtnClick(Sender: TObject);
begin
  RandomCells;
  DrawCells;
end;

 

Однако для того, чтобы подобный вызов был возможен, необходимо в главном модуле объявить эти процедуры в интерфейсе. Извне доступно только то, что объявлено в интерфейсе. В MainUnit необходимо "вывести" в интерфейс объявление процедур:

 

var

    MainForm: TMainForm;

    A: TLifeCells;

procedure RandomCells;
procedure DrawCells;

implementation

uses ControlUnit;

{&r *.DFM}

ТrackBar устанавливает интервал для таймера, задавая тем самым скорость визуализации. А кнопка редактирования переключает режим редактирования, останавливая и включая таймер.

 

procedure TLifeForm.FillBtnClick(Sender: TObject);
begin
  RandomCells;
  DrawCells;
end;

procedure TLifeForm.SpeedTrkBarChange(Sender: TObject); {Меняет скорость «жизни»}
begin
  MainForm.LifeTimer.Interval := SpeedTrkBar.Position;
end;

procedure TLifeForm.EditSpBtnClick(Sender: TObject);{включает/выключает режим редактирования}
begin
  MainForm.LifeTimer.Enabled := not EditSpBtn.Down;
end;

Само редактирование клеток происходит в главном модуле, в обработчике OnMouseDown компонента LifeImg.

procedure TMainForm.LifeImgMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
  if LifeForm.EditSpBtn.Down then
  A[X div 10, Y div 10] := not A[X div 10, Y div 10];
  DrawCells;
end;

В uses нужно описать ссылку на LifeUnit.

При запуске программы получается примерно такая картинка.

 


Теперь можно редактировать позиции.

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

           Типизированные файлы содержат записи определенного типа. К примеру, file of integer может хранить произвольное количество переменных integer; file of TLifeCells – произвольное количество переменных типа TLifeCells. Однако для наших целей достаточно только одной записи. При работе с типизированными файлами используются следующие процедуры.

AssignFile(var F; FileName: string) – связывает файловую переменную F с текстовым именем файла. Это действие необходимо сделать перед всеми последующими операциями.

Reset(F) – открывает существующий файл, связанный с переменной F, и устанавливает позицию чтения-записи в начало.

Rewrite(F) – создает файл, соответствующий файловой переменной F.

Read(F, V) – читает из файла, связанного с F, данные в типизированную переменную V. Позиция файла увеличивается на один файловый компонент, байтовый размер которого равен размеру типа переменной (для integer это 4 байта).

Write(F, V) – записывает в файл, связанный с F, типизированную переменную V. Позиция файла увеличивается на один файловый компонент.

Seek(F, N: LongInt) – перемещает позицию файла к номеру N, начальная позиция при N = 0.

CloseFile(var F) – закрывает файл F.

В главном модуле описан тип TFileCells:

      {Private declaration}

 public

      {Public  declaration}

end;

 

const

           XSize = 40;

           YSize = 25;

type

           TFileCells = array [0 .. XSize – 1, 0 .. YSize – 1] of  boolean;

var

           MainForm: TMainForm;

           A: TFileCells;

В обработчике OnClick кнопки LifeOpen определяем файл такого же типа. Если файл выбран в диалоге, ассоциируем имя файла с переменной, устанавливаем позицию для чтения в начало и считываем из файла данные в переменную A, описанную в MainUnit и хранящую положения клеток.

procedure TLifeForm.LifeOpenBtnClick(Sender: TObject);
var
  F: file of TLifeCells;
begin
  if LifeOpenDlg.Execute then
           if FileExists(LifeOpenDlg.FileName) then
  begin
    AssignFile(F, LifeOpenDlg.FileName);
    Reset(F);
    Read(F, A);
    CloseFile(F);
    DrawCells;
  end;
end;

Подобное чтение корректно, поскольку типы файла и переменной A совпадают.

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


procedure TLifeForm.LifeSaveBtnClick(Sender: TObject);
var
  F: file of TLifeCells;
begin
  if LifeSaveDlg.Execute then
  begin
    AssignFile(F, LifeSaveDlg.FileName);
    if not FileExists(LifeSaveDlg.FileName) then
      Rewrite(F)
    else if MessageDlg('Перезаписать?', mtWarning, [mbYes, mbNo], 0) = mrYes then
      Reset(F)
    else begin
      CloseFile(F);
      exit;
    end;
    Write(F, A);
    CloseFile(F);
  end;
end;

Работа с нетипизированными файлами похожа на работу с типизированными. Отличия в том, что объявляются такие файлы просто var F: file; (без of …), при открытии – Reset(F, 1) – нужно указать размер блока записи (который по умолчанию равен 128), чтение и запись происходят с помощью процедур BlockRead, BlockWirite(var F: File; var Buf; Count: Integer), где Buf – переменная, в которую (или из которой) идет запись, Count – число блоков записи (размер блока был определен в Reset). Если надо писать произвольные данные (к примеру, a: integer; b: boolean; c: TLifeCells; d: string[20]), сделайте следующее.

При записи:

Rewrite(F, 1);
BlockWrite(F, a, SizeOf(a));
BlockWrite(F, b, SizeOf(b));
BlockWrite(F, c, SizeOf(c));
BlockWrite(F, d, SizeOf(d));
CloseFile(F);

При чтении, соответственно:

Reset(F, 1);
BlockRead(F, a, SizeOf(a));
BlockRead(F, b, SizeOf(b));
BlockRead(F, c, SizeOf(c));
BlockRead(F, d, SizeOf(d));
CloseFile(F);

17. Чтобы решить проблему цвета, необходимо сделать специальное собственное диалоговое окно, позволяющее выбрать цвет. Раньше мы вызывали стандартные диалоги.

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

Создайте новую форму, сохраните модуль как ColorUnit. Добавьте на нее две фигуры Shape (CellShape: TShape, FieldShape: TShape), кнопку «Утвердить» (ModalResult = mrOk), кнопку «Отмена» (ModalResult = mrCancel).

 


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

ColorForm: TColorForm

BorderStyle = bsToolWindoiw

Caption = выберите цвета

Position = poScreenCenter

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

В модуле ColorUnit – обработчики нажатия на FieldShape и CellShape:

 

procedure TColorForm.FieldShapeMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
  MainForm.FieldColorDlg.Execute;
  FieldShape.Brush.Color := MainForm.FieldColorDlg.Color;
end;

procedure TColorForm.CellShapeMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
  MainForm.CellColorDlg.Execute;
  CellShape.Brush.Color := MainForm.CellColorDlg.Color;
end;

В обработчике кнопки "задать цвета" на форме визуализации опишите модальный вызов этого нового диалога.

В модуле LifeUnit описываем обработчик SetColorBtn:

procedure TLifeForm.ColorBtnClick(Sender: TObject);
var
  C1, C2: TColor;
begin
  C1 := MainForm.FieldColorDlg.Color;
  C2 := MainForm.CellColorDlg.Color;
  ColorForm.FieldShape.Brush.Color := C1;
  ColorForm.CellShape.Brush.Color := C2;
  if ColorForm.ShowModal = mrCancel then
  begin
    MainForm.FieldColorDlg.Color := C1;
    MainForm.CellColorDlg.Color := C2;
  end;
end;

В данном случае вызывается форма ColorForm – диалог установки цвета. Если в нем нажата кнопка "Отмена", то значения цветов восстанавливаются. Они специально для этого перед вызовом сохраняются в C1 и C2.

 

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

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

 

procedure TControlForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if MessageDlg('Выйти из программы?', mtWarning,
  [mbYes, mbNo], 0) = mrYes then MainForm.Close
  else CanClose := false;
end;

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

Запустите программу.

 


19. Подведем итоги. Для создания многооконности необходимо:

  1. Создать очередную форму.
  2. Модули, предназначенные пользователю, поместить в interface, а модули, предназначенные программистуто, поместить в uses.
  3. Необходимо помнить о круговых ссылкахи и избегать ignotum per ignotius. В интерфейс поместить только необходимое.
  4. В OnCreate делать описание, не забывая о порядке создания форм в файле проекта.
  5. Программа заканчивается, когда закрывается главная форма. Остальные формы приложения могут закрываться сколько угодно.
  6. Диалоги: вызов ShowModal, результат – ModalResult нажатой кнопки.
  7. Выбрать нужную форму или модуль для редактирования можно с помощью кнопок панели инструментов или клавишами Ctrl-F12 и Shift-F12. Переключение модуль-форма – просто клавишей F12.

ПРИЛОЖЕНИЕ 1



Содержание раздела